blob: f9d3b4229df0a5b16faea31ab5b63bfe2ee0bf12 [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 "lib/fxl/functional/make_copyable.h"
#include "lib/fxl/logging.h"
#include "lib/fxl/strings/string_printf.h"
#include "logical_link.h"
namespace bluetooth {
namespace l2cap {
Channel::Channel(ChannelId id) : id_(id) {
FXL_DCHECK(id_);
}
namespace internal {
ChannelImpl::ChannelImpl(ChannelId id, internal::LogicalLink* link)
: Channel(id), tx_mtu_(kDefaultMTU), rx_mtu_(kDefaultMTU), link_(link) {
FXL_DCHECK(link_);
}
ChannelImpl::~ChannelImpl() {
FXL_DCHECK(IsCreationThreadCurrent());
// Cancel all unprocessed SDU tasks before acquiring |mtx_|.
send_sdu_task_factory_.CancelAll();
std::lock_guard<std::mutex> lock(mtx_);
if (link_)
link_->RemoveChannel(this);
}
bool ChannelImpl::Send(std::unique_ptr<const common::ByteBuffer> sdu) {
FXL_DCHECK(sdu);
if (sdu->size() > tx_mtu_) {
FXL_VLOG(1) << fxl::StringPrintf(
"l2cap: SDU size exceeds channel TxMTU (channel-id: 0x%04x)", id());
return false;
}
std::lock_guard<std::mutex> lock(mtx_);
if (!link_) {
FXL_LOG(ERROR) << "l2cap: Cannot send SDU on a closed link";
return false;
}
link_->io_task_runner()->PostTask(send_sdu_task_factory_.MakeTask(
fxl::MakeCopyable([ this, sdu = std::move(sdu) ] {
std::lock_guard<std::mutex> lock(mtx_);
// Check if the link was closed in the mean time.
if (!link_) {
FXL_LOG(ERROR) << "l2cap: Cannot send SDU on a closed link";
return;
}
// TODO(armansito): Since we only support Basic Mode we send the SDU out
// right away. This is the point where a channel mode implementation
// should take over.
link_->SendBasicFrame(id(), *sdu);
})));
return true;
}
void ChannelImpl::SetRxHandler(const RxCallback& rx_cb,
fxl::RefPtr<fxl::TaskRunner> rx_task_runner) {
FXL_DCHECK(IsCreationThreadCurrent());
FXL_DCHECK(static_cast<bool>(rx_cb) == static_cast<bool>(rx_task_runner));
std::lock_guard<std::mutex> lock(mtx_);
// TODO(armansito): Consider wrapping |rx_cb| around a
// common::CancelableCallback to make it cancelable when a new |rx_cb| is set.
// This would guarantee that a previously set callback will never run after
// this method returns and the packet carried by the canceled |rx_cb| would be
// dropped.
rx_cb_ = rx_cb;
rx_task_runner_ = rx_task_runner;
if (link_ && rx_cb_) {
while (!pending_rx_sdus_.empty()) {
auto cb = fxl::MakeCopyable([
cb = rx_cb_, sdu = std::move(pending_rx_sdus_.front())
] { cb(sdu); });
pending_rx_sdus_.pop();
rx_task_runner_->PostTask(cb);
}
}
}
void ChannelImpl::OnLinkClosed() {
FXL_DCHECK(IsCreationThreadCurrent());
ClosedCallback cb;
// Cancel all unprocessed SDU tasks before acquiring |mtx_|.
send_sdu_task_factory_.CancelAll();
{
std::lock_guard<std::mutex> lock(mtx_);
FXL_DCHECK(link_);
link_ = nullptr;
// Drop any previously buffered SDUs.
pending_rx_sdus_ = {};
if (!closed_callback())
return;
// We'll invoke the callback synchronously. We copy the callback here and
// invoke it outside of this block to prevent a potential deadlock.
cb = closed_callback();
}
cb();
}
void ChannelImpl::HandleRxPdu(PDU&& pdu) {
// Data is always received on the HCI I/O thread which is assumed to be
// different from this Channel's creation thread.
FXL_DCHECK(!IsCreationThreadCurrent());
// TODO(armansito): This is the point where the channel mode implementation
// should take over the PDU. Since we only support basic mode: SDU == PDU.
std::lock_guard<std::mutex> lock(mtx_);
FXL_DCHECK(link_);
FXL_DCHECK(link_->io_task_runner()->RunsTasksOnCurrentThread());
if (!rx_cb_) {
pending_rx_sdus_.emplace(std::forward<PDU>(pdu));
return;
}
FXL_DCHECK(rx_task_runner_);
rx_task_runner_->PostTask(
fxl::MakeCopyable([ cb = rx_cb_, pdu = std::move(pdu) ] { cb(pdu); }));
}
} // namespace internal
} // namespace l2cap
} // namespace bluetooth