Bluetooth: controller: LLCP PRT rewrite

Rewrite the entire Procedure Response Timeout mechanism.

Use two separate timers for local and remote initiated procedures.

Signed-off-by: Thomas Ebert Hansen <thoh@oticon.com>
This commit is contained in:
Thomas Ebert Hansen 2022-03-28 12:43:05 +02:00 committed by Carles Cufí
commit d1c9efa83d
15 changed files with 180 additions and 57 deletions

View file

@ -1039,7 +1039,9 @@ uint8_t ll_adv_enable(uint8_t enable)
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_TX */
conn->connect_expire = 6;
conn->supervision_expire = 0;
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
conn->procedure_expire = 0;
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
#if defined(CONFIG_BT_CTLR_LE_PING)
conn->apto_expire = 0U;

View file

@ -285,9 +285,11 @@ uint8_t ll_create_connection(uint16_t scan_interval, uint16_t scan_window,
conn->supervision_reload = RADIO_CONN_EVENTS(timeout * 10000U,
conn_interval_us);
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
conn->procedure_expire = 0U;
conn->procedure_reload = RADIO_CONN_EVENTS(40000000,
conn_interval_us);
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
#if defined(CONFIG_BT_CTLR_LE_PING)
conn->apto_expire = 0U;
@ -361,6 +363,9 @@ uint8_t ll_create_connection(uint16_t scan_interval, uint16_t scan_window,
/* Re-initialize the control procedure data structures */
ull_llcp_init(conn);
/* Setup the PRT reload */
ull_cp_prt_reload_set(conn, conn_interval_us);
conn->central.terminate_ack = 0U;
conn->llcp_terminate.reason_final = 0U;

View file

@ -1581,6 +1581,7 @@ void ull_conn_done(struct node_rx_event_done *done)
}
/* check procedure timeout */
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
if (conn->procedure_expire != 0U) {
if (conn->procedure_expire > elapsed_event) {
conn->procedure_expire -= elapsed_event;
@ -1590,6 +1591,13 @@ void ull_conn_done(struct node_rx_event_done *done)
return;
}
}
#else /* CONFIG_BT_LL_SW_LLCP_LEGACY */
if (-ETIMEDOUT == ull_cp_prt_elapse(conn, elapsed_event)) {
conn_cleanup(conn, BT_HCI_ERR_LL_RESP_TIMEOUT);
return;
}
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
#if defined(CONFIG_BT_CTLR_LE_PING)
/* check apto */
@ -7612,21 +7620,6 @@ void ull_conn_resume_rx_data(struct ll_conn *conn)
}
#endif /* CONFIG_BT_CTLR_LE_ENC */
/**
* @brief Restart procedure timeout 'timer'
*/
void ull_conn_prt_reload(struct ll_conn *conn, uint16_t procedure_reload)
{
conn->procedure_expire = procedure_reload;
}
/**
* @brief Clear procedure timeout 'timer'
*/
void ull_conn_prt_clear(struct ll_conn *conn)
{
conn->procedure_expire = 0U;
}
uint16_t ull_conn_event_counter(struct ll_conn *conn)
{
struct lll_conn *lll;
@ -7667,12 +7660,6 @@ void ull_conn_update_parameters(struct ll_conn *conn, uint8_t is_cu_proc, uint8_
instant_latency = (event_counter - instant) & 0xFFFF;
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
if (!is_cu_proc) {
/* Stop procedure timeout */
conn->procedure_expire = 0U;
}
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
ticks_at_expire = conn->llcp.prep.ticks_at_expire;
@ -7766,7 +7753,7 @@ void ull_conn_update_parameters(struct ll_conn *conn, uint8_t is_cu_proc, uint8_
lll->latency = latency;
conn->supervision_reload = RADIO_CONN_EVENTS((timeout * 10U * 1000U), conn_interval_us);
conn->procedure_reload = RADIO_CONN_EVENTS((40U * 1000U * 1000U), conn_interval_us);
ull_cp_prt_reload_set(conn, conn_interval_us);
#if defined(CONFIG_BT_CTLR_LE_PING)
/* APTO in no. of connection events */

View file

@ -87,14 +87,4 @@ void ull_conn_pause_rx_data(struct ll_conn *conn);
*/
void ull_conn_resume_rx_data(struct ll_conn *conn);
/**
* @brief Restart procedure timeout timer
*/
void ull_conn_prt_reload(struct ll_conn *conn, uint16_t procedure_reload);
/**
* @brief Clear procedure timeout timer
*/
void ull_conn_prt_clear(struct ll_conn *conn);
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */

View file

@ -358,6 +358,8 @@ struct llcp_struct {
struct {
sys_slist_t pend_proc_list;
uint8_t state;
/* Procedure Response Timeout timer expire value */
uint16_t prt_expire;
uint8_t pause;
} local;
@ -365,6 +367,8 @@ struct llcp_struct {
struct {
sys_slist_t pend_proc_list;
uint8_t state;
/* Procedure Response Timeout timer expire value */
uint16_t prt_expire;
uint8_t pause;
uint8_t collision;
uint8_t incompat;
@ -374,6 +378,9 @@ struct llcp_struct {
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP || CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
} remote;
/* Procedure Response Timeout timer reload value */
uint16_t prt_reload;
/* Prepare parameters */
struct {
uint32_t ticks_at_expire;
@ -526,8 +533,7 @@ struct ll_conn {
uint16_t connect_expire;
uint16_t supervision_reload;
uint16_t supervision_expire;
uint16_t procedure_reload;
uint16_t procedure_expire;
#if defined(CONFIG_BT_CTLR_PHY)
uint8_t phy_pref_tx:3;

View file

@ -464,6 +464,11 @@ void ull_llcp_init(struct ll_conn *conn)
conn->llcp.remote.paused_cmd = PROC_NONE;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */
/* Reset the Procedure Response Timeout to be disabled,
* 'ull_cp_prt_reload_set' must be called to setup this value.
*/
conn->llcp.prt_reload = 0U;
/* Reset the cached version Information (PROC_VERSION_EXCHANGE) */
memset(&conn->llcp.vex, 0, sizeof(conn->llcp.vex));
@ -530,6 +535,44 @@ void ull_cp_release_ntf(struct node_rx_pdu *ntf)
ll_rx_mem_release((void **)&ntf);
}
static int prt_elapse(uint16_t *expire, uint16_t elapsed_event)
{
if (*expire != 0U) {
if (*expire > elapsed_event) {
*expire -= elapsed_event;
} else {
/* Timer expired */
return -ETIMEDOUT;
}
}
/* Timer still running */
return 0;
}
int ull_cp_prt_elapse(struct ll_conn *conn, uint16_t elapsed_event)
{
int loc_ret;
int rem_ret;
loc_ret = prt_elapse(&conn->llcp.local.prt_expire, elapsed_event);
rem_ret = prt_elapse(&conn->llcp.remote.prt_expire, elapsed_event);
if (loc_ret == -ETIMEDOUT || rem_ret == -ETIMEDOUT) {
/* One of the timers expired */
return -ETIMEDOUT;
}
/* Both timers are still running */
return 0;
}
void ull_cp_prt_reload_set(struct ll_conn *conn, uint32_t conn_intv_us)
{
/* Convert 40s Procedure Response Timeout into events */
conn->llcp.prt_reload = RADIO_CONN_EVENTS((40U * 1000U * 1000U), conn_intv_us);
}
void ull_cp_run(struct ll_conn *conn)
{
llcp_rr_run(conn);

View file

@ -35,6 +35,15 @@ void ull_cp_release_tx(struct ll_conn *conn, struct node_tx *tx);
*/
void ull_cp_release_ntf(struct node_rx_pdu *ntf);
/**
* @brief Procedure Response Timeout Check
* @param elapsed_event The number of elapsed events.
* @return 0 on success, -ETIMEDOUT if timer expired.
*/
int ull_cp_prt_elapse(struct ll_conn *conn, uint16_t elapsed_event);
void ull_cp_prt_reload_set(struct ll_conn *conn, uint32_t conn_intv);
/**
* @brief Run pending LL Control Procedures.
*/

View file

@ -158,9 +158,8 @@ static void lp_comm_tx(struct ll_conn *conn, struct proc_ctx *ctx)
/* Enqueue LL Control PDU towards LLL */
llcp_tx_enqueue(conn, tx);
/* Update procedure timeout. For TERMINATE supervision_timeout is used */
ull_conn_prt_reload(conn, (ctx->proc != PROC_TERMINATE) ? conn->procedure_reload :
conn->supervision_reload);
/* Restart procedure response timeout timer */
llcp_lr_prt_restart(conn);
}
static void lp_comm_ntf_feature_exchange(struct ll_conn *conn, struct proc_ctx *ctx,
@ -858,6 +857,9 @@ static void rp_comm_tx(struct ll_conn *conn, struct proc_ctx *ctx)
/* Enqueue LL Control PDU towards LLL */
llcp_tx_enqueue(conn, tx);
/* Restart procedure response timeout timer */
llcp_rr_prt_restart(conn);
}
static void rp_comm_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)

View file

@ -220,6 +220,13 @@ static void lp_cu_tx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t opcode)
/* Enqueue LL Control PDU towards LLL */
llcp_tx_enqueue(conn, tx);
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
if (ctx->proc == PROC_CONN_PARAM_REQ) {
/* Restart procedure response timeout timer */
llcp_lr_prt_restart(conn);
}
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
}
static void lp_cu_ntf(struct ll_conn *conn, struct proc_ctx *ctx)
@ -486,6 +493,14 @@ static void lp_cu_check_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint
*/
llcp_rr_set_incompat(conn, INCOMPAT_NO_COLLISION);
cu_update_conn_parameters(conn, ctx);
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
if (ctx->proc == PROC_CONN_PARAM_REQ) {
/* Stop procedure response timeout timer */
llcp_lr_prt_stop(conn);
}
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
notify = cu_should_notify_host(ctx);
if (notify) {
ctx->data.cu.error = BT_HCI_ERR_SUCCESS;
@ -645,6 +660,13 @@ static void rp_cu_tx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t opcode)
/* Enqueue LL Control PDU towards LLL */
llcp_tx_enqueue(conn, tx);
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
if (ctx->proc == PROC_CONN_PARAM_REQ) {
/* Restart procedure response timeout timer */
llcp_rr_prt_restart(conn);
}
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
}
static void rp_cu_ntf(struct ll_conn *conn, struct proc_ctx *ctx)
@ -930,10 +952,6 @@ static void rp_cu_st_wait_rx_conn_update_ind(struct ll_conn *conn, struct proc_c
case BT_HCI_ROLE_PERIPHERAL:
llcp_pdu_decode_conn_update_ind(ctx, param);
/* TODO(tosk): skip/terminate if instant passed? */
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
/* conn param req procedure, if any, is complete */
ull_conn_prt_clear(conn);
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
ctx->state = RP_CU_STATE_WAIT_INSTANT;
break;
default:
@ -958,6 +976,14 @@ static void rp_cu_check_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint
* new connection event parameters have been applied.
*/
cu_update_conn_parameters(conn, ctx);
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
if (ctx->proc == PROC_CONN_PARAM_REQ) {
/* Stop procedure response timeout timer */
llcp_rr_prt_stop(conn);
}
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
notify = cu_should_notify_host(ctx);
if (notify) {
ctx->data.cu.error = BT_HCI_ERR_SUCCESS;

View file

@ -175,8 +175,8 @@ static struct node_tx *llcp_lp_enc_tx(struct ll_conn *conn, struct proc_ctx *ctx
/* Enqueue LL Control PDU towards LLL */
llcp_tx_enqueue(conn, tx);
/* Update procedure timeout */
ull_conn_prt_reload(conn, conn->procedure_reload);
/* Restart procedure response timeout timer */
llcp_lr_prt_restart(conn);
return tx;
}
@ -667,6 +667,9 @@ static struct node_tx *llcp_rp_enc_tx(struct ll_conn *conn, struct proc_ctx *ctx
/* Enqueue LL Control PDU towards LLL */
llcp_tx_enqueue(conn, tx);
/* Restart procedure response timeout timer */
llcp_rr_prt_restart(conn);
return tx;
}

View file

@ -351,6 +351,14 @@ void llcp_tx_pause_data(struct ll_conn *conn, enum llcp_tx_q_pause_data_mask pau
void llcp_tx_resume_data(struct ll_conn *conn, enum llcp_tx_q_pause_data_mask resume_mask);
void llcp_tx_flush(struct ll_conn *conn);
/*
* LLCP Procedure Response Timeout
*/
void llcp_lr_prt_restart(struct ll_conn *conn);
void llcp_lr_prt_stop(struct ll_conn *conn);
void llcp_rr_prt_restart(struct ll_conn *conn);
void llcp_rr_prt_stop(struct ll_conn *conn);
/*
* LLCP Local Procedure Common
*/

View file

@ -75,10 +75,6 @@ static void lr_check_done(struct ll_conn *conn, struct proc_ctx *ctx)
lr_dequeue(conn);
if ((ctx->proc != PROC_CHAN_MAP_UPDATE) && (ctx->proc != PROC_CONN_UPDATE)) {
ull_conn_prt_clear(conn);
}
llcp_proc_ctx_release(ctx);
}
}
@ -127,6 +123,16 @@ void llcp_lr_resume(struct ll_conn *conn)
conn->llcp.local.pause = 0U;
}
void llcp_lr_prt_restart(struct ll_conn *conn)
{
conn->llcp.local.prt_expire = conn->llcp.prt_reload;
}
void llcp_lr_prt_stop(struct ll_conn *conn)
{
conn->llcp.local.prt_expire = 0U;
}
void llcp_lr_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx)
{
switch (ctx->proc) {
@ -282,7 +288,12 @@ static void lr_act_complete(struct ll_conn *conn)
struct proc_ctx *ctx;
ctx = llcp_lr_peek(conn);
LL_ASSERT(ctx != NULL);
/* Stop procedure response timeout timer */
llcp_lr_prt_stop(conn);
/* Mark the procedure as safe to delete */
ctx->done = 1U;
}
@ -421,6 +432,7 @@ static void lr_execute_fsm(struct ll_conn *conn, uint8_t evt, void *param)
void llcp_lr_init(struct ll_conn *conn)
{
lr_set_state(conn, LR_STATE_DISCONNECT);
conn->llcp.local.prt_expire = 0U;
}
void llcp_lr_run(struct ll_conn *conn)
@ -454,7 +466,7 @@ void llcp_lr_abort(struct ll_conn *conn)
ctx = lr_dequeue(conn);
}
ull_conn_prt_clear(conn);
llcp_lr_prt_stop(conn);
llcp_rr_set_incompat(conn, 0U);
lr_set_state(conn, LR_STATE_IDLE);
}

View file

@ -356,8 +356,8 @@ static void lp_pu_tx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t opcode)
/* Enqueue LL Control PDU towards LLL */
llcp_tx_enqueue(conn, tx);
/* Update procedure timeout */
ull_conn_prt_reload(conn, conn->procedure_reload);
/* Restart procedure response timeout timer */
llcp_lr_prt_restart(conn);
}
static void pu_ntf(struct ll_conn *conn, struct proc_ctx *ctx)
@ -601,8 +601,10 @@ static void lp_pu_st_wait_tx_ack_phy_update_ind(struct ll_conn *conn, struct pro
pu_set_timing_restrict(conn, ctx->data.pu.c_to_p_phy);
}
/* Since at least one phy will change we clear procedure response timeout */
ull_conn_prt_clear(conn);
/* Since at least one phy will change,
* stop the procedure response timeout
*/
llcp_lr_prt_stop(conn);
/* Now we should wait for instant */
ctx->state = LP_PU_STATE_WAIT_INSTANT;
@ -637,8 +639,10 @@ static void lp_pu_st_wait_rx_phy_update_ind(struct ll_conn *conn, struct proc_ct
pu_set_timing_restrict(conn, ctx->data.pu.p_to_c_phy);
}
/* Since at least one phy will change we clear procedure response timeout */
ull_conn_prt_clear(conn);
/* Since at least one phy will change,
* stop the procedure response timeout
*/
llcp_lr_prt_stop(conn);
ctx->state = LP_PU_STATE_WAIT_INSTANT;
} else {
@ -819,6 +823,9 @@ static void rp_pu_tx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t opcode)
/* Enqueue LL Control PDU towards LLL */
llcp_tx_enqueue(conn, tx);
/* Restart procedure response timeout timer */
llcp_rr_prt_restart(conn);
}
static void rp_pu_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
@ -1025,8 +1032,10 @@ static void rp_pu_st_wait_rx_phy_update_ind(struct ll_conn *conn, struct proc_ct
const uint8_t end_procedure = pu_check_update_ind(conn, ctx);
if (!end_procedure) {
/* Since at least one phy will change we clear procedure response timeout */
ull_conn_prt_clear(conn);
/* Since at least one phy will change,
* stop the procedure response timeout
*/
llcp_rr_prt_stop(conn);
ctx->state = LP_PU_STATE_WAIT_INSTANT;
} else {

View file

@ -108,6 +108,7 @@ static void rr_check_done(struct ll_conn *conn, struct proc_ctx *ctx)
LL_ASSERT(ctx_header == ctx);
rr_dequeue(conn);
llcp_proc_ctx_release(ctx);
}
}
@ -192,6 +193,15 @@ void llcp_rr_resume(struct ll_conn *conn)
conn->llcp.remote.pause = 0U;
}
void llcp_rr_prt_restart(struct ll_conn *conn)
{
conn->llcp.remote.prt_expire = conn->llcp.prt_reload;
}
void llcp_rr_prt_stop(struct ll_conn *conn)
{
conn->llcp.remote.prt_expire = 0U;
}
void llcp_rr_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx)
{
@ -416,10 +426,13 @@ static void rr_act_complete(struct ll_conn *conn)
rr_set_collision(conn, 0U);
/* Dequeue pending request that just completed */
ctx = llcp_rr_peek(conn);
LL_ASSERT(ctx != NULL);
/* Stop procedure response timeout timer */
llcp_rr_prt_stop(conn);
/* Mark the procedure as safe to delete */
ctx->done = 1U;
}
@ -641,6 +654,7 @@ static void rr_execute_fsm(struct ll_conn *conn, uint8_t evt, void *param)
void llcp_rr_init(struct ll_conn *conn)
{
rr_set_state(conn, RR_STATE_DISCONNECT);
conn->llcp.remote.prt_expire = 0U;
}
void llcp_rr_prepare(struct ll_conn *conn, struct node_rx_pdu *rx)
@ -803,6 +817,7 @@ static void rr_abort(struct ll_conn *conn)
ctx = rr_dequeue(conn);
}
llcp_rr_prt_stop(conn);
rr_set_collision(conn, 0U);
rr_set_state(conn, RR_STATE_IDLE);
}

View file

@ -204,8 +204,14 @@ void ull_periph_setup(struct node_rx_hdr *rx, struct node_rx_ftr *ftr,
timeout = sys_le16_to_cpu(pdu_adv->connect_ind.timeout);
conn->supervision_reload =
RADIO_CONN_EVENTS((timeout * 10U * 1000U), conn_interval_us);
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
conn->procedure_reload =
RADIO_CONN_EVENTS((40 * 1000 * 1000), conn_interval_us);
#else
/* Setup the PRT reload */
ull_cp_prt_reload_set(conn, conn_interval_us);
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
#if defined(CONFIG_BT_CTLR_LE_PING)
/* APTO in no. of connection events */