Configuration files

Updated: April 19, 2023

The USB launcher service configuration files contain instructions for processing attached devices based on their properties, as well as descriptors to expose to USB hosts when the USB server is running in device mode.

The service interprets the configuration files as Lua scripts (see the Lua project homepage for information on the Lua language). By default, the service reads the file at /etc/usblauncher/rules.lua, but you can specify a custom configuration file by using the -c command-line option.

You can name only one configuration file, but this file can use the dofile() command to include other Lua files. For example, the configuration files shipped with the QNX SDP installers store the USB device mode descriptors in separate Lua files that are included in the main file. This way, the USB launcher service can override the default descriptors when the server runs in device mode instead of host mode.

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 (vendor ID, device ID, USB class, etc.) that the service compares against the device descriptors provided by the USB server. When the service finds a rule with properties matching those of a newly attached device, it executes the commands listed 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. 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 those levels. For rules at the same level, the service 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 and rule syntaxes supported at each level.
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 example 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 this command line are the same as the attribute names in PPS device information objects.

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

Variables

You can define variables to store values so you can use them in matching rules listed later in the configuration file. Here are some sample definitions:
USB_CLASS_HID = 0x03
    USB_HID_SUBCLASS_NONE = 0x00
        USB_HID_PROTOCOL_NONE = 0x00
        USB_HID_PROTOCOL_KEYBOARD = 0x01
        USB_HID_PROTOCOL_MOUSE = 0x02
    USB_HID_SUBCLASS_BOOTINTERFACE = 0x01
The matching rule below demonstrates the use of variables. This specification matches any device that has a Human Interface Device (HID) class, for the given vendor and product IDs:
device(0x2996, 0x0123) { 
    class(USB_CLASS_HID) {
        driver"io-hid -dusb verbose=3 priority=$(hid_drvr_prio)\z
                upath=/dev/usb/io-usb-otg &"
    };
}

Note that variables can be defined within a rule and used in the driver commands. When searching for variable definitions, the service searches from the innermost to the outermost scope.

Flags

You can use flags to change the range of devices matched and the actions subsequently performed.

Ignore flag

At the device level, the Ignore flag tells the USB launcher service to ignore the device by not attaching to it or reading its descriptors. Here, 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 server 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 service performs the Ignore check after retrieving device descriptors, so you can 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. In the following example, the nested class rule indicates an interface class of 8, but the outer rule matches devices of any class because of the flag:
device() {
    class(8) {
        Always;
        start"echo this is busno=$(busno) devno=$(devno) inserted";
     };
};

Multiple interface classes

You can specify multiple interface classes for a device or product rule, as shown here:
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):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

The foreach rule lets you launch a certain driver for any device in a list. This rule saves you from having to specify many product or device rules that differ only by their device or vendor IDs:
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 versus 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 echo commands, as shown here:
device() {
    class(USB_CLASS_MASS_STORAGE) {
        start"echo Device busno=$(busno) devno=$(devno) inserted";
        removal"echo Device busno=$(busno) devno=$(devno) removed";
    };
};