| # Copyright 2024 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| |
| # This file contains Emboss definitions for the logical link control and |
| # adaptation protocol (l2cap) frames found in the Bluetooth core specification. |
| # The Emboss compiler is used to generate a C++ header from this file. |
| |
| [$default byte_order: "LittleEndian"] |
| [(cpp) namespace: "pw::bluetooth::emboss"] |
| # ========================= Data Packet Format ================================= |
| # Core Spec v6.0 Vol 3, Part A, Section 3 |
| |
| |
| struct BasicL2capHeader: |
| 0 [+2] UInt pdu_length |
| -- For B-frames, the PDU Length equals the payload size. |
| |
| $next [+2] UInt channel_id |
| -- The channel ID (CID) identifies the destination channel endpoint of the |
| -- packet. |
| [requires: this != 0] |
| |
| |
| struct BFrame: |
| -- Defined in Core Spec v6.0 Vol 3, Part A, Section 3.1. |
| 0 [+BasicL2capHeader.$size_in_bytes] BasicL2capHeader header |
| let pdu_length = header.pdu_length |
| let channel_id = header.channel_id |
| $next [+pdu_length] UInt:8[pdu_length] payload |
| |
| |
| struct BFrameHeader: |
| # TODO: b/364711772 - Migrate clients to `BFrame` & delete. |
| -- 3.1 |
| -- A frame header for a B-Frame. |
| -- In basic L2CAP mode, the L2CAP PDU on a connection-oriented channel is also |
| -- referred to as a "B-frame". |
| 0 [+2] UInt pdu_length |
| -- For B-frames, the PDU Length equals the payload size. |
| |
| $next [+2] UInt channel_id |
| -- The channel ID (CID) identifies the destination channel endpoint of the |
| -- packet. |
| |
| |
| struct FirstKFrame: |
| -- Defined in Core Spec v6.0 Vol 3, Part A, Section 3.4. |
| [requires: 0 <= payload_size <= 65533] |
| 0 [+BasicL2capHeader.$size_in_bytes] BasicL2capHeader header |
| let pdu_length = header.pdu_length |
| let channel_id = header.channel_id |
| let payload_size = pdu_length-2 |
| $next [+2] UInt sdu_length |
| -- The first K-Frame of an SDU contains an SDU length field following the |
| -- L2CAP header. Subsequent frames that are part of the same SDU do not |
| -- contain the SDU length field. |
| |
| $next [+payload_size] UInt:8[payload_size] payload |
| |
| |
| struct SubsequentKFrame: |
| -- Defined in Core Spec v6.0 Vol 3, Part A, Section 3.4. |
| [requires: 0 <= payload_size <= 65533] |
| 0 [+BasicL2capHeader.$size_in_bytes] BasicL2capHeader header |
| let pdu_length = header.pdu_length |
| let channel_id = header.channel_id |
| let payload_size = pdu_length |
| $next [+payload_size] UInt:8[payload_size] payload |
| |
| |
| struct KFrameSduHeader: |
| # TODO: b/364711772 - Migrate clients to `FirstKFrame` & delete. |
| -- 3.4.2 |
| -- A frame header for a K-Frame (credit based flow control) SDU. |
| 0 [+2] UInt sdu_length |
| |
| |
| struct KFramePduHeader: |
| # TODO: b/364711772 - Migrate clients to `SubsequentKFrame` & delete. |
| -- 3.4.1 |
| -- A frame header for a K-Frame (credit based flow control) PDU. |
| 0 [+2] UInt pdu_length |
| $next [+2] UInt channel_id |
| |
| # ====================== Signaling Packet Formats ============================== |
| # Core Spec v6.0 Vol 3, Part A, Section 4 |
| |
| |
| enum L2capSignalingPacketCode: |
| -- Possible values for the Code field of an L2CAP C-frame. See Core Spec v5.4, |
| -- Vol 3, Part A, Table 4.2 for a list of the codes. |
| [maximum_bits: 8] |
| COMMAND_REJECT_RSP = 0x01 |
| CONNECTION_REQ = 0x02 |
| CONNECTION_RSP = 0x03 |
| CONFIGURATION_REQ = 0x04 |
| CONFIGURATION_RSP = 0x05 |
| DISCONNECTION_REQ = 0x06 |
| DISCONNECTION_RSP = 0x07 |
| ECHO_REQ = 0x08 |
| ECHO_RSP = 0x09 |
| INFORMATION_REQ = 0x0A |
| INFORMATION_RSP = 0x0B |
| CONNECTION_PARAMETER_UPDATE_REQ = 0x12 |
| CONNECTION_PARAMETER_UPDATE_RSP = 0x13 |
| LE_CREDIT_BASED_CONNECTION_REQ = 0x14 |
| LE_CREDIT_BASED_CONNECTION_RSP = 0x15 |
| FLOW_CONTROL_CREDIT_IND = 0x16 |
| CREDIT_BASED_CONNECTION_REQ = 0x17 |
| CREDIT_BASED_CONNECTION_RSP = 0x18 |
| CREDIT_BASED_RECONFIGURE_REQ = 0x19 |
| CREDIT_BASED_RECONFIGURE_RSP = 0x1A |
| |
| |
| enum L2capFixedCid: |
| -- Possible fixed values for L2CAP channel ID. See Core Spec v6.0, Vol 3, |
| -- Part A, Table 2.1 |
| [maximum_bits: 16] |
| ACL_U_SIGNALING = 0x0001 |
| CONNECTIONLESS = 0x0002 # Applicable to ACL-U and APB-U |
| ACL_U_SECURITY_MANAGER = 0x0007 |
| LE_U_ATTRIBUTE_PROTOCOL = 0x0004 |
| LE_U_SIGNALING = 0x0005 |
| LE_U_SECURITY_MANAGER = 0x0006 |
| |
| |
| enum L2capFixedPsmCode: |
| -- Possible fixed values for PSM or SPSM as assigned by Bluetooth SIG. See |
| -- Core Spec v5.4, Vol 3, Part A, Section 4.2 and Assigned_Numbers Section 2.5 |
| [maximum_bits: 16] |
| SDP = 0x0001 # PSM |
| RFCOMM = 0x0003 # PSM |
| ATT = 0x001F # PSM |
| EATT = 0x0027 # PSM or SPSM |
| |
| |
| enum L2capCommandRejectReasonCode: |
| -- Reported in L2CAP_COMMAND_REJECT_RSP packets. |
| [maximum_bits: 16] |
| NOT_UNDERSTOOD = 0x0000 |
| SIG_MTU_EXCEEDED = 0x0001 |
| INVALID_CID = 0x0002 |
| -- Typically a channel is invalid because it does not exist. |
| |
| |
| struct CFrame: |
| -- A Control Frame (C-frame) contains L2CAP signaling commands exchanged |
| -- between peer L2CAP entities. C-frames are exclusively used on the L2CAP |
| -- signaling channels. |
| 0 [+BasicL2capHeader.$size_in_bytes] BasicL2capHeader header |
| let pdu_length = header.pdu_length |
| let channel_id = header.channel_id |
| $next [+pdu_length] UInt:8[pdu_length] payload |
| |
| |
| struct L2capSignalingCommandHeader: |
| -- C-frame refers to an L2CAP PDU payload containing one or more signaling |
| -- commands. |
| 0 [+1] L2capSignalingPacketCode code |
| $next [+1] UInt identifier |
| -- Matches responses with requests. |
| |
| $next [+2] UInt data_length |
| |
| |
| struct L2capSignalingCommand: |
| -- Generic L2CAP signaling command. |
| 0 [+L2capSignalingCommandHeader.$size_in_bytes] L2capSignalingCommandHeader command_header |
| let data_length = command_header.data_length |
| $next [+data_length] UInt:8[data_length] payload |
| |
| |
| struct L2capCommandRejectRsp: |
| -- Section 4.1 |
| -- An L2CAP_COMMAND_REJECT_RSP packet shall be sent in response to a command |
| -- packet with an unknown command code or when sending the corresponding |
| -- response is inappropriate. |
| |
| [requires: command_header.code == L2capSignalingPacketCode.COMMAND_REJECT_RSP] |
| |
| 0 [+L2capSignalingCommandHeader.$size_in_bytes] L2capSignalingCommandHeader command_header |
| |
| $next [+2] L2capCommandRejectReasonCode reason |
| -- Describes why the request packet was rejected. |
| |
| if reason == L2capCommandRejectReasonCode.SIG_MTU_EXCEEDED: |
| 6 [+2] UInt actual_sig_mtu |
| |
| if reason == L2capCommandRejectReasonCode.INVALID_CID: |
| # The local and remote endpoints of the disputed channel relative to the |
| # sender of this L2CAP_COMMAND_REJECT_RSP packet. If the rejected command |
| # contains only one of the channel endpoints, the other one shall be |
| # replaced by the null CID 0x0000. |
| 6 [+2] UInt local_endpoint |
| -- The destination CID from the rejected command. |
| |
| 8 [+2] UInt remote_endpoint |
| -- The source CID from the rejected command. |
| |
| |
| struct L2capConnectionReq: |
| -- Section 4.2 |
| -- L2CAP_CONNECTION_REQ packets are sent to create an L2CAP channel between |
| -- two devices. |
| [requires: command_header.code == L2capSignalingPacketCode.CONNECTION_REQ] |
| 0 [+L2capSignalingCommandHeader.$size_in_bytes] L2capSignalingCommandHeader command_header |
| $next [+2] UInt psm |
| -- Protocol/Service Multiplexer. |
| |
| $next [+2] UInt source_cid |
| -- The channel endpoint on the device sending the request. |
| |
| |
| enum L2capConnectionRspResultCode: |
| -- Reported in L2CAP_CONNECTION_RSP packets. |
| [maximum_bits: 16] |
| SUCCESSFUL = 0x0000 |
| PENDING = 0x0001 |
| PSM_NOT_SUPPORTED = 0x0002 |
| SECURITY_BLOCK = 0x0003 |
| NO_RESOURCES_AVAILABLE = 0x0004 |
| INVALID_SOURCE_CID = 0x0005 |
| SOURCE_CID_ALREADY_ALLOCATED = 0x0006 |
| |
| |
| enum L2capConnectionRspStatusCode: |
| -- Reported in L2CAP_CONNECTION_RSP packets if result is PENDING. |
| [maximum_bits: 16] |
| NO_INFORMATION = 0x0000 |
| AUTHENTICATION_PENDING = 0x0001 |
| AUTHORIZATION_PENDING = 0x0002 |
| |
| |
| struct L2capConnectionRsp: |
| -- Section 4.3 |
| -- L2CAP_CONNECTION_RSP packets are sent in response to a L2CAP_CONNECTION_REQ |
| -- packet. |
| [requires: command_header.code == L2capSignalingPacketCode.CONNECTION_RSP] |
| 0 [+L2capSignalingCommandHeader.$size_in_bytes] L2capSignalingCommandHeader command_header |
| $next [+2] UInt destination_cid |
| -- The channel endpoint on the device sending this response packet. |
| |
| $next [+2] UInt source_cid |
| -- The channel endpoint on the device receiving the packet. |
| |
| $next [+2] L2capConnectionRspResultCode result |
| -- The outcome of the connection request. |
| |
| if result == L2capConnectionRspResultCode.PENDING: |
| $next [+2] L2capConnectionRspStatusCode status |
| -- The status of the connection. |
| |
| |
| struct L2capDisconnectionReq: |
| -- Section 4.6 |
| -- Once an L2CAP_DISCONNECTION_REQ packet is issued, all incoming data in |
| -- transit on this L2CAP channel shall be discarded and any new additional |
| -- outgoing data shall be discarded. |
| [requires: command_header.code == L2capSignalingPacketCode.DISCONNECTION_REQ] |
| 0 [+L2capSignalingCommandHeader.$size_in_bytes] L2capSignalingCommandHeader command_header |
| $next [+2] UInt destination_cid |
| -- The endpoint of the channel to be disconnected on the device receiving |
| -- this request. |
| |
| $next [+2] UInt source_cid |
| -- The endpoint of the channel to be disconnected on the device sending this |
| -- request. |
| |
| |
| struct L2capDisconnectionRsp: |
| -- Section 4.7 |
| -- L2CAP_DISCONNECTION_RSP packets shall be sent in response to each valid |
| -- L2CAP_DISCONNECTION_REQ packet. |
| [requires: command_header.code == L2capSignalingPacketCode.DISCONNECTION_RSP] |
| 0 [+L2capSignalingCommandHeader.$size_in_bytes] L2capSignalingCommandHeader command_header |
| $next [+2] UInt destination_cid |
| -- The channel endpoint on the device sending the packet. |
| |
| $next [+2] UInt source_cid |
| -- The channel endpoint on the device receiving the packet. |
| |
| |
| struct L2capLeCreditBasedConnectionReq: |
| -- Section 4.22 |
| -- L2CAP_LE_CREDIT_BASED_CONNECTION_REQ packets are sent to create and |
| -- configure an L2CAP channel between two devices using LE Credit Based Flow |
| -- Control mode. |
| [requires: command_header.code == L2capSignalingPacketCode.LE_CREDIT_BASED_CONNECTION_REQ] |
| 0 [+L2capSignalingCommandHeader.$size_in_bytes] L2capSignalingCommandHeader command_header |
| -- `code` is expected to be LE_CREDIT_BASED_CONNECTION_REQ |
| |
| $next [+2] UInt spsm |
| -- Simplified Protocol/Service Multiplexer. |
| |
| $next [+2] UInt source_cid |
| -- Channel endpoint on the device sending the request. |
| |
| let min_mtu = 23 |
| $next [+2] UInt mtu |
| -- Maximum Transmission Unit |
| [requires: this >= L2capLeCreditBasedConnectionReq.min_mtu] |
| |
| let min_mps = 23 |
| let max_mps = 65533 |
| $next [+2] UInt mps |
| -- Maximum PDU Payload Size |
| [requires: L2capLeCreditBasedConnectionReq.min_mps <= this <= L2capLeCreditBasedConnectionReq.max_mps] |
| |
| let max_credit_value = 65535 |
| $next [+2] UInt initial_credits |
| [requires: this <= L2capLeCreditBasedConnectionReq.max_credit_value] |
| |
| |
| struct L2capFlowControlCreditInd: |
| -- Section 4.24 |
| -- A device shall send an L2CAP_FLOW_CONTROL_CREDIT_IND packet when it is |
| -- capable of receiving additional K-frames (for example after it has |
| -- processed one or more K-frames) in LE Credit Based Flow Control mode and |
| -- Enhanced Credit Based Flow Control mode. |
| [requires: command_header.code == L2capSignalingPacketCode.FLOW_CONTROL_CREDIT_IND] |
| 0 [+L2capSignalingCommandHeader.$size_in_bytes] L2capSignalingCommandHeader command_header |
| $next [+2] UInt cid |
| -- The source channel endpoint of the device sending the packet. |
| |
| $next [+2] UInt credits |
| -- The credit value field represents number of credits the receiving device |
| -- can increment. |
| [requires: 1 <= this <= 65535] |