• 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/test_packets.h"
16 
17 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
18 #include "pw_bluetooth_sapphire/internal/host/l2cap/fcs.h"
19 #include "pw_bluetooth_sapphire/internal/host/l2cap/frame_headers.h"
20 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
21 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
22 
23 namespace bt::l2cap::testing {
24 
AclExtFeaturesInfoReq(l2cap::CommandId id,hci_spec::ConnectionHandle handle)25 DynamicByteBuffer AclExtFeaturesInfoReq(l2cap::CommandId id,
26                                         hci_spec::ConnectionHandle handle) {
27   return DynamicByteBuffer(StaticByteBuffer(
28       // ACL data header (handle, length: 10)
29       LowerBits(handle),
30       UpperBits(handle),
31       0x0a,
32       0x00,
33 
34       // L2CAP B-frame header (length: 6, chanel-id: 0x0001 (ACL sig))
35       0x06,
36       0x00,
37       0x01,
38       0x00,
39 
40       // Extended Features Information Request
41       // (ID, length: 2, type)
42       0x0a,
43       id,
44       0x02,
45       0x00,
46       LowerBits(
47           static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
48       UpperBits(
49           static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported))));
50 }
51 
AclCommandRejectNotUnderstoodRsp(l2cap::CommandId id,hci_spec::ConnectionHandle handle,ChannelId chan_id)52 DynamicByteBuffer AclCommandRejectNotUnderstoodRsp(
53     l2cap::CommandId id, hci_spec::ConnectionHandle handle, ChannelId chan_id) {
54   return DynamicByteBuffer(StaticByteBuffer(
55       // ACL data header (handle: |link_handle|, length: 10 bytes)
56       LowerBits(handle),
57       UpperBits(handle),
58       0x0a,
59       0x00,
60       // L2CAP B-frame header (length: 6 bytes, channel-id: 0x0001 (ACL sig))
61       0x06,
62       0x00,
63       LowerBits(chan_id),
64       UpperBits(chan_id),
65       // Information Response (type, ID, length: 2)
66       l2cap::kCommandRejectCode,
67       id,
68       0x02,
69       0x00,
70       // Reason = Not Understood
71       LowerBits(static_cast<uint16_t>(RejectReason::kNotUnderstood)),
72       UpperBits(static_cast<uint16_t>(RejectReason::kNotUnderstood))));
73 }
74 
AclExtFeaturesInfoRsp(l2cap::CommandId id,hci_spec::ConnectionHandle handle,l2cap::ExtendedFeatures features)75 DynamicByteBuffer AclExtFeaturesInfoRsp(l2cap::CommandId id,
76                                         hci_spec::ConnectionHandle handle,
77                                         l2cap::ExtendedFeatures features) {
78   const auto features_bytes = ToBytes(features);
79   return DynamicByteBuffer(StaticByteBuffer(
80       // ACL data header (handle: |link_handle|, length: 16 bytes)
81       LowerBits(handle),
82       UpperBits(handle),
83       0x10,
84       0x00,
85       // L2CAP B-frame header (length: 12 bytes, channel-id: 0x0001 (ACL sig))
86       0x0c,
87       0x00,
88       0x01,
89       0x00,
90       // Information Response (type, ID, length: 8)
91       l2cap::kInformationResponse,
92       id,
93       0x08,
94       0x00,
95       // Type = Features Mask
96       0x02,
97       0x00,
98       // Result = Success
99       0x00,
100       0x00,
101       // Data (Mask)
102       features_bytes[0],
103       features_bytes[1],
104       features_bytes[2],
105       features_bytes[3]));
106 }
107 
AclFixedChannelsSupportedInfoReq(l2cap::CommandId id,hci_spec::ConnectionHandle handle)108 DynamicByteBuffer AclFixedChannelsSupportedInfoReq(
109     l2cap::CommandId id, hci_spec::ConnectionHandle handle) {
110   return DynamicByteBuffer(StaticByteBuffer(
111       // ACL data header (handle, length: 10)
112       LowerBits(handle),
113       UpperBits(handle),
114       0x0a,
115       0x00,
116 
117       // L2CAP B-frame header (length: 6, chanel-id: 0x0001 (ACL sig))
118       0x06,
119       0x00,
120       0x01,
121       0x00,
122 
123       // Fixed Channels Supported Information Request
124       // (ID, length: 2, info type)
125       l2cap::kInformationRequest,
126       id,
127       0x02,
128       0x00,
129       LowerBits(
130           static_cast<uint16_t>(InformationType::kFixedChannelsSupported)),
131       UpperBits(
132           static_cast<uint16_t>(InformationType::kFixedChannelsSupported))));
133 }
134 
AclFixedChannelsSupportedInfoRsp(l2cap::CommandId id,hci_spec::ConnectionHandle handle,l2cap::FixedChannelsSupported chan_mask)135 DynamicByteBuffer AclFixedChannelsSupportedInfoRsp(
136     l2cap::CommandId id,
137     hci_spec::ConnectionHandle handle,
138     l2cap::FixedChannelsSupported chan_mask) {
139   const auto chan_bytes = ToBytes(chan_mask);
140   return DynamicByteBuffer(StaticByteBuffer(
141       // ACL data header (handle: |link_handle|, length: 20 bytes)
142       LowerBits(handle),
143       UpperBits(handle),
144       0x14,
145       0x00,
146       // L2CAP B-frame header (length: 16 bytes, channel-id: 0x0001 (ACL sig))
147       0x10,
148       0x00,
149       0x01,
150       0x00,
151       // Information Response (type, ID, length: 12)
152       l2cap::kInformationResponse,
153       id,
154       0x0c,
155       0x00,
156       // Type = Fixed Channels Supported
157       0x03,
158       0x00,
159       // Result = Success
160       0x00,
161       0x00,
162       // Data (Mask)
163       chan_bytes[0],
164       chan_bytes[1],
165       chan_bytes[2],
166       chan_bytes[3],
167       chan_bytes[4],
168       chan_bytes[5],
169       chan_bytes[6],
170       chan_bytes[7]));
171 }
172 
AclNotSupportedInformationResponse(l2cap::CommandId id,hci_spec::ConnectionHandle handle)173 DynamicByteBuffer AclNotSupportedInformationResponse(
174     l2cap::CommandId id, hci_spec::ConnectionHandle handle) {
175   return DynamicByteBuffer(StaticByteBuffer(
176       // ACL data header (handle: |link_handle|, length: 12 bytes)
177       LowerBits(handle),
178       UpperBits(handle),
179       0x0c,
180       0x00,
181       // L2CAP B-frame header (length: 8 bytes, channel-id: 0x0001 (ACL sig))
182       0x08,
183       0x00,
184       0x01,
185       0x00,
186       // Information Response (type, ID, length: 4)
187       l2cap::kInformationResponse,
188       id,
189       0x04,
190       0x00,
191       // Type = invalid type
192       0xFF,
193       0xFF,
194       // Result
195       LowerBits(static_cast<uint16_t>(l2cap::InformationResult::kNotSupported)),
196       UpperBits(
197           static_cast<uint16_t>(l2cap::InformationResult::kNotSupported))));
198 }
199 
AclConfigReq(l2cap::CommandId id,hci_spec::ConnectionHandle handle,l2cap::ChannelId dst_id,l2cap::ChannelParameters params)200 DynamicByteBuffer AclConfigReq(l2cap::CommandId id,
201                                hci_spec::ConnectionHandle handle,
202                                l2cap::ChannelId dst_id,
203                                l2cap::ChannelParameters params) {
204   const auto any_mode =
205       params.mode.value_or(l2cap::RetransmissionAndFlowControlMode::kBasic);
206   const auto mtu = params.max_rx_sdu_size.value_or(l2cap::kMaxMTU);
207 
208   BT_ASSERT_MSG(
209       std::holds_alternative<l2cap::RetransmissionAndFlowControlMode>(any_mode),
210       "Channel mode is unsupported for configuration request.");
211   const auto mode = std::get<l2cap::RetransmissionAndFlowControlMode>(any_mode);
212   switch (mode) {
213     case l2cap::RetransmissionAndFlowControlMode::kBasic:
214       return DynamicByteBuffer(StaticByteBuffer(
215           // ACL data header (handle, length: 16 bytes)
216           LowerBits(handle),
217           UpperBits(handle),
218           0x10,
219           0x00,
220           // L2CAP B-frame header (length: 12, channel-id: 0x0001 (ACL sig))
221           0x0c,
222           0x00,
223           0x01,
224           0x00,
225           // Configuration Request (ID, length: 8, |dst_id|, flags: 0,
226           0x04,
227           id,
228           0x08,
229           0x00,
230           LowerBits(dst_id),
231           UpperBits(dst_id),
232           0x00,
233           0x00,
234           // MTU option: (ID: 1, length: 2, mtu)
235           0x01,
236           0x02,
237           LowerBits(mtu),
238           UpperBits(mtu)));
239     case l2cap::RetransmissionAndFlowControlMode::kEnhancedRetransmission:
240       return DynamicByteBuffer(StaticByteBuffer(
241           // ACL data header (handle, length: 27 bytes)
242           LowerBits(handle),
243           UpperBits(handle),
244           0x1b,
245           0x00,
246           // L2CAP B-frame header (length: 23, channel-id: 0x0001 (ACL sig))
247           0x17,
248           0x00,
249           0x01,
250           0x00,
251           // Configuration Request (ID, length: 19, |dst_id|, flags: 0,
252           0x04,
253           id,
254           0x13,
255           0x00,
256           LowerBits(dst_id),
257           UpperBits(dst_id),
258           0x00,
259           0x00,
260           // MTU option: (ID: 1, length: 2, mtu)
261           0x01,
262           0x02,
263           LowerBits(mtu),
264           UpperBits(mtu),
265           // Retransmission & Flow Control option (Type, Length = 9, mode,
266           // fields)
267           0x04,
268           0x09,
269           static_cast<uint8_t>(mode),
270           l2cap::kErtmMaxUnackedInboundFrames,
271           l2cap::kErtmMaxInboundRetransmissions,
272           0x00,
273           0x00,
274           0x00,
275           0x00,
276           LowerBits(l2cap::kMaxInboundPduPayloadSize),
277           UpperBits(l2cap::kMaxInboundPduPayloadSize)));
278     default:
279       BT_ASSERT_MSG(false, "unsupported mode");
280   }
281 }
282 
AclConfigRsp(l2cap::CommandId id,hci_spec::ConnectionHandle link_handle,l2cap::ChannelId src_id,l2cap::ChannelParameters params)283 DynamicByteBuffer AclConfigRsp(l2cap::CommandId id,
284                                hci_spec::ConnectionHandle link_handle,
285                                l2cap::ChannelId src_id,
286                                l2cap::ChannelParameters params) {
287   const auto any_mode =
288       params.mode.value_or(l2cap::RetransmissionAndFlowControlMode::kBasic);
289   const auto mtu = params.max_rx_sdu_size.value_or(l2cap::kMaxMTU);
290 
291   BT_ASSERT_MSG(
292       std::holds_alternative<l2cap::RetransmissionAndFlowControlMode>(any_mode),
293       "Channel mode is unsupported for configuration response.");
294   const auto mode = std::get<l2cap::RetransmissionAndFlowControlMode>(any_mode);
295   switch (mode) {
296     case l2cap::RetransmissionAndFlowControlMode::kBasic:
297       return DynamicByteBuffer(StaticByteBuffer(
298           // ACL data header (handle: |link_handle|, length: 18 bytes)
299           LowerBits(link_handle),
300           UpperBits(link_handle),
301           0x12,
302           0x00,
303           // L2CAP B-frame header (length: 14 bytes, channel-id: 0x0001 (ACL
304           // sig))
305           0x0e,
306           0x00,
307           0x01,
308           0x00,
309           // Configuration Response (ID, length: 10, src cid, flags: 0,
310           // result: success)
311           0x05,
312           id,
313           0x0a,
314           0x00,
315           LowerBits(src_id),
316           UpperBits(src_id),
317           0x00,
318           0x00,
319           0x00,
320           0x00,
321           // MTU option: (ID: 1, length: 2, mtu)
322           0x01,
323           0x02,
324           LowerBits(mtu),
325           UpperBits(mtu)));
326     case l2cap::RetransmissionAndFlowControlMode::kEnhancedRetransmission: {
327       const auto rtx_timeout = kErtmReceiverReadyPollTimerMsecs;
328       const auto monitor_timeout = kErtmMonitorTimerMsecs;
329       return DynamicByteBuffer(StaticByteBuffer(
330           // ACL data header (handle: |link_handle|, length: 29 bytes)
331           LowerBits(link_handle),
332           UpperBits(link_handle),
333           0x1d,
334           0x00,
335           // L2CAP B-frame header (length: 25 bytes, channel-id: 0x0001 (ACL
336           // sig))
337           0x19,
338           0x00,
339           0x01,
340           0x00,
341           // Configuration Response (ID, length: 21, src cid, flags: 0,
342           // result: success)
343           0x05,
344           id,
345           0x15,
346           0x00,
347           LowerBits(src_id),
348           UpperBits(src_id),
349           0x00,
350           0x00,
351           0x00,
352           0x00,
353           // MTU option: (ID: 1, length: 2, mtu)
354           0x01,
355           0x02,
356           LowerBits(mtu),
357           UpperBits(mtu),
358           // Retransmission & Flow Control option (Type, Length = 9, mode,
359           // fields)
360           0x04,
361           0x09,
362           static_cast<uint8_t>(mode),
363           l2cap::kErtmMaxUnackedInboundFrames,
364           l2cap::kErtmMaxInboundRetransmissions,
365           LowerBits(rtx_timeout),
366           UpperBits(rtx_timeout),
367           LowerBits(monitor_timeout),
368           UpperBits(monitor_timeout),
369           LowerBits(l2cap::kMaxInboundPduPayloadSize),
370           UpperBits(l2cap::kMaxInboundPduPayloadSize)));
371     }
372     default:
373       BT_ASSERT_MSG(false, "unsupported mode");
374   }
375 }
376 
AclConnectionReq(l2cap::CommandId id,hci_spec::ConnectionHandle link_handle,l2cap::ChannelId src_id,l2cap::Psm psm)377 DynamicByteBuffer AclConnectionReq(l2cap::CommandId id,
378                                    hci_spec::ConnectionHandle link_handle,
379                                    l2cap::ChannelId src_id,
380                                    l2cap::Psm psm) {
381   return DynamicByteBuffer(StaticByteBuffer(
382       // ACL data header (handle: |link_handle|, length: 12 bytes)
383       LowerBits(link_handle),
384       UpperBits(link_handle),
385       0x0c,
386       0x00,
387 
388       // L2CAP B-frame header (length: 8 bytes, channel-id: 0x0001 (ACL sig))
389       0x08,
390       0x00,
391       0x01,
392       0x00,
393 
394       // Connection Request (ID, length: 4, |psm|, |src_id|)
395       0x02,
396       id,
397       0x04,
398       0x00,
399       LowerBits(psm),
400       UpperBits(psm),
401       LowerBits(src_id),
402       UpperBits(src_id)));
403 }
404 
AclConnectionRsp(l2cap::CommandId id,hci_spec::ConnectionHandle link_handle,l2cap::ChannelId src_id,l2cap::ChannelId dst_id,l2cap::ConnectionResult result)405 DynamicByteBuffer AclConnectionRsp(l2cap::CommandId id,
406                                    hci_spec::ConnectionHandle link_handle,
407                                    l2cap::ChannelId src_id,
408                                    l2cap::ChannelId dst_id,
409                                    l2cap::ConnectionResult result) {
410   return DynamicByteBuffer(StaticByteBuffer(
411       // ACL data header (handle: |link handle|, length: 16 bytes)
412       LowerBits(link_handle),
413       UpperBits(link_handle),
414       0x10,
415       0x00,
416       // L2CAP B-frame header: length 12, channel-id 1 (signaling)
417       0x0c,
418       0x00,
419       0x01,
420       0x00,
421       // Connection Response (0x03), id, length 8
422       l2cap::kConnectionResponse,
423       id,
424       0x08,
425       0x00,
426       // destination cid
427       LowerBits(dst_id),
428       UpperBits(dst_id),
429       // source cid
430       LowerBits(src_id),
431       UpperBits(src_id),
432       // Result
433       LowerBits(static_cast<uint16_t>(result)),
434       UpperBits(static_cast<uint16_t>(result)),
435       // Status (no further information available)
436       0x00,
437       0x00));
438 }
439 
AclDisconnectionReq(l2cap::CommandId id,hci_spec::ConnectionHandle link_handle,l2cap::ChannelId src_id,l2cap::ChannelId dst_id)440 DynamicByteBuffer AclDisconnectionReq(l2cap::CommandId id,
441                                       hci_spec::ConnectionHandle link_handle,
442                                       l2cap::ChannelId src_id,
443                                       l2cap::ChannelId dst_id) {
444   return DynamicByteBuffer(StaticByteBuffer(
445       // ACL data header (handle: |link handle|, length: 12 bytes)
446       LowerBits(link_handle),
447       UpperBits(link_handle),
448       0x0c,
449       0x00,
450       // L2CAP B-frame header: length 8, channel-id 1 (signaling)
451       0x08,
452       0x00,
453       0x01,
454       0x00,
455       // Disconnection Request, id, length 4
456       l2cap::kDisconnectionRequest,
457       id,
458       0x04,
459       0x00,
460       // Destination CID
461       LowerBits(dst_id),
462       UpperBits(dst_id),
463       // Source CID
464       LowerBits(src_id),
465       UpperBits(src_id)));
466 }
467 
AclDisconnectionRsp(l2cap::CommandId id,hci_spec::ConnectionHandle link_handle,l2cap::ChannelId src_id,l2cap::ChannelId dst_id)468 DynamicByteBuffer AclDisconnectionRsp(l2cap::CommandId id,
469                                       hci_spec::ConnectionHandle link_handle,
470                                       l2cap::ChannelId src_id,
471                                       l2cap::ChannelId dst_id) {
472   return DynamicByteBuffer(StaticByteBuffer(
473       // ACL data header (handle: |link handle|, length: 12 bytes)
474       LowerBits(link_handle),
475       UpperBits(link_handle),
476       0x0c,
477       0x00,
478       // L2CAP B-frame header: length 8, channel-id 1 (signaling)
479       0x08,
480       0x00,
481       0x01,
482       0x00,
483       // Disconnection Response, id, length 4
484       l2cap::kDisconnectionResponse,
485       id,
486       0x04,
487       0x00,
488       // Destination CID
489       LowerBits(dst_id),
490       UpperBits(dst_id),
491       // Source CID
492       LowerBits(src_id),
493       UpperBits(src_id)));
494 }
495 
AclConnectionParameterUpdateReq(l2cap::CommandId id,hci_spec::ConnectionHandle link_handle,uint16_t interval_min,uint16_t interval_max,uint16_t peripheral_latency,uint16_t timeout_multiplier)496 DynamicByteBuffer AclConnectionParameterUpdateReq(
497     l2cap::CommandId id,
498     hci_spec::ConnectionHandle link_handle,
499     uint16_t interval_min,
500     uint16_t interval_max,
501     uint16_t peripheral_latency,
502     uint16_t timeout_multiplier) {
503   return DynamicByteBuffer(StaticByteBuffer(
504       // ACL data header (handle: |link handle|, length: 16 bytes)
505       LowerBits(link_handle),
506       UpperBits(link_handle),
507       0x10,
508       0x00,
509       // L2CAP B-frame header: length 12, channel-id 5 (LE signaling)
510       0x0c,
511       0x00,
512       0x05,
513       0x00,
514       // Connection Parameter Update Request (0x12), id, length 8
515       l2cap::kConnectionParameterUpdateRequest,
516       id,
517       0x08,
518       0x00,
519       // interval min
520       LowerBits(interval_min),
521       UpperBits(interval_min),
522       // interval max
523       LowerBits(interval_max),
524       UpperBits(interval_max),
525       // peripheral latency
526       LowerBits(peripheral_latency),
527       UpperBits(peripheral_latency),
528       // timeout multiplier
529       LowerBits(timeout_multiplier),
530       UpperBits(timeout_multiplier)));
531 }
532 
AclConnectionParameterUpdateRsp(l2cap::CommandId id,hci_spec::ConnectionHandle link_handle,ConnectionParameterUpdateResult result)533 DynamicByteBuffer AclConnectionParameterUpdateRsp(
534     l2cap::CommandId id,
535     hci_spec::ConnectionHandle link_handle,
536     ConnectionParameterUpdateResult result) {
537   return DynamicByteBuffer(StaticByteBuffer(
538       // ACL data header (handle: |link handle|, length: 10 bytes)
539       LowerBits(link_handle),
540       UpperBits(link_handle),
541       0x0a,
542       0x00,
543       // L2CAP B-frame header: length 6, channel-id 5 (LE signaling)
544       0x06,
545       0x00,
546       0x05,
547       0x00,
548       // Connection Parameter Update Response (0x13), id, length 2
549       l2cap::kConnectionParameterUpdateResponse,
550       id,
551       0x02,
552       0x00,
553       // Result
554       LowerBits(static_cast<uint16_t>(result)),
555       UpperBits(static_cast<uint16_t>(result))));
556 }
557 
AclSFrame(hci_spec::ConnectionHandle link_handle,l2cap::ChannelId channel_id,l2cap::internal::SupervisoryFunction function,uint8_t receive_seq_num,bool is_poll_request,bool is_poll_response)558 DynamicByteBuffer AclSFrame(hci_spec::ConnectionHandle link_handle,
559                             l2cap::ChannelId channel_id,
560                             l2cap::internal::SupervisoryFunction function,
561                             uint8_t receive_seq_num,
562                             bool is_poll_request,
563                             bool is_poll_response) {
564   StaticByteBuffer acl_packet{
565       // ACL data header (handle: |link handle|, length: 8 bytes)
566       LowerBits(link_handle),
567       UpperBits(link_handle),
568       0x08,
569       0x00,
570 
571       // L2CAP B-frame header: length 4, channel-id
572       0x04,
573       0x00,
574       LowerBits(channel_id),
575       UpperBits(channel_id),
576 
577       // Enhanced Control Field: F is_poll_response, P is_poll_request,
578       // Supervisory function,
579       // Type S-Frame, ReqSeq receive_seq_num
580       (is_poll_response ? 0b1000'0000 : 0) | (is_poll_request ? 0b1'0000 : 0) |
581           (static_cast<uint8_t>(function) << 2) | 0b1,
582       receive_seq_num & 0b11'1111,
583 
584       // Frame Check Sequence
585       0x00,
586       0x00};
587   const FrameCheckSequence fcs = ComputeFcs(
588       acl_packet.view(sizeof(hci_spec::ACLDataHeader),
589                       acl_packet.size() - sizeof(hci_spec::ACLDataHeader) -
590                           sizeof(FrameCheckSequence)));
591   acl_packet[acl_packet.size() - 2] = LowerBits(fcs.fcs);
592   acl_packet[acl_packet.size() - 1] = UpperBits(fcs.fcs);
593   return DynamicByteBuffer(acl_packet);
594 }
595 
AclIFrame(hci_spec::ConnectionHandle link_handle,l2cap::ChannelId channel_id,uint8_t receive_seq_num,uint8_t tx_seq,bool is_poll_response,const ByteBuffer & payload)596 DynamicByteBuffer AclIFrame(hci_spec::ConnectionHandle link_handle,
597                             l2cap::ChannelId channel_id,
598                             uint8_t receive_seq_num,
599                             uint8_t tx_seq,
600                             bool is_poll_response,
601                             const ByteBuffer& payload) {
602   const uint16_t l2cap_size =
603       static_cast<uint16_t>(sizeof(internal::SimpleInformationFrameHeader) +
604                             payload.size() + sizeof(FrameCheckSequence));
605   const uint16_t acl_size = l2cap_size + sizeof(BasicHeader);
606   StaticByteBuffer headers(
607       // ACL data header (handle: |link handle|, length)
608       LowerBits(link_handle),
609       UpperBits(link_handle),
610       LowerBits(acl_size),
611       UpperBits(acl_size),
612 
613       // L2CAP B-frame header: length, channel-id
614       LowerBits(l2cap_size),
615       UpperBits(l2cap_size),
616       LowerBits(channel_id),
617       UpperBits(channel_id),
618 
619       // Enhanced Control Field: F is_poll_response, TxSeq tx_seq, Type I-Frame,
620       // ReqSeq receive_seq_num
621       (is_poll_response ? 0b1000'0000 : 0) | ((tx_seq << 1) & 0b111'1110),
622       receive_seq_num & 0b11'1111);
623 
624   FrameCheckSequence fcs =
625       ComputeFcs(headers.view(sizeof(hci_spec::ACLDataHeader), acl_size));
626   fcs = ComputeFcs(payload.view(), fcs);
627 
628   DynamicByteBuffer acl_packet(headers.size() + payload.size() + sizeof(fcs));
629   headers.Copy(&acl_packet);
630   auto payload_destination = acl_packet.mutable_view(headers.size());
631   payload.Copy(&payload_destination);
632   acl_packet[acl_packet.size() - 2] = LowerBits(fcs.fcs);
633   acl_packet[acl_packet.size() - 1] = UpperBits(fcs.fcs);
634   return acl_packet;
635 }
636 
637 }  // namespace bt::l2cap::testing
638