• 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/low_energy_command_handler.h"
16 
17 #include <pw_bytes/endian.h>
18 
19 #include <type_traits>
20 
21 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
22 
23 namespace bt::l2cap::internal {
24 namespace {
25 
26 // Helpers to make converting values less verbose.
27 // TODO(fxbug.dev/373673027) - Remove once we use Emboss.
28 template <typename T>
Serialize(T value)29 T Serialize(T value) {
30   return pw::bytes::ConvertOrderTo(cpp20::endian::little, value);
31 }
32 template <typename T>
SerializeEnum(T value)33 T SerializeEnum(T value) {
34   return static_cast<T>(pw::bytes::ConvertOrderTo(
35       cpp20::endian::little, static_cast<std::underlying_type_t<T>>(value)));
36 }
37 template <typename T>
Deserialize(T value)38 T Deserialize(T value) {
39   return pw::bytes::ConvertOrderFrom(cpp20::endian::little, value);
40 }
41 template <typename T>
DeserializeEnum(T value)42 T DeserializeEnum(T value) {
43   return static_cast<T>(pw::bytes::ConvertOrderFrom(
44       cpp20::endian::little, static_cast<std::underlying_type_t<T>>(value)));
45 }
46 
47 }  // namespace
48 
Decode(const ByteBuffer & payload_buf)49 bool LowEnergyCommandHandler::ConnectionParameterUpdateResponse::Decode(
50     const ByteBuffer& payload_buf) {
51   const uint16_t result = pw::bytes::ConvertOrderFrom(
52       cpp20::endian::little,
53       static_cast<uint16_t>(payload_buf.ReadMember<&PayloadT::result>()));
54   result_ = ConnectionParameterUpdateResult{result};
55   return true;
56 }
57 
Decode(const ByteBuffer & payload_buf)58 bool LowEnergyCommandHandler::LeCreditBasedConnectionResponse::Decode(
59     const ByteBuffer& payload_buf) {
60   const uint16_t destination_cid = pw::bytes::ConvertOrderFrom(
61       cpp20::endian::little,
62       static_cast<uint16_t>(payload_buf.ReadMember<&PayloadT::dst_cid>()));
63   destination_cid_ = ChannelId{destination_cid};
64   const uint16_t mtu = pw::bytes::ConvertOrderFrom(
65       cpp20::endian::little,
66       static_cast<uint16_t>(payload_buf.ReadMember<&PayloadT::mtu>()));
67   mtu_ = mtu;
68   const uint16_t mps = pw::bytes::ConvertOrderFrom(
69       cpp20::endian::little,
70       static_cast<uint16_t>(payload_buf.ReadMember<&PayloadT::mps>()));
71   mps_ = mps;
72   const uint16_t initial_credits = pw::bytes::ConvertOrderFrom(
73       cpp20::endian::little,
74       static_cast<uint16_t>(
75           payload_buf.ReadMember<&PayloadT::initial_credits>()));
76   initial_credits_ = initial_credits;
77   const uint16_t result = pw::bytes::ConvertOrderFrom(
78       cpp20::endian::little,
79       static_cast<uint16_t>(payload_buf.ReadMember<&PayloadT::result>()));
80   result_ = LECreditBasedConnectionResult{result};
81   return true;
82 }
83 
84 LowEnergyCommandHandler::ConnectionParameterUpdateResponder::
ConnectionParameterUpdateResponder(SignalingChannel::Responder * sig_responder)85     ConnectionParameterUpdateResponder(
86         SignalingChannel::Responder* sig_responder)
87     : Responder(sig_responder) {}
88 
Send(ConnectionParameterUpdateResult result)89 void LowEnergyCommandHandler::ConnectionParameterUpdateResponder::Send(
90     ConnectionParameterUpdateResult result) {
91   ConnectionParameterUpdateResponsePayload payload;
92   payload.result = ConnectionParameterUpdateResult{pw::bytes::ConvertOrderTo(
93       cpp20::endian::little, static_cast<uint16_t>(result))};
94   sig_responder_->Send(BufferView(&payload, sizeof(payload)));
95 }
96 
97 LowEnergyCommandHandler::LeCreditBasedConnectionResponder::
LeCreditBasedConnectionResponder(SignalingChannel::Responder * sig_responder)98     LeCreditBasedConnectionResponder(SignalingChannel::Responder* sig_responder)
99     : Responder(sig_responder) {}
100 
Send(ChannelId destination_cid,uint16_t mtu,uint16_t mps,uint16_t initial_credits,LECreditBasedConnectionResult result)101 void LowEnergyCommandHandler::LeCreditBasedConnectionResponder::Send(
102     ChannelId destination_cid,
103     uint16_t mtu,
104     uint16_t mps,
105     uint16_t initial_credits,
106     LECreditBasedConnectionResult result) {
107   LECreditBasedConnectionResponsePayload payload;
108   payload.dst_cid = Serialize(destination_cid);
109   payload.mtu = Serialize(mtu);
110   payload.mps = Serialize(mps);
111   payload.initial_credits = Serialize(initial_credits);
112   payload.result = SerializeEnum(result);
113   sig_responder_->Send(BufferView(&payload, sizeof(payload)));
114 }
115 
LowEnergyCommandHandler(SignalingChannelInterface * sig,fit::closure request_fail_callback)116 LowEnergyCommandHandler::LowEnergyCommandHandler(
117     SignalingChannelInterface* sig, fit::closure request_fail_callback)
118     : CommandHandler(sig, std::move(request_fail_callback)) {}
119 
SendLeCreditBasedConnectionRequest(uint16_t psm,uint16_t cid,uint16_t mtu,uint16_t mps,uint16_t credits,SendLeCreditBasedConnectionRequestCallback cb)120 bool LowEnergyCommandHandler::SendLeCreditBasedConnectionRequest(
121     uint16_t psm,
122     uint16_t cid,
123     uint16_t mtu,
124     uint16_t mps,
125     uint16_t credits,
126     SendLeCreditBasedConnectionRequestCallback cb) {
127   auto on_le_credit_based_connection_rsp =
128       BuildResponseHandler<LeCreditBasedConnectionResponse>(std::move(cb));
129 
130   LECreditBasedConnectionRequestPayload payload;
131   payload.le_psm = pw::bytes::ConvertOrderTo(cpp20::endian::little, psm);
132   payload.src_cid = pw::bytes::ConvertOrderTo(cpp20::endian::little, cid);
133   payload.mtu = pw::bytes::ConvertOrderTo(cpp20::endian::little, mtu);
134   payload.mps = pw::bytes::ConvertOrderTo(cpp20::endian::little, mps);
135   payload.initial_credits =
136       pw::bytes::ConvertOrderTo(cpp20::endian::little, credits);
137 
138   return sig()->SendRequest(kLECreditBasedConnectionRequest,
139                             BufferView(&payload, sizeof(payload)),
140                             std::move(on_le_credit_based_connection_rsp));
141 }
142 
SendConnectionParameterUpdateRequest(uint16_t interval_min,uint16_t interval_max,uint16_t peripheral_latency,uint16_t timeout_multiplier,ConnectionParameterUpdateResponseCallback cb)143 bool LowEnergyCommandHandler::SendConnectionParameterUpdateRequest(
144     uint16_t interval_min,
145     uint16_t interval_max,
146     uint16_t peripheral_latency,
147     uint16_t timeout_multiplier,
148     ConnectionParameterUpdateResponseCallback cb) {
149   auto on_param_update_rsp =
150       BuildResponseHandler<ConnectionParameterUpdateResponse>(std::move(cb));
151 
152   ConnectionParameterUpdateRequestPayload payload;
153   payload.interval_min =
154       pw::bytes::ConvertOrderTo(cpp20::endian::little, interval_min);
155   payload.interval_max =
156       pw::bytes::ConvertOrderTo(cpp20::endian::little, interval_max);
157   payload.peripheral_latency =
158       pw::bytes::ConvertOrderTo(cpp20::endian::little, peripheral_latency);
159   payload.timeout_multiplier =
160       pw::bytes::ConvertOrderTo(cpp20::endian::little, timeout_multiplier);
161 
162   return sig()->SendRequest(kConnectionParameterUpdateRequest,
163                             BufferView(&payload, sizeof(payload)),
164                             std::move(on_param_update_rsp));
165 }
166 
ServeConnectionParameterUpdateRequest(ConnectionParameterUpdateRequestCallback callback)167 void LowEnergyCommandHandler::ServeConnectionParameterUpdateRequest(
168     ConnectionParameterUpdateRequestCallback callback) {
169   auto on_param_update_req = [cb = std::move(callback)](
170                                  const ByteBuffer& request_payload,
171                                  SignalingChannel::Responder* sig_responder) {
172     if (request_payload.size() !=
173         sizeof(ConnectionParameterUpdateRequestPayload)) {
174       bt_log(DEBUG,
175              "l2cap-le",
176              "cmd: rejecting malformed Connection Parameter Update Request, "
177              "size %zu",
178              request_payload.size());
179       sig_responder->RejectNotUnderstood();
180       return;
181     }
182 
183     const auto& req =
184         request_payload.To<ConnectionParameterUpdateRequestPayload>();
185     const uint16_t interval_min =
186         pw::bytes::ConvertOrderFrom(cpp20::endian::little, req.interval_min);
187     const uint16_t interval_max =
188         pw::bytes::ConvertOrderFrom(cpp20::endian::little, req.interval_max);
189     const uint16_t peripheral_latency = pw::bytes::ConvertOrderFrom(
190         cpp20::endian::little, req.peripheral_latency);
191     const uint16_t timeout_multiplier = pw::bytes::ConvertOrderFrom(
192         cpp20::endian::little, req.timeout_multiplier);
193     ConnectionParameterUpdateResponder responder(sig_responder);
194     cb(interval_min,
195        interval_max,
196        peripheral_latency,
197        timeout_multiplier,
198        &responder);
199   };
200 
201   sig()->ServeRequest(kConnectionParameterUpdateRequest,
202                       std::move(on_param_update_req));
203 }
204 
ServeLeCreditBasedConnectionRequest(LeCreditBasedConnectionRequestCallback callback)205 void LowEnergyCommandHandler::ServeLeCreditBasedConnectionRequest(
206     LeCreditBasedConnectionRequestCallback callback) {
207   using Request = LECreditBasedConnectionRequestPayload;
208   auto on_le_credit_based_connection_request =
209       [cb = std::move(callback)](const ByteBuffer& request_payload,
210                                  SignalingChannel::Responder* sig_responder) {
211         if (request_payload.size() != sizeof(Request)) {
212           bt_log(DEBUG,
213                  "l2cap-le",
214                  "cmd: rejecting malformed LE Credit-based Connection Request, "
215                  "size %zu",
216                  request_payload.size());
217           sig_responder->RejectNotUnderstood();
218           return;
219         }
220 
221         LeCreditBasedConnectionResponder responder(sig_responder);
222         cb(Deserialize(request_payload.ReadMember<&Request::le_psm>()),
223            Deserialize(request_payload.ReadMember<&Request::src_cid>()),
224            Deserialize(request_payload.ReadMember<&Request::mtu>()),
225            Deserialize(request_payload.ReadMember<&Request::mps>()),
226            Deserialize(request_payload.ReadMember<&Request::initial_credits>()),
227            &responder);
228       };
229 
230   sig()->ServeRequest(kLECreditBasedConnectionRequest,
231                       std::move(on_le_credit_based_connection_request));
232 }
233 
234 }  // namespace bt::l2cap::internal
235