From aee67a76f3b17dc94b9658584a8ba9eb87c6ef41 Mon Sep 17 00:00:00 2001 From: Lephe Date: Sat, 18 Feb 2023 16:55:17 +0100 Subject: [PATCH] usb: clear pipes/FIFOs during world switches, delay configuration * Clear pipes and FIFOs during world switches to avoid interference with the OS. LINK uses pipes 3 and 4, and attempts to add a second pipe to the fxlink interface (thus using pipe 4) would interfere with LINK and somehow prevent the pipe from being used (Wireshark captures showed no responses on that pipe). Forcing a blank state is a valid move because that state occurs naturally after a RESET, thus LINK and other add-ins must support it as well. * Delay the application of configuration to the USB configuration stage (specifically, the DVST configured interrupt, even though technically we should do that in SET_CONFIGURATION 0). This is because we previously relied on world switches preserving pipe settings (by not changing them) to reconnect the gint driver after a world switch. This is no longer possible as the world switch now clears the pipes. The new timing makes the driver automatically re-configure as the connection restarts. --- include/gint/drivers/states.h | 23 ++++++--- src/usb/configure.c | 9 ---- src/usb/pipes.c | 35 ++++++++++++- src/usb/usb.c | 92 +++++++++++++++++++++++++---------- src/usb/usb_private.h | 6 +++ 5 files changed, 122 insertions(+), 43 deletions(-) diff --git a/include/gint/drivers/states.h b/include/gint/drivers/states.h index ff7d827..018c46a 100644 --- a/include/gint/drivers/states.h +++ b/include/gint/drivers/states.h @@ -13,6 +13,7 @@ extern "C" { #endif +#include #include #include @@ -88,13 +89,23 @@ typedef struct { typedef struct { /* Control and power-up. We don't save power-related registers from other modules nor UPONCR, because they must be changed to use the module */ - uint16_t SYSCFG, DVSTCTR, TESTMODE, REG_C2; - /* FIFO configuration */ - uint16_t CFIFOSEL, D0FIFOSEL, D1FIFOSEL; + uint16_t SYSCFG, BUSWAIT, DVSTCTR, SOFCFG, TESTMODE, REG_C2; /* Interrupt configuration */ - uint16_t INTENB0, BRDYENB, NRDYENB, BEMPENB, SOFCFG; - /* Default Control Pipe (maybe not needed) */ - uint16_t DCPCFG, DCPMAXP, DCPCTR; + uint16_t INTENB0, INTENB1, BRDYENB, NRDYENB, BEMPENB; + /* Default Control Pipe */ + uint16_t DCPMAXP; + +#ifdef GINT_USB_DEBUG + /* Registers tracked read-only for state analysis and debugging */ + uint16_t SYSSTS, FRMNUM, UFRMNUM; + uint16_t CFIFOSEL, D0FIFOSEL, D1FIFOSEL, CFIFOCTR, D0FIFOCTR, D1FIFOCTR; + uint16_t INTSTS0, INTSTS1, BRDYSTS, NRDYSTS, BEMPSTS; + uint16_t DCPCFG, DCPCTR; + uint16_t USBADDR, USBREQ, USBVAL, USBINDX, USBLENG; + uint16_t PIPESEL, PIPECFG[9], PIPEnCTR[9], PIPEBUF[9]; + /* Ignored: UPONCR, PIPEnMAXP, PIPEnPERI, PIPEnTRN, PIPEnTRE, DEVADDn */ +#endif + } usb_state_t; #ifdef __cplusplus diff --git a/src/usb/configure.c b/src/usb/configure.c index 38cf14f..deba0e0 100644 --- a/src/usb/configure.c +++ b/src/usb/configure.c @@ -208,15 +208,6 @@ void usb_configure(void) } } -void usb_configure_clear_pipes(void) -{ - for(int i = 0; i < 32; i++) - { - if(!conf_ep[i].intf) continue; - usb_pipe_clear(conf_ep[i].pipe); - } -} - usb_interface_t const * const *usb_configure_interfaces(void) { return conf_if; diff --git a/src/usb/pipes.c b/src/usb/pipes.c index 433acd7..02ab5cf 100644 --- a/src/usb/pipes.c +++ b/src/usb/pipes.c @@ -58,7 +58,15 @@ void usb_pipe_configure(int address, endpoint_t const *ep) void usb_pipe_clear(int pipe) { - if(pipe <= 0 || pipe > 9) return; + if(pipe < 0 || pipe > 9) return; + + if(pipe == 0) { + USB.DCPCTR.PID = PID_NAK; + usb_while(USB.DCPCTR.PBUSY); + + USB.DCPCTR.SQCLR = 1; + usb_while(USB.DCPCTR.SQMON != 0); + } /* Set PID=NAK then use ACLRM to clear the pipe */ USB.PIPECTR[pipe-1].PID = PID_NAK; @@ -74,6 +82,24 @@ void usb_pipe_clear(int pipe) usb_while(USB.PIPECTR[pipe-1].SQMON != 0); } +void usb_pipe_reset(int pipe) +{ + if(pipe < 0 || pipe > 9) return; + usb_pipe_clear(pipe); + + if(pipe == 0) { + USB.DCPCFG.word = 0x0000; + USB.DCPCTR.word = 0x0000; + USB.DCPMAXP.word = 0x0000; + return; + } + + USB.PIPESEL.PIPESEL = pipe; + USB.PIPECFG.word = 0x0000; + USB.PIPECTR[pipe-1].word = 0x0000; + USB.PIPEBUF.word = 0x0000; +} + //--- // Operation on FIFO controllers //--- @@ -161,6 +187,13 @@ static void fifo_unbind(fifo_t ct) } } +void usb_pipe_reset_fifos(void) +{ + fifo_unbind(CF); + fifo_unbind(D0F); + fifo_unbind(D1F); +} + //--- // Writing operations //--- diff --git a/src/usb/usb.c b/src/usb/usb.c index c0e7228..f2461bc 100644 --- a/src/usb/usb.c +++ b/src/usb/usb.c @@ -176,6 +176,11 @@ int usb_open(usb_interface_t const **interfaces, gint_call_t callback) USB.SYSCFG.USBE = 1; } + usb_pipe_reset_fifos(); + for(int i = 0; i <= 9; i++) + usb_pipe_reset(i); + usb_pipe_init_transfers(); + /* Prepare the default control pipe. */ USB.DCPCFG.DIR = 0; USB.DCPMAXP.DEVSEL = 0; @@ -187,18 +192,10 @@ int usb_open(usb_interface_t const **interfaces, gint_call_t callback) USB.CFIFOSEL.RCNT = 0; USB.CFIFOSEL.REW = 0; USB.CFIFOSEL.BIGEND = 1; - /* Clear D0FIFOSEL and D1FIFOSEL so that pipes can be configured */ - USB.D0FIFOSEL.word = 0x0000; - USB.D1FIFOSEL.word = 0x0000; - - /* Configure other pipes to use activated interfaces */ - usb_configure(); - usb_configure_clear_pipes(); - /* Initialize transfer tracker for multi-part transfers */ - usb_pipe_init_transfers(); /* VBSE=1 RSME=0 SOFE=0 DVSE=1 CTRE=1 BEMPE=1 NRDYE=0 BRDYE=0 */ USB.INTENB0.word = 0x9c00; + USB.INTENB1.word = 0x0000; USB.BRDYENB = 0x0000; USB.NRDYENB = 0x0000; USB.BEMPENB = 0x0000; @@ -271,7 +268,8 @@ static void usb_interrupt_handler(void) /* When configured, run the callback for usb_open() */ if(USB.INTSTS0.DVSQ == 3) { - usb_configure_clear_pipes(); + usb_configure(); + usb_open_status = true; gint_call(usb_open_callback); usb_open_callback = GINT_CALL_NULL; @@ -301,24 +299,61 @@ static void usb_interrupt_handler(void) void hsave(usb_state_t *s) { s->SYSCFG = USB.SYSCFG.word; + s->BUSWAIT = USB.BUSWAIT.word; + s->DVSTCTR = USB.DVSTCTR.word; + s->SOFCFG = USB.SOFCFG.word; s->TESTMODE = USB.TESTMODE.word; s->REG_C2 = USB.REG_C2; + s->INTENB0 = USB.INTENB0.word; + s->INTENB1 = USB.INTENB1.word; + s->BRDYENB = USB.BRDYENB; + s->NRDYENB = USB.NRDYENB; + s->BEMPENB = USB.BEMPENB; + + s->DCPMAXP = USB.DCPMAXP.word; + +#ifdef GINT_USB_DEBUG + /* Save some other state for inspection only. Since we need to write to + PIPESEL in order to read PIPECFG etc, enable writing. */ + hpoweron_write(); + + s->SYSSTS = USB.SYSSTS.word; + s->FRMNUM = USB.FRMNUM.word; + s->UFRMNUM = USB.UFRMNUM.word; + s->CFIFOSEL = USB.CFIFOSEL.word; s->D0FIFOSEL = USB.D0FIFOSEL.word; s->D1FIFOSEL = USB.D1FIFOSEL.word; + s->CFIFOCTR = USB.CFIFOCTR.word; + s->D0FIFOCTR = USB.D0FIFOCTR.word; + s->D1FIFOCTR = USB.D1FIFOCTR.word; - s->INTENB0 = USB.INTENB0.word; - s->BRDYENB = USB.BRDYENB; - s->NRDYENB = USB.NRDYENB; - s->BEMPENB = USB.BEMPENB; - s->SOFCFG = USB.SOFCFG.word; + s->INTSTS0 = USB.INTSTS0.word; + s->INTSTS1 = USB.INTSTS1.word; + s->BRDYSTS = USB.BRDYSTS; + s->NRDYSTS = USB.NRDYSTS; + s->BEMPSTS = USB.BEMPSTS; s->DCPCFG = USB.DCPCFG.word; - s->DCPMAXP = USB.DCPMAXP.word; s->DCPCTR = USB.DCPCTR.word; + s->USBADDR = USB.USBADDR.word; + s->USBREQ = USB.USBREQ.word; + s->USBVAL = USB.USBVAL.word; + s->USBINDX = USB.USBINDX.word; + s->USBLENG = USB.USBLENG.word; + + s->PIPESEL = USB.PIPESEL.word; + for(int i = 0; i < 9; i++) { + USB.PIPESEL.PIPESEL = i+1; + s->PIPECFG[i] = USB.PIPECFG.word; + s->PIPEnCTR[i] = USB.PIPECTR[i].word; + s->PIPEBUF[i] = USB.PIPEBUF.word; + } +#endif + /* Leave the module open for gint to use it, or for the next restore to proceed more quickly (if during a world switch) */ } @@ -331,30 +366,33 @@ static void hrestore(usb_state_t const *s) usb_open_status = false; USB.DVSTCTR.word = s->DVSTCTR; + USB.SOFCFG.word = s->SOFCFG; USB.TESTMODE.word = s->TESTMODE; + USB.FRMNUM.word = 0; USB.REG_C2 = s->REG_C2; - USB.CFIFOSEL.word = s->CFIFOSEL; - USB.D0FIFOSEL.word = s->D0FIFOSEL; - USB.D1FIFOSEL.word = s->D1FIFOSEL; - USB.INTENB0.word = s->INTENB0; + USB.INTENB1.word = s->INTENB1; USB.BRDYENB = s->BRDYENB; USB.NRDYENB = s->NRDYENB; USB.BEMPENB = s->BEMPENB; - USB.SOFCFG.word = s->SOFCFG; - USB.DCPCFG.word = s->DCPCFG; + usb_pipe_reset_fifos(); + for(int i = 0; i <= 9; i++) + usb_pipe_reset(i); + USB.DCPMAXP.word = s->DCPMAXP; - USB.DCPCTR.word = s->DCPCTR; + USB.PIPESEL.word = 0x0000; /* Clear remaining interrupts. Read-only bits will be ignored */ - USB.INTSTS0.word = 0; - USB.BRDYSTS = 0; - USB.NRDYSTS = 0; - USB.BEMPSTS = 0; + USB.INTSTS0.word = 0x0000; + USB.INTSTS1.word = 0x0000; + USB.BRDYSTS = 0x0000; + USB.NRDYSTS = 0x0000; + USB.BEMPSTS = 0x0000; /* Restore SYSCFG last as clearing SCKE disables writing */ + USB.BUSWAIT.word = s->BUSWAIT; USB.SYSCFG.word = s->SYSCFG; } diff --git a/src/usb/usb_private.h b/src/usb/usb_private.h index 592858b..02ed9fc 100644 --- a/src/usb/usb_private.h +++ b/src/usb/usb_private.h @@ -126,6 +126,12 @@ void usb_pipe_configure(int address, endpoint_t const *ep); /* usb_pipe_clear(): Clear all data in the pipe */ void usb_pipe_clear(int pipe); +/* usb_pipe_reset(): Clear all data in pipe and reset it to a blank state */ +void usb_pipe_reset(int pipe); + +/* usb_pipe_reset_fifos(): Unbind all FIFOs from pipes */ +void usb_pipe_reset_fifos(void); + /* usb_pipe_write_bemp(): Callback for the BEMP interrupt on a pipe */ void usb_pipe_write_bemp(int pipe);