fix race condition with control since TASKS_EP0RCVOUT also require EasyDMA
This commit is contained in:
		@@ -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)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -408,20 +427,17 @@ 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 EasyDMA 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user