mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-29 13:03:36 +01:00
usb: replace struct transfer with a more generic async. I/O op structure (WIP)
This lays the ground for both generalization to reading and sharing that logic with the serial driver.
This commit is contained in:
parent
6f758cd36c
commit
a091efc894
2 changed files with 190 additions and 68 deletions
140
src/usb/asyncio.h
Normal file
140
src/usb/asyncio.h
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
//---
|
||||||
|
// gint:usb:asyncio - Asynchronous I/O common definitions
|
||||||
|
//---
|
||||||
|
|
||||||
|
#ifndef GINT_USB_ASYNCIO
|
||||||
|
#define GINT_USB_ASYNCIO
|
||||||
|
|
||||||
|
#include <gint/defs/types.h>
|
||||||
|
#include <gint/defs/call.h>
|
||||||
|
|
||||||
|
/* Data tracking the progress of a multi-part multi-round async I/O operation.
|
||||||
|
|
||||||
|
* Multi-part refers to writes being constructed over several calls to
|
||||||
|
write(2) followed by a "commit" with sync(2) (for async file descriptors;
|
||||||
|
synchronous file descriptors are committed at every write).
|
||||||
|
* Multi-round refers to the operation interacting multiple times with
|
||||||
|
hardware in order to communicate the complete data.
|
||||||
|
|
||||||
|
The process of performing such an I/O operation, as tracked by this
|
||||||
|
structure and use throughout gint, is as follows. For a write:
|
||||||
|
|
||||||
|
WRITING ---------------------.
|
||||||
|
^ | | HW buffer
|
||||||
|
Start writing | | Not full | full: start
|
||||||
|
| | | transmission
|
||||||
|
write(2) | v v
|
||||||
|
--> IDLE ------------------> PENDING <------------- FLYING-WRITE
|
||||||
|
^ ^ | DONE interrupt
|
||||||
|
| DONE write(2) | |
|
||||||
|
| interrupt | |
|
||||||
|
| | | Data exhausted
|
||||||
|
| sync(2): start | v
|
||||||
|
FLYING-COMMIT <------------ IN-PROGRESS
|
||||||
|
transmission
|
||||||
|
|
||||||
|
Initially the operation is in the IDLE state. When a write(2) is issued, it
|
||||||
|
interacts with hardware then transitions to the IN-PROGRESS state, where it
|
||||||
|
remains for any subsequent write(2). A sync(2) will properly commit data to
|
||||||
|
the hardware, finish the operation and return to the IDLE state.
|
||||||
|
|
||||||
|
The FLYING-WRITE and FLYING-COMMIT states refer to waiting periods, after
|
||||||
|
issuing hardware commands, during which hardware communicates. Usually an
|
||||||
|
interrupt signals when hardware is ready to resume work.
|
||||||
|
|
||||||
|
Note that in a series of write(2), hardware is only instructed to send data
|
||||||
|
once the hardware buffer is full. Therefore, a write(2) might transition
|
||||||
|
directly from IDLE or IN-PROGRESS, to PENDING, to IN-PROGRESS, without
|
||||||
|
actually communicating with the outside world.
|
||||||
|
|
||||||
|
The invariants and meaning for each state are as follow:
|
||||||
|
|
||||||
|
State Characterization Description
|
||||||
|
============================================================================
|
||||||
|
IDLE type == ASYNCIO_NONE No I/O operation
|
||||||
|
PENDING data_w && !flying_w \ Ready to write pending data
|
||||||
|
&& round_size == 0
|
||||||
|
WRITING round_size > 0 CPU/DMA write to HW in progress
|
||||||
|
FLYING-WRITE flying_w && !committed_w HW transmission in progress
|
||||||
|
IN-PROGRESS data_w != NULL && !flying_w Waiting for write(2) or sync(2)
|
||||||
|
FLYING-COMMIT flying_w && committed_w HW commit in progress
|
||||||
|
============================================================================
|
||||||
|
|
||||||
|
For a read:
|
||||||
|
IN interrupt
|
||||||
|
--> IDLE-EMPTY --------------> IDLE-READY
|
||||||
|
| \ | ^
|
||||||
|
read(2) | \ Transaction read(2) | | Buffer full with
|
||||||
|
| \ exhausted | | transaction not exhausted
|
||||||
|
| '----<----------. | |
|
||||||
|
| \ | |
|
||||||
|
v IN interrupt \ v | .---. Read from
|
||||||
|
WAITING ------------------> READING v hardware
|
||||||
|
'---'
|
||||||
|
|
||||||
|
On this diagram, the right side indicates the presence of data to read from
|
||||||
|
hardware while the bottom side indicates a read(2) request by the user.
|
||||||
|
Notice the diagonal arrow back to IDLE-EMPTY, which means that read(2) will
|
||||||
|
always return at the end of a transaction even if the user-provided buffer
|
||||||
|
is not full (to avoid waiting).
|
||||||
|
|
||||||
|
The invariants and meaning for each state are as follow:
|
||||||
|
|
||||||
|
State Characterization Description
|
||||||
|
============================================================================
|
||||||
|
IDLE-EMPTY type == ASYNCIO_NONE No I/O operation
|
||||||
|
IDLE-READY !data_r && buffer_size > 0 Hardware waiting for us to read
|
||||||
|
WAITING data_r && !buffer_size Waiting for further HW data
|
||||||
|
READING round_size > 0 DMA/CPU read from HW in progress
|
||||||
|
============================================================================
|
||||||
|
|
||||||
|
States can be checked and transitioned with the API functions below. */
|
||||||
|
|
||||||
|
enum { ASYNCIO_NONE, ASYNCIO_READ, ASYNCIO_WRITE };
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/** User-facing information **/
|
||||||
|
|
||||||
|
/* Direction of I/O operation */
|
||||||
|
uint8_t type;
|
||||||
|
/* Whether the DMA should be used for hardware access */
|
||||||
|
bool dma;
|
||||||
|
/* Whether the data has been committed by sync(2) [write] */
|
||||||
|
bool committed_w;
|
||||||
|
/* Operation's unit size (meaning depends on hardware) */
|
||||||
|
uint8_t unit_size;
|
||||||
|
|
||||||
|
union {
|
||||||
|
/* Address of data to transfer, incremented gradually [write] */
|
||||||
|
void const *data_w;
|
||||||
|
/* Address of buffer to store data to, incremented gradually [read] */
|
||||||
|
void *data_r;
|
||||||
|
};
|
||||||
|
/* Size of data left to transfer / buffer space available */
|
||||||
|
int size;
|
||||||
|
/* Callback at the end of the current write, final commit, or read */
|
||||||
|
gint_call_t callback;
|
||||||
|
|
||||||
|
/** Hardware state information **/
|
||||||
|
|
||||||
|
/* Size of data currently in the hardware buffer */
|
||||||
|
uint16_t buffer_used;
|
||||||
|
/* Size of data being read/written in the current round (which may itself
|
||||||
|
be asynchronous if it's using the DMA) */
|
||||||
|
uint16_t round_size;
|
||||||
|
/* Hardware resource being used for access (meaning depends on hardware).
|
||||||
|
Usually, this is assigned during hardware transactions, ie.:
|
||||||
|
- During a write, a controller is assigned when leaving the IDLE state
|
||||||
|
and returned when re-entering the IDLE state.
|
||||||
|
- During a read, a controller is assigne when leaving the IDLE-EMPTY
|
||||||
|
state and returned when re-entering the IDLE-EMPTY state. */
|
||||||
|
uint8_t controller;
|
||||||
|
/* Whether a hardware operation is in progress ("flying" write states) */
|
||||||
|
bool flying_w;
|
||||||
|
|
||||||
|
} asyncio_op_t;
|
||||||
|
|
||||||
|
/* */
|
||||||
|
|
||||||
|
#endif /* GINT_USB_ASYNCIO */
|
118
src/usb/pipes.c
118
src/usb/pipes.c
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "asyncio.h"
|
||||||
#include "usb_private.h"
|
#include "usb_private.h"
|
||||||
|
|
||||||
#define USB SH7305_USB
|
#define USB SH7305_USB
|
||||||
|
@ -173,39 +174,19 @@ 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), and (used) has no meaning.
|
(size != 0), and (buffer_used) has no meaning.
|
||||||
-> Either there is no transfer going on, and (data = NULL), (size = 0).
|
-> 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
|
A controller is assigned to t->controller when a write first occurs until
|
||||||
is fully committed. (ct = NOF) indicates an unused pipe, while (ct != NOF)
|
the pipe is fully committed. (ct = NOF) indicates an unused pipe, while
|
||||||
indicates that stuff has been written and is waiting a commit.
|
(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 (round_size) attribute is set to a non-zero value
|
||||||
how many bytes are waiting for write completion. */
|
indicating how many bytes are waiting for write completion. */
|
||||||
struct transfer {
|
|
||||||
/* Address of data to transfer next */
|
|
||||||
void const *data;
|
|
||||||
/* Size of data left to transfer */
|
|
||||||
int size;
|
|
||||||
/* Size of data currently in the FIFO (less than the FIFO capacity) */
|
|
||||||
uint16_t used;
|
|
||||||
/* Data sent in the last transfer not yet finished by finish_round() */
|
|
||||||
uint16_t flying;
|
|
||||||
/* Write size */
|
|
||||||
uint8_t unit_size;
|
|
||||||
/* Whether the data has been committed to a transfer */
|
|
||||||
bool committed;
|
|
||||||
/* Whether to use the DMA */
|
|
||||||
bool dma;
|
|
||||||
/* FIFO controller being used for this transfer */
|
|
||||||
fifo_t ct;
|
|
||||||
/* Callback to be invoked at the end of the current write or commit
|
|
||||||
(both cannot exist at the same time) */
|
|
||||||
gint_call_t callback;
|
|
||||||
};
|
|
||||||
/* Multi-round operations to be continued whenever buffers are ready */
|
/* Multi-round operations to be continued whenever buffers are ready */
|
||||||
GBSS static struct transfer volatile pipe_transfers[10];
|
GBSS static asyncio_op_t volatile pipe_transfers[10];
|
||||||
|
|
||||||
void usb_pipe_init_transfers(void)
|
void usb_pipe_init_transfers(void)
|
||||||
{
|
{
|
||||||
|
@ -229,11 +210,11 @@ static void write_32(uint32_t const *data, int size, uint32_t volatile *FIFO)
|
||||||
GINLINE static bool pipe_busy(int pipe)
|
GINLINE static bool pipe_busy(int pipe)
|
||||||
{
|
{
|
||||||
/* Multi-round write still not finished */
|
/* Multi-round write still not finished */
|
||||||
if(pipe_transfers[pipe].data) return true;
|
if(pipe_transfers[pipe].data_w) return true;
|
||||||
/* Transfer in progress */
|
/* Transfer in progress */
|
||||||
if(pipe && !USB.PIPECTR[pipe-1].BSTS) return true;
|
if(pipe && !USB.PIPECTR[pipe-1].BSTS) return true;
|
||||||
/* Callback for a just-finished transfer not yet called */
|
/* Callback for a just-finished transfer not yet called */
|
||||||
if(pipe_transfers[pipe].flying) return true;
|
if(pipe_transfers[pipe].round_size) return true;
|
||||||
/* All good */
|
/* All good */
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -241,7 +222,8 @@ GINLINE static bool pipe_busy(int pipe)
|
||||||
/* Size of a pipe's buffer area, in bytes */
|
/* Size of a pipe's buffer area, in bytes */
|
||||||
static int pipe_bufsize(int pipe)
|
static int pipe_bufsize(int pipe)
|
||||||
{
|
{
|
||||||
if(pipe == 0) return USB.DCPMAXP.MXPS;
|
if(pipe == 0)
|
||||||
|
return USB.DCPMAXP.MXPS;
|
||||||
|
|
||||||
USB.PIPESEL.PIPESEL = pipe;
|
USB.PIPESEL.PIPESEL = pipe;
|
||||||
return (USB.PIPEBUF.BUFSIZE + 1) * 64;
|
return (USB.PIPEBUF.BUFSIZE + 1) * 64;
|
||||||
|
@ -252,15 +234,15 @@ static int pipe_bufsize(int pipe)
|
||||||
This function is called when the final round of a transfer has completed,
|
This function is called when the final round of a transfer has completed,
|
||||||
either by the handler of the BEMP interrupt or by the usb_commit_async()
|
either by the handler of the BEMP interrupt or by the usb_commit_async()
|
||||||
function if the pipe is being committed when empty. */
|
function if the pipe is being committed when empty. */
|
||||||
static void finish_transfer(struct transfer volatile *t, int pipe)
|
static void finish_transfer(asyncio_op_t volatile *t, int pipe)
|
||||||
{
|
{
|
||||||
/* Free the FIFO controller */
|
/* Free the FIFO controller */
|
||||||
fifo_unbind(t->ct);
|
fifo_unbind(t->controller);
|
||||||
t->ct = NOF;
|
t->controller = NOF;
|
||||||
|
|
||||||
/* Mark the transfer as unused */
|
/* Mark the transfer as unused */
|
||||||
t->committed = false;
|
t->committed_w = false;
|
||||||
t->used = 0;
|
t->buffer_used = 0;
|
||||||
|
|
||||||
/* Disable the interrupt */
|
/* Disable the interrupt */
|
||||||
if(pipe != 0)
|
if(pipe != 0)
|
||||||
|
@ -278,22 +260,22 @@ static void finish_transfer(struct transfer volatile *t, int pipe)
|
||||||
|
|
||||||
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. */
|
||||||
static void finish_round(struct transfer volatile *t, int pipe)
|
static void finish_round(asyncio_op_t volatile *t, int pipe)
|
||||||
{
|
{
|
||||||
/* Update the pointer as a result of the newly-finished write */
|
/* Update the pointer as a result of the newly-finished write */
|
||||||
t->used += t->flying;
|
t->buffer_used += t->round_size;
|
||||||
t->data += t->flying;
|
t->data_w += t->round_size;
|
||||||
t->size -= t->flying;
|
t->size -= t->round_size;
|
||||||
t->flying = 0;
|
t->round_size = 0;
|
||||||
|
|
||||||
/* Account for auto-transfers */
|
/* Account for auto-transfers */
|
||||||
if(t->used == pipe_bufsize(pipe))
|
if(t->buffer_used == pipe_bufsize(pipe))
|
||||||
t->used = 0;
|
t->buffer_used = 0;
|
||||||
|
|
||||||
/* At the end, free the FIFO and invoke the callback. Hold the
|
/* At the end, free the FIFO and invoke the callback. Hold the
|
||||||
controller until the pipe is committed */
|
controller until the pipe is committed */
|
||||||
if(t->size == 0) {
|
if(t->size == 0) {
|
||||||
t->data = NULL;
|
t->data_w = NULL;
|
||||||
gint_call(t->callback);
|
gint_call(t->callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,9 +287,9 @@ static void finish_round(struct transfer volatile *t, int pipe)
|
||||||
If this is a partial round (FIFO not going to be full), finish_round() is
|
If this is a partial round (FIFO not going to be full), finish_round() is
|
||||||
invoked after the write. Otherwise the FIFO is transmitted automatically and
|
invoked after the write. Otherwise the FIFO is transmitted automatically and
|
||||||
the BEMP handler will call finish_round() after the transfer. */
|
the BEMP handler will call finish_round() after the transfer. */
|
||||||
static void write_round(struct transfer volatile *t, int pipe)
|
static void write_round(asyncio_op_t volatile *t, int pipe)
|
||||||
{
|
{
|
||||||
fifo_t ct = t->ct;
|
fifo_t ct = t->controller;
|
||||||
void volatile *FIFO = NULL;
|
void volatile *FIFO = NULL;
|
||||||
|
|
||||||
if(ct == CF) FIFO = &USB.CFIFO;
|
if(ct == CF) FIFO = &USB.CFIFO;
|
||||||
|
@ -316,9 +298,9 @@ static void write_round(struct transfer volatile *t, int pipe)
|
||||||
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 */
|
/* 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->buffer_used);
|
||||||
int size = min(t->size, available);
|
int size = min(t->size, available);
|
||||||
t->flying = size;
|
t->round_size = size;
|
||||||
|
|
||||||
/* If this is a partial write (size < available), call finish_round()
|
/* If this is a partial write (size < available), call finish_round()
|
||||||
after the copy to notify the user that the pipe is ready. Otherwise,
|
after the copy to notify the user that the pipe is ready. Otherwise,
|
||||||
|
@ -340,14 +322,14 @@ static void write_round(struct transfer volatile *t, int pipe)
|
||||||
int channel = (ct == D0F) ? 3 : 4;
|
int channel = (ct == D0F) ? 3 : 4;
|
||||||
|
|
||||||
bool ok = dma_transfer_async(channel, block_size, size,
|
bool ok = dma_transfer_async(channel, block_size, size,
|
||||||
t->data, DMA_INC, (void *)FIFO, DMA_FIXED, callback);
|
t->data_w, DMA_INC, (void *)FIFO, DMA_FIXED, callback);
|
||||||
if(!ok) USB_LOG("DMA async failed on channel %d!\n", channel);
|
if(!ok) USB_LOG("DMA async failed on channel %d!\n", channel);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(t->unit_size == 1) write_8(t->data, size, FIFO);
|
if(t->unit_size == 1) write_8(t->data_w, size, FIFO);
|
||||||
if(t->unit_size == 2) write_16(t->data, size >> 1, FIFO);
|
if(t->unit_size == 2) write_16(t->data_w, size >> 1, FIFO);
|
||||||
if(t->unit_size == 4) write_32(t->data, size >> 2, FIFO);
|
if(t->unit_size == 4) write_32(t->data_w, size >> 2, FIFO);
|
||||||
if(partial) finish_round(t, pipe);
|
if(partial) finish_round(t, pipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,22 +341,22 @@ int usb_write_async(int pipe, void const *data, int size, int unit_size,
|
||||||
{
|
{
|
||||||
if(pipe_busy(pipe)) return USB_WRITE_BUSY;
|
if(pipe_busy(pipe)) return USB_WRITE_BUSY;
|
||||||
|
|
||||||
struct transfer volatile *t = &pipe_transfers[pipe];
|
asyncio_op_t volatile *t = &pipe_transfers[pipe];
|
||||||
if(!data || !size) return 0;
|
if(!data || !size) return 0;
|
||||||
|
|
||||||
/* Re-use the controller from a previous write if there is one,
|
/* Re-use the controller from a previous write if there is one,
|
||||||
otherwise try to get a new free one */
|
otherwise try to get a new free one */
|
||||||
/* TODO: usb_write_async(): TOC/TOU race on controller being free */
|
/* TODO: usb_write_async(): TOC/TOU race on controller being free */
|
||||||
fifo_t ct = t->ct;
|
fifo_t ct = t->controller;
|
||||||
if(ct == NOF) ct = fifo_find_available_controller(pipe);
|
if(ct == NOF) ct = fifo_find_available_controller(pipe);
|
||||||
if(ct == NOF) return USB_WRITE_NOFIFO;
|
if(ct == NOF) return USB_WRITE_NOFIFO;
|
||||||
|
|
||||||
t->data = data;
|
t->data_w = data;
|
||||||
t->size = size;
|
t->size = size;
|
||||||
t->unit_size = (pipe == 0) ? 1 : unit_size;
|
t->unit_size = (pipe == 0) ? 1 : unit_size;
|
||||||
t->dma = use_dma;
|
t->dma = use_dma;
|
||||||
t->committed = false;
|
t->committed_w = false;
|
||||||
t->ct = ct;
|
t->controller = ct;
|
||||||
t->callback = callback;
|
t->callback = callback;
|
||||||
|
|
||||||
/* Set up the Buffer Empty interrupt to refill the buffer when it gets
|
/* Set up the Buffer Empty interrupt to refill the buffer when it gets
|
||||||
|
@ -421,12 +403,12 @@ int usb_write_sync(int pipe, void const *data, int size, int unit, bool dma)
|
||||||
|
|
||||||
int usb_commit_async(int pipe, gint_call_t callback)
|
int usb_commit_async(int pipe, gint_call_t callback)
|
||||||
{
|
{
|
||||||
struct transfer volatile *t = &pipe_transfers[pipe];
|
asyncio_op_t volatile *t = &pipe_transfers[pipe];
|
||||||
if(pipe_busy(pipe)) return USB_COMMIT_BUSY;
|
if(pipe_busy(pipe)) return USB_COMMIT_BUSY;
|
||||||
|
|
||||||
if(t->ct == NOF) return USB_COMMIT_INACTIVE;
|
if(t->controller == NOF) return USB_COMMIT_INACTIVE;
|
||||||
|
|
||||||
t->committed = true;
|
t->committed_w = true;
|
||||||
t->callback = callback;
|
t->callback = callback;
|
||||||
|
|
||||||
/* TODO: Handle complex commits on the DCP */
|
/* TODO: Handle complex commits on the DCP */
|
||||||
|
@ -438,17 +420,17 @@ int usb_commit_async(int pipe, gint_call_t callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Committing 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->buffer_used == 0)
|
||||||
{
|
{
|
||||||
finish_transfer(t, pipe);
|
finish_transfer(t, pipe);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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_w flag; the handler will invoke finish_transfer() */
|
||||||
fifo_bind(t->ct, pipe, FIFO_WRITE, t->unit_size);
|
fifo_bind(t->controller, pipe, FIFO_WRITE, t->unit_size);
|
||||||
if(t->ct == D0F) USB.D0FIFOCTR.BVAL = 1;
|
if(t->controller == D0F) USB.D0FIFOCTR.BVAL = 1;
|
||||||
if(t->ct == D1F) USB.D1FIFOCTR.BVAL = 1;
|
if(t->controller == D1F) USB.D1FIFOCTR.BVAL = 1;
|
||||||
USB_LOG("[PIPE%d] Committed transfer\n", pipe);
|
USB_LOG("[PIPE%d] Committed transfer\n", pipe);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -489,9 +471,9 @@ void usb_commit_sync(int pipe)
|
||||||
/* usb_pipe_write_bemp(): Callback for the BEMP interrupt on a pipe */
|
/* usb_pipe_write_bemp(): Callback for the BEMP interrupt on a pipe */
|
||||||
void usb_pipe_write_bemp(int pipe)
|
void usb_pipe_write_bemp(int pipe)
|
||||||
{
|
{
|
||||||
struct transfer volatile *t = &pipe_transfers[pipe];
|
asyncio_op_t volatile *t = &pipe_transfers[pipe];
|
||||||
|
|
||||||
if(t->committed)
|
if(t->committed_w)
|
||||||
{
|
{
|
||||||
finish_transfer(t, pipe);
|
finish_transfer(t, pipe);
|
||||||
}
|
}
|
||||||
|
@ -499,6 +481,6 @@ void usb_pipe_write_bemp(int pipe)
|
||||||
{
|
{
|
||||||
/* Finish a round; if there is more data, keep going */
|
/* Finish a round; if there is more data, keep going */
|
||||||
finish_round(t, pipe);
|
finish_round(t, pipe);
|
||||||
if(t->data) write_round(t, pipe);
|
if(t->data_w) write_round(t, pipe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue