blob: ddf41408494cab19393bc6153c8b2730d78128e9 [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 <fbl/function.h>
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap.h"
#include "session.h"
namespace bt {
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 bt