Skip to content

Commit e9eb91e

Browse files
committed
usb: host: update host core to support multi usb classes
Register all of usb classes that host needs supports during initialization. Call connection function for all of classes that one attached device has. Signed-off-by: Aiden Hu <[email protected]>
1 parent 5cb0667 commit e9eb91e

File tree

1 file changed

+311
-7
lines changed

1 file changed

+311
-7
lines changed

subsys/usb/host/usbh_core.c

Lines changed: 311 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,258 @@ static int usbh_event_carrier(const struct device *dev,
4343
return err;
4444
}
4545

46+
/**
47+
* @brief Check if USB device matches class driver
48+
*
49+
* @param cdata Pointer to class driver data
50+
* @param code Pointer to USB class code triple
51+
* @return true if matched, false otherwise
52+
*/
53+
static bool usbh_match_class_driver(struct usbh_class_data *cdata,
54+
struct usbh_code_triple *code)
55+
{
56+
if (!cdata || !code || !cdata->device_code_table) {
57+
return false;
58+
}
59+
60+
/* Traverse device code table (cdata->device_code_table) */
61+
for (int i = 0; i < cdata->table_items_count; i++) {
62+
struct usbh_device_code_table *table_entry = &cdata->device_code_table[i];
63+
/* TODO: match device code */
64+
65+
if (table_entry->match_type & USBH_MATCH_INTFACE) {
66+
/* Match interface class code */
67+
if (table_entry->interface_class_code == code->dclass &&
68+
(table_entry->interface_subclass_code == 0xFF ||
69+
table_entry->interface_subclass_code == code->sub) &&
70+
(table_entry->interface_protocol_code == 0x00 ||
71+
table_entry->interface_protocol_code == code->proto)) {
72+
return true;
73+
}
74+
}
75+
}
76+
77+
return false;
78+
}
79+
80+
/**
81+
* @brief Enumerate device descriptors and match class drivers
82+
*
83+
* This function traverses the USB device descriptors, identifies class-specific
84+
* descriptor segments, and attempts to match them with registered class drivers.
85+
*
86+
* @param ctx USB host context
87+
* @param udev USB device to process
88+
* @return 0 on success, negative errno on failure
89+
*/
90+
static int usbh_match_classes(struct usbh_contex *const ctx,
91+
struct usb_device *udev)
92+
{
93+
struct usb_cfg_descriptor *cfg_desc = udev->cfg_desc;
94+
95+
if (!cfg_desc) {
96+
LOG_ERR("No configuration descriptor found");
97+
return -EINVAL;
98+
}
99+
100+
uint8_t *desc_buf_base = (uint8_t *)cfg_desc;
101+
uint8_t *desc_buf_end = desc_buf_base + sys_le16_to_cpu(cfg_desc->wTotalLength);
102+
uint8_t *current_desc = desc_buf_base + cfg_desc->bLength; /* Skip config descriptor */
103+
int matched_count = 0;
104+
105+
LOG_DBG("Starting class enumeration for device (total desc length: %d)",
106+
sys_le16_to_cpu(cfg_desc->wTotalLength));
107+
108+
/* Main descriptor traversal loop - traverse entire descriptor */
109+
while (current_desc < desc_buf_end) {
110+
uint8_t *start_addr = current_desc; /* Initialize to current descriptor */
111+
uint8_t *end_addr = current_desc; /* Initialize to current descriptor */
112+
uint8_t *search_ptr;
113+
struct usbh_code_triple class_code = {0};
114+
bool found_iad = false;
115+
bool found_interface = false;
116+
struct usb_desc_header *iad_desc = NULL;
117+
118+
/* Step 1: Find first IAD or interface descriptor from start_addr */
119+
search_ptr = start_addr;
120+
while (search_ptr < desc_buf_end) {
121+
struct usb_desc_header *header = (struct usb_desc_header *)search_ptr;
122+
123+
if (header->bLength == 0) {
124+
goto exit_loop;
125+
}
126+
127+
if (header->bDescriptorType == USB_DESC_INTERFACE_ASSOC) {
128+
start_addr = search_ptr;
129+
found_iad = true;
130+
iad_desc = search_ptr;
131+
break;
132+
} else if (header->bDescriptorType == USB_DESC_INTERFACE) {
133+
start_addr = search_ptr;
134+
found_interface = true;
135+
/* Save class code for interface */
136+
struct usb_if_descriptor *if_desc = (struct usb_if_descriptor *)search_ptr;
137+
class_code.dclass = if_desc->bInterfaceClass;
138+
class_code.sub = if_desc->bInterfaceSubClass;
139+
class_code.proto = if_desc->bInterfaceProtocol;
140+
break;
141+
}
142+
143+
search_ptr += header->bLength;
144+
}
145+
146+
/* If no IAD or interface found, exit main loop (error condition) */
147+
if (!found_iad && !found_interface) {
148+
LOG_ERR("No IAD or interface descriptor found - error condition");
149+
break;
150+
}
151+
152+
/* Step 2: Continue searching for subsequent descriptors to determine end_addr */
153+
search_ptr = start_addr + ((struct usb_desc_header *)start_addr)->bLength;
154+
uint8_t *next_iad_addr = NULL;
155+
156+
/* Find next IAD */
157+
while (search_ptr < desc_buf_end) {
158+
struct usb_desc_header *header = (struct usb_desc_header *)search_ptr;
159+
160+
if (header->bLength == 0) {
161+
break;
162+
}
163+
164+
if (header->bDescriptorType == USB_DESC_INTERFACE_ASSOC) {
165+
next_iad_addr = search_ptr;
166+
break;
167+
}
168+
169+
search_ptr += header->bLength;
170+
}
171+
172+
/* Handle different cases and determine end_addr and class_code */
173+
if (!found_iad && !next_iad_addr) {
174+
/* Case 2a: No IAD in step 1, no IAD in subsequent descriptors */
175+
end_addr = desc_buf_end;
176+
/* class_code already saved in step 1 */
177+
} else if (!found_iad && next_iad_addr) {
178+
/* Case 2b: No IAD in step 1, found new IAD in subsequent descriptors */
179+
end_addr = next_iad_addr;
180+
/* class_code already saved in step 1 */
181+
} else if (found_iad && next_iad_addr) {
182+
/* Case 2c: Found IAD in step 1, found new IAD in subsequent descriptors */
183+
end_addr = next_iad_addr;
184+
/* Get class code from first interface after IAD */
185+
search_ptr = start_addr + iad_desc->bLength;
186+
while (search_ptr < end_addr) {
187+
struct usb_desc_header *header = (struct usb_desc_header *)search_ptr;
188+
if (header->bLength == 0) {
189+
break;
190+
}
191+
if (header->bDescriptorType == USB_DESC_INTERFACE) {
192+
struct usb_if_descriptor *if_desc = (struct usb_if_descriptor *)search_ptr;
193+
class_code.dclass = if_desc->bInterfaceClass;
194+
class_code.sub = if_desc->bInterfaceSubClass;
195+
class_code.proto = if_desc->bInterfaceProtocol;
196+
break;
197+
}
198+
search_ptr += header->bLength;
199+
}
200+
} else if (found_iad && !next_iad_addr) {
201+
/* Case 2d: Found IAD in step 1, no new IAD in subsequent descriptors */
202+
/* Get class code from first interface after IAD */
203+
search_ptr = start_addr + iad_desc->bLength;
204+
while (search_ptr < desc_buf_end) {
205+
struct usb_desc_header *header = (struct usb_desc_header *)search_ptr;
206+
if (header->bLength == 0) {
207+
break;
208+
}
209+
if (header->bDescriptorType == USB_DESC_INTERFACE) {
210+
struct usb_if_descriptor *if_desc = (struct usb_if_descriptor *)search_ptr;
211+
class_code.dclass = if_desc->bInterfaceClass;
212+
class_code.sub = if_desc->bInterfaceSubClass;
213+
class_code.proto = if_desc->bInterfaceProtocol;
214+
break;
215+
}
216+
search_ptr += header->bLength;
217+
}
218+
219+
/* Search for interface descriptor with different class code after IAD */
220+
search_ptr = start_addr + iad_desc->bLength;
221+
bool found_different_interface = false;
222+
223+
while (search_ptr < desc_buf_end) {
224+
struct usb_desc_header *header = (struct usb_desc_header *)search_ptr;
225+
if (header->bLength == 0) {
226+
break;
227+
}
228+
229+
if (header->bDescriptorType == USB_DESC_INTERFACE) {
230+
struct usb_if_descriptor *if_desc = (struct usb_if_descriptor *)search_ptr;
231+
/* Only compare class code */
232+
if (if_desc->bInterfaceClass != class_code.dclass) {
233+
end_addr = search_ptr;
234+
found_different_interface = true;
235+
break;
236+
}
237+
}
238+
search_ptr += header->bLength;
239+
}
240+
241+
if (!found_different_interface) {
242+
end_addr = desc_buf_end;
243+
}
244+
}
245+
246+
LOG_DBG("Found class segment: class=0x%02x, sub=0x%02x, proto=0x%02x, start=%p, end=%p",
247+
class_code.dclass, class_code.sub, class_code.proto,
248+
start_addr, end_addr);
249+
250+
/* Step 3: Loop through registered class drivers and call usbh_match_class_driver */
251+
struct usbh_class_data *cdata;
252+
bool matched = false;
253+
254+
SYS_SLIST_FOR_EACH_CONTAINER(&ctx->registered_classes, cdata, node) {
255+
if (!cdata->api || !cdata->api->connected) {
256+
continue;
257+
}
258+
259+
/* Call usbh_match_class_driver with cdata and code */
260+
if (usbh_match_class_driver(cdata, &class_code)) {
261+
LOG_INF("Class driver %s matched for class 0x%02x",
262+
cdata->name, class_code.dclass);
263+
264+
/* Step 4: Call connected handler */
265+
int ret = cdata->api->connected(udev, start_addr, end_addr, (void *)cdata);
266+
if (ret == 0) {
267+
LOG_INF("Class driver %s successfully claimed device", cdata->name);
268+
matched = true;
269+
matched_count++;
270+
} else {
271+
LOG_WRN("Class driver %s failed to claim device: %d",
272+
cdata->name, ret);
273+
}
274+
}
275+
}
276+
277+
if (!matched) {
278+
LOG_DBG("No class driver matched for class 0x%02x", class_code.dclass);
279+
}
280+
281+
/* Step 4: assign end_addr to current_desc and continue main loop */
282+
current_desc = end_addr;
283+
284+
/* Ensure we advance to next valid descriptor */
285+
if (current_desc < desc_buf_end) {
286+
struct usb_desc_header *header = (struct usb_desc_header *)current_desc;
287+
if (header->bLength == 0) {
288+
break;
289+
}
290+
}
291+
}
292+
293+
exit_loop:
294+
LOG_INF("Class enumeration completed: %d driver(s) matched", matched_count);
295+
return 0;
296+
}
297+
46298
static void dev_connected_handler(struct usbh_contex *const ctx,
47299
const struct uhc_event *const event)
48300
{
@@ -71,6 +323,11 @@ static void dev_connected_handler(struct usbh_contex *const ctx,
71323
if (usbh_device_init(ctx->root)) {
72324
LOG_ERR("Failed to reset new USB device");
73325
}
326+
327+
/* Now only consider about one device connected (root device) */
328+
if (usbh_match_classes(ctx, ctx->root)) {
329+
LOG_ERR("Failed to match classes");
330+
}
74331
}
75332

76333
static void dev_removed_handler(struct usbh_contex *const ctx)
@@ -182,6 +439,41 @@ static void usbh_thread(void *p1, void *p2, void *p3)
182439
}
183440
}
184441

442+
/**
443+
* @brief Auto-register all compile-time defined class drivers
444+
*/
445+
static int usbh_register_all_classes(struct usbh_contex *uhs_ctx)
446+
{
447+
int registered_count = 0;
448+
449+
STRUCT_SECTION_FOREACH(usbh_class_data, cdata) {
450+
/* Check if already registered */
451+
struct usbh_class_data *existing;
452+
bool already_registered = false;
453+
454+
SYS_SLIST_FOR_EACH_CONTAINER(&uhs_ctx->registered_classes, existing, node) {
455+
if (existing == cdata) {
456+
already_registered = true;
457+
break;
458+
}
459+
}
460+
461+
if (!already_registered) {
462+
sys_slist_append(&uhs_ctx->registered_classes, &cdata->node);
463+
registered_count++;
464+
LOG_DBG("Auto-registered class: %s", cdata->name);
465+
}
466+
}
467+
468+
LOG_INF("Auto-registered %d classes to controller %s",
469+
registered_count, uhs_ctx->name);
470+
471+
return 0;
472+
}
473+
474+
/**
475+
* @brief Initialize USB host controller and class drivers
476+
*/
185477
int usbh_init_device_intl(struct usbh_contex *const uhs_ctx)
186478
{
187479
int ret;
@@ -193,13 +485,25 @@ int usbh_init_device_intl(struct usbh_contex *const uhs_ctx)
193485
}
194486

195487
sys_dlist_init(&uhs_ctx->udevs);
488+
sys_slist_init(&uhs_ctx->registered_classes);
196489

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;
490+
/* Register all class drivers */
491+
ret = usbh_register_all_classes(uhs_ctx);
492+
if (ret != 0) {
493+
LOG_WRN("Failed to auto-register classes");
494+
}
495+
496+
/* Initialize registered class drivers */
497+
struct usbh_class_data *cdata;
498+
SYS_SLIST_FOR_EACH_CONTAINER(&uhs_ctx->registered_classes, cdata, node) {
499+
if (cdata->api && cdata->api->init) {
500+
ret = cdata->api->init(uhs_ctx, cdata);
501+
if (ret != 0) {
502+
LOG_WRN("Failed to init class driver %s", cdata->name);
503+
} else {
504+
LOG_INF("Class driver %s initialized", cdata->name);
505+
}
506+
}
203507
}
204508

205509
return 0;
@@ -226,4 +530,4 @@ static int uhs_pre_init(void)
226530
return 0;
227531
}
228532

229-
SYS_INIT(uhs_pre_init, POST_KERNEL, CONFIG_USBH_INIT_PRIO);
533+
SYS_INIT(uhs_pre_init, POST_KERNEL, CONFIG_USBH_INIT_PRIO);

0 commit comments

Comments
 (0)