Open FFBoard
Open source force feedback firmware
usbh.c
Go to the documentation of this file.
1/*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2019 Ha Thach (tinyusb.org)
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 *
24 * This file is part of the TinyUSB stack.
25 */
26
27#include "tusb_option.h"
28
29#if CFG_TUH_ENABLED
30
31#include "host/hcd.h"
32#include "tusb.h"
33#include "host/usbh_pvt.h"
34#include "hub.h"
35
36//--------------------------------------------------------------------+
37// USBH Configuration
38//--------------------------------------------------------------------+
39#ifndef CFG_TUH_TASK_QUEUE_SZ
40 #define CFG_TUH_TASK_QUEUE_SZ 16
41#endif
42
43#ifndef CFG_TUH_INTERFACE_MAX
44 #define CFG_TUH_INTERFACE_MAX 8
45#endif
46
47//--------------------------------------------------------------------+
48// Weak stubs: invoked if no strong implementation is available
49//--------------------------------------------------------------------+
50TU_ATTR_WEAK bool hcd_deinit(uint8_t rhport) {
51 (void) rhport;
52 return false;
53}
54
55TU_ATTR_WEAK bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) {
56 (void) rhport;
57 (void) cfg_id;
58 (void) cfg_param;
59 return false;
60}
61
62TU_ATTR_WEAK void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) {
63 (void) rhport;
64 (void) eventid;
65 (void) in_isr;
66}
67
68//--------------------------------------------------------------------+
69// USBH-HCD common data structure
70//--------------------------------------------------------------------+
71typedef struct {
72 // port
73 uint8_t rhport;
74 uint8_t hub_addr;
75 uint8_t hub_port;
76
78 uint8_t speed : 4; // packed speed to save footprint
79 volatile uint8_t enumerating : 1; // enumeration is in progress, false if not connected or all interfaces are configured
80 uint8_t TU_RESERVED : 3;
81 };
83
84typedef struct {
85 // port, must be same layout as usbh_dev0_t
86 uint8_t rhport;
87 uint8_t hub_addr;
88 uint8_t hub_port;
89 uint8_t speed;
90
91 // Device State
93 volatile uint8_t connected : 1; // After 1st transfer
94 volatile uint8_t addressed : 1; // After SET_ADDR
95 volatile uint8_t configured : 1; // After SET_CONFIG and all drivers are configured
96 volatile uint8_t suspended : 1; // Bus suspended
97
98 // volatile uint8_t removing : 1; // Physically disconnected, waiting to be processed by usbh
99 };
100
101 // Device Descriptor
102 uint8_t ep0_size;
103
104 uint16_t vid;
105 uint16_t pid;
106
108 uint8_t i_product;
109 uint8_t i_serial;
110
111 // Configuration Descriptor
112 // uint8_t interface_count; // bNumInterfaces alias
113
114 // Endpoint & Interface
115 uint8_t itf2drv[CFG_TUH_INTERFACE_MAX]; // map interface number to driver (0xff is invalid)
116 uint8_t ep2drv[CFG_TUH_ENDPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each
117
118 tu_edpt_state_t ep_status[CFG_TUH_ENDPOINT_MAX][2];
119
120#if CFG_TUH_API_EDPT_XFER
121 // TODO array can be CFG_TUH_ENDPOINT_MAX-1
122 struct {
124 uintptr_t user_data;
125 }ep_callback[CFG_TUH_ENDPOINT_MAX][2];
126#endif
127
129
130//--------------------------------------------------------------------+
131// MACRO CONSTANT TYPEDEF
132//--------------------------------------------------------------------+
133#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL
134 #define DRIVER_NAME(_name) _name
135#else
136 #define DRIVER_NAME(_name) NULL
137#endif
138
140 #if CFG_TUH_CDC
141 {
142 .name = DRIVER_NAME("CDC"),
143 .init = cdch_init,
144 .deinit = cdch_deinit,
145 .open = cdch_open,
146 .set_config = cdch_set_config,
147 .xfer_cb = cdch_xfer_cb,
148 .close = cdch_close
149 },
150 #endif
151
152 #if CFG_TUH_MSC
153 {
154 .name = DRIVER_NAME("MSC"),
155 .init = msch_init,
156 .deinit = msch_deinit,
157 .open = msch_open,
158 .set_config = msch_set_config,
159 .xfer_cb = msch_xfer_cb,
160 .close = msch_close
161 },
162 #endif
163
164 #if CFG_TUH_HID
165 {
166 .name = DRIVER_NAME("HID"),
167 .init = hidh_init,
168 .deinit = hidh_deinit,
169 .open = hidh_open,
170 .set_config = hidh_set_config,
171 .xfer_cb = hidh_xfer_cb,
172 .close = hidh_close
173 },
174 #endif
175
176 #if CFG_TUH_HUB
177 {
178 .name = DRIVER_NAME("HUB"),
179 .init = hub_init,
180 .deinit = hub_deinit,
181 .open = hub_open,
182 .set_config = hub_set_config,
183 .xfer_cb = hub_xfer_cb,
184 .close = hub_close
185 },
186 #endif
187
188 #if CFG_TUH_VENDOR
189 {
190 .name = DRIVER_NAME("VENDOR"),
191 .init = cush_init,
192 .deinit = cush_deinit,
193 .open = cush_open,
194 .set_config = cush_set_config,
195 .xfer_cb = cush_isr,
196 .close = cush_close
197 }
198 #endif
199};
200
201enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(usbh_class_drivers) };
202enum { CONFIG_NUM = 1 }; // default to use configuration 1
203
204// Additional class drivers implemented by application
205tu_static usbh_class_driver_t const * _app_driver = NULL;
206tu_static uint8_t _app_driver_count = 0;
207
208#define TOTAL_DRIVER_COUNT (_app_driver_count + BUILTIN_DRIVER_COUNT)
209
210static inline usbh_class_driver_t const *get_driver(uint8_t drv_id) {
211 usbh_class_driver_t const *driver = NULL;
212
213 if ( drv_id < _app_driver_count ) {
214 driver = &_app_driver[drv_id];
215 } else if ( drv_id < TOTAL_DRIVER_COUNT && BUILTIN_DRIVER_COUNT > 0) {
216 driver = &usbh_class_drivers[drv_id - _app_driver_count];
217 }
218
219 return driver;
220}
221
222//--------------------------------------------------------------------+
223// INTERNAL OBJECT & FUNCTION DECLARATION
224//--------------------------------------------------------------------+
225
226// sum of end device + hub
227#define TOTAL_DEVICES (CFG_TUH_DEVICE_MAX + CFG_TUH_HUB)
228
230
231// Device with address = 0 for enumeration
233
234// all devices excluding zero-address
235// hub address start from CFG_TUH_DEVICE_MAX+1
236// TODO: hub can has its own simpler struct to save memory
237static usbh_device_t _usbh_devices[TOTAL_DEVICES];
238
239// Mutex for claiming endpoint
240#if OSAL_MUTEX_REQUIRED
243#else
244 #define _usbh_mutex NULL
245#endif
246
247// Event queue
248// usbh_int_set is used as mutex in OS NONE config
249OSAL_QUEUE_DEF(usbh_int_set, _usbh_qdef, CFG_TUH_TASK_QUEUE_SZ, hcd_event_t);
251
252CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN
253static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE];
254
255// Control transfers: since most controllers do not support multiple control transfers
256// on multiple devices concurrently and control transfers are not used much except for
257// enumeration, we will only execute control transfers one at a time.
258CFG_TUH_MEM_SECTION struct {
260 uint8_t* buffer;
262 uintptr_t user_data;
263
264 uint8_t daddr;
265 volatile uint8_t stage;
266 volatile uint16_t actual_len;
268
269//------------- Helper Function -------------//
270
271TU_ATTR_ALWAYS_INLINE static inline usbh_device_t* get_device(uint8_t dev_addr) {
272 TU_VERIFY(dev_addr > 0 && dev_addr <= TOTAL_DEVICES, NULL);
273 return &_usbh_devices[dev_addr-1];
274}
275
276static bool enum_new_device(hcd_event_t* event);
277static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port);
278static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size);
279static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
280
281TU_ATTR_ALWAYS_INLINE static inline bool queue_event(hcd_event_t const * event, bool in_isr) {
282 TU_ASSERT(osal_queue_send(_usbh_q, event, in_isr));
283 tuh_event_hook_cb(event->rhport, event->event_id, in_isr);
284 return true;
285}
286
287//--------------------------------------------------------------------+
288// Device API
289//--------------------------------------------------------------------+
290
291bool tuh_mounted(uint8_t dev_addr) {
293 TU_VERIFY(dev);
294 return dev->configured;
295}
296
297bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t *vid, uint16_t *pid) {
298 *vid = *pid = 0;
299
300 usbh_device_t const *dev = get_device(dev_addr);
301 TU_VERIFY(dev && dev->addressed && dev->vid != 0);
302
303 *vid = dev->vid;
304 *pid = dev->pid;
305
306 return true;
307}
308
311 return (tusb_speed_t) (dev ? get_device(dev_addr)->speed : _dev0.speed);
312}
313
314bool tuh_rhport_is_active(uint8_t rhport) {
315 return _usbh_controller == rhport;
316}
317
318bool tuh_rhport_reset_bus(uint8_t rhport, bool active) {
319 TU_VERIFY(tuh_rhport_is_active(rhport));
320 if ( active ) {
321 hcd_port_reset(rhport);
322 } else {
323 hcd_port_reset_end(rhport);
324 }
325 return true;
326}
327
328//--------------------------------------------------------------------+
329// PUBLIC API (Parameter Verification is required)
330//--------------------------------------------------------------------+
331
332bool tuh_configure(uint8_t rhport, uint32_t cfg_id, const void *cfg_param) {
333 return hcd_configure(rhport, cfg_id, cfg_param);
334}
335
336static void clear_device(usbh_device_t* dev) {
337 tu_memclr(dev, sizeof(usbh_device_t));
338 memset(dev->itf2drv, TUSB_INDEX_INVALID_8, sizeof(dev->itf2drv)); // invalid mapping
339 memset(dev->ep2drv , TUSB_INDEX_INVALID_8, sizeof(dev->ep2drv )); // invalid mapping
340}
341
342bool tuh_inited(void) {
344}
345
346bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
347 if (tuh_rhport_is_active(rhport)) {
348 return true; // skip if already initialized
349 }
350
351 TU_LOG_USBH("USBH init on controller %u, speed = %s\r\n", rhport,
352 rh_init->speed == TUSB_SPEED_HIGH ? "High" : "Full");
353
354 // Init host stack if not already
355 if (!tuh_inited()) {
356 TU_LOG_INT_USBH(sizeof(usbh_device_t));
357 TU_LOG_INT_USBH(sizeof(hcd_event_t));
358 TU_LOG_INT_USBH(sizeof(_ctrl_xfer));
359 TU_LOG_INT_USBH(sizeof(tuh_xfer_t));
360 TU_LOG_INT_USBH(sizeof(tu_fifo_t));
361 TU_LOG_INT_USBH(sizeof(tu_edpt_stream_t));
362
363 // Event queue
364 _usbh_q = osal_queue_create(&_usbh_qdef);
365 TU_ASSERT(_usbh_q != NULL);
366
367#if OSAL_MUTEX_REQUIRED
368 // Init mutex
370 TU_ASSERT(_usbh_mutex);
371#endif
372
373 // Get application driver if available
376 }
377
378 // Device
379 tu_memclr(&_dev0, sizeof(_dev0));
380 tu_memclr(_usbh_devices, sizeof(_usbh_devices));
381 tu_memclr(&_ctrl_xfer, sizeof(_ctrl_xfer));
382
383 for (uint8_t i = 0; i < TOTAL_DEVICES; i++) {
385 }
386
387 // Class drivers
388 for (uint8_t drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) {
389 usbh_class_driver_t const* driver = get_driver(drv_id);
390 if (driver) {
391 TU_LOG_USBH("%s init\r\n", driver->name);
392 driver->init();
393 }
394 }
395 }
396
397 // Init host controller
398 _usbh_controller = rhport;
399 TU_ASSERT(hcd_init(rhport, rh_init));
400 hcd_int_enable(rhport);
401
402 return true;
403}
404
405bool tuh_deinit(uint8_t rhport) {
406 if (!tuh_rhport_is_active(rhport)) return true;
407
408 // deinit host controller
409 hcd_int_disable(rhport);
410 hcd_deinit(rhport);
412
413 // "unplug" all devices on this rhport (hub_addr = 0, hub_port = 0)
414 process_removing_device(rhport, 0, 0);
415
416 // deinit host stack if no controller is active
417 if (!tuh_inited()) {
418 // Class drivers
419 for (uint8_t drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) {
420 usbh_class_driver_t const* driver = get_driver(drv_id);
421 if (driver && driver->deinit) {
422 TU_LOG_USBH("%s deinit\r\n", driver->name);
423 driver->deinit();
424 }
425 }
426
428 _usbh_q = NULL;
429
430 #if OSAL_MUTEX_REQUIRED
431 // TODO make sure there is no task waiting on this mutex
433 _usbh_mutex = NULL;
434 #endif
435 }
436
437 return true;
438}
439
441 if (!tuh_inited()) {
442 return false; // Skip if stack is not initialized
443 }
444 return !osal_queue_empty(_usbh_q);
445}
446
447/* USB Host Driver task
448 * This top level thread manages all host controller event and delegates events to class-specific drivers.
449 * This should be called periodically within the mainloop or rtos thread.
450 *
451 @code
452 int main(void)
453 {
454 application_init();
455 tusb_init(0, TUSB_ROLE_HOST);
456
457 while(1) // the mainloop
458 {
459 application_code();
460 tuh_task(); // tinyusb host task
461 }
462 }
463 @endcode
464 */
465void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
466 (void) in_isr; // not implemented yet
467
468 // Skip if stack is not initialized
469 if (!tuh_inited()) return;
470
471 // Loop until there is no more events in the queue
472 while (1) {
473 hcd_event_t event;
474 if (!osal_queue_receive(_usbh_q, &event, timeout_ms)) return;
475
476 switch (event.event_id) {
477 case HCD_EVENT_DEVICE_ATTACH:
478 // due to the shared _usbh_ctrl_buf, we must complete enumerating one device before enumerating another one.
479 // TODO better to have an separated queue for newly attached devices
480 if (_dev0.enumerating) {
481 // Some device can cause multiple duplicated attach events
482 // drop current enumerating and start over for a proper port reset
483 if (event.rhport == _dev0.rhport && event.connection.hub_addr == _dev0.hub_addr &&
485 // abort/cancel current enumeration and start new one
486 TU_LOG1("[%u:] USBH Device Attach (duplicated)\r\n", event.rhport);
488 enum_new_device(&event);
489 } else {
490 TU_LOG_USBH("[%u:] USBH Defer Attach until current enumeration complete\r\n", event.rhport);
491
492 bool is_empty = osal_queue_empty(_usbh_q);
493 queue_event(&event, in_isr);
494
495 if (is_empty) {
496 // Exit if this is the only event in the queue, otherwise we may loop forever
497 return;
498 }
499 }
500 } else {
501 TU_LOG1("[%u:] USBH Device Attach\r\n", event.rhport);
502 _dev0.enumerating = 1;
503 enum_new_device(&event);
504 }
505 break;
506
507 case HCD_EVENT_DEVICE_REMOVE:
508 TU_LOG_USBH("[%u:%u:%u] USBH DEVICE REMOVED\r\n", event.rhport, event.connection.hub_addr, event.connection.hub_port);
510
511 #if CFG_TUH_HUB
512 // TODO remove
513 if (event.connection.hub_addr != 0 && event.connection.hub_port != 0) {
514 // done with hub, waiting for next data on status pipe
516 }
517 #endif
518 break;
519
520 case HCD_EVENT_XFER_COMPLETE: {
521 uint8_t const ep_addr = event.xfer_complete.ep_addr;
522 uint8_t const epnum = tu_edpt_number(ep_addr);
523 uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
524
525 TU_LOG_USBH("on EP %02X with %u bytes: %s\r\n", ep_addr, (unsigned int) event.xfer_complete.len, tu_str_xfer_result[event.xfer_complete.result]);
526
527 if (event.dev_addr == 0) {
528 // device 0 only has control endpoint
529 TU_ASSERT(epnum == 0,);
531 } else {
532 usbh_device_t* dev = get_device(event.dev_addr);
533 TU_VERIFY(dev && dev->connected,);
534
535 dev->ep_status[epnum][ep_dir].busy = 0;
536 dev->ep_status[epnum][ep_dir].claimed = 0;
537
538 if (0 == epnum) {
540 } else {
541 // Prefer application callback over built-in one if available. This occurs when tuh_edpt_xfer() is used
542 // with enabled driver e.g HID endpoint
543 #if CFG_TUH_API_EDPT_XFER
544 tuh_xfer_cb_t const complete_cb = dev->ep_callback[epnum][ep_dir].complete_cb;
545 if ( complete_cb ) {
546 // re-construct xfer info
547 tuh_xfer_t xfer = {
548 .daddr = event.dev_addr,
549 .ep_addr = ep_addr,
550 .result = event.xfer_complete.result,
551 .actual_len = event.xfer_complete.len,
552 .buflen = 0, // not available
553 .buffer = NULL, // not available
554 .complete_cb = complete_cb,
555 .user_data = dev->ep_callback[epnum][ep_dir].user_data
556 };
558 }else
559 #endif
560 {
561 uint8_t drv_id = dev->ep2drv[epnum][ep_dir];
562 usbh_class_driver_t const* driver = get_driver(drv_id);
563 if (driver) {
564 TU_LOG_USBH("%s xfer callback\r\n", driver->name);
565 driver->xfer_cb(event.dev_addr, ep_addr, (xfer_result_t) event.xfer_complete.result,
566 event.xfer_complete.len);
567 } else {
568 // no driver/callback responsible for this transfer
569 TU_ASSERT(false,);
570 }
571 }
572 }
573 }
574 break;
575 }
576
577 case USBH_EVENT_FUNC_CALL:
578 if (event.func_call.func) event.func_call.func(event.func_call.param);
579 break;
580
581 default:
582 break;
583 }
584
585#if CFG_TUSB_OS != OPT_OS_NONE && CFG_TUSB_OS != OPT_OS_PICO
586 // return if there is no more events, for application to run other background
587 if (osal_queue_empty(_usbh_q)) return;
588#endif
589 }
590}
591
592//--------------------------------------------------------------------+
593// Control transfer
594//--------------------------------------------------------------------+
595
597 // update result
598 *((xfer_result_t*) xfer->user_data) = xfer->result;
599}
600
601// TODO timeout_ms is not supported yet
603 // EP0 with setup packet
604 TU_VERIFY(xfer->ep_addr == 0 && xfer->setup);
605
606 // Check if device is still connected (enumerating for dev0)
607 const uint8_t daddr = xfer->daddr;
608 if (daddr == 0) {
609 TU_VERIFY(_dev0.enumerating);
610 } else {
611 const usbh_device_t* dev = get_device(daddr);
612 TU_VERIFY(dev && dev->connected);
613 }
614
615 // pre-check to help reducing mutex lock
616 TU_VERIFY(_ctrl_xfer.stage == CONTROL_STAGE_IDLE);
617 (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
618
619 bool const is_idle = (_ctrl_xfer.stage == CONTROL_STAGE_IDLE);
620 if (is_idle) {
622 _ctrl_xfer.daddr = daddr;
623 _ctrl_xfer.actual_len = 0;
624
625 _ctrl_xfer.request = (*xfer->setup);
626 _ctrl_xfer.buffer = xfer->buffer;
627 _ctrl_xfer.complete_cb = xfer->complete_cb;
628 _ctrl_xfer.user_data = xfer->user_data;
629 }
630
632
633 TU_VERIFY(is_idle);
634 const uint8_t rhport = usbh_get_rhport(daddr);
635
636 TU_LOG_USBH("[%u:%u] %s: ", rhport, daddr,
637 (xfer->setup->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && xfer->setup->bRequest <= TUSB_REQ_SYNCH_FRAME) ?
638 tu_str_std_request[xfer->setup->bRequest] : "Class Request");
639 TU_LOG_BUF_USBH(xfer->setup, 8);
640
641 if (xfer->complete_cb) {
642 TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t const*) &_ctrl_xfer.request) );
643 }else {
644 // blocking if complete callback is not provided
645 // change callback to internal blocking, and result as user argument
646 volatile xfer_result_t result = XFER_RESULT_INVALID;
647
648 // use user_data to point to xfer_result_t
649 _ctrl_xfer.user_data = (uintptr_t) &result;
651
652 TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t*) &_ctrl_xfer.request) );
653
654 while (result == XFER_RESULT_INVALID) {
655 // Note: this can be called within an callback ie. part of tuh_task()
656 // therefore event with RTOS tuh_task() still need to be invoked
657 if (tuh_task_event_ready()) {
658 tuh_task();
659 }
660 // TODO probably some timeout to prevent hanged
661 }
662
663 // update transfer result, user_data is expected to point to xfer_result_t
664 if (xfer->user_data != 0) {
665 *((xfer_result_t*) xfer->user_data) = result;
666 }
667 xfer->result = result;
668 xfer->actual_len = _ctrl_xfer.actual_len;
669 }
670
671 return true;
672}
673
674TU_ATTR_ALWAYS_INLINE static inline void _set_control_xfer_stage(uint8_t stage) {
675 (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
676 _ctrl_xfer.stage = stage;
678}
679
680static void _control_xfer_complete(uint8_t daddr, xfer_result_t result) {
681 TU_LOG_USBH("\r\n");
682
683 // duplicate xfer since user can execute control transfer within callback
685 tuh_xfer_t xfer_temp = {
686 .daddr = daddr,
687 .ep_addr = 0,
688 .result = result,
689 .setup = &request,
690 .actual_len = (uint32_t) _ctrl_xfer.actual_len,
691 .buffer = _ctrl_xfer.buffer,
692 .complete_cb = _ctrl_xfer.complete_cb,
693 .user_data = _ctrl_xfer.user_data
694 };
695
697
698 if (xfer_temp.complete_cb) {
699 xfer_temp.complete_cb(&xfer_temp);
700 }
701}
702
703static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
704 (void) ep_addr;
705
706 const uint8_t rhport = usbh_get_rhport(daddr);
707 tusb_control_request_t const * request = &_ctrl_xfer.request;
708
709 if (XFER_RESULT_SUCCESS != result) {
710 TU_LOG_USBH("[%u:%u] Control %s, xferred_bytes = %" PRIu32 "\r\n", rhport, daddr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED", xferred_bytes);
711 TU_LOG_BUF_USBH(request, 8);
712
713 // terminate transfer if any stage failed
715 }else {
716 switch(_ctrl_xfer.stage) {
718 if (request->wLength) {
719 // DATA stage: initial data toggle is always 1
721 TU_ASSERT( hcd_edpt_xfer(rhport, daddr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.buffer, request->wLength) );
722 return true;
723 }
724 TU_ATTR_FALLTHROUGH;
725
727 if (request->wLength) {
728 TU_LOG_USBH("[%u:%u] Control data:\r\n", rhport, daddr);
729 TU_LOG_MEM_USBH(_ctrl_xfer.buffer, xferred_bytes, 2);
730 }
731
732 _ctrl_xfer.actual_len = (uint16_t) xferred_bytes;
733
734 // ACK stage: toggle is always 1
736 TU_ASSERT( hcd_edpt_xfer(rhport, daddr, tu_edpt_addr(0, 1 - request->bmRequestType_bit.direction), NULL, 0) );
737 break;
738
739 case CONTROL_STAGE_ACK: {
740 // Abort all pending transfers if SET_CONFIGURATION request
741 // NOTE: should we force closing all non-control endpoints in the future?
743 for(uint8_t epnum=1; epnum<CFG_TUH_ENDPOINT_MAX; epnum++) {
744 for(uint8_t dir=0; dir<2; dir++) {
746 }
747 }
748 }
749
751 break;
752 }
753
754 default: return false;
755 }
756 }
757
758 return true;
759}
760
761//--------------------------------------------------------------------+
762//
763//--------------------------------------------------------------------+
764
766 uint8_t const daddr = xfer->daddr;
767 uint8_t const ep_addr = xfer->ep_addr;
768
769 TU_VERIFY(daddr && ep_addr);
770 TU_VERIFY(usbh_edpt_claim(daddr, ep_addr));
771
772 if (!usbh_edpt_xfer_with_callback(daddr, ep_addr, xfer->buffer, (uint16_t) xfer->buflen,
773 xfer->complete_cb, xfer->user_data)) {
774 usbh_edpt_release(daddr, ep_addr);
775 return false;
776 }
777
778 return true;
779}
780
781bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr) {
782 TU_LOG_USBH("[%u] Aborted transfer on EP %02X\r\n", daddr, ep_addr);
783
784 const uint8_t epnum = tu_edpt_number(ep_addr);
785 const uint8_t dir = tu_edpt_dir(ep_addr);
786
787 if (epnum == 0) {
788 // Also include dev0 for aborting enumerating
789 const uint8_t rhport = usbh_get_rhport(daddr);
790
791 // control transfer: only 1 control at a time, check if we are aborting the current one
792 TU_VERIFY(daddr == _ctrl_xfer.daddr && _ctrl_xfer.stage != CONTROL_STAGE_IDLE);
793 hcd_edpt_abort_xfer(rhport, daddr, ep_addr);
794 _set_control_xfer_stage(CONTROL_STAGE_IDLE); // reset control transfer state to idle
795 } else {
797 TU_VERIFY(dev);
798
799 TU_VERIFY(dev->ep_status[epnum][dir].busy); // non-control skip if not busy
800 hcd_edpt_abort_xfer(dev->rhport, daddr, ep_addr);
801
802 // mark as ready and release endpoint if transfer is aborted
803 dev->ep_status[epnum][dir].busy = false;
804 tu_edpt_release(&dev->ep_status[epnum][dir], _usbh_mutex);
805 }
806
807 return true;
808}
809
810//--------------------------------------------------------------------+
811// USBH API For Class Driver
812//--------------------------------------------------------------------+
813
814uint8_t usbh_get_rhport(uint8_t dev_addr) {
816 return dev ? dev->rhport : _dev0.rhport;
817}
818
819uint8_t *usbh_get_enum_buf(void) {
820 return _usbh_ctrl_buf;
821}
822
823void usbh_int_set(bool enabled) {
824 // TODO all host controller if multiple are used since they shared the same event queue
825 if (enabled) {
827 } else {
829 }
830}
831
832void usbh_defer_func(osal_task_func_t func, void *param, bool in_isr) {
833 hcd_event_t event = { 0 };
834 event.event_id = USBH_EVENT_FUNC_CALL;
835 event.func_call.func = func;
836 event.func_call.param = param;
837
838 queue_event(&event, in_isr);
839}
840
841//--------------------------------------------------------------------+
842// Endpoint API
843//--------------------------------------------------------------------+
844
845// Claim an endpoint for transfer
846bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr) {
847 // Note: addr0 only use tuh_control_xfer
849 TU_ASSERT(dev && dev->connected);
850
851 uint8_t const epnum = tu_edpt_number(ep_addr);
852 uint8_t const dir = tu_edpt_dir(ep_addr);
853
854 TU_VERIFY(tu_edpt_claim(&dev->ep_status[epnum][dir], _usbh_mutex));
855 TU_LOG_USBH("[%u] Claimed EP 0x%02x\r\n", dev_addr, ep_addr);
856
857 return true;
858}
859
860// Release an claimed endpoint due to failed transfer attempt
861bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr) {
862 // Note: addr0 only use tuh_control_xfer
864 TU_VERIFY(dev && dev->connected);
865
866 uint8_t const epnum = tu_edpt_number(ep_addr);
867 uint8_t const dir = tu_edpt_dir(ep_addr);
868
869 TU_VERIFY(tu_edpt_release(&dev->ep_status[epnum][dir], _usbh_mutex));
870 TU_LOG_USBH("[%u] Released EP 0x%02x\r\n", dev_addr, ep_addr);
871
872 return true;
873}
874
875// Submit an transfer
876// TODO call usbh_edpt_release if failed
877bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes,
879 (void) complete_cb;
880 (void) user_data;
881
883 TU_VERIFY(dev);
884
885 uint8_t const epnum = tu_edpt_number(ep_addr);
886 uint8_t const dir = tu_edpt_dir(ep_addr);
887 tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir];
888
889 TU_LOG_USBH(" Queue EP %02X with %u bytes ... \r\n", ep_addr, total_bytes);
890
891 // Attempt to transfer on a busy endpoint, sound like an race condition !
892 TU_ASSERT(ep_state->busy == 0);
893
894 // Set busy first since the actual transfer can be complete before hcd_edpt_xfer()
895 // could return and USBH task can preempt and clear the busy
896 ep_state->busy = 1;
897
898#if CFG_TUH_API_EDPT_XFER
899 dev->ep_callback[epnum][dir].complete_cb = complete_cb;
900 dev->ep_callback[epnum][dir].user_data = user_data;
901#endif
902
903 if (hcd_edpt_xfer(dev->rhport, dev_addr, ep_addr, buffer, total_bytes)) {
904 TU_LOG_USBH("OK\r\n");
905 return true;
906 } else {
907 // HCD error, mark endpoint as ready to allow next transfer
908 ep_state->busy = 0;
909 ep_state->claimed = 0;
910 TU_LOG1("Failed\r\n");
911// TU_BREAKPOINT();
912 return false;
913 }
914}
915
916static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size) {
917 TU_LOG_USBH("[%u:%u] Open EP0 with Size = %u\r\n", usbh_get_rhport(dev_addr), dev_addr, max_packet_size);
919 .bLength = sizeof(tusb_desc_endpoint_t),
920 .bDescriptorType = TUSB_DESC_ENDPOINT,
921 .bEndpointAddress = 0,
922 .bmAttributes = { .xfer = TUSB_XFER_CONTROL },
923 .wMaxPacketSize = max_packet_size,
924 .bInterval = 0
925 };
926
928}
929
930bool tuh_edpt_open(uint8_t dev_addr, tusb_desc_endpoint_t const* desc_ep) {
931 TU_ASSERT(tu_edpt_validate(desc_ep, tuh_speed_get(dev_addr)));
933}
934
935bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) {
937 TU_VERIFY(dev);
938
939 uint8_t const epnum = tu_edpt_number(ep_addr);
940 uint8_t const dir = tu_edpt_dir(ep_addr);
941
942 return dev->ep_status[epnum][dir].busy;
943}
944
945//--------------------------------------------------------------------+
946// HCD Event Handler
947//--------------------------------------------------------------------+
948
950 usbh_device_t const* dev = get_device(dev_addr);
951 if (dev) {
952 devtree_info->rhport = dev->rhport;
953 devtree_info->hub_addr = dev->hub_addr;
954 devtree_info->hub_port = dev->hub_port;
955 devtree_info->speed = dev->speed;
956 } else {
957 devtree_info->rhport = _dev0.rhport;
958 devtree_info->hub_addr = _dev0.hub_addr;
959 devtree_info->hub_port = _dev0.hub_port;
960 devtree_info->speed = _dev0.speed;
961 }
962}
963
964TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr) {
965 switch (event->event_id) {
966 case HCD_EVENT_DEVICE_REMOVE:
967 // FIXME device remove from a hub need an HCD API for hcd to free up endpoint
968 // mark device as removing to prevent further xfer before the event is processed in usbh task
969
970 // Check if dev0 is removed
971 if ((event->rhport == _dev0.rhport) && (event->connection.hub_addr == _dev0.hub_addr) &&
972 (event->connection.hub_port == _dev0.hub_port)) {
973 _dev0.enumerating = 0;
974 }
975 break;
976
977 default: break;
978 }
979
980 queue_event(event, in_isr);
981}
982
983//--------------------------------------------------------------------+
984// Descriptors Async
985//--------------------------------------------------------------------+
986
987// generic helper to get a descriptor
988// if blocking, user_data is pointed to xfer_result
989static bool _get_descriptor(uint8_t daddr, uint8_t type, uint8_t index, uint16_t language_id, void* buffer, uint16_t len,
993 .recipient = TUSB_REQ_RCPT_DEVICE,
995 .direction = TUSB_DIR_IN
996 },
997 .bRequest = TUSB_REQ_GET_DESCRIPTOR,
998 .wValue = tu_htole16( TU_U16(type, index) ),
999 .wIndex = tu_htole16(language_id),
1000 .wLength = tu_htole16(len)
1001 };
1002 tuh_xfer_t xfer = {
1003 .daddr = daddr,
1004 .ep_addr = 0,
1005 .setup = &request,
1006 .buffer = buffer,
1007 .complete_cb = complete_cb,
1008 .user_data = user_data
1009 };
1010
1011 return tuh_control_xfer(&xfer);
1012}
1013
1014bool tuh_descriptor_get(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len,
1015 tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
1016 return _get_descriptor(daddr, type, index, 0x0000, buffer, len, complete_cb, user_data);
1017}
1018
1019bool tuh_descriptor_get_device(uint8_t daddr, void* buffer, uint16_t len,
1020 tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
1021 len = tu_min16(len, sizeof(tusb_desc_device_t));
1023}
1024
1025bool tuh_descriptor_get_configuration(uint8_t daddr, uint8_t index, void* buffer, uint16_t len,
1026 tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
1028}
1029
1030//------------- String Descriptor -------------//
1031
1032bool tuh_descriptor_get_string(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len,
1033 tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
1034 return _get_descriptor(daddr, TUSB_DESC_STRING, index, language_id, buffer, len, complete_cb, user_data);
1035}
1036
1037// Get manufacturer string descriptor
1038bool tuh_descriptor_get_manufacturer_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
1040{
1041 usbh_device_t const* dev = get_device(daddr);
1042 TU_VERIFY(dev && dev->i_manufacturer);
1043 return tuh_descriptor_get_string(daddr, dev->i_manufacturer, language_id, buffer, len, complete_cb, user_data);
1044}
1045
1046// Get product string descriptor
1047bool tuh_descriptor_get_product_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
1048 tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
1049 usbh_device_t const* dev = get_device(daddr);
1050 TU_VERIFY(dev && dev->i_product);
1051 return tuh_descriptor_get_string(daddr, dev->i_product, language_id, buffer, len, complete_cb, user_data);
1052}
1053
1054// Get serial string descriptor
1055bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
1056 tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
1057 usbh_device_t const* dev = get_device(daddr);
1058 TU_VERIFY(dev && dev->i_serial);
1059 return tuh_descriptor_get_string(daddr, dev->i_serial, language_id, buffer, len, complete_cb, user_data);
1060}
1061
1062// Get HID report descriptor
1063// if blocking, user_data is pointed to xfer_result
1064bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len,
1065 tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
1066 TU_LOG_USBH("HID Get Report Descriptor\r\n");
1069 .recipient = TUSB_REQ_RCPT_INTERFACE,
1070 .type = TUSB_REQ_TYPE_STANDARD,
1071 .direction = TUSB_DIR_IN
1072 },
1073 .bRequest = TUSB_REQ_GET_DESCRIPTOR,
1074 .wValue = tu_htole16(TU_U16(desc_type, index)),
1075 .wIndex = tu_htole16((uint16_t) itf_num),
1076 .wLength = len
1077 };
1078 tuh_xfer_t xfer = {
1079 .daddr = daddr,
1080 .ep_addr = 0,
1081 .setup = &request,
1082 .buffer = buffer,
1083 .complete_cb = complete_cb,
1084 .user_data = user_data
1085 };
1086
1087 return tuh_control_xfer(&xfer);
1088}
1089
1090bool tuh_configuration_set(uint8_t daddr, uint8_t config_num,
1091 tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
1092 TU_LOG_USBH("Set Configuration = %d\r\n", config_num);
1095 .recipient = TUSB_REQ_RCPT_DEVICE,
1096 .type = TUSB_REQ_TYPE_STANDARD,
1097 .direction = TUSB_DIR_OUT
1098 },
1099 .bRequest = TUSB_REQ_SET_CONFIGURATION,
1100 .wValue = tu_htole16(config_num),
1101 .wIndex = 0,
1102 .wLength = 0
1103 };
1104 tuh_xfer_t xfer = {
1105 .daddr = daddr,
1106 .ep_addr = 0,
1107 .setup = &request,
1108 .buffer = NULL,
1109 .complete_cb = complete_cb,
1110 .user_data = user_data
1111 };
1112
1113 return tuh_control_xfer(&xfer);
1114}
1115
1116bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt,
1117 tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
1118 TU_LOG_USBH("Set Interface %u Alternate %u\r\n", itf_num, itf_alt);
1121 .recipient = TUSB_REQ_RCPT_INTERFACE,
1122 .type = TUSB_REQ_TYPE_STANDARD,
1123 .direction = TUSB_DIR_OUT
1124 },
1125 .bRequest = TUSB_REQ_SET_INTERFACE,
1126 .wValue = tu_htole16(itf_alt),
1127 .wIndex = tu_htole16(itf_num),
1128 .wLength = 0
1129 };
1130 tuh_xfer_t xfer = {
1131 .daddr = daddr,
1132 .ep_addr = 0,
1133 .setup = &request,
1134 .buffer = NULL,
1135 .complete_cb = complete_cb,
1136 .user_data = user_data
1137 };
1138
1139 return tuh_control_xfer(&xfer);
1140}
1141
1142//--------------------------------------------------------------------+
1143// Descriptor Sync
1144//--------------------------------------------------------------------+
1145
1146#define _CONTROL_SYNC_API(_async_func, ...) \
1147 xfer_result_t result = XFER_RESULT_INVALID;\
1148 TU_VERIFY(_async_func(__VA_ARGS__, NULL, (uintptr_t) &result), XFER_RESULT_TIMEOUT); \
1149 return (uint8_t) result
1150
1151uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index,
1152 void* buffer, uint16_t len) {
1153 _CONTROL_SYNC_API(tuh_descriptor_get, daddr, type, index, buffer, len);
1154}
1155
1156uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len) {
1157 _CONTROL_SYNC_API(tuh_descriptor_get_device, daddr, buffer, len);
1158}
1159
1160uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index,
1161 void* buffer, uint16_t len) {
1162 _CONTROL_SYNC_API(tuh_descriptor_get_configuration, daddr, index, buffer, len);
1163}
1164
1165uint8_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index,
1166 void* buffer, uint16_t len) {
1167 _CONTROL_SYNC_API(tuh_descriptor_get_hid_report, daddr, itf_num, desc_type, index, buffer, len);
1168}
1169
1170uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id,
1171 void* buffer, uint16_t len) {
1172 _CONTROL_SYNC_API(tuh_descriptor_get_string, daddr, index, language_id, buffer, len);
1173}
1174
1175uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id,
1176 void* buffer, uint16_t len) {
1177 _CONTROL_SYNC_API(tuh_descriptor_get_manufacturer_string, daddr, language_id, buffer, len);
1178}
1179
1180uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id,
1181 void* buffer, uint16_t len) {
1182 _CONTROL_SYNC_API(tuh_descriptor_get_product_string, daddr, language_id, buffer, len);
1183}
1184
1185uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id,
1186 void* buffer, uint16_t len) {
1187 _CONTROL_SYNC_API(tuh_descriptor_get_serial_string, daddr, language_id, buffer, len);
1188}
1189
1190//--------------------------------------------------------------------+
1191// Detaching
1192//--------------------------------------------------------------------+
1193
1194TU_ATTR_ALWAYS_INLINE static inline bool is_hub_addr(uint8_t daddr) {
1195 return (CFG_TUH_HUB > 0) && (daddr > CFG_TUH_DEVICE_MAX);
1196}
1197
1198//static void mark_removing_device_isr(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) {
1199// for (uint8_t dev_id = 0; dev_id < TOTAL_DEVICES; dev_id++) {
1200// usbh_device_t *dev = &_usbh_devices[dev_id];
1201// uint8_t const daddr = dev_id + 1;
1202//
1203// // hub_addr = 0 means roothub, hub_port = 0 means all devices of downstream hub
1204// if (dev->rhport == rhport && dev->connected &&
1205// (hub_addr == 0 || dev->hub_addr == hub_addr) &&
1206// (hub_port == 0 || dev->hub_port == hub_port)) {
1207// if (is_hub_addr(daddr)) {
1208// // If the device itself is a usb hub, mark all downstream devices.
1209// // FIXME recursive calls
1210// mark_removing_device_isr(rhport, daddr, 0);
1211// }
1212//
1213// dev->removing = 1;
1214// }
1215// }
1216//}
1217
1218// a device unplugged from rhport:hub_addr:hub_port
1219static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) {
1220 //------------- find the all devices (star-network) under port that is unplugged -------------//
1221 // TODO mark as disconnected in ISR, also handle dev0
1222 uint32_t removing_hubs = 0;
1223 do {
1224 for (uint8_t dev_id = 0; dev_id < TOTAL_DEVICES; dev_id++) {
1225 usbh_device_t* dev = &_usbh_devices[dev_id];
1226 uint8_t const daddr = dev_id + 1;
1227
1228 // hub_addr = 0 means roothub, hub_port = 0 means all devices of downstream hub
1229 if (dev->rhport == rhport && dev->connected &&
1230 (hub_addr == 0 || dev->hub_addr == hub_addr) &&
1231 (hub_port == 0 || dev->hub_port == hub_port)) {
1232 TU_LOG_USBH("[%u:%u:%u] unplugged address = %u\r\n", rhport, hub_addr, hub_port, daddr);
1233
1234 if (is_hub_addr(daddr)) {
1235 TU_LOG_USBH(" is a HUB device %u\r\n", daddr);
1236 removing_hubs |= TU_BIT(dev_id - CFG_TUH_DEVICE_MAX);
1237 } else {
1238 // Invoke callback before closing driver (maybe call it later ?)
1240 }
1241
1242 // Close class driver
1243 for (uint8_t drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) {
1244 usbh_class_driver_t const* driver = get_driver(drv_id);
1245 if (driver) driver->close(daddr);
1246 }
1247
1248 hcd_device_close(rhport, daddr);
1249 clear_device(dev);
1250
1251 // abort on-going control xfer on this device if any
1253 }
1254 }
1255
1256 // if removing a hub, we need to remove its downstream devices
1257 #if CFG_TUH_HUB
1258 if (removing_hubs == 0) break;
1259
1260 // find a marked hub to process
1261 for (uint8_t h_id = 0; h_id < CFG_TUH_HUB; h_id++) {
1262 if (tu_bit_test(removing_hubs, h_id)) {
1263 removing_hubs &= ~TU_BIT(h_id);
1264
1265 // update hub_addr and hub_port for next loop
1266 hub_addr = h_id + 1 + CFG_TUH_DEVICE_MAX;
1267 hub_port = 0;
1268 break;
1269 }
1270 }
1271 #else
1272 (void) removing_hubs;
1273 break;
1274 #endif
1275 } while(1);
1276}
1277
1278//--------------------------------------------------------------------+
1279// Enumeration Process
1280// is a lengthy process with a series of control transfer to configure
1281// newly attached device.
1282// NOTE: due to the shared _usbh_ctrl_buf, we must complete enumerating
1283// one device before enumerating another one.
1284//--------------------------------------------------------------------+
1285
1286enum {
1287 ENUM_RESET_DELAY_MS = 50, // USB specs: 10 to 50ms
1288 ENUM_DEBOUNCING_DELAY_MS = 450, // when plug/unplug a device, physical connection can be bouncing and may
1289 // generate a series of attach/detach event. This delay wait for stable connection
1290};
1291
1292enum {
1294 ENUM_RESET_1, // 1st reset when attached
1295 //ENUM_HUB_GET_STATUS_1,
1298 ENUM_RESET_2, // 2nd reset before set address (not used)
1302
1309
1310static bool enum_request_set_addr(void);
1311static bool _parse_configuration_descriptor (uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg);
1312static void enum_full_complete(void);
1313
1314// process device enumeration
1316 // Retry a few times with transfers in enumeration since device can be unstable when starting up
1317 enum {
1318 ATTEMPT_COUNT_MAX = 3,
1319 ATTEMPT_DELAY_MS = 100
1320 };
1321 static uint8_t failed_count = 0;
1322
1323 if (XFER_RESULT_SUCCESS != xfer->result) {
1324 // retry if not reaching max attempt
1325 bool retry = _dev0.enumerating && (failed_count < ATTEMPT_COUNT_MAX);
1326 if ( retry ) {
1327 failed_count++;
1328 tusb_time_delay_ms_api(ATTEMPT_DELAY_MS); // delay a bit
1329 TU_LOG1("Enumeration attempt %u\r\n", failed_count);
1330 retry = tuh_control_xfer(xfer);
1331 }
1332
1333 if (!retry) {
1335 }
1336
1337 return;
1338 }
1339 failed_count = 0;
1340
1341 uint8_t const daddr = xfer->daddr;
1342 uintptr_t const state = xfer->user_data;
1343
1344 switch (state) {
1345 #if CFG_TUH_HUB
1346 //case ENUM_HUB_GET_STATUS_1: break;
1347
1349 hub_port_status_response_t port_status;
1350 memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t));
1351
1352 if (!port_status.status.connection) {
1353 // device unplugged while delaying, nothing else to do
1355 return;
1356 }
1357
1358 _dev0.speed = (port_status.status.high_speed) ? TUSB_SPEED_HIGH :
1360
1361 // Acknowledge Port Reset Change
1362 if (port_status.change.reset) {
1365 }
1366 break;
1367 }
1368
1373 break;
1374
1376 hub_port_status_response_t port_status;
1377 memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t));
1378
1379 // Acknowledge Port Reset Change if Reset Successful
1380 if (port_status.change.reset) {
1383 }
1384 break;
1385 }
1386 #endif
1387
1389 // TODO probably doesn't need to open/close each enumeration
1390 uint8_t const addr0 = 0;
1391 TU_ASSERT(usbh_edpt_control_open(addr0, 8),);
1392
1393 // Get first 8 bytes of device descriptor for Control Endpoint size
1394 TU_LOG_USBH("Get 8 byte of Device Descriptor\r\n");
1395 TU_ASSERT(tuh_descriptor_get_device(addr0, _usbh_ctrl_buf, 8,
1397 break;
1398 }
1399
1400#if 0
1401 case ENUM_RESET_2:
1402 // TODO not used by now, but may be needed for some devices !?
1403 // Reset device again before Set Address
1404 TU_LOG_USBH("Port reset2 \r\n");
1405 if (_dev0.hub_addr == 0) {
1406 // connected directly to roothub
1408 tusb_time_delay_ms_api(RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since
1409 // sof of controller may not running while resetting
1411 // TODO: fall through to SET ADDRESS, refactor later
1412 }
1413#if CFG_TUH_HUB
1414 else {
1415 // after RESET_DELAY the hub_port_reset() already complete
1418 break;
1419 }
1420#endif
1421 TU_ATTR_FALLTHROUGH;
1422#endif
1423
1424 case ENUM_SET_ADDR:
1426 break;
1427
1428 case ENUM_GET_DEVICE_DESC: {
1429 // Allow 2ms for address recovery time, Ref USB Spec 9.2.6.3
1431
1432 const uint8_t new_addr = (uint8_t) tu_le16toh(xfer->setup->wValue);
1433
1434 usbh_device_t* new_dev = get_device(new_addr);
1435 TU_ASSERT(new_dev,);
1436 new_dev->addressed = 1;
1437
1438 // Close device 0
1440
1441 // open control pipe for new address
1442 TU_ASSERT(usbh_edpt_control_open(new_addr, new_dev->ep0_size),);
1443
1444 // Get full device descriptor
1445 TU_LOG_USBH("Get Device Descriptor\r\n");
1446 TU_ASSERT(tuh_descriptor_get_device(new_addr, _usbh_ctrl_buf, sizeof(tusb_desc_device_t),
1448 break;
1449 }
1450
1452 tusb_desc_device_t const* desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf;
1454 TU_ASSERT(dev,);
1455
1456 dev->vid = desc_device->idVendor;
1457 dev->pid = desc_device->idProduct;
1458 dev->i_manufacturer = desc_device->iManufacturer;
1459 dev->i_product = desc_device->iProduct;
1460 dev->i_serial = desc_device->iSerialNumber;
1461
1462 // if (tuh_attach_cb) tuh_attach_cb((tusb_desc_device_t*) _usbh_ctrl_buf);
1463
1464 // Get 9-byte for total length
1465 uint8_t const config_idx = CONFIG_NUM - 1;
1466 TU_LOG_USBH("Get Configuration[0] Descriptor (9 bytes)\r\n");
1467 TU_ASSERT(tuh_descriptor_get_configuration(daddr, config_idx, _usbh_ctrl_buf, 9,
1469 break;
1470 }
1471
1473 uint8_t const* desc_config = _usbh_ctrl_buf;
1474
1475 // Use offsetof to avoid pointer to the odd/misaligned address
1476 uint16_t const total_len = tu_le16toh(
1477 tu_unaligned_read16(desc_config + offsetof(tusb_desc_configuration_t, wTotalLength)));
1478
1479 // TODO not enough buffer to hold configuration descriptor
1480 TU_ASSERT(total_len <= CFG_TUH_ENUMERATION_BUFSIZE,);
1481
1482 // Get full configuration descriptor
1483 uint8_t const config_idx = CONFIG_NUM - 1;
1484 TU_LOG_USBH("Get Configuration[0] Descriptor\r\n");
1485 TU_ASSERT(tuh_descriptor_get_configuration(daddr, config_idx, _usbh_ctrl_buf, total_len,
1487 break;
1488 }
1489
1490 case ENUM_SET_CONFIG:
1492 break;
1493
1494 case ENUM_CONFIG_DRIVER: {
1495 TU_LOG_USBH("Device configured\r\n");
1497 TU_ASSERT(dev,);
1498
1499 dev->configured = 1;
1500
1501 // Parse configuration & set up drivers
1502 // driver_open() must not make any usb transfer
1504
1505 // Start the Set Configuration process for interfaces (itf = TUSB_INDEX_INVALID_8)
1506 // Since driver can perform control transfer within its set_config, this is done asynchronously.
1507 // The process continue with next interface when class driver complete its sequence with usbh_driver_set_config_complete()
1508 // TODO use separated API instead of using TUSB_INDEX_INVALID_8
1510 break;
1511 }
1512
1513 default:
1514 // stop enumeration if unknown state
1516 break;
1517 }
1518}
1519
1520
1521
1522static bool enum_new_device(hcd_event_t* event) {
1523 _dev0.rhport = event->rhport;
1524 _dev0.hub_addr = event->connection.hub_addr;
1525 _dev0.hub_port = event->connection.hub_port;
1526
1527 if (_dev0.hub_addr == 0) {
1528 // connected directly to roothub
1530
1531 // Since we are in middle of rhport reset, frame number is not available yet.
1532 // need to depend on tusb_time_millis_api()
1534
1536
1537 // wait until device connection is stable TODO non blocking
1539
1540 // device unplugged while delaying
1543 return true;
1544 }
1545
1547 TU_LOG_USBH("%s Speed\r\n", tu_str_speed[_dev0.speed]);
1548
1549 // fake transfer to kick-off the enumeration process
1551 xfer.daddr = 0;
1552 xfer.result = XFER_RESULT_SUCCESS;
1553 xfer.user_data = ENUM_ADDR0_DEVICE_DESC;
1554
1556 }
1557#if CFG_TUH_HUB
1558 else {
1559 // connected via external hub
1560 // wait until device connection is stable TODO non blocking
1562
1563 // ENUM_HUB_GET_STATUS
1566 }
1567#endif // hub
1568
1569 return true;
1570}
1571
1572static uint8_t get_new_address(bool is_hub) {
1573 uint8_t start;
1574 uint8_t end;
1575
1576 if ( is_hub ) {
1577 start = CFG_TUH_DEVICE_MAX;
1578 end = start + CFG_TUH_HUB;
1579 }else {
1580 start = 0;
1581 end = start + CFG_TUH_DEVICE_MAX;
1582 }
1583
1584 for (uint8_t idx = start; idx < end; idx++) {
1585 if (!_usbh_devices[idx].connected) return (idx+1);
1586 }
1587
1588 return 0; // invalid address
1589}
1590
1591static bool enum_request_set_addr(void) {
1592 tusb_desc_device_t const* desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf;
1593
1594 // Get new address
1595 uint8_t const new_addr = get_new_address(desc_device->bDeviceClass == TUSB_CLASS_HUB);
1596 TU_ASSERT(new_addr != 0);
1597 TU_LOG_USBH("Set Address = %d\r\n", new_addr);
1598
1599 usbh_device_t* new_dev = get_device(new_addr);
1600 new_dev->rhport = _dev0.rhport;
1601 new_dev->hub_addr = _dev0.hub_addr;
1602 new_dev->hub_port = _dev0.hub_port;
1603 new_dev->speed = _dev0.speed;
1604 new_dev->connected = 1;
1605 new_dev->ep0_size = desc_device->bMaxPacketSize0;
1606
1609 .recipient = TUSB_REQ_RCPT_DEVICE,
1610 .type = TUSB_REQ_TYPE_STANDARD,
1611 .direction = TUSB_DIR_OUT
1612 },
1613 .bRequest = TUSB_REQ_SET_ADDRESS,
1614 .wValue = tu_htole16(new_addr),
1615 .wIndex = 0,
1616 .wLength = 0
1617 };
1618 tuh_xfer_t xfer = {
1619 .daddr = 0, // dev0
1620 .ep_addr = 0,
1621 .setup = &request,
1622 .buffer = NULL,
1623 .complete_cb = process_enumeration,
1624 .user_data = ENUM_GET_DEVICE_DESC
1625 };
1626
1627 TU_ASSERT(tuh_control_xfer(&xfer));
1628 return true;
1629}
1630
1633 uint16_t const total_len = tu_le16toh(desc_cfg->wTotalLength);
1634 uint8_t const* desc_end = ((uint8_t const*) desc_cfg) + total_len;
1635 uint8_t const* p_desc = tu_desc_next(desc_cfg);
1636
1637 TU_LOG_USBH("Parsing Configuration descriptor (wTotalLength = %u)\r\n", total_len);
1638
1639 // parse each interfaces
1640 while( p_desc < desc_end ) {
1641 uint8_t assoc_itf_count = 1;
1642
1643 // Class will always starts with Interface Association (if any) and then Interface descriptor
1645 tusb_desc_interface_assoc_t const * desc_iad = (tusb_desc_interface_assoc_t const *) p_desc;
1646 assoc_itf_count = desc_iad->bInterfaceCount;
1647
1648 p_desc = tu_desc_next(p_desc); // next to Interface
1649
1650 // IAD's first interface number and class should match with opened interface
1651 //TU_ASSERT(desc_iad->bFirstInterface == desc_itf->bInterfaceNumber &&
1652 // desc_iad->bFunctionClass == desc_itf->bInterfaceClass);
1653 }
1654
1655 TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) );
1656 tusb_desc_interface_t const* desc_itf = (tusb_desc_interface_t const*) p_desc;
1657
1658#if CFG_TUH_MIDI
1659 // MIDI has 2 interfaces (Audio Control v1 + MIDIStreaming) but does not have IAD
1660 // manually force associated count = 2
1661 if (1 == assoc_itf_count &&
1662 TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
1665 assoc_itf_count = 2;
1666 }
1667#endif
1668
1669#if CFG_TUH_CDC
1670 // Some legacy CDC device does not use IAD but rather use device class as hint to combine 2 interfaces
1671 // manually force associated count = 2
1672 if (1 == assoc_itf_count &&
1673 TUSB_CLASS_CDC == desc_itf->bInterfaceClass &&
1674 CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == desc_itf->bInterfaceSubClass) {
1675 assoc_itf_count = 2;
1676 }
1677#endif
1678
1679 uint16_t const drv_len = tu_desc_get_interface_total_len(desc_itf, assoc_itf_count, (uint16_t) (desc_end-p_desc));
1680 TU_ASSERT(drv_len >= sizeof(tusb_desc_interface_t));
1681
1682 // Find driver for this interface
1683 for (uint8_t drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) {
1684 usbh_class_driver_t const * driver = get_driver(drv_id);
1685 if (driver && driver->open(dev->rhport, dev_addr, desc_itf, drv_len) ) {
1686 // open successfully
1687 TU_LOG_USBH(" %s opened\r\n", driver->name);
1688
1689 // bind (associated) interfaces to found driver
1690 for(uint8_t i=0; i<assoc_itf_count; i++) {
1691 uint8_t const itf_num = desc_itf->bInterfaceNumber+i;
1692
1693 // Interface number must not be used already
1694 TU_ASSERT( TUSB_INDEX_INVALID_8 == dev->itf2drv[itf_num] );
1695 dev->itf2drv[itf_num] = drv_id;
1696 }
1697
1698 // bind all endpoints to found driver
1699 tu_edpt_bind_driver(dev->ep2drv, desc_itf, drv_len, drv_id);
1700
1701 break; // exit driver find loop
1702 }
1703
1704 if ( drv_id == TOTAL_DRIVER_COUNT - 1 ) {
1705 TU_LOG_USBH("[%u:%u] Interface %u: class = %u subclass = %u protocol = %u is not supported\r\n",
1706 dev->rhport, dev_addr, desc_itf->bInterfaceNumber, desc_itf->bInterfaceClass, desc_itf->bInterfaceSubClass, desc_itf->bInterfaceProtocol);
1707 }
1708 }
1709
1710 // next Interface or IAD descriptor
1711 p_desc += drv_len;
1712 }
1713
1714 return true;
1715}
1716
1717void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) {
1719
1720 for(itf_num++; itf_num < CFG_TUH_INTERFACE_MAX; itf_num++) {
1721 // continue with next valid interface
1722 // IAD binding interface such as CDCs should return itf_num + 1 when complete
1723 // with usbh_driver_set_config_complete()
1724 uint8_t const drv_id = dev->itf2drv[itf_num];
1725 usbh_class_driver_t const * driver = get_driver(drv_id);
1726 if (driver) {
1727 TU_LOG_USBH("%s set config: itf = %u\r\n", driver->name, itf_num);
1728 driver->set_config(dev_addr, itf_num);
1729 break;
1730 }
1731 }
1732
1733 // all interface are configured
1734 if (itf_num == CFG_TUH_INTERFACE_MAX) {
1736
1737 if (is_hub_addr(dev_addr)) {
1738 TU_LOG_USBH("HUB address = %u is mounted\r\n", dev_addr);
1739 }else {
1740 // Invoke callback if available
1742 }
1743 }
1744}
1745
1746static void enum_full_complete(void) {
1747 // mark enumeration as complete
1748 _dev0.enumerating = 0;
1749
1750#if CFG_TUH_HUB
1751 // get next hub status
1753#endif
1754
1755}
1756
1757#endif
bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
Definition: cdc_host.c:733
bool cdch_set_config(uint8_t daddr, uint8_t itf_num)
Definition: cdc_host.c:772
void cdch_close(uint8_t daddr)
Definition: cdc_host.c:650
bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
Definition: cdc_host.c:668
bool cdch_init(void)
Definition: cdc_host.c:624
bool cdch_deinit(void)
Definition: cdc_host.c:641
static bool in_isr
xfer_td_t xfer[EP_CBI_COUNT+1][2]
Definition: dcd_nrf5x.c:119
uint16_t total_bytes
Definition: dcd_nuc505.c:113
uint8_t dev_addr
Definition: dcd_pic32mz.c:81
static const tusb_desc_endpoint_t ep0_desc
Definition: dcd_samx7x.c:84
@ AUDIO_SUBCLASS_CONTROL
Audio Control.
Definition: audio.h:61
@ AUDIO_FUNC_PROTOCOL_CODE_UNDEF
Definition: audio.h:53
bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void *resp, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
Definition: hub.c:150
void hub_close(uint8_t dev_addr)
Definition: hub.c:224
bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
Definition: hub.c:194
bool hub_deinit(void)
Definition: hub.c:190
bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
Definition: hub.c:334
bool hub_edpt_status_xfer(uint8_t dev_addr)
Definition: hub.c:235
bool hub_init(void)
Definition: hub.c:185
static TU_ATTR_ALWAYS_INLINE bool hub_port_clear_reset_change(uint8_t hub_addr, uint8_t hub_port, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
Definition: hub.h:197
bool hub_set_config(uint8_t dev_addr, uint8_t itf_num)
Definition: hub.c:249
static TU_ATTR_ALWAYS_INLINE bool hub_port_reset(uint8_t hub_addr, uint8_t hub_port, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
Definition: hub.h:191
void hcd_int_disable(uint8_t rhport)
Definition: hcd_max3421.c:577
bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const *ep_desc)
Definition: hcd_max3421.c:625
void hcd_int_enable(uint8_t rhport)
Definition: hcd_max3421.c:571
void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
Definition: hcd_max3421.c:615
bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr)
Definition: hcd_max3421.c:759
void hcd_port_reset_end(uint8_t rhport)
Definition: hcd_max3421.c:604
bool hcd_setup_send(uint8_t rhport, uint8_t daddr, uint8_t const setup_packet[8])
Definition: hcd_max3421.c:775
void hcd_port_reset(uint8_t rhport)
Definition: hcd_max3421.c:599
bool hcd_port_connect_status(uint8_t rhport)
Definition: hcd_max3421.c:592
tusb_speed_t hcd_port_speed_get(uint8_t rhport)
Definition: hcd_max3421.c:609
bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen)
Definition: hcd_max3421.c:733
bool hcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init)
Definition: hcd_max3421.c:497
void hidh_close(uint8_t daddr)
Definition: hid_host.c:451
bool hidh_init(void)
Definition: hid_host.c:419
bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
Definition: hid_host.c:466
bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
Definition: hid_host.c:429
bool hidh_set_config(uint8_t daddr, uint8_t itf_num)
Definition: hid_host.c:537
bool hidh_deinit(void)
Definition: hid_host.c:425
bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
Definition: msc_host.c:371
void msch_close(uint8_t dev_addr)
Definition: msc_host.c:297
bool msch_init(void)
Definition: msc_host.c:287
bool msch_set_config(uint8_t dev_addr, uint8_t itf_num)
Definition: msc_host.c:402
bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
Definition: msc_host.c:312
bool msch_deinit(void)
Definition: msc_host.c:293
static TU_ATTR_ALWAYS_INLINE bool osal_queue_empty(osal_queue_t qhdl)
static TU_ATTR_ALWAYS_INLINE osal_queue_t osal_queue_create(osal_queue_def_t *qdef)
StaticSemaphore_t osal_mutex_def_t
Definition: osal_freertos.h:46
QueueHandle_t osal_queue_t
Definition: osal_freertos.h:55
static TU_ATTR_ALWAYS_INLINE bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec)
static TU_ATTR_ALWAYS_INLINE bool osal_queue_send(osal_queue_t qhdl, void const *data, bool in_isr)
static TU_ATTR_ALWAYS_INLINE bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
static TU_ATTR_ALWAYS_INLINE bool osal_mutex_delete(osal_mutex_t mutex_hdl)
static TU_ATTR_ALWAYS_INLINE bool osal_queue_receive(osal_queue_t qhdl, void *data, uint32_t msec)
static TU_ATTR_ALWAYS_INLINE bool osal_queue_delete(osal_queue_t qhdl)
SemaphoreHandle_t osal_mutex_t
Definition: osal_freertos.h:54
static TU_ATTR_ALWAYS_INLINE osal_mutex_t osal_mutex_create(osal_mutex_def_t *mdef)
static void * memcpy(void *dst, const void *src, size_t n)
Definition: ringbuffer.c:8
AUDIO Channel Cluster Descriptor (4.1)
Definition: audio.h:647
uint16_t wTotalLength
Total number of bytes returned for the class-specific AudioControl interface descriptor....
Definition: audio.h:661
volatile uint8_t busy
Definition: tusb_private.h:44
struct TU_ATTR_PACKED::@16::TU_ATTR_PACKED bmRequestType_bit
uint16_t wLength
Definition: audio.h:840
uint8_t * buffer
Definition: video_device.c:111
uint8_t bInterfaceCount
Total number of associated interfaces.
Definition: tusb_types.h:413
volatile uint8_t claimed
Definition: tusb_private.h:46
uint8_t bInterfaceClass
Class code (assigned by the USB-IF).
Definition: tusb_types.h:349
uint8_t bLength
Size of this descriptor in bytes: 9.
Definition: audio.h:656
uint8_t bInterfaceSubClass
Subclass code (assigned by the USB-IF). These codes are qualified by the value of the bInterfaceCla...
Definition: tusb_types.h:350
uint8_t bmRequestType
Definition: audio.h:828
uint8_t bRequest
Request type audio_cs_req_t.
Definition: audio.h:831
uint8_t bInterfaceProtocol
Protocol code (assigned by the USB). These codes are qualified by the value of the bInterfaceClass ...
Definition: tusb_types.h:351
uint8_t bInterfaceNumber
Number of this interface. Zero-based value identifying the index in the array of concurrent interface...
Definition: tusb_types.h:346
uint8_t hub_port
Definition: hcd.h:96
uint8_t hub_addr
Definition: hcd.h:95
uint8_t speed
Definition: hcd.h:97
uint8_t rhport
Definition: hcd.h:94
struct hcd_event_t::@86::@88 connection
uint8_t rhport
Definition: hcd.h:66
void * param
Definition: hcd.h:88
uint8_t hub_port
Definition: hcd.h:74
struct hcd_event_t::@86::@90 func_call
uint8_t hub_addr
Definition: hcd.h:73
struct hcd_event_t::@86::@89 xfer_complete
void(* func)(void *)
Definition: hcd.h:87
uint32_t len
Definition: hcd.h:82
uint8_t result
Definition: hcd.h:81
uint8_t dev_addr
Definition: hcd.h:68
uint8_t event_id
Definition: hcd.h:67
union hub_port_status_response_t::@93 status
union hub_port_status_response_t::@93 change
uint8_t daddr
Definition: usbh.h:51
tuh_xfer_cb_t complete_cb
Definition: usbh.h:64
tusb_speed_t speed
Definition: tusb_types.h:282
bool(*const set_config)(uint8_t dev_addr, uint8_t itf_num)
Definition: usbh_pvt.h:57
bool(*const deinit)(void)
Definition: usbh_pvt.h:55
char const * name
Definition: usbh_pvt.h:53
void(*const close)(uint8_t dev_addr)
Definition: usbh_pvt.h:59
bool(*const init)(void)
Definition: usbh_pvt.h:54
bool(*const open)(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
Definition: usbh_pvt.h:56
bool(*const xfer_cb)(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
Definition: usbh_pvt.h:58
volatile uint8_t enumerating
Definition: usbh.c:79
uint8_t hub_addr
Definition: usbh.c:74
uint8_t rhport
Definition: usbh.c:73
uint8_t hub_port
Definition: usbh.c:75
volatile uint8_t connected
Definition: usbh.c:93
volatile uint8_t configured
Definition: usbh.c:95
volatile uint8_t suspended
Definition: usbh.c:96
volatile uint8_t addressed
Definition: usbh.c:94
uint8_t i_serial
Definition: usbh.c:109
uint16_t pid
Definition: usbh.c:105
uintptr_t user_data
Definition: usbh.c:124
tu_edpt_state_t ep_status[CFG_TUH_ENDPOINT_MAX][2]
Definition: usbh.c:118
uint8_t i_manufacturer
Definition: usbh.c:107
uint8_t i_product
Definition: usbh.c:108
uint16_t vid
Definition: usbh.c:104
uint8_t rhport
Definition: usbh.c:86
uint8_t speed
Definition: usbh.c:89
uint8_t itf2drv[CFG_TUH_INTERFACE_MAX]
Definition: usbh.c:115
uint8_t ep0_size
Definition: usbh.c:102
uint8_t hub_port
Definition: usbh.c:88
uint8_t ep2drv[CFG_TUH_ENDPOINT_MAX][2]
Definition: usbh.c:116
tuh_xfer_cb_t complete_cb
Definition: usbh.c:123
struct usbh_device_t::@118 ep_callback[CFG_TUH_ENDPOINT_MAX][2]
uint8_t hub_addr
Definition: usbh.c:87
volatile uint16_t actual_len
Definition: dcd_nrf5x.c:101
uint8_t * buffer
Definition: dcd_nrf5x.c:99
char const *const tu_str_speed[]
Definition: tusb.c:475
TU_ATTR_WEAK void tusb_time_delay_ms_api(uint32_t ms)
Definition: tusb.c:48
static TU_ATTR_ALWAYS_INLINE uint16_t tu_min16(uint16_t x, uint16_t y)
Definition: tusb_common.h:155
static TU_ATTR_ALWAYS_INLINE uint16_t tu_unaligned_read16(const void *mem)
Definition: tusb_common.h:219
static TU_ATTR_ALWAYS_INLINE bool tu_bit_test(uint32_t value, uint8_t pos)
Definition: tusb_common.h:151
char const *const tu_str_std_request[]
Definition: tusb.c:476
char const *const tu_str_xfer_result[]
Definition: tusb.c:492
bool tu_edpt_validate(tusb_desc_endpoint_t const *desc_ep, tusb_speed_t speed)
Definition: tusb.c:202
bool tu_edpt_claim(tu_edpt_state_t *ep_state, osal_mutex_t mutex)
Definition: tusb.c:171
bool tu_edpt_release(tu_edpt_state_t *ep_state, osal_mutex_t mutex)
Definition: tusb.c:188
uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const *desc_itf, uint8_t itf_count, uint16_t max_len)
Definition: tusb.c:251
void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const *p_desc, uint16_t desc_len, uint8_t driver_id)
Definition: tusb.c:236
@ TUSB_DIR_IN
Definition: tusb_types.h:67
@ TUSB_DIR_OUT
Definition: tusb_types.h:66
@ TUSB_REQ_SET_ADDRESS
Definition: tusb_types.h:127
@ TUSB_REQ_GET_DESCRIPTOR
Definition: tusb_types.h:128
@ TUSB_REQ_SET_CONFIGURATION
Definition: tusb_types.h:131
@ TUSB_REQ_SET_INTERFACE
Definition: tusb_types.h:133
@ TUSB_REQ_SYNCH_FRAME
Definition: tusb_types.h:134
tusb_speed_t
defined base on EHCI specs value for Endpoint Speed
Definition: tusb_types.h:49
@ TUSB_SPEED_FULL
Definition: tusb_types.h:50
@ TUSB_SPEED_LOW
Definition: tusb_types.h:51
@ TUSB_SPEED_HIGH
Definition: tusb_types.h:52
@ TUSB_CLASS_CDC
Definition: tusb_types.h:161
@ TUSB_CLASS_AUDIO
Definition: tusb_types.h:160
@ TUSB_CLASS_HUB
Definition: tusb_types.h:168
static TU_ATTR_ALWAYS_INLINE uint8_t tu_edpt_number(uint8_t addr)
Definition: tusb_types.h:507
TU_ATTR_PACKED_BEGIN TU_ATTR_BIT_FIELD_ORDER_BEGIN struct TU_ATTR_PACKED tusb_desc_device_t
USB Device Descriptor.
xfer_result_t
Definition: tusb_types.h:236
@ XFER_RESULT_SUCCESS
Definition: tusb_types.h:237
@ XFER_RESULT_INVALID
Definition: tusb_types.h:241
@ XFER_RESULT_STALLED
Definition: tusb_types.h:239
@ TUSB_REQ_RCPT_DEVICE
Definition: tusb_types.h:151
@ TUSB_REQ_RCPT_INTERFACE
Definition: tusb_types.h:152
@ TUSB_XFER_CONTROL
Definition: tusb_types.h:59
TU_ATTR_PACKED_END TU_ATTR_BIT_FIELD_ORDER_END static TU_ATTR_ALWAYS_INLINE tusb_dir_t tu_edpt_dir(uint8_t addr)
Definition: tusb_types.h:502
struct TU_ATTR_PACKED tusb_desc_endpoint_t
USB Endpoint Descriptor.
@ CONTROL_STAGE_ACK
Definition: tusb_types.h:270
@ CONTROL_STAGE_IDLE
Definition: tusb_types.h:267
@ CONTROL_STAGE_DATA
Definition: tusb_types.h:269
@ CONTROL_STAGE_SETUP
Definition: tusb_types.h:268
static TU_ATTR_ALWAYS_INLINE uint8_t tu_edpt_addr(uint8_t num, uint8_t dir)
Definition: tusb_types.h:511
static TU_ATTR_ALWAYS_INLINE uint8_t tu_desc_type(void const *desc)
Definition: tusb_types.h:537
@ TUSB_REQ_TYPE_STANDARD
Definition: tusb_types.h:144
static TU_ATTR_ALWAYS_INLINE uint8_t const * tu_desc_next(void const *desc)
Definition: tusb_types.h:531
@ TUSB_DESC_STRING
Definition: tusb_types.h:95
@ TUSB_DESC_INTERFACE_ASSOCIATION
Definition: tusb_types.h:103
@ TUSB_DESC_CONFIGURATION
Definition: tusb_types.h:94
@ TUSB_DESC_ENDPOINT
Definition: tusb_types.h:97
@ TUSB_DESC_INTERFACE
Definition: tusb_types.h:96
@ TUSB_DESC_DEVICE
Definition: tusb_types.h:93
@ TUSB_INDEX_INVALID_8
Definition: tusb_types.h:274
static void _control_blocking_complete_cb(tuh_xfer_t *xfer)
Definition: usbh.c:596
bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
Definition: usbh.c:877
@ BUILTIN_DRIVER_COUNT
Definition: usbh.c:201
bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t *vid, uint16_t *pid)
Definition: usbh.c:297
bool tuh_descriptor_get_manufacturer_string(uint8_t daddr, uint16_t language_id, void *buffer, uint16_t len, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
Definition: usbh.c:1038
uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void *buffer, uint16_t len)
Definition: usbh.c:1156
bool tuh_edpt_xfer(tuh_xfer_t *xfer)
Definition: usbh.c:765
static TU_ATTR_ALWAYS_INLINE usbh_device_t * get_device(uint8_t dev_addr)
Definition: usbh.c:271
void tuh_task_ext(uint32_t timeout_ms, bool in_isr)
Definition: usbh.c:465
TU_ATTR_WEAK bool hcd_deinit(uint8_t rhport)
Definition: usbh.c:50
@ ENUM_RESET_DELAY_MS
Definition: usbh.c:1287
@ ENUM_DEBOUNCING_DELAY_MS
Definition: usbh.c:1288
bool tuh_descriptor_get_device(uint8_t daddr, void *buffer, uint16_t len, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
Definition: usbh.c:1019
volatile uint16_t actual_len
Definition: usbh.c:266
uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id, void *buffer, uint16_t len)
Definition: usbh.c:1185
bool tuh_task_event_ready(void)
Definition: usbh.c:440
static TU_ATTR_ALWAYS_INLINE void _set_control_xfer_stage(uint8_t stage)
Definition: usbh.c:674
bool tuh_descriptor_get(uint8_t daddr, uint8_t type, uint8_t index, void *buffer, uint16_t len, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
Definition: usbh.c:1014
bool tuh_rhport_is_active(uint8_t rhport)
Definition: usbh.c:314
static usbh_class_driver_t const usbh_class_drivers[]
Definition: usbh.c:139
void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num)
Definition: usbh.c:1717
bool tuh_descriptor_get_configuration(uint8_t daddr, uint8_t index, void *buffer, uint16_t len, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
Definition: usbh.c:1025
static TU_ATTR_ALWAYS_INLINE bool queue_event(hcd_event_t const *event, bool in_isr)
Definition: usbh.c:281
tu_static usbh_class_driver_t const * _app_driver
Definition: usbh.c:205
CFG_TUH_MEM_SECTION struct @98 _ctrl_xfer
CFG_TUH_MEM_ALIGN tusb_control_request_t request
Definition: usbh.c:259
OSAL_QUEUE_DEF(usbh_int_set, _usbh_qdef, CFG_TUH_TASK_QUEUE_SZ, hcd_event_t)
uint8_t * buffer
Definition: usbh.c:260
bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr)
Definition: usbh.c:861
bool tuh_configuration_set(uint8_t daddr, uint8_t config_num, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
Definition: usbh.c:1090
static void enum_full_complete(void)
Definition: usbh.c:1746
bool tuh_descriptor_get_string(uint8_t daddr, uint8_t index, uint16_t language_id, void *buffer, uint16_t len, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
Definition: usbh.c:1032
static usbh_device_t _usbh_devices[TOTAL_DEVICES]
Definition: usbh.c:237
bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
Definition: usbh.c:1116
static osal_queue_t _usbh_q
Definition: usbh.c:250
uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id, void *buffer, uint16_t len)
Definition: usbh.c:1180
tusb_speed_t tuh_speed_get(uint8_t dev_addr)
Definition: usbh.c:309
bool tuh_descriptor_get_product_string(uint8_t daddr, uint16_t language_id, void *buffer, uint16_t len, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
Definition: usbh.c:1047
uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void *buffer, uint16_t len)
Definition: usbh.c:1170
static osal_mutex_def_t _usbh_mutexdef
Definition: usbh.c:241
bool tuh_configure(uint8_t rhport, uint32_t cfg_id, const void *cfg_param)
Definition: usbh.c:332
static void clear_device(usbh_device_t *dev)
Definition: usbh.c:336
tu_static uint8_t _app_driver_count
Definition: usbh.c:206
uint8_t daddr
Definition: usbh.c:264
static void _control_xfer_complete(uint8_t daddr, xfer_result_t result)
Definition: usbh.c:680
@ ENUM_ADDR0_DEVICE_DESC
Definition: usbh.c:1297
@ ENUM_RESET_1
Definition: usbh.c:1294
@ ENUM_SET_CONFIG
Definition: usbh.c:1306
@ ENUM_HUB_CLEAR_RESET_2
Definition: usbh.c:1300
@ ENUM_SET_ADDR
Definition: usbh.c:1301
@ ENUM_HUB_GET_STATUS_2
Definition: usbh.c:1299
@ ENUM_GET_9BYTE_CONFIG_DESC
Definition: usbh.c:1304
@ ENUM_RESET_2
Definition: usbh.c:1298
@ ENUM_HUB_CLEAR_RESET_1
Definition: usbh.c:1296
@ ENUM_CONFIG_DRIVER
Definition: usbh.c:1307
@ ENUM_GET_FULL_CONFIG_DESC
Definition: usbh.c:1305
@ ENUM_IDLE
Definition: usbh.c:1293
@ ENUM_GET_DEVICE_DESC
Definition: usbh.c:1303
bool tuh_inited(void)
Definition: usbh.c:342
static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const *desc_cfg)
Definition: usbh.c:1631
@ CONFIG_NUM
Definition: usbh.c:202
void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t *devtree_info)
Definition: usbh.c:949
uintptr_t user_data
Definition: usbh.c:262
void usbh_defer_func(osal_task_func_t func, void *param, bool in_isr)
Definition: usbh.c:832
static bool usbh_control_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
Definition: usbh.c:703
volatile uint8_t stage
Definition: usbh.c:265
uint8_t * usbh_get_enum_buf(void)
Definition: usbh.c:819
bool tuh_control_xfer(tuh_xfer_t *xfer)
Definition: usbh.c:602
static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size)
Definition: usbh.c:916
CFG_TUH_MEM_SECTION static CFG_TUH_MEM_ALIGN uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE]
Definition: usbh.c:253
TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const *event, bool in_isr)
Definition: usbh.c:964
uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void *buffer, uint16_t len)
Definition: usbh.c:1175
static osal_mutex_t _usbh_mutex
Definition: usbh.c:242
bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t *rh_init)
Definition: usbh.c:346
void usbh_int_set(bool enabled)
Definition: usbh.c:823
bool tuh_deinit(uint8_t rhport)
Definition: usbh.c:405
bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr)
Definition: usbh.c:846
static bool enum_new_device(hcd_event_t *event)
Definition: usbh.c:1522
static uint8_t get_new_address(bool is_hub)
Definition: usbh.c:1572
bool tuh_rhport_reset_bus(uint8_t rhport, bool active)
Definition: usbh.c:318
bool tuh_mounted(uint8_t dev_addr)
Definition: usbh.c:291
static void process_enumeration(tuh_xfer_t *xfer)
Definition: usbh.c:1315
uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void *buffer, uint16_t len)
Definition: usbh.c:1151
tuh_xfer_cb_t complete_cb
Definition: usbh.c:261
TU_ATTR_WEAK void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr)
Definition: usbh.c:62
uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void *buffer, uint16_t len)
Definition: usbh.c:1160
bool tuh_edpt_open(uint8_t dev_addr, tusb_desc_endpoint_t const *desc_ep)
Definition: usbh.c:930
bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr)
Definition: usbh.c:935
uint8_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void *buffer, uint16_t len)
Definition: usbh.c:1165
bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void *buffer, uint16_t len, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
Definition: usbh.c:1064
TU_ATTR_WEAK bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void *cfg_param)
Definition: usbh.c:55
static bool _get_descriptor(uint8_t daddr, uint8_t type, uint8_t index, uint16_t language_id, void *buffer, uint16_t len, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
Definition: usbh.c:989
static usbh_class_driver_t const * get_driver(uint8_t drv_id)
Definition: usbh.c:210
static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port)
Definition: usbh.c:1219
static bool enum_request_set_addr(void)
Definition: usbh.c:1591
bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void *buffer, uint16_t len, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
Definition: usbh.c:1055
bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr)
Definition: usbh.c:781
static uint8_t _usbh_controller
Definition: usbh.c:229
static TU_ATTR_ALWAYS_INLINE bool is_hub_addr(uint8_t daddr)
Definition: usbh.c:1194
uint8_t usbh_get_rhport(uint8_t dev_addr)
Definition: usbh.c:814
static usbh_dev0_t _dev0
Definition: usbh.c:232
static TU_ATTR_ALWAYS_INLINE void tuh_task(void)
Definition: usbh.h:152
TU_ATTR_WEAK void tuh_umount_cb(uint8_t daddr)
void(* tuh_xfer_cb_t)(tuh_xfer_t *xfer)
Definition: usbh.h:44
TU_ATTR_WEAK void tuh_mount_cb(uint8_t daddr)
usbh_class_driver_t const * usbh_app_driver_get_cb(uint8_t *driver_count) TU_ATTR_WEAK
void cush_init(void)
Definition: vendor_host.c:91
void cush_isr(pipe_handle_t pipe_hdl, xfer_result_t event)
Definition: vendor_host.c:120
void cush_close(uint8_t dev_addr)
Definition: vendor_host.c:125