• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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