what are l2cap features – ble l2cap tutorial

 

L2CAP Features — CIDs, Fragmentation & Multiplexing
Chapter 10 Sections 10.4–10.5 — MTU Comparison · Fixed Channel IDs · Zero-Setup Channels · Fragmentation · Multiplexing
ATT CID
0x0004
Signaling CID
0x0005
SMP CID
0x0006
Setup PDUs
Zero needed
Keywords:

BLE L2CAP fixed channel identifiers CID BLE CID 0x0004 ATT 0x0005 0x0006 SMP BLE L2CAP no CONNECT CONFIGURE BLE L2CAP fragmentation defragmentation BLE L2CAP channel multiplexing LE-U BLE L2CAP 4.1 additional CID credit-based BLE faster connection setup zero PDUs

Continuing Section 10.4 — MTU Comparison

The previous file introduced the MTU concept and the 23-octet minimum for LE. This section completes the MTU comparison with BR/EDR and explains the practical benefits of keeping the LE MTU small. It then moves into Section 10.5 — the three key features L2CAP provides to higher layers.

10.4 — MTU (Completed)

LE MTU vs BR/EDR MTU — A Deliberate Design Choice

The minimum MTU for LE (23 octets) is much smaller than BR/EDR’s minimum (48 octets without extended flow spec, 672 with). This was not an oversight — it was a deliberate design decision with two direct benefits:

Smaller buffers needed

The maximum SDU size directly determines how large the receive and transmit buffers need to be in the controller and host. A 23-byte maximum requires far less RAM than a 672-byte maximum. For small BLE sensors running on tiny microcontrollers with 4–8 KB of total RAM, this difference is critical.

Less power consumed per exchange

Smaller packets mean shorter radio-on time per packet. When a heart rate monitor only needs to send 4 bytes of data, packing it into a 23-byte PDU and transmitting that costs far less power than the minimum 48-byte BR/EDR packet would. Every microsecond the radio is on costs battery.

What happens at the L2CAP level if the sender tries to exceed the receiver’s MTU? The receiver returns a Command_Reject PDU. The Command_Reject carries a reason code — in this case “Signaling MTU exceeded.” The sender must reduce its packet size or close the channel.

/* Checking MTU on a BLE connection with BlueZ              */

/* The effective L2CAP MTU for ATT is negotiated via ATT    */
/* Exchange MTU Request (opcode 0x02) / Response (0x03)     */

/* Using gatttool to negotiate a larger MTU:                */
/* gatttool --device=AA:BB:CC:DD:EE:FF --mtu=247 -I         */

/* Reading current ATT MTU from BlueZ kernel trace:         */
/* sudo cat /sys/kernel/debug/bluetooth/hci0/l2cap           */
/* or via btmon: shows ATT MTU exchange in connection setup */

/* Default after BLE connection established: 23 bytes       */
/* After Exchange MTU: up to min(client_mtu, server_mtu)   */

10.5 — L2CAP Features

Three Features L2CAP Provides to Higher Layers

Like BR/EDR, LE L2CAP provides the abstraction of channels to the layers above it. To ATT and the Security Manager, L2CAP looks like a simple delivery service with named channels. The three features that make this work are:

Fixed Channel Identifiers
Fragmentation & Defragmentation
Multiplexing & Demultiplexing

10.5.1 — Fixed Channel Identifiers

Every LE Channel Has a Permanent Number — No Dynamic Allocation

A Channel Identifier (CID) is a number that tells L2CAP which higher-layer entity a packet belongs to. It is the “address” at the L2CAP layer — like a port number in TCP/IP. All data for ATT is labelled with CID 0x0004; all data for the Security Manager is labelled with CID 0x0006. The receiving L2CAP reads the CID from the packet header and knows exactly which protocol to deliver it to.

BR/EDR vs LE — the big difference: In BR/EDR, most CIDs are dynamically allocated. If SDP wants to create a connection, L2CAP goes through a CONNECT → CONFIGURE handshake to establish the channel and get a CID assigned. This takes several PDU round trips before any data can flow.

In LE, all channels use fixed CIDs. The CIDs for ATT and SMP are defined in the specification and never change. No negotiation, no handshake, no CONNECT or CONFIGURE PDUs needed. As soon as the Link Layer connection is established, ATT can immediately send data on CID 0x0004 and SMP can immediately send data on CID 0x0006.

Table 10.1 — Fixed CID Name Space for LE (BLE 4.0)
CID Description
0x0004 Attribute Protocol (ATT)

All GATT reads, writes, notifications, and indications travel over this channel. Available immediately after connection establishment — no setup needed.

0x0005 LE L2CAP Signaling Channel

L2CAP control messages — connection parameter update requests, credit-based connection requests (4.1), and command reject PDUs. One command per PDU (simpler than BR/EDR).

0x0006 Security Manager Protocol (SMP)

Pairing, key exchange, and authentication messages. Also available immediately after connection with no prior setup.

Why zero setup PDUs matters for connection speed:

In BR/EDR, if an application needs to use SDP (Service Discovery), L2CAP must first exchange CONNECT and CONFIGURE signals to establish the channel. These are multiple round trips over the air before any real data can flow. In BLE, the moment the Link Layer reports a connection established, ATT and SMP can start sending immediately. This is one of the reasons BLE feels so much faster to connect than classic Bluetooth.

Table 10.2 — Additional CID Name Space for LE (BLE 4.1 Onwards)
CID Range Description
0x0020 – 0x003E As per SIG assigned numbers page — fixed assignments for specific protocols
0x0040 – 0x007F Dynamically allocated during credit-based connection mechanisms (the LE credit-based flow control introduced in 4.1)

BLE 4.1 added these additional CID ranges to support connection-oriented channels — channels that are dynamically created for specific use cases (like the Internet Protocol Support Profile, IPSP). These use the credit-based flow control mode introduced in 4.1. The dynamically allocated range 0x0040–0x007F is assigned when the channel is created and released when it is closed.

/* L2CAP channel CIDs in BlueZ kernel source               */
/* From include/net/bluetooth/l2cap.h                      */

#define L2CAP_CID_ATT           0x0004  /* Attribute Protocol */
#define L2CAP_CID_LE_SIGNALING  0x0005  /* LE Signaling       */
#define L2CAP_CID_SMP           0x0006  /* Security Manager   */

/* These are available as soon as LE connection is created  */
/* No LE_Connect or LE_Config PDUs needed — just send data  */

/* Checking L2CAP channels on an active connection:        */
/* sudo cat /sys/kernel/debug/bluetooth/hci0/l2cap          */
/* or:                                                     */
/* sudo bt-monitor | grep L2CAP                            */

10.5.2 — Fragmentation and Defragmentation

L2CAP Transparently Splits Large SDUs and Reassembles Them

Higher layer protocols like ATT may need to send packets larger than what the link layer can carry in a single PDU. The link layer has a maximum payload size determined by the controller’s ACL buffer size (27 bytes in BLE 4.0, up to 251 bytes in BLE 4.2). L2CAP handles the mismatch transparently.

On the transmit side — Fragmentation

When ATT hands L2CAP an SDU that is larger than the controller’s ACL buffer size, L2CAP splits it into multiple PDU fragments. Each fragment fits in one HCI ACL data packet. The first fragment’s L2CAP header contains the full SDU length so the receiver knows how much to expect.

On the receive side — Defragmentation

As fragments arrive, L2CAP collects them using the Length field in the first PDU to know how many bytes to expect. When all fragments have arrived, L2CAP reassembles them into the original SDU and delivers it to the upper layer (ATT or SMP) as one complete packet.

Fragmentation — One Large SDU Split Into Three PDU Fragments

ATT SDU — 90 bytes (too large for one ACL packet)
↓ L2CAP fragments into 3 ACL packets

Fragment 1 (first)
Len=90
CID=4
data[0..27]
PB=10 (first)
Fragment 2
data[28..55]
PB=01 (cont.)
Fragment 3 (last)
data[56..89]
PB=01 (cont.)
↓ Receiver reassembles using Len=90 from Fragment 1
ATT SDU — 90 bytes — delivered intact to ATT layer

The PB (Packet Boundary) bits in the HCI ACL packet header tell the controller and the L2CAP layer whether a fragment is the start of a new SDU (PB=10) or a continuation of the previous one (PB=01). L2CAP uses the Length field in the first fragment to know when it has received all pieces.

10.5.3 — Channel Multiplexing and Demultiplexing

Three Channels Over One Link — L2CAP Sorts Traffic by CID

There is only one LE-U logical link between two connected BLE devices. But there are three L2CAP channels that all need to share it: ATT (CID 0x0004), SMP (CID 0x0006), and L2CAP’s own signaling channel (CID 0x0005). L2CAP handles this by labelling every packet with its CID and sorting incoming packets by CID.

Figure 10.3 — Channel Multiplexing (Three Channels Over One LE-U Logical Link)

SMP
CID = 0x0006
ATT
CID = 0x0004
L2CAP Sig
CID = 0x0005

L2CAP — Multiplexer/Demultiplexer
Labels TX with CID → sends over LE-U link
Reads CID on RX → delivers to right protocol

LE-U Logical Link (single shared link)
CID:06
CID:04
CID:05
CID:04
CID:06
Mixed stream of packets from all three channels

How multiplexing works: When ATT sends data to L2CAP, L2CAP adds the L2CAP header (with CID=0x0004) and puts the packet into the LE-U link’s transmission queue. When SMP sends something, L2CAP adds CID=0x0006. When its own signaling needs to be sent, it uses CID=0x0005. All these packets flow through the same physical data channel using the connection’s frequency hopping pattern.

How demultiplexing works: When a packet arrives, L2CAP reads the CID from the packet header. If CID=0x0004 it passes the payload to ATT. If CID=0x0006 it passes it to SMP. If CID=0x0005 it processes it internally as a signaling command. The upper layers never see raw PDUs — they only receive the extracted payload (the SDU).

/* L2CAP multiplexing is transparent — upper layers just     */
/* use sockets with a specific CID/protocol type             */

/* In BlueZ, connecting to the ATT channel (CID 0x0004):    */
struct sockaddr_l2 addr = {
    .l2_family   = AF_BLUETOOTH,
    .l2_psm      = 0,           /* not used for fixed CIDs  */
    .l2_cid      = htobs(L2CAP_CID_ATT),  /* 0x0004        */
    .l2_bdaddr_type = BDADDR_LE_PUBLIC,
};

/* Alternatively: BlueZ bt_io library handles this          */
/* automatically when you open a GATT connection            */
/* The L2CAP layer is completely transparent to GATT code   */

Chapter 10 L2CAP Series
PDF 1618

Intro, PDU/SDU, Assumptions, MTU
PDF 1920 ✅

MTU cont., Fixed CIDs, Fragmentation, Multiplexing

PDF 2123

Data Packets, B-Frame, Parameters, Signaling

Next — Data Packets, B-Frame & L2CAP Signaling

You now understand all three L2CAP features. PDF 2123 covers the actual data packet format (B-Frame), the five modes of operation, L2CAP parameters, and the signaling channel with its commands.

Next: Data Packets, Signaling & Commands → ← PDF 1618: L2CAP Intro

Leave a Reply

Your email address will not be published. Required fields are marked *