Bluetooth Logical Links, Packet Format & Packet Types

 

Bluetooth Logical Links, Packet Format & Packet Types
LC · ACL-C · ACL-U · SCO-S · eSCO-S · Access Codes · Header Fields · Link Control Packets
🔗
5 Link Types
LC, ACL-C/U, SCO-S, eSCO-S
📦
BR + EDR
Packet formats
💻
BlueZ Snippets
Real code examples
🎯
3 Scenarios
File · Voice · Music

What This Article Covers

In the previous article we covered Physical Channels, Physical Links, and the three Logical Transports — ACL, SCO, and eSCO. Now we go one layer higher and look at Logical Links, which are the actual named channels that carry specific traffic types like LMP control signals or L2CAP user data.

After that, we open a real Bluetooth packet and see what is inside — what the access code does, what every header field means, and how BR (Basic Rate) and EDR (Enhanced Data Rate) packets differ in structure. Finally, we look at the different packet types that exist and what each one is used for. Code snippets from BlueZ are included wherever they make the concept clearer.

Keywords covered in this article

Bluetooth Logical Links ACL-C ACL-U SCO-S eSCO-S Bluetooth Packet Format Access Code DAC CAC IAC LT_ADDR Header Field BR EDR Packet Link Control Packets NULL POLL FHS DM1 BlueZ ACL Socket Bluetooth Baseband SBC Codec A2DP

In the previous article we saw that a Logical Transport (ACL, SCO, eSCO) is a pipe between a Master and a Slave. A Logical Link is a named sub-channel that rides inside that pipe and is dedicated to carrying a specific kind of traffic.

The simplest way to understand this distinction is with an analogy. Imagine an ACL transport as a highway. On that highway you have different lanes — one lane carries trucks (control messages between link managers), another carries cars (user data from your app). Each lane is a Logical Link. They all share the same physical highway, but they carry different types of cargo and have different priority rules.


LOGICAL LINKS ON TOP OF LOGICAL TRANSPORTS
============================================================
ACL Logical Transport
┌─────────────────────────────────────┐
│  ┌─────────┐      ┌─────────────────┐  │
│  │  ACL-C  │      │      ACL-U      │  │
│  │ Control │      │    User Data    │  │
│  │  (LMP)  │      │  (L2CAP layer)  │  │
│  └─────────┘      └─────────────────┘  │
└─────────────────────────────────────┘
(LC link rides inside packet headers, not payload)

SCO Logical Transport
┌─────────────────────────────────────┐
│  ┌─────────────────────────────┐    │
│  │            SCO-S            │    │
│  │   Synchronous Voice Stream  │    │
│  └─────────────────────────────┘    │
└─────────────────────────────────────┘

eSCO Logical Transport
┌─────────────────────────────────────┐
│  ┌─────────────────────────────┐    │
│  │           eSCO-S            │    │
│  │   Enhanced Sync Voice/WBS   │    │
│  └─────────────────────────────┘    │
└─────────────────────────────────────┘
============================================================

Link Type Full Name What it Carries Where it Lives
LC Link Control ARQ, flow control, payload type info — very low-level bookkeeping Inside the packet header of every packet
ACL-C ACL Control LMP (Link Manager Protocol) messages between the Master and Slave link managers Packet payload, on ACL transport
ACL-U ACL User L2CAP data — this is where all your application data flows (A2DP, HFP, FTP, etc.) Packet payload, on ACL transport
SCO-S SCO Synchronous Synchronous voice stream (narrow-band, 64 kbps) SCO logical transport
eSCO-S eSCO Synchronous Higher-quality voice, Wide Band Speech eSCO logical transport
💡 Naming shortcut in practice: Engineers and documentation almost always drop the suffix (–C, –U, –S) and just say ACL, SCO, or eSCO link. Context makes it clear which one is meant. If someone says “the ACL link carries LMP messages,” they mean ACL-C. If they say “the ACL link carries L2CAP data,” they mean ACL-U.

LC Link — the Invisible Link

The LC link is special because it does not get a slot in the packet payload. Instead, it is embedded directly in the packet header of every single Bluetooth packet that is transmitted. Fields like the acknowledgement bit (ARQN), the flow control bit (FLOW), and the sequence number (SEQN) are all LC link fields. This makes the LC link an ever-present background channel — you cannot send any Bluetooth packet without also sending LC information.

ACL-C vs ACL-U — Priority Matters

Both ACL-C and ACL-U share the same ACL logical transport, but they are not treated equally. ACL-C has higher priority than ACL-U. This makes sense — you never want your application’s file download to block a critical LMP control message that manages the connection itself.

3. Real-World Scenarios: File, Voice, and Music
File Transfer Voice Routing Music Streaming SBC Codec

Let’s walk through three everyday Bluetooth use cases and see exactly which links are involved in each one.

📁 Scenario 1 — File Transfer (Phone ↔ Laptop)

When you transfer a file between your phone and a laptop over Bluetooth, the whole transfer happens over a single ACL link. Specifically, the file data flows on the ACL-U logical link (carried by L2CAP), while the FTP profile commands (open file, read chunk, close) also travel on ACL but via higher protocol layers.


[Phone] ──────── ACL Link ──────────► [Laptop]
                 │
               ACL-U 
           (L2CAP data)
                 │
        FTP profile on top
       File chunks transferred 
       in bursts, retransmitted 
             if lost
🎧 Scenario 2 — Voice Call via Mono Headset

When a call arrives and the user accepts it on their Bluetooth headset, two links are active at the same time on the same physical connection:

  • ACL link — used first to set up the connection and to carry HFP (Hands-Free Profile) commands. Things like “call accepted”, “volume changed”, “call ended” all travel here.
  • SCO or eSCO link — created once the user is ready to talk. The actual voice audio flows here. SCO for narrow-band (older headsets), eSCO for wideband/HD voice (modern ones).

[Phone] ──── Physical Link ────► [Headset]
             │            │
           [ACL]     [SCO / eSCO]
             │            │
       HFP commands   Voice audio
       (call accept,  (64 kbps or 
       volume, mute)  WBS 16kHz)
🎵 Scenario 3 — Music on a Stereo Headset (A2DP)

Music streaming might seem like it should use SCO or eSCO since it’s audio — but it doesn’t. Here’s why: SCO/eSCO were designed for voice sampled at 8–16 kHz. Music is sampled at up to 48 kHz, which needs far more bandwidth than SCO can provide.

So music travels over the ACL link. But raw music at 48 kHz would be too large even for ACL, so the phone first compresses the audio using the SBC codec (Sub-Band Codec — the default codec specified by the Bluetooth A2DP profile). The headset decompresses and plays it back.


[Phone]                             [Stereo Headset]
   │                                       │
   │   MP3 / AAC → SBC encode              │
   │                                       │
   │ ────── ACL-U link (L2CAP + A2DP) ────►│
   │                                       │
   │         SBC compressed stream         │
   │                                       │
   │                        SBC decode → speaker


Sampling rates:
────────────────────────────────────────────────────────────
Voice (SCO) :  8,000 Hz → only 64 kbps, fine for calls
WBS (eSCO)  : 16,000 Hz → richer but still voice-only
Music (ACL) : up to 48,000 Hz → SBC codec needed
============================================================
⚠️ Common confusion: “Voice and music are both audio — why different links?” The answer is always bandwidth. A voice call needs about 64–256 kbps. Music at CD quality needs 1.4 Mbps raw. SCO cannot handle that. ACL is the only option, with compression helping bridge the gap.

4. BlueZ — Opening an ACL Socket in Code
BlueZ L2CAP BTPROTO_L2CAP ACL Socket HCI Connect

In BlueZ (the Linux Bluetooth stack), when user-space code opens an L2CAP socket to a remote device, it is actually using the ACL-U logical link underneath. The kernel sets up the ACL transport automatically. Here is what that looks like at the application level:

BlueZ — Opening an L2CAP (ACL-U) socket
#include <stdio.h> #include <string.h> #include <sys/socket.h> #include <bluetooth/bluetooth.h> #include <bluetooth/l2cap.h> int main(void) { /* AF_BLUETOOTH + BTPROTO_L2CAP creates a socket that rides on the ACL-U logical link. Every L2CAP PSM connection you open to a remote device goes through the ACL transport. */ int sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (sock < 0) { perror("socket"); return 1; } struct sockaddr_l2 addr = {0}; addr.l2_family = AF_BLUETOOTH; addr.l2_psm = htobs(0x1001); /* PSM for your custom service */ /* Remote device address — replace with your target */ str2ba("AA:BB:CC:DD:EE:FF", &addr.l2_bdaddr); /* connect() triggers an ACL connection at the HCI level. BlueZ's kernel module (net/bluetooth/l2cap_core.c) will: 1. Check if an ACL link to this BD_ADDR already exists. 2. If not, issue HCI_Create_Connection to the controller. 3. Once ACL is up, open the L2CAP channel on ACL-U. */ if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("connect"); return 1; } printf("ACL-U link established via L2CAP socket\n"); /* From here: send() / recv() carry your user data on ACL-U */ return 0; }

If you want to watch the actual ACL link come up at the HCI level, you can use btmon while running the above code:

Terminal — watch HCI events with btmon
# Terminal 1 — start monitor $ sudo btmon # Terminal 2 — run the connect code # btmon will show: # > HCI Event: Connect Request (0x04) # > HCI Event: Connection Complete (0x03) ← ACL link is now up # > ACL Data TX: handle 0x000b, flags 0x02 ← ACL-U data flowing

For SCO (voice) connections, BlueZ uses BTPROTO_SCO instead. The kernel internally issues HCI_Add_SCO_Connection or HCI_Setup_Synchronous_Connection to the controller, which creates the SCO or eSCO logical transport and the SCO-S / eSCO-S link on top.

BlueZ — SCO socket for voice (eSCO-S link underneath)
#include <bluetooth/sco.h> int sco_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); struct sockaddr_sco sco_addr = {0}; sco_addr.sco_family = AF_BLUETOOTH; str2ba(“AA:BB:CC:DD:EE:FF”, &sco_addr.sco_bdaddr); /* connect() → BlueZ issues HCI_Setup_Synchronous_Connection to the controller. The controller negotiates SCO or eSCO based on supported packet types and creates the SCO-S or eSCO-S logical link. 8-bit PCM voice samples flow here. */ connect(sco_sock, (struct sockaddr *)&sco_addr, sizeof(sco_addr));

5. Inside a Bluetooth Packet — BR and EDR Format
Access Code Header Payload BR vs EDR

Every packet sent over a Bluetooth physical channel has the same three-part structure at the top level: an Access Code, a Header, and a Payload. Only the Access Code is mandatory. The Header and Payload are optional depending on the packet type.


STANDARD BR (BASIC RATE) PACKET FORMAT
============================================================
┌──────────────┬──────────────┬──────────────────────┐
│ ACCESS CODE  │    HEADER    │       PAYLOAD        │
│  (72 bits)   │  (54 bits)   │   (0 – 2745 bits)    │
└──────────────┴──────────────┴──────────────────────┘
  ^ Mandatory     ^ Optional      ^ Optional

STANDARD EDR (ENHANCED DATA RATE) PACKET FORMAT
============================================================
┌──────────────┬──────────────┬───────┬──────┬──────────────┬─────────┐
│ ACCESS CODE  │    HEADER    │ GUARD │ SYNC │   PAYLOAD    │ TRAILER │
│  (72 bits)   │  (54 bits)   │       │      │              │         │
└──────────────┴──────────────┴───────┴──────┴──────────────┴─────────┘
  ^ Extra fields needed because EDR switches modulation for payload
============================================================

The modulation scheme changes between the header and payload in an EDR packet. The GUARD and SYNC fields signal this transition to the radio hardware so it can switch cleanly. The TRAILER marks the end of the high-speed payload section. We will cover this more in the EDR section below.

6. Access Code — The Packet’s Front Door
DAC CAC IAC DC Offset

The Access Code is the very first thing in every Bluetooth packet and it is always present. Before the receiver even looks at the header or payload, it checks the Access Code. If the code does not match what the receiver expects, the entire packet is silently discarded.

It serves three purposes at once: synchronisation (helps the receiver lock its clock to the incoming signal), DC offset compensation (corrects for any DC bias in the radio hardware), and identification (tells the receiver whether this packet belongs to its piconet or not).

1
Device Access Code (DAC) — Used before a connection is established, during the paging phase. A device trying to connect sends the DAC derived from the target device’s BD_ADDR. Only the intended target recognises and responds to it.
2
Channel Access Code (CAC) — Once two devices are connected and in a piconet, every packet they exchange carries the CAC. Any device hearing this knows whether a packet belongs to its own piconet or to a neighbouring one — and ignores packets from foreign piconets.
3
Inquiry Access Code (IAC) — Used during the discovery phase. When a device wants to find nearby Bluetooth devices, it sends packets with the IAC. Any device in discoverable mode recognises the IAC and responds with its device information.

WHICH ACCESS CODE DOES EACH PHASE USE?
============================================================
Phase         Access Code Used      Purpose
───────────── ───────────────────  ──────────────────────
Discovery     IAC                  "Is anyone out there?"
Paging        DAC (target BD_ADDR) "Calling device XYZ..."
Connected     CAC (piconet key)    "This belongs to our net"

Receiver logic on every packet:
┌──────────────────────────────────────────────┐
│              Read Access Code                │
│                     ↓                        │
│   Does it match expected code for my mode?   │
│                                              │
│       YES → process header + payload         │
│        NO → silently discard, look for next  │
└──────────────────────────────────────────────┘
============================================================

The 54-bit packet header carries six fields. Together they tell the receiver who should process this packet, what type it is, whether to pause or resume sending, whether the previous packet was received correctly, and whether this header itself is intact.


BLUETOOTH PACKET HEADER (54 bits total, includes 1/3 rate FEC)
============================================================
┌──────────┬──────┬──────┬──────┬──────┬─────┐
│ LT_ADDR  │ TYPE │ FLOW │ ARQN │ SEQN │ HEC │
│  3 bits  │4 bits│1 bit │1 bit │1 bit │8bits│
└──────────┴──────┴──────┴──────┴──────┴─────┘

The full 54 bits = 18 raw bits encoded with 1/3 rate FEC 
(every bit sent 3 times for robust header delivery)
============================================================
🏷
LT_ADDR (Logical Transport Address) — 3 bits
This identifies which Slave in the piconet this packet is addressed to (when sent by the Master) or which Slave is sending (when the Slave responds). With 3 bits you can address 0–7. Address 0 is reserved for broadcasts, leaving addresses 1–7 for up to seven Slaves — which is exactly the piconet limit.
📋
TYPE — 4 bits
Specifies the packet type (DM1, DH1, DH3, HV1, EV3, etc.). The receiver reads this to know how many slots the packet occupies, how to decode the payload, and whether to expect CRC or FEC. We’ll cover all packet types in the next sections.
🚦
FLOW — 1 bit
Flow control. When the receiver’s buffer is full and it cannot accept more data, it sets FLOW = 0 in its next outgoing packet. The sender sees this and pauses. Once the receiver drains its buffer, it sets FLOW = 1 again and the sender resumes. This prevents buffer overflows at the application level.
ARQN — 1 bit (Acknowledgement)
After receiving a packet and checking its CRC, the receiver sends back ARQN = 1 (ACK, all good) or ARQN = 0 (NAK, please resend). The sender uses this to decide whether to move to the next packet or retransmit the current one. This is the heart of the ARQ (Automatic Repeat reQuest) mechanism in Bluetooth.
🔢
SEQN — 1 bit (Sequence Number)
A single alternating bit (0 → 1 → 0 → 1…) that helps the receiver detect duplicate packets. If the sender retransmits a packet (because it did not get an ACK), the sequence number stays the same. When the receiver sees the same SEQN twice in a row, it knows it’s a duplicate and discards the second copy.
🛡
HEC — 8 bits (Header Error Check)
An 8-bit checksum covering the header fields. If the receiver calculates a different HEC than the one in the packet, the entire packet is thrown away immediately — the payload is not even looked at. This protects against corrupted headers causing misinterpretation of the packet type or address.
💡 Why is the header encoded with 1/3 rate FEC?
FEC means Forward Error Correction — every bit in the header is sent three times so the receiver can recover the original bit even if one copy is corrupted. The header is small (18 bits raw) so the cost is low. This extra protection is worth it because a misread TYPE or LT_ADDR would cause the entire packet to be misinterpreted.

8. Payload — Where the Real Data Lives
Async Data Field Sync Data Field CRC DV Packet

The payload section holds the actual data being transferred, followed by a CRC for integrity checking. The payload’s internal structure depends on the logical transport it belongs to:


PAYLOAD STRUCTURE BY PACKET TYPE
============================================================
ACL Packet Payload:
┌──────────────────────────────────┬───────┐
│     Asynchronous Data Field      │  CRC  │
│  (L2CAP frames, LMP messages)    │       │
└──────────────────────────────────┴───────┘

SCO/eSCO Packet Payload:
┌──────────────────────────────────┐
│     Synchronous Data Field       │
│  (PCM voice samples — no CRC)    │
└──────────────────────────────────┘
Note: SCO payloads have no CRC because dropped voice 
bytes are not retransmitted.

DV Packet Payload (special — carries both):
┌────────────────┬──────────────────────┬───────┐
│   Sync Data    │      Async Data      │  CRC  │
│    (Voice)     │   (ACL-C LMP data)   │       │
└────────────────┴──────────────────────┴───────┘
DV = Data + Voice in a single packet
============================================================

The DV (Data-Voice) packet is an interesting special case. It carries voice in the synchronous field and control data (LMP) in the asynchronous field — both inside a single packet. Only the async part has a CRC because voice data doesn’t need error correction.

9. How EDR Packets Differ from BR
GFSK vs π/4-DQPSK GUARD SYNC TRAILER 2 Mbps 3 Mbps

BR (Basic Rate) packets use GFSK (Gaussian Frequency Shift Keying) modulation throughout — for both the header and payload. At 1 Mbps it gives decent throughput.

EDR (Enhanced Data Rate) keeps GFSK for the access code and header (for backward compatibility with every Bluetooth receiver), but then switches to a more efficient modulation for the payload — either π/4-DQPSK (2 Mbps) or 8DPSK (3 Mbps). This is why EDR packets need three extra fields that BR packets don’t have:


EDR MODULATION SWITCH
============================================================
[ACCESS CODE] [HEADER] [GUARD][SYNC][PAYLOAD][TRAILER]
      ↑          ↑       ↑     ↑      ↑        ↑
     GFSK       GFSK  Transition    EDR mod  Return to
   (1 Mbps)   (1 Mbps) ramp-down  π/4-DQPSK   GFSK / end
                                   or 8DPSK

GUARD : ramp-down period, radio transitions out of GFSK
SYNC  : synchronisation sequence, receiver locks to EDR
TRAILER: marks end of EDR payload, radio transitions back

Result: header stays compatible with all BT receivers. 
Only EDR-capable receivers decode the payload.
============================================================
💡 Why keep GFSK for the header in EDR?
Backward compatibility. Any Bluetooth device (even pre-EDR) can read the header and understand the packet type and addressing. If the device does not support EDR, it simply ignores the payload. This is a deliberate design decision so EDR-capable devices can coexist with older ones.

10. Packet Types — What the TYPE Field Selects
3 Categories Link Control ACL Packets Sync Packets

The 4-bit TYPE field in the header can represent 16 values (0–15). Different sets of values are interpreted differently depending on the logical transport. Broadly, packet types fall into three categories:

1
Link Control Packets — Used for piconet management. These do not carry user data. Examples: ID, NULL, POLL, FHS, DM1.
2
ACL Packets — Carry asynchronous user data and control. Examples: DM1, DM3, DM5, DH1, DH3, DH5 (BR) and 2DH1, 3DH5 etc. (EDR).
3
Synchronous Packets — Carry voice streams. HV1, HV2, HV3, DV (SCO), EV3, EV4, EV5 (eSCO), 2EV3, 3EV5 etc. (EDR eSCO).

11. Reading a Packet Type Name — 3DH5 and 2EV3 Decoded
Naming Convention Slot Count BR vs EDR prefix

Bluetooth packet names look cryptic at first but each character has a specific meaning. Let’s decode two examples:


READING ACL PACKET NAME — 3DH5
============================================================
  3  D  H  5
  │  │  │  │
  │  │  │  └── Occupies 5 time slots
  │  │  │
  │  │  └────── High Rate packet (H = High Rate)
  │  │
  │  └────────── D = ACL Data Packet
  │
  └────────────── EDR prefix: 3 = 8DPSK (3 Mbps)
                  (No prefix = Basic Rate 1 Mbps)

So "3DH5" = EDR, ACL data, high-rate, 5-slot packet
─────────────────────────────────────────────────
READING SYNC PACKET NAME — 2EV3
============================================================
  2  E  V  3
  │  │  │  │
  │  │  │  └── Occupies 3 time slots
  │  │  │
  │  │  └────── V = Voice (synchronous)
  │  │
  │  └────────── EV = Enhanced Voice → eSCO packet
  │
  └────────────── EDR prefix: 2 = π/4-DQPSK (2 Mbps)
                  (HV or DV prefix = SCO packet)

So "2EV3" = EDR, eSCO, 3-slot voice packet
============================================================

For BR packets there is no numeric prefix — so DH3 is a 1 Mbps ACL high-rate 3-slot packet, and HV1 is a 1 Mbps SCO voice 1-slot packet.

Name Transport Rate Slots Type
DM1 ACL BR (1 Mbps) 1 Medium-rate data
DH5 ACL BR (1 Mbps) 5 High-rate data
3DH5 ACL EDR (3 Mbps) 5 High-rate data
HV1 SCO BR (1 Mbps) 1 Voice (no FEC)
EV3 eSCO BR (1 Mbps) 3 Enhanced voice
2EV3 eSCO EDR (2 Mbps) 3 Enhanced voice

12. Link Control Packet Types in Detail
ID NULL POLL FHS DM1

These five packet types are used to manage the piconet itself — before, during, and after connection setup. None of them carry user data (except DM1 which can also carry control data).

📍 ID — Identity Packet

Used before any connection exists. It contains only the Device Access Code (DAC) or Inquiry Access Code (IAC). There is no header and no payload — it is literally just the access code by itself. This makes it the most lightweight packet in the entire Bluetooth spec. A paging device broadcasts ID packets repeatedly until the target responds.


ID Packet:
┌──────────────┐
│ ACCESS CODE  │  <-- No Header, No Payload
│  (DAC / IAC) │      Most robust, minimal size
└──────────────┘
🔕 NULL — The Empty Acknowledgement

Once connected, a Slave must respond to every Master transmission in its allotted slot — even if it has nothing to send. The NULL packet is the Slave’s way of saying “I received your packet, I have nothing to send back.” It carries no payload, but the ARQN field in the header is set to 1 (positive ACK) or 0 (negative ACK / please resend).

NULL is also used by the Slave to tell the Master that its receive buffer is full (FLOW = 0). The Master will then pause transmission until the Slave clears its buffer and sends FLOW = 1 in a subsequent NULL.


NULL Packet use-cases:
┌──────────────────────────────────────────────────┐
│  Master sends data → Slave has nothing to send   │
│  Slave replies with NULL (ARQN=1 = got it fine)  │
│                                                  │
│  Slave buffer fills up → FLOW=0 in NULL          │
│  Master pauses → Slave clears buffer             │
│  Slave sends FLOW=1 → Master resumes             │
└──────────────────────────────────────────────────┘
📡 POLL — Master’s Check-In

The POLL packet is the Master’s equivalent of NULL, but with one key difference: a Slave receiving a POLL must reply. It cannot ignore it. The Master uses POLL to check two things: is the Slave still there (alive check), and does the Slave have any data to send?

If the Slave has nothing to send, it responds with a NULL packet. If it has data, it responds with a data packet. Unlike NULL, the Master does not need to acknowledge the POLL — it simply sends it and waits for the reply.


POLL / NULL interaction:
   Master                         Slave
     │                              │
     │──── POLL ──────────────────► │  "Are you there? Any data?"
     │                              │
     │◄─── NULL ─────────────────── │  "Yes I'm here, nothing to send"
     │                              │
     │             OR               │
     │                              │
     │◄─── [Data Packet] ────────── │  "Yes I'm here, here's my data"
     │                              │

Note: NULL does not require an ACK from Master.
POLL always requires a response from Slave.
🕐 FHS — Frequency Hop Synchronization

The FHS (Frequency Hop Synchronization) packet carries the Master’s real-time clock value and BD_ADDR. This information is what allows a Slave to derive the Master’s frequency-hopping sequence and synchronise to it. An FHS packet is sent in two situations:

  • During the initial connection setup — the Master sends its FHS so the Slave can join the piconet and sync clocks.
  • During a Role Switch — when the two devices swap Master/Slave roles, the new Master sends an FHS so the former Master (now Slave) can resync to the new piconet parameters.
📨 DM1 — Data Medium Rate 1-slot

DM1 is the odd one out in this group because it can carry actual data in its payload. It occupies a single slot and uses medium data rate. It is classified as both a Link Control packet type (used for LMP messages on ACL-C) and an ACL data packet type (used for small L2CAP payloads on ACL-U).

In practice, DM1 is heavily used for LMP control messages — things like encryption requests, channel map updates, and feature exchange messages — because these are small and need to be sent reliably.

💡 Observing these packets with BlueZ tools:
You can watch NULL, POLL and FHS events at the HCI level using sudo btmon while connecting devices. The HCI event stream will show connection request, connection complete (FHS exchanged), and then data events once ACL is up.
BlueZ — Monitor HCI packet flow (observe Link Control packets)
# btmon captures all HCI traffic between host and controller. # When a remote device pages your machine you’ll see: # # @ RAW Open: hci0 (vendor: snoop) # > HCI Event: Connect Request (0x04) ← ID packet received, now paging # Address: AA:BB:CC:DD:EE:FF (Public) # Class: 0x000000 # Link type: ACL (0x01) # # Then the stack responds with HCI_Accept_Connection_Request. # The controller handles the FHS exchange internally and reports: # # > HCI Event: Connection Complete (0x03) ← FHS done, piconet formed # Status: Success (0x00) # Handle: 0x000b # # At this point: ACL logical transport + ACL-C/ACL-U links are live. # POLL/NULL packets are handled entirely in the controller firmware. # They are not visible at the HCI layer — they happen below HCI. $ sudo btmon

13. Summary and Key Takeaways
Five logical links exist: LC (in every header), ACL-C (LMP control, higher priority), ACL-U (L2CAP user data), SCO-S (voice), and eSCO-S (HD voice).
File transfers use ACL-U. Voice calls use ACL (setup/commands) + SCO or eSCO (voice stream). Music streaming uses ACL-U with SBC-encoded audio — SCO bandwidth is not enough for music.
Every packet starts with an Access Code. DAC for paging, CAC for piconet traffic, IAC for discovery. If the code doesn’t match, the packet is dropped immediately.
The packet header has 6 fields: LT_ADDR (which Slave), TYPE (packet type), FLOW (buffer control), ARQN (ACK/NAK), SEQN (duplicate detection), HEC (header integrity). Protected by 1/3 rate FEC.
EDR packets add GUARD + SYNC + TRAILER fields to handle the modulation switch from GFSK (header) to π/4-DQPSK or 8DPSK (payload) for higher throughput.
Packet type names encode everything: prefix (2/3 = EDR modulation), letter (D = data, EV = eSCO voice, HV = SCO voice), rate (H = high, M = medium), slots (1/3/5).
Five Link Control packets: ID (pre-connection, no header), NULL (ACK with no data), POLL (Master check-in, Slave must reply), FHS (clock sync for piconet join / role switch), DM1 (small control data).
🔜 What’s Next?
In the next article we go up the stack into L2CAP — how it multiplexes multiple protocols over the single ACL-U link, what channels and PSMs are, and how flow control and segmentation work. We will also look at how BlueZ exposes L2CAP to user-space applications.

Keep Climbing the Bluetooth Stack 🚀

This is part of the EmbeddedPathashala Bluetooth Lower Layers series. Each article builds directly on the previous one — no gaps, no hand-waving.

Leave a Reply

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