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;
if(ct == CF)
if(pipe == 0)
{
if(USB.CFIFOSEL.ISEL == 1 && USB.DCPCTR.PID == 1) return;
if(mode == FIFO_WRITE) USB.DCPCTR.PID = 1;
/* RCNT=0 REW=0 MBW=size BIGEND=1 ISEL=mode CURPIPE=0 */
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
possible states for a pipe's transfer data:
-> Either there is a transfer going on, in which case (data != NULL),
(size != 0), (controller != NOF), and (used) has no meaning.
-> Either there is no transfer going on, and (data = NULL), (size = 0), and
(controller = NOF).
(size != 0), and (used) has no meaning.
-> Either there is no transfer going on, and (data = NULL), (size = 0).
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
finish_write(), the (flying) attribute is set to a non-zero value indicating
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
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
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 == D0F) FIFO = &USB.D0FIFO;
if(ct == D1F) FIFO = &USB.D1FIFO;
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);
fifo_bind(ct, pipe, FIFO_WRITE, t->unit_size);
/* Amount of data that can be transferred in a single run */
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->size = size;
t->unit_size = unit_size;
t->unit_size = (pipe == 0) ? 1 : unit_size;
t->dma = use_dma;
t->committed = false;
t->ct = ct;
@ -375,7 +375,7 @@ int usb_commit_async(int pipe, gint_call_t callback)
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)
{
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
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 == D1F) USB.D1FIFOCTR.BVAL = 1;
usb_log("[PIPE%d] Committed transfer\n", pipe);

View file

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