Host Controller Drivers


Host Controller Drivers -- Structure and Interface


This section is mainly of interest to developers who want to write a new host controller driver. It describes the interface used by the USB stack to initiate HCD operations and the API that an HCD can use to interact with the USB stack.

HCD Object

The main interface between the USB stack and each type of HCD is the usb_hcd object:

struct usb_hcd
    const char                  *name;          // Driver name

    // Initialization etc.
    void (*init)( void );                       // Initialize controller(s)
    int  (*attach)( usb_bus *bus );             // Attach to hardware
    int  (*detach)( usb_bus *bus );             // Detach from hardware

    // Endpoint handling
    int (*endpoint_attach)( usb_device *dev, usb_device_endpoint *dep );
    int (*endpoint_detach)( usb_device *dev, void *hcd_endpoint );

    // Transfer handling
    int  (*submit)( usb_device *dev, usb_tfr *tfr ); // Submit transfer (chain)
    int  (*cancel)( usb_device *dev, usb_tfr *tfr ); // Cancel transfer

    // Controller operation
    void (*poll)( usb_bus *bus );               // Poll controller for events

    int  (*frame_number)( usb_bus *bus );       // Get current frame number

    // Root hub support

    int (*port_status)( usb_bus *bus, int port, usb_hub_port_status *status);
    int (*set_port_feature)(usb_bus *bus, int port, usb_uint16 feature );
    int (*clear_port_feature)(usb_bus *bus, int port, usb_uint16 feature );

    // TODO: Bandwidth support


The fields are as follows:


This is a pointer to a string that names this device. It is mainly used for debugging.


This is called once by the USB stack to initialize all HCDs of this type. In combination with platform code this function should enumerate all the HCDs of the supported type and eventually call usb_hcd_register() to make the controller available to the USB stack.

The call to usb_hcd_register() is passed a hcd_bus object that the HCD should allocate in its private data structures. Within this object the hcd field should be set to this HCDs usb_hcd object. The hcd_priv field should be set to point to the HCDs per-controller private data structure; this value will be copied to the hcd_priv field of any device attached to this bus. The hcd_ep0 field should be set to point to an HCD control endpoint for device 0; this will be used to communicate with a newly attached device before its ID has been set. The second argument to usb_hcd_register() is a count of the number of downstream, ports the root hub contains.

While this function should locate the devices and initialize the HCD data structures it should not access the Host Controller hardware at this point.


This is called to attach the HCD to the hardware. This is when the hardware should be initialized, interrupt handlers registered and everything made ready for transfers to occur.


This is called to detach the HCD from the hardware. It should undo the initialization done by the attach function, leaving the device free for other software to take control.

The main reason for this attach/detach mechanism is to allow OTG devices to be shared between host and peripheral drivers.


This is called to create an endpoint in the host controller. The HCD should use the id of the device plus the endpoint descriptor in the usb_device_endpoint object to create an endpoint of the correct type and direction for the device.

The HCD will typically allocate controller and driver data structures to represent this endpoint. If the underlying controller only supports a limited number of endpoints, then the driver should either fail excess endpoint attachments, or arrange to share the physical endpoints between a larger number of virtual endpoints. If the HCD endpoint is created successfully the it should assign a pointer to it to the hcd field in the usb_device_endpoint object.


This is called when the device is detached, or changes its active interface. It undoes the resource allocation made in endpoint_attach. Additionally, this function must cancel any transfers that are pending on the endpoint. Depending on the nature of the controller, these transfer cancellations and the eventual deallocation of the endpoint may happen after this function returns.


This is called to submit a transfer to a device. Internally, this function should extract the HCD private data from the device hcd_priv and the endpoint from the device's usb_device_endpoint object for the transfer's endpoint address. The HCD is free to use the hcd_endpoint and hcd_list fields in the usb_tfr object; the latter should be initialized before use.


This is called to cancel a pending transfer. In general this is only necessary for interrupt or isochronous transfers, control and bulk transfers will always terminate within a finite time. The transfer will not necessarily be available for reuse once this function returns. This is only guaranteed once the transfer's callback is invoked, either with a USB_TFR_CANCELLED status, some other error, or even USB_OK.


This is called from the main USB handling loop to give the HCD the chance to service the hardware. In general all controller operations should be handled in this function rather than the ISR or DSR. The HCD should test the hardware for transfer completion, device attach/detach and errors and handle them here.

If a transfer completes in this polling routine its callback may either be invoked directly by calling usb_tfr_callback_pop() or may be deferred for later processing by calling usb_tfr_complete_async(). The latter is preferable since it avoids any problems of recursion if the callback submits another transfer.

The simplest way to write an HCD is to do all device event handling in the poll() routine. If the controller supports interrupts then the HCD can call usb_signal_poll() to cause the poll routine to be called. If it makes sense to handle device events in the ISR or DSR, callbacks, such as returning transfers, should still happen in the poll routine.


This simply returns the current USB frame number.


This call fills in the status buffer with information on the state of the given port. This routine should query the port in the host controller's root hub registers and translate the results into the standard format expected in the status result, which should be returned in little endian order.


This is called to set a port feature. The feature argument is a standard hub port feature code as defined in the USB standard. Only the subset of features relevant to a root hub are supported.


This is called to clear a port feature. The feature argument is a standard hub port feature code as defined in the USB standard. Only the subset of features relevant to a root hub are supported.

Documentation license for this page: eCosPro License