Management of Drivers

The USB bus system is implemented in two layers in the kernel.

□ A driver must be available for the host adapter. The adapter must provide a connection option for the USB chain and assume responsibility for electrical communication with the terminated devices; the adapter itself must be connected to another system bus (currently, three different host adapter types called OHCI, EHCI, and UHCI are available; they cover all controller types offered on the market).

□ Device drivers communicate with individual USB devices and export their functionality to other parts of the kernel respectively into userspace. These drivers interact with the host controllers via a standardized interface so that the controller type is irrelevant to the USB driver. Any other approach is obviously impractical because it would then be necessary to develop a host controller-dependent driver for each USB device.

Below I shall examine the structure and mode of operation of USB drivers. In doing so, I shall regard the host controller simply as a transparent interface without discussing the details of its implementation.

Although the structure and layout of the USB subsystem are closely based on the USB standard in terms of the contents of data structures and the names of constants, account must be taken of several subtle details during the practical development of USB drivers. To keep the following information as concise as possible, I shall limit our discussion to the core aspects of the USB subsystem. Consequently, I have "relieved" the data elements I examine of their less relevant members. Once the structure of the subsystem has become clear, it is a simple matter to look up the corresponding details in the kernel sources.

The USB subsystem performs four principal tasks.

□ Registering and managing the device drivers present.

□ Finding a suitable driver for a USB device plus initialization and configuration.

□ Representing the device tree in kernel memory.

□ Communicating with the device (exchanging data).

24Assuming, of course, that there are no hardware faults or other effects of force majeure.

The following data structures are associated with the items in this list.

The usb_driver structure is the starting point of the collaboration between the USB device driver and the rest of the kernel (above all the USB layer).

struct usb_driver {

const char *name;

int (*probe) (struct usb_interface *intf, const struct usb_device_id *id); void (*disconnect) (struct usb_interface *intf); int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf);

const struct usb_device_id *id_table;

struct usbdrv_wrap drvwrap;

The name and owner fields fulfill the usual management purposes. The former holds the name of the driver, which must be unique within the kernel (the filename of the module is normally used). The latter creates an association between usb_driver and the module structure if the driver has been added to the kernel as a module. The usual embedded driver object is hidden in another structure this time.

struct usbdrv_wrap {

struct device_driver driver; int for_devices;

The extra data structure allows distinguishing between interface drivers (for_devices is zero in this case) and proper device drivers.

Of special interest are the function pointers probe and disconnect. Together with id_table, they form the backbone of the hotplugging capabilities of the USB subsystem. When the host adapter detects that a new device has been inserted, a probing process is started to find a suitable device driver.

The kernel then traverses all elements of the device tree to ascertain whether any driver is interested. This presupposes, of course, that a driver has not already been assigned to the device. If a driver has been allocated, the device is skipped.

The kernel first scans the list of all devices that are supported by the driver and are included in its id_table list. This approach is familiar because USB devices (like PCI devices) can be uniquely identified by a number. Once a match has been found between device and table, the driver-specific probe function is invoked to perform further checks and initialization work.

If no match is found between the device ID and the list of drivers, the kernel skips to the next driver and need not invoke the function stored in probe.

The ID table is made up of several instances of the following structure, which describes a USB device by means of several identifiers:


struct usb_device_id {

/* which fields to match against? */ _u16 match_flags;

/* Used for product specific matches; range is inclusive */

_u16 idVendor;

_u16 idProduct;

_u16 bcdDevice_lo;

_u16 bcdDevice_hi;

/* Used for device class matches */

_u8 bDeviceClass;

_u8 bDeviceSubClass;

_u8 bDeviceProtocol;

/* Used for interface class matches */

_u8 bInterfaceClass;

_u8 bInterfaceSubClass;

_u8 bInterfaceProtocol;

match_flags is used to specify which fields of the structure are to be compared with the device data; various pre-processor constants are defined for this purpose. For example, usb_device_id_match_vendor indicates that the idVendor field is to be checked, and usb_device_id_match_dev_protocol instructs the kernel to check the protocol field. The meaning of the other fields of usb_device_id is self-explanatory.

The association between driver and device is established not only when a new device is added to the system but also when a new driver is loaded. The same approach is adopted as described above. The starting point is the usb_register routine, which must be invoked to register a new USB driver.

The probe and remove functions work with interfaces that are described by a separate data structure (usb_interface). Besides interface characteristics, these include pointers to the associated device, the driver, and the USB class to which the interface belongs. It is not therefore necessary to go into the details of the data structure definition.

Continue reading here: Representation of the Device Tree

Was this article helpful?

0 0