xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/l2cap/channel.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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/channel.h"
16 
17 #include <cpp-string/string_printf.h>
18 
19 #include <memory>
20 #include <utility>
21 
22 #include "lib/fit/result.h"
23 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
24 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
25 #include "pw_bluetooth_sapphire/internal/host/common/weak_self.h"
26 #include "pw_bluetooth_sapphire/internal/host/hci-spec/vendor_protocol.h"
27 #include "pw_bluetooth_sapphire/internal/host/l2cap/basic_mode_rx_engine.h"
28 #include "pw_bluetooth_sapphire/internal/host/l2cap/basic_mode_tx_engine.h"
29 #include "pw_bluetooth_sapphire/internal/host/l2cap/credit_based_flow_control_rx_engine.h"
30 #include "pw_bluetooth_sapphire/internal/host/l2cap/credit_based_flow_control_tx_engine.h"
31 #include "pw_bluetooth_sapphire/internal/host/l2cap/enhanced_retransmission_mode_engines.h"
32 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
33 #include "pw_bluetooth_sapphire/internal/host/l2cap/logical_link.h"
34 #include "pw_bluetooth_sapphire/internal/host/transport/link_type.h"
35 
36 namespace bt::l2cap {
37 
38 namespace android_hci = bt::hci_spec::vendor::android;
39 using pw::bluetooth::AclPriority;
40 
Channel(ChannelId id,ChannelId remote_id,bt::LinkType link_type,hci_spec::ConnectionHandle link_handle,ChannelInfo info,uint16_t max_tx_queued)41 Channel::Channel(ChannelId id,
42                  ChannelId remote_id,
43                  bt::LinkType link_type,
44                  hci_spec::ConnectionHandle link_handle,
45                  ChannelInfo info,
46                  uint16_t max_tx_queued)
47     : WeakSelf(this),
48       id_(id),
49       remote_id_(remote_id),
50       link_type_(link_type),
51       link_handle_(link_handle),
52       info_(info),
53       max_tx_queued_(max_tx_queued),
54       requested_acl_priority_(AclPriority::kNormal) {
55   PW_DCHECK(id_);
56   PW_DCHECK(link_type_ == bt::LinkType::kLE ||
57             link_type_ == bt::LinkType::kACL);
58 }
59 
60 namespace internal {
61 
62 namespace {
63 
64 constexpr const char* kInspectPsmPropertyName = "psm";
65 constexpr const char* kInspectLocalIdPropertyName = "local_id";
66 constexpr const char* kInspectRemoteIdPropertyName = "remote_id";
67 constexpr const char* kInspectDroppedPacketsPropertyName = "dropped_packets";
68 
69 }  // namespace
70 
CreateFixedChannel(pw::async::Dispatcher & dispatcher,ChannelId id,internal::LogicalLinkWeakPtr link,hci::CommandChannel::WeakPtr cmd_channel,uint16_t max_acl_payload_size,A2dpOffloadManager & a2dp_offload_manager,uint16_t max_tx_queued)71 std::unique_ptr<ChannelImpl> ChannelImpl::CreateFixedChannel(
72     pw::async::Dispatcher& dispatcher,
73     ChannelId id,
74     internal::LogicalLinkWeakPtr link,
75     hci::CommandChannel::WeakPtr cmd_channel,
76     uint16_t max_acl_payload_size,
77     A2dpOffloadManager& a2dp_offload_manager,
78     uint16_t max_tx_queued) {
79   // A fixed channel's endpoints have the same local and remote identifiers.
80   // Setting the ChannelInfo MTU to kMaxMTU effectively cancels any L2CAP-level
81   // MTU enforcement for services which operate over fixed channels. Such
82   // services often define minimum MTU values in their specification, so they
83   // are required to respect these MTUs internally by:
84   //   1.) never sending packets larger than their spec-defined MTU.
85   //   2.) handling inbound PDUs which are larger than their spec-defined MTU
86   //   appropriately.
87   return std::unique_ptr<ChannelImpl>(
88       new ChannelImpl(dispatcher,
89                       id,
90                       id,
91                       link,
92                       ChannelInfo::MakeBasicMode(kMaxMTU, kMaxMTU),
93                       std::move(cmd_channel),
94                       max_acl_payload_size,
95                       a2dp_offload_manager,
96                       max_tx_queued));
97 }
98 
CreateDynamicChannel(pw::async::Dispatcher & dispatcher,ChannelId id,ChannelId peer_id,internal::LogicalLinkWeakPtr link,ChannelInfo info,hci::CommandChannel::WeakPtr cmd_channel,uint16_t max_acl_payload_size,A2dpOffloadManager & a2dp_offload_manager,uint16_t max_tx_queued)99 std::unique_ptr<ChannelImpl> ChannelImpl::CreateDynamicChannel(
100     pw::async::Dispatcher& dispatcher,
101     ChannelId id,
102     ChannelId peer_id,
103     internal::LogicalLinkWeakPtr link,
104     ChannelInfo info,
105     hci::CommandChannel::WeakPtr cmd_channel,
106     uint16_t max_acl_payload_size,
107     A2dpOffloadManager& a2dp_offload_manager,
108     uint16_t max_tx_queued) {
109   return std::unique_ptr<ChannelImpl>(new ChannelImpl(dispatcher,
110                                                       id,
111                                                       peer_id,
112                                                       link,
113                                                       info,
114                                                       std::move(cmd_channel),
115                                                       max_acl_payload_size,
116                                                       a2dp_offload_manager,
117                                                       max_tx_queued));
118 }
119 
ChannelImpl(pw::async::Dispatcher & dispatcher,ChannelId id,ChannelId remote_id,internal::LogicalLinkWeakPtr link,ChannelInfo info,hci::CommandChannel::WeakPtr cmd_channel,uint16_t max_acl_payload_size,A2dpOffloadManager & a2dp_offload_manager,uint16_t max_tx_queued)120 ChannelImpl::ChannelImpl(pw::async::Dispatcher& dispatcher,
121                          ChannelId id,
122                          ChannelId remote_id,
123                          internal::LogicalLinkWeakPtr link,
124                          ChannelInfo info,
125                          hci::CommandChannel::WeakPtr cmd_channel,
126                          uint16_t max_acl_payload_size,
127                          A2dpOffloadManager& a2dp_offload_manager,
128                          uint16_t max_tx_queued)
129     : Channel(id, remote_id, link->type(), link->handle(), info, max_tx_queued),
130       pw_dispatcher_(dispatcher),
131       active_(false),
132       link_(link),
133       cmd_channel_(std::move(cmd_channel)),
134       fragmenter_(link->handle(), max_acl_payload_size),
135       a2dp_offload_manager_(a2dp_offload_manager),
136       weak_self_(this) {
137   PW_CHECK(link_.is_alive());
138   PW_CHECK(
139       info_.mode == RetransmissionAndFlowControlMode::kBasic ||
140           info_.mode ==
141               RetransmissionAndFlowControlMode::kEnhancedRetransmission ||
142           info.mode == CreditBasedFlowControlMode::kLeCreditBasedFlowControl,
143       "Channel constructed with unsupported mode: %s\n",
144       AnyChannelModeToString(info_.mode).c_str());
145 
146   auto connection_failure_cb = [link] {
147     if (link.is_alive()) {
148       // |link| is expected to ignore this call if it has been closed.
149       link->SignalError();
150     }
151   };
152 
153   if (info_.mode == RetransmissionAndFlowControlMode::kBasic) {
154     rx_engine_ = std::make_unique<BasicModeRxEngine>();
155     tx_engine_ =
156         std::make_unique<BasicModeTxEngine>(id, max_tx_sdu_size(), *this);
157   } else if (std::holds_alternative<CreditBasedFlowControlMode>(info_.mode)) {
158     PW_CHECK(info_.remote_initial_credits.has_value());
159     auto mode = std::get<CreditBasedFlowControlMode>(info_.mode);
160     rx_engine_ = std::make_unique<CreditBasedFlowControlRxEngine>(
161         std::move(connection_failure_cb));
162     tx_engine_ = std::make_unique<CreditBasedFlowControlTxEngine>(
163         id,
164         max_tx_sdu_size(),
165         *this,
166         mode,
167         info_.max_tx_pdu_payload_size,
168         *info_.remote_initial_credits);
169   } else {
170     std::tie(rx_engine_, tx_engine_) =
171         MakeLinkedEnhancedRetransmissionModeEngines(
172             id,
173             max_tx_sdu_size(),
174             info_.max_transmissions,
175             info_.n_frames_in_tx_window,
176             *this,
177             std::move(connection_failure_cb),
178             pw_dispatcher_);
179   }
180 }
181 
~ChannelImpl()182 ChannelImpl::~ChannelImpl() {
183   size_t removed_count = this->pending_tx_sdus_.size();
184   if (removed_count > 0) {
185     bt_log(TRACE,
186            "hci",
187            "packets dropped (count: %zu) due to channel destruction (link: "
188            "%#.4x, id: %#.4x)",
189            removed_count,
190            link_handle(),
191            id());
192   }
193 }
194 
security()195 const sm::SecurityProperties ChannelImpl::security() {
196   if (link_.is_alive()) {
197     return link_->security();
198   }
199   return sm::SecurityProperties();
200 }
201 
Activate(RxCallback rx_callback,ClosedCallback closed_callback)202 bool ChannelImpl::Activate(RxCallback rx_callback,
203                            ClosedCallback closed_callback) {
204   PW_CHECK(rx_callback);
205   PW_CHECK(closed_callback);
206 
207   // Activating on a closed link has no effect. We also clear this on
208   // deactivation to prevent a channel from being activated more than once.
209   if (!link_.is_alive())
210     return false;
211 
212   PW_CHECK(!active_);
213   active_ = true;
214   rx_cb_ = std::move(rx_callback);
215   closed_cb_ = std::move(closed_callback);
216 
217   // Route the buffered packets.
218   if (!pending_rx_sdus_.empty()) {
219     TRACE_DURATION("bluetooth", "ChannelImpl::Activate pending drain");
220     // Channel may be destroyed in rx_cb_, so we need to check self after
221     // calling rx_cb_.
222     auto self = GetWeakPtr();
223     auto pending = std::move(pending_rx_sdus_);
224     while (self.is_alive() && !pending.empty()) {
225       TRACE_FLOW_END(
226           "bluetooth", "ChannelImpl::HandleRxPdu queued", pending.size());
227       rx_cb_(std::move(pending.front()));
228       pending.pop();
229     }
230   }
231 
232   return true;
233 }
234 
Deactivate()235 void ChannelImpl::Deactivate() {
236   bt_log(TRACE,
237          "l2cap",
238          "deactivating channel (link: %#.4x, id: %#.4x)",
239          link_handle(),
240          id());
241 
242   // De-activating on a closed link has no effect.
243   if (!link_.is_alive() || !active_) {
244     link_ = internal::LogicalLinkWeakPtr();
245     return;
246   }
247 
248   auto link = link_;
249 
250   CleanUp();
251 
252   // |link| is expected to ignore this call if it has been closed.
253   link->RemoveChannel(this, /*removed_cb=*/[] {});
254 }
255 
SignalLinkError()256 void ChannelImpl::SignalLinkError() {
257   // Cannot signal an error on a closed or deactivated link.
258   if (!link_.is_alive() || !active_)
259     return;
260 
261   // |link_| is expected to ignore this call if it has been closed.
262   link_->SignalError();
263 }
264 
Send(ByteBufferPtr sdu)265 bool ChannelImpl::Send(ByteBufferPtr sdu) {
266   PW_DCHECK(sdu);
267 
268   TRACE_DURATION(
269       "bluetooth", "l2cap:send", "handle", link_->handle(), "id", id());
270 
271   if (!link_.is_alive()) {
272     bt_log(ERROR, "l2cap", "cannot send SDU on a closed link");
273     return false;
274   }
275 
276   if (!active_) {
277     bt_log(INFO, "l2cap", "not sending SDU on an inactive channel");
278     return false;
279   }
280 
281   // Cap the Tx SDU queue to |max_tx_queued|.
282   if (pending_tx_sdus_.size() > max_tx_queued()) {
283     return false;
284   }
285 
286   pending_tx_sdus_.push(std::move(sdu));
287   tx_engine_->NotifySduQueued();
288   return true;
289 }
290 
GetNextOutboundPacket()291 std::unique_ptr<hci::ACLDataPacket> ChannelImpl::GetNextOutboundPacket() {
292   // Channel's next packet is a starting fragment
293   if (!HasFragments() && HasPDUs()) {
294     // B-frames for Basic Mode contain only an "Information payload" (v5.0 Vol
295     // 3, Part A, Sec 3.1)
296     FrameCheckSequenceOption fcs_option =
297         info().mode == RetransmissionAndFlowControlMode::kEnhancedRetransmission
298             ? FrameCheckSequenceOption::kIncludeFcs
299             : FrameCheckSequenceOption::kNoFcs;
300     // Get new PDU and release fragments
301     auto pdu =
302         fragmenter_.BuildFrame(remote_id(),
303                                *pending_tx_pdus_.front(),
304                                fcs_option,
305                                /*flushable=*/info().flush_timeout.has_value());
306     pending_tx_fragments_ = pdu.ReleaseFragments();
307     pending_tx_pdus_.pop();
308   }
309 
310   // Send next packet if it exists
311   std::unique_ptr<hci::ACLDataPacket> fragment = nullptr;
312   if (HasFragments()) {
313     fragment = std::move(pending_tx_fragments_.front());
314     pending_tx_fragments_.pop_front();
315   }
316   return fragment;
317 }
318 
UpgradeSecurity(sm::SecurityLevel level,sm::ResultFunction<> callback)319 void ChannelImpl::UpgradeSecurity(sm::SecurityLevel level,
320                                   sm::ResultFunction<> callback) {
321   PW_CHECK(callback);
322 
323   if (!link_.is_alive() || !active_) {
324     bt_log(DEBUG, "l2cap", "Ignoring security request on inactive channel");
325     return;
326   }
327 
328   link_->UpgradeSecurity(level, std::move(callback));
329 }
330 
RequestAclPriority(AclPriority priority,fit::callback<void (fit::result<fit::failed>)> callback)331 void ChannelImpl::RequestAclPriority(
332     AclPriority priority,
333     fit::callback<void(fit::result<fit::failed>)> callback) {
334   if (!link_.is_alive() || !active_) {
335     bt_log(DEBUG, "l2cap", "Ignoring ACL priority request on inactive channel");
336     callback(fit::failed());
337     return;
338   }
339 
340   // Callback is only called after checking that the weak pointer passed is
341   // alive, so using this in lambda is safe.
342   link_->RequestAclPriority(
343       GetWeakPtr(),
344       priority,
345       [self = GetWeakPtr(), this, priority, cb = std::move(callback)](
346           auto result) mutable {
347         if (self.is_alive() && result.is_ok()) {
348           requested_acl_priority_ = priority;
349         }
350         cb(result);
351       });
352 }
353 
SetBrEdrAutomaticFlushTimeout(pw::chrono::SystemClock::duration flush_timeout,hci::ResultCallback<> callback)354 void ChannelImpl::SetBrEdrAutomaticFlushTimeout(
355     pw::chrono::SystemClock::duration flush_timeout,
356     hci::ResultCallback<> callback) {
357   PW_CHECK(link_type_ == bt::LinkType::kACL);
358 
359   // Channel may be inactive if this method is called before activation.
360   if (!link_.is_alive()) {
361     bt_log(DEBUG, "l2cap", "Ignoring %s on closed channel", __FUNCTION__);
362     callback(ToResult(pw::bluetooth::emboss::StatusCode::COMMAND_DISALLOWED));
363     return;
364   }
365 
366   auto cb_wrapper = [self = GetWeakPtr(),
367                      this,
368                      cb = std::move(callback),
369                      flush_timeout](auto result) mutable {
370     if (!self.is_alive()) {
371       cb(ToResult(pw::bluetooth::emboss::StatusCode::UNSPECIFIED_ERROR));
372       return;
373     }
374 
375     if (result.is_ok()) {
376       info_.flush_timeout = flush_timeout;
377     }
378 
379     cb(result);
380   };
381 
382   link_->SetBrEdrAutomaticFlushTimeout(flush_timeout, std::move(cb_wrapper));
383 }
384 
AttachInspect(inspect::Node & parent,std::string name)385 void ChannelImpl::AttachInspect(inspect::Node& parent, std::string name) {
386   inspect_.node = parent.CreateChild(name);
387   if (info_.psm) {
388     inspect_.psm = inspect_.node.CreateString(kInspectPsmPropertyName,
389                                               PsmToString(info_.psm.value()));
390   }
391   inspect_.local_id = inspect_.node.CreateString(
392       kInspectLocalIdPropertyName,
393       bt_lib_cpp_string::StringPrintf("%#.4x", id()));
394   inspect_.remote_id = inspect_.node.CreateString(
395       kInspectRemoteIdPropertyName,
396       bt_lib_cpp_string::StringPrintf("%#.4x", remote_id()));
397 
398   if (dropped_packets) {
399     inspect_.dropped_packets = inspect_.node.CreateUint(
400         kInspectDroppedPacketsPropertyName, dropped_packets);
401   }
402 }
403 
StartA2dpOffload(const A2dpOffloadManager::Configuration & config,hci::ResultCallback<> callback)404 void ChannelImpl::StartA2dpOffload(
405     const A2dpOffloadManager::Configuration& config,
406     hci::ResultCallback<> callback) {
407   a2dp_offload_manager_.StartA2dpOffload(config,
408                                          id(),
409                                          remote_id(),
410                                          link_handle(),
411                                          max_tx_sdu_size(),
412                                          std::move(callback));
413 }
414 
StopA2dpOffload(hci::ResultCallback<> callback)415 void ChannelImpl::StopA2dpOffload(hci::ResultCallback<> callback) {
416   a2dp_offload_manager_.RequestStopA2dpOffload(
417       id(), link_handle(), std::move(callback));
418 }
419 
OnClosed()420 void ChannelImpl::OnClosed() {
421   bt_log(TRACE,
422          "l2cap",
423          "channel closed (link: %#.4x, id: %#.4x)",
424          link_handle(),
425          id());
426 
427   if (!link_.is_alive() || !active_) {
428     link_ = internal::LogicalLinkWeakPtr();
429     return;
430   }
431 
432   PW_CHECK(closed_cb_);
433   auto closed_cb = std::move(closed_cb_);
434 
435   CleanUp();
436 
437   closed_cb();
438 }
439 
HandleRxPdu(PDU && pdu)440 void ChannelImpl::HandleRxPdu(PDU&& pdu) {
441   TRACE_DURATION("bluetooth",
442                  "ChannelImpl::HandleRxPdu",
443                  "handle",
444                  link_->handle(),
445                  "channel_id",
446                  id_);
447 
448   // link_ may be nullptr if a pdu is received after the channel has been
449   // deactivated but before LogicalLink::RemoveChannel has been dispatched
450   if (!link_.is_alive()) {
451     bt_log(TRACE, "l2cap", "ignoring pdu on deactivated channel");
452     return;
453   }
454 
455   PW_CHECK(rx_engine_);
456 
457   ByteBufferPtr sdu = rx_engine_->ProcessPdu(std::move(pdu));
458   if (!sdu) {
459     // The PDU may have been invalid, out-of-sequence, or part of a segmented
460     // SDU.
461     // * If invalid, we drop the PDU (per Core Spec Ver 5, Vol 3, Part A,
462     //   Secs. 3.3.6 and/or 3.3.7).
463     // * If out-of-sequence or part of a segmented SDU, we expect that some
464     //   later call to ProcessPdu() will return us an SDU containing this
465     //   PDU's data.
466     return;
467   }
468 
469   // Buffer the packets if the channel hasn't been activated.
470   if (!active_) {
471     pending_rx_sdus_.emplace(std::move(sdu));
472     // Tracing: we assume pending_rx_sdus_ is only filled once and use the
473     // length of queue for trace ids.
474     TRACE_FLOW_BEGIN("bluetooth",
475                      "ChannelImpl::HandleRxPdu queued",
476                      pending_rx_sdus_.size());
477     return;
478   }
479 
480   PW_CHECK(rx_cb_);
481   {
482     TRACE_DURATION("bluetooth", "ChannelImpl::HandleRxPdu callback");
483     rx_cb_(std::move(sdu));
484   }
485 }
486 
CleanUp()487 void ChannelImpl::CleanUp() {
488   RequestAclPriority(AclPriority::kNormal, [](auto result) {
489     if (result.is_error()) {
490       bt_log(WARN, "l2cap", "Resetting ACL priority on channel closed failed");
491     }
492   });
493 
494   a2dp_offload_manager_.RequestStopA2dpOffload(
495       id(), link_handle(), [](auto result) {
496         if (result.is_error()) {
497           bt_log(WARN,
498                  "l2cap",
499                  "Stopping A2DP offloading on channel closed failed: %s",
500                  bt_str(result));
501         }
502       });
503 
504   active_ = false;
505   link_ = internal::LogicalLinkWeakPtr();
506   rx_cb_ = nullptr;
507   closed_cb_ = nullptr;
508   rx_engine_ = nullptr;
509   tx_engine_ = nullptr;
510 }
511 
SendFrame(ByteBufferPtr pdu)512 void ChannelImpl::SendFrame(ByteBufferPtr pdu) {
513   if (!link_.is_alive() || !active_) {
514     bt_log(DEBUG,
515            "l2cap",
516            "dropping ACL packet for inactive connection (handle: %#.4x)",
517            link_->handle());
518     return;
519   }
520 
521   pending_tx_pdus_.emplace(std::move(pdu));
522 
523   // Ensure that |pending_tx_pdus_| does not exceed its maximum queue size
524   if (pending_tx_pdus_.size() > max_tx_queued()) {
525     if (dropped_packets % 100 == 0) {
526       bt_log(DEBUG,
527              "l2cap",
528              "Queued packets (%zu) exceeds maximum (%u). "
529              "Dropping oldest ACL packet (handle: %#.4x)",
530              pending_tx_pdus_.size(),
531              max_tx_queued(),
532              link_->handle());
533 
534       inspect_.dropped_packets.Set(dropped_packets);
535     }
536     dropped_packets += 1;
537 
538     pending_tx_pdus_.pop();  // Remove the oldest (aka first) element
539   }
540 
541   // Notify LogicalLink that a packet is available. This is only necessary for
542   // the first packet of an empty queue (flow control will poll this connection
543   // otherwise).
544   if (pending_tx_pdus_.size() == 1u) {
545     link_->OnOutboundPacketAvailable();
546   }
547 }
548 
GetNextQueuedSdu()549 std::optional<ByteBufferPtr> ChannelImpl::GetNextQueuedSdu() {
550   if (pending_tx_sdus_.empty())
551     return std::nullopt;
552   ByteBufferPtr next_sdu = std::move(pending_tx_sdus_.front());
553   pending_tx_sdus_.pop();
554   return next_sdu;
555 }
556 
557 }  // namespace internal
558 }  // namespace bt::l2cap
559