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

@ -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)