1 // Copyright 2024 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_proxy/proxy_host.h"
16
17 #include "pw_assert/check.h" // IWYU pragma: keep
18 #include "pw_bluetooth/emboss_util.h"
19 #include "pw_bluetooth/hci_common.emb.h"
20 #include "pw_bluetooth/hci_data.emb.h"
21 #include "pw_bluetooth/l2cap_frames.emb.h"
22 #include "pw_bluetooth_proxy/h4_packet.h"
23 #include "pw_bluetooth_proxy/internal/gatt_notify_channel_internal.h"
24 #include "pw_bluetooth_proxy/internal/l2cap_coc_internal.h"
25 #include "pw_bluetooth_proxy/internal/logical_transport.h"
26 #include "pw_log/log.h"
27
28 namespace pw::bluetooth::proxy {
29
ProxyHost(pw::Function<void (H4PacketWithHci && packet)> && send_to_host_fn,pw::Function<void (H4PacketWithH4 && packet)> && send_to_controller_fn,uint16_t le_acl_credits_to_reserve,uint16_t br_edr_acl_credits_to_reserve)30 ProxyHost::ProxyHost(
31 pw::Function<void(H4PacketWithHci&& packet)>&& send_to_host_fn,
32 pw::Function<void(H4PacketWithH4&& packet)>&& send_to_controller_fn,
33 uint16_t le_acl_credits_to_reserve,
34 uint16_t br_edr_acl_credits_to_reserve)
35 : hci_transport_(std::move(send_to_host_fn),
36 std::move(send_to_controller_fn)),
37 acl_data_channel_(hci_transport_,
38 l2cap_channel_manager_,
39 le_acl_credits_to_reserve,
40 br_edr_acl_credits_to_reserve),
41 l2cap_channel_manager_(acl_data_channel_) {}
42
~ProxyHost()43 ProxyHost::~ProxyHost() { acl_data_channel_.Reset(); }
44
HandleH4HciFromHost(H4PacketWithH4 && h4_packet)45 void ProxyHost::HandleH4HciFromHost(H4PacketWithH4&& h4_packet) {
46 if (h4_packet.GetH4Type() == emboss::H4PacketType::ACL_DATA) {
47 HandleAclFromHost(std::move(h4_packet));
48 return;
49 }
50 hci_transport_.SendToController(std::move(h4_packet));
51 }
52
HandleH4HciFromController(H4PacketWithHci && h4_packet)53 void ProxyHost::HandleH4HciFromController(H4PacketWithHci&& h4_packet) {
54 if (h4_packet.GetH4Type() == emboss::H4PacketType::EVENT) {
55 HandleEventFromController(std::move(h4_packet));
56 return;
57 }
58 if (h4_packet.GetH4Type() == emboss::H4PacketType::ACL_DATA) {
59 HandleAclFromController(std::move(h4_packet));
60 return;
61 }
62 hci_transport_.SendToHost(std::move(h4_packet));
63 }
64
CheckForActiveFragmenting(AclDataChannel::Direction direction,emboss::AclDataFrameWriter & acl)65 bool ProxyHost::CheckForActiveFragmenting(AclDataChannel::Direction direction,
66 emboss::AclDataFrameWriter& acl) {
67 const uint16_t handle = acl.header().handle().Read();
68 const emboss::AclDataPacketBoundaryFlag boundary_flag =
69 acl.header().packet_boundary_flag().Read();
70
71 pw::Result<bool> connection_is_receiving_fragmented_pdu =
72 acl_data_channel_.IsReceivingFragmentedPdu(direction, handle);
73 if (connection_is_receiving_fragmented_pdu.ok() &&
74 *connection_is_receiving_fragmented_pdu) {
75 // We're in a state where this connection is dropping continuing fragments
76 // in a fragmented PDU.
77 if (boundary_flag !=
78 emboss::AclDataPacketBoundaryFlag::CONTINUING_FRAGMENT) {
79 // The fragmented PDU has been fully received, so note that then proceed
80 // to process the new PDU as normal.
81 PW_CHECK(acl_data_channel_.FragmentedPduFinished(direction, handle).ok());
82 } else {
83 PW_LOG_INFO("(Connection: 0x%X) Dropping continuing PDU fragment.",
84 handle);
85 return true;
86 }
87 }
88 return false;
89 }
90
CheckForFragmentedStart(AclDataChannel::Direction direction,emboss::AclDataFrameWriter & acl,emboss::BasicL2capHeaderView & l2cap_header,L2capReadChannel * channel)91 bool ProxyHost::CheckForFragmentedStart(
92 AclDataChannel::Direction direction,
93 emboss::AclDataFrameWriter& acl,
94 emboss::BasicL2capHeaderView& l2cap_header,
95 L2capReadChannel* channel) {
96 const uint16_t handle = acl.header().handle().Read();
97 const emboss::AclDataPacketBoundaryFlag boundary_flag =
98 acl.header().packet_boundary_flag().Read();
99 // TODO: https://pwbug.dev/365179076 - Support recombination.
100 if (boundary_flag == emboss::AclDataPacketBoundaryFlag::CONTINUING_FRAGMENT) {
101 PW_LOG_INFO("(CID: 0x%X) Received unexpected continuing PDU fragment.",
102 handle);
103 channel->OnFragmentedPduReceived();
104 return true;
105 }
106 const uint16_t l2cap_frame_length =
107 emboss::BasicL2capHeader::IntrinsicSizeInBytes() +
108 l2cap_header.pdu_length().Read();
109 if (l2cap_frame_length > acl.data_total_length().Read()) {
110 pw::Status status =
111 acl_data_channel_.FragmentedPduStarted(direction, handle);
112 PW_CHECK(status.ok());
113 channel->OnFragmentedPduReceived();
114 return true;
115 }
116
117 return false;
118 }
119
HandleEventFromController(H4PacketWithHci && h4_packet)120 void ProxyHost::HandleEventFromController(H4PacketWithHci&& h4_packet) {
121 pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
122 Result<emboss::EventHeaderView> event =
123 MakeEmbossView<emboss::EventHeaderView>(hci_buffer);
124 if (!event.ok()) {
125 PW_LOG_ERROR(
126 "Buffer is too small for EventHeader. So will pass on to host without "
127 "processing.");
128 hci_transport_.SendToHost(std::move(h4_packet));
129 return;
130 }
131
132 PW_MODIFY_DIAGNOSTICS_PUSH();
133 PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
134 switch (event->event_code_enum().Read()) {
135 case emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS: {
136 acl_data_channel_.HandleNumberOfCompletedPacketsEvent(
137 std::move(h4_packet));
138 break;
139 }
140 case emboss::EventCode::DISCONNECTION_COMPLETE: {
141 acl_data_channel_.HandleDisconnectionCompleteEvent(std::move(h4_packet));
142 break;
143 }
144 case emboss::EventCode::COMMAND_COMPLETE: {
145 HandleCommandCompleteEvent(std::move(h4_packet));
146 break;
147 }
148 case emboss::EventCode::CONNECTION_COMPLETE: {
149 acl_data_channel_.HandleConnectionCompleteEvent(std::move(h4_packet));
150 break;
151 }
152 case emboss::EventCode::LE_META_EVENT: {
153 HandleLeMetaEvent(std::move(h4_packet));
154 break;
155 }
156 default: {
157 hci_transport_.SendToHost(std::move(h4_packet));
158 return;
159 }
160 }
161 PW_MODIFY_DIAGNOSTICS_POP();
162 }
163
HandleAclFromController(H4PacketWithHci && h4_packet)164 void ProxyHost::HandleAclFromController(H4PacketWithHci&& h4_packet) {
165 pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
166
167 Result<emboss::AclDataFrameWriter> acl =
168 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_buffer);
169 if (!acl.ok()) {
170 PW_LOG_ERROR(
171 "Buffer is too small for ACL header. So will pass on to host.");
172 hci_transport_.SendToHost(std::move(h4_packet));
173 return;
174 }
175 const uint16_t handle = acl->header().handle().Read();
176
177 if (CheckForActiveFragmenting(AclDataChannel::Direction::kFromController,
178 *acl)) {
179 return;
180 }
181
182 emboss::BasicL2capHeaderView l2cap_header = emboss::MakeBasicL2capHeaderView(
183 acl->payload().BackingStorage().data(),
184 acl->payload().BackingStorage().SizeInBytes());
185 // TODO: https://pwbug.dev/365179076 - Technically, the first fragment of a
186 // fragmented PDU may include an incomplete L2CAP header.
187 if (!l2cap_header.Ok()) {
188 PW_LOG_ERROR(
189 "(Connection: 0x%X) ACL packet does not include a valid L2CAP header. "
190 "So will pass on to host.",
191 handle);
192 hci_transport_.SendToHost(std::move(h4_packet));
193 return;
194 }
195
196 L2capReadChannel* channel = l2cap_channel_manager_.FindReadChannel(
197 acl->header().handle().Read(), l2cap_header.channel_id().Read());
198 if (!channel) {
199 hci_transport_.SendToHost(std::move(h4_packet));
200 return;
201 }
202
203 if (CheckForFragmentedStart(AclDataChannel::Direction::kFromController,
204 *acl,
205 l2cap_header,
206 channel)) {
207 return;
208 }
209
210 if (!channel->HandlePduFromController(
211 pw::span(acl->payload().BackingStorage().data(),
212 acl->payload().SizeInBytes()))) {
213 hci_transport_.SendToHost(std::move(h4_packet));
214 }
215 }
216
HandleLeMetaEvent(H4PacketWithHci && h4_packet)217 void ProxyHost::HandleLeMetaEvent(H4PacketWithHci&& h4_packet) {
218 pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
219 Result<emboss::LEMetaEventView> le_meta_event_view =
220 MakeEmbossView<emboss::LEMetaEventView>(hci_buffer);
221 if (!le_meta_event_view.ok()) {
222 PW_LOG_ERROR(
223 "Buffer is too small for LE_META_EVENT event. So will not process.");
224 hci_transport_.SendToHost(std::move(h4_packet));
225 return;
226 }
227
228 PW_MODIFY_DIAGNOSTICS_PUSH();
229 PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
230 switch (le_meta_event_view->subevent_code_enum().Read()) {
231 case emboss::LeSubEventCode::CONNECTION_COMPLETE: {
232 acl_data_channel_.HandleLeConnectionCompleteEvent(std::move(h4_packet));
233 return;
234 }
235 case emboss::LeSubEventCode::ENHANCED_CONNECTION_COMPLETE_V1: {
236 acl_data_channel_.HandleLeEnhancedConnectionCompleteV1Event(
237 std::move(h4_packet));
238 return;
239 }
240 case emboss::LeSubEventCode::ENHANCED_CONNECTION_COMPLETE_V2: {
241 acl_data_channel_.HandleLeEnhancedConnectionCompleteV2Event(
242 std::move(h4_packet));
243 return;
244 }
245 default:
246 break;
247 }
248 PW_MODIFY_DIAGNOSTICS_POP();
249 hci_transport_.SendToHost(std::move(h4_packet));
250 }
251
HandleCommandCompleteEvent(H4PacketWithHci && h4_packet)252 void ProxyHost::HandleCommandCompleteEvent(H4PacketWithHci&& h4_packet) {
253 pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
254 Result<emboss::CommandCompleteEventView> command_complete_event =
255 MakeEmbossView<emboss::CommandCompleteEventView>(hci_buffer);
256 if (!command_complete_event.ok()) {
257 PW_LOG_ERROR(
258 "Buffer is too small for COMMAND_COMPLETE event. So will not process.");
259 hci_transport_.SendToHost(std::move(h4_packet));
260 return;
261 }
262
263 PW_MODIFY_DIAGNOSTICS_PUSH();
264 PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
265 switch (command_complete_event->command_opcode_enum().Read()) {
266 case emboss::OpCode::READ_BUFFER_SIZE: {
267 Result<emboss::ReadBufferSizeCommandCompleteEventWriter> read_event =
268 MakeEmbossWriter<emboss::ReadBufferSizeCommandCompleteEventWriter>(
269 hci_buffer);
270 if (!read_event.ok()) {
271 PW_LOG_ERROR(
272 "Buffer is too small for READ_BUFFER_SIZE command complete event. "
273 "Will not process.");
274 break;
275 }
276 acl_data_channel_.ProcessReadBufferSizeCommandCompleteEvent(*read_event);
277 break;
278 }
279 case emboss::OpCode::LE_READ_BUFFER_SIZE_V1: {
280 Result<emboss::LEReadBufferSizeV1CommandCompleteEventWriter> read_event =
281 MakeEmbossWriter<
282 emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(hci_buffer);
283 if (!read_event.ok()) {
284 PW_LOG_ERROR(
285 "Buffer is too small for LE_READ_BUFFER_SIZE_V1 command complete "
286 "event. So will not process.");
287 break;
288 }
289 acl_data_channel_.ProcessLEReadBufferSizeCommandCompleteEvent(
290 *read_event);
291 break;
292 }
293 case emboss::OpCode::LE_READ_BUFFER_SIZE_V2: {
294 Result<emboss::LEReadBufferSizeV2CommandCompleteEventWriter> read_event =
295 MakeEmbossWriter<
296 emboss::LEReadBufferSizeV2CommandCompleteEventWriter>(hci_buffer);
297 if (!read_event.ok()) {
298 PW_LOG_ERROR(
299 "Buffer is too small for LE_READ_BUFFER_SIZE_V2 command complete "
300 "event. So will not process.");
301 break;
302 }
303 acl_data_channel_.ProcessLEReadBufferSizeCommandCompleteEvent(
304 *read_event);
305 break;
306 }
307 default:
308 break;
309 }
310 PW_MODIFY_DIAGNOSTICS_POP();
311 hci_transport_.SendToHost(std::move(h4_packet));
312 }
313
HandleAclFromHost(H4PacketWithH4 && h4_packet)314 void ProxyHost::HandleAclFromHost(H4PacketWithH4&& h4_packet) {
315 pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
316
317 Result<emboss::AclDataFrameWriter> acl =
318 MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_buffer);
319 if (!acl.ok()) {
320 PW_LOG_ERROR(
321 "Buffer is too small for ACL header. So will pass on to controller.");
322 hci_transport_.SendToController(std::move(h4_packet));
323 return;
324 }
325
326 if (CheckForActiveFragmenting(AclDataChannel::Direction::kFromHost, *acl)) {
327 return;
328 }
329
330 const uint16_t handle = acl->header().handle().Read();
331 emboss::BasicL2capHeaderView l2cap_header = emboss::MakeBasicL2capHeaderView(
332 acl->payload().BackingStorage().data(),
333 acl->payload().BackingStorage().SizeInBytes());
334 // TODO: https://pwbug.dev/365179076 - Technically, the first fragment of a
335 // fragmented PDU may include an incomplete L2CAP header.
336 if (!l2cap_header.Ok()) {
337 PW_LOG_ERROR(
338 "(Connection: 0x%X) ACL packet does not include a valid L2CAP header. "
339 "So will pass on to controller.",
340 handle);
341 hci_transport_.SendToController(std::move(h4_packet));
342 return;
343 }
344
345 L2capReadChannel* channel = acl_data_channel_.FindSignalingChannel(
346 acl->header().handle().Read(), l2cap_header.channel_id().Read());
347 if (!channel) {
348 hci_transport_.SendToController(std::move(h4_packet));
349
350 return;
351 }
352
353 if (CheckForFragmentedStart(
354 AclDataChannel::Direction::kFromHost, *acl, l2cap_header, channel)) {
355 return;
356 }
357
358 if (!channel->HandlePduFromHost(
359 pw::span(acl->payload().BackingStorage().data(),
360 acl->payload().SizeInBytes()))) {
361 hci_transport_.SendToController(std::move(h4_packet));
362 }
363 }
364
Reset()365 void ProxyHost::Reset() {
366 acl_data_channel_.Reset();
367 l2cap_channel_manager_.Reset();
368 }
369
AcquireL2capCoc(uint16_t connection_handle,L2capCoc::CocConfig rx_config,L2capCoc::CocConfig tx_config,pw::Function<void (pw::span<uint8_t> payload)> && receive_fn,pw::Function<void (L2capCoc::Event event)> && event_fn,uint16_t rx_additional_credits)370 pw::Result<L2capCoc> ProxyHost::AcquireL2capCoc(
371 uint16_t connection_handle,
372 L2capCoc::CocConfig rx_config,
373 L2capCoc::CocConfig tx_config,
374 pw::Function<void(pw::span<uint8_t> payload)>&& receive_fn,
375 pw::Function<void(L2capCoc::Event event)>&& event_fn,
376 uint16_t rx_additional_credits) {
377 Status status = acl_data_channel_.CreateAclConnection(connection_handle,
378 AclTransportType::kLe);
379 if (status.IsResourceExhausted()) {
380 return pw::Status::Unavailable();
381 }
382 PW_CHECK(status.ok() || status.IsAlreadyExists());
383 if (rx_additional_credits > 0) {
384 L2capSignalingChannel* signaling_channel =
385 acl_data_channel_.FindSignalingChannel(
386 connection_handle,
387 static_cast<uint16_t>(emboss::L2capFixedCid::LE_U_SIGNALING));
388 PW_CHECK(signaling_channel);
389 status = signaling_channel->SendFlowControlCreditInd(rx_config.cid,
390 rx_additional_credits);
391 if (!status.ok()) {
392 return status;
393 }
394 }
395 return L2capCocInternal::Create(l2cap_channel_manager_,
396 connection_handle,
397 rx_config,
398 tx_config,
399 std::move(receive_fn),
400 std::move(event_fn));
401 }
402
AcquireBasicL2capChannel(uint16_t connection_handle,uint16_t local_cid,uint16_t remote_cid,AclTransportType transport,pw::Function<void (pw::span<uint8_t> payload)> && payload_from_controller_fn)403 pw::Result<BasicL2capChannel> ProxyHost::AcquireBasicL2capChannel(
404 uint16_t connection_handle,
405 uint16_t local_cid,
406 uint16_t remote_cid,
407 AclTransportType transport,
408 pw::Function<void(pw::span<uint8_t> payload)>&&
409 payload_from_controller_fn) {
410 Status status =
411 acl_data_channel_.CreateAclConnection(connection_handle, transport);
412 if (status.IsResourceExhausted()) {
413 return pw::Status::Unavailable();
414 }
415 PW_CHECK(status.ok() || status.IsAlreadyExists());
416 return BasicL2capChannel::Create(
417 /*l2cap_channel_manager=*/l2cap_channel_manager_,
418 /*connection_handle=*/connection_handle,
419 /*local_cid=*/local_cid,
420 /*remote_cid=*/remote_cid,
421 /*payload_from_controller_fn=*/std::move(payload_from_controller_fn));
422 }
423
SendGattNotify(uint16_t connection_handle,uint16_t attribute_handle,pw::span<const uint8_t> attribute_value)424 pw::Status ProxyHost::SendGattNotify(uint16_t connection_handle,
425 uint16_t attribute_handle,
426 pw::span<const uint8_t> attribute_value) {
427 // TODO: https://pwbug.dev/369709521 - Migrate clients to channel API.
428 Status status = acl_data_channel_.CreateAclConnection(connection_handle,
429 AclTransportType::kLe);
430 if (status != OkStatus() && status != Status::AlreadyExists()) {
431 return pw::Status::Unavailable();
432 }
433 pw::Result<GattNotifyChannel> channel_result =
434 GattNotifyChannelInternal::Create(
435 l2cap_channel_manager_, connection_handle, attribute_handle);
436 if (!channel_result.ok()) {
437 return channel_result.status();
438 }
439 return channel_result->Write(attribute_value);
440 }
441
AcquireRfcommChannel(uint16_t connection_handle,RfcommChannel::Config rx_config,RfcommChannel::Config tx_config,uint8_t channel_number,pw::Function<void (pw::span<uint8_t> payload)> && receive_fn)442 pw::Result<RfcommChannel> ProxyHost::AcquireRfcommChannel(
443 uint16_t connection_handle,
444 RfcommChannel::Config rx_config,
445 RfcommChannel::Config tx_config,
446 uint8_t channel_number,
447 pw::Function<void(pw::span<uint8_t> payload)>&& receive_fn) {
448 Status status = acl_data_channel_.CreateAclConnection(
449 connection_handle, AclTransportType::kBrEdr);
450 if (status != OkStatus() && status != Status::AlreadyExists()) {
451 return pw::Status::Unavailable();
452 }
453 return RfcommChannel::Create(l2cap_channel_manager_,
454 connection_handle,
455 rx_config,
456 tx_config,
457 channel_number,
458 std::move(receive_fn));
459 }
460
HasSendLeAclCapability() const461 bool ProxyHost::HasSendLeAclCapability() const {
462 return acl_data_channel_.HasSendAclCapability(AclTransportType::kLe);
463 }
464
HasSendBrEdrAclCapability() const465 bool ProxyHost::HasSendBrEdrAclCapability() const {
466 return acl_data_channel_.HasSendAclCapability(AclTransportType::kBrEdr);
467 }
468
GetNumFreeLeAclPackets() const469 uint16_t ProxyHost::GetNumFreeLeAclPackets() const {
470 return acl_data_channel_.GetNumFreeAclPackets(AclTransportType::kLe);
471 }
472
GetNumFreeBrEdrAclPackets() const473 uint16_t ProxyHost::GetNumFreeBrEdrAclPackets() const {
474 return acl_data_channel_.GetNumFreeAclPackets(AclTransportType::kBrEdr);
475 }
476
477 } // namespace pw::bluetooth::proxy
478