1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/l2cap/command_handler.h"
16
17 namespace bt::l2cap::internal {
18
ParseReject(const ByteBuffer & rej_payload_buf)19 bool CommandHandler::Response::ParseReject(const ByteBuffer& rej_payload_buf) {
20 if (rej_payload_buf.size() < sizeof(CommandRejectPayload)) {
21 bt_log(DEBUG,
22 "l2cap",
23 "cmd: ignoring malformed Command Reject, size %zu (expected >= %zu)",
24 rej_payload_buf.size(),
25 sizeof(CommandRejectPayload));
26 return false;
27 }
28 reject_reason_ = static_cast<RejectReason>(
29 le16toh(rej_payload_buf.ReadMember<&CommandRejectPayload::reason>()));
30
31 if (reject_reason() == RejectReason::kInvalidCID) {
32 if (rej_payload_buf.size() - sizeof(CommandRejectPayload) <
33 sizeof(InvalidCIDPayload)) {
34 bt_log(DEBUG,
35 "l2cap",
36 "cmd: ignoring malformed Command Reject Invalid Channel ID, size "
37 "%zu (expected %zu)",
38 rej_payload_buf.size(),
39 sizeof(CommandRejectPayload) + sizeof(InvalidCIDPayload));
40 return false;
41 }
42 const auto& invalid_cid_payload =
43 rej_payload_buf.view(sizeof(CommandRejectPayload))
44 .To<InvalidCIDPayload>();
45 remote_cid_ = le16toh(invalid_cid_payload.src_cid);
46 local_cid_ = le16toh(invalid_cid_payload.dst_cid);
47 }
48
49 return true;
50 }
51
Decode(const ByteBuffer & payload_buf)52 bool CommandHandler::DisconnectionResponse::Decode(
53 const ByteBuffer& payload_buf) {
54 const auto disconn_rsp_payload = payload_buf.To<PayloadT>();
55 local_cid_ = le16toh(disconn_rsp_payload.src_cid);
56 remote_cid_ = le16toh(disconn_rsp_payload.dst_cid);
57 return true;
58 }
59
Responder(SignalingChannel::Responder * sig_responder,ChannelId local_cid,ChannelId remote_cid)60 CommandHandler::Responder::Responder(SignalingChannel::Responder* sig_responder,
61 ChannelId local_cid,
62 ChannelId remote_cid)
63 : sig_responder_(sig_responder),
64 local_cid_(local_cid),
65 remote_cid_(remote_cid) {}
66
RejectNotUnderstood()67 void CommandHandler::Responder::RejectNotUnderstood() {
68 sig_responder_->RejectNotUnderstood();
69 }
70
RejectInvalidChannelId()71 void CommandHandler::Responder::RejectInvalidChannelId() {
72 sig_responder_->RejectInvalidChannelId(local_cid(), remote_cid());
73 }
74
SendDisconnectionRequest(ChannelId remote_cid,ChannelId local_cid,DisconnectionResponseCallback cb)75 bool CommandHandler::SendDisconnectionRequest(
76 ChannelId remote_cid,
77 ChannelId local_cid,
78 DisconnectionResponseCallback cb) {
79 auto on_discon_rsp =
80 BuildResponseHandler<DisconnectionResponse>(std::move(cb));
81
82 DisconnectionRequestPayload payload = {htole16(remote_cid),
83 htole16(local_cid)};
84 return sig()->SendRequest(kDisconnectionRequest,
85 BufferView(&payload, sizeof(payload)),
86 std::move(on_discon_rsp));
87 }
88
ServeDisconnectionRequest(DisconnectionRequestCallback cb)89 void CommandHandler::ServeDisconnectionRequest(
90 DisconnectionRequestCallback cb) {
91 auto on_discon_req = [cb = std::move(cb)](
92 const ByteBuffer& request_payload,
93 SignalingChannel::Responder* sig_responder) {
94 if (request_payload.size() != sizeof(DisconnectionRequestPayload)) {
95 bt_log(DEBUG,
96 "l2cap",
97 "cmd: rejecting malformed Disconnection Request, size %zu",
98 request_payload.size());
99 sig_responder->RejectNotUnderstood();
100 return;
101 }
102
103 const auto& discon_req = request_payload.To<DisconnectionRequestPayload>();
104 const ChannelId local_cid = le16toh(discon_req.dst_cid);
105 const ChannelId remote_cid = le16toh(discon_req.src_cid);
106 DisconnectionResponder responder(sig_responder, local_cid, remote_cid);
107 cb(local_cid, remote_cid, &responder);
108 };
109
110 sig()->ServeRequest(kDisconnectionRequest, std::move(on_discon_req));
111 }
112
CommandHandler(SignalingChannelInterface * sig,fit::closure request_fail_callback)113 CommandHandler::CommandHandler(SignalingChannelInterface* sig,
114 fit::closure request_fail_callback)
115 : sig_(sig), request_fail_callback_(std::move(request_fail_callback)) {
116 BT_ASSERT(sig_);
117 }
118
119 } // namespace bt::l2cap::internal
120