| // 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_manager.h" |
| |
| #include <zircon/assert.h> |
| |
| #include <trace/event.h> |
| |
| #include "logical_link.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/log.h" |
| #include "src/lib/fxl/strings/string_printf.h" |
| |
| namespace bt { |
| namespace l2cap { |
| |
| ChannelManager::ChannelManager(size_t max_acl_payload_size, size_t max_le_payload_size, |
| SendAclCallback send_acl_cb, |
| DropQueuedAclCallback drop_queued_acl_cb, |
| async_dispatcher_t* l2cap_dispatcher) |
| : max_acl_payload_size_(max_acl_payload_size), |
| max_le_payload_size_(max_le_payload_size), |
| send_acl_cb_(std::move(send_acl_cb)), |
| drop_queued_acl_cb_(std::move(drop_queued_acl_cb)), |
| l2cap_dispatcher_(l2cap_dispatcher), |
| weak_ptr_factory_(this) { |
| ZX_ASSERT(send_acl_cb_); |
| ZX_ASSERT(drop_queued_acl_cb_); |
| ZX_ASSERT(l2cap_dispatcher_); |
| } |
| |
| ChannelManager::~ChannelManager() { |
| ZX_DEBUG_ASSERT(thread_checker_.IsCreationThreadCurrent()); |
| |
| // Explicitly shut down all links to force associated L2CAP channels to |
| // release their strong references. |
| for (auto& [handle, link] : ll_map_) { |
| link->Close(); |
| } |
| } |
| |
| hci::ACLPacketHandler ChannelManager::MakeInboundDataHandler() { |
| return [self = weak_ptr_factory_.GetWeakPtr()](auto packet) { |
| if (self) { |
| self->OnACLDataReceived(std::move(packet)); |
| } |
| }; |
| } |
| |
| void ChannelManager::RegisterACL(hci::ConnectionHandle handle, hci::Connection::Role role, |
| LinkErrorCallback link_error_cb, |
| SecurityUpgradeCallback security_cb) { |
| ZX_DEBUG_ASSERT(thread_checker_.IsCreationThreadCurrent()); |
| bt_log(TRACE, "l2cap", "register ACL link (handle: %#.4x)", handle); |
| |
| auto* ll = RegisterInternal(handle, hci::Connection::LinkType::kACL, role, max_acl_payload_size_); |
| ll->set_error_callback(std::move(link_error_cb)); |
| ll->set_security_upgrade_callback(std::move(security_cb)); |
| } |
| |
| void ChannelManager::RegisterLE(hci::ConnectionHandle handle, hci::Connection::Role role, |
| LEConnectionParameterUpdateCallback conn_param_cb, |
| LinkErrorCallback link_error_cb, |
| SecurityUpgradeCallback security_cb) { |
| ZX_DEBUG_ASSERT(thread_checker_.IsCreationThreadCurrent()); |
| bt_log(TRACE, "l2cap", "register LE link (handle: %#.4x)", handle); |
| |
| auto* ll = RegisterInternal(handle, hci::Connection::LinkType::kLE, role, max_le_payload_size_); |
| ll->set_error_callback(std::move(link_error_cb)); |
| ll->set_security_upgrade_callback(std::move(security_cb)); |
| ll->set_connection_parameter_update_callback(std::move(conn_param_cb)); |
| } |
| |
| void ChannelManager::Unregister(hci::ConnectionHandle handle) { |
| ZX_DEBUG_ASSERT(thread_checker_.IsCreationThreadCurrent()); |
| |
| bt_log(TRACE, "l2cap", "unregister link (handle: %#.4x)", handle); |
| |
| pending_packets_.erase(handle); |
| auto iter = ll_map_.find(handle); |
| if (iter == ll_map_.end()) { |
| bt_log(TRACE, "l2cap", "attempt to unregister unknown link (handle: %#.4x)", handle); |
| return; |
| } |
| |
| // Explicitly shut down the link to force associated L2CAP channels to release |
| // their strong references. |
| iter->second->Close(); |
| ll_map_.erase(iter); |
| } |
| |
| void ChannelManager::AssignLinkSecurityProperties(hci::ConnectionHandle handle, |
| sm::SecurityProperties security) { |
| ZX_DEBUG_ASSERT(thread_checker_.IsCreationThreadCurrent()); |
| |
| bt_log(TRACE, "l2cap", "received new security properties (handle: %#.4x)", handle); |
| |
| auto iter = ll_map_.find(handle); |
| if (iter == ll_map_.end()) { |
| bt_log(TRACE, "l2cap", "ignoring new security properties on unknown link"); |
| return; |
| } |
| |
| iter->second->AssignSecurityProperties(security); |
| } |
| |
| fbl::RefPtr<Channel> ChannelManager::OpenFixedChannel(hci::ConnectionHandle handle, |
| ChannelId channel_id) { |
| ZX_DEBUG_ASSERT(thread_checker_.IsCreationThreadCurrent()); |
| |
| auto iter = ll_map_.find(handle); |
| if (iter == ll_map_.end()) { |
| bt_log(ERROR, "l2cap", "cannot open fixed channel on unknown connection handle: %#.4x", handle); |
| return nullptr; |
| } |
| |
| return iter->second->OpenFixedChannel(channel_id); |
| } |
| |
| void ChannelManager::OpenChannel(hci::ConnectionHandle handle, PSM psm, ChannelParameters params, |
| ChannelCallback cb, async_dispatcher_t* dispatcher) { |
| ZX_DEBUG_ASSERT(thread_checker_.IsCreationThreadCurrent()); |
| |
| auto iter = ll_map_.find(handle); |
| if (iter == ll_map_.end()) { |
| bt_log(ERROR, "l2cap", "Cannot open channel on unknown connection handle: %#.4x", handle); |
| async::PostTask(dispatcher, [cb = std::move(cb)] { cb(nullptr); }); |
| return; |
| } |
| |
| iter->second->OpenChannel(psm, params, std::move(cb), dispatcher); |
| } |
| |
| bool ChannelManager::RegisterService(PSM psm, ChannelParameters params, ChannelCallback cb, |
| async_dispatcher_t* dispatcher) { |
| ZX_DEBUG_ASSERT(thread_checker_.IsCreationThreadCurrent()); |
| |
| // v5.0 Vol 3, Part A, Sec 4.2: PSMs shall be odd and the least significant |
| // bit of the most significant byte shall be zero |
| if (((psm & 0x0001) != 0x0001) || ((psm & 0x0100) != 0x0000)) { |
| return false; |
| } |
| |
| auto iter = services_.find(psm); |
| if (iter != services_.end()) { |
| return false; |
| } |
| |
| // Bind |dispatcher| in callback that forwards the created channel to the |
| // service provider. |
| ChannelCallback pass_channel = [this, dispatcher, |
| cb = std::move(cb)](fbl::RefPtr<Channel> chan) mutable { |
| ZX_DEBUG_ASSERT(thread_checker_.IsCreationThreadCurrent()); |
| |
| // Do not transfer ownership of |cb| when passing a channel to L2CAP. |
| // |chan| is safe to move because its lifetime spans each invocation. |
| async::PostTask(dispatcher, [cb = cb.share(), chan = std::move(chan)] { cb(std::move(chan)); }); |
| }; |
| |
| services_.emplace(psm, ServiceInfo(params, std::move(pass_channel))); |
| return true; |
| } |
| |
| void ChannelManager::UnregisterService(PSM psm) { |
| FX_DCHECK(thread_checker_.IsCreationThreadCurrent()); |
| |
| services_.erase(psm); |
| } |
| |
| void ChannelManager::RequestConnectionParameterUpdate( |
| hci::ConnectionHandle handle, hci::LEPreferredConnectionParameters params, |
| ConnectionParameterUpdateRequestCallback request_cb, async_dispatcher_t* dispatcher) { |
| ZX_ASSERT(thread_checker_.IsCreationThreadCurrent()); |
| |
| auto iter = ll_map_.find(handle); |
| if (iter == ll_map_.end()) { |
| bt_log(TRACE, "l2cap", "ignoring Connection Parameter Update request on unknown link"); |
| return; |
| } |
| |
| iter->second->SendConnectionParameterUpdateRequest(params, std::move(request_cb), dispatcher); |
| } |
| |
| // Called when an ACL data packet is received from the controller. This method |
| // is responsible for routing the packet to the corresponding LogicalLink. |
| void ChannelManager::OnACLDataReceived(hci::ACLDataPacketPtr packet) { |
| ZX_DEBUG_ASSERT(thread_checker_.IsCreationThreadCurrent()); |
| |
| auto handle = packet->connection_handle(); |
| TRACE_DURATION("bluetooth", "ChannelManager::OnDataReceived", "handle", handle); |
| |
| auto iter = ll_map_.find(handle); |
| PendingPacketMap::iterator pp_iter; |
| |
| // If a LogicalLink does not exist, we set up a queue for its packets to be |
| // delivered when the LogicalLink gets created. |
| if (iter == ll_map_.end()) { |
| pp_iter = pending_packets_.emplace(handle, LinkedList<hci::ACLDataPacket>()).first; |
| } else { |
| // A logical link exists. |pp_iter| will be valid only if the drain task has |
| // not run yet (see ChannelManager::RegisterInternal()). |
| pp_iter = pending_packets_.find(handle); |
| } |
| |
| if (pp_iter != pending_packets_.end()) { |
| packet->set_trace_id(TRACE_NONCE()); |
| TRACE_FLOW_BEGIN("bluetooth", "ChannelMaager::OnDataReceived queued", packet->trace_id()); |
| pp_iter->second.push_back(std::move(packet)); |
| bt_log(SPEW, "l2cap", "queued rx packet on handle: %#.4x", handle); |
| return; |
| } |
| |
| iter->second->HandleRxPacket(std::move(packet)); |
| } |
| |
| internal::LogicalLink* ChannelManager::RegisterInternal(hci::ConnectionHandle handle, |
| hci::Connection::LinkType ll_type, |
| hci::Connection::Role role, |
| size_t max_payload_size) { |
| ZX_DEBUG_ASSERT(thread_checker_.IsCreationThreadCurrent()); |
| TRACE_DURATION("bluetooth", "ChannelManager::RegisterInternal", "handle", handle); |
| |
| // TODO(armansito): Return nullptr instead of asserting. Callers shouldn't |
| // assume this will succeed. |
| auto iter = ll_map_.find(handle); |
| ZX_DEBUG_ASSERT_MSG(iter == ll_map_.end(), "connection handle re-used! (handle=%#.4x)", handle); |
| |
| auto send_acl_cb = [this](auto packets, ChannelId channel_id) { |
| return send_acl_cb_(std::move(packets), channel_id, |
| ChannelManager::ChannelPriority(channel_id)); |
| }; |
| |
| auto ll = internal::LogicalLink::New(handle, ll_type, role, l2cap_dispatcher_, max_payload_size, |
| std::move(send_acl_cb), drop_queued_acl_cb_.share(), |
| fit::bind_member(this, &ChannelManager::QueryService)); |
| |
| // Route all pending packets to the link. |
| auto pp_iter = pending_packets_.find(handle); |
| if (pp_iter != pending_packets_.end()) { |
| auto& packets = pp_iter->second; |
| while (!packets.is_empty()) { |
| auto packet = packets.pop_front(); |
| TRACE_FLOW_END("bluetooth", "ChannelManager::OnDataReceived queued", packet->trace_id()); |
| ll->HandleRxPacket(std::move(packet)); |
| } |
| pending_packets_.erase(pp_iter); |
| } |
| |
| auto* ll_raw = ll.get(); |
| ll_map_[handle] = std::move(ll); |
| |
| return ll_raw; |
| } |
| |
| std::optional<ChannelManager::ServiceInfo> ChannelManager::QueryService( |
| hci::ConnectionHandle handle, PSM psm) { |
| auto iter = services_.find(psm); |
| if (iter == services_.end()) { |
| return std::nullopt; |
| } |
| |
| // |channel_cb| will be called in LogicalLink. Each callback in |services_| already trampolines to |
| // the appropriate dispatcher (passed to RegisterService). |
| return ChannelManager::ServiceInfo(iter->second.channel_params, iter->second.channel_cb.share()); |
| } |
| |
| hci::ACLDataChannel::PacketPriority ChannelManager::ChannelPriority(ChannelId id) { |
| switch (id) { |
| case kSignalingChannelId: |
| case kLESignalingChannelId: |
| case kSMPChannelId: |
| case kLESMPChannelId: |
| return hci::ACLDataChannel::PacketPriority::kHigh; |
| default: |
| return hci::ACLDataChannel::PacketPriority::kLow; |
| } |
| } |
| |
| } // namespace l2cap |
| } // namespace bt |