| /* |
| * Copyright (c) 2020 Demant |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/types.h> |
| |
| #include <zephyr/bluetooth/hci.h> |
| #include <zephyr/sys/byteorder.h> |
| #include <zephyr/sys/slist.h> |
| #include <zephyr/sys/util.h> |
| |
| #include "hal/ccm.h" |
| |
| #include "util/util.h" |
| #include "util/mem.h" |
| #include "util/memq.h" |
| #include "util/dbuf.h" |
| |
| #include "pdu.h" |
| #include "ll.h" |
| #include "ll_feat.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_iso_types.h" |
| #include "ull_conn_iso_types.h" |
| #include "ull_conn_iso_internal.h" |
| |
| #include "ull_conn_internal.h" |
| #include "ull_conn_types.h" |
| |
| #if defined(CONFIG_BT_CTLR_USER_EXT) |
| #include "ull_vendor.h" |
| #endif /* CONFIG_BT_CTLR_USER_EXT */ |
| |
| #include "ull_internal.h" |
| #include "ull_llcp.h" |
| #include "ull_llcp_features.h" |
| #include "ull_llcp_internal.h" |
| |
| #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER) |
| #define LOG_MODULE_NAME bt_ctlr_ull_llcp_conn_upd |
| #include "common/log.h" |
| #include <soc.h> |
| #include "hal/debug.h" |
| |
| /* Hardcoded instant delta +6 */ |
| #define CONN_UPDATE_INSTANT_DELTA 6U |
| |
| /* CPR parameter ranges */ |
| #define CONN_UPDATE_TIMEOUT_100MS 10U |
| #define CONN_UPDATE_TIMEOUT_32SEC 3200U |
| #define CONN_UPDATE_LATENCY_MAX 499U |
| #define CONN_UPDATE_CONN_INTV_4SEC 3200U |
| |
| /* |
| * TODO - Known, missing items (missing implementation): |
| * |
| * If DLE procedure supported: |
| * and current PHY is Coded PHY: |
| * ... (5.3.6.B.5.1.1) the new connection interval shall be at least connIntervalCodedMin us. |
| * ... (5.3.6.B.5.1.7.4) packet tx time restrictions should be in effect |
| * |
| * Inter-connection mutual exclusion on CPR |
| * |
| * LL/CON/MAS/BV-34-C [Accepting Connection Parameter Request w. event masked] |
| */ |
| |
| /* LLCP Local Procedure Connection Update FSM states */ |
| enum { |
| LP_CU_STATE_IDLE, |
| LP_CU_STATE_WAIT_TX_CONN_PARAM_REQ, |
| LP_CU_STATE_WAIT_RX_CONN_PARAM_RSP, |
| LP_CU_STATE_WAIT_TX_CONN_UPDATE_IND, |
| LP_CU_STATE_WAIT_RX_CONN_UPDATE_IND, |
| LP_CU_STATE_WAIT_TX_REJECT_EXT_IND, |
| LP_CU_STATE_WAIT_INSTANT, |
| LP_CU_STATE_WAIT_NTF, |
| }; |
| |
| /* LLCP Local Procedure Connection Update FSM events */ |
| enum { |
| /* Procedure run */ |
| LP_CU_EVT_RUN, |
| |
| /* Response received */ |
| LP_CU_EVT_CONN_PARAM_RSP, |
| |
| /* Indication received */ |
| LP_CU_EVT_CONN_UPDATE_IND, |
| |
| /* Reject response received */ |
| LP_CU_EVT_REJECT, |
| |
| /* Unknown response received */ |
| LP_CU_EVT_UNKNOWN, |
| }; |
| |
| /* LLCP Remote Procedure Connection Update FSM states */ |
| enum { |
| RP_CU_STATE_IDLE, |
| RP_CU_STATE_WAIT_RX_CONN_PARAM_REQ, |
| RP_CU_STATE_WAIT_CONN_PARAM_REQ_AVAILABLE, |
| RP_CU_STATE_WAIT_NTF_CONN_PARAM_REQ, |
| RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY, |
| RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY_CONTINUE, |
| RP_CU_STATE_WAIT_TX_REJECT_EXT_IND, |
| RP_CU_STATE_WAIT_TX_CONN_PARAM_RSP, |
| RP_CU_STATE_WAIT_TX_CONN_UPDATE_IND, |
| RP_CU_STATE_WAIT_RX_CONN_UPDATE_IND, |
| RP_CU_STATE_WAIT_INSTANT, |
| RP_CU_STATE_WAIT_NTF, |
| RP_CU_STATE_WAIT_TX_UNKNOWN_RSP |
| }; |
| |
| /* LLCP Remote Procedure Connection Update FSM events */ |
| enum { |
| /* Procedure run */ |
| RP_CU_EVT_RUN, |
| |
| /* Request received */ |
| RP_CU_EVT_CONN_PARAM_REQ, |
| |
| /* Indication received */ |
| RP_CU_EVT_CONN_UPDATE_IND, |
| |
| /* CONN_PARAM_REQ reply */ |
| RP_CU_EVT_CONN_PARAM_REQ_REPLY, |
| |
| /* CONN_PARAM_REQ negative reply */ |
| RP_CU_EVT_CONN_PARAM_REQ_NEG_REPLY, |
| }; |
| |
| /* |
| * LLCP Local Procedure Connection Update FSM |
| */ |
| |
| static bool cu_have_params_changed(struct ll_conn *conn, uint16_t interval, uint16_t latency, |
| uint16_t timeout) |
| { |
| struct lll_conn *lll = &conn->lll; |
| |
| if ((interval != lll->interval) || (latency != lll->latency) || |
| (RADIO_CONN_EVENTS(timeout * 10000U, lll->interval * CONN_INT_UNIT_US) != |
| conn->supervision_reload)) { |
| return true; |
| } |
| return false; |
| } |
| |
| static void cu_update_conn_parameters(struct ll_conn *conn, struct proc_ctx *ctx) |
| { |
| ctx->data.cu.params_changed = cu_have_params_changed( |
| conn, ctx->data.cu.interval_max, ctx->data.cu.latency, ctx->data.cu.timeout); |
| |
| ull_conn_update_parameters(conn, (ctx->proc == PROC_CONN_UPDATE), ctx->data.cu.win_size, |
| ctx->data.cu.win_offset_us, ctx->data.cu.interval_max, |
| ctx->data.cu.latency, ctx->data.cu.timeout, |
| ctx->data.cu.instant); |
| } |
| |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| static bool cu_check_conn_parameters(struct ll_conn *conn, struct proc_ctx *ctx) |
| { |
| const uint16_t interval_min = ctx->data.cu.interval_min; |
| const uint16_t interval_max = ctx->data.cu.interval_max; /* unit conn events (ie 1.25ms) */ |
| const uint16_t timeout = ctx->data.cu.timeout; /* unit 10ms */ |
| const uint16_t latency = ctx->data.cu.latency; |
| const uint16_t preferred_periodicity = ctx->data.cu.preferred_periodicity; |
| |
| /* Invalid parameters */ |
| const bool invalid = ((interval_min < CONN_INTERVAL_MIN(conn)) || |
| (interval_max > CONN_UPDATE_CONN_INTV_4SEC) || |
| (interval_min > interval_max) || |
| (latency > CONN_UPDATE_LATENCY_MAX) || |
| (timeout < CONN_UPDATE_TIMEOUT_100MS) || (timeout > CONN_UPDATE_TIMEOUT_32SEC) || |
| ((timeout * 4U) <= /* *4U re. conn events is equivalent to *2U re. ms */ |
| ((latency + 1) * interval_max)) || |
| (preferred_periodicity > interval_max)); |
| |
| return !invalid; |
| } |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| |
| static void cu_prepare_update_ind(struct ll_conn *conn, struct proc_ctx *ctx) |
| { |
| ctx->data.cu.win_size = 1U; |
| ctx->data.cu.win_offset_us = 0U; |
| |
| |
| ctx->data.cu.instant = ull_conn_event_counter(conn) + conn->lll.latency + |
| CONN_UPDATE_INSTANT_DELTA; |
| } |
| |
| static bool cu_should_notify_host(struct proc_ctx *ctx) |
| { |
| return (((ctx->proc == PROC_CONN_PARAM_REQ) && (ctx->data.cu.error != 0U)) || |
| (ctx->data.cu.params_changed != 0U)); |
| } |
| |
| #if defined(CONFIG_BT_CENTRAL) || defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| static void lp_cu_tx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t opcode) |
| { |
| struct node_tx *tx; |
| struct pdu_data *pdu; |
| |
| /* Allocate tx node */ |
| tx = llcp_tx_alloc(conn, ctx); |
| LL_ASSERT(tx); |
| |
| pdu = (struct pdu_data *)tx->pdu; |
| |
| /* Encode LL Control PDU */ |
| switch (opcode) { |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| case PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ: |
| llcp_pdu_encode_conn_param_req(ctx, pdu); |
| break; |
| case PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND: |
| llcp_pdu_encode_reject_ext_ind(pdu, ctx->data.cu.rejected_opcode, |
| ctx->data.cu.error); |
| break; |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| #if defined(CONFIG_BT_CENTRAL) |
| case PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND: |
| llcp_pdu_encode_conn_update_ind(ctx, pdu); |
| break; |
| #endif /* CONFIG_BT_CENTRAL */ |
| default: |
| /* Unknown opcode */ |
| LL_ASSERT(0); |
| break; |
| } |
| |
| ctx->tx_opcode = pdu->llctrl.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 */ |
| } |
| #endif /* CONFIG_BT_CENTRAL || CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| |
| static void lp_cu_ntf(struct ll_conn *conn, struct proc_ctx *ctx) |
| { |
| struct node_rx_pdu *ntf; |
| struct node_rx_cu *pdu; |
| |
| /* Allocate ntf node */ |
| ntf = llcp_ntf_alloc(); |
| LL_ASSERT(ntf); |
| |
| ntf->hdr.type = NODE_RX_TYPE_CONN_UPDATE; |
| ntf->hdr.handle = conn->lll.handle; |
| pdu = (struct node_rx_cu *)ntf->pdu; |
| |
| pdu->status = ctx->data.cu.error; |
| pdu->interval = ctx->data.cu.interval_max; |
| pdu->latency = ctx->data.cu.latency; |
| pdu->timeout = ctx->data.cu.timeout; |
| |
| /* Enqueue notification towards LL */ |
| ll_rx_put(ntf->hdr.link, ntf); |
| ll_rx_sched(); |
| } |
| |
| static void lp_cu_complete(struct ll_conn *conn, struct proc_ctx *ctx) |
| { |
| llcp_lr_complete(conn); |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| if (ctx->proc == PROC_CONN_PARAM_REQ && |
| !(conn->lll.role && ull_cp_remote_cpr_pending(conn))) { |
| /* For a peripheral without a remote initiated CPR */ |
| cpr_active_check_and_reset(conn); |
| } |
| #endif /* defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) */ |
| ctx->state = LP_CU_STATE_IDLE; |
| } |
| |
| static void lp_cu_wait_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| if (!llcp_ntf_alloc_is_available()) { |
| ctx->state = LP_CU_STATE_WAIT_NTF; |
| } else { |
| lp_cu_ntf(conn, ctx); |
| lp_cu_complete(conn, ctx); |
| } |
| } |
| |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| static void lp_cu_send_reject_ext_ind(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| if (llcp_lr_ispaused(conn) || !llcp_tx_alloc_peek(conn, ctx)) { |
| ctx->state = LP_CU_STATE_WAIT_TX_REJECT_EXT_IND; |
| } else { |
| llcp_rr_set_incompat(conn, INCOMPAT_NO_COLLISION); |
| lp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND); |
| lp_cu_complete(conn, ctx); |
| } |
| } |
| |
| static void lp_cu_send_conn_param_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| if (cpr_active_is_set(conn) || llcp_lr_ispaused(conn) || |
| llcp_rr_get_collision(conn) || !llcp_tx_alloc_peek(conn, ctx)) { |
| ctx->state = LP_CU_STATE_WAIT_TX_CONN_PARAM_REQ; |
| } else { |
| uint16_t event_counter = ull_conn_event_counter(conn); |
| |
| llcp_rr_set_incompat(conn, INCOMPAT_RESOLVABLE); |
| |
| ctx->data.cu.reference_conn_event_count = event_counter; |
| ctx->data.cu.preferred_periodicity = 0U; |
| ctx->data.cu.offset0 = 0x0000U; |
| ctx->data.cu.offset1 = 0xffffU; |
| ctx->data.cu.offset2 = 0xffffU; |
| ctx->data.cu.offset3 = 0xffffU; |
| ctx->data.cu.offset4 = 0xffffU; |
| ctx->data.cu.offset5 = 0xffffU; |
| |
| /* Mark CPR as active */ |
| cpr_active_set(conn); |
| |
| lp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ); |
| |
| switch (conn->lll.role) { |
| #if defined(CONFIG_BT_CENTRAL) |
| case BT_HCI_ROLE_CENTRAL: |
| ctx->state = LP_CU_STATE_WAIT_RX_CONN_PARAM_RSP; |
| ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP; |
| break; |
| #endif /* CONFIG_BT_CENTRAL */ |
| #if defined(CONFIG_BT_PERIPHERAL) |
| case BT_HCI_ROLE_PERIPHERAL: |
| ctx->state = LP_CU_STATE_WAIT_RX_CONN_UPDATE_IND; |
| ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND; |
| break; |
| #endif /* CONFIG_BT_PERIPHERAL */ |
| default: |
| /* Unknown role */ |
| LL_ASSERT(0); |
| break; |
| } |
| } |
| } |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| |
| #if defined(CONFIG_BT_CENTRAL) |
| static void lp_cu_send_conn_update_ind(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| if (llcp_lr_ispaused(conn) || !llcp_tx_alloc_peek(conn, ctx)) { |
| ctx->state = LP_CU_STATE_WAIT_TX_CONN_UPDATE_IND; |
| } else { |
| cu_prepare_update_ind(conn, ctx); |
| lp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND); |
| ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_UNUSED; |
| ctx->state = LP_CU_STATE_WAIT_INSTANT; |
| } |
| } |
| #endif /* CONFIG_BT_CENTRAL */ |
| |
| static void lp_cu_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param) |
| { |
| switch (evt) { |
| case LP_CU_EVT_RUN: |
| switch (ctx->proc) { |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| case PROC_CONN_PARAM_REQ: |
| lp_cu_send_conn_param_req(conn, ctx, evt, param); |
| break; |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| #if defined(CONFIG_BT_CENTRAL) |
| case PROC_CONN_UPDATE: |
| lp_cu_send_conn_update_ind(conn, ctx, evt, param); |
| break; |
| #endif /* CONFIG_BT_CENTRAL */ |
| default: |
| /* Unknown procedure */ |
| LL_ASSERT(0); |
| break; |
| } |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| static void lp_cu_st_wait_tx_reject_ext_ind(struct ll_conn *conn, struct proc_ctx *ctx, |
| uint8_t evt, void *param) |
| { |
| switch (evt) { |
| case LP_CU_EVT_RUN: |
| lp_cu_send_reject_ext_ind(conn, ctx, evt, param); |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| |
| |
| static void lp_cu_st_wait_tx_conn_param_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| switch (evt) { |
| case LP_CU_EVT_RUN: |
| lp_cu_send_conn_param_req(conn, ctx, evt, param); |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| |
| #if defined(CONFIG_BT_CENTRAL) |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| static void lp_cu_st_wait_rx_conn_param_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| struct pdu_data *pdu = (struct pdu_data *)param; |
| |
| switch (evt) { |
| case LP_CU_EVT_CONN_PARAM_RSP: |
| llcp_pdu_decode_conn_param_rsp(ctx, param); |
| llcp_rr_set_incompat(conn, INCOMPAT_RESERVED); |
| /* Perform Param check and possibly reject (LL_REJECT_EXT_IND) */ |
| if (!cu_check_conn_parameters(conn, ctx)) { |
| ctx->data.cu.rejected_opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP; |
| ctx->data.cu.error = BT_HCI_ERR_INVALID_LL_PARAM; |
| lp_cu_send_reject_ext_ind(conn, ctx, evt, param); |
| break; |
| } |
| lp_cu_send_conn_update_ind(conn, ctx, evt, param); |
| break; |
| case LP_CU_EVT_UNKNOWN: |
| llcp_rr_set_incompat(conn, INCOMPAT_RESERVED); |
| /* Unsupported in peer, so disable locally for this connection */ |
| feature_unmask_features(conn, LL_FEAT_BIT_CONN_PARAM_REQ); |
| lp_cu_send_conn_update_ind(conn, ctx, evt, param); |
| break; |
| case LP_CU_EVT_REJECT: |
| if (pdu->llctrl.reject_ext_ind.error_code == BT_HCI_ERR_UNSUPP_REMOTE_FEATURE) { |
| /* Remote legacy Host */ |
| llcp_rr_set_incompat(conn, INCOMPAT_RESERVED); |
| /* Unsupported in peer, so disable locally for this connection */ |
| feature_unmask_features(conn, LL_FEAT_BIT_CONN_PARAM_REQ); |
| lp_cu_send_conn_update_ind(conn, ctx, evt, param); |
| } else { |
| llcp_rr_set_incompat(conn, INCOMPAT_NO_COLLISION); |
| ctx->data.cu.error = pdu->llctrl.reject_ext_ind.error_code; |
| lp_cu_wait_complete(conn, ctx, evt, param); |
| } |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| |
| static void lp_cu_st_wait_tx_conn_update_ind(struct ll_conn *conn, struct proc_ctx *ctx, |
| uint8_t evt, void *param) |
| { |
| switch (evt) { |
| case LP_CU_EVT_RUN: |
| lp_cu_send_conn_update_ind(conn, ctx, evt, param); |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| #endif /* CONFIG_BT_CENTRAL */ |
| |
| #if defined(CONFIG_BT_PERIPHERAL) |
| static void lp_cu_st_wait_rx_conn_update_ind(struct ll_conn *conn, struct proc_ctx *ctx, |
| uint8_t evt, void *param) |
| { |
| struct pdu_data *pdu = (struct pdu_data *)param; |
| |
| switch (evt) { |
| case LP_CU_EVT_CONN_UPDATE_IND: |
| llcp_pdu_decode_conn_update_ind(ctx, param); |
| ctx->state = LP_CU_STATE_WAIT_INSTANT; |
| break; |
| case LP_CU_EVT_UNKNOWN: |
| /* Unsupported in peer, so disable locally for this connection */ |
| feature_unmask_features(conn, LL_FEAT_BIT_CONN_PARAM_REQ); |
| ctx->data.cu.error = BT_HCI_ERR_UNSUPP_REMOTE_FEATURE; |
| lp_cu_wait_complete(conn, ctx, evt, param); |
| break; |
| case LP_CU_EVT_REJECT: |
| ctx->data.cu.error = pdu->llctrl.reject_ext_ind.error_code; |
| lp_cu_wait_complete(conn, ctx, evt, param); |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| #endif /* CONFIG_BT_PERIPHERAL */ |
| |
| static void lp_cu_check_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| uint16_t event_counter = ull_conn_event_counter(conn); |
| |
| if (is_instant_reached_or_passed(ctx->data.cu.instant, event_counter)) { |
| bool notify; |
| |
| /* Procedure is complete when the instant has passed, and the |
| * new connection event parameters have been applied. |
| */ |
| 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; |
| lp_cu_wait_complete(conn, ctx, evt, param); |
| } else { |
| lp_cu_complete(conn, ctx); |
| } |
| } |
| } |
| |
| static void lp_cu_st_wait_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| switch (evt) { |
| case LP_CU_EVT_RUN: |
| lp_cu_check_instant(conn, ctx, evt, param); |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| |
| static void lp_cu_st_wait_ntf(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param) |
| { |
| switch (evt) { |
| case LP_CU_EVT_RUN: |
| lp_cu_wait_complete(conn, ctx, evt, param); |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| |
| static void lp_cu_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param) |
| { |
| switch (ctx->state) { |
| case LP_CU_STATE_IDLE: |
| lp_cu_st_idle(conn, ctx, evt, param); |
| break; |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| case LP_CU_STATE_WAIT_TX_CONN_PARAM_REQ: |
| lp_cu_st_wait_tx_conn_param_req(conn, ctx, evt, param); |
| break; |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| #if defined(CONFIG_BT_CENTRAL) |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| case LP_CU_STATE_WAIT_RX_CONN_PARAM_RSP: |
| lp_cu_st_wait_rx_conn_param_rsp(conn, ctx, evt, param); |
| break; |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| case LP_CU_STATE_WAIT_TX_CONN_UPDATE_IND: |
| lp_cu_st_wait_tx_conn_update_ind(conn, ctx, evt, param); |
| break; |
| #endif /* CONFIG_BT_CENTRAL */ |
| #if defined(CONFIG_BT_PERIPHERAL) |
| case LP_CU_STATE_WAIT_RX_CONN_UPDATE_IND: |
| lp_cu_st_wait_rx_conn_update_ind(conn, ctx, evt, param); |
| break; |
| #endif /* CONFIG_BT_PERIPHERAL */ |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| case LP_CU_STATE_WAIT_TX_REJECT_EXT_IND: |
| lp_cu_st_wait_tx_reject_ext_ind(conn, ctx, evt, param); |
| break; |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| case LP_CU_STATE_WAIT_INSTANT: |
| lp_cu_st_wait_instant(conn, ctx, evt, param); |
| break; |
| case LP_CU_STATE_WAIT_NTF: |
| lp_cu_st_wait_ntf(conn, ctx, evt, param); |
| break; |
| default: |
| /* Unknown state */ |
| LL_ASSERT(0); |
| break; |
| } |
| } |
| |
| void llcp_lp_cu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx) |
| { |
| struct pdu_data *pdu = (struct pdu_data *)rx->pdu; |
| |
| switch (pdu->llctrl.opcode) { |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| case PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP: |
| lp_cu_execute_fsm(conn, ctx, LP_CU_EVT_CONN_PARAM_RSP, pdu); |
| break; |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| case PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND: |
| lp_cu_execute_fsm(conn, ctx, LP_CU_EVT_CONN_UPDATE_IND, pdu); |
| break; |
| case PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP: |
| lp_cu_execute_fsm(conn, ctx, LP_CU_EVT_UNKNOWN, pdu); |
| break; |
| case PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND: |
| lp_cu_execute_fsm(conn, ctx, LP_CU_EVT_REJECT, pdu); |
| break; |
| default: |
| /* Invalid behaviour */ |
| /* Invalid PDU received so terminate connection */ |
| conn->llcp_terminate.reason_final = BT_HCI_ERR_LMP_PDU_NOT_ALLOWED; |
| lp_cu_complete(conn, ctx); |
| break; |
| } |
| } |
| |
| void llcp_lp_cu_init_proc(struct proc_ctx *ctx) |
| { |
| ctx->state = LP_CU_STATE_IDLE; |
| } |
| |
| void llcp_lp_cu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param) |
| { |
| lp_cu_execute_fsm(conn, ctx, LP_CU_EVT_RUN, param); |
| } |
| |
| /* |
| * LLCP Remote Procedure Connection Update FSM |
| */ |
| |
| static void rp_cu_tx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t opcode) |
| { |
| struct node_tx *tx; |
| struct pdu_data *pdu; |
| |
| /* Allocate tx node */ |
| tx = llcp_tx_alloc(conn, ctx); |
| LL_ASSERT(tx); |
| |
| pdu = (struct pdu_data *)tx->pdu; |
| |
| /* Encode LL Control PDU */ |
| switch (opcode) { |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| case PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP: |
| llcp_pdu_encode_conn_param_rsp(ctx, pdu); |
| break; |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| case PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND: |
| llcp_pdu_encode_conn_update_ind(ctx, pdu); |
| break; |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| case PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND: |
| llcp_pdu_encode_reject_ext_ind(pdu, ctx->data.cu.rejected_opcode, |
| ctx->data.cu.error); |
| break; |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| case PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP: |
| llcp_pdu_encode_unknown_rsp(ctx, pdu); |
| break; |
| default: |
| /* Unknown opcode */ |
| LL_ASSERT(0); |
| break; |
| } |
| |
| ctx->tx_opcode = pdu->llctrl.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) |
| { |
| struct node_rx_pdu *ntf; |
| struct node_rx_cu *pdu; |
| |
| /* Allocate ntf node */ |
| ntf = llcp_ntf_alloc(); |
| LL_ASSERT(ntf); |
| |
| ntf->hdr.type = NODE_RX_TYPE_CONN_UPDATE; |
| ntf->hdr.handle = conn->lll.handle; |
| pdu = (struct node_rx_cu *)ntf->pdu; |
| |
| pdu->status = ctx->data.cu.error; |
| pdu->interval = ctx->data.cu.interval_max; |
| pdu->latency = ctx->data.cu.latency; |
| pdu->timeout = ctx->data.cu.timeout; |
| |
| /* Enqueue notification towards LL */ |
| ll_rx_put(ntf->hdr.link, ntf); |
| ll_rx_sched(); |
| } |
| |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| static void rp_cu_conn_param_req_ntf(struct ll_conn *conn, struct proc_ctx *ctx) |
| { |
| struct node_rx_pdu *ntf; |
| struct pdu_data *pdu; |
| |
| /* Allocate ntf node */ |
| ntf = llcp_ntf_alloc(); |
| LL_ASSERT(ntf); |
| |
| ntf->hdr.type = NODE_RX_TYPE_DC_PDU; |
| ntf->hdr.handle = conn->lll.handle; |
| pdu = (struct pdu_data *)ntf->pdu; |
| |
| llcp_pdu_encode_conn_param_req(ctx, pdu); |
| |
| /* Enqueue notification towards LL */ |
| ll_rx_put(ntf->hdr.link, ntf); |
| ll_rx_sched(); |
| } |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| |
| static void rp_cu_complete(struct ll_conn *conn, struct proc_ctx *ctx) |
| { |
| llcp_rr_complete(conn); |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| if (ctx->proc == PROC_CONN_PARAM_REQ) { |
| cpr_active_check_and_reset(conn); |
| } |
| #endif /* defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) */ |
| ctx->state = RP_CU_STATE_IDLE; |
| } |
| |
| static void rp_cu_wait_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| if (!llcp_ntf_alloc_is_available()) { |
| ctx->state = RP_CU_STATE_WAIT_NTF; |
| } else { |
| rp_cu_ntf(conn, ctx); |
| rp_cu_complete(conn, ctx); |
| } |
| } |
| |
| static void rp_cu_send_conn_update_ind(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| if (llcp_rr_ispaused(conn) || !llcp_tx_alloc_peek(conn, ctx)) { |
| ctx->state = RP_CU_STATE_WAIT_TX_CONN_UPDATE_IND; |
| } else { |
| cu_prepare_update_ind(conn, ctx); |
| rp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND); |
| ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_UNUSED; |
| ctx->state = RP_CU_STATE_WAIT_INSTANT; |
| } |
| } |
| |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| static void rp_cu_send_reject_ext_ind(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| if (llcp_rr_ispaused(conn) || !llcp_tx_alloc_peek(conn, ctx)) { |
| ctx->state = RP_CU_STATE_WAIT_TX_REJECT_EXT_IND; |
| } else { |
| rp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND); |
| rp_cu_complete(conn, ctx); |
| } |
| } |
| |
| static void rp_cu_send_conn_param_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| if (llcp_rr_ispaused(conn) || !llcp_tx_alloc_peek(conn, ctx)) { |
| ctx->state = RP_CU_STATE_WAIT_TX_CONN_PARAM_RSP; |
| } else { |
| rp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP); |
| ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND; |
| ctx->state = RP_CU_STATE_WAIT_RX_CONN_UPDATE_IND; |
| } |
| } |
| |
| static void rp_cu_send_conn_param_req_ntf(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| if (!llcp_ntf_alloc_is_available()) { |
| ctx->state = RP_CU_STATE_WAIT_NTF_CONN_PARAM_REQ; |
| } else { |
| rp_cu_conn_param_req_ntf(conn, ctx); |
| ctx->state = RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY; |
| } |
| } |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| |
| static void rp_cu_send_unknown_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| if (llcp_rr_ispaused(conn) || !llcp_tx_alloc_peek(conn, ctx)) { |
| ctx->state = RP_CU_STATE_WAIT_TX_UNKNOWN_RSP; |
| } else { |
| rp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP); |
| rp_cu_complete(conn, ctx); |
| } |
| } |
| |
| static void rp_cu_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param) |
| { |
| switch (evt) { |
| case RP_CU_EVT_RUN: |
| switch (ctx->proc) { |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| case PROC_CONN_PARAM_REQ: |
| ctx->state = RP_CU_STATE_WAIT_RX_CONN_PARAM_REQ; |
| break; |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| case PROC_CONN_UPDATE: |
| ctx->state = RP_CU_STATE_WAIT_RX_CONN_UPDATE_IND; |
| break; |
| default: |
| /* Unknown procedure */ |
| LL_ASSERT(0); |
| break; |
| } |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| static void rp_cu_st_wait_conn_param_req_available(struct ll_conn *conn, struct proc_ctx *ctx, |
| uint8_t evt, void *param) |
| { |
| /* Check if CPR is already active on other connection. |
| * If so check if possible to send reject right away |
| * otherwise stay in wait state in case CPR becomes |
| * available before we can send send reject |
| */ |
| switch (evt) { |
| case RP_CU_EVT_CONN_PARAM_REQ: |
| case RP_CU_EVT_RUN: |
| if (cpr_active_is_set(conn)) { |
| ctx->state = RP_CU_STATE_WAIT_CONN_PARAM_REQ_AVAILABLE; |
| if (!llcp_rr_ispaused(conn) && llcp_tx_alloc_peek(conn, ctx)) { |
| /* We're good to reject immediately */ |
| ctx->data.cu.rejected_opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ; |
| ctx->data.cu.error = BT_HCI_ERR_UNSUPP_LL_PARAM_VAL; |
| rp_cu_send_reject_ext_ind(conn, ctx, evt, param); |
| } |
| } else { |
| cpr_active_set(conn); |
| const bool params_changed = |
| cu_have_params_changed(conn, ctx->data.cu.interval_max, |
| ctx->data.cu.latency, ctx->data.cu.timeout); |
| |
| /* notify Host if conn parameters changed, else respond */ |
| if (params_changed) { |
| rp_cu_send_conn_param_req_ntf(conn, ctx, evt, param); |
| } else { |
| ctx->state = RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY_CONTINUE; |
| } |
| } |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| |
| static void rp_cu_st_wait_rx_conn_param_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| switch (evt) { |
| case RP_CU_EVT_CONN_PARAM_REQ: |
| llcp_pdu_decode_conn_param_req(ctx, param); |
| |
| /* Perform Param check and reject if invalid (LL_REJECT_EXT_IND) */ |
| if (!cu_check_conn_parameters(conn, ctx)) { |
| ctx->data.cu.rejected_opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ; |
| ctx->data.cu.error = BT_HCI_ERR_INVALID_LL_PARAM; |
| rp_cu_send_reject_ext_ind(conn, ctx, evt, param); |
| break; |
| } |
| |
| rp_cu_st_wait_conn_param_req_available(conn, ctx, evt, param); |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| |
| static void rp_cu_state_wait_ntf_conn_param_req(struct ll_conn *conn, struct proc_ctx *ctx, |
| uint8_t evt, void *param) |
| { |
| switch (evt) { |
| case RP_CU_EVT_RUN: |
| rp_cu_send_conn_param_req_ntf(conn, ctx, evt, param); |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| |
| static void rp_cu_state_wait_conn_param_req_reply(struct ll_conn *conn, struct proc_ctx *ctx, |
| uint8_t evt, void *param) |
| { |
| switch (evt) { |
| case RP_CU_EVT_CONN_PARAM_REQ_REPLY: |
| /* Continue procedure in next prepare run */ |
| ctx->state = RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY_CONTINUE; |
| break; |
| case RP_CU_EVT_CONN_PARAM_REQ_NEG_REPLY: |
| /* Send reject in next prepare run */ |
| ctx->data.cu.rejected_opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ; |
| ctx->state = RP_CU_STATE_WAIT_TX_REJECT_EXT_IND; |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| |
| static void rp_cu_state_wait_conn_param_req_reply_continue(struct ll_conn *conn, |
| struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| switch (evt) { |
| case RP_CU_EVT_RUN: |
| if (conn->lll.role == BT_HCI_ROLE_CENTRAL) { |
| rp_cu_send_conn_update_ind(conn, ctx, evt, param); |
| } else if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL) { |
| rp_cu_send_conn_param_rsp(conn, ctx, evt, param); |
| } else { |
| /* Unknown role */ |
| LL_ASSERT(0); |
| } |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| |
| static void rp_cu_state_wait_tx_reject_ext_ind(struct ll_conn *conn, struct proc_ctx *ctx, |
| uint8_t evt, void *param) |
| { |
| switch (evt) { |
| case RP_CU_EVT_RUN: |
| rp_cu_send_reject_ext_ind(conn, ctx, evt, param); |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| |
| static void rp_cu_st_wait_tx_conn_param_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| switch (evt) { |
| case RP_CU_EVT_RUN: |
| rp_cu_send_conn_param_rsp(conn, ctx, evt, param); |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| |
| static void rp_cu_st_wait_tx_conn_update_ind(struct ll_conn *conn, struct proc_ctx *ctx, |
| uint8_t evt, void *param) |
| { |
| switch (evt) { |
| case RP_CU_EVT_RUN: |
| rp_cu_send_conn_update_ind(conn, ctx, evt, param); |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| |
| static void rp_cu_check_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| uint16_t event_counter = ull_conn_event_counter(conn); |
| |
| if (is_instant_reached_or_passed(ctx->data.cu.instant, event_counter)) { |
| bool notify; |
| |
| /* Procedure is complete when the instant has passed, and the |
| * 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; |
| rp_cu_wait_complete(conn, ctx, evt, param); |
| } else { |
| rp_cu_complete(conn, ctx); |
| } |
| } |
| } |
| |
| static void rp_cu_st_wait_rx_conn_update_ind(struct ll_conn *conn, struct proc_ctx *ctx, |
| uint8_t evt, void *param) |
| { |
| switch (evt) { |
| case RP_CU_EVT_CONN_UPDATE_IND: |
| switch (conn->lll.role) { |
| case BT_HCI_ROLE_CENTRAL: |
| ctx->unknown_response.type = PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND; |
| rp_cu_send_unknown_rsp(conn, ctx, evt, param); |
| break; |
| case BT_HCI_ROLE_PERIPHERAL: |
| llcp_pdu_decode_conn_update_ind(ctx, param); |
| |
| if (is_instant_not_passed(ctx->data.cu.instant, |
| ull_conn_event_counter(conn))) { |
| |
| ctx->state = RP_CU_STATE_WAIT_INSTANT; |
| /* In case we only just received it in time */ |
| rp_cu_check_instant(conn, ctx, evt, param); |
| } else { |
| conn->llcp_terminate.reason_final = BT_HCI_ERR_INSTANT_PASSED; |
| llcp_rr_complete(conn); |
| ctx->state = RP_CU_STATE_IDLE; |
| } |
| break; |
| default: |
| /* Unknown role */ |
| LL_ASSERT(0); |
| } |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| |
| static void rp_cu_st_wait_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, |
| void *param) |
| { |
| switch (evt) { |
| case RP_CU_EVT_RUN: |
| rp_cu_check_instant(conn, ctx, evt, param); |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| |
| static void rp_cu_st_wait_ntf(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param) |
| { |
| switch (evt) { |
| case RP_CU_EVT_RUN: |
| rp_cu_wait_complete(conn, ctx, evt, param); |
| break; |
| default: |
| /* Ignore other evts */ |
| break; |
| } |
| } |
| |
| static void rp_cu_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param) |
| { |
| switch (ctx->state) { |
| case RP_CU_STATE_IDLE: |
| rp_cu_st_idle(conn, ctx, evt, param); |
| break; |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| case RP_CU_STATE_WAIT_RX_CONN_PARAM_REQ: |
| rp_cu_st_wait_rx_conn_param_req(conn, ctx, evt, param); |
| break; |
| case RP_CU_STATE_WAIT_CONN_PARAM_REQ_AVAILABLE: |
| rp_cu_st_wait_conn_param_req_available(conn, ctx, evt, param); |
| break; |
| case RP_CU_STATE_WAIT_NTF_CONN_PARAM_REQ: |
| rp_cu_state_wait_ntf_conn_param_req(conn, ctx, evt, param); |
| break; |
| case RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY: |
| rp_cu_state_wait_conn_param_req_reply(conn, ctx, evt, param); |
| break; |
| case RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY_CONTINUE: |
| rp_cu_state_wait_conn_param_req_reply_continue(conn, ctx, evt, param); |
| break; |
| case RP_CU_STATE_WAIT_TX_REJECT_EXT_IND: |
| rp_cu_state_wait_tx_reject_ext_ind(conn, ctx, evt, param); |
| break; |
| case RP_CU_STATE_WAIT_TX_CONN_PARAM_RSP: |
| rp_cu_st_wait_tx_conn_param_rsp(conn, ctx, evt, param); |
| break; |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| case RP_CU_STATE_WAIT_TX_CONN_UPDATE_IND: |
| rp_cu_st_wait_tx_conn_update_ind(conn, ctx, evt, param); |
| break; |
| case RP_CU_STATE_WAIT_RX_CONN_UPDATE_IND: |
| rp_cu_st_wait_rx_conn_update_ind(conn, ctx, evt, param); |
| break; |
| case RP_CU_STATE_WAIT_INSTANT: |
| rp_cu_st_wait_instant(conn, ctx, evt, param); |
| break; |
| case RP_CU_STATE_WAIT_NTF: |
| rp_cu_st_wait_ntf(conn, ctx, evt, param); |
| break; |
| default: |
| /* Unknown state */ |
| LL_ASSERT(0); |
| break; |
| } |
| } |
| |
| void llcp_rp_cu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx) |
| { |
| struct pdu_data *pdu = (struct pdu_data *)rx->pdu; |
| |
| switch (pdu->llctrl.opcode) { |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| case PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ: |
| rp_cu_execute_fsm(conn, ctx, RP_CU_EVT_CONN_PARAM_REQ, pdu); |
| break; |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |
| case PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND: |
| rp_cu_execute_fsm(conn, ctx, RP_CU_EVT_CONN_UPDATE_IND, pdu); |
| break; |
| default: |
| /* Invalid behaviour */ |
| /* Invalid PDU received so terminate connection */ |
| conn->llcp_terminate.reason_final = BT_HCI_ERR_LMP_PDU_NOT_ALLOWED; |
| rp_cu_complete(conn, ctx); |
| break; |
| } |
| } |
| |
| void llcp_rp_cu_init_proc(struct proc_ctx *ctx) |
| { |
| ctx->state = RP_CU_STATE_IDLE; |
| } |
| |
| void llcp_rp_cu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param) |
| { |
| rp_cu_execute_fsm(conn, ctx, RP_CU_EVT_RUN, param); |
| } |
| |
| #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) |
| void llcp_rp_conn_param_req_reply(struct ll_conn *conn, struct proc_ctx *ctx) |
| { |
| rp_cu_execute_fsm(conn, ctx, RP_CU_EVT_CONN_PARAM_REQ_REPLY, NULL); |
| } |
| |
| void llcp_rp_conn_param_req_neg_reply(struct ll_conn *conn, struct proc_ctx *ctx) |
| { |
| rp_cu_execute_fsm(conn, ctx, RP_CU_EVT_CONN_PARAM_REQ_NEG_REPLY, NULL); |
| } |
| #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ |