Rarely used
4 HCI commands
7 HCI commands
251 bytes
Continuing Section 9.2.3 — Host Flow Control
The previous file covered Controller Flow Control (data going host → controller) and the counter-based buffer management system. This file covers the remaining Host Flow Control commands (data going controller → host), the White List HCI commands used for device filtering, the Resolving List commands introduced in BLE 4.2 for controller-side privacy address resolution, Controller Information queries, and Remote Information commands.
9.2.3 — Host Flow Control Commands
Host flow control is the reverse of what was covered in section 9.2.2. While controller flow control prevents the host from overwhelming the controller’s buffers, host flow control prevents the controller from overwhelming the host’s buffers when sending received data upward.
In most implementations this is not needed — modern processors handle incoming BLE data easily. But a device with a very slow CPU or very limited RAM may genuinely need to pace the controller. The following commands make up this group:
The host uses this command to tell the controller how much buffer space the host itself has for incoming data. The host provides two values: the maximum size of ACL data packets it can receive, and how many ACL data packet slots it has. The controller uses this information to know how much data it can push up before the host’s buffers fill.
A simple on/off switch for the host flow control mechanism. By default, host flow control is disabled after HCI_Reset — the controller sends data upward as fast as it arrives. This command can enable ACL flow control, synchronous flow control, or both. Setting it to enabled instructs the controller to respect the host buffer limits reported by Host_Buffer_Size.
This is the host’s equivalent of the controller’s Number_Of_Completed_Packets_Event. When host flow control is enabled, the host sends this command to tell the controller how many packets it has finished processing. The controller uses this count to know it can send that many more packets to the host. This creates a two-way credit system — each side tells the other how many buffers have been freed.
Sent by the controller when the host has sent more data packets than the controller had free buffer slots — meaning the controller had to drop packets. This is an error condition. In a correctly written host, this event should never fire because the host is supposed to respect the buffer count from HCI_Read_Buffer_Size. Seeing this event in logs means there is a bug in the host’s flow control logic.
Not a flow control command in the traditional sense, but it is grouped here because it controls which events the controller will ever send to the host. It is a 64-bit bitmask — each bit corresponds to one event type. Setting a bit to 1 enables that event; setting it to 0 silences it forever. After HCI_Reset, a specific default mask is active. The host can narrow this further to avoid waking up for events it does not care about, saving CPU cycles.
/* Host flow control setup with BlueZ (rarely needed) */
/* Step 1: Tell controller about host buffers */
/* OGF=0x03, OCF=0x0033 = Host Buffer Size */
typedef struct {
uint16_t acl_mtu; /* max ACL packet size host can accept */
uint8_t sco_mtu; /* max SCO packet size (0 for LE only) */
uint16_t acl_max_pkt;/* max ACL packets host can buffer */
uint16_t sco_max_pkt;/* max SCO packets (0 for LE only) */
} __attribute__((packed)) host_buffer_size_cp;
/* Step 2: Enable host flow control for ACL */
/* OGF=0x03, OCF=0x0031 */
/* Parameter: 0x01 = enable ACL flow control */
/* Step 3: After processing N received packets, notify controller */
/* OGF=0x03, OCF=0x0035 = Host Number Of Completed Packets */
/* Parameter: list of (handle, count) pairs */
White List HCI Commands
The White List is a table inside the controller that stores device addresses. When a filter policy is active, the controller only processes advertising packets or connection requests from devices on this list. All four management commands are grouped under Host Flow Control in Table 9.1 because they relate to how the controller is configured to handle incoming traffic.
HCI_LE_Read_White_List_Size
Reads how many entries the controller’s white list can hold. This is controller-hardware-dependent — some chips support 8 entries, others support 32 or more. The host should always read this first to know how many devices it can add. Returns a single byte: the maximum number of entries.
HCI_LE_Clear_White_List
Removes all entries from the white list in one operation. The controller initialises the white list to empty automatically after HCI_Reset, so this command is used when the host needs to rebuild the list from scratch at any point during operation. Cannot be used when the white list is in use by a currently active advertising, scanning, or connection procedure.
HCI_LE_Add_Device_To_White_List
Adds a single device’s address to the white list. Parameters: address type (0=public, 1=random) and 6-byte Bluetooth address. The combination of both parameters uniquely identifies the entry. Adding the same address twice has no effect. Returns error if the list is full.
HCI_LE_Remove_Device_From_White_List
Removes a specific device from the white list. Uses the same address type + address parameters as the Add command. The remaining entries in the list are unaffected. Returns an error if the specified device was not in the list.
/* White list management via BlueZ HCI */
/* Read capacity: OGF=0x08, OCF=0x000F */
hcitool cmd 0x08 0x000f
/* Returns: 1 byte = max entries (e.g. 0x08 = 8 entries) */
/* Clear the white list: OGF=0x08, OCF=0x0010 */
hcitool cmd 0x08 0x0010
/* Add a device (public addr): OGF=0x08, OCF=0x0011 */
/* Parameters: addr_type (1 byte) + addr (6 bytes, reversed) */
/* Example: add AA:BB:CC:DD:EE:FF (public) */
hcitool cmd 0x08 0x0011 00 FF EE DD CC BB AA
/* Remove a device: OGF=0x08, OCF=0x0012 */
hcitool cmd 0x08 0x0012 00 FF EE DD CC BB AA
/* Using bluetoothctl for white list (paired devices auto-added): */
/* [bluetooth]# trust AA:BB:CC:DD:EE:FF */
Resolving List HCI Commands (BLE 4.2)
As explained in Chapter 8, BLE 4.2 moved private address resolution from the Host down into the Controller. For the Controller to resolve incoming Resolvable Private Addresses (RPAs) on its own, the Host must first provide the necessary keying material. The Host does this by populating the Controller’s Resolving List — a table of device identities, each containing an Identity Address and the Identity Resolution Key (IRK) pair for that device.
Seven HCI commands manage this resolving list:
HCI_LE_Add_Device_To_Resolving_List
The main setup command. The host calls this once for each bonded device. Parameters include: the peer’s identity address type and address, the peer’s IRK (16 bytes), and the local IRK (16 bytes) to use when generating this device’s own RPA for communications with this peer. Without this step, the controller cannot resolve addresses from that peer device.
HCI_LE_Remove_Device_From_Resolving_List
Removes the identity information for one specific peer device from the resolving list. Used when a device is un-bonded or its keys are invalidated. Parameters: peer address type and identity address.
HCI_LE_Clear_Resolving_List
Removes all entries from the resolving list at once. Equivalent to calling Remove for every entry. Used when rebuilding the list from scratch — for example after a factory reset of all bonded device data.
HCI_LE_Read_Resolving_List_Size
Returns the maximum number of device identity entries the controller can store. Like the white list, this is hardware-dependent. If the controller can hold fewer entries than the host has bonded devices, the host must prioritise which entries to load based on which devices are most likely to be encountered.
HCI_LE_Read_Peer_Resolvable_Address
Reads the current Resolvable Private Address that the controller is using for a specific peer device. Because the peer’s RPA rotates every 15 minutes, the host may need to ask the controller “what RPA is the peer currently using?” to populate the address field when creating a directed advertising packet aimed at that peer. Input: peer identity address. Output: the currently active RPA for that peer.
HCI_LE_Read_Local_Resolvable_Address
Reads the Resolvable Private Address that this device is currently using when communicating with a specific peer. Each peer relationship has its own local RPA (generated from the local IRK for that peer). This command takes the peer’s identity address as input and returns the local RPA currently active for that peer.
HCI_LE_Set_Address_Resolution_Enable
The master on/off switch for controller-side address resolution. After populating the resolving list with HCI_LE_Add_Device_To_Resolving_List, the host must call this command with parameter 0x01 to actually enable the resolution feature. The default after HCI_Reset is disabled (0x00). Only when enabled does the controller attempt to resolve incoming RPAs against its resolving list.
The typical sequence for setting up controller-side address resolution follows a clear order. Every step must be completed before the next one makes sense:
• peer identity addr type + addr
• peer IRK (16 bytes from bonding database)
• local IRK (16 bytes)
/* Complete resolving list setup in BlueZ C code */
struct bonded_device {
uint8_t addr_type;
uint8_t addr[6];
uint8_t peer_irk[16];
uint8_t local_irk[16];
};
int setup_resolving_list(int hci_sock,
struct bonded_device *devices,
int count)
{
/* Step 1: Disable resolution before modifying list */
uint8_t disable = 0x00;
hci_send_cmd(hci_sock, OGF_LE_CTL,
OCF_LE_SET_ADDRESS_RESOLUTION_ENABLE,
1, &disable);
/* Step 2: Clear existing list */
hci_send_cmd(hci_sock, OGF_LE_CTL,
OCF_LE_CLEAR_RESOLVING_LIST, 0, NULL);
/* Step 3: Add each bonded device */
for (int i = 0; i < count; i++) {
le_add_device_to_resolving_list_cp cp;
cp.peer_identity_address_type = devices[i].addr_type;
memcpy(cp.peer_identity_address, devices[i].addr, 6);
memcpy(cp.peer_irk, devices[i].peer_irk, 16);
memcpy(cp.local_irk, devices[i].local_irk, 16);
hci_send_cmd(hci_sock, OGF_LE_CTL,
OCF_LE_ADD_DEVICE_TO_RESOLVING_LIST,
sizeof(cp), &cp);
}
/* Step 4: Enable resolution */
uint8_t enable = 0x01;
return hci_send_cmd(hci_sock, OGF_LE_CTL,
OCF_LE_SET_ADDRESS_RESOLUTION_ENABLE,
1, &enable);
}
9.2.4 — Controller Information Commands
These are all read-only queries that the host sends to find out what capabilities the controller hardware has. They are typically called during stack initialisation, right after HCI_Reset, so the host knows what features it can rely on.
HCI_Read_Local_Version_Information
Returns the HCI version, HCI revision, LMP/PAL version, manufacturer name, and LMP sub-version of the controller. The LMP version is the most useful field — it tells you whether the chip supports BLE at all. LMP version 6 = BT 4.0 (first BLE version), 7 = BT 4.1, 8 = BT 4.2. If the controller returns a version below 6, it is a classic-only chip with no LE capability.
HCI_Read_Local_Supported_Command
Returns a 64-byte bitmask (512 bits) where each bit indicates whether one specific HCI command is implemented by the controller. The host should check this before sending any command to avoid sending unsupported commands that would return an error. This is the definitive way to test for command support.
HCI_Read_Support_Features_Command
Returns the LMP feature bits for the controller — a bitmask indicating which LMP-level features are supported. For a dual-mode controller this covers both BR/EDR and LE features. The LE Host Supported bit and LE Supported bit in this mask indicate dual-mode capability.
HCI_LE_Read_Support_Features_Command
The LE-specific feature mask query. Returns the 64-bit FeatureSet bitmap we covered in the LLCP Feature Exchange section — bits 0–7 indicating LE Encryption, Connection Parameters Request, Extended Reject, Slave Feature Exchange, LE Ping, Data Length Extension, LL Privacy, and Extended Scanner Filters. Bit = 0 means not supported.
HCI_LE_Read_Supported_States
Returns a 64-bit bitmask of all the link layer state combinations this controller supports. For example, some lower-cost controllers may not support advertising while connected (a combination that is optional). The host checks this before attempting any multi-state operation to avoid errors.
HCI_LE_Read_Maximum_Data_Length BLE 4.2
Reads the absolute maximum PDU payload sizes and transmission times the controller’s hardware can handle. Returns four values: supportedMaxTxOctets, supportedMaxTxTime, supportedMaxRxOctets, supportedMaxRxTime. These are the hardware ceiling values — the actual connection negotiation during Data Length Update can only go up to these limits, not beyond them.
/* Reading controller capabilities with BlueZ hcitool */
/* Read version (check LMP version field): */
hcitool info /* shows version, manufacturer, etc. */
/* Check LE feature support: */
/* OGF=0x08, OCF=0x0003 = LE Read Local Supported Features */
hcitool cmd 0x08 0x0003
/* Returns 8 bytes LE feature bitmask: */
/* Byte 0: bit0=LE Enc, bit1=Conn Param Req, bit2=Ext Reject */
/* bit3=Slave Feat Req, bit4=LE Ping, bit5=Data Ext */
/* bit6=LL Privacy, bit7=Ext Scan Filter */
/* Check max data length (BLE 4.2): */
/* OGF=0x08, OCF=0x002F = LE Read Maximum Data Length */
hcitool cmd 0x08 0x002f
/* Returns: MaxTxOctets(2) MaxTxTime(2) MaxRxOctets(2) MaxRxTime(2) */
9.2.5 — Remote Information Commands
While the Controller Information commands query the local device’s capabilities, the Remote Information commands query the remote (peer) device’s capabilities across an active connection. These are the HCI-layer equivalents of the LLCP Feature Exchange and Version Exchange procedures.
When the host calls this command with a connection handle, the controller initiates the LLCP Feature Exchange procedure with the remote device. The remote device sends back its FeatureSet bitmap. The host has no direct access to the link layer — it goes through this HCI command and gets the result via an event.
At present the FeatureSet only has meaningful content in the bits we covered in Chapter 8 (bits 0–7). The textbook notes that bit 0 (LE Encryption) is the main bit of interest — it tells the local host whether the remote side supports AES-128 encryption. Without this bit being set in the remote’s features, the host must not attempt to start encryption.
This event is generated asynchronously after the LLCP procedure completes and the peer has responded. It contains: the status (success or error), the connection handle, and the 8-byte FeatureSet received from the peer. The host reads the feature bits to decide what capabilities to enable for this connection.
Read Remote Features
CTRL
DEVICE
DEVICE
Read Remote Features Complete
(FeatureSet 8 bytes)
CTRL
/* Requesting remote LE features via BlueZ */
/* OGF=0x08, OCF=0x0016 = LE Read Remote Used Features */
/* Parameter: connection handle (2 bytes) */
uint8_t params[2];
params[0] = conn_handle & 0xFF;
params[1] = (conn_handle >> 8) & 0xFF;
hci_send_cmd(hci_sock, OGF_LE_CTL,
OCF_LE_READ_REMOTE_USED_FEATURES,
2, params);
/* The result comes back as a LE Meta Event subevent 0x04: */
/* EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE = 0x04 */
/* Payload: status(1) + handle(2) + LE_features(8) */
/* Checking if remote supports encryption: */
/* if (le_features[0] & 0x01) → remote supports LE Encrypt */
/* Using btmon to observe the exchange: */
/* sudo btmon | grep -A 10 "LE Remote Features" */
- Rarely needed in practice
- Host_Buffer_Size: tell controller host capacity
- Set_Ctrl_To_Host_Flow_Ctrl: enable/disable
- Host_Num_Completed: free slots signal
- Data_Buffer_Overflow: error event
- WL: Read Size, Clear, Add, Remove
- RL: Add (identity addr + IRKs)
- RL: Remove, Clear, Read Size
- RL: Read Peer/Local RPA
- Set_Address_Resolution_Enable
- Read_Local_Version → LMP version
- Read_Supported_Commands → 512-bit mask
- LE_Read_Local_Supported_Features
- LE_Read_Supported_States
- LE_Read_Maximum_Data_Length (4.2)
- LE_Read_Remote_Used_Features
- Triggers LLCP Feature Exchange
- Result via LE Meta Event 0x04
- Key check: bit 0 = remote supports encryption
PDF 46 (Chapter 9) — Here
Chapter 9 HCI Sections Complete
You now have the full Chapter 9 HCI coverage — every command group, every packet format, the data flow control diagram, White List and Resolving List management, and all controller information queries.
