You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
676 lines
15 KiB
676 lines
15 KiB
/* |
|
* Copyright (c) 2020 Demant |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#include <zephyr/kernel.h> |
|
|
|
#include <zephyr/sys/byteorder.h> |
|
#include <zephyr/sys/slist.h> |
|
#include <zephyr/sys/util.h> |
|
|
|
#include <zephyr/bluetooth/hci_types.h> |
|
|
|
#include "hal/ccm.h" |
|
|
|
#include "util/util.h" |
|
#include "util/mem.h" |
|
#include "util/memq.h" |
|
#include "util/mayfly.h" |
|
#include "util/dbuf.h" |
|
|
|
#include "pdu_df.h" |
|
#include "lll/pdu_vendor.h" |
|
#include "pdu.h" |
|
|
|
#include "ll.h" |
|
#include "ll_settings.h" |
|
|
|
#include "lll.h" |
|
#include "lll/lll_df_types.h" |
|
#include "lll_conn.h" |
|
#include "lll_conn_iso.h" |
|
|
|
#include "ull_tx_queue.h" |
|
|
|
#include "isoal.h" |
|
#include "ull_internal.h" |
|
#include "ull_iso_types.h" |
|
#include "ull_conn_iso_types.h" |
|
#include "ull_conn_iso_internal.h" |
|
|
|
#include "ull_conn_types.h" |
|
#include "ull_llcp.h" |
|
#include "ull_llcp_internal.h" |
|
#include "ull_conn_internal.h" |
|
|
|
#include <soc.h> |
|
#include "hal/debug.h" |
|
|
|
static struct proc_ctx *lr_dequeue(struct ll_conn *conn); |
|
|
|
/* LLCP Local Request FSM State */ |
|
enum lr_state { |
|
LR_STATE_IDLE, |
|
LR_STATE_ACTIVE, |
|
LR_STATE_DISCONNECT, |
|
LR_STATE_TERMINATE, |
|
}; |
|
|
|
/* LLCP Local Request FSM Event */ |
|
enum { |
|
/* Procedure run */ |
|
LR_EVT_RUN, |
|
|
|
/* Procedure completed */ |
|
LR_EVT_COMPLETE, |
|
|
|
/* Link connected */ |
|
LR_EVT_CONNECT, |
|
|
|
/* Link disconnected */ |
|
LR_EVT_DISCONNECT, |
|
}; |
|
|
|
void llcp_lr_check_done(struct ll_conn *conn, struct proc_ctx *ctx) |
|
{ |
|
if (ctx->done) { |
|
struct proc_ctx *ctx_header; |
|
|
|
ctx_header = llcp_lr_peek(conn); |
|
LL_ASSERT(ctx_header == ctx); |
|
|
|
/* If we have a node rx it must not be marked RETAIN as |
|
* the memory referenced would leak |
|
*/ |
|
LL_ASSERT(ctx->node_ref.rx == NULL || |
|
ctx->node_ref.rx->hdr.type != NODE_RX_TYPE_RETAIN); |
|
|
|
lr_dequeue(conn); |
|
|
|
llcp_proc_ctx_release(ctx); |
|
} |
|
} |
|
|
|
/* |
|
* LLCP Local Request Shared Data Locking |
|
*/ |
|
|
|
static ALWAYS_INLINE uint32_t shared_data_access_lock(void) |
|
{ |
|
bool enabled; |
|
|
|
if (mayfly_is_running()) { |
|
/* We are in Mayfly context, nothing to be done */ |
|
return false; |
|
} |
|
|
|
/* We are in thread context and have to disable TICKER_USER_ID_ULL_HIGH */ |
|
enabled = mayfly_is_enabled(TICKER_USER_ID_THREAD, TICKER_USER_ID_ULL_HIGH) != 0U; |
|
mayfly_enable(TICKER_USER_ID_THREAD, TICKER_USER_ID_ULL_HIGH, 0U); |
|
|
|
return enabled; |
|
} |
|
|
|
static ALWAYS_INLINE void shared_data_access_unlock(bool key) |
|
{ |
|
if (key) { |
|
/* We are in thread context and have to reenable TICKER_USER_ID_ULL_HIGH */ |
|
mayfly_enable(TICKER_USER_ID_THREAD, TICKER_USER_ID_ULL_HIGH, 1U); |
|
} |
|
} |
|
|
|
/* |
|
* LLCP Local Request FSM |
|
*/ |
|
|
|
static void lr_set_state(struct ll_conn *conn, enum lr_state state) |
|
{ |
|
conn->llcp.local.state = state; |
|
} |
|
|
|
void llcp_lr_enqueue(struct ll_conn *conn, struct proc_ctx *ctx) |
|
{ |
|
/* This function is called from both Thread and Mayfly (ISR), |
|
* make sure only a single context have access at a time. |
|
*/ |
|
|
|
bool key = shared_data_access_lock(); |
|
|
|
sys_slist_append(&conn->llcp.local.pend_proc_list, &ctx->node); |
|
|
|
shared_data_access_unlock(key); |
|
} |
|
|
|
static struct proc_ctx *lr_dequeue(struct ll_conn *conn) |
|
{ |
|
/* This function is called from both Thread and Mayfly (ISR), |
|
* make sure only a single context have access at a time. |
|
*/ |
|
|
|
struct proc_ctx *ctx; |
|
|
|
bool key = shared_data_access_lock(); |
|
|
|
ctx = (struct proc_ctx *)sys_slist_get(&conn->llcp.local.pend_proc_list); |
|
|
|
shared_data_access_unlock(key); |
|
|
|
return ctx; |
|
} |
|
|
|
struct proc_ctx *llcp_lr_peek(struct ll_conn *conn) |
|
{ |
|
/* This function is called from both Thread and Mayfly (ISR), |
|
* make sure only a single context have access at a time. |
|
*/ |
|
struct proc_ctx *ctx; |
|
|
|
bool key = shared_data_access_lock(); |
|
|
|
ctx = (struct proc_ctx *)sys_slist_peek_head(&conn->llcp.local.pend_proc_list); |
|
|
|
shared_data_access_unlock(key); |
|
|
|
return ctx; |
|
} |
|
|
|
struct proc_ctx *llcp_lr_peek_proc(struct ll_conn *conn, uint8_t proc) |
|
{ |
|
/* This function is called from both Thread and Mayfly (ISR), |
|
* make sure only a single context have access at a time. |
|
*/ |
|
|
|
struct proc_ctx *ctx, *tmp; |
|
|
|
bool key = shared_data_access_lock(); |
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&conn->llcp.local.pend_proc_list, ctx, tmp, node) { |
|
if (ctx->proc == proc) { |
|
break; |
|
} |
|
} |
|
|
|
shared_data_access_unlock(key); |
|
|
|
return ctx; |
|
} |
|
|
|
bool llcp_lr_ispaused(struct ll_conn *conn) |
|
{ |
|
return conn->llcp.local.pause == 1U; |
|
} |
|
|
|
void llcp_lr_pause(struct ll_conn *conn) |
|
{ |
|
conn->llcp.local.pause = 1U; |
|
} |
|
|
|
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_restart_with_value(struct ll_conn *conn, uint16_t value) |
|
{ |
|
conn->llcp.local.prt_expire = value; |
|
} |
|
|
|
void llcp_lr_prt_stop(struct ll_conn *conn) |
|
{ |
|
conn->llcp.local.prt_expire = 0U; |
|
} |
|
|
|
void llcp_lr_flush_procedures(struct ll_conn *conn) |
|
{ |
|
struct proc_ctx *ctx; |
|
|
|
/* Flush all pending procedures */ |
|
ctx = lr_dequeue(conn); |
|
while (ctx) { |
|
llcp_nodes_release(conn, ctx); |
|
llcp_proc_ctx_release(ctx); |
|
ctx = lr_dequeue(conn); |
|
} |
|
} |
|
|
|
void llcp_lr_rx(struct ll_conn *conn, struct proc_ctx *ctx, memq_link_t *link, |
|
struct node_rx_pdu *rx) |
|
{ |
|
/* In the case of a specific connection update procedure collision it can occur that |
|
* an 'unexpected' REJECT_IND_PDU is received and passed as RX'ed and will then result in |
|
* discarding of the retention of the previously received CONNECTION_UPDATE_IND |
|
* and following this, an assert will be hit when attempting to use this retained |
|
* RX node for creating the notification on completion of connection param request. |
|
* (see comment in ull_llcp_conn_upd.c::lp_cu_st_wait_instant() for more details) |
|
* |
|
* The workaround/fix for this is to only store an RX node for retention if |
|
* 'we havent already' got one |
|
*/ |
|
if (!ctx->node_ref.rx) { |
|
/* Store RX node and link */ |
|
ctx->node_ref.rx = rx; |
|
ctx->node_ref.link = link; |
|
} |
|
|
|
switch (ctx->proc) { |
|
#if defined(CONFIG_BT_CTLR_LE_PING) |
|
case PROC_LE_PING: |
|
llcp_lp_comm_rx(conn, ctx, rx); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_LE_PING */ |
|
case PROC_FEATURE_EXCHANGE: |
|
llcp_lp_comm_rx(conn, ctx, rx); |
|
break; |
|
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN) |
|
case PROC_MIN_USED_CHANS: |
|
llcp_lp_comm_rx(conn, ctx, rx); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN */ |
|
case PROC_VERSION_EXCHANGE: |
|
llcp_lp_comm_rx(conn, ctx, rx); |
|
break; |
|
#if defined(CONFIG_BT_CTLR_LE_ENC) && defined(CONFIG_BT_CENTRAL) |
|
case PROC_ENCRYPTION_START: |
|
case PROC_ENCRYPTION_PAUSE: |
|
llcp_lp_enc_rx(conn, ctx, rx); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_LE_ENC && CONFIG_BT_CENTRAL */ |
|
#ifdef CONFIG_BT_CTLR_PHY |
|
case PROC_PHY_UPDATE: |
|
llcp_lp_pu_rx(conn, ctx, rx); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_PHY */ |
|
case PROC_CONN_UPDATE: |
|
case PROC_CONN_PARAM_REQ: |
|
llcp_lp_cu_rx(conn, ctx, rx); |
|
break; |
|
case PROC_TERMINATE: |
|
llcp_lp_comm_rx(conn, ctx, rx); |
|
break; |
|
#if defined(CONFIG_BT_CENTRAL) |
|
case PROC_CHAN_MAP_UPDATE: |
|
llcp_lp_chmu_rx(conn, ctx, rx); |
|
break; |
|
#endif /* CONFIG_BT_CENTRAL */ |
|
#if defined(CONFIG_BT_CTLR_DATA_LENGTH) |
|
case PROC_DATA_LENGTH_UPDATE: |
|
llcp_lp_comm_rx(conn, ctx, rx); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */ |
|
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ) |
|
case PROC_CTE_REQ: |
|
llcp_lp_comm_rx(conn, ctx, rx); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ |
|
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) |
|
case PROC_CIS_TERMINATE: |
|
llcp_lp_comm_rx(conn, ctx, rx); |
|
break; |
|
#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ |
|
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) |
|
case PROC_CIS_CREATE: |
|
llcp_lp_cc_rx(conn, ctx, rx); |
|
break; |
|
#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) */ |
|
#if defined(CONFIG_BT_CTLR_SCA_UPDATE) |
|
case PROC_SCA_UPDATE: |
|
llcp_lp_comm_rx(conn, ctx, rx); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_SCA_UPDATE */ |
|
default: |
|
/* Unknown procedure */ |
|
LL_ASSERT(0); |
|
break; |
|
} |
|
|
|
/* If rx node was not retained clear reference */ |
|
if (ctx->node_ref.rx && ctx->node_ref.rx->hdr.type != NODE_RX_TYPE_RETAIN) { |
|
ctx->node_ref.rx = NULL; |
|
} |
|
|
|
llcp_lr_check_done(conn, ctx); |
|
} |
|
|
|
void llcp_lr_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, struct node_tx *tx) |
|
{ |
|
switch (ctx->proc) { |
|
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN) |
|
case PROC_MIN_USED_CHANS: |
|
llcp_lp_comm_tx_ack(conn, ctx, tx); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN */ |
|
case PROC_TERMINATE: |
|
llcp_lp_comm_tx_ack(conn, ctx, tx); |
|
break; |
|
#if defined(CONFIG_BT_CTLR_DATA_LENGTH) |
|
case PROC_DATA_LENGTH_UPDATE: |
|
llcp_lp_comm_tx_ack(conn, ctx, tx); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */ |
|
#ifdef CONFIG_BT_CTLR_PHY |
|
case PROC_PHY_UPDATE: |
|
llcp_lp_pu_tx_ack(conn, ctx, tx); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_PHY */ |
|
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) |
|
case PROC_CIS_TERMINATE: |
|
llcp_lp_comm_tx_ack(conn, ctx, tx); |
|
break; |
|
#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ |
|
#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) |
|
case PROC_PERIODIC_SYNC: |
|
llcp_lp_past_tx_ack(conn, ctx, tx); |
|
break; |
|
#endif /* defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) */ |
|
default: |
|
break; |
|
/* Ignore tx_ack */ |
|
} |
|
|
|
/* Clear TX node reference */ |
|
ctx->node_ref.tx_ack = NULL; |
|
|
|
llcp_lr_check_done(conn, ctx); |
|
} |
|
|
|
void llcp_lr_tx_ntf(struct ll_conn *conn, struct proc_ctx *ctx) |
|
{ |
|
switch (ctx->proc) { |
|
#if defined(CONFIG_BT_CTLR_PHY) |
|
case PROC_PHY_UPDATE: |
|
llcp_lp_pu_tx_ntf(conn, ctx); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_PHY */ |
|
default: |
|
/* Ignore other procedures */ |
|
break; |
|
} |
|
|
|
llcp_lr_check_done(conn, ctx); |
|
} |
|
|
|
static void lr_act_run(struct ll_conn *conn) |
|
{ |
|
struct proc_ctx *ctx; |
|
|
|
ctx = llcp_lr_peek(conn); |
|
|
|
switch (ctx->proc) { |
|
#if defined(CONFIG_BT_CTLR_LE_PING) |
|
case PROC_LE_PING: |
|
llcp_lp_comm_run(conn, ctx, NULL); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_LE_PING */ |
|
case PROC_FEATURE_EXCHANGE: |
|
llcp_lp_comm_run(conn, ctx, NULL); |
|
break; |
|
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN) |
|
case PROC_MIN_USED_CHANS: |
|
llcp_lp_comm_run(conn, ctx, NULL); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN */ |
|
case PROC_VERSION_EXCHANGE: |
|
llcp_lp_comm_run(conn, ctx, NULL); |
|
break; |
|
#if defined(CONFIG_BT_CTLR_LE_ENC) && defined(CONFIG_BT_CENTRAL) |
|
case PROC_ENCRYPTION_START: |
|
case PROC_ENCRYPTION_PAUSE: |
|
llcp_lp_enc_run(conn, ctx, NULL); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_LE_ENC && CONFIG_BT_CENTRAL */ |
|
#ifdef CONFIG_BT_CTLR_PHY |
|
case PROC_PHY_UPDATE: |
|
llcp_lp_pu_run(conn, ctx, NULL); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_PHY */ |
|
case PROC_CONN_UPDATE: |
|
case PROC_CONN_PARAM_REQ: |
|
llcp_lp_cu_run(conn, ctx, NULL); |
|
break; |
|
case PROC_TERMINATE: |
|
llcp_lp_comm_run(conn, ctx, NULL); |
|
break; |
|
#if defined(CONFIG_BT_CENTRAL) |
|
case PROC_CHAN_MAP_UPDATE: |
|
llcp_lp_chmu_run(conn, ctx, NULL); |
|
break; |
|
#endif /* CONFIG_BT_CENTRAL */ |
|
#if defined(CONFIG_BT_CTLR_DATA_LENGTH) |
|
case PROC_DATA_LENGTH_UPDATE: |
|
llcp_lp_comm_run(conn, ctx, NULL); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */ |
|
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ) |
|
case PROC_CTE_REQ: |
|
/* 3rd partam null? */ |
|
llcp_lp_comm_run(conn, ctx, NULL); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ |
|
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) |
|
case PROC_CIS_CREATE: |
|
llcp_lp_cc_run(conn, ctx, NULL); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_CENTRAL_ISO */ |
|
#if defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) |
|
case PROC_CIS_TERMINATE: |
|
llcp_lp_comm_run(conn, ctx, NULL); |
|
break; |
|
#endif /* defined(CONFIG_BT_CTLR_CENTRAL_ISO) || defined(CONFIG_BT_CTLR_PERIPHERAL_ISO) */ |
|
#if defined(CONFIG_BT_CTLR_SCA_UPDATE) |
|
case PROC_SCA_UPDATE: |
|
llcp_lp_comm_run(conn, ctx, NULL); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */ |
|
#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER) |
|
case PROC_PERIODIC_SYNC: |
|
llcp_lp_past_run(conn, ctx, NULL); |
|
break; |
|
#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */ |
|
default: |
|
/* Unknown procedure */ |
|
LL_ASSERT(0); |
|
break; |
|
} |
|
|
|
llcp_lr_check_done(conn, ctx); |
|
} |
|
|
|
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; |
|
} |
|
|
|
static void lr_act_connect(struct ll_conn *conn) |
|
{ |
|
/* Empty on purpose */ |
|
} |
|
|
|
static void lr_act_disconnect(struct ll_conn *conn) |
|
{ |
|
llcp_lr_flush_procedures(conn); |
|
} |
|
|
|
static void lr_st_disconnect(struct ll_conn *conn, uint8_t evt, void *param) |
|
{ |
|
switch (evt) { |
|
case LR_EVT_CONNECT: |
|
lr_act_connect(conn); |
|
lr_set_state(conn, LR_STATE_IDLE); |
|
break; |
|
default: |
|
/* Ignore other evts */ |
|
break; |
|
} |
|
} |
|
|
|
static void lr_st_idle(struct ll_conn *conn, uint8_t evt, void *param) |
|
{ |
|
struct proc_ctx *ctx; |
|
|
|
switch (evt) { |
|
case LR_EVT_RUN: |
|
ctx = llcp_lr_peek(conn); |
|
if (ctx) { |
|
/* |
|
* since the call to lr_act_run may release the context we need to remember |
|
* which procedure we are running |
|
*/ |
|
const enum llcp_proc curr_proc = ctx->proc; |
|
lr_act_run(conn); |
|
if (curr_proc != PROC_TERMINATE) { |
|
lr_set_state(conn, LR_STATE_ACTIVE); |
|
} else { |
|
lr_set_state(conn, LR_STATE_TERMINATE); |
|
} |
|
} |
|
break; |
|
case LR_EVT_DISCONNECT: |
|
lr_act_disconnect(conn); |
|
lr_set_state(conn, LR_STATE_DISCONNECT); |
|
break; |
|
case LR_EVT_COMPLETE: |
|
/* Some procedures like CTE request may be completed without actual run due to |
|
* change in conditions while the procedure was waiting in a queue. |
|
*/ |
|
lr_act_complete(conn); |
|
break; |
|
default: |
|
/* Ignore other evts */ |
|
break; |
|
} |
|
} |
|
|
|
static void lr_st_active(struct ll_conn *conn, uint8_t evt, void *param) |
|
{ |
|
switch (evt) { |
|
case LR_EVT_RUN: |
|
if (llcp_lr_peek(conn)) { |
|
lr_act_run(conn); |
|
} |
|
break; |
|
case LR_EVT_COMPLETE: |
|
lr_act_complete(conn); |
|
lr_set_state(conn, LR_STATE_IDLE); |
|
break; |
|
case LR_EVT_DISCONNECT: |
|
lr_act_disconnect(conn); |
|
lr_set_state(conn, LR_STATE_DISCONNECT); |
|
break; |
|
default: |
|
/* Ignore other evts */ |
|
break; |
|
} |
|
} |
|
|
|
static void lr_st_terminate(struct ll_conn *conn, uint8_t evt, void *param) |
|
{ |
|
switch (evt) { |
|
case LR_EVT_RUN: |
|
if (llcp_lr_peek(conn)) { |
|
lr_act_run(conn); |
|
} |
|
break; |
|
case LR_EVT_COMPLETE: |
|
lr_act_complete(conn); |
|
lr_set_state(conn, LR_STATE_IDLE); |
|
break; |
|
case LR_EVT_DISCONNECT: |
|
lr_act_disconnect(conn); |
|
lr_set_state(conn, LR_STATE_DISCONNECT); |
|
break; |
|
default: |
|
/* Ignore other evts */ |
|
break; |
|
} |
|
} |
|
|
|
static void lr_execute_fsm(struct ll_conn *conn, uint8_t evt, void *param) |
|
{ |
|
switch (conn->llcp.local.state) { |
|
case LR_STATE_DISCONNECT: |
|
lr_st_disconnect(conn, evt, param); |
|
break; |
|
case LR_STATE_IDLE: |
|
lr_st_idle(conn, evt, param); |
|
break; |
|
case LR_STATE_ACTIVE: |
|
lr_st_active(conn, evt, param); |
|
break; |
|
case LR_STATE_TERMINATE: |
|
lr_st_terminate(conn, evt, param); |
|
break; |
|
default: |
|
/* Unknown state */ |
|
LL_ASSERT(0); |
|
} |
|
} |
|
|
|
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) |
|
{ |
|
lr_execute_fsm(conn, LR_EVT_RUN, NULL); |
|
} |
|
|
|
void llcp_lr_complete(struct ll_conn *conn) |
|
{ |
|
lr_execute_fsm(conn, LR_EVT_COMPLETE, NULL); |
|
} |
|
|
|
void llcp_lr_connect(struct ll_conn *conn) |
|
{ |
|
lr_execute_fsm(conn, LR_EVT_CONNECT, NULL); |
|
} |
|
|
|
void llcp_lr_disconnect(struct ll_conn *conn) |
|
{ |
|
lr_execute_fsm(conn, LR_EVT_DISCONNECT, NULL); |
|
} |
|
|
|
void llcp_lr_terminate(struct ll_conn *conn) |
|
{ |
|
|
|
llcp_lr_flush_procedures(conn); |
|
llcp_lr_prt_stop(conn); |
|
llcp_rr_set_incompat(conn, 0U); |
|
lr_set_state(conn, LR_STATE_IDLE); |
|
} |
|
|
|
#ifdef ZTEST_UNITTEST |
|
|
|
bool llcp_lr_is_disconnected(struct ll_conn *conn) |
|
{ |
|
return conn->llcp.local.state == LR_STATE_DISCONNECT; |
|
} |
|
|
|
bool llcp_lr_is_idle(struct ll_conn *conn) |
|
{ |
|
return conn->llcp.local.state == LR_STATE_IDLE; |
|
} |
|
|
|
struct proc_ctx *llcp_lr_dequeue(struct ll_conn *conn) |
|
{ |
|
return lr_dequeue(conn); |
|
} |
|
|
|
#endif
|
|
|