| // 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 |