blob: 57598dd0639917c9e4a62a1953ff96ad948d6839 [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 "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/test_packets.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/fcs.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/frame_headers.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
namespace bt::l2cap::testing {
DynamicByteBuffer AclExtFeaturesInfoReq(l2cap::CommandId id,
hci_spec::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_spec::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_spec::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_spec::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_spec::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_spec::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_spec::ConnectionHandle handle,
l2cap::ChannelId dst_id,
l2cap::ChannelParameters params) {
const auto any_mode =
params.mode.value_or(l2cap::RetransmissionAndFlowControlMode::kBasic);
const auto mtu = params.max_rx_sdu_size.value_or(l2cap::kMaxMTU);
BT_ASSERT_MSG(
std::holds_alternative<l2cap::RetransmissionAndFlowControlMode>(any_mode),
"Channel mode is unsupported for configuration request.");
const auto mode = std::get<l2cap::RetransmissionAndFlowControlMode>(any_mode);
switch (mode) {
case l2cap::RetransmissionAndFlowControlMode::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::RetransmissionAndFlowControlMode::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:
BT_ASSERT_MSG(false, "unsupported mode");
}
}
DynamicByteBuffer AclConfigRsp(l2cap::CommandId id,
hci_spec::ConnectionHandle link_handle,
l2cap::ChannelId src_id,
l2cap::ChannelParameters params) {
const auto any_mode =
params.mode.value_or(l2cap::RetransmissionAndFlowControlMode::kBasic);
const auto mtu = params.max_rx_sdu_size.value_or(l2cap::kMaxMTU);
BT_ASSERT_MSG(
std::holds_alternative<l2cap::RetransmissionAndFlowControlMode>(any_mode),
"Channel mode is unsupported for configuration response.");
const auto mode = std::get<l2cap::RetransmissionAndFlowControlMode>(any_mode);
switch (mode) {
case l2cap::RetransmissionAndFlowControlMode::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::RetransmissionAndFlowControlMode::kEnhancedRetransmission: {
const auto rtx_timeout = kErtmReceiverReadyPollTimerMsecs;
const auto monitor_timeout = kErtmMonitorTimerMsecs;
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:
BT_ASSERT_MSG(false, "unsupported mode");
}
}
DynamicByteBuffer AclConnectionReq(l2cap::CommandId id,
hci_spec::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_spec::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_spec::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_spec::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_spec::ConnectionHandle link_handle,
uint16_t interval_min,
uint16_t interval_max,
uint16_t peripheral_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),
// peripheral latency
LowerBits(peripheral_latency),
UpperBits(peripheral_latency),
// timeout multiplier
LowerBits(timeout_multiplier),
UpperBits(timeout_multiplier)));
}
DynamicByteBuffer AclConnectionParameterUpdateRsp(
l2cap::CommandId id,
hci_spec::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_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) {
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_spec::ACLDataHeader),
acl_packet.size() - sizeof(hci_spec::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_spec::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 =
static_cast<uint16_t>(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_spec::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