From c59b2f90fb6f6e121bc27db7275a1eb66ec6baec Mon Sep 17 00:00:00 2001 From: Lephe Date: Sat, 4 Mar 2023 18:06:21 +0100 Subject: [PATCH] usb: prototype reading mode Currently only tested with short messages using the fxlink interface. There is much to be expanded upon, but this is a worthy start. --- include/gint/drivers/asyncio.h | 55 ++++++++--- include/gint/usb.h | 46 ++++++++- src/usb/asyncio.c | 39 +++++++- src/usb/classes/ff-bulk.c | 75 ++++++++++++++- src/usb/pipes.c | 170 ++++++++++++++++++++++++++++++--- src/usb/usb.c | 23 ++++- src/usb/usb_private.h | 3 + 7 files changed, 374 insertions(+), 37 deletions(-) diff --git a/include/gint/drivers/asyncio.h b/include/gint/drivers/asyncio.h index fc42436..601e716 100644 --- a/include/gint/drivers/asyncio.h +++ b/include/gint/drivers/asyncio.h @@ -11,10 +11,11 @@ /* 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 fsync(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. + write(2) followed by a "commit" with fsync(2), and reads on a single data + transfer being made over several calls to read(2) (for asynchronous file + descriptors at least). + * Multi-round refers to the writes 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: @@ -71,8 +72,8 @@ IN interrupt --> IDLE-EMPTY --------------> IDLE-READY | \ | ^ - read(2) | \ Transaction read(2) | | Buffer full with - | \ exhausted | | transaction not exhausted + read(2) | \ Transaction read(2) | | Buffer full + | \ exhausted* | | | '----<----------. | | | \ | | v IN interrupt \ v | .---. Read from @@ -81,9 +82,17 @@ 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). + Notice the diagonal arrow back to IDLE-EMPTY insteaf of WAITING, which + highlights 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 state returns to IDLE-EMPTY only if the transaction was exhausted while + the buffer is not full. This is to ensure that the caller can detect the end + of a data transfer by waiting for a read(2) which returns less bytes than + requested. In the case where a read(2) consumes exactly all remaining data, + we do not transition immediately to IDLE-EMPTY because we return as many + bytes as were requested. Instead, we return to IDLE-EMPTY after successfully + yielding 0 bytes in the next call. The invariants and meaning for each state are as follow: @@ -91,9 +100,9 @@ ============================================================================ IDLE-EMPTY type == ASYNCIO_NONE No I/O operation ---------------------------------------------------------------------------- - IDLE-READY !data_r && buffer_size > 0 Hardware waiting for us to read + IDLE-READY !data_r && buffer_used > 0 Hardware waiting for us to read ---------------------------------------------------------------------------- - WAITING data_r && !buffer_size Waiting for further HW data + WAITING data_r && !buffer_used Waiting for further HW data ---------------------------------------------------------------------------- READING round_size > 0 DMA/CPU read from HW in progress ============================================================================ @@ -159,7 +168,12 @@ void asyncio_op_clear(asyncio_op_t *op); bool asyncio_op_busy(asyncio_op_t const *op); //--- -// State transition functions +// Operations and call functions +// +// Notice that for a write, the process is a single write call containing many +// write rounds and only a single finish_call(), whereas for a read the process +// is a single read reception contaning many read calls each with their own +// finish_call(), and only a single finish_read_reception(). //--- /* asyncio_op_start_write(): Start a write call */ @@ -169,6 +183,11 @@ void asyncio_op_start_write(asyncio_op_t *op, void const *data, size_t size, /* asyncio_op_start_sync(): Transition a write I/O operation to a fsync call */ void asyncio_op_start_sync(asyncio_op_t *op, gint_call_t const *callback); +/* asyncio_op_start_read(): Start a single-block read from hardware + Returns the size that will actually be read (may be smaller than `size`). */ +size_t asyncio_op_start_read(asyncio_op_t *op, void *data, size_t size, + bool use_dma, gint_call_t const *callback); + /* asyncio_op_finish_call(): Update state after a read/write/fsync call This function should be called when the read(2)/write(2)/fsync(2) call last @@ -178,7 +197,7 @@ void asyncio_op_start_sync(asyncio_op_t *op, gint_call_t const *callback); void asyncio_op_finish_call(asyncio_op_t *op); //--- -// Write call functions +// Write round functions //--- /* asyncio_op_start_write_round(): Start a single-block write to hardware */ @@ -187,4 +206,14 @@ void asyncio_op_start_write_round(asyncio_op_t *op, size_t size); /* asyncio_op_finish_write_round(): Finish a write round and advance data */ void asyncio_op_finish_write_round(asyncio_op_t *op); +//--- +// Read group functions +//--- + +/* asyncio_op_start_read_group(): Start a read call */ +void asyncio_op_start_read_group(asyncio_op_t *op, size_t total_size); + +/* asyncio_op_fininsh_read_group(): Reset operation after a read group */ +void asyncio_op_finish_read_group(asyncio_op_t *op); + #endif /* GINT_USB_ASYNCIO */ diff --git a/include/gint/usb.h b/include/gint/usb.h index ffffa3c..185a34a 100644 --- a/include/gint/usb.h +++ b/include/gint/usb.h @@ -137,8 +137,8 @@ struct usb_interface { /* Answer class-specific SETUP requests */ /* TODO */ - /* Receive data from an endpoint */ - /* TODO */ + /* Notification that an endpoint has data read to be read */ + void (*notify_read)(int endpoint, int size); }; /* usb_interface_endpoint_t: Parameters for an interface endpoint @@ -266,6 +266,48 @@ int usb_commit_sync_timeout(int pipe, timeout_t const *timeout); of the remaining data completes. */ int usb_commit_async(int pipe, gint_call_t callback); +/* usb_read_sync(): Synchronously read from a USB pipe + + This function waits for data to become available on the specified `pipe`, + and then reads up to `size` bytes into `data`. This function will return as + soon as any data gets read, even if it's less than `size`. Thus, in order to + read all available data you should call this function in a loop until you + get less bytes than you requested. + + It is possible for this function to return 0 bytes. If the data available on + the pipe was consumed by a previous read call which perfectly filled the + provided buffer, the pipe will still be considered in the "data available" + state with 0 bytes left to read. The next read call will then successfully + return 0 bytes. This makes it possible to consistently detect the end of a + transfer as the first read which returns less bytes than requested. + + If `use_dma=true`, uses the DMA for transfers. This requires 4-byte + alignment on the buffer, size, and number of bytes previously read in the + current transaction. + + This function will use a FIFO to access the pipe. The FIFO will only be + released once the full buffer. + + Returns the number of bytes read or a negative error code. */ +int usb_read_sync(int pipe, void *data, int size, bool use_dma); + +/* usb_read_sync_timeout(): Synchronous read with a timeout */ +int usb_read_sync_timeout(int pipe, void *data, int size, bool use_dma, + timeout_t const *timeout); + +/* usb_read_async(): Asynchronously read from a USB pipe + + This function is similar to usb_read_sync() except that it is non-blocking; + it returns USB_READ_INACTIVE if there is no active transfer on the pipe. It + will also read 0 bytes under the same conditions as usb_read_sync(). + + Being asynchronous, this function starts the read process and returns + instantly; 0 on success, an error code otherwise. When the read finishes, + the provided callback is called with `*read_size` set to the number of bytes + read. */ +int usb_read_async(int pipe, void *data, int size, bool use_dma, + int *read_size, gint_call_t callback); + //--- // USB debugging functions //--- diff --git a/src/usb/asyncio.c b/src/usb/asyncio.c index b008848..2f3953a 100644 --- a/src/usb/asyncio.c +++ b/src/usb/asyncio.c @@ -53,12 +53,40 @@ void asyncio_op_start_sync(asyncio_op_t *op, gint_call_t const *callback) op->callback = *callback; } +void asyncio_op_start_read_group(asyncio_op_t *op, size_t total_size) +{ + op->type = ASYNCIO_READ; + op->size = total_size; + /* These are specified differently on each read(2) call */ + op->dma = false; + op->data_r = NULL; + op->callback = GINT_CALL_NULL; + op->round_size = 0; +} + +size_t asyncio_op_start_read(asyncio_op_t *op, void *data, size_t size, + bool use_dma, gint_call_t const *callback) +{ + op->dma = use_dma; + op->data_r = data; + op->callback = *callback; + op->round_size = size; + + return ((int)size < op->size ? (int)size : op->size); +} + +void asyncio_op_finish_read_group(asyncio_op_t *op) +{ + asyncio_op_clear(op); +} + void asyncio_op_finish_call(asyncio_op_t *op) { gint_call(op->callback); - /* Clean up the operation, unless it is a write, in which case keep - relevant states until the transaction finishes after a fsync(2). */ + /* Generally clean up the operation; for a write, keep relevant states + until the transaction finishes with an fsync(2); for a read, keep info + needed for further read(2) calls. */ if(op->type == ASYNCIO_WRITE) { op->dma = false; op->data_w = NULL; @@ -66,6 +94,13 @@ void asyncio_op_finish_call(asyncio_op_t *op) op->callback = GINT_CALL_NULL; op->round_size = 0; } + else if(op->type == ASYNCIO_READ) { + op->dma = false; + op->data_r = NULL; + op->size -= op->round_size; + op->callback = GINT_CALL_NULL; + op->round_size = 0; + } else { asyncio_op_clear(op); } diff --git a/src/usb/classes/ff-bulk.c b/src/usb/classes/ff-bulk.c index d73ea46..0bbc929 100644 --- a/src/usb/classes/ff-bulk.c +++ b/src/usb/classes/ff-bulk.c @@ -1,15 +1,17 @@ #include #include #include - #include +#include + +static void notify_read(int endpoint, int size); static usb_dc_interface_t dc_interface = { .bLength = sizeof(usb_dc_interface_t), .bDescriptorType = USB_DC_INTERFACE, .bInterfaceNumber = -1 /* Set by driver */, .bAlternateSetting = 0, - .bNumEndpoints = 1, + .bNumEndpoints = 2, .bInterfaceClass = 0xff, /* Vendor-Specific */ .bInterfaceSubClass = 0x77, /* (not recognized by Casio tools?) */ .bInterfaceProtocol = 0x00, @@ -22,7 +24,15 @@ static usb_dc_endpoint_t dc_endpoint1i = { .bDescriptorType = USB_DC_ENDPOINT, .bEndpointAddress = 0x81, /* 1 IN */ .bmAttributes = 0x02, /* Bulk transfer */ - /* TODO: Additional transactions per µframe ?! */ + .wMaxPacketSize = htole16(512), + .bInterval = 1, +}; +/* Endpoint for PC -> calculator communication */ +static usb_dc_endpoint_t dc_endpoint1o = { + .bLength = sizeof(usb_dc_endpoint_t), + .bDescriptorType = USB_DC_ENDPOINT, + .bEndpointAddress = 0x02, /* 2 OUT */ + .bmAttributes = 0x02, /* Bulk transfer */ .wMaxPacketSize = htole16(512), .bInterval = 1, }; @@ -32,14 +42,18 @@ usb_interface_t const usb_ff_bulk = { .dc = (void const *[]){ &dc_interface, &dc_endpoint1i, + &dc_endpoint1o, NULL, }, /* Parameters for each endpoint */ .params = (usb_interface_endpoint_t []){ { .endpoint = 0x81, /* 1 IN */ .buffer_size = 2048, }, + { .endpoint = 0x02, /* 2 OUT */ + .buffer_size = 2048, }, { 0 }, }, + .notify_read = notify_read, }; GCONSTRUCTOR static void set_strings(void) @@ -56,6 +70,11 @@ int usb_ff_bulk_output(void) return usb_interface_pipe(&usb_ff_bulk, 0x81); } +int usb_ff_bulk_input(void) +{ + return usb_interface_pipe(&usb_ff_bulk, 0x02); +} + //--- // fxlink protocol //--- @@ -68,7 +87,7 @@ bool usb_fxlink_fill_header(usb_fxlink_header_t *header, memset(header, 0, sizeof *header); header->version = htole32(0x00000100); header->size = htole32(data_size); - /* TODO: usb_fxlink_header: avoid sync with interace definition */ + /* TODO: usb_fxlink_fill_header: avoid harcoded transfer size */ header->transfer_size = htole32(2048); strncpy(header->application, application, 16); @@ -136,3 +155,51 @@ void usb_fxlink_videocapture(bool onscreen) { capture_vram(onscreen, "video"); } + +//--- +// Data reception +//--- + +/* Copy of the header for received BULK messages */ +static usb_fxlink_header_t recv_header; +/* Size of transfer not yet read (implies valid header when non-zero) */ +static int recv_size = 0; + +static void header_finished(void) +{ + recv_header.version = le32toh(recv_header.version); + recv_header.size = le32toh(recv_header.size); + recv_header.transfer_size = le32toh(recv_header.transfer_size); + + int major = (recv_header.version >> 8) & 0xff; + int minor = (recv_header.version & 0xff); + + USB_LOG("[ff-bulk %d.%d] New %.16s.%.16s (%d bytes)\n", major, minor, + recv_header.application, recv_header.type, recv_header.size); +} + +static void notify_read(int endpoint, int size) +{ + /* We only have one endpoint for reading, the bulk OUT */ + (void)endpoint; + + USB_LOG("[ff-bulk] Data available on %02x (%d bytes)\n", endpoint, size); + + if(recv_size <= 0) { + usb_read_sync(usb_ff_bulk_input(), &recv_header, sizeof recv_header, + false); + header_finished(); + recv_size = recv_header.size; + } + else { + USB_LOG("-> malloc dropping\n"); + + char *data = malloc(size); + usb_read_sync(usb_ff_bulk_input(), data, size, false); + free(data); + + recv_size -= size; + if(recv_size < 0) + recv_size = 0; + } +} diff --git a/src/usb/pipes.c b/src/usb/pipes.c index ffc5050..d79e38c 100644 --- a/src/usb/pipes.c +++ b/src/usb/pipes.c @@ -33,7 +33,7 @@ void usb_pipe_configure(int address, endpoint_t const *ep) USB.PIPESEL.PIPESEL = ep->pipe; USB.PIPECFG.TYPE = type; - USB.PIPECFG.BFRE = dir_receiving; + USB.PIPECFG.BFRE = 0; /* Enable continuous mode on all bulk transfer pipes TODO: Also make it double mode*/ USB.PIPECFG.DBLB = 0; @@ -54,6 +54,12 @@ void usb_pipe_configure(int address, endpoint_t const *ep) USB.PIPETR[ep->pipe-1].TRE.TRCLR = 1; USB.PIPETR[ep->pipe-1].TRE.TRENB = 0; } + + /* Keep receiving pipes open all the time */ + if(dir_receiving) { + USB.PIPECTR[ep->pipe-1].PID = PID_BUF; + USB.BRDYENB |= (1 << ep->pipe); + } } void usb_pipe_clear(int pipe) @@ -281,7 +287,7 @@ static void finish_call(asyncio_op_t *t, int pipe) { /* Unbind the USB controller used for the call, except for writes since the USB module requires us to keep it until the final commit */ - if(t->type != ASYNCIO_WRITE) { + if(t->type != ASYNCIO_WRITE && t->type != ASYNCIO_READ) { fifo_unbind(t->controller); t->controller = NOF; } @@ -292,31 +298,46 @@ static void finish_call(asyncio_op_t *t, int pipe) asyncio_op_finish_call(t); USB_TRACE("finish_call()"); + + if(t->type == ASYNCIO_READ && t->size < 0) { + if(t->controller == CF) USB.CFIFOCTR.BCLR = 1; + if(t->controller == D0F) USB.D0FIFOCTR.BCLR = 1; + if(t->controller == D1F) USB.D1FIFOCTR.BCLR = 1; + + fifo_unbind(t->controller); + t->controller = NOF; + asyncio_op_finish_read_group(t); + USB_TRACE("finish_call() read group"); + + USB.PIPECTR[pipe-1].PID = PID_BUF; + } } /* This function is called when a round of writing has completed, including all hardware interactions. If the FIFO got filled by the writing, this is after the transmission and BEMP interrupt; otherwise this is when the CPU/DMA finished writing. */ -static void finish_round(asyncio_op_t *t, int pipe) +static void finish_write_round(asyncio_op_t *t, int pipe) { -// USB_LOG("[PIPE%d] finish_round() for %d bytes\n", pipe, t->round_size); +// USB_LOG("[PIPE%d] finish_write_round() for %d bytes\n", +// pipe, t->round_size); asyncio_op_finish_write_round(t); /* Account for auto-transfers */ if(t->buffer_used == pipe_bufsize(pipe)) t->buffer_used = 0; - USB_TRACE("finish_round()"); + USB_TRACE("finish_write_round()"); if(t->size == 0) finish_call(t, pipe); } + /* write_round(): Write up to a FIFO's worth of data to a pipe - 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 - the BEMP handler will call finish_round() after the transfer. */ + If this is a partial round (FIFO not going to be full), finish_write_round() + is invoked after the write. Otherwise the FIFO is transmitted automatically + and the BEMP handler will call finish_write_round() after the transfer. */ static void write_round(asyncio_op_t *t, int pipe) { fifo_t ct = t->controller; @@ -330,7 +351,7 @@ static void write_round(asyncio_op_t *t, int pipe) int available = pipe_bufsize(pipe) - t->buffer_used; int size = min(t->size, available); - /* If this is a partial write (size < available), call finish_round() + /* If we write partially (size < available), call finish_write_round() after the copy to notify the user that the pipe is ready. Otherwise, a USB transfer will occur and the BEMP handler will do it. */ bool partial = (size < available); @@ -340,7 +361,7 @@ static void write_round(asyncio_op_t *t, int pipe) if(t->dma) { gint_call_t callback = partial ? - GINT_CALL(finish_round, (void *)t, pipe) : + GINT_CALL(finish_write_round, (void *)t, pipe) : GINT_CALL_NULL; /* Use DMA channel 3 for D0F and 4 for D1F */ @@ -355,7 +376,7 @@ static void write_round(asyncio_op_t *t, int pipe) { usb_pipe_write4(t->data_w, size, &t->shbuf, &t->shbuf_size, FIFO); - if(partial) finish_round(t, pipe); + if(partial) finish_write_round(t, pipe); } USB_TRACE("write_round()"); @@ -506,7 +527,132 @@ void usb_pipe_write_bemp(int pipe) else { /* Finish a round; if there is more data, keep going */ - finish_round(t, pipe); + finish_write_round(t, pipe); if(t->data_w) write_round(t, pipe); } } + +int usb_read_async(int pipe, void *data, int size, bool use_dma, + int *read_size, gint_call_t callback) +{ + asyncio_op_t *t = &pipe_transfers[pipe]; + if(asyncio_op_busy(t)) + return USB_BUSY; + if(t->type == ASYNCIO_NONE) + return USB_READ_IDLE; + + int actual_size = asyncio_op_start_read(t, data, size, use_dma, + &callback); + if(*read_size) + *read_size = actual_size; + + USB_LOG("async read request for %d/%d bytes\n", size, t->size); + + /* No data to read: finish the call immediately */ + if(actual_size == 0) { + finish_call(t, pipe); + return 0; + } + + /* Read stuff (TODO: Smart reads + DMA) */ + uint32_t volatile *FIFO = NULL; + if(t->controller == CF) FIFO = &USB.CFIFO; + if(t->controller == D0F) FIFO = &USB.D0FIFO; + if(t->controller == D1F) FIFO = &USB.D1FIFO; + + void *dataptr = data; + for(int i = 0; i < size / 4; i++) { + *(uint32_t *)dataptr = *FIFO; + dataptr += 4; + } + if(size & 2) { + *(uint16_t *)dataptr = *(uint16_t volatile *)FIFO; + dataptr += 2; + } + if(size & 1) { + *(uint8_t *)dataptr = *(uint8_t volatile *)FIFO; + dataptr += 1; + } + + finish_call(t, pipe); + return 0; +} + +int usb_read_sync_timeout(int pipe, void *data, int size, bool use_dma, + timeout_t const *timeout) +{ + int volatile flag = 0; + int read_size = 0; + + /* Wait until there is stuff to read, then read it */ + while(1) + { + int rc = usb_read_async(pipe, data, size, use_dma, &read_size, + GINT_CALL_SET(&flag)); + if(rc == 0) + break; + if(rc != USB_BUSY && rc != USB_READ_IDLE) + return rc; + if(timeout_elapsed(timeout)) + return USB_TIMEOUT; + sleep(); + } + + /* Wait until the read completes */ + while(!flag) + { + if(timeout_elapsed(timeout)) + return USB_TIMEOUT; + sleep(); + } + + /* Finish the read if there are 0 bytes left to read */ + asyncio_op_t *t = &pipe_transfers[pipe]; + if(t->size == 0) { + t->size = -1; + finish_call(t, pipe); + } + + return read_size; +} + +int usb_read_sync(int pipe, void *data, int size, bool use_dma) +{ + return usb_read_sync_timeout(pipe, data, size, use_dma, NULL); +} + +void usb_pipe_read_brdy(int pipe) +{ + asyncio_op_t *t = &pipe_transfers[pipe]; + if(asyncio_op_busy(t)) + USB_LOG("pipe %d BRDY while busy?!\n", pipe); + + USB_LOG("[PIPE%d] BRDY with PIPECTR %04x\n", pipe, + USB.PIPECTR[pipe-1].word); + if(!USB.PIPECTR[pipe-1].BSTS) + return; + + /* Prepare a FIFO and the transfer structure so the read can be done + TODO: FIXME: Prevent race accesses during USB interrupts */ + if(t->controller != NOF) + USB_LOG("pipe %d BRDY bound while not busy?!\n", pipe); + else { + fifo_t ct = fifo_find_available_controller(pipe); + /* TODO: BRDY: Delay FIFO binding until read syscall */ + if(ct == NOF) + return; + fifo_bind(ct, pipe, FIFO_READ); + t->controller = ct; + } + + int data_available = 0; + if(t->controller == CF) data_available = USB.CFIFOCTR.DTLN; + if(t->controller == D0F) data_available = USB.D0FIFOCTR.DTLN; + if(t->controller == D1F) data_available = USB.D1FIFOCTR.DTLN; + + asyncio_op_start_read_group(t, data_available); + + /* Notify the interface so it can read or schedule to read */ + endpoint_t *ep = usb_get_endpoint_by_pipe(pipe); + ep->intf->notify_read(ep->dc->bEndpointAddress, data_available); +} diff --git a/src/usb/usb.c b/src/usb/usb.c index f2461bc..3f608a8 100644 --- a/src/usb/usb.c +++ b/src/usb/usb.c @@ -193,12 +193,15 @@ int usb_open(usb_interface_t const **interfaces, gint_call_t callback) USB.CFIFOSEL.REW = 0; USB.CFIFOSEL.BIGEND = 1; - /* VBSE=1 RSME=0 SOFE=0 DVSE=1 CTRE=1 BEMPE=1 NRDYE=0 BRDYE=0 */ - USB.INTENB0.word = 0x9c00; + /* VBSE=1 RSME=0 SOFE=0 DVSE=1 CTRE=1 BEMPE=1 NRDYE=0 BRDYE=1 */ + USB.INTENB0.word = 0x9d00; USB.INTENB1.word = 0x0000; USB.BRDYENB = 0x0000; USB.NRDYENB = 0x0000; USB.BEMPENB = 0x0000; + USB.BRDYSTS = 0x0000; + USB.NRDYSTS = 0x0000; + USB.BEMPSTS = 0x0000; intc_handler_function(0xa20, GINT_CALL(usb_interrupt_handler)); intc_priority(INTC_USB, 8); @@ -278,12 +281,24 @@ static void usb_interrupt_handler(void) else if(USB.INTSTS0.BEMP) { /* Invoke callbacks for each buffer-empty interrupt */ - uint16_t status = USB.BEMPSTS; + uint16_t status = USB.BEMPSTS & USB.BEMPENB; USB.BEMPSTS = 0; for(int i = 0; i <= 9; i++) { - if(status & (1 << i)) usb_pipe_write_bemp(i); + if(status & (1 << i)) + usb_pipe_write_bemp(i); + } + } + else if(USB.INTSTS0.BRDY) + { + uint16_t status = USB.BRDYSTS & USB.BRDYENB; + USB.BRDYSTS = 0; + + for(int i = 0; i <= 9; i++) + { + if(status & (1 << i)) + usb_pipe_read_brdy(i); } } else USB_LOG("<%04X> -> ???\n", USB.INTSTS0.word); diff --git a/src/usb/usb_private.h b/src/usb/usb_private.h index bb594d2..58be9c7 100644 --- a/src/usb/usb_private.h +++ b/src/usb/usb_private.h @@ -129,6 +129,9 @@ 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); +/* usb_pipe_read_brdy(): Callback for the BRDY interrupt on a read pipe */ +void usb_pipe_read_brdy(int pipe); + /* usb_pipe_init_transfers(): Initialize transfer information */ void usb_pipe_init_transfers(void);