// Copyright 2023 The Pigweed Authors // // 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 // // https://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 "pw_bluetooth_sapphire/internal/host/transport/transport.h" #include #include "pw_bluetooth_sapphire/internal/host/common/log.h" #include "pw_bluetooth_sapphire/internal/host/transport/acl_data_channel.h" namespace bt::hci { using FeaturesBits = pw::bluetooth::Controller::FeaturesBits; Transport::Transport(std::unique_ptr controller, pw::async::Dispatcher& dispatcher) : WeakSelf(this), dispatcher_(dispatcher), controller_(std::move(controller)) { PW_CHECK(controller_); } Transport::~Transport() { bt_log(INFO, "hci", "Transport shutting down"); } void Transport::Initialize( fit::callback complete_callback) { PW_CHECK(!command_channel_); bt_log(DEBUG, "hci", "initializing Transport"); auto self = GetWeakPtr(); auto complete_cb_wrapper = [self, complete_cb = std::move(complete_callback)]( pw::Status status) mutable { if (!self.is_alive()) { return; } if (!status.ok()) { complete_cb(/*success=*/false); return; } self->command_channel_ = std::make_unique( self->controller_.get(), self->dispatcher_); self->command_channel_->set_channel_timeout_cb([self] { if (self.is_alive()) { self->OnChannelError(); } }); self->controller_->GetFeatures( [self, cb = std::move(complete_cb)](FeaturesBits features) mutable { if (!self.is_alive()) { return; } self->features_ = features; bt_log(INFO, "hci", "Transport initialized"); cb(/*success=*/true); }); }; auto error_cb = [self](pw::Status) { if (self.is_alive()) { self->OnChannelError(); } }; controller_->Initialize(std::move(complete_cb_wrapper), std::move(error_cb)); } bool Transport::InitializeACLDataChannel( const DataBufferInfo& bredr_buffer_info, const DataBufferInfo& le_buffer_info) { acl_data_channel_ = AclDataChannel::Create( this, controller_.get(), bredr_buffer_info, le_buffer_info); if (hci_node_) { acl_data_channel_->AttachInspect(hci_node_, AclDataChannel::kInspectNodeName); } return true; } bool Transport::InitializeScoDataChannel(const DataBufferInfo& buffer_info) { if (!buffer_info.IsAvailable()) { bt_log( WARN, "hci", "failed to initialize SCO data channel: buffer info is not available"); return false; } if (static_cast(*features_ & FeaturesBits::kHciSco) == 0) { bt_log(WARN, "hci", "HCI SCO not supported"); return false; } sco_data_channel_ = ScoDataChannel::Create( buffer_info, command_channel_.get(), controller_.get()); return true; } bool Transport::InitializeIsoDataChannel(const DataBufferInfo& buffer_info) { PW_CHECK(buffer_info.IsAvailable()); if (static_cast(*features_ & FeaturesBits::kHciIso) == 0) { bt_log(WARN, "hci", "HCI ISO not supported"); return false; } iso_data_channel_ = IsoDataChannel::Create( buffer_info, command_channel_.get(), controller_.get()); return true; } FeaturesBits Transport::GetFeatures() { PW_CHECK(features_); return features_.value(); } void Transport::SetTransportErrorCallback(fit::closure callback) { PW_CHECK(callback); PW_CHECK(!error_cb_); error_cb_ = std::move(callback); } void Transport::OnChannelError() { bt_log(ERROR, "hci", "channel error, calling Transport error callback"); // The channels should not be shut down yet. That should be left to higher // layers so dependent objects can be destroyed first. if (error_cb_) { error_cb_(); } } void Transport::AttachInspect(inspect::Node& parent, const std::string& name) { PW_CHECK(acl_data_channel_); hci_node_ = parent.CreateChild(name); if (command_channel_) { command_channel_->AttachInspect(hci_node_); } if (acl_data_channel_) { acl_data_channel_->AttachInspect(hci_node_, AclDataChannel::kInspectNodeName); } } } // namespace bt::hci