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