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 #include "pw_bluetooth_sapphire/internal/host/l2cap/dynamic_channel_registry.h"
16
17 #include <pw_assert/check.h>
18
19 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
20 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
21
22 namespace bt::l2cap::internal {
23 // Run return callbacks on the L2CAP thread. LogicalLink takes care of out-of-
24 // thread dispatch for delivering the pointer to the channel.
OpenOutbound(Psm psm,ChannelParameters params,DynamicChannelCallback open_cb)25 void DynamicChannelRegistry::OpenOutbound(Psm psm,
26 ChannelParameters params,
27 DynamicChannelCallback open_cb) {
28 const ChannelId id = FindAvailableChannelId();
29 if (id == kInvalidChannelId) {
30 bt_log(ERROR, "l2cap", "No dynamic channel IDs available");
31 open_cb(nullptr);
32 return;
33 }
34
35 auto iter = channels_.emplace(id, MakeOutbound(psm, id, params)).first;
36 ActivateChannel(iter->second.get(), std::move(open_cb), /*pass_failed=*/true);
37 }
38
CloseChannel(ChannelId local_cid,fit::closure close_callback)39 void DynamicChannelRegistry::CloseChannel(ChannelId local_cid,
40 fit::closure close_callback) {
41 DynamicChannel* channel = FindChannelByLocalId(local_cid);
42 if (!channel) {
43 close_callback();
44 return;
45 }
46
47 PW_DCHECK(channel->IsConnected());
48 auto disconn_done_cb =
49 [self = GetWeakPtr(), close_cb = std::move(close_callback), channel] {
50 if (!self.is_alive()) {
51 close_cb();
52 return;
53 }
54 self->RemoveChannel(channel);
55 close_cb();
56 };
57 channel->Disconnect(std::move(disconn_done_cb));
58 }
59
DynamicChannelRegistry(uint16_t max_num_channels,DynamicChannelCallback close_cb,ServiceRequestCallback service_request_cb,bool random_channel_ids)60 DynamicChannelRegistry::DynamicChannelRegistry(
61 uint16_t max_num_channels,
62 DynamicChannelCallback close_cb,
63 ServiceRequestCallback service_request_cb,
64 bool random_channel_ids)
65 : WeakSelf(this),
66 max_num_channels_(max_num_channels),
67 close_cb_(std::move(close_cb)),
68 service_request_cb_(std::move(service_request_cb)),
69 random_channel_ids_(random_channel_ids) {
70 PW_DCHECK(max_num_channels > 0);
71 PW_DCHECK(max_num_channels < 65473);
72 PW_DCHECK(close_cb_);
73 PW_DCHECK(service_request_cb_);
74 }
75
RequestService(Psm psm,ChannelId local_cid,ChannelId remote_cid)76 DynamicChannel* DynamicChannelRegistry::RequestService(Psm psm,
77 ChannelId local_cid,
78 ChannelId remote_cid) {
79 PW_DCHECK(local_cid != kInvalidChannelId);
80
81 auto service_info = service_request_cb_(psm);
82 if (!service_info) {
83 bt_log(WARN,
84 "l2cap",
85 "No service found for PSM %#.4x from %#.4x",
86 psm,
87 remote_cid);
88 return nullptr;
89 }
90
91 auto iter =
92 channels_
93 .emplace(
94 local_cid,
95 MakeInbound(
96 psm, local_cid, remote_cid, service_info->channel_params))
97 .first;
98 ActivateChannel(iter->second.get(),
99 std::move(service_info->channel_cb),
100 /*pass_failed=*/false);
101 return iter->second.get();
102 }
103
FindAvailableChannelId()104 ChannelId DynamicChannelRegistry::FindAvailableChannelId() {
105 uint16_t offset = 0;
106 if (random_channel_ids_) {
107 random_generator()->GetInt(offset, max_num_channels_);
108 }
109 for (uint16_t i = 0; i < max_num_channels_; i++) {
110 ChannelId id = kFirstDynamicChannelId + ((offset + i) % max_num_channels_);
111 if (channels_.count(id) == 0) {
112 return id;
113 }
114 }
115
116 return kInvalidChannelId;
117 }
118
AliveChannelCount() const119 size_t DynamicChannelRegistry::AliveChannelCount() const {
120 return channels_.size();
121 }
122
FindChannelByLocalId(ChannelId local_cid) const123 DynamicChannel* DynamicChannelRegistry::FindChannelByLocalId(
124 ChannelId local_cid) const {
125 auto iter = channels_.find(local_cid);
126 if (iter == channels_.end()) {
127 return nullptr;
128 }
129 return iter->second.get();
130 }
131
FindChannelByRemoteId(ChannelId remote_cid) const132 DynamicChannel* DynamicChannelRegistry::FindChannelByRemoteId(
133 ChannelId remote_cid) const {
134 for (auto& [id, channel_ptr] : channels_) {
135 if (channel_ptr->remote_cid() == remote_cid) {
136 return channel_ptr.get();
137 }
138 }
139 return nullptr;
140 }
141
ForEach(fit::function<void (DynamicChannel *)> f) const142 void DynamicChannelRegistry::ForEach(
143 fit::function<void(DynamicChannel*)> f) const {
144 for (auto iter = channels_.begin(); iter != channels_.end();) {
145 // f() may remove the channel from the registry, so get next iterator to
146 // avoid invalidation. Only the erased iterator is invalidated.
147 auto next = std::next(iter);
148 f(iter->second.get());
149 iter = next;
150 }
151 }
152
ActivateChannel(DynamicChannel * channel,DynamicChannelCallback open_callback,bool pass_failed)153 void DynamicChannelRegistry::ActivateChannel(
154 DynamicChannel* channel,
155 DynamicChannelCallback open_callback,
156 bool pass_failed) {
157 // It's safe to capture |this| here because the callback will be owned by the
158 // DynamicChannel, which this registry owns.
159 auto return_chan = [this,
160 channel,
161 open_cb = std::move(open_callback),
162 pass_failed]() mutable {
163 if (channel->IsOpen()) {
164 open_cb(channel);
165 return;
166 }
167
168 bt_log(DEBUG,
169 "l2cap",
170 "Failed to open dynamic channel %#.4x (remote %#.4x) for PSM %#.4x",
171 channel->local_cid(),
172 channel->remote_cid(),
173 channel->psm());
174
175 // TODO(fxbug.dev/42057179): Maybe negotiate channel parameters here?
176 // For now, just disconnect the channel. Move the callback to the stack
177 // to prepare for channel destruction.
178 auto pass_failure = [cb = std::move(open_cb), pass_failed] {
179 if (pass_failed) {
180 cb(nullptr);
181 }
182 };
183
184 // This lambda is owned by the channel, so captures are no longer valid
185 // after this call.
186 auto disconn_done_cb = [self = GetWeakPtr(), channel] {
187 if (!self.is_alive()) {
188 return;
189 }
190 self->RemoveChannel(channel);
191 };
192 channel->Disconnect(std::move(disconn_done_cb));
193
194 pass_failure();
195 };
196
197 channel->Open(std::move(return_chan));
198 }
199
OnChannelDisconnected(DynamicChannel * channel)200 void DynamicChannelRegistry::OnChannelDisconnected(DynamicChannel* channel) {
201 if (channel->opened()) {
202 close_cb_(channel);
203 }
204 RemoveChannel(channel);
205 }
206
RemoveChannel(DynamicChannel * channel)207 void DynamicChannelRegistry::RemoveChannel(DynamicChannel* channel) {
208 PW_DCHECK(channel);
209 PW_DCHECK(!channel->IsConnected());
210
211 auto iter = channels_.find(channel->local_cid());
212 if (iter == channels_.end()) {
213 return;
214 }
215
216 if (channel != iter->second.get()) {
217 return;
218 }
219
220 channels_.erase(iter);
221 }
222
223 } // namespace bt::l2cap::internal
224