• 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.h"
16 
17 #include <mutex>
18 
19 #include "lib/stdcompat/utility.h"
20 #include "pw_bluetooth/emboss_util.h"
21 #include "pw_bluetooth/hci_data.emb.h"
22 #include "pw_bluetooth/hci_h4.emb.h"
23 #include "pw_bluetooth/l2cap_frames.emb.h"
24 #include "pw_bluetooth_proxy/internal/l2cap_channel_manager.h"
25 #include "pw_bluetooth_proxy/l2cap_channel_common.h"
26 #include "pw_log/log.h"
27 #include "pw_span/cast.h"
28 #include "pw_status/status.h"
29 #include "pw_status/try.h"
30 
31 namespace pw::bluetooth::proxy {
32 
MoveFields(L2capChannel & other)33 void L2capChannel::MoveFields(L2capChannel& other) {
34   // TODO: https://pwbug.dev/380504851 - Add tests for move operators.
35   state_ = other.state();
36   connection_handle_ = other.connection_handle();
37   transport_ = other.transport();
38   local_cid_ = other.local_cid();
39   remote_cid_ = other.remote_cid();
40   event_fn_ = std::move(other.event_fn_);
41   payload_from_controller_fn_ = std::move(other.payload_from_controller_fn_);
42   payload_from_host_fn_ = std::move(other.payload_from_host_fn_);
43   rx_multibuf_allocator_ = other.rx_multibuf_allocator_;
44   {
45     std::lock_guard lock(send_queue_mutex_);
46     std::lock_guard other_lock(other.send_queue_mutex_);
47     payload_queue_ = std::move(other.payload_queue_);
48     notify_on_dequeue_ = other.notify_on_dequeue_;
49     l2cap_channel_manager_.DeregisterChannel(other);
50     l2cap_channel_manager_.RegisterChannel(*this);
51   }
52   other.Undefine();
53 }
54 
L2capChannel(L2capChannel && other)55 L2capChannel::L2capChannel(L2capChannel&& other)
56     : l2cap_channel_manager_(other.l2cap_channel_manager_) {
57   MoveFields(other);
58 }
59 
operator =(L2capChannel && other)60 L2capChannel& L2capChannel::operator=(L2capChannel&& other) {
61   if (this != &other) {
62     l2cap_channel_manager_.DeregisterChannel(*this);
63     MoveFields(other);
64   }
65   return *this;
66 }
67 
~L2capChannel()68 L2capChannel::~L2capChannel() {
69   // Don't log dtor of moved-from channels.
70   if (state_ != State::kUndefined) {
71     PW_LOG_INFO(
72         "btproxy: L2capChannel dtor - transport_: %u, connection_handle_ : "
73         "%#x, local_cid_: %#x, remote_cid_: %#x, state_: %u",
74         cpp23::to_underlying(transport_),
75         connection_handle_,
76         local_cid_,
77         remote_cid_,
78         cpp23::to_underlying(state_));
79   }
80 
81   // Channel objects may outlive `ProxyHost`, but they are closed on
82   // `ProxyHost` dtor, so this check will prevent a crash from trying to access
83   // a destructed `L2capChannelManager`.
84   if (state_ != State::kClosed) {
85     l2cap_channel_manager_.DeregisterChannel(*this);
86     ClearQueue();
87   }
88 }
89 
Stop()90 void L2capChannel::Stop() {
91   PW_LOG_INFO(
92       "btproxy: L2capChannel::Stop - transport_: %u, connection_handle_: %#x, "
93       "local_cid_: %#x, remote_cid_: %#x, previous state_: %u",
94       cpp23::to_underlying(transport_),
95       connection_handle_,
96       local_cid_,
97       remote_cid_,
98       cpp23::to_underlying(state_));
99 
100   PW_CHECK(state_ != State::kUndefined && state_ != State::kClosed);
101 
102   state_ = State::kStopped;
103   ClearQueue();
104 }
105 
Close()106 void L2capChannel::Close() {
107   l2cap_channel_manager_.DeregisterChannel(*this);
108   InternalClose();
109 }
110 
InternalClose(L2capChannelEvent event)111 void L2capChannel::InternalClose(L2capChannelEvent event) {
112   PW_LOG_INFO(
113       "btproxy: L2capChannel::Close - transport_: %u, "
114       "connection_handle_: %#x, local_cid_: %#x, remote_cid_: %#x, previous "
115       "state_: %u",
116       cpp23::to_underlying(transport_),
117       connection_handle_,
118       local_cid_,
119       remote_cid_,
120       cpp23::to_underlying(state_));
121 
122   PW_CHECK(state_ != State::kUndefined);
123   if (state_ == State::kClosed) {
124     return;
125   }
126   state_ = State::kClosed;
127 
128   ClearQueue();
129   DoClose();
130   SendEvent(event);
131 }
132 
Undefine()133 void L2capChannel::Undefine() { state_ = State::kUndefined; }
134 
Write(pw::multibuf::MultiBuf && payload)135 StatusWithMultiBuf L2capChannel::Write(pw::multibuf::MultiBuf&& payload) {
136   StatusWithMultiBuf result = WriteLocked(std::move(payload));
137   l2cap_channel_manager_.DrainChannelQueuesIfNewTx();
138   return result;
139 }
140 
WriteLocked(pw::multibuf::MultiBuf && payload)141 StatusWithMultiBuf L2capChannel::WriteLocked(pw::multibuf::MultiBuf&& payload) {
142   if (UsesPayloadQueue()) {
143     return WriteToPayloadQueue(std::move(payload));
144   } else {
145     return WriteToPduQueue(std::move(payload));
146   }
147 }
148 
QueuePacket(H4PacketWithH4 && packet)149 Status L2capChannel::QueuePacket(H4PacketWithH4&& packet) {
150   PW_CHECK(!UsesPayloadQueue());
151 
152   if (state() != State::kRunning) {
153     return Status::FailedPrecondition();
154   }
155 
156   Status status;
157   {
158     std::lock_guard lock(send_queue_mutex_);
159     if (send_queue_.full()) {
160       status = Status::Unavailable();
161       notify_on_dequeue_ = true;
162     } else {
163       send_queue_.push(std::move(packet));
164       status = OkStatus();
165     }
166   }
167   l2cap_channel_manager_.ForceDrainChannelQueues();
168   return status;
169 }
170 
WriteToPayloadQueue(multibuf::MultiBuf && payload)171 StatusWithMultiBuf L2capChannel::WriteToPayloadQueue(
172     multibuf::MultiBuf&& payload) {
173   if (!payload.IsContiguous()) {
174     return {Status::InvalidArgument(), std::move(payload)};
175   }
176 
177   if (state() != State::kRunning) {
178     return {Status::FailedPrecondition(), std::move(payload)};
179   }
180 
181   PW_CHECK(UsesPayloadQueue());
182 
183   return QueuePayload(std::move(payload));
184 }
185 
186 // TODO: https://pwbug.dev/379337272 - Delete when all channels are
187 // transitioned to using payload queues.
WriteToPduQueue(multibuf::MultiBuf && payload)188 StatusWithMultiBuf L2capChannel::WriteToPduQueue(multibuf::MultiBuf&& payload) {
189   if (!payload.IsContiguous()) {
190     return {Status::InvalidArgument(), std::move(payload)};
191   }
192 
193   if (state() != State::kRunning) {
194     return {Status::FailedPrecondition(), std::move(payload)};
195   }
196 
197   PW_CHECK(!UsesPayloadQueue());
198 
199   std::optional<ByteSpan> span = payload.ContiguousSpan();
200   PW_CHECK(span.has_value());
201   Status status = Write(span_cast<const uint8_t>(*span));
202 
203   if (!status.ok()) {
204     return {status, std::move(payload)};
205   }
206 
207   return {OkStatus(), std::nullopt};
208 }
209 
Write(pw::span<const uint8_t> payload)210 pw::Status L2capChannel::Write(
211     [[maybe_unused]] pw::span<const uint8_t> payload) {
212   PW_LOG_ERROR(
213       "btproxy: Write(span) called on class that only supports "
214       "Write(MultiBuf)");
215   return Status::Unimplemented();
216 }
217 
IsWriteAvailable()218 Status L2capChannel::IsWriteAvailable() {
219   if (state() != State::kRunning) {
220     return Status::FailedPrecondition();
221   }
222 
223   std::lock_guard lock(send_queue_mutex_);
224 
225   // TODO: https://pwbug.dev/379337272 - Only check payload_queue_ once all
226   // channels have transitioned to payload_queue_.
227   const bool queue_full =
228       UsesPayloadQueue() ? payload_queue_.full() : send_queue_.full();
229   if (queue_full) {
230     notify_on_dequeue_ = true;
231     return Status::Unavailable();
232   }
233 
234   notify_on_dequeue_ = false;
235   return OkStatus();
236 }
237 
DequeuePacket()238 std::optional<H4PacketWithH4> L2capChannel::DequeuePacket() {
239   std::optional<H4PacketWithH4> packet;
240   bool should_notify = false;
241   {
242     std::lock_guard lock(send_queue_mutex_);
243     packet = GenerateNextTxPacket();
244     if (packet) {
245       should_notify = notify_on_dequeue_;
246       notify_on_dequeue_ = false;
247     }
248   }
249 
250   if (should_notify) {
251     SendEvent(L2capChannelEvent::kWriteAvailable);
252   }
253 
254   return packet;
255 }
256 
QueuePayload(multibuf::MultiBuf && buf)257 StatusWithMultiBuf L2capChannel::QueuePayload(multibuf::MultiBuf&& buf) {
258   PW_CHECK(UsesPayloadQueue());
259 
260   PW_CHECK(state() == State::kRunning);
261   PW_CHECK(buf.IsContiguous());
262 
263   {
264     std::lock_guard lock(send_queue_mutex_);
265     if (payload_queue_.full()) {
266       notify_on_dequeue_ = true;
267       return {Status::Unavailable(), std::move(buf)};
268     }
269     payload_queue_.push(std::move(buf));
270   }
271 
272   ReportNewTxPacketsOrCredits();
273   return {OkStatus(), std::nullopt};
274 }
275 
PopFrontPayload()276 void L2capChannel::PopFrontPayload() {
277   PW_CHECK(!payload_queue_.empty());
278   payload_queue_.pop();
279 }
280 
GetFrontPayloadSpan() const281 ConstByteSpan L2capChannel::GetFrontPayloadSpan() const {
282   PW_CHECK(!payload_queue_.empty());
283   const multibuf::MultiBuf& buf = payload_queue_.front();
284   std::optional<ConstByteSpan> span = buf.ContiguousSpan();
285   PW_CHECK(span);
286   return *span;
287 }
288 
PayloadQueueEmpty() const289 bool L2capChannel::PayloadQueueEmpty() const { return payload_queue_.empty(); }
290 
HandlePduFromController(pw::span<uint8_t> l2cap_pdu)291 bool L2capChannel::HandlePduFromController(pw::span<uint8_t> l2cap_pdu) {
292   if (state() != State::kRunning) {
293     PW_LOG_ERROR(
294         "btproxy: L2capChannel::OnPduReceivedFromController on non-running "
295         "channel. local_cid: %#x, remote_cid: %#x, state: %u",
296         local_cid(),
297         remote_cid(),
298         cpp23::to_underlying(state()));
299     SendEvent(L2capChannelEvent::kRxWhileStopped);
300     return true;
301   }
302   return DoHandlePduFromController(l2cap_pdu);
303 }
304 
L2capChannel(L2capChannelManager & l2cap_channel_manager,multibuf::MultiBufAllocator * rx_multibuf_allocator,uint16_t connection_handle,AclTransportType transport,uint16_t local_cid,uint16_t remote_cid,OptionalPayloadReceiveCallback && payload_from_controller_fn,OptionalPayloadReceiveCallback && payload_from_host_fn,ChannelEventCallback && event_fn)305 L2capChannel::L2capChannel(
306     L2capChannelManager& l2cap_channel_manager,
307     multibuf::MultiBufAllocator* rx_multibuf_allocator,
308     uint16_t connection_handle,
309     AclTransportType transport,
310     uint16_t local_cid,
311     uint16_t remote_cid,
312     OptionalPayloadReceiveCallback&& payload_from_controller_fn,
313     OptionalPayloadReceiveCallback&& payload_from_host_fn,
314     ChannelEventCallback&& event_fn)
315     : l2cap_channel_manager_(l2cap_channel_manager),
316       state_(State::kRunning),
317       connection_handle_(connection_handle),
318       transport_(transport),
319       local_cid_(local_cid),
320       remote_cid_(remote_cid),
321       event_fn_(std::move(event_fn)),
322       rx_multibuf_allocator_(rx_multibuf_allocator),
323       payload_from_controller_fn_(std::move(payload_from_controller_fn)),
324       payload_from_host_fn_(std::move(payload_from_host_fn)) {
325   PW_LOG_INFO(
326       "btproxy: L2capChannel ctor - transport_: %u, connection_handle_ : %u, "
327       "local_cid_ : %#x, remote_cid_: %#x",
328       cpp23::to_underlying(transport_),
329       connection_handle_,
330       local_cid_,
331       remote_cid_);
332 
333   l2cap_channel_manager_.RegisterChannel(*this);
334 }
335 
336 // Send `event` to client if an event callback was provided.
SendEvent(L2capChannelEvent event)337 void L2capChannel::SendEvent(L2capChannelEvent event) {
338   // We don't log kWriteAvailable since they happen often. Optimally we would
339   // just debug log them also, but one of our downstreams logs all levels.
340   if (event != L2capChannelEvent::kWriteAvailable) {
341     PW_LOG_INFO(
342         "btproxy: SendEvent - event: %u, transport_: %u, "
343         "connection_handle_: %#x, local_cid_ : %#x, remote_cid_: %#x, "
344         "state_: %u",
345         cpp23::to_underlying(event),
346         cpp23::to_underlying(transport_),
347         connection_handle_,
348         local_cid_,
349         remote_cid_,
350         cpp23::to_underlying(state_));
351   }
352 
353   if (event_fn_) {
354     event_fn_(event);
355   }
356 }
357 
AreValidParameters(uint16_t connection_handle,uint16_t local_cid,uint16_t remote_cid)358 bool L2capChannel::AreValidParameters(uint16_t connection_handle,
359                                       uint16_t local_cid,
360                                       uint16_t remote_cid) {
361   if (connection_handle > kMaxValidConnectionHandle) {
362     PW_LOG_ERROR(
363         "Invalid connection handle %#x. Maximum connection handle is 0x0EFF.",
364         connection_handle);
365     return false;
366   }
367   if (local_cid == 0 || remote_cid == 0) {
368     PW_LOG_ERROR("L2CAP channel identifier 0 is not valid.");
369     return false;
370   }
371   return true;
372 }
373 
GenerateNextTxPacket()374 std::optional<H4PacketWithH4> L2capChannel::GenerateNextTxPacket() {
375   if (send_queue_.empty()) {
376     return std::nullopt;
377   }
378   H4PacketWithH4 packet = std::move(send_queue_.front());
379   send_queue_.pop();
380   return packet;
381 }
382 
PopulateTxL2capPacket(uint16_t data_length)383 pw::Result<H4PacketWithH4> L2capChannel::PopulateTxL2capPacket(
384     uint16_t data_length) {
385   return PopulateL2capPacket(data_length);
386 }
387 
PopulateTxL2capPacketDuringWrite(uint16_t data_length)388 pw::Result<H4PacketWithH4> L2capChannel::PopulateTxL2capPacketDuringWrite(
389     uint16_t data_length) {
390   pw::Result<H4PacketWithH4> packet_result = PopulateL2capPacket(data_length);
391   if (packet_result.status().IsUnavailable()) {
392     std::lock_guard lock(send_queue_mutex_);
393     // If there were no buffers, they are all in the queue currently. This can
394     // happen if queue size == buffer count. Mark that a writer is getting an
395     // Unavailable status, and should be notified when queue space opens up.
396     notify_on_dequeue_ = true;
397   }
398   return packet_result;
399 }
400 
401 namespace {
402 
H4SizeForL2capData(uint16_t data_length)403 constexpr size_t H4SizeForL2capData(uint16_t data_length) {
404   const size_t l2cap_packet_size =
405       emboss::BasicL2capHeader::IntrinsicSizeInBytes() + data_length;
406   const size_t acl_packet_size =
407       emboss::AclDataFrameHeader::IntrinsicSizeInBytes() + l2cap_packet_size;
408   return sizeof(emboss::H4PacketType) + acl_packet_size;
409 }
410 
411 }  // namespace
412 
IsOkL2capDataLength(uint16_t data_length)413 bool L2capChannel::IsOkL2capDataLength(uint16_t data_length) {
414   return H4SizeForL2capData(data_length) <=
415          l2cap_channel_manager_.GetH4BuffSize();
416 }
417 
PopulateL2capPacket(uint16_t data_length)418 pw::Result<H4PacketWithH4> L2capChannel::PopulateL2capPacket(
419     uint16_t data_length) {
420   const size_t l2cap_packet_size =
421       emboss::BasicL2capHeader::IntrinsicSizeInBytes() + data_length;
422   const size_t h4_packet_size = H4SizeForL2capData(data_length);
423 
424   pw::Result<H4PacketWithH4> h4_packet_res =
425       l2cap_channel_manager_.GetAclH4Packet(h4_packet_size);
426   if (!h4_packet_res.ok()) {
427     return h4_packet_res.status();
428   }
429   H4PacketWithH4 h4_packet = std::move(h4_packet_res.value());
430   h4_packet.SetH4Type(emboss::H4PacketType::ACL_DATA);
431 
432   PW_TRY_ASSIGN(
433       auto acl,
434       MakeEmbossWriter<emboss::AclDataFrameWriter>(h4_packet.GetHciSpan()));
435   acl.header().handle().Write(connection_handle_);
436   // TODO: https://pwbug.dev/360932103 - Support packet segmentation, so this
437   // value will not always be FIRST_NON_FLUSHABLE.
438   acl.header().packet_boundary_flag().Write(
439       emboss::AclDataPacketBoundaryFlag::FIRST_NON_FLUSHABLE);
440   acl.header().broadcast_flag().Write(
441       emboss::AclDataPacketBroadcastFlag::POINT_TO_POINT);
442   acl.data_total_length().Write(l2cap_packet_size);
443 
444   PW_TRY_ASSIGN(auto l2cap_header,
445                 MakeEmbossWriter<emboss::BasicL2capHeaderWriter>(
446                     acl.payload().BackingStorage().data(),
447                     emboss::BasicL2capHeader::IntrinsicSizeInBytes()));
448   l2cap_header.pdu_length().Write(data_length);
449   l2cap_header.channel_id().Write(remote_cid_);
450 
451   return h4_packet;
452 }
453 
MaxL2capPayloadSize() const454 std::optional<uint16_t> L2capChannel::MaxL2capPayloadSize() const {
455   std::optional<uint16_t> le_acl_data_packet_length =
456       l2cap_channel_manager_.le_acl_data_packet_length();
457   if (!le_acl_data_packet_length) {
458     return std::nullopt;
459   }
460 
461   uint16_t max_acl_data_size_based_on_h4_buffer =
462       l2cap_channel_manager_.GetH4BuffSize() - sizeof(emboss::H4PacketType) -
463       emboss::AclDataFrameHeader::IntrinsicSizeInBytes();
464   uint16_t max_acl_data_size = std::min(max_acl_data_size_based_on_h4_buffer,
465                                         *le_acl_data_packet_length);
466   return max_acl_data_size - emboss::BasicL2capHeader::IntrinsicSizeInBytes();
467 }
468 
ReportNewTxPacketsOrCredits()469 void L2capChannel::ReportNewTxPacketsOrCredits() {
470   l2cap_channel_manager_.ReportNewTxPacketsOrCredits();
471 }
472 
DrainChannelQueuesIfNewTx()473 void L2capChannel::DrainChannelQueuesIfNewTx()
474     PW_LOCKS_EXCLUDED(send_queue_mutex_) {
475   l2cap_channel_manager_.DrainChannelQueuesIfNewTx();
476 }
477 
ClearQueue()478 void L2capChannel::ClearQueue() {
479   std::lock_guard lock(send_queue_mutex_);
480   send_queue_.clear();
481 }
482 
483 //-------
484 //  Rx (protected)
485 //-------
486 
SendPayloadFromHostToClient(pw::span<uint8_t> payload)487 bool L2capChannel::SendPayloadFromHostToClient(pw::span<uint8_t> payload) {
488   return SendPayloadToClient(payload, payload_from_host_fn_);
489 }
490 
SendPayloadFromControllerToClient(pw::span<uint8_t> payload)491 bool L2capChannel::SendPayloadFromControllerToClient(
492     pw::span<uint8_t> payload) {
493   return SendPayloadToClient(payload, payload_from_controller_fn_);
494 }
495 
SendPayloadToClient(pw::span<uint8_t> payload,OptionalPayloadReceiveCallback & callback)496 bool L2capChannel::SendPayloadToClient(
497     pw::span<uint8_t> payload, OptionalPayloadReceiveCallback& callback) {
498   if (!callback) {
499     return false;
500   }
501 
502   std::optional<multibuf::MultiBuf> buffer =
503       rx_multibuf_allocator()->AllocateContiguous(payload.size());
504 
505   if (!buffer) {
506     PW_LOG_ERROR(
507         "(CID %#x) Rx MultiBuf allocator out of memory. So stopping "
508         "channel "
509         "and reporting it needs to be closed.",
510         local_cid());
511     StopAndSendEvent(L2capChannelEvent::kRxOutOfMemory);
512     return true;
513   }
514 
515   StatusWithSize status = buffer->CopyFrom(/*source=*/as_bytes(payload),
516                                            /*position=*/0);
517   PW_CHECK_OK(status);
518 
519   std::optional<multibuf::MultiBuf> client_multibuf =
520       callback(std::move(*buffer));
521   // If client returned multibuf to us, we drop it and indicate to caller that
522   // packet should be forwarded. In the future when whole path is operating
523   // with multibuf's, we could pass it back up to container to be forwarded.
524   return !client_multibuf.has_value();
525 }
526 
527 }  // namespace pw::bluetooth::proxy
528