@@ -82,7 +82,8 @@ typedef struct {
8282
8383enum {
8484 PIPE0_STATE_IDLE = 0 , // no active control transfer
85- PIPE0_STATE_DATA , // DATA stage (IN or OUT — direction implied by CSR/dir)
85+ PIPE0_STATE_DATA_IN , // DATA IN stage
86+ PIPE0_STATE_DATA_OUT , // DATA OUT stage
8687 PIPE0_STATE_STATUS_IN , // STATUS IN — device sends IN-ZLP; awaits send-ACK IRQ
8788 PIPE0_STATE_STATUS_OUT , // post-DATAEND, neither edpt0_xfer(STATUS OUT) nor confirmation IRQ has happened yet
8889 PIPE0_STATE_STATUS_OUT_PENDING , // one of {edpt0_xfer(STATUS OUT), confirmation IRQ} has happened; the other fires xfer_complete
@@ -95,12 +96,41 @@ typedef struct {
9596 uint16_t remain_wlength ; // bytes remaining in the control transfer's DATA stage
9697 uint8_t state ;
9798 uint8_t pending_addr ; // new USB address latched by dcd_set_address; applied when STATUS IN completes
99+ tusb_control_request_t deferred_setup ;
100+ bool deferred_setup_valid ;
98101 } pipe0 ;
99102 pipe_state_t pipe [MUSB_PIPE_COUNT ];
100103} dcd_data_t ;
101104
102105static dcd_data_t _dcd ;
103106
107+ static void pipe0_start_setup (uint8_t rhport , musb_ep_csr_t * ep_csr ,
108+ tusb_control_request_t const * req , bool is_isr ) {
109+ _dcd .pipe0 .remain_wlength = req -> wLength ;
110+
111+ if (req -> wLength == 0 ) {
112+ _dcd .pipe0 .state = PIPE0_STATE_STATUS_IN ;
113+ } else {
114+ if (req -> bmRequestType & TUSB_DIR_IN_MASK ) {
115+ _dcd .pipe0 .state = PIPE0_STATE_DATA_IN ;
116+ ep_csr -> csr0l = MUSB_CSRL0_RXRDYC ;
117+ } else {
118+ _dcd .pipe0 .state = PIPE0_STATE_DATA_OUT ;
119+ }
120+ }
121+
122+ dcd_event_setup_received (rhport , (const uint8_t * ) req , is_isr );
123+ }
124+
125+ static void pipe0_process_deferred_setup (uint8_t rhport , musb_ep_csr_t * ep_csr , bool is_isr ) {
126+ if (!_dcd .pipe0 .deferred_setup_valid ) {
127+ return ;
128+ }
129+
130+ _dcd .pipe0 .deferred_setup_valid = false;
131+ pipe0_start_setup (rhport , ep_csr , & _dcd .pipe0 .deferred_setup , is_isr );
132+ }
133+
104134// EP0 must not call this — it has its own scalars in dcd_data_t.
105135TU_ATTR_ALWAYS_INLINE static inline pipe_state_t * pipe_get (uint8_t epnum , tusb_dir_t epdir ) {
106136 size_t idx = epnum - 1u ;
@@ -323,7 +353,7 @@ static void process_epout(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum,
323353
324354static bool edpt_n_xfer (uint8_t rhport , uint8_t ep_addr , void * buffer , uint16_t total_bytes , bool use_fifo , bool is_isr ) {
325355 const uint8_t epnum = tu_edpt_number (ep_addr );
326- const unsigned dir_in = tu_edpt_dir (ep_addr );
356+ const tusb_dir_t dir_in = tu_edpt_dir (ep_addr );
327357
328358 pipe_state_t * pipe = pipe_get (epnum , dir_in );
329359 if (use_fifo ) {
@@ -361,7 +391,8 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_
361391 const unsigned dir_in = tu_edpt_dir (ep_addr );
362392
363393 switch (_dcd .pipe0 .state ) {
364- case PIPE0_STATE_DATA : {
394+ case PIPE0_STATE_DATA_IN :
395+ case PIPE0_STATE_DATA_OUT : {
365396 _dcd .pipe0 .xact_len = total_bytes ;
366397 if (dir_in ) {
367398 // DATA IN: load FIFO, set TXRDY. Add DATAEND on the last chunk
@@ -396,6 +427,7 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_
396427 // Second event — IRQ already arrived, fire complete now.
397428 _dcd .pipe0 .state = PIPE0_STATE_IDLE ;
398429 dcd_event_xfer_complete (rhport , ep_addr , 0 , XFER_RESULT_SUCCESS , is_isr );
430+ pipe0_process_deferred_setup (rhport , ep_csr , is_isr );
399431 break ;
400432
401433 default : break ;
@@ -410,9 +442,14 @@ static void process_ep0(uint8_t rhport) {
410442 musb_ep_csr_t * ep_csr = get_ep_csr (musb_regs , 0 );
411443 uint_fast8_t csrl = ep_csr -> csr0l ;
412444
445+ if (csrl & MUSB_CSRL0_DATAEND ) {
446+ return ;
447+ }
448+
413449 if (csrl & MUSB_CSRL0_STALLED ) {
414450 ep_csr -> csr0l = 0 ;
415451 _dcd .pipe0 .state = PIPE0_STATE_IDLE ;
452+ _dcd .pipe0 .deferred_setup_valid = false;
416453 return ;
417454 }
418455
@@ -421,6 +458,7 @@ static void process_ep0(uint8_t rhport) {
421458 // do nothing, it is probably another setup packet, usbd will reset its state.
422459 ep_csr -> csr0l = MUSB_CSRL0_SETENDC ;
423460 _dcd .pipe0 .state = PIPE0_STATE_IDLE ;
461+ _dcd .pipe0 .deferred_setup_valid = false;
424462 if (!(csrl & MUSB_CSRL0_RXRDY )) {
425463 return ; /* no SETUP waiting behind it */
426464 }
@@ -430,30 +468,19 @@ static void process_ep0(uint8_t rhport) {
430468 if (csrl & MUSB_CSRL0_RXRDY ) {
431469 const uint16_t count0 = ep_csr -> count0 ;
432470 switch (_dcd .pipe0 .state ) {
433- case PIPE0_STATE_IDLE :
471+ case PIPE0_STATE_IDLE : {
434472 TU_ASSERT (sizeof (tusb_control_request_t ) == count0 , );
435473 union {
436474 tusb_control_request_t req ;
437475 uint32_t u32 [2 ];
438476 } setup_packet ;
439477 setup_packet .u32 [0 ] = musb_regs -> fifo [0 ];
440478 setup_packet .u32 [1 ] = musb_regs -> fifo [0 ];
441-
442- _dcd .pipe0 .remain_wlength = setup_packet .req .wLength ;
443-
444- if (setup_packet .req .wLength == 0 ) {
445- _dcd .pipe0 .state = PIPE0_STATE_STATUS_IN ;
446- } else {
447- _dcd .pipe0 .state = PIPE0_STATE_DATA ;
448- // If OUT (rx) direction, let edpt0_xfer() clear RXRDY when it's ready to receive data.
449- if (setup_packet .req .bmRequestType & TUSB_DIR_IN_MASK ) {
450- ep_csr -> csr0l = MUSB_CSRL0_RXRDYC ;
451- }
452- }
453- dcd_event_setup_received (rhport , (const uint8_t * )& setup_packet .req , true);
479+ pipe0_start_setup (rhport , ep_csr , & setup_packet .req , true);
454480 break ;
481+ }
455482
456- case PIPE0_STATE_DATA : {
483+ case PIPE0_STATE_DATA_OUT : {
457484 // EP0 OUT is single-packet (TU_ASSERT total_bytes <= EP0_SIZE in edpt0_xfer)
458485 // so the whole packet drains in one shot.
459486 if (count0 ) {
@@ -470,24 +497,50 @@ static void process_ep0(uint8_t rhport) {
470497 break ;
471498 }
472499
473- default : break ;
500+ // New SETUP packet arrived while old control transfer is not finished yet. This could happen in following scenarios:
501+ // - Status IN/OUT finished, IRQ and new setup packet IRQ arrive at the same time.
502+ // - Data IN finished and status OUT is received, both IRQs and new setup packet IRQ arrive at the same time.
503+ // could happen when CPU load is high, save the new setup packet for later processing after current status stage complete.
504+ case PIPE0_STATE_STATUS_OUT :
505+ case PIPE0_STATE_STATUS_OUT_PENDING :
506+ case PIPE0_STATE_STATUS_IN :
507+ case PIPE0_STATE_DATA_IN : {
508+ TU_ASSERT (sizeof (tusb_control_request_t ) == count0 , );
509+ union {
510+ tusb_control_request_t req ;
511+ uint32_t u32 [2 ];
512+ } setup_packet ;
513+ setup_packet .u32 [0 ] = musb_regs -> fifo [0 ];
514+ setup_packet .u32 [1 ] = musb_regs -> fifo [0 ];
515+
516+ _dcd .pipe0 .deferred_setup = setup_packet .req ;
517+ _dcd .pipe0 .deferred_setup_valid = true;
518+ ep_csr -> csr0l = MUSB_CSRL0_RXRDYC ;
519+ goto process_status ;
520+ }
474521 }
475522
476523 return ;
477524 }
478525
526+ process_status :
479527 /* When CSRL0 is zero, it means that either
480528 * - completion of sending any length packet TxPktRdy clear
481529 * - or status stage is complete (ZLP) after DataEnd is set */
482530 switch (_dcd .pipe0 .state ) {
483- case PIPE0_STATE_DATA :
531+ case PIPE0_STATE_DATA_IN :
484532 // csrl == 0 in DATA state = TXRDY just cleared, i.e. a DATA IN packet was successfully sent. If the just-sent
485533 // packet was the last (DATAEND was set when ep0_remain_datalen hit zero), transition
486534 // to STATUS_OUT to await the host's STATUS-OUT ZLP confirmation IRQ.
487535 if (_dcd .pipe0 .remain_wlength == 0 ) {
488536 _dcd .pipe0 .state = PIPE0_STATE_STATUS_OUT ;
537+ // If a new SETUP was deferred then STATUS OUT IRQ is missed, manually transition to STATUS_OUT_PENDING to allow ep0_xfer(STATUS OUT) to fire complete immediately.
538+ if (_dcd .pipe0 .deferred_setup_valid ) {
539+ _dcd .pipe0 .state = PIPE0_STATE_STATUS_OUT_PENDING ;
540+ }
489541 }
490542 dcd_event_xfer_complete (rhport , TU_EP0_IN , _dcd .pipe0 .xact_len , XFER_RESULT_SUCCESS , true);
543+
491544 break ;
492545
493546 case PIPE0_STATE_STATUS_OUT :
@@ -499,6 +552,7 @@ static void process_ep0(uint8_t rhport) {
499552 // Second event — edpt0_xfer(STATUS OUT) already called, fire complete now.
500553 _dcd .pipe0 .state = PIPE0_STATE_IDLE ;
501554 dcd_event_xfer_complete (rhport , TU_EP0_OUT , 0 , XFER_RESULT_SUCCESS , true);
555+ pipe0_process_deferred_setup (rhport , ep_csr , true);
502556 break ;
503557
504558 case PIPE0_STATE_STATUS_IN :
@@ -508,6 +562,7 @@ static void process_ep0(uint8_t rhport) {
508562 }
509563 _dcd .pipe0 .state = PIPE0_STATE_IDLE ;
510564 dcd_event_xfer_complete (rhport , TU_EP0_IN , 0 , XFER_RESULT_SUCCESS , true);
565+ pipe0_process_deferred_setup (rhport , ep_csr , true);
511566 break ;
512567
513568 default : break ;
@@ -527,6 +582,7 @@ static void process_bus_reset(uint8_t rhport) {
527582 _dcd .pipe0 .buf = NULL ;
528583 _dcd .pipe0 .xact_len = 0 ;
529584 _dcd .pipe0 .remain_wlength = 0 ;
585+ _dcd .pipe0 .deferred_setup_valid = false;
530586
531587 musb -> intr_txen = 1 ; /* Enable only EP0 */
532588 musb -> intr_rxen = 0 ;
@@ -646,7 +702,7 @@ void dcd_sof_enable(uint8_t rhport, bool en)
646702bool dcd_edpt_open (uint8_t rhport , tusb_desc_endpoint_t const * ep_desc ) {
647703 const unsigned ep_addr = ep_desc -> bEndpointAddress ;
648704 const unsigned epn = tu_edpt_number (ep_addr );
649- const unsigned epdir = tu_edpt_dir (ep_addr );
705+ const tusb_dir_t epdir = tu_edpt_dir (ep_addr );
650706 const unsigned mps = tu_edpt_packet_size (ep_desc );
651707
652708 pipe_state_t * pipe = pipe_get (epn , epdir );
@@ -689,7 +745,7 @@ bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet
689745bool dcd_edpt_iso_activate (uint8_t rhport , tusb_desc_endpoint_t const * ep_desc ) {
690746 const unsigned ep_addr = ep_desc -> bEndpointAddress ;
691747 const unsigned epn = tu_edpt_number (ep_addr );
692- const unsigned dir_in = tu_edpt_dir (ep_addr );
748+ const tusb_dir_t dir_in = tu_edpt_dir (ep_addr );
693749 const unsigned mps = tu_edpt_packet_size (ep_desc );
694750
695751 unsigned const ie = musb_dcd_get_int_enable (rhport );
@@ -804,6 +860,7 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
804860 if (ep_addr == TU_EP0_OUT ) { /* Ignore EP0 OUT */
805861 _dcd .pipe0 .state = PIPE0_STATE_IDLE ;
806862 _dcd .pipe0 .buf = NULL ;
863+ _dcd .pipe0 .deferred_setup_valid = false;
807864 ep_csr -> csr0l = MUSB_CSRL0_STALL ;
808865 }
809866 } else {
0 commit comments