blob: bbed2aecf00bc04c07cba3c7579b2d20d2c0758f [file] [log] [blame]
// Copyright 2017 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 "channel.h"
#include <zircon/assert.h>
#include "logical_link.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/common/run_or_post.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/basic_mode_rx_engine.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/basic_mode_tx_engine.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace bt {
namespace l2cap {
Channel::Channel(ChannelId id, ChannelId remote_id,
hci::Connection::LinkType link_type,
hci::ConnectionHandle link_handle)
: id_(id),
remote_id_(remote_id),
link_type_(link_type),
link_handle_(link_handle),
// TODO(armansito): IWBN if the MTUs could be specified dynamically
// instead (see NET-308).
tx_mtu_(kDefaultMTU),
rx_mtu_(kDefaultMTU) {
ZX_DEBUG_ASSERT(id_);
ZX_DEBUG_ASSERT(link_type_ == hci::Connection::LinkType::kLE ||
link_type_ == hci::Connection::LinkType::kACL);
}
namespace internal {
ChannelImpl::ChannelImpl(ChannelId id, ChannelId remote_id,
fbl::RefPtr<internal::LogicalLink> link,
std::list<PDU> buffered_pdus)
: Channel(id, remote_id, link->type(), link->handle()),
active_(false),
dispatcher_(nullptr),
link_(link),
rx_engine_(std::make_unique<BasicModeRxEngine>()),
tx_engine_(std::make_unique<BasicModeTxEngine>(
id, tx_mtu_, [rid = remote_id, link = link_](auto pdu) {
async::PostTask(link->dispatcher(), [=, pdu = std::move(pdu)] {
// |link| is expected to ignore this call and drop the
// packet if it has been closed.
link->SendBasicFrame(rid, *pdu);
});
})) {
ZX_DEBUG_ASSERT(link_);
for (const auto& pdu : buffered_pdus) {
auto sdu = std::make_unique<DynamicByteBuffer>(pdu.length());
pdu.Copy(sdu.get());
pending_rx_sdus_.push(std::move(sdu));
}
}
const sm::SecurityProperties ChannelImpl::security() {
std::lock_guard<std::mutex> lock(mtx_);
if (link_) {
return link_->security();
}
return sm::SecurityProperties();
}
bool ChannelImpl::Activate(RxCallback rx_callback,
ClosedCallback closed_callback,
async_dispatcher_t* dispatcher) {
ZX_DEBUG_ASSERT(rx_callback);
ZX_DEBUG_ASSERT(closed_callback);
fit::closure task;
bool run_task = false;
{
std::lock_guard<std::mutex> lock(mtx_);
// Activating on a closed link has no effect. We also clear this on
// deactivation to prevent a channel from being activated more than once.
if (!link_)
return false;
ZX_DEBUG_ASSERT(!active_);
active_ = true;
ZX_DEBUG_ASSERT(!dispatcher_);
dispatcher_ = dispatcher;
rx_cb_ = std::move(rx_callback);
closed_cb_ = std::move(closed_callback);
// Route the buffered packets.
if (!pending_rx_sdus_.empty()) {
run_task = true;
dispatcher = dispatcher_;
task = [func = rx_cb_.share(),
pending = std::move(pending_rx_sdus_)]() mutable {
while (!pending.empty()) {
func(std::move(pending.front()));
pending.pop();
}
};
ZX_DEBUG_ASSERT(pending_rx_sdus_.empty());
}
}
if (run_task) {
RunOrPost(std::move(task), dispatcher);
}
return true;
}
void ChannelImpl::Deactivate() {
std::lock_guard<std::mutex> lock(mtx_);
// De-activating on a closed link has no effect.
if (!link_ || !active_) {
link_ = nullptr;
return;
}
active_ = false;
dispatcher_ = nullptr;
rx_cb_ = {};
closed_cb_ = {};
rx_engine_ = {};
tx_engine_ = {};
// Tell the link to release this channel on its thread.
async::PostTask(link_->dispatcher(), [this, link = link_] {
// |link| is expected to ignore this call if it has been closed.
link->RemoveChannel(this);
});
link_ = nullptr;
}
void ChannelImpl::SignalLinkError() {
std::lock_guard<std::mutex> lock(mtx_);
// Cannot signal an error on a closed or deactivated link.
if (!link_ || !active_)
return;
async::PostTask(link_->dispatcher(), [link = link_] {
// |link| is expected to ignore this call if it has been closed.
link->SignalError();
});
}
bool ChannelImpl::Send(ByteBufferPtr sdu) {
ZX_DEBUG_ASSERT(sdu);
std::lock_guard<std::mutex> lock(mtx_);
if (!link_) {
bt_log(ERROR, "l2cap", "cannot send SDU on a closed link");
return false;
}
// Drop the packet if the channel is inactive.
if (!active_)
return false;
return tx_engine_->QueueSdu(std::move(sdu));
}
void ChannelImpl::UpgradeSecurity(sm::SecurityLevel level,
sm::StatusCallback callback) {
ZX_DEBUG_ASSERT(callback);
std::lock_guard<std::mutex> lock(mtx_);
if (!link_ || !active_) {
bt_log(TRACE, "l2cap", "Ignoring security request on inactive channel");
return;
}
ZX_DEBUG_ASSERT(dispatcher_);
async::PostTask(
link_->dispatcher(), [link = link_, level, callback = std::move(callback),
dispatcher = dispatcher_]() mutable {
link->UpgradeSecurity(level, std::move(callback), dispatcher);
});
}
void ChannelImpl::OnClosed() {
async_dispatcher_t* dispatcher;
fit::closure task;
{
std::lock_guard<std::mutex> lock(mtx_);
if (!link_ || !active_) {
link_ = nullptr;
return;
}
ZX_DEBUG_ASSERT(closed_cb_);
dispatcher = dispatcher_;
task = std::move(closed_cb_);
active_ = false;
link_ = nullptr;
dispatcher_ = nullptr;
}
RunOrPost(std::move(task), dispatcher);
}
void ChannelImpl::HandleRxPdu(PDU&& pdu) {
async_dispatcher_t* dispatcher;
fit::closure task;
{
std::lock_guard<std::mutex> lock(mtx_);
// This will only be called on a live link.
ZX_DEBUG_ASSERT(link_);
ZX_DEBUG_ASSERT(rx_engine_);
ByteBufferPtr sdu = rx_engine_->ProcessPdu(std::move(pdu));
if (!sdu) {
// The PDU may have been invalid, out-of-sequence, or part of a segmented
// SDU.
// * If invalid, we drop the PDU (per Core Spec Ver 5, Vol 3, Part A,
// Secs. 3.3.6 and/or 3.3.7).
// * If out-of-sequence or part of a segmented SDU, we expect that some
// later call to ProcessPdu() will return us an SDU containing this
// PDU's data.
return;
}
// Buffer the packets if the channel hasn't been activated.
if (!active_) {
pending_rx_sdus_.emplace(std::move(sdu));
return;
}
dispatcher = dispatcher_;
task = [func = rx_cb_.share(), sdu = std::move(sdu)]() mutable {
func(std::move(sdu));
};
ZX_DEBUG_ASSERT(rx_cb_);
}
RunOrPost(std::move(task), dispatcher);
}
} // namespace internal
} // namespace l2cap
} // namespace bt