• 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/fake_channel.h"
16 
17 #include <pw_assert/check.h>
18 
19 #include "pw_bluetooth_sapphire/internal/host/common/host_error.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
21 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
22 
23 namespace bt::l2cap::testing {
24 
FakeChannel(ChannelId id,ChannelId remote_id,hci_spec::ConnectionHandle handle,bt::LinkType link_type,ChannelInfo info,uint16_t max_tx_queued)25 FakeChannel::FakeChannel(ChannelId id,
26                          ChannelId remote_id,
27                          hci_spec::ConnectionHandle handle,
28                          bt::LinkType link_type,
29                          ChannelInfo info,
30                          uint16_t max_tx_queued)
31     : Channel(id, remote_id, link_type, handle, info, max_tx_queued),
32       handle_(handle),
33       fragmenter_(handle),
34       activate_fails_(false),
35       link_error_(false),
36       acl_priority_fails_(false),
37       weak_fake_chan_(this) {}
38 
Receive(const ByteBuffer & data)39 void FakeChannel::Receive(const ByteBuffer& data) {
40   auto pdu =
41       fragmenter_.BuildFrame(id(), data, FrameCheckSequenceOption::kNoFcs);
42   auto sdu = std::make_unique<DynamicByteBuffer>(pdu.length());
43   pdu.Copy(sdu.get());
44   if (rx_cb_) {
45     rx_cb_(std::move(sdu));
46   } else {
47     pending_rx_sdus_.push(std::move(sdu));
48   }
49 }
50 
SetSendCallback(SendCallback callback)51 void FakeChannel::SetSendCallback(SendCallback callback) {
52   send_cb_ = std::move(callback);
53 }
54 
SetSendCallback(SendCallback callback,pw::async::Dispatcher & dispatcher)55 void FakeChannel::SetSendCallback(SendCallback callback,
56                                   pw::async::Dispatcher& dispatcher) {
57   SetSendCallback(std::move(callback));
58   send_dispatcher_.emplace(dispatcher);
59 }
60 
SetLinkErrorCallback(LinkErrorCallback callback)61 void FakeChannel::SetLinkErrorCallback(LinkErrorCallback callback) {
62   link_err_cb_ = std::move(callback);
63 }
64 
SetSecurityCallback(SecurityUpgradeCallback callback,pw::async::Dispatcher & dispatcher)65 void FakeChannel::SetSecurityCallback(SecurityUpgradeCallback callback,
66                                       pw::async::Dispatcher& dispatcher) {
67   security_cb_ = std::move(callback);
68   security_dispatcher_.emplace(dispatcher);
69 }
70 
Close()71 void FakeChannel::Close() {
72   if (closed_cb_)
73     closed_cb_();
74 }
75 
Activate(RxCallback rx_callback,ClosedCallback closed_callback)76 bool FakeChannel::Activate(RxCallback rx_callback,
77                            ClosedCallback closed_callback) {
78   PW_DCHECK(rx_callback);
79   PW_DCHECK(closed_callback);
80   PW_DCHECK(!rx_cb_);
81   PW_DCHECK(!closed_cb_);
82 
83   if (activate_fails_)
84     return false;
85 
86   closed_cb_ = std::move(closed_callback);
87   rx_cb_ = std::move(rx_callback);
88 
89   while (!pending_rx_sdus_.empty()) {
90     rx_cb_(std::move(pending_rx_sdus_.front()));
91     pending_rx_sdus_.pop();
92   }
93 
94   return true;
95 }
96 
Deactivate()97 void FakeChannel::Deactivate() {
98   closed_cb_ = {};
99   rx_cb_ = {};
100 }
101 
SignalLinkError()102 void FakeChannel::SignalLinkError() {
103   if (link_error_) {
104     return;
105   }
106   link_error_ = true;
107 
108   if (link_err_cb_) {
109     link_err_cb_();
110   }
111 }
112 
Send(ByteBufferPtr sdu)113 bool FakeChannel::Send(ByteBufferPtr sdu) {
114   PW_DCHECK(sdu);
115 
116   if (!send_cb_)
117     return false;
118 
119   if (sdu->size() > max_tx_sdu_size()) {
120     bt_log(ERROR,
121            "l2cap",
122            "Dropping oversized SDU (sdu->size()=%zu, max_tx_sdu_size()=%u)",
123            sdu->size(),
124            max_tx_sdu_size());
125     return false;
126   }
127 
128   if (send_dispatcher_) {
129     (void)send_dispatcher_->Post(
130         [cb = send_cb_.share(), sdu = std::move(sdu)](
131             pw::async::Context /*ctx*/, pw::Status status) mutable {
132           if (status.ok()) {
133             cb(std::move(sdu));
134           }
135         });
136   } else {
137     send_cb_(std::move(sdu));
138   }
139 
140   return true;
141 }
142 
UpgradeSecurity(sm::SecurityLevel level,sm::ResultFunction<> callback)143 void FakeChannel::UpgradeSecurity(sm::SecurityLevel level,
144                                   sm::ResultFunction<> callback) {
145   PW_CHECK(security_dispatcher_);
146   (void)security_dispatcher_->Post(
147       [cb = std::move(callback),
148        f = security_cb_.share(),
149        handle = handle_,
150        level](pw::async::Context /*ctx*/, pw::Status status) mutable {
151         if (status.ok()) {
152           f(handle, level, std::move(cb));
153         }
154       });
155 }
156 
RequestAclPriority(pw::bluetooth::AclPriority priority,fit::callback<void (fit::result<fit::failed>)> cb)157 void FakeChannel::RequestAclPriority(
158     pw::bluetooth::AclPriority priority,
159     fit::callback<void(fit::result<fit::failed>)> cb) {
160   if (acl_priority_fails_) {
161     cb(fit::failed());
162     return;
163   }
164   requested_acl_priority_ = priority;
165   cb(fit::ok());
166 }
167 
SetBrEdrAutomaticFlushTimeout(pw::chrono::SystemClock::duration flush_timeout,hci::ResultCallback<> callback)168 void FakeChannel::SetBrEdrAutomaticFlushTimeout(
169     pw::chrono::SystemClock::duration flush_timeout,
170     hci::ResultCallback<> callback) {
171   if (!flush_timeout_succeeds_) {
172     callback(ToResult(pw::bluetooth::emboss::StatusCode::UNSPECIFIED_ERROR));
173     return;
174   }
175   info_.flush_timeout = flush_timeout;
176   callback(fit::ok());
177 }
178 
StartA2dpOffload(const A2dpOffloadManager::Configuration &,hci::ResultCallback<> callback)179 void FakeChannel::StartA2dpOffload(const A2dpOffloadManager::Configuration&,
180                                    hci::ResultCallback<> callback) {
181   if (a2dp_offload_error_.has_value()) {
182     callback(ToResult(a2dp_offload_error_.value()));
183     audio_offloading_status_ = A2dpOffloadStatus::kStopped;
184     return;
185   }
186   audio_offloading_status_ = A2dpOffloadStatus::kStarted;
187   callback(fit::ok());
188 }
189 
StopA2dpOffload(hci::ResultCallback<> callback)190 void FakeChannel::StopA2dpOffload(hci::ResultCallback<> callback) {
191   if (a2dp_offload_error_.has_value()) {
192     callback(ToResult(a2dp_offload_error_.value()));
193     return;
194   }
195   audio_offloading_status_ = A2dpOffloadStatus::kStopped;
196   callback(fit::ok());
197 }
198 
199 }  // namespace bt::l2cap::testing
200