• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"
18 #include "pw_bluetooth/emboss_util.h"
19 #include "pw_bluetooth/hci_commands.emb.h"
20 #include "pw_bluetooth/hci_common.emb.h"
21 #include "pw_bluetooth/hci_data.emb.h"
22 #include "pw_bluetooth/hci_h4.emb.h"
23 #include "pw_bluetooth/l2cap_frames.emb.h"
24 #include "pw_bluetooth_proxy/h4_packet.h"
25 #include "pw_bluetooth_proxy/internal/gatt_notify_channel_internal.h"
26 #include "pw_bluetooth_proxy/internal/l2cap_coc_internal.h"
27 #include "pw_bluetooth_proxy/internal/logical_transport.h"
28 #include "pw_bluetooth_proxy/l2cap_channel_common.h"
29 #include "pw_log/log.h"
30 
31 namespace pw::bluetooth::proxy {
32 
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)33 ProxyHost::ProxyHost(
34     pw::Function<void(H4PacketWithHci&& packet)>&& send_to_host_fn,
35     pw::Function<void(H4PacketWithH4&& packet)>&& send_to_controller_fn,
36     uint16_t le_acl_credits_to_reserve,
37     uint16_t br_edr_acl_credits_to_reserve)
38     : hci_transport_(std::move(send_to_host_fn),
39                      std::move(send_to_controller_fn)),
40       acl_data_channel_(hci_transport_,
41                         l2cap_channel_manager_,
42                         le_acl_credits_to_reserve,
43                         br_edr_acl_credits_to_reserve),
44       l2cap_channel_manager_(acl_data_channel_) {
45   PW_LOG_INFO(
46       "btproxy: ProxyHost ctor - le_acl_credits_to_reserve: %u, "
47       "br_edr_acl_credits_to_reserve: %u",
48       le_acl_credits_to_reserve,
49       br_edr_acl_credits_to_reserve);
50 }
51 
~ProxyHost()52 ProxyHost::~ProxyHost() {
53   PW_LOG_INFO("btproxy: ProxyHost dtor");
54   acl_data_channel_.Reset();
55   l2cap_channel_manager_.DeregisterAndCloseChannels(
56       L2capChannelEvent::kChannelClosedByOther);
57 }
58 
Reset()59 void ProxyHost::Reset() {
60   // Reset AclDataChannel first, so that send credits are reset to 0 until
61   // reinitialized by controller event. This way, new channels can still be
62   // registered, but they cannot erroneously use invalidated send credits.
63   acl_data_channel_.Reset();
64   l2cap_channel_manager_.DeregisterAndCloseChannels(L2capChannelEvent::kReset);
65 }
66 
HandleH4HciFromHost(H4PacketWithH4 && h4_packet)67 void ProxyHost::HandleH4HciFromHost(H4PacketWithH4&& h4_packet) {
68   switch (h4_packet.GetH4Type()) {
69     case emboss::H4PacketType::COMMAND:
70       HandleCommandFromHost(std::move(h4_packet));
71       return;
72     case emboss::H4PacketType::EVENT:
73       HandleEventFromHost(std::move(h4_packet));
74       return;
75     case emboss::H4PacketType::ACL_DATA:
76       HandleAclFromHost(std::move(h4_packet));
77       return;
78     case emboss::H4PacketType::UNKNOWN:
79     case emboss::H4PacketType::SYNC_DATA:
80     case emboss::H4PacketType::ISO_DATA:
81     default:
82       hci_transport_.SendToController(std::move(h4_packet));
83       return;
84   }
85 }
86 
HandleH4HciFromController(H4PacketWithHci && h4_packet)87 void ProxyHost::HandleH4HciFromController(H4PacketWithHci&& h4_packet) {
88   switch (h4_packet.GetH4Type()) {
89     case emboss::H4PacketType::EVENT:
90       HandleEventFromController(std::move(h4_packet));
91       return;
92     case emboss::H4PacketType::ACL_DATA:
93       HandleAclFromController(std::move(h4_packet));
94       return;
95     case emboss::H4PacketType::UNKNOWN:
96     case emboss::H4PacketType::COMMAND:
97     case emboss::H4PacketType::SYNC_DATA:
98     case emboss::H4PacketType::ISO_DATA:
99     default:
100       hci_transport_.SendToHost(std::move(h4_packet));
101       return;
102   }
103 }
104 
HandleEventFromController(H4PacketWithHci && h4_packet)105 void ProxyHost::HandleEventFromController(H4PacketWithHci&& h4_packet) {
106   pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
107   Result<emboss::EventHeaderView> event =
108       MakeEmbossView<emboss::EventHeaderView>(hci_buffer);
109   if (!event.ok()) {
110     PW_LOG_ERROR(
111         "Buffer is too small for EventHeader. So will pass on to host without "
112         "processing.");
113     hci_transport_.SendToHost(std::move(h4_packet));
114     return;
115   }
116 
117   PW_MODIFY_DIAGNOSTICS_PUSH();
118   PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
119   switch (event->event_code().Read()) {
120     case emboss::EventCode::NUMBER_OF_COMPLETED_PACKETS: {
121       acl_data_channel_.HandleNumberOfCompletedPacketsEvent(
122           std::move(h4_packet));
123       break;
124     }
125     case emboss::EventCode::DISCONNECTION_COMPLETE: {
126       acl_data_channel_.ProcessDisconnectionCompleteEvent(
127           h4_packet.GetHciSpan());
128       hci_transport_.SendToHost(std::move(h4_packet));
129       break;
130     }
131     case emboss::EventCode::COMMAND_COMPLETE: {
132       HandleCommandCompleteEvent(std::move(h4_packet));
133       break;
134     }
135     case emboss::EventCode::CONNECTION_COMPLETE: {
136       acl_data_channel_.HandleConnectionCompleteEvent(std::move(h4_packet));
137       break;
138     }
139     case emboss::EventCode::LE_META_EVENT: {
140       HandleLeMetaEvent(std::move(h4_packet));
141       break;
142     }
143     default: {
144       hci_transport_.SendToHost(std::move(h4_packet));
145       return;
146     }
147   }
148   PW_MODIFY_DIAGNOSTICS_POP();
149 
150   l2cap_channel_manager_.DeliverPendingEvents();
151 }
152 
HandleEventFromHost(H4PacketWithH4 && h4_packet)153 void ProxyHost::HandleEventFromHost(H4PacketWithH4&& h4_packet) {
154   pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
155   Result<emboss::EventHeaderView> event =
156       MakeEmbossView<emboss::EventHeaderView>(hci_buffer);
157   if (!event.ok()) {
158     PW_LOG_ERROR(
159         "Buffer is too small for EventHeader. So will pass on to controller "
160         "without processing.");
161     hci_transport_.SendToController(std::move(h4_packet));
162     return;
163   }
164 
165   PW_MODIFY_DIAGNOSTICS_PUSH();
166   PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
167   switch (event->event_code().Read()) {
168     case emboss::EventCode::DISCONNECTION_COMPLETE: {
169       acl_data_channel_.ProcessDisconnectionCompleteEvent(
170           h4_packet.GetHciSpan());
171       hci_transport_.SendToController(std::move(h4_packet));
172       break;
173     }
174     default: {
175       hci_transport_.SendToController(std::move(h4_packet));
176       return;
177     }
178   }
179   PW_MODIFY_DIAGNOSTICS_POP();
180 }
181 
HandleAclFromController(H4PacketWithHci && h4_packet)182 void ProxyHost::HandleAclFromController(H4PacketWithHci&& h4_packet) {
183   pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
184 
185   Result<emboss::AclDataFrameWriter> acl =
186       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_buffer);
187   if (!acl.ok()) {
188     PW_LOG_ERROR(
189         "Buffer is too small for ACL header. So will pass on to host.");
190     hci_transport_.SendToHost(std::move(h4_packet));
191     return;
192   }
193 
194   if (!acl_data_channel_.HandleAclData(
195           AclDataChannel::Direction::kFromController, *acl)) {
196     hci_transport_.SendToHost(std::move(h4_packet));
197     return;
198   }
199 }
200 
HandleLeMetaEvent(H4PacketWithHci && h4_packet)201 void ProxyHost::HandleLeMetaEvent(H4PacketWithHci&& h4_packet) {
202   pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
203   Result<emboss::LEMetaEventView> le_meta_event_view =
204       MakeEmbossView<emboss::LEMetaEventView>(hci_buffer);
205   if (!le_meta_event_view.ok()) {
206     PW_LOG_ERROR(
207         "Buffer is too small for LE_META_EVENT event. So will not process.");
208     hci_transport_.SendToHost(std::move(h4_packet));
209     return;
210   }
211 
212   PW_MODIFY_DIAGNOSTICS_PUSH();
213   PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
214   switch (le_meta_event_view->subevent_code_enum().Read()) {
215     case emboss::LeSubEventCode::CONNECTION_COMPLETE: {
216       acl_data_channel_.HandleLeConnectionCompleteEvent(std::move(h4_packet));
217       return;
218     }
219     case emboss::LeSubEventCode::ENHANCED_CONNECTION_COMPLETE_V1: {
220       acl_data_channel_.HandleLeEnhancedConnectionCompleteV1Event(
221           std::move(h4_packet));
222       return;
223     }
224     case emboss::LeSubEventCode::ENHANCED_CONNECTION_COMPLETE_V2: {
225       acl_data_channel_.HandleLeEnhancedConnectionCompleteV2Event(
226           std::move(h4_packet));
227       return;
228     }
229     default:
230       break;
231   }
232   PW_MODIFY_DIAGNOSTICS_POP();
233   hci_transport_.SendToHost(std::move(h4_packet));
234 }
235 
HandleCommandCompleteEvent(H4PacketWithHci && h4_packet)236 void ProxyHost::HandleCommandCompleteEvent(H4PacketWithHci&& h4_packet) {
237   pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
238   Result<emboss::CommandCompleteEventView> command_complete_event =
239       MakeEmbossView<emboss::CommandCompleteEventView>(hci_buffer);
240   if (!command_complete_event.ok()) {
241     PW_LOG_ERROR(
242         "Buffer is too small for COMMAND_COMPLETE event. So will not process.");
243     hci_transport_.SendToHost(std::move(h4_packet));
244     return;
245   }
246 
247   PW_MODIFY_DIAGNOSTICS_PUSH();
248   PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
249   switch (command_complete_event->command_opcode().Read()) {
250     case emboss::OpCode::READ_BUFFER_SIZE: {
251       Result<emboss::ReadBufferSizeCommandCompleteEventWriter> read_event =
252           MakeEmbossWriter<emboss::ReadBufferSizeCommandCompleteEventWriter>(
253               hci_buffer);
254       if (!read_event.ok()) {
255         PW_LOG_ERROR(
256             "Buffer is too small for READ_BUFFER_SIZE command complete event. "
257             "Will not process.");
258         break;
259       }
260       acl_data_channel_.ProcessReadBufferSizeCommandCompleteEvent(*read_event);
261       break;
262     }
263     case emboss::OpCode::LE_READ_BUFFER_SIZE_V1: {
264       Result<emboss::LEReadBufferSizeV1CommandCompleteEventWriter> read_event =
265           MakeEmbossWriter<
266               emboss::LEReadBufferSizeV1CommandCompleteEventWriter>(hci_buffer);
267       if (!read_event.ok()) {
268         PW_LOG_ERROR(
269             "Buffer is too small for LE_READ_BUFFER_SIZE_V1 command complete "
270             "event. So will not process.");
271         break;
272       }
273       acl_data_channel_.ProcessLEReadBufferSizeCommandCompleteEvent(
274           *read_event);
275       break;
276     }
277     case emboss::OpCode::LE_READ_BUFFER_SIZE_V2: {
278       Result<emboss::LEReadBufferSizeV2CommandCompleteEventWriter> read_event =
279           MakeEmbossWriter<
280               emboss::LEReadBufferSizeV2CommandCompleteEventWriter>(hci_buffer);
281       if (!read_event.ok()) {
282         PW_LOG_ERROR(
283             "Buffer is too small for LE_READ_BUFFER_SIZE_V2 command complete "
284             "event. So will not process.");
285         break;
286       }
287       acl_data_channel_.ProcessLEReadBufferSizeCommandCompleteEvent(
288           *read_event);
289       break;
290     }
291     default:
292       break;
293   }
294   PW_MODIFY_DIAGNOSTICS_POP();
295   hci_transport_.SendToHost(std::move(h4_packet));
296 }
297 
HandleCommandFromHost(H4PacketWithH4 && h4_packet)298 void ProxyHost::HandleCommandFromHost(H4PacketWithH4&& h4_packet) {
299   pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
300   Result<emboss::GenericHciCommandView> command =
301       MakeEmbossView<emboss::GenericHciCommandView>(hci_buffer);
302   if (!command.ok()) {
303     hci_transport_.SendToController(std::move(h4_packet));
304     return;
305   }
306 
307   if (command->header().opcode().Read() == emboss::OpCode::RESET) {
308     PW_LOG_INFO("Resetting proxy on HCI_Reset Command from host.");
309     Reset();
310   }
311 
312   hci_transport_.SendToController(std::move(h4_packet));
313 }
314 
HandleAclFromHost(H4PacketWithH4 && h4_packet)315 void ProxyHost::HandleAclFromHost(H4PacketWithH4&& h4_packet) {
316   pw::span<uint8_t> hci_buffer = h4_packet.GetHciSpan();
317 
318   Result<emboss::AclDataFrameWriter> acl =
319       MakeEmbossWriter<emboss::AclDataFrameWriter>(hci_buffer);
320   if (!acl.ok()) {
321     PW_LOG_ERROR(
322         "Buffer is too small for ACL header. So will pass on to controller.");
323     hci_transport_.SendToController(std::move(h4_packet));
324     return;
325   }
326 
327   if (!acl_data_channel_.HandleAclData(AclDataChannel::Direction::kFromHost,
328                                        *acl)) {
329     hci_transport_.SendToController(std::move(h4_packet));
330     return;
331   }
332 }
333 
AcquireL2capCoc(pw::multibuf::MultiBufAllocator & rx_multibuf_allocator,uint16_t connection_handle,L2capCoc::CocConfig rx_config,L2capCoc::CocConfig tx_config,Function<void (multibuf::MultiBuf && payload)> && receive_fn,ChannelEventCallback && event_fn)334 pw::Result<L2capCoc> ProxyHost::AcquireL2capCoc(
335     pw::multibuf::MultiBufAllocator& rx_multibuf_allocator,
336     uint16_t connection_handle,
337     L2capCoc::CocConfig rx_config,
338     L2capCoc::CocConfig tx_config,
339     Function<void(multibuf::MultiBuf&& payload)>&& receive_fn,
340     ChannelEventCallback&& event_fn) {
341   Status status = acl_data_channel_.CreateAclConnection(connection_handle,
342                                                         AclTransportType::kLe);
343   if (status.IsResourceExhausted()) {
344     return pw::Status::Unavailable();
345   }
346   PW_CHECK(status.ok() || status.IsAlreadyExists());
347 
348   L2capSignalingChannel* signaling_channel =
349       acl_data_channel_.FindSignalingChannel(
350           connection_handle,
351           static_cast<uint16_t>(emboss::L2capFixedCid::LE_U_SIGNALING));
352   PW_CHECK(signaling_channel);
353   return L2capCocInternal::Create(rx_multibuf_allocator,
354                                   l2cap_channel_manager_,
355                                   signaling_channel,
356                                   connection_handle,
357                                   rx_config,
358                                   tx_config,
359                                   std::move(event_fn),
360                                   /*receive_fn=*/std::move(receive_fn));
361 }
362 
SendAdditionalRxCredits(uint16_t connection_handle,uint16_t local_cid,uint16_t additional_rx_credits)363 pw::Status ProxyHost::SendAdditionalRxCredits(uint16_t connection_handle,
364                                               uint16_t local_cid,
365                                               uint16_t additional_rx_credits) {
366   std::optional<L2capChannelManager::LockedL2capChannel> channel =
367       l2cap_channel_manager_.FindChannelByLocalCid(connection_handle,
368                                                    local_cid);
369   PW_CHECK(channel.has_value());
370   return static_cast<L2capCoc&>(channel->channel())
371       .SendAdditionalRxCredits(additional_rx_credits);
372 }
373 
AcquireBasicL2capChannel(multibuf::MultiBufAllocator & rx_multibuf_allocator,uint16_t connection_handle,uint16_t local_cid,uint16_t remote_cid,AclTransportType transport,OptionalPayloadReceiveCallback && payload_from_controller_fn,OptionalPayloadReceiveCallback && payload_from_host_fn,ChannelEventCallback && event_fn)374 pw::Result<BasicL2capChannel> ProxyHost::AcquireBasicL2capChannel(
375     multibuf::MultiBufAllocator& rx_multibuf_allocator,
376     uint16_t connection_handle,
377     uint16_t local_cid,
378     uint16_t remote_cid,
379     AclTransportType transport,
380     OptionalPayloadReceiveCallback&& payload_from_controller_fn,
381     OptionalPayloadReceiveCallback&& payload_from_host_fn,
382     ChannelEventCallback&& event_fn) {
383   Status status =
384       acl_data_channel_.CreateAclConnection(connection_handle, transport);
385   if (status.IsResourceExhausted()) {
386     return pw::Status::Unavailable();
387   }
388   PW_CHECK(status.ok() || status.IsAlreadyExists());
389   return BasicL2capChannel::Create(l2cap_channel_manager_,
390                                    &rx_multibuf_allocator,
391                                    /*connection_handle=*/connection_handle,
392                                    /*transport=*/transport,
393                                    /*local_cid=*/local_cid,
394                                    /*remote_cid=*/remote_cid,
395                                    /*payload_from_controller_fn=*/
396                                    std::move(payload_from_controller_fn),
397                                    /*payload_from_host_fn=*/
398                                    std::move(payload_from_host_fn),
399                                    /*event_fn=*/std::move(event_fn));
400 }
401 
AcquireGattNotifyChannel(int16_t connection_handle,uint16_t attribute_handle,ChannelEventCallback && event_fn)402 pw::Result<GattNotifyChannel> ProxyHost::AcquireGattNotifyChannel(
403     int16_t connection_handle,
404     uint16_t attribute_handle,
405     ChannelEventCallback&& event_fn) {
406   Status status = acl_data_channel_.CreateAclConnection(connection_handle,
407                                                         AclTransportType::kLe);
408   if (status != OkStatus() && status != Status::AlreadyExists()) {
409     return pw::Status::Unavailable();
410   }
411   return GattNotifyChannelInternal::Create(l2cap_channel_manager_,
412                                            connection_handle,
413                                            attribute_handle,
414                                            std::move(event_fn));
415 }
416 
SendGattNotify(uint16_t connection_handle,uint16_t attribute_handle,pw::multibuf::MultiBuf && payload)417 StatusWithMultiBuf ProxyHost::SendGattNotify(uint16_t connection_handle,
418                                              uint16_t attribute_handle,
419                                              pw::multibuf::MultiBuf&& payload) {
420   // TODO: https://pwbug.dev/369709521 - Migrate clients to channel API.
421   pw::Result<GattNotifyChannel> channel_result =
422       AcquireGattNotifyChannel(connection_handle, attribute_handle, nullptr);
423   if (!channel_result.ok()) {
424     return {channel_result.status(), std::move(payload)};
425   }
426   return channel_result->Write(std::move(payload));
427 }
428 
SendGattNotify(uint16_t connection_handle,uint16_t attribute_handle,pw::span<const uint8_t> attribute_value)429 pw::Status ProxyHost::SendGattNotify(uint16_t connection_handle,
430                                      uint16_t attribute_handle,
431                                      pw::span<const uint8_t> attribute_value) {
432   // TODO: https://pwbug.dev/369709521 - Migrate clients to channel API.
433   pw::Result<GattNotifyChannel> channel_result =
434       AcquireGattNotifyChannel(connection_handle, attribute_handle, nullptr);
435   if (!channel_result.ok()) {
436     return channel_result.status();
437   }
438   return channel_result->Write(attribute_value);
439 }
440 
AcquireRfcommChannel(multibuf::MultiBufAllocator & rx_multibuf_allocator,uint16_t connection_handle,RfcommChannel::Config rx_config,RfcommChannel::Config tx_config,uint8_t channel_number,Function<void (multibuf::MultiBuf && payload)> && payload_from_controller_fn,ChannelEventCallback && event_fn)441 pw::Result<RfcommChannel> ProxyHost::AcquireRfcommChannel(
442     multibuf::MultiBufAllocator& rx_multibuf_allocator,
443     uint16_t connection_handle,
444     RfcommChannel::Config rx_config,
445     RfcommChannel::Config tx_config,
446     uint8_t channel_number,
447     Function<void(multibuf::MultiBuf&& payload)>&& payload_from_controller_fn,
448     ChannelEventCallback&& event_fn) {
449   Status status = acl_data_channel_.CreateAclConnection(
450       connection_handle, AclTransportType::kBrEdr);
451   if (status != OkStatus() && status != Status::AlreadyExists()) {
452     return pw::Status::Unavailable();
453   }
454   return RfcommChannel::Create(l2cap_channel_manager_,
455                                rx_multibuf_allocator,
456                                connection_handle,
457                                rx_config,
458                                tx_config,
459                                channel_number,
460                                std::move(payload_from_controller_fn),
461                                std::move(event_fn));
462 }
463 
HasSendLeAclCapability() const464 bool ProxyHost::HasSendLeAclCapability() const {
465   return acl_data_channel_.HasSendAclCapability(AclTransportType::kLe);
466 }
467 
HasSendBrEdrAclCapability() const468 bool ProxyHost::HasSendBrEdrAclCapability() const {
469   return acl_data_channel_.HasSendAclCapability(AclTransportType::kBrEdr);
470 }
471 
GetNumFreeLeAclPackets() const472 uint16_t ProxyHost::GetNumFreeLeAclPackets() const {
473   return acl_data_channel_.GetNumFreeAclPackets(AclTransportType::kLe);
474 }
475 
GetNumFreeBrEdrAclPackets() const476 uint16_t ProxyHost::GetNumFreeBrEdrAclPackets() const {
477   return acl_data_channel_.GetNumFreeAclPackets(AclTransportType::kBrEdr);
478 }
479 
RegisterL2capStatusDelegate(L2capStatusDelegate & delegate)480 void ProxyHost::RegisterL2capStatusDelegate(L2capStatusDelegate& delegate) {
481   l2cap_channel_manager_.RegisterStatusDelegate(delegate);
482 }
483 
UnregisterL2capStatusDelegate(L2capStatusDelegate & delegate)484 void ProxyHost::UnregisterL2capStatusDelegate(L2capStatusDelegate& delegate) {
485   l2cap_channel_manager_.UnregisterStatusDelegate(delegate);
486 }
487 
488 }  // namespace pw::bluetooth::proxy
489