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