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