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.
This commit is contained in:
Lephe 2023-02-18 16:55:17 +01:00
parent c0671649df
commit aee67a76f3
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
5 changed files with 122 additions and 43 deletions

View File

@ -13,6 +13,7 @@
extern "C" {
#endif
#include <gint/config.h>
#include <gint/mpu/dma.h>
#include <gint/clock.h>
@ -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

View File

@ -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;

View File

@ -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
//---

View File

@ -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;
}

View File

@ -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);