1 // Copyright 2024 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_proxy_private/test_utils.h"
16
17 #include <cstdint>
18
19 #include "pw_allocator/testing.h"
20 #include "pw_bluetooth/emboss_util.h"
21 #include "pw_bluetooth/hci_common.emb.h"
22 #include "pw_bluetooth/hci_data.emb.h"
23 #include "pw_bluetooth/hci_events.emb.h"
24 #include "pw_bluetooth/hci_h4.emb.h"
25 #include "pw_bluetooth/l2cap_frames.emb.h"
26 #include "pw_bluetooth_proxy/basic_l2cap_channel.h"
27 #include "pw_bluetooth_proxy/h4_packet.h"
28 #include "pw_bluetooth_proxy/internal/logical_transport.h"
29 #include "pw_bluetooth_proxy/l2cap_channel_common.h"
30 #include "pw_bluetooth_proxy/l2cap_status_delegate.h"
31 #include "pw_bluetooth_proxy/proxy_host.h"
32 #include "pw_status/status.h"
33 #include "pw_status/try.h"
34 #include "pw_unit_test/framework.h"
35
36 namespace pw::bluetooth::proxy {
37
38 // Allocate storage and populate an ACL packet header with the given length.
SetupAcl(uint16_t handle,uint16_t l2cap_length)39 Result<AclFrameWithStorage> SetupAcl(uint16_t handle, uint16_t l2cap_length) {
40 AclFrameWithStorage frame;
41
42 frame.storage.resize(l2cap_length + emboss::AclDataFrame::MinSizeInBytes() +
43 AclFrameWithStorage::kH4HeaderSize);
44 std::fill(frame.storage.begin(), frame.storage.end(), 0);
45 PW_TRY_ASSIGN(frame.writer,
46 MakeEmbossWriter<emboss::AclDataFrameWriter>(frame.hci_span()));
47 frame.writer.header().handle().Write(handle);
48 frame.writer.data_total_length().Write(l2cap_length);
49 EXPECT_EQ(l2cap_length,
50 frame.writer.payload().BackingStorage().SizeInBytes());
51 return frame;
52 }
53
SetupBFrame(uint16_t handle,uint16_t channel_id,uint16_t bframe_len)54 Result<BFrameWithStorage> SetupBFrame(uint16_t handle,
55 uint16_t channel_id,
56 uint16_t bframe_len) {
57 BFrameWithStorage frame;
58 PW_TRY_ASSIGN(
59 frame.acl,
60 SetupAcl(handle,
61 bframe_len + emboss::BasicL2capHeader::IntrinsicSizeInBytes()));
62
63 PW_TRY_ASSIGN(frame.writer,
64 MakeEmbossWriter<emboss::BFrameWriter>(
65 frame.acl.writer.payload().BackingStorage().data(),
66 frame.acl.writer.payload().SizeInBytes()));
67 frame.writer.pdu_length().Write(bframe_len);
68 frame.writer.channel_id().Write(channel_id);
69 EXPECT_TRUE(frame.writer.Ok());
70 EXPECT_EQ(frame.writer.payload().SizeInBytes(), bframe_len);
71 return frame;
72 }
73
SetupCFrame(uint16_t handle,uint16_t channel_id,uint16_t cframe_len)74 Result<CFrameWithStorage> SetupCFrame(uint16_t handle,
75 uint16_t channel_id,
76 uint16_t cframe_len) {
77 CFrameWithStorage frame;
78 PW_TRY_ASSIGN(
79 frame.acl,
80 SetupAcl(handle,
81 cframe_len + emboss::BasicL2capHeader::IntrinsicSizeInBytes()));
82
83 PW_TRY_ASSIGN(frame.writer,
84 MakeEmbossWriter<emboss::CFrameWriter>(
85 frame.acl.writer.payload().BackingStorage().data(),
86 frame.acl.writer.payload().SizeInBytes()));
87 frame.writer.pdu_length().Write(cframe_len);
88 frame.writer.channel_id().Write(channel_id);
89 EXPECT_TRUE(frame.writer.Ok());
90 EXPECT_EQ(frame.writer.payload().SizeInBytes(), cframe_len);
91 return frame;
92 }
93
SetupKFrame(uint16_t handle,uint16_t channel_id,uint16_t mps,uint16_t segment_no,span<const uint8_t> payload)94 Result<KFrameWithStorage> SetupKFrame(uint16_t handle,
95 uint16_t channel_id,
96 uint16_t mps,
97 uint16_t segment_no,
98 span<const uint8_t> payload) {
99 uint16_t sdu_length_field_offset = segment_no == 0 ? kSduLengthFieldSize : 0;
100 // Requested segment encodes payload starting at `payload[payload_offset]`.
101 uint16_t payload_offset =
102 segment_no * mps - (segment_no == 0 ? 0 : kSduLengthFieldSize);
103 if (payload_offset >= payload.size()) {
104 return Status::OutOfRange();
105 }
106 uint16_t remaining_payload_length = payload.size() - payload_offset;
107 uint16_t segment_pdu_length =
108 remaining_payload_length + sdu_length_field_offset;
109 if (segment_pdu_length > mps) {
110 segment_pdu_length = mps;
111 }
112
113 KFrameWithStorage kframe;
114 PW_TRY_ASSIGN(kframe.acl,
115 SetupAcl(handle,
116 segment_pdu_length +
117 emboss::BasicL2capHeader::IntrinsicSizeInBytes()));
118
119 if (segment_no == 0) {
120 PW_TRY_ASSIGN(kframe.writer,
121 MakeEmbossWriter<emboss::FirstKFrameWriter>(
122 kframe.acl.writer.payload().BackingStorage().data(),
123 kframe.acl.writer.payload().SizeInBytes()));
124 emboss::FirstKFrameWriter first_kframe_writer =
125 std::get<emboss::FirstKFrameWriter>(kframe.writer);
126 first_kframe_writer.pdu_length().Write(segment_pdu_length);
127 first_kframe_writer.channel_id().Write(channel_id);
128 first_kframe_writer.sdu_length().Write(payload.size());
129 EXPECT_TRUE(first_kframe_writer.Ok());
130 PW_CHECK(TryToCopyToEmbossStruct(
131 /*emboss_dest=*/first_kframe_writer.payload(),
132 /*src=*/payload.subspan(payload_offset,
133 segment_pdu_length - sdu_length_field_offset)));
134 } else {
135 PW_TRY_ASSIGN(kframe.writer,
136 MakeEmbossWriter<emboss::SubsequentKFrameWriter>(
137 kframe.acl.writer.payload().BackingStorage().data(),
138 kframe.acl.writer.payload().SizeInBytes()));
139 emboss::SubsequentKFrameWriter subsequent_kframe_writer =
140 std::get<emboss::SubsequentKFrameWriter>(kframe.writer);
141 subsequent_kframe_writer.pdu_length().Write(segment_pdu_length);
142 subsequent_kframe_writer.channel_id().Write(channel_id);
143 EXPECT_TRUE(subsequent_kframe_writer.Ok());
144 PW_CHECK(TryToCopyToEmbossStruct(
145 /*emboss_dest=*/subsequent_kframe_writer.payload(),
146 /*src=*/payload.subspan(payload_offset,
147 segment_pdu_length - sdu_length_field_offset)));
148 }
149
150 return kframe;
151 }
152
153 // Send an LE_Read_Buffer_Size (V2) CommandComplete event to `proxy` to request
154 // the reservation of a number of LE ACL send credits.
SendLeReadBufferResponseFromController(ProxyHost & proxy,uint8_t num_credits_to_reserve,uint16_t le_acl_data_packet_length)155 Status SendLeReadBufferResponseFromController(
156 ProxyHost& proxy,
157 uint8_t num_credits_to_reserve,
158 uint16_t le_acl_data_packet_length) {
159 std::array<
160 uint8_t,
161 emboss::LEReadBufferSizeV2CommandCompleteEventWriter::SizeInBytes()>
162 hci_arr{};
163 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
164 PW_TRY_ASSIGN(auto view,
165 CreateAndPopulateToHostEventWriter<
166 emboss::LEReadBufferSizeV2CommandCompleteEventWriter>(
167 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
168 view.command_complete().command_opcode().Write(
169 emboss::OpCode::LE_READ_BUFFER_SIZE_V2);
170 view.total_num_le_acl_data_packets().Write(num_credits_to_reserve);
171 view.le_acl_data_packet_length().Write(le_acl_data_packet_length);
172
173 proxy.HandleH4HciFromController(std::move(h4_packet));
174 return OkStatus();
175 }
176
SendReadBufferResponseFromController(ProxyHost & proxy,uint8_t num_credits_to_reserve)177 Status SendReadBufferResponseFromController(ProxyHost& proxy,
178 uint8_t num_credits_to_reserve) {
179 std::array<uint8_t,
180 emboss::ReadBufferSizeCommandCompleteEventWriter::SizeInBytes()>
181 hci_arr{};
182 H4PacketWithHci h4_packet{emboss::H4PacketType::UNKNOWN, hci_arr};
183 PW_TRY_ASSIGN(auto view,
184 CreateAndPopulateToHostEventWriter<
185 emboss::ReadBufferSizeCommandCompleteEventWriter>(
186 h4_packet, emboss::EventCode::COMMAND_COMPLETE));
187 view.command_complete().command_opcode().Write(
188 emboss::OpCode::READ_BUFFER_SIZE);
189 view.total_num_acl_data_packets().Write(num_credits_to_reserve);
190 view.acl_data_packet_length().Write(0xFFFF);
191 view.synchronous_data_packet_length().Write(0xFF);
192 EXPECT_TRUE(view.Ok());
193
194 proxy.HandleH4HciFromController(std::move(h4_packet));
195 return OkStatus();
196 }
197
198 // Send a Connection_Complete event to `proxy` indicating the provided
199 // `handle` has disconnected.
SendConnectionCompleteEvent(ProxyHost & proxy,uint16_t handle,emboss::StatusCode status)200 Status SendConnectionCompleteEvent(ProxyHost& proxy,
201 uint16_t handle,
202 emboss::StatusCode status) {
203 std::array<uint8_t, emboss::ConnectionCompleteEvent::IntrinsicSizeInBytes()>
204 hci_arr{};
205 H4PacketWithHci h4_packet{emboss::H4PacketType::EVENT, hci_arr};
206 PW_TRY_ASSIGN(
207 auto view,
208 CreateAndPopulateToHostEventWriter<emboss::ConnectionCompleteEventWriter>(
209 h4_packet, emboss::EventCode::CONNECTION_COMPLETE));
210 view.status().Write(status);
211 view.connection_handle().Write(handle);
212 proxy.HandleH4HciFromController(std::move(h4_packet));
213 return OkStatus();
214 }
215
216 // Send a LE_Connection_Complete event to `proxy` indicating the provided
217 // `handle` has disconnected.
SendLeConnectionCompleteEvent(ProxyHost & proxy,uint16_t handle,emboss::StatusCode status)218 Status SendLeConnectionCompleteEvent(ProxyHost& proxy,
219 uint16_t handle,
220 emboss::StatusCode status) {
221 std::array<uint8_t,
222 emboss::LEConnectionCompleteSubevent::IntrinsicSizeInBytes()>
223 hci_arr_dc{};
224 H4PacketWithHci dc_event{emboss::H4PacketType::EVENT, hci_arr_dc};
225 PW_TRY_ASSIGN(auto view,
226 MakeEmbossWriter<emboss::LEConnectionCompleteSubeventWriter>(
227 dc_event.GetHciSpan()));
228 view.le_meta_event().header().event_code().Write(
229 emboss::EventCode::LE_META_EVENT);
230 view.le_meta_event().subevent_code_enum().Write(
231 emboss::LeSubEventCode::CONNECTION_COMPLETE);
232 view.status().Write(status);
233 view.connection_handle().Write(handle);
234 proxy.HandleH4HciFromController(std::move(dc_event));
235 return OkStatus();
236 }
237
238 // Send a Disconnection_Complete event to `proxy` indicating the provided
239 // `handle` has disconnected.
SendDisconnectionCompleteEvent(ProxyHost & proxy,uint16_t handle,Direction direction,bool successful)240 Status SendDisconnectionCompleteEvent(ProxyHost& proxy,
241 uint16_t handle,
242 Direction direction,
243 bool successful) {
244 std::array<uint8_t,
245 sizeof(emboss::H4PacketType) +
246 emboss::DisconnectionCompleteEvent::IntrinsicSizeInBytes()>
247 h4_arr_dc;
248 h4_arr_dc.fill(0);
249 H4PacketWithHci dc_event_from_controller{emboss::H4PacketType::EVENT,
250 pw::span(h4_arr_dc).subspan(1)};
251 H4PacketWithH4 dc_event_from_host{emboss::H4PacketType::EVENT, h4_arr_dc};
252 PW_TRY_ASSIGN(auto view,
253 MakeEmbossWriter<emboss::DisconnectionCompleteEventWriter>(
254 dc_event_from_controller.GetHciSpan()));
255 view.header().event_code().Write(emboss::EventCode::DISCONNECTION_COMPLETE);
256 view.header().parameter_total_size().Write(
257 emboss::DisconnectionCompleteEvent::IntrinsicSizeInBytes() -
258 emboss::EventHeader::IntrinsicSizeInBytes());
259 view.status().Write(successful ? emboss::StatusCode::SUCCESS
260 : emboss::StatusCode::HARDWARE_FAILURE);
261 view.connection_handle().Write(handle);
262 if (direction == Direction::kFromController) {
263 proxy.HandleH4HciFromController(std::move(dc_event_from_controller));
264 } else if (direction == Direction::kFromHost) {
265 proxy.HandleH4HciFromHost(std::move(dc_event_from_host));
266 } else {
267 return Status::InvalidArgument();
268 }
269 return OkStatus();
270 }
271
SendL2capConnectionReq(ProxyHost & proxy,uint16_t handle,uint16_t source_cid,uint16_t psm)272 Status SendL2capConnectionReq(ProxyHost& proxy,
273 uint16_t handle,
274 uint16_t source_cid,
275 uint16_t psm) {
276 // First send CONNECTION_REQ to setup partial connection.
277 constexpr size_t kConnectionReqLen =
278 emboss::L2capConnectionReq::IntrinsicSizeInBytes();
279 PW_TRY_ASSIGN(
280 CFrameWithStorage cframe,
281 SetupCFrame(handle,
282 cpp23::to_underlying(emboss::L2capFixedCid::ACL_U_SIGNALING),
283 kConnectionReqLen));
284
285 emboss::L2capConnectionReqWriter conn_req_writer =
286 emboss::MakeL2capConnectionReqView(
287 cframe.writer.payload().BackingStorage().data(),
288 cframe.writer.payload().SizeInBytes());
289 conn_req_writer.command_header().code().Write(
290 emboss::L2capSignalingPacketCode::CONNECTION_REQ);
291 // Note data_length doesn't include command header.
292 conn_req_writer.command_header().data_length().Write(
293 kConnectionReqLen -
294 emboss::L2capSignalingCommandHeader::IntrinsicSizeInBytes());
295 conn_req_writer.psm().Write(psm);
296 conn_req_writer.source_cid().Write(source_cid);
297
298 H4PacketWithHci connection_req_packet{emboss::H4PacketType::ACL_DATA,
299 cframe.acl.hci_span()};
300 proxy.HandleH4HciFromController(std::move(connection_req_packet));
301 return OkStatus();
302 }
303
SendL2capConnectionRsp(ProxyHost & proxy,uint16_t handle,uint16_t source_cid,uint16_t destination_cid,emboss::L2capConnectionRspResultCode result_code)304 Status SendL2capConnectionRsp(
305 ProxyHost& proxy,
306 uint16_t handle,
307 uint16_t source_cid,
308 uint16_t destination_cid,
309 emboss::L2capConnectionRspResultCode result_code) {
310 constexpr size_t kConnectionRspLen =
311 emboss::L2capConnectionRsp::MinSizeInBytes();
312 PW_TRY_ASSIGN(
313 auto cframe,
314 SetupCFrame(handle,
315 cpp23::to_underlying(emboss::L2capFixedCid::ACL_U_SIGNALING),
316 kConnectionRspLen));
317
318 emboss::L2capConnectionRspWriter conn_rsp_writer =
319 emboss::MakeL2capConnectionRspView(
320 cframe.writer.payload().BackingStorage().data(),
321 cframe.writer.payload().SizeInBytes());
322 conn_rsp_writer.command_header().code().Write(
323 emboss::L2capSignalingPacketCode::CONNECTION_RSP);
324
325 conn_rsp_writer.command_header().data_length().Write(
326 kConnectionRspLen -
327 emboss::L2capSignalingCommandHeader::IntrinsicSizeInBytes());
328 conn_rsp_writer.source_cid().Write(source_cid);
329 conn_rsp_writer.destination_cid().Write(destination_cid);
330 conn_rsp_writer.result().Write(result_code);
331
332 H4PacketWithH4 connection_rsp_packet{emboss::H4PacketType::ACL_DATA,
333 cframe.acl.h4_span()};
334 proxy.HandleH4HciFromHost(std::move(connection_rsp_packet));
335 return OkStatus();
336 }
337
SendL2capDisconnectRsp(ProxyHost & proxy,AclTransportType transport,uint16_t handle,uint16_t source_cid,uint16_t destination_cid,Direction direction)338 Status SendL2capDisconnectRsp(ProxyHost& proxy,
339 AclTransportType transport,
340 uint16_t handle,
341 uint16_t source_cid,
342 uint16_t destination_cid,
343 Direction direction) {
344 constexpr size_t kDisconnectionRspLen =
345 emboss::L2capDisconnectionRsp::MinSizeInBytes();
346 PW_TRY_ASSIGN(
347 auto cframe,
348 SetupCFrame(
349 handle,
350 transport == AclTransportType::kBrEdr
351 ? cpp23::to_underlying(emboss::L2capFixedCid::ACL_U_SIGNALING)
352 : cpp23::to_underlying(emboss::L2capFixedCid::LE_U_SIGNALING),
353 kDisconnectionRspLen));
354
355 emboss::L2capDisconnectionRspWriter disconn_rsp_writer =
356 emboss::MakeL2capDisconnectionRspView(
357 cframe.writer.payload().BackingStorage().data(),
358 cframe.writer.payload().SizeInBytes());
359 disconn_rsp_writer.command_header().code().Write(
360 emboss::L2capSignalingPacketCode::DISCONNECTION_RSP);
361
362 disconn_rsp_writer.command_header().data_length().Write(
363 kDisconnectionRspLen -
364 emboss::L2capSignalingCommandHeader::IntrinsicSizeInBytes());
365 disconn_rsp_writer.source_cid().Write(source_cid);
366 disconn_rsp_writer.destination_cid().Write(destination_cid);
367
368 if (direction == Direction::kFromHost) {
369 H4PacketWithH4 packet{emboss::H4PacketType::ACL_DATA, cframe.acl.h4_span()};
370 proxy.HandleH4HciFromHost(std::move(packet));
371 } else if (direction == Direction::kFromController) {
372 H4PacketWithHci packet{emboss::H4PacketType::ACL_DATA,
373 cframe.acl.hci_span()};
374 proxy.HandleH4HciFromController(std::move(packet));
375 } else {
376 return Status::InvalidArgument();
377 }
378 return OkStatus();
379 }
380
SendL2capBFrame(ProxyHost & proxy,uint16_t handle,pw::span<const uint8_t> payload,size_t pdu_length,uint16_t channel_id)381 void SendL2capBFrame(ProxyHost& proxy,
382 uint16_t handle,
383 pw::span<const uint8_t> payload,
384 size_t pdu_length,
385 uint16_t channel_id) {
386 constexpr size_t kHeadersSize =
387 emboss::AclDataFrameHeader::IntrinsicSizeInBytes() +
388 emboss::BasicL2capHeader::IntrinsicSizeInBytes();
389
390 const size_t acl_data_size =
391 emboss::BasicL2capHeader::IntrinsicSizeInBytes() + payload.size();
392
393 std::vector<uint8_t> hci_buf(kHeadersSize + payload.size());
394 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_buf};
395
396 // ACL header
397 Result<emboss::AclDataFrameWriter> acl =
398 MakeEmbossWriter<emboss::AclDataFrameWriter>(h4_packet.GetHciSpan());
399 acl->header().handle().Write(handle);
400 acl->data_total_length().Write(acl_data_size);
401
402 // L2CAP B-Frame header
403 emboss::BFrameWriter bframe = emboss::MakeBFrameView(
404 acl->payload().BackingStorage().data(), acl->payload().SizeInBytes());
405 bframe.pdu_length().Write(pdu_length);
406 bframe.channel_id().Write(channel_id);
407
408 // Payload
409 std::copy(payload.begin(),
410 payload.end(),
411 h4_packet.GetHciSpan().begin() + kHeadersSize);
412
413 proxy.HandleH4HciFromController(std::move(h4_packet));
414 }
415
SendAclContinuingFrag(ProxyHost & proxy,uint16_t handle,pw::span<const uint8_t> payload)416 void SendAclContinuingFrag(ProxyHost& proxy,
417 uint16_t handle,
418 pw::span<const uint8_t> payload) {
419 constexpr size_t kHeadersSize =
420 emboss::AclDataFrameHeader::IntrinsicSizeInBytes();
421 // No BasicL2capHeader.
422
423 const size_t acl_data_size =
424 // No BasicL2capHeader.
425 payload.size();
426
427 std::vector<uint8_t> hci_buf(kHeadersSize + payload.size());
428 H4PacketWithHci h4_packet{emboss::H4PacketType::ACL_DATA, hci_buf};
429
430 // ACL header
431 Result<emboss::AclDataFrameWriter> acl =
432 MakeEmbossWriter<emboss::AclDataFrameWriter>(h4_packet.GetHciSpan());
433 acl->header().handle().Write(handle);
434 acl->header().packet_boundary_flag().Write(
435 emboss::AclDataPacketBoundaryFlag::CONTINUING_FRAGMENT);
436 acl->data_total_length().Write(acl_data_size);
437
438 // Payload
439 std::copy(payload.begin(),
440 payload.end(),
441 h4_packet.GetHciSpan().begin() + kHeadersSize);
442
443 proxy.HandleH4HciFromController(std::move(h4_packet));
444 }
445
BuildCocWithResult(ProxyHost & proxy,CocParameters params)446 pw::Result<L2capCoc> ProxyHostTest::BuildCocWithResult(ProxyHost& proxy,
447 CocParameters params) {
448 return proxy.AcquireL2capCoc(
449 /*rx_multibuf_allocator=*/sut_multibuf_allocator_,
450 params.handle,
451 {.cid = params.local_cid,
452 .mtu = params.rx_mtu,
453 .mps = params.rx_mps,
454 .credits = params.rx_credits},
455 {.cid = params.remote_cid,
456 .mtu = params.tx_mtu,
457 .mps = params.tx_mps,
458 .credits = params.tx_credits},
459 std::move(params.receive_fn),
460 std::move(params.event_fn));
461 }
462
BuildCoc(ProxyHost & proxy,CocParameters params)463 L2capCoc ProxyHostTest::BuildCoc(ProxyHost& proxy, CocParameters params) {
464 pw::Result<L2capCoc> channel = BuildCocWithResult(proxy, std::move(params));
465 PW_TEST_EXPECT_OK(channel);
466 return std::move(channel.value());
467 }
468
BuildBasicL2capChannelWithResult(ProxyHost & proxy,BasicL2capParameters params)469 Result<BasicL2capChannel> ProxyHostTest::BuildBasicL2capChannelWithResult(
470 ProxyHost& proxy, BasicL2capParameters params) {
471 return proxy.AcquireBasicL2capChannel(
472 sut_multibuf_allocator_,
473 params.handle,
474 params.local_cid,
475 params.remote_cid,
476 params.transport,
477 std::move(params.payload_from_controller_fn),
478 std::move(params.payload_from_host_fn),
479 std::move(params.event_fn));
480 }
481
BuildBasicL2capChannel(ProxyHost & proxy,BasicL2capParameters params)482 BasicL2capChannel ProxyHostTest::BuildBasicL2capChannel(
483 ProxyHost& proxy, BasicL2capParameters params) {
484 pw::Result<BasicL2capChannel> channel =
485 BuildBasicL2capChannelWithResult(proxy, std::move(params));
486 PW_TEST_EXPECT_OK(channel);
487 return std::move(channel.value());
488 }
489
BuildGattNotifyChannelWithResult(ProxyHost & proxy,GattNotifyChannelParameters params)490 Result<GattNotifyChannel> ProxyHostTest::BuildGattNotifyChannelWithResult(
491 ProxyHost& proxy, GattNotifyChannelParameters params) {
492 return proxy.AcquireGattNotifyChannel(
493 params.handle, params.attribute_handle, std::move(params.event_fn));
494 }
495
BuildGattNotifyChannel(ProxyHost & proxy,GattNotifyChannelParameters params)496 GattNotifyChannel ProxyHostTest::BuildGattNotifyChannel(
497 ProxyHost& proxy, GattNotifyChannelParameters params) {
498 pw::Result<GattNotifyChannel> channel =
499 BuildGattNotifyChannelWithResult(proxy, std::move(params));
500 PW_TEST_EXPECT_OK(channel);
501 return std::move(channel.value());
502 }
503
BuildRfcomm(ProxyHost & proxy,RfcommParameters params,Function<void (multibuf::MultiBuf && payload)> && receive_fn,ChannelEventCallback && event_fn)504 RfcommChannel ProxyHostTest::BuildRfcomm(
505 ProxyHost& proxy,
506 RfcommParameters params,
507 Function<void(multibuf::MultiBuf&& payload)>&& receive_fn,
508 ChannelEventCallback&& event_fn) {
509 pw::Result<RfcommChannel> channel = proxy.AcquireRfcommChannel(
510 sut_multibuf_allocator_,
511 params.handle,
512 RfcommChannel::Config{
513 .cid = params.rx_config.cid,
514 .max_information_length = params.rx_config.max_information_length,
515 .credits = params.rx_config.credits},
516 RfcommChannel::Config{
517 .cid = params.tx_config.cid,
518 .max_information_length = params.tx_config.max_information_length,
519 .credits = params.tx_config.credits},
520 params.rfcomm_channel,
521 std::move(receive_fn),
522 std::move(event_fn));
523 PW_TEST_EXPECT_OK(channel);
524 return std::move((channel.value()));
525 }
526
BuildOneOfEachChannel(ProxyHost & proxy,ChannelEventCallback & shared_event_fn)527 OneOfEachChannel ProxyHostTest::BuildOneOfEachChannel(
528 ProxyHost& proxy, ChannelEventCallback& shared_event_fn) {
529 // Each channel its unique cids and its own rvalue lambda which calls the
530 // shared_event_fn.
531 return OneOfEachChannel(
532 BuildBasicL2capChannel(proxy,
533 {.local_cid = 201,
534 .remote_cid = 301,
535 .event_fn =
536 [&shared_event_fn](L2capChannelEvent event) {
537 shared_event_fn(event);
538 }}),
539 BuildCoc(proxy,
540 {.local_cid = 202,
541 .remote_cid = 302,
542 .event_fn =
543 [&shared_event_fn](L2capChannelEvent event) {
544 shared_event_fn(event);
545 }}),
546 BuildRfcomm(proxy,
547 {.rx_config{.cid = 203}, .tx_config = {.cid = 303}},
548 /*receive_fn=*/nullptr,
549 /*event_fn=*/
550 [&shared_event_fn](L2capChannelEvent event) {
551 shared_event_fn(event);
552 }),
553 BuildGattNotifyChannel(
554 proxy, {.event_fn = [&shared_event_fn](L2capChannelEvent event) {
555 shared_event_fn(event);
556 }}));
557 }
558
559 } // namespace pw::bluetooth::proxy
560