nrfx_uarte: Set of fixes in the driver
Fixing race condition in nrfx_uarte_rx_abort. Fixing wrong error code returned by nrfx_uarte_tx_abort. Adding uarte interrupt locking to API functions to ensure that they are not interrupted by the UARTE interrupt handler. Allow to use TX_LINK without PPI. Signed-off-by: Krzysztof Chruściński <krzysztof.chruscinski@nordicsemi.no>
This commit is contained in:
parent
b8e5d50bd0
commit
ab62e42afe
|
@ -54,7 +54,7 @@
|
|||
#define UARTE_LENGTH_VALIDATE(drv_inst_idx, len) \
|
||||
(NRFX_FOREACH_ENABLED(UARTE, UARTEX_LENGTH_VALIDATE, (||), (0), drv_inst_idx, len, 0))
|
||||
|
||||
#if NRFX_UARTE_CONFIG_RX_CACHE_ENABLED
|
||||
#if NRFX_CHECK(NRFX_UARTE_CONFIG_RX_CACHE_ENABLED)
|
||||
// Internal cache buffer is used if buffers provided by a user cannot be used in DMA. This is a
|
||||
// HW limitation on some platforms but for testing purposes it can be emulated on any platform.
|
||||
#define RX_CACHE_SUPPORTED 1
|
||||
|
@ -125,6 +125,9 @@
|
|||
|
||||
// Flag indicates that RX was aborted.
|
||||
#define UARTE_FLAG_RX_ABORTED UARTE_FLAG(RX, 6)
|
||||
//
|
||||
// Flag indicates that there are new bytes from flushed buffer copied to the user buffer.
|
||||
#define UARTE_FLAG_RX_FROM_FLUSH UARTE_FLAG(RX, 7)
|
||||
|
||||
// Flag is set if instance was configured to control PSEL pins during the initialization.
|
||||
#define UARTE_FLAG_PSEL_UNINIT UARTE_FLAG(MISC, 0)
|
||||
|
@ -337,6 +340,20 @@ static void apply_workaround_for_enable_anomaly(nrfx_uarte_t const * p_instance)
|
|||
#endif // defined(NRF53_SERIES) || defined(NRF91_SERIES)
|
||||
}
|
||||
|
||||
static uint32_t uarte_int_lock(NRF_UARTE_Type * p_uarte)
|
||||
{
|
||||
uint32_t int_enabled = nrfy_uarte_int_enable_check(p_uarte, UINT32_MAX);
|
||||
|
||||
nrfy_uarte_int_disable(p_uarte, int_enabled);
|
||||
|
||||
return int_enabled;
|
||||
}
|
||||
|
||||
static void uarte_int_unlock(NRF_UARTE_Type * p_uarte, uint32_t int_mask)
|
||||
{
|
||||
nrfy_uarte_int_enable(p_uarte, int_mask);
|
||||
}
|
||||
|
||||
/* Function returns true if new transfer can be started. Since TXSTOPPED
|
||||
* (and ENDTX) is cleared before triggering new transfer, TX is ready for new
|
||||
* transfer if any event is set.
|
||||
|
@ -435,7 +452,7 @@ nrfx_err_t nrfx_uarte_init(nrfx_uarte_t const * p_instance,
|
|||
};
|
||||
if (nrfx_prs_acquire(p_instance->p_reg, irq_handlers[inst_idx]) != NRFX_SUCCESS)
|
||||
{
|
||||
nrfx_err_t err_code = NRFX_ERROR_BUSY;
|
||||
err_code = NRFX_ERROR_BUSY;
|
||||
NRFX_LOG_WARNING("Function: %s, error code: %s.",
|
||||
__func__,
|
||||
NRFX_LOG_ERROR_STRING_GET(err_code));
|
||||
|
@ -474,14 +491,19 @@ nrfx_err_t nrfx_uarte_init(nrfx_uarte_t const * p_instance,
|
|||
}
|
||||
}
|
||||
|
||||
#if NRF_UARTE_HAS_ENDTX_STOPTX_SHORT
|
||||
uint32_t tx_int_mask = 0;
|
||||
|
||||
nrfy_uarte_shorts_enable(p_instance->p_reg, NRF_UARTE_SHORT_ENDTX_STOPTX);
|
||||
#else
|
||||
uint32_t tx_int_mask = (!event_handler || p_config->tx_stop_on_end) ?
|
||||
0 : NRF_UARTE_INT_ENDTX_MASK;
|
||||
|
||||
if (p_config->tx_stop_on_end)
|
||||
{
|
||||
p_cb->flags |= UARTE_FLAG_TX_STOP_ON_END;
|
||||
#if NRF_UARTE_HAS_ENDTX_STOPTX_SHORT
|
||||
nrfy_uarte_shorts_enable(p_instance->p_reg, NRF_UARTE_SHORT_ENDTX_STOPTX);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
p_cb->handler = event_handler;
|
||||
p_cb->state = NRFX_DRV_STATE_INITIALIZED;
|
||||
|
@ -489,6 +511,9 @@ nrfx_err_t nrfx_uarte_init(nrfx_uarte_t const * p_instance,
|
|||
// Handle case when other user (e.g. bootloader) left RX in active state.
|
||||
if (!prepare_rx(p_instance->p_reg))
|
||||
{
|
||||
#if NRFX_CHECK(NRFX_PRS_ENABLED)
|
||||
nrfx_prs_release(p_instance->p_reg);
|
||||
#endif
|
||||
return NRFX_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
|
@ -496,11 +521,12 @@ nrfx_err_t nrfx_uarte_init(nrfx_uarte_t const * p_instance,
|
|||
|
||||
if (!prepare_tx(p_instance->p_reg, p_config->tx_stop_on_end))
|
||||
{
|
||||
#if NRFX_CHECK(NRFX_PRS_ENABLED)
|
||||
nrfx_prs_release(p_instance->p_reg);
|
||||
#endif
|
||||
return NRFX_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
uint32_t tx_int_mask = (!event_handler || p_config->tx_stop_on_end) ?
|
||||
0 : NRF_UARTE_INT_ENDTX_MASK;
|
||||
uint32_t int_mask = tx_int_mask | ((event_handler) ? rx_int_mask : 0);
|
||||
|
||||
nrfy_uarte_int_enable(p_instance->p_reg, int_mask);
|
||||
|
@ -557,6 +583,10 @@ void nrfx_uarte_uninit(nrfx_uarte_t const * p_instance)
|
|||
NRF_UARTE_INT_TXSTOPPED_MASK);
|
||||
nrfy_uarte_int_uninit(p_uarte);
|
||||
|
||||
#if NRF_UARTE_HAS_ENDTX_STOPTX_SHORT
|
||||
nrfy_uarte_shorts_disable(p_uarte, NRF_UARTE_SHORT_ENDTX_STOPTX);
|
||||
#endif
|
||||
|
||||
#if NRFX_CHECK(NRFX_PRS_ENABLED)
|
||||
nrfx_prs_release(p_uarte);
|
||||
#endif
|
||||
|
@ -831,8 +861,24 @@ nrfx_err_t nrfx_uarte_tx(nrfx_uarte_t const * p_instance,
|
|||
return NRFX_ERROR_INVALID_LENGTH;
|
||||
}
|
||||
|
||||
if (!p_cb->handler)
|
||||
{
|
||||
if (p_cb->tx.curr.length == 0)
|
||||
{
|
||||
p_cb->tx.curr.length = length;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NRFX_ERROR_BUSY;
|
||||
}
|
||||
|
||||
err_code = blocking_tx(p_instance, p_data, length, 0);
|
||||
p_cb->tx.curr.length = 0;
|
||||
return err_code;
|
||||
}
|
||||
|
||||
// Handle case when transfer is blocking.
|
||||
if (!p_cb->handler || (flags & (NRFX_UARTE_TX_EARLY_RETURN | NRFX_UARTE_TX_BLOCKING)))
|
||||
if (flags & (NRFX_UARTE_TX_EARLY_RETURN | NRFX_UARTE_TX_BLOCKING))
|
||||
{
|
||||
return blocking_tx(p_instance, p_data, length, flags);
|
||||
}
|
||||
|
@ -870,7 +916,18 @@ nrfx_err_t nrfx_uarte_tx(nrfx_uarte_t const * p_instance,
|
|||
if (p_cb->tx.curr.length == 0)
|
||||
{
|
||||
p_cb->tx.curr.length = length;
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||
#endif
|
||||
|
||||
p_cb->tx.curr.p_buffer = (uint8_t *)p_data;
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
if (use_cache)
|
||||
{
|
||||
p_cb->flags |= UARTE_FLAG_TX_USE_CACHE;
|
||||
|
@ -886,17 +943,27 @@ nrfx_err_t nrfx_uarte_tx(nrfx_uarte_t const * p_instance,
|
|||
bool res;
|
||||
|
||||
p_cb->flags |= UARTE_FLAG_TX_LINKED;
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||
#endif
|
||||
|
||||
p_cb->tx.next.p_buffer = (uint8_t *)p_data;
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
p_cb->tx.next.length = length;
|
||||
NRFX_WAIT_FOR(nrfy_uarte_event_check(p_uarte, NRF_UARTE_EVENT_TXSTARTED), 10, 1, res);
|
||||
if (res)
|
||||
{
|
||||
#if NRF_UARTE_HAS_ENDTX_STOPTX_SHORT
|
||||
if (p_cb->flags & UARTE_FLAG_TX_STOP_ON_END)
|
||||
{
|
||||
nrfy_uarte_shorts_disable(p_uarte, NRF_UARTE_SHORT_ENDTX_STOPTX);
|
||||
}
|
||||
nrfy_uarte_int_enable(p_instance->p_reg, NRF_UARTE_INT_ENDTX_MASK);
|
||||
nrfy_uarte_shorts_disable(p_uarte, NRF_UARTE_SHORT_ENDTX_STOPTX);
|
||||
#endif
|
||||
nrfy_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_TXSTARTED);
|
||||
nrfy_uarte_tx_buffer_set(p_uarte, p_data, length);
|
||||
err_code = NRFX_SUCCESS;
|
||||
}
|
||||
|
@ -930,26 +997,28 @@ nrfx_err_t nrfx_uarte_tx_abort(nrfx_uarte_t const * p_instance, bool sync)
|
|||
{
|
||||
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
|
||||
NRF_UARTE_Type * p_uarte = p_instance->p_reg;
|
||||
uint32_t mask;
|
||||
uint32_t int_mask;
|
||||
|
||||
int_mask = uarte_int_lock(p_uarte);
|
||||
if (p_cb->tx.curr.length == 0)
|
||||
{
|
||||
uarte_int_unlock(p_uarte, int_mask);
|
||||
return NRFX_ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
NRFX_ATOMIC_FETCH_OR(&p_cb->flags, UARTE_FLAG_TX_ABORTED);
|
||||
|
||||
if (sync)
|
||||
{
|
||||
mask = nrfy_uarte_int_enable_check(p_uarte, NRF_UARTE_INT_ENDTX_MASK);
|
||||
nrfy_uarte_int_disable(p_uarte, NRF_UARTE_INT_TXSTOPPED_MASK | NRF_UARTE_INT_ENDTX_MASK);
|
||||
}
|
||||
|
||||
nrfy_uarte_task_trigger(p_uarte, NRF_UARTE_TASK_STOPTX);
|
||||
|
||||
if (sync)
|
||||
{
|
||||
block_on_tx(p_uarte, p_cb);
|
||||
nrfy_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_ENDTX);
|
||||
nrfy_uarte_int_enable(p_uarte, mask);
|
||||
p_cb->tx.curr.length = 0;
|
||||
}
|
||||
|
||||
uarte_int_unlock(p_uarte, int_mask);
|
||||
|
||||
NRFX_LOG_INFO("TX transaction aborted.");
|
||||
|
||||
return NRFX_SUCCESS;
|
||||
|
@ -996,6 +1065,11 @@ static void user_handler_on_rx_done(uarte_control_block_t * p_cb,
|
|||
const uint8_t * p_data,
|
||||
size_t len)
|
||||
{
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||
#endif
|
||||
|
||||
nrfx_uarte_event_t event = {
|
||||
.type = NRFX_UARTE_EVT_RX_DONE,
|
||||
.data = {
|
||||
|
@ -1006,6 +1080,10 @@ static void user_handler_on_rx_done(uarte_control_block_t * p_cb,
|
|||
}
|
||||
};
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
p_cb->handler(&event, p_cb->p_context);
|
||||
}
|
||||
|
||||
|
@ -1014,6 +1092,11 @@ static void user_handler_on_tx_done(uarte_control_block_t * p_cb,
|
|||
size_t len,
|
||||
bool abort)
|
||||
{
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||
#endif
|
||||
|
||||
nrfx_uarte_event_t event = {
|
||||
.type = NRFX_UARTE_EVT_TX_DONE,
|
||||
.data = {
|
||||
|
@ -1025,6 +1108,10 @@ static void user_handler_on_tx_done(uarte_control_block_t * p_cb,
|
|||
}
|
||||
};
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
p_cb->handler(&event, p_cb->p_context);
|
||||
}
|
||||
|
||||
|
@ -1104,6 +1191,14 @@ static bool rx_flushed_handler(NRF_UARTE_Type * p_uarte, uarte_control_block_t *
|
|||
memcpy(p_cb->rx.curr.p_buffer, p_cb->rx.flush.p_buffer, p_cb->rx.flush.length);
|
||||
p_cb->rx.off = p_cb->rx.flush.length;
|
||||
p_cb->rx.flush.length = 0;
|
||||
if (nrfy_uarte_int_enable_check(p_uarte, NRF_UARTE_INT_RXDRDY_MASK))
|
||||
{
|
||||
user_handler(p_cb, NRFX_UARTE_EVT_RX_BYTE);
|
||||
}
|
||||
else
|
||||
{
|
||||
NRFX_ATOMIC_FETCH_OR(&p_cb->flags, UARTE_FLAG_RX_FROM_FLUSH);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1257,14 +1352,10 @@ nrfx_err_t nrfx_uarte_rx_buffer_set(nrfx_uarte_t const * p_instance,
|
|||
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
|
||||
NRF_UARTE_Type * p_uarte = p_instance->p_reg;
|
||||
bool cont = false;
|
||||
bool int_enabled;
|
||||
uint32_t int_enabled;
|
||||
nrfx_err_t err = NRFX_SUCCESS;
|
||||
|
||||
int_enabled = nrfy_uarte_int_enable_check(p_uarte, rx_int_mask) != 0;
|
||||
if (int_enabled)
|
||||
{
|
||||
nrfy_uarte_int_disable(p_uarte, rx_int_mask);
|
||||
}
|
||||
int_enabled = uarte_int_lock(p_uarte);
|
||||
|
||||
if (!nrf_dma_accessible_check(p_uarte, p_data))
|
||||
{
|
||||
|
@ -1324,10 +1415,7 @@ nrfx_err_t nrfx_uarte_rx_buffer_set(nrfx_uarte_t const * p_instance,
|
|||
err = rx_buffer_set(p_uarte, p_cb, p_data, length);
|
||||
}
|
||||
|
||||
if (int_enabled)
|
||||
{
|
||||
nrfy_uarte_int_enable(p_uarte, rx_int_mask);
|
||||
}
|
||||
uarte_int_unlock(p_uarte, int_enabled);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -1406,12 +1494,18 @@ static nrfx_err_t rx_abort(NRF_UARTE_Type * p_uarte,
|
|||
{
|
||||
uint32_t flag;
|
||||
bool endrx_startrx = nrfy_uarte_shorts_get(p_uarte, NRF_UARTE_SHORT_ENDRX_STARTRX) != 0;
|
||||
uint32_t int_enabled;
|
||||
|
||||
if (!(p_cb->flags & UARTE_FLAG_RX_ENABLED))
|
||||
// We need to ensure that operation is not interrupted by the UARTE interrupt since we
|
||||
// are changing state flags. Otherwise interrupt may be executed with RX_ABORTED flag set
|
||||
// but before STOPRX task is triggered which may lead to unexpected behavior.
|
||||
if (!((p_cb->flags & (UARTE_FLAG_RX_ENABLED | UARTE_FLAG_RX_ABORTED)) == UARTE_FLAG_RX_ENABLED))
|
||||
{
|
||||
return NRFX_ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
int_enabled = uarte_int_lock(p_uarte);
|
||||
|
||||
if (disable_all || !endrx_startrx)
|
||||
{
|
||||
nrfy_uarte_shorts_disable(p_uarte, NRF_UARTE_SHORT_ENDRX_STARTRX);
|
||||
|
@ -1429,12 +1523,15 @@ static nrfx_err_t rx_abort(NRF_UARTE_Type * p_uarte,
|
|||
nrfy_uarte_int_disable(p_uarte, rx_int_mask);
|
||||
nrfy_uarte_task_trigger(p_uarte, NRF_UARTE_TASK_STOPRX);
|
||||
wait_for_rx_completion(p_uarte, p_cb);
|
||||
int_enabled &= ~rx_int_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
nrfy_uarte_task_trigger(p_uarte, NRF_UARTE_TASK_STOPRX);
|
||||
}
|
||||
|
||||
uarte_int_unlock(p_uarte, int_enabled);
|
||||
|
||||
return NRFX_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -1550,7 +1647,11 @@ uint32_t nrfx_uarte_errorsrc_get(nrfx_uarte_t const * p_instance)
|
|||
|
||||
bool nrfx_uarte_rx_new_data_check(nrfx_uarte_t const * p_instance)
|
||||
{
|
||||
if (nrfy_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_RXDRDY))
|
||||
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
|
||||
bool flushed_data = (NRFX_ATOMIC_FETCH_AND(&p_cb->flags, ~UARTE_FLAG_RX_FROM_FLUSH) &
|
||||
UARTE_FLAG_RX_FROM_FLUSH) != 0;
|
||||
|
||||
if (nrfy_uarte_event_check(p_instance->p_reg, NRF_UARTE_EVENT_RXDRDY) || flushed_data)
|
||||
{
|
||||
nrfy_uarte_event_clear(p_instance->p_reg, NRF_UARTE_EVENT_RXDRDY);
|
||||
return true;
|
||||
|
@ -1665,6 +1766,7 @@ static bool endrx_irq_handler(NRF_UARTE_Type * p_uarte,
|
|||
if (p_cb->flags & UARTE_FLAG_RX_STOP_ON_END)
|
||||
{
|
||||
nrfy_uarte_task_trigger(p_uarte, NRF_UARTE_TASK_STOPRX);
|
||||
p_cb->flags |= UARTE_FLAG_RX_ABORTED;
|
||||
}
|
||||
}
|
||||
else if (!(p_cb->flags & UARTE_FLAG_RX_CONT && rxstarted))
|
||||
|
@ -1782,10 +1884,17 @@ static void endtx_irq_handler(NRF_UARTE_Type * p_uarte, uarte_control_block_t *
|
|||
{
|
||||
uint8_t const * p_buf = p_cb->tx.curr.p_buffer;
|
||||
uint32_t len = p_cb->tx.curr.length;
|
||||
bool aborted;
|
||||
bool aborted = p_cb->flags & UARTE_FLAG_TX_ABORTED;
|
||||
|
||||
nrfy_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_ENDTX);
|
||||
// If we have no PPI connection then start the transfer ASAP.
|
||||
if (!nrfy_uarte_event_check(p_uarte, NRF_UARTE_EVENT_TXSTARTED) && !aborted)
|
||||
{
|
||||
nrfy_uarte_task_trigger(p_uarte, NRF_UARTE_TASK_STARTTX);
|
||||
}
|
||||
|
||||
#if NRF_UARTE_HAS_ENDTX_STOPTX_SHORT
|
||||
nrfy_uarte_int_disable(p_uarte, NRF_UARTE_INT_ENDTX_MASK);
|
||||
nrfy_uarte_shorts_enable(p_uarte, NRF_UARTE_SHORT_ENDTX_STOPTX);
|
||||
#endif
|
||||
NRFX_CRITICAL_SECTION_ENTER();
|
||||
|
|
Loading…
Reference in New Issue