esp-idf: Wrong default LE Event Mask in HCI for ESP32-C3 (IDFGH-4905)
Environment
- Development Kit: ESP32-C3-DevKitM-1
- Module or chip used: ESP32-C3-MINI-1
- IDF version: v4.4-dev-4-g73db14240
- Build System: idf.py
- Compiler version: 1.24.0.123_64eb9ff-8.4.0
- Operating System: Linux
- Using an IDE?: No
- Power Supply: USB
Problem Description
See Bluetooth Core specification, 5.0, Vol 2 Part E (HCI Functional Specification), section 7.8.1 LE Set Event Mask Command.
This command can be used to change the LE Event Mask. If the host does not send this event, the default mask is used by the controller.
The spec says the following:
The default is for bits 0 to 4 inclusive (the value 0x0000 0000 0000 001F) to be set.
This means the bit for “LE Connection Complete Event” is set, while “LE Enhanced Connection Complete Event” and “LE Channel Selection Algorithm Event” are not set.
For LE Connection Complete Event (section 7.8.12), we can read
Note: This event is not sent if the LE Enhanced Connection Complete event (see Section 7.7.65.10) is unmasked.
But by default, “LE Enhanced Connection Complete event” is masked away, and hence “LE Connection Complete Event” should be sent.
Steps to reproduce
Over vhci, send the following commands:
- Reset Command.
- Set Event Mask Command, with the “LE Meta Event” bit set to 1. (Note that “Set Event Mask” is a different command than “LE Set Event Mask”.)
- LE Create Connection Command
Then let a BLE peripheral advertise with the address specified in the LE Create Connection Command so that it connects.
Expected Behavior
A “LE Connection Complete Event” should be emitted when the device connects. “LE Enhanced Connection Complete Event” and “LE Channel Selection Algorithm Event” should not be emitted when the device connects.
Actual Behavior
The HCI layer in ESP32-C3 will emit a “LE Enhanced Connection Complete event” and no “LE Connection Complete Event”. This will cause Bluetooth Host stacks not supporting the LE Enhanced Connection Complete event to fail, since the expected LE Connection Complete Event is never emitted.
The “LE Channel Selection Algorithm Event” is also sent.
This worked fine in the older ESP32 chip, but not with the new ESP32-C3 chip. A workaround is to explicitly send an LE Set Event Mask command with the default mask 0x1f before creating a connection, to get the expected behaviour.
Code to reproduce this issue
Start from the examples/bluetooth/hci/controller_vhci_ble_adv example and change app_bt.c to the following (fill in the bd addr to an advertising target device in hci_cmd_send_create_conn):
/*
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_bt.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "bt_hci_common.h"
#define HCI_BLE_CREATE_CONNECTION (0x000D | HCI_GRP_BLE_CMDS)
#define UINT32_TO_STREAM(buf, val) do { UINT16_TO_STREAM((buf), (uint16_t)(val)); UINT16_TO_STREAM((buf), (uint16_t)((val) >> 16)); } while(0)
#define UINT64_TO_STREAM(buf, val) do { UINT32_TO_STREAM((buf), (uint32_t)(val)); UINT32_TO_STREAM((buf), (uint32_t)((val) >> 32)); } while(0)
static const char *tag = "BLE_TEST";
static uint8_t hci_cmd_buf[128];
/*
* @brief: BT controller callback function, used to notify the upper layer that
* controller is ready to receive command
*/
static void controller_rcv_pkt_ready(void)
{
printf("controller rcv pkt ready\n");
}
/*
* @brief: BT controller callback function, to transfer data packet to upper
* controller is ready to receive command
*/
static int host_rcv_pkt(uint8_t *data, uint16_t len)
{
printf("host rcv pkt: ");
for (uint16_t i = 0; i < len; i++) {
printf("%02x", data[i]);
}
printf("\n");
return 0;
}
static esp_vhci_host_callback_t vhci_host_cb = {
controller_rcv_pkt_ready,
host_rcv_pkt
};
static void hci_cmd_send_reset(void)
{
uint16_t sz = make_cmd_reset (hci_cmd_buf);
esp_vhci_host_send_packet(hci_cmd_buf, sz);
}
static void hci_cmd_send_set_event_mask(void) {
uint8_t *buf = hci_cmd_buf;
UINT8_TO_STREAM(buf, H4_TYPE_COMMAND);
UINT16_TO_STREAM(buf, HCI_SET_EVT_MASK);
UINT8_TO_STREAM(buf, 8);
uint64_t mask = 0x2000000000000000ULL; // LE Meta Event
UINT64_TO_STREAM(buf, mask);
esp_vhci_host_send_packet(hci_cmd_buf, HCI_H4_CMD_PREAMBLE_SIZE + 8);
}
static void hci_cmd_send_create_conn(void) {
uint8_t *buf = hci_cmd_buf;
UINT8_TO_STREAM(buf, H4_TYPE_COMMAND);
UINT16_TO_STREAM(buf, HCI_BLE_CREATE_CONNECTION);
UINT8_TO_STREAM(buf, 25); // len
UINT16_TO_STREAM(buf, 160); // scan interval
UINT16_TO_STREAM(buf, 160); // scan win
UINT8_TO_STREAM(buf, 0); // no white list
UINT8_TO_STREAM(buf, ...); // public addr type=0, random addr type=1
uint8_t addr[6] = { ... }; // <-- fill in BD ADDR
BDADDR_TO_STREAM(buf, addr);
UINT8_TO_STREAM(buf, 0); // public own addr type
UINT16_TO_STREAM(buf, 80); // con min
UINT16_TO_STREAM(buf, 90); // con max
UINT16_TO_STREAM(buf, 0); // conn latency
UINT16_TO_STREAM(buf, 500); // supervision timeout
UINT16_TO_STREAM(buf, 0); // min ce len
UINT16_TO_STREAM(buf, 0); // max ce len
esp_vhci_host_send_packet(hci_cmd_buf, HCI_H4_CMD_PREAMBLE_SIZE + 25);
}
/*
* @brief: send HCI commands to perform BLE advertising;
*/
void bleAdvtTask(void *pvParameters)
{
int cmd_cnt = 0;
bool send_avail = false;
esp_vhci_host_register_callback(&vhci_host_cb);
printf("BLE advt task start\n");
while (1) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
send_avail = esp_vhci_host_check_send_available();
if (send_avail) {
switch (cmd_cnt) {
case 0: hci_cmd_send_reset(); ++cmd_cnt; break;
case 1: hci_cmd_send_set_event_mask(); ++cmd_cnt; break;
case 2: hci_cmd_send_create_conn(); ++cmd_cnt; break;
}
}
printf("BLE Advertise, flag_send_avail: %d, cmd_sent: %d\n", send_avail, cmd_cnt);
}
}
void app_main(void)
{
/* Initialize NVS — it is used to store PHY calibration data */
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK( ret );
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
if (ret) {
ESP_LOGI(tag, "Bluetooth controller release classic bt memory failed: %s", esp_err_to_name(ret));
return;
}
if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
ESP_LOGI(tag, "Bluetooth controller initialize failed: %s", esp_err_to_name(ret));
return;
}
if ((ret = esp_bt_controller_enable(ESP_BT_MODE_BLE)) != ESP_OK) {
ESP_LOGI(tag, "Bluetooth controller enable failed: %s", esp_err_to_name(ret));
return;
}
/*
* If call mem release here, also work. Input ESP_BT_MODE_CLASSIC_BT, the function will
* release the memory of classic bt mode.
* esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
*
*/
/*
* If call mem release here, also work. Input ESP_BT_MODE_BTDM, the function will calculate
* that the BLE mode is already used, so it will release of only classic bt mode.
* esp_bt_controller_mem_release(ESP_BT_MODE_BTDM);
*/
xTaskCreatePinnedToCore(&bleAdvtTask, "bleAdvtTask", 2048, NULL, 5, NULL, 0);
}
Debug Logs
BLE advt task start
controller rcv pkt ready
host rcv pkt: 040e0405030c00
BLE Advertise, flag_send_avail: 1, cmd_sent: 1
controller rcv pkt ready
host rcv pkt: 040e0405010c00
BLE Advertise, flag_send_avail: 1, cmd_sent: 2
controller rcv pkt ready
host rcv pkt: 040f0400050d20
BLE Advertise, flag_send_avail: 1, cmd_sent: 3
BLE Advertise, flag_send_avail: 1, cmd_sent: 3
BLE Advertise, flag_send_avail: 1, cmd_sent: 3
BLE Advertise, flag_send_avail: 1, cmd_sent: 3
host rcv pkt: 043e1f0a00000000[bd addr here]0000000000000000000000005a000000f40100
host rcv pkt: 043e0414000001
Decoding of the received packet 043e1f0a…: 04: Type is Event packet 3e: LE Meta Event 1f: Length of packet 0a: Subevent Code for the LE Enhanced Connection Complete Event
Decoding of the received packet 043e0414…: 04: Type is Event packet 3e: LE Meta Event 04: Length of packet 14: Subevent code for the LE Channel Selection Algorithm event
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 25 (10 by maintainers)
@AxelLin @Emill
We will fix it ASAP.
The default values are not allowed to be customized per implementation by a vendor, since these are standardised in the Bluetooth Core specification.
As I wrote in my initial post:
The spec says the following:
So what Espressif must do, to be Bluetooth standard compliant, is to change their default value to 0x1f.
@AxelLin
Code:
Log: