Bluetooth: controller: integrating SCA procedure and adding HCI API

Adding controller support for updating SCA in ACL and ISO peripherals
Adding HCI API support for le_request_peer_sca and complete event.

Signed-off-by: Erik Brockhoff <erbr@oticon.com>
This commit is contained in:
Erik Brockhoff 2022-10-28 13:01:45 +02:00 committed by Carles Cufí
commit 76439d0ba4
10 changed files with 271 additions and 49 deletions

View file

@ -2418,6 +2418,20 @@ static void le_reject_cis(struct net_buf *buf, struct net_buf **evt)
#endif /* CONFIG_BT_PERIPHERAL */
#if defined(CONFIG_BT_CONN)
#if defined(CONFIG_BT_CTLR_SCA_UPDATE)
static void le_req_peer_sca(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_req_peer_sca *cmd = (void *)buf->data;
uint16_t handle;
uint8_t status;
handle = sys_le16_to_cpu(cmd->handle);
status = ll_req_peer_sca(handle);
*evt = cmd_status(status);
}
#endif /* CONFIG_BT_CTLR_SCA_UPDATE */
#if defined(CONFIG_BT_CENTRAL) || defined(CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG)
static void le_read_remote_features(struct net_buf *buf, struct net_buf **evt)
{
@ -4348,6 +4362,12 @@ static int controller_cmd_handle(uint16_t ocf, struct net_buf *cmd,
#endif /* CONFIG_BT_CTLR_PERIPHERAL_ISO */
#endif /* CONFIG_BT_PERIPHERAL */
#if defined(CONFIG_BT_CTLR_SCA_UPDATE)
case BT_OCF(BT_HCI_OP_LE_REQ_PEER_SC):
le_req_peer_sca(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_SCA_UPDATE */
#if defined(CONFIG_BT_CTLR_ISO)
case BT_OCF(BT_HCI_OP_LE_SETUP_ISO_PATH):
le_setup_iso_path(cmd, evt);
@ -8014,6 +8034,31 @@ static void le_phy_upd_complete(struct pdu_data *pdu_data, uint16_t handle,
sep->rx_phy = find_lsb_set(pu->rx);
}
#endif /* CONFIG_BT_CTLR_PHY */
#if defined(CONFIG_BT_CTLR_SCA_UPDATE)
static void le_req_peer_sca_complete(struct pdu_data *pdu, uint16_t handle,
struct net_buf *buf)
{
struct bt_hci_evt_le_req_peer_sca_complete *sep;
struct node_rx_sca *scau;
scau = (void *)pdu;
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
!(le_event_mask & BT_EVT_MASK_LE_REQ_PEER_SCA_COMPLETE)) {
BT_WARN("handle: 0x%04x, status: %x, sca: %x.", handle,
scau->status,
scau->sca);
return;
}
sep = meta_evt(buf, BT_HCI_EVT_LE_REQ_PEER_SCA_COMPLETE, sizeof(*sep));
sep->status = scau->status;
sep->handle = sys_cpu_to_le16(handle);
sep->sca = scau->sca;
}
#endif /* CONFIG_BT_CTLR_SCA_UPDATE */
#endif /* CONFIG_BT_CONN */
#if defined(CONFIG_BT_HCI_MESH_EXT)
@ -8181,6 +8226,12 @@ static void encode_control(struct node_rx_pdu *node_rx,
return;
#endif /* CONFIG_BT_CTLR_CONN_ISO */
#if defined(CONFIG_BT_CTLR_SCA_UPDATE)
case NODE_RX_TYPE_REQ_PEER_SCA_COMPLETE:
le_req_peer_sca_complete(pdu_data, handle, buf);
return;
#endif /* CONFIG_BT_CTLR_SCA_UPDATE */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RX)
case NODE_RX_TYPE_CONN_IQ_SAMPLE_REPORT:
#if defined(CONFIG_BT_CTLR_DF_VS_CONN_IQ_REPORT_16_BITS_IQ_SAMPLES)
@ -8637,6 +8688,10 @@ uint8_t hci_get_class(struct node_rx_pdu *node_rx)
case NODE_RX_TYPE_CIS_REQUEST:
#endif /* CONFIG_BT_CTLR_PERIPHERAL_ISO */
#if defined(CONFIG_BT_CTLR_SCA_UPDATE)
case NODE_RX_TYPE_REQ_PEER_SCA_COMPLETE:
#endif /* CONFIG_BT_CTLR_SCA_UPDATE */
#if defined(CONFIG_BT_CTLR_CONN_ISO)
case NODE_RX_TYPE_CIS_ESTABLISHED:
#endif /* CONFIG_BT_CTLR_CONN_ISO */

View file

@ -246,6 +246,7 @@ uint8_t ll_enc_req_send(uint16_t handle, uint8_t const *const rand_num, uint8_t
uint8_t const *const ltk);
uint8_t ll_start_enc_req_send(uint16_t handle, uint8_t err_code,
uint8_t const *const ltk);
uint8_t ll_req_peer_sca(uint16_t handle);
uint8_t ll_feature_req_send(uint16_t handle);
uint8_t ll_version_ind_send(uint16_t handle);
uint8_t ll_terminate_ind_send(uint16_t handle, uint8_t reason);

View file

@ -1305,6 +1305,10 @@ void ll_rx_dequeue(void)
case NODE_RX_TYPE_CIS_REQUEST:
#endif /* CONFIG_BT_CTLR_PERIPHERAL_ISO */
#if defined(CONFIG_BT_CTLR_SCA_UPDATE)
case NODE_RX_TYPE_REQ_PEER_SCA_COMPLETE:
#endif /* CONFIG_BT_CTLR_SCA_UPDATE */
#if defined(CONFIG_BT_CTLR_CONN_ISO)
case NODE_RX_TYPE_CIS_ESTABLISHED:
#endif /* CONFIG_BT_CTLR_CONN_ISO */
@ -1503,6 +1507,10 @@ void ll_rx_mem_release(void **node_rx)
case NODE_RX_TYPE_CIS_ESTABLISHED:
#endif /* CONFIG_BT_CTLR_CONN_ISO */
#if defined(CONFIG_BT_CTLR_SCA_UPDATE)
case NODE_RX_TYPE_REQ_PEER_SCA_COMPLETE:
#endif /* CONFIG_BT_CTLR_SCA_UPDATE */
#if defined(CONFIG_BT_CTLR_ISO)
case NODE_RX_TYPE_ISO_PDU:
#endif

View file

@ -546,6 +546,20 @@ uint8_t ll_chm_get(uint16_t handle, uint8_t *chm)
return 0;
}
#if defined(CONFIG_BT_CTLR_SCA_UPDATE)
uint8_t ll_req_peer_sca(uint16_t handle)
{
struct ll_conn *conn;
conn = ll_connected_get(handle);
if (!conn) {
return BT_HCI_ERR_UNKNOWN_CONN_ID;
}
return ull_cp_req_peer_sca(conn);
}
#endif /* CONFIG_BT_CTLR_SCA_UPDATE */
static bool is_valid_disconnect_reason(uint8_t reason)
{
switch (reason) {
@ -7905,6 +7919,57 @@ uint16_t ull_conn_event_counter(struct ll_conn *conn)
return event_counter;
}
static void ull_conn_update_ticker(struct ll_conn *conn,
uint32_t ticks_win_offset,
uint32_t ticks_slot_overhead,
uint32_t periodic_us,
uint32_t ticks_at_expire)
{
#if (CONFIG_BT_CTLR_ULL_HIGH_PRIO == CONFIG_BT_CTLR_ULL_LOW_PRIO)
/* disable ticker job, in order to chain stop and start
* to avoid RTC being stopped if no tickers active.
*/
uint32_t mayfly_was_enabled =
mayfly_is_enabled(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_ULL_LOW);
mayfly_enable(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_ULL_LOW, 0U);
#endif /* CONFIG_BT_CTLR_ULL_HIGH_PRIO == CONFIG_BT_CTLR_ULL_LOW_PRIO */
/* start periph/central with new timings */
uint8_t ticker_id_conn = TICKER_ID_CONN_BASE + ll_conn_handle_get(conn);
uint32_t ticker_status = ticker_stop(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_HIGH,
ticker_id_conn, ticker_stop_conn_op_cb, (void *)conn);
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
(ticker_status == TICKER_STATUS_BUSY));
ticker_status = ticker_start(
TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_HIGH, ticker_id_conn, ticks_at_expire,
ticks_win_offset, HAL_TICKER_US_TO_TICKS(periodic_us),
HAL_TICKER_REMAINDER(periodic_us),
#if defined(CONFIG_BT_TICKER_LOW_LAT)
TICKER_NULL_LAZY,
#else /* !CONFIG_BT_TICKER_LOW_LAT */
TICKER_LAZY_MUST_EXPIRE_KEEP,
#endif /* CONFIG_BT_TICKER_LOW_LAT */
(ticks_slot_overhead + conn->ull.ticks_slot),
#if defined(CONFIG_BT_PERIPHERAL) && defined(CONFIG_BT_CENTRAL)
conn->lll.role == BT_HCI_ROLE_PERIPHERAL ?
ull_periph_ticker_cb : ull_central_ticker_cb,
#elif defined(CONFIG_BT_PERIPHERAL)
ull_periph_ticker_cb,
#else
ull_central_ticker_cb,
#endif /* CONFIG_BT_PERIPHERAL && CONFIG_BT_CENTRAL */
conn, ticker_start_conn_op_cb, (void *)conn);
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
(ticker_status == TICKER_STATUS_BUSY));
#if (CONFIG_BT_CTLR_ULL_HIGH_PRIO == CONFIG_BT_CTLR_ULL_LOW_PRIO)
/* enable ticker job, if disabled in this function */
if (mayfly_was_enabled) {
mayfly_enable(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_ULL_LOW, 1U);
}
#endif /* CONFIG_BT_CTLR_ULL_HIGH_PRIO == CONFIG_BT_CTLR_ULL_LOW_PRIO */
}
void ull_conn_update_parameters(struct ll_conn *conn, uint8_t is_cu_proc, uint8_t win_size,
uint16_t win_offset_us, uint16_t interval, uint16_t latency,
@ -7916,8 +7981,6 @@ void ull_conn_update_parameters(struct ll_conn *conn, uint8_t is_cu_proc, uint8_
uint16_t conn_interval_old;
uint16_t conn_interval_new;
uint32_t conn_interval_us;
uint8_t ticker_id_conn;
uint32_t ticker_status;
uint32_t periodic_us;
uint16_t latency_upd;
uint16_t instant_latency;
@ -7980,10 +8043,9 @@ void ull_conn_update_parameters(struct ll_conn *conn, uint8_t is_cu_proc, uint8_
lll->periph.window_widening_periodic_us * instant_latency;
lll->periph.window_widening_periodic_us =
(((lll_clock_ppm_local_get() + lll_clock_ppm_get(conn->periph.sca)) *
conn_interval_us) +
(1000000U - 1U)) /
1000000U;
ceiling_fraction(((lll_clock_ppm_local_get() +
lll_clock_ppm_get(conn->periph.sca)) *
conn_interval_us), 1000000U);
lll->periph.window_widening_max_us = (conn_interval_us >> 1U) - EVENT_IFS_US;
lll->periph.window_size_prepare_us = win_size * CONN_INT_UNIT_US;
@ -8042,53 +8104,41 @@ void ull_conn_update_parameters(struct ll_conn *conn, uint8_t is_cu_proc, uint8_
conn->supervision_expire = 0U;
}
#if (CONFIG_BT_CTLR_ULL_HIGH_PRIO == CONFIG_BT_CTLR_ULL_LOW_PRIO)
/* disable ticker job, in order to chain stop and start
* to avoid RTC being stopped if no tickers active.
*/
uint32_t mayfly_was_enabled =
mayfly_is_enabled(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_ULL_LOW);
mayfly_enable(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_ULL_LOW, 0U);
#endif /* CONFIG_BT_CTLR_ULL_HIGH_PRIO == CONFIG_BT_CTLR_ULL_LOW_PRIO */
/* start periph/central with new timings */
ticker_id_conn = TICKER_ID_CONN_BASE + ll_conn_handle_get(conn);
ticker_status = ticker_stop(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_HIGH,
ticker_id_conn, ticker_stop_conn_op_cb, (void *)conn);
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
(ticker_status == TICKER_STATUS_BUSY));
ticker_status = ticker_start(
TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_HIGH, ticker_id_conn, ticks_at_expire,
ticks_win_offset, HAL_TICKER_US_TO_TICKS(periodic_us),
HAL_TICKER_REMAINDER(periodic_us),
#if defined(CONFIG_BT_TICKER_LOW_LAT)
TICKER_NULL_LAZY,
#else /* !CONFIG_BT_TICKER_LOW_LAT */
TICKER_LAZY_MUST_EXPIRE_KEEP,
#endif /* CONFIG_BT_TICKER_LOW_LAT */
(ticks_slot_overhead + conn->ull.ticks_slot),
#if defined(CONFIG_BT_PERIPHERAL) && defined(CONFIG_BT_CENTRAL)
lll->role == BT_HCI_ROLE_PERIPHERAL ? ull_periph_ticker_cb : ull_central_ticker_cb,
#elif defined(CONFIG_BT_PERIPHERAL)
ull_periph_ticker_cb,
#else
ull_central_ticker_cb,
#endif /* CONFIG_BT_PERIPHERAL && CONFIG_BT_CENTRAL */
conn, ticker_start_conn_op_cb, (void *)conn);
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
(ticker_status == TICKER_STATUS_BUSY));
#if (CONFIG_BT_CTLR_ULL_HIGH_PRIO == CONFIG_BT_CTLR_ULL_LOW_PRIO)
/* enable ticker job, if disabled in this function */
if (mayfly_was_enabled) {
mayfly_enable(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_ULL_LOW, 1U);
}
#endif /* CONFIG_BT_CTLR_ULL_HIGH_PRIO == CONFIG_BT_CTLR_ULL_LOW_PRIO */
/* Update ACL ticker */
ull_conn_update_ticker(conn, ticks_win_offset, ticks_slot_overhead, periodic_us,
ticks_at_expire);
/* Signal that the prepare needs to be canceled */
conn->cancel_prepare = 1U;
}
#if defined(CONFIG_BT_PERIPHERAL)
void ull_conn_update_peer_sca(struct ll_conn *conn)
{
struct lll_conn *lll;
uint32_t conn_interval_us;
uint32_t periodic_us;
lll = &conn->lll;
/* calculate the window widening and interval */
conn_interval_us = lll->interval * CONN_INT_UNIT_US;
periodic_us = conn_interval_us;
lll->periph.window_widening_periodic_us =
ceiling_fraction(((lll_clock_ppm_local_get() +
lll_clock_ppm_get(conn->periph.sca)) *
conn_interval_us), 1000000U);
periodic_us -= lll->periph.window_widening_periodic_us;
/* Update ACL ticker */
ull_conn_update_ticker(conn, HAL_TICKER_US_TO_TICKS(periodic_us), 0, periodic_us,
conn->llcp.prep.ticks_at_expire);
}
#endif /* CONFIG_BT_PERIPHERAL */
void ull_conn_chan_map_set(struct ll_conn *conn, const uint8_t chm[5])
{
struct lll_conn *lll = &conn->lll;

View file

@ -85,6 +85,8 @@ void ull_conn_update_parameters(struct ll_conn *conn, uint8_t is_cu_proc,
uint16_t interval, uint16_t latency,
uint16_t timeout, uint16_t instant);
void ull_conn_update_peer_sca(struct ll_conn *conn);
void ull_conn_default_tx_octets_set(uint16_t tx_octets);
void ull_conn_default_tx_time_set(uint16_t tx_time);

View file

@ -49,6 +49,7 @@ struct ll_conn_iso_group {
uint16_t iso_interval;
uint8_t cig_id;
uint8_t started:1; /* 1 if CIG started and ticker is running */
uint8_t sca_update:4; /* (new SCA)+1 to trigger restart of ticker */
};
struct node_rx_conn_iso_req {

View file

@ -33,7 +33,9 @@
#include "isoal.h"
#include "ull_iso_types.h"
#include "ull_conn_iso_types.h"
#include "ull_iso_internal.h"
#include "ull_conn_iso_internal.h"
#include "ull_peripheral_iso_internal.h"
#include "ull_conn_types.h"
#include "ull_chan_internal.h"
@ -544,6 +546,17 @@ static void lp_comm_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t
ctx->data.sca_update.error_code = BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL;
/* Fall through to complete procedure */
case PDU_DATA_LLCTRL_TYPE_CLOCK_ACCURACY_RSP:
#if defined(CONFIG_BT_PERIPHERAL)
if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL &&
!ctx->data.sca_update.error_code &&
conn->periph.sca != ctx->data.sca_update.sca) {
conn->periph.sca = ctx->data.sca_update.sca;
ull_conn_update_peer_sca(conn);
#if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
ull_peripheral_iso_update_peer_sca(conn);
#endif /* defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */
}
#endif /* CONFIG_BT_PERIPHERAL */
lp_sca_ntf(conn, ctx);
llcp_lr_complete(conn);
ctx->state = LP_COMMON_STATE_IDLE;
@ -1394,6 +1407,15 @@ static void rp_comm_st_wait_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, u
#if defined(CONFIG_BT_CTLR_SCA_UPDATE)
case PROC_SCA_UPDATE: {
ctx->tx_ack = NULL;
#if defined(CONFIG_BT_PERIPHERAL)
if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL) {
conn->periph.sca = ctx->data.sca_update.sca;
ull_conn_update_peer_sca(conn);
#if defined(CONFIG_BT_CTLR_PERIPHERAL_ISO)
ull_peripheral_iso_update_peer_sca(conn);
#endif /* defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */
}
#endif /* CONFIG_BT_PERIPHERAL */
llcp_rr_complete(conn);
ctx->state = RP_COMMON_STATE_IDLE;
}

View file

@ -314,6 +314,10 @@ uint8_t ull_peripheral_iso_setup(struct pdu_data_llctrl_cis_ind *ind,
return 0;
}
static void ull_peripheral_iso_update_ticker(struct ll_conn_iso_group *cig,
uint32_t ticks_at_expire,
uint32_t iso_interval_us_frac);
static void ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift,
uint32_t remainder, uint16_t lazy, uint8_t force,
void *param)
@ -387,6 +391,21 @@ static void ticker_cb(uint32_t ticks_at_expire, uint32_t ticks_drift,
p.param = &cig->lll;
mfy.param = &p;
if (cig->sca_update) {
/* CIG/ACL affilaition established */
uint32_t iso_interval_us_frac =
EVENT_US_TO_US_FRAC(cig->iso_interval * CONN_INT_UNIT_US);
cig->lll.window_widening_periodic_us_frac =
ceiling_fraction(((lll_clock_ppm_local_get() +
lll_clock_ppm_get(cig->sca_update - 1)) *
iso_interval_us_frac),
1000000U);
iso_interval_us_frac -= cig->lll.window_widening_periodic_us_frac;
ull_peripheral_iso_update_ticker(cig, ticks_at_expire, iso_interval_us_frac);
cig->sca_update = 0;
}
/* Kick LLL prepare */
err = mayfly_enqueue(TICKER_USER_ID_ULL_HIGH, TICKER_USER_ID_LLL,
0, &mfy);
@ -403,6 +422,35 @@ static void ticker_op_cb(uint32_t status, void *param)
LL_ASSERT(status == TICKER_STATUS_SUCCESS);
}
static void ull_peripheral_iso_update_ticker(struct ll_conn_iso_group *cig,
uint32_t ticks_at_expire,
uint32_t iso_interval_us_frac)
{
/* stop/start with new updated timings */
uint8_t ticker_id_cig = TICKER_ID_CONN_ISO_BASE + ll_conn_iso_group_handle_get(cig);
uint32_t ticker_status = ticker_stop(TICKER_INSTANCE_ID_CTLR, TICKER_USER_ID_ULL_HIGH,
ticker_id_cig, ticker_op_cb, NULL);
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
(ticker_status == TICKER_STATUS_BUSY));
ticker_status = ticker_start(TICKER_INSTANCE_ID_CTLR,
TICKER_USER_ID_ULL_HIGH,
ticker_id_cig,
ticks_at_expire,
EVENT_US_FRAC_TO_TICKS(iso_interval_us_frac),
EVENT_US_FRAC_TO_TICKS(iso_interval_us_frac),
EVENT_US_FRAC_TO_REMAINDER(iso_interval_us_frac),
TICKER_NULL_LAZY,
0,
ticker_cb, cig,
ticker_op_cb, NULL);
LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
(ticker_status == TICKER_STATUS_BUSY));
}
void ull_peripheral_iso_start(struct ll_conn *acl, uint32_t ticks_at_expire,
uint16_t cis_handle)
{
@ -491,3 +539,33 @@ void ull_peripheral_iso_start(struct ll_conn *acl, uint32_t ticks_at_expire,
cig->started = 1;
}
void ull_peripheral_iso_update_peer_sca(struct ll_conn *acl)
{
uint8_t cig_handle;
/* Find CIG associated with ACL conn */
for (cig_handle = 0; cig_handle < CONFIG_BT_CTLR_CONN_ISO_GROUPS; cig_handle++) {
/* Go through all ACL affiliated CIGs and update peer SCA */
struct ll_conn_iso_stream *cis;
struct ll_conn_iso_group *cig;
cig = ll_conn_iso_group_get(cig_handle);
if (!cig || !cig->lll.num_cis) {
continue;
}
cis = ll_conn_iso_stream_get_by_group(cig, NULL);
LL_ASSERT(cis);
uint16_t cis_handle = cis->lll.handle;
cis = ll_iso_stream_connected_get(cis_handle);
if (!cis) {
continue;
}
if (cis->lll.acl_handle == acl->lll.handle) {
cig->sca_update = acl->periph.sca + 1;
}
}
}

View file

@ -17,3 +17,4 @@ uint8_t ull_peripheral_iso_setup(struct pdu_data_llctrl_cis_ind *ind,
uint16_t cis_handle);
void ull_peripheral_iso_start(struct ll_conn *acl, uint32_t ticks_at_expire,
uint16_t cis_handle);
void ull_peripheral_iso_update_peer_sca(struct ll_conn *acl);

View file

@ -60,3 +60,7 @@ void ull_peripheral_iso_start(struct ll_conn *acl, uint32_t ticks_at_expire,
uint16_t cis_handle)
{
}
void ull_peripheral_iso_update_peer_sca(struct ll_conn *acl)
{
}