• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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