Bluetooth: Controller: Fix HCI Reset Command implementation

Added implementation for HCI Reset Command. Implementation
gracefully disables any running advertiser, observer, and/
or connection roles, and it resets controller context members.
The HCI Reset Command is implemented in such a way that
driver instances shared with other sub-systems and
application is not disturbed and instance/references used
by Bluetooth Controller are gracefully returned back.

Jira: ZEP-1282

Change-id: Ifb9ae6807736b5ec2d9f346cf2a590322056bcee
Signed-off-by: Vinayak Chettimada <vinayak.kariappa.chettimada@nordicsemi.no>
Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
This commit is contained in:
Vinayak Chettimada 2016-11-08 05:23:27 +01:00 committed by Johan Hedberg
commit 11770b8bf4
6 changed files with 147 additions and 63 deletions

View file

@ -145,7 +145,7 @@ static void reset(struct net_buf *buf, struct net_buf *evt)
{
struct bt_hci_evt_cc_status *ccst;
/** TODO */
ctrl_reset();
ccst = cmd_complete(evt, sizeof(*ccst));
ccst->status = 0x00;

View file

@ -43,6 +43,7 @@
#include "hal/rand.h"
#include "hal/ccm.h"
#include "hal/radio.h"
#include "hal/hal_rtc.h"
#include "ll/ticker.h"
#include "ll/ctrl_internal.h"
#include "hci_internal.h"
@ -267,6 +268,9 @@ static int hci_driver_open(void)
DEBUG_INIT();
/* TODO: bind and use RNG driver */
rand_init(_rand_context, sizeof(_rand_context));
clk_k32 = device_get_binding(CONFIG_CLOCK_CONTROL_NRF5_K32SRC_DRV_NAME);
if (!clk_k32) {
return -ENODEV;
@ -274,6 +278,9 @@ static int hci_driver_open(void)
clock_control_on(clk_k32, (void *)CLOCK_CONTROL_NRF5_K32SRC);
/* TODO: bind and use counter driver */
rtc_init();
_ticker_users[RADIO_TICKER_USER_ID_WORKER][0] =
RADIO_TICKER_USER_WORKER_OPS;
_ticker_users[RADIO_TICKER_USER_ID_JOB][0] =
@ -287,8 +294,6 @@ static int hci_driver_open(void)
, RADIO_TICKER_USER_OPS, &_ticker_user_ops[0]
);
rand_init(_rand_context, sizeof(_rand_context));
clk_m16 = device_get_binding(CONFIG_CLOCK_CONTROL_NRF5_M16SRC_DRV_NAME);
if (!clk_m16) {
return -ENODEV;

View file

@ -213,6 +213,7 @@ static struct {
static uint16_t const gc_lookup_ppm[] = { 500, 250, 150, 100, 75, 50, 30, 20 };
static void common_init(void);
static void ticker_success_assert(uint32_t status, void *params);
static void event_inactive(uint32_t ticks_at_expire, uint32_t remainder,
uint16_t lazy, void *context);
@ -271,6 +272,8 @@ static void reject_ind_ext_send(struct connection *conn,
static void length_resp_send(struct connection *conn,
uint16_t eff_rx_octets,
uint16_t eff_tx_octets);
static uint32_t role_disable(uint8_t ticker_id_primary,
uint8_t ticker_id_stop);
static void rx_fc_lock(uint16_t handle);
/*****************************************************************************
@ -282,9 +285,7 @@ uint32_t radio_init(void *hf_clock, uint8_t sca, uint8_t connection_count_max,
uint16_t mem_size)
{
uint32_t retcode;
uint16_t packet_tx_ctrl_size;
uint8_t *mem_radio_end;
void *link;
/* intialise hf_clock device to use in prepare */
_radio.hf_clock = hf_clock;
@ -339,11 +340,9 @@ uint32_t radio_init(void *hf_clock, uint8_t sca, uint8_t connection_count_max,
(ALIGN4(offsetof(struct radio_pdu_node_rx, pdu_data) +
(RADIO_ACPDU_SIZE_MAX + 1)) * rx_count_max);
}
_radio.packet_rx_data_size =
ALIGN4(offsetof(struct radio_pdu_node_rx, pdu_data) +
(RADIO_ACPDU_SIZE_MAX + 1));
_radio.packet_rx_data_count =
_radio.packet_rx_data_pool_size / _radio.packet_rx_data_size;
_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;
@ -356,10 +355,7 @@ uint32_t radio_init(void *hf_clock, uint8_t sca, uint8_t connection_count_max,
/* initialise tx ctrl pool memory */
_radio.pkt_tx_ctrl_pool = mem_radio;
packet_tx_ctrl_size =
ALIGN4(offsetof(struct radio_pdu_node_tx, pdu_data) +
offsetof(struct pdu_data, payload) + 27);
mem_radio += packet_tx_ctrl_size * PACKET_MEM_COUNT_TX_CTRL;
mem_radio += PACKET_TX_CTRL_SIZE_MIN * PACKET_MEM_COUNT_TX_CTRL;
/* initialise tx data memory size and count */
_radio.packet_tx_data_size =
@ -381,6 +377,61 @@ uint32_t radio_init(void *hf_clock, uint8_t sca, uint8_t connection_count_max,
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();
return retcode;
}
void ctrl_reset(void)
{
uint16_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,
@ -402,17 +453,17 @@ uint32_t radio_init(void *hf_clock, uint8_t sca, uint8_t connection_count_max,
&_radio.link_rx_free);
/* initialise ctrl tx pool. */
mem_init(_radio.pkt_tx_ctrl_pool, packet_tx_ctrl_size,
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 controller states and flags */
_radio.role = ROLE_NONE;
_radio.state = STATE_NONE;
_radio.fc_ena = 1;
/* 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;
@ -427,16 +478,6 @@ uint32_t radio_init(void *hf_clock, uint8_t sca, uint8_t connection_count_max,
/* allocate the rx queue */
packet_rx_allocate(0xFF);
/* initialise the event-cum-data memq */
link = mem_acquire(&_radio.link_rx_free);
link = memq_init(link, &_radio.link_rx_head,
(void *)&_radio.link_rx_tail);
/* after rx queue allocation, one link mem should still be available */
LL_ASSERT(link);
return retcode;
}
static inline void isr_radio_state_tx(void)
@ -6361,7 +6402,21 @@ uint32_t radio_irk_add(uint8_t *irk)
return 0;
}
static inline void do_adv_scan_disable(uint8_t ticker_id_stop,
static struct connection *connection_get(uint16_t handle)
{
struct connection *conn;
if (handle < _radio.connection_count) {
conn = mem_get(_radio.conn_pool, CONNECTION_T_SIZE, handle);
if ((conn) && (conn->handle == handle)) {
return conn;
}
}
return 0;
}
static inline void role_active_disable(uint8_t ticker_id_stop,
uint32_t ticks_xtal_to_start,
uint32_t ticks_active_to_start)
{
@ -6485,12 +6540,48 @@ static inline void do_adv_scan_disable(uint8_t ticker_id_stop,
}
static uint32_t adv_scan_disable(uint8_t ticker_id_primary,
uint8_t ticker_id_stop,
uint32_t ticks_xtal_to_start,
uint32_t ticks_active_to_start)
static uint32_t role_disable(uint8_t ticker_id_primary,
uint8_t ticker_id_stop)
{
uint32_t volatile ticker_status;
uint32_t ticks_xtal_to_start = 0;
uint32_t ticks_active_to_start = 0;
switch (ticker_id_primary) {
case RADIO_TICKER_ID_ADV:
ticks_xtal_to_start =
_radio.advertiser.hdr.ticks_xtal_to_start;
ticks_active_to_start =
_radio.advertiser.hdr.ticks_active_to_start;
break;
case RADIO_TICKER_ID_OBS:
ticks_xtal_to_start =
_radio.observer.hdr.ticks_xtal_to_start;
ticks_active_to_start =
_radio.observer.hdr.ticks_active_to_start;
break;
default:
if (ticker_id_primary >= RADIO_TICKER_ID_FIRST_CONNECTION) {
struct connection *conn;
uint16_t conn_handle;
conn_handle = ticker_id_primary -
RADIO_TICKER_ID_FIRST_CONNECTION;
conn = connection_get(conn_handle);
if (!conn) {
return 1;
}
ticks_xtal_to_start =
conn->hdr.ticks_xtal_to_start;
ticks_active_to_start =
conn->hdr.ticks_active_to_start;
} else {
BT_ASSERT(0);
}
break;
}
/* Step 1: Is Primary started? Stop the Primary ticker */
ticker_status =
@ -6519,7 +6610,7 @@ static uint32_t adv_scan_disable(uint8_t ticker_id_primary,
if ((_radio.ticker_id_prepare == ticker_id_primary)
|| (_radio.ticker_id_event == ticker_id_primary)) {
do_adv_scan_disable(ticker_id_stop,
role_active_disable(ticker_id_stop,
ticks_xtal_to_start, ticks_active_to_start);
}
@ -6709,10 +6800,8 @@ uint32_t radio_adv_disable(void)
{
uint32_t status;
status = adv_scan_disable(RADIO_TICKER_ID_ADV,
RADIO_TICKER_ID_ADV_STOP,
_radio.advertiser.hdr.ticks_xtal_to_start,
_radio.advertiser.hdr.ticks_active_to_start);
status = role_disable(RADIO_TICKER_ID_ADV,
RADIO_TICKER_ID_ADV_STOP);
if (!status) {
struct connection *conn;
@ -6816,10 +6905,8 @@ uint32_t radio_scan_disable(void)
{
uint32_t status;
status = adv_scan_disable(RADIO_TICKER_ID_OBS,
RADIO_TICKER_ID_OBS_STOP,
_radio.observer.hdr.ticks_xtal_to_start,
_radio.observer.hdr.ticks_active_to_start);
status = role_disable(RADIO_TICKER_ID_OBS,
RADIO_TICKER_ID_OBS_STOP);
if (!status) {
struct connection *conn;
@ -6954,20 +7041,6 @@ uint32_t radio_connect_disable(void)
return status;
}
static struct connection *connection_get(uint16_t handle)
{
struct connection *conn;
if (handle < _radio.connection_count) {
conn = mem_get(_radio.conn_pool, CONNECTION_T_SIZE, handle);
if ((conn) && (conn->handle == handle)) {
return conn;
}
}
return 0;
}
uint32_t radio_conn_update(uint16_t handle, uint8_t cmd, uint8_t status,
uint16_t interval, uint16_t latency,
uint16_t timeout)

View file

@ -208,6 +208,7 @@ uint32_t radio_init(void *hf_clock, uint8_t sca, uint8_t connection_count_max,
uint8_t rx_count_max, uint8_t tx_count_max,
uint16_t data_octets_max, uint8_t *mem_radio,
uint16_t mem_size);
void ctrl_reset(void);
void radio_ticks_active_to_start_set(uint32_t ticks_active_to_start);
struct radio_adv_data *radio_adv_data_get(void);
struct radio_adv_data *radio_scan_data_get(void);

View file

@ -198,6 +198,16 @@ struct pdu_data_q_tx {
struct radio_pdu_node_tx *node_tx;
};
/* Minimum Rx Data allocation size */
#define PACKET_RX_DATA_SIZE_MIN \
ALIGN4(offsetof(struct radio_pdu_node_rx, pdu_data) + \
(RADIO_ACPDU_SIZE_MAX + 1))
/* Minimum Tx Ctrl allocation size */
#define PACKET_TX_CTRL_SIZE_MIN \
ALIGN4(offsetof(struct radio_pdu_node_tx, pdu_data) + \
offsetof(struct pdu_data, payload) + 27)
/** @todo fix starvation when ctrl rx in radio ISR
* for multiple connections needs to tx back to peer.
*/
@ -223,10 +233,7 @@ struct pdu_data_q_tx {
#define LL_MEM_RX_LINK_POOL (sizeof(void *) * 2 * ((RADIO_PACKET_COUNT_RX_MAX +\
4) + RADIO_CONNECTION_CONTEXT_MAX))
#define LL_MEM_TX_CTRL_POOL ((ALIGN4(offsetof( \
struct radio_pdu_node_tx, pdu_data) + \
offsetof(struct pdu_data, payload) + 27)) * \
PACKET_MEM_COUNT_TX_CTRL)
#define LL_MEM_TX_CTRL_POOL (PACKET_TX_CTRL_SIZE_MIN * PACKET_MEM_COUNT_TX_CTRL)
#define LL_MEM_TX_DATA_POOL ((ALIGN4(offsetof( \
struct radio_pdu_node_tx, pdu_data) + \
offsetof(struct pdu_data, payload) + \

View file

@ -1315,8 +1315,6 @@ uint32_t ticker_init(uint8_t instance_index, uint8_t count_node, void *node,
instance->ticks_elapsed_first = 0;
instance->ticks_elapsed_last = 0;
rtc_init();
return TICKER_STATUS_SUCCESS;
}