Skip to content

Commit

Permalink
nimble/ll: Add ISO sync feedback over HCI
Browse files Browse the repository at this point in the history
This adds optional synchronization feedback over HCI for ISO. See syscfg
descrption for details.
  • Loading branch information
andrzej-kaczmarek committed Oct 16, 2023
1 parent a988afe commit 8b4fd16
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 5 deletions.
64 changes: 59 additions & 5 deletions nimble/controller/src/ble_ll_iso_big.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ struct ble_ll_iso_big {

struct ble_ll_iso_bis_q bis_q;

#if MYNEWT_VAL(BLE_LL_ISO_HCI_FEEDBACK_INTERVAL_MS)
uint32_t last_feedback;
#endif

uint8_t cstf : 1;
uint8_t cssn : 4;
uint8_t control_active : 3;
Expand Down Expand Up @@ -398,8 +402,16 @@ ble_ll_iso_big_event_done(struct ble_ll_iso_big *big)
{
struct ble_ll_iso_bis *bis;
struct ble_hci_ev *hci_ev;
#if MYNEWT_VAL(BLE_LL_ISO_HCI_FEEDBACK_INTERVAL_MS)
struct ble_hci_ev *fb_hci_ev = NULL;
struct ble_hci_ev_vs *fb_hci_ev_vs;
struct ble_hci_vs_subev_iso_hci_feedback *fb_hci_subev = NULL;
uint16_t exp;
uint32_t now;
#endif
struct ble_hci_ev_num_comp_pkts *hci_ev_ncp;
int num_completed_pkt;
int idx;
int rc;

ble_ll_rfmgmt_release();
Expand All @@ -417,18 +429,49 @@ ble_ll_iso_big_event_done(struct ble_ll_iso_big *big)
hci_ev_ncp->count = 0;
}

#if MYNEWT_VAL(BLE_LL_ISO_HCI_FEEDBACK_INTERVAL_MS)
now = os_time_get();
if (OS_TIME_TICK_GEQ(now, big->last_feedback +
os_time_ms_to_ticks32(MYNEWT_VAL(BLE_LL_ISO_HCI_FEEDBACK_INTERVAL_MS)))) {
fb_hci_ev = ble_transport_alloc_evt(1);
if (fb_hci_ev) {
fb_hci_ev->opcode = BLE_HCI_EVCODE_VS;
fb_hci_ev->length = sizeof(*fb_hci_ev_vs) + sizeof(*fb_hci_subev);
fb_hci_ev_vs = (void *)fb_hci_ev->data;
fb_hci_ev_vs->id = BLE_HCI_VS_SUBEV_ISO_HCI_FEEDBACK;
fb_hci_subev = (void *)fb_hci_ev_vs->data;
fb_hci_subev->big_handle = big->handle;
fb_hci_subev->count = 0;
}
}
#endif

STAILQ_FOREACH(bis, &big->bis_q, bis_q_next) {
num_completed_pkt = ble_ll_isoal_mux_event_done(&bis->mux);
if (hci_ev && num_completed_pkt) {
hci_ev_ncp->completed[hci_ev_ncp->count].handle =
htole16(bis->conn_handle);
hci_ev_ncp->completed[hci_ev_ncp->count].packets =
htole16(num_completed_pkt + bis->num_completed_pkt);
idx = hci_ev_ncp->count++;
hci_ev_ncp->completed[idx].handle = htole16(bis->conn_handle);
hci_ev_ncp->completed[idx].packets = htole16(num_completed_pkt +
bis->num_completed_pkt);
bis->num_completed_pkt = 0;
hci_ev_ncp->count++;
} else {
bis->num_completed_pkt += num_completed_pkt;
}

#if MYNEWT_VAL(BLE_LL_ISO_HCI_FEEDBACK_INTERVAL_MS)
if (fb_hci_ev) {
/* Expected SDUs in queue after an event -> host should send
* sdu_per_interval SDUs until next event so there are sdu_per_event
* SDUs queued at next event. Feedback value is the difference between
* expected and actual SDUs count.
*/
exp = bis->mux.sdu_per_event - bis->mux.sdu_per_interval;
idx = fb_hci_subev->count++;
fb_hci_subev->feedback[idx].handle = htole16(bis->conn_handle);
fb_hci_subev->feedback[idx].sdu_per_interval = bis->mux.sdu_per_interval;
fb_hci_subev->feedback[idx].diff = (int8_t)(bis->mux.sdu_q_len - exp);
}
#endif
}

if (hci_ev) {
Expand All @@ -441,6 +484,15 @@ ble_ll_iso_big_event_done(struct ble_ll_iso_big *big)
}
}

#if MYNEWT_VAL(BLE_LL_ISO_HCI_FEEDBACK_INTERVAL_MS)
if (fb_hci_ev) {
fb_hci_ev->length = sizeof(*fb_hci_ev_vs) + sizeof(*fb_hci_subev) +
fb_hci_subev->count * sizeof(fb_hci_subev->feedback[0]);
ble_transport_to_hs_evt(fb_hci_ev);
big->last_feedback = now;
}
#endif

big->sch.start_time = big->event_start;
big->sch.remainder = big->event_start_us;

Expand Down Expand Up @@ -760,6 +812,8 @@ ble_ll_iso_big_event_sched_cb(struct ble_ll_sched_item *sch)
ble_phy_mode_set(phy_mode, phy_mode);
#endif

ble_ll_tx_power_set(g_ble_ll_tx_power);

BLE_LL_ASSERT(!big->framed);

/* XXX calculate this in advance at the end of previous event? */
Expand Down
17 changes: 17 additions & 0 deletions nimble/controller/syscfg.yml
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,23 @@ syscfg.defs:
- BLE_LL_ISO if 1
value: 0
state: experimental
BLE_LL_ISO_HCI_FEEDBACK_INTERVAL_MS:
description: >
Enables ISO synchronization feedback using vendor-specific HCI event.
The event is sent at configured interval after completed ISO event
and contains BIG handle, number of expected SDUs per ISO interval
and difference between expected vs actual number of SDUs queued in
controller.
The expected number of SDUs queued after each event is number of
SDUs required for each ISO event (i.e. including pre-transmissions)
minus number of SDUs expected per each ISO interval.
The host can use feedback to e.g. adjust for jitter between audio
clock and LL clock.
Set to 0 to disable.
value: 0
experimental: 1
restrictions:
- BLE_LL_HCI_VS || BLE_LL_ISO_HCI_FEEDBACK_INTERVAL_MS == 0

BLE_LL_SYSINIT_STAGE:
description: >
Expand Down
12 changes: 12 additions & 0 deletions nimble/include/nimble/hci_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -1611,6 +1611,18 @@ struct ble_hci_ev_vs_css_slot_changed {
uint16_t slot_idx;
};

#define BLE_HCI_VS_SUBEV_ISO_HCI_FEEDBACK (0x03)
struct feedback_pkt {
uint16_t handle;
uint8_t sdu_per_interval;
int8_t diff;
} __attribute__((packed));
struct ble_hci_vs_subev_iso_hci_feedback {
uint8_t big_handle;
uint8_t count;
struct feedback_pkt feedback[0];
} __attribute__((packed));

#define BLE_HCI_VS_SUBEV_ID_LLCP_TRACE (0x17)

/* LE sub-event codes */
Expand Down

0 comments on commit 8b4fd16

Please sign in to comment.