mirror of
https://github.com/hathach/tinyusb.git
synced 2025-02-21 03:40:52 +00:00
fix race condition with control since TASKS_EP0RCVOUT also require EasyDMA
This commit is contained in:
parent
2b5bad7362
commit
6b621baeb3
@ -108,13 +108,21 @@ static inline uint32_t NVIC_GetEnableIRQ(IRQn_Type IRQn)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// check if we are in ISR
|
||||||
|
TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void)
|
||||||
|
{
|
||||||
|
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
// helper to start DMA
|
// helper to start DMA
|
||||||
|
// TODO use Cortex M4 LDREX and STREX command (atomic) to have better mutex access to EasyDMA
|
||||||
|
// since current implementation does not 100% guarded against race condition
|
||||||
static void edpt_dma_start(volatile uint32_t* reg_startep)
|
static void edpt_dma_start(volatile uint32_t* reg_startep)
|
||||||
{
|
{
|
||||||
// Only one dma can be active
|
// Only one dma can be active
|
||||||
if ( _dcd.dma_pending )
|
if ( _dcd.dma_pending )
|
||||||
{
|
{
|
||||||
if (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk)
|
if (is_in_isr())
|
||||||
{
|
{
|
||||||
// Called within ISR, use usbd task to defer later
|
// Called within ISR, use usbd task to defer later
|
||||||
usbd_defer_func( (osal_task_func_t) edpt_dma_start, (void*) reg_startep, true );
|
usbd_defer_func( (osal_task_func_t) edpt_dma_start, (void*) reg_startep, true );
|
||||||
@ -159,6 +167,17 @@ static void edpt_dma_end(void)
|
|||||||
_dcd.dma_pending = 0;
|
_dcd.dma_pending = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// helper to set TASKS_EP0STATUS / TASKS_EP0RCVOUT since they also need EasyDMA
|
||||||
|
// However TASKS_EP0STATUS doesn't trigger any DMA transfer and got ENDED event subsequently
|
||||||
|
// Therefore dma_running state will be corrected right away
|
||||||
|
void start_ep0_task(volatile uint32_t* reg_task)
|
||||||
|
{
|
||||||
|
edpt_dma_start(reg_task);
|
||||||
|
|
||||||
|
// correct the dma_running++ in dma start
|
||||||
|
if (_dcd.dma_pending) _dcd.dma_pending--;
|
||||||
|
}
|
||||||
|
|
||||||
// helper getting td
|
// helper getting td
|
||||||
static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir)
|
static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir)
|
||||||
{
|
{
|
||||||
@ -407,21 +426,18 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
|
|||||||
|
|
||||||
if ( control_status )
|
if ( control_status )
|
||||||
{
|
{
|
||||||
// Status Phase also requires Easy DMA has to be available as well !!!!
|
// Status Phase also requires EasyDMA has to be available as well !!!!
|
||||||
// However TASKS_EP0STATUS doesn't trigger any DMA transfer and got ENDED event subsequently
|
start_ep0_task(&NRF_USBD->TASKS_EP0STATUS);
|
||||||
// Therefore dma_running state will be corrected right away
|
|
||||||
edpt_dma_start(&NRF_USBD->TASKS_EP0STATUS);
|
|
||||||
if (_dcd.dma_pending) _dcd.dma_pending--; // correct the dma_running++ in dma start
|
|
||||||
|
|
||||||
// The nRF doesn't interrupt on status transmit so we queue up a success response.
|
// The nRF doesn't interrupt on status transmit so we queue up a success response.
|
||||||
dcd_event_xfer_complete(0, ep_addr, 0, XFER_RESULT_SUCCESS, false);
|
dcd_event_xfer_complete(0, ep_addr, 0, XFER_RESULT_SUCCESS, is_in_isr());
|
||||||
}
|
}
|
||||||
else if ( dir == TUSB_DIR_OUT )
|
else if ( dir == TUSB_DIR_OUT )
|
||||||
{
|
{
|
||||||
if ( epnum == 0 )
|
if ( epnum == 0 )
|
||||||
{
|
{
|
||||||
// Accept next Control Out packet
|
// Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA
|
||||||
NRF_USBD->TASKS_EP0RCVOUT = 1;
|
start_ep0_task(&NRF_USBD->TASKS_EP0RCVOUT);
|
||||||
}else
|
}else
|
||||||
{
|
{
|
||||||
if ( xfer->data_received )
|
if ( xfer->data_received )
|
||||||
@ -581,12 +597,6 @@ void dcd_int_handler(uint8_t rhport)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( int_status & EDPT_END_ALL_MASK )
|
|
||||||
{
|
|
||||||
// DMA complete move data from SRAM -> Endpoint
|
|
||||||
edpt_dma_end();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup tokens are specific to the Control endpoint.
|
// Setup tokens are specific to the Control endpoint.
|
||||||
if ( int_status & USBD_INTEN_EP0SETUP_Msk )
|
if ( int_status & USBD_INTEN_EP0SETUP_Msk )
|
||||||
{
|
{
|
||||||
@ -607,6 +617,12 @@ void dcd_int_handler(uint8_t rhport)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( int_status & EDPT_END_ALL_MASK )
|
||||||
|
{
|
||||||
|
// DMA complete move data from SRAM -> Endpoint
|
||||||
|
edpt_dma_end();
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------+
|
//--------------------------------------------------------------------+
|
||||||
/* Control/Bulk/Interrupt (CBI) Transfer
|
/* Control/Bulk/Interrupt (CBI) Transfer
|
||||||
*
|
*
|
||||||
@ -655,8 +671,15 @@ void dcd_int_handler(uint8_t rhport)
|
|||||||
{
|
{
|
||||||
if ( epnum == 0 )
|
if ( epnum == 0 )
|
||||||
{
|
{
|
||||||
// Accept next Control Out packet
|
// Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA
|
||||||
NRF_USBD->TASKS_EP0RCVOUT = 1;
|
if ( _dcd.dma_pending )
|
||||||
|
{
|
||||||
|
// use usbd task to defer later
|
||||||
|
usbd_defer_func( (osal_task_func_t) start_ep0_task, (void*) &NRF_USBD->TASKS_EP0RCVOUT, true );
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
start_ep0_task(&NRF_USBD->TASKS_EP0RCVOUT);
|
||||||
|
}
|
||||||
}else
|
}else
|
||||||
{
|
{
|
||||||
// nRF auto accept next Bulk/Interrupt OUT packet
|
// nRF auto accept next Bulk/Interrupt OUT packet
|
||||||
@ -973,7 +996,7 @@ void tusb_hal_nrf_power_event (uint32_t event)
|
|||||||
|
|
||||||
hfclk_disable();
|
hfclk_disable();
|
||||||
|
|
||||||
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) ? true : false);
|
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, is_in_isr());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user