Name

Class Drivers — Structure and Interface

Synopsis

#include <cyg/io/usb.h>
    

int usb_class_driver_register(usb_class_driver *class_driver);

int usb_class_driver_deregister(usb_class_driver *class_driver);

usb_descriptor *usb_descriptor_find(usb_descriptor *desc, usb_uint8 type);

usb_descriptor *usb_device_class_find(usb_device *dev, int class, int subclass, int protocol, usb_descriptor *configuration, usb_descriptor *interface);

int usb_device_configure(usb_device *dev, usb_descriptor *config, usb_tfr_callback *callback, void *callback_data);

Description

A class driver translates between operations on a standard eCos device interface and operations on a USB device. For example the USB mass storage class driver translates between disk driver operations and USB mass storage operations.

USB Class Object

A class driver interfaces initially to the USB stack through a usb_class_driver object:

struct usb_class_driver
{
    usb_node            node;           // Link in class driver list
    int                 priority;       // Priority in list

    int                 (*attach)( usb_class_driver *class_driver,
                                   usb_device *device );

    int                 (*detach)( usb_class_driver *class_driver,
                                   usb_device *device );

    void                (*poll)( usb_class_driver *class_driver,
                                 usb_uint32 interval );
};

The fields in this structure are as follows:

node
A list node, which is used to link this object into a prioritized list of class drivers. This is initialized by usb_class_driver_register() so does not need to be initialized by the class driver.
priority
The priority of this class driver. This should be a positive integer. When looking for a class driver to handle a newly attached device, the list is scanned in increasing priority order. So lower values are handled first.
attach

Whenever a new device is attached to a hub, the USB stack assigns it an address, fetches any descriptors, and then tried to find a class driver for it. It does this by calling the attach() functions of all registered class drivers until one indicates that it is willing to handle this device. The attach() function indicates acceptance by returning USB_OK, it indicates non-acceptance by returning an error code, preferably USB_ERR_NO_SUPPORT.

If the attach() function returns USB_OK then it should also set the device's device.class_driver field to point to the usb_class_driver object. It should call usb_device_ref() on the USB device to ensure that it remains valid. It must also call usb_device_select_interface.

detach
This function is called when the USB device detaches. In addition to cleaning up its own data structures, the main thing this function should do it arrange for the reference to the USB device taken in the attach function to be released by calling usb_device_unref(), directly or otherwise.
poll
This function is called periodically from the USB subsystem. The interval parameter indicates the number of milliseconds since the last call to this function. It can be used by the class driver to operate timeouts and retries. The exact interval between calls will depend on the level of activity of the USB stack and the resolution of the main system timer. Class drivers should therefore not depend on this for accurate timing operation and may need to make their own arrangements for such things.

USB Class API

The USB stack exports a number of functions that are intended for use by class drivers.

The function usb_class_driver_register() is used by a class driver to register itself with the USB stack. Typically a class driver is initialized as an instance of the driver type that it is intending to serve (e.g. disk, network, serial etc.) and during the initialization function for that driver will call usb_class_driver_register(). If the driver ever needs to detach itself from the USB stack then it can call usb_class_driver_deregister().

Within the attach() function, the class driver can call some USB stack functions to help it decide whether a device is one that it can support. The most important of these is usb_device_class_find() which scans the descriptors attached to a a device for an interface that implements the given class, subclass and protocol types. A value of -1 for subclass and protocol acts as a wildcard. If successful it returns pointers to the configuration and interface found. The function usb_descriptor_find() can be used on dev->desc_chain, or any other descriptor pointer, to find the next descriptor of a given type. The class driver can also just inspect the device and parse the descriptor chain itself if necessary.

Before returning, the attach() function should call usb_device_configure(). Which will configure the device to use the configuration descriptor is supplied. The class driver must also supply a callback which will be called when the configuration has been done, or has failed. Further device setup can then be done in the callback.

Putting all that together, the functions of a class driver should have the following approximate form:

//-----------------------------------------------------------------------------
// Attach device call

static int mydev_attach( usb_class_driver *class_driver, usb_device *dev )
{
    int result = USB_OK;
    mydev_data *mydev;

    // Look for configuration and interface. Here we assume that the
    // first configuration is the one we want to use and that we are
    // not worried about the function type, hence the wildcard.
    usb_descriptor *cdesc, *idesc;

    usb_device_class_find( dev, USB_CLASS_MYCLASS, USB_SUBCLASS_MYSUBCLASS, -1, &cdesc, &idesc );

    if( cdesc == NULL || idesc == NULL )
        return USB_ERR_NO_SUPPORT;

    // Set up device data structures here...
    mydev = mydev_alloc();
    mydev->dev = dev;

    // Extract any useful information from the interface descriptor
    // chain, such as endpoint addresses...

    // Reference the device
    usb_device_ref( dev );

    // Send off a command to select the configuration
    // we have found.
    result = usb_device_configure( dev, cdesc, mydev_attach_tfr_done, mydev );

    // If it all worked, set the device's class driver to point to us.
    if( result == USB_OK )
        dev->device.class_driver = class_driver;

    return result;
}

//-----------------------------------------------------------------------------
// Configuration callback

static int mydev_attach_tfr_done( usb_tfr *tfr )
{
    int result = tfr->status;
    mydev_data *mydev = tfr->callback_data;
    usb_device *dev = tfr->dev;

    // Release tfr object
    usb_tfr_unref( tfr );

    if( result != USB_OK )
    {
        // Handle configuration error by detaching from USB device and
        // freeing local resources.
        mydev_free( mydev );
        usb_device_unref( dev );
        return result;
    }

    // Continue device initialization...

    return result;
}

//-----------------------------------------------------------------------------
// Device detach call

static int mydev_detach( usb_class_driver *class_driver, usb_device *dev )
{
    int result = USB_OK;

    // Find my device data from device pointer.
    mydev_data *mydev = mydev_find( dev );

    // Shut down device...

    // Free device data structure
    mydev_free( mydev );

    // Release reference to device
    usb_device_unref( dev );

    return result;
}

//-----------------------------------------------------------------------------
// Device poll call

static void mydev_poll( usb_class_driver *class_driver, usb_uint32 interval )
{
    // Handle timeouts and delays in active devices...
}