Name

Transfer Object — Structure and Interface

Synopsis

#include <cyg/io/usb.h>
    

usb_tfr *usb_device_tfr_alloc(usb_device *dev);

usb_tfr *usb_target_tfr_alloc(usb_target *tgt);

void usb_tfr_ref(usb_tfr *tfr);

void usb_tfr_unref(usb_tfr *tfr);

void usb_tfr_init(usb_tfr *tfr, usb_uint8 endpoint, usb_uint8 attr, void *buffer, usb_uint32 size, usb_tfr_callback *callback, void *callback_data);

#define usb_request_init(usb_request req, usb_uint8 request_type, usb_uint8 request, usb_uint16 value, usb_uint16 index, usb_uint16 length);

void usb_tfr_control(usb_tfr *tfr, usb_uint8 endpoint, usb_request *req, void *buffer, usb_uint32 size, usb_tfr_callback *callback, void *callback_data);

usb_tfr *usb_control_tfr(usb_device *dev, usb_uint8 request_type, usb_uint8 request, usb_uint16 value, usb_uint16 index, usb_tfr_callback *callback, void *callback_data);

void usb_tfr_bulk(usb_tfr *tfr, usb_uint8 endpoint, usb_request *req, void *buffer, usb_uint32 size, usb_tfr_callback *callback, void *callback_data);

void usb_tfr_submit(usb_tfr *tfr);

void usb_tfr_cancel(usb_tfr *tfr);

Description

Most interaction between clients of the USB stack and the stack itself happens through the use of transfer objects (or just transfers). These data structures are allocated and initialized by the client and passed to the USB stack. When the transfer completes, the client is notified via a callback, following which it may release or reuse the object for another transfer.

Transfer Class Object

A transfer has the following structure:

struct usb_tfr
{
    usb_node                    node;           // List/queue node

    union
    {
        usb_device              *dev;           // Host device
        usb_target              *tgt;           // Target device
    };

    usb_uint16                  refcount;       // Reference count
    usb_uint16                  endpoint;       // Endpoint address
    usb_uint8                   attr;           // Transfer type
    usb_uint8                   flags;          // Additional flags
    int                         status;         // Current/returned status

    void                        *tfr_buffer;    // Data buffer
    usb_uint16                  tfr_size;       // Size of transfer/buffer
    usb_uint16                  tfr_actual;     // Actual size transferred

    usb_tfr_callback            *callback[2];   // Completion callback stack
    void                        *callback_data; // Callback data

    // The following fields are used by the HCD or PCD
    void                        *hcd_endpoint;  // HCD endpoint private data
    usb_list                    hcd_list;       // HCD transfer list

    // Per-transfer-type fields.
    union
    {
        struct
        {
            usb_uint8           setup[8];       // Setup packet
        } control;
        struct
        {
        } bulk;
#ifdef USB_CONFIG_INTERRUPT
        struct
        {
        } interrupt;
#endif
#ifdef USB_CONFIG_ISOCHRONOUS
        struct
        {
            int                 start_frame;    // Start frame
            int                 interval;       // transfer interval
            int                 pkt_count;      // Number of packets
            usb_iso_packet_desc *iso_packet;    // packet descriptors
        } isochronous;
#endif
    };
};

The fields of the transfer are as follows:

node
A list node, which is used to link this transfer into internal lists in the USB stack. It may also be used by the client when the object is in its possession, but should be unlinked whenever passed to usb_tfr_submit().
dev
A pointer to the host device object on which this transfer will operate. This is filled in when the transfer is allocated by usb_device_tfr_alloc(). This value should not be changed directly by the client since other fields in this object may depend on this field. The transfer also holds a reference to the device which may not be decremented properly if this field is changed. If the client needs to communicate with a different device, it should allocate a new transfer object.
dev
A pointer to the target object on which this transfer will operate. This is filled in when the transfer is allocated by usb_target_tfr_alloc(). This value should not be changed directly by the client since other fields in this object may depend on this field. The transfer also holds a reference to the target which may not be decremented properly if this field is changed.
refcount
Transfer reference count. This controls the existence of this transfer. It can be incremented with a call to usb_tfr_ref() and decremented with a call usb_tfr_unref(). This field is set to 1 when the transfer is allocated, and the reference count on the associated device or target is also incremented. If the refcount is decremented to zero then the device or target reference is decremented, and the transfer returned to the free pool.
endpoint
This is the endpoint number and direction. This field has the same format as the bEndpointAddress field of an endpoint descriptor and is initialized from the descriptor.
attr
This is the endpoint attributes; it mainly defines the type of transfer: control, bulk, interrupt or isochronous. It may contain other information for some transfer types. This field is initialized from the bmAttributes field of an endpoint descriptor and has the same format.
flags

This field contains a number of flag bits that control the nature of the transfer. If USB_TFR_FLAGS_TARGET is set then this is a target mode transfer and the tgt field is is valid, otherwise it is a host mode transfer and the dev field is valid. The flags USB_TFR_FLAGS_START and USB_TFR_FLAGS_END allow transfers to be chained together to provide a scatter/gather facility; this is currently not implemented. The USB_TFR_FLAGS_CALLBACK flag indicates that this transfer's callback should be called when it is complete. The USB_TFR_FLAGS_SHORT_OK flag indicates that a short transfer should be treated as a success and not a failure; this is currently not implemented for host transfers, but is for target OUT transfers.

By default, a transfer is initialized with the START, END and CALLBACK flags set. Additionally a target mode transfer is initialized with the TARGET flag set.

status
This field contains the transfer status. While it is in the possession of the USB stack, this field may be used to record internal state transitions. When it is returned to the client via a callback, it will contain either USB_OK to indicate the transfer was successful, an error code, or a status code (e.g. USB_TFR_CANCELLED).
tfr_buffer
A pointer to a buffer containing the data to be transmitted, or where the received data should be places. There are no explicit alignment requirements on this buffer, but on some platforms this buffer may need to be synchronized to external memory or flushed from the data cache, so if it is not cache line aligned these operations may have a side-effect on other data.
tfr_size
The size of the data buffer, in bytes, and hence the size of the transfer. The transfer size is not limited by the maximum packet size of the addressed endpoint, the driver may split the transfer into a sequence of packets if necessary.
tfr_actual
When the transfer is complete, this will contain the number of bytes actually transferred. This should only differ from tfr_size if the SHORT_OK flag is set.
callback
When a transfer has completed, this is signalled to the client by calling a callback routine. Callbacks are managed as a stack, with the client callback as the lowest, last, callback. This mechanism allow the USB stack to interpose its own finalization processing for a transfer if required. This callback stacking is opaque to the client.
callback_data
This is a client-supplied data value that the client can supply to ensure continuity between the submitter and the callback; it is usually a pointer to some controlling data structure. The client's callback can retrieve this value from the transfer by accessing this field. While callbacks are stacked, the callback_data is not, internal callbacks only make use of the standard transfer fields.
hcd_endpoint
This field is for use by the HCD or PCD, and typically contains a pointer to the data structure in the driver that controls the endpoint to which this transfer is directed.
hcd_list
This field is for use by the HCD or PCD, and typically is the root of a chain of internal data structures that define the data transfer.
control
This sub-structure is part of an anonymous union that defines per-transfer-type fields consisting of this field and the following bulk, interrupt and isochronous fields. This structure contains the contents of the setup packet. In host mode normally the setup packet is not assigned directly, but is copied here by usb_tfr_control() after being initialized elsewhere with usb_request_init().
bulk
This field contains bulk transfer control fields. It is currently empty.
interrupt
This field contains interrupt transfer control fields. It is currently empty and is only defined if interrupt transfer support is configured. It's contents may change significantly when interrupt transfer support is implemented.
isochronous
This field contains isochronous transfer control fields. It is currently empty and is only defined if isochronous transfer support is configured. At present isochronous transfers are not supported. It's contents may change significantly when isochronous transfer support is implemented.

API

There are a number of API functions associated with the management of transfer objects.

Host mode clients should allocate a transfer by calling usb_device_tfr_alloc(). The result will be a pointer to a transfer which has been zeroed except that the node field will be initialized, the dev field will be set to the supplied device pointer and the refcount will be set to 1. Additionally, usb_device_ref() will have been called on the device. This function will return a NULL pointer if there are no transfers available for allocation.

Target mode clients should allocate a transfer by calling usb_target_tfr_alloc(). The result will be a pointer to a transfer which has been zeroed except that the node field will be initialized, the tgt field will be set to the supplied target pointer, the refcount will be set to 1 and usb_target_ref() will have been called on the target. The flags field will be initialized with the USB_TFR_FLAGS_TARGET flag. This function will return a NULL pointer if there are no transfers available for allocation.

If a client needs to take further reference to the transfer it can call usb_tfr_ref(). References are released by calling usb_tfr_unref(). When a client is finished with a transfer it should call usb_tfr_unref() a last time to release the initial reference. This may have the side-effect of calling usb_device_unref() or usb_target_unref() which may result in the device object being freed.

In general, a transfer is initialized by one of several routines to create a specific type of transfer. The most general initialization routine is usb_tfr_init() which sets up the transfer with the endpoint address and attributes, a data buffer, and a callback. This is the only initialization function that should be applied to target mode transfers, the remaining initialization functions only apply to host mode transfers.

Control transfer initialization is supported by a number of functions. The function usb_tfr_control() initializes a general control transfer. The endpoint argument is used to look up the endpoint in the device and initialize the endpoint and attr fields in the transfer. The req argument points to a completed USB request that will be copied into the setup buffer. The data buffer and callback are initialized too. The USB request can be initialized using the usb_request_init() macro. The first argument to this is the name of the request to initialize, not a pointer. The remaining arguments give values for the various fields; the 16 bit fields will potentially be byte swapped into little endian order.

The function usb_control_tfr() provided a higher level interface to create control transfers that do not transfer additional data. This is given the destination device plus values for the request type, request code, value and index field (but not the length), and the callback. Using these parameters, a transfer is allocated, a request buffer created and the transfer initialized with an endpoint address of zero. If a transfer cannot be allocated, a NULL pointer is returned.

A bulk transfer can be initialized using usb_tfr_bulk(). The endpoint argument is used to look up the endpoint in the device and initialize the endpoint and attr fields in the transfer. The buffer and callback are also initialized.

Interrupt and Isochronous transfers are not currently supported, but when they are similar functions to initialize those will be available.

A transfer is submitted to the USB stack by calling usb_submit(). This function performs some simple checks before passing the transfer on to the appropriate driver. If this function is passed a NULL transfer, it will return USB_ERR_TFR_ALLOC. The transfer initialization routines also return if a NULL transfer is passed to them. This allows detection and handling of transfer allocation failures in most cases to be deferred until this call, keeping error handling simpler.

A transfer can be cancelled by calling usb_cancel(). This may simply mark the transfer for cancellation, the transfer may not actually be cancelled until some time after this function returns. When the transfer is actually cancelled, its callback will be called with a status of USB_TFR_CANCELLED. However, if the transfer was already finished, or caused an error, the callback status may be USB_OK or an error code. Thus client code cannot rely on the transfer completing with a CANCELLED status; this call just ensures that the transfer will be returned to the client in some way. Note that it is not very useful to cancel host mode control or bulk transfers since they will usually be processed as soon as submitted and will be returned quickly; the client is unlikely to catch them in time.