Skip to content

Commit 8442887

Browse files
Josuah DemangeonAidenHu
andcommitted
usb: host: implement class filtering and descriptor browsing
Add utilities to filter a class and search the next descriptor of a given type. This can be used to in combination to seek device information from the USB descriptors and try to match a host class instance given a set of filters. Co-authored-by: Aiden Hu <[email protected]> Signed-off-by: Josuah Demangeon <[email protected]>
1 parent abb27d6 commit 8442887

File tree

6 files changed

+367
-13
lines changed

6 files changed

+367
-13
lines changed

include/zephyr/usb/usbh.h

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
2-
* Copyright (c) 2022 Nordic Semiconductor ASA
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
* Copyright 2025 NXP
34
*
45
* SPDX-License-Identifier: Apache-2.0
56
*/
@@ -72,18 +73,6 @@ struct usbh_context {
7273
.addr_ba = &ba_##device_name, \
7374
}
7475

75-
/**
76-
* @brief USB Class Code triple
77-
*/
78-
struct usbh_code_triple {
79-
/** Device Class Code */
80-
uint8_t dclass;
81-
/** Class Subclass Code */
82-
uint8_t sub;
83-
/** Class Protocol Code */
84-
uint8_t proto;
85-
};
86-
8776
struct usbh_class_data;
8877

8978
/**

subsys/usb/host/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ zephyr_library_sources(
99
usbh_ch9.c
1010
usbh_class.c
1111
usbh_core.c
12+
usbh_desc.c
1213
usbh_device.c
1314
)
1415

subsys/usb/host/usbh_class.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,104 @@
1010

1111
#include "usbh_class.h"
1212
#include "usbh_class_api.h"
13+
#include "usbh_desc.h"
1314

1415
LOG_MODULE_REGISTER(usbh_class, CONFIG_USBH_LOG_LEVEL);
1516

17+
int usbh_class_init_all(struct usbh_context *const uhs_ctx)
18+
{
19+
int ret;
20+
21+
STRUCT_SECTION_FOREACH(usbh_class_node, c_node) {
22+
ret = usbh_class_init(c_node->c_data, uhs_ctx);
23+
if (ret != 0) {
24+
LOG_ERR("Failed to initialize all registered class instances");
25+
return ret;
26+
}
27+
}
28+
29+
return 0;
30+
}
31+
32+
int usbh_class_remove_all(const struct usb_device *const udev)
33+
{
34+
int ret;
35+
36+
STRUCT_SECTION_FOREACH(usbh_class_node, c_node) {
37+
if (c_node->c_data->udev == udev) {
38+
ret = usbh_class_removed(c_node->c_data);
39+
if (ret != 0) {
40+
LOG_ERR("Failed to handle device removal for each classes");
41+
return ret;
42+
}
43+
}
44+
}
45+
46+
return 0;
47+
}
48+
49+
int usbh_class_probe_one(struct usbh_class_data *const c_data, struct usb_device *const udev)
50+
{
51+
const uint8_t *const desc_beg = usbh_desc_get_cfg_beg(udev);
52+
const uint8_t *const desc_end = usbh_desc_get_cfg_end(udev);
53+
const struct usb_desc_header *desc;
54+
const struct usb_cfg_descriptor *cfg_desc;
55+
int ret;
56+
57+
desc = usbh_desc_get_by_type(desc_beg, desc_end, USB_DESC_CONFIGURATION);
58+
if (desc == NULL) {
59+
LOG_ERR("Unable to find a configuration descriptor");
60+
return -EBADMSG;
61+
}
62+
63+
cfg_desc = (struct usb_cfg_descriptor *)desc;
64+
65+
for (uint8_t ifnum = 0; ifnum < cfg_desc->bNumInterfaces; ifnum++) {
66+
ret = usbh_class_probe(c_data, udev, ifnum);
67+
if (ret == -ENOTSUP) {
68+
LOG_DBG("Class %s not supporting this device, skipping", c_data->name);
69+
continue;
70+
}
71+
if (ret != 0) {
72+
LOG_ERR("Failed to register the class %s for interface %u",
73+
c_data->name, ifnum);
74+
return ret;
75+
}
76+
if (ret == 0) {
77+
LOG_INF("Class %s is matching this device for interface %u, assigning it",
78+
c_data->name);
79+
c_data->udev = udev;
80+
c_data->ifnum = ifnum;
81+
return 0;
82+
}
83+
}
84+
85+
return -ENOTSUP;
86+
}
87+
88+
int usbh_class_probe_all(struct usb_device *const udev)
89+
{
90+
int ret;
91+
92+
STRUCT_SECTION_FOREACH(usbh_class_node, c_node) {
93+
struct usbh_class_data *const c_data = c_node->c_data;
94+
95+
/* If the class is not already having a device */
96+
if (c_data->udev == NULL) {
97+
ret = usbh_class_probe_one(c_data, udev);
98+
if (ret == -ENOTSUP) {
99+
continue;
100+
}
101+
if (ret != 0) {
102+
return ret;
103+
}
104+
}
105+
}
106+
107+
LOG_WRN("Could not find any class for this device");
108+
return -ENODEV;
109+
}
110+
16111
bool usbh_class_is_matching(struct usbh_class_filter *const filters, size_t n_filters,
17112
struct usb_device_descriptor *const desc)
18113
{

subsys/usb/host/usbh_class.h

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
* Copyright 2025 NXP
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#ifndef ZEPHYR_INCLUDE_USBD_CLASS_H
9+
#define ZEPHYR_INCLUDE_USBD_CLASS_H
10+
11+
#include <zephyr/usb/usbh.h>
12+
13+
/**
14+
* @brief Information about a device, which is relevant for matching a particular class.
15+
*/
16+
struct usbh_class_filter {
17+
/** Vendor ID */
18+
uint16_t vid;
19+
/** Product ID */
20+
uint16_t pid;
21+
/** Device Class Code */
22+
uint8_t dclass;
23+
/** Class Subclass Code */
24+
uint8_t sub;
25+
/** Class Protocol Code */
26+
uint8_t proto;
27+
/** Flags that tell which field to match */
28+
uint8_t flags;
29+
};
30+
31+
/** Match a device's vendor ID */
32+
#define USBH_CLASS_MATCH_VID BIT(1)
33+
34+
/** Match a device's product ID */
35+
#define USBH_CLASS_MATCH_PID BIT(2)
36+
37+
/** Match a class code */
38+
#define USBH_CLASS_MATCH_DCLASS BIT(3)
39+
40+
/** Match a subclass code */
41+
#define USBH_CLASS_MATCH_SUB BIT(4)
42+
43+
/** Match a protocol code */
44+
#define USBH_CLASS_MATCH_PROTO BIT(5)
45+
46+
/** Match a code triple */
47+
#define USBH_CLASS_MATCH_CODE_TRIPLE \
48+
(USBH_CLASS_MATCH_DCLASS | USBH_CLASS_MATCH_SUB | USBH_CLASS_MATCH_PROTO)
49+
50+
/**
51+
* @brief Match an USB host class (a driver) against a device descriptor.
52+
*
53+
* @param[in] filters Array of filter rules to match
54+
* @param[in] n_filters Number of rules in the array.
55+
* @param[in] desc USB Device descriptor to match against each rule
56+
* @retval true if the USB Device descriptor matches at least one rule.
57+
*/
58+
bool usbh_class_is_matching(struct usbh_class_filter *const filters, size_t n_filters,
59+
struct usb_device_descriptor *const desc);
60+
61+
/**
62+
* @brief Initialize every class instantiated on the system
63+
*
64+
* @param[in] uhs_ctx USB Host context to pass to the class.
65+
* @retval 0 on success or negative error code on failure
66+
*/
67+
int usbh_class_init_all(struct usbh_context *const uhs_ctx);
68+
69+
/**
70+
* @brief Probe all classes to against a newly connected USB device.
71+
*
72+
* @param[in] udev USB device to probe.
73+
* @retval 0 on success or negative error code on failure
74+
*/
75+
int usbh_class_probe_all(struct usb_device *const udev);
76+
77+
/**
78+
* @brief Call the device removal handler for every class configured with it
79+
*
80+
* @param[in] udev USB device that got removed.
81+
* @retval 0 on success or negative error code on failure
82+
*/
83+
int usbh_class_remove_all(const struct usb_device *const udev);
84+
85+
#endif /* ZEPHYR_INCLUDE_USBD_CLASS_H */

subsys/usb/host/usbh_desc.c

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
* Copyright NXP
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#include <zephyr/usb/usb_ch9.h>
9+
10+
#include "usbh_desc.h"
11+
12+
const struct usb_desc_header *usbh_desc_get_next(const struct usb_desc_header *const desc,
13+
const void *const desc_end)
14+
{
15+
const struct usb_desc_header *next;
16+
17+
if (desc == NULL) {
18+
return NULL;
19+
}
20+
21+
next = (const struct usb_desc_header *)((uint8_t *)desc + desc->bLength);
22+
23+
/* Validate the bLength field to make sure it does not point past the end */
24+
if ((uint8_t *)next >= (uint8_t *)desc_end ||
25+
(uint8_t *)next + next->bLength > (uint8_t *)desc_end ||
26+
desc->bLength == 0) {
27+
return NULL;
28+
}
29+
30+
return next;
31+
}
32+
33+
const struct usb_desc_header *usbh_desc_get_by_type(const void *const desc_beg,
34+
const void *const desc_end,
35+
uint32_t type_mask)
36+
{
37+
const struct usb_desc_header *desc;
38+
39+
if (desc_beg == NULL) {
40+
return NULL;
41+
}
42+
43+
for (desc = desc_beg; desc != NULL; desc = usbh_desc_get_next(desc, desc_end)) {
44+
if ((BIT(desc->bDescriptorType) & type_mask) != 0) {
45+
return desc;
46+
}
47+
}
48+
49+
return NULL;
50+
}
51+
52+
const struct usb_desc_header *usbh_desc_get_by_ifnum(const void *const desc_beg,
53+
const void *const desc_end,
54+
const uint8_t ifnum)
55+
{
56+
const struct usb_desc_header *desc;
57+
58+
if (desc_beg == NULL) {
59+
return NULL;
60+
}
61+
62+
for (desc = desc_beg; desc != NULL; desc = usbh_desc_get_next(desc, desc_end)) {
63+
if (desc->bDescriptorType == USB_DESC_INTERFACE &&
64+
((struct usb_if_descriptor *)desc)->bInterfaceNumber == ifnum) {
65+
return desc;
66+
}
67+
if (desc->bDescriptorType == USB_DESC_INTERFACE_ASSOC &&
68+
((struct usb_association_descriptor *)desc)->bFirstInterface == ifnum) {
69+
return desc;
70+
}
71+
}
72+
73+
return NULL;
74+
}
75+
76+
const void *usbh_desc_get_cfg_beg(const struct usb_device *udev)
77+
{
78+
return udev->cfg_desc;
79+
}
80+
81+
const void *usbh_desc_get_cfg_end(const struct usb_device *udev)
82+
{
83+
const struct usb_cfg_descriptor *cfg_desc;
84+
85+
if (udev->cfg_desc == NULL) {
86+
return NULL;
87+
}
88+
89+
cfg_desc = (struct usb_cfg_descriptor *)udev->cfg_desc;
90+
91+
return (uint8_t *)cfg_desc + cfg_desc->wTotalLength;
92+
}

0 commit comments

Comments
 (0)