blob: ee259d51fe3fc5f58169d94991000a42b04c1934 [file] [log] [blame]
// Copyright 2018 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 "l2cap.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/common/task_domain.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/control_packets.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/transport.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/channel_manager.h"
namespace bt::l2cap {
class Impl final : public L2cap {
public:
Impl(fxl::WeakPtr<hci::Transport> hci, bool random_channel_ids)
: L2cap(), dispatcher_(async_get_default_dispatcher()), hci_(std::move(hci)) {
ZX_ASSERT(hci_);
ZX_ASSERT(hci_->acl_data_channel());
const auto& acl_buffer_info = hci_->acl_data_channel()->GetBufferInfo();
const auto& le_buffer_info = hci_->acl_data_channel()->GetLEBufferInfo();
// LE Buffer Info is always available from ACLDataChannel.
ZX_ASSERT(acl_buffer_info.IsAvailable());
auto send_packets =
fit::bind_member(hci_->acl_data_channel(), &hci::ACLDataChannel::SendPackets);
auto drop_queued_acl =
fit::bind_member(hci_->acl_data_channel(), &hci::ACLDataChannel::DropQueuedPackets);
auto acl_priority = fit::bind_member(this, &Impl::RequestAclPriority);
channel_manager_ = std::make_unique<ChannelManager>(
acl_buffer_info.max_data_length(), le_buffer_info.max_data_length(),
std::move(send_packets), std::move(drop_queued_acl), std::move(acl_priority),
random_channel_ids);
hci_->acl_data_channel()->SetDataRxHandler(channel_manager_->MakeInboundDataHandler());
bt_log(DEBUG, "l2cap", "initialized");
}
~Impl() {
bt_log(DEBUG, "l2cap", "shutting down");
ZX_ASSERT(hci_->acl_data_channel());
hci_->acl_data_channel()->SetDataRxHandler(nullptr);
channel_manager_ = nullptr;
}
void AddACLConnection(hci::ConnectionHandle handle, hci::Connection::Role role,
LinkErrorCallback link_error_callback,
SecurityUpgradeCallback security_callback) override {
channel_manager_->RegisterACL(handle, role, std::move(link_error_callback),
std::move(security_callback));
}
void AttachInspect(inspect::Node& parent, std::string name) override {
node_ = parent.CreateChild(name);
channel_manager_->AttachInspect(node_);
}
LEFixedChannels AddLEConnection(hci::ConnectionHandle handle, hci::Connection::Role role,
LinkErrorCallback link_error_callback,
LEConnectionParameterUpdateCallback conn_param_callback,
SecurityUpgradeCallback security_callback) override {
channel_manager_->RegisterLE(handle, role, std::move(conn_param_callback),
std::move(link_error_callback), std::move(security_callback));
auto att = channel_manager_->OpenFixedChannel(handle, kATTChannelId);
auto smp = channel_manager_->OpenFixedChannel(handle, kLESMPChannelId);
ZX_ASSERT(att);
ZX_ASSERT(smp);
return LEFixedChannels{.att = std::move(att), .smp = std::move(smp)};
}
void RemoveConnection(hci::ConnectionHandle handle) override {
channel_manager_->Unregister(handle);
}
void AssignLinkSecurityProperties(hci::ConnectionHandle handle,
sm::SecurityProperties security) override {
channel_manager_->AssignLinkSecurityProperties(handle, security);
}
void RequestConnectionParameterUpdate(
hci::ConnectionHandle handle, hci::LEPreferredConnectionParameters params,
ConnectionParameterUpdateRequestCallback request_cb) override {
channel_manager_->RequestConnectionParameterUpdate(handle, params, std::move(request_cb));
}
void OpenL2capChannel(hci::ConnectionHandle handle, PSM psm, ChannelParameters params,
ChannelCallback cb) override {
channel_manager_->OpenChannel(handle, psm, params, std::move(cb));
}
void RegisterService(PSM psm, ChannelParameters params, ChannelCallback callback) override {
const bool result = channel_manager_->RegisterService(psm, params, std::move(callback));
ZX_DEBUG_ASSERT(result);
}
void UnregisterService(PSM psm) override { channel_manager_->UnregisterService(psm); }
private:
void RequestAclPriority(AclPriority priority, hci::ConnectionHandle handle,
fit::callback<void(fit::result<>)> cb) {
bt_log(TRACE, "l2cap", "sending ACL priority command");
bt_vendor_set_acl_priority_params_t priority_params = {
.connection_handle = handle,
.priority = static_cast<bt_vendor_acl_priority_t>((priority == AclPriority::kNormal)
? BT_VENDOR_ACL_PRIORITY_NORMAL
: BT_VENDOR_ACL_PRIORITY_HIGH),
.direction = static_cast<bt_vendor_acl_direction_t>((priority == AclPriority::kSource)
? BT_VENDOR_ACL_DIRECTION_SOURCE
: BT_VENDOR_ACL_DIRECTION_SINK)};
bt_vendor_params_t cmd_params = {.set_acl_priority = priority_params};
auto encode_result = hci_->EncodeVendorCommand(BT_VENDOR_COMMAND_SET_ACL_PRIORITY, cmd_params);
if (encode_result.is_error()) {
bt_log(TRACE, "l2cap", "encoding ACL priority command failed");
cb(fit::error());
return;
}
auto encoded = encode_result.take_value();
if (encoded.size() < sizeof(hci::CommandHeader)) {
bt_log(TRACE, "l2cap", "encoded ACL priority command too small (size: %zu)", encoded.size());
cb(fit::error());
return;
}
hci::OpCode op_code = letoh16(encoded.As<hci::CommandHeader>().opcode);
auto packet = bt::hci::CommandPacket::New(op_code, encoded.size() - sizeof(hci::CommandHeader));
auto packet_view = packet->mutable_view()->mutable_data();
encoded.Copy(&packet_view);
hci_->command_channel()->SendCommand(
std::move(packet),
[cb = std::move(cb), priority](auto id, const hci::EventPacket& event) mutable {
if (hci_is_error(event, WARN, "l2cap", "acl priority failed")) {
cb(fit::error());
return;
}
bt_log(DEBUG, "l2cap", "acl priority updated (priority: %#.8x)",
static_cast<uint32_t>(priority));
cb(fit::ok());
});
}
async_dispatcher_t* dispatcher_;
// Inspect hierarchy node representing the data domain.
inspect::Node node_;
// Handle to the underlying HCI transport.
fxl::WeakPtr<hci::Transport> hci_;
std::unique_ptr<ChannelManager> channel_manager_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Impl);
}; // namespace bt::l2cap
// static
fbl::RefPtr<L2cap> L2cap::Create(fxl::WeakPtr<hci::Transport> hci, bool random_channel_ids) {
ZX_DEBUG_ASSERT(hci);
return AdoptRef(new Impl(hci, random_channel_ids));
}
} // namespace bt::l2cap