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 #include <pw_assert/check.h>
18 #include <pw_bytes/endian.h>
19
20 namespace bt::l2cap::internal {
21
ParseReject(const ByteBuffer & rej_payload_buf)22 bool CommandHandler::Response::ParseReject(const ByteBuffer& rej_payload_buf) {
23 if (rej_payload_buf.size() < sizeof(CommandRejectPayload)) {
24 bt_log(DEBUG,
25 "l2cap",
26 "cmd: ignoring malformed Command Reject, size %zu (expected >= %zu)",
27 rej_payload_buf.size(),
28 sizeof(CommandRejectPayload));
29 return false;
30 }
31 reject_reason_ = static_cast<RejectReason>(pw::bytes::ConvertOrderFrom(
32 cpp20::endian::little,
33 rej_payload_buf.ReadMember<&CommandRejectPayload::reason>()));
34
35 if (reject_reason() == RejectReason::kInvalidCID) {
36 if (rej_payload_buf.size() - sizeof(CommandRejectPayload) <
37 sizeof(InvalidCIDPayload)) {
38 bt_log(DEBUG,
39 "l2cap",
40 "cmd: ignoring malformed Command Reject Invalid Channel ID, size "
41 "%zu (expected %zu)",
42 rej_payload_buf.size(),
43 sizeof(CommandRejectPayload) + sizeof(InvalidCIDPayload));
44 return false;
45 }
46 const auto& invalid_cid_payload =
47 rej_payload_buf.view(sizeof(CommandRejectPayload))
48 .To<InvalidCIDPayload>();
49 remote_cid_ = pw::bytes::ConvertOrderFrom(cpp20::endian::little,
50 invalid_cid_payload.src_cid);
51 local_cid_ = pw::bytes::ConvertOrderFrom(cpp20::endian::little,
52 invalid_cid_payload.dst_cid);
53 }
54
55 return true;
56 }
57
Decode(const ByteBuffer & payload_buf)58 bool CommandHandler::DisconnectionResponse::Decode(
59 const ByteBuffer& payload_buf) {
60 const auto disconn_rsp_payload = payload_buf.To<PayloadT>();
61 local_cid_ = pw::bytes::ConvertOrderFrom(cpp20::endian::little,
62 disconn_rsp_payload.src_cid);
63 remote_cid_ = pw::bytes::ConvertOrderFrom(cpp20::endian::little,
64 disconn_rsp_payload.dst_cid);
65 return true;
66 }
67
Responder(SignalingChannel::Responder * sig_responder,ChannelId local_cid,ChannelId remote_cid)68 CommandHandler::Responder::Responder(SignalingChannel::Responder* sig_responder,
69 ChannelId local_cid,
70 ChannelId remote_cid)
71 : sig_responder_(sig_responder),
72 local_cid_(local_cid),
73 remote_cid_(remote_cid) {}
74
RejectNotUnderstood()75 void CommandHandler::Responder::RejectNotUnderstood() {
76 sig_responder_->RejectNotUnderstood();
77 }
78
RejectInvalidChannelId()79 void CommandHandler::Responder::RejectInvalidChannelId() {
80 sig_responder_->RejectInvalidChannelId(local_cid(), remote_cid());
81 }
82
SendDisconnectionRequest(ChannelId remote_cid,ChannelId local_cid,DisconnectionResponseCallback cb)83 bool CommandHandler::SendDisconnectionRequest(
84 ChannelId remote_cid,
85 ChannelId local_cid,
86 DisconnectionResponseCallback cb) {
87 auto on_discon_rsp =
88 BuildResponseHandler<DisconnectionResponse>(std::move(cb));
89
90 DisconnectionRequestPayload payload = {
91 pw::bytes::ConvertOrderTo(cpp20::endian::little, remote_cid),
92 pw::bytes::ConvertOrderTo(cpp20::endian::little, local_cid)};
93 return sig()->SendRequest(kDisconnectionRequest,
94 BufferView(&payload, sizeof(payload)),
95 std::move(on_discon_rsp));
96 }
97
ServeDisconnectionRequest(DisconnectionRequestCallback callback)98 void CommandHandler::ServeDisconnectionRequest(
99 DisconnectionRequestCallback callback) {
100 auto on_discon_req = [cb = std::move(callback)](
101 const ByteBuffer& request_payload,
102 SignalingChannel::Responder* sig_responder) {
103 if (request_payload.size() != sizeof(DisconnectionRequestPayload)) {
104 bt_log(DEBUG,
105 "l2cap",
106 "cmd: rejecting malformed Disconnection Request, size %zu",
107 request_payload.size());
108 sig_responder->RejectNotUnderstood();
109 return;
110 }
111
112 const auto& discon_req = request_payload.To<DisconnectionRequestPayload>();
113 const ChannelId local_cid =
114 pw::bytes::ConvertOrderFrom(cpp20::endian::little, discon_req.dst_cid);
115 const ChannelId remote_cid =
116 pw::bytes::ConvertOrderFrom(cpp20::endian::little, discon_req.src_cid);
117 DisconnectionResponder responder(sig_responder, local_cid, remote_cid);
118 cb(local_cid, remote_cid, &responder);
119 };
120
121 sig()->ServeRequest(kDisconnectionRequest, std::move(on_discon_req));
122 }
123
CommandHandler(SignalingChannelInterface * sig,fit::closure request_fail_callback)124 CommandHandler::CommandHandler(SignalingChannelInterface* sig,
125 fit::closure request_fail_callback)
126 : sig_(sig), request_fail_callback_(std::move(request_fail_callback)) {
127 PW_CHECK(sig_);
128 }
129
130 } // namespace bt::l2cap::internal
131