blob: 74ffed3f6f639f92962b8b127a9fa401a2a3bd6f [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "test_packets.h"
#include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h"
#include "src/connectivity/bluetooth/core/bt-host/hci-spec/protocol.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/fcs.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/frame_headers.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap_defs.h"
namespace bt::l2cap::testing {
DynamicByteBuffer AclExtFeaturesInfoReq(l2cap::CommandId id, hci::ConnectionHandle handle) {
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle, length: 10)
LowerBits(handle), UpperBits(handle), 0x0a, 0x00,
// L2CAP B-frame header (length: 6, chanel-id: 0x0001 (ACL sig))
0x06, 0x00, 0x01, 0x00,
// Extended Features Information Request
// (ID, length: 2, type)
0x0a, id, 0x02, 0x00,
LowerBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
UpperBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported))));
}
DynamicByteBuffer AclCommandRejectNotUnderstoodRsp(l2cap::CommandId id,
hci::ConnectionHandle handle,
ChannelId chan_id) {
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle: |link_handle|, length: 10 bytes)
LowerBits(handle), UpperBits(handle), 0x0a, 0x00,
// L2CAP B-frame header (length: 6 bytes, channel-id: 0x0001 (ACL sig))
0x06, 0x00, LowerBits(chan_id), UpperBits(chan_id),
// Information Response (type, ID, length: 2)
l2cap::kCommandRejectCode, id, 0x02, 0x00,
// Reason = Not Understood
LowerBits(static_cast<uint16_t>(RejectReason::kNotUnderstood)),
UpperBits(static_cast<uint16_t>(RejectReason::kNotUnderstood))));
}
DynamicByteBuffer AclExtFeaturesInfoRsp(l2cap::CommandId id, hci::ConnectionHandle handle,
l2cap::ExtendedFeatures features) {
const auto features_bytes = ToBytes(features);
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle: |link_handle|, length: 16 bytes)
LowerBits(handle), UpperBits(handle), 0x10, 0x00,
// L2CAP B-frame header (length: 12 bytes, channel-id: 0x0001 (ACL sig))
0x0c, 0x00, 0x01, 0x00,
// Information Response (type, ID, length: 8)
l2cap::kInformationResponse, id, 0x08, 0x00,
// Type = Features Mask
0x02, 0x00,
// Result = Success
0x00, 0x00,
// Data (Mask)
features_bytes[0], features_bytes[1], features_bytes[2], features_bytes[3]));
}
DynamicByteBuffer AclFixedChannelsSupportedInfoReq(l2cap::CommandId id,
hci::ConnectionHandle handle) {
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle, length: 10)
LowerBits(handle), UpperBits(handle), 0x0a, 0x00,
// L2CAP B-frame header (length: 6, chanel-id: 0x0001 (ACL sig))
0x06, 0x00, 0x01, 0x00,
// Fixed Channels Supported Information Request
// (ID, length: 2, info type)
l2cap::kInformationRequest, id, 0x02, 0x00,
LowerBits(static_cast<uint16_t>(InformationType::kFixedChannelsSupported)),
UpperBits(static_cast<uint16_t>(InformationType::kFixedChannelsSupported))));
}
DynamicByteBuffer AclFixedChannelsSupportedInfoRsp(l2cap::CommandId id,
hci::ConnectionHandle handle,
l2cap::FixedChannelsSupported chan_mask) {
const auto chan_bytes = ToBytes(chan_mask);
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle: |link_handle|, length: 20 bytes)
LowerBits(handle), UpperBits(handle), 0x14, 0x00,
// L2CAP B-frame header (length: 16 bytes, channel-id: 0x0001 (ACL sig))
0x10, 0x00, 0x01, 0x00,
// Information Response (type, ID, length: 12)
l2cap::kInformationResponse, id, 0x0c, 0x00,
// Type = Fixed Channels Supported
0x03, 0x00,
// Result = Success
0x00, 0x00,
// Data (Mask)
chan_bytes[0], chan_bytes[1], chan_bytes[2], chan_bytes[3], chan_bytes[4], chan_bytes[5],
chan_bytes[6], chan_bytes[7]));
}
DynamicByteBuffer AclNotSupportedInformationResponse(l2cap::CommandId id,
hci::ConnectionHandle handle) {
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle: |link_handle|, length: 12 bytes)
LowerBits(handle), UpperBits(handle), 0x0c, 0x00,
// L2CAP B-frame header (length: 8 bytes, channel-id: 0x0001 (ACL sig))
0x08, 0x00, 0x01, 0x00,
// Information Response (type, ID, length: 4)
l2cap::kInformationResponse, id, 0x04, 0x00,
// Type = invalid type
0xFF, 0xFF,
// Result
LowerBits(static_cast<uint16_t>(l2cap::InformationResult::kNotSupported)),
UpperBits(static_cast<uint16_t>(l2cap::InformationResult::kNotSupported))));
}
DynamicByteBuffer AclConfigReq(l2cap::CommandId id, hci::ConnectionHandle handle,
l2cap::ChannelId dst_id, l2cap::ChannelParameters params) {
const auto mode = params.mode.value_or(l2cap::ChannelMode::kBasic);
const auto mtu = params.max_rx_sdu_size.value_or(l2cap::kMaxMTU);
switch (mode) {
case l2cap::ChannelMode::kBasic:
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle, length: 16 bytes)
LowerBits(handle), UpperBits(handle), 0x10, 0x00,
// L2CAP B-frame header (length: 12, channel-id: 0x0001 (ACL sig))
0x0c, 0x00, 0x01, 0x00,
// Configuration Request (ID, length: 8, |dst_id|, flags: 0,
0x04, id, 0x08, 0x00, LowerBits(dst_id), UpperBits(dst_id), 0x00, 0x00,
// MTU option: (ID: 1, length: 2, mtu)
0x01, 0x02, LowerBits(mtu), UpperBits(mtu)));
case l2cap::ChannelMode::kEnhancedRetransmission:
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle, length: 27 bytes)
LowerBits(handle), UpperBits(handle), 0x1b, 0x00,
// L2CAP B-frame header (length: 23, channel-id: 0x0001 (ACL sig))
0x17, 0x00, 0x01, 0x00,
// Configuration Request (ID, length: 19, |dst_id|, flags: 0,
0x04, id, 0x13, 0x00, LowerBits(dst_id), UpperBits(dst_id), 0x00, 0x00,
// MTU option: (ID: 1, length: 2, mtu)
0x01, 0x02, LowerBits(mtu), UpperBits(mtu),
// Retransmission & Flow Control option (Type, Length = 9, mode, fields)
0x04, 0x09, static_cast<uint8_t>(mode), l2cap::kErtmMaxUnackedInboundFrames,
l2cap::kErtmMaxInboundRetransmissions, 0x00, 0x00, 0x00, 0x00,
LowerBits(l2cap::kMaxInboundPduPayloadSize),
UpperBits(l2cap::kMaxInboundPduPayloadSize)));
default:
ZX_ASSERT_MSG(false, "unsupported mode");
}
}
DynamicByteBuffer AclConfigRsp(l2cap::CommandId id, hci::ConnectionHandle link_handle,
l2cap::ChannelId src_id, l2cap::ChannelParameters params) {
const auto mode = params.mode.value_or(l2cap::ChannelMode::kBasic);
const auto mtu = params.max_rx_sdu_size.value_or(l2cap::kMaxMTU);
switch (mode) {
case l2cap::ChannelMode::kBasic:
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle: |link_handle|, length: 18 bytes)
LowerBits(link_handle), UpperBits(link_handle), 0x12, 0x00,
// L2CAP B-frame header (length: 14 bytes, channel-id: 0x0001 (ACL sig))
0x0e, 0x00, 0x01, 0x00,
// Configuration Response (ID, length: 10, src cid, flags: 0,
// result: success)
0x05, id, 0x0a, 0x00, LowerBits(src_id), UpperBits(src_id), 0x00, 0x00, 0x00, 0x00,
// MTU option: (ID: 1, length: 2, mtu)
0x01, 0x02, LowerBits(mtu), UpperBits(mtu)));
case l2cap::ChannelMode::kEnhancedRetransmission: {
const auto rtx_timeout = kErtmReceiverReadyPollTimerDuration.to_msecs();
const auto monitor_timeout = kErtmMonitorTimerDuration.to_msecs();
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle: |link_handle|, length: 29 bytes)
LowerBits(link_handle), UpperBits(link_handle), 0x1d, 0x00,
// L2CAP B-frame header (length: 25 bytes, channel-id: 0x0001 (ACL sig))
0x19, 0x00, 0x01, 0x00,
// Configuration Response (ID, length: 21, src cid, flags: 0,
// result: success)
0x05, id, 0x15, 0x00, LowerBits(src_id), UpperBits(src_id), 0x00, 0x00, 0x00, 0x00,
// MTU option: (ID: 1, length: 2, mtu)
0x01, 0x02, LowerBits(mtu), UpperBits(mtu),
// Retransmission & Flow Control option (Type, Length = 9, mode, fields)
0x04, 0x09, static_cast<uint8_t>(mode), l2cap::kErtmMaxUnackedInboundFrames,
l2cap::kErtmMaxInboundRetransmissions, LowerBits(rtx_timeout), UpperBits(rtx_timeout),
LowerBits(monitor_timeout), UpperBits(monitor_timeout),
LowerBits(l2cap::kMaxInboundPduPayloadSize),
UpperBits(l2cap::kMaxInboundPduPayloadSize)));
}
default:
ZX_ASSERT_MSG(false, "unsupported mode");
}
}
DynamicByteBuffer AclConnectionReq(l2cap::CommandId id, hci::ConnectionHandle link_handle,
l2cap::ChannelId src_id, l2cap::PSM psm) {
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle: |link_handle|, length: 12 bytes)
LowerBits(link_handle), UpperBits(link_handle), 0x0c, 0x00,
// L2CAP B-frame header (length: 8 bytes, channel-id: 0x0001 (ACL sig))
0x08, 0x00, 0x01, 0x00,
// Connection Request (ID, length: 4, |psm|, |src_id|)
0x02, id, 0x04, 0x00, LowerBits(psm), UpperBits(psm), LowerBits(src_id), UpperBits(src_id)));
}
DynamicByteBuffer AclConnectionRsp(l2cap::CommandId id, hci::ConnectionHandle link_handle,
l2cap::ChannelId src_id, l2cap::ChannelId dst_id,
l2cap::ConnectionResult result) {
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle: |link handle|, length: 16 bytes)
LowerBits(link_handle), UpperBits(link_handle), 0x10, 0x00,
// L2CAP B-frame header: length 12, channel-id 1 (signaling)
0x0c, 0x00, 0x01, 0x00,
// Connection Response (0x03), id, length 8
l2cap::kConnectionResponse, id, 0x08, 0x00,
// destination cid
LowerBits(dst_id), UpperBits(dst_id),
// source cid
LowerBits(src_id), UpperBits(src_id),
// Result
LowerBits(static_cast<uint16_t>(result)), UpperBits(static_cast<uint16_t>(result)),
// Status (no further information available)
0x00, 0x00));
}
DynamicByteBuffer AclDisconnectionReq(l2cap::CommandId id, hci::ConnectionHandle link_handle,
l2cap::ChannelId src_id, l2cap::ChannelId dst_id) {
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle: |link handle|, length: 12 bytes)
LowerBits(link_handle), UpperBits(link_handle), 0x0c, 0x00,
// L2CAP B-frame header: length 8, channel-id 1 (signaling)
0x08, 0x00, 0x01, 0x00,
// Disconnection Request, id, length 4
l2cap::kDisconnectionRequest, id, 0x04, 0x00,
// Destination CID
LowerBits(dst_id), UpperBits(dst_id),
// Source CID
LowerBits(src_id), UpperBits(src_id)));
}
DynamicByteBuffer AclDisconnectionRsp(l2cap::CommandId id, hci::ConnectionHandle link_handle,
l2cap::ChannelId src_id, l2cap::ChannelId dst_id) {
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle: |link handle|, length: 12 bytes)
LowerBits(link_handle), UpperBits(link_handle), 0x0c, 0x00,
// L2CAP B-frame header: length 8, channel-id 1 (signaling)
0x08, 0x00, 0x01, 0x00,
// Disconnection Response, id, length 4
l2cap::kDisconnectionResponse, id, 0x04, 0x00,
// Destination CID
LowerBits(dst_id), UpperBits(dst_id),
// Source CID
LowerBits(src_id), UpperBits(src_id)));
}
DynamicByteBuffer AclConnectionParameterUpdateReq(l2cap::CommandId id,
hci::ConnectionHandle link_handle,
uint16_t interval_min, uint16_t interval_max,
uint16_t slave_latency,
uint16_t timeout_multiplier) {
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle: |link handle|, length: 16 bytes)
LowerBits(link_handle), UpperBits(link_handle), 0x10, 0x00,
// L2CAP B-frame header: length 12, channel-id 5 (LE signaling)
0x0c, 0x00, 0x05, 0x00,
// Connection Parameter Update Request (0x12), id, length 8
l2cap::kConnectionParameterUpdateRequest, id, 0x08, 0x00,
// interval min
LowerBits(interval_min), UpperBits(interval_min),
// interval max
LowerBits(interval_max), UpperBits(interval_max),
// slave latency
LowerBits(slave_latency), UpperBits(slave_latency),
// timeout multiplier
LowerBits(timeout_multiplier), UpperBits(timeout_multiplier)));
}
DynamicByteBuffer AclConnectionParameterUpdateRsp(l2cap::CommandId id,
hci::ConnectionHandle link_handle,
ConnectionParameterUpdateResult result) {
return DynamicByteBuffer(StaticByteBuffer(
// ACL data header (handle: |link handle|, length: 10 bytes)
LowerBits(link_handle), UpperBits(link_handle), 0x0a, 0x00,
// L2CAP B-frame header: length 6, channel-id 5 (LE signaling)
0x06, 0x00, 0x05, 0x00,
// Connection Parameter Update Response (0x13), id, length 2
l2cap::kConnectionParameterUpdateResponse, id, 0x02, 0x00,
// Result
LowerBits(static_cast<uint16_t>(result)), UpperBits(static_cast<uint16_t>(result))));
}
DynamicByteBuffer AclSFrame(hci::ConnectionHandle link_handle, l2cap::ChannelId channel_id,
l2cap::internal::SupervisoryFunction function, uint8_t receive_seq_num,
bool is_poll_request, bool is_poll_response) {
StaticByteBuffer acl_packet{
// ACL data header (handle: |link handle|, length: 8 bytes)
LowerBits(link_handle), UpperBits(link_handle), 0x08, 0x00,
// L2CAP B-frame header: length 4, channel-id
0x04, 0x00, LowerBits(channel_id), UpperBits(channel_id),
// Enhanced Control Field: F is_poll_response, P is_poll_request, Supervisory function,
// Type S-Frame, ReqSeq receive_seq_num
(is_poll_response ? 0b1000'0000 : 0) | (is_poll_request ? 0b1'0000 : 0) |
(static_cast<uint8_t>(function) << 2) | 0b1,
receive_seq_num & 0b11'1111,
// Frame Check Sequence
0x00, 0x00};
const FrameCheckSequence fcs = ComputeFcs(
acl_packet.view(sizeof(hci::ACLDataHeader),
acl_packet.size() - sizeof(hci::ACLDataHeader) - sizeof(FrameCheckSequence)));
acl_packet[acl_packet.size() - 2] = LowerBits(fcs.fcs);
acl_packet[acl_packet.size() - 1] = UpperBits(fcs.fcs);
return DynamicByteBuffer(acl_packet);
}
DynamicByteBuffer AclIFrame(hci::ConnectionHandle link_handle, l2cap::ChannelId channel_id,
uint8_t receive_seq_num, uint8_t tx_seq, bool is_poll_response,
const ByteBuffer& payload) {
const uint16_t l2cap_size =
sizeof(internal::SimpleInformationFrameHeader) + payload.size() + sizeof(FrameCheckSequence);
const uint16_t acl_size = l2cap_size + sizeof(BasicHeader);
StaticByteBuffer headers(
// ACL data header (handle: |link handle|, length)
LowerBits(link_handle), UpperBits(link_handle), LowerBits(acl_size), UpperBits(acl_size),
// L2CAP B-frame header: length, channel-id
LowerBits(l2cap_size), UpperBits(l2cap_size), LowerBits(channel_id), UpperBits(channel_id),
// Enhanced Control Field: F is_poll_response, TxSeq tx_seq, Type I-Frame,
// ReqSeq receive_seq_num
(is_poll_response ? 0b1000'0000 : 0) | ((tx_seq << 1) & 0b111'1110),
receive_seq_num & 0b11'1111);
FrameCheckSequence fcs = ComputeFcs(headers.view(sizeof(hci::ACLDataHeader), acl_size));
fcs = ComputeFcs(payload.view(), fcs);
DynamicByteBuffer acl_packet(headers.size() + payload.size() + sizeof(fcs));
headers.Copy(&acl_packet);
auto payload_destination = acl_packet.mutable_view(headers.size());
payload.Copy(&payload_destination);
acl_packet[acl_packet.size() - 2] = LowerBits(fcs.fcs);
acl_packet[acl_packet.size() - 1] = UpperBits(fcs.fcs);
return acl_packet;
}
} // namespace bt::l2cap::testing