| // 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 "pw_bluetooth_sapphire/internal/host/common/assert.h" |
| #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<pw::bluetooth::Controller> controller, |
| pw::async::Dispatcher& dispatcher) |
| : WeakSelf(this), |
| dispatcher_(dispatcher), |
| controller_(std::move(controller)) { |
| BT_ASSERT(controller_); |
| } |
| |
| Transport::~Transport() { bt_log(INFO, "hci", "Transport shutting down"); } |
| |
| void Transport::Initialize( |
| fit::callback<void(bool /*success*/)> complete_callback) { |
| BT_ASSERT(!command_channel_); |
| |
| bt_log(DEBUG, "hci", "initializing Transport"); |
| auto self = GetWeakPtr(); |
| auto complete_cb_wrapper = |
| [self, cb = std::move(complete_callback)](pw::Status status) mutable { |
| if (!self.is_alive()) { |
| return; |
| } |
| |
| if (!status.ok()) { |
| cb(/*success=*/false); |
| return; |
| } |
| |
| self->command_channel_ = std::make_unique<CommandChannel>( |
| 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(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 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<uint32_t>(*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; |
| } |
| |
| FeaturesBits Transport::GetFeatures() { |
| BT_ASSERT(features_); |
| return features_.value(); |
| } |
| |
| void Transport::SetTransportErrorCallback(fit::closure callback) { |
| BT_ASSERT(callback); |
| BT_ASSERT(!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) { |
| BT_ASSERT(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 |