blob: 4f87d70760f1ff554605b830e9a5026518ea62e5 [file] [log] [blame]
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* 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_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_types.h"
#include "ull_llcp.h"
#include "ull_llcp_internal.h"
#include "ull_conn_internal.h"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#define LOG_MODULE_NAME bt_ctlr_ull_llcp_chmu
#include "common/log.h"
#include <soc.h>
#include "hal/debug.h"
/* Hardcoded instant delta +6 */
#define CHMU_INSTANT_DELTA 6U
/* LLCP Local Procedure Channel Map Update FSM states */
enum {
LP_CHMU_STATE_IDLE,
LP_CHMU_STATE_WAIT_TX_CHAN_MAP_IND,
LP_CHMU_STATE_WAIT_INSTANT,
};
/* LLCP Local Procedure Channel Map Update FSM events */
enum {
/* Procedure run */
LP_CHMU_EVT_RUN,
};
/* LLCP Remote Procedure Channel Map Update FSM states */
enum {
RP_CHMU_STATE_IDLE,
RP_CHMU_STATE_WAIT_RX_CHAN_MAP_IND,
RP_CHMU_STATE_WAIT_INSTANT,
};
/* LLCP Remote Procedure Channel Map Update FSM events */
enum {
/* Procedure run */
RP_CHMU_EVT_RUN,
/* Indication received */
RP_CHMU_EVT_RX_CHAN_MAP_IND,
};
#if defined(CONFIG_BT_CENTRAL)
/*
* LLCP Local Procedure Channel Map Update FSM
*/
static void lp_chmu_tx(struct ll_conn *conn, struct proc_ctx *ctx)
{
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 */
llcp_pdu_encode_chan_map_update_ind(ctx, pdu);
ctx->tx_opcode = pdu->llctrl.opcode;
/* Enqueue LL Control PDU towards LLL */
llcp_tx_enqueue(conn, tx);
}
static void lp_chmu_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
ull_conn_chan_map_set(conn, ctx->data.chmu.chm);
llcp_lr_complete(conn);
ctx->state = LP_CHMU_STATE_IDLE;
}
static void lp_chmu_send_channel_map_update_ind(struct ll_conn *conn, struct proc_ctx *ctx,
uint8_t evt, void *param)
{
if (llcp_lr_ispaused(conn) || llcp_rr_get_collision(conn) ||
!llcp_tx_alloc_peek(conn, ctx)) {
ctx->state = LP_CHMU_STATE_WAIT_TX_CHAN_MAP_IND;
} else {
llcp_rr_set_incompat(conn, INCOMPAT_RESOLVABLE);
ctx->data.chmu.instant = ull_conn_event_counter(conn) + CHMU_INSTANT_DELTA;
lp_chmu_tx(conn, ctx);
ctx->state = LP_CHMU_STATE_WAIT_INSTANT;
}
}
static void lp_chmu_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
switch (evt) {
case LP_CHMU_EVT_RUN:
lp_chmu_send_channel_map_update_ind(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
static void lp_chmu_st_wait_tx_chan_map_ind(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (evt) {
case LP_CHMU_EVT_RUN:
lp_chmu_send_channel_map_update_ind(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
static void lp_chmu_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.chmu.instant, event_counter)) {
llcp_rr_set_incompat(conn, INCOMPAT_NO_COLLISION);
lp_chmu_complete(conn, ctx, evt, param);
}
}
static void lp_chmu_st_wait_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (evt) {
case LP_CHMU_EVT_RUN:
lp_chmu_check_instant(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
static void lp_chmu_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (ctx->state) {
case LP_CHMU_STATE_IDLE:
lp_chmu_st_idle(conn, ctx, evt, param);
break;
case LP_CHMU_STATE_WAIT_TX_CHAN_MAP_IND:
lp_chmu_st_wait_tx_chan_map_ind(conn, ctx, evt, param);
break;
case LP_CHMU_STATE_WAIT_INSTANT:
lp_chmu_st_wait_instant(conn, ctx, evt, param);
break;
default:
/* Unknown state */
LL_ASSERT(0);
}
}
void llcp_lp_chmu_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) {
default:
/* Invalid behaviour */
/* Invalid PDU received so terminate connection */
conn->llcp_terminate.reason_final = BT_HCI_ERR_LMP_PDU_NOT_ALLOWED;
llcp_lr_complete(conn);
ctx->state = LP_CHMU_STATE_IDLE;
break;
}
}
void llcp_lp_chmu_init_proc(struct proc_ctx *ctx)
{
ctx->state = LP_CHMU_STATE_IDLE;
}
void llcp_lp_chmu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param)
{
lp_chmu_execute_fsm(conn, ctx, LP_CHMU_EVT_RUN, param);
}
#endif /* CONFIG_BT_CENTRAL */
#if defined(CONFIG_BT_PERIPHERAL)
/*
* LLCP Remote Procedure Channel Map Update FSM
*/
static void rp_chmu_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
ull_conn_chan_map_set(conn, ctx->data.chmu.chm);
llcp_rr_complete(conn);
ctx->state = RP_CHMU_STATE_IDLE;
}
static void rp_chmu_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
switch (evt) {
case RP_CHMU_EVT_RUN:
ctx->state = RP_CHMU_STATE_WAIT_RX_CHAN_MAP_IND;
break;
default:
/* Ignore other evts */
break;
}
}
static void rp_chmu_st_wait_rx_channel_map_update_ind(struct ll_conn *conn, struct proc_ctx *ctx,
uint8_t evt, void *param)
{
switch (evt) {
case RP_CHMU_EVT_RX_CHAN_MAP_IND:
llcp_pdu_decode_chan_map_update_ind(ctx, param);
if (is_instant_not_passed(ctx->data.chmu.instant,
ull_conn_event_counter(conn))) {
ctx->state = RP_CHMU_STATE_WAIT_INSTANT;
} else {
conn->llcp_terminate.reason_final = BT_HCI_ERR_INSTANT_PASSED;
llcp_rr_complete(conn);
ctx->state = RP_CHMU_STATE_IDLE;
}
break;
default:
/* Ignore other evts */
break;
}
}
static void rp_chmu_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 (((event_counter - ctx->data.chmu.instant) & 0xFFFF) <= 0x7FFF) {
rp_chmu_complete(conn, ctx, evt, param);
}
}
static void rp_chmu_st_wait_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (evt) {
case RP_CHMU_EVT_RUN:
rp_chmu_check_instant(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
static void rp_chmu_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (ctx->state) {
case RP_CHMU_STATE_IDLE:
rp_chmu_st_idle(conn, ctx, evt, param);
break;
case RP_CHMU_STATE_WAIT_RX_CHAN_MAP_IND:
rp_chmu_st_wait_rx_channel_map_update_ind(conn, ctx, evt, param);
break;
case RP_CHMU_STATE_WAIT_INSTANT:
rp_chmu_st_wait_instant(conn, ctx, evt, param);
break;
default:
/* Unknown state */
LL_ASSERT(0);
}
}
void llcp_rp_chmu_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) {
case PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND:
rp_chmu_execute_fsm(conn, ctx, RP_CHMU_EVT_RX_CHAN_MAP_IND, pdu);
break;
default:
/* Invalid behaviour */
/* Invalid PDU received so terminate connection */
conn->llcp_terminate.reason_final = BT_HCI_ERR_LMP_PDU_NOT_ALLOWED;
llcp_rr_complete(conn);
ctx->state = RP_CHMU_STATE_IDLE;
break;
}
}
void llcp_rp_chmu_init_proc(struct proc_ctx *ctx)
{
ctx->state = RP_CHMU_STATE_IDLE;
}
void llcp_rp_chmu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param)
{
rp_chmu_execute_fsm(conn, ctx, RP_CHMU_EVT_RUN, param);
}
#endif /* CONFIG_BT_PERIPHERAL */