Skip to content

Commit 3238285

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 ea7e077 commit 3238285

File tree

8 files changed

+643
-20
lines changed

8 files changed

+643
-20
lines changed

include/zephyr/usb/usbh.h

Lines changed: 52 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
*/
@@ -127,10 +134,44 @@ struct usbh_class_data {
127134
};
128135

129136
/**
137+
* @cond INTERNAL_HIDDEN
138+
*
139+
* Variables used by the USB host stack but not exposed to the class
140+
* through the class API.
130141
*/
131-
#define USBH_DEFINE_CLASS(name) \
132-
static STRUCT_SECTION_ITERABLE(usbh_class_data, name)
142+
struct usbh_class_node {
143+
/** Class information exposed to host class implementations (drivers). */
144+
struct usbh_class_data *const c_data;
145+
/** Filter rules to match this USB host class instance against a device class **/
146+
struct usbh_class_filter *filters;
147+
/** Number of filters in the array */
148+
size_t num_filters;
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+
* @param[in] filt Array of @ref usbh_class_filter to match this class or NULL to match everything
162+
* @param[in] num_filt Number of filters in the array
163+
*/
164+
#define USBH_DEFINE_CLASS(class_name, class_api, class_priv, filt, num_filt) \
165+
static struct usbh_class_data class_data_##class_name = { \
166+
.name = STRINGIFY(class_name), \
167+
.api = class_api, \
168+
.priv = class_priv, \
169+
}; \
170+
static STRUCT_SECTION_ITERABLE(usbh_class_node, class_name) = { \
171+
.c_data = &class_data_##class_name, \
172+
.filters = filt, \
173+
.num_filters = num_filt, \
174+
};
134175

135176
/**
136177
* @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: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
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 classes, %d", ret);
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 class, %d", ret);
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_node *const c_node,
53+
struct usb_device *const udev)
54+
{
55+
const void *const desc_beg = usbh_desc_get_cfg_beg(udev);
56+
const void *const desc_end = usbh_desc_get_cfg_end(udev);
57+
const struct usb_desc_header *desc = desc_beg;
58+
struct usbh_class_data *const c_data = c_node->c_data;
59+
struct usbh_class_filter info = {
60+
.vid = udev->dev_desc.idVendor,
61+
.pid = udev->dev_desc.idProduct,
62+
};
63+
uint8_t iface;
64+
int ret;
65+
66+
if (c_data->udev != NULL) {
67+
LOG_DBG("Class %s already matched, skipping", c_data->name);
68+
return -EALREADY;
69+
}
70+
71+
while (true) {
72+
desc = usbh_desc_get_next_function(desc, desc_end);
73+
if (desc == NULL) {
74+
break;
75+
}
76+
77+
ret = usbh_desc_get_iface_info(desc, &info, &iface);
78+
if (ret < 0) {
79+
LOG_ERR("Failed to collect class codes for matching interface %u", iface);
80+
return ret;
81+
}
82+
83+
if (!usbh_class_is_matching(c_node->filters, c_node->num_filters, &info)) {
84+
LOG_DBG("Class %s not matching interface %u", c_data->name, iface);
85+
continue;
86+
}
87+
88+
ret = usbh_class_probe(c_data, udev, iface);
89+
if (ret == -ENOTSUP) {
90+
LOG_DBG("Class %s not supporting this device, skipping", c_data->name);
91+
continue;
92+
}
93+
if (ret != 0) {
94+
LOG_ERR("Error while probing the class %s for interface %u",
95+
c_data->name, iface);
96+
return ret;
97+
}
98+
99+
LOG_INF("Class %s matches this device's interface %u", c_data->name, iface);
100+
c_data->udev = udev;
101+
c_data->iface = iface;
102+
103+
return 0;
104+
}
105+
106+
return -ENOTSUP;
107+
}
108+
109+
int usbh_class_probe_all(struct usb_device *const udev)
110+
{
111+
bool matching = false;
112+
int ret;
113+
114+
STRUCT_SECTION_FOREACH(usbh_class_node, c_node) {
115+
/* If the class does not have a device assigned */
116+
ret = usbh_class_probe_each_iface(c_node, udev);
117+
if (ret == -ENOTSUP) {
118+
LOG_DBG("USB device not matching %s", c_node->c_data->name);
119+
continue;
120+
}
121+
if (ret != 0) {
122+
return ret;
123+
}
124+
125+
matching = true;
126+
break;
127+
}
128+
129+
if (!matching) {
130+
LOG_WRN("Could not find any class for the device");
131+
return -ENOTSUP;
132+
}
133+
134+
return 0;
135+
}
136+
137+
bool usbh_class_is_matching(struct usbh_class_filter *const filters, size_t num_filters,
138+
struct usbh_class_filter *const info)
139+
{
140+
/* Make empty filter set match everything (use class_api->probe() only) */
141+
if (num_filters == 0) {
142+
return true;
143+
}
144+
145+
/* Try to find a rule that matches completely */
146+
for (int i = 0; i < num_filters; i++) {
147+
const struct usbh_class_filter *filt = &filters[i];
148+
149+
if ((filt->flags & USBH_CLASS_MATCH_VID) &&
150+
info->vid != filt->vid) {
151+
continue;
152+
}
153+
154+
if ((filt->flags & USBH_CLASS_MATCH_PID) &&
155+
info->pid != filt->pid) {
156+
continue;
157+
}
158+
159+
if (filt->flags & USBH_CLASS_MATCH_CLASS &&
160+
info->class != filt->class) {
161+
continue;
162+
}
163+
164+
if ((filt->flags & USBH_CLASS_MATCH_SUB) &&
165+
info->sub != filt->sub) {
166+
continue;
167+
}
168+
169+
if ((filt->flags & USBH_CLASS_MATCH_PROTO) &&
170+
info->proto != filt->proto) {
171+
continue;
172+
}
173+
174+
/* All the selected filters did match */
175+
return true;
176+
}
177+
178+
/* At the end of the filter table and still no match */
179+
return false;
180+
}

subsys/usb/host/usbh_class.h

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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_USBH_CLASS_H
9+
#define ZEPHYR_INCLUDE_USBH_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_CLASS | 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+
* An empty filter set matches everything.
36+
* This can be used to only rely on @c class_api->probe() return value.
37+
*
38+
* @param[in] filters Array of filter rules to match
39+
* @param[in] num_filters Number of rules in the array.
40+
* @param[in] device_info Device information filled by this function
41+
* @retval true if the USB Device descriptor matches at least one rule.
42+
*/
43+
bool usbh_class_is_matching(struct usbh_class_filter *const filters, size_t num_filters,
44+
struct usbh_class_filter *const device_info);
45+
46+
/**
47+
* @brief Initialize every class instantiated on the system
48+
*
49+
* @param[in] uhs_ctx USB Host context to pass to the class.
50+
* @retval 0 on success or negative error code on failure
51+
*/
52+
int usbh_class_init_all(struct usbh_context *const uhs_ctx);
53+
54+
/**
55+
* @brief Probe all classes against a newly connected USB device.
56+
*
57+
* @param[in] udev USB device to probe.
58+
* @retval 0 on success or negative error code on failure
59+
*/
60+
int usbh_class_probe_all(struct usb_device *const udev);
61+
62+
/**
63+
* @brief Call the device removal handler for every class configured with it
64+
*
65+
* @param[in] udev USB device that got removed.
66+
* @retval 0 on success or negative error code on failure
67+
*/
68+
int usbh_class_remove_all(const struct usb_device *const udev);
69+
70+
#endif /* ZEPHYR_INCLUDE_USBH_CLASS_H */

0 commit comments

Comments
 (0)