@@ -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+
46298static 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
76333static 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+ */
185477int 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