• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/transport/sco_data_channel.h"
16 
17 #include <lib/fit/defer.h>
18 
19 namespace bt::hci {
20 
21 using pw::bluetooth::Controller;
22 using ScoCodingFormat = pw::bluetooth::Controller::ScoCodingFormat;
23 using ScoEncoding = pw::bluetooth::Controller::ScoEncoding;
24 using ScoSampleRate = pw::bluetooth::Controller::ScoSampleRate;
25 
26 class ScoDataChannelImpl final : public ScoDataChannel {
27  public:
28   ScoDataChannelImpl(const DataBufferInfo& buffer_info,
29                      CommandChannel* command_channel,
30                      Controller* hci);
31   ~ScoDataChannelImpl() override;
32 
33   // ScoDataChannel overrides:
34   void RegisterConnection(WeakPtr<ConnectionInterface> connection) override;
35   void UnregisterConnection(hci_spec::ConnectionHandle handle) override;
36   void ClearControllerPacketCount(hci_spec::ConnectionHandle handle) override;
37   void OnOutboundPacketReadable() override;
max_data_length() const38   uint16_t max_data_length() const override {
39     return static_cast<uint16_t>(buffer_info_.max_data_length());
40   }
41 
42  private:
43   enum class HciConfigState {
44     kPending,
45     kConfigured,
46   };
47 
48   struct ConnectionData {
49     WeakPtr<ConnectionInterface> connection;
50     HciConfigState config_state = HciConfigState::kPending;
51   };
52 
53   void OnRxPacket(pw::span<const std::byte> buffer);
54 
55   // Send packets queued on the active channel if the controller has free buffer
56   // slots.
57   void TrySendNextPackets();
58 
59   // The number of free buffer slots in the controller.
60   size_t GetNumFreePackets();
61 
62   // Chooses a new connection to be active if one isn't already active.
63   void MaybeUpdateActiveConnection();
64 
65   // Configure the HCI driver for the active SCO connection. Must be called
66   // before sending packets. Called when the active connection is changed.
67   void ConfigureHci();
68 
69   // Called when SCO configuration is complete.
70   void OnHciConfigured(hci_spec::ConnectionHandle conn_handle,
71                        pw::Status status);
72 
73   // Handler for the HCI Number of Completed Packets Event, used for
74   // packet-based data flow control.
75   CommandChannel::EventCallbackResult OnNumberOfCompletedPacketsEvent(
76       const EventPacket& event);
77 
IsActiveConnectionConfigured()78   bool IsActiveConnectionConfigured() {
79     if (!active_connection_.is_alive()) {
80       return false;
81     }
82     auto iter = connections_.find(active_connection_->handle());
83     BT_ASSERT(iter != connections_.end());
84     return (iter->second.config_state == HciConfigState::kConfigured);
85   }
86 
87   CommandChannel* command_channel_;
88   Controller* hci_;
89   DataBufferInfo buffer_info_;
90 
91   std::unordered_map<hci_spec::ConnectionHandle, ConnectionData> connections_;
92 
93   // Only 1 connection may send packets at a time.
94   WeakPtr<ConnectionInterface> active_connection_;
95 
96   // Stores per-connection counts of unacknowledged packets sent to the
97   // controller. Entries are updated/removed on the HCI Number Of Completed
98   // Packets event and removed when a connection is unregistered (the controller
99   // does not acknowledge packets of disconnected links).
100   std::unordered_map<hci_spec::ConnectionHandle, size_t> pending_packet_counts_;
101 
102   // The event handler ID for the Number Of Completed Packets event.
103   CommandChannel::EventHandlerId num_completed_packets_event_handler_id_;
104 
105   WeakSelf<ScoDataChannelImpl> weak_self_{this};
106 };
107 
ScoDataChannelImpl(const DataBufferInfo & buffer_info,CommandChannel * command_channel,Controller * hci)108 ScoDataChannelImpl::ScoDataChannelImpl(const DataBufferInfo& buffer_info,
109                                        CommandChannel* command_channel,
110                                        Controller* hci)
111     : command_channel_(command_channel), hci_(hci), buffer_info_(buffer_info) {
112   // ScoDataChannel shouldn't be used if the buffer is unavailable (implying the
113   // controller doesn't support SCO).
114   BT_ASSERT(buffer_info_.IsAvailable());
115 
116   num_completed_packets_event_handler_id_ = command_channel_->AddEventHandler(
117       hci_spec::kNumberOfCompletedPacketsEventCode,
118       fit::bind_member<&ScoDataChannelImpl::OnNumberOfCompletedPacketsEvent>(
119           this));
120   BT_ASSERT(num_completed_packets_event_handler_id_);
121 
122   hci_->SetReceiveScoFunction(
123       fit::bind_member<&ScoDataChannelImpl::OnRxPacket>(this));
124 }
125 
~ScoDataChannelImpl()126 ScoDataChannelImpl::~ScoDataChannelImpl() {
127   command_channel_->RemoveEventHandler(num_completed_packets_event_handler_id_);
128 }
129 
RegisterConnection(WeakPtr<ConnectionInterface> connection)130 void ScoDataChannelImpl::RegisterConnection(
131     WeakPtr<ConnectionInterface> connection) {
132   BT_ASSERT(connection->parameters().view().output_data_path().Read() ==
133             pw::bluetooth::emboss::ScoDataPath::HCI);
134   ConnectionData conn_data{.connection = connection};
135   auto [_, inserted] =
136       connections_.emplace(connection->handle(), std::move(conn_data));
137   BT_ASSERT_MSG(inserted,
138                 "connection with handle %#.4x already registered",
139                 connection->handle());
140   MaybeUpdateActiveConnection();
141 }
142 
UnregisterConnection(hci_spec::ConnectionHandle handle)143 void ScoDataChannelImpl::UnregisterConnection(
144     hci_spec::ConnectionHandle handle) {
145   auto iter = connections_.find(handle);
146   if (iter == connections_.end()) {
147     return;
148   }
149   connections_.erase(iter);
150   MaybeUpdateActiveConnection();
151 }
152 
ClearControllerPacketCount(hci_spec::ConnectionHandle handle)153 void ScoDataChannelImpl::ClearControllerPacketCount(
154     hci_spec::ConnectionHandle handle) {
155   bt_log(DEBUG, "hci", "clearing pending packets (handle: %#.4x)", handle);
156   BT_ASSERT(connections_.find(handle) == connections_.end());
157 
158   auto iter = pending_packet_counts_.find(handle);
159   if (iter == pending_packet_counts_.end()) {
160     return;
161   }
162 
163   pending_packet_counts_.erase(iter);
164   TrySendNextPackets();
165 }
166 
OnOutboundPacketReadable()167 void ScoDataChannelImpl::OnOutboundPacketReadable() { TrySendNextPackets(); }
168 
OnRxPacket(pw::span<const std::byte> buffer)169 void ScoDataChannelImpl::OnRxPacket(pw::span<const std::byte> buffer) {
170   if (buffer.size() < sizeof(hci_spec::SynchronousDataHeader)) {
171     // TODO(fxbug.dev/42179582): Handle these types of errors by signaling
172     // Transport.
173     bt_log(ERROR,
174            "hci",
175            "malformed packet - expected at least %zu bytes, got %zu",
176            sizeof(hci_spec::SynchronousDataHeader),
177            buffer.size());
178     return;
179   }
180 
181   const size_t payload_size =
182       buffer.size() - sizeof(hci_spec::SynchronousDataHeader);
183   std::unique_ptr<ScoDataPacket> packet =
184       ScoDataPacket::New(static_cast<uint8_t>(payload_size));
185   packet->mutable_view()->mutable_data().Write(
186       reinterpret_cast<const uint8_t*>(buffer.data()), buffer.size());
187   packet->InitializeFromBuffer();
188 
189   if (packet->view().header().data_total_length != payload_size) {
190     // TODO(fxbug.dev/42179582): Handle these types of errors by signaling
191     // Transport.
192     bt_log(ERROR,
193            "hci",
194            "malformed packet - payload size from header (%hu) does not match"
195            " received payload size: %zu",
196            packet->view().header().data_total_length,
197            payload_size);
198     return;
199   }
200 
201   auto conn_iter = connections_.find(le16toh(packet->connection_handle()));
202   if (conn_iter == connections_.end()) {
203     // Ignore inbound packets for connections that aren't registered. Unlike
204     // ACL, buffering data received before a connection is registered is
205     // unnecessary for SCO (it's realtime and not expected to be reliable).
206     bt_log(DEBUG,
207            "hci",
208            "ignoring inbound SCO packet for unregistered connection: %#.4x",
209            packet->connection_handle());
210     return;
211   }
212   conn_iter->second.connection->ReceiveInboundPacket(std::move(packet));
213 }
214 
215 CommandChannel::EventCallbackResult
OnNumberOfCompletedPacketsEvent(const EventPacket & event)216 ScoDataChannelImpl::OnNumberOfCompletedPacketsEvent(const EventPacket& event) {
217   BT_ASSERT(event.event_code() == hci_spec::kNumberOfCompletedPacketsEventCode);
218   const auto& payload =
219       event.params<hci_spec::NumberOfCompletedPacketsEventParams>();
220 
221   const size_t handles_in_packet =
222       (event.view().payload_size() -
223        sizeof(hci_spec::NumberOfCompletedPacketsEventParams)) /
224       sizeof(hci_spec::NumberOfCompletedPacketsEventData);
225 
226   if (payload.number_of_handles != handles_in_packet) {
227     bt_log(ERROR,
228            "hci",
229            "packets handle count (%d) doesn't match params size (%zu); either "
230            "the packet was "
231            "parsed incorrectly or the controller is buggy",
232            payload.number_of_handles,
233            handles_in_packet);
234   }
235 
236   for (uint8_t i = 0; i < payload.number_of_handles && i < handles_in_packet;
237        ++i) {
238     const hci_spec::NumberOfCompletedPacketsEventData* data = payload.data + i;
239 
240     auto iter = pending_packet_counts_.find(le16toh(data->connection_handle));
241     if (iter == pending_packet_counts_.end()) {
242       // This is expected if the completed packet is an ACL packet.
243       bt_log(TRACE,
244              "hci",
245              "controller reported completed packets for connection handle "
246              "without pending packets: "
247              "%#.4x",
248              data->connection_handle);
249       continue;
250     }
251 
252     uint16_t comp_packets = le16toh(data->hc_num_of_completed_packets);
253 
254     if (iter->second < comp_packets) {
255       // TODO(fxbug.dev/42102535): This can be caused by the controller reusing
256       // the connection handle of a connection that just disconnected. We should
257       // somehow avoid sending the controller packets for a connection that has
258       // disconnected. ScoDataChannel already dequeues such packets, but this is
259       // insufficient: packets can be queued in the channel to the transport
260       // driver, and possibly in the transport driver or USB/UART drivers.
261       bt_log(ERROR,
262              "hci",
263              "SCO packet tx count mismatch! (handle: %#.4x, expected: %zu, "
264              "actual : %u)",
265              le16toh(data->connection_handle),
266              iter->second,
267              comp_packets);
268       // This should eventually result in convergence with the correct pending
269       // packet count. If it undercounts the true number of pending packets,
270       // this branch will be reached again when the controller sends an updated
271       // Number of Completed Packets event. However, ScoDataChannel may overflow
272       // the controller's buffer in the meantime!
273       comp_packets = static_cast<uint16_t>(iter->second);
274     }
275 
276     iter->second -= comp_packets;
277     if (iter->second == 0u) {
278       pending_packet_counts_.erase(iter);
279     }
280   }
281 
282   TrySendNextPackets();
283   return CommandChannel::EventCallbackResult::kContinue;
284 }
285 
TrySendNextPackets()286 void ScoDataChannelImpl::TrySendNextPackets() {
287   if (!IsActiveConnectionConfigured()) {
288     // If there is no active connection configured, then there is probably no
289     // bandwidth, so we shouldn't send packets.
290     return;
291   }
292 
293   // Even though we only expect to have enough bandwidth for the 1
294   // active/configured SCO connection (especially for USB, see fxb/91560), try
295   // to service all connections.
296   for (auto& [conn_handle, conn_data] : connections_) {
297     for (size_t num_free_packets = GetNumFreePackets(); num_free_packets != 0u;
298          num_free_packets--) {
299       std::unique_ptr<ScoDataPacket> packet =
300           conn_data.connection->GetNextOutboundPacket();
301       if (!packet) {
302         // This connection has no more packets available.
303         break;
304       }
305 
306       hci_->SendScoData(packet->view().data().subspan());
307 
308       auto [iter, _] = pending_packet_counts_.try_emplace(conn_handle, 0u);
309       iter->second++;
310     }
311   }
312 }
313 
GetNumFreePackets()314 size_t ScoDataChannelImpl::GetNumFreePackets() {
315   size_t pending_packets_sum = 0u;
316   for (auto& [_, count] : pending_packet_counts_) {
317     pending_packets_sum += count;
318   }
319   return buffer_info_.max_num_packets() - pending_packets_sum;
320 }
321 
MaybeUpdateActiveConnection()322 void ScoDataChannelImpl::MaybeUpdateActiveConnection() {
323   if (active_connection_.is_alive() &&
324       connections_.count(active_connection_->handle())) {
325     // Active connection is still registered.
326     return;
327   }
328 
329   if (connections_.empty()) {
330     active_connection_.reset();
331     ConfigureHci();
332     return;
333   }
334 
335   active_connection_ = connections_.begin()->second.connection;
336   ConfigureHci();
337 }
338 
ConfigureHci()339 void ScoDataChannelImpl::ConfigureHci() {
340   if (!active_connection_.is_alive()) {
341     hci_->ResetSco([](pw::Status status) {
342       bt_log(DEBUG,
343              "hci",
344              "ResetSco completed with status %s",
345              pw_StatusString(status));
346     });
347     return;
348   }
349 
350   bt::StaticPacket<pw::bluetooth::emboss::SynchronousConnectionParametersWriter>
351       params = active_connection_->parameters();
352   auto view = params.view();
353 
354   ScoCodingFormat coding_format;
355   if (view.output_coding_format().coding_format().Read() ==
356       pw::bluetooth::emboss::CodingFormat::MSBC) {
357     coding_format = ScoCodingFormat::kMsbc;
358   } else if (view.output_coding_format().coding_format().Read() ==
359              pw::bluetooth::emboss::CodingFormat::CVSD) {
360     coding_format = ScoCodingFormat::kCvsd;
361   } else {
362     bt_log(WARN,
363            "hci",
364            "SCO connection has unsupported coding format, treating as CVSD");
365     coding_format = ScoCodingFormat::kCvsd;
366   }
367 
368   ScoSampleRate sample_rate;
369   const uint16_t bits_per_byte = CHAR_BIT;
370   uint16_t bytes_per_sample =
371       view.output_coded_data_size_bits().Read() / bits_per_byte;
372   if (bytes_per_sample == 0) {
373     // Err on the side of reserving too much bandwidth in the transport drivers.
374     bt_log(WARN,
375            "hci",
376            "SCO connection has unsupported encoding size, treating as 16-bit");
377     bytes_per_sample = 2;
378   }
379   const uint32_t bytes_per_second = view.output_bandwidth().Read();
380   const uint32_t samples_per_second = bytes_per_second / bytes_per_sample;
381   if (samples_per_second == 8000) {
382     sample_rate = ScoSampleRate::k8Khz;
383   } else if (samples_per_second == 16000) {
384     sample_rate = ScoSampleRate::k16Khz;
385   } else {
386     // Err on the side of reserving too much bandwidth in the transport drivers.
387     bt_log(WARN,
388            "hci",
389            "SCO connection has unsupported sample rate, treating as 16kHz");
390     sample_rate = ScoSampleRate::k16Khz;
391   }
392 
393   ScoEncoding encoding;
394   if (view.output_coded_data_size_bits().Read() == 8) {
395     encoding = ScoEncoding::k8Bits;
396   } else if (view.output_coded_data_size_bits().Read() == 16) {
397     encoding = ScoEncoding::k16Bits;
398   } else {
399     // Err on the side of reserving too much bandwidth in the transport drivers.
400     bt_log(WARN,
401            "hci",
402            "SCO connection has unsupported sample rate, treating as 16-bit");
403     encoding = ScoEncoding::k16Bits;
404   }
405 
406   auto conn = connections_.find(active_connection_->handle());
407   BT_ASSERT(conn != connections_.end());
408 
409   auto callback = [self = weak_self_.GetWeakPtr(),
410                    handle = conn->first](pw::Status status) {
411     if (self.is_alive()) {
412       self->OnHciConfigured(handle, status);
413     }
414   };
415   hci_->ConfigureSco(coding_format, encoding, sample_rate, std::move(callback));
416 }
417 
418 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
OnHciConfigured(hci_spec::ConnectionHandle conn_handle,pw::Status status)419 void ScoDataChannelImpl::OnHciConfigured(hci_spec::ConnectionHandle conn_handle,
420                                          pw::Status status) {
421   auto iter = connections_.find(conn_handle);
422   if (iter == connections_.end()) {
423     // The connection may have been unregistered before the config callback was
424     // called.
425     return;
426   }
427 
428   if (!status.ok()) {
429     bt_log(WARN,
430            "hci",
431            "ConfigureSco failed with status %s (handle: %#.4x)",
432            pw_StatusString(status),
433            conn_handle);
434     // The error callback may unregister the connection synchronously, so |iter|
435     // should not be used past this line.
436     iter->second.connection->OnHciError();
437     UnregisterConnection(conn_handle);
438     return;
439   }
440 
441   iter->second.config_state = HciConfigState::kConfigured;
442   TrySendNextPackets();
443 }
444 
Create(const DataBufferInfo & buffer_info,CommandChannel * command_channel,Controller * hci)445 std::unique_ptr<ScoDataChannel> ScoDataChannel::Create(
446     const DataBufferInfo& buffer_info,
447     CommandChannel* command_channel,
448     Controller* hci) {
449   return std::make_unique<ScoDataChannelImpl>(
450       buffer_info, command_channel, hci);
451 }
452 
453 }  // namespace bt::hci
454