Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
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.
 
 
 
 
 
 

575 lines
15 KiB

/*
* Copyright (c) 2024 Demant A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <zephyr/sys/slist.h>
#include <zephyr/bluetooth/hci_types.h>
#include "hal/ccm.h"
#include "hal/debug.h"
#include "util/util.h"
#include "util/memq.h"
#include "util/dbuf.h"
#include "pdu_df.h"
#include "lll/pdu_vendor.h"
#include "pdu.h"
#include "lll.h"
#include "lll/lll_df_types.h"
#include "lll_filter.h"
#include "lll_scan.h"
#include "lll_sync.h"
#include "lll_sync_iso.h"
#include "lll_conn.h"
#include "lll_conn_iso.h"
#include "ull_tx_queue.h"
#include "isoal.h"
#include "ull_scan_types.h"
#include "ull_sync_types.h"
#include "ull_iso_types.h"
#include "ull_conn_types.h"
#include "ull_conn_iso_types.h"
#include "ll_settings.h"
#include "ll_feat.h"
#include "ull_llcp.h"
#include "ull_llcp_features.h"
#include "ull_internal.h"
#include "ull_sync_internal.h"
#include "ull_conn_internal.h"
#include "ull_llcp_internal.h"
#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER)
/* LLCP Remote Procedure FSM states */
enum {
RP_PAST_STATE_IDLE,
RP_PAST_STATE_WAIT_RX,
RP_PAST_STATE_WAIT_NEXT_EVT,
#if defined(CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY)
RP_PAST_STATE_WAIT_RESOLVE_INTERFACE_AVAIL,
RP_PAST_STATE_WAIT_RESOLVE_COMPLETE,
#endif /* CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY */
};
/* LLCP Remote Procedure PAST (receiver) FSM events */
enum {
/* Procedure run */
RP_PAST_EVT_RUN,
/* IND received */
RP_PAST_EVT_RX,
/* RPA resolve completed */
RP_PAST_EVT_RESOLVED,
};
#if defined(CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY)
/* Active connection for RPA resolve */
static struct ll_conn *rp_past_resolve_conn;
#endif /* CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY */
static uint8_t rp_check_phy(struct ll_conn *conn, struct proc_ctx *ctx,
struct pdu_data *pdu)
{
if (!phy_valid(pdu->llctrl.periodic_sync_ind.phy)) {
/* zero, more than one or any rfu bit selected in either phy */
return BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL;
}
#if defined(CONFIG_BT_CTLR_PHY)
const uint8_t phy = pdu->llctrl.periodic_sync_ind.phy;
if (((phy & PHY_2M) && !IS_ENABLED(CONFIG_BT_CTLR_PHY_2M)) ||
((phy & PHY_CODED) && !IS_ENABLED(CONFIG_BT_CTLR_PHY_CODED))) {
/* Unsupported phy selected */
return BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL;
}
#endif /* CONFIG_BT_CTLR_PHY */
return BT_HCI_ERR_SUCCESS;
}
static void rp_past_complete(struct ll_conn *conn, struct proc_ctx *ctx)
{
llcp_rr_complete(conn);
ctx->state = RP_PAST_STATE_IDLE;
}
static void rp_past_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
switch (evt) {
case RP_PAST_EVT_RUN:
ctx->state = RP_PAST_STATE_WAIT_RX;
break;
default:
/* Ignore other evts */
break;
}
}
#if defined(CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY)
static void rp_past_resolve_cb(void *param)
{
uint8_t rl_idx = (uint8_t)(uint32_t)param;
struct ll_conn *conn = rp_past_resolve_conn;
struct proc_ctx *ctx;
uint8_t id_addr_type;
/* Release resolving interface */
rp_past_resolve_conn = NULL;
ctx = llcp_rr_peek(conn);
if (!ctx) {
/* No context - possibly due to connection termination or
* other cause of procedure completion.
*/
return;
}
if (ctx->state != RP_PAST_STATE_WAIT_RESOLVE_COMPLETE) {
/* Wrong state - possibly due to connection termination or
* other cause of procedure completion.
*/
return;
}
/* If resolve failed then just continue in next event using the RPA */
if (rl_idx != FILTER_IDX_NONE) {
const bt_addr_t *id_addr;
id_addr = ull_filter_lll_id_addr_get(rl_idx, &id_addr_type);
if (id_addr) {
memcpy(&ctx->data.periodic_sync.adv_addr, &id_addr->val, sizeof(bt_addr_t));
ctx->data.periodic_sync.addr_type = id_addr_type;
ctx->data.periodic_sync.addr_resolved = 1U;
}
}
/* Let sync creation continue in next event */
ctx->state = RP_PAST_STATE_WAIT_NEXT_EVT;
}
static bool rp_past_addr_resolve(struct ll_conn *conn, struct proc_ctx *ctx)
{
bt_addr_t adv_addr;
if (rp_past_resolve_conn) {
/* Resolve interface busy */
return false;
}
(void)memcpy(&adv_addr.val, ctx->data.periodic_sync.adv_addr, sizeof(bt_addr_t));
rp_past_resolve_conn = conn;
if (ull_filter_deferred_resolve(&adv_addr, rp_past_resolve_cb)) {
/* Resolve initiated - wait for callback */
return true;
}
/* Resolve interface busy (in ull_filter) */
rp_past_resolve_conn = NULL;
return false;
}
static void rp_past_st_wait_resolve_if(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (evt) {
case RP_PAST_EVT_RUN:
if (rp_past_addr_resolve(conn, ctx)) {
ctx->state = RP_PAST_STATE_WAIT_RESOLVE_COMPLETE;
}
break;
default:
/* Ignore other evts */
break;
}
}
#endif /* CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY */
static void rp_past_st_wait_rx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
struct pdu_data *pdu = (struct pdu_data *)param;
switch (evt) {
case RP_PAST_EVT_RX:
llcp_pdu_decode_periodic_sync_ind(ctx, pdu);
/* Check PHY */
if (rp_check_phy(conn, ctx, pdu) != BT_HCI_ERR_SUCCESS) {
/* Invalid PHY - ignore and silently complete */
rp_past_complete(conn, ctx);
return;
}
ctx->data.periodic_sync.addr_resolved = 0U;
if (ctx->data.periodic_sync.addr_type == BT_ADDR_LE_RANDOM) {
/* TODO: For efficiency, check if address resolve is needed */
#if defined(CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY)
if (ull_filter_lll_rl_enabled()) {
if (rp_past_addr_resolve(conn, ctx)) {
ctx->state = RP_PAST_STATE_WAIT_RESOLVE_COMPLETE;
return;
}
ctx->state = RP_PAST_STATE_WAIT_RESOLVE_INTERFACE_AVAIL;
return;
}
#else
/* TODO: Not implemented - use RPA */
#endif /* CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY */
}
if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) {
/* If sync_conn_event_count is this connection event,
* we have to wait for drift correction for this event to be applied -
* continue processing in the next conn event
*/
if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL &&
ctx->data.periodic_sync.sync_conn_event_count ==
ull_conn_event_counter(conn)) {
ctx->state = RP_PAST_STATE_WAIT_NEXT_EVT;
return;
}
}
/* Hand over to ULL */
ull_sync_transfer_received(conn,
ctx->data.periodic_sync.id,
&ctx->data.periodic_sync.sync_info,
ctx->data.periodic_sync.conn_event_count,
ctx->data.periodic_sync.last_pa_event_counter,
ctx->data.periodic_sync.sid,
ctx->data.periodic_sync.addr_type,
ctx->data.periodic_sync.sca,
ctx->data.periodic_sync.phy,
ctx->data.periodic_sync.adv_addr,
ctx->data.periodic_sync.sync_conn_event_count,
ctx->data.periodic_sync.addr_resolved);
rp_past_complete(conn, ctx);
break;
default:
/* Ignore other evts */
break;
}
}
static void rp_past_st_wait_next_evt(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (evt) {
case RP_PAST_EVT_RUN:
#if defined(CONFIG_BT_PERIPHERAL)
/* If sync_conn_event_count is this connection event,
* we have to wait for drift correction for this event to be applied -
* continue processing in the next conn event
*/
if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL &&
ctx->data.periodic_sync.sync_conn_event_count == ull_conn_event_counter(conn)) {
return;
}
#endif /* CONFIG_BT_PERIPHERAL */
/* Hand over to ULL */
ull_sync_transfer_received(conn,
ctx->data.periodic_sync.id,
&ctx->data.periodic_sync.sync_info,
ctx->data.periodic_sync.conn_event_count,
ctx->data.periodic_sync.last_pa_event_counter,
ctx->data.periodic_sync.sid,
ctx->data.periodic_sync.addr_type,
ctx->data.periodic_sync.sca,
ctx->data.periodic_sync.phy,
ctx->data.periodic_sync.adv_addr,
ctx->data.periodic_sync.sync_conn_event_count,
ctx->data.periodic_sync.addr_resolved);
rp_past_complete(conn, ctx);
break;
default:
/* Ignore other evts */
break;
}
}
static void rp_past_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (ctx->state) {
case RP_PAST_STATE_IDLE:
rp_past_st_idle(conn, ctx, evt, param);
break;
case RP_PAST_STATE_WAIT_RX:
rp_past_st_wait_rx(conn, ctx, evt, param);
break;
case RP_PAST_STATE_WAIT_NEXT_EVT:
rp_past_st_wait_next_evt(conn, ctx, evt, param);
break;
#if defined(CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY)
case RP_PAST_STATE_WAIT_RESOLVE_INTERFACE_AVAIL:
rp_past_st_wait_resolve_if(conn, ctx, evt, param);
break;
case RP_PAST_STATE_WAIT_RESOLVE_COMPLETE:
/* Waiting for callback - do nothing */
break;
#endif /* CONFIG_BT_CTLR_SW_DEFERRED_PRIVACY */
default:
/* Unknown state */
LL_ASSERT(0);
break;
}
}
void llcp_rp_past_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx)
{
rp_past_execute_fsm(conn, ctx, RP_PAST_EVT_RX, rx->pdu);
}
void llcp_rp_past_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param)
{
rp_past_execute_fsm(conn, ctx, RP_PAST_EVT_RUN, param);
}
#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_RECEIVER */
#if defined(CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER)
/* LLCP Local Procedure FSM states */
enum {
LP_PAST_STATE_IDLE,
LP_PAST_STATE_WAIT_TX_REQ,
LP_PAST_STATE_WAIT_OFFSET_CALC,
LP_PAST_STATE_WAIT_TX_ACK,
LP_PAST_STATE_WAIT_EVT_DONE,
};
/* LLCP Local Procedure PAST (sender) FSM events */
enum {
/* Procedure run */
LP_PAST_EVT_RUN,
/* Offset calculation reply received */
LP_PAST_EVT_OFFSET_CALC_REPLY,
/* RX received in connection event */
LP_PAST_EVT_RX_RECEIVED,
/* RX not received in connection event */
LP_PAST_EVT_NO_RX_RECEIVED,
/* Ack received */
LP_PAST_EVT_ACK,
};
static void lp_past_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param);
static void lp_past_complete(struct ll_conn *conn, struct proc_ctx *ctx)
{
llcp_lr_complete(conn);
ctx->state = LP_PAST_STATE_IDLE;
}
static void lp_past_tx(struct ll_conn *conn, struct proc_ctx *ctx)
{
struct node_tx *tx;
struct pdu_data *pdu;
if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) {
if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL) {
uint32_t offset_us;
/* Correct offset can be calculated now that we know the event start time */
offset_us = ctx->data.periodic_sync.offset_us -
ctx->data.periodic_sync.conn_start_to_actual_us;
llcp_pdu_fill_sync_info_offset(&ctx->data.periodic_sync.sync_info,
offset_us);
}
}
/* 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_periodic_sync_ind(ctx, pdu);
/* Enqueue LL Control PDU towards LLL */
llcp_tx_enqueue(conn, tx);
/* Wait for TX Ack */
ctx->state = LP_PAST_STATE_WAIT_TX_ACK;
ctx->node_ref.tx_ack = tx;
}
void llcp_lp_past_offset_calc_reply(struct ll_conn *conn, struct proc_ctx *ctx)
{
lp_past_execute_fsm(conn, ctx, LP_PAST_EVT_OFFSET_CALC_REPLY, NULL);
}
static void lp_past_offset_calc_req(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_PAST_STATE_WAIT_TX_REQ;
} else {
/* Call ULL and wait for reply */
ctx->state = LP_PAST_STATE_WAIT_OFFSET_CALC;
ull_conn_past_sender_offset_request(conn);
}
}
static void lp_past_st_wait_tx_req(struct ll_conn *conn, struct proc_ctx *ctx,
uint8_t evt, void *param)
{
switch (evt) {
case LP_PAST_EVT_RUN:
lp_past_offset_calc_req(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
static void lp_past_st_wait_offset_calc(struct ll_conn *conn, struct proc_ctx *ctx,
uint8_t evt, void *param)
{
switch (evt) {
case LP_PAST_EVT_OFFSET_CALC_REPLY:
if (ull_ref_get(&conn->ull)) {
/* Connection event still ongoing, wait for done */
ctx->state = LP_PAST_STATE_WAIT_EVT_DONE;
} else if (ctx->data.periodic_sync.conn_evt_trx) {
/* Connection event done with successful rx from peer */
lp_past_tx(conn, ctx);
} else {
/* Reset state and try again in next connection event */
ctx->state = LP_PAST_STATE_WAIT_TX_REQ;
}
break;
default:
/* Ignore other evts */
break;
}
}
void llcp_lp_past_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, struct node_tx *tx)
{
lp_past_execute_fsm(conn, ctx, LP_PAST_EVT_ACK, tx->pdu);
}
void llcp_lp_past_conn_evt_done(struct ll_conn *conn, struct proc_ctx *ctx)
{
if (ctx->data.periodic_sync.conn_evt_trx != 0) {
lp_past_execute_fsm(conn, ctx, LP_PAST_EVT_RX_RECEIVED, NULL);
} else {
lp_past_execute_fsm(conn, ctx, LP_PAST_EVT_NO_RX_RECEIVED, NULL);
}
}
static void lp_past_state_wait_evt_done(struct ll_conn *conn, struct proc_ctx *ctx,
uint8_t evt, void *param)
{
/* Offset calculation has to be done in a connection event where a packet
* was received from the peer.
* From Core Spec v5.4, Vol 6, Part B, Section 2.4.2.27:
* syncConnEventCount shall be set to the connection event counter for the
* connection event that the sending device used in determining the contents
* of this PDU. This shall be a connection event where the sending device
* received a packet from the device it will send the LL_PERIODIC_SYNC_-
* IND PDU to and, if the sending device is the Peripheral on the piconet con-
* taining those two devices, it used the received packet to synchronize its
* anchor point
*/
switch (evt) {
case LP_PAST_EVT_RX_RECEIVED:
lp_past_tx(conn, ctx);
break;
case LP_PAST_EVT_NO_RX_RECEIVED:
/* Try again in next connection event */
ctx->state = LP_PAST_STATE_WAIT_TX_REQ;
break;
}
}
static void lp_past_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
switch (evt) {
case LP_PAST_EVT_RUN:
/* In case feature exchange completed after past was enqueued
* peer PAST receiver support should be confirmed
*/
if (feature_peer_periodic_sync_recv(conn)) {
lp_past_offset_calc_req(conn, ctx, evt, param);
} else {
/* Peer doesn't support PAST Receiver; HCI gives us no way to
* indicate this to the host so just silently complete the procedure
*/
lp_past_complete(conn, ctx);
}
break;
default:
/* Ignore other evts */
break;
}
}
static void lp_past_st_wait_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (evt) {
case LP_PAST_EVT_ACK:
/* Received Ack - All done now */
lp_past_complete(conn, ctx);
break;
default:
/* Ignore other evts */
break;
}
}
static void lp_past_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (ctx->state) {
case LP_PAST_STATE_IDLE:
lp_past_st_idle(conn, ctx, evt, param);
break;
case LP_PAST_STATE_WAIT_TX_REQ:
lp_past_st_wait_tx_req(conn, ctx, evt, param);
break;
case LP_PAST_STATE_WAIT_OFFSET_CALC:
lp_past_st_wait_offset_calc(conn, ctx, evt, param);
break;
case LP_PAST_STATE_WAIT_TX_ACK:
lp_past_st_wait_tx_ack(conn, ctx, evt, param);
break;
case LP_PAST_STATE_WAIT_EVT_DONE:
lp_past_state_wait_evt_done(conn, ctx, evt, param);
break;
default:
/* Unknown state */
LL_ASSERT(0);
break;
}
}
void llcp_lp_past_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param)
{
lp_past_execute_fsm(conn, ctx, LP_PAST_EVT_RUN, param);
}
#endif /* CONFIG_BT_CTLR_SYNC_TRANSFER_SENDER */