5151 USBD_INTENCLR_ENDISOIN_Msk | USBD_INTEN_ENDISOOUT_Msk
5252};
5353
54+ enum
55+ {
56+ EP_COUNT = 8
57+ };
58+
5459// Transfer descriptor
5560typedef struct
5661{
@@ -69,36 +74,73 @@ typedef struct
6974static struct
7075{
7176 // All 8 endpoints including control IN & OUT (offset 1)
72- xfer_td_t xfer [8 ][2 ];
77+ xfer_td_t xfer [EP_COUNT ][2 ];
7378
74- // Only one DMA can run at a time
75- volatile bool dma_running ;
79+ // Number of pending DMA that is started but not handled yet by dcd_int_handler().
80+ // Since nRF can only carry one DMA can run at a time, this value is normally be either 0 or 1.
81+ // However, in critical section with interrupt disabled, the DMA can be finished and added up
82+ // until handled by dcd_init_handler() when exiting critical section.
83+ volatile uint8_t dma_pending ;
7684}_dcd ;
7785
7886/*------------------------------------------------------------------*/
7987/* Control / Bulk / Interrupt (CBI) Transfer
8088 *------------------------------------------------------------------*/
8189
90+ // NVIC_GetEnableIRQ is only available in CMSIS v5
91+ #ifndef NVIC_GetEnableIRQ
92+ static inline uint32_t NVIC_GetEnableIRQ (IRQn_Type IRQn )
93+ {
94+ if ((int32_t )(IRQn ) >= 0 )
95+ {
96+ return ((uint32_t )(((NVIC -> ISER [(((uint32_t )(int32_t )IRQn ) >> 5UL )] & (1UL << (((uint32_t )(int32_t )IRQn ) & 0x1FUL ))) != 0UL ) ? 1UL : 0UL ));
97+ }
98+ else
99+ {
100+ return (0U );
101+ }
102+ }
103+ #endif
104+
82105// helper to start DMA
83106static void edpt_dma_start (volatile uint32_t * reg_startep )
84107{
85108 // Only one dma can be active
86- if ( _dcd .dma_running )
109+ if ( _dcd .dma_pending )
87110 {
88111 if (SCB -> ICSR & SCB_ICSR_VECTACTIVE_Msk )
89112 {
90- // If called within ISR, use usbd task to defer later
113+ // Called within ISR, use usbd task to defer later
91114 usbd_defer_func ( (osal_task_func_t ) edpt_dma_start , (void * ) reg_startep , true );
92115 return ;
93116 }
94117 else
95118 {
96- // Otherwise simply block wait
97- while ( _dcd .dma_running ) { }
119+ if ( __get_PRIMASK () || !NVIC_GetEnableIRQ (USBD_IRQn ) )
120+ {
121+ // Called in critical section with interrupt disabled. We have to manually check
122+ // for the DMA complete by comparing current pending DMA with number of ENDED Events
123+ uint32_t ended = 0 ;
124+
125+ while ( _dcd .dma_pending < ((uint8_t ) ended ) )
126+ {
127+ ended = NRF_USBD -> EVENTS_ENDISOIN + NRF_USBD -> EVENTS_ENDISOOUT ;
128+
129+ for (uint8_t i = 0 ; i < EP_COUNT ; i ++ )
130+ {
131+ ended += NRF_USBD -> EVENTS_ENDEPIN [i ] + NRF_USBD -> EVENTS_ENDEPOUT [i ];
132+ }
133+ }
134+ }else
135+ {
136+ // Called in non-critical thread-mode, should be 99% of the time.
137+ // Should be safe to blocking wait until previous DMA transfer complete
138+ while ( _dcd .dma_pending ) { }
139+ }
98140 }
99141 }
100142
101- _dcd .dma_running = true ;
143+ _dcd .dma_pending ++ ;
102144
103145 (* reg_startep ) = 1 ;
104146 __ISB (); __DSB ();
@@ -107,8 +149,8 @@ static void edpt_dma_start(volatile uint32_t* reg_startep)
107149// DMA is complete
108150static void edpt_dma_end (void )
109151{
110- TU_ASSERT (_dcd .dma_running , );
111- _dcd .dma_running = false ;
152+ TU_ASSERT (_dcd .dma_pending , );
153+ _dcd .dma_pending = 0 ;
112154}
113155
114156// helper getting td
@@ -282,9 +324,11 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
282324
283325 if ( control_status )
284326 {
285- // Status Phase also require Easy DMA has to be free as well !!!!
327+ // Status Phase also requires Easy DMA has to be available as well !!!!
328+ // However TASKS_EP0STATUS doesn't trigger any DMA transfer and got ENDED event subsequently
329+ // Therefore dma_running state will be corrected right away
286330 edpt_dma_start (& NRF_USBD -> TASKS_EP0STATUS );
287- edpt_dma_end ();
331+ if ( _dcd . dma_pending ) _dcd . dma_pending -- ; // correct the dma_running++ in dma start
288332
289333 // The nRF doesn't interrupt on status transmit so we queue up a success response.
290334 dcd_event_xfer_complete (0 , ep_addr , 0 , XFER_RESULT_SUCCESS , false);
0 commit comments