#include <cyg/io/usb/usbs.h> typedef struct usbs_rx_endpoint { void (*start_rx_fn)(struct usbs_rx_endpoint*); void (*set_halted_fn)(struct usbs_rx_endpoint*, cyg_bool); void (*complete_fn)(void*, int); void* complete_data; unsigned char* buffer; int buffer_size; cyg_bool halted; } usbs_rx_endpoint; typedef struct usbs_tx_endpoint { void (*start_tx_fn)(struct usbs_tx_endpoint*); void (*set_halted_fn)(struct usbs_tx_endpoint*, cyg_bool); void (*complete_fn)(void*, int); void* complete_data; const unsigned char* buffer; int buffer_size; cyg_bool halted; } usbs_tx_endpoint; |
In addition to a single usbs_control_endpoint
data structure per USB slave device, the USB device driver should also
provide receive and transmit data structures corresponding to the
other endpoints. The names of these are determined by the device
driver. For example, the SA1110 USB device driver package provides
usbs_sa11x0_ep1 for receives and
usbs_sa11x0_ep2 for transmits.
Unlike control endpoints, the common USB slave package does provide a
number of utility routines to manipulate data endpoints. For example
usbs_start_rx_buffer
can be used to receive data from the host into a buffer. In addition
the USB device driver can provide devtab entries such as
/dev/usbs1r and /dev/usbs2w, so
higher-level code can open
these devices and then
perform blocking read
and
write
operations.
However, the operation of data endpoints and the various
endpoint-related functions is relatively straightforward. First
consider a usbs_rx_endpoint
structure. The
device driver will provide the members
start_rx_fn
and
set_halted_fn
, and it will maintain the
halted
field. To receive data, higher-level
code sets the buffer
,
buffer_size
,
complete_fn
and optionally the
complete_data
fields. Next the
start_rx_fn
member should be called. When
the transfer has finished the device driver will invoke the completion
function, using complete_data
as the first
argument and a size field for the second argument. A negative size
indicates an error of some sort: -EGAIN indicates
that the endpoint has been halted, usually at the request of the host;
-EPIPE indicates that the connection between the
host and the peripheral has been broken. Certain device drivers may
generate other error codes.
If higher-level code needs to halt or unhalt an endpoint then it can
invoke the set_halted_fn
member. When an
endpoint is halted, invoking start_rx_fn
wit buffer_size
set to 0 indicates that
higher-level code wants to block until the endpoint is no longer
halted; at that point the completion function will be invoked.
USB device drivers are allowed to assume that higher-level protocols ensure that host and peripheral agree on the amount of data that will be transferred, or at least on an upper bound. Therefore there is no need for the device driver to maintain its own buffers, and copy operations are avoided. If the host sends more data than expected then the resulting behaviour is undefined.
Transmit endpoints work in essentially the same way as receive
endpoints. Higher-level code should set the
buffer
and
buffer_size
fields to point at the data to
be transferred, then call start_tx_fn
, and
the device driver will invoked the completion function when the
transfer has completed.
USB device drivers are not expected to perform any locking. If at any time there are two concurrent receive operations for a given endpoint, or two concurrent transmit operations, then the resulting behaviour is undefined. It is the responsibility of higher-level code to perform any synchronisation that may be necessary. In practice, conflicts are unlikely because typically a given endpoint will only be accessed sequentially by just one part of the overall system.