@@ -63,7 +63,7 @@ typedef struct
63
63
uint8_t * buffer ;
64
64
uint16_t total_len ;
65
65
volatile uint16_t actual_len ;
66
- uint16_t mps ; // max packet size
66
+ uint16_t mps ; // max packet size
67
67
68
68
// nRF will auto accept OUT packet after DMA is done
69
69
// indicate packet is already ACK
@@ -84,8 +84,6 @@ static struct
84
84
85
85
// Number of pending DMA that is started but not handled yet by dcd_int_handler().
86
86
// Since nRF can only carry one DMA can run at a time, this value is normally be either 0 or 1.
87
- // However, in critical section with interrupt disabled, the DMA can be finished and added up
88
- // until handled by dcd_int_handler() when exiting critical section.
89
87
volatile uint8_t dma_pending ;
90
88
}_dcd ;
91
89
@@ -115,67 +113,68 @@ TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void)
115
113
}
116
114
117
115
// helper to start DMA
116
+ static void start_dma (volatile uint32_t * reg_startep )
117
+ {
118
+ _dcd .dma_pending = true;
119
+
120
+ (* reg_startep ) = 1 ;
121
+ __ISB (); __DSB ();
122
+
123
+ // TASKS_EP0STATUS, TASKS_EP0RCVOUT seem to need EasyDMA to be available
124
+ // However these don't trigger any DMA transfer and got ENDED event subsequently
125
+ // Therefore dma_pending is corrected right away
126
+ if ( (reg_startep == & NRF_USBD -> TASKS_EP0STATUS ) || (reg_startep == & NRF_USBD -> TASKS_EP0RCVOUT ) )
127
+ {
128
+ _dcd .dma_pending = false;
129
+ }
130
+ }
131
+
132
+ // only 1 EasyDMA can be active at any time
118
133
// TODO use Cortex M4 LDREX and STREX command (atomic) to have better mutex access to EasyDMA
119
134
// since current implementation does not 100% guarded against race condition
120
135
static void edpt_dma_start (volatile uint32_t * reg_startep )
121
136
{
122
- // Only one dma can be active
123
- if ( _dcd . dma_pending )
137
+ // Called in critical section i.e within USB ISR, or USB/Global interrupt disabled
138
+ if ( is_in_isr () || __get_PRIMASK () || ! NVIC_GetEnableIRQ ( USBD_IRQn ) )
124
139
{
125
- if (is_in_isr () )
140
+ if (_dcd . dma_pending )
126
141
{
127
- // Called within ISR, use usbd task to defer later
142
+ //use usbd task to defer later
128
143
usbd_defer_func ((osal_task_func_t ) edpt_dma_start , (void * ) (uintptr_t ) reg_startep , true);
129
- return ;
144
+ }else
145
+ {
146
+ start_dma (reg_startep );
130
147
}
131
- else
148
+ }else
149
+ {
150
+ // Called in non-critical thread-mode, should be 99% of the time.
151
+ // Should be safe to blocking wait until previous DMA transfer complete
152
+ uint8_t const rhport = 0 ;
153
+ bool started = false;
154
+ while (!started )
132
155
{
133
- if ( __get_PRIMASK () || !NVIC_GetEnableIRQ (USBD_IRQn ) )
134
- {
135
- // Called in critical section with interrupt disabled. We have to manually check
136
- // for the DMA complete by comparing current pending DMA with number of ENDED Events
137
- uint32_t ended = 0 ;
156
+ // LDREX/STREX may be needed in form of std atomic (required C11) or
157
+ // use osal mutex to guard against multiple core MCUs such as nRF53
158
+ dcd_int_disable (rhport );
138
159
139
- while ( _dcd .dma_pending > ((uint8_t ) ended ) )
140
- {
141
- ended = NRF_USBD -> EVENTS_ENDISOIN + NRF_USBD -> EVENTS_ENDISOOUT ;
142
-
143
- for (uint8_t i = 0 ; i < EP_CBI_COUNT ; i ++ )
144
- {
145
- ended += NRF_USBD -> EVENTS_ENDEPIN [i ] + NRF_USBD -> EVENTS_ENDEPOUT [i ];
146
- }
147
- }
148
- }else
160
+ if ( !_dcd .dma_pending )
149
161
{
150
- // Called in non-critical thread-mode, should be 99% of the time.
151
- // Should be safe to blocking wait until previous DMA transfer complete
152
- while ( _dcd .dma_pending ) { }
162
+ start_dma (reg_startep );
163
+ started = true;
153
164
}
154
- }
155
- }
156
165
157
- _dcd . dma_pending ++ ;
166
+ dcd_int_enable ( rhport ) ;
158
167
159
- (* reg_startep ) = 1 ;
160
- __ISB (); __DSB ();
168
+ // osal_yield();
169
+ }
170
+ }
161
171
}
162
172
163
173
// DMA is complete
164
174
static void edpt_dma_end (void )
165
175
{
166
176
TU_ASSERT (_dcd .dma_pending , );
167
- _dcd .dma_pending = 0 ;
168
- }
169
-
170
- // helper to set TASKS_EP0STATUS / TASKS_EP0RCVOUT since they also need EasyDMA
171
- // However TASKS_EP0STATUS doesn't trigger any DMA transfer and got ENDED event subsequently
172
- // Therefore dma_running state will be corrected right away
173
- void start_ep0_task (volatile uint32_t * reg_task )
174
- {
175
- edpt_dma_start (reg_task );
176
-
177
- // correct the dma_running++ in dma start
178
- if (_dcd .dma_pending ) _dcd .dma_pending -- ;
177
+ _dcd .dma_pending = false;
179
178
}
180
179
181
180
// helper getting td
@@ -194,7 +193,10 @@ static void xact_out_dma(uint8_t epnum)
194
193
{
195
194
xact_len = NRF_USBD -> SIZE .ISOOUT ;
196
195
// If ZERO bit is set, ignore ISOOUT length
197
- if (xact_len & USBD_SIZE_ISOOUT_ZERO_Msk ) xact_len = 0 ;
196
+ if (xact_len & USBD_SIZE_ISOOUT_ZERO_Msk )
197
+ {
198
+ xact_len = 0 ;
199
+ }
198
200
else
199
201
{
200
202
// Trigger DMA move data from Endpoint -> SRAM
@@ -216,8 +218,8 @@ static void xact_out_dma(uint8_t epnum)
216
218
edpt_dma_start (& NRF_USBD -> TASKS_STARTEPOUT [epnum ]);
217
219
}
218
220
219
- xfer -> buffer += xact_len ;
220
- xfer -> actual_len += xact_len ;
221
+ // xfer->buffer += xact_len;
222
+ // xfer->actual_len += xact_len;
221
223
}
222
224
223
225
// Prepare for a CBI transaction IN, call at the start
@@ -232,7 +234,7 @@ static void xact_in_dma(uint8_t epnum)
232
234
NRF_USBD -> EPIN [epnum ].PTR = (uint32_t ) xfer -> buffer ;
233
235
NRF_USBD -> EPIN [epnum ].MAXCNT = xact_len ;
234
236
235
- xfer -> buffer += xact_len ;
237
+ // xfer->buffer += xact_len;
236
238
237
239
edpt_dma_start (& NRF_USBD -> TASKS_STARTEPIN [epnum ]);
238
240
}
@@ -242,6 +244,7 @@ static void xact_in_dma(uint8_t epnum)
242
244
//--------------------------------------------------------------------+
243
245
void dcd_init (uint8_t rhport )
244
246
{
247
+ TU_LOG1 ("dcd init\r\n" );
245
248
(void ) rhport ;
246
249
}
247
250
@@ -466,7 +469,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
466
469
if ( control_status )
467
470
{
468
471
// Status Phase also requires EasyDMA has to be available as well !!!!
469
- start_ep0_task (& NRF_USBD -> TASKS_EP0STATUS );
472
+ edpt_dma_start (& NRF_USBD -> TASKS_EP0STATUS );
470
473
471
474
// The nRF doesn't interrupt on status transmit so we queue up a success response.
472
475
dcd_event_xfer_complete (0 , ep_addr , 0 , XFER_RESULT_SUCCESS , is_in_isr ());
@@ -476,7 +479,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
476
479
if ( epnum == 0 )
477
480
{
478
481
// Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA
479
- start_ep0_task (& NRF_USBD -> TASKS_EP0RCVOUT );
482
+ edpt_dma_start (& NRF_USBD -> TASKS_EP0RCVOUT );
480
483
}else
481
484
{
482
485
if ( xfer -> data_received )
@@ -522,7 +525,6 @@ void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
522
525
// There maybe data in endpoint fifo already, we need to pull it out
523
526
if ( (dir == TUSB_DIR_OUT ) && xfer -> data_received )
524
527
{
525
- TU_LOG_LOCATION ();
526
528
xfer -> data_received = false;
527
529
xact_out_dma (epnum );
528
530
}
@@ -717,7 +719,8 @@ void dcd_int_handler(uint8_t rhport)
717
719
718
720
if ( int_status & EDPT_END_ALL_MASK )
719
721
{
720
- // DMA complete move data from SRAM -> Endpoint
722
+ // DMA complete move data from SRAM <-> Endpoint
723
+ // Must before endpoint transfer handling
721
724
edpt_dma_end ();
722
725
}
723
726
@@ -732,7 +735,7 @@ void dcd_int_handler(uint8_t rhport)
732
735
* - Host -> Endpoint
733
736
* EPDATA (or EP0DATADONE) interrupted, check EPDATASTATUS.EPOUT[i]
734
737
* to start DMA. For Bulk/Interrupt, this step can occur automatically (without sw),
735
- * which means data may or may not be ready (data_received flag).
738
+ * which means data may or may not be ready (out_received flag).
736
739
* - Endpoint -> RAM
737
740
* ENDEPOUT[i] interrupted, transaction complete, sw prepare next transaction
738
741
*
@@ -764,20 +767,16 @@ void dcd_int_handler(uint8_t rhport)
764
767
xfer_td_t * xfer = get_td (epnum , TUSB_DIR_OUT );
765
768
uint8_t const xact_len = NRF_USBD -> EPOUT [epnum ].AMOUNT ;
766
769
770
+ xfer -> buffer += xact_len ;
771
+ xfer -> actual_len += xact_len ;
772
+
767
773
// Transfer complete if transaction len < Max Packet Size or total len is transferred
768
774
if ( (epnum != EP_ISO_NUM ) && (xact_len == xfer -> mps ) && (xfer -> actual_len < xfer -> total_len ) )
769
775
{
770
776
if ( epnum == 0 )
771
777
{
772
778
// Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA
773
- if ( _dcd .dma_pending )
774
- {
775
- // use usbd task to defer later
776
- usbd_defer_func ((osal_task_func_t ) start_ep0_task , (void * ) (uintptr_t ) & NRF_USBD -> TASKS_EP0RCVOUT , true);
777
- }else
778
- {
779
- start_ep0_task (& NRF_USBD -> TASKS_EP0RCVOUT );
780
- }
779
+ edpt_dma_start (& NRF_USBD -> TASKS_EP0RCVOUT );
781
780
}else
782
781
{
783
782
// nRF auto accept next Bulk/Interrupt OUT packet
@@ -814,8 +813,10 @@ void dcd_int_handler(uint8_t rhport)
814
813
if ( tu_bit_test (data_status , epnum ) || (epnum == 0 && is_control_in ) )
815
814
{
816
815
xfer_td_t * xfer = get_td (epnum , TUSB_DIR_IN );
816
+ uint8_t const xact_len = NRF_USBD -> EPIN [epnum ].AMOUNT ; // MAXCNT
817
817
818
- xfer -> actual_len += NRF_USBD -> EPIN [epnum ].MAXCNT ;
818
+ xfer -> buffer += xact_len ;
819
+ xfer -> actual_len += xact_len ;
819
820
820
821
if ( xfer -> actual_len < xfer -> total_len )
821
822
{
@@ -842,6 +843,7 @@ void dcd_int_handler(uint8_t rhport)
842
843
}else
843
844
{
844
845
// Data overflow !!! Nah, nRF will auto accept next Bulk/Interrupt OUT packet
846
+ // If USBD is already queued this
845
847
// Mark this endpoint with data received
846
848
xfer -> data_received = true;
847
849
}
0 commit comments