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