First command sent
Separate or Shared
Count-based
Event signals free
Keywords:
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
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_UpdateHCI_LE_Remote_Connection_Parameter_Request_ReplyHCI_LE_Remote_Connection_Parameter_Request_Negative_ReplyLE_Set_Data_LengthLE_Read_Suggested_Default_Data_Length_CommandLE_Write_Suggested_Default_Data_Length_Command |
LE_Connection_Update_Complete_EventLE_Remote_Connection_Parameter_Request_EventLE_Data_Length_Change_Event |
| Physical Links | HCI_LE_Set_Host_Channel_Classification |
— |
| Link Information | HCI_Read_Transmit_Power_LevelHCI_Read_RSSIHCI_LE_Read_Advertising_Channel_Tx_PowerHCI_LE_Read_Channel_Map |
— |
| Authentication & Encryption | HCI_LE_EncryptHCI_LE_Long_Term_Key_Request_ReplyHCI_LE_Long_Term_Key_Request_Negative_ReplyHCI_LE_RandHCI_LE_Start_EncryptionHCI_Write_Authenticated_Payload_TimeoutHCI_Read_Authenticated_Payload_TimeoutHCI_LE_Read_Local_P-256_Public_KeyLE_Generate_DHKey |
Encryption_Change_EventEncryption_Key_Refresh_Complete_EventLE_Long_Term_Key_Requested_EventAuthenticated_Payload_Timeout_Expired_EventLE_Read_Local_P-256_Public_Key_Complete_EventLE_Generate_DHKey_Complete_Event |
| Testing | HCI_LE_Receiver_TestHCI_LE_Transmitter_TestHCI_LE_Test_End |
— |
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
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_Eventback to the host with a status code indicating success or failure
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
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.
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 sizeHCI_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.
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.
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.
— or — HCI_Read_Buffer_Size (shared)
HC_Total_Num_LE_ACL_Data_Packets = e.g. 4
counter: 4→1(one per buffer slot used)
counter: 1→3(2 packets processed → 2 buffers freed)
(repeat loop)
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) */
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.
