1 // Copyright 2023 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 15 #pragma once 16 #include <lib/fit/function.h> 17 18 #include <unordered_map> 19 20 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h" 21 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h" 22 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h" 23 #include "pw_bluetooth_sapphire/internal/host/testing/fake_dynamic_channel.h" 24 25 namespace bt::testing { 26 27 // This class unpacks data units received from ACL-U and LE-U logical links 28 // into L2CAP SDUs and then routes them to indvidually-registered handler 29 // functions. Each FakePeer should have its own FakeL2cap instance and its 30 // own set of ACL-U and LE-U logical links. 31 // This class also manages the dynamic L2CAP channels, stored in 32 // FakeDynamicChannel objects. 33 // Generally, dynamic channels are created by means of ConnectionRequest packets 34 // sent over the fixed signaling channel, but FakeL2cap actually holds the 35 // channels created and modified by those requests. 36 // Services can register with FakeL2cap and then assign callbacks to individual 37 // FakeDynamicChannels (based on their PSMs), but those services do not own and 38 // cannot directly delete or create new channels without using the FakeL2cap 39 // instance managing those dynamic channels. 40 class FakeL2cap final { 41 public: 42 // Entities that instantiate FakeL2cap must provide a 43 // SendFrameCallback function to handle adding necessary protocol data unit 44 // header information to the packet and actually sending the packet using 45 // the associated device. 46 using SendFrameCallback = fit::function<void(hci_spec::ConnectionHandle conn, 47 l2cap::ChannelId cid, 48 const ByteBuffer& pdu)>; 49 50 // After registering a channel with RegisterHandler, ChannelReceiveCallback 51 // is called with a received L2CAP Service Data Unit |sdu| when FakeL2cap 52 // handles a packet for the registered channel. Includes the |handle| that 53 // the L2CAP packet was received on. 54 using ChannelReceiveCallback = fit::function<void( 55 hci_spec::ConnectionHandle handle, const ByteBuffer& sdu)>; 56 57 // When FakeL2cap receives a packet with a ChannelID that does not have a 58 // registered handler, it calls UnexpectedPduCallback (set via the 59 // constructor or defaulted to a no-op). To aid with debugging, this callback 60 // takes the entire Protocol Data Unit |pdu| (including the intact L2CAP 61 // header). Also includes the |handle| that the L2CAP packet was received on. 62 using UnexpectedPduCallback = fit::function<void( 63 hci_spec::ConnectionHandle handle, const ByteBuffer& pdu)>; 64 65 // Each service that registers itself on a particular PSM provides a callback 66 // that takes a pointer to the FakeDynamicChannel with its 67 using FakeDynamicChannelCallback = 68 fit::function<void(FakeDynamicChannel::WeakPtr)>; 69 70 // Calls |send_frame_callback| to send packets through the device 71 // instantiating the FakeL2cap instance. 72 // Calls |unexpected_pdu_callback| for packets received that don't have a 73 // handler registered. Defaults to a no-op if no callback provided. 74 // Assumes the largest possible dynamic channel ID is 75 // l2cap::kLastACLDynamicChannelId. 76 explicit FakeL2cap( 77 SendFrameCallback send_frame_callback, 78 UnexpectedPduCallback unexpected_pdu_callback = [](auto, auto&) {}, 79 l2cap::ChannelId largest_channel_id = l2cap::kLastACLDynamicChannelId); 80 81 // Public methods for services/clients that operate over L2CAP channels: 82 // TODO(fxbug.dev/42135392) Add Unit tests for public methods 83 84 // Register |callback| to be called when a Service Data Unit (SDU) is 85 // received on an L2CAP channel identified by |cid|. |callback| will be 86 // retained until overwritten by another RegisterHandler call or this 87 // class is destroyed. To remove a specific |callback|, overwrite it by 88 // registering a no-op (or other handler) on the corresponding |cid|. 89 // This should only be used for registering fixed channel handlers - use 90 // RegisterDynamicChannel for dynamic channel management. 91 void RegisterHandler(l2cap::ChannelId cid, ChannelReceiveCallback callback); 92 93 // Register a |callback| function that configures a FakeDynamicChannel which 94 // operates over a Protocol Service Multiplexer |psm|. When 95 // RegisterDynamicChannelWithPsm() will call this FakeDynamicChannelCallback 96 // if the channel is open. 97 void RegisterService(l2cap::Psm psm, FakeDynamicChannelCallback callback); 98 99 // The following methods are generally for use by the FakeSignalingServer when 100 // creating and managing individual FakeDynamicChannel instances. 101 102 // Register a new FakeDynamicChannel with an associated connection handle 103 // |conn|, Protocol Service Multiplexer PSM, remote Channel ID 104 // |remote_cid|, and local Channel ID |local_cid|. 105 // This channel will be closed, as this function only allocates 106 // a local ID for the channel. This function also does not register the 107 // channel with the service associated with the channel's PSM, as this 108 // happens after the channel is open. 109 // Return status of if the registration was successful. 110 // This should only be used for registering dynamic channels - use 111 // RegisterHandler for fixed channel management. 112 bool RegisterDynamicChannel(hci_spec::ConnectionHandle conn, 113 l2cap::Psm psm, 114 l2cap::ChannelId local_cid, 115 l2cap::ChannelId remote_cid); 116 117 // Call the DynamicChannelCallback associated with the service operating 118 // on the channel's PSM once this channel has been opened. 119 // Applications should only call this function after confirming that the 120 // channel associated with the connection handle |conn| and local channel ID 121 // |local_cid| is already open, as the DynamicChannelCallback associated with 122 // the PSM will assume that it can operate over the channel. 123 // Return status of if the registration was successful. 124 bool RegisterDynamicChannelWithPsm(hci_spec::ConnectionHandle conn, 125 l2cap::ChannelId local_cid); 126 127 // Return true if there is a FakeDynamicChannelCallback registered for 128 // Protocol Service Multiplexer |psm|, return false otherwise. 129 bool ServiceRegisteredForPsm(l2cap::Psm psm); 130 131 // Methods to observe and manipulate L2CAP channel states: 132 133 // Return the first local dynamic Channel Id that does not already have a 134 // FakeDynamicChannel object associated with it. This incrementally checks 135 // each individual ID starting from l2cap::kFirstDynamicChannelId up to this 136 // FakeL2cap instance's largest_channel_id_, so runtime increases linearly 137 // with the number of consecutive registered channels but can be limited by 138 // limiting the value of largest_channel_id_ when initializing FakeL2cap. If 139 // there are no available Channel IDs, returns l2cap::kInvalidChannelId. 140 // Requires identification of the specific ConnectionHandle |conn| associated 141 // with this connection. 142 l2cap::ChannelId FindAvailableDynamicChannelId( 143 hci_spec::ConnectionHandle conn); 144 145 // Find the FakeDynamicChannel object with the local channel ID |local_cid| 146 // and connection handle |conn| in the dynamic_channels_ map. If there is no 147 // channel registered with the |local_cid|, return a nullptr. 148 FakeDynamicChannel::WeakPtr FindDynamicChannelByLocalId( 149 hci_spec::ConnectionHandle conn, l2cap::ChannelId local_cid); 150 151 // Find the FakeDynamicChannel object with the connection handle |conn| and 152 // local channel ID |remote_cid| in the dynamic_channels_ map. If there is 153 // no channel registered with the |remote_cid|, return a nullptr. 154 FakeDynamicChannel::WeakPtr FindDynamicChannelByRemoteId( 155 hci_spec::ConnectionHandle conn, l2cap::ChannelId remote_cid); 156 157 // Remove a dynamic channel associated with the connection handle |conn| and 158 // locassigned |local_cid|. Will call the channel's ChannelClosedCallback if 159 // it has one, set the channel to closed, and then delete the value from the 160 // map FakeL2cap uses to store the channels (which should also destroy the 161 // channel itself), This is the only way that dynamic channels should be 162 // deleted - if they are torn down individually, FakeL2cap will incorrectly 163 // hold that local_cid. 164 void DeleteDynamicChannelByLocalId(hci_spec::ConnectionHandle conn, 165 l2cap::ChannelId local_cid); 166 167 // Routes the |pdu| to the appropriate calllback function by extracting the 168 // ChannelID of the received packet |pdu| and calling the corresponding 169 // registered handler function (and providing it with the |handle| the packet 170 // was received on and the payload Service Data Unit |sdu|. 171 void HandlePdu(hci_spec::ConnectionHandle conn, const ByteBuffer& pdu); 172 173 // Return the SendFrameCallback associated with this FakeL2cap instance. send_frame_callback()174 SendFrameCallback& send_frame_callback() { return send_frame_callback_; } 175 176 private: 177 // Map of channel IDs and corresponding functions. Use an unordered map for 178 // constant-time (in the best case) for search/insertion/deletion when 179 // accessing calllbacks associated with specific channel IDs. 180 std::unordered_map<l2cap::ChannelId, ChannelReceiveCallback> callbacks_; 181 182 // Map of dynamically allocated Channel IDs and corresponding channels. Use 183 // an unordered map for constant-time search/insertion/deletion when 184 // accessing channels associated with specific channel IDs. 185 std::unordered_map< 186 hci_spec::ConnectionHandle, 187 std::unordered_map<l2cap::ChannelId, std::unique_ptr<FakeDynamicChannel>>> 188 dynamic_channels_; 189 190 // Map of individual channel configuration callbacks associated with 191 // individual services 192 std::unordered_map<l2cap::Psm, FakeDynamicChannelCallback> 193 registered_services_; 194 195 // Function provided by the device that instantiates the FakeL2cap instance 196 // that adds PDU header information and actually sends the packet. 197 SendFrameCallback send_frame_callback_; 198 199 // Handler function associated with unexpected PDUs. Defaults to a no-op. 200 UnexpectedPduCallback unexpected_pdu_callback_; 201 202 // Largest dynamic channel ID this FakeL2cap instance can allocate IDs for. 203 // Defaults to l2cap::kLastACLDynamicChannelId. 204 l2cap::ChannelId largest_channel_id_; 205 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(FakeL2cap); 206 }; 207 208 } // namespace bt::testing 209