Cleanup, removing TODOs that should just be removed Signed-off-by: Erik Brockhoff <erbr@oticon.com>
1204 lines
30 KiB
C
1204 lines
30 KiB
C
/*
|
|
* Copyright (c) 2020 Demant
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/types.h>
|
|
|
|
#include <bluetooth/hci.h>
|
|
#include <sys/byteorder.h>
|
|
#include <sys/slist.h>
|
|
#include <sys/util.h>
|
|
|
|
#include "hal/ecb.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 "ull_tx_queue.h"
|
|
|
|
#include "ull_conn_types.h"
|
|
#include "ull_internal.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_enc
|
|
#include "common/log.h"
|
|
#include <soc.h>
|
|
#include "hal/debug.h"
|
|
|
|
#if defined(CONFIG_BT_CENTRAL)
|
|
/* LLCP Local Procedure Encryption FSM states */
|
|
enum {
|
|
/* Start Procedure */
|
|
LP_ENC_STATE_UNENCRYPTED,
|
|
LP_ENC_STATE_WAIT_TX_ENC_REQ,
|
|
LP_ENC_STATE_WAIT_RX_ENC_RSP,
|
|
LP_ENC_STATE_WAIT_RX_START_ENC_REQ,
|
|
LP_ENC_STATE_WAIT_TX_START_ENC_RSP,
|
|
LP_ENC_STATE_WAIT_RX_START_ENC_RSP,
|
|
LP_ENC_STATE_WAIT_NTF,
|
|
/* Pause Procedure */
|
|
LP_ENC_STATE_ENCRYPTED,
|
|
LP_ENC_STATE_WAIT_TX_PAUSE_ENC_REQ,
|
|
LP_ENC_STATE_WAIT_RX_PAUSE_ENC_RSP,
|
|
LP_ENC_STATE_WAIT_TX_PAUSE_ENC_RSP,
|
|
};
|
|
|
|
/* LLCP Local Procedure Encryption FSM events */
|
|
enum {
|
|
/* Procedure prepared */
|
|
LP_ENC_EVT_RUN,
|
|
|
|
/* Response received */
|
|
LP_ENC_EVT_ENC_RSP,
|
|
|
|
/* Request received */
|
|
LP_ENC_EVT_START_ENC_REQ,
|
|
|
|
/* Response received */
|
|
LP_ENC_EVT_START_ENC_RSP,
|
|
|
|
/* Reject response received */
|
|
LP_ENC_EVT_REJECT,
|
|
|
|
/* Unknown response received */
|
|
LP_ENC_EVT_UNKNOWN,
|
|
|
|
/* Response received */
|
|
LP_ENC_EVT_PAUSE_ENC_RSP,
|
|
};
|
|
#endif /* CONFIG_BT_CENTRAL */
|
|
|
|
#if defined(CONFIG_BT_PERIPHERAL)
|
|
/* LLCP Remote Procedure Encryption FSM states */
|
|
enum {
|
|
/* Start Procedure */
|
|
RP_ENC_STATE_UNENCRYPTED,
|
|
RP_ENC_STATE_WAIT_RX_ENC_REQ,
|
|
RP_ENC_STATE_WAIT_TX_ENC_RSP,
|
|
RP_ENC_STATE_WAIT_NTF_LTK_REQ,
|
|
RP_ENC_STATE_WAIT_LTK_REPLY,
|
|
RP_ENC_STATE_WAIT_TX_START_ENC_REQ,
|
|
RP_ENC_STATE_WAIT_TX_REJECT_IND,
|
|
RP_ENC_STATE_WAIT_RX_START_ENC_RSP,
|
|
RP_ENC_STATE_WAIT_NTF,
|
|
RP_ENC_STATE_WAIT_TX_START_ENC_RSP,
|
|
/* Pause Procedure */
|
|
RP_ENC_STATE_ENCRYPTED,
|
|
RP_ENC_STATE_WAIT_RX_PAUSE_ENC_REQ,
|
|
RP_ENC_STATE_WAIT_TX_PAUSE_ENC_RSP,
|
|
RP_ENC_STATE_WAIT_RX_PAUSE_ENC_RSP,
|
|
};
|
|
|
|
/* LLCP Remote Procedure Encryption FSM events */
|
|
enum {
|
|
/* Procedure prepared */
|
|
RP_ENC_EVT_RUN,
|
|
|
|
/* Request received */
|
|
RP_ENC_EVT_ENC_REQ,
|
|
|
|
/* Response received */
|
|
RP_ENC_EVT_START_ENC_RSP,
|
|
|
|
/* LTK request reply */
|
|
RP_ENC_EVT_LTK_REQ_REPLY,
|
|
|
|
/* LTK request negative reply */
|
|
RP_ENC_EVT_LTK_REQ_NEG_REPLY,
|
|
|
|
/* Reject response received */
|
|
RP_ENC_EVT_REJECT,
|
|
|
|
/* Unknown response received */
|
|
RP_ENC_EVT_UNKNOWN,
|
|
|
|
/* Request received */
|
|
RP_ENC_EVT_PAUSE_ENC_REQ,
|
|
|
|
/* Response received */
|
|
RP_ENC_EVT_PAUSE_ENC_RSP,
|
|
};
|
|
#endif /* CONFIG_BT_PERIPHERAL */
|
|
|
|
#if defined(CONFIG_BT_CENTRAL)
|
|
/*
|
|
* LLCP Local Procedure Encryption FSM
|
|
*/
|
|
|
|
static struct node_tx *llcp_lp_enc_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) {
|
|
case PDU_DATA_LLCTRL_TYPE_ENC_REQ:
|
|
llcp_pdu_encode_enc_req(ctx, pdu);
|
|
break;
|
|
case PDU_DATA_LLCTRL_TYPE_START_ENC_RSP:
|
|
llcp_pdu_encode_start_enc_rsp(pdu);
|
|
break;
|
|
case PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ:
|
|
llcp_pdu_encode_pause_enc_req(pdu);
|
|
break;
|
|
case PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP:
|
|
llcp_pdu_encode_pause_enc_rsp(pdu);
|
|
break;
|
|
default:
|
|
LL_ASSERT(0);
|
|
}
|
|
|
|
ctx->tx_opcode = pdu->llctrl.opcode;
|
|
|
|
/* Enqueue LL Control PDU towards LLL */
|
|
llcp_tx_enqueue(conn, tx);
|
|
|
|
/* Update procedure timeout */
|
|
ull_conn_prt_reload(conn, conn->procedure_reload);
|
|
|
|
return tx;
|
|
}
|
|
|
|
static void lp_enc_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;
|
|
|
|
if (ctx->data.enc.error == BT_HCI_ERR_SUCCESS) {
|
|
if (ctx->proc == PROC_ENCRYPTION_START) {
|
|
/* Encryption Change Event */
|
|
/* TODO(thoh): is this correct? */
|
|
llcp_pdu_encode_start_enc_rsp(pdu);
|
|
} else if (ctx->proc == PROC_ENCRYPTION_PAUSE) {
|
|
/* Encryption Key Refresh Complete Event */
|
|
ntf->hdr.type = NODE_RX_TYPE_ENC_REFRESH;
|
|
} else {
|
|
/* Should never happen */
|
|
LL_ASSERT(0);
|
|
}
|
|
} else {
|
|
llcp_pdu_encode_reject_ind(pdu, ctx->data.enc.error);
|
|
}
|
|
|
|
/* Enqueue notification towards LL */
|
|
ll_rx_put(ntf->hdr.link, ntf);
|
|
ll_rx_sched();
|
|
}
|
|
|
|
static void lp_enc_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
|
|
{
|
|
if (!llcp_ntf_alloc_is_available()) {
|
|
ctx->state = LP_ENC_STATE_WAIT_NTF;
|
|
} else {
|
|
lp_enc_ntf(conn, ctx);
|
|
llcp_lr_complete(conn);
|
|
ctx->state = LP_ENC_STATE_UNENCRYPTED;
|
|
}
|
|
}
|
|
|
|
static void lp_enc_store_m(struct ll_conn *conn, struct proc_ctx *ctx, struct pdu_data *pdu)
|
|
{
|
|
/* Store SKDm */
|
|
memcpy(&ctx->data.enc.skd[0], pdu->llctrl.enc_req.skdm, sizeof(pdu->llctrl.enc_req.skdm));
|
|
/* Store IVm in the LLL CCM RX
|
|
* TODO(thoh): Should this be made into a ULL function, as it
|
|
* interacts with data outside of LLCP?
|
|
*/
|
|
memcpy(&conn->lll.ccm_rx.iv[0], pdu->llctrl.enc_req.ivm, sizeof(pdu->llctrl.enc_req.ivm));
|
|
}
|
|
|
|
static void lp_enc_send_enc_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
struct node_tx *tx;
|
|
|
|
if (!llcp_tx_alloc_peek(conn, ctx)) {
|
|
ctx->state = LP_ENC_STATE_WAIT_TX_ENC_REQ;
|
|
} else {
|
|
tx = llcp_lp_enc_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_ENC_REQ);
|
|
lp_enc_store_m(conn, ctx, (struct pdu_data *)tx->pdu);
|
|
/* Wait for the LL_ENC_RSP */
|
|
ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_ENC_RSP;
|
|
ctx->state = LP_ENC_STATE_WAIT_RX_ENC_RSP;
|
|
|
|
/* Pause possibly ongoing remote procedure */
|
|
llcp_rr_pause(conn);
|
|
|
|
}
|
|
}
|
|
|
|
static void lp_enc_send_pause_enc_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
if (!llcp_tx_alloc_peek(conn, ctx)) {
|
|
ctx->state = LP_ENC_STATE_WAIT_TX_PAUSE_ENC_REQ;
|
|
} else {
|
|
llcp_lp_enc_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ);
|
|
/* Wait for the LL_PAUSE_ENC_RSP */
|
|
ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP;
|
|
ctx->state = LP_ENC_STATE_WAIT_RX_PAUSE_ENC_RSP;
|
|
}
|
|
}
|
|
|
|
static void lp_enc_send_pause_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
if (!llcp_tx_alloc_peek(conn, ctx)) {
|
|
ctx->state = LP_ENC_STATE_WAIT_TX_PAUSE_ENC_RSP;
|
|
} else {
|
|
llcp_lp_enc_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP);
|
|
|
|
ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_UNUSED;
|
|
/* Continue with an encapsulated Start Procedure */
|
|
ctx->state = LP_ENC_STATE_UNENCRYPTED;
|
|
|
|
/* Tx Encryption disabled */
|
|
conn->lll.enc_tx = 0U;
|
|
|
|
/* Rx Decryption disabled */
|
|
conn->lll.enc_rx = 0U;
|
|
}
|
|
}
|
|
|
|
static void lp_enc_setup_lll(struct ll_conn *conn, struct proc_ctx *ctx)
|
|
{
|
|
/* TODO(thoh): Move LLL/CCM manipulation to ULL? */
|
|
|
|
/* Calculate the Session Key */
|
|
ecb_encrypt(&ctx->data.enc.ltk[0], &ctx->data.enc.skd[0], NULL, &conn->lll.ccm_rx.key[0]);
|
|
|
|
/* Copy the Session Key */
|
|
memcpy(&conn->lll.ccm_tx.key[0], &conn->lll.ccm_rx.key[0], sizeof(conn->lll.ccm_tx.key));
|
|
|
|
/* Copy the IV */
|
|
memcpy(&conn->lll.ccm_tx.iv[0], &conn->lll.ccm_rx.iv[0], sizeof(conn->lll.ccm_tx.iv));
|
|
|
|
/* Reset CCM counter */
|
|
conn->lll.ccm_tx.counter = 0U;
|
|
conn->lll.ccm_rx.counter = 0U;
|
|
|
|
/* Set CCM direction:
|
|
* periph to central = 0,
|
|
* central to periph = 1
|
|
*/
|
|
conn->lll.ccm_tx.direction = 1U;
|
|
conn->lll.ccm_rx.direction = 0U;
|
|
}
|
|
|
|
static void lp_enc_send_start_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
if (!llcp_tx_alloc_peek(conn, ctx)) {
|
|
ctx->state = LP_ENC_STATE_WAIT_TX_START_ENC_RSP;
|
|
} else {
|
|
lp_enc_setup_lll(conn, ctx);
|
|
llcp_lp_enc_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_START_ENC_RSP);
|
|
|
|
/* Wait for LL_START_ENC_RSP */
|
|
ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_START_ENC_RSP;
|
|
ctx->state = LP_ENC_STATE_WAIT_RX_START_ENC_RSP;
|
|
|
|
/* Tx Encryption enabled */
|
|
conn->lll.enc_tx = 1U;
|
|
|
|
/* Rx Decryption enabled */
|
|
conn->lll.enc_rx = 1U;
|
|
}
|
|
}
|
|
|
|
static void lp_enc_st_unencrypted(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
switch (evt) {
|
|
case LP_ENC_EVT_RUN:
|
|
/* Pause Tx data */
|
|
llcp_tx_pause_data(conn);
|
|
llcp_tx_flush(conn);
|
|
lp_enc_send_enc_req(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void lp_enc_st_wait_tx_enc_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
switch (evt) {
|
|
case LP_ENC_EVT_RUN:
|
|
lp_enc_send_enc_req(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void lp_enc_store_s(struct ll_conn *conn, struct proc_ctx *ctx, struct pdu_data *pdu)
|
|
{
|
|
/* Store SKDs */
|
|
memcpy(&ctx->data.enc.skd[8], pdu->llctrl.enc_rsp.skds, sizeof(pdu->llctrl.enc_rsp.skds));
|
|
/* Store IVs in the LLL CCM RX
|
|
* TODO(thoh): Should this be made into a ULL function, as it
|
|
* interacts with data outside of LLCP?
|
|
*/
|
|
memcpy(&conn->lll.ccm_rx.iv[4], pdu->llctrl.enc_rsp.ivs, sizeof(pdu->llctrl.enc_rsp.ivs));
|
|
}
|
|
|
|
static void lp_enc_st_wait_rx_enc_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_ENC_EVT_ENC_RSP:
|
|
/* Pause Rx data */
|
|
ull_conn_pause_rx_data(conn);
|
|
lp_enc_store_s(conn, ctx, pdu);
|
|
/* Wait for LL_START_ENC_REQ */
|
|
ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_START_ENC_REQ;
|
|
ctx->state = LP_ENC_STATE_WAIT_RX_START_ENC_REQ;
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void lp_enc_st_wait_rx_start_enc_req(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_ENC_EVT_START_ENC_REQ:
|
|
lp_enc_send_start_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
case LP_ENC_EVT_REJECT:
|
|
/* Resume Tx data */
|
|
llcp_tx_resume_data(conn);
|
|
/* Resume Rx data */
|
|
ull_conn_resume_rx_data(conn);
|
|
ctx->data.enc.error = (pdu->llctrl.opcode == PDU_DATA_LLCTRL_TYPE_REJECT_IND) ?
|
|
pdu->llctrl.reject_ind.error_code :
|
|
pdu->llctrl.reject_ext_ind.error_code;
|
|
/* Resume possibly paused remote procedure */
|
|
llcp_rr_resume(conn);
|
|
|
|
lp_enc_complete(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void lp_enc_st_wait_tx_start_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
switch (evt) {
|
|
case LP_ENC_EVT_RUN:
|
|
lp_enc_send_start_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void lp_enc_st_wait_rx_start_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
switch (evt) {
|
|
case LP_ENC_EVT_START_ENC_RSP:
|
|
/* Resume Tx data */
|
|
llcp_tx_resume_data(conn);
|
|
/* Resume Rx data */
|
|
ull_conn_resume_rx_data(conn);
|
|
ctx->data.enc.error = BT_HCI_ERR_SUCCESS;
|
|
|
|
/* Resume possibly paused remote procedure */
|
|
llcp_rr_resume(conn);
|
|
|
|
lp_enc_complete(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void lp_enc_st_wait_ntf(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
|
|
{
|
|
switch (evt) {
|
|
case LP_ENC_EVT_RUN:
|
|
lp_enc_complete(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void lp_enc_state_encrypted(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
switch (evt) {
|
|
case LP_ENC_EVT_RUN:
|
|
/* Pause Tx data */
|
|
llcp_tx_pause_data(conn);
|
|
llcp_tx_flush(conn);
|
|
lp_enc_send_pause_enc_req(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void lp_enc_state_wait_tx_pause_enc_req(struct ll_conn *conn, struct proc_ctx *ctx,
|
|
uint8_t evt, void *param)
|
|
{
|
|
switch (evt) {
|
|
case LP_ENC_EVT_RUN:
|
|
lp_enc_send_pause_enc_req(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void lp_enc_state_wait_rx_pause_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx,
|
|
uint8_t evt, void *param)
|
|
{
|
|
switch (evt) {
|
|
case LP_ENC_EVT_PAUSE_ENC_RSP:
|
|
/*
|
|
* Pause Rx data; will be resumed when the encapsulated
|
|
* Start Procedure is done.
|
|
*/
|
|
ull_conn_pause_rx_data(conn);
|
|
lp_enc_send_pause_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void lp_enc_state_wait_tx_pause_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx,
|
|
uint8_t evt, void *param)
|
|
{
|
|
switch (evt) {
|
|
case LP_ENC_EVT_RUN:
|
|
lp_enc_send_pause_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void lp_enc_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
|
|
{
|
|
switch (ctx->state) {
|
|
/* Start Procedure */
|
|
case LP_ENC_STATE_UNENCRYPTED:
|
|
lp_enc_st_unencrypted(conn, ctx, evt, param);
|
|
break;
|
|
case LP_ENC_STATE_WAIT_TX_ENC_REQ:
|
|
lp_enc_st_wait_tx_enc_req(conn, ctx, evt, param);
|
|
break;
|
|
case LP_ENC_STATE_WAIT_RX_ENC_RSP:
|
|
lp_enc_st_wait_rx_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
case LP_ENC_STATE_WAIT_RX_START_ENC_REQ:
|
|
lp_enc_st_wait_rx_start_enc_req(conn, ctx, evt, param);
|
|
break;
|
|
case LP_ENC_STATE_WAIT_TX_START_ENC_RSP:
|
|
lp_enc_st_wait_tx_start_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
case LP_ENC_STATE_WAIT_RX_START_ENC_RSP:
|
|
lp_enc_st_wait_rx_start_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
case LP_ENC_STATE_WAIT_NTF:
|
|
lp_enc_st_wait_ntf(conn, ctx, evt, param);
|
|
break;
|
|
/* Pause Procedure */
|
|
case LP_ENC_STATE_ENCRYPTED:
|
|
lp_enc_state_encrypted(conn, ctx, evt, param);
|
|
break;
|
|
case LP_ENC_STATE_WAIT_TX_PAUSE_ENC_REQ:
|
|
lp_enc_state_wait_tx_pause_enc_req(conn, ctx, evt, param);
|
|
break;
|
|
case LP_ENC_STATE_WAIT_RX_PAUSE_ENC_RSP:
|
|
lp_enc_state_wait_rx_pause_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
case LP_ENC_STATE_WAIT_TX_PAUSE_ENC_RSP:
|
|
lp_enc_state_wait_tx_pause_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Unknown state */
|
|
LL_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
void llcp_lp_enc_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_ENC_RSP:
|
|
lp_enc_execute_fsm(conn, ctx, LP_ENC_EVT_ENC_RSP, pdu);
|
|
break;
|
|
case PDU_DATA_LLCTRL_TYPE_START_ENC_REQ:
|
|
lp_enc_execute_fsm(conn, ctx, LP_ENC_EVT_START_ENC_REQ, pdu);
|
|
break;
|
|
case PDU_DATA_LLCTRL_TYPE_START_ENC_RSP:
|
|
lp_enc_execute_fsm(conn, ctx, LP_ENC_EVT_START_ENC_RSP, pdu);
|
|
break;
|
|
case PDU_DATA_LLCTRL_TYPE_REJECT_IND:
|
|
case PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND:
|
|
lp_enc_execute_fsm(conn, ctx, LP_ENC_EVT_REJECT, pdu);
|
|
break;
|
|
case PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP:
|
|
lp_enc_execute_fsm(conn, ctx, LP_ENC_EVT_PAUSE_ENC_RSP, pdu);
|
|
break;
|
|
default:
|
|
/* Unknown opcode */
|
|
LL_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
void llcp_lp_enc_init_proc(struct proc_ctx *ctx)
|
|
{
|
|
switch (ctx->proc) {
|
|
case PROC_ENCRYPTION_START:
|
|
ctx->state = LP_ENC_STATE_UNENCRYPTED;
|
|
break;
|
|
case PROC_ENCRYPTION_PAUSE:
|
|
ctx->state = LP_ENC_STATE_ENCRYPTED;
|
|
break;
|
|
default:
|
|
LL_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
void llcp_lp_enc_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param)
|
|
{
|
|
lp_enc_execute_fsm(conn, ctx, LP_ENC_EVT_RUN, param);
|
|
}
|
|
|
|
#endif /* CONFIG_BT_CENTRAL */
|
|
|
|
#if defined(CONFIG_BT_PERIPHERAL)
|
|
/*
|
|
* LLCP Remote Procedure Encryption FSM
|
|
*/
|
|
|
|
static struct node_tx *llcp_rp_enc_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) {
|
|
case PDU_DATA_LLCTRL_TYPE_ENC_RSP:
|
|
llcp_pdu_encode_enc_rsp(pdu);
|
|
break;
|
|
case PDU_DATA_LLCTRL_TYPE_START_ENC_REQ:
|
|
llcp_pdu_encode_start_enc_req(pdu);
|
|
break;
|
|
case PDU_DATA_LLCTRL_TYPE_START_ENC_RSP:
|
|
llcp_pdu_encode_start_enc_rsp(pdu);
|
|
break;
|
|
case PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP:
|
|
llcp_pdu_encode_pause_enc_rsp(pdu);
|
|
break;
|
|
case PDU_DATA_LLCTRL_TYPE_REJECT_IND:
|
|
/* TODO(thoh): Select between LL_REJECT_IND and LL_REJECT_EXT_IND */
|
|
llcp_pdu_encode_reject_ext_ind(pdu, PDU_DATA_LLCTRL_TYPE_ENC_REQ,
|
|
BT_HCI_ERR_PIN_OR_KEY_MISSING);
|
|
break;
|
|
default:
|
|
LL_ASSERT(0);
|
|
}
|
|
|
|
ctx->tx_opcode = pdu->llctrl.opcode;
|
|
|
|
/* Enqueue LL Control PDU towards LLL */
|
|
llcp_tx_enqueue(conn, tx);
|
|
|
|
return tx;
|
|
}
|
|
|
|
static void rp_enc_ntf_ltk(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_ntf_encode_enc_req(ctx, pdu);
|
|
|
|
/* Enqueue notification towards LL */
|
|
ll_rx_put(ntf->hdr.link, ntf);
|
|
ll_rx_sched();
|
|
}
|
|
|
|
static void rp_enc_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;
|
|
|
|
if (ctx->proc == PROC_ENCRYPTION_START) {
|
|
/* Encryption Change Event */
|
|
/* TODO(thoh): is this correct? */
|
|
llcp_pdu_encode_start_enc_rsp(pdu);
|
|
} else if (ctx->proc == PROC_ENCRYPTION_PAUSE) {
|
|
/* Encryption Key Refresh Complete Event */
|
|
ntf->hdr.type = NODE_RX_TYPE_ENC_REFRESH;
|
|
} else {
|
|
/* Should never happen */
|
|
LL_ASSERT(0);
|
|
}
|
|
|
|
/* Enqueue notification towards LL */
|
|
ll_rx_put(ntf->hdr.link, ntf);
|
|
ll_rx_sched();
|
|
}
|
|
|
|
static void rp_enc_send_start_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param);
|
|
|
|
static void rp_enc_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
|
|
{
|
|
if (!llcp_ntf_alloc_is_available()) {
|
|
ctx->state = RP_ENC_STATE_WAIT_NTF;
|
|
} else {
|
|
rp_enc_ntf(conn, ctx);
|
|
rp_enc_send_start_enc_rsp(conn, ctx, evt, param);
|
|
}
|
|
}
|
|
|
|
static void rp_enc_send_ltk_ntf(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
if (!llcp_ntf_alloc_is_available()) {
|
|
ctx->state = RP_ENC_STATE_WAIT_NTF_LTK_REQ;
|
|
} else {
|
|
rp_enc_ntf_ltk(conn, ctx);
|
|
ctx->state = RP_ENC_STATE_WAIT_LTK_REPLY;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_store_s(struct ll_conn *conn, struct proc_ctx *ctx, struct pdu_data *pdu)
|
|
{
|
|
/* Store SKDs */
|
|
memcpy(&ctx->data.enc.skds, pdu->llctrl.enc_rsp.skds, sizeof(pdu->llctrl.enc_rsp.skds));
|
|
/* Store IVs in the LLL CCM RX
|
|
* TODO(thoh): Should this be made into a ULL function, as it
|
|
* interacts with data outside of LLCP?
|
|
*/
|
|
memcpy(&conn->lll.ccm_rx.iv[4], pdu->llctrl.enc_rsp.ivs, sizeof(pdu->llctrl.enc_rsp.ivs));
|
|
}
|
|
|
|
static void rp_enc_send_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
struct node_tx *tx;
|
|
|
|
if (!llcp_tx_alloc_peek(conn, ctx)) {
|
|
ctx->state = RP_ENC_STATE_WAIT_TX_ENC_RSP;
|
|
} else {
|
|
tx = llcp_rp_enc_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_ENC_RSP);
|
|
rp_enc_store_s(conn, ctx, (struct pdu_data *)tx->pdu);
|
|
rp_enc_send_ltk_ntf(conn, ctx, evt, param);
|
|
}
|
|
}
|
|
|
|
static void rp_enc_setup_lll(struct ll_conn *conn, struct proc_ctx *ctx)
|
|
{
|
|
/* TODO(thoh): Move LLL/CCM manipulation to ULL? */
|
|
|
|
/* Calculate the Session Key */
|
|
ecb_encrypt(&ctx->data.enc.ltk[0], &ctx->data.enc.skd[0], NULL, &conn->lll.ccm_rx.key[0]);
|
|
|
|
/* Copy the Session Key */
|
|
memcpy(&conn->lll.ccm_tx.key[0], &conn->lll.ccm_rx.key[0], sizeof(conn->lll.ccm_tx.key));
|
|
|
|
/* Copy the IV */
|
|
memcpy(&conn->lll.ccm_tx.iv[0], &conn->lll.ccm_rx.iv[0], sizeof(conn->lll.ccm_tx.iv));
|
|
|
|
/* Reset CCM counter */
|
|
conn->lll.ccm_tx.counter = 0U;
|
|
conn->lll.ccm_rx.counter = 0U;
|
|
|
|
/* Set CCM direction:
|
|
* periph to central = 0,
|
|
* central to periph = 1
|
|
*/
|
|
conn->lll.ccm_tx.direction = 0U;
|
|
conn->lll.ccm_rx.direction = 1U;
|
|
}
|
|
|
|
static void rp_enc_send_start_enc_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
if (!llcp_tx_alloc_peek(conn, ctx)) {
|
|
ctx->state = RP_ENC_STATE_WAIT_TX_START_ENC_REQ;
|
|
} else {
|
|
rp_enc_setup_lll(conn, ctx);
|
|
llcp_rp_enc_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_START_ENC_REQ);
|
|
/* Wait for the LL_START_ENC_RSP */
|
|
ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_START_ENC_RSP;
|
|
ctx->state = RP_ENC_STATE_WAIT_RX_START_ENC_RSP;
|
|
|
|
/* Rx Decryption enabled */
|
|
conn->lll.enc_rx = 1U;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_send_reject_ind(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
if (!llcp_tx_alloc_peek(conn, ctx)) {
|
|
ctx->state = RP_ENC_STATE_WAIT_TX_REJECT_IND;
|
|
} else {
|
|
llcp_rp_enc_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_REJECT_IND);
|
|
llcp_rr_complete(conn);
|
|
ctx->state = RP_ENC_STATE_UNENCRYPTED;
|
|
|
|
/* Resume Tx data */
|
|
llcp_tx_resume_data(conn);
|
|
/* Resume Rx data */
|
|
ull_conn_resume_rx_data(conn);
|
|
/* Resume possibly paused local procedure */
|
|
llcp_lr_resume(conn);
|
|
}
|
|
}
|
|
|
|
static void rp_enc_send_start_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
if (!llcp_tx_alloc_peek(conn, ctx)) {
|
|
ctx->state = RP_ENC_STATE_WAIT_TX_START_ENC_RSP;
|
|
} else {
|
|
llcp_rp_enc_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_START_ENC_RSP);
|
|
llcp_rr_complete(conn);
|
|
ctx->state = RP_ENC_STATE_UNENCRYPTED;
|
|
|
|
/* Resume Tx data */
|
|
llcp_tx_resume_data(conn);
|
|
/* Resume Rx data */
|
|
ull_conn_resume_rx_data(conn);
|
|
|
|
/* Resume possibly paused local procedure */
|
|
llcp_lr_resume(conn);
|
|
|
|
/* Tx Encryption enabled */
|
|
conn->lll.enc_tx = 1U;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_send_pause_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
if (!llcp_tx_alloc_peek(conn, ctx)) {
|
|
ctx->state = RP_ENC_STATE_WAIT_TX_PAUSE_ENC_RSP;
|
|
} else {
|
|
llcp_rp_enc_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP);
|
|
/* Wait for the LL_PAUSE_ENC_RSP */
|
|
ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP;
|
|
ctx->state = RP_ENC_STATE_WAIT_RX_PAUSE_ENC_RSP;
|
|
|
|
/* Rx Decryption disabled */
|
|
conn->lll.enc_rx = 0U;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_state_unencrypted(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
switch (evt) {
|
|
case RP_ENC_EVT_RUN:
|
|
ctx->state = RP_ENC_STATE_WAIT_RX_ENC_REQ;
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_store_m(struct ll_conn *conn, struct proc_ctx *ctx, struct pdu_data *pdu)
|
|
{
|
|
/* Store Rand */
|
|
memcpy(ctx->data.enc.rand, pdu->llctrl.enc_req.rand, sizeof(ctx->data.enc.rand));
|
|
|
|
/* Store EDIV */
|
|
ctx->data.enc.ediv[0] = pdu->llctrl.enc_req.ediv[0];
|
|
ctx->data.enc.ediv[1] = pdu->llctrl.enc_req.ediv[1];
|
|
|
|
/* Store SKDm */
|
|
memcpy(&ctx->data.enc.skdm, pdu->llctrl.enc_req.skdm, sizeof(ctx->data.enc.skdm));
|
|
|
|
/* Store IVm in the LLL CCM RX
|
|
* TODO(thoh): Should this be made into a ULL function, as it
|
|
* interacts with data outside of LLCP?
|
|
*/
|
|
memcpy(&conn->lll.ccm_rx.iv[0], pdu->llctrl.enc_req.ivm, sizeof(pdu->llctrl.enc_req.ivm));
|
|
}
|
|
|
|
static void rp_enc_state_wait_rx_enc_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
switch (evt) {
|
|
case RP_ENC_EVT_ENC_REQ:
|
|
/* Pause Tx data */
|
|
llcp_tx_pause_data(conn);
|
|
llcp_tx_flush(conn);
|
|
/* Pause Rx data */
|
|
ull_conn_pause_rx_data(conn);
|
|
|
|
/* Pause possibly paused local procedure */
|
|
llcp_lr_pause(conn);
|
|
|
|
rp_enc_store_m(conn, ctx, param);
|
|
rp_enc_send_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_state_wait_tx_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
switch (evt) {
|
|
case RP_ENC_EVT_RUN:
|
|
rp_enc_send_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_state_wait_ntf_ltk_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
switch (evt) {
|
|
case RP_ENC_EVT_RUN:
|
|
rp_enc_send_ltk_ntf(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_state_wait_ltk_reply(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
switch (evt) {
|
|
case RP_ENC_EVT_LTK_REQ_REPLY:
|
|
rp_enc_send_start_enc_req(conn, ctx, evt, param);
|
|
break;
|
|
case RP_ENC_EVT_LTK_REQ_NEG_REPLY:
|
|
rp_enc_send_reject_ind(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_state_wait_tx_start_enc_req(struct ll_conn *conn, struct proc_ctx *ctx,
|
|
uint8_t evt, void *param)
|
|
{
|
|
switch (evt) {
|
|
case RP_ENC_EVT_RUN:
|
|
rp_enc_send_start_enc_req(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_state_wait_tx_reject_ind(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
switch (evt) {
|
|
case RP_ENC_EVT_RUN:
|
|
rp_enc_send_reject_ind(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_state_wait_rx_start_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx,
|
|
uint8_t evt, void *param)
|
|
{
|
|
switch (evt) {
|
|
case RP_ENC_EVT_START_ENC_RSP:
|
|
rp_enc_complete(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_state_wait_ntf(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
switch (evt) {
|
|
case RP_ENC_EVT_RUN:
|
|
rp_enc_complete(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_state_wait_tx_start_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx,
|
|
uint8_t evt, void *param)
|
|
{
|
|
switch (evt) {
|
|
case RP_ENC_EVT_RUN:
|
|
rp_enc_send_start_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_state_encrypted(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
|
|
void *param)
|
|
{
|
|
switch (evt) {
|
|
case RP_ENC_EVT_RUN:
|
|
ctx->state = RP_ENC_STATE_WAIT_RX_PAUSE_ENC_REQ;
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_state_wait_rx_pause_enc_req(struct ll_conn *conn, struct proc_ctx *ctx,
|
|
uint8_t evt, void *param)
|
|
{
|
|
switch (evt) {
|
|
case RP_ENC_EVT_PAUSE_ENC_REQ:
|
|
/* Pause Tx data */
|
|
llcp_tx_pause_data(conn);
|
|
llcp_tx_flush(conn);
|
|
/*
|
|
* Pause Rx data; will be resumed when the encapsulated
|
|
* Start Procedure is done.
|
|
*/
|
|
ull_conn_pause_rx_data(conn);
|
|
rp_enc_send_pause_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_state_wait_tx_pause_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx,
|
|
uint8_t evt, void *param)
|
|
{
|
|
switch (evt) {
|
|
case RP_ENC_EVT_RUN:
|
|
rp_enc_send_pause_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_state_wait_rx_pause_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx,
|
|
uint8_t evt, void *param)
|
|
{
|
|
switch (evt) {
|
|
case RP_ENC_EVT_PAUSE_ENC_RSP:
|
|
/* Continue with an encapsulated Start Procedure */
|
|
/* Wait for the LL_ENC_REQ */
|
|
ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_ENC_REQ;
|
|
ctx->state = RP_ENC_STATE_WAIT_RX_ENC_REQ;
|
|
|
|
/* Tx Encryption disabled */
|
|
conn->lll.enc_tx = 0U;
|
|
break;
|
|
default:
|
|
/* Ignore other evts */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rp_enc_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
|
|
{
|
|
switch (ctx->state) {
|
|
/* Start Procedure */
|
|
case RP_ENC_STATE_UNENCRYPTED:
|
|
rp_enc_state_unencrypted(conn, ctx, evt, param);
|
|
break;
|
|
case RP_ENC_STATE_WAIT_RX_ENC_REQ:
|
|
rp_enc_state_wait_rx_enc_req(conn, ctx, evt, param);
|
|
break;
|
|
case RP_ENC_STATE_WAIT_TX_ENC_RSP:
|
|
rp_enc_state_wait_tx_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
case RP_ENC_STATE_WAIT_NTF_LTK_REQ:
|
|
rp_enc_state_wait_ntf_ltk_req(conn, ctx, evt, param);
|
|
break;
|
|
case RP_ENC_STATE_WAIT_LTK_REPLY:
|
|
rp_enc_state_wait_ltk_reply(conn, ctx, evt, param);
|
|
break;
|
|
case RP_ENC_STATE_WAIT_TX_START_ENC_REQ:
|
|
rp_enc_state_wait_tx_start_enc_req(conn, ctx, evt, param);
|
|
break;
|
|
case RP_ENC_STATE_WAIT_TX_REJECT_IND:
|
|
rp_enc_state_wait_tx_reject_ind(conn, ctx, evt, param);
|
|
break;
|
|
case RP_ENC_STATE_WAIT_RX_START_ENC_RSP:
|
|
rp_enc_state_wait_rx_start_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
case RP_ENC_STATE_WAIT_NTF:
|
|
rp_enc_state_wait_ntf(conn, ctx, evt, param);
|
|
break;
|
|
case RP_ENC_STATE_WAIT_TX_START_ENC_RSP:
|
|
rp_enc_state_wait_tx_start_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
/* Pause Procedure */
|
|
case RP_ENC_STATE_ENCRYPTED:
|
|
rp_enc_state_encrypted(conn, ctx, evt, param);
|
|
break;
|
|
case RP_ENC_STATE_WAIT_RX_PAUSE_ENC_REQ:
|
|
rp_enc_state_wait_rx_pause_enc_req(conn, ctx, evt, param);
|
|
break;
|
|
case RP_ENC_STATE_WAIT_TX_PAUSE_ENC_RSP:
|
|
rp_enc_state_wait_tx_pause_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
case RP_ENC_STATE_WAIT_RX_PAUSE_ENC_RSP:
|
|
rp_enc_state_wait_rx_pause_enc_rsp(conn, ctx, evt, param);
|
|
break;
|
|
default:
|
|
/* Unknown state */
|
|
LL_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
void llcp_rp_enc_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_ENC_REQ:
|
|
rp_enc_execute_fsm(conn, ctx, RP_ENC_EVT_ENC_REQ, pdu);
|
|
break;
|
|
case PDU_DATA_LLCTRL_TYPE_START_ENC_RSP:
|
|
rp_enc_execute_fsm(conn, ctx, RP_ENC_EVT_START_ENC_RSP, pdu);
|
|
break;
|
|
case PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ:
|
|
rp_enc_execute_fsm(conn, ctx, RP_ENC_EVT_PAUSE_ENC_REQ, pdu);
|
|
break;
|
|
case PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP:
|
|
rp_enc_execute_fsm(conn, ctx, RP_ENC_EVT_PAUSE_ENC_RSP, pdu);
|
|
break;
|
|
default:
|
|
/* Unknown opcode */
|
|
LL_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
void llcp_rp_enc_init_proc(struct proc_ctx *ctx)
|
|
{
|
|
switch (ctx->proc) {
|
|
case PROC_ENCRYPTION_START:
|
|
ctx->state = RP_ENC_STATE_UNENCRYPTED;
|
|
break;
|
|
case PROC_ENCRYPTION_PAUSE:
|
|
ctx->state = RP_ENC_STATE_ENCRYPTED;
|
|
break;
|
|
default:
|
|
LL_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
void llcp_rp_enc_ltk_req_reply(struct ll_conn *conn, struct proc_ctx *ctx)
|
|
{
|
|
rp_enc_execute_fsm(conn, ctx, RP_ENC_EVT_LTK_REQ_REPLY, NULL);
|
|
}
|
|
|
|
void llcp_rp_enc_ltk_req_neg_reply(struct ll_conn *conn, struct proc_ctx *ctx)
|
|
{
|
|
rp_enc_execute_fsm(conn, ctx, RP_ENC_EVT_LTK_REQ_NEG_REPLY, NULL);
|
|
}
|
|
|
|
void llcp_rp_enc_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param)
|
|
{
|
|
rp_enc_execute_fsm(conn, ctx, RP_ENC_EVT_RUN, param);
|
|
}
|
|
#endif /* CONFIG_BT_PERIPHERAL */
|