xref: /aosp_15_r20/external/pigweed/pw_bluetooth_proxy/l2cap_channel_manager.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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_write_channel_(write_channels_.end()) {}
28 
Reset()29 void L2capChannelManager::Reset() { h4_storage_.Reset(); }
30 
RegisterReadChannel(L2capReadChannel & channel)31 void L2capChannelManager::RegisterReadChannel(L2capReadChannel& channel) {
32   read_channels_.push_front(channel);
33 }
34 
ReleaseReadChannel(L2capReadChannel & channel)35 bool L2capChannelManager::ReleaseReadChannel(L2capReadChannel& channel) {
36   return read_channels_.remove(channel);
37 }
38 
RegisterWriteChannel(L2capWriteChannel & channel)39 void L2capChannelManager::RegisterWriteChannel(L2capWriteChannel& channel) {
40   std::lock_guard lock(write_channels_mutex_);
41   write_channels_.push_front(channel);
42   if (lrd_write_channel_ == write_channels_.end()) {
43     lrd_write_channel_ = write_channels_.begin();
44   }
45 }
46 
ReleaseWriteChannel(L2capWriteChannel & channel)47 bool L2capChannelManager::ReleaseWriteChannel(L2capWriteChannel& channel) {
48   std::lock_guard lock(write_channels_mutex_);
49   if (&channel == &(*lrd_write_channel_)) {
50     Advance(lrd_write_channel_);
51   }
52 
53   bool was_removed = write_channels_.remove(channel);
54 
55   // If `channel` was the only element in `write_channels_`, advancing
56   // `lrd_write_channel_` just wrapped it back on itself, so we reset it here.
57   if (write_channels_.empty()) {
58     lrd_write_channel_ = write_channels_.end();
59   }
60 
61   return was_removed;
62 }
63 
GetTxH4Packet(uint16_t size)64 pw::Result<H4PacketWithH4> L2capChannelManager::GetTxH4Packet(uint16_t size) {
65   if (size > GetH4BuffSize()) {
66     PW_LOG_ERROR(
67         "Requested packet is too large for H4 buffer. So will not send.");
68     return pw::Status::InvalidArgument();
69   }
70 
71   std::optional<span<uint8_t>> h4_buff = h4_storage_.ReserveH4Buff();
72   if (!h4_buff) {
73     PW_LOG_WARN("No H4 buffers available.");
74     return pw::Status::Unavailable();
75   }
76 
77   H4PacketWithH4 h4_packet(
78       span(h4_buff->data(), size),
79       /*release_fn=*/[h4_storage = &h4_storage_](const uint8_t* buffer) {
80         h4_storage->ReleaseH4Buff(buffer);
81       });
82   h4_packet.SetH4Type(emboss::H4PacketType::ACL_DATA);
83 
84   return h4_packet;
85 }
86 
GetH4BuffSize() const87 uint16_t L2capChannelManager::GetH4BuffSize() const {
88   return H4Storage::GetH4BuffSize();
89 }
90 
DrainWriteChannelQueues()91 void L2capChannelManager::DrainWriteChannelQueues() {
92   std::lock_guard lock(write_channels_mutex_);
93 
94   if (write_channels_.empty()) {
95     return;
96   }
97 
98   DrainWriteChannelQueues(AclTransportType::kBrEdr);
99   DrainWriteChannelQueues(AclTransportType::kLe);
100 }
101 
DrainWriteChannelQueues(AclTransportType transport)102 void L2capChannelManager::DrainWriteChannelQueues(AclTransportType transport) {
103   IntrusiveForwardList<L2capWriteChannel>::iterator round_robin_start =
104       lrd_write_channel_;
105   // Iterate around `write_channels_` in round robin fashion. For each channel,
106   // send as many queued packets as are available. Proceed until we run out of
107   // ACL send credits or finish visiting every channel.
108   // TODO: https://pwbug.dev/379337260 - Only drain one L2CAP PDU per channel
109   // before moving on. (This may require sending multiple ACL fragments.)
110   while (acl_data_channel_.GetNumFreeAclPackets(transport) > 0) {
111     if (lrd_write_channel_->transport() != transport) {
112       Advance(lrd_write_channel_);
113       if (lrd_write_channel_ == round_robin_start) {
114         return;
115       }
116       continue;
117     }
118 
119     std::optional<H4PacketWithH4> packet = lrd_write_channel_->DequeuePacket();
120     if (!packet) {
121       Advance(lrd_write_channel_);
122       if (lrd_write_channel_ == round_robin_start) {
123         return;
124       }
125       continue;
126     }
127 
128     PW_CHECK_OK(acl_data_channel_.SendAcl(std::move(*packet)));
129   }
130 }
131 
FindWriteChannel(uint16_t connection_handle,uint16_t remote_cid)132 L2capWriteChannel* L2capChannelManager::FindWriteChannel(
133     uint16_t connection_handle, uint16_t remote_cid) {
134   std::lock_guard lock(write_channels_mutex_);
135   auto connection_it = containers::FindIf(
136       write_channels_,
137       [connection_handle, remote_cid](const L2capWriteChannel& channel) {
138         return channel.connection_handle() == connection_handle &&
139                channel.remote_cid() == remote_cid;
140       });
141   return connection_it == write_channels_.end() ? nullptr : &(*connection_it);
142 }
143 
FindReadChannel(uint16_t connection_handle,uint16_t local_cid)144 L2capReadChannel* L2capChannelManager::FindReadChannel(
145     uint16_t connection_handle, uint16_t local_cid) {
146   auto connection_it = containers::FindIf(
147       read_channels_,
148       [connection_handle, local_cid](const L2capReadChannel& channel) {
149         return channel.connection_handle() == connection_handle &&
150                channel.local_cid() == local_cid;
151       });
152   return connection_it == read_channels_.end() ? nullptr : &(*connection_it);
153 }
154 
Advance(IntrusiveForwardList<L2capWriteChannel>::iterator & it)155 void L2capChannelManager::Advance(
156     IntrusiveForwardList<L2capWriteChannel>::iterator& it) {
157   if (++it == write_channels_.end()) {
158     it = write_channels_.begin();
159   }
160 }
161 
162 }  // namespace pw::bluetooth::proxy
163