blob: d96613c81180058c23f6377d0d0c3be41433a43c [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 "channel_manager.h"
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include "garnet/drivers/bluetooth/lib/common/log.h"
#include "garnet/drivers/bluetooth/lib/l2cap/l2cap.h"
#include "session.h"
namespace btlib {
namespace rfcomm {
ChannelManager::ChannelManager(OnL2capConnectionRequest l2cap_delegate)
: dispatcher_(async_get_default_dispatcher()),
l2cap_delegate_(std::move(l2cap_delegate)),
weak_ptr_factory_(this) {
ZX_DEBUG_ASSERT(l2cap_delegate_);
}
bool ChannelManager::RegisterL2CAPChannel(
fbl::RefPtr<l2cap::Channel> l2cap_channel) {
auto handle = l2cap_channel->link_handle();
if (handle_to_session_.find(handle) != handle_to_session_.end()) {
bt_log(WARN, "rfcomm",
"L2CAP channel for link (handle: %#.4x) already registered", handle);
return false;
}
auto session = Session::Create(
l2cap_channel, fit::bind_member(this, &ChannelManager::ChannelOpened));
if (!session) {
bt_log(ERROR, "rfcomm",
"could not start session on the given L2CAP channel");
return false;
}
handle_to_session_[handle] = std::move(session);
return true;
}
void ChannelManager::OpenRemoteChannel(hci::ConnectionHandle handle,
ServerChannel server_channel,
ChannelOpenedCallback channel_opened_cb,
async_dispatcher_t* dispatcher) {
ZX_DEBUG_ASSERT(channel_opened_cb);
ZX_DEBUG_ASSERT(dispatcher);
auto session_it = handle_to_session_.find(handle);
if (session_it == handle_to_session_.end()) {
l2cap_delegate_(
handle,
[this, handle, server_channel, dispatcher,
cb = std::move(channel_opened_cb)](auto l2cap_channel) mutable {
if (!l2cap_channel) {
bt_log(ERROR, "rfcomm",
"failed to open L2CAP channel with handle %#.4x", handle);
async::PostTask(dispatcher, [cb_ = std::move(cb)] {
cb_(nullptr, kInvalidServerChannel);
});
return;
}
bt_log(INFO, "rfcomm", "opened L2CAP session with handle %#.4x",
handle);
ZX_DEBUG_ASSERT(handle_to_session_.find(handle) ==
handle_to_session_.end());
handle_to_session_.emplace(
handle,
Session::Create(
l2cap_channel,
fbl::BindMember(this, &ChannelManager::ChannelOpened)));
// Re-run OpenRemoteChannel now that the session is opened.
async::PostTask(dispatcher_,
[this, handle, server_channel, dispatcher,
cb_ = std::move(cb)]() mutable {
OpenRemoteChannel(handle, server_channel,
std::move(cb_), dispatcher);
});
});
return;
}
ZX_DEBUG_ASSERT(session_it != handle_to_session_.end());
session_it->second->OpenRemoteChannel(
server_channel, [cb = std::move(channel_opened_cb), dispatcher](
auto rfcomm_channel, auto server_channel) mutable {
async::PostTask(dispatcher, [cb_ = std::move(cb), rfcomm_channel,
server_channel]() {
cb_(rfcomm_channel, server_channel);
});
});
}
ServerChannel ChannelManager::AllocateLocalChannel(
ChannelOpenedCallback cb, async_dispatcher_t* dispatcher) {
// Find the first free Server Channel and allocate it.
for (ServerChannel server_channel = kMinServerChannel;
server_channel <= kMaxServerChannel; ++server_channel) {
if (server_channels_.find(server_channel) == server_channels_.end()) {
server_channels_[server_channel] =
std::make_pair(std::move(cb), dispatcher);
return server_channel;
}
}
return kInvalidServerChannel;
}
void ChannelManager::ChannelOpened(fbl::RefPtr<Channel> rfcomm_channel,
ServerChannel server_channel) {
auto server_channel_it = server_channels_.find(server_channel);
ZX_DEBUG_ASSERT_MSG(server_channel_it != server_channels_.end(),
"new channel created on an unallocated Server Channel");
async::PostTask(server_channel_it->second.second,
[server_channel, rfcomm_channel,
cb = server_channel_it->second.first.share()]() {
cb(rfcomm_channel, server_channel);
});
}
} // namespace rfcomm
} // namespace btlib