Manual

Control Endpoints
Non-standard control messages always have to be processed by higher-level code. This could be class-specific
packages. For example, the USB-ethernet package will handle requests for getting the MAC address and for en-
abling or disabling promiscuous mode. In all cases the device driver will store the initial request in the con-
trol_buffer field, check for an appropriate handler, and invoke it with details of the control endpoint and
any handler-specific data that has been installed alongside the handler itself. The handler should return either
USBS_CONTROL_RETURN_HANDLED to report success or USBS_CONTROL_RETURN_STALL to report failure. The de-
vice driver will report this to the host.
If there are multiple parties interested in a particular type of control messages, it is the responsibility of application
code to install an appropriate handler and process the requests appropriately.
Buffer Management
typedef struct usbs_control_endpoint {
...
unsigned char* buffer;
int buffer_size;
void (*fill_buffer_fn)(struct usbs_control_endpoint*);
void* fill_data;
int fill_index;
usbs_control_return (*complete_fn)(struct usbs_control_endpoint*, int);
...
} usbs_control_endpoint;
Many USB control messages involve transferring more data than just the initial eight-byte header. The header
indicates the direction of the transfer, OUT for host to peripheral or IN for peripheral to host. It also specifies a
length field, which is exact for an OUT transfer or an upper bound for an IN transfer. Control message handlers
can manipulate six fields within the control endpoint data structure to ensure that the transfer happens correctly.
For an OUT transfer, the handler should examine the length field in the header and provide a single buffer for all the
data. A class-specific protocol would typically impose an upper bound on the amount of data, allowing the buffer
to be allocated statically. The handler should update the buffer and complete_fn fields. When all the data has
been transferred the completion callback will be invoked, and its return value determines the response sent back to
the host. The USB standard allows for a new control message to be sent before the current transfer has completed,
effectively cancelling the current operation. When this happens the completion function will also be invoked. The
second argument to the completion function specifies what has happened, with a value of 0 indicating success and
an error code such as -EPIPE or -EIO indicating that the current transfer has been cancelled.
IN transfers are a little bit more complicated. The required information, for example the enumeration data, may not
be in a single contiguous buffer. Instead a mechanism is provided by which the buffer can be refilled, thus allowing
the transfer to move from one record to the next. Essentially, the transfer operates as follows:
1. When the host requests another chunk of data (typically eight bytes), the USB device driver will exam-
ine the buffer_size field. If non-zero then buffer contains at least one more byte of data, and then
buffer_size is decremented.
2. When buffer_size has dropped to 0, the fill_buffer_fn field will be examined. If non-null it will
be invoked to refill the buffer.
3. The fill_data and fill_index fields are not used by the device driver. Instead these fields are available
to the refill function to keep track of the current state of the transfer.
603