Device Matching Mechanism

id_table points to an array of usbh_dev_id_t that declares which devices this driver supports. During enumeration, the USB Core iterates through this table and matches it against the interface descriptor of the inserted device to decide whether to load this driver.

Structure Definition

typedef struct {
    u16 mMatchFlags;            /* Match mask */
    u16 idVendor;               /* Vendor ID (VID) */
    u16 idProduct;              /* Product ID (PID) */
    u8  bDeviceClass;           /* Device class code */
    u8  bDeviceSubClass;        /* Device subclass code */
    u8  bDeviceProtocol;        /* Device protocol code */
    u8  bInterfaceClass;        /* Interface class code */
    u8  bInterfaceSubClass;     /* Interface subclass code */
    u8  bInterfaceProtocol;     /* Interface protocol code */
} usbh_dev_id_t;

Match Flags

mMatchFlags uses a bitwise OR combination to specify which fields participate in matching:

Macro

Value

Description

USBH_DEV_ID_MATCH_VID

BIT0

Match idVendor

USBH_DEV_ID_MATCH_PID

BIT1

Match idProduct

USBH_DEV_ID_MATCH_DEV_CLASS

BIT2

Match bDeviceClass

USBH_DEV_ID_MATCH_DEV_SUBCLASS

BIT3

Match bDeviceSubClass

USBH_DEV_ID_MATCH_DEV_PROTOCOL

BIT4

Match bDeviceProtocol

USBH_DEV_ID_MATCH_ITF_CLASS

BIT5

Match bInterfaceClass

USBH_DEV_ID_MATCH_ITF_SUBCLASS

BIT6

Match bInterfaceSubClass

USBH_DEV_ID_MATCH_ITF_PROTOCOL

BIT7

Match bInterfaceProtocol

Commonly Used Composite Macros:

Macro

Expansion

USBH_DEV_ID_MATCH_DEVICE

VID | PID (Match by vendor and product)

USBH_DEV_ID_MATCH_DEV_INFO

DEV_CLASS | DEV_SUBCLASS | DEV_PROTOCOL (Match by device class info)

USBH_DEV_ID_MATCH_ITF_INFO

ITF_CLASS | ITF_SUBCLASS | ITF_PROTOCOL (Match by interface class info)

Matching Rules

  • The Core iterates through the id_table array and checks each entry against the corresponding fields of the device/interface descriptor.

  • The array is terminated by { } (all-zero entry).

  • Only fields with their corresponding bits set in mMatchFlags participate in comparison; unset fields are ignored.

  • An entry is considered a match only when all participating fields are consistent.

Matching Characteristics

For a composite device (e.g., HID + UAC, CDC ACM + CDC ECM), the driver only needs to match one sub-function’s interface to confirm the device is within its support scope. Other sub-functions find their own interfaces through independent usbh_dev_id_t matching in the attach phase.

Note

Developers can customize mMatchFlags to flexibly configure matching conditions:

  • Match by VID/PID (USBH_DEV_ID_MATCH_DEVICE): Supports only a specific vendor and product model - highest matching precision.

  • Match by interface class/subclass (USBH_DEV_ID_MATCH_ITF_CLASS or USBH_DEV_ID_MATCH_ITF_INFO): Supports all devices conforming to a specific USB class - broadest compatibility.

  • Combined matching: OR multiple flags to achieve multi-dimensional matching (e.g., match both VID and interface class).

  • If the corresponding flag is not set in mMatchFlags, the field value is ignored during comparison even if it has been assigned.

Usage Examples

The following examples are organized by matching scope. Refer to {SDK}/component/usb/host/ for actual driver code.

Match by VID/PID (Specific Device)

Loads the driver only when both Vendor ID (VID) and Product ID (PID) match. Suitable for custom hardware scenarios.

static const usbh_dev_id_t my_device_devs[] = {
    {
        .mMatchFlags = USBH_DEV_ID_MATCH_DEVICE,   /* Expands to VID | PID */
        .idVendor  = 0x0BDB,
        .idProduct = 0x1234,
    },
    { },
};

VID or PID can also be matched individually:

static const usbh_dev_id_t my_vid_devs[] = {
    {
        .mMatchFlags = USBH_DEV_ID_MATCH_VID,      /* Match vendor only */
        .idVendor  = 0x0BDB,
    },
    { },
};

Match by Device Descriptor

Matches using bDeviceClass / bDeviceSubClass / bDeviceProtocol from the device descriptor. Suitable for devices that define class at the device level (e.g., Hub).

static const usbh_dev_id_t hub_devs[] = {
    {
        .mMatchFlags = USBH_DEV_ID_MATCH_DEV_CLASS,
        .bDeviceClass = USB_CLASS_HUB,
    },
    { },
};

The USBH_DEV_ID_MATCH_DEV_INFO composite macro can match class, subclass, and protocol simultaneously:

static const usbh_dev_id_t comm_devs[] = {
    {
        .mMatchFlags = USBH_DEV_ID_MATCH_DEV_INFO,
        .bDeviceClass    = 0x02,   /* Communications Device class */
        .bDeviceSubClass = 0x00,
        .bDeviceProtocol = 0x00,
    },
    { },
};

Match by Interface Descriptor

The most common matching method, using bInterfaceClass / bInterfaceSubClass / bInterfaceProtocol from the interface descriptor. Suitable for standard USB class drivers (MSC, CDC ACM, UAC, HID, etc.).

Match by Interface Class Only

The simplest form - identifies devices by interface class alone, for example the MSC driver:

static const usbh_dev_id_t msc_devs[] = {
    {
        .mMatchFlags = USBH_DEV_ID_MATCH_ITF_CLASS,
        .bInterfaceClass = MSC_CLASS_CODE,
    },
    { },
};

Match by Interface Class + Subclass

Narrows the matching scope, suitable for drivers that differentiate subclasses within the same class, such as UAC:

static const usbh_dev_id_t uac_devs[] = {
    {
        .mMatchFlags = USBH_DEV_ID_MATCH_ITF_CLASS | USBH_DEV_ID_MATCH_ITF_SUBCLASS,
        .bInterfaceClass    = USB_UAC1_CLASS_CODE,
        .bInterfaceSubClass = USB_UAC1_SUBCLASS_AUDIOSTREAMING,
    },
    { },
};

Note

The UAC example intentionally omits bInterfaceProtocol so that UAC 2.0 devices (protocol = 0x20) still enter the attach callback, where the driver issues a clear “UAC 2.0 not supported” message instead of a generic probe failure - making debugging easier.

Match by Interface Class + Subclass + Protocol

The highest precision interface-level matching:

static const usbh_dev_id_t vendor_devs[] = {
    {
        .mMatchFlags = USBH_DEV_ID_MATCH_ITF_INFO,  /* Expands to ITF_CLASS | ITF_SUBCLASS | ITF_PROTOCOL */
        .bInterfaceClass    = VENDOR_CLASS_CODE,
        .bInterfaceSubClass = VENDOR_SUBCLASS_CODE,
        .bInterfaceProtocol = VENDOR_PROTOCOL,
    },
    { },
};

Combined Match: Interface Class + VID

Multi-dimensional matching: first filter by interface class, then narrow to a specific vendor:

static const usbh_dev_id_t uvc_devs[] = {
    {
        .mMatchFlags = USBH_DEV_ID_MATCH_ITF_CLASS | USBH_DEV_ID_MATCH_VID,
        .idVendor      = 0x0BDB,
        .bInterfaceClass = USBH_UVC_CLASS_CODE,
    },
    { },
};

Complete Driver Registration

The id_table must be associated with a usbh_class_driver_t struct for the USB Core to load the driver during enumeration:

static const usbh_dev_id_t cdc_acm_devs[] = {
    {
        .mMatchFlags = USBH_DEV_ID_MATCH_ITF_CLASS,
        .bInterfaceClass = USB_CDC_CLASS_CODE,
    },
    { },
};

/* USB Host CDC ACM class driver */
static usbh_class_driver_t usbh_cdc_acm_driver = {
    .id_table = cdc_acm_devs,
    .attach   = usbh_cdc_acm_attach,
    .detach   = usbh_cdc_acm_detach,
    .setup    = usbh_cdc_acm_setup,
    .process  = usbh_cdc_acm_process,
};

Driver Authoring Steps

  1. Define an id_table array terminated by { } (all-zero entry).

  2. Set mMatchFlags to declare which fields participate in matching.

  3. Fill in the corresponding match values (interface class, VID/PID, etc.).

  4. Assign the id_table to the .id_table member of usbh_class_driver_t.

  5. The USB Core iterates through id_table during enumeration and automatically loads the driver on a successful match.