/*
* Copyright (c) 2016-2017 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <soc.h>
#include <device.h>
#include <clock_control.h>
#include <bluetooth/hci.h>
#include <misc/util.h>
#include "ll.h"
#include "hal/cpu.h"
#include "hal/rand.h"
#include "hal/ecb.h"
#include "hal/ccm.h"
#include "hal/radio.h"
#include "hal/debug.h"
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "util/mayfly.h"
#include "ticker/ticker.h"
#include "pdu.h"
#include "ctrl.h"
#include "ctrl_internal.h"
#include "ll_filter.h"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#include "common/log.h"
#define RADIO_TIFS 150
#define RADIO_CONN_EVENTS(x, y) ((u16_t)(((x) + (y) - 1) / (y)))
/* Convert payload length to tx/rx time
* On 1M PHY, add 14 bytes for preamble (1), access address (4),
* header (2), MIC (4), and CRC (3). Convert to bits (1 Msps).
*/
#define RADIO_PKT_TIME(octets) (((octets) + 14) << 3)
#define RADIO_TICKER_JITTER_US 16
#define RADIO_TICKER_START_PART_US 300
#define RADIO_TICKER_XTAL_OFFSET_US 1200
#define RADIO_TICKER_PREEMPT_PART_US 0
#define RADIO_TICKER_PREEMPT_PART_MIN_US 0
#define RADIO_TICKER_PREEMPT_PART_MAX_US RADIO_TICKER_XTAL_OFFSET_US
#if defined(CONFIG_BT_CTLR_CONN_RSSI)
#define RADIO_RSSI_SAMPLE_COUNT 10
#define RADIO_RSSI_THRESHOLD 4
#endif /* CONFIG_BT_CTLR_CONN_RSSI */
#define RADIO_IRK_COUNT_MAX 8
#define SILENT_CONNECTION 0
enum role {
ROLE_NONE,
ROLE_ADV,
ROLE_SCAN,
ROLE_SLAVE,
ROLE_MASTER,
};
enum state {
STATE_NONE,
STATE_RX,
STATE_TX,
STATE_CLOSE,
STATE_STOP,
STATE_ABORT,
};
struct advertiser {
struct shdr hdr;
u8_t is_enabled:1;
u8_t chan_map_current:3;
u8_t rfu:4;
#if defined(CONFIG_BT_CTLR_ADV_EXT)
u8_t phy_p:3;
#endif /* CONFIG_BT_CTLR_ADV_EXT */
u8_t chan_map:3;
u8_t filter_policy:2;
#if defined(CONFIG_BT_CTLR_PRIVACY)
u8_t rl_idx;
#endif /* CONFIG_BT_CTLR_PRIVACY */
struct radio_adv_data adv_data;
struct radio_adv_data scan_data;
struct connection *conn;
};
struct scanner {
struct shdr hdr;
u8_t is_enabled:1;
u8_t state:1;
u8_t chan:2;
u8_t rfu:4;
#if defined(CONFIG_BT_CTLR_ADV_EXT)
u8_t phy:3;
#endif /* CONFIG_BT_CTLR_ADV_EXT */
u8_t type:1;
u8_t filter_policy:2;
u8_t adv_addr_type:1;
u8_t init_addr_type:1;
#if defined(CONFIG_BT_CTLR_PRIVACY)
u8_t rpa_gen:1;
/* initiator only */
u8_t rl_idx;
#endif /* CONFIG_BT_CTLR_PRIVACY */
u8_t init_addr[BDADDR_SIZE];
u8_t adv_addr[BDADDR_SIZE];
u32_t ticks_window;
u16_t conn_interval;
u16_t conn_latency;
u16_t conn_timeout;
u32_t ticks_conn_slot;
struct connection *conn;
u32_t win_offset_us;
};
static struct {
struct device *hf_clock;
u32_t ticks_anchor;
u32_t remainder_anchor;
u8_t volatile ticker_id_prepare;
u8_t volatile ticker_id_event;
u8_t volatile ticker_id_stop;
enum role volatile role;
enum state state;
struct advertiser advertiser;
struct scanner scanner;
void *conn_pool;
void *conn_free;
u8_t connection_count;
struct connection *conn_curr;
u8_t packet_counter;
u8_t crc_expire;
u8_t data_chan_map[5];
u8_t data_chan_count;
u8_t sca;
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
/* DLE global settings */
u16_t default_tx_octets;
u16_t default_tx_time;
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#if defined(CONFIG_BT_CTLR_PHY)
u16_t default_phy_tx;
u16_t default_phy_rx;
#endif /* CONFIG_BT_CTLR_PHY */
/** @todo below members to be made role specific and quota managed for
* Rx-es.
*/
/* Advertiser, Scanner, and Connections Rx data pool */
void *pkt_rx_data_pool;
void *pkt_rx_data_free;
u16_t packet_data_octets_max;
u16_t packet_rx_data_pool_size;
u16_t packet_rx_data_size;
u8_t packet_rx_data_count;
/* Free queue Rx data buffers */
struct radio_pdu_node_rx **packet_rx;
u8_t packet_rx_count;
u8_t volatile packet_rx_last;
u8_t packet_rx_acquire;
/* Controller to Host event-cum-data queue */
void *link_rx_pool;
void *link_rx_free;
void *link_rx_head;
void *volatile link_rx_tail;
u8_t link_rx_data_quota;
/* Connections common Tx ctrl and data pool */
void *pkt_tx_ctrl_pool;
void *pkt_tx_ctrl_free;
void *pkt_tx_data_pool;
void *pkt_tx_data_free;
u16_t packet_tx_data_size;
/* Host to Controller Tx, and Controller to Host Num complete queue */
struct pdu_data_q_tx *pkt_tx;
struct pdu_data_q_tx *pkt_release;
u8_t packet_tx_count;
u8_t volatile packet_tx_first;
u8_t packet_tx_last;
u8_t packet_release_first;
u8_t volatile packet_release_last;
u16_t fc_handle[TRIPLE_BUFFER_SIZE];
u8_t volatile fc_req;
u8_t fc_ack;
u8_t fc_ena;
u32_t ticks_active_to_start;
struct connection *conn_upd;
} _radio;
static u16_t const gc_lookup_ppm[] = { 500, 250, 150, 100, 75, 50, 30, 20 };
static void common_init(void);
static void ticker_success_assert(u32_t status, void *params);
static void ticker_stop_adv_assert(u32_t status, void *params);
static void ticker_stop_scan_assert(u32_t status, void *params);
static void ticker_update_adv_assert(u32_t status, void *params);
static void ticker_update_slave_assert(u32_t status, void *params);
static void event_inactive(u32_t ticks_at_expire, u32_t remainder,
u16_t lazy, void *context);
#if defined(RADIO_UNIT_TEST) && \
defined(CONFIG_BT_CTLR_CHAN_SEL_2)
static void chan_sel_2_ut(void);
#endif /* CONFIG_BT_CTLR_CHAN_SEL_2 */
static void adv_setup(void);
static void event_adv(u32_t ticks_at_expire, u32_t remainder, u16_t lazy,
void *context);
static void event_scan(u32_t ticks_at_expire, u32_t remainder, u16_t lazy,
void *context);
static void event_slave_prepare(u32_t ticks_at_expire, u32_t remainder,
u16_t lazy, void *context);
static void event_slave(u32_t ticks_at_expire, u32_t remainder, u16_t lazy,
void *context);
static void event_master_prepare(u32_t ticks_at_expire, u32_t remainder,
u16_t lazy, void *context);
static void event_master(u32_t ticks_at_expire, u32_t remainder, u16_t lazy,
void *context);
static void rx_packet_set(struct connection *conn,
struct pdu_data *pdu_data_rx);
static void tx_packet_set(struct connection *conn,
struct pdu_data *pdu_data_tx);
static void prepare_pdu_data_tx(struct connection *conn,
struct pdu_data **pdu_data_tx);
static void packet_rx_allocate(u8_t max);
static u8_t packet_rx_acquired_count_get(void);
static struct radio_pdu_node_rx *packet_rx_reserve_get(u8_t count);
static void packet_rx_enqueue(void);
static void packet_tx_enqueue(u8_t max);
static struct pdu_data *empty_tx_enqueue(struct connection *conn);
static void ctrl_tx_enqueue(struct connection *conn,
struct radio_pdu_node_tx *node_tx);
static void pdu_node_tx_release(u16_t handle,
struct radio_pdu_node_tx *node_tx);
static void connection_release(struct connection *conn);
static void terminate_ind_rx_enqueue(struct connection *conn, u8_t reason);
static u32_t conn_update(struct connection *conn, struct pdu_data *pdu_data_rx);
#if defined(CONFIG_BT_CTLR_SCHED_ADVANCED)
static u32_t conn_update_req(struct connection *conn);
#endif /* CONFIG_BT_CTLR_SCHED_ADVANCED */
static u32_t chan_map_update(struct connection *conn,
struct pdu_data *pdu_data_rx);
#if defined(CONFIG_BT_CTLR_PHY)
static inline u32_t phy_upd_ind(struct radio_pdu_node_rx *radio_pdu_node_rx,
u8_t *rx_enqueue);
#endif /* CONFIG_BT_CTLR_PHY */
#if defined(CONFIG_BT_CTLR_LE_ENC)
static void enc_req_reused_send(struct connection *conn,
struct radio_pdu_node_tx *node_tx);
static void enc_rsp_send(struct connection *conn);
static void start_enc_rsp_send(struct connection *conn,
struct pdu_data *pdu_ctrl_tx);
static void pause_enc_rsp_send(struct connection *conn);
#endif /* CONFIG_BT_CTLR_LE_ENC */
static void unknown_rsp_send(struct connection *conn, u8_t type);
static void feature_rsp_send(struct connection *conn);
static void version_ind_send(struct connection *conn);
#if defined(CONFIG_BT_CTLR_LE_PING)
static void ping_resp_send(struct connection *conn);
#endif /* CONFIG_BT_CTLR_LE_PING */
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) || \
defined(CONFIG_BT_CTLR_PHY)
static void reject_ext_ind_send(struct connection *conn, u8_t reject_opcode,
u8_t error_code);
#endif
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
#if !defined(CONFIG_BT_CTLR_PHY)
static void length_resp_send(struct connection *conn, u16_t eff_rx_octets,
u16_t eff_tx_octets);
#else /* CONFIG_BT_CTLR_PHY */
static void length_resp_send(struct connection *conn, u16_t eff_rx_octets,
u16_t eff_rx_time, u16_t eff_tx_octets,
u16_t eff_tx_time);
#endif /* CONFIG_BT_CTLR_PHY */
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#if defined(CONFIG_BT_CTLR_PHY)
static void phy_rsp_send(struct connection *conn);
#endif /* CONFIG_BT_CTLR_PHY */
static u32_t role_disable(u8_t ticker_id_primary, u8_t ticker_id_stop);
static void rx_fc_lock(u16_t handle);
/*****************************************************************************
*RADIO
****************************************************************************/
u32_t radio_init(void *hf_clock, u8_t sca, u8_t connection_count_max,
u8_t rx_count_max, u8_t tx_count_max,
u16_t packet_data_octets_max,
u16_t packet_tx_data_size, u8_t *mem_radio,
u16_t mem_size)
{
u32_t retcode;
u8_t *mem_radio_end;
/* intialise hf_clock device to use in prepare */
_radio.hf_clock = hf_clock;
/* initialise SCA */
_radio.sca = sca;
/* initialised radio mem end variable */
mem_radio_end = mem_radio + mem_size;
/* initialise connection context memory */
_radio.connection_count = connection_count_max;
_radio.conn_pool = mem_radio;
mem_radio += (sizeof(struct connection) * _radio.connection_count);
/* initialise rx and tx queue counts */
/* additional for pdu to NACK or receive empty PDU,
* 1 scan resp and 1* ctrl event.
*/
rx_count_max += 3;
/* additional pdu to send enc_req ctrl pdu */
tx_count_max += 1;
_radio.packet_rx_count = (rx_count_max + 1);
_radio.packet_tx_count = (tx_count_max + 1);
_radio.link_rx_data_quota = rx_count_max;
/* initialise rx queue memory */
_radio.packet_rx = (void *)mem_radio;
mem_radio +=
(sizeof(struct radio_pdu_node_rx *)*_radio.packet_rx_count);
/* initialise tx queue memory */
_radio.pkt_tx = (void *)mem_radio;
mem_radio += (sizeof(struct pdu_data_q_tx) * _radio.packet_tx_count);
/* initialise tx release queue memory */
_radio.pkt_release = (void *)mem_radio;
mem_radio += (sizeof(struct pdu_data_q_tx) * _radio.packet_tx_count);
/* initialise rx memory size and count */
_radio.packet_data_octets_max = packet_data_octets_max;
if ((PDU_AC_SIZE_MAX + PDU_AC_SIZE_EXTRA) <
(offsetof(struct pdu_data, payload) +
_radio.packet_data_octets_max)) {
_radio.packet_rx_data_pool_size =
(MROUND(offsetof(struct radio_pdu_node_rx, pdu_data) +
offsetof(struct pdu_data, payload) +
_radio.packet_data_octets_max) * rx_count_max);
} else {
_radio.packet_rx_data_pool_size =
(MROUND(offsetof(struct radio_pdu_node_rx, pdu_data) +
(PDU_AC_SIZE_MAX + PDU_AC_SIZE_EXTRA)) * rx_count_max);
}
_radio.packet_rx_data_size = PACKET_RX_DATA_SIZE_MIN;
_radio.packet_rx_data_count = (_radio.packet_rx_data_pool_size /
_radio.packet_rx_data_size);
/* initialise rx data pool memory */
_radio.pkt_rx_data_pool = mem_radio;
mem_radio += _radio.packet_rx_data_pool_size;
/* initialise rx link pool memory */
_radio.link_rx_pool = mem_radio;
mem_radio += (sizeof(void *) * 2 * (_radio.packet_rx_count +
_radio.connection_count));
/* initialise tx ctrl pool memory */
_radio.pkt_tx_ctrl_pool = mem_radio;
mem_radio += PACKET_TX_CTRL_SIZE_MIN * PACKET_MEM_COUNT_TX_CTRL;
/* initialise tx data memory size and count */
_radio.packet_tx_data_size =
MROUND(offsetof(struct radio_pdu_node_tx, pdu_data) +
offsetof(struct pdu_data, payload) +
packet_tx_data_size);
/* initialise tx data pool memory */
_radio.pkt_tx_data_pool = mem_radio;
mem_radio += (_radio.packet_tx_data_size * tx_count_max);
/* check for sufficient memory allocation for stack
* configuration.
*/
retcode = (mem_radio - mem_radio_end);
if (retcode) {
return (retcode + mem_size);
}
/* enable connection handle based on-off flow control feature.
* This is a simple flow control to rx data only on one selected
* connection handle.
* TODO: replace this feature with host-to-controller flowcontrol
* implementation/design.
*/
_radio.fc_ena = 1;
/* memory allocations */
common_init();
#if defined(RADIO_UNIT_TEST) && defined(CONFIG_BT_CTLR_CHAN_SEL_2)
chan_sel_2_ut();
#endif /* RADIO_UNIT_TEST && CONFIG_BT_CTLR_CHAN_SEL_2 */
return retcode;
}
void ll_reset(void)
{
u16_t conn_handle;
/* disable advertiser events */
role_disable(RADIO_TICKER_ID_ADV, RADIO_TICKER_ID_ADV_STOP);
/* disable oberver events */
role_disable(RADIO_TICKER_ID_SCAN, RADIO_TICKER_ID_SCAN_STOP);
/* disable connection events */
for (conn_handle = 0; conn_handle < _radio.connection_count;
conn_handle++) {
role_disable(RADIO_TICKER_ID_FIRST_CONNECTION + conn_handle,
TICKER_NULL);
}
/* reset controller context members */
_radio.advertiser.is_enabled = 0;
_radio.advertiser.conn = NULL;
_radio.scanner.is_enabled = 0;
_radio.scanner.conn = NULL;
_radio.packet_rx_data_size = PACKET_RX_DATA_SIZE_MIN;
_radio.packet_rx_data_count = (_radio.packet_rx_data_pool_size /
_radio.packet_rx_data_size);
_radio.packet_rx_last = 0;
_radio.packet_rx_acquire = 0;
_radio.link_rx_data_quota = _radio.packet_rx_count - 1;
_radio.packet_tx_first = 0;
_radio.packet_tx_last = 0;
_radio.packet_release_first = 0;
_radio.packet_release_last = 0;
/* reset whitelist and resolving list */
ll_filter_reset(false);
/* memory alloca