/src/tinyusb/src/device/usbd.c
Line | Count | Source |
1 | | /* |
2 | | * SPDX-FileCopyrightText: Copyright (c) 2019 Ha Thach (tinyusb.org) |
3 | | * SPDX-License-Identifier: MIT |
4 | | * |
5 | | * This file is part of the TinyUSB stack. |
6 | | */ |
7 | | |
8 | | #include "tusb_option.h" |
9 | | |
10 | | #if CFG_TUD_ENABLED |
11 | | |
12 | | #include "tusb.h" |
13 | | #include "device/dcd.h" |
14 | | #include "common/tusb_private.h" |
15 | | |
16 | | #include "device/usbd.h" |
17 | | #include "device/usbd_pvt.h" |
18 | | |
19 | | //--------------------------------------------------------------------+ |
20 | | // USBD Configuration |
21 | | //--------------------------------------------------------------------+ |
22 | | #ifndef CFG_TUD_TASK_QUEUE_SZ |
23 | | #define CFG_TUD_TASK_QUEUE_SZ 16 |
24 | | #endif |
25 | | |
26 | | //--------------------------------------------------------------------+ |
27 | | // Weak stubs: invoked if no strong implementation is available |
28 | | //--------------------------------------------------------------------+ |
29 | 118k | TU_ATTR_WEAK void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) { |
30 | 118k | (void) rhport; (void) eventid; (void) in_isr; |
31 | 118k | } |
32 | | |
33 | 0 | TU_ATTR_WEAK void tud_sof_cb(uint32_t frame_count) { |
34 | 0 | (void) frame_count; |
35 | 0 | } |
36 | | |
37 | 607 | TU_ATTR_WEAK uint8_t const* tud_descriptor_bos_cb(void) { |
38 | 607 | return NULL; |
39 | 607 | } |
40 | | |
41 | 594 | TU_ATTR_WEAK uint8_t const* tud_descriptor_device_qualifier_cb(void) { |
42 | 594 | return NULL; |
43 | 594 | } |
44 | | |
45 | 620 | TU_ATTR_WEAK uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) { |
46 | 620 | (void) index; |
47 | 620 | return NULL; |
48 | 620 | } |
49 | | |
50 | 2.99k | TU_ATTR_WEAK void tud_mount_cb(void) { |
51 | 2.99k | } |
52 | | |
53 | 14.9k | TU_ATTR_WEAK void tud_umount_cb(void) { |
54 | 14.9k | } |
55 | | |
56 | 5.16k | TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en) { |
57 | 5.16k | (void) remote_wakeup_en; |
58 | 5.16k | } |
59 | | |
60 | 4.99k | TU_ATTR_WEAK void tud_resume_cb(void) { |
61 | 4.99k | } |
62 | | |
63 | 1.24k | TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) { |
64 | 1.24k | (void) rhport; (void) stage; (void) request; |
65 | 1.24k | return false; |
66 | 1.24k | } |
67 | | |
68 | 0 | TU_ATTR_WEAK bool dcd_deinit(uint8_t rhport) { |
69 | 0 | (void) rhport; |
70 | 0 | return false; |
71 | 0 | } |
72 | | |
73 | 0 | TU_ATTR_WEAK void dcd_connect(uint8_t rhport) { |
74 | 0 | (void) rhport; |
75 | 0 | } |
76 | | |
77 | 0 | TU_ATTR_WEAK void dcd_disconnect(uint8_t rhport) { |
78 | 0 | (void) rhport; |
79 | 0 | } |
80 | | |
81 | 0 | TU_ATTR_WEAK bool dcd_dcache_clean(const void* addr, uint32_t data_size) { |
82 | 0 | (void) addr; (void) data_size; |
83 | 0 | return true; |
84 | 0 | } |
85 | | |
86 | 0 | TU_ATTR_WEAK bool dcd_dcache_invalidate(const void* addr, uint32_t data_size) { |
87 | 0 | (void) addr; (void) data_size; |
88 | 0 | return true; |
89 | 0 | } |
90 | | |
91 | 0 | TU_ATTR_WEAK bool dcd_dcache_clean_invalidate(const void* addr, uint32_t data_size) { |
92 | 0 | (void) addr; (void) data_size; |
93 | 0 | return true; |
94 | 0 | } |
95 | | |
96 | | //--------------------------------------------------------------------+ |
97 | | // Device Data |
98 | | //--------------------------------------------------------------------+ |
99 | | |
100 | | // Per-control-transfer state: populated at process_setup_received() entry, |
101 | | // consumed asynchronously by usbd_control_xfer_cb() when the EP0 transfer completes. |
102 | | typedef struct { |
103 | | tusb_control_request_t request; |
104 | | uint8_t* buffer; |
105 | | uint16_t data_len; |
106 | | uint16_t total_xferred; |
107 | | usbd_control_xfer_cb_t complete_cb; |
108 | | } usbd_control_xfer_t; |
109 | | |
110 | | typedef struct { |
111 | | usbd_control_xfer_t ctrl_xfer; |
112 | | |
113 | | // Note: these may share an enum state |
114 | | volatile uint8_t connected; |
115 | | volatile uint8_t addressed; |
116 | | volatile uint8_t suspended; |
117 | | |
118 | | union { |
119 | | struct TU_ATTR_PACKED { |
120 | | uint8_t self_powered : 1; // configuration descriptor's attribute; |
121 | | uint8_t remote_wakeup_en : 1; // enable/disable by host |
122 | | }; |
123 | | uint8_t dev_state_bm; |
124 | | }; |
125 | | |
126 | | uint8_t cfg_num; // current active configuration (0x00 is not configured) |
127 | | uint8_t speed; |
128 | | volatile uint8_t sof_consumer; |
129 | | |
130 | | uint8_t itf2drv[CFG_TUD_INTERFACE_MAX]; // map interface number to driver (0xff is invalid) |
131 | | uint8_t ep2drv[CFG_TUD_ENDPPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each |
132 | | |
133 | | volatile uint8_t ep_status[CFG_TUD_ENDPPOINT_MAX][2]; |
134 | | } usbd_device_t; |
135 | | |
136 | | static usbd_device_t _usbd_dev; |
137 | | static volatile uint8_t _usbd_queued_setup; |
138 | | |
139 | | CFG_TUD_MEM_SECTION static struct { |
140 | | TUD_EPBUF_DEF(buf, CFG_TUD_ENDPOINT0_BUFSIZE); |
141 | | } _ctrl_epbuf; |
142 | | |
143 | | //--------------------------------------------------------------------+ |
144 | | // Class Driver |
145 | | //--------------------------------------------------------------------+ |
146 | | #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL |
147 | | #define DRIVER_NAME(_name) _name |
148 | | #else |
149 | | #define DRIVER_NAME(_name) NULL |
150 | | #endif |
151 | | |
152 | | // Built-in class drivers |
153 | | static const usbd_class_driver_t _usbd_driver[] = { |
154 | | #if CFG_TUD_CDC |
155 | | { |
156 | | .name = DRIVER_NAME("CDC"), |
157 | | .init = cdcd_init, |
158 | | .deinit = cdcd_deinit, |
159 | | .reset = cdcd_reset, |
160 | | .open = cdcd_open, |
161 | | .control_xfer_cb = cdcd_control_xfer_cb, |
162 | | .xfer_cb = cdcd_xfer_cb, |
163 | | .xfer_isr = NULL, |
164 | | .sof = NULL |
165 | | }, |
166 | | #endif |
167 | | |
168 | | #if CFG_TUD_MSC |
169 | | { |
170 | | .name = DRIVER_NAME("MSC"), |
171 | | .init = mscd_init, |
172 | | .deinit = NULL, |
173 | | .reset = mscd_reset, |
174 | | .open = mscd_open, |
175 | | .control_xfer_cb = mscd_control_xfer_cb, |
176 | | .xfer_cb = mscd_xfer_cb, |
177 | | .xfer_isr = NULL, |
178 | | .sof = NULL |
179 | | }, |
180 | | #endif |
181 | | |
182 | | #if CFG_TUD_HID |
183 | | { |
184 | | .name = DRIVER_NAME("HID"), |
185 | | .init = hidd_init, |
186 | | .deinit = hidd_deinit, |
187 | | .reset = hidd_reset, |
188 | | .open = hidd_open, |
189 | | .control_xfer_cb = hidd_control_xfer_cb, |
190 | | .xfer_cb = hidd_xfer_cb, |
191 | | .xfer_isr = NULL, |
192 | | .sof = NULL |
193 | | }, |
194 | | #endif |
195 | | |
196 | | #if CFG_TUD_AUDIO |
197 | | { |
198 | | .name = DRIVER_NAME("AUDIO"), |
199 | | .init = audiod_init, |
200 | | .deinit = audiod_deinit, |
201 | | .reset = audiod_reset, |
202 | | .open = audiod_open, |
203 | | .control_xfer_cb = audiod_control_xfer_cb, |
204 | | .xfer_cb = audiod_xfer_cb, |
205 | | .xfer_isr = audiod_xfer_isr, |
206 | | .sof = audiod_sof_isr |
207 | | }, |
208 | | #endif |
209 | | |
210 | | #if CFG_TUD_VIDEO |
211 | | { |
212 | | .name = DRIVER_NAME("VIDEO"), |
213 | | .init = videod_init, |
214 | | .deinit = videod_deinit, |
215 | | .reset = videod_reset, |
216 | | .open = videod_open, |
217 | | .control_xfer_cb = videod_control_xfer_cb, |
218 | | .xfer_cb = videod_xfer_cb, |
219 | | .xfer_isr = NULL, |
220 | | .sof = NULL |
221 | | }, |
222 | | #endif |
223 | | |
224 | | #if CFG_TUD_MIDI |
225 | | { |
226 | | .name = DRIVER_NAME("MIDI"), |
227 | | .init = midid_init, |
228 | | .deinit = midid_deinit, |
229 | | .open = midid_open, |
230 | | .reset = midid_reset, |
231 | | .control_xfer_cb = midid_control_xfer_cb, |
232 | | .xfer_cb = midid_xfer_cb, |
233 | | .xfer_isr = NULL, |
234 | | .sof = NULL |
235 | | }, |
236 | | #endif |
237 | | |
238 | | #if CFG_TUD_MIDI2 |
239 | | { |
240 | | .name = DRIVER_NAME("MIDI2"), |
241 | | .init = midi2d_init, |
242 | | .deinit = midi2d_deinit, |
243 | | .open = midi2d_open, |
244 | | .reset = midi2d_reset, |
245 | | .control_xfer_cb = midi2d_control_xfer_cb, |
246 | | .xfer_cb = midi2d_xfer_cb, |
247 | | .xfer_isr = NULL, |
248 | | .sof = NULL |
249 | | }, |
250 | | #endif |
251 | | |
252 | | #if CFG_TUD_VENDOR |
253 | | { |
254 | | .name = DRIVER_NAME("VENDOR"), |
255 | | .init = vendord_init, |
256 | | .deinit = vendord_deinit, |
257 | | .reset = vendord_reset, |
258 | | .open = vendord_open, |
259 | | .control_xfer_cb = vendord_control_xfer_cb, |
260 | | .xfer_cb = vendord_xfer_cb, |
261 | | .xfer_isr = NULL, |
262 | | .sof = NULL |
263 | | }, |
264 | | #endif |
265 | | |
266 | | #if CFG_TUD_USBTMC |
267 | | { |
268 | | .name = DRIVER_NAME("TMC"), |
269 | | .init = usbtmcd_init_cb, |
270 | | .deinit = usbtmcd_deinit, |
271 | | .reset = usbtmcd_reset_cb, |
272 | | .open = usbtmcd_open_cb, |
273 | | .control_xfer_cb = usbtmcd_control_xfer_cb, |
274 | | .xfer_cb = usbtmcd_xfer_cb, |
275 | | .xfer_isr = NULL, |
276 | | .sof = NULL |
277 | | }, |
278 | | #endif |
279 | | |
280 | | #if CFG_TUD_DFU_RUNTIME |
281 | | { |
282 | | .name = DRIVER_NAME("DFU-RUNTIME"), |
283 | | .init = dfu_rtd_init, |
284 | | .deinit = dfu_rtd_deinit, |
285 | | .reset = dfu_rtd_reset, |
286 | | .open = dfu_rtd_open, |
287 | | .control_xfer_cb = dfu_rtd_control_xfer_cb, |
288 | | .xfer_cb = NULL, |
289 | | .xfer_isr = NULL, |
290 | | .sof = NULL |
291 | | }, |
292 | | #endif |
293 | | |
294 | | #if CFG_TUD_DFU |
295 | | { |
296 | | .name = DRIVER_NAME("DFU"), |
297 | | .init = dfu_moded_init, |
298 | | .deinit = dfu_moded_deinit, |
299 | | .reset = dfu_moded_reset, |
300 | | .open = dfu_moded_open, |
301 | | .control_xfer_cb = dfu_moded_control_xfer_cb, |
302 | | .xfer_cb = NULL, |
303 | | .xfer_isr = NULL, |
304 | | .sof = NULL |
305 | | }, |
306 | | #endif |
307 | | |
308 | | #if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM |
309 | | { |
310 | | .name = DRIVER_NAME("NET"), |
311 | | .init = netd_init, |
312 | | .deinit = netd_deinit, |
313 | | .reset = netd_reset, |
314 | | .open = netd_open, |
315 | | .control_xfer_cb = netd_control_xfer_cb, |
316 | | .xfer_cb = netd_xfer_cb, |
317 | | .xfer_isr = NULL, |
318 | | .sof = NULL, |
319 | | }, |
320 | | #endif |
321 | | |
322 | | #if CFG_TUD_BTH |
323 | | { |
324 | | .name = DRIVER_NAME("BTH"), |
325 | | .init = btd_init, |
326 | | .deinit = btd_deinit, |
327 | | .reset = btd_reset, |
328 | | .open = btd_open, |
329 | | .control_xfer_cb = btd_control_xfer_cb, |
330 | | .xfer_cb = btd_xfer_cb, |
331 | | .xfer_isr = NULL, |
332 | | .sof = NULL |
333 | | }, |
334 | | #endif |
335 | | |
336 | | #if CFG_TUD_MTP |
337 | | { |
338 | | .name = DRIVER_NAME("MTP"), |
339 | | .init = mtpd_init, |
340 | | .deinit = mtpd_deinit, |
341 | | .reset = mtpd_reset, |
342 | | .open = mtpd_open, |
343 | | .control_xfer_cb = mtpd_control_xfer_cb, |
344 | | .xfer_cb = mtpd_xfer_cb, |
345 | | .xfer_isr = NULL, |
346 | | .sof = NULL |
347 | | }, |
348 | | #endif |
349 | | |
350 | | #if CFG_TUD_PRINTER |
351 | | { |
352 | | .name = DRIVER_NAME("PRINTER"), |
353 | | .init = printerd_init, |
354 | | .deinit = printerd_deinit, |
355 | | .reset = printerd_reset, |
356 | | .open = printerd_open, |
357 | | .control_xfer_cb = printerd_control_xfer_cb, |
358 | | .xfer_cb = printerd_xfer_cb, |
359 | | .sof = NULL |
360 | | }, |
361 | | #endif |
362 | | }; |
363 | | |
364 | | // Additional class drivers implemented by application |
365 | | static const usbd_class_driver_t *_app_driver = NULL; |
366 | | static const uint8_t _builtin_driver_count = TU_ARRAY_SIZE(_usbd_driver); |
367 | | static uint8_t _app_driver_count = 0; |
368 | | |
369 | 119k | #define TOTAL_DRIVER_COUNT ((uint8_t) (_app_driver_count + _builtin_driver_count)) |
370 | | |
371 | | // virtually joins built-in and application drivers together. |
372 | | // Application is positioned first to allow overwriting built-in ones. |
373 | 82.8k | TU_ATTR_ALWAYS_INLINE static inline usbd_class_driver_t const * get_driver(uint8_t drvid) { |
374 | 82.8k | usbd_class_driver_t const *driver = NULL; |
375 | 82.8k | if (drvid < _app_driver_count) { |
376 | | // Application drivers |
377 | 0 | driver = &_app_driver[drvid]; |
378 | 82.8k | } else{ |
379 | 82.8k | drvid -= _app_driver_count; |
380 | 82.8k | if (_builtin_driver_count > 0 && drvid < _builtin_driver_count) { |
381 | 75.0k | driver = &_usbd_driver[drvid]; |
382 | 75.0k | } |
383 | 82.8k | } |
384 | | |
385 | 82.8k | return driver; |
386 | 82.8k | } |
387 | | |
388 | | //--------------------------------------------------------------------+ |
389 | | // DCD Event |
390 | | //--------------------------------------------------------------------+ |
391 | | enum { |
392 | | RHPORT_INVALID = 0xFFu |
393 | | }; |
394 | | static uint8_t _usbd_rhport = RHPORT_INVALID; |
395 | | |
396 | | static OSAL_SPINLOCK_DEF(_usbd_spin, usbd_int_set); |
397 | | |
398 | | // Event queue: usbd_int_set() is used as mutex in OS NONE config |
399 | | OSAL_QUEUE_DEF(usbd_int_set, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t); |
400 | | static osal_queue_t _usbd_q; |
401 | | |
402 | | // held by usbd_defer_func_after_queue(), set and run by the usbd task only |
403 | | static struct { |
404 | | osal_task_func_t func; |
405 | | void* param; |
406 | | } _usbd_after_queue; |
407 | | |
408 | | // Mutex for claiming endpoint |
409 | | #if OSAL_MUTEX_REQUIRED |
410 | | static osal_mutex_def_t _ubsd_mutexdef; |
411 | | static osal_mutex_t _usbd_mutex; |
412 | | #else |
413 | 7.09k | #define _usbd_mutex NULL |
414 | | #endif |
415 | | |
416 | 118k | TU_ATTR_ALWAYS_INLINE static inline bool queue_event(dcd_event_t const * event, bool in_isr) { |
417 | 118k | TU_ASSERT(osal_queue_send(_usbd_q, event, in_isr)); |
418 | 118k | tud_event_hook_cb(event->rhport, event->event_id, in_isr); |
419 | 118k | return true; |
420 | 118k | } |
421 | | |
422 | | //--------------------------------------------------------------------+ |
423 | | // Prototypes |
424 | | //--------------------------------------------------------------------+ |
425 | | static bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); |
426 | | static bool process_setup_received(uint8_t rhport, tusb_control_request_t const * p_request); |
427 | | static bool process_get_status(uint8_t rhport, tusb_control_request_t const * request, uint16_t status); |
428 | | static bool process_set_config(uint8_t rhport, uint8_t cfg_num); |
429 | | static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request); |
430 | | |
431 | | #if CFG_TUD_TEST_MODE |
432 | | static bool process_test_mode_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) { |
433 | | TU_VERIFY(CONTROL_STAGE_ACK == stage); |
434 | | uint8_t const selector = tu_u16_high(request->wIndex); |
435 | | TU_LOG_USBD(" Enter Test Mode (test selector index: %d)\r\n", selector); |
436 | | dcd_enter_test_mode(rhport, (tusb_feature_test_mode_t) selector); |
437 | | return true; |
438 | | } |
439 | | #endif |
440 | | |
441 | | //--------------------------------------------------------------------+ |
442 | | // Weak stubs: invoked if no strong implementation is available |
443 | | //--------------------------------------------------------------------+ |
444 | 3 | TU_ATTR_WEAK usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) { |
445 | 3 | *driver_count = 0; |
446 | 3 | return NULL; |
447 | 3 | } |
448 | | |
449 | 0 | TU_ATTR_WEAK bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes, bool is_isr) { |
450 | 0 | (void) rhport; (void) ep_addr; (void) ff; (void) total_bytes; (void) is_isr; |
451 | 0 | return false; |
452 | 0 | } |
453 | | |
454 | 0 | TU_ATTR_WEAK bool dcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) { |
455 | 0 | (void) rhport; (void) cfg_id; (void) cfg_param; |
456 | 0 | return false; |
457 | 0 | } |
458 | | |
459 | | //--------------------------------------------------------------------+ |
460 | | // Debug |
461 | | //--------------------------------------------------------------------+ |
462 | | #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL |
463 | | static char const *const _usbd_event_str[DCD_EVENT_COUNT] = { |
464 | | "Invalid", |
465 | | "Bus Reset Start", |
466 | | "Bus Reset End", |
467 | | "Unplugged", |
468 | | "SOF", |
469 | | "Suspend", |
470 | | "Resume", |
471 | | "Setup Received", |
472 | | "Xfer Complete", |
473 | | "Func Call" |
474 | | }; |
475 | | |
476 | | #endif |
477 | | |
478 | | //--------------------------------------------------------------------+ |
479 | | // Application API |
480 | | //--------------------------------------------------------------------+ |
481 | 0 | tusb_speed_t tud_speed_get(void) { |
482 | 0 | return (tusb_speed_t) _usbd_dev.speed; |
483 | 0 | } |
484 | | |
485 | 0 | bool tud_connected(void) { |
486 | 0 | return _usbd_dev.connected; |
487 | 0 | } |
488 | | |
489 | 30.9k | bool tud_mounted(void) { |
490 | 30.9k | return _usbd_dev.cfg_num ? true : false; |
491 | 30.9k | } |
492 | | |
493 | 30.9k | bool tud_suspended(void) { |
494 | 30.9k | return _usbd_dev.suspended; |
495 | 30.9k | } |
496 | | |
497 | 0 | bool tud_remote_wakeup(void) { |
498 | | // only wake up host if this feature is enabled and we are suspended |
499 | 0 | TU_VERIFY(_usbd_dev.suspended && _usbd_dev.remote_wakeup_en); |
500 | 0 | dcd_remote_wakeup(_usbd_rhport); |
501 | 0 | return true; |
502 | 0 | } |
503 | | |
504 | 0 | bool tud_disconnect(void) { |
505 | 0 | dcd_disconnect(_usbd_rhport); |
506 | 0 | return true; |
507 | 0 | } |
508 | | |
509 | 0 | bool tud_connect(void) { |
510 | 0 | dcd_connect(_usbd_rhport); |
511 | 0 | return true; |
512 | 0 | } |
513 | | |
514 | 0 | void tud_sof_cb_enable(bool en) { |
515 | 0 | usbd_sof_enable(_usbd_rhport, SOF_CONSUMER_USER, en); |
516 | 0 | } |
517 | | |
518 | 206k | bool tud_inited(void) { |
519 | 206k | return _usbd_rhport != RHPORT_INVALID; |
520 | 206k | } |
521 | | |
522 | 0 | bool tud_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) { |
523 | 0 | return dcd_configure(rhport, cfg_id, cfg_param); |
524 | 0 | } |
525 | | |
526 | 4.30k | bool tud_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { |
527 | 4.30k | if (tud_inited()) { |
528 | 4.30k | return true; // skip if already initialized |
529 | 4.30k | } |
530 | 3 | TU_ASSERT(rh_init); |
531 | | #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL |
532 | | char const* speed_str = 0; |
533 | | switch (rh_init->speed) { |
534 | | case TUSB_SPEED_HIGH: |
535 | | speed_str = "High"; |
536 | | break; |
537 | | case TUSB_SPEED_FULL: |
538 | | speed_str = "Full"; |
539 | | break; |
540 | | case TUSB_SPEED_LOW: |
541 | | speed_str = "Low"; |
542 | | break; |
543 | | case TUSB_SPEED_AUTO: |
544 | | speed_str = "Auto"; |
545 | | break; |
546 | | default: |
547 | | break; |
548 | | } |
549 | | TU_LOG_USBD("USBD init on controller %u, speed = %s\r\n", rhport, speed_str); |
550 | | TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(usbd_device_t)); |
551 | | TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(dcd_event_t)); |
552 | | TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_fifo_t)); |
553 | | TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_edpt_stream_t)); |
554 | | #endif |
555 | | |
556 | 3 | tu_varclr(&_usbd_dev); |
557 | 3 | _usbd_queued_setup = 0; |
558 | | |
559 | 3 | osal_spin_init(&_usbd_spin); |
560 | | |
561 | | #if OSAL_MUTEX_REQUIRED |
562 | | // Init device mutex |
563 | | _usbd_mutex = osal_mutex_create(&_ubsd_mutexdef); |
564 | | TU_ASSERT(_usbd_mutex); |
565 | | #endif |
566 | | |
567 | | // Init device queue & task |
568 | 3 | _usbd_q = osal_queue_create(&_usbd_qdef); |
569 | 3 | TU_ASSERT(_usbd_q); |
570 | | |
571 | | // Get application driver if available |
572 | 3 | _app_driver = usbd_app_driver_get_cb(&_app_driver_count); |
573 | 3 | TU_ASSERT(_app_driver_count + _builtin_driver_count <= UINT8_MAX); |
574 | | |
575 | | // Init class drivers |
576 | 7 | for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { |
577 | 4 | usbd_class_driver_t const* driver = get_driver(i); |
578 | 4 | TU_ASSERT(driver && driver->init); |
579 | 4 | TU_LOG_USBD("%s init\r\n", driver->name); |
580 | 4 | driver->init(); |
581 | 4 | } |
582 | | |
583 | 3 | _usbd_rhport = rhport; |
584 | | |
585 | | // Init device controller driver |
586 | 3 | TU_ASSERT(dcd_init(rhport, rh_init)); |
587 | 3 | dcd_int_enable(rhport); |
588 | | |
589 | 3 | return true; |
590 | 3 | } |
591 | | |
592 | 0 | bool tud_deinit(uint8_t rhport) { |
593 | 0 | if (!tud_inited()) { |
594 | 0 | return true; // skip if not initialized |
595 | 0 | } |
596 | | |
597 | 0 | TU_LOG_USBD("USBD deinit on controller %u\r\n", rhport); |
598 | |
|
599 | 0 | const uint8_t cfg_num = _usbd_dev.cfg_num; |
600 | | |
601 | | // Deinit device controller driver |
602 | 0 | dcd_int_disable(rhport); |
603 | 0 | dcd_disconnect(rhport); |
604 | 0 | TU_ASSERT(dcd_deinit(rhport)); |
605 | | |
606 | | // Deinit class drivers |
607 | 0 | for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { |
608 | 0 | usbd_class_driver_t const* driver = get_driver(i); |
609 | 0 | if(driver && driver->deinit) { |
610 | 0 | TU_LOG_USBD("%s deinit\r\n", driver->name); |
611 | 0 | driver->deinit(); |
612 | 0 | } |
613 | 0 | } |
614 | |
|
615 | 0 | tu_varclr(&_usbd_dev); // Clear device data |
616 | | |
617 | | // Deinit device queue & task |
618 | 0 | osal_queue_delete(_usbd_q); |
619 | 0 | _usbd_q = NULL; |
620 | 0 | tu_varclr(&_usbd_after_queue); |
621 | |
|
622 | | #if OSAL_MUTEX_REQUIRED |
623 | | // TODO make sure there is no task waiting on this mutex |
624 | | osal_mutex_delete(_usbd_mutex); |
625 | | _usbd_mutex = NULL; |
626 | | #endif |
627 | |
|
628 | 0 | osal_spin_deinit(&_usbd_spin); |
629 | |
|
630 | 0 | _usbd_rhport = RHPORT_INVALID; |
631 | |
|
632 | 0 | if (cfg_num > 0) { |
633 | 0 | tud_umount_cb(); |
634 | 0 | } |
635 | |
|
636 | 0 | return true; |
637 | 0 | } |
638 | | |
639 | 45.2k | static void configuration_reset(uint8_t rhport) { |
640 | 107k | for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { |
641 | 61.7k | usbd_class_driver_t const* driver = get_driver(i); |
642 | 61.7k | TU_ASSERT(driver,); |
643 | 61.7k | driver->reset(rhport); |
644 | 61.7k | } |
645 | | |
646 | 45.2k | tu_varclr(&_usbd_dev); |
647 | 45.2k | (void)memset(_usbd_dev.itf2drv, TUSB_INDEX_INVALID_8, sizeof(_usbd_dev.itf2drv)); // invalid mapping |
648 | 45.2k | (void)memset(_usbd_dev.ep2drv, TUSB_INDEX_INVALID_8, sizeof(_usbd_dev.ep2drv)); // invalid mapping |
649 | 45.2k | } |
650 | | |
651 | 43.3k | static void usbd_reset(uint8_t rhport) { |
652 | 43.3k | configuration_reset(rhport); |
653 | | // discard any pre-reset SETUP still counted: a stale count skips post-reset SETUPs |
654 | 43.3k | _usbd_queued_setup = 0; |
655 | 43.3k | } |
656 | | |
657 | 0 | bool tud_task_event_ready(void) { |
658 | 0 | TU_VERIFY(tud_inited()); // Skip if stack is not initialized |
659 | 0 | return !osal_queue_empty(_usbd_q) || _usbd_after_queue.func != NULL; |
660 | 0 | } |
661 | | |
662 | | // the call usbd_defer_func_after_queue() holds; false if none |
663 | 202k | static bool run_after_queue_call(void) { |
664 | 202k | osal_task_func_t const func = _usbd_after_queue.func; |
665 | 202k | if (func == NULL) { |
666 | 202k | return false; |
667 | 202k | } |
668 | 0 | _usbd_after_queue.func = NULL; |
669 | 0 | func(_usbd_after_queue.param); |
670 | 0 | return true; |
671 | 202k | } |
672 | | |
673 | | //--------------------------------------------------------------------+ |
674 | | // USBD Task |
675 | | //--------------------------------------------------------------------+ |
676 | | /* USB Device Driver task |
677 | | * This top level thread manages all device controller event and delegates events to class-specific drivers. |
678 | | * This should be called periodically within the mainloop or rtos thread. |
679 | | * |
680 | | int main(void) { |
681 | | application_init(); |
682 | | tusb_init(0, TUSB_ROLE_DEVICE); |
683 | | |
684 | | while(1) { // the mainloop |
685 | | application_code(); |
686 | | tud_task(); // tinyusb device task |
687 | | } |
688 | | } |
689 | | */ |
690 | 202k | void tud_task_ext(uint32_t timeout_ms, bool in_isr) { |
691 | 202k | (void) in_isr; // not implemented yet |
692 | | |
693 | | // Skip if stack is not initialized |
694 | 202k | if (!tud_inited()) { |
695 | 0 | return; |
696 | 0 | } |
697 | | |
698 | | // Loop until there are no more events in the queue or CFG_TUD_TASK_EVENTS_PER_RUN is reached |
699 | 320k | for (unsigned epr = 0;; epr++) { |
700 | 320k | #if CFG_TUD_TASK_EVENTS_PER_RUN > 0 |
701 | 320k | if (epr >= CFG_TUD_TASK_EVENTS_PER_RUN) { |
702 | 0 | TU_LOG_USBD("USBD event limit (" TU_XSTRING(CFG_TUD_TASK_EVENTS_PER_RUN) ") reached\r\n"); |
703 | 0 | break; |
704 | 0 | } |
705 | 320k | #endif |
706 | 320k | dcd_event_t event; |
707 | 320k | if (!osal_queue_receive(_usbd_q, &event, (_usbd_after_queue.func != NULL) ? 0 : timeout_ms)) { |
708 | 202k | if (!run_after_queue_call()) { |
709 | 202k | return; |
710 | 202k | } |
711 | 0 | timeout_ms = 0; |
712 | 0 | continue; |
713 | 202k | } |
714 | | |
715 | | #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL |
716 | | if (event.event_id == DCD_EVENT_SETUP_RECEIVED) { |
717 | | TU_LOG_USBD("\r\n"); // extra line for setup |
718 | | } |
719 | | TU_LOG_USBD("USBD %s ", event.event_id < DCD_EVENT_COUNT ? _usbd_event_str[event.event_id] : "CORRUPTED"); |
720 | | #endif |
721 | | |
722 | 118k | switch (event.event_id) { |
723 | 0 | case DCD_EVENT_BUS_RESET_START: |
724 | 0 | TU_LOG_USBD("\r\n"); |
725 | 0 | usbd_reset(event.rhport); |
726 | 0 | break; |
727 | | |
728 | 28.5k | case DCD_EVENT_BUS_RESET_END: |
729 | 28.5k | TU_LOG_USBD(": %s Speed\r\n", tu_str_speed[event.bus_reset.speed]); |
730 | | // TODO a DCD that reports both edges pays for two teardowns: track a per-rhport |
731 | | // "start seen" flag and skip this reset, keeping it for the single-event DCDs. |
732 | 28.5k | usbd_reset(event.rhport); |
733 | 28.5k | _usbd_dev.speed = event.bus_reset.speed; |
734 | 28.5k | break; |
735 | | |
736 | 14.8k | case DCD_EVENT_UNPLUGGED: |
737 | 14.8k | TU_LOG_USBD("\r\n"); |
738 | 14.8k | usbd_reset(event.rhport); |
739 | 14.8k | tud_umount_cb(); |
740 | 14.8k | break; |
741 | | |
742 | 64.8k | case DCD_EVENT_SETUP_RECEIVED: |
743 | 64.8k | if (_usbd_queued_setup == 0) { |
744 | 17.1k | break; |
745 | 17.1k | } |
746 | 47.7k | _usbd_queued_setup--; |
747 | 47.7k | TU_LOG_BUF(CFG_TUD_LOG_LEVEL, &event.setup_received, 8); |
748 | 47.7k | if (_usbd_queued_setup != 0) { |
749 | 0 | TU_LOG_USBD(" Skipped since there is other SETUP in queue\r\n"); |
750 | 0 | break; |
751 | 0 | } |
752 | 47.7k | if (!dcd_edpt0_setup_begin(event.rhport, event.setup_tagid)) { |
753 | 0 | TU_LOG_USBD(" Skipped since DCD saw a newer SETUP or bus reset\r\n"); |
754 | 0 | break; |
755 | 0 | } |
756 | | |
757 | | // Mark as connected after receiving 1st setup packet. |
758 | | // But it is easier to set it every time instead of wasting time to check then set |
759 | 47.7k | _usbd_dev.connected = 1; |
760 | | |
761 | | // reset ep state |
762 | 47.7k | _usbd_dev.ep_status[0][TUSB_DIR_OUT] = 0; |
763 | 47.7k | _usbd_dev.ep_status[0][TUSB_DIR_IN] = 0; |
764 | | |
765 | | // Process control request |
766 | 47.7k | if (!process_setup_received(event.rhport, &event.setup_received)) { |
767 | 31.5k | TU_LOG_USBD(" Stall EP0\r\n"); |
768 | | // Failed -> stall both control endpoint IN and OUT |
769 | 31.5k | dcd_edpt_stall(event.rhport, TU_EP0_OUT); |
770 | 31.5k | dcd_edpt_stall(event.rhport, TU_EP0_IN); |
771 | 31.5k | } |
772 | 47.7k | break; |
773 | | |
774 | 0 | case DCD_EVENT_XFER_COMPLETE: { |
775 | | // Invoke the class callback associated with the endpoint address |
776 | 0 | uint8_t const ep_addr = event.xfer_complete.ep_addr; |
777 | 0 | uint8_t const epnum = tu_edpt_number(ep_addr); |
778 | 0 | uint8_t const ep_dir = tu_edpt_dir(ep_addr); |
779 | |
|
780 | 0 | TU_LOG_USBD("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len); |
781 | |
|
782 | 0 | usbd_class_driver_t const* driver = NULL; // EP0 has no class driver |
783 | 0 | if (0 != epnum) { |
784 | 0 | driver = get_driver(_usbd_dev.ep2drv[epnum][ep_dir]); |
785 | 0 | TU_ASSERT(driver,); |
786 | 0 | } |
787 | | |
788 | | // Clear busy + claimed. An OUT endpoint changed to RX_PENDING, so no other task can claim and re-arm it before |
789 | | // its buffer is consumed |
790 | 0 | uint8_t const rx_pending = (0 != epnum && ep_dir == TUSB_DIR_OUT) ? TU_EDPT_STATE_RX_PENDING : 0u; |
791 | 0 | _usbd_dev.ep_status[epnum][ep_dir] = (uint8_t) ( |
792 | 0 | (_usbd_dev.ep_status[epnum][ep_dir] & ~(TU_EDPT_STATE_BUSY | TU_EDPT_STATE_CLAIMED)) | rx_pending); |
793 | |
|
794 | 0 | if (driver == NULL) { |
795 | | // Not stalled on failure: a DCD refuses an EP0 prime when a newer setup is already |
796 | | // latched, and EP0 stalls are cleared by hardware when that setup arrives - so a stall |
797 | | // issued here lands after the auto-clear and would stall the transfer that superseded |
798 | | // this one. The pending setup re-drives EP0 by itself. |
799 | 0 | if (!usbd_control_xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, |
800 | 0 | event.xfer_complete.len)) { |
801 | 0 | TU_LOG_USBD(" Control stage not continued\r\n"); |
802 | 0 | } |
803 | 0 | } else { |
804 | 0 | TU_LOG_USBD(" %s xfer callback\r\n", driver->name); |
805 | 0 | driver->xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len); |
806 | 0 | } |
807 | 0 | break; |
808 | 0 | } |
809 | | |
810 | 5.16k | case DCD_EVENT_SUSPEND: |
811 | | // NOTE: When plugging/unplugging device, the D+/D- state are unstable and |
812 | | // can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ), which result in a series of event |
813 | | // e.g suspend -> resume -> unplug/plug. Skip suspend/resume if not connected |
814 | 5.16k | if (_usbd_dev.connected) { |
815 | 5.16k | TU_LOG_USBD(": Remote Wakeup = %u\r\n", _usbd_dev.remote_wakeup_en); |
816 | 5.16k | tud_suspend_cb(_usbd_dev.remote_wakeup_en); |
817 | 5.16k | } else { |
818 | 0 | TU_LOG_USBD(" Skipped\r\n"); |
819 | 0 | } |
820 | 5.16k | break; |
821 | | |
822 | 4.99k | case DCD_EVENT_RESUME: |
823 | 4.99k | if (_usbd_dev.connected) { |
824 | 4.99k | TU_LOG_USBD("\r\n"); |
825 | 4.99k | tud_resume_cb(); |
826 | 4.99k | } else { |
827 | 0 | TU_LOG_USBD(" Skipped\r\n"); |
828 | 0 | } |
829 | 4.99k | break; |
830 | | |
831 | 0 | case USBD_EVENT_FUNC_CALL: |
832 | 0 | TU_LOG_USBD("\r\n"); |
833 | 0 | if (event.func_call.func != NULL) { |
834 | 0 | event.func_call.func(event.func_call.param); |
835 | 0 | } |
836 | 0 | break; |
837 | | |
838 | 0 | case DCD_EVENT_SOF: |
839 | 0 | if (tu_bit_test(_usbd_dev.sof_consumer, SOF_CONSUMER_USER)) { |
840 | 0 | TU_LOG_USBD("\r\n"); |
841 | 0 | tud_sof_cb(event.sof.frame_count); |
842 | 0 | } |
843 | 0 | break; |
844 | | |
845 | 0 | default: |
846 | 0 | TU_BREAKPOINT(); |
847 | 0 | break; |
848 | 118k | } |
849 | | |
850 | | // allow to exit tud_task() if there is no event in the next run |
851 | 118k | timeout_ms = 0; |
852 | 118k | } |
853 | 202k | } |
854 | | |
855 | | //--------------------------------------------------------------------+ |
856 | | // Control Endpoint |
857 | | //--------------------------------------------------------------------+ |
858 | | |
859 | | // Weak hook: invoked when the stack starts processing a SETUP |
860 | 47.7k | TU_ATTR_WEAK bool dcd_edpt0_setup_begin(uint8_t rhport, uint8_t setup_tagid) { |
861 | 47.7k | (void) rhport; |
862 | 47.7k | (void) setup_tagid; |
863 | 47.7k | return true; |
864 | 47.7k | } |
865 | | |
866 | | // Weak hook: invoked when the control transfer's status stage completes |
867 | 0 | TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, const tusb_control_request_t* request) { |
868 | 0 | (void) rhport; |
869 | 0 | (void) request; |
870 | 0 | } |
871 | | |
872 | 0 | uint8_t* usbd_get_ctrl_buf(void) { |
873 | 0 | return _ctrl_epbuf.buf; |
874 | 0 | } |
875 | | |
876 | | // Endpoint used for the Status stage of a control transfer. |
877 | | // Per USB 2.0 §9.3.1, when wLength == 0 the Direction bit is ignored and the Status |
878 | | // stage is always IN. Otherwise the Status stage is opposite of the Data stage direction. |
879 | 11.0k | TU_ATTR_ALWAYS_INLINE static inline uint8_t status_stage_ep(const tusb_control_request_t* request) { |
880 | 11.0k | return (request->wLength != 0 && request->bmRequestType_bit.direction) ? TU_EP0_OUT : TU_EP0_IN; |
881 | 11.0k | } |
882 | | |
883 | | // Queue ZLP status transaction |
884 | 12.6k | TU_ATTR_ALWAYS_INLINE static inline bool status_stage_xact(uint8_t rhport, uint8_t ep_status) { |
885 | 12.6k | return usbd_edpt_xfer(rhport, ep_status, NULL, 0, false); |
886 | 12.6k | } |
887 | | |
888 | | // Queue a transaction in Data Stage. Each transaction has up to Endpoint0's max |
889 | | // packet size. This function can also transfer a zero-length packet. |
890 | 17.6k | static bool data_stage_xact(uint8_t rhport) { |
891 | 17.6k | usbd_control_xfer_t* const ctrl_xfer = &_usbd_dev.ctrl_xfer; |
892 | 17.6k | const uint16_t xact_len = tu_min16(ctrl_xfer->data_len - ctrl_xfer->total_xferred, CFG_TUD_ENDPOINT0_BUFSIZE); |
893 | 17.6k | uint8_t ep_addr = TU_EP0_OUT; |
894 | | |
895 | 17.6k | if (ctrl_xfer->request.bmRequestType_bit.direction == TUSB_DIR_IN) { |
896 | 5.39k | ep_addr = TU_EP0_IN; |
897 | 5.39k | if (0u != xact_len && ctrl_xfer->buffer != _ctrl_epbuf.buf) { |
898 | 5.39k | TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_BUFSIZE, ctrl_xfer->buffer, xact_len)); |
899 | 5.39k | } |
900 | 5.39k | } |
901 | | |
902 | 17.6k | return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _ctrl_epbuf.buf : NULL, xact_len, false); |
903 | 17.6k | } Line | Count | Source | 890 | 5.87k | static bool data_stage_xact(uint8_t rhport) { | 891 | 5.87k | usbd_control_xfer_t* const ctrl_xfer = &_usbd_dev.ctrl_xfer; | 892 | 5.87k | const uint16_t xact_len = tu_min16(ctrl_xfer->data_len - ctrl_xfer->total_xferred, CFG_TUD_ENDPOINT0_BUFSIZE); | 893 | 5.87k | uint8_t ep_addr = TU_EP0_OUT; | 894 | | | 895 | 5.87k | if (ctrl_xfer->request.bmRequestType_bit.direction == TUSB_DIR_IN) { | 896 | 1.79k | ep_addr = TU_EP0_IN; | 897 | 1.79k | if (0u != xact_len && ctrl_xfer->buffer != _ctrl_epbuf.buf) { | 898 | 1.79k | TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_BUFSIZE, ctrl_xfer->buffer, xact_len)); | 899 | 1.79k | } | 900 | 1.79k | } | 901 | | | 902 | 5.87k | return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _ctrl_epbuf.buf : NULL, xact_len, false); | 903 | 5.87k | } |
Line | Count | Source | 890 | 5.87k | static bool data_stage_xact(uint8_t rhport) { | 891 | 5.87k | usbd_control_xfer_t* const ctrl_xfer = &_usbd_dev.ctrl_xfer; | 892 | 5.87k | const uint16_t xact_len = tu_min16(ctrl_xfer->data_len - ctrl_xfer->total_xferred, CFG_TUD_ENDPOINT0_BUFSIZE); | 893 | 5.87k | uint8_t ep_addr = TU_EP0_OUT; | 894 | | | 895 | 5.87k | if (ctrl_xfer->request.bmRequestType_bit.direction == TUSB_DIR_IN) { | 896 | 1.79k | ep_addr = TU_EP0_IN; | 897 | 1.79k | if (0u != xact_len && ctrl_xfer->buffer != _ctrl_epbuf.buf) { | 898 | 1.79k | TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_BUFSIZE, ctrl_xfer->buffer, xact_len)); | 899 | 1.79k | } | 900 | 1.79k | } | 901 | | | 902 | 5.87k | return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _ctrl_epbuf.buf : NULL, xact_len, false); | 903 | 5.87k | } |
Line | Count | Source | 890 | 5.87k | static bool data_stage_xact(uint8_t rhport) { | 891 | 5.87k | usbd_control_xfer_t* const ctrl_xfer = &_usbd_dev.ctrl_xfer; | 892 | 5.87k | const uint16_t xact_len = tu_min16(ctrl_xfer->data_len - ctrl_xfer->total_xferred, CFG_TUD_ENDPOINT0_BUFSIZE); | 893 | 5.87k | uint8_t ep_addr = TU_EP0_OUT; | 894 | | | 895 | 5.87k | if (ctrl_xfer->request.bmRequestType_bit.direction == TUSB_DIR_IN) { | 896 | 1.79k | ep_addr = TU_EP0_IN; | 897 | 1.79k | if (0u != xact_len && ctrl_xfer->buffer != _ctrl_epbuf.buf) { | 898 | 1.79k | TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_BUFSIZE, ctrl_xfer->buffer, xact_len)); | 899 | 1.79k | } | 900 | 1.79k | } | 901 | | | 902 | 5.87k | return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _ctrl_epbuf.buf : NULL, xact_len, false); | 903 | 5.87k | } |
|
904 | | |
905 | | // Status phase |
906 | 11.0k | bool tud_control_status(uint8_t rhport, const tusb_control_request_t* request) { |
907 | | // _usbd_dev.ctrl_xfer fields are pre-initialized at process_setup_received entry |
908 | 11.0k | (void) request; |
909 | 11.0k | return status_stage_xact(rhport, status_stage_ep(&_usbd_dev.ctrl_xfer.request)); |
910 | 11.0k | } |
911 | | |
912 | | // Transmit data to/from the control endpoint. If wLength is zero, a status packet is sent instead. |
913 | 7.47k | bool tud_control_xfer(uint8_t rhport, const tusb_control_request_t* request, void* buffer, uint16_t len) { |
914 | | // _usbd_dev.ctrl_xfer.request and reset fields are pre-initialized at process_setup_received entry |
915 | 7.47k | (void) request; |
916 | 7.47k | usbd_control_xfer_t* const ctrl_xfer = &_usbd_dev.ctrl_xfer; |
917 | 7.47k | ctrl_xfer->buffer = (uint8_t*) buffer; |
918 | 7.47k | ctrl_xfer->data_len = tu_min16(len, ctrl_xfer->request.wLength); |
919 | | |
920 | 7.47k | if (ctrl_xfer->request.wLength > 0U) { |
921 | 5.87k | if (ctrl_xfer->data_len > 0U) { |
922 | 5.87k | TU_ASSERT(buffer); |
923 | 5.87k | } |
924 | 5.87k | TU_VERIFY(data_stage_xact(rhport)); |
925 | 5.87k | } else { |
926 | | // wLength == 0: Status stage is always IN per USB 2.0 §9.3.1 |
927 | 1.59k | TU_VERIFY(status_stage_xact(rhport, TU_EP0_IN)); |
928 | 1.59k | } |
929 | | |
930 | 3.17k | return true; |
931 | 7.47k | } |
932 | | |
933 | | // Callback when a transaction completes on the DATA stage or Status stage of EP0 |
934 | 0 | static bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { |
935 | 0 | (void) result; |
936 | 0 | usbd_control_xfer_t* const ctrl_xfer = &_usbd_dev.ctrl_xfer; |
937 | | |
938 | | // Status Stage complete: ep_addr matches the resolved Status stage endpoint |
939 | 0 | uint8_t const ep_status = status_stage_ep(&ctrl_xfer->request); |
940 | 0 | if (ep_addr == ep_status) { |
941 | 0 | TU_ASSERT(0 == xferred_bytes); |
942 | | |
943 | | // invoke optional dcd hook if available |
944 | 0 | dcd_edpt0_status_complete(rhport, &ctrl_xfer->request); |
945 | |
|
946 | 0 | if (NULL != ctrl_xfer->complete_cb) { |
947 | 0 | ctrl_xfer->complete_cb(rhport, CONTROL_STAGE_ACK, &ctrl_xfer->request); |
948 | 0 | } |
949 | |
|
950 | 0 | return true; |
951 | 0 | } |
952 | | |
953 | | // Data stage progress |
954 | 0 | if (ctrl_xfer->request.bmRequestType_bit.direction == TUSB_DIR_OUT) { |
955 | 0 | TU_VERIFY(ctrl_xfer->buffer); |
956 | | // Clamp host overrun to remaining capacity (data_len) so memcpy can't overflow the caller buffer |
957 | 0 | xferred_bytes = tu_min32(xferred_bytes, ctrl_xfer->data_len - ctrl_xfer->total_xferred); |
958 | 0 | if (ctrl_xfer->buffer != _ctrl_epbuf.buf) { |
959 | 0 | memcpy(ctrl_xfer->buffer, _ctrl_epbuf.buf, xferred_bytes); |
960 | 0 | } |
961 | 0 | TU_LOG_MEM(CFG_TUD_LOG_LEVEL, ctrl_xfer->buffer, xferred_bytes, 2); |
962 | 0 | } |
963 | | |
964 | 0 | ctrl_xfer->total_xferred += (uint16_t) xferred_bytes; |
965 | 0 | ctrl_xfer->buffer += xferred_bytes; |
966 | | |
967 | | // Data Stage complete when wLength reached or short packet (incl. ZLP) seen |
968 | 0 | if ((ctrl_xfer->request.wLength == ctrl_xfer->total_xferred) || |
969 | 0 | (xferred_bytes < CFG_TUD_ENDPOINT0_BUFSIZE)) { |
970 | 0 | bool is_ok = true; |
971 | |
|
972 | 0 | if (NULL != ctrl_xfer->complete_cb) { |
973 | | // Callback can still stall control in status phase, e.g. OUT data doesn't make sense |
974 | 0 | is_ok = ctrl_xfer->complete_cb(rhport, CONTROL_STAGE_DATA, &ctrl_xfer->request); |
975 | 0 | } |
976 | |
|
977 | 0 | if (is_ok) { |
978 | 0 | TU_VERIFY(status_stage_xact(rhport, ep_status)); |
979 | 0 | } else { |
980 | | // Stall both IN and OUT control endpoint |
981 | 0 | dcd_edpt_stall(rhport, TU_EP0_OUT); |
982 | 0 | dcd_edpt_stall(rhport, TU_EP0_IN); |
983 | 0 | } |
984 | 0 | } else { |
985 | | // More data to transfer |
986 | 0 | TU_VERIFY(data_stage_xact(rhport)); |
987 | 0 | } |
988 | | |
989 | 0 | return true; |
990 | 0 | } Unexecuted instantiation: usbd.c:usbd_control_xfer_cb Unexecuted instantiation: usbd.c:usbd_control_xfer_cb Unexecuted instantiation: usbd.c:usbd_control_xfer_cb |
991 | | |
992 | | //--------------------------------------------------------------------+ |
993 | | // Control Request Parser & Handling |
994 | | //--------------------------------------------------------------------+ |
995 | | |
996 | | // Helper to invoke class driver control request handler |
997 | 4.56k | static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * driver, tusb_control_request_t const * request) { |
998 | 4.56k | _usbd_dev.ctrl_xfer.complete_cb = driver->control_xfer_cb; |
999 | 4.56k | TU_LOG_USBD(" %s control request\r\n", driver->name); |
1000 | 4.56k | return driver->control_xfer_cb(rhport, CONTROL_STAGE_SETUP, request); |
1001 | 4.56k | } |
1002 | | |
1003 | | // Process a standard request to the device recipient. |
1004 | 27.2k | static bool process_std_device_request(uint8_t rhport, tusb_control_request_t const * p_request) { |
1005 | 27.2k | switch (p_request->bRequest) { //-V2520 |
1006 | 677 | case TUSB_REQ_SET_ADDRESS: |
1007 | | // Depending on mcu, status phase could be sent either before or after changing device address, |
1008 | | // or even require stack to not response with status at all |
1009 | | // Therefore DCD must take full responsibility to response and include zlp status packet if needed. |
1010 | 677 | dcd_set_address(rhport, (uint8_t) p_request->wValue); |
1011 | 677 | _usbd_dev.addressed = 1; |
1012 | 677 | return true; |
1013 | | |
1014 | 987 | case TUSB_REQ_GET_CONFIGURATION: { |
1015 | 987 | uint8_t cfg_num = _usbd_dev.cfg_num; |
1016 | 987 | tud_control_xfer(rhport, p_request, &cfg_num, 1); |
1017 | 987 | return true; |
1018 | 0 | } |
1019 | | |
1020 | 9.13k | case TUSB_REQ_SET_CONFIGURATION: { |
1021 | 9.13k | uint8_t const cfg_num = (uint8_t) p_request->wValue; |
1022 | | |
1023 | | // Only process if new configure is different |
1024 | 9.13k | if (_usbd_dev.cfg_num != cfg_num) { |
1025 | 6.95k | if (_usbd_dev.cfg_num != 0) { |
1026 | | // already configured: need to clear all endpoints and driver first |
1027 | 1.89k | TU_LOG_USBD(" Clear current Configuration (%u) before switching\r\n", _usbd_dev.cfg_num); |
1028 | | |
1029 | 1.89k | dcd_sof_enable(rhport, false); |
1030 | 1.89k | dcd_edpt_close_all(rhport); |
1031 | | |
1032 | | // close all drivers and current configured state except bus speed |
1033 | 1.89k | const uint8_t speed = _usbd_dev.speed; |
1034 | 1.89k | configuration_reset(rhport); |
1035 | | |
1036 | 1.89k | _usbd_dev.speed = speed; // restore speed |
1037 | 1.89k | } |
1038 | | |
1039 | 6.95k | _usbd_dev.cfg_num = cfg_num; |
1040 | | |
1041 | | // Handle the new configuration |
1042 | 6.95k | if (cfg_num == 0) { |
1043 | 145 | tud_umount_cb(); |
1044 | 6.80k | } else { |
1045 | 6.80k | if (!process_set_config(rhport, cfg_num)) { |
1046 | 3.81k | _usbd_dev.cfg_num = 0; |
1047 | 3.81k | TU_ASSERT(false); |
1048 | 3.81k | } |
1049 | 2.99k | tud_mount_cb(); |
1050 | 2.99k | } |
1051 | 6.95k | } |
1052 | | |
1053 | 5.31k | tud_control_status(rhport, p_request); |
1054 | 5.31k | return true; |
1055 | 9.13k | } |
1056 | | |
1057 | 6.90k | case TUSB_REQ_GET_DESCRIPTOR: |
1058 | 6.90k | return process_get_descriptor(rhport, p_request); |
1059 | | |
1060 | 2.94k | case TUSB_REQ_SET_FEATURE: |
1061 | 2.94k | switch (p_request->wValue) { //-V2520 |
1062 | 2.21k | case TUSB_REQ_FEATURE_REMOTE_WAKEUP: |
1063 | 2.21k | TU_LOG_USBD(" Enable Remote Wakeup\r\n"); |
1064 | | // Host may enable remote wake up before suspending especially HID device |
1065 | 2.21k | _usbd_dev.remote_wakeup_en = 1; |
1066 | 2.21k | tud_control_status(rhport, p_request); |
1067 | 2.21k | return true; |
1068 | | |
1069 | | #if CFG_TUD_TEST_MODE |
1070 | | case TUSB_REQ_FEATURE_TEST_MODE: { |
1071 | | // Only handle the test mode if supported and valid |
1072 | | TU_VERIFY(0 == tu_u16_low(p_request->wIndex)); |
1073 | | |
1074 | | uint8_t const selector = tu_u16_high(p_request->wIndex); |
1075 | | TU_VERIFY(TUSB_FEATURE_TEST_J <= selector && selector <= TUSB_FEATURE_TEST_FORCE_ENABLE); |
1076 | | |
1077 | | _usbd_dev.ctrl_xfer.complete_cb = process_test_mode_cb; |
1078 | | tud_control_status(rhport, p_request); |
1079 | | return true; |
1080 | | } |
1081 | | #endif |
1082 | | |
1083 | | // Stall unsupported feature selector |
1084 | 733 | default: return false; |
1085 | 2.94k | } |
1086 | | |
1087 | 2.79k | case TUSB_REQ_CLEAR_FEATURE: |
1088 | | // Only support remote wakeup for device feature |
1089 | 2.79k | TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue); |
1090 | 2.04k | TU_LOG_USBD(" Disable Remote Wakeup\r\n"); |
1091 | | |
1092 | | // Host may disable remote wake up after resuming |
1093 | 2.04k | _usbd_dev.remote_wakeup_en = 0; |
1094 | 2.04k | tud_control_status(rhport, p_request); |
1095 | 2.04k | return true; |
1096 | | |
1097 | 2.27k | case TUSB_REQ_GET_STATUS: { |
1098 | | // Device status bit mask |
1099 | | // - Bit 0: Self Powered TODO must invoke callback to get actual status |
1100 | | // - Bit 1: Remote Wakeup enabled |
1101 | 2.27k | return process_get_status(rhport, p_request, (uint16_t) _usbd_dev.dev_state_bm); |
1102 | 2.79k | } |
1103 | | |
1104 | 1.49k | default: |
1105 | 1.49k | TU_BREAKPOINT(); |
1106 | 1.49k | return false; |
1107 | 27.2k | } |
1108 | 27.2k | } |
1109 | | |
1110 | | |
1111 | | // Reply to a standard GET_STATUS (device/interface/endpoint) with its 2-byte status word. |
1112 | | // GET_STATUS is Device-to-host only; reject a mis-directed (OUT) request rather than handing |
1113 | | // usbd the address of a stack local to write host data into after this frame has returned. |
1114 | 4.92k | static bool process_get_status(uint8_t rhport, tusb_control_request_t const * request, uint16_t status) { |
1115 | 4.92k | TU_VERIFY(request->bmRequestType_bit.direction == TUSB_DIR_IN); |
1116 | 2.10k | return tud_control_xfer(rhport, request, &status, 2); |
1117 | 4.92k | } |
1118 | | |
1119 | | // This handles the actual request and its response. |
1120 | | // Returns false if unable to complete the request, causing caller to stall control endpoints. |
1121 | 47.7k | static bool process_setup_received(uint8_t rhport, tusb_control_request_t const * p_request) { |
1122 | | // Initialize control transfer state for this request. The request copy must be |
1123 | | // visible to usbd_control_xfer_cb when the (asynchronous) status ZLP completes, |
1124 | | // since the SETUP packet event has already gone out of scope by then. |
1125 | 47.7k | usbd_control_xfer_t* const ctrl_xfer = &_usbd_dev.ctrl_xfer; |
1126 | 47.7k | ctrl_xfer->request = *p_request; |
1127 | 47.7k | ctrl_xfer->buffer = NULL; |
1128 | 47.7k | ctrl_xfer->total_xferred = 0; |
1129 | 47.7k | ctrl_xfer->data_len = 0; |
1130 | 47.7k | ctrl_xfer->complete_cb = NULL; |
1131 | | |
1132 | 47.7k | p_request = &ctrl_xfer->request; // re-direct request pointer to internal copy (modifiable for hacking) |
1133 | 47.7k | TU_ASSERT(p_request->bmRequestType_bit.type < TUSB_REQ_TYPE_INVALID); |
1134 | | |
1135 | | // Vendor request |
1136 | 45.8k | if ( p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR ) { |
1137 | 1.24k | ctrl_xfer->complete_cb = tud_vendor_control_xfer_cb; |
1138 | 1.24k | return tud_vendor_control_xfer_cb(rhport, CONTROL_STAGE_SETUP, p_request); |
1139 | 1.24k | } |
1140 | | |
1141 | | #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL |
1142 | | if (TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type && p_request->bRequest <= TUSB_REQ_SYNCH_FRAME) { |
1143 | | TU_LOG_USBD(" %s", tu_str_std_request[p_request->bRequest]); |
1144 | | if (TUSB_REQ_GET_DESCRIPTOR != p_request->bRequest) { |
1145 | | TU_LOG_USBD("\r\n"); |
1146 | | } |
1147 | | } |
1148 | | #endif |
1149 | | |
1150 | 44.5k | switch (p_request->bmRequestType_bit.recipient) { //-V2520 |
1151 | | //------------- Device Requests e.g in enumeration -------------// |
1152 | 29.9k | case TUSB_REQ_RCPT_DEVICE: |
1153 | 29.9k | if ( TUSB_REQ_TYPE_CLASS == p_request->bmRequestType_bit.type ) { |
1154 | 2.72k | uint8_t const itf = tu_u16_low(p_request->wIndex); |
1155 | 2.72k | TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv)); |
1156 | | |
1157 | 2.00k | usbd_class_driver_t const * driver = get_driver(_usbd_dev.itf2drv[itf]); |
1158 | 2.00k | TU_VERIFY(driver); |
1159 | | |
1160 | | // forward to class driver: "non-STD request to Interface" |
1161 | 1.35k | return invoke_class_control(rhport, driver, p_request); |
1162 | 2.00k | } |
1163 | | |
1164 | 27.2k | if (TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type) { |
1165 | | // Non-standard request is not supported |
1166 | 0 | TU_BREAKPOINT(); |
1167 | 0 | return false; |
1168 | 0 | } |
1169 | | |
1170 | 27.2k | return process_std_device_request(rhport, p_request); |
1171 | | |
1172 | | //------------- Class/Interface Specific Request -------------// |
1173 | 4.26k | case TUSB_REQ_RCPT_INTERFACE: { |
1174 | 4.26k | uint8_t itf; |
1175 | | #if CFG_TUD_PRINTER |
1176 | | // Printer GET_DEVICE_ID has a weird wIndex = interface (high) | alt (low) |
1177 | | // attempt to interpret this as a printer request if matched |
1178 | | if (TUSB_REQ_TYPE_CLASS == p_request->bmRequestType_bit.type && |
1179 | | TUSB_DIR_IN == p_request->bmRequestType_bit.direction && |
1180 | | TUSB_PRINTER_REQUEST_GET_DEVICE_ID == p_request->bRequest) { |
1181 | | itf = tu_u16_high(p_request->wIndex); |
1182 | | if (itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv)) { |
1183 | | const usbd_class_driver_t * driver = get_driver(_usbd_dev.itf2drv[itf]); |
1184 | | if (driver != NULL && driver->control_xfer_cb == printerd_control_xfer_cb) { |
1185 | | if (invoke_class_control(rhport, driver, p_request)) { |
1186 | | return true; |
1187 | | } |
1188 | | } |
1189 | | } |
1190 | | } |
1191 | | #endif |
1192 | 4.26k | itf = tu_u16_low(p_request->wIndex); |
1193 | 4.26k | TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv)); |
1194 | | |
1195 | 3.40k | usbd_class_driver_t const * driver = get_driver(_usbd_dev.itf2drv[itf]); |
1196 | 3.40k | TU_VERIFY(driver); |
1197 | | |
1198 | | // all requests to Interface (STD or Class) is forwarded to class driver. |
1199 | | // notable requests are: GET HID REPORT DESCRIPTOR, SET_INTERFACE, GET_INTERFACE |
1200 | 2.36k | if (!invoke_class_control(rhport, driver, p_request)) { |
1201 | | // For GET_INTERFACE and SET_INTERFACE, it is mandatory to respond even if the class |
1202 | | // driver doesn't use alternate settings or implement this |
1203 | 2.15k | TU_VERIFY(TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type); |
1204 | | |
1205 | | // Clear complete callback if driver set since it can also stall the request. |
1206 | 1.68k | ctrl_xfer->complete_cb = NULL; |
1207 | | |
1208 | 1.68k | switch (p_request->bRequest) { //-V2520 |
1209 | 124 | case TUSB_REQ_GET_INTERFACE: { |
1210 | 124 | uint8_t alternate = 0; |
1211 | 124 | tud_control_xfer(rhport, p_request, &alternate, 1); |
1212 | 124 | break; |
1213 | 0 | } |
1214 | | |
1215 | 348 | case TUSB_REQ_SET_INTERFACE: |
1216 | | // A class that implements altsettings handles SET_INTERFACE itself and returns true, |
1217 | | // so reaching here means the class does not — where only alt 0 is valid. Any non-zero |
1218 | | // alt (unimplemented, or rejected as invalid by the class) is a Request Error (stall). |
1219 | 348 | TU_VERIFY(tu_u16_low(p_request->wValue) == 0); |
1220 | 214 | tud_control_status(rhport, p_request); |
1221 | 214 | break; |
1222 | | |
1223 | 708 | case TUSB_REQ_GET_STATUS: |
1224 | | // USB 2.0 9.4.5: interface GET_STATUS returns 2 reserved (zero) bytes |
1225 | 708 | TU_VERIFY(process_get_status(rhport, p_request, 0x0000)); |
1226 | 79 | break; |
1227 | | |
1228 | 504 | default: return false; |
1229 | 1.68k | } |
1230 | 1.68k | } |
1231 | 632 | break; |
1232 | 2.36k | } |
1233 | | |
1234 | | //------------- Endpoint Request -------------// |
1235 | 7.77k | case TUSB_REQ_RCPT_ENDPOINT: { |
1236 | 7.77k | uint8_t const ep_addr = tu_u16_low(p_request->wIndex); |
1237 | 7.77k | uint8_t const ep_num = tu_edpt_number(ep_addr); |
1238 | 7.77k | uint8_t const ep_dir = tu_edpt_dir(ep_addr); |
1239 | | |
1240 | 7.77k | TU_ASSERT(ep_num < TU_ARRAY_SIZE(_usbd_dev.ep2drv) ); |
1241 | 7.08k | usbd_class_driver_t const * driver = get_driver(_usbd_dev.ep2drv[ep_num][ep_dir]); |
1242 | | |
1243 | 7.08k | if (TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type) { |
1244 | | // Forward class request to its driver |
1245 | 1.12k | TU_VERIFY(driver); |
1246 | 451 | return invoke_class_control(rhport, driver, p_request); |
1247 | 5.96k | } else { |
1248 | | // Handle STD request to endpoint |
1249 | 5.96k | switch (p_request->bRequest) { //-V2520 |
1250 | 1.94k | case TUSB_REQ_GET_STATUS: |
1251 | | // USB 2.0 9.4.5: endpoint GET_STATUS bit 0 = Halt |
1252 | 1.94k | TU_VERIFY(process_get_status(rhport, p_request, usbd_edpt_stalled(rhport, ep_addr) ? 0x0001u : 0x0000u)); |
1253 | 644 | break; |
1254 | | |
1255 | 1.44k | case TUSB_REQ_CLEAR_FEATURE: |
1256 | 3.07k | case TUSB_REQ_SET_FEATURE: { |
1257 | | // ENDPOINT_HALT is the only endpoint feature; it exists only on a non-control endpoint |
1258 | | // that an interface actually owns. Any other selector, the control endpoint (EP0 has no |
1259 | | // Halt feature, USB 2.0 9.4.9), or an endpoint no driver owns is a Request Error (stall). |
1260 | 3.07k | TU_VERIFY(TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue); |
1261 | 1.91k | TU_VERIFY(ep_num != 0); |
1262 | 1.23k | TU_VERIFY(driver != NULL); |
1263 | | |
1264 | 397 | if ( TUSB_REQ_CLEAR_FEATURE == p_request->bRequest ) { |
1265 | 192 | usbd_edpt_clear_stall(rhport, ep_addr); |
1266 | 205 | } else { |
1267 | 205 | usbd_edpt_stall(rhport, ep_addr); |
1268 | 205 | } |
1269 | | |
1270 | | // Some classes such as USBTMC need to clear/re-init their buffer on CLEAR_FEATURE. |
1271 | | // Clear complete callback if driver set since it can also stall the request. |
1272 | 397 | (void) invoke_class_control(rhport, driver, p_request); |
1273 | 397 | ctrl_xfer->complete_cb = NULL; |
1274 | | |
1275 | | // STD request must always be ACKed; skip ZLP status if driver already did that. |
1276 | 397 | if (!(_usbd_dev.ep_status[0][TUSB_DIR_IN] & TU_EDPT_STATE_BUSY)) { |
1277 | 397 | tud_control_status(rhport, p_request); |
1278 | 397 | } |
1279 | 397 | } |
1280 | 0 | break; |
1281 | | |
1282 | | // Unknown/Unsupported request |
1283 | 943 | default: |
1284 | 943 | TU_BREAKPOINT(); |
1285 | 943 | return false; |
1286 | 5.96k | } |
1287 | 5.96k | } |
1288 | 1.04k | break; |
1289 | 7.08k | } |
1290 | | |
1291 | | // Unknown recipient |
1292 | 2.60k | default: |
1293 | 2.60k | TU_BREAKPOINT(); |
1294 | 2.60k | return false; |
1295 | 44.5k | } |
1296 | | |
1297 | 1.67k | return true; |
1298 | 44.5k | } |
1299 | | |
1300 | | // Process Set Configure Request |
1301 | | // This function parse configuration descriptor & open drivers accordingly |
1302 | 6.80k | static bool process_set_config(uint8_t rhport, uint8_t cfg_num) { |
1303 | | // index is cfg_num-1 |
1304 | 6.80k | const tusb_desc_configuration_t *desc_cfg = |
1305 | 6.80k | (const tusb_desc_configuration_t *)tud_descriptor_configuration_cb(cfg_num - 1); |
1306 | 6.80k | TU_ASSERT(desc_cfg != NULL && desc_cfg->bDescriptorType == TUSB_DESC_CONFIGURATION); |
1307 | | |
1308 | | // Parse configuration descriptor |
1309 | 6.80k | _usbd_dev.self_powered = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_SELF_POWERED) ? 1u : 0u; |
1310 | | |
1311 | | // Parse interface descriptor |
1312 | 6.80k | const uint8_t *p_desc = ((const uint8_t *)desc_cfg) + sizeof(tusb_desc_configuration_t); |
1313 | 6.80k | const uint8_t *desc_end = ((const uint8_t *)desc_cfg) + tu_le16toh(desc_cfg->wTotalLength); |
1314 | | |
1315 | 16.3k | while (tu_desc_in_bounds(p_desc, desc_end)) { |
1316 | | // Class will always start with Interface Association (if any) and then Interface descriptor |
1317 | 13.3k | if (TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc)) { |
1318 | 6.56k | p_desc = tu_desc_next(p_desc); // next to Interface |
1319 | 6.56k | continue; |
1320 | 6.56k | } |
1321 | | |
1322 | 6.80k | TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc)); |
1323 | 6.80k | const tusb_desc_interface_t *desc_itf = (const tusb_desc_interface_t *)p_desc; |
1324 | | |
1325 | | // Find driver for this interface |
1326 | 6.80k | const uint16_t remaining_len = (uint16_t)(desc_end - p_desc); |
1327 | 6.80k | uint8_t drv_id; |
1328 | 12.4k | for (drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) { |
1329 | 8.61k | const usbd_class_driver_t *driver = get_driver(drv_id); |
1330 | 8.61k | TU_ASSERT(driver); |
1331 | 8.61k | const uint16_t drv_len = driver->open(rhport, desc_itf, remaining_len); |
1332 | | |
1333 | 8.61k | if ((sizeof(tusb_desc_interface_t) <= drv_len) && (drv_len <= remaining_len)) { |
1334 | | // Open successfully |
1335 | 2.99k | TU_LOG_USBD(" %s opened\r\n", driver->name); |
1336 | | |
1337 | | // bind found driver to all interfaces and endpoint within drv_len |
1338 | 2.99k | TU_ASSERT(tu_bind_driver_to_ep_itf(drv_id, _usbd_dev.ep2drv, _usbd_dev.itf2drv, CFG_TUD_INTERFACE_MAX, |
1339 | 2.99k | CFG_TUD_ENDPPOINT_MAX, p_desc, drv_len)); |
1340 | | |
1341 | 2.99k | p_desc += drv_len; // next Interface |
1342 | 2.99k | break; // exit driver find loop |
1343 | 2.99k | } |
1344 | 8.61k | } |
1345 | | |
1346 | | // Failed if there is no supported drivers |
1347 | 6.80k | TU_ASSERT(drv_id < TOTAL_DRIVER_COUNT); |
1348 | 6.80k | } |
1349 | | |
1350 | 2.99k | return true; |
1351 | 6.80k | } |
1352 | | |
1353 | | // return descriptor's buffer and update desc_len |
1354 | 20.7k | static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request) { |
1355 | 20.7k | tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue); |
1356 | 20.7k | uint8_t const desc_index = tu_u16_low( p_request->wValue ); |
1357 | | |
1358 | 20.7k | switch(desc_type) { //-V2520 |
1359 | 3.15k | case TUSB_DESC_DEVICE: { |
1360 | 3.15k | TU_LOG_USBD(" Device\r\n"); |
1361 | | |
1362 | 3.15k | void *desc_device = (void *)(uintptr_t)tud_descriptor_device_cb(); |
1363 | 3.15k | TU_ASSERT(desc_device); |
1364 | | |
1365 | | // Only response with exactly 1 Packet if: not addressed and host requested more data than device descriptor has. |
1366 | | // This only happens with the very first get device descriptor and EP0 size = 8 or 16. |
1367 | 3.15k | if ((CFG_TUD_ENDPOINT0_SIZE < sizeof(tusb_desc_device_t)) && !_usbd_dev.addressed && |
1368 | 0 | p_request->wLength > sizeof(tusb_desc_device_t)) { |
1369 | | // Hack here: we modify the request length to prevent usbd_control response with zlp |
1370 | | // since we are responding with 1 packet & less data than wLength. |
1371 | 0 | ((tusb_control_request_t *)(uintptr_t)p_request)->wLength = CFG_TUD_ENDPOINT0_SIZE; |
1372 | 0 | return tud_control_xfer(rhport, p_request, desc_device, CFG_TUD_ENDPOINT0_SIZE); |
1373 | 3.15k | } else { |
1374 | 3.15k | return tud_control_xfer(rhport, p_request, desc_device, sizeof(tusb_desc_device_t)); |
1375 | 3.15k | } |
1376 | 3.15k | } |
1377 | | // break; // unreachable |
1378 | | |
1379 | 1.82k | case TUSB_DESC_BOS: { |
1380 | 1.82k | TU_LOG_USBD(" BOS\r\n"); |
1381 | | |
1382 | | // requested by host if USB > 2.0 ( i.e 2.1 or 3.x ) |
1383 | 1.82k | uintptr_t desc_bos = (uintptr_t) tud_descriptor_bos_cb(); |
1384 | 1.82k | TU_VERIFY(desc_bos != 0); |
1385 | | |
1386 | | // Use offsetof to avoid pointer to the odd/misaligned address |
1387 | 0 | uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_bos + offsetof(tusb_desc_bos_t, wTotalLength))) ); |
1388 | |
|
1389 | 0 | return tud_control_xfer(rhport, p_request, (void*) desc_bos, total_len); |
1390 | 1.82k | } |
1391 | | // break; // unreachable |
1392 | | |
1393 | 2.69k | case TUSB_DESC_CONFIGURATION: |
1394 | 4.55k | case TUSB_DESC_OTHER_SPEED_CONFIG: { |
1395 | 4.55k | uintptr_t desc_config; |
1396 | | |
1397 | 4.55k | if ( desc_type == TUSB_DESC_CONFIGURATION ) { |
1398 | 2.69k | TU_LOG_USBD(" Configuration[%u]\r\n", desc_index); |
1399 | 2.69k | desc_config = (uintptr_t) tud_descriptor_configuration_cb(desc_index); |
1400 | 2.69k | TU_ASSERT(desc_config != 0); |
1401 | 2.69k | }else { |
1402 | | // Host only request this after getting Device Qualifier descriptor |
1403 | 1.86k | TU_LOG_USBD(" Other Speed Configuration\r\n"); |
1404 | 1.86k | desc_config = (uintptr_t) tud_descriptor_other_speed_configuration_cb(desc_index); |
1405 | 1.86k | TU_VERIFY(desc_config != 0); |
1406 | 1.86k | } |
1407 | | |
1408 | | // Use offsetof to avoid pointer to the odd/misaligned address |
1409 | 2.69k | uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_config + offsetof(tusb_desc_configuration_t, wTotalLength))) ); |
1410 | | |
1411 | 2.69k | return tud_control_xfer(rhport, p_request, (void*) desc_config, total_len); |
1412 | 4.55k | } |
1413 | | // break; // unreachable |
1414 | | |
1415 | 7.34k | case TUSB_DESC_STRING: { |
1416 | 7.34k | TU_LOG_USBD(" String[%u]\r\n", desc_index); |
1417 | | |
1418 | | // String Descriptor always uses the desc set from user |
1419 | 7.34k | uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index, p_request->wIndex); |
1420 | 7.34k | TU_VERIFY(desc_str); |
1421 | | |
1422 | | // first byte of descriptor is its size |
1423 | 5.33k | return tud_control_xfer(rhport, p_request, (void*) (uintptr_t) desc_str, tu_desc_len(desc_str)); |
1424 | 7.34k | } |
1425 | | // break; // unreachable |
1426 | | |
1427 | 1.78k | case TUSB_DESC_DEVICE_QUALIFIER: { |
1428 | 1.78k | TU_LOG_USBD(" Device Qualifier\r\n"); |
1429 | 1.78k | uint8_t const* desc_qualifier = tud_descriptor_device_qualifier_cb(); |
1430 | 1.78k | TU_VERIFY(desc_qualifier); |
1431 | 0 | return tud_control_xfer(rhport, p_request, (void*) (uintptr_t) desc_qualifier, tu_desc_len(desc_qualifier)); |
1432 | 1.78k | } |
1433 | | // break; // unreachable |
1434 | | |
1435 | 2.05k | default: return false; |
1436 | 20.7k | } |
1437 | 20.7k | } usbd.c:process_get_descriptor Line | Count | Source | 1354 | 6.90k | static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request) { | 1355 | 6.90k | tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue); | 1356 | 6.90k | uint8_t const desc_index = tu_u16_low( p_request->wValue ); | 1357 | | | 1358 | 6.90k | switch(desc_type) { //-V2520 | 1359 | 1.05k | case TUSB_DESC_DEVICE: { | 1360 | 1.05k | TU_LOG_USBD(" Device\r\n"); | 1361 | | | 1362 | 1.05k | void *desc_device = (void *)(uintptr_t)tud_descriptor_device_cb(); | 1363 | 1.05k | TU_ASSERT(desc_device); | 1364 | | | 1365 | | // Only response with exactly 1 Packet if: not addressed and host requested more data than device descriptor has. | 1366 | | // This only happens with the very first get device descriptor and EP0 size = 8 or 16. | 1367 | 1.05k | if ((CFG_TUD_ENDPOINT0_SIZE < sizeof(tusb_desc_device_t)) && !_usbd_dev.addressed && | 1368 | 0 | p_request->wLength > sizeof(tusb_desc_device_t)) { | 1369 | | // Hack here: we modify the request length to prevent usbd_control response with zlp | 1370 | | // since we are responding with 1 packet & less data than wLength. | 1371 | 0 | ((tusb_control_request_t *)(uintptr_t)p_request)->wLength = CFG_TUD_ENDPOINT0_SIZE; | 1372 | 0 | return tud_control_xfer(rhport, p_request, desc_device, CFG_TUD_ENDPOINT0_SIZE); | 1373 | 1.05k | } else { | 1374 | 1.05k | return tud_control_xfer(rhport, p_request, desc_device, sizeof(tusb_desc_device_t)); | 1375 | 1.05k | } | 1376 | 1.05k | } | 1377 | | // break; // unreachable | 1378 | | | 1379 | 607 | case TUSB_DESC_BOS: { | 1380 | 607 | TU_LOG_USBD(" BOS\r\n"); | 1381 | | | 1382 | | // requested by host if USB > 2.0 ( i.e 2.1 or 3.x ) | 1383 | 607 | uintptr_t desc_bos = (uintptr_t) tud_descriptor_bos_cb(); | 1384 | 607 | TU_VERIFY(desc_bos != 0); | 1385 | | | 1386 | | // Use offsetof to avoid pointer to the odd/misaligned address | 1387 | 0 | uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_bos + offsetof(tusb_desc_bos_t, wTotalLength))) ); | 1388 | |
| 1389 | 0 | return tud_control_xfer(rhport, p_request, (void*) desc_bos, total_len); | 1390 | 607 | } | 1391 | | // break; // unreachable | 1392 | | | 1393 | 899 | case TUSB_DESC_CONFIGURATION: | 1394 | 1.51k | case TUSB_DESC_OTHER_SPEED_CONFIG: { | 1395 | 1.51k | uintptr_t desc_config; | 1396 | | | 1397 | 1.51k | if ( desc_type == TUSB_DESC_CONFIGURATION ) { | 1398 | 899 | TU_LOG_USBD(" Configuration[%u]\r\n", desc_index); | 1399 | 899 | desc_config = (uintptr_t) tud_descriptor_configuration_cb(desc_index); | 1400 | 899 | TU_ASSERT(desc_config != 0); | 1401 | 899 | }else { | 1402 | | // Host only request this after getting Device Qualifier descriptor | 1403 | 620 | TU_LOG_USBD(" Other Speed Configuration\r\n"); | 1404 | 620 | desc_config = (uintptr_t) tud_descriptor_other_speed_configuration_cb(desc_index); | 1405 | 620 | TU_VERIFY(desc_config != 0); | 1406 | 620 | } | 1407 | | | 1408 | | // Use offsetof to avoid pointer to the odd/misaligned address | 1409 | 899 | uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_config + offsetof(tusb_desc_configuration_t, wTotalLength))) ); | 1410 | | | 1411 | 899 | return tud_control_xfer(rhport, p_request, (void*) desc_config, total_len); | 1412 | 1.51k | } | 1413 | | // break; // unreachable | 1414 | | | 1415 | 2.44k | case TUSB_DESC_STRING: { | 1416 | 2.44k | TU_LOG_USBD(" String[%u]\r\n", desc_index); | 1417 | | | 1418 | | // String Descriptor always uses the desc set from user | 1419 | 2.44k | uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index, p_request->wIndex); | 1420 | 2.44k | TU_VERIFY(desc_str); | 1421 | | | 1422 | | // first byte of descriptor is its size | 1423 | 1.77k | return tud_control_xfer(rhport, p_request, (void*) (uintptr_t) desc_str, tu_desc_len(desc_str)); | 1424 | 2.44k | } | 1425 | | // break; // unreachable | 1426 | | | 1427 | 594 | case TUSB_DESC_DEVICE_QUALIFIER: { | 1428 | 594 | TU_LOG_USBD(" Device Qualifier\r\n"); | 1429 | 594 | uint8_t const* desc_qualifier = tud_descriptor_device_qualifier_cb(); | 1430 | 594 | TU_VERIFY(desc_qualifier); | 1431 | 0 | return tud_control_xfer(rhport, p_request, (void*) (uintptr_t) desc_qualifier, tu_desc_len(desc_qualifier)); | 1432 | 594 | } | 1433 | | // break; // unreachable | 1434 | | | 1435 | 685 | default: return false; | 1436 | 6.90k | } | 1437 | 6.90k | } |
usbd.c:process_get_descriptor Line | Count | Source | 1354 | 6.90k | static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request) { | 1355 | 6.90k | tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue); | 1356 | 6.90k | uint8_t const desc_index = tu_u16_low( p_request->wValue ); | 1357 | | | 1358 | 6.90k | switch(desc_type) { //-V2520 | 1359 | 1.05k | case TUSB_DESC_DEVICE: { | 1360 | 1.05k | TU_LOG_USBD(" Device\r\n"); | 1361 | | | 1362 | 1.05k | void *desc_device = (void *)(uintptr_t)tud_descriptor_device_cb(); | 1363 | 1.05k | TU_ASSERT(desc_device); | 1364 | | | 1365 | | // Only response with exactly 1 Packet if: not addressed and host requested more data than device descriptor has. | 1366 | | // This only happens with the very first get device descriptor and EP0 size = 8 or 16. | 1367 | 1.05k | if ((CFG_TUD_ENDPOINT0_SIZE < sizeof(tusb_desc_device_t)) && !_usbd_dev.addressed && | 1368 | 0 | p_request->wLength > sizeof(tusb_desc_device_t)) { | 1369 | | // Hack here: we modify the request length to prevent usbd_control response with zlp | 1370 | | // since we are responding with 1 packet & less data than wLength. | 1371 | 0 | ((tusb_control_request_t *)(uintptr_t)p_request)->wLength = CFG_TUD_ENDPOINT0_SIZE; | 1372 | 0 | return tud_control_xfer(rhport, p_request, desc_device, CFG_TUD_ENDPOINT0_SIZE); | 1373 | 1.05k | } else { | 1374 | 1.05k | return tud_control_xfer(rhport, p_request, desc_device, sizeof(tusb_desc_device_t)); | 1375 | 1.05k | } | 1376 | 1.05k | } | 1377 | | // break; // unreachable | 1378 | | | 1379 | 607 | case TUSB_DESC_BOS: { | 1380 | 607 | TU_LOG_USBD(" BOS\r\n"); | 1381 | | | 1382 | | // requested by host if USB > 2.0 ( i.e 2.1 or 3.x ) | 1383 | 607 | uintptr_t desc_bos = (uintptr_t) tud_descriptor_bos_cb(); | 1384 | 607 | TU_VERIFY(desc_bos != 0); | 1385 | | | 1386 | | // Use offsetof to avoid pointer to the odd/misaligned address | 1387 | 0 | uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_bos + offsetof(tusb_desc_bos_t, wTotalLength))) ); | 1388 | |
| 1389 | 0 | return tud_control_xfer(rhport, p_request, (void*) desc_bos, total_len); | 1390 | 607 | } | 1391 | | // break; // unreachable | 1392 | | | 1393 | 899 | case TUSB_DESC_CONFIGURATION: | 1394 | 1.51k | case TUSB_DESC_OTHER_SPEED_CONFIG: { | 1395 | 1.51k | uintptr_t desc_config; | 1396 | | | 1397 | 1.51k | if ( desc_type == TUSB_DESC_CONFIGURATION ) { | 1398 | 899 | TU_LOG_USBD(" Configuration[%u]\r\n", desc_index); | 1399 | 899 | desc_config = (uintptr_t) tud_descriptor_configuration_cb(desc_index); | 1400 | 899 | TU_ASSERT(desc_config != 0); | 1401 | 899 | }else { | 1402 | | // Host only request this after getting Device Qualifier descriptor | 1403 | 620 | TU_LOG_USBD(" Other Speed Configuration\r\n"); | 1404 | 620 | desc_config = (uintptr_t) tud_descriptor_other_speed_configuration_cb(desc_index); | 1405 | 620 | TU_VERIFY(desc_config != 0); | 1406 | 620 | } | 1407 | | | 1408 | | // Use offsetof to avoid pointer to the odd/misaligned address | 1409 | 899 | uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_config + offsetof(tusb_desc_configuration_t, wTotalLength))) ); | 1410 | | | 1411 | 899 | return tud_control_xfer(rhport, p_request, (void*) desc_config, total_len); | 1412 | 1.51k | } | 1413 | | // break; // unreachable | 1414 | | | 1415 | 2.44k | case TUSB_DESC_STRING: { | 1416 | 2.44k | TU_LOG_USBD(" String[%u]\r\n", desc_index); | 1417 | | | 1418 | | // String Descriptor always uses the desc set from user | 1419 | 2.44k | uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index, p_request->wIndex); | 1420 | 2.44k | TU_VERIFY(desc_str); | 1421 | | | 1422 | | // first byte of descriptor is its size | 1423 | 1.77k | return tud_control_xfer(rhport, p_request, (void*) (uintptr_t) desc_str, tu_desc_len(desc_str)); | 1424 | 2.44k | } | 1425 | | // break; // unreachable | 1426 | | | 1427 | 594 | case TUSB_DESC_DEVICE_QUALIFIER: { | 1428 | 594 | TU_LOG_USBD(" Device Qualifier\r\n"); | 1429 | 594 | uint8_t const* desc_qualifier = tud_descriptor_device_qualifier_cb(); | 1430 | 594 | TU_VERIFY(desc_qualifier); | 1431 | 0 | return tud_control_xfer(rhport, p_request, (void*) (uintptr_t) desc_qualifier, tu_desc_len(desc_qualifier)); | 1432 | 594 | } | 1433 | | // break; // unreachable | 1434 | | | 1435 | 685 | default: return false; | 1436 | 6.90k | } | 1437 | 6.90k | } |
usbd.c:process_get_descriptor Line | Count | Source | 1354 | 6.90k | static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request) { | 1355 | 6.90k | tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue); | 1356 | 6.90k | uint8_t const desc_index = tu_u16_low( p_request->wValue ); | 1357 | | | 1358 | 6.90k | switch(desc_type) { //-V2520 | 1359 | 1.05k | case TUSB_DESC_DEVICE: { | 1360 | 1.05k | TU_LOG_USBD(" Device\r\n"); | 1361 | | | 1362 | 1.05k | void *desc_device = (void *)(uintptr_t)tud_descriptor_device_cb(); | 1363 | 1.05k | TU_ASSERT(desc_device); | 1364 | | | 1365 | | // Only response with exactly 1 Packet if: not addressed and host requested more data than device descriptor has. | 1366 | | // This only happens with the very first get device descriptor and EP0 size = 8 or 16. | 1367 | 1.05k | if ((CFG_TUD_ENDPOINT0_SIZE < sizeof(tusb_desc_device_t)) && !_usbd_dev.addressed && | 1368 | 0 | p_request->wLength > sizeof(tusb_desc_device_t)) { | 1369 | | // Hack here: we modify the request length to prevent usbd_control response with zlp | 1370 | | // since we are responding with 1 packet & less data than wLength. | 1371 | 0 | ((tusb_control_request_t *)(uintptr_t)p_request)->wLength = CFG_TUD_ENDPOINT0_SIZE; | 1372 | 0 | return tud_control_xfer(rhport, p_request, desc_device, CFG_TUD_ENDPOINT0_SIZE); | 1373 | 1.05k | } else { | 1374 | 1.05k | return tud_control_xfer(rhport, p_request, desc_device, sizeof(tusb_desc_device_t)); | 1375 | 1.05k | } | 1376 | 1.05k | } | 1377 | | // break; // unreachable | 1378 | | | 1379 | 607 | case TUSB_DESC_BOS: { | 1380 | 607 | TU_LOG_USBD(" BOS\r\n"); | 1381 | | | 1382 | | // requested by host if USB > 2.0 ( i.e 2.1 or 3.x ) | 1383 | 607 | uintptr_t desc_bos = (uintptr_t) tud_descriptor_bos_cb(); | 1384 | 607 | TU_VERIFY(desc_bos != 0); | 1385 | | | 1386 | | // Use offsetof to avoid pointer to the odd/misaligned address | 1387 | 0 | uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_bos + offsetof(tusb_desc_bos_t, wTotalLength))) ); | 1388 | |
| 1389 | 0 | return tud_control_xfer(rhport, p_request, (void*) desc_bos, total_len); | 1390 | 607 | } | 1391 | | // break; // unreachable | 1392 | | | 1393 | 899 | case TUSB_DESC_CONFIGURATION: | 1394 | 1.51k | case TUSB_DESC_OTHER_SPEED_CONFIG: { | 1395 | 1.51k | uintptr_t desc_config; | 1396 | | | 1397 | 1.51k | if ( desc_type == TUSB_DESC_CONFIGURATION ) { | 1398 | 899 | TU_LOG_USBD(" Configuration[%u]\r\n", desc_index); | 1399 | 899 | desc_config = (uintptr_t) tud_descriptor_configuration_cb(desc_index); | 1400 | 899 | TU_ASSERT(desc_config != 0); | 1401 | 899 | }else { | 1402 | | // Host only request this after getting Device Qualifier descriptor | 1403 | 620 | TU_LOG_USBD(" Other Speed Configuration\r\n"); | 1404 | 620 | desc_config = (uintptr_t) tud_descriptor_other_speed_configuration_cb(desc_index); | 1405 | 620 | TU_VERIFY(desc_config != 0); | 1406 | 620 | } | 1407 | | | 1408 | | // Use offsetof to avoid pointer to the odd/misaligned address | 1409 | 899 | uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_config + offsetof(tusb_desc_configuration_t, wTotalLength))) ); | 1410 | | | 1411 | 899 | return tud_control_xfer(rhport, p_request, (void*) desc_config, total_len); | 1412 | 1.51k | } | 1413 | | // break; // unreachable | 1414 | | | 1415 | 2.44k | case TUSB_DESC_STRING: { | 1416 | 2.44k | TU_LOG_USBD(" String[%u]\r\n", desc_index); | 1417 | | | 1418 | | // String Descriptor always uses the desc set from user | 1419 | 2.44k | uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index, p_request->wIndex); | 1420 | 2.44k | TU_VERIFY(desc_str); | 1421 | | | 1422 | | // first byte of descriptor is its size | 1423 | 1.77k | return tud_control_xfer(rhport, p_request, (void*) (uintptr_t) desc_str, tu_desc_len(desc_str)); | 1424 | 2.44k | } | 1425 | | // break; // unreachable | 1426 | | | 1427 | 594 | case TUSB_DESC_DEVICE_QUALIFIER: { | 1428 | 594 | TU_LOG_USBD(" Device Qualifier\r\n"); | 1429 | 594 | uint8_t const* desc_qualifier = tud_descriptor_device_qualifier_cb(); | 1430 | 594 | TU_VERIFY(desc_qualifier); | 1431 | 0 | return tud_control_xfer(rhport, p_request, (void*) (uintptr_t) desc_qualifier, tu_desc_len(desc_qualifier)); | 1432 | 594 | } | 1433 | | // break; // unreachable | 1434 | | | 1435 | 685 | default: return false; | 1436 | 6.90k | } | 1437 | 6.90k | } |
|
1438 | | |
1439 | | //--------------------------------------------------------------------+ |
1440 | | // DCD Event Handler |
1441 | | //--------------------------------------------------------------------+ |
1442 | 133k | TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr) { |
1443 | 133k | bool send = false; |
1444 | 133k | switch (event->event_id) { |
1445 | 14.8k | case DCD_EVENT_UNPLUGGED: |
1446 | 14.8k | _usbd_dev.connected = 0; |
1447 | 14.8k | _usbd_dev.addressed = 0; |
1448 | 14.8k | _usbd_dev.cfg_num = 0; |
1449 | 14.8k | _usbd_dev.suspended = 0; |
1450 | 14.8k | send = true; |
1451 | 14.8k | break; |
1452 | | |
1453 | 14.3k | case DCD_EVENT_SUSPEND: |
1454 | | // NOTE: When plugging/unplugging device, the D+/D- state are unstable and |
1455 | | // can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ). |
1456 | | // In addition, some MCUs such as SAMD or boards that haven no VBUS detection cannot distinguish |
1457 | | // suspended vs disconnected. We will skip handling SUSPEND/RESUME event if not currently connected |
1458 | 14.3k | if (_usbd_dev.connected) { |
1459 | 5.16k | _usbd_dev.suspended = 1; |
1460 | 5.16k | send = true; |
1461 | 5.16k | } |
1462 | 14.3k | break; |
1463 | | |
1464 | 10.4k | case DCD_EVENT_RESUME: |
1465 | | // skip event if not connected (especially required for SAMD) |
1466 | 10.4k | if (_usbd_dev.connected) { |
1467 | 4.99k | _usbd_dev.suspended = 0; |
1468 | 4.99k | send = true; |
1469 | 4.99k | } |
1470 | 10.4k | break; |
1471 | | |
1472 | 0 | case DCD_EVENT_SOF: |
1473 | | // SOF driver handler in ISR context |
1474 | 0 | for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { |
1475 | 0 | usbd_class_driver_t const* driver = get_driver(i); |
1476 | 0 | if (driver && driver->sof) { |
1477 | 0 | driver->sof(event->rhport, event->sof.frame_count); |
1478 | 0 | } |
1479 | 0 | } |
1480 | | |
1481 | | // Some MCUs after running dcd_remote_wakeup() does not have way to detect the end of remote wakeup |
1482 | | // which last 1-15 ms. DCD can use SOF as a clear indicator that bus is back to operational |
1483 | 0 | if (_usbd_dev.suspended) { |
1484 | 0 | _usbd_dev.suspended = 0; |
1485 | |
|
1486 | 0 | dcd_event_t const event_resume = {.rhport = event->rhport, .event_id = DCD_EVENT_RESUME}; |
1487 | 0 | queue_event(&event_resume, in_isr); |
1488 | 0 | } |
1489 | |
|
1490 | 0 | if (tu_bit_test(_usbd_dev.sof_consumer, SOF_CONSUMER_USER)) { |
1491 | 0 | dcd_event_t const event_sof = {.rhport = event->rhport, .event_id = DCD_EVENT_SOF, .sof.frame_count = event->sof.frame_count}; |
1492 | 0 | queue_event(&event_sof, in_isr); |
1493 | 0 | } |
1494 | 0 | break; |
1495 | | |
1496 | 64.8k | case DCD_EVENT_SETUP_RECEIVED: |
1497 | 64.8k | _usbd_queued_setup++; |
1498 | 64.8k | send = true; |
1499 | 64.8k | break; |
1500 | | |
1501 | 0 | case DCD_EVENT_XFER_COMPLETE: { |
1502 | | // Invoke the class callback associated with the endpoint address |
1503 | 0 | uint8_t const ep_addr = event->xfer_complete.ep_addr; |
1504 | 0 | uint8_t const epnum = tu_edpt_number(ep_addr); |
1505 | 0 | uint8_t const ep_dir = tu_edpt_dir(ep_addr); |
1506 | |
|
1507 | 0 | send = true; |
1508 | 0 | if(epnum > 0) { |
1509 | 0 | usbd_class_driver_t const* driver = get_driver(_usbd_dev.ep2drv[epnum][ep_dir]); |
1510 | |
|
1511 | 0 | if (driver && driver->xfer_isr) { |
1512 | | // Clear busy + claimed |
1513 | 0 | _usbd_dev.ep_status[epnum][ep_dir] &= (uint8_t) ~(TU_EDPT_STATE_BUSY | TU_EDPT_STATE_CLAIMED); |
1514 | |
|
1515 | 0 | send = !driver->xfer_isr(event->rhport, ep_addr, (xfer_result_t) event->xfer_complete.result, event->xfer_complete.len); |
1516 | | |
1517 | | // xfer_isr() is deferred to xfer_cb(), revert busy/claimed status |
1518 | 0 | if (send) { |
1519 | | // set busy + claimed |
1520 | 0 | _usbd_dev.ep_status[epnum][ep_dir] |= (TU_EDPT_STATE_BUSY | TU_EDPT_STATE_CLAIMED); |
1521 | 0 | } |
1522 | 0 | } |
1523 | 0 | } |
1524 | 0 | break; |
1525 | 0 | } |
1526 | | |
1527 | 28.5k | default: |
1528 | 28.5k | send = true; |
1529 | 28.5k | break; |
1530 | 133k | } |
1531 | | |
1532 | 133k | if (send && !queue_event(event, in_isr)) { |
1533 | | // event dropped by a full queue: undo state that would otherwise wedge permanently |
1534 | 0 | if (event->event_id == DCD_EVENT_SETUP_RECEIVED) { |
1535 | | // undo the increment, else every later SETUP is skipped as "other SETUP in queue" |
1536 | | // and EP0 is deaf until re-init |
1537 | 0 | _usbd_queued_setup--; |
1538 | 0 | } else if (event->event_id == DCD_EVENT_XFER_COMPLETE) { |
1539 | | // clear busy + claimed, else the endpoint can never be claimed or re-armed again |
1540 | 0 | uint8_t const epnum = tu_edpt_number(event->xfer_complete.ep_addr); |
1541 | 0 | uint8_t const ep_dir = tu_edpt_dir(event->xfer_complete.ep_addr); |
1542 | 0 | _usbd_dev.ep_status[epnum][ep_dir] &= (uint8_t) ~(TU_EDPT_STATE_BUSY | TU_EDPT_STATE_CLAIMED); |
1543 | 0 | } |
1544 | 0 | } |
1545 | 133k | } |
1546 | | |
1547 | | //--------------------------------------------------------------------+ |
1548 | | // USBD API For Class Driver |
1549 | | //--------------------------------------------------------------------+ |
1550 | | |
1551 | 750k | void usbd_int_set(bool enabled) { |
1552 | 750k | if (enabled) { |
1553 | 375k | dcd_int_enable(_usbd_rhport); |
1554 | 375k | } else { |
1555 | 375k | dcd_int_disable(_usbd_rhport); |
1556 | 375k | } |
1557 | 750k | } |
1558 | | |
1559 | 0 | void usbd_spin_lock(bool in_isr) { |
1560 | 0 | osal_spin_lock(&_usbd_spin, in_isr); |
1561 | 0 | } |
1562 | 0 | void usbd_spin_unlock(bool in_isr) { |
1563 | 0 | osal_spin_unlock(&_usbd_spin, in_isr); |
1564 | 0 | } |
1565 | | |
1566 | | // Parse consecutive endpoint descriptors (IN & OUT) |
1567 | | bool usbd_open_edpt_pair(uint8_t rhport, const uint8_t *p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t *ep_out, |
1568 | 243 | uint8_t *ep_in) { |
1569 | 243 | for (int i = 0; i < ep_count; i++) { |
1570 | 243 | const tusb_desc_endpoint_t *desc_ep = (const tusb_desc_endpoint_t *)p_desc; |
1571 | | |
1572 | 243 | TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && xfer_type == desc_ep->bmAttributes.xfer); |
1573 | 243 | TU_ASSERT(usbd_edpt_open(rhport, desc_ep)); |
1574 | | |
1575 | 0 | if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { |
1576 | 0 | (*ep_in) = desc_ep->bEndpointAddress; |
1577 | 0 | } else { |
1578 | 0 | (*ep_out) = desc_ep->bEndpointAddress; |
1579 | 0 | } |
1580 | |
|
1581 | 0 | p_desc = tu_desc_next(p_desc); |
1582 | 0 | } |
1583 | | |
1584 | 0 | return true; |
1585 | 243 | } |
1586 | | |
1587 | | // Helper to defer an isr function |
1588 | 0 | void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr) { |
1589 | 0 | dcd_event_t event = { |
1590 | 0 | .rhport = 0, |
1591 | 0 | .event_id = USBD_EVENT_FUNC_CALL, |
1592 | 0 | }; |
1593 | 0 | event.func_call.func = func; |
1594 | 0 | event.func_call.param = param; |
1595 | |
|
1596 | 0 | queue_event(&event, in_isr); |
1597 | 0 | } |
1598 | | |
1599 | 0 | bool usbd_defer_func_after_queue(osal_task_func_t func, void* param) { |
1600 | 0 | if (_usbd_after_queue.func != NULL) { |
1601 | 0 | return _usbd_after_queue.func == func && _usbd_after_queue.param == param; |
1602 | 0 | } |
1603 | 0 | _usbd_after_queue.func = func; |
1604 | 0 | _usbd_after_queue.param = param; |
1605 | 0 | return true; |
1606 | 0 | } |
1607 | | |
1608 | | //--------------------------------------------------------------------+ |
1609 | | // USBD Endpoint API |
1610 | | //--------------------------------------------------------------------+ |
1611 | | |
1612 | 15.2k | bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) { |
1613 | 15.2k | rhport = _usbd_rhport; |
1614 | | |
1615 | 15.2k | TU_ASSERT(tu_edpt_number(desc_ep->bEndpointAddress) < CFG_TUD_ENDPPOINT_MAX); |
1616 | 15.0k | TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t)_usbd_dev.speed)); |
1617 | | |
1618 | 15.0k | return dcd_edpt_open(rhport, desc_ep); |
1619 | 15.0k | } |
1620 | | |
1621 | 7.09k | bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr) { |
1622 | 7.09k | (void) rhport; |
1623 | | |
1624 | | // TODO add this check later, also make sure we don't starve an out endpoint while suspending |
1625 | | // TU_VERIFY(tud_ready()); |
1626 | | |
1627 | 7.09k | uint8_t const epnum = tu_edpt_number(ep_addr); |
1628 | 7.09k | uint8_t const dir = tu_edpt_dir(ep_addr); |
1629 | 7.09k | return tu_edpt_claim(&_usbd_dev.ep_status[epnum][dir], _usbd_mutex); |
1630 | 7.09k | } |
1631 | | |
1632 | 0 | bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr) { |
1633 | 0 | (void) rhport; |
1634 | |
|
1635 | 0 | uint8_t const epnum = tu_edpt_number(ep_addr); |
1636 | 0 | uint8_t const dir = tu_edpt_dir(ep_addr); |
1637 | 0 | return tu_edpt_release(&_usbd_dev.ep_status[epnum][dir], _usbd_mutex); |
1638 | 0 | } |
1639 | | |
1640 | 0 | void usbd_edpt_rx_consume(uint8_t rhport, uint8_t ep_addr) { |
1641 | 0 | (void) rhport; |
1642 | |
|
1643 | 0 | uint8_t const epnum = tu_edpt_number(ep_addr); |
1644 | 0 | uint8_t const dir = tu_edpt_dir(ep_addr); |
1645 | 0 | volatile uint8_t *ep_state = &_usbd_dev.ep_status[epnum][dir]; |
1646 | |
|
1647 | 0 | TU_VERIFY((*ep_state & TU_EDPT_STATE_RX_PENDING) != 0,); |
1648 | 0 | (void) osal_mutex_lock(_usbd_mutex, OSAL_TIMEOUT_WAIT_FOREVER); |
1649 | 0 | if ((*ep_state & TU_EDPT_STATE_RX_PENDING) != 0) { |
1650 | 0 | *ep_state &= (uint8_t) ~TU_EDPT_STATE_RX_PENDING; |
1651 | 0 | } |
1652 | 0 | (void) osal_mutex_unlock(_usbd_mutex); |
1653 | 0 | } |
1654 | | |
1655 | 45.4k | bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes, bool is_isr) { |
1656 | 45.4k | rhport = _usbd_rhport; |
1657 | | |
1658 | 45.4k | uint8_t const epnum = tu_edpt_number(ep_addr); |
1659 | 45.4k | uint8_t const dir = tu_edpt_dir(ep_addr); |
1660 | | |
1661 | | // TODO skip ready() check for now since enumeration also use this API |
1662 | | // TU_VERIFY(tud_ready()); |
1663 | | |
1664 | 45.4k | TU_LOG_USBD(" Queue EP %02X with %u bytes ...\r\n", ep_addr, total_bytes); |
1665 | | #if CFG_TUD_LOG_LEVEL >= 3 |
1666 | | if(dir == TUSB_DIR_IN) { |
1667 | | TU_LOG_MEM(CFG_TUD_LOG_LEVEL, buffer, total_bytes, 2); |
1668 | | } |
1669 | | #endif |
1670 | | |
1671 | | // Attempt to transfer on a busy endpoint, sound like an race condition ! |
1672 | 45.4k | TU_ASSERT((_usbd_dev.ep_status[epnum][dir] & TU_EDPT_STATE_BUSY) == 0); |
1673 | | |
1674 | | // Set busy first since the actual transfer can be complete before dcd_edpt_xfer() |
1675 | | // could return and USBD task can preempt and clear the busy |
1676 | 41.6k | _usbd_dev.ep_status[epnum][dir] = |
1677 | 41.6k | (uint8_t) ((_usbd_dev.ep_status[epnum][dir] & ~TU_EDPT_STATE_RX_PENDING) | TU_EDPT_STATE_BUSY); |
1678 | | |
1679 | 41.6k | if (dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes, is_isr)) { |
1680 | 12.1k | return true; |
1681 | 29.4k | } else { |
1682 | | // Driver refused the transfer, mark endpoint as ready to allow next transfer. This is a |
1683 | | // recoverable condition (e.g. a new setup superseding a control response), not a bug, so |
1684 | | // do not break into the debugger - TU_BREAKPOINT() halts the CPU whenever a probe is |
1685 | | // attached, which on a test rig is always. |
1686 | 29.4k | _usbd_dev.ep_status[epnum][dir] &= (uint8_t) ~(TU_EDPT_STATE_BUSY | TU_EDPT_STATE_CLAIMED); |
1687 | 29.4k | TU_LOG_USBD("FAILED\r\n"); |
1688 | 29.4k | return false; |
1689 | 29.4k | } |
1690 | 41.6k | } |
1691 | | |
1692 | | // The number of bytes has to be given explicitly to allow more flexible control of how many |
1693 | | // bytes should be written and second to keep the return value free to give back a boolean |
1694 | | // success message. If total_bytes is too big, the FIFO will copy only what is available |
1695 | | // into the USB buffer! |
1696 | 0 | bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t total_bytes, bool is_isr) { |
1697 | | #if CFG_TUD_EDPT_DEDICATED_HWFIFO |
1698 | | rhport = _usbd_rhport; |
1699 | | |
1700 | | uint8_t const epnum = tu_edpt_number(ep_addr); |
1701 | | uint8_t const dir = tu_edpt_dir(ep_addr); |
1702 | | |
1703 | | TU_LOG_USBD(" Queue FIFO EP %02X with %u bytes ... ", ep_addr, total_bytes); |
1704 | | |
1705 | | // Attempt to transfer on a busy endpoint, sound like a race condition ! |
1706 | | TU_ASSERT((_usbd_dev.ep_status[epnum][dir] & TU_EDPT_STATE_BUSY) == 0); |
1707 | | |
1708 | | // Set busy first since the actual transfer can be complete before dcd_edpt_xfer() could return |
1709 | | // and usbd task can preempt and clear the busy |
1710 | | _usbd_dev.ep_status[epnum][dir] = |
1711 | | (uint8_t) ((_usbd_dev.ep_status[epnum][dir] & ~TU_EDPT_STATE_RX_PENDING) | TU_EDPT_STATE_BUSY); |
1712 | | |
1713 | | if (dcd_edpt_xfer_fifo(rhport, ep_addr, ff, total_bytes, is_isr)) { |
1714 | | TU_LOG_USBD("OK\r\n"); |
1715 | | return true; |
1716 | | } else { |
1717 | | // DCD error, mark endpoint as ready to allow next transfer |
1718 | | _usbd_dev.ep_status[epnum][dir] &= (uint8_t) ~(TU_EDPT_STATE_BUSY | TU_EDPT_STATE_CLAIMED); |
1719 | | TU_LOG_USBD("failed\r\n"); |
1720 | | TU_BREAKPOINT(); |
1721 | | return false; |
1722 | | } |
1723 | | #else |
1724 | 0 | (void)rhport; |
1725 | 0 | (void)ep_addr; |
1726 | 0 | (void)ff; |
1727 | 0 | (void)total_bytes; |
1728 | 0 | (void)is_isr; |
1729 | 0 | return false; |
1730 | 0 | #endif |
1731 | 0 | } |
1732 | | |
1733 | 0 | bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr) { |
1734 | 0 | (void) rhport; |
1735 | |
|
1736 | 0 | uint8_t const epnum = tu_edpt_number(ep_addr); |
1737 | 0 | uint8_t const dir = tu_edpt_dir(ep_addr); |
1738 | |
|
1739 | 0 | return (_usbd_dev.ep_status[epnum][dir] & TU_EDPT_STATE_BUSY) != 0; |
1740 | 0 | } |
1741 | | |
1742 | 450 | void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { |
1743 | 450 | rhport = _usbd_rhport; |
1744 | | |
1745 | 450 | uint8_t const epnum = tu_edpt_number(ep_addr); |
1746 | 450 | uint8_t const dir = tu_edpt_dir(ep_addr); |
1747 | | |
1748 | | // only stalled if currently cleared |
1749 | 450 | TU_LOG_USBD(" Stall EP %02X\r\n", ep_addr); |
1750 | 450 | dcd_edpt_stall(rhport, ep_addr); |
1751 | 450 | _usbd_dev.ep_status[epnum][dir] |= (TU_EDPT_STATE_STALLED | TU_EDPT_STATE_BUSY); |
1752 | 450 | } |
1753 | | |
1754 | 192 | void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { |
1755 | 192 | rhport = _usbd_rhport; |
1756 | | |
1757 | 192 | uint8_t const epnum = tu_edpt_number(ep_addr); |
1758 | 192 | uint8_t const dir = tu_edpt_dir(ep_addr); |
1759 | | |
1760 | 192 | TU_LOG_USBD(" Clear Stall EP %02X\r\n", ep_addr); |
1761 | 192 | const bool was_stalled = (_usbd_dev.ep_status[epnum][dir] & TU_EDPT_STATE_STALLED) != 0; |
1762 | 192 | dcd_edpt_clear_stall(rhport, ep_addr); |
1763 | | // Clear STALLED|BUSY unconditionally (long-standing behavior; some classes, e.g. audio's |
1764 | | // set-interface, call this on a non-stalled endpoint solely to drop a leftover BUSY bit). |
1765 | | // Only release the CLAIMED ownership bit when the endpoint was actually stalled: the stall |
1766 | | // aborts the in-flight transfer in the dcd with no completion event to release the claim, so |
1767 | | // clearing it here prevents starvation. On a non-stalled clear (e.g. a data-toggle reset) a |
1768 | | // transfer may still be legitimately claimed by another task, so keep CLAIMED to preserve the |
1769 | | // claim->xfer mutual exclusion. |
1770 | 192 | uint8_t clear_mask = TU_EDPT_STATE_STALLED | TU_EDPT_STATE_BUSY; |
1771 | 192 | if (was_stalled) { |
1772 | 51 | clear_mask |= TU_EDPT_STATE_CLAIMED; |
1773 | 51 | } |
1774 | 192 | _usbd_dev.ep_status[epnum][dir] &= (uint8_t) ~clear_mask; |
1775 | 192 | } |
1776 | | |
1777 | | // Not valid for EP0, where dcd_edpt_stall() means "stall the control transfer" rather than "cancel". |
1778 | | // TODO replace this stall/clear-stall stand-in with a dcd_edpt_abort_xfer() returning bool, like |
1779 | | // hcd_edpt_abort_xfer(): the stall is wire-visible and the clear resets the toggle. |
1780 | 0 | void usbd_edpt_abort(uint8_t rhport, uint8_t ep_addr) { |
1781 | 0 | usbd_edpt_stall(rhport, ep_addr); |
1782 | 0 | usbd_edpt_clear_stall(rhport, ep_addr); |
1783 | 0 | } |
1784 | | |
1785 | 1.94k | bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) { |
1786 | 1.94k | (void) rhport; |
1787 | | |
1788 | 1.94k | uint8_t const epnum = tu_edpt_number(ep_addr); |
1789 | 1.94k | uint8_t const dir = tu_edpt_dir(ep_addr); |
1790 | | |
1791 | 1.94k | return (_usbd_dev.ep_status[epnum][dir] & TU_EDPT_STATE_STALLED) != 0; |
1792 | 1.94k | } |
1793 | | |
1794 | | /** |
1795 | | * usbd_edpt_close will disable an endpoint. |
1796 | | * In progress transfers on this EP may be delivered after this call. |
1797 | | */ |
1798 | 0 | void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr) { |
1799 | 0 | #ifdef TUP_DCD_EDPT_ISO_ALLOC |
1800 | 0 | (void) rhport; (void) ep_addr; |
1801 | | // ISO alloc/activate Should be used instead |
1802 | | #else |
1803 | | rhport = _usbd_rhport; |
1804 | | |
1805 | | TU_LOG_USBD(" CLOSING Endpoint: 0x%02X\r\n", ep_addr); |
1806 | | |
1807 | | uint8_t const epnum = tu_edpt_number(ep_addr); |
1808 | | uint8_t const dir = tu_edpt_dir(ep_addr); |
1809 | | |
1810 | | dcd_edpt_close(rhport, ep_addr); |
1811 | | _usbd_dev.ep_status[epnum][dir] = 0; |
1812 | | #endif |
1813 | |
|
1814 | 0 | return; |
1815 | 0 | } |
1816 | | |
1817 | 0 | void usbd_sof_enable(uint8_t rhport, sof_consumer_t consumer, bool en) { |
1818 | 0 | rhport = _usbd_rhport; |
1819 | |
|
1820 | 0 | uint8_t consumer_old = _usbd_dev.sof_consumer; |
1821 | | // Keep track how many class instances need the SOF interrupt |
1822 | 0 | if (en) { |
1823 | 0 | _usbd_dev.sof_consumer |= (uint8_t)(1 << consumer); |
1824 | 0 | } else { |
1825 | 0 | _usbd_dev.sof_consumer &= (uint8_t)(~(1 << consumer)); |
1826 | 0 | } |
1827 | | |
1828 | | // Test logically unequal |
1829 | 0 | if(!_usbd_dev.sof_consumer != !consumer_old) { |
1830 | 0 | dcd_sof_enable(rhport, _usbd_dev.sof_consumer); |
1831 | 0 | } |
1832 | 0 | } |
1833 | | |
1834 | 0 | bool usbd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { |
1835 | 0 | #ifdef TUP_DCD_EDPT_ISO_ALLOC |
1836 | 0 | rhport = _usbd_rhport; |
1837 | |
|
1838 | 0 | TU_ASSERT(tu_edpt_number(ep_addr) < CFG_TUD_ENDPPOINT_MAX); |
1839 | 0 | return dcd_edpt_iso_alloc(rhport, ep_addr, largest_packet_size); |
1840 | | #else |
1841 | | (void) rhport; (void) ep_addr; (void) largest_packet_size; |
1842 | | return false; |
1843 | | #endif |
1844 | 0 | } |
1845 | | |
1846 | 0 | bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) { |
1847 | 0 | #ifdef TUP_DCD_EDPT_ISO_ALLOC |
1848 | 0 | rhport = _usbd_rhport; |
1849 | |
|
1850 | 0 | uint8_t const epnum = tu_edpt_number(desc_ep->bEndpointAddress); |
1851 | 0 | uint8_t const dir = tu_edpt_dir(desc_ep->bEndpointAddress); |
1852 | |
|
1853 | 0 | TU_ASSERT(epnum < CFG_TUD_ENDPPOINT_MAX); |
1854 | 0 | TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t)_usbd_dev.speed)); |
1855 | | |
1856 | 0 | _usbd_dev.ep_status[epnum][dir] = 0; |
1857 | 0 | return dcd_edpt_iso_activate(rhport, desc_ep); |
1858 | | #else |
1859 | | (void) rhport; (void) desc_ep; |
1860 | | return false; |
1861 | | #endif |
1862 | 0 | } |
1863 | | |
1864 | | #endif |