The basics of USB drivers

From Crashcourse Wiki

Jump to: navigation, search

Contents

Overview

Random thoughts on USB devices and drivers, very much a work in progress.

USB device IDs

An overview of USB device IDs, how they're assigned, how they're matched and so on.

struct usb_device_id

From the header file include/linux/mod_devicetable.h, we have the general structure of a USB device ID:

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;

        /* not matched against */
        kernel_ulong_t  driver_info;
};

However, it's rare that, when creating an ID, you'd fill in all of those fields since that would almost certainly be overkill -- you'd assign only the fields necessary to identify the device and set the match_flags field to the appropriate bitmask corresponding to the immediately following macros:

/* Some useful macros to use to create struct usb_device_id */
#define USB_DEVICE_ID_MATCH_VENDOR              0x0001
#define USB_DEVICE_ID_MATCH_PRODUCT             0x0002
#define USB_DEVICE_ID_MATCH_DEV_LO              0x0004
#define USB_DEVICE_ID_MATCH_DEV_HI              0x0008
#define USB_DEVICE_ID_MATCH_DEV_CLASS           0x0010
#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS        0x0020
#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL        0x0040
#define USB_DEVICE_ID_MATCH_INT_CLASS           0x0080
#define USB_DEVICE_ID_MATCH_INT_SUBCLASS        0x0100
#define USB_DEVICE_ID_MATCH_INT_PROTOCOL        0x0200

As a trivial and common example, a device might be addressed solely by its idVendor and idProduct fields, so you'd assign only those two member fields and set those two bits in the match_flags field to specify that those are the only two valid fields against which to compare when trying to "match" this device.

Creating USB device IDs

Rather than manually and tediously creating USB device IDs as you need them, the kernel header file include/linux/usb.h defines a number of macros to simplify creating the most common combinations. Since there aren't that many, here's the entire list:

#define USB_DEVICE(vend, prod) \
        .match_flags = USB_DEVICE_ID_MATCH_DEVICE, \
        .idVendor = (vend), \
        .idProduct = (prod)

#define USB_DEVICE_VER(vend, prod, lo, hi) \
        .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, \
        .idVendor = (vend), \
        .idProduct = (prod), \
        .bcdDevice_lo = (lo), \
        .bcdDevice_hi = (hi)

#define USB_DEVICE_INTERFACE_PROTOCOL(vend, prod, pr) \
        .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
                       USB_DEVICE_ID_MATCH_INT_PROTOCOL, \
        .idVendor = (vend), \
        .idProduct = (prod), \
        .bInterfaceProtocol = (pr)

#define USB_DEVICE_INFO(cl, sc, pr) \
        .match_flags = USB_DEVICE_ID_MATCH_DEV_INFO, \
        .bDeviceClass = (cl), \
        .bDeviceSubClass = (sc), \
        .bDeviceProtocol = (pr)

#define USB_INTERFACE_INFO(cl, sc, pr) \
        .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \
        .bInterfaceClass = (cl), \
        .bInterfaceSubClass = (sc), \
        .bInterfaceProtocol = (pr)

#define USB_DEVICE_AND_INTERFACE_INFO(vend, prod, cl, sc, pr) \
        .match_flags = USB_DEVICE_ID_MATCH_INT_INFO \
                | USB_DEVICE_ID_MATCH_DEVICE, \
        .idVendor = (vend), \
        .idProduct = (prod), \
        .bInterfaceClass = (cl), \
        .bInterfaceSubClass = (sc), \
        .bInterfaceProtocol = (pr)

Even the more common match flag combinations are defined as macros in that same header file to save time:

#define USB_DEVICE_ID_MATCH_DEVICE \
                (USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT)
#define USB_DEVICE_ID_MATCH_DEV_RANGE \
                (USB_DEVICE_ID_MATCH_DEV_LO | USB_DEVICE_ID_MATCH_DEV_HI)
#define USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION \
                (USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_RANGE)
#define USB_DEVICE_ID_MATCH_DEV_INFO \
                (USB_DEVICE_ID_MATCH_DEV_CLASS | \
                USB_DEVICE_ID_MATCH_DEV_SUBCLASS | \
                USB_DEVICE_ID_MATCH_DEV_PROTOCOL)
#define USB_DEVICE_ID_MATCH_INT_INFO \
                (USB_DEVICE_ID_MATCH_INT_CLASS | \
                USB_DEVICE_ID_MATCH_INT_SUBCLASS | \
                USB_DEVICE_ID_MATCH_INT_PROTOCOL)

Some simple(?) examples

Relative to the drivers/usb directory, here's the appropriate snippet from serial/zio.c, the ZIO Motherboard USB driver:

static const struct usb_device_id id_table[] = {
        { USB_DEVICE(0x1CBE, 0x0103) },
        { },
};
MODULE_DEVICE_TABLE(usb, id_table);

which defines the USB device ID for that device as having precisely that vendor and product code, then adds that information to the exported USB module device table.

Here's a slightly more complicated example -- serial/pl2303.c -- which represents a sizable number of products from different vendors all of which will use the same driver for a USB-serial adapter:

static const struct usb_device_id id_table[] = {
        { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) },
        { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
        { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_DCU11) },
        ... big snip ...
        { }
};

MODULE_DEVICE_TABLE(usb, id_table);

And for something slightly different, there's class/usblp.c, which associates itself with a number of variations of USB device IDs:

static const struct usb_device_id usblp_ids[] = {
        { USB_DEVICE_INFO(7, 1, 1) },
        { USB_DEVICE_INFO(7, 1, 2) },
        { USB_DEVICE_INFO(7, 1, 3) },
        { USB_INTERFACE_INFO(7, 1, 1) },
        { USB_INTERFACE_INFO(7, 1, 2) },
        { USB_INTERFACE_INFO(7, 1, 3) },
        { USB_DEVICE(0x04b8, 0x0202) }, /* Seiko Epson Receipt Printer M129C */
        { }                                             /* Terminating entry */
};

MODULE_DEVICE_TABLE (usb, usblp_ids);

You get the idea.

usb_match_id()

Personal tools