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