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/command_channel.h"
16
17 #include <cpp-string/string_printf.h>
18 #include <lib/fit/defer.h>
19 #include <pw_assert/check.h>
20 #include <pw_bluetooth/hci_android.emb.h>
21 #include <pw_bluetooth/hci_common.emb.h>
22 #include <pw_bytes/endian.h>
23
24 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
25 #include "pw_bluetooth_sapphire/internal/host/common/trace.h"
26 #include "pw_bluetooth_sapphire/internal/host/transport/slab_allocators.h"
27
28 namespace bt::hci {
29
IsAsync(hci_spec::EventCode code)30 static bool IsAsync(hci_spec::EventCode code) {
31 return code != hci_spec::kCommandCompleteEventCode &&
32 code != hci_spec::kCommandStatusEventCode;
33 }
34
EventTypeToString(CommandChannel::EventType event_type)35 static std::string EventTypeToString(CommandChannel::EventType event_type) {
36 switch (event_type) {
37 case CommandChannel::EventType::kHciEvent:
38 return "hci_event";
39 case CommandChannel::EventType::kLEMetaEvent:
40 return "le_meta_event";
41 case CommandChannel::EventType::kVendorEvent:
42 return "vendor_event";
43 }
44 PW_CRASH("Unhandled event type %u", static_cast<unsigned int>(event_type));
45 return "(invalid)";
46 }
47
QueuedCommand(CommandPacket command_packet,std::unique_ptr<TransactionData> transaction_data)48 CommandChannel::QueuedCommand::QueuedCommand(
49 CommandPacket command_packet,
50 std::unique_ptr<TransactionData> transaction_data)
51 : packet(std::move(command_packet)), data(std::move(transaction_data)) {
52 PW_DCHECK(data);
53 }
54
TransactionData(CommandChannel * channel,TransactionId transaction_id,hci_spec::OpCode opcode,hci_spec::EventCode complete_event_code,std::optional<hci_spec::EventCode> le_meta_subevent_code,std::unordered_set<hci_spec::OpCode> exclusions,CommandCallback callback)55 CommandChannel::TransactionData::TransactionData(
56 CommandChannel* channel,
57 TransactionId transaction_id,
58 hci_spec::OpCode opcode,
59 hci_spec::EventCode complete_event_code,
60 std::optional<hci_spec::EventCode> le_meta_subevent_code,
61 std::unordered_set<hci_spec::OpCode> exclusions,
62 CommandCallback callback)
63 : channel_(channel),
64 transaction_id_(transaction_id),
65 opcode_(opcode),
66 complete_event_code_(complete_event_code),
67 le_meta_subevent_code_(le_meta_subevent_code),
68 exclusions_(std::move(exclusions)),
69 callback_(std::move(callback)),
70 timeout_task_(channel_->dispatcher_),
71 handler_id_(0u) {
72 PW_DCHECK(transaction_id != 0u);
73 exclusions_.insert(opcode_);
74 }
75
~TransactionData()76 CommandChannel::TransactionData::~TransactionData() {
77 if (callback_) {
78 bt_log(DEBUG,
79 "hci",
80 "destroying unfinished transaction: %zu",
81 transaction_id_);
82 }
83 }
84
StartTimer()85 void CommandChannel::TransactionData::StartTimer() {
86 // Transactions should only ever be started once.
87 PW_DCHECK(!timeout_task_.is_pending());
88 timeout_task_.set_function(
89 [chan = channel_, tid = id()](auto, pw::Status status) {
90 if (status.ok()) {
91 chan->OnCommandTimeout(tid);
92 }
93 });
94 timeout_task_.PostAfter(hci_spec::kCommandTimeout);
95 }
96
Complete(std::unique_ptr<EventPacket> event)97 void CommandChannel::TransactionData::Complete(
98 std::unique_ptr<EventPacket> event) {
99 timeout_task_.Cancel();
100
101 if (!callback_) {
102 return;
103 }
104
105 // Call callback_ synchronously to ensure that asynchronous status &
106 // complete events are not handled out of order if they are dispatched
107 // from the HCI API simultaneously.
108 callback_(transaction_id_, *event);
109
110 // Asynchronous commands will have an additional reference to callback_
111 // in the event map. Clear this reference to ensure that destruction or
112 // unexpected command complete events or status events do not call this
113 // reference to callback_ twice.
114 callback_ = nullptr;
115 }
116
Cancel()117 void CommandChannel::TransactionData::Cancel() {
118 timeout_task_.Cancel();
119 callback_ = nullptr;
120 }
121
MakeCallback()122 CommandChannel::EventCallback CommandChannel::TransactionData::MakeCallback() {
123 return [transaction_id = transaction_id_,
124 cb = callback_.share()](const EventPacket& event) {
125 cb(transaction_id, event);
126 return EventCallbackResult::kContinue;
127 };
128 }
129
CommandChannel(pw::bluetooth::Controller * hci,pw::async::Dispatcher & dispatcher)130 CommandChannel::CommandChannel(pw::bluetooth::Controller* hci,
131 pw::async::Dispatcher& dispatcher)
132 : next_transaction_id_(1u),
133 next_event_handler_id_(1u),
134 hci_(hci),
135 allowed_command_packets_(1u),
136 dispatcher_(dispatcher),
137 weak_ptr_factory_(this) {
138 hci_->SetEventFunction(fit::bind_member<&CommandChannel::OnEvent>(this));
139
140 bt_log(DEBUG, "hci", "CommandChannel initialized");
141 }
142
~CommandChannel()143 CommandChannel::~CommandChannel() {
144 bt_log(INFO, "hci", "CommandChannel destroyed");
145 hci_->SetEventFunction(nullptr);
146 }
147
SendCommand(CommandPacket command_packet,CommandCallback callback,const hci_spec::EventCode complete_event_code)148 CommandChannel::TransactionId CommandChannel::SendCommand(
149 CommandPacket command_packet,
150 CommandCallback callback,
151 const hci_spec::EventCode complete_event_code) {
152 return SendExclusiveCommand(
153 std::move(command_packet), std::move(callback), complete_event_code);
154 }
155
SendLeAsyncCommand(CommandPacket command_packet,CommandCallback callback,hci_spec::EventCode le_meta_subevent_code)156 CommandChannel::TransactionId CommandChannel::SendLeAsyncCommand(
157 CommandPacket command_packet,
158 CommandCallback callback,
159 hci_spec::EventCode le_meta_subevent_code) {
160 return SendLeAsyncExclusiveCommand(
161 std::move(command_packet), std::move(callback), le_meta_subevent_code);
162 }
163
SendExclusiveCommand(CommandPacket command_packet,CommandCallback callback,const hci_spec::EventCode complete_event_code,std::unordered_set<hci_spec::OpCode> exclusions)164 CommandChannel::TransactionId CommandChannel::SendExclusiveCommand(
165 CommandPacket command_packet,
166 CommandCallback callback,
167 const hci_spec::EventCode complete_event_code,
168 std::unordered_set<hci_spec::OpCode> exclusions) {
169 return SendExclusiveCommandInternal(std::move(command_packet),
170 std::move(callback),
171 complete_event_code,
172 std::nullopt,
173 std::move(exclusions));
174 }
175
SendLeAsyncExclusiveCommand(CommandPacket command_packet,CommandCallback callback,std::optional<hci_spec::EventCode> le_meta_subevent_code,std::unordered_set<hci_spec::OpCode> exclusions)176 CommandChannel::TransactionId CommandChannel::SendLeAsyncExclusiveCommand(
177 CommandPacket command_packet,
178 CommandCallback callback,
179 std::optional<hci_spec::EventCode> le_meta_subevent_code,
180 std::unordered_set<hci_spec::OpCode> exclusions) {
181 return SendExclusiveCommandInternal(std::move(command_packet),
182 std::move(callback),
183 hci_spec::kLEMetaEventCode,
184 le_meta_subevent_code,
185 std::move(exclusions));
186 }
187
SendExclusiveCommandInternal(CommandPacket command_packet,CommandCallback callback,hci_spec::EventCode complete_event_code,std::optional<hci_spec::EventCode> le_meta_subevent_code,std::unordered_set<hci_spec::OpCode> exclusions)188 CommandChannel::TransactionId CommandChannel::SendExclusiveCommandInternal(
189 CommandPacket command_packet,
190 CommandCallback callback,
191 hci_spec::EventCode complete_event_code,
192 std::optional<hci_spec::EventCode> le_meta_subevent_code,
193 std::unordered_set<hci_spec::OpCode> exclusions) {
194 if (!active_) {
195 bt_log(INFO, "hci", "ignoring command (CommandChannel is inactive)");
196 return 0;
197 }
198
199 PW_CHECK((complete_event_code == hci_spec::kLEMetaEventCode) ==
200 le_meta_subevent_code.has_value(),
201 "only LE Meta Event subevents are supported");
202
203 if (IsAsync(complete_event_code)) {
204 // Cannot send an asynchronous command if there's an external event handler
205 // registered for the completion event.
206 EventHandlerData* handler = nullptr;
207 if (le_meta_subevent_code.has_value()) {
208 handler = FindLEMetaEventHandler(*le_meta_subevent_code);
209 } else {
210 handler = FindEventHandler(complete_event_code);
211 }
212
213 if (handler && !handler->is_async()) {
214 bt_log(DEBUG, "hci", "event handler already handling this event");
215 return 0u;
216 }
217 }
218
219 if (next_transaction_id_.value() == 0u) {
220 next_transaction_id_.Set(1);
221 }
222
223 const hci_spec::OpCode opcode = command_packet.opcode();
224 const TransactionId transaction_id = next_transaction_id_.value();
225 next_transaction_id_.Set(transaction_id + 1);
226
227 std::unique_ptr<CommandChannel::TransactionData> data =
228 std::make_unique<TransactionData>(this,
229 transaction_id,
230 opcode,
231 complete_event_code,
232 le_meta_subevent_code,
233 std::move(exclusions),
234 std::move(callback));
235
236 QueuedCommand command(std::move(command_packet), std::move(data));
237
238 if (IsAsync(complete_event_code)) {
239 MaybeAddTransactionHandler(command.data.get());
240 }
241
242 send_queue_.push_back(std::move(command));
243 TrySendQueuedCommands();
244
245 return transaction_id;
246 }
247
RemoveQueuedCommand(TransactionId transaction_id)248 bool CommandChannel::RemoveQueuedCommand(TransactionId transaction_id) {
249 auto it = std::find_if(send_queue_.begin(),
250 send_queue_.end(),
251 [transaction_id](const QueuedCommand& cmd) {
252 return cmd.data->id() == transaction_id;
253 });
254 if (it == send_queue_.end()) {
255 // The transaction to remove has already finished or never existed.
256 bt_log(
257 TRACE, "hci", "command to remove not found, id: %zu", transaction_id);
258 return false;
259 }
260
261 bt_log(TRACE, "hci", "removing queued command id: %zu", transaction_id);
262 TransactionData& data = *it->data;
263 data.Cancel();
264
265 RemoveEventHandlerInternal(data.handler_id());
266 send_queue_.erase(it);
267 return true;
268 }
269
AddEventHandler(hci_spec::EventCode event_code,EventCallback event_callback)270 CommandChannel::EventHandlerId CommandChannel::AddEventHandler(
271 hci_spec::EventCode event_code, EventCallback event_callback) {
272 if (event_code == hci_spec::kCommandStatusEventCode ||
273 event_code == hci_spec::kCommandCompleteEventCode ||
274 event_code == hci_spec::kLEMetaEventCode) {
275 return 0u;
276 }
277
278 EventHandlerData* handler = FindEventHandler(event_code);
279 if (handler && handler->is_async()) {
280 bt_log(ERROR,
281 "hci",
282 "async event handler %zu already registered for event code %#.2x",
283 handler->handler_id,
284 event_code);
285 return 0u;
286 }
287
288 EventHandlerId handler_id = NewEventHandler(event_code,
289 EventType::kHciEvent,
290 hci_spec::kNoOp,
291 std::move(event_callback));
292 event_code_handlers_.emplace(event_code, handler_id);
293 return handler_id;
294 }
295
AddLEMetaEventHandler(hci_spec::EventCode le_meta_subevent_code,EventCallback event_callback)296 CommandChannel::EventHandlerId CommandChannel::AddLEMetaEventHandler(
297 hci_spec::EventCode le_meta_subevent_code, EventCallback event_callback) {
298 EventHandlerData* handler = FindLEMetaEventHandler(le_meta_subevent_code);
299 if (handler && handler->is_async()) {
300 bt_log(ERROR,
301 "hci",
302 "async event handler %zu already registered for LE Meta Event "
303 "subevent code %#.2x",
304 handler->handler_id,
305 le_meta_subevent_code);
306 return 0u;
307 }
308
309 EventHandlerId handler_id = NewEventHandler(le_meta_subevent_code,
310 EventType::kLEMetaEvent,
311 hci_spec::kNoOp,
312 std::move(event_callback));
313 le_meta_subevent_code_handlers_.emplace(le_meta_subevent_code, handler_id);
314 return handler_id;
315 }
316
AddVendorEventHandler(hci_spec::EventCode vendor_subevent_code,EventCallback event_callback)317 CommandChannel::EventHandlerId CommandChannel::AddVendorEventHandler(
318 hci_spec::EventCode vendor_subevent_code, EventCallback event_callback) {
319 CommandChannel::EventHandlerData* handler =
320 FindVendorEventHandler(vendor_subevent_code);
321 if (handler && handler->is_async()) {
322 bt_log(ERROR,
323 "hci",
324 "async event handler %zu already registered for Vendor Event "
325 "subevent code %#.2x",
326 handler->handler_id,
327 vendor_subevent_code);
328 return 0u;
329 }
330
331 EventHandlerId handler_id = NewEventHandler(vendor_subevent_code,
332 EventType::kVendorEvent,
333 hci_spec::kNoOp,
334 std::move(event_callback));
335 vendor_subevent_code_handlers_.emplace(vendor_subevent_code, handler_id);
336 return handler_id;
337 }
338
RemoveEventHandler(EventHandlerId handler_id)339 void CommandChannel::RemoveEventHandler(EventHandlerId handler_id) {
340 // If the ID doesn't exist or it is internal. it can't be removed.
341 auto iter = event_handler_id_map_.find(handler_id);
342 if (iter == event_handler_id_map_.end() || iter->second.is_async()) {
343 return;
344 }
345
346 RemoveEventHandlerInternal(handler_id);
347 }
348
FindEventHandler(hci_spec::EventCode code)349 CommandChannel::EventHandlerData* CommandChannel::FindEventHandler(
350 hci_spec::EventCode code) {
351 auto it = event_code_handlers_.find(code);
352 if (it == event_code_handlers_.end()) {
353 return nullptr;
354 }
355 return &event_handler_id_map_[it->second];
356 }
357
FindLEMetaEventHandler(hci_spec::EventCode le_meta_subevent_code)358 CommandChannel::EventHandlerData* CommandChannel::FindLEMetaEventHandler(
359 hci_spec::EventCode le_meta_subevent_code) {
360 auto it = le_meta_subevent_code_handlers_.find(le_meta_subevent_code);
361 if (it == le_meta_subevent_code_handlers_.end()) {
362 return nullptr;
363 }
364 return &event_handler_id_map_[it->second];
365 }
366
FindVendorEventHandler(hci_spec::EventCode vendor_subevent_code)367 CommandChannel::EventHandlerData* CommandChannel::FindVendorEventHandler(
368 hci_spec::EventCode vendor_subevent_code) {
369 auto it = vendor_subevent_code_handlers_.find(vendor_subevent_code);
370 if (it == vendor_subevent_code_handlers_.end()) {
371 return nullptr;
372 }
373
374 return &event_handler_id_map_[it->second];
375 }
376
RemoveEventHandlerInternal(EventHandlerId handler_id)377 void CommandChannel::RemoveEventHandlerInternal(EventHandlerId handler_id) {
378 auto iter = event_handler_id_map_.find(handler_id);
379 if (iter == event_handler_id_map_.end()) {
380 return;
381 }
382
383 std::unordered_multimap<hci_spec::EventCode, EventHandlerId>* handlers =
384 nullptr;
385 switch (iter->second.event_type) {
386 case EventType::kHciEvent:
387 handlers = &event_code_handlers_;
388 break;
389 case EventType::kLEMetaEvent:
390 handlers = &le_meta_subevent_code_handlers_;
391 break;
392 case EventType::kVendorEvent:
393 handlers = &vendor_subevent_code_handlers_;
394 break;
395 }
396
397 bt_log(TRACE,
398 "hci",
399 "removing handler for %s event code %#.2x",
400 EventTypeToString(iter->second.event_type).c_str(),
401 iter->second.event_code);
402
403 auto range = handlers->equal_range(iter->second.event_code);
404 for (auto it = range.first; it != range.second; ++it) {
405 if (it->second == handler_id) {
406 it = handlers->erase(it);
407 break;
408 }
409 }
410
411 event_handler_id_map_.erase(iter);
412 }
413
TrySendQueuedCommands()414 void CommandChannel::TrySendQueuedCommands() {
415 if (allowed_command_packets_.value() == 0) {
416 bt_log(TRACE, "hci", "controller queue full, waiting");
417 return;
418 }
419
420 // Walk the waiting and see if any are sendable.
421 for (auto it = send_queue_.begin();
422 allowed_command_packets_.value() > 0 && it != send_queue_.end();) {
423 // Care must be taken not to dangle this reference if its owner
424 // QueuedCommand is destroyed.
425 const TransactionData& data = *it->data;
426
427 // Can't send if another is running with an opcode this can't coexist with.
428 bool excluded = false;
429 for (hci_spec::OpCode excluded_opcode : data.exclusions()) {
430 if (pending_transactions_.count(excluded_opcode) != 0) {
431 bt_log(TRACE,
432 "hci",
433 "pending command (%#.4x) delayed due to running opcode %#.4x",
434 it->data->opcode(),
435 excluded_opcode);
436 excluded = true;
437 break;
438 }
439 }
440 if (excluded) {
441 ++it;
442 continue;
443 }
444
445 bool transaction_waiting_on_event =
446 event_code_handlers_.count(data.complete_event_code());
447 bool transaction_waiting_on_subevent =
448 data.le_meta_subevent_code() &&
449 le_meta_subevent_code_handlers_.count(*data.le_meta_subevent_code());
450 bool waiting_for_other_transaction =
451 transaction_waiting_on_event || transaction_waiting_on_subevent;
452
453 // We can send this if we only expect one update, or if we aren't waiting
454 // for another transaction to complete on the same event. It is unlikely but
455 // possible to have commands with different opcodes wait on the same
456 // completion event.
457 if (!IsAsync(data.complete_event_code()) || data.handler_id() != 0 ||
458 !waiting_for_other_transaction) {
459 bt_log(
460 TRACE, "hci", "sending previously queued command id %zu", data.id());
461 SendQueuedCommand(std::move(*it));
462 it = send_queue_.erase(it);
463 continue;
464 }
465 ++it;
466 }
467 }
468
SendQueuedCommand(QueuedCommand && cmd)469 void CommandChannel::SendQueuedCommand(QueuedCommand&& cmd) {
470 pw::span packet_span = cmd.packet.data().subspan();
471 hci_->SendCommand(packet_span);
472
473 allowed_command_packets_.Set(allowed_command_packets_.value() - 1);
474
475 std::unique_ptr<TransactionData>& transaction = cmd.data;
476
477 transaction->StartTimer();
478
479 MaybeAddTransactionHandler(transaction.get());
480
481 pending_transactions_.insert(
482 std::make_pair(transaction->opcode(), std::move(transaction)));
483 }
484
MaybeAddTransactionHandler(TransactionData * data)485 void CommandChannel::MaybeAddTransactionHandler(TransactionData* data) {
486 // We don't need to add a transaction handler for synchronous transactions.
487 if (!IsAsync(data->complete_event_code())) {
488 return;
489 }
490
491 EventType event_type = EventType::kHciEvent;
492 std::unordered_multimap<hci_spec::EventCode, EventHandlerId>* handlers =
493 nullptr;
494
495 if (data->le_meta_subevent_code().has_value()) {
496 event_type = EventType::kLEMetaEvent;
497 handlers = &le_meta_subevent_code_handlers_;
498 } else {
499 event_type = EventType::kHciEvent;
500 handlers = &event_code_handlers_;
501 }
502
503 const hci_spec::EventCode code =
504 data->le_meta_subevent_code().value_or(data->complete_event_code());
505
506 // We already have a handler for this transaction, or another transaction is
507 // already waiting and it will be queued.
508 if (handlers->count(code)) {
509 bt_log(TRACE, "hci", "async command %zu: already has handler", data->id());
510 return;
511 }
512
513 EventHandlerId handler_id =
514 NewEventHandler(code, event_type, data->opcode(), data->MakeCallback());
515
516 PW_CHECK(handler_id != 0u);
517 data->set_handler_id(handler_id);
518 handlers->emplace(code, handler_id);
519 bt_log(TRACE,
520 "hci",
521 "async command %zu assigned handler %zu",
522 data->id(),
523 handler_id);
524 }
525
NewEventHandler(hci_spec::EventCode event_code,EventType event_type,hci_spec::OpCode pending_opcode,EventCallback event_callback)526 CommandChannel::EventHandlerId CommandChannel::NewEventHandler(
527 hci_spec::EventCode event_code,
528 EventType event_type,
529 hci_spec::OpCode pending_opcode,
530 EventCallback event_callback) {
531 PW_DCHECK(event_code);
532
533 auto handler_id = next_event_handler_id_.value();
534 next_event_handler_id_.Set(handler_id + 1);
535 EventHandlerData data;
536 data.handler_id = handler_id;
537 data.event_code = event_code;
538 data.event_type = event_type;
539 data.pending_opcode = pending_opcode;
540 data.event_callback = std::move(event_callback);
541
542 bt_log(TRACE,
543 "hci",
544 "adding event handler %zu for %s event code %#.2x",
545 handler_id,
546 EventTypeToString(event_type).c_str(),
547 event_code);
548 PW_DCHECK(event_handler_id_map_.find(handler_id) ==
549 event_handler_id_map_.end());
550 event_handler_id_map_[handler_id] = std::move(data);
551
552 return handler_id;
553 }
554
UpdateTransaction(std::unique_ptr<EventPacket> event)555 void CommandChannel::UpdateTransaction(std::unique_ptr<EventPacket> event) {
556 hci_spec::EventCode event_code = event->event_code();
557
558 PW_DCHECK(event_code == hci_spec::kCommandStatusEventCode ||
559 event_code == hci_spec::kCommandCompleteEventCode);
560
561 hci_spec::OpCode matching_opcode;
562
563 // The HCI Command Status event with an error status might indicate that an
564 // async command failed. We use this to unregister async command handlers
565 // below.
566 bool unregister_async_handler = false;
567
568 if (event->event_code() == hci_spec::kCommandCompleteEventCode) {
569 auto command_complete_view =
570 event->view<pw::bluetooth::emboss::CommandCompleteEventView>();
571 matching_opcode = command_complete_view.command_opcode_uint().Read();
572 allowed_command_packets_.Set(
573 command_complete_view.num_hci_command_packets().Read());
574 } else { // hci_spec::kCommandStatusEventCode
575 auto command_status_view =
576 event->view<pw::bluetooth::emboss::CommandStatusEventView>();
577 matching_opcode = command_status_view.command_opcode_uint().Read();
578 allowed_command_packets_.Set(
579 command_status_view.num_hci_command_packets().Read());
580 unregister_async_handler = command_status_view.status().Read() !=
581 pw::bluetooth::emboss::StatusCode::SUCCESS;
582 }
583 bt_log(TRACE,
584 "hci",
585 "allowed packets update: %zu",
586 allowed_command_packets_.value());
587
588 if (matching_opcode == hci_spec::kNoOp) {
589 return;
590 }
591
592 auto it = pending_transactions_.find(matching_opcode);
593 if (it == pending_transactions_.end()) {
594 bt_log(
595 ERROR, "hci", "update for unexpected opcode: %#.4x", matching_opcode);
596 return;
597 }
598
599 std::unique_ptr<TransactionData>& transaction_ref = it->second;
600 PW_DCHECK(transaction_ref->opcode() == matching_opcode);
601
602 // If the command is synchronous or there's no handler to cleanup, we're done.
603 if (transaction_ref->handler_id() == 0u) {
604 std::unique_ptr<TransactionData> transaction = std::move(it->second);
605 pending_transactions_.erase(it);
606 transaction->Complete(std::move(event));
607 return;
608 }
609
610 // TODO(fxbug.dev/42062242): Do not allow asynchronous commands to finish with
611 // Command Complete.
612 if (event_code == hci_spec::kCommandCompleteEventCode) {
613 bt_log(WARN, "hci", "async command received CommandComplete");
614 unregister_async_handler = true;
615 }
616
617 // If an asynchronous command failed, then remove its event handler.
618 if (unregister_async_handler) {
619 bt_log(TRACE, "hci", "async command failed; removing its handler");
620 RemoveEventHandlerInternal(transaction_ref->handler_id());
621 std::unique_ptr<TransactionData> transaction = std::move(it->second);
622 pending_transactions_.erase(it);
623 transaction->Complete(std::move(event));
624 } else {
625 // Send the status event to the async transaction.
626 transaction_ref->Complete(std::move(event));
627 }
628 }
629
NotifyEventHandler(std::unique_ptr<EventPacket> event)630 void CommandChannel::NotifyEventHandler(std::unique_ptr<EventPacket> event) {
631 struct PendingCallback {
632 EventCallback callback;
633 EventHandlerId handler_id;
634 };
635 std::vector<PendingCallback> pending_callbacks;
636
637 hci_spec::EventCode event_code;
638 const std::unordered_multimap<hci_spec::EventCode, EventHandlerId>*
639 event_handlers;
640
641 EventType event_type;
642 switch (event->event_code()) {
643 case hci_spec::kLEMetaEventCode:
644 event_type = EventType::kLEMetaEvent;
645 event_code = event->view<pw::bluetooth::emboss::LEMetaEventView>()
646 .subevent_code()
647 .Read();
648 event_handlers = &le_meta_subevent_code_handlers_;
649 break;
650 case hci_spec::kVendorDebugEventCode:
651 event_type = EventType::kVendorEvent;
652 event_code = event->view<pw::bluetooth::emboss::VendorDebugEventView>()
653 .subevent_code()
654 .Read();
655 event_handlers = &vendor_subevent_code_handlers_;
656 break;
657 default:
658 event_type = EventType::kHciEvent;
659 event_code = event->event_code();
660 event_handlers = &event_code_handlers_;
661 break;
662 }
663
664 auto range = event_handlers->equal_range(event_code);
665 if (range.first == range.second) {
666 bt_log(DEBUG,
667 "hci",
668 "%s event %#.2x received with no handler",
669 EventTypeToString(event_type).c_str(),
670 event_code);
671 return;
672 }
673
674 auto iter = range.first;
675 while (iter != range.second) {
676 EventHandlerId event_id = iter->second;
677 bt_log(TRACE,
678 "hci",
679 "notifying handler (id %zu) for event code %#.2x",
680 event_id,
681 event_code);
682 auto handler_iter = event_handler_id_map_.find(event_id);
683 PW_DCHECK(handler_iter != event_handler_id_map_.end());
684
685 EventHandlerData& handler = handler_iter->second;
686 PW_DCHECK(handler.event_code == event_code);
687
688 pending_callbacks.push_back({handler.event_callback.share(), event_id});
689
690 ++iter; // Advance so we don't point to an invalid iterator.
691 if (handler.is_async()) {
692 bt_log(TRACE,
693 "hci",
694 "removing completed async handler (id %zu, event code: %#.2x)",
695 event_id,
696 event_code);
697 pending_transactions_.erase(handler.pending_opcode);
698 RemoveEventHandlerInternal(event_id); // |handler| is now dangling.
699 }
700 }
701
702 // Process queue so callbacks can't add a handler if another queued command
703 // finishes on the same event.
704 TrySendQueuedCommands();
705
706 EventPacket& event_packet = *event;
707 for (auto it = pending_callbacks.begin(); it != pending_callbacks.end();
708 ++it) {
709 // Execute the event callback.
710 EventCallbackResult result = it->callback(event_packet);
711
712 if (result == EventCallbackResult::kRemove) {
713 RemoveEventHandler(it->handler_id);
714 }
715 }
716 }
717
OnEvent(pw::span<const std::byte> buffer)718 void CommandChannel::OnEvent(pw::span<const std::byte> buffer) {
719 if (!active_) {
720 bt_log(INFO, "hci", "ignoring event (CommandChannel is inactive)");
721 return;
722 }
723
724 constexpr size_t kEventHeaderSize =
725 pw::bluetooth::emboss::EventHeader::IntrinsicSizeInBytes();
726 if (buffer.size() < kEventHeaderSize) {
727 // TODO(fxbug.dev/42179582): Handle these types of errors by signaling
728 // Transport.
729 bt_log(ERROR,
730 "hci",
731 "malformed packet - expected at least %zu bytes, got %zu",
732 kEventHeaderSize,
733 buffer.size());
734 return;
735 }
736
737 std::unique_ptr<EventPacket> event =
738 std::make_unique<EventPacket>(EventPacket::New(buffer.size()));
739 event->mutable_data().Write(reinterpret_cast<const uint8_t*>(buffer.data()),
740 buffer.size());
741
742 uint16_t header_payload_size =
743 event->view<pw::bluetooth::emboss::EventHeaderView>()
744 .parameter_total_size()
745 .Read();
746 const size_t received_payload_size = buffer.size() - kEventHeaderSize;
747 if (header_payload_size != received_payload_size) {
748 // TODO(fxbug.dev/42179582): Handle these types of errors by signaling
749 // Transport.
750 bt_log(ERROR,
751 "hci",
752 "malformed packet - payload size from header (%hu) does not match"
753 " received payload size: %zu",
754 header_payload_size,
755 received_payload_size);
756 return;
757 }
758
759 if (event->event_code() == hci_spec::kCommandStatusEventCode ||
760 event->event_code() == hci_spec::kCommandCompleteEventCode) {
761 UpdateTransaction(std::move(event));
762 TrySendQueuedCommands();
763 } else {
764 NotifyEventHandler(std::move(event));
765 }
766 }
767
OnCommandTimeout(TransactionId transaction_id)768 void CommandChannel::OnCommandTimeout(TransactionId transaction_id) {
769 if (!active_) {
770 return;
771 }
772 bt_log(
773 ERROR, "hci", "command %zu timed out, notifying error", transaction_id);
774 active_ = false;
775 if (channel_timeout_cb_) {
776 fit::closure cb = std::move(channel_timeout_cb_);
777 // The callback may destroy CommandChannel, so no state should be accessed
778 // after this line.
779 cb();
780 }
781 }
782
AttachInspect(inspect::Node & parent,const std::string & name)783 void CommandChannel::AttachInspect(inspect::Node& parent,
784 const std::string& name) {
785 command_channel_node_ = parent.CreateChild(name);
786 next_event_handler_id_.AttachInspect(command_channel_node_,
787 "next_event_handler_id");
788 allowed_command_packets_.AttachInspect(command_channel_node_,
789 "allowed_command_packets");
790 }
791
792 } // namespace bt::hci
793