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