Skip to content

Commit 8907a81

Browse files
committed
dcd_pic: handle bus resume correctly
1 parent 99e6b32 commit 8907a81

File tree

1 file changed

+49
-9
lines changed

1 file changed

+49
-9
lines changed

src/portable/microchip/pic/dcd_pic.c

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -741,16 +741,19 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
741741
//--------------------------------------------------------------------+
742742
void dcd_int_handler(uint8_t rhport)
743743
{
744-
uint32_t is = U1IR;
745-
uint32_t msk = U1IE;
744+
uint32_t is, msk;
745+
746+
// Part 1 - "USB interrupts"
747+
is = U1IR;
748+
msk = U1IE;
746749

747750
U1IR = is & ~msk;
748751
is &= msk;
749752

750753
if (is & _U1IR_UERRIF_MASK) {
751754
uint32_t es = U1EIR;
752755
U1EIR = es;
753-
U1IR = is; /* discard any pending events */
756+
U1IR = is; /* discard any pending events */
754757
}
755758

756759
if (is & _U1IR_URSTIF_MASK) {
@@ -761,29 +764,66 @@ void dcd_int_handler(uint8_t rhport)
761764
if (is & _U1IR_IDLEIF_MASK) {
762765
// Note Host usually has extra delay after bus reset (without SOF), which could falsely
763766
// detected as Sleep event. Though usbd has debouncing logic so we are good
767+
768+
/*
769+
* NOTE: Do not clear U1OTGIRbits.ACTVIF here!
770+
* Reason:
771+
* ACTVIF is only generated once an IDLEIF has been generated.
772+
* This is a 1:1 ratio interrupt generation.
773+
* For every IDLEIF, there will be only one ACTVIF regardless of
774+
* the number of subsequent bus transitions.
775+
*
776+
* If the ACTIF is cleared here, a problem could occur when:
777+
* [ IDLE ][bus activity ->
778+
* <--- 3 ms -----> ^
779+
* ^ ACTVIF=1
780+
* IDLEIF=1
781+
* # # # # (#=Program polling flags)
782+
* ^
783+
* This polling loop will see both
784+
* IDLEIF=1 and ACTVIF=1.
785+
* However, the program services IDLEIF first
786+
* because ACTIVIE=0.
787+
* If this routine clears the only ACTIVIF,
788+
* then it can never get out of the suspend
789+
* mode.
790+
*/
791+
U1OTGIESET = _U1OTGIE_ACTVIE_MASK;
764792
U1IR = _U1IR_IDLEIF_MASK;
765793
process_bus_sleep(rhport);
766794
}
767795

768-
if (is & _U1IR_RESUMEIF_MASK) {
769-
U1IR = _U1IR_RESUMEIF_MASK;
770-
process_bus_resume(rhport);
771-
}
772-
773796
if (is & _U1IR_SOFIF_MASK) {
774797
U1IR = _U1IR_SOFIF_MASK;
775798
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
776799
}
777800

778801
if (is & _U1IR_STALLIF_MASK) {
779-
U1IR = _U1IR_STALLIF_MASK;
780802
process_stall(rhport);
803+
U1IR = _U1IR_STALLIF_MASK;
781804
}
782805

783806
if (is & _U1IR_TRNIF_MASK) {
784807
process_tokdne(rhport);
785808
}
786809

810+
// Part 2 - "USB OTG interrupts"
811+
is = U1OTGIR;
812+
msk = U1OTGIE;
813+
814+
U1OTGIR = is & ~msk;
815+
is &= msk;
816+
817+
if (is & _U1OTGIR_ACTVIF_MASK) {
818+
#if TU_PIC_INT_SIZE == 4
819+
U1OTGIECLR = _U1OTGIE_ACTVIE_MASK;
820+
#else
821+
U1OTGIE &= ~_U1OTGIE_ACTVIE_MASK;
822+
#endif
823+
U1OTGIR = _U1OTGIR_ACTVIF_MASK;
824+
process_bus_resume(rhport);
825+
}
826+
787827
intr_clear(rhport);
788828
}
789829

0 commit comments

Comments
 (0)