HCI Commands in BLE – BLE HCI Layer

 

HCI Commands — Table 9.1 Continued
Chapter 9 — Connection State, Encryption, Testing Groups · Device Setup · Controller Flow Control & Host-to-Controller Data Flow
HCI_Reset
First command sent
Buffer Types
Separate or Shared
Flow Control
Count-based
Completed PKTs
Event signals free

Keywords:

BLE HCI Table 9.1 continued HCI_LE_Connection_Update HCI_LE_Encrypt LTK BLE HCI_Reset device setup BLE HCI_LE_Read_Buffer_Size Number_Of_Completed_Packets_Event BLE host controller flow control HC_LE_ACL_Data_Packet_Length

Continuing Table 9.1 — Remaining Command Groups

The previous file (PDF 1013) presented the first half of Table 9.1 — Device Setup, Controller/Host Flow Control, Controller Configuration, Device Discovery, and Connection Setup groups. This file covers the second half: Connection State commands, Physical Links, Link Information, Authentication and Encryption, and Testing. It then goes deeper into two specific sections: Device Setup (9.2.1) and Controller Flow Control (9.2.2), including the full Host-to-Controller data flow diagram.

Table 9.1 (Continued) — Remaining Command Groups

Connection State, Physical Links, Link Info, Encryption & Testing

These five groups cover commands used after a connection is established — managing connection parameters, reading signal information, controlling encryption, and running RF compliance tests.

Group Commands Events
Connection State HCI_LE_Connection_Update
HCI_LE_Remote_Connection_Parameter_Request_Reply
HCI_LE_Remote_Connection_Parameter_Request_Negative_Reply
LE_Set_Data_Length
LE_Read_Suggested_Default_Data_Length_Command
LE_Write_Suggested_Default_Data_Length_Command
LE_Connection_Update_Complete_Event
LE_Remote_Connection_Parameter_Request_Event
LE_Data_Length_Change_Event
Physical Links HCI_LE_Set_Host_Channel_Classification
Link Information HCI_Read_Transmit_Power_Level
HCI_Read_RSSI
HCI_LE_Read_Advertising_Channel_Tx_Power
HCI_LE_Read_Channel_Map
Authentication & Encryption HCI_LE_Encrypt
HCI_LE_Long_Term_Key_Request_Reply
HCI_LE_Long_Term_Key_Request_Negative_Reply
HCI_LE_Rand
HCI_LE_Start_Encryption
HCI_Write_Authenticated_Payload_Timeout
HCI_Read_Authenticated_Payload_Timeout
HCI_LE_Read_Local_P-256_Public_Key
LE_Generate_DHKey
Encryption_Change_Event
Encryption_Key_Refresh_Complete_Event
LE_Long_Term_Key_Requested_Event
Authenticated_Payload_Timeout_Expired_Event
LE_Read_Local_P-256_Public_Key_Complete_Event
LE_Generate_DHKey_Complete_Event
Testing HCI_LE_Receiver_Test
HCI_LE_Transmitter_Test
HCI_LE_Test_End
HCI_* = shared BR/EDR + LE
HCI_LE_* = LE only
Green = added in BLE 4.1 or 4.2

What Each Group Does — Plain English

Connection State

Commands used to adjust parameters of an already-established connection. HCI_LE_Connection_Update is how the host (Master side) asks the controller to change connInterval, slave latency, or supervision timeout. The 4.1 additions handle the new parameter request/response flow where the Slave can now ask for changes, and the 4.2 additions manage the larger PDU sizes.

Physical Links

HCI_LE_Set_Host_Channel_Classification lets the host tell the controller which of the 37 data channels have bad RF conditions (interference from Wi-Fi, etc.). The controller uses this to update its adaptive frequency hopping channel map.

Link Information

Read-only diagnostic commands. HCI_Read_RSSI returns the Received Signal Strength Indicator for a connection — how strong the incoming radio signal is, useful for proximity detection. HCI_Read_Transmit_Power_Level returns the current TX power level. HCI_LE_Read_Channel_Map returns the current 37-bit channel bitmap for an active connection.

Authentication and Encryption

Everything needed to enable, manage, and verify AES-128 encryption on a connection. HCI_LE_Start_Encryption kicks off the Encryption Start LLCP procedure. When the Slave’s link layer needs the Long Term Key, it fires LE_Long_Term_Key_Requested_Event to the host, which retrieves the LTK from its security database and replies with HCI_LE_Long_Term_Key_Request_Reply. The 4.2 additions add Diffie-Hellman key exchange support for stronger pairing.

Testing

RF compliance test commands required for Bluetooth certification. HCI_LE_Transmitter_Test puts the controller into a mode where it continuously transmits a test pattern on a specified channel at a specified packet length. HCI_LE_Receiver_Test puts it into receive test mode. HCI_LE_Test_End stops either test and returns the number of packets received (for receiver test). These are used on the test bench, not in production operation.

/* Reading RSSI for an active BLE connection via BlueZ */
/* OGF=0x05, OCF=0x0005 = Read RSSI                   */

#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

int read_ble_rssi(int hci_sock, uint16_t handle)
{
    read_rssi_rp rp;
    struct hci_request rq = {
        .ogf    = OGF_STATUS_PARAM,
        .ocf    = OCF_READ_RSSI,
        .cparam = &handle,
        .clen   = 2,
        .rparam = &rp,
        .rlen   = READ_RSSI_RP_SIZE,
    };
    if (hci_send_req(hci_sock, &rq, 1000) < 0)
        return -1;
    /* rp.rssi is a signed int8_t, range typically -127 to +20 dBm */
    printf("RSSI: %d dBm\n", (int8_t)rp.rssi);
    return rp.rssi;
}

/* Or via hcitool command line:                         */
/* hcitool rssi AA:BB:CC:DD:EE:FF                       */

9.2.1 — Device Setup

HCI_Reset — The Very First Command

Device Setup contains only a single command for LE: HCI_Reset. Despite being a one-command group, this is one of the most important commands in the entire HCI specification because it is almost always the first command sent to the controller when a Bluetooth stack initialises.

What HCI_Reset does:

  • Resets the controller hardware to a known clean state
  • Resets the Link Layer — all connection state, any active advertising, any ongoing scan, all cleared
  • Sets all parameters to their defined default values — advertising interval resets to default, white list clears, channel map resets to all channels enabled
  • Moves the Link Layer to the Standby state
  • When the reset is complete, the controller sends a Command_Complete_Event back to the host with a status code indicating success or failure
HCI_Reset — Sequence and Effect on Controller State
Host initialises — stack starts up
Host
HCI_Reset
Controller
Controller resets hardware, link layer, all state → sets all to defaults
Host
Command_Complete_Event (status=0x00)
Controller
Controller now in Standby state. All defaults applied. Ready for further initialisation commands.

After HCI_Reset, the host typically sends a series of initialisation commands: read the controller’s version and capabilities, configure advertising parameters, write the device name, set the event mask to enable the events it cares about, and so on. The reset ensures all of these start from a predictable baseline, regardless of what state the controller was in before.

/* Sending HCI_Reset and waiting for Command_Complete via BlueZ */

#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

int hci_reset_controller(int hci_sock)
{
    /* OGF_HOST_CTL = 0x03, OCF_RESET = 0x0003               */
    if (hci_send_cmd(hci_sock, OGF_HOST_CTL, OCF_RESET, 0, NULL) < 0) {
        perror("hci_send_cmd HCI_Reset");
        return -1;
    }

    /* Wait up to 2 seconds for Command_Complete_Event        */
    /* The event includes a 1-byte status: 0x00 = success     */
    uint8_t status;
    struct hci_filter flt;
    hci_filter_clear(&flt);
    hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
    hci_filter_set_event(EVT_CMD_COMPLETE, &flt);
    setsockopt(hci_sock, SOL_HCI, HCI_FILTER, &flt, sizeof(flt));

    uint8_t buf[HCI_MAX_EVENT_SIZE];
    int n = read(hci_sock, buf, sizeof(buf));
    if (n < 0) return -1;

    evt_cmd_complete *cc = (void *)(buf + 1 + HCI_EVENT_HDR_SIZE);
    status = *((uint8_t *)(cc + 1));
    printf("HCI_Reset status: 0x%02X (%s)\n",
           status, status == 0 ? "success" : "error");
    return status;
}

/* Command line equivalent: */
/* hciconfig hci0 reset     */

9.2.2 — Controller Flow Control

Why Flow Control Is Needed — The Controller Has Finite Buffers

The controller chip has a limited amount of RAM for buffering outgoing ACL data packets. The host can produce data much faster than the radio can transmit it. Without flow control, the host would overflow the controller’s buffers — data would be silently dropped and the Bluetooth link would behave incorrectly.

The HCI layer solves this with a counter-based flow control mechanism. The host keeps a local count of how many free ACL buffers the controller has. It decrements the count each time it sends a packet, and increments it each time the controller reports that packets have been transmitted via the Number_Of_Completed_Packets_Event.

Separate Buffers vs Shared Buffers — How to Tell the Difference

A controller can implement its ACL buffers in two ways, and the host needs to know which to use the right commands:

Separate Buffers

The controller has a dedicated pool of buffers just for LE ACL packets, and a separate pool for BR/EDR ACL packets. In this case:

  • HCI_LE_Read_Buffer_Size → returns the LE buffer count and size
  • HCI_Read_Buffer_Size → returns the BR/EDR buffer count and size

Shared Buffers

The controller has one combined pool used by both BR/EDR and LE. In this case:

  • HCI_LE_Read_Buffer_Size → returns 0 for packet length (signals shared mode)
  • HCI_Read_Buffer_Size → returns the shared pool count and size

So the host always calls HCI_LE_Read_Buffer_Size first. If the returned packet length is 0, it knows to use the shared buffer count from HCI_Read_Buffer_Size for LE flow control too.

The Three Key Commands and Events

HCI_Read_Buffer_Size

Sent during controller initialisation. Returns three values: the maximum size of each ACL data packet the controller can hold, the total number of ACL data packet slots, and the maximum number of SCO packets (irrelevant for LE-only controllers). The host uses the ACL packet count to set its initial buffer availability counter.

HCI_LE_Read_Buffer_Size

The LE-specific version of the buffer query. Returns two values: HC_LE_ACL_Data_Packet_Length (max bytes per LE ACL packet) and HC_Total_Num_LE_ACL_Data_Packets (how many LE ACL slots the controller has). If HC_LE_ACL_Data_Packet_Length comes back as 0, fall back to HCI_Read_Buffer_Size for the shared count.

Number_Of_Completed_Packets_Event

Sent by the controller — not requested by the host. The controller fires this event automatically each time it finishes transmitting (or flushing) one or more ACL packets. The event contains a list of connection handles and for each one how many packets were completed. The host increments its free-buffer counter by the reported count, allowing it to send that many more packets.

Figure 9.5 — Host-to-Controller Data Flow Control

This is the complete sequence from initialisation to steady-state flow-controlled data transfer. Every BLE implementation that sends data from host to controller follows this exact pattern.

Figure 9.5 — Host to Controller Data Flow Control (LE)
HOST CONTROLLER

① Initialisation
HCI_LE_Read_Buffer_Size
— or — HCI_Read_Buffer_Size (shared)

HC_LE_ACL_Data_Packet_Length = e.g. 251
HC_Total_Num_LE_ACL_Data_Packets = e.g. 4

Host initialises counter: available_buffers = 4

② Send 3 packets
counter: 4→1
HCI ACL Data Packets ×3
(one per buffer slot used)

Controller transmits packets over LE-U logical link…

③ Receive event
counter: 1→3
Number_Of_Completed_Packets_Event
(2 packets processed → 2 buffers freed)

Host increments counter: available_buffers += 2 → now 3 — can send 2 more packets

④ Send more
(repeat loop)
More HCI ACL Data Packets

The key rule: the host must never send more packets than the current value of its available_buffers counter. If the counter reaches 0, the host must wait for a Number_Of_Completed_Packets_Event before it can send more. This is a credit-based system — each free buffer slot is a credit that can be spent on one outgoing packet.

/* Host-side buffer management — simplified BlueZ concept     */
/* The actual implementation is inside src/shared/queue.c     */

struct hci_flow_ctrl {
    int available_buffers;  /* credits remaining                */
    int packet_size;        /* max bytes per HCI ACL packet     */
};

/* Step 1: initialise after HCI_LE_Read_Buffer_Size response  */
void init_flow_ctrl(struct hci_flow_ctrl *fc,
                    uint8_t  pkt_len,   /* HC_LE_ACL_Data_Packet_Length */
                    uint8_t  num_pkts)  /* HC_Total_Num_LE_ACL_Data_Packets */
{
    fc->packet_size       = pkt_len;
    fc->available_buffers = num_pkts;
    printf("LE buffers: %d × %d bytes\n", num_pkts, pkt_len);
}

/* Step 2: decrement when sending */
int send_acl_packet(struct hci_flow_ctrl *fc, ...)
{
    if (fc->available_buffers <= 0) {
        printf("No buffers — must wait for Completed Packets event\n");
        return -EAGAIN;
    }
    fc->available_buffers--;
    /* ... actually send the packet ... */
    return 0;
}

/* Step 3: increment when Number_Of_Completed_Packets_Event fires */
void on_completed_packets(struct hci_flow_ctrl *fc, int count)
{
    fc->available_buffers += count;
    printf("Buffers freed: %d, available now: %d\n",
           count, fc->available_buffers);
}

/* Reading LE buffer size:                                    */
/* hcitool cmd 0x08 0x0002   (HCI_LE_Read_Buffer_Size)       */
/* Response: pkt_len (2 bytes LE) + num_pkts (1 byte LE)     */

9.2.3 — Host Flow Control (Introduction)

The flow control described above runs in one direction only — host to controller. The controller never queues up more data than its buffers can hold, and the host backs off when buffers fill up.

But there is also an optional reverse direction: host flow control, which controls data flowing from the controller back to the host. This is rarely needed in modern implementations because host CPUs are fast enough to consume incoming data as fast as the Bluetooth radio can deliver it. However, if the host has a very slow processor or very limited RAM — perhaps a tiny microcontroller — it may need to tell the controller to slow down its incoming data delivery.

The commands for host flow control are covered in detail in PDF 46. This section introduces the concept: the host tells the controller about its own receive buffer capacity, and the controller uses that information to pace the data it sends up.

/* Host flow control is off by default after HCI_Reset       */
/* Most embedded systems leave it off                        */

/* To enable (rarely needed): */
/* OGF=0x03, OCF=0x0031 = Set Controller To Host Flow Control */
/* parameter: 0x00 = off (default)                           */
/*            0x01 = ACL data flow control on               */
/*            0x02 = sync data flow control on              */
/*            0x03 = both on                                */
hcitool cmd 0x03 0x0031 01  /* enable ACL host flow control */

Next — Host Flow Control Commands, White List & Resolving List HCI, Controller Information

You have seen the complete Table 9.1, Device Setup (HCI_Reset) and Controller Flow Control in full. The next file covers Host Flow Control commands, White List HCI operations, Resolving List HCI operations, Controller Information queries, and Remote Information commands.

Leave a Reply

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