blob: 8a56dfbb7be9aedc88b61fc69308b2ee143a8112 [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 <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include "garnet/drivers/bluetooth/lib/l2cap/l2cap.h"
#include "garnet/drivers/bluetooth/lib/rfcomm/channel_manager.h"
#include "garnet/drivers/bluetooth/lib/rfcomm/session.h"
namespace btlib {
namespace rfcomm {
std::unique_ptr<ChannelManager> ChannelManager::Create(l2cap::L2CAP* l2cap) {
FXL_DCHECK(l2cap);
// ChannelManager constructor private; can't use make_unique.
auto channel_manager =
std::unique_ptr<ChannelManager>(new ChannelManager(l2cap));
if (l2cap->RegisterService(
l2cap::kRFCOMM,
[cm = channel_manager->weak_ptr_factory_.GetWeakPtr()](
auto l2cap_channel) {
if (cm) {
if (cm->RegisterL2CAPChannel(l2cap_channel)) {
FXL_LOG(INFO)
<< "rfcomm: Registered incoming channel with handle "
<< l2cap_channel->link_handle();
} else {
FXL_LOG(WARNING)
<< "rfcomm: Failed to register incoming channel with"
<< " handle " << l2cap_channel->link_handle();
}
}
},
async_get_default_dispatcher())) {
return channel_manager;
}
return nullptr;
}
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()) {
FXL_LOG(WARNING) << "Handle " << handle << " already registered";
return false;
}
auto session = Session::Create(
l2cap_channel, fit::bind_member(this, &ChannelManager::ChannelOpened));
if (!session) {
FXL_LOG(ERROR) << "Couldn't start a 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) {
FXL_DCHECK(channel_opened_cb);
FXL_DCHECK(dispatcher);
auto session_it = handle_to_session_.find(handle);
if (session_it == handle_to_session_.end()) {
l2cap_->OpenChannel(
handle, l2cap::kRFCOMM,
[this, handle, server_channel, dispatcher,
cb = std::move(channel_opened_cb)](auto l2cap_channel) mutable {
if (!l2cap_channel) {
FXL_LOG(ERROR) << "rfcomm: Failed to open L2CAP channel with"
<< " handle " << handle;
async::PostTask(dispatcher, [cb_ = std::move(cb)] {
cb_(nullptr, kInvalidServerChannel);
});
return;
}
FXL_LOG(INFO) << "rfcomm: opened L2CAP session with handle "
<< handle;
FXL_DCHECK(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);
});
},
dispatcher_);
return;
}
FXL_DCHECK(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;
}
ChannelManager::ChannelManager(l2cap::L2CAP* l2cap)
: dispatcher_(async_get_default_dispatcher()),
l2cap_(l2cap),
weak_ptr_factory_(this) {
FXL_DCHECK(l2cap_);
}
void ChannelManager::ChannelOpened(fbl::RefPtr<Channel> rfcomm_channel,
ServerChannel server_channel) {
auto server_channel_it = server_channels_.find(server_channel);
FXL_DCHECK(server_channel_it != server_channels_.end())
<< "rfcomm: 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