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