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