/*
* 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 "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"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLUETOOTH_DEBUG_HCI_DRIVER)
#include "common/log.h"
#define RADIO_TIFS 150
#define RADIO_CONN_EVENTS(x, y) ((u16_t)((x) / (y)))
#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_BLUETOOTH_CONTROLLER_CONN_RSSI)
#define RADIO_RSSI_SAMPLE_COUNT 10
#define RADIO_RSSI_THRESHOLD 4
#endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */
#define RADIO_IRK_COUNT_MAX 8
#define SILENT_CONNECTION 0
enum role {
ROLE_NONE,
ROLE_ADV,
ROLE_OBS,
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 chl_map:3;
u8_t chl_map_current:3;
u8_t filter_policy:2;
u8_t filter_enable_bitmask;
u8_t filter_addr_type_bitmask;
u8_t filter_bdaddr[8][BDADDR_SIZE];
struct radio_adv_data adv_data;
struct radio_adv_data scan_data;
struct connection *conn;
};
struct observer {
struct shdr hdr;
u8_t scan_type:1;
u8_t scan_state:1;
u8_t scan_chan:2;
u8_t filter_policy:2;
u8_t adv_addr_type:1;
u8_t init_addr_type:1;
u8_t adv_addr[BDADDR_SIZE];
u8_t init_addr[BDADDR_SIZE];
u32_t ticks_window;
u8_t filter_enable_bitmask;
u8_t filter_addr_type_bitmask;
u8_t filter_bdaddr[8][BDADDR_SIZE];
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;
u8_t filter_enable_bitmask;
u8_t filter_addr_type_bitmask;
u8_t filter_bdaddr[8][BDADDR_SIZE];
u8_t nirk;
u8_t irk[RADIO_IRK_COUNT_MAX][16];
struct advertiser advertiser;
struct observer observer;
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_BLUETOOTH_CONTROLLER_DATA_LENGTH)
/* DLE global settings */
u16_t default_tx_octets;
u16_t default_tx_time;
#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY)
u16_t default_phy_tx;
u16_t default_phy_rx;
#endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */
/** @todo below members to be made role specific and quota managed for
* Rx-es.
*/
/* Advertiser, Observer, 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_obs_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_BLUETOOTH_CONTROLLER_CHAN_SEL_2)
static void chan_sel_2_ut(void);
#endif /* CONFIG_BLUETOOTH_CONTROLLER_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_obs(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 u32_t conn_update(struct connection *conn, struct pdu_data *pdu_data_rx);
static u32_t is_peer_compatible(struct connection *conn);
static u32_t conn_update_req(struct connection *conn);
static u32_t chan_map_update(struct connection *conn,
struct pdu_data *pdu_data_rx);
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY)
static inline u32_t phy_upd_ind(struct radio_pdu_node_rx *radio_pdu_node_rx,
u8_t *rx_enqueue);
#endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */
static void enc_req_reused_send(struct connection *conn,
struct radio_pdu_node_tx *node_tx);
static void terminate_ind_rx_enqueue(struct connection *conn, u8_t reason);
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 unknown_rsp_send(struct connection *conn, u8_t type);
static void feature_rsp_send(struct connection *conn);
static void pause_enc_rsp_send(struct connection *conn);
static void version_ind_send(struct connection *conn);
#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING)
static void ping_resp_send(struct connection *conn);
#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */
static void reject_ind_ext_send(struct connection *conn, u8_t reject_opcode,
u8_t error_code);
#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH)
static void length_resp_send(struct connection *conn, u16_t eff_rx_octets,
u16_t eff_tx_octets);
#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY)
static void phy_rsp_send(struct connection *conn);
#endif /* CONFIG_BLUETOOTH_CONTROLLER_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 + 1) <
(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 + 1)) * 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_BLUETOOTH_CONTROLLER_CHAN_SEL_2)
chan_sel_2_ut();
#endif /* RADIO_UNIT_TEST && CONFIG_BLUETOOTH_CONTROLLER_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_OBS, RADIO_TICKER_ID_OBS_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.filter_enable_bitmask = 0;
_radio.nirk = 0;
_radio.advertiser.conn = NULL;
_radio.observer.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;
/* memory allocations */
common_init();
}
static void common_init(void)
{
void *link;
/* initialise connection pool. */
if (_radio.connection_count) {
mem_init(_radio.conn_pool, CONNECTION_T_SIZE,
_radio.connection_count,
&_radio.conn_free);
} else {
_radio.conn_free = NULL;
}
/* initialise rx pool. */
mem_init(_radio.pkt_rx_data_pool,
_radio.packet_rx_data_size,
_radio.packet_rx_data_count,
&_radio.pkt_rx_data_free);
/* initialise rx link pool. */
mem_init(_radio.link_rx_pool, (sizeof(void *) * 2),
(_radio.packet_rx_count + _radio.connection_count),
&_radio.link_rx_free);
/* initialise ctrl tx pool. */
mem_init(_radio.pkt_tx_ctrl_pool, PACKET_TX_CTRL_SIZE_MIN,
PACKET_MEM_COUNT_TX_CTRL, &_radio.pkt_tx_ctrl_free);
/* initialise data tx pool. */
mem_init(_radio.pkt_tx_data_pool, _radio.packet_tx_data_size,
(_radio.packet_tx_count - 1), &_radio.pkt_tx_data_free);
/* initialise the event-cum-data memq */
link = mem_acquire(&_radio.link_rx_free);
LL_ASSERT(link);
memq_init(link, &_radio.link_rx_head, (void *)&_radio.link_rx_tail);
/* initialise advertiser channel map */
_radio.advertiser.chl_map = 0x07;
/* initialise connection channel map */
_radio.data_chan_map[0] = 0xFF;
_radio.data_chan_map[1] = 0xFF;
_radio.data_chan_map[2] = 0xFF;
_radio.data_chan_map[3] = 0xFF;
_radio.data_chan_map[4] = 0x1F;
_radio.data_chan_count = 37;
#if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH)
/* Initialize the DLE defaults */
_radio.default_tx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN;
_radio.default_tx_time = RADIO_LL_LENGTH_TIME_RX_MIN;
#endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY)
/* Initialize the DLE defaults */
_radio.default_phy_tx = BIT(0);
_radio.default_phy_rx = BIT(0);
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY_2M)
/* Initialize the DLE defaults */
_radio.default_phy_tx |= BIT(1);
_radio.default_phy_rx |= BIT(1);
#endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY_2M */
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY_CODED)
/* Initialize the DLE defaults */
_radio.default_phy_tx |= BIT(2);
_radio.default_phy_rx |= BIT(2);
#endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY_CODED */
#endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */
/* allocate the rx queue */
packet_rx_allocate(0xFF);
}
static inline u32_t addr_us_get(u8_t phy)
{
switch (phy) {
default:
case BIT(0):
return 40;
case BIT(1):
return 20;
case BIT(2):
return 336 + 64; /* TODO: Fix the hack */
}
}
#if defined(SILENT_CONNECTION)
static inline u32_t empty_pkt_us_get(u8_t phy)
{
switch (phy) {
default:
case BIT(0):
return 80;
case BIT(1):
return 40;
case BIT(2):
return 720;
}
}
#endif
static inline void isr_radio_state_tx(void)
{
u32_t hcto;
_radio.state = STATE_RX;
hcto = radio_tmr_end_get() + RADIO_RX_CHAIN_DELAY_US + RADIO_TIFS -
RADIO_TX_CHAIN_DELAY_US + 4;
radio_tmr_tifs_set(RADIO_TIFS);
radio_switch_complete_and_tx();
switch (_radio.role) {
case ROLE_ADV:
radio_pkt_rx_set(radio_pkt_scratch_get());
/* assert if radio packet ptr is not set and radio started rx */
LL_ASSERT(!radio_is_ready());
if (_radio.advertiser.filter_policy && _radio.nirk) {
radio_ar_configure(_radio.nirk, _radio.irk);
}
hcto += addr_us_get(0);
radio_tmr_hcto_configure(hcto);
radio_tmr_end_capture();
#if defined(CONFIG_BLUETOOTH_CONTROLLER_SCAN_REQ_RSSI)
radio_rssi_measure();
#endif /* CONFIG_BLUETOOTH_CONTROLLER_SCAN_REQ_RSSI */
break;
case ROLE_OBS:
radio_pkt_rx_set(_radio.packet_rx[_radio.packet_rx_last]->
pdu_data);
/* assert if radio packet ptr is not set and radio started rx */
LL_ASSERT(!radio_is_ready());
hcto += addr_us_get(0);
radio_tmr_hcto_configure(hcto);
radio_rssi_measure();
break;
case ROLE_MASTER:
#if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI)
if (_radio.packet_counter == 0) {
radio_rssi_measure();
}
#endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */
/* fall thru */
case ROLE_SLAVE:
rx_packet_set(_radio.conn_curr, (struct pdu_data *)_radio.
packet_rx[_radio.packet_rx_last]->pdu_data);
/* assert if radio packet ptr is not set and radio started rx */
LL_ASSERT(!radio_is_ready());
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY)
hcto += addr_us_get(_radio.conn_curr->phy_rx);
#else /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */
hcto += addr_us_get(0);
#endif /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */
radio_tmr_hcto_configure(hcto);
radio_tmr_end_capture();
/* Route the tx packet to respective connections */
/* TODO: use timebox for tx enqueue (instead of 1 packet
* that is routed, which may not be for the current connection)
* try to route as much tx packet in queue into corresponding
* connection's tx list.
*/
packet_tx_enqueue(1);
break;
case ROLE_NONE:
default:
LL_ASSERT(0);
break;
}
}
#if defined(CONFIG_BLUETOOTH_CONTROLLER_SCAN_REQ_NOTIFY)
static u32_t isr_rx_adv_sr_report(struct pdu_adv *pdu_adv_rx, u8_t rssi_ready)
{
struct radio_pdu_node_rx *radio_pdu_node_rx;
struct pdu_adv *pdu_adv;
u8_t pdu_len;
radio_pdu_node_rx = packet_rx_reserve_get(3);
if (radio_pdu_node_rx == 0) {
return 1;
}
/* Prepare the report (scan req) */
radio_pdu_node_rx->hdr.handle = 0xffff;
radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_SCAN_REQ;
/* Make a copy of PDU into Rx node (as the received PDU is in the
* scratch buffer), and save the RSSI value.
*/
pdu_adv = (struct pdu_adv *)radio_pdu_node_rx->pdu_data;
pdu_len = offsetof(struct pdu_adv, payload) + pdu_adv_rx->len;
memcpy(pdu_adv, pdu_adv_rx, pdu_len);
((u8_t *)pdu_adv)[pdu_len] =
(rssi_ready) ? (radio_rssi_get() & 0x7f) : 0x7f;
packet_rx_enqueue();
return 0;
}
#endif /* CONFIG_BLUETOOTH_CONTROLLER_SCAN_REQ_NOTIFY */
static inline u32_t isr_rx_adv(u8_t devmatch_ok, u8_t irkmatch_ok,
u8_t irkmatch_id, u8_t rssi_ready)
{
struct pdu_adv *pdu_adv, *_pdu_adv;
struct radio_pdu_node_rx *radio_pdu_node_rx;
pdu_adv = (struct pdu_adv *)radio_pkt_scratch_get();
if ((pdu_adv->type == PDU_ADV_TYPE_SCAN_REQ) &&
(pdu_adv->len == sizeof(struct pdu_adv_payload_scan_req)) &&
(((_radio.advertiser.filter_policy & 0x01) == 0) ||
(devmatch_ok) || (irkmatch_ok)) &&
(1 /** @todo own addr match check */)) {
#if defined(CONFIG_BLUETOOTH_CONTROLLER_SCAN_REQ_NOTIFY)
u32_t err;
/* Generate the scan request event */
err = isr_rx_adv_sr_report(pdu_adv, rssi_ready);
if (err) {
/* Scan Response will not be transmitted */
return err;
}
#endif /* CONFIG_BLUETOOTH_CONTROLLER_SCAN_REQ_NOTIFY */
_radio.state = STATE_CLOSE;
radio_switch_complete_and_disable();
/* use the latest scan data, if any */
if (_radio.advertiser.scan_data.first != _radio.
advertiser.scan_data.last) {
u8_t first;
first = _radio.advertiser.scan_data.first + 1;
if (first == DOUBLE_BUFFER_SIZE) {
first = 0;
}
_radio.advertiser.scan_data.first = first;
}
radio_pkt_tx_set(&_radio.advertiser.scan_data.
data[_radio.advertiser.scan_data.first][0]);
return 0;
} else if ((pdu_adv->type == PDU_ADV_TYPE_CONNECT_IND) &&
(pdu_adv->len == sizeof(struct pdu_adv_payload_connect_ind)) &&
(((_radio.advertiser.filter_policy & 0x02) == 0) ||
(devmatch_ok) || (irkmatch_ok)) &&
(1 /** @todo own addr match check */) &&
((_radio.fc_ena == 0) || (_radio.fc_req == _radio.fc_ack)) &&
(_radio.advertiser.conn)) {
struct radio_le_conn_cmplt *radio_le_conn_cmplt;
u32_t ticks_slot_offset;
u32_t conn_interval_us;
struct pdu_data *pdu_data;
struct connection *conn;
u32_t ticker_status;
if (IS_ENABLED(CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2)) {
radio_pdu_node_rx = packet_rx_reserve_get(4);
} else {
radio_pdu_node_rx = packet_rx_reserve_get(3);
}
if (radio_pdu_node_rx == 0) {
return 1;
}
_radio.state = STATE_STOP;
radio_disable();
/* acquire the slave context from advertiser */
conn = _radio.advertiser.conn;
_radio.advertiser.conn = NULL;
/* Populate the slave context */
conn->handle = mem_index_get(conn, _radio.conn_pool,
CONNECTION_T_SIZE);
memcpy(&conn->crc_init[0],
&pdu_adv->payload.connect_ind.lldata.crc_init[0],
3);
memcpy(&conn->access_addr[0],
&pdu_adv->payload.connect_ind.lldata.access_addr[0],
4);
memcpy(&conn->data_chan_map[0],
&pdu_adv->payload.connect_ind.lldata.chan_map[0],
sizeof(conn->data_chan_map));
conn->data_chan_count =
util_ones_count_get(&conn->data_chan_map[0],
sizeof(conn->data_chan_map));
conn->data_chan_hop = pdu_adv->payload.connect_ind.lldata.hop;
conn->conn_interval =
pdu_adv->payload.connect_ind.lldata.interval;
conn_interval_us =
pdu_adv->payload.connect_ind.lldata.interval * 1250;
conn->latency = pdu_adv->payload.connect_ind.lldata.latency;
memcpy((void *)&conn->role.slave.force, &conn->access_addr[0],
sizeof(conn->role.slave.force));
conn->supervision_reload =
RADIO_CONN_EVENTS((pdu_adv->payload.connect_ind.lldata.timeout
* 10 * 1000), conn_interval_us);
conn->procedure_reload = RADIO_CONN_EVENTS((40 * 1000 * 1000),
conn_interval_us);
#if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING)
/* APTO in no. of connection events */
conn->apto_reload = RADIO_CONN_EVENTS((30 * 1000 * 1000),
conn_interval_us);
/* Dispatch LE Ping PDU 6 connection events (that peer would
* listen to) before 30s timeout
* TODO: "peer listens to" is greater than 30s due to latency
*/
conn->appto_reload = (conn->apto_reload > (conn->latency + 6)) ?
(conn->apto_reload - (conn->latency + 6)) :
conn->apto_reload;
#endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */
/* Prepare the rx packet structure */
radio_pdu_node_rx->hdr.handle = conn->handle;
radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_CONNECTION;
/* prepare connection complete structure */
pdu_data = (struct pdu_data *)radio_pdu_node_rx->pdu_data;
radio_le_conn_cmplt =
(struct radio_le_conn_cmplt *)&pdu_data->payload;
radio_le_conn_cmplt->status = 0x00;
radio_le_conn_cmplt->role = 0x01;
radio_le_conn_cmplt->peer_addr_type = pdu_adv->tx_addr;
memcpy(&radio_le_conn_cmplt->peer_addr[0],
&pdu_adv->payload.connect_ind.init_addr[0],
BDADDR_SIZE);
radio_le_conn_cmplt->own_addr_type = pdu_adv->rx_addr;
memcpy(&radio_le_conn_cmplt->own_addr[0],
&pdu_adv->payload.connect_ind.adv_addr[0], BDADDR_SIZE);
radio_le_conn_cmplt->peer_irk_index = irkmatch_id;
radio_le_conn_cmplt->interval =
pdu_adv->payload.connect_ind.lldata.interval;
radio_le_conn_cmplt->latency =
pdu_adv->payload.connect_ind.lldata.latency;
radio_le_conn_cmplt->timeout =
pdu_adv->payload.connect_ind.lldata.timeout;
radio_le_conn_cmplt->mca =
pdu_adv->payload.connect_ind.lldata.sca;
/* enqueue connection complete structure into queue */
rx_fc_lock(conn->handle);
packet_rx_enqueue();
/* Use Channel Selection Algorithm #2 if peer too supports it */
if (IS_ENABLED(CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2)) {
struct radio_le_chan_sel_algo *le_chan_sel_algo;
/* Generate LE Channel Selection Algorithm event */
radio_pdu_node_rx = packet_rx_reserve_get(3);
LL_ASSERT(radio_pdu_node_rx);
radio_pdu_node_rx->hdr.handle = conn->handle;
radio_pdu_node_rx->hdr.type =
NODE_RX_TYPE_CHAN_SEL_ALGO;
pdu_data = (struct pdu_data *)
radio_pdu_node_rx->pdu_data;
le_chan_sel_algo = (struct radio_le_chan_sel_algo *)
&pdu_data->payload;
if (pdu_adv->chan_sel) {
u16_t aa_ls =
((u16_t)conn->access_addr[1] << 8) |
conn->access_addr[0];
u16_t aa_ms =
((u16_t)conn->access_addr[3] << 8) |
conn->access_addr[2];
conn->data_chan_sel = 1;
conn->data_chan_id = aa_ms ^ aa_ls;
le_chan_sel_algo->chan_sel_algo = 0x01;
} else {
le_chan_sel_algo->chan_sel_algo = 0x00;
}
packet_rx_enqueue();
}
/* calculate the window widening */
conn->role.slave.sca = pdu_adv->payload.connect_ind.lldata.sca;
conn->role.slave.window_widening_periodic_us =
(((gc_lookup_ppm[_radio.sca] +
gc_lookup_ppm[conn->role.slave.sca]) *
conn_interval_us) + (1000000 - 1)) / 1000000;
conn->role.slave.window_widening_max_us =
(conn_interval_us >> 1) - 150;
conn->role.slave.window_size_event_us =
pdu_adv->payload.connect_ind.lldata.win_size * 1250;
conn->role.slave.window_size_prepare_us = 0;
/* calculate slave slot */
conn->hdr.ticks_slot =
TICKER_US_TO_TICKS(RADIO_TICKER_START_PART_US +
RADIO_RX_READY_DELAY_US + 328 +
328 + 150);
conn->hdr.ticks_active_to_start = _radio.ticks_active_to_start;
conn->hdr.ticks_xtal_to_start =
TICKER_US_TO_TICKS(RADIO_TICKER_XTAL_OFFSET_US);
conn->hdr.ticks_preempt_to_start =
TICKER_US_TO_TICKS(RADIO_TICKER_PREEMPT_PART_MIN_US);
ticks_slot_offset =
(conn->hdr.ticks_active_to_start <
conn->hdr.ticks_xtal_to_start) ?
conn->hdr.ticks_xtal_to_start :
conn->hdr.ticks_active_to_start;
conn_interval_us -=
conn->role.slave.window_widening_periodic_us;
/* Stop Advertiser */
ticker_status = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO,
RADIO_TICKER_USER_ID_WORKER,
RADIO_TICKER_ID_ADV,
ticker_stop_adv_assert