USB matching rules

The USB matching rules in the configuration files tell the USB launcher service how to process specific types of devices when they're attached through a USB port.

The matching rules specify device properties (for instance, vendor ID, device ID, USB class) that the service compares against the device descriptors provided by the USB stack. When the service finds a rule with properties matching those of a newly attached device, it executes the commands listed in the rule.

Matching rule levels

Matching rules are expressed in a hierarchy that defines different levels at which to match device descriptors. Each level of depth is more specific than the last, meaning that its rules match a more restricted set of devices. Here are the five levels of device specification rules:

  1. device
  2. configuration
  3. interface
  4. class
  5. ms_desc

When an outer (more general) rule matches a device, the USB launcher service compares the inner (more specific) rules in a depth-first search order. You can omit some or all of the inner rules if you don't need to match any descriptors at that level. For rules at the same level, USB launcher applies the rule listed earliest. You can verify the search order by calling show_config() at the end of your configuration file.

Matching rule arguments

The matching rules take variable numbers of arguments so that they may be as specific as necessary. The following table outlines the descriptors matched at each level; the Rule syntax column lists the syntax variants for each level, starting from the most specific.
Level Descriptors matched Rule syntax
device Vendor ID, device ID, earliest revision, latest revision
device(vid, did, rev_low, rev_high)
device(vid, did, rev_low)
device(vid, did)
device()
product

(same as device rule except you can specify a range of device IDs)

Vendor ID, lowest device ID, highest device ID
product(vid, did_low, did_high)
product(vid, did_low)
product(vid)
product()
configuration Configuration number
configuration(num)
configuration()
interface Interface number or range indicated by lowest and highest numbers
interface(num_low, num_high)
interface(num)
interface()
class Interface class, subclass, and protocol
class(class, subclass, protocol)
class(class, subclass)
class(class)
class()
ms_desc Microsoft descriptor
ms_desc(compatibleID, subcompatibleID,
    vendorID)
ms_desc(compatibleID, subcompatibleID)
ms_desc(compatibleID)
ms_desc()

The device specification below shows three levels of matching rules. The driver command line is enclosed in double quotes and prefixed with the driver keyword. The names of macro variables in the driver command line are the same as the attribute names in PPS device objects.

device(vid, did, rev_low, rev_high) {
    configuration(1) {
        class(class, subclass, protocol) {
            driver"io-fs-media -dxxx,\z
                    device=$(busno):$(devno):$(interface)";
        };
    };
};

Variables

You can define variables to store values and use those values in matching rules listed later in the configuration file. Here are some sample definitions:

-- definitions of common USB properties 
USB_CLASS_AUDIO        = 0x01
    USB_AUDIO_SUBCLASS_CONTROL        = 0x01
    USB_AUDIO_SUBCLASS_STREAMING      = 0x02
        USB_AUDIO_PROTOCOL_1_0 = 0x00 
        USB_AUDIO_PROTOCOL_2_0 = 0x20 
USB_CLASS_PHYSICAL     = 0x05
USB_CLASS_IMAGING = 0x06
    USB_IMAGING_SUBCLASS_STILL = 0x01
        USB_IMAGING_STILL_PROTOCOL_PTP = 0x01
USB_CLASS_PRINTER      = 0x07
USB_CLASS_MASS_STORAGE = 0x08

The matching rule shown below demonstrates the use of variables. This specification matches any device that has a mass-storage class:

-- generic mass storage rule
device() {
    class(USB_CLASS_MASS_STORAGE) {
        DISK_OPTS  = "blk cache=1m,vnode=384,\z
                        auto=none,delwri=2:2,rmvto=none,noatime \z
                        disk name=umass cdrom name=umasscd";
        UMASS_OPTS = "umass priority=21";

        driver"devb-umass $(DISK_OPTS) $(UMASS_OPTS),\z
                vid=$(vendor_id),did=$(product_id),\z
                busno=$(busno),devno=$(devno),\z
                interface=$(interface),ign_remove";
    };
};

Notice that variables can be defined in the scope of a rule and used in the driver command. When searching for variable definitions, the USB launcher service searches from the innermost to the outermost scope.

Flags

You can place flags in a rule to change the range of devices matched and to change the actions performed after matching a device.

Ignore flag

At the device level, the Ignore flag instructs the USB launcher service to ignore the device by not attaching to it and reading its descriptors. In this case, no PPS objects are published and no driver is launched.

Consider the following example:

device(0x0e0f, 0x0003) {
    Ignore;  -- don't even attach to this device
}

Because it performs the Ignore check before attaching to the device, the USB launcher service doesn't have any device descriptors to match against. As a result, it can look at only the vendor ID and device ID supplied by the stack when the device was physically attached to the target system. Therefore, the device rule can't be made more specific about ignoring devices with certain revisions.

For rules at inner levels, the USB launcher service performs the Ignore check after retrieving device descriptors, allowing you to make these rules more specific. At any inner level, the Ignore flag stops the service from launching a driver and from matching the device with any subsequent rules.

product(0x05AC) {
    class(USB_CLASS_AUDIO, USB_AUDIO_SUBCLASS_CONTROL) {
        driver"io-audio -dipod busno=$(busno),devno=$(devno),\z
                cap_name=ipod-$(busno)-$(devno)";
    };
    class(USB_CLASS_HID) {
        driver"io-fs-media -dipod,$(IPOD_OPTS)";
    };
    class(USB_CLASS_MASS_STORAGE) {
        Ignore;
    };
};

Default flag

You can include the Default flag in a configuration rule to limit driver matching to the device's default configuration, as shown here:

configuration(1) {
    Default;
    driver"io-fs-media -dipod,$(IPOD_OPTS)";
}

Never flag

The Never flag means no device is matched, which lets you temporarily disable a rule. The device can match another rule specified later in the configuration file and have an alternative driver launched.

Always flag

The Always flag means any device is matched (if it hasn't been matched with another rule already). For example, although the class rule in the following device specification indicates an interface class of 8, the rule actually matches devices of any class:

device() {
    class(8) {
        Always;
        start"echo this is busno=$(busno) devno=$(devno) inserted";
     };
};

Multiple interface classes

You can specify multiple interface classes for one device or product rule, as in the iPod device specification in the following example:

-- iPod
product(0x05AC, 0x1200, 0x12FF) {
    class(USB_CLASS_AUDIO, USB_AUDIO_SUBCLASS_CONTROL) {
        driver"io-audio -dipod busno=$(busno),devno=$(devno),\z
               cap_name=ipod-$(busno)-$(devno)";
    };
    class(USB_CLASS_HID) {
        driver"io-fs-media -dipod,transport=usb:busno=$(busno):\z
               devno=$(devno):\z
               audio=/dev/snd/ipod-$(busno)-$(devno),\z
               darates=+8000:11025:12000:16000:22050:24000,\z
               playback,acp=i2c:addr=0x11:path=/dev/i2c99,\z
               fnames=short,config=/etc/mm/ipod.cfg";
    };
    class(USB_CLASS_MASS_STORAGE) {
        Never;
    };
};

foreach rule

With the foreach rule, you can launch a particular driver for any device in a given list of devices. This rule saves you from having to specify many device (or product) rules that differ only by their device or vendor IDs, as shown in this example:

char_devices = {
    device(0x0557, 0x2008);  -- ATEN_232A/GUC_232A
    device(0x2478, 0x2008);  -- TRIPP LITE U2009-000-R
    device(0x9710, 0x7720);  -- MOSCHIP 7720
    device(0x0403, 0x6001);  -- FTDI 8U232AM
    device(0x1199, 0x0120);  -- Sierra AirCard U595
    device(0x0681, 0x0040);  -- Siemens HC25
    device(0x1bc7, 0x1003);  -- Telit UC864E
    device(0x067b, 0x2303);  -- Prolific
}

foreach (char_devices) {
    driver" devc-serusb -d vid=$(vendor_id),did=$(product_id),\z
            busno=$(busno),devno=$(devno)";
}

The foreach rule also works with rules of inner scopes such as configuration or interface.

driver and start functions

You can specify a driver with either of the driver or start functions. The difference is that driver is used to launch programs that are expected to run as long as the device is attached. These programs are usually daemons. Consider the following rule for launching a daemon:

device() {
    class(USB_CLASS_MASS_STORAGE) {
        driver"devb-umass $(DISK_OPTS) $(UMASS_OPTS),\z
                vid=$(vendor_id),did=$(product_id),busno=$(busno),\z
                devno=$(devno),interface=$(interface)";
    };
};

You can use the start function and its corresponding removal function to execute short, simple scripts that mount or unmount a network DLL. You can also use these functions to launch short-lived commands, such as the echo commands, below:

device() {
    class(USB_CLASS_MASS_STORAGE) {
        start"echo Device busno=$(busno) devno=$(devno) inserted";
        removal"echo Device busno=$(busno) devno=$(devno) removed";
    };
};