zephyr/subsys/bluetooth/controller/ll_sw/ull_sched.c
Vinayak Kariappa Chettimada 82925a8c99 Bluetooth: controller: Avoid sizeof to access ULL/LLL struct member
Avoid using sizeof to access ULL and LLL struct members.
Based on the alignment requirements of structures, due to
padding between structure members, use of sizeof of previous
struct member to access next struct member is incorrect.

Continue to use explicitly stored parent pointer to access
ULL context. Combine event header and ULL header so that
the parent pointer point directly to the combined ULL
struct.

Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
2021-04-19 10:26:38 +02:00

565 lines
15 KiB
C

/*
* Copyright (c) 2018-2019 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <sys/byteorder.h>
#include "hal/ccm.h"
#include "hal/radio.h"
#include "hal/ticker.h"
#include "util/memq.h"
#include "util/mayfly.h"
#include "ticker/ticker.h"
#include "pdu.h"
#include "lll.h"
#include "lll/lll_vendor.h"
#include "lll_scan.h"
#include "lll_conn.h"
#include "ull_scan_types.h"
#include "ull_conn_types.h"
#include "ull_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_sched
#include "common/log.h"
#include "hal/debug.h"
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
static void win_offset_calc(struct ll_conn *conn_curr, uint8_t is_select,
uint32_t *ticks_to_offset_next,
uint16_t conn_interval, uint8_t *offset_max,
uint8_t *win_offset);
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
static void after_mstr_offset_get(uint16_t conn_interval, uint32_t ticks_slot,
uint32_t ticks_anchor,
uint32_t *win_offset_us);
static void ticker_op_cb(uint32_t status, void *param);
void ull_sched_after_mstr_slot_get(uint8_t user_id, uint32_t ticks_slot_abs,
uint32_t *ticks_anchor, uint32_t *us_offset)
{
uint32_t ticks_to_expire_prev;
uint32_t ticks_slot_abs_prev;
uint32_t ticks_to_expire;
uint8_t ticker_id_prev;
uint8_t ticker_id;
ticks_slot_abs += HAL_TICKER_US_TO_TICKS(EVENT_JITTER_US << 3);
ticker_id = ticker_id_prev = 0xff;
ticks_to_expire = ticks_to_expire_prev = *us_offset = 0U;
ticks_slot_abs_prev = 0U;
while (1) {
uint32_t volatile ret_cb;
struct ll_conn *conn;
uint32_t ret;
bool success;
ret_cb = TICKER_STATUS_BUSY;
ret = ticker_next_slot_get(TICKER_INSTANCE_ID_CTLR, user_id,
&ticker_id, ticks_anchor,
&ticks_to_expire, ticker_op_cb,
(void *)&ret_cb);
if (ret == TICKER_STATUS_BUSY) {
while (ret_cb == TICKER_STATUS_BUSY) {
ticker_job_sched(TICKER_INSTANCE_ID_CTLR,
user_id);
}
}
success = (ret_cb == TICKER_STATUS_SUCCESS);
LL_ASSERT(success);
if (ticker_id == 0xff) {
break;
}
if ((ticker_id < TICKER_ID_CONN_BASE) ||
(ticker_id > TICKER_ID_CONN_LAST)) {
continue;
}
conn = ll_conn_get(ticker_id - TICKER_ID_CONN_BASE);
if (conn && !conn->lll.role) {
uint32_t ticks_to_expire_normal = ticks_to_expire;
uint32_t ticks_slot_abs_curr = 0;
#if defined(CONFIG_BT_CTLR_LOW_LAT)
#if defined(CONFIG_BT_CTLR_XTAL_ADVANCED)
if (conn->ull.ticks_prepare_to_start & XON_BITMASK) {
uint32_t ticks_prepare_to_start =
MAX(conn->ull.ticks_active_to_start,
conn->ull.ticks_preempt_to_start);
ticks_slot_abs_curr =
conn->ull.ticks_prepare_to_start &
~XON_BITMASK;
ticks_to_expire_normal -=
ticks_slot_abs_curr -
ticks_prepare_to_start;
} else
#endif /* CONFIG_BT_CTLR_XTAL_ADVANCED */
{
uint32_t ticks_prepare_to_start =
MAX(conn->ull.ticks_active_to_start,
conn->ull.ticks_prepare_to_start);
ticks_slot_abs_curr = ticks_prepare_to_start;
}
#endif
ticks_slot_abs_curr += conn->ull.ticks_slot;
if ((ticker_id_prev != 0xff) &&
(ticker_ticks_diff_get(ticks_to_expire_normal,
ticks_to_expire_prev) >
(ticks_slot_abs_prev + ticks_slot_abs))) {
break;
}
ticker_id_prev = ticker_id;
ticks_to_expire_prev = ticks_to_expire_normal;
ticks_slot_abs_prev = ticks_slot_abs_curr;
}
}
if (ticker_id_prev != 0xff) {
*us_offset = HAL_TICKER_TICKS_TO_US(ticks_to_expire_prev +
ticks_slot_abs_prev) +
(EVENT_JITTER_US << 3);
}
}
#if defined(CONFIG_BT_CENTRAL)
void ull_sched_mfy_after_mstr_offset_get(void *param)
{
struct lll_prepare_param *p = param;
struct lll_scan *lll = p->param;
uint32_t ticks_slot_overhead;
struct ll_conn *conn;
conn = HDR_LLL2ULL(lll->conn);
if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) {
ticks_slot_overhead = MAX(conn->ull.ticks_active_to_start,
conn->ull.ticks_prepare_to_start);
} else {
ticks_slot_overhead = 0U;
}
after_mstr_offset_get(lll->conn->interval,
(ticks_slot_overhead + conn->ull.ticks_slot),
p->ticks_at_expire, &lll->conn_win_offset_us);
}
#endif /* CONFIG_BT_CENTRAL */
void ull_sched_mfy_win_offset_use(void *param)
{
struct ll_conn *conn = param;
uint32_t ticks_slot_overhead;
uint16_t win_offset;
if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) {
ticks_slot_overhead = MAX(conn->ull.ticks_active_to_start,
conn->ull.ticks_prepare_to_start);
} else {
ticks_slot_overhead = 0U;
}
after_mstr_offset_get(conn->lll.interval,
(ticks_slot_overhead + conn->ull.ticks_slot),
conn->llcp.conn_upd.ticks_anchor,
&conn->llcp_cu.win_offset_us);
win_offset = conn->llcp_cu.win_offset_us / CONN_INT_UNIT_US;
sys_put_le16(win_offset, (void *)conn->llcp.conn_upd.pdu_win_offset);
/* move to offset calculated state */
conn->llcp_cu.state = LLCP_CUI_STATE_OFFS_RDY;
}
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
void ull_sched_mfy_free_win_offset_calc(void *param)
{
uint32_t ticks_to_offset_default = 0U;
uint32_t *ticks_to_offset_next;
struct ll_conn *conn = param;
uint8_t offset_max = 6U;
ticks_to_offset_next = &ticks_to_offset_default;
#if defined(CONFIG_BT_PERIPHERAL)
if (conn->lll.role) {
conn->llcp_conn_param.ticks_to_offset_next =
conn->slave.ticks_to_offset;
ticks_to_offset_next =
&conn->llcp_conn_param.ticks_to_offset_next;
}
#endif /* CONFIG_BT_PERIPHERAL */
win_offset_calc(conn, 0, ticks_to_offset_next,
conn->llcp_conn_param.interval_max, &offset_max,
(void *)conn->llcp_conn_param.pdu_win_offset0);
/* move to offset calculated state */
conn->llcp_conn_param.state = LLCP_CPR_STATE_OFFS_RDY;
}
void ull_sched_mfy_win_offset_select(void *param)
{
#define OFFSET_S_MAX 6
#define OFFSET_M_MAX 6
uint16_t win_offset_m[OFFSET_M_MAX] = {0, };
uint8_t offset_m_max = OFFSET_M_MAX;
struct ll_conn *conn = param;
uint8_t offset_index_s = 0U;
uint8_t has_offset_s = 0U;
uint32_t ticks_to_offset;
uint16_t win_offset_s;
ticks_to_offset = HAL_TICKER_US_TO_TICKS(conn->llcp_conn_param.offset0 *
CONN_INT_UNIT_US);
win_offset_calc(conn, 1, &ticks_to_offset,
conn->llcp_conn_param.interval_max, &offset_m_max,
(void *)win_offset_m);
while (offset_index_s < OFFSET_S_MAX) {
uint8_t offset_index_m = 0U;
win_offset_s =
sys_get_le16((uint8_t *)&conn->llcp_conn_param.offset0 +
(sizeof(uint16_t) * offset_index_s));
while (offset_index_m < offset_m_max) {
if (win_offset_s != 0xffff) {
if (win_offset_s ==
win_offset_m[offset_index_m]) {
break;
}
has_offset_s = 1U;
}
offset_index_m++;
}
if (offset_index_m < offset_m_max) {
break;
}
offset_index_s++;
}
if (offset_index_s < OFFSET_S_MAX) {
conn->llcp_cu.win_offset_us = win_offset_s * CONN_INT_UNIT_US;
sys_put_le16(win_offset_s,
(void *)conn->llcp.conn_upd.pdu_win_offset);
/* move to offset calculated state */
conn->llcp_cu.state = LLCP_CUI_STATE_OFFS_RDY;
} else if (!has_offset_s) {
conn->llcp_cu.win_offset_us = win_offset_m[0] *
CONN_INT_UNIT_US;
sys_put_le16(win_offset_m[0],
(void *)conn->llcp.conn_upd.pdu_win_offset);
/* move to offset calculated state */
conn->llcp_cu.state = LLCP_CUI_STATE_OFFS_RDY;
} else {
struct pdu_data *pdu_ctrl_tx;
/* send reject_ind_ext */
pdu_ctrl_tx = CONTAINER_OF(conn->llcp.conn_upd.pdu_win_offset,
struct pdu_data,
llctrl.conn_update_ind.win_offset);
pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL;
pdu_ctrl_tx->len =
offsetof(struct pdu_data_llctrl, reject_ext_ind) +
sizeof(struct pdu_data_llctrl_reject_ext_ind);
pdu_ctrl_tx->llctrl.opcode =
PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND;
pdu_ctrl_tx->llctrl.reject_ext_ind.reject_opcode =
PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ;
pdu_ctrl_tx->llctrl.reject_ext_ind.error_code =
BT_HCI_ERR_UNSUPP_LL_PARAM_VAL;
/* move to conn param reject */
conn->llcp_cu.state = LLCP_CUI_STATE_REJECT;
}
#undef OFFSET_S_MAX
#undef OFFSET_M_MAX
}
static void win_offset_calc(struct ll_conn *conn_curr, uint8_t is_select,
uint32_t *ticks_to_offset_next,
uint16_t conn_interval, uint8_t *offset_max,
uint8_t *win_offset)
{
uint32_t ticks_prepare_reduced = 0U;
uint32_t ticks_to_expire_prev;
uint32_t ticks_slot_abs_prev;
uint32_t ticks_slot_abs = 0U;
uint32_t ticks_anchor_prev;
uint32_t ticks_to_expire;
uint8_t ticker_id_other;
uint8_t ticker_id_prev;
uint32_t ticks_anchor;
uint8_t offset_index;
uint8_t ticker_id;
uint16_t offset;
#if defined(CONFIG_BT_CTLR_LOW_LAT)
#if defined(CONFIG_BT_CTLR_XTAL_ADVANCED)
if (conn_curr->ull.ticks_prepare_to_start & XON_BITMASK) {
uint32_t ticks_prepare_to_start =
MAX(conn_curr->ull.ticks_active_to_start,
conn_curr->ull.ticks_preempt_to_start);
ticks_slot_abs = conn_curr->ull.ticks_prepare_to_start &
~XON_BITMASK;
ticks_prepare_reduced = ticks_slot_abs - ticks_prepare_to_start;
} else
#endif /* CONFIG_BT_CTLR_XTAL_ADVANCED */
{
uint32_t ticks_prepare_to_start =
MAX(conn_curr->ull.ticks_active_to_start,
conn_curr->ull.ticks_prepare_to_start);
ticks_slot_abs = ticks_prepare_to_start;
}
#endif
ticks_slot_abs += conn_curr->ull.ticks_slot;
if (conn_curr->lll.role) {
ticks_slot_abs += HAL_TICKER_US_TO_TICKS(EVENT_TIES_US);
}
ticker_id = ticker_id_prev = ticker_id_other = 0xff;
ticks_to_expire = ticks_to_expire_prev = ticks_anchor =
ticks_anchor_prev = offset_index = offset = 0U;
ticks_slot_abs_prev = 0U;
do {
uint32_t volatile ret_cb;
struct ll_conn *conn;
uint32_t ret;
bool success;
ret_cb = TICKER_STATUS_BUSY;
ret = ticker_next_slot_get(TICKER_INSTANCE_ID_CTLR,
TICKER_USER_ID_ULL_LOW,
&ticker_id, &ticks_anchor,
&ticks_to_expire, ticker_op_cb,
(void *)&ret_cb);
if (ret == TICKER_STATUS_BUSY) {
while (ret_cb == TICKER_STATUS_BUSY) {
ticker_job_sched(TICKER_INSTANCE_ID_CTLR,
TICKER_USER_ID_ULL_LOW);
}
}
success = (ret_cb == TICKER_STATUS_SUCCESS);
LL_ASSERT(success);
if (ticker_id == 0xff) {
break;
}
/* ticks_anchor shall not change during this loop */
if ((ticker_id_prev != 0xff) &&
(ticks_anchor != ticks_anchor_prev)) {
LL_ASSERT(0);
}
/* consider advertiser time as available. Any other time used by
* tickers declared outside the controller is also available.
*/
#if defined(CONFIG_BT_BROADCASTER)
if ((ticker_id < TICKER_ID_ADV_BASE) ||
(ticker_id > TICKER_ID_CONN_LAST))
#else /* !CONFIG_BT_BROADCASTER */
if ((ticker_id < TICKER_ID_SCAN_BASE) ||
(ticker_id > TICKER_ID_CONN_LAST))
#endif /* !CONFIG_BT_BROADCASTER */
{
continue;
}
if (ticker_id < TICKER_ID_CONN_BASE) {
/* non conn role found which could have preempted a
* conn role, hence do not consider this free space
* and any further as free slot for offset,
*/
ticker_id_other = ticker_id;
continue;
}
/* TODO: handle scanner; for now we exit with as much we
* where able to fill (offsets).
*/
if (ticker_id_other != 0xff) {
break;
}
conn = ll_conn_get(ticker_id - TICKER_ID_CONN_BASE);
if ((conn != conn_curr) && (is_select || !conn->lll.role)) {
uint32_t ticks_to_expire_normal =
ticks_to_expire + ticks_prepare_reduced;
uint32_t ticks_slot_margin = 0U;
uint32_t ticks_slot_abs_curr = 0U;
#if defined(CONFIG_BT_CTLR_LOW_LAT)
#if defined(CONFIG_BT_CTLR_XTAL_ADVANCED)
if (conn->ull.ticks_prepare_to_start & XON_BITMASK) {
uint32_t ticks_prepare_to_start =
MAX(conn->ull.ticks_active_to_start,
conn->ull.ticks_preempt_to_start);
ticks_slot_abs_curr =
conn->ull.ticks_prepare_to_start &
~XON_BITMASK;
ticks_to_expire_normal -=
ticks_slot_abs_curr -
ticks_prepare_to_start;
} else
#endif /* CONFIG_BT_CTLR_XTAL_ADVANCED */
{
uint32_t ticks_prepare_to_start =
MAX(conn->ull.ticks_active_to_start,
conn->ull.ticks_prepare_to_start);
ticks_slot_abs_curr = ticks_prepare_to_start;
}
#endif
ticks_slot_abs_curr += conn->ull.ticks_slot +
HAL_TICKER_US_TO_TICKS(CONN_INT_UNIT_US);
if (conn->lll.role) {
ticks_slot_margin =
HAL_TICKER_US_TO_TICKS(EVENT_TIES_US);
ticks_slot_abs_curr += ticks_slot_margin;
}
if (*ticks_to_offset_next < ticks_to_expire_normal) {
if (ticks_to_expire_prev <
*ticks_to_offset_next) {
ticks_to_expire_prev =
*ticks_to_offset_next;
}
while ((offset_index < *offset_max) &&
(ticker_ticks_diff_get(
ticks_to_expire_normal,
ticks_to_expire_prev) >=
(ticks_slot_abs_prev + ticks_slot_abs +
ticks_slot_margin))) {
offset = (ticks_to_expire_prev +
ticks_slot_abs_prev) /
HAL_TICKER_US_TO_TICKS(
CONN_INT_UNIT_US);
if (offset >= conn_interval) {
ticks_to_expire_prev = 0U;
break;
}
sys_put_le16(offset,
(win_offset +
(sizeof(uint16_t) *
offset_index)));
offset_index++;
ticks_to_expire_prev +=
HAL_TICKER_US_TO_TICKS(
CONN_INT_UNIT_US);
}
*ticks_to_offset_next = ticks_to_expire_prev;
if (offset >= conn_interval) {
break;
}
}
ticks_anchor_prev = ticks_anchor;
ticker_id_prev = ticker_id;
ticks_to_expire_prev = ticks_to_expire_normal;
ticks_slot_abs_prev = ticks_slot_abs_curr;
}
} while (offset_index < *offset_max);
if (ticker_id == 0xff) {
if (ticks_to_expire_prev < *ticks_to_offset_next) {
ticks_to_expire_prev = *ticks_to_offset_next;
}
while (offset_index < *offset_max) {
offset = (ticks_to_expire_prev + ticks_slot_abs_prev) /
HAL_TICKER_US_TO_TICKS(CONN_INT_UNIT_US);
if (offset >= conn_interval) {
ticks_to_expire_prev = 0U;
break;
}
sys_put_le16(offset, (win_offset + (sizeof(uint16_t) *
offset_index)));
offset_index++;
ticks_to_expire_prev += HAL_TICKER_US_TO_TICKS(
CONN_INT_UNIT_US);
}
*ticks_to_offset_next = ticks_to_expire_prev;
}
*offset_max = offset_index;
}
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
static void after_mstr_offset_get(uint16_t conn_interval, uint32_t ticks_slot,
uint32_t ticks_anchor,
uint32_t *win_offset_us)
{
uint32_t ticks_anchor_offset = ticks_anchor;
ull_sched_after_mstr_slot_get(TICKER_USER_ID_ULL_LOW, ticks_slot,
&ticks_anchor_offset, win_offset_us);
if (!*win_offset_us) {
return;
}
if ((ticks_anchor_offset - ticks_anchor) & BIT(HAL_TICKER_CNTR_MSBIT)) {
*win_offset_us -= HAL_TICKER_TICKS_TO_US(
ticker_ticks_diff_get(ticks_anchor,
ticks_anchor_offset));
} else {
*win_offset_us += HAL_TICKER_TICKS_TO_US(
ticker_ticks_diff_get(ticks_anchor_offset,
ticks_anchor));
}
if ((*win_offset_us & BIT(31)) == 0) {
uint32_t conn_interval_us = conn_interval * CONN_INT_UNIT_US;
while (*win_offset_us > conn_interval_us) {
*win_offset_us -= conn_interval_us;
}
}
}
static void ticker_op_cb(uint32_t status, void *param)
{
*((uint32_t volatile *)param) = status;
}