• 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_l2cap.h"
16 
17 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
18 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
19 
20 namespace bt {
21 
22 using l2cap::testing::FakeChannel;
23 
24 namespace l2cap::testing {
25 namespace {
26 
27 // Use plausible ERTM parameters that do not necessarily match values in
28 // production. See Core Spec v5.0 Vol 3, Part A, Sec 5.4 for meanings.
29 constexpr uint8_t kErtmNFramesInTxWindow = 32;
30 constexpr uint8_t kErtmMaxTransmissions = 8;
31 constexpr uint16_t kMaxTxPduPayloadSize = 1024;
32 
33 }  // namespace
34 
IsLinkConnected(hci_spec::ConnectionHandle handle) const35 bool FakeL2cap::IsLinkConnected(hci_spec::ConnectionHandle handle) const {
36   auto link_iter = links_.find(handle);
37   if (link_iter == links_.end()) {
38     return false;
39   }
40   return link_iter->second.connected;
41 }
42 
TriggerLEConnectionParameterUpdate(hci_spec::ConnectionHandle handle,const hci_spec::LEPreferredConnectionParameters & params)43 void FakeL2cap::TriggerLEConnectionParameterUpdate(
44     hci_spec::ConnectionHandle handle,
45     const hci_spec::LEPreferredConnectionParameters& params) {
46   LinkData& link_data = ConnectedLinkData(handle);
47   link_data.le_conn_param_cb(params);
48 }
49 
ExpectOutboundL2capChannel(hci_spec::ConnectionHandle handle,l2cap::Psm psm,l2cap::ChannelId id,l2cap::ChannelId remote_id,l2cap::ChannelParameters params)50 void FakeL2cap::ExpectOutboundL2capChannel(hci_spec::ConnectionHandle handle,
51                                            l2cap::Psm psm,
52                                            l2cap::ChannelId id,
53                                            l2cap::ChannelId remote_id,
54                                            l2cap::ChannelParameters params) {
55   LinkData& link_data = GetLinkData(handle);
56   ChannelData chan_data;
57   chan_data.local_id = id;
58   chan_data.remote_id = remote_id;
59   chan_data.params = params;
60   link_data.expected_outbound_conns[psm].push(chan_data);
61 }
62 
TriggerInboundL2capChannel(hci_spec::ConnectionHandle handle,l2cap::Psm psm,l2cap::ChannelId id,l2cap::ChannelId remote_id,uint16_t max_tx_sdu_size)63 bool FakeL2cap::TriggerInboundL2capChannel(hci_spec::ConnectionHandle handle,
64                                            l2cap::Psm psm,
65                                            l2cap::ChannelId id,
66                                            l2cap::ChannelId remote_id,
67                                            uint16_t max_tx_sdu_size) {
68   LinkData& link_data = ConnectedLinkData(handle);
69   auto cb_iter = registered_services_.find(psm);
70 
71   // No service registered for the PSM.
72   if (cb_iter == registered_services_.end()) {
73     return false;
74   }
75 
76   l2cap::ChannelCallback& cb = cb_iter->second.channel_cb;
77   auto chan_params = cb_iter->second.channel_params;
78   auto mode = chan_params.mode.value_or(
79       l2cap::RetransmissionAndFlowControlMode::kBasic);
80   auto max_rx_sdu_size =
81       chan_params.max_rx_sdu_size.value_or(l2cap::kDefaultMTU);
82   auto channel_info =
83       l2cap::ChannelInfo::MakeBasicMode(max_rx_sdu_size, max_tx_sdu_size);
84   if (mode ==
85       l2cap::RetransmissionAndFlowControlMode::kEnhancedRetransmission) {
86     channel_info = l2cap::ChannelInfo::MakeEnhancedRetransmissionMode(
87         max_rx_sdu_size,
88         max_tx_sdu_size,
89         /*n_frames_in_tx_window=*/kErtmNFramesInTxWindow,
90         /*max_transmissions=*/kErtmMaxTransmissions,
91         /*max_tx_pdu_payload_size=*/kMaxTxPduPayloadSize);
92   }
93 
94   auto chan = OpenFakeChannel(&link_data, id, remote_id, channel_info);
95   cb(chan->GetWeakPtr());
96 
97   return true;
98 }
99 
TriggerLinkError(hci_spec::ConnectionHandle handle)100 void FakeL2cap::TriggerLinkError(hci_spec::ConnectionHandle handle) {
101   LinkData& link_data = ConnectedLinkData(handle);
102 
103   // Safely handle re-entrancy.
104   if (link_data.link_error_signaled) {
105     return;
106   }
107   link_data.link_error_signaled = true;
108 
109   for (auto chan_iter = link_data.channels_.begin();
110        chan_iter != link_data.channels_.end();) {
111     auto& [id, channel] = *chan_iter++;
112     channel->Close();
113     link_data.channels_.erase(id);
114   }
115   link_data.link_error_cb();
116 }
117 
AddACLConnection(hci_spec::ConnectionHandle handle,pw::bluetooth::emboss::ConnectionRole role,l2cap::LinkErrorCallback link_error_cb,l2cap::SecurityUpgradeCallback security_cb)118 ChannelManager::BrEdrFixedChannels FakeL2cap::AddACLConnection(
119     hci_spec::ConnectionHandle handle,
120     pw::bluetooth::emboss::ConnectionRole role,
121     l2cap::LinkErrorCallback link_error_cb,
122     l2cap::SecurityUpgradeCallback security_cb) {
123   LinkData* link = RegisterInternal(
124       handle, role, bt::LinkType::kACL, std::move(link_error_cb));
125   auto smp = OpenFakeFixedChannel(link, l2cap::kSMPChannelId);
126   return BrEdrFixedChannels{.smp = std::move(smp)};
127 }
128 
AddLEConnection(hci_spec::ConnectionHandle handle,pw::bluetooth::emboss::ConnectionRole role,l2cap::LinkErrorCallback link_error_cb,l2cap::LEConnectionParameterUpdateCallback conn_param_cb,l2cap::SecurityUpgradeCallback security_cb)129 ChannelManager::LEFixedChannels FakeL2cap::AddLEConnection(
130     hci_spec::ConnectionHandle handle,
131     pw::bluetooth::emboss::ConnectionRole role,
132     l2cap::LinkErrorCallback link_error_cb,
133     l2cap::LEConnectionParameterUpdateCallback conn_param_cb,
134     l2cap::SecurityUpgradeCallback security_cb) {
135   LinkData* data = RegisterInternal(
136       handle, role, bt::LinkType::kLE, std::move(link_error_cb));
137   data->le_conn_param_cb = std::move(conn_param_cb);
138 
139   // Open the ATT and SMP fixed channels.
140   auto att = OpenFakeFixedChannel(data, l2cap::kATTChannelId);
141   auto smp = OpenFakeFixedChannel(data, l2cap::kLESMPChannelId);
142   return LEFixedChannels{.att = att->GetWeakPtr(), .smp = smp->GetWeakPtr()};
143 }
144 
RemoveConnection(hci_spec::ConnectionHandle handle)145 void FakeL2cap::RemoveConnection(hci_spec::ConnectionHandle handle) {
146   links_.erase(handle);
147 }
148 
AssignLinkSecurityProperties(hci_spec::ConnectionHandle handle,sm::SecurityProperties security)149 void FakeL2cap::AssignLinkSecurityProperties(hci_spec::ConnectionHandle handle,
150                                              sm::SecurityProperties security) {
151   // TODO(armansito): implement
152 }
153 
RequestConnectionParameterUpdate(hci_spec::ConnectionHandle handle,hci_spec::LEPreferredConnectionParameters params,l2cap::ConnectionParameterUpdateRequestCallback request_cb)154 void FakeL2cap::RequestConnectionParameterUpdate(
155     hci_spec::ConnectionHandle handle,
156     hci_spec::LEPreferredConnectionParameters params,
157     l2cap::ConnectionParameterUpdateRequestCallback request_cb) {
158   bool response =
159       connection_parameter_update_request_responder_
160           ? connection_parameter_update_request_responder_(handle, params)
161           : true;
162   // Simulate async response.
163   (void)heap_dispatcher_.Post(
164       [request_cb = std::move(request_cb), response](pw::async::Context /*ctx*/,
165                                                      pw::Status status) {
166         if (status.ok()) {
167           request_cb(response);
168         }
169       });
170 }
171 
OpenL2capChannel(hci_spec::ConnectionHandle handle,l2cap::Psm psm,l2cap::ChannelParameters params,l2cap::ChannelCallback cb)172 void FakeL2cap::OpenL2capChannel(hci_spec::ConnectionHandle handle,
173                                  l2cap::Psm psm,
174                                  l2cap::ChannelParameters params,
175                                  l2cap::ChannelCallback cb) {
176   LinkData& link_data = ConnectedLinkData(handle);
177   auto psm_it = link_data.expected_outbound_conns.find(psm);
178 
179   BT_DEBUG_ASSERT_MSG(psm_it != link_data.expected_outbound_conns.end() &&
180                           !psm_it->second.empty(),
181                       "Unexpected outgoing L2CAP connection (PSM %#.4x)",
182                       psm);
183 
184   auto chan_data = psm_it->second.front();
185   psm_it->second.pop();
186 
187   auto mode =
188       params.mode.value_or(l2cap::RetransmissionAndFlowControlMode::kBasic);
189   auto max_rx_sdu_size = params.max_rx_sdu_size.value_or(l2cap::kMaxMTU);
190 
191   BT_ASSERT_MSG(chan_data.params == params,
192                 "Didn't receive expected L2CAP channel parameters (expected: "
193                 "%s, found: %s)",
194                 bt_str(chan_data.params),
195                 bt_str(params));
196 
197   auto channel_info =
198       l2cap::ChannelInfo::MakeBasicMode(max_rx_sdu_size, l2cap::kDefaultMTU);
199   if (mode ==
200       l2cap::RetransmissionAndFlowControlMode::kEnhancedRetransmission) {
201     channel_info = l2cap::ChannelInfo::MakeEnhancedRetransmissionMode(
202         max_rx_sdu_size,
203         l2cap::kDefaultMTU,
204         /*n_frames_in_tx_window=*/kErtmNFramesInTxWindow,
205         /*max_transmissions=*/kErtmMaxTransmissions,
206         /*max_tx_pdu_payload_size=*/kMaxTxPduPayloadSize);
207   }
208 
209   auto fake_chan = OpenFakeChannel(
210       &link_data, chan_data.local_id, chan_data.remote_id, channel_info);
211   l2cap::Channel::WeakPtr chan;
212   if (fake_chan.is_alive()) {
213     chan = fake_chan->GetWeakPtr();
214   }
215 
216   // Simulate async channel creation process.
217   (void)heap_dispatcher_.Post(
218       [cb = std::move(cb), chan = std::move(chan)](pw::async::Context /*ctx*/,
219                                                    pw::Status status) {
220         if (status.ok()) {
221           cb(chan);
222         }
223       });
224 }
225 
RegisterService(l2cap::Psm psm,l2cap::ChannelParameters params,l2cap::ChannelCallback channel_callback)226 bool FakeL2cap::RegisterService(l2cap::Psm psm,
227                                 l2cap::ChannelParameters params,
228                                 l2cap::ChannelCallback channel_callback) {
229   BT_DEBUG_ASSERT(registered_services_.count(psm) == 0);
230   registered_services_.emplace(
231       psm, ServiceInfo(params, std::move(channel_callback)));
232   return true;
233 }
234 
UnregisterService(l2cap::Psm psm)235 void FakeL2cap::UnregisterService(l2cap::Psm psm) {
236   registered_services_.erase(psm);
237 }
238 
~FakeL2cap()239 FakeL2cap::~FakeL2cap() {
240   for (auto& link_it : links_) {
241     for (auto& psm_it : link_it.second.expected_outbound_conns) {
242       BT_DEBUG_ASSERT_MSG(psm_it.second.empty(),
243                           "didn't receive expected connection on PSM %#.4x",
244                           psm_it.first);
245     }
246   }
247 }
248 
RegisterInternal(hci_spec::ConnectionHandle handle,pw::bluetooth::emboss::ConnectionRole role,bt::LinkType link_type,l2cap::LinkErrorCallback link_error_cb)249 FakeL2cap::LinkData* FakeL2cap::RegisterInternal(
250     hci_spec::ConnectionHandle handle,
251     pw::bluetooth::emboss::ConnectionRole role,
252     bt::LinkType link_type,
253     l2cap::LinkErrorCallback link_error_cb) {
254   auto& data = GetLinkData(handle);
255   BT_DEBUG_ASSERT_MSG(
256       !data.connected, "connection handle re-used (handle: %#.4x)", handle);
257 
258   data.connected = true;
259   data.role = role;
260   data.type = link_type;
261   data.link_error_cb = std::move(link_error_cb);
262 
263   return &data;
264 }
265 
OpenFakeChannel(LinkData * link,l2cap::ChannelId id,l2cap::ChannelId remote_id,l2cap::ChannelInfo info)266 FakeChannel::WeakPtr FakeL2cap::OpenFakeChannel(LinkData* link,
267                                                 l2cap::ChannelId id,
268                                                 l2cap::ChannelId remote_id,
269                                                 l2cap::ChannelInfo info) {
270   FakeChannel::WeakPtr chan;
271   if (!simulate_open_channel_failure_) {
272     auto channel = std::make_unique<FakeChannel>(
273         id, remote_id, link->handle, link->type, info);
274     chan = channel->AsWeakPtr();
275     channel->SetLinkErrorCallback(
276         [this, handle = link->handle] { TriggerLinkError(handle); });
277     link->channels_.emplace(id, std::move(channel));
278   }
279 
280   if (chan_cb_) {
281     chan_cb_(chan);
282   }
283 
284   return chan;
285 }
286 
OpenFakeFixedChannel(LinkData * link,l2cap::ChannelId id)287 FakeChannel::WeakPtr FakeL2cap::OpenFakeFixedChannel(LinkData* link,
288                                                      l2cap::ChannelId id) {
289   return OpenFakeChannel(link, id, id);
290 }
291 
GetLinkData(hci_spec::ConnectionHandle handle)292 FakeL2cap::LinkData& FakeL2cap::GetLinkData(hci_spec::ConnectionHandle handle) {
293   auto [it, inserted] = links_.try_emplace(handle);
294   auto& data = it->second;
295   if (inserted) {
296     data.connected = false;
297     data.handle = handle;
298   }
299   return data;
300 }
301 
ConnectedLinkData(hci_spec::ConnectionHandle handle)302 FakeL2cap::LinkData& FakeL2cap::ConnectedLinkData(
303     hci_spec::ConnectionHandle handle) {
304   auto link_iter = links_.find(handle);
305   BT_DEBUG_ASSERT_MSG(
306       link_iter != links_.end(), "fake link not found (handle: %#.4x)", handle);
307   BT_DEBUG_ASSERT_MSG(link_iter->second.connected,
308                       "fake link not connected yet (handle: %#.4x)",
309                       handle);
310   return link_iter->second;
311 }
312 
313 }  // namespace l2cap::testing
314 }  // namespace bt
315