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/random.h" 21 #include "pw_bluetooth_sapphire/internal/host/common/weak_self.h" 22 #include "pw_bluetooth_sapphire/internal/host/l2cap/dynamic_channel.h" 23 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h" 24 #include "pw_bluetooth_sapphire/internal/host/l2cap/signaling_channel.h" 25 #include "pw_bluetooth_sapphire/internal/host/l2cap/types.h" 26 27 namespace bt::l2cap::internal { 28 29 // Base class for registries of dynamic L2CAP channels. It serves both as the 30 // factory and owner of dynamic channels created on a logical link, including 31 // assigning and tracking dynamic channel IDs. 32 // 33 // Registry entries are DynamicChannels that do not implement the l2cap::Channel 34 // interface used for user data transfer. 35 // 36 // This class is not thread-safe and is intended to be created and run on the 37 // L2CAP thread for each logical link connected. 38 class DynamicChannelRegistry : public WeakSelf<DynamicChannelRegistry> { 39 public: 40 // Used to pass an optional channel to clients of the registry. |channel| may 41 // be nullptr upon failure to open. Otherwise, it points to an instance owned 42 // by the registry and should not be retained by the callee. 43 using DynamicChannelCallback = 44 fit::function<void(const DynamicChannel* channel)>; 45 using ServiceInfo = ServiceInfo<DynamicChannelCallback>; 46 47 // Used to query the upper layers for the presence of a service that is 48 // accepting channels. If the service exists, it should return a callback 49 // that accepts the inbound dynamic channel opened. 50 using ServiceRequestCallback = 51 fit::function<std::optional<ServiceInfo>(Psm psm)>; 52 53 virtual ~DynamicChannelRegistry() = default; 54 55 // Create and connect a dynamic channel. The result will be returned by 56 // calling |open_cb| on the L2CAP thread the channel is ready for data 57 // transfer, with a nullptr if unsuccessful. The DynamicChannel passed will 58 // contain the local and remote channel IDs to be used for user data transfer 59 // over the new channel. Preferred channel parameters can be set in |params|. 60 void OpenOutbound(Psm psm, 61 ChannelParameters params, 62 DynamicChannelCallback open_cb); 63 64 // Disconnect and remove the channel identified by |local_cid|. After this 65 // call completes, incoming PDUs with |local_cid| should be discarded as in 66 // error or considered to belong to a subsequent channel with that ID. Any 67 // outbound PDUs passed to the Channel interface for this channel should be 68 // discarded. When the close operation completes, |close_callback| will be 69 // called, the internal channel will be destroyed, and |local_cid| may be 70 // recycled for another dynamic channel. |close_callback| will be called 71 // immediately if the channel doesn't exist. 72 void CloseChannel(ChannelId local_cid, fit::closure close_callback); 73 74 protected: 75 // |max_num_channels| is the number of dynamic channel IDs that can be 76 // allocated on this link, and must be non-zero and less than 65473. 77 // 78 // |close_cb| will be called upon a remote-initiated closure of an open 79 // channel. The registry's internal channel is passed as a parameter, and it 80 // will be closed for user data transfer before the callback fires. When the 81 // callback returns, the channel is destroyed and its ID may be recycled for 82 // another dynamic channel. Channels that fail to open due to error or are 83 // closed using CloseChannel will not trigger this callback. 84 // 85 // |service_request_cb| will be called upon remote-initiated channel requests. 86 // For services accepting channels, it shall return a callback to accept the 87 // opened channel, which only be called if the channel successfully opens. To 88 // deny the channel creation, |service_request_cb| should return a nullptr. 89 // 90 // If |random_channel_ids| is true then the channel IDs assigned will be 91 // randomized. Otherwise, they will be assigned starting at the lowest 92 // available dynamic channel id (for testing). 93 DynamicChannelRegistry(uint16_t max_num_channels, 94 DynamicChannelCallback close_cb, 95 ServiceRequestCallback service_request_cb, 96 bool random_channel_ids); 97 98 // Factory method for a DynamicChannel implementation that represents an 99 // outbound channel with an endpoint on this device identified by |local_cid|. 100 virtual DynamicChannelPtr MakeOutbound(Psm psm, 101 ChannelId local_cid, 102 ChannelParameters params) = 0; 103 104 // Factory method for a DynamicChannel implementation that represents an 105 // inbound channel from a remote endpoint identified by |remote_cid| to an 106 // endpoint on this device identified by |local_cid|. 107 virtual DynamicChannelPtr MakeInbound(Psm psm, 108 ChannelId local_cid, 109 ChannelId remote_cid, 110 ChannelParameters params) = 0; 111 112 // Open an inbound channel for a service |psm| from the remote endpoint 113 // identified by |remote_cid| to the local endpoint by |local_cid|. 114 DynamicChannel* RequestService(Psm psm, 115 ChannelId local_cid, 116 ChannelId remote_cid); 117 118 // In the range starting at kFirstDynamicChannelId with |max_num_channels_|, 119 // pick a dynamic channel ID that is available on this link. Returns 120 // kInvalidChannelId if all IDs have been exhausted. 121 ChannelId FindAvailableChannelId(); 122 123 // Return the number of alive channels on this link. 124 size_t AliveChannelCount() const; 125 126 // Returns null if not found. Can be downcast to the derived DynamicChannel 127 // created by MakeOutbound. 128 DynamicChannel* FindChannelByLocalId(ChannelId local_cid) const; 129 130 // Searches for alive dynamic channel with given remote channel id. 131 // Returns null if not found. 132 DynamicChannel* FindChannelByRemoteId(ChannelId remote_cid) const; 133 134 // Iterates over all channels, running |f| on each entry synchronously. 135 void ForEach(fit::function<void(DynamicChannel*)> f) const; 136 137 private: 138 friend class DynamicChannel; 139 140 // Open a newly-created channel. If |pass_failed| is true, always invoke 141 // |open_callback| with the result of the operation, including with nullptr if 142 // the channel failed to open. Otherwise if |pass_failed| is false, only 143 // invoke |open_callback| for successfully-opened channels. 144 void ActivateChannel(DynamicChannel* channel, 145 DynamicChannelCallback open_callback, 146 bool pass_failed); 147 148 // Signal a remote-initiated closure of a channel owned by this registry, then 149 // delete it. |close_cb_| is invoked if the channel was ever open (see 150 // |DynamicChannel::opened|). 151 void OnChannelDisconnected(DynamicChannel* channel); 152 153 // Delete a channel owned by this registry. Then, after this returns, 154 // |local_cid| may be recycled for another dynamic channel. 155 void RemoveChannel(DynamicChannel* channel); 156 157 // Greatest dynamic channel ID that can be assigned on the kind of logical 158 // link associated to this registry. 159 const uint16_t max_num_channels_; 160 161 // Called only for channels that were already open (see 162 // |DynamicChannel::opened|). 163 DynamicChannelCallback close_cb_; 164 ServiceRequestCallback service_request_cb_; 165 166 // Maps local CIDs to alive dynamic channels on this logical link. 167 using ChannelMap = std::unordered_map<ChannelId, DynamicChannelPtr>; 168 ChannelMap channels_; 169 170 bool random_channel_ids_; 171 172 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(DynamicChannelRegistry); 173 }; 174 175 } // namespace bt::l2cap::internal 176