@@ -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 ) {
@@ -463,31 +490,54 @@ static void process_ep0(uint8_t rhport) {
463490 if (_dcd .pipe0 .remain_wlength == 0 ) {
464491 // last packet: change state and leave RXRDY for edpt0_xfer(STATUS IN) to ack
465492 _dcd .pipe0 .state = PIPE0_STATE_STATUS_IN ;
466- } else {
467- ep_csr -> csr0l = MUSB_CSRL0_RXRDYC ;
468493 }
469494 dcd_event_xfer_complete (rhport , TU_EP0_OUT , count0 , XFER_RESULT_SUCCESS , true);
470495 break ;
471496 }
472497
473- default : break ;
498+ // New SETUP packet arrived while old control transfer is not finished yet. This could happen in following scenarios:
499+ // - Status IN/OUT finished, IRQ and new setup packet IRQ arrive at the same time.
500+ // - Data IN finished and status OUT is received, both IRQs and new setup packet IRQ arrive at the same time.
501+ // could happen when CPU load is high, save the new setup packet for later processing after current status stage complete.
502+ case PIPE0_STATE_STATUS_OUT :
503+ case PIPE0_STATE_STATUS_OUT_PENDING :
504+ case PIPE0_STATE_STATUS_IN :
505+ case PIPE0_STATE_DATA_IN : {
506+ TU_ASSERT (sizeof (tusb_control_request_t ) == count0 , );
507+ union {
508+ tusb_control_request_t req ;
509+ uint32_t u32 [2 ];
510+ } setup_packet ;
511+ setup_packet .u32 [0 ] = musb_regs -> fifo [0 ];
512+ setup_packet .u32 [1 ] = musb_regs -> fifo [0 ];
513+
514+ _dcd .pipe0 .deferred_setup = setup_packet .req ;
515+ _dcd .pipe0 .deferred_setup_valid = true;
516+ goto process_status ;
517+ }
474518 }
475519
476520 return ;
477521 }
478522
523+ process_status :
479524 /* When CSRL0 is zero, it means that either
480525 * - completion of sending any length packet TxPktRdy clear
481526 * - or status stage is complete (ZLP) after DataEnd is set */
482527 switch (_dcd .pipe0 .state ) {
483- case PIPE0_STATE_DATA :
528+ case PIPE0_STATE_DATA_IN :
484529 // csrl == 0 in DATA state = TXRDY just cleared, i.e. a DATA IN packet was successfully sent. If the just-sent
485530 // packet was the last (DATAEND was set when ep0_remain_datalen hit zero), transition
486531 // to STATUS_OUT to await the host's STATUS-OUT ZLP confirmation IRQ.
487532 if (_dcd .pipe0 .remain_wlength == 0 ) {
488533 _dcd .pipe0 .state = PIPE0_STATE_STATUS_OUT ;
534+ // 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.
535+ if (_dcd .pipe0 .deferred_setup_valid ) {
536+ _dcd .pipe0 .state = PIPE0_STATE_STATUS_OUT_PENDING ;
537+ }
489538 }
490539 dcd_event_xfer_complete (rhport , TU_EP0_IN , _dcd .pipe0 .xact_len , XFER_RESULT_SUCCESS , true);
540+
491541 break ;
492542
493543 case PIPE0_STATE_STATUS_OUT :
@@ -499,6 +549,7 @@ static void process_ep0(uint8_t rhport) {
499549 // Second event — edpt0_xfer(STATUS OUT) already called, fire complete now.
500550 _dcd .pipe0 .state = PIPE0_STATE_IDLE ;
501551 dcd_event_xfer_complete (rhport , TU_EP0_OUT , 0 , XFER_RESULT_SUCCESS , true);
552+ pipe0_process_deferred_setup (rhport , ep_csr , true);
502553 break ;
503554
504555 case PIPE0_STATE_STATUS_IN :
@@ -508,6 +559,7 @@ static void process_ep0(uint8_t rhport) {
508559 }
509560 _dcd .pipe0 .state = PIPE0_STATE_IDLE ;
510561 dcd_event_xfer_complete (rhport , TU_EP0_IN , 0 , XFER_RESULT_SUCCESS , true);
562+ pipe0_process_deferred_setup (rhport , ep_csr , true);
511563 break ;
512564
513565 default : break ;
@@ -527,6 +579,7 @@ static void process_bus_reset(uint8_t rhport) {
527579 _dcd .pipe0 .buf = NULL ;
528580 _dcd .pipe0 .xact_len = 0 ;
529581 _dcd .pipe0 .remain_wlength = 0 ;
582+ _dcd .pipe0 .deferred_setup_valid = false;
530583
531584 musb -> intr_txen = 1 ; /* Enable only EP0 */
532585 musb -> intr_rxen = 0 ;
@@ -646,7 +699,7 @@ void dcd_sof_enable(uint8_t rhport, bool en)
646699bool dcd_edpt_open (uint8_t rhport , tusb_desc_endpoint_t const * ep_desc ) {
647700 const unsigned ep_addr = ep_desc -> bEndpointAddress ;
648701 const unsigned epn = tu_edpt_number (ep_addr );
649- const unsigned epdir = tu_edpt_dir (ep_addr );
702+ const tusb_dir_t epdir = tu_edpt_dir (ep_addr );
650703 const unsigned mps = tu_edpt_packet_size (ep_desc );
651704
652705 pipe_state_t * pipe = pipe_get (epn , epdir );
@@ -689,7 +742,7 @@ bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet
689742bool dcd_edpt_iso_activate (uint8_t rhport , tusb_desc_endpoint_t const * ep_desc ) {
690743 const unsigned ep_addr = ep_desc -> bEndpointAddress ;
691744 const unsigned epn = tu_edpt_number (ep_addr );
692- const unsigned dir_in = tu_edpt_dir (ep_addr );
745+ const tusb_dir_t dir_in = tu_edpt_dir (ep_addr );
693746 const unsigned mps = tu_edpt_packet_size (ep_desc );
694747
695748 unsigned const ie = musb_dcd_get_int_enable (rhport );
@@ -804,6 +857,7 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
804857 if (ep_addr == TU_EP0_OUT ) { /* Ignore EP0 OUT */
805858 _dcd .pipe0 .state = PIPE0_STATE_IDLE ;
806859 _dcd .pipe0 .buf = NULL ;
860+ _dcd .pipe0 .deferred_setup_valid = false;
807861 ep_csr -> csr0l = MUSB_CSRL0_STALL ;
808862 }
809863 } else {
0 commit comments