| /* |
| * Copyright 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "hci/hci_layer.h" |
| |
| #include "common/bind.h" |
| #include "common/callback.h" |
| #include "os/alarm.h" |
| #include "os/queue.h" |
| #include "packet/packet_builder.h" |
| |
| namespace { |
| using bluetooth::common::Bind; |
| using bluetooth::common::BindOnce; |
| using bluetooth::common::Callback; |
| using bluetooth::common::Closure; |
| using bluetooth::common::OnceCallback; |
| using bluetooth::common::OnceClosure; |
| using bluetooth::hci::CommandCompleteView; |
| using bluetooth::hci::CommandPacketBuilder; |
| using bluetooth::hci::CommandStatusView; |
| using bluetooth::hci::EventPacketView; |
| using bluetooth::hci::LeMetaEventView; |
| using bluetooth::os::Handler; |
| |
| class EventHandler { |
| public: |
| EventHandler() : event_handler(), handler(nullptr) {} |
| EventHandler(Callback<void(EventPacketView)> on_event, Handler* on_event_handler) |
| : event_handler(std::move(on_event)), handler(on_event_handler) {} |
| Callback<void(EventPacketView)> event_handler; |
| Handler* handler; |
| }; |
| |
| class SubeventHandler { |
| public: |
| SubeventHandler() : subevent_handler(), handler(nullptr) {} |
| SubeventHandler(Callback<void(LeMetaEventView)> on_event, Handler* on_event_handler) |
| : subevent_handler(std::move(on_event)), handler(on_event_handler) {} |
| Callback<void(LeMetaEventView)> subevent_handler; |
| Handler* handler; |
| }; |
| |
| class CommandQueueEntry { |
| public: |
| CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet, |
| OnceCallback<void(CommandCompleteView)> on_complete_function, Handler* handler) |
| : command(std::move(command_packet)), waiting_for_status_(false), on_complete(std::move(on_complete_function)), |
| caller_handler(handler) {} |
| |
| CommandQueueEntry(std::unique_ptr<CommandPacketBuilder> command_packet, |
| OnceCallback<void(CommandStatusView)> on_status_function, Handler* handler) |
| : command(std::move(command_packet)), waiting_for_status_(true), on_status(std::move(on_status_function)), |
| caller_handler(handler) {} |
| |
| std::unique_ptr<CommandPacketBuilder> command; |
| bool waiting_for_status_; |
| OnceCallback<void(CommandStatusView)> on_status; |
| OnceCallback<void(CommandCompleteView)> on_complete; |
| Handler* caller_handler; |
| }; |
| } // namespace |
| |
| namespace bluetooth { |
| namespace hci { |
| |
| using common::BidiQueue; |
| using common::BidiQueueEnd; |
| using os::Alarm; |
| using os::Handler; |
| |
| namespace { |
| using hci::OpCode; |
| using hci::ResetCompleteView; |
| |
| void fail_if_reset_complete_not_success(CommandCompleteView complete) { |
| auto reset_complete = ResetCompleteView::Create(complete); |
| ASSERT(reset_complete.IsValid()); |
| ASSERT(reset_complete.GetStatus() == ErrorCode::SUCCESS); |
| } |
| |
| void on_hci_timeout(OpCode op_code) { |
| ASSERT_LOG(false, "Timed out waiting for 0x%02hx (%s)", op_code, OpCodeText(op_code).c_str()); |
| } |
| } // namespace |
| |
| class SecurityInterfaceImpl : public SecurityInterface { |
| public: |
| SecurityInterfaceImpl(HciLayer& hci) : hci_(hci) {} |
| virtual ~SecurityInterfaceImpl() = default; |
| |
| virtual void EnqueueCommand(std::unique_ptr<SecurityCommandBuilder> command, |
| common::OnceCallback<void(CommandCompleteView)> on_complete, |
| os::Handler* handler) override { |
| hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler); |
| } |
| |
| virtual void EnqueueCommand(std::unique_ptr<SecurityCommandBuilder> command, |
| common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override { |
| hci_.EnqueueCommand(std::move(command), std::move(on_status), handler); |
| } |
| HciLayer& hci_; |
| }; |
| |
| class LeSecurityInterfaceImpl : public LeSecurityInterface { |
| public: |
| LeSecurityInterfaceImpl(HciLayer& hci) : hci_(hci) {} |
| virtual ~LeSecurityInterfaceImpl() = default; |
| |
| virtual void EnqueueCommand(std::unique_ptr<LeSecurityCommandBuilder> command, |
| common::OnceCallback<void(CommandCompleteView)> on_complete, |
| os::Handler* handler) override { |
| hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler); |
| } |
| |
| virtual void EnqueueCommand(std::unique_ptr<LeSecurityCommandBuilder> command, |
| common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override { |
| hci_.EnqueueCommand(std::move(command), std::move(on_status), handler); |
| } |
| HciLayer& hci_; |
| }; |
| |
| class LeAdvertisingInterfaceImpl : public LeAdvertisingInterface { |
| public: |
| LeAdvertisingInterfaceImpl(HciLayer& hci) : hci_(hci) {} |
| virtual ~LeAdvertisingInterfaceImpl() = default; |
| |
| virtual void EnqueueCommand(std::unique_ptr<LeAdvertisingCommandBuilder> command, |
| common::OnceCallback<void(CommandCompleteView)> on_complete, |
| os::Handler* handler) override { |
| hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler); |
| } |
| |
| virtual void EnqueueCommand(std::unique_ptr<LeAdvertisingCommandBuilder> command, |
| common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override { |
| hci_.EnqueueCommand(std::move(command), std::move(on_status), handler); |
| } |
| HciLayer& hci_; |
| }; |
| |
| class LeScanningInterfaceImpl : public LeScanningInterface { |
| public: |
| LeScanningInterfaceImpl(HciLayer& hci) : hci_(hci) {} |
| virtual ~LeScanningInterfaceImpl() = default; |
| |
| virtual void EnqueueCommand(std::unique_ptr<LeScanningCommandBuilder> command, |
| common::OnceCallback<void(CommandCompleteView)> on_complete, |
| os::Handler* handler) override { |
| hci_.EnqueueCommand(std::move(command), std::move(on_complete), handler); |
| } |
| |
| virtual void EnqueueCommand(std::unique_ptr<LeScanningCommandBuilder> command, |
| common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) override { |
| hci_.EnqueueCommand(std::move(command), std::move(on_status), handler); |
| } |
| HciLayer& hci_; |
| }; |
| |
| struct HciLayer::impl : public hal::HciHalCallbacks { |
| impl(HciLayer& module) : hal_(nullptr), module_(module) {} |
| |
| ~impl() {} |
| |
| void Start(hal::HciHal* hal) { |
| hal_ = hal; |
| hci_timeout_alarm_ = new Alarm(module_.GetHandler()); |
| |
| auto queue_end = acl_queue_.GetDownEnd(); |
| Handler* handler = module_.GetHandler(); |
| queue_end->RegisterDequeue(handler, Bind(&impl::dequeue_and_send_acl, common::Unretained(this))); |
| RegisterEventHandler(EventCode::COMMAND_COMPLETE, Bind(&impl::command_complete_callback, common::Unretained(this)), |
| handler); |
| RegisterEventHandler(EventCode::COMMAND_STATUS, Bind(&impl::command_status_callback, common::Unretained(this)), |
| handler); |
| RegisterEventHandler(EventCode::LE_META_EVENT, Bind(&impl::le_meta_event_callback, common::Unretained(this)), |
| handler); |
| // TODO find the right place |
| RegisterEventHandler(EventCode::PAGE_SCAN_REPETITION_MODE_CHANGE, Bind(&impl::drop, common::Unretained(this)), |
| handler); |
| RegisterEventHandler(EventCode::MAX_SLOTS_CHANGE, Bind(&impl::drop, common::Unretained(this)), handler); |
| RegisterEventHandler(EventCode::VENDOR_SPECIFIC, Bind(&impl::drop, common::Unretained(this)), handler); |
| |
| EnqueueCommand(ResetBuilder::Create(), BindOnce(&fail_if_reset_complete_not_success), handler); |
| hal_->registerIncomingPacketCallback(this); |
| } |
| |
| void drop(EventPacketView) {} |
| |
| void dequeue_and_send_acl() { |
| auto packet = acl_queue_.GetDownEnd()->TryDequeue(); |
| send_acl(std::move(packet)); |
| } |
| |
| void Stop() { |
| hal_->unregisterIncomingPacketCallback(); |
| acl_queue_.GetDownEnd()->UnregisterDequeue(); |
| incoming_acl_packet_buffer_.Clear(); |
| delete hci_timeout_alarm_; |
| command_queue_.clear(); |
| hal_ = nullptr; |
| } |
| |
| void send_acl(std::unique_ptr<hci::BasePacketBuilder> packet) { |
| std::vector<uint8_t> bytes; |
| BitInserter bi(bytes); |
| packet->Serialize(bi); |
| hal_->sendAclData(bytes); |
| } |
| |
| void send_sco(std::unique_ptr<hci::BasePacketBuilder> packet) { |
| std::vector<uint8_t> bytes; |
| BitInserter bi(bytes); |
| packet->Serialize(bi); |
| hal_->sendScoData(bytes); |
| } |
| |
| void command_status_callback(EventPacketView event) { |
| CommandStatusView status_view = CommandStatusView::Create(event); |
| ASSERT(status_view.IsValid()); |
| command_credits_ = status_view.GetNumHciCommandPackets(); |
| OpCode op_code = status_view.GetCommandOpCode(); |
| if (op_code == OpCode::NONE) { |
| send_next_command(); |
| return; |
| } |
| ASSERT_LOG(!command_queue_.empty(), "Unexpected status event with OpCode 0x%02hx (%s)", op_code, |
| OpCodeText(op_code).c_str()); |
| ASSERT_LOG(waiting_command_ == op_code, "Waiting for 0x%02hx (%s), got 0x%02hx (%s)", waiting_command_, |
| OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str()); |
| ASSERT_LOG(command_queue_.front().waiting_for_status_, |
| "Waiting for command complete 0x%02hx (%s), got command status for 0x%02hx (%s)", waiting_command_, |
| OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str()); |
| auto caller_handler = command_queue_.front().caller_handler; |
| caller_handler->Post(BindOnce(std::move(command_queue_.front().on_status), std::move(status_view))); |
| command_queue_.pop_front(); |
| waiting_command_ = OpCode::NONE; |
| hci_timeout_alarm_->Cancel(); |
| send_next_command(); |
| } |
| |
| void command_complete_callback(EventPacketView event) { |
| CommandCompleteView complete_view = CommandCompleteView::Create(event); |
| ASSERT(complete_view.IsValid()); |
| command_credits_ = complete_view.GetNumHciCommandPackets(); |
| OpCode op_code = complete_view.GetCommandOpCode(); |
| if (op_code == OpCode::NONE) { |
| send_next_command(); |
| return; |
| } |
| ASSERT_LOG(command_queue_.size() > 0, "Unexpected command complete with OpCode 0x%02hx (%s)", op_code, |
| OpCodeText(op_code).c_str()); |
| ASSERT_LOG(waiting_command_ == op_code, "Waiting for 0x%02hx (%s), got 0x%02hx (%s)", waiting_command_, |
| OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str()); |
| ASSERT_LOG(!command_queue_.front().waiting_for_status_, |
| "Waiting for command status 0x%02hx (%s), got command complete for 0x%02hx (%s)", waiting_command_, |
| OpCodeText(waiting_command_).c_str(), op_code, OpCodeText(op_code).c_str()); |
| auto caller_handler = command_queue_.front().caller_handler; |
| caller_handler->Post(BindOnce(std::move(command_queue_.front().on_complete), complete_view)); |
| command_queue_.pop_front(); |
| waiting_command_ = OpCode::NONE; |
| hci_timeout_alarm_->Cancel(); |
| send_next_command(); |
| } |
| |
| void le_meta_event_callback(EventPacketView event) { |
| LeMetaEventView meta_event_view = LeMetaEventView::Create(event); |
| ASSERT(meta_event_view.IsValid()); |
| SubeventCode subevent_code = meta_event_view.GetSubeventCode(); |
| ASSERT_LOG(subevent_handlers_.find(subevent_code) != subevent_handlers_.end(), |
| "Unhandled le event of type 0x%02hhx (%s)", subevent_code, SubeventCodeText(subevent_code).c_str()); |
| auto& registered_handler = subevent_handlers_[subevent_code].subevent_handler; |
| subevent_handlers_[subevent_code].handler->Post(BindOnce(registered_handler, meta_event_view)); |
| } |
| |
| void hciEventReceived(hal::HciPacket event_bytes) override { |
| auto packet = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(event_bytes)); |
| EventPacketView event = EventPacketView::Create(packet); |
| ASSERT(event.IsValid()); |
| module_.GetHandler()->Post( |
| BindOnce(&HciLayer::impl::hci_event_received_handler, common::Unretained(this), std::move(event))); |
| } |
| |
| void hci_event_received_handler(EventPacketView event) { |
| EventCode event_code = event.GetEventCode(); |
| ASSERT_LOG(event_handlers_.find(event_code) != event_handlers_.end(), "Unhandled event of type 0x%02hhx (%s)", |
| event_code, EventCodeText(event_code).c_str()); |
| auto& registered_handler = event_handlers_[event_code].event_handler; |
| event_handlers_[event_code].handler->Post(BindOnce(registered_handler, std::move(event))); |
| } |
| |
| void aclDataReceived(hal::HciPacket data_bytes) override { |
| auto packet = |
| packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(std::move(data_bytes))); |
| AclPacketView acl = AclPacketView::Create(packet); |
| incoming_acl_packet_buffer_.Enqueue(std::make_unique<AclPacketView>(acl), module_.GetHandler()); |
| } |
| |
| void scoDataReceived(hal::HciPacket data_bytes) override { |
| auto packet = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(data_bytes)); |
| ScoPacketView sco = ScoPacketView::Create(packet); |
| } |
| |
| void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, |
| OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) { |
| module_.GetHandler()->Post(common::BindOnce(&impl::handle_enqueue_command_with_complete, common::Unretained(this), |
| std::move(command), std::move(on_complete), |
| common::Unretained(handler))); |
| } |
| |
| void EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, OnceCallback<void(CommandStatusView)> on_status, |
| os::Handler* handler) { |
| module_.GetHandler()->Post(common::BindOnce(&impl::handle_enqueue_command_with_status, common::Unretained(this), |
| std::move(command), std::move(on_status), common::Unretained(handler))); |
| } |
| |
| void handle_enqueue_command_with_complete(std::unique_ptr<CommandPacketBuilder> command, |
| OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) { |
| command_queue_.emplace_back(std::move(command), std::move(on_complete), handler); |
| |
| send_next_command(); |
| } |
| |
| void handle_enqueue_command_with_status(std::unique_ptr<CommandPacketBuilder> command, |
| OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) { |
| command_queue_.emplace_back(std::move(command), std::move(on_status), handler); |
| |
| send_next_command(); |
| } |
| |
| void send_next_command() { |
| if (command_credits_ == 0) { |
| return; |
| } |
| if (waiting_command_ != OpCode::NONE) { |
| return; |
| } |
| if (command_queue_.size() == 0) { |
| return; |
| } |
| std::shared_ptr<std::vector<uint8_t>> bytes = std::make_shared<std::vector<uint8_t>>(); |
| BitInserter bi(*bytes); |
| command_queue_.front().command->Serialize(bi); |
| hal_->sendHciCommand(*bytes); |
| auto cmd_view = CommandPacketView::Create(bytes); |
| ASSERT(cmd_view.IsValid()); |
| OpCode op_code = cmd_view.GetOpCode(); |
| waiting_command_ = op_code; |
| command_credits_ = 0; // Only allow one outstanding command |
| hci_timeout_alarm_->Schedule(BindOnce(&on_hci_timeout, op_code), kHciTimeoutMs); |
| } |
| |
| BidiQueueEnd<AclPacketBuilder, AclPacketView>* GetAclQueueEnd() { |
| return acl_queue_.GetUpEnd(); |
| } |
| |
| void RegisterEventHandler(EventCode event_code, Callback<void(EventPacketView)> event_handler, os::Handler* handler) { |
| module_.GetHandler()->Post(common::BindOnce(&impl::handle_register_event_handler, common::Unretained(this), |
| event_code, event_handler, common::Unretained(handler))); |
| } |
| |
| void handle_register_event_handler(EventCode event_code, Callback<void(EventPacketView)> event_handler, |
| os::Handler* handler) { |
| ASSERT_LOG(event_handlers_.count(event_code) == 0, "Can not register a second handler for event_code %02hhx (%s)", |
| event_code, EventCodeText(event_code).c_str()); |
| EventHandler to_save(event_handler, handler); |
| event_handlers_[event_code] = to_save; |
| } |
| |
| void UnregisterEventHandler(EventCode event_code) { |
| module_.GetHandler()->Post( |
| common::BindOnce(&impl::handle_unregister_event_handler, common::Unretained(this), event_code)); |
| } |
| |
| void handle_unregister_event_handler(EventCode event_code) { |
| event_handlers_.erase(event_code); |
| } |
| |
| void RegisterLeEventHandler(SubeventCode subevent_code, Callback<void(LeMetaEventView)> event_handler, |
| os::Handler* handler) { |
| module_.GetHandler()->Post(common::BindOnce(&impl::handle_register_le_event_handler, common::Unretained(this), |
| subevent_code, event_handler, common::Unretained(handler))); |
| } |
| |
| void handle_register_le_event_handler(SubeventCode subevent_code, Callback<void(LeMetaEventView)> subevent_handler, |
| os::Handler* handler) { |
| ASSERT_LOG(subevent_handlers_.count(subevent_code) == 0, |
| "Can not register a second handler for subevent_code %02hhx (%s)", subevent_code, |
| SubeventCodeText(subevent_code).c_str()); |
| SubeventHandler to_save(subevent_handler, handler); |
| subevent_handlers_[subevent_code] = to_save; |
| } |
| |
| void UnregisterLeEventHandler(SubeventCode subevent_code) { |
| module_.GetHandler()->Post( |
| common::BindOnce(&impl::handle_unregister_le_event_handler, common::Unretained(this), subevent_code)); |
| } |
| |
| void handle_unregister_le_event_handler(SubeventCode subevent_code) { |
| subevent_handlers_.erase(subevent_code); |
| } |
| |
| // The HAL |
| hal::HciHal* hal_; |
| |
| // A reference to the HciLayer module |
| HciLayer& module_; |
| |
| // Interfaces |
| SecurityInterfaceImpl security_interface{module_}; |
| LeSecurityInterfaceImpl le_security_interface{module_}; |
| LeAdvertisingInterfaceImpl le_advertising_interface{module_}; |
| LeScanningInterfaceImpl le_scanning_interface{module_}; |
| |
| // Command Handling |
| std::list<CommandQueueEntry> command_queue_; |
| |
| std::map<EventCode, EventHandler> event_handlers_; |
| std::map<SubeventCode, SubeventHandler> subevent_handlers_; |
| OpCode waiting_command_{OpCode::NONE}; |
| uint8_t command_credits_{1}; // Send reset first |
| Alarm* hci_timeout_alarm_{nullptr}; |
| |
| // Acl packets |
| BidiQueue<AclPacketView, AclPacketBuilder> acl_queue_{3 /* TODO: Set queue depth */}; |
| os::EnqueueBuffer<AclPacketView> incoming_acl_packet_buffer_{acl_queue_.GetDownEnd()}; |
| }; |
| |
| HciLayer::HciLayer() : impl_(std::make_unique<impl>(*this)) {} |
| |
| HciLayer::~HciLayer() { |
| impl_.reset(); |
| } |
| |
| void HciLayer::EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, |
| common::OnceCallback<void(CommandCompleteView)> on_complete, os::Handler* handler) { |
| impl_->EnqueueCommand(std::move(command), std::move(on_complete), handler); |
| } |
| |
| void HciLayer::EnqueueCommand(std::unique_ptr<CommandPacketBuilder> command, |
| common::OnceCallback<void(CommandStatusView)> on_status, os::Handler* handler) { |
| impl_->EnqueueCommand(std::move(command), std::move(on_status), handler); |
| } |
| |
| common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* HciLayer::GetAclQueueEnd() { |
| return impl_->GetAclQueueEnd(); |
| } |
| |
| void HciLayer::RegisterEventHandler(EventCode event_code, common::Callback<void(EventPacketView)> event_handler, |
| os::Handler* handler) { |
| impl_->RegisterEventHandler(event_code, std::move(event_handler), handler); |
| } |
| |
| void HciLayer::UnregisterEventHandler(EventCode event_code) { |
| impl_->UnregisterEventHandler(event_code); |
| } |
| |
| void HciLayer::RegisterLeEventHandler(SubeventCode subevent_code, common::Callback<void(LeMetaEventView)> event_handler, |
| os::Handler* handler) { |
| impl_->RegisterLeEventHandler(subevent_code, std::move(event_handler), handler); |
| } |
| |
| void HciLayer::UnregisterLeEventHandler(SubeventCode subevent_code) { |
| impl_->UnregisterLeEventHandler(subevent_code); |
| } |
| |
| SecurityInterface* HciLayer::GetSecurityInterface(common::Callback<void(EventPacketView)> event_handler, |
| os::Handler* handler) { |
| for (const auto event : SecurityInterface::SecurityEvents) { |
| RegisterEventHandler(event, event_handler, handler); |
| } |
| return &impl_->security_interface; |
| } |
| |
| LeSecurityInterface* HciLayer::GetLeSecurityInterface(common::Callback<void(LeMetaEventView)> event_handler, |
| os::Handler* handler) { |
| for (const auto subevent : LeSecurityInterface::LeSecurityEvents) { |
| RegisterLeEventHandler(subevent, event_handler, handler); |
| } |
| return &impl_->le_security_interface; |
| } |
| |
| LeAdvertisingInterface* HciLayer::GetLeAdvertisingInterface(common::Callback<void(LeMetaEventView)> event_handler, |
| os::Handler* handler) { |
| for (const auto subevent : LeAdvertisingInterface::LeAdvertisingEvents) { |
| RegisterLeEventHandler(subevent, event_handler, handler); |
| } |
| return &impl_->le_advertising_interface; |
| } |
| |
| LeScanningInterface* HciLayer::GetLeScanningInterface(common::Callback<void(LeMetaEventView)> event_handler, |
| os::Handler* handler) { |
| for (const auto subevent : LeScanningInterface::LeScanningEvents) { |
| RegisterLeEventHandler(subevent, event_handler, handler); |
| } |
| return &impl_->le_scanning_interface; |
| } |
| |
| const ModuleFactory HciLayer::Factory = ModuleFactory([]() { return new HciLayer(); }); |
| |
| void HciLayer::ListDependencies(ModuleList* list) { |
| list->add<hal::HciHal>(); |
| } |
| |
| void HciLayer::Start() { |
| impl_->Start(GetDependency<hal::HciHal>()); |
| } |
| |
| void HciLayer::Stop() { |
| impl_->Stop(); |
| } |
| |
| std::string HciLayer::ToString() const { |
| return "Hci Layer"; |
| } |
| } // namespace hci |
| } // namespace bluetooth |