mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2025-04-04 01:27:11 +02:00
* Move logic around tracking transfers to asyncio.c. * Add a "short buffer" holding 0-3 bytes between writes, so that the driver performs only 4-byte writes in the FIFO and a short write in the commit, if needed. - This is partially due to me thinking at some point that degrading writing size was impossible, but it might actually be possible by writing to FIFO/FIFO+2 or FIFO/FIFO+1/FIFO+2/FIFO+3. - In any case I think this new approach wins on performance. * Get rid of unit_size since we now always use 4 bytes. * Add a waiting function which is used in usb_close() (and once tested should be used in world switches too). * Eliminate some of the special cases for the DCP, though not all (in particular I can't get the commit to rely on the BEMP interrupt yet, nor can I properly clear PID to NAK when unbinding).
229 lines
8.3 KiB
C
229 lines
8.3 KiB
C
//---
|
|
// gint:usb:usb-private - Private definitions for the USB driver
|
|
//---
|
|
|
|
#ifndef GINT_USB_USB_PRIVATE
|
|
#define GINT_USB_USB_PRIVATE
|
|
|
|
#include <gint/defs/attributes.h>
|
|
#include <gint/defs/timeout.h>
|
|
#include <gint/gint.h>
|
|
#include <gint/usb.h>
|
|
|
|
//---
|
|
// Configuration of the communication surface between module and host
|
|
//---
|
|
|
|
/* usb_configure_solve(): Find a configuration that can open these interfaces
|
|
|
|
This function determines a way to share USB resources (endpoint numbers,
|
|
pipes, and FIFO memory) between the provided interfaces, in order to open
|
|
the connection with all of these interfaces enabled.
|
|
|
|
This function must only be called when the USB connection is closed. It
|
|
returns 0 on success and one of the USB_* error codes otherwise.
|
|
|
|
@interfaces NULL-terminated list of interfaces to open
|
|
Returns an USB_* error code. */
|
|
int usb_configure_solve(usb_interface_t const **interfaces);
|
|
|
|
/* usb_configure_log(): Print configuration results with USB_LOG()
|
|
This function can be called even if usb_configure_solve() fails. */
|
|
void usb_configure_log(void);
|
|
|
|
/* usb_configure(): Load the solved configuration to the USB module
|
|
|
|
This function configures the USB module's pipes and FIFO memory to prepare
|
|
handling requests to the interfaces activated in usb_configure_solve(). This
|
|
configuration step is un-done by either another configuration through a
|
|
successful usb_open(), or a context restore in the USB driver.
|
|
|
|
This function loads all of the "static" data for the pipes, ie. PIPCFG,
|
|
PIPEBUF, PIPEMAXP, and PIPERI, and doesn't change the "dynamic" data in
|
|
PIPECTR. */
|
|
void usb_configure(void);
|
|
|
|
/* usb_configure_clear_pipes(): Clear configured pipes
|
|
|
|
This function clears configured pipes' dynamic data in PIPECTR. It shoud be
|
|
used when initializing the module but also when resetting the connection to
|
|
the host (eg. after a world switch), since a renewed host will not expect
|
|
any leftover data, non-zero sequence bits, etc. */
|
|
void usb_configure_clear_pipes(void);
|
|
|
|
/* endpoint_t: Driver information for each open endpoint in the device
|
|
|
|
There is one such structure for all 16 configurable endpoints, for each
|
|
direction (totaling 32). endpoint_get() is used to query the structure by
|
|
endpoint number (including the IN/OUT bit). */
|
|
typedef struct {
|
|
/* Which interface this endpoint belongs to */
|
|
usb_interface_t const *intf;
|
|
/* Associated endpoint descriptor */
|
|
usb_dc_endpoint_t const *dc;
|
|
/* Associated pipe, must be a number from 1..9 */
|
|
uint8_t pipe;
|
|
/* Allocated pipe buffer area; this is valid for pipes 1..5. The
|
|
bufsize here is in range 1..32, as opposed to the field in PIPEBUF
|
|
which is in range 0..31. */
|
|
uint8_t bufnmb;
|
|
uint8_t bufsize;
|
|
|
|
} endpoint_t;
|
|
|
|
/* usb_configure_interfaces(): List configured interfaces */
|
|
usb_interface_t const * const *usb_configure_interfaces(void);
|
|
|
|
/* usb_configure_address(): Get the concrete endpoint address
|
|
|
|
This function returns the endpoint address associated with the provided
|
|
interface-numbered endpoint. The value is defined if an interface-provided
|
|
endpoint descriptor with bEndpointAddress equal to either (address) or
|
|
(address ^ 0x80) has been processed.
|
|
|
|
This function is used both to access endpoint data for an interface-provided
|
|
endpoint number, and to ensure that two interface-provided enpoint numbers
|
|
with same base and opposing directions are assigned the same concrete
|
|
endpoint number with its two opposing directions.
|
|
|
|
@intf Interface that provided the address
|
|
@address Endpoint address (as numbered by the interaface)
|
|
-> Returns the assigned endpoint address, or -1 if unassigned. */
|
|
int usb_configure_address(usb_interface_t const *intf, int address);
|
|
|
|
/* usb_configure_endpoint(): Get endpoint data for a concrete address */
|
|
endpoint_t *usb_configure_endpoint(int endpoint);
|
|
|
|
//---
|
|
// Pipe operations
|
|
//
|
|
// When writing to a pipe, the general workflow is as follows:
|
|
//
|
|
// 1. The user performs a write of a block of memory of any size. Because the
|
|
// FIFO for the pipe only has a limited size, the driver splits the write
|
|
// into "rounds" of the size of the FIFO.
|
|
//
|
|
// The rounds are written to the FIFO. If the FIFO is full, the write
|
|
// continues until the FIFO can be accessed again (often after the contents
|
|
// of the FIFO have been transmitted, except in double-buffer mode).
|
|
//
|
|
// If the last round is smaller than the size of the FIFO, the data is not
|
|
// transmitted; this allows the user to perform another write immediately.
|
|
//
|
|
// 2. The user performs more writes, each of which are split into rounds, with
|
|
// each round possibly triggering a transfer (if the FIFO is full). Each
|
|
// write only finishes after all the data is written and the pipe is
|
|
// available for more writing.
|
|
//
|
|
// 3. After the last write, the user *commits* the pipe, causing any data
|
|
// remaining in the FIFO to be transferred even if the FIFO is not full. The
|
|
// commit operation finishes when the pipe is writable again.
|
|
//---
|
|
|
|
/* usb_pipe_configure(): Configure a pipe when opening the connection */
|
|
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_write_bemp(): Callback for the BEMP interrupt on a pipe */
|
|
void usb_pipe_write_bemp(int pipe);
|
|
|
|
/* usb_pipe_init_transfers(): Initialize transfer information */
|
|
void usb_pipe_init_transfers(void);
|
|
|
|
/* usb_wait_all_transfers(): Wait for all transfers to finish
|
|
|
|
This function waits for all current operations on the pipes to finish their
|
|
current read/write/fsync call. Once the waiting period is finished, the
|
|
calls are guaranteed to be finished, but write transactions might not (as
|
|
they require multiple calls finishing with a fsync(2)).
|
|
|
|
If `await_long_writes` is set, this function also waits for all writes to be
|
|
committed, which only makes sense if said writes are executed in a thread
|
|
that is able to run while this is waiting. */
|
|
void usb_wait_all_transfers(bool await_long_writes);
|
|
|
|
/* usb_pipe_write4(): Copy arbitrary ranges of memory to a 4-byte USB FIFO
|
|
|
|
This function copies arbitrarily-aligned data of any size into a 4-byte
|
|
USB FIFO register. It rearranges data so as to perform only 4-byte aligned
|
|
writes. If the data size isn't a multiple of 4 bytes, it stores the
|
|
remainder into a short buffer (holding between 0 and 3 bytes), to be
|
|
combined with fresh data on the next call. The remainder of the buffer can
|
|
be discharged eventually with usb_pipe_flush4().
|
|
|
|
@data Data to write into the FIFO
|
|
@size Number of bytes to write
|
|
@buffer Address of short buffer
|
|
@buffer_size Address of short buffer's size tracker
|
|
@FIFO FIFO to output to */
|
|
void usb_pipe_write4(void const *data, int size, uint32_t volatile *buffer,
|
|
uint8_t volatile *buffer_size, uint32_t volatile *FIFO);
|
|
|
|
/* usb_pipe_flush4(): Flush usb_pipe_write4()'s short buffer
|
|
|
|
This function is used after a sequence of usb_pipe_write4() to flush the
|
|
last few bytes remaining in the short buffer.
|
|
|
|
@buffer Contents of short buffer
|
|
@buffer_size Short buffer's size tracker
|
|
@FIFO FIFO to output to */
|
|
void usb_pipe_flush4(uint32_t buffer, int buffer_size,
|
|
uint32_t volatile *FIFO);
|
|
|
|
//---
|
|
// Timeout waits
|
|
//---
|
|
|
|
/* usb_while(): A while loop with a timeout */
|
|
#define usb_while(COND) ({ \
|
|
timeout_t __t = timeout_make_ms(100); \
|
|
bool __b = false; \
|
|
while((COND) && !(__b = timeout_elapsed(&__t))) {} \
|
|
if(__b) USB_LOG("%s:%d: inf. while(" #COND ")\n", __FUNCTION__, __LINE__); \
|
|
})
|
|
|
|
//---
|
|
// SETUP requests
|
|
//---
|
|
|
|
/* Standard SETUP requests */
|
|
enum {
|
|
GET_STATUS = 0,
|
|
CLEAR_FEATURE = 1,
|
|
SET_FEATURE = 3,
|
|
SET_ADDRESS = 5,
|
|
GET_DESCRIPTOR = 6,
|
|
SET_DESCRIPTOR = 7,
|
|
GET_CONFIGURATION = 8,
|
|
SET_CONFIGURATION = 9,
|
|
GET_INTERFACE = 10,
|
|
SET_INTERFACE = 11,
|
|
SYNCH_FRAME = 12,
|
|
};
|
|
|
|
/* usb_req_setup(): Answer a SETUP request from the userspace handler
|
|
|
|
THis function handles a SETUP request from the host, detected with the VALID
|
|
bit in the INTSTS0 register. The inputs are the USBREQ, USBVAL, USBINDX and
|
|
USBLENG registers, along with the DCP FIFO. */
|
|
void usb_req_setup(void);
|
|
|
|
//---
|
|
// Enumerated constants
|
|
//---
|
|
|
|
enum {
|
|
PID_NAK = 0,
|
|
PID_BUF = 1,
|
|
PID_STALL2 = 2,
|
|
PID_STALL3 = 3,
|
|
};
|
|
enum {
|
|
TYPE_BULK = 1,
|
|
TYPE_INTERRUPT = 2,
|
|
TYPE_ISOCHRONOUS = 3,
|
|
};
|
|
|
|
#endif /* GINT_USB_USB_PRIVATE */
|