blob: 63875ead1dbc18050e07bb9c8d2f8937d42fe815 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "banjo_controller.h"
#include <lib/async/cpp/task.h>
#include <lib/fit/defer.h>
#include <variant>
#include "helpers.h"
#include "pw_result/result.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/assert.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/trace.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/iso/iso_common.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/transport/slab_allocators.h"
namespace bt::controllers {
namespace {
sco_coding_format_t ScoCodingFormatToBanjo(
pw::bluetooth::Controller::ScoCodingFormat coding_format) {
switch (coding_format) {
case pw::bluetooth::Controller::ScoCodingFormat::kCvsd:
return SCO_CODING_FORMAT_CVSD;
case pw::bluetooth::Controller::ScoCodingFormat::kMsbc:
return SCO_CODING_FORMAT_MSBC;
default:
BT_PANIC("invalid SCO coding format");
}
}
sco_encoding_t ScoEncodingToBanjo(pw::bluetooth::Controller::ScoEncoding encoding) {
switch (encoding) {
case pw::bluetooth::Controller::ScoEncoding::k8Bits:
return SCO_ENCODING_BITS_8;
case pw::bluetooth::Controller::ScoEncoding::k16Bits:
return SCO_ENCODING_BITS_16;
default:
BT_PANIC("invalid SCO encoding");
}
}
sco_sample_rate_t ScoSampleRateToBanjo(pw::bluetooth::Controller::ScoSampleRate sample_rate) {
switch (sample_rate) {
case pw::bluetooth::Controller::ScoSampleRate::k8Khz:
return SCO_SAMPLE_RATE_KHZ_8;
case pw::bluetooth::Controller::ScoSampleRate::k16Khz:
return SCO_SAMPLE_RATE_KHZ_16;
default:
BT_PANIC("invalid SCO sample rate");
}
}
bt_vendor_acl_priority_t AclPriorityToBanjo(pw::bluetooth::AclPriority priority) {
switch (priority) {
case pw::bluetooth::AclPriority::kNormal:
return BT_VENDOR_ACL_PRIORITY_NORMAL;
case pw::bluetooth::AclPriority::kSink:
case pw::bluetooth::AclPriority::kSource:
return BT_VENDOR_ACL_PRIORITY_HIGH;
}
}
bt_vendor_acl_direction_t AclPriorityToBanjoAclDirection(pw::bluetooth::AclPriority priority) {
switch (priority) {
// The direction for kNormal is arbitrary.
case pw::bluetooth::AclPriority::kNormal:
case pw::bluetooth::AclPriority::kSink:
return BT_VENDOR_ACL_DIRECTION_SINK;
case pw::bluetooth::AclPriority::kSource:
return BT_VENDOR_ACL_DIRECTION_SOURCE;
}
}
} // namespace
BanjoController::BanjoController(ddk::BtHciProtocolClient hci_proto,
std::optional<ddk::BtVendorProtocolClient> vendor_proto,
async_dispatcher_t* dispatcher)
: hci_proto_(hci_proto),
vendor_proto_(vendor_proto),
dispatcher_(dispatcher),
callback_data_(fbl::AdoptRef(new CallbackData{.dispatcher = dispatcher})) {}
BanjoController::~BanjoController() { CleanUp(); }
void BanjoController::Initialize(PwStatusCallback complete_callback,
PwStatusCallback error_callback) {
error_cb_ = std::move(error_callback);
zx::channel their_command_chan;
zx_status_t status = zx::channel::create(0, &command_channel_, &their_command_chan);
if (status != ZX_OK) {
bt_log(ERROR, "controllers", "Failed to create command channel");
complete_callback(pw::Status::Internal());
return;
}
status = hci_proto_.OpenCommandChannel(std::move(their_command_chan));
if (status != ZX_OK) {
bt_log(ERROR, "controllers", "Failed to open command channel");
complete_callback(pw::Status::Internal());
return;
}
InitializeWait(command_wait_, command_channel_);
zx::channel their_acl_chan;
status = zx::channel::create(0, &acl_channel_, &their_acl_chan);
if (status != ZX_OK) {
bt_log(ERROR, "controllers", "Failed to create ACL channel");
complete_callback(pw::Status::Internal());
return;
}
status = hci_proto_.OpenAclDataChannel(std::move(their_acl_chan));
if (status != ZX_OK) {
bt_log(ERROR, "controllers", "Failed to open ACL channel");
complete_callback(pw::Status::Internal());
return;
}
InitializeWait(acl_wait_, acl_channel_);
zx::channel their_sco_chan;
status = zx::channel::create(0, &sco_channel_, &their_sco_chan);
if (status != ZX_OK) {
bt_log(ERROR, "controllers", "Failed to create SCO channel");
complete_callback(pw::Status::Internal());
return;
}
status = hci_proto_.OpenScoChannel(std::move(their_sco_chan));
if (status == ZX_OK) {
InitializeWait(sco_wait_, sco_channel_);
} else {
// Failing to open a SCO channel is not fatal, it just indicates lack of SCO support.
bt_log(INFO, "controllers", "Failed to open SCO channel: %s", zx_status_get_string(status));
sco_channel_.reset();
}
zx::channel their_iso_chan;
status = zx::channel::create(0, &iso_channel_, &their_iso_chan);
if (status != ZX_OK) {
bt_log(ERROR, "controllers", "Failed to create ISO channel");
complete_callback(pw::Status::Internal());
return;
}
status = hci_proto_.OpenIsoDataChannel(std::move(their_iso_chan));
if (status == ZX_OK) {
InitializeWait(iso_wait_, iso_channel_);
} else {
// Failing to open an ISO channel is not fatal, it just indicates lack of ISO support.
bt_log(INFO, "controllers", "Failed to open ISO channel: %s", zx_status_get_string(status));
iso_channel_.reset();
}
complete_callback(PW_STATUS_OK);
}
void BanjoController::Close(PwStatusCallback callback) {
CleanUp();
callback(PW_STATUS_OK);
}
void BanjoController::SendCommand(pw::span<const std::byte> command) {
zx_status_t status =
command_channel_.write(/*flags=*/0, command.data(), static_cast<uint32_t>(command.size()),
/*handles=*/nullptr, /*num_handles=*/0);
if (status != ZX_OK) {
bt_log(ERROR, "controllers", "failed to write command channel: %s",
zx_status_get_string(status));
OnError(status);
}
}
void BanjoController::SendAclData(pw::span<const std::byte> data) {
zx_status_t status =
acl_channel_.write(/*flags=*/0, data.data(), static_cast<uint32_t>(data.size()),
/*handles=*/nullptr, /*num_handles=*/0);
if (status != ZX_OK) {
bt_log(ERROR, "controllers", "failed to write ACL channel: %s", zx_status_get_string(status));
OnError(status);
}
}
void BanjoController::SendScoData(pw::span<const std::byte> data) {
zx_status_t status =
sco_channel_.write(/*flags=*/0, data.data(), static_cast<uint32_t>(data.size()),
/*handles=*/nullptr, /*num_handles=*/0);
if (status != ZX_OK) {
bt_log(ERROR, "controllers", "failed to write SCO channel: %s", zx_status_get_string(status));
OnError(status);
}
}
void BanjoController::SendIsoData(pw::span<const std::byte> data) {
zx_status_t status =
iso_channel_.write(/*flags=*/0, data.data(), static_cast<uint32_t>(data.size()),
/*handles=*/nullptr, /*num_handles=*/0);
if (status != ZX_OK) {
bt_log(ERROR, "controllers", "failed to write ISO channel: %s", zx_status_get_string(status));
OnError(status);
}
}
void BanjoController::ConfigureSco(ScoCodingFormat coding_format, ScoEncoding encoding,
ScoSampleRate sample_rate, PwStatusCallback callback) {
hci_proto_.ConfigureSco(
ScoCodingFormatToBanjo(coding_format), ScoEncodingToBanjo(encoding),
ScoSampleRateToBanjo(sample_rate),
[](void* ctx, zx_status_t status) {
std::unique_ptr<PwStatusCallback> callback(static_cast<PwStatusCallback*>(ctx));
(*callback)(ZxStatusToPwStatus(status));
},
new PwStatusCallback(ThreadSafeCallbackWrapper(std::move(callback))));
}
void BanjoController::ResetSco(pw::Callback<void(pw::Status)> callback) {
hci_proto_.ResetSco(
[](void* ctx, zx_status_t status) {
std::unique_ptr<PwStatusCallback> callback(static_cast<PwStatusCallback*>(ctx));
(*callback)(ZxStatusToPwStatus(status));
},
new PwStatusCallback(ThreadSafeCallbackWrapper(std::move(callback))));
}
void BanjoController::GetFeatures(pw::Callback<void(FeaturesBits)> callback) {
if (!vendor_proto_) {
callback(FeaturesBits{0});
return;
}
FeaturesBits features_bits = BanjoVendorFeaturesToFeaturesBits(vendor_proto_->GetFeatures());
if (sco_channel_.is_valid()) {
features_bits |= FeaturesBits::kHciSco;
}
if (iso_channel_.is_valid()) {
features_bits |= FeaturesBits::kHciIso;
}
callback(features_bits);
}
void BanjoController::EncodeVendorCommand(
pw::bluetooth::VendorCommandParameters parameters,
pw::Callback<void(pw::Result<pw::span<const std::byte>>)> callback) {
BT_ASSERT(vendor_proto_.has_value());
if (!std::holds_alternative<pw::bluetooth::SetAclPriorityCommandParameters>(parameters)) {
callback(pw::Status::Unimplemented());
return;
}
pw::bluetooth::SetAclPriorityCommandParameters params =
std::get<pw::bluetooth::SetAclPriorityCommandParameters>(parameters);
bt_vendor_set_acl_priority_params_t priority_params = {
.connection_handle = params.connection_handle,
.priority = AclPriorityToBanjo(params.priority),
.direction = AclPriorityToBanjoAclDirection(params.priority)};
bt_vendor_params_t cmd_params = {.set_acl_priority = priority_params};
StaticByteBuffer<pw::bluetooth::kMaxVendorCommandBufferSize> encoded_command;
size_t actual_size = 0;
zx_status_t encode_result = vendor_proto_->EncodeCommand(
BT_VENDOR_COMMAND_SET_ACL_PRIORITY, &cmd_params, encoded_command.mutable_data(),
encoded_command.size(), &actual_size);
if (encode_result != ZX_OK) {
bt_log(WARN, "controllers", "Failed to encode vendor command");
callback(ZxStatusToPwStatus(encode_result));
return;
}
callback(encoded_command.subspan(/*pos=*/0, actual_size));
}
void BanjoController::OnError(zx_status_t status) {
CleanUp();
if (error_cb_) {
error_cb_(ZxStatusToPwStatus(status));
}
}
void BanjoController::CleanUp() {
{
std::lock_guard<std::mutex> guard(callback_data_->lock);
callback_data_->dispatcher = nullptr;
}
// Waits need to be canceled before the underlying channels are destroyed.
acl_wait_.Cancel();
command_wait_.Cancel();
sco_wait_.Cancel();
iso_wait_.Cancel();
acl_channel_.reset();
sco_channel_.reset();
iso_channel_.reset();
command_channel_.reset();
}
// Wraps a callback in one that posts on the bt-host thread.
BanjoController::PwStatusCallback BanjoController::ThreadSafeCallbackWrapper(
PwStatusCallback callback) {
return [cb = std::move(callback), data = callback_data_](pw::Status status) mutable {
std::lock_guard<std::mutex> guard(data->lock);
// Don't run the callback if BanjoController has been destroyed.
if (data->dispatcher) {
// This callback may be run on a different thread, so post the result callback to the
// bt-host thread.
async::PostTask(data->dispatcher, [cb = std::move(cb), status]() mutable { cb(status); });
}
};
}
void BanjoController::InitializeWait(async::WaitBase& wait, zx::channel& channel) {
BT_ASSERT(channel.is_valid());
wait.Cancel();
wait.set_object(channel.get());
wait.set_trigger(ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED);
BT_ASSERT(wait.Begin(dispatcher_) == ZX_OK);
}
void BanjoController::OnChannelSignal(const char* chan_name, async::WaitBase* wait,
pw::span<std::byte> buffer, zx::channel& channel,
DataFunction& data_cb) {
uint32_t actual_size;
zx_status_t read_status =
channel.read(/*flags=*/0u, /*bytes=*/buffer.data(), /*handles=*/nullptr,
/*num_bytes=*/static_cast<uint32_t>(buffer.size()), /*num_handles=*/0,
/*actual_bytes=*/&actual_size, /*actual_handles=*/nullptr);
if (read_status != ZX_OK) {
bt_log(ERROR, "controllers", "%s channel: failed to read RX bytes: %s", chan_name,
zx_status_get_string(read_status));
OnError(read_status);
return;
}
if (data_cb) {
data_cb(buffer.subspan(0, actual_size));
} else {
bt_log(WARN, "controllers", "Dropping packet received on %s channel (no rx callback set)",
chan_name);
}
// The wait needs to be restarted after every signal.
zx_status_t wait_status = wait->Begin(dispatcher_);
if (wait_status != ZX_OK) {
bt_log(ERROR, "controllers", "%s wait error: %s", chan_name, zx_status_get_string(wait_status));
OnError(wait_status);
return;
}
}
void BanjoController::OnAclSignal(async_dispatcher_t* dispatcher, async::WaitBase* wait,
zx_status_t status, const zx_packet_signal_t* signal) {
const char* kChannelName = "ACL";
TRACE_DURATION("bluetooth", "BanjoController::OnAclSignal");
if (status != ZX_OK) {
bt_log(ERROR, "controllers", "%s channel error: %s", kChannelName,
zx_status_get_string(status));
OnError(status);
return;
}
if (signal->observed & ZX_CHANNEL_PEER_CLOSED) {
bt_log(ERROR, "controllers", "%s channel closed", kChannelName);
OnError(ZX_ERR_PEER_CLOSED);
return;
}
BT_ASSERT(signal->observed & ZX_CHANNEL_READABLE);
// Allocate a buffer for the packet. Since we don't know the size beforehand we allocate the
// largest possible buffer.
std::byte packet[hci::allocators::kLargeACLDataPacketSize];
OnChannelSignal(kChannelName, wait, packet, acl_channel_, acl_cb_);
}
void BanjoController::OnCommandSignal(async_dispatcher_t* /*dispatcher*/, async::WaitBase* wait,
zx_status_t status, const zx_packet_signal_t* signal) {
const char* kChannelName = "command";
TRACE_DURATION("bluetooth", "BanjoController::OnCommandSignal");
if (status != ZX_OK) {
bt_log(ERROR, "controllers", "%s channel error: %s", kChannelName,
zx_status_get_string(status));
OnError(status);
return;
}
if (signal->observed & ZX_CHANNEL_PEER_CLOSED) {
bt_log(ERROR, "controllers", "%s channel closed", kChannelName);
OnError(ZX_ERR_PEER_CLOSED);
return;
}
BT_ASSERT(signal->observed & ZX_CHANNEL_READABLE);
// Allocate a buffer for the packet. Since we don't know the size beforehand we allocate the
// largest possible buffer.
std::byte packet[hci_spec::kMaxEventPacketPayloadSize + sizeof(hci_spec::EventHeader)];
OnChannelSignal(kChannelName, wait, packet, command_channel_, event_cb_);
}
void BanjoController::OnScoSignal(async_dispatcher_t* /*dispatcher*/, async::WaitBase* wait,
zx_status_t status, const zx_packet_signal_t* signal) {
const char* kChannelName = "SCO";
TRACE_DURATION("bluetooth", "BanjoController::OnScoSignal");
if (status != ZX_OK) {
bt_log(ERROR, "controllers", "%s channel error: %s", kChannelName,
zx_status_get_string(status));
OnError(status);
return;
}
if (signal->observed & ZX_CHANNEL_PEER_CLOSED) {
bt_log(ERROR, "controllers", "%s channel closed", kChannelName);
OnError(ZX_ERR_PEER_CLOSED);
return;
}
BT_ASSERT(signal->observed & ZX_CHANNEL_READABLE);
// Allocate a buffer for the packet. Since we don't know the size beforehand we allocate the
// largest possible buffer.
std::byte packet[hci::allocators::kMaxScoDataPacketSize];
OnChannelSignal(kChannelName, wait, packet, sco_channel_, sco_cb_);
}
void BanjoController::OnIsoSignal(async_dispatcher_t* /*dispatcher*/, async::WaitBase* wait,
zx_status_t status, const zx_packet_signal_t* signal) {
const char* kChannelName = "ISO";
TRACE_DURATION("bluetooth", "BanjoController::OnIsoSignal");
if (status != ZX_OK) {
bt_log(ERROR, "controllers", "%s channel error: %s", kChannelName,
zx_status_get_string(status));
OnError(status);
return;
}
if (signal->observed & ZX_CHANNEL_PEER_CLOSED) {
bt_log(ERROR, "controllers", "%s channel closed", kChannelName);
OnError(ZX_ERR_PEER_CLOSED);
return;
}
BT_ASSERT(signal->observed & ZX_CHANNEL_READABLE);
// Isochronous data frames can be quite large (16KB), so dynamically allocate only what is needed.
uint32_t read_size = 0;
status = iso_channel_.read(/*flags=*/0u, /*bytes=*/nullptr, /*handles=*/nullptr, /*num_bytes=*/0u,
/*num_handles=*/0, &read_size, /*actual_handles=*/nullptr);
if (status == ZX_OK) {
bt_log(WARN, "controllers", "%s channel: read 0-length packet", kChannelName);
return;
}
if (status != ZX_ERR_BUFFER_TOO_SMALL) {
bt_log(ERROR, "controllers", "%s channel: failed to read packet size: %s", kChannelName,
zx_status_get_string(status));
OnError(status);
return;
}
if (read_size > iso::kMaxIsochronousDataPacketSize) {
bt_log(ERROR, "controllers", "%s channel: packet size (%d) exceeds maximum (%zu)", kChannelName,
read_size, iso::kMaxIsochronousDataPacketSize);
OnError(status);
return;
}
std::unique_ptr<std::byte[]> buffer_ptr = std::make_unique<std::byte[]>(read_size);
pw::span<std::byte> packet{buffer_ptr.get(), read_size};
OnChannelSignal(kChannelName, wait, packet, iso_channel_, iso_cb_);
}
pw::bluetooth::Controller::FeaturesBits BanjoController::BanjoVendorFeaturesToFeaturesBits(
bt_vendor_features_t features) {
FeaturesBits out{0};
if (features & BT_VENDOR_FEATURES_SET_ACL_PRIORITY_COMMAND) {
out |= FeaturesBits::kSetAclPriorityCommand;
}
if (features & BT_VENDOR_FEATURES_ANDROID_VENDOR_EXTENSIONS) {
out |= FeaturesBits::kAndroidVendorExtensions;
}
return out;
}
} // namespace bt::controllers