usb: fix transmissions resuming early after world switch

When the driver goes through a world switch a reconnection with the host
is needed before operations can resume. This requires usb_open_status to
be reset.

Also: bind a FIFO before a commit that involves data transfer, mirroring
what happens in writes. This ensures that PID is set to BUF, mainly.
This commit is contained in:
Lephe 2022-11-12 14:49:12 +01:00
parent d110ab608e
commit 5f9553f3b8
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
2 changed files with 18 additions and 14 deletions

View file

@ -87,8 +87,10 @@ static void fifo_bind(fifo_t ct, int pipe, int mode, int size)
{ {
size = (size - (size == 4) - 1) & 3; size = (size - (size == 4) - 1) & 3;
if(ct == CF) if(pipe == 0)
{ {
if(USB.CFIFOSEL.ISEL == 1 && USB.DCPCTR.PID == 1) return;
if(mode == FIFO_WRITE) USB.DCPCTR.PID = 1; if(mode == FIFO_WRITE) USB.DCPCTR.PID = 1;
/* RCNT=0 REW=0 MBW=size BIGEND=1 ISEL=mode CURPIPE=0 */ /* RCNT=0 REW=0 MBW=size BIGEND=1 ISEL=mode CURPIPE=0 */
USB.CFIFOSEL.word = 0x0100 | (mode << 5) | (size << 10); USB.CFIFOSEL.word = 0x0100 | (mode << 5) | (size << 10);
@ -129,9 +131,13 @@ static void fifo_unbind(fifo_t ct)
/* Current operation waiting to be performed on each pipe. There are two /* Current operation waiting to be performed on each pipe. There are two
possible states for a pipe's transfer data: possible states for a pipe's transfer data:
-> Either there is a transfer going on, in which case (data != NULL), -> Either there is a transfer going on, in which case (data != NULL),
(size != 0), (controller != NOF), and (used) has no meaning. (size != 0), and (used) has no meaning.
-> Either there is no transfer going on, and (data = NULL), (size = 0), and -> Either there is no transfer going on, and (data = NULL), (size = 0).
(controller = NOF).
A controller is assigned to t->ct when a write first occurs until the pipe
is fully committed. (ct = NOF) indicates an unused pipe, while (ct != NOF)
indicates that stuff has been written and is waiting a commit.
Additionally, between a call to write_round() and the corresponding Additionally, between a call to write_round() and the corresponding
finish_write(), the (flying) attribute is set to a non-zero value indicating finish_write(), the (flying) attribute is set to a non-zero value indicating
how many bytes are waiting for write completion. */ how many bytes are waiting for write completion. */
@ -224,7 +230,7 @@ static void finish_transfer(struct transfer volatile *t, int pipe)
This function is called when a write round completes, either by the handler This function is called when a write round completes, either by the handler
of the BEMP interrupt if the round filled the FIFO, or by the handler of the of the BEMP interrupt if the round filled the FIFO, or by the handler of the
DMA transfer the or write_round() function itself if it didn't. DMA transfer or the write_round() function itself if it didn't.
It the current write operation has finished with this round, this function It the current write operation has finished with this round, this function
invokes the write_async callback. */ invokes the write_async callback. */
@ -261,13 +267,7 @@ static void write_round(struct transfer volatile *t, int pipe)
if(ct == CF) FIFO = &USB.CFIFO; if(ct == CF) FIFO = &USB.CFIFO;
if(ct == D0F) FIFO = &USB.D0FIFO; if(ct == D0F) FIFO = &USB.D0FIFO;
if(ct == D1F) FIFO = &USB.D1FIFO; if(ct == D1F) FIFO = &USB.D1FIFO;
fifo_bind(ct, pipe, FIFO_WRITE, t->unit_size);
if(pipe == 0)
{
if(USB.CFIFOSEL.ISEL != 1 || USB.DCPCTR.PID != 1)
fifo_bind(ct, 0, FIFO_WRITE, 1);
}
else fifo_bind(ct, pipe, FIFO_WRITE, t->unit_size);
/* Amount of data that can be transferred in a single run */ /* Amount of data that can be transferred in a single run */
int available = pipe_bufsize(pipe) - (pipe == 0 ? 0 : t->used); int available = pipe_bufsize(pipe) - (pipe == 0 ? 0 : t->used);
@ -323,7 +323,7 @@ int usb_write_async(int pipe, void const *data, int size, int unit_size,
t->data = data; t->data = data;
t->size = size; t->size = size;
t->unit_size = unit_size; t->unit_size = (pipe == 0) ? 1 : unit_size;
t->dma = use_dma; t->dma = use_dma;
t->committed = false; t->committed = false;
t->ct = ct; t->ct = ct;
@ -375,7 +375,7 @@ int usb_commit_async(int pipe, gint_call_t callback)
return 0; return 0;
} }
/* Commiting an empty pipe ends the transfer on the spot */ /* Committing an empty pipe ends the transfer on the spot */
if(t->used == 0) if(t->used == 0)
{ {
finish_transfer(t, pipe); finish_transfer(t, pipe);
@ -384,6 +384,7 @@ int usb_commit_async(int pipe, gint_call_t callback)
/* Set BVAL=1 and inform the BEMP handler of the commitment with the /* Set BVAL=1 and inform the BEMP handler of the commitment with the
committed flag; the handler will invoke finish_transfer() */ committed flag; the handler will invoke finish_transfer() */
fifo_bind(t->ct, pipe, FIFO_WRITE, t->unit_size);
if(t->ct == D0F) USB.D0FIFOCTR.BVAL = 1; if(t->ct == D0F) USB.D0FIFOCTR.BVAL = 1;
if(t->ct == D1F) USB.D1FIFOCTR.BVAL = 1; if(t->ct == D1F) USB.D1FIFOCTR.BVAL = 1;
usb_log("[PIPE%d] Committed transfer\n", pipe); usb_log("[PIPE%d] Committed transfer\n", pipe);

View file

@ -287,6 +287,9 @@ static void hrestore(usb_state_t const *s)
{ {
hpoweron_write(); hpoweron_write();
/* We will need to reconnect with the PC */
usb_open_status = false;
USB.DVSTCTR.word = s->DVSTCTR; USB.DVSTCTR.word = s->DVSTCTR;
USB.TESTMODE.word = s->TESTMODE; USB.TESTMODE.word = s->TESTMODE;
USB.REG_C2 = s->REG_C2; USB.REG_C2 = s->REG_C2;