• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 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_proxy/internal/l2cap_channel_manager.h"
16 
17 #include <mutex>
18 
19 #include "pw_containers/algorithm.h"
20 #include "pw_log/log.h"
21 #include "pw_status/status.h"
22 
23 namespace pw::bluetooth::proxy {
24 
L2capChannelManager(AclDataChannel & acl_data_channel)25 L2capChannelManager::L2capChannelManager(AclDataChannel& acl_data_channel)
26     : acl_data_channel_(acl_data_channel),
27       lrd_channel_(channels_.end()),
28       round_robin_terminus_(channels_.end()) {}
29 
RegisterChannel(L2capChannel & channel)30 void L2capChannelManager::RegisterChannel(L2capChannel& channel) {
31   std::lock_guard lock(channels_mutex_);
32   // Insert new channels before `lrd_channel_`.
33   IntrusiveForwardList<L2capChannel>::iterator before_it =
34       channels_.before_begin();
35   for (auto it = channels_.begin(); it != lrd_channel_; ++it) {
36     ++before_it;
37   }
38   channels_.insert_after(before_it, channel);
39   if (lrd_channel_ == channels_.end()) {
40     lrd_channel_ = channels_.begin();
41   }
42 }
43 
DeregisterChannelLocked(L2capChannel & channel)44 void L2capChannelManager::DeregisterChannelLocked(L2capChannel& channel) {
45   if (&channel == &(*lrd_channel_)) {
46     Advance(lrd_channel_);
47   }
48   if (&channel == &(*round_robin_terminus_)) {
49     Advance(round_robin_terminus_);
50   }
51 
52   // Channel will only be removed once, but DeregisterChannel() may be called
53   // multiple times on the same channel so it's ok for this to return false.
54   channels_.remove(channel);
55 
56   // If `channel` was the only element in `channels_`, advancing channels just
57   // wrapped them back on itself, so we reset it here.
58   if (channels_.empty()) {
59     lrd_channel_ = channels_.end();
60     round_robin_terminus_ = channels_.end();
61   }
62 }
63 
DeregisterChannel(L2capChannel & channel)64 void L2capChannelManager::DeregisterChannel(L2capChannel& channel) {
65   std::lock_guard lock(channels_mutex_);
66   DeregisterChannelLocked(channel);
67 }
68 
DeregisterAndCloseChannels(L2capChannelEvent event)69 void L2capChannelManager::DeregisterAndCloseChannels(L2capChannelEvent event) {
70   std::lock_guard lock(channels_mutex_);
71   while (!channels_.empty()) {
72     L2capChannel& front = channels_.front();
73     channels_.pop_front();
74     front.InternalClose(event);
75   }
76   lrd_channel_ = channels_.end();
77   round_robin_terminus_ = channels_.end();
78 }
79 
GetAclH4Packet(uint16_t size)80 pw::Result<H4PacketWithH4> L2capChannelManager::GetAclH4Packet(uint16_t size) {
81   if (size > GetH4BuffSize()) {
82     PW_LOG_ERROR(
83         "Requested packet is too large for H4 buffer. So will not send.");
84     return pw::Status::InvalidArgument();
85   }
86 
87   std::optional<span<uint8_t>> h4_buff = h4_storage_.ReserveH4Buff();
88   if (!h4_buff) {
89     PW_LOG_WARN("No H4 buffers available.");
90     return pw::Status::Unavailable();
91   }
92 
93   H4PacketWithH4 h4_packet(span(h4_buff->data(), size),
94                            /*release_fn=*/[this](const uint8_t* buffer) {
95                              this->h4_storage_.ReleaseH4Buff(buffer);
96                              ForceDrainChannelQueues();
97                            });
98   h4_packet.SetH4Type(emboss::H4PacketType::ACL_DATA);
99 
100   return h4_packet;
101 }
102 
GetH4BuffSize() const103 uint16_t L2capChannelManager::GetH4BuffSize() const {
104   return H4Storage::GetH4BuffSize();
105 }
106 
ForceDrainChannelQueues()107 void L2capChannelManager::ForceDrainChannelQueues() {
108   ReportNewTxPacketsOrCredits();
109   DrainChannelQueuesIfNewTx();
110 }
111 
ReportNewTxPacketsOrCredits()112 void L2capChannelManager::ReportNewTxPacketsOrCredits() {
113   new_tx_since_drain_ = true;
114 }
115 
DrainChannelQueuesIfNewTx()116 void L2capChannelManager::DrainChannelQueuesIfNewTx() {
117   if (!new_tx_since_drain_) {
118     return;
119   }
120   new_tx_since_drain_ = false;
121 
122   for (;;) {
123     std::optional<AclDataChannel::SendCredit> credit;
124     std::optional<H4PacketWithH4> packet;
125     {
126       std::lock_guard lock(channels_mutex_);
127       if (lrd_channel_ == channels_.end()) {
128         // This means the container is empty.
129         return;
130       }
131       if (round_robin_terminus_ == channels_.end()) {
132         round_robin_terminus_ = lrd_channel_;
133       }
134       credit = acl_data_channel_.ReserveSendCredit(lrd_channel_->transport());
135       if (credit) {
136         packet = lrd_channel_->DequeuePacket();
137       }
138       Advance(lrd_channel_);
139       if (packet) {
140         // Round robin should continue until we have done a full loop with no
141         // packets dequeued.
142         round_robin_terminus_ = lrd_channel_;
143       }
144     }
145 
146     if (packet) {
147       // Send while unlocked. This can trigger a recursive round robin once
148       // `packet` is released, but this is fine because `lrd_channel_` has
149       // been adjusted so the recursive call will start where this one left off,
150       // and `round_robin_terminus_` will be updated to point to channels with
151       // dequeued packets.
152       PW_CHECK_OK(
153           acl_data_channel_.SendAcl(std::move(*packet), std::move(*credit)));
154       continue;
155     }
156 
157     std::lock_guard lock(channels_mutex_);
158     if (lrd_channel_ == round_robin_terminus_) {
159       break;
160     }
161   }
162 }
163 
164 std::optional<L2capChannelManager::LockedL2capChannel>
FindChannelByLocalCid(uint16_t connection_handle,uint16_t local_cid)165 L2capChannelManager::FindChannelByLocalCid(
166     uint16_t connection_handle, uint16_t local_cid) PW_NO_LOCK_SAFETY_ANALYSIS {
167   // Lock annotations don't work with unique_lock
168   std::unique_lock lock(channels_mutex_);
169   L2capChannel* channel =
170       FindChannelByLocalCidLocked(connection_handle, local_cid);
171   if (!channel) {
172     return std::nullopt;
173   }
174   return LockedL2capChannel(*channel, std::move(lock));
175 }
176 
177 std::optional<L2capChannelManager::LockedL2capChannel>
FindChannelByRemoteCid(uint16_t connection_handle,uint16_t remote_cid)178 L2capChannelManager::FindChannelByRemoteCid(uint16_t connection_handle,
179                                             uint16_t remote_cid)
180     PW_NO_LOCK_SAFETY_ANALYSIS {
181   // Lock annotations don't work with unique_lock
182   std::unique_lock lock(channels_mutex_);
183   L2capChannel* channel =
184       FindChannelByRemoteCidLocked(connection_handle, remote_cid);
185   if (!channel) {
186     return std::nullopt;
187   }
188   return LockedL2capChannel(*channel, std::move(lock));
189 }
190 
FindChannelByLocalCidLocked(uint16_t connection_handle,uint16_t local_cid)191 L2capChannel* L2capChannelManager::FindChannelByLocalCidLocked(
192     uint16_t connection_handle, uint16_t local_cid) PW_NO_LOCK_SAFETY_ANALYSIS {
193   auto channel_it = containers::FindIf(
194       channels_, [connection_handle, local_cid](const L2capChannel& channel) {
195         return channel.connection_handle() == connection_handle &&
196                channel.local_cid() == local_cid;
197       });
198   if (channel_it == channels_.end()) {
199     return nullptr;
200   }
201   return &(*channel_it);
202 }
203 
FindChannelByRemoteCidLocked(uint16_t connection_handle,uint16_t remote_cid)204 L2capChannel* L2capChannelManager::FindChannelByRemoteCidLocked(
205     uint16_t connection_handle,
206     uint16_t remote_cid) PW_NO_LOCK_SAFETY_ANALYSIS {
207   auto channel_it = containers::FindIf(
208       channels_, [connection_handle, remote_cid](const L2capChannel& channel) {
209         return channel.connection_handle() == connection_handle &&
210                channel.remote_cid() == remote_cid;
211       });
212   if (channel_it == channels_.end()) {
213     return nullptr;
214   }
215   return &(*channel_it);
216 }
217 
Advance(IntrusiveForwardList<L2capChannel>::iterator & it)218 void L2capChannelManager::Advance(
219     IntrusiveForwardList<L2capChannel>::iterator& it) {
220   if (++it == channels_.end()) {
221     it = channels_.begin();
222   }
223 }
224 
RegisterStatusDelegate(L2capStatusDelegate & delegate)225 void L2capChannelManager::RegisterStatusDelegate(
226     L2capStatusDelegate& delegate) {
227   status_tracker_.RegisterDelegate(delegate);
228 }
229 
UnregisterStatusDelegate(L2capStatusDelegate & delegate)230 void L2capChannelManager::UnregisterStatusDelegate(
231     L2capStatusDelegate& delegate) {
232   status_tracker_.UnregisterDelegate(delegate);
233 }
234 
HandleConnectionComplete(const L2capChannelConnectionInfo & info)235 void L2capChannelManager::HandleConnectionComplete(
236     const L2capChannelConnectionInfo& info) {
237   status_tracker_.HandleConnectionComplete(info);
238 }
239 
HandleAclDisconnectionComplete(uint16_t connection_handle)240 void L2capChannelManager::HandleAclDisconnectionComplete(
241     uint16_t connection_handle) {
242   PW_LOG_INFO(
243       "btproxy: L2capChannelManager::HandleAclDisconnectionComplete - "
244       "connection_handle: %u",
245       connection_handle);
246   for (;;) {
247     IntrusiveForwardList<L2capChannel>::iterator channel_it;
248 
249     std::lock_guard lock(channels_mutex_);
250     channel_it = containers::FindIf(
251         channels_, [connection_handle](L2capChannel& channel) {
252           return channel.connection_handle() == connection_handle &&
253                  channel.state() == L2capChannel::State::kRunning;
254         });
255     if (channel_it == channels_.end()) {
256       break;
257     }
258 
259     DeregisterChannelLocked(*channel_it);
260     channel_it->InternalClose();
261   }
262 
263   status_tracker_.HandleAclDisconnectionComplete(connection_handle);
264 }
265 
HandleDisconnectionCompleteLocked(const L2capStatusTracker::DisconnectParams & params)266 void L2capChannelManager::HandleDisconnectionCompleteLocked(
267     const L2capStatusTracker::DisconnectParams& params)
268     PW_NO_LOCK_SAFETY_ANALYSIS {
269   // Must be called under channels_lock_ but we can't use proper lock annotation
270   // here since the call comes via signaling channel.
271   // TODO: https://pwbug.dev/390511432 - Figure out way to add annotations to
272   // enforce this invariant.
273 
274   L2capChannel* channel =
275       FindChannelByLocalCidLocked(params.connection_handle, params.local_cid);
276   if (channel) {
277     DeregisterChannelLocked(*channel);
278     channel->InternalClose();
279   }
280   status_tracker_.HandleDisconnectionComplete(params);
281 }
282 
DeliverPendingEvents()283 void L2capChannelManager::DeliverPendingEvents() {
284   status_tracker_.DeliverPendingEvents();
285 }
286 
287 }  // namespace pw::bluetooth::proxy
288