blob: 5af3ada18b43490942b66097e42b85d65f69e1e4 [file] [log] [blame]
// 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