mirror of
https://github.com/espressif/esp-idf.git
synced 2026-08-18 06:35:35 +03:00
Merge branch 'idf/ble_iso_enh' into 'master'
feat(ble_iso): Split ISO task queue into priority tiers & add dispatch monitor See merge request espressif/esp-idf!49265
This commit is contained in:
@@ -104,6 +104,40 @@ if BT_ISO
|
||||
|
||||
endif # BT_ISO_BROADCAST
|
||||
|
||||
config BT_ISO_DISPATCH_MONITOR
|
||||
bool "Monitor ISO task callback dispatch latency"
|
||||
depends on !BT_ISO_NO_LOG
|
||||
default n
|
||||
help
|
||||
Time every callback dispatched by the ISO task and keep per-event-
|
||||
type stats (count / max / slow-count), logged periodically and at
|
||||
deinit. Used to find callbacks that run long enough to delay the
|
||||
latency-critical ISO data path.
|
||||
|
||||
Off by default: it adds per-dispatch timing overhead, and the
|
||||
measurement is wall-clock, so callbacks that log over UART read as
|
||||
"slow". Enable only while profiling.
|
||||
|
||||
config BT_ISO_DISPATCH_THRESHOLD_US
|
||||
int "ISO dispatch threshold (in microseconds)"
|
||||
depends on BT_ISO_DISPATCH_MONITOR
|
||||
range 100 100000
|
||||
default 2000
|
||||
help
|
||||
A callback dispatched by the ISO task that runs longer than this
|
||||
many microseconds is counted as slow in the stats. Default 2000 is
|
||||
~1/4 of a 7.5-10ms SDU interval, well above any healthy callback.
|
||||
|
||||
config BT_ISO_DISPATCH_DUMP_PERIOD_S
|
||||
int "ISO dispatch stats dump period (in seconds)"
|
||||
depends on BT_ISO_DISPATCH_MONITOR
|
||||
range 1 600
|
||||
default 10
|
||||
help
|
||||
The ISO task logs the per-type timing table (count / max / slow-
|
||||
count) every this many seconds while running, in addition to the
|
||||
dump at deinit.
|
||||
|
||||
config BT_ISO_NO_LOG
|
||||
bool "Disable BLE ISO Debug Log"
|
||||
default n
|
||||
|
||||
@@ -178,6 +178,7 @@ static void bt_le_bluedroid_gap_post_event_bta(tBTA_DM_BLE_5_GAP_EVENT event,
|
||||
tBTA_DM_BLE_5_GAP_CB_PARAMS *params)
|
||||
{
|
||||
struct bt_le_gap_app_param *qev = NULL;
|
||||
enum iso_queue_item_type q_type;
|
||||
int err;
|
||||
|
||||
qev = calloc(1, sizeof(*qev));
|
||||
@@ -350,9 +351,31 @@ static void bt_le_bluedroid_gap_post_event_bta(tBTA_DM_BLE_5_GAP_EVENT event,
|
||||
return;
|
||||
}
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GAP_EVENT, qev, sizeof(*qev));
|
||||
/* High-volume reports go to the droppable floodable queue (named 1:1 after
|
||||
* the HCI report event); connection / PA-sync lifecycle events stay
|
||||
* reliable on the normal queue. Keep this mapping in sync with
|
||||
* nimble/gap.c. */
|
||||
switch (qev->type) {
|
||||
case BT_LE_GAP_APP_PARAM_EXT_SCAN_RECV:
|
||||
q_type = ISO_QUEUE_ITEM_TYPE_EXT_ADV_REPORT;
|
||||
break;
|
||||
case BT_LE_GAP_APP_PARAM_PA_SYNC_RECV:
|
||||
q_type = ISO_QUEUE_ITEM_TYPE_PER_ADV_REPORT;
|
||||
break;
|
||||
default:
|
||||
q_type = ISO_QUEUE_ITEM_TYPE_GAP_EVENT;
|
||||
break;
|
||||
}
|
||||
|
||||
err = bt_le_iso_task_post(q_type, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GapPostEvtBtaFail[%d][%u]", err, qev->type);
|
||||
/* Floodable reports drop by design when the queue is full; only a
|
||||
* failure on the reliable (normal-queue) path is a real error. */
|
||||
if (q_type == ISO_QUEUE_ITEM_TYPE_GAP_EVENT) {
|
||||
LOG_ERR("[B]GapPostEvtBtaFail[%d][%u]", err, qev->type);
|
||||
} else {
|
||||
LOG_DBG("[B]GapRptDrop[%u]", qev->type);
|
||||
}
|
||||
goto free;
|
||||
}
|
||||
|
||||
|
||||
@@ -829,6 +829,7 @@ int bt_le_bluedroid_iso_cmd_send_sync(uint16_t opcode,
|
||||
|
||||
static void iso_evt_handler(tBTM_BLE_ISO_EVENT event, tBTM_BLE_ISO_CB_PARAMS *params)
|
||||
{
|
||||
enum iso_queue_item_type q_type;
|
||||
uint8_t *qdata = NULL;
|
||||
size_t qdata_len = 0;
|
||||
int err;
|
||||
@@ -1039,7 +1040,14 @@ static void iso_evt_handler(tBTM_BLE_ISO_EVENT event, tBTM_BLE_ISO_CB_PARAMS *pa
|
||||
return;
|
||||
}
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_ISO_HCI_EVENT, qdata, qdata_len);
|
||||
/* BIGInfo reports arrive at the PA interval rate (one per PA report that
|
||||
* carries a BIGInfo field): route them to the droppable floodable queue.
|
||||
* Other ISO HCI events (CIS/BIG lifecycle) stay reliable. Keep the
|
||||
* classification in sync with nimble/iso.c. */
|
||||
q_type = (event == BTM_BLE_ISO_BIGINFO_ADV_REPORT_EVT)
|
||||
? ISO_QUEUE_ITEM_TYPE_BIGINFO_ADV_REPORT : ISO_QUEUE_ITEM_TYPE_ISO_HCI_EVENT;
|
||||
|
||||
err = bt_le_iso_task_post(q_type, qdata, qdata_len);
|
||||
if (err) {
|
||||
LOG_ERR("[B]IsoPostEvtFail[%d][%02x]", err, event);
|
||||
free(qdata);
|
||||
|
||||
@@ -79,6 +79,7 @@ void bt_le_nimble_gap_post_event(void *param)
|
||||
struct bt_le_gap_app_param *qev = NULL;
|
||||
struct ble_gap_conn_desc desc = {0};
|
||||
struct ble_gap_event *ev = NULL;
|
||||
enum iso_queue_item_type q_type;
|
||||
int err;
|
||||
|
||||
qev = calloc(1, sizeof(*qev));
|
||||
@@ -222,9 +223,31 @@ void bt_le_nimble_gap_post_event(void *param)
|
||||
return;
|
||||
}
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GAP_EVENT, qev, sizeof(*qev));
|
||||
/* High-volume reports go to the droppable floodable queue (named 1:1 after
|
||||
* the HCI report event); connection / PA-sync lifecycle events stay
|
||||
* reliable on the normal queue. Keep this mapping in sync with
|
||||
* bluedroid/gap.c. */
|
||||
switch (qev->type) {
|
||||
case BT_LE_GAP_APP_PARAM_EXT_SCAN_RECV:
|
||||
q_type = ISO_QUEUE_ITEM_TYPE_EXT_ADV_REPORT;
|
||||
break;
|
||||
case BT_LE_GAP_APP_PARAM_PA_SYNC_RECV:
|
||||
q_type = ISO_QUEUE_ITEM_TYPE_PER_ADV_REPORT;
|
||||
break;
|
||||
default:
|
||||
q_type = ISO_QUEUE_ITEM_TYPE_GAP_EVENT;
|
||||
break;
|
||||
}
|
||||
|
||||
err = bt_le_iso_task_post(q_type, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[N]GapPostEvtFail[%d][%u]", err, qev->type);
|
||||
/* Floodable reports drop by design when the queue is full; only a
|
||||
* failure on the reliable (normal-queue) path is a real error. */
|
||||
if (q_type == ISO_QUEUE_ITEM_TYPE_GAP_EVENT) {
|
||||
LOG_ERR("[N]GapPostEvtFail[%d][%u]", err, qev->type);
|
||||
} else {
|
||||
LOG_DBG("[N]GapRptDrop[%u]", qev->type);
|
||||
}
|
||||
goto free;
|
||||
}
|
||||
|
||||
|
||||
@@ -781,6 +781,7 @@ int bt_le_nimble_iso_cmd_send_sync(uint16_t opcode,
|
||||
static void iso_evt_rx(uint8_t event, const void *data,
|
||||
unsigned int len, bool le_meta)
|
||||
{
|
||||
enum iso_queue_item_type q_type;
|
||||
size_t qdata_len;
|
||||
uint8_t *qdata;
|
||||
int err;
|
||||
@@ -798,7 +799,14 @@ static void iso_evt_rx(uint8_t event, const void *data,
|
||||
qdata[1] = event;
|
||||
memcpy(qdata + 2, data, len);
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_ISO_HCI_EVENT, qdata, qdata_len);
|
||||
/* BIGInfo reports arrive at the PA interval rate (one per PA report that
|
||||
* carries a BIGInfo field): route them to the droppable floodable queue.
|
||||
* Other ISO HCI events (CIS/BIG lifecycle) stay reliable. Keep the
|
||||
* classification in sync with bluedroid/iso.c. */
|
||||
q_type = (event == BT_HCI_EVT_LE_BIGINFO_ADV_REPORT)
|
||||
? ISO_QUEUE_ITEM_TYPE_BIGINFO_ADV_REPORT : ISO_QUEUE_ITEM_TYPE_ISO_HCI_EVENT;
|
||||
|
||||
err = bt_le_iso_task_post(q_type, qdata, qdata_len);
|
||||
if (err) {
|
||||
LOG_ERR("[N]IsoPostEvtFail[%d][%02x]", err, event);
|
||||
free(qdata);
|
||||
|
||||
@@ -47,13 +47,33 @@ extern "C" {
|
||||
#define ISO_TASK_PRIO (configMAX_PRIORITIES - 4)
|
||||
#endif
|
||||
|
||||
/* Ordered by priority tier, highest first. The numeric value is not used for
|
||||
* prioritization (that comes from which queue bt_le_iso_task_post routes to);
|
||||
* the order is for readability of the three tiers. */
|
||||
enum iso_queue_item_type {
|
||||
/* critical: latency-critical ISO data, drained first, posted non-blocking
|
||||
* from the controller; an overflow drops the newest. */
|
||||
ISO_QUEUE_ITEM_TYPE_ISO_RX_DATA,
|
||||
ISO_QUEUE_ITEM_TYPE_ISO_TX_COMP,
|
||||
|
||||
/* normal: reliable events (blocking send, never dropped). */
|
||||
ISO_QUEUE_ITEM_TYPE_TIMER_EVENT,
|
||||
ISO_QUEUE_ITEM_TYPE_GAP_EVENT,
|
||||
ISO_QUEUE_ITEM_TYPE_GATT_EVENT,
|
||||
ISO_QUEUE_ITEM_TYPE_ISO_HCI_EVENT,
|
||||
ISO_QUEUE_ITEM_TYPE_ISO_TX_COMP,
|
||||
ISO_QUEUE_ITEM_TYPE_ISO_RX_DATA,
|
||||
|
||||
/* floodable: high-volume best-effort reports, posted non-blocking, newest
|
||||
* dropped on overflow. Named 1:1 after the HCI report events. They share
|
||||
* the floodable queue but EXT/PER use bt_le_gap_handle_event while BIGInfo
|
||||
* uses bt_le_iso_handle_hci_event:
|
||||
* EXT_ADV_REPORT - LE Extended Advertising Report
|
||||
* PER_ADV_REPORT - LE Periodic Advertising Report
|
||||
* BIGINFO_ADV_REPORT - LE BIGInfo Advertising Report (per spec it follows
|
||||
* every PA report carrying a BIGInfo field, i.e. at
|
||||
* the PA interval rate) */
|
||||
ISO_QUEUE_ITEM_TYPE_EXT_ADV_REPORT,
|
||||
ISO_QUEUE_ITEM_TYPE_PER_ADV_REPORT,
|
||||
ISO_QUEUE_ITEM_TYPE_BIGINFO_ADV_REPORT,
|
||||
|
||||
ISO_QUEUE_ITEM_TYPE_MAX,
|
||||
};
|
||||
@@ -64,8 +84,27 @@ struct iso_queue_item {
|
||||
size_t data_len;
|
||||
};
|
||||
|
||||
#define ISO_QUEUE_ITEM_COUNT 100
|
||||
#define ISO_QUEUE_ITEM_SIZE sizeof(struct iso_queue_item)
|
||||
/* Per-priority queue depths. The single FIFO is split into three queues so a
|
||||
* flood of GAP reports cannot delay the latency-critical ISO data path:
|
||||
* critical - ISO_RX_DATA / ISO_TX_COMP (drained first, non-blocking send)
|
||||
* normal - timer / gatt / hci / GAP lifecycle (reliable, blocking send)
|
||||
* floodable - GAP scan / PA data reports (droppable, non-blocking send)
|
||||
*/
|
||||
#define ISO_CRITICAL_QUEUE_LEN 32
|
||||
#define ISO_NORMAL_QUEUE_LEN 64
|
||||
#define ISO_FLOODABLE_QUEUE_LEN 32
|
||||
/* The set must be able to hold one token per item across all three queues. */
|
||||
#define ISO_QUEUE_SET_LEN (ISO_CRITICAL_QUEUE_LEN + \
|
||||
ISO_NORMAL_QUEUE_LEN + \
|
||||
ISO_FLOODABLE_QUEUE_LEN)
|
||||
#define ISO_QUEUE_ITEM_SIZE sizeof(struct iso_queue_item)
|
||||
|
||||
#if CONFIG_BT_ISO_DISPATCH_MONITOR
|
||||
#define ISO_DISPATCH_THRESHOLD_US CONFIG_BT_ISO_DISPATCH_THRESHOLD_US
|
||||
#define ISO_STATS_DUMP_PERIOD_US (CONFIG_BT_ISO_DISPATCH_DUMP_PERIOD_S * 1000 * 1000)
|
||||
|
||||
void bt_le_iso_dispatch_stats_dump(void);
|
||||
#endif /* CONFIG_BT_ISO_DISPATCH_MONITOR */
|
||||
|
||||
int bt_le_iso_task_post(enum iso_queue_item_type type,
|
||||
void *data, size_t data_len);
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "esp_timer.h"
|
||||
|
||||
#include <../host/conn_internal.h>
|
||||
|
||||
#include "common/host.h"
|
||||
@@ -23,45 +25,138 @@
|
||||
|
||||
LOG_MODULE_REGISTER(ISO_TASK, CONFIG_BT_ISO_LOG_LEVEL);
|
||||
|
||||
static QueueHandle_t iso_queue_handle;
|
||||
/* Three priority tiers share one task via a queue set. The task drains
|
||||
* critical before normal before floodable, so a flood of GAP reports cannot
|
||||
* delay the latency-critical ISO data path. See common/task.h for the mapping.
|
||||
*/
|
||||
static QueueHandle_t iso_critical_queue;
|
||||
static QueueHandle_t iso_normal_queue;
|
||||
static QueueHandle_t iso_floodable_queue;
|
||||
static QueueSetHandle_t iso_queue_set;
|
||||
|
||||
static TaskHandle_t iso_task_handle;
|
||||
|
||||
extern void bt_le_timer_handle_event(void *arg);
|
||||
|
||||
#if CONFIG_BT_ISO_DISPATCH_MONITOR
|
||||
/* Per-type dispatch timing, indexed by iso_queue_item_type. Written only by
|
||||
* iso_task (single writer); read by bt_le_iso_dispatch_stats_dump. */
|
||||
static struct iso_dispatch_stats {
|
||||
int64_t max_us;
|
||||
uint32_t count;
|
||||
uint32_t slow_count;
|
||||
} iso_stats[ISO_QUEUE_ITEM_TYPE_MAX];
|
||||
|
||||
static void iso_dispatch_record(uint8_t type, int64_t elapsed_us)
|
||||
{
|
||||
struct iso_dispatch_stats *st;
|
||||
|
||||
if (type >= ISO_QUEUE_ITEM_TYPE_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
st = &iso_stats[type];
|
||||
st->count++;
|
||||
if (elapsed_us > st->max_us) {
|
||||
st->max_us = elapsed_us;
|
||||
}
|
||||
if (elapsed_us > ISO_DISPATCH_THRESHOLD_US) {
|
||||
st->slow_count++;
|
||||
LOG_WRN("IsoCbSlow[%u][%lld]", type, (long long)elapsed_us);
|
||||
}
|
||||
}
|
||||
|
||||
void bt_le_iso_dispatch_stats_dump(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_INF("IsoCbStats thr=%dus", ISO_DISPATCH_THRESHOLD_US);
|
||||
|
||||
for (i = 0; i < ISO_QUEUE_ITEM_TYPE_MAX; i++) {
|
||||
if (iso_stats[i].count == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_INF(" type[%d] cnt=%u max=%lld slow=%u", i, iso_stats[i].count,
|
||||
(long long)iso_stats[i].max_us, iso_stats[i].slow_count);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BT_ISO_DISPATCH_MONITOR */
|
||||
|
||||
static void iso_dispatch_item(const struct iso_queue_item *item)
|
||||
{
|
||||
#if CONFIG_BT_ISO_DISPATCH_MONITOR
|
||||
int64_t elapsed_us;
|
||||
int64_t start_us;
|
||||
|
||||
start_us = esp_timer_get_time();
|
||||
#endif /* CONFIG_BT_ISO_DISPATCH_MONITOR */
|
||||
|
||||
switch (item->type) {
|
||||
case ISO_QUEUE_ITEM_TYPE_TIMER_EVENT:
|
||||
bt_le_timer_handle_event(item->data);
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_GAP_EVENT:
|
||||
case ISO_QUEUE_ITEM_TYPE_EXT_ADV_REPORT:
|
||||
case ISO_QUEUE_ITEM_TYPE_PER_ADV_REPORT:
|
||||
bt_le_gap_handle_event(item->data, item->data_len);
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_GATT_EVENT:
|
||||
bt_le_gatt_handle_event(item->data, item->data_len);
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_ISO_HCI_EVENT:
|
||||
case ISO_QUEUE_ITEM_TYPE_BIGINFO_ADV_REPORT:
|
||||
bt_le_iso_handle_hci_event(item->data, item->data_len);
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_ISO_TX_COMP:
|
||||
bt_le_iso_handle_tx_comp(item->data, item->data_len);
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_ISO_RX_DATA:
|
||||
bt_le_iso_handle_rx_data(item->data, item->data_len);
|
||||
break;
|
||||
default:
|
||||
if (item->data) {
|
||||
free(item->data);
|
||||
}
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
#if CONFIG_BT_ISO_DISPATCH_MONITOR
|
||||
elapsed_us = esp_timer_get_time() - start_us;
|
||||
iso_dispatch_record(item->type, elapsed_us);
|
||||
#endif /* CONFIG_BT_ISO_DISPATCH_MONITOR */
|
||||
}
|
||||
|
||||
static void iso_task(void *p)
|
||||
{
|
||||
#if CONFIG_BT_ISO_DISPATCH_MONITOR
|
||||
int64_t last_dump_us = esp_timer_get_time();
|
||||
#endif /* CONFIG_BT_ISO_DISPATCH_MONITOR */
|
||||
struct iso_queue_item item = {0};
|
||||
|
||||
while (1) {
|
||||
xQueueReceive(iso_queue_handle, &item, portMAX_DELAY);
|
||||
/* Block until any tier has data. The returned member handle is ignored:
|
||||
* we always service by strict priority below (critical > normal >
|
||||
* floodable), processing one item per wakeup and re-checking critical
|
||||
* first on the next loop. A pdFALSE receive is tolerated as a benign
|
||||
* side effect of servicing queues outside xQueueSelectFromSet. */
|
||||
(void)xQueueSelectFromSet(iso_queue_set, portMAX_DELAY);
|
||||
|
||||
switch (item.type) {
|
||||
case ISO_QUEUE_ITEM_TYPE_TIMER_EVENT:
|
||||
bt_le_timer_handle_event(item.data);
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_GAP_EVENT:
|
||||
bt_le_gap_handle_event(item.data, item.data_len);
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_GATT_EVENT:
|
||||
bt_le_gatt_handle_event(item.data, item.data_len);
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_ISO_HCI_EVENT:
|
||||
bt_le_iso_handle_hci_event(item.data, item.data_len);
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_ISO_TX_COMP:
|
||||
bt_le_iso_handle_tx_comp(item.data, item.data_len);
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_ISO_RX_DATA:
|
||||
bt_le_iso_handle_rx_data(item.data, item.data_len);
|
||||
break;
|
||||
default:
|
||||
if (item.data) {
|
||||
free(item.data);
|
||||
}
|
||||
assert(0);
|
||||
break;
|
||||
if (xQueueReceive(iso_critical_queue, &item, 0) == pdTRUE) {
|
||||
iso_dispatch_item(&item);
|
||||
} else if (xQueueReceive(iso_normal_queue, &item, 0) == pdTRUE) {
|
||||
iso_dispatch_item(&item);
|
||||
} else if (xQueueReceive(iso_floodable_queue, &item, 0) == pdTRUE) {
|
||||
iso_dispatch_item(&item);
|
||||
}
|
||||
|
||||
#if CONFIG_BT_ISO_DISPATCH_MONITOR
|
||||
if (esp_timer_get_time() - last_dump_us >= ISO_STATS_DUMP_PERIOD_US) {
|
||||
bt_le_iso_dispatch_stats_dump();
|
||||
last_dump_us = esp_timer_get_time();
|
||||
}
|
||||
#endif /* CONFIG_BT_ISO_DISPATCH_MONITOR */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,31 +164,103 @@ int bt_le_iso_task_post(enum iso_queue_item_type type,
|
||||
void *data, size_t data_len)
|
||||
{
|
||||
struct iso_queue_item item = {0};
|
||||
QueueHandle_t queue;
|
||||
TickType_t wait;
|
||||
int ret;
|
||||
|
||||
item.type = type;
|
||||
item.data = data;
|
||||
item.data_len = data_len;
|
||||
|
||||
ret = xQueueSend(iso_queue_handle, &item, portMAX_DELAY);
|
||||
switch (type) {
|
||||
case ISO_QUEUE_ITEM_TYPE_ISO_RX_DATA:
|
||||
case ISO_QUEUE_ITEM_TYPE_ISO_TX_COMP:
|
||||
/* Latency-critical, posted from the controller task: never block.
|
||||
* On a full queue the caller drops + frees the payload. */
|
||||
queue = iso_critical_queue;
|
||||
wait = 0;
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_EXT_ADV_REPORT:
|
||||
case ISO_QUEUE_ITEM_TYPE_PER_ADV_REPORT:
|
||||
case ISO_QUEUE_ITEM_TYPE_BIGINFO_ADV_REPORT:
|
||||
/* High-volume best-effort reports: never block, drop newest on full. */
|
||||
queue = iso_floodable_queue;
|
||||
wait = 0;
|
||||
break;
|
||||
default:
|
||||
/* Timer / GATT / HCI / GAP lifecycle: reliable, block until space. */
|
||||
queue = iso_normal_queue;
|
||||
wait = portMAX_DELAY;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = xQueueSend(queue, &item, wait);
|
||||
if (ret != pdTRUE) {
|
||||
LOG_ERR("IsoQPostFail[%d]", ret);
|
||||
/* A non-blocking send failing is an expected drop (handled + logged by
|
||||
* the caller); only a blocking send failing is a genuine error. */
|
||||
if (wait != 0) {
|
||||
LOG_ERR("IsoQPostFail[%d][%u]", ret, type);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iso_queue_destroy_one(QueueHandle_t *queue)
|
||||
{
|
||||
struct iso_queue_item item = {0};
|
||||
|
||||
if (*queue == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Empty it so it can leave the set; pending payloads are not freed here
|
||||
* (matches the pre-split deinit behavior). */
|
||||
while (xQueueReceive(*queue, &item, 0) == pdTRUE) {
|
||||
}
|
||||
|
||||
if (iso_queue_set) {
|
||||
xQueueRemoveFromSet(*queue, iso_queue_set);
|
||||
}
|
||||
|
||||
vQueueDelete(*queue);
|
||||
*queue = NULL;
|
||||
}
|
||||
|
||||
static void iso_queues_destroy(void)
|
||||
{
|
||||
iso_queue_destroy_one(&iso_critical_queue);
|
||||
iso_queue_destroy_one(&iso_normal_queue);
|
||||
iso_queue_destroy_one(&iso_floodable_queue);
|
||||
|
||||
if (iso_queue_set) {
|
||||
vQueueDelete(iso_queue_set);
|
||||
iso_queue_set = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int bt_le_iso_task_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
LOG_DBG("IsoTaskInit");
|
||||
|
||||
iso_queue_handle = xQueueCreate(ISO_QUEUE_ITEM_COUNT, ISO_QUEUE_ITEM_SIZE);
|
||||
if (iso_queue_handle == NULL) {
|
||||
iso_critical_queue = xQueueCreate(ISO_CRITICAL_QUEUE_LEN, ISO_QUEUE_ITEM_SIZE);
|
||||
iso_normal_queue = xQueueCreate(ISO_NORMAL_QUEUE_LEN, ISO_QUEUE_ITEM_SIZE);
|
||||
iso_floodable_queue = xQueueCreate(ISO_FLOODABLE_QUEUE_LEN, ISO_QUEUE_ITEM_SIZE);
|
||||
iso_queue_set = xQueueCreateSet(ISO_QUEUE_SET_LEN);
|
||||
if (iso_critical_queue == NULL || iso_normal_queue == NULL ||
|
||||
iso_floodable_queue == NULL || iso_queue_set == NULL) {
|
||||
LOG_ERR("IsoQCreateFail");
|
||||
return -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (xQueueAddToSet(iso_critical_queue, iso_queue_set) != pdPASS ||
|
||||
xQueueAddToSet(iso_normal_queue, iso_queue_set) != pdPASS ||
|
||||
xQueueAddToSet(iso_floodable_queue, iso_queue_set) != pdPASS) {
|
||||
LOG_ERR("IsoQSetAddFail");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = xTaskCreatePinnedToCore(iso_task,
|
||||
@@ -105,12 +272,14 @@ int bt_le_iso_task_init(void)
|
||||
ISO_TASK_CORE);
|
||||
if (ret != pdTRUE) {
|
||||
LOG_ERR("IsoTaskCreateFail[%d]", ret);
|
||||
vQueueDelete(iso_queue_handle);
|
||||
iso_queue_handle = NULL;
|
||||
return -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
iso_queues_destroy();
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
void bt_le_iso_task_deinit(void)
|
||||
@@ -122,8 +291,10 @@ void bt_le_iso_task_deinit(void)
|
||||
iso_task_handle = NULL;
|
||||
}
|
||||
|
||||
if (iso_queue_handle) {
|
||||
vQueueDelete(iso_queue_handle);
|
||||
iso_queue_handle = NULL;
|
||||
}
|
||||
#if CONFIG_BT_ISO_DISPATCH_MONITOR
|
||||
/* Task is gone: no concurrent writer, safe to read the stats. */
|
||||
bt_le_iso_dispatch_stats_dump();
|
||||
#endif /* CONFIG_BT_ISO_DISPATCH_MONITOR */
|
||||
|
||||
iso_queues_destroy();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user