xref: /aosp_15_r20/external/pigweed/pw_bluetooth_proxy/proxy_host.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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