Skip to content

Commit 2aa5226

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. The class instances are registered and connected together as a linked list. Co-authored-by: Aiden Hu <[email protected]> Signed-off-by: Josuah Demangeon <[email protected]>
1 parent cad9ff3 commit 2aa5226

File tree

6 files changed

+191
-11
lines changed

6 files changed

+191
-11
lines changed

include/zephyr/usb/usbh.h

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,36 @@ struct usbh_class_data {
127127
};
128128

129129
/**
130+
* @cond INTERNAL_HIDDEN
131+
*
132+
* Variables used by the USB host stack but not exposed to the class
133+
* through the class API.
130134
*/
131-
#define USBH_DEFINE_CLASS(name) \
132-
static STRUCT_SECTION_ITERABLE(usbh_class_data, name)
135+
struct usbh_class_node {
136+
/** Class information exposed to host class implementations (drivers). */
137+
struct usbh_class_data *const c_data;
138+
};
139+
/* @endcond */
133140

141+
/**
142+
* @brief Define USB host support class data
143+
*
144+
* Macro defines class (function) data, as well as corresponding node
145+
* structures used internally by the stack.
146+
*
147+
* @param[in] class_name Class name
148+
* @param[in] class_api Pointer to struct usbh_class_api
149+
* @param[in] class_priv Class private data
150+
*/
151+
#define USBH_DEFINE_CLASS(class_name, class_api, class_priv) \
152+
static struct usbh_class_data class_data_##class_name = { \
153+
.name = STRINGIFY(class_name), \
154+
.api = class_api, \
155+
.priv = class_priv, \
156+
}; \
157+
static STRUCT_SECTION_ITERABLE(usbh_class_node, class_name) = { \
158+
.c_data = &class_data_##class_name, \
159+
};
134160

135161
/**
136162
* @brief Initialize the USB host support;

subsys/usb/host/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ 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
1112
usbh_device.c
1213
)
1314

subsys/usb/host/usbh_class.c

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,107 @@
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+
/* 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_one(struct usbh_class_data *const c_data, struct usb_device *const udev)
53+
{
54+
const uint8_t *const desc_beg = usbh_desc_get_cfg_beg(udev);
55+
const uint8_t *const desc_end = usbh_desc_get_cfg_end(udev);
56+
const struct usb_desc_header *desc;
57+
const struct usb_cfg_descriptor *cfg_desc;
58+
int ret;
59+
60+
desc = usbh_desc_get_by_type(desc_beg, desc_end, USB_DESC_CONFIGURATION);
61+
if (desc == NULL) {
62+
LOG_ERR("Unable to find a configuration descriptor");
63+
return -EBADMSG;
64+
}
65+
66+
cfg_desc = (struct usb_cfg_descriptor *)desc;
67+
68+
for (uint8_t ifnum = 0; ifnum < cfg_desc->bNumInterfaces; ifnum++) {
69+
ret = usbh_class_probe(c_data, udev, ifnum);
70+
if (ret == -ENOTSUP) {
71+
LOG_DBG("Class %s not supporting this device, skipping", c_data->name);
72+
continue;
73+
}
74+
if (ret != 0) {
75+
LOG_ERR("Failed to register the class %s for interface %u",
76+
c_data->name, ifnum);
77+
return ret;
78+
}
79+
if (ret == 0) {
80+
LOG_INF("Class %s is matching this device for interface %u, assigning it",
81+
c_data->name, ifnum);
82+
c_data->udev = udev;
83+
c_data->ifnum = ifnum;
84+
return 0;
85+
}
86+
}
87+
88+
return -ENOTSUP;
89+
}
90+
91+
int usbh_class_probe_all(struct usb_device *const udev)
92+
{
93+
int ret;
94+
95+
STRUCT_SECTION_FOREACH(usbh_class_node, c_node) {
96+
struct usbh_class_data *const c_data = c_node->c_data;
97+
98+
/* If the class is not already having a device */
99+
if (c_data->udev == NULL) {
100+
ret = usbh_class_probe_one(c_data, udev);
101+
if (ret == -ENOTSUP) {
102+
continue;
103+
}
104+
if (ret != 0) {
105+
return ret;
106+
}
107+
}
108+
}
109+
110+
LOG_WRN("Could not find any class for this device");
111+
return -ENODEV;
112+
}
113+
16114
bool usbh_class_is_matching(struct usbh_class_filter *const filters, size_t n_filters,
17115
struct usb_device_descriptor *const desc)
18116
{

subsys/usb/host/usbh_class.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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 Match an USB host class (a driver) against a device descriptor.
15+
*
16+
* @param[in] filters Array of filter rules to match
17+
* @param[in] n_filters Number of rules in the array.
18+
* @param[in] desc USB Device descriptor to match against each rule
19+
* @retval true if the USB Device descriptor matches at least one rule.
20+
*/
21+
bool usbh_class_is_matching(struct usbh_class_filter *const filters, size_t n_filters,
22+
struct usb_device_descriptor *const desc);
23+
24+
/**
25+
* @brief Initialize every class instantiated on the system
26+
*
27+
* @param[in] uhs_ctx USB Host context to pass to the class.
28+
* @retval 0 on success or negative error code on failure
29+
*/
30+
int usbh_class_init_all(struct usbh_context *const uhs_ctx);
31+
32+
/**
33+
* @brief Probe all classes to against a newly connected USB device.
34+
*
35+
* @param[in] udev USB device to probe.
36+
* @retval 0 on success or negative error code on failure
37+
*/
38+
int usbh_class_probe_all(struct usb_device *const udev);
39+
40+
/**
41+
* @brief Call the device removal handler for every class configured with it
42+
*
43+
* @param[in] udev USB device that got removed.
44+
* @retval 0 on success or negative error code on failure
45+
*/
46+
int usbh_class_remove_all(const struct usb_device *const udev);
47+
48+
#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
@@ -1,4 +1,4 @@
11
#include <zephyr/linker/iterable_sections.h>
22

33
ITERABLE_SECTION_RAM(usbh_context, Z_LINK_ITERABLE_SUBALIGN)
4-
ITERABLE_SECTION_RAM(usbh_class_data, Z_LINK_ITERABLE_SUBALIGN)
4+
ITERABLE_SECTION_RAM(usbh_class_node, Z_LINK_ITERABLE_SUBALIGN)

0 commit comments

Comments
 (0)