• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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