mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
feat(ble_audio): Support running ISO & LE Audio with Bluedroid Host
This commit is contained in:
+110
-4
@@ -4,16 +4,21 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_BLE_LOG_ENABLED
|
||||
#include "ble_log.h"
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_BLUEDROID_ENABLED
|
||||
#include "esp_bt.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_gap_ble_api.h"
|
||||
#endif /* CONFIG_BT_BLUEDROID_ENABLED */
|
||||
|
||||
#ifdef CONFIG_BT_NIMBLE_ENABLED
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
|
||||
@@ -24,9 +29,109 @@
|
||||
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "services/gatt/ble_svc_gatt.h"
|
||||
#endif /* CONFIG_BT_NIMBLE_ENABLED */
|
||||
|
||||
#include "ble_audio_example_init.h"
|
||||
|
||||
#define TAG "EXAMPLE_INIT"
|
||||
|
||||
#ifdef CONFIG_BT_BLUEDROID_ENABLED
|
||||
esp_err_t bluetooth_init(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
/* When controller log is disabled (or running in v1 mode), the
|
||||
* automatic ble_log_init() inside esp_bt_controller_init() is skipped.
|
||||
* Init manually here so HOST/ISO/AUDIO compressed logs aren't dropped
|
||||
* during host bring-up. The function is idempotent.
|
||||
*
|
||||
* TODO: Remove this block (and the ble_log_flush() below) once
|
||||
* ble_log_init() is decoupled from the controller log path and
|
||||
* owns its own initialization independently. */
|
||||
#if CONFIG_BLE_LOG_ENABLED && \
|
||||
!(CONFIG_BT_LE_CONTROLLER_LOG_ENABLED && CONFIG_BT_LE_CONTROLLER_LOG_MODE_BLE_LOG_V2)
|
||||
if (!ble_log_init()) {
|
||||
ESP_LOGE(TAG, "Failed to init ble_log");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
|
||||
if (ret && ret != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(TAG, "Failed to release classic BT memory, err %d", ret);
|
||||
goto err_log;
|
||||
}
|
||||
|
||||
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
ret = esp_bt_controller_init(&bt_cfg);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "Failed to init controller, err %d", ret);
|
||||
goto err_log;
|
||||
}
|
||||
|
||||
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "Failed to enable controller, err %d", ret);
|
||||
goto err_controller_init;
|
||||
}
|
||||
|
||||
esp_bluedroid_config_t cfg = BT_BLUEDROID_INIT_CONFIG_DEFAULT();
|
||||
ret = esp_bluedroid_init_with_cfg(&cfg);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "Failed to init bluedroid, err %d", ret);
|
||||
goto err_controller_enable;
|
||||
}
|
||||
|
||||
ret = esp_bluedroid_enable();
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "Failed to enable bluedroid, err %d", ret);
|
||||
goto err_bluedroid_init;
|
||||
}
|
||||
|
||||
/* Just Works pairing (no MITM, SC + bond, IO_CAP=NONE).
|
||||
* Matches the NimBLE ble_hs_cfg defaults below. */
|
||||
esp_ble_auth_req_t auth_req = ESP_LE_AUTH_REQ_SC_BOND;
|
||||
esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;
|
||||
uint8_t key_size = 16;
|
||||
uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
|
||||
uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
|
||||
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(auth_req));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(iocap));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(key_size));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(init_key));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(rsp_key));
|
||||
|
||||
/* Drain startup-phase log buffers and emit a FLUSH boundary so the
|
||||
* parser resets stats here — the example's runtime logs start clean.
|
||||
*
|
||||
* TODO: Remove together with the manual ble_log_init() above once
|
||||
* ble_log_init() is decoupled from the controller log path. */
|
||||
#if CONFIG_BLE_LOG_ENABLED
|
||||
ble_log_flush();
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err_bluedroid_init:
|
||||
esp_bluedroid_deinit();
|
||||
err_controller_enable:
|
||||
esp_bt_controller_disable();
|
||||
err_controller_init:
|
||||
esp_bt_controller_deinit();
|
||||
err_log:
|
||||
/* Mirror the manual ble_log_init() block above — same Kconfig gate so
|
||||
* we only deinit what we initialized; the controller-owned path is
|
||||
* left alone. */
|
||||
#if CONFIG_BLE_LOG_ENABLED && \
|
||||
!(CONFIG_BT_LE_CONTROLLER_LOG_ENABLED && CONFIG_BT_LE_CONTROLLER_LOG_MODE_BLE_LOG_V2)
|
||||
ble_log_deinit();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_BT_BLUEDROID_ENABLED */
|
||||
|
||||
#ifdef CONFIG_BT_NIMBLE_ENABLED
|
||||
static SemaphoreHandle_t example_audio_sem;
|
||||
|
||||
void ble_store_config_init(void);
|
||||
@@ -228,7 +333,7 @@ esp_err_t bluetooth_init(void)
|
||||
/* When controller log is disabled (or running in v1 mode), the
|
||||
* automatic ble_log_init() inside esp_bt_controller_init() is skipped.
|
||||
* Init manually here so HOST/ISO/AUDIO compressed logs aren't dropped
|
||||
* during NimBLE bring-up. The function is idempotent.
|
||||
* during host bring-up. The function is idempotent.
|
||||
*
|
||||
* TODO: Remove this block (and the ble_log_flush() below) once
|
||||
* ble_log_init() is decoupled from the controller log path and
|
||||
@@ -297,3 +402,4 @@ err_log:
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_BT_NIMBLE_ENABLED */
|
||||
|
||||
-60
@@ -26,66 +26,6 @@
|
||||
*/
|
||||
#define LOG_INTERVAL_PACKETS 6000
|
||||
|
||||
int example_audio_gap_event_cb(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
if (event->type == BLE_GAP_EVENT_EXT_DISC ||
|
||||
event->type == BLE_GAP_EVENT_PERIODIC_SYNC ||
|
||||
event->type == BLE_GAP_EVENT_PERIODIC_REPORT ||
|
||||
event->type == BLE_GAP_EVENT_PERIODIC_SYNC_LOST ||
|
||||
event->type == BLE_GAP_EVENT_PERIODIC_TRANSFER ||
|
||||
event->type == BLE_GAP_EVENT_PERIODIC_TRANSFER_V2 ||
|
||||
event->type == BLE_GAP_EVENT_CONNECT ||
|
||||
event->type == BLE_GAP_EVENT_DISCONNECT ||
|
||||
event->type == BLE_GAP_EVENT_ENC_CHANGE) {
|
||||
esp_ble_audio_gap_app_post_event(event->type, event);
|
||||
} else if (event->type == BLE_GAP_EVENT_MTU ||
|
||||
event->type == BLE_GAP_EVENT_NOTIFY_RX ||
|
||||
event->type == BLE_GAP_EVENT_NOTIFY_TX ||
|
||||
event->type == BLE_GAP_EVENT_SUBSCRIBE) {
|
||||
esp_ble_audio_gatt_app_post_event(event->type, event);
|
||||
} else if (event->type == BLE_GAP_EVENT_REPEAT_PAIRING) {
|
||||
/* Peer wants to re-pair on top of an existing bond (e.g. phone
|
||||
* cleared its LTK via "Forget device" but our NVS still has it).
|
||||
* Delete our stale bond and tell NimBLE to retry pairing.
|
||||
*/
|
||||
struct ble_gap_conn_desc desc = {0};
|
||||
int rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
|
||||
if (rc == 0) {
|
||||
ble_store_util_delete_peer(&desc.peer_id_addr);
|
||||
}
|
||||
return BLE_GAP_REPEAT_PAIRING_RETRY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void example_audio_security_failed_recover(const char *tag, uint16_t conn_handle, uint8_t status)
|
||||
{
|
||||
struct ble_gap_conn_desc desc = {0};
|
||||
int rc;
|
||||
|
||||
/* status 13 = BLE_HS_ETIMEOUT: SMP exchange did not complete. Typical
|
||||
* cause is asymmetric bond state — the two sides bonded previously,
|
||||
* then one side erased its NVS while the other kept the LTK. The side
|
||||
* that still has the bond tries encryption with the cached key; the
|
||||
* other side has nothing to match it against, so SMP times out. Drop
|
||||
* our entry for this peer and disconnect; the next connection runs
|
||||
* fresh pairing (peer side recovers via REPEAT_PAIRING).
|
||||
*/
|
||||
ESP_LOGE(tag, "Security change failed, status %u, clearing local bond and reconnecting", status);
|
||||
|
||||
rc = ble_gap_conn_find(conn_handle, &desc);
|
||||
if (rc == 0) {
|
||||
ble_store_util_delete_peer(&desc.peer_id_addr);
|
||||
}
|
||||
|
||||
/* Tear down the link: SMP cannot be retried on the same connection
|
||||
* after a failed/timed-out exchange. The next reconnect starts a
|
||||
* fresh SMP procedure with the now-cleared bond state.
|
||||
*/
|
||||
ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
|
||||
}
|
||||
|
||||
static void print_hex(const uint8_t *ptr, size_t len)
|
||||
{
|
||||
while (len--) {
|
||||
|
||||
+56
-7
@@ -16,12 +16,10 @@
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "zephyr/kernel.h"
|
||||
|
||||
#include "host/ble_gap.h"
|
||||
#include "host/ble_hs_adv.h"
|
||||
#include "host/ble_store.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "esp_ble_audio_bap_api.h"
|
||||
|
||||
@@ -52,9 +50,60 @@
|
||||
#define EXAMPLE_BYTES_LIST_LE48 BT_BYTES_LIST_LE48
|
||||
#define EXAMPLE_BYTES_LIST_LE64 BT_BYTES_LIST_LE64
|
||||
|
||||
int example_audio_gap_event_cb(struct ble_gap_event *event, void *arg);
|
||||
/* bt_le_addr.val carries the host stack's native byte order: NimBLE is
|
||||
* LSB-first (BT spec wire order), Bluedroid is MSB-first (BD_ADDR). Use
|
||||
* this with "%02x:%02x:%02x:%02x:%02x:%02x" to print MSB-first regardless
|
||||
* of host. Examples can otherwise pass val[] straight to their host APIs
|
||||
* without conversion. */
|
||||
#if CONFIG_BT_BLUEDROID_ENABLED
|
||||
#define EXAMPLE_BT_ADDR_PRINT_ARGS(_v) \
|
||||
(_v)[0], (_v)[1], (_v)[2], (_v)[3], (_v)[4], (_v)[5]
|
||||
|
||||
void example_audio_security_failed_recover(const char *tag, uint16_t conn_handle, uint8_t status);
|
||||
/* Fire-and-wait helper for Bluedroid GAP APIs that pair an async call with
|
||||
* a matching ESP_GAP_BLE_*_COMPLETE_EVT. The example's GAP event handler
|
||||
* is expected to xSemaphoreGive(_sem) on that COMPLETE_EVT. `TAG` and the
|
||||
* enclosing function's `esp_err_t` return are taken from the call site.
|
||||
*
|
||||
* !!! Demo-only — DO NOT copy into production code. !!!
|
||||
* Risks (acceptable in linear init sequences, not in customer apps):
|
||||
* - Same-task deadlock: must NOT be called from the task that dispatches
|
||||
* the COMPLETE_EVT (BTC task by default; lib forwarding can route some
|
||||
* events through other tasks — verify the path for your event).
|
||||
* - portMAX_DELAY = no timeout: hangs forever on controller/firmware
|
||||
* misbehavior. Production must use a bounded timeout + recovery.
|
||||
* - Shared binary sem: concurrent callers race; first take consumes
|
||||
* whichever give arrived. Examples avoid this by serializing init.
|
||||
* - No abort hook: nothing can break a portMAX_DELAY wait at shutdown.
|
||||
* - Status not checked: see EXAMPLE_WAIT_API_CHECK below.
|
||||
*
|
||||
* Production should drive BLE init/teardown via an event-driven state
|
||||
* machine (one task owns the BLE flow, callbacks advance state, no
|
||||
* blocking from inside callbacks). NimBLE host returns ctrl errors
|
||||
* synchronously and sidesteps most of this complexity. */
|
||||
#define EXAMPLE_WAIT_API(_call, _sem, _delay) do { \
|
||||
esp_err_t _err = (_call); \
|
||||
if (_err != ESP_OK) { \
|
||||
ESP_LOGE(TAG, #_call " failed, err %d", _err); \
|
||||
return _err; \
|
||||
} \
|
||||
xSemaphoreTake(_sem, _delay); \
|
||||
} while (0)
|
||||
|
||||
/* As EXAMPLE_WAIT_API but also checks the controller status latched by the
|
||||
* gap_event_handler into `_op_status` after the sem is taken. Returns
|
||||
* ESP_FAIL on async failure. Caller must provide a static esp_bt_status_t
|
||||
* and write it from each COMPLETE_EVT param before xSemaphoreGive. */
|
||||
#define EXAMPLE_WAIT_API_CHECK(_call, _sem, _delay, _op_status) do { \
|
||||
EXAMPLE_WAIT_API(_call, _sem, _delay); \
|
||||
if ((_op_status) != ESP_BT_STATUS_SUCCESS) { \
|
||||
ESP_LOGE(TAG, #_call " ctrl status %d", (_op_status)); \
|
||||
return ESP_FAIL; \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define EXAMPLE_BT_ADDR_PRINT_ARGS(_v) \
|
||||
(_v)[5], (_v)[4], (_v)[3], (_v)[2], (_v)[1], (_v)[0]
|
||||
#endif
|
||||
|
||||
void example_print_codec_cfg(const char *tag, const esp_ble_audio_codec_cfg_t *codec_cfg);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user