Skip to content

Commit 92ec973

Browse files
Josuah DemangeonAidenHu
andcommitted
usb: host: introduce usbh_class with init/remove functions
Add functions to probe/remove all classes as part of a new usbh_class.c and a matching usbh_class.h. These functions are called from the function usbh_init_device_intl() in usbh_core.c to initialize every class upon connection of a device. Every class driver provide filters to match the interfaces of the device. Co-authored-by: Aiden Hu <[email protected]> Signed-off-by: Josuah Demangeon <[email protected]>
1 parent 5e90770 commit 92ec973

File tree

8 files changed

+551
-20
lines changed

8 files changed

+551
-20
lines changed

include/zephyr/usb/usbh.h

Lines changed: 48 additions & 11 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,20 +73,26 @@ struct usbh_context {
7273
.addr_ba = &ba_##device_name, \
7374
}
7475

76+
struct usbh_class_data;
77+
7578
/**
76-
* @brief USB Class Code triple
79+
* @brief Information about a device, which is relevant for matching a particular class.
7780
*/
78-
struct usbh_code_triple {
79-
/** Device Class Code */
80-
uint8_t dclass;
81-
/** Class Subclass Code */
81+
struct usbh_class_filter {
82+
/** Vendor ID */
83+
uint16_t vid;
84+
/** Product ID */
85+
uint16_t pid;
86+
/** Class Code */
87+
uint8_t class;
88+
/** Subclass Code */
8289
uint8_t sub;
83-
/** Class Protocol Code */
90+
/** Protocol Code */
8491
uint8_t proto;
92+
/** Flags that tell which field to match */
93+
uint8_t flags;
8594
};
8695

87-
struct usbh_class_data;
88-
8996
/**
9097
* @brief USB host class instance API
9198
*/
@@ -114,6 +121,10 @@ struct usbh_class_api {
114121
struct usbh_class_data {
115122
/** Name of the USB host class instance */
116123
const char *name;
124+
/** Filter rules to match this USB host class instance against a device class **/
125+
struct usbh_class_filter *filters;
126+
/** Number of filters in the array */
127+
size_t num_filters;
117128
/** Pointer to USB host stack context structure */
118129
struct usbh_context *uhs_ctx;
119130
/** Pointer to USB device this class is used for */
@@ -127,10 +138,36 @@ struct usbh_class_data {
127138
};
128139

129140
/**
141+
* @cond INTERNAL_HIDDEN
142+
*
143+
* Variables used by the USB host stack but not exposed to the class
144+
* through the class API.
130145
*/
131-
#define USBH_DEFINE_CLASS(name) \
132-
static STRUCT_SECTION_ITERABLE(usbh_class_data, name)
146+
struct usbh_class_node {
147+
/** Class information exposed to host class implementations (drivers). */
148+
struct usbh_class_data *const c_data;
149+
};
150+
/* @endcond */
133151

152+
/**
153+
* @brief Define USB host support class data
154+
*
155+
* Macro defines class (function) data, as well as corresponding node
156+
* structures used internally by the stack.
157+
*
158+
* @param[in] class_name Class name
159+
* @param[in] class_api Pointer to struct usbh_class_api
160+
* @param[in] class_priv Class private data
161+
*/
162+
#define USBH_DEFINE_CLASS(class_name, class_api, class_priv) \
163+
static struct usbh_class_data class_data_##class_name = { \
164+
.name = STRINGIFY(class_name), \
165+
.api = class_api, \
166+
.priv = class_priv, \
167+
}; \
168+
static STRUCT_SECTION_ITERABLE(usbh_class_node, class_name) = { \
169+
.c_data = &class_data_##class_name, \
170+
};
134171

135172
/**
136173
* @brief Initialize the USB host support;

subsys/usb/host/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ zephyr_library()
55
zephyr_library_include_directories(${CMAKE_CURRENT_SOURCE_DIR})
66

77
zephyr_library_sources(
8+
usbh_api.c
89
usbh_ch9.c
10+
usbh_class.c
911
usbh_core.c
10-
usbh_api.c
12+
usbh_desc.c
1113
usbh_device.c
1214
)
1315

subsys/usb/host/usbh_class.c

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
* Copyright 2025 NXP
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#include <zephyr/usb/usbh.h>
9+
#include <zephyr/logging/log.h>
10+
11+
#include "usbh_class.h"
12+
#include "usbh_class_api.h"
13+
#include "usbh_desc.h"
14+
15+
LOG_MODULE_REGISTER(usbh_class, CONFIG_USBH_LOG_LEVEL);
16+
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+
/* The class instance is now free to bind to a new device */
45+
c_node->c_data->udev = NULL;
46+
}
47+
}
48+
49+
return 0;
50+
}
51+
52+
static int usbh_class_probe_each_iface(struct usbh_class_data *const c_data,
53+
struct usb_device *const udev)
54+
{
55+
const struct usb_cfg_descriptor *const cfg_desc = usbh_desc_get_cfg_beg(udev);
56+
int ret;
57+
58+
for (uint8_t next, iface = 0; iface < cfg_desc->bNumInterfaces; iface = next) {
59+
struct usbh_class_filter device_info = {};
60+
61+
ret = usbh_desc_get_device_info(&device_info, udev, iface);
62+
if (ret < 0) {
63+
LOG_ERR("Failed to collect class codes for matching interface %u", iface);
64+
return ret;
65+
}
66+
next = ret;
67+
68+
if (!usbh_class_is_matching(c_data->filters, c_data->num_filters, &device_info)) {
69+
LOG_DBG("Class %s not matching interface %u", c_data->name, iface);
70+
continue;
71+
}
72+
73+
ret = usbh_class_probe(c_data, udev, iface);
74+
if (ret == -ENOTSUP) {
75+
LOG_DBG("Class %s not supporting this device, skipping", c_data->name);
76+
continue;
77+
}
78+
if (ret != 0) {
79+
LOG_ERR("Error while probing the class %s for interface %u",
80+
c_data->name, iface);
81+
return ret;
82+
}
83+
84+
LOG_INF("Class %s matches this device's interface %u", c_data->name, iface);
85+
c_data->udev = udev;
86+
c_data->iface = iface;
87+
88+
return 0;
89+
}
90+
91+
return -ENOTSUP;
92+
}
93+
94+
int usbh_class_probe_all(struct usb_device *const udev)
95+
{
96+
bool matching = false;
97+
int ret;
98+
99+
STRUCT_SECTION_FOREACH(usbh_class_node, c_node) {
100+
struct usbh_class_data *const c_data = c_node->c_data;
101+
102+
/* If the class is not already having a device */
103+
if (c_data->udev == NULL) {
104+
ret = usbh_class_probe_each_iface(c_data, udev);
105+
if (ret == -ENOTSUP) {
106+
LOG_DBG("This device has no %s class matching", c_data->name);
107+
continue;
108+
}
109+
if (ret != 0) {
110+
return ret;
111+
}
112+
matching = true;
113+
}
114+
}
115+
116+
if (!matching) {
117+
LOG_WRN("Could not find any class for this device");
118+
return -ENOTSUP;
119+
}
120+
121+
return 0;
122+
}
123+
124+
bool usbh_class_is_matching(struct usbh_class_filter *const filters, size_t num_filters,
125+
struct usbh_class_filter *const device_info)
126+
{
127+
/* Make empty filter set match everything (use class_api->probe() only) */
128+
if (num_filters == 0) {
129+
return true;
130+
}
131+
132+
/* Try to find a rule that matches completely */
133+
for (int i = 0; i < num_filters; i++) {
134+
const struct usbh_class_filter *filt = &filters[i];
135+
136+
if ((filt->flags & USBH_CLASS_MATCH_VID) &&
137+
device_info->vid != filt->vid) {
138+
continue;
139+
}
140+
141+
if ((filt->flags & USBH_CLASS_MATCH_PID) &&
142+
device_info->pid != filt->pid) {
143+
continue;
144+
}
145+
146+
if (filt->flags & USBH_CLASS_MATCH_CLASS &&
147+
device_info->class != filt->class) {
148+
continue;
149+
}
150+
151+
if ((filt->flags & USBH_CLASS_MATCH_SUB) &&
152+
device_info->sub != filt->sub) {
153+
continue;
154+
}
155+
156+
if ((filt->flags & USBH_CLASS_MATCH_PROTO) &&
157+
device_info->proto != filt->proto) {
158+
continue;
159+
}
160+
161+
/* All the selected filters did match */
162+
return true;
163+
}
164+
165+
/* At the end of the filter table and still no match */
166+
return false;
167+
}

subsys/usb/host/usbh_class.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
/** Match a device's vendor ID */
14+
#define USBH_CLASS_MATCH_VID BIT(1)
15+
16+
/** Match a device's product ID */
17+
#define USBH_CLASS_MATCH_PID BIT(2)
18+
19+
/** Match a class code */
20+
#define USBH_CLASS_MATCH_CLASS BIT(3)
21+
22+
/** Match a subclass code */
23+
#define USBH_CLASS_MATCH_SUB BIT(4)
24+
25+
/** Match a protocol code */
26+
#define USBH_CLASS_MATCH_PROTO BIT(5)
27+
28+
/** Match a code triple */
29+
#define USBH_CLASS_MATCH_CODE_TRIPLE \
30+
(USBH_CLASS_MATCH_DCLASS | USBH_CLASS_MATCH_SUB | USBH_CLASS_MATCH_PROTO)
31+
32+
/**
33+
* @brief Match an USB host class (a driver) against a device descriptor.
34+
*
35+
* @param[in] filters Array of filter rules to match
36+
* @param[in] num_filters Number of rules in the array.
37+
* @param[in] device_info Device information filled by this function
38+
* @retval true if the USB Device descriptor matches at least one rule.
39+
*/
40+
bool usbh_class_is_matching(struct usbh_class_filter *const filters, size_t num_filters,
41+
struct usbh_class_filter *const device_info);
42+
43+
/**
44+
* @brief Initialize every class instantiated on the system
45+
*
46+
* @param[in] uhs_ctx USB Host context to pass to the class.
47+
* @retval 0 on success or negative error code on failure
48+
*/
49+
int usbh_class_init_all(struct usbh_context *const uhs_ctx);
50+
51+
/**
52+
* @brief Probe all classes to against a newly connected USB device.
53+
*
54+
* @param[in] udev USB device to probe.
55+
* @retval 0 on success or negative error code on failure
56+
*/
57+
int usbh_class_probe_all(struct usb_device *const udev);
58+
59+
/**
60+
* @brief Call the device removal handler for every class configured with it
61+
*
62+
* @param[in] udev USB device that got removed.
63+
* @retval 0 on success or negative error code on failure
64+
*/
65+
int usbh_class_remove_all(const struct usb_device *const udev);
66+
67+
#endif /* ZEPHYR_INCLUDE_USBD_CLASS_H */

subsys/usb/host/usbh_core.c

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
#include <zephyr/devicetree.h>
1111
#include <zephyr/init.h>
1212
#include <zephyr/sys/iterable_sections.h>
13+
#include <zephyr/usb/usbh.h>
1314

14-
#include "usbh_internal.h"
15+
#include "usbh_class.h"
16+
#include "usbh_class_api.h"
1517
#include "usbh_device.h"
18+
#include "usbh_internal.h"
1619

1720
#include <zephyr/logging/log.h>
1821
LOG_MODULE_REGISTER(uhs, CONFIG_USBH_LOG_LEVEL);
@@ -46,6 +49,7 @@ static int usbh_event_carrier(const struct device *dev,
4649
static void dev_connected_handler(struct usbh_context *const ctx,
4750
const struct uhc_event *const event)
4851
{
52+
int ret;
4953

5054
LOG_DBG("Device connected event");
5155
if (ctx->root != NULL) {
@@ -71,6 +75,11 @@ static void dev_connected_handler(struct usbh_context *const ctx,
7175
if (usbh_device_init(ctx->root)) {
7276
LOG_ERR("Failed to reset new USB device");
7377
}
78+
79+
ret = usbh_class_probe_all(ctx->root);
80+
if (ret != 0) {
81+
LOG_ERR("Failed to probe all classes for this new USB device");
82+
}
7483
}
7584

7685
static void dev_removed_handler(struct usbh_context *const ctx)
@@ -194,12 +203,10 @@ int usbh_init_device_intl(struct usbh_context *const uhs_ctx)
194203

195204
sys_dlist_init(&uhs_ctx->udevs);
196205

197-
STRUCT_SECTION_FOREACH(usbh_class_data, cdata) {
198-
/*
199-
* For now, we have not implemented any class drivers,
200-
* so just keep it as placeholder.
201-
*/
202-
break;
206+
ret = usbh_class_init_all(uhs_ctx);
207+
if (ret != 0) {
208+
LOG_ERR("Failed to initialize all classes");
209+
return ret;
203210
}
204211

205212
return 0;

subsys/usb/host/usbh_data.ld

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
#include <zephyr/linker/iterable_sections.h>
88

99
ITERABLE_SECTION_RAM(usbh_context, Z_LINK_ITERABLE_SUBALIGN)
10-
ITERABLE_SECTION_RAM(usbh_class_data, Z_LINK_ITERABLE_SUBALIGN)
10+
ITERABLE_SECTION_RAM(usbh_class_node, Z_LINK_ITERABLE_SUBALIGN)

0 commit comments

Comments
 (0)