Bluetooth: Controller: Fix assert on advertise start

Using the shell application, starting advertisement while
the scanner is already running caused the controller to
assert.
The work which is supposed to set the radio active state to
active is not executed before the radio ISR resets the radio
active state, causing the assert. This can happen when a
newly started role is initialized to setup the radio trx and
the radio start code then detects that its hard realtime
instant has passed (as time was spent setting up the ticker
and ticker then firing thereafter) and hence cancels radio
trx.
Optionally (without this commit), dynamic calculation of
preparation time can be disabled by setting a constant time
in the define RADIO_TICKER_PREEMPT_PART_US in ctrl.c to
avoid the assert.

Change-id: Ib4415ec4b1bfdcc89aa0f3912e5a8fdd2e817fde
Signed-off-by: Vinayak Chettimada <vinayak.kariappa.chettimada@nordicsemi.no>
This commit is contained in:
Vinayak Chettimada 2016-08-25 18:22:23 +02:00 committed by Johan Hedberg
commit 1254ac51ef

View file

@ -209,7 +209,8 @@ static struct {
static uint16_t const gc_lookup_ppm[] = { 500, 250, 150, 100, 75, 50, 30, 20 };
static void ticker_success_assert(uint32_t status, void *params);
static void work_radio_active(void *params);
static void event_inactive(uint32_t ticks_at_expire, uint32_t remainder,
uint16_t lazy, void *context);
static void adv_setup(void);
static void event_adv(uint32_t ticks_at_expire, uint32_t remainder,
uint16_t lazy, void *context);
@ -2398,7 +2399,7 @@ static inline void isr_radio_state_close(void)
radio_tmr_stop();
work_radio_active(0);
event_inactive(0, 0, 0, 0);
clock_m16src_stop();
@ -2537,7 +2538,7 @@ static void event_active(uint32_t ticks_at_expire, uint32_t remainder,
ASSERT(!retval);
}
static void work_radio_deassert(void *params)
static void work_radio_inactive(void *params)
{
ARG_UNUSED(params);
@ -2546,6 +2547,22 @@ static void work_radio_deassert(void *params)
DEBUG_RADIO_CLOSE(0);
}
static void event_inactive(uint32_t ticks_at_expire, uint32_t remainder,
uint16_t lazy, void *context)
{
static struct work s_work_radio_inactive = { 0, 0, 0,
WORK_TICKER_WORKER0_IRQ, (work_fp) work_radio_inactive, 0};
uint32_t retval;
ARG_UNUSED(ticks_at_expire);
ARG_UNUSED(remainder);
ARG_UNUSED(lazy);
ARG_UNUSED(context);
retval = work_schedule(&s_work_radio_inactive, 0);
ASSERT(!retval);
}
static void work_xtal_start(void *params)
{
ARG_UNUSED(params);
@ -3350,38 +3367,52 @@ static void event_common_prepare(uint32_t ticks_at_expire,
ticks_preempt_to_start;
}
/* decide whether its XTAL start or active event that is the current
* execution context and accordingly setup the ticker for the other
* event (XTAL or active event). These are oneshot ticker.
*/
if (_ticks_active_to_start < _ticks_xtal_to_start) {
uint32_t ticks_to_active;
/* XTAL is before Active */
ticks_to_active = _ticks_xtal_to_start - _ticks_active_to_start;
ticks_to_start = _ticks_xtal_to_start;
ticker_status =
ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO,
RADIO_TICKER_USER_ID_WORKER,
RADIO_TICKER_ID_MARKER_0, ticks_at_expire,
(_ticks_xtal_to_start - _ticks_active_to_start),
TICKER_NULL_PERIOD, TICKER_NULL_REMAINDER,
TICKER_NULL_LAZY, TICKER_NULL_SLOT,
event_active, 0, ticker_success_assert,
(void *)__LINE__);
ticks_to_active, TICKER_NULL_PERIOD,
TICKER_NULL_REMAINDER, TICKER_NULL_LAZY,
TICKER_NULL_SLOT, event_active, 0,
ticker_success_assert, (void *)__LINE__);
ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
(ticker_status == TICKER_STATUS_BUSY));
event_xtal(0, 0, 0, 0);
} else if (_ticks_active_to_start > _ticks_xtal_to_start) {
uint32_t ticks_to_xtal;
/* Active is before XTAL */
ticks_to_xtal = _ticks_active_to_start - _ticks_xtal_to_start;
ticks_to_start = _ticks_active_to_start;
event_active(0, 0, 0, 0);
ticker_status = ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO,
ticker_status =
ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO,
RADIO_TICKER_USER_ID_WORKER,
RADIO_TICKER_ID_MARKER_0, ticks_at_expire,
(_ticks_active_to_start - _ticks_xtal_to_start),
TICKER_NULL_PERIOD, TICKER_NULL_REMAINDER,
TICKER_NULL_LAZY, TICKER_NULL_SLOT,
event_xtal, 0, ticker_success_assert,
(void *)__LINE__);
ticks_to_xtal, TICKER_NULL_PERIOD,
TICKER_NULL_REMAINDER, TICKER_NULL_LAZY,
TICKER_NULL_SLOT, event_xtal, 0,
ticker_success_assert, (void *)__LINE__);
ASSERT((ticker_status == TICKER_STATUS_SUCCESS) ||
(ticker_status == TICKER_STATUS_BUSY));
} else {
/* Active and XTAL are at the same time,
* no ticker required to be setup.
*/
ticks_to_start = _ticks_xtal_to_start;
event_active(0, 0, 0, 0);
@ -6188,8 +6219,8 @@ static inline void do_adv_scan_disable(uint8_t ticker_id_stop,
uint32_t ticks_xtal_to_start,
uint32_t ticks_active_to_start)
{
static struct work s_work_radio_deassert = { 0, 0, 0,
WORK_TICKER_WORKER0_IRQ, (work_fp) work_radio_deassert, 0 };
static struct work s_work_radio_inactive = { 0, 0, 0,
WORK_TICKER_WORKER0_IRQ, (work_fp) work_radio_inactive, 0 };
uint32_t volatile ticker_status_event;
/* Step 2: Is caller before Event? Stop Event */
@ -6235,7 +6266,7 @@ static inline void do_adv_scan_disable(uint8_t ticker_id_stop,
/* radio active asserted, handle deasserting
* here
*/
retval = work_schedule(&s_work_radio_deassert,
retval = work_schedule(&s_work_radio_inactive,
0);
ASSERT(!retval);
} else {
@ -6251,7 +6282,7 @@ static inline void do_adv_scan_disable(uint8_t ticker_id_stop,
/* Step 2.1.2: Deassert Radio Active and XTAL start */
/* radio active asserted, handle deasserting here */
retval = work_schedule(&s_work_radio_deassert, 0);
retval = work_schedule(&s_work_radio_inactive, 0);
ASSERT(!retval);
/* XTAL started, handle XTAL stop here */
@ -6297,6 +6328,7 @@ static inline void do_adv_scan_disable(uint8_t ticker_id_stop,
retval = work_schedule(&s_work_radio_stop, 0);
ASSERT(!retval);
/* wait for radio ISR to exit */
while (_radio.role != ROLE_NONE) {
cpu_sleep();
}