USB matching rules

The USB matching rules in the main configuration file (rules.lua) provide usblauncher with commands for launching the appropriate driver with the appropriate configuration when a device is attached through a USB port.

The matching rules specify device properties (e.g., vendor ID, device ID, USB class) that usblauncher compares against the descriptors of newly attached devices; these descriptors are provided by the USB stack. When usblauncher finds a matching rule with properties matching those of a newly attached USB device, it executes the driver command specified in that 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. The five levels in device specification rules are:

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

When an outer (more general) rule matches a newly attached device, usblauncher 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 care about matching any descriptors at that level.

For rules at the same level, usblauncher applies the rule listed the earliest in the configuration file. You can confirm the exact search order by calling show_config() at the end of your configuration file.

The device specification that follows provides 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)";

        };
    };
};

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 device specification level:

Level Descriptors matched Rule syntax
device vendor ID, device ID, earliest revision, latest revision device(<vid>, <did>, <rev_low>, <rev_high>) -- most specific

device(<vid>, <did>, <rev_low>)

device(<vid>, <did>)

device() -- most general
product (see 1) vendor ID, lowest device ID, highest device ID product(<vid>, <did_low>, <did_high>)       -- most specific

product(<vid>, <did_low>)

product(<vid>)

product() -- most general
configuration configuration number configuration(<num>) -- specific

configuration() -- general
interface interface number or range indicated by lowest and highest numbers interface(<num_low>, <num_high>)              -- most specific

interface(<num>)

interface() -- most general
class interface class, subclass, and protocol class(<class>, <subclass>, <protocol>)       -- most specific

class(<class>, <subclass>)

class(<class>)

class() -- most general
ms_desc Microsoft descriptor ms_desc(<compatibleID>, <subcompatibleID>, <vendorID>) -- most specific

ms_desc(<compatibleID>, <subcompatibleID>)

ms_desc(<compatibleID>)

ms_desc() -- most general

Variables

You can define variables at the top of the configuration file to hold configuration values used in the matching rules:

-- 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 rules that follow demonstrate the use of variables. The first specification matches any device that has a mass-storage class while the second specification matches a Sony Walkman device with an MTP Microsoft descriptor.

-- generic mass storage rule
device() {
    class(USB_CLASS_MASS_STORAGE) {

        DISK_OPTS  = "cam quiet 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";
    };
};


-- Sony Walkman
device(0x054c, 0x03fd) {

        ms_desc("MTP", "", 1) {
                driver"io-fs-media -dpfs,device=$(busno):\z
                        $(devno):$(interface)";
        };
};

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

Flags

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

At the device level, the Ignore flag instructs usblauncher 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, usblauncher doesn't have any device descriptors to match against. This means that it can look at only the vendor ID and device ID supplied by the stack when the device was physically attached to the system. Therefore, the device rule can't be made more specific about ignoring certain devices.

For rules at inner levels, the Ignore check is done after retrieving device descriptors, which means that you can make these rules more specific. At any inner level, the Ignore flag stops usblauncher 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;
    };
};

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

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

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

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";
     };
};

These last two flags provide a helpful way to test a rule during development.

Multiple interface classes

You can specify multiple interface classes for one device or product rule, as in the sample iPod device specification that follows.

-- 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 seen in the example that follows:

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 device-specification 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)";
    };
};

The start function is used for launching short-lived commands, such as the echo command shown in the following example:

device() {
    class(USB_CLASS_MASS_STORAGE) {
        start"echo Device busno=$(busno) devno=$(devno) inserted";
    };
};
1 The product rule is the same as the device rule except that you can specify a range of device IDs.