Open FFBoard
Open source force feedback firmware
ehci.c
Go to the documentation of this file.
1/*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2019 Ha Thach (tinyusb.org)
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 *
24 * This file is part of the TinyUSB stack.
25 */
26
27#include "tusb_option.h"
28
29#if CFG_TUH_ENABLED && defined(TUP_USBIP_EHCI)
30
31//--------------------------------------------------------------------+
32// INCLUDE
33//--------------------------------------------------------------------+
34#include "osal/osal.h"
35
36#include "host/hcd.h"
37#include "ehci_api.h"
38#include "ehci.h"
39
40//--------------------------------------------------------------------+
41// MACRO CONSTANT TYPEDEF
42//--------------------------------------------------------------------+
43
44// Debug level of EHCI
45#define EHCI_DBG 2
46
47// Framelist size as small as possible to save SRAM
48#ifdef TUP_USBIP_CHIPIDEA_HS
49 // NXP Transdimension: 8 elements
50 #define FRAMELIST_SIZE_BIT_VALUE 7u
51 #define FRAMELIST_SIZE_USBCMD_VALUE (((FRAMELIST_SIZE_BIT_VALUE & 3) << EHCI_USBCMD_FRAMELIST_SIZE_SHIFT) | \
52 ((FRAMELIST_SIZE_BIT_VALUE >> 2) << EHCI_USBCMD_CHIPIDEA_FRAMELIST_SIZE_MSB_SHIFT))
53#else
54 // STD EHCI: 256 elements
55 #define FRAMELIST_SIZE_BIT_VALUE 2u
56 #define FRAMELIST_SIZE_USBCMD_VALUE ((FRAMELIST_SIZE_BIT_VALUE & 3) << EHCI_USBCMD_POS_FRAMELIST_SIZE)
57#endif
58
59#define FRAMELIST_SIZE (1024 >> FRAMELIST_SIZE_BIT_VALUE)
60
61// Total queue head pool. TODO should be user configurable and more optimize memory usage in the future
62#define QHD_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX + CFG_TUH_HUB)
63#define QTD_MAX QHD_MAX
64
65typedef struct
66{
67 ehci_link_t period_framelist[FRAMELIST_SIZE];
68
69 // TODO only implement 1 ms & 2 ms & 4 ms, 8 ms (framelist)
70 // [0] : 1ms, [1] : 2ms, [2] : 4ms, [3] : 8 ms
71 // TODO better implementation without dummy head to save SRAM
72 ehci_qhd_t period_head_arr[4];
73
74 // Note control qhd of dev0 is used as head of async list
75 struct {
78 }control[CFG_TUH_DEVICE_MAX+CFG_TUH_HUB+1];
79
80 ehci_qhd_t qhd_pool[QHD_MAX];
81 ehci_qtd_t qtd_pool[QTD_MAX] TU_ATTR_ALIGNED(32);
82
83 ehci_registers_t* regs; // operational register
84 ehci_cap_registers_t* cap_regs; // capability register
85
86 volatile uint32_t uframe_number;
88
89// Periodic frame list must be 4K alignment
90CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(4096) static ehci_data_t ehci_data;
91
92//--------------------------------------------------------------------+
93// Debug
94//--------------------------------------------------------------------+
95#if 0 && CFG_TUSB_DEBUG >= (EHCI_DBG + 1)
96static inline void print_portsc(ehci_registers_t* regs) {
97 TU_LOG_HEX(EHCI_DBG, regs->portsc);
98 TU_LOG(EHCI_DBG, " Connect Status : %u\r\n", regs->portsc_bm.current_connect_status);
99 TU_LOG(EHCI_DBG, " Connect Change : %u\r\n", regs->portsc_bm.connect_status_change);
100 TU_LOG(EHCI_DBG, " Enabled : %u\r\n", regs->portsc_bm.port_enabled);
101 TU_LOG(EHCI_DBG, " Enabled Change : %u\r\n", regs->portsc_bm.port_enable_change);
102
103 TU_LOG(EHCI_DBG, " OverCurr Change: %u\r\n", regs->portsc_bm.over_current_change);
104 TU_LOG(EHCI_DBG, " Force Resume : %u\r\n", regs->portsc_bm.force_port_resume);
105 TU_LOG(EHCI_DBG, " Suspend : %u\r\n", regs->portsc_bm.suspend);
106 TU_LOG(EHCI_DBG, " Reset : %u\r\n", regs->portsc_bm.port_reset);
107 TU_LOG(EHCI_DBG, " Power : %u\r\n", regs->portsc_bm.port_power);
108}
109
110static inline void print_intr(uint32_t intr) {
111 TU_LOG_HEX(EHCI_DBG, intr);
112 TU_LOG(EHCI_DBG, " USB Interrupt : %u\r\n", (intr & EHCI_INT_MASK_USB) ? 1 : 0);
113 TU_LOG(EHCI_DBG, " USB Error : %u\r\n", (intr & EHCI_INT_MASK_ERROR) ? 1 : 0);
114 TU_LOG(EHCI_DBG, " Port Change Detect : %u\r\n", (intr & EHCI_INT_MASK_PORT_CHANGE) ? 1 : 0);
115 TU_LOG(EHCI_DBG, " Frame List Rollover: %u\r\n", (intr & EHCI_INT_MASK_FRAMELIST_ROLLOVER) ? 1 : 0);
116 TU_LOG(EHCI_DBG, " Host System Error : %u\r\n", (intr & EHCI_INT_MASK_PCI_HOST_SYSTEM_ERROR) ? 1 : 0);
117 TU_LOG(EHCI_DBG, " Async Advance : %u\r\n", (intr & EHCI_INT_MASK_ASYNC_ADVANCE) ? 1 : 0);
118// TU_LOG(EHCI_DBG, " Interrupt on Async: %u\r\n", (intr & EHCI_INT_MASK_NXP_ASYNC));
119// TU_LOG(EHCI_DBG, " Periodic Schedule : %u\r\n", (intr & EHCI_INT_MASK_NXP_PERIODIC));
120}
121
122#else
123#define print_portsc(_reg)
124#endif
125
126//--------------------------------------------------------------------+
127// PROTOTYPE
128//--------------------------------------------------------------------+
129
130// weak dcache for non-cacheable MCU
131TU_ATTR_WEAK bool hcd_dcache_clean(void const* addr, uint32_t data_size) { (void) addr; (void) data_size; return true; }
132TU_ATTR_WEAK bool hcd_dcache_invalidate(void const* addr, uint32_t data_size) { (void) addr; (void) data_size; return true; }
133TU_ATTR_WEAK bool hcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) { (void) addr; (void) data_size; return true; }
134
135TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* qhd_control(uint8_t dev_addr);
136TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* qhd_next (ehci_qhd_t const * p_qhd);
137TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* qhd_find_free (void);
138static ehci_qhd_t* qhd_get_from_addr (uint8_t dev_addr, uint8_t ep_addr);
139static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc);
140static void qhd_attach_qtd(ehci_qhd_t *qhd, ehci_qtd_t *qtd);
141static void qhd_remove_qtd(ehci_qhd_t *qhd);
142
143TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t* qtd_control(uint8_t dev_addr);
144TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t* qtd_find_free (void);
145static void qtd_init (ehci_qtd_t* qtd, void const* buffer, uint16_t total_bytes);
146
147TU_ATTR_ALWAYS_INLINE static inline ehci_link_t* list_get_period_head(uint8_t rhport, uint32_t interval_ms);
148TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* list_get_async_head(uint8_t rhport);
149TU_ATTR_ALWAYS_INLINE static inline void list_insert (ehci_link_t *current, ehci_link_t *new, uint8_t new_type);
150TU_ATTR_ALWAYS_INLINE static inline ehci_link_t* list_next (ehci_link_t const *p_link);
151static void list_remove_qhd_by_daddr(ehci_link_t* list_head, uint8_t dev_addr);
152
153static void ehci_disable_schedule(ehci_registers_t* regs, bool is_period) {
154 // maybe have a timeout for status
155 if (is_period) {
156 regs->command_bm.periodic_enable = 0;
157 while(regs->status_bm.periodic_status) {}
158 } else {
159 regs->command_bm.async_enable = 0;
160 while(regs->status_bm.async_status) {} // should have a timeout
161 }
162}
163
164static void ehci_enable_schedule(ehci_registers_t* regs, bool is_period) {
165 // maybe have a timeout for status
166 if (is_period) {
167 regs->command_bm.periodic_enable = 1;
168 while ( 0 == regs->status_bm.periodic_status ) {}
169 } else {
170 regs->command_bm.async_enable = 1;
171 while( 0 == regs->status_bm.async_status ) {}
172 }
173}
174
175//--------------------------------------------------------------------+
176// HCD API
177//--------------------------------------------------------------------+
178
179uint32_t hcd_frame_number(uint8_t rhport)
180{
181 (void) rhport;
182 return (ehci_data.uframe_number + ehci_data.regs->frame_index) >> 3;
183}
184
185void hcd_port_reset(uint8_t rhport)
186{
187 (void) rhport;
188
189 ehci_registers_t* regs = ehci_data.regs;
190
191 // skip if already in reset
192 if (regs->portsc_bm.port_reset) {
193 return;
194 }
195
196 // mask out Write-1-to-Clear bits
197 uint32_t portsc = regs->portsc & ~EHCI_PORTSC_MASK_W1C;
198
199 // EHCI Table 2-16 PortSC
200 // when software writes Port Reset bit to a one, it must also write a zero to the Port Enable bit.
203
204 regs->portsc = portsc;
205}
206
207void hcd_port_reset_end(uint8_t rhport)
208{
209 (void) rhport;
210 ehci_registers_t* regs = ehci_data.regs;
211
212 // skip if reset is already complete
213 if (!regs->portsc_bm.port_reset) {
214 return;
215 }
216
217 // mask out all change bits since they are Write 1 to clear
218 uint32_t portsc = regs->portsc & ~EHCI_PORTSC_MASK_W1C;
219 portsc &= ~EHCI_PORTSC_MASK_PORT_RESET;
220
221 regs->portsc = portsc;
222}
223
224bool hcd_port_connect_status(uint8_t rhport)
225{
226 (void) rhport;
227 return ehci_data.regs->portsc_bm.current_connect_status;
228}
229
231{
232 (void) rhport;
233 return (tusb_speed_t) ehci_data.regs->portsc_bm.nxp_port_speed; // NXP specific port speed
234}
235
236// Close all opened endpoint belong to this device
237void hcd_device_close(uint8_t rhport, uint8_t daddr)
238{
239 // skip dev0
240 if (daddr == 0) {
241 return;
242 }
243
244 // Remove from async list
246
247 // Remove from all interval period list
248 for(uint8_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++) {
249 list_remove_qhd_by_daddr((ehci_link_t *) &ehci_data.period_head_arr[i], daddr);
250 }
251
252 // Async doorbell (EHCI 4.8.2 for operational details)
253 ehci_data.regs->command_bm.async_adv_doorbell = 1;
254}
255
256static void init_periodic_list(uint8_t rhport) {
257 (void) rhport;
258
259 // Build the polling interval tree with 1 ms, 2 ms, 4 ms and 8 ms (framesize) only
260 for ( uint32_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++ ) {
261 ehci_data.period_head_arr[i].int_smask = 1; // queue head in period list must have smask non-zero
262 ehci_data.period_head_arr[i].qtd_overlay.halted = 1; // dummy node, always inactive
263 }
264
265 // TODO EHCI_FRAMELIST_SIZE with other size than 8
266 // all links --> period_head_arr[0] (1ms)
267 // 0, 2, 4, 6 etc --> period_head_arr[1] (2ms)
268 // 1, 5 --> period_head_arr[2] (4ms)
269 // 3 --> period_head_arr[3] (8ms)
270
271 ehci_link_t * const framelist = ehci_data.period_framelist;
272 ehci_link_t * const head_1ms = (ehci_link_t *) &ehci_data.period_head_arr[0];
273 ehci_link_t * const head_2ms = (ehci_link_t *) &ehci_data.period_head_arr[1];
274 ehci_link_t * const head_4ms = (ehci_link_t *) &ehci_data.period_head_arr[2];
275 ehci_link_t * const head_8ms = (ehci_link_t *) &ehci_data.period_head_arr[3];
276
277 for (uint32_t i = 0; i < FRAMELIST_SIZE; i++) {
278 framelist[i].address = (uint32_t) head_1ms;
279 framelist[i].type = EHCI_QTYPE_QHD;
280 }
281
282 for (uint32_t i = 0; i < FRAMELIST_SIZE; i += 2) {
283 list_insert(framelist + i, head_2ms, EHCI_QTYPE_QHD);
284 }
285
286 for (uint32_t i = 1; i < FRAMELIST_SIZE; i += 4) {
287 list_insert(framelist + i, head_4ms, EHCI_QTYPE_QHD);
288 }
289
290 list_insert(framelist + 3, head_8ms, EHCI_QTYPE_QHD);
291
292 head_1ms->terminate = 1;
293}
294
295bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
296{
297 tu_memclr(&ehci_data, sizeof(ehci_data_t));
298
299 ehci_data.regs = (ehci_registers_t*) operatial_reg;
300 ehci_data.cap_regs = (ehci_cap_registers_t*) capability_reg;
301
302 ehci_registers_t* regs = ehci_data.regs;
303
304 // EHCI 4.1 Host Controller Initialization
305
306 //------------- CTRLDSSEGMENT Register (skip) -------------//
307
308 //------------- USB INT Register -------------//
309
310 // disable all the interrupt
311 regs->inten = 0;
312
313 // clear all status except port change since device maybe connected before this driver is initialized
314 regs->status = (EHCI_INT_MASK_ALL & ~EHCI_INT_MASK_PORT_CHANGE);
315
316 // Enable interrupts
319
320 //------------- Asynchronous List -------------//
321 ehci_qhd_t * const async_head = list_get_async_head(rhport);
322 tu_memclr(async_head, sizeof(ehci_qhd_t));
323
324 async_head->next.address = (uint32_t) async_head; // circular list, next is itself
325 async_head->next.type = EHCI_QTYPE_QHD;
326 async_head->head_list_flag = 1;
327 async_head->qtd_overlay.halted = 1; // inactive most of time
328 async_head->qtd_overlay.next.terminate = 1; // TODO removed if verified
329
330 regs->async_list_addr = (uint32_t) async_head;
331
332 //------------- Periodic List -------------//
333 init_periodic_list(rhport);
334 regs->periodic_list_base = (uint32_t) ehci_data.period_framelist;
335
336 hcd_dcache_clean(&ehci_data, sizeof(ehci_data_t));
337
338 //------------- TT Control (NXP only) -------------//
339 regs->nxp_tt_control = 0;
340
341 //------------- USB CMD Register -------------//
343 FRAMELIST_SIZE_USBCMD_VALUE;
344
345 //------------- ConfigFlag Register (skip) -------------//
346
347 // enable port power bit in portsc. The function of this bit depends on the value of the Port
348 // Power Control (PPC) field in the HCSPARAMS register.
349 if (ehci_data.cap_regs->hcsparams_bm.port_power_control) {
350 // mask out all change bits since they are Write 1 to clear
351 uint32_t portsc = (regs->portsc & ~EHCI_PORTSC_MASK_W1C);
353
354 regs->portsc = portsc;
355 }
356
357 return true;
358}
359
360#if 0
361static void ehci_stop(uint8_t rhport)
362{
363 (void) rhport;
364
365 ehci_registers_t* regs = ehci_data.regs;
366
367 regs->command_bm.run_stop = 0;
368
369 // USB Spec: controller has to stop within 16 uframe = 2 frames
370 while( regs->status_bm.hc_halted == 0 ) {}
371}
372#endif
373
374//--------------------------------------------------------------------+
375// Endpoint API
376//--------------------------------------------------------------------+
377
378bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
379{
380 (void) rhport;
381
382 // TODO not support ISO yet
383 TU_ASSERT (ep_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
384
385 //------------- Prepare Queue Head -------------//
386 ehci_qhd_t *p_qhd = (ep_desc->bEndpointAddress == 0) ? qhd_control(dev_addr) : qhd_find_free();
387 TU_ASSERT(p_qhd);
388
389 qhd_init(p_qhd, dev_addr, ep_desc);
390
391 // control of dev0 is always present as async head
392 if ( dev_addr == 0 ) return true;
393
394 // Insert to list
395 ehci_link_t * list_head = NULL;
396
397 switch (ep_desc->bmAttributes.xfer)
398 {
400 case TUSB_XFER_BULK:
401 list_head = (ehci_link_t*) list_get_async_head(rhport);
402 break;
403
405 list_head = list_get_period_head(rhport, p_qhd->interval_ms);
406 break;
407
409 // TODO iso is not supported
410 break;
411
412 default: break;
413 }
414 TU_ASSERT(list_head);
415
416 list_insert(list_head, (ehci_link_t*) p_qhd, EHCI_QTYPE_QHD);
417
418 hcd_dcache_clean(p_qhd, sizeof(ehci_qhd_t));
419 hcd_dcache_clean(list_head, sizeof(ehci_qhd_t));
420
421 return true;
422}
423
424bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
425{
426 (void) rhport;
427
428 ehci_qhd_t* qhd = &ehci_data.control[dev_addr].qhd;
429 ehci_qtd_t* td = &ehci_data.control[dev_addr].qtd;
430
431 qtd_init(td, setup_packet, 8);
432 td->pid = EHCI_PID_SETUP;
433
434 hcd_dcache_clean(setup_packet, 8);
435
436 // Control endpoint never be stalled. Skip reset Data Toggle since it is fixed per stage
437 if (qhd->qtd_overlay.halted) {
438 qhd->qtd_overlay.halted = false;
439 }
440
441 // attach TD to QHD -> start transferring
442 qhd_attach_qtd(qhd, td);
443
444 return true;
445}
446
447bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
448{
449 (void) rhport;
450
451 uint8_t const epnum = tu_edpt_number(ep_addr);
452 uint8_t const dir = tu_edpt_dir(ep_addr);
453
454 ehci_qhd_t* qhd = qhd_get_from_addr(dev_addr, ep_addr);
455 ehci_qtd_t* qtd;
456
457 if (epnum == 0) {
458 // Control endpoint never be stalled. Skip reset Data Toggle since it is fixed per stage
459 if (qhd->qtd_overlay.halted) {
460 qhd->qtd_overlay.halted = false;
461 }
462
463 qtd = qtd_control(dev_addr);
464 qtd_init(qtd, buffer, buflen);
465
466 // first data toggle is always 1 (data & setup stage)
467 qtd->data_toggle = 1;
468 qtd->pid = dir ? EHCI_PID_IN : EHCI_PID_OUT;
469 } else {
470 // skip if endpoint is halted
471 TU_VERIFY(!qhd->qtd_overlay.halted);
472
473 qtd = qtd_find_free();
474 TU_ASSERT(qtd);
475
476 qtd_init(qtd, buffer, buflen);
477 qtd->pid = qhd->pid;
478 }
479
480 // IN transfer: invalidate buffer, OUT transfer: clean buffer
481 if (dir) {
483 }else {
484 hcd_dcache_clean(buffer, buflen);
485 }
486
487 // attach TD to QHD -> start transferring
488 qhd_attach_qtd(qhd, qtd);
489
490 return true;
491}
492
493bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
494 (void) rhport;
495
496 // TODO ISO not supported yet
497 ehci_qhd_t* qhd = qhd_get_from_addr(dev_addr, ep_addr);
498 ehci_qtd_t * volatile qtd = qhd->attached_qtd;
499 TU_VERIFY(qtd != NULL); // no queued transfer
500
501 hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t));
502 TU_VERIFY(qtd->active); // transfer is already complete
503
504 // HC is still processing, disable HC list schedule before making changes
505 bool const is_period = (qhd->interval_ms > 0);
506
507 ehci_disable_schedule(ehci_data.regs, is_period);
508
509 // check active bit again just in case HC has just processed the TD
510 bool const still_active = qtd->active;
511 if (still_active) {
512 // remove TD from QH overlay
513 qhd->qtd_overlay.next.terminate = 1;
514 hcd_dcache_clean(qhd, sizeof(ehci_qhd_t));
515
516 // remove TD from QH software list
517 qhd_remove_qtd(qhd);
518 }
519
520 ehci_enable_schedule(ehci_data.regs, is_period);
521
522 return still_active; // true if removed an active transfer
523}
524
525bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
526 (void) rhport;
527 ehci_qhd_t *qhd = qhd_get_from_addr(daddr, ep_addr);
528 qhd->qtd_overlay.halted = 0;
529 qhd->qtd_overlay.data_toggle = 0;
531
532 return true;
533}
534
535//--------------------------------------------------------------------+
536// EHCI Interrupt Handler
537//--------------------------------------------------------------------+
538
539// async_advance is handshake between usb stack & ehci controller.
540// This isr mean it is safe to modify previously removed queue head from async list.
541// In tinyusb, queue head is only removed when device is unplugged.
542TU_ATTR_ALWAYS_INLINE static inline
543void async_advance_isr(uint8_t rhport)
544{
545 (void) rhport;
546
547 ehci_qhd_t *qhd_pool = ehci_data.qhd_pool;
548 for (uint32_t i = 0; i < QHD_MAX; i++) {
549 if (qhd_pool[i].removing) {
550 qhd_pool[i].removing = 0;
551 qhd_pool[i].used = 0;
552 }
553 }
554}
555
556TU_ATTR_ALWAYS_INLINE static inline
557void port_connect_status_change_isr(uint8_t rhport) {
558 // NOTE There is an sequence plug->unplug->…..-> plug if device is powering with pre-plugged device
559 if ( ehci_data.regs->portsc_bm.current_connect_status ) {
560 hcd_port_reset(rhport);
561 hcd_event_device_attach(rhport, true);
562 } else // device unplugged
563 {
564 hcd_event_device_remove(rhport, true);
565 }
566}
567
568// Check queue head for potential transfer complete (successful or error)
569TU_ATTR_ALWAYS_INLINE static inline
571 hcd_dcache_invalidate(qhd, sizeof(ehci_qhd_t)); // HC may have updated the overlay
572 volatile ehci_qtd_t *qtd_overlay = &qhd->qtd_overlay;
573
574 // process non-active (completed) QHD with attached (scheduled) TD
575 if ( !qtd_overlay->active && qhd->attached_qtd != NULL ) {
576 xfer_result_t xfer_result;
577
578 if ( qtd_overlay->halted ) {
579 if (qtd_overlay->xact_err || qtd_overlay->err_count == 0 || qtd_overlay->buffer_err || qtd_overlay->babble_err) {
580 // Error count = 0 often occurs when device disconnected, or other bus-related error
581 // clear halted bit if not caused by STALL to allow more transfer
582 xfer_result = XFER_RESULT_FAILED;
583 qtd_overlay->halted = false;
584 TU_LOG3(" QHD xfer err count: %d\r\n", qtd_overlay->err_count);
585 // TU_BREAKPOINT(); // TODO skip unplugged device
586 }else {
587 // no error bits are set, endpoint is halted due to STALL
588 xfer_result = XFER_RESULT_STALLED;
589 }
590 } else {
591 xfer_result = XFER_RESULT_SUCCESS;
592 }
593
594 ehci_qtd_t * volatile qtd = qhd->attached_qtd;
595 hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t)); // HC may have written back TD
596
597 uint8_t const dir = (qtd->pid == EHCI_PID_IN) ? 1 : 0;
598 uint32_t const xferred_bytes = qtd->expected_bytes - qtd->total_bytes;
599
600 // invalidate dcache if IN transfer with data
601 if (dir == 1 && qhd->attached_buffer != 0 && xferred_bytes > 0) {
602 hcd_dcache_invalidate((void*) qhd->attached_buffer, xferred_bytes);
603 }
604
605 // remove and free TD before invoking callback
606 qhd_remove_qtd(qhd);
607
608 // notify usbh
609 uint8_t const ep_addr = tu_edpt_addr(qhd->ep_number, dir);
610 hcd_event_xfer_complete(qhd->dev_addr, ep_addr, xferred_bytes, xfer_result, true);
611 }
612}
613
614TU_ATTR_ALWAYS_INLINE static inline
615void proccess_async_xfer_isr(ehci_qhd_t * const list_head)
616{
617 ehci_qhd_t *qhd = list_head;
618
619 do {
621 qhd = qhd_next(qhd);
622 } while ( qhd != list_head ); // async list traversal, stop if loop around
623}
624
625TU_ATTR_ALWAYS_INLINE static inline
626void process_period_xfer_isr(uint8_t rhport, uint32_t interval_ms)
627{
628 uint32_t const period_1ms_addr = (uint32_t) list_get_period_head(rhport, 1u);
629 ehci_link_t next_link = *list_get_period_head(rhport, interval_ms);
630
631 while (!next_link.terminate) {
632 if (interval_ms > 1 && period_1ms_addr == tu_align32(next_link.address)) {
633 // 1ms period list is end of list for all larger interval
634 break;
635 }
636
637 uintptr_t const entry_addr = tu_align32(next_link.address);
638
639 switch (next_link.type) {
640 case EHCI_QTYPE_QHD: {
641 ehci_qhd_t *qhd = (ehci_qhd_t *) entry_addr;
643 }
644 break;
645
646 // TODO support hs/fs ISO
647 case EHCI_QTYPE_ITD:
648 case EHCI_QTYPE_SITD:
649 case EHCI_QTYPE_FSTN:
650 default:
651 break;
652 }
653
654 next_link = *list_next(&next_link);
655 }
656}
657
658//------------- Host Controller Driver's Interrupt Handler -------------//
659void hcd_int_handler(uint8_t rhport, bool in_isr) {
660 (void) in_isr;
661 ehci_registers_t* regs = ehci_data.regs;
662 uint32_t const int_status = regs->status;
663
664 if (int_status & EHCI_INT_MASK_HC_HALTED) {
665 // something seriously wrong, maybe forget to flush/invalidate cache
666 TU_BREAKPOINT();
667 TU_LOG1(" HC halted\r\n");
668 return;
669 }
670
671 if (int_status & EHCI_INT_MASK_FRAMELIST_ROLLOVER) {
672 ehci_data.uframe_number += (FRAMELIST_SIZE << 3);
673 regs->status = EHCI_INT_MASK_FRAMELIST_ROLLOVER; // Acknowledge
674 }
675
676 if (int_status & EHCI_INT_MASK_PORT_CHANGE) {
677 // Including: Force port resume, over-current change, enable/disable change and connect status change.
678 uint32_t const port_status = regs->portsc & EHCI_PORTSC_MASK_W1C;
679 // print_portsc(regs);
680
683 }
684
685 regs->portsc |= port_status; // Acknowledge change bits in portsc
686 regs->status = EHCI_INT_MASK_PORT_CHANGE; // Acknowledge
687 }
688
689 // A USB transfer is completed (OK or error)
690 uint32_t const usb_int = int_status & (EHCI_INT_MASK_USB | EHCI_INT_MASK_ERROR);
691 if (usb_int) {
693
694 for ( uint32_t i = 1; i <= FRAMELIST_SIZE; i *= 2 ) {
695 process_period_xfer_isr(rhport, i);
696 }
697
698 regs->status = usb_int; // Acknowledge
699 }
700
701 //------------- There is some removed async previously -------------//
702 // need to place after EHCI_INT_MASK_NXP_ASYNC
703 if (int_status & EHCI_INT_MASK_ASYNC_ADVANCE) {
704 async_advance_isr(rhport);
705 regs->status = EHCI_INT_MASK_ASYNC_ADVANCE; // Acknowledge
706 }
707}
708
709//--------------------------------------------------------------------+
710// List Managing Helper
711//--------------------------------------------------------------------+
712
713// Get head of periodic list
714TU_ATTR_ALWAYS_INLINE static inline ehci_link_t* list_get_period_head(uint8_t rhport, uint32_t interval_ms) {
715 (void) rhport;
716 return (ehci_link_t*) &ehci_data.period_head_arr[ tu_log2( tu_min32(FRAMELIST_SIZE, interval_ms) ) ];
717}
718
719// Get head of async list
720TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* list_get_async_head(uint8_t rhport) {
721 (void) rhport;
722 return qhd_control(0); // control qhd of dev0 is used as async head
723}
724
725TU_ATTR_ALWAYS_INLINE static inline ehci_link_t* list_next(ehci_link_t const *p_link) {
726 return (ehci_link_t*) tu_align32(p_link->address);
727}
728
729TU_ATTR_ALWAYS_INLINE static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type)
730{
731 new->address = current->address;
732 current->address = ((uint32_t) new) | (new_type << 1);
733}
734
735// Remove all queue head belong to this device address
736static void list_remove_qhd_by_daddr(ehci_link_t* list_head, uint8_t dev_addr) {
737 ehci_link_t* prev = list_head;
738
739 while (prev && !prev->terminate) {
740 ehci_qhd_t* qhd = (ehci_qhd_t*) (uintptr_t) list_next(prev);
741
742 // done if loop back to head
743 if ( (uintptr_t) qhd == (uintptr_t) list_head) {
744 break;
745 }
746
747 if ( qhd->dev_addr == dev_addr ) {
748 // TODO deactivate all TD, wait for QHD to inactive before removal
749 prev->address = qhd->next.address;
750
751 // EHCI 4.8.2 link the removed qhd's next to async head (which always reachable by Host Controller)
752 qhd->next.address = ((uint32_t) list_head) | (EHCI_QTYPE_QHD << 1);
753
754 if ( qhd->int_smask )
755 {
756 // period list queue element is guarantee to be free in the next frame (1 ms)
757 qhd->used = 0;
758 }else
759 {
760 // async list use async advance handshake
761 // mark as removing, will completely re-usable when async advance isr occurs
762 qhd->removing = 1;
763 }
764
765 hcd_dcache_clean(qhd, sizeof(ehci_qhd_t));
766 hcd_dcache_clean(prev, sizeof(ehci_qhd_t));
767 }else {
768 prev = list_next(prev);
769 }
770 }
771}
772
773
774//--------------------------------------------------------------------+
775// Queue Header helper
776//--------------------------------------------------------------------+
777
778// Get queue head for control transfer (always available)
779TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* qhd_control(uint8_t dev_addr) {
780 return &ehci_data.control[dev_addr].qhd;
781}
782
783// Find a free queue head
784TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t *qhd_find_free(void) {
785 for ( uint32_t i = 0; i < QHD_MAX; i++ ) {
786 if ( !ehci_data.qhd_pool[i].used ) return &ehci_data.qhd_pool[i];
787 }
788 return NULL;
789}
790
791// Next queue head link
792TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t *qhd_next(ehci_qhd_t const *p_qhd) {
793 return (ehci_qhd_t *) tu_align32(p_qhd->next.address);
794}
795
796// Get queue head from device + endpoint address
797static ehci_qhd_t *qhd_get_from_addr(uint8_t dev_addr, uint8_t ep_addr) {
798 if ( 0 == tu_edpt_number(ep_addr) ) {
799 return qhd_control(dev_addr);
800 }
801
802 ehci_qhd_t *qhd_pool = ehci_data.qhd_pool;
803
804 for ( uint32_t i = 0; i < QHD_MAX; i++ ) {
805 if ( (qhd_pool[i].dev_addr == dev_addr) &&
806 ep_addr == tu_edpt_addr(qhd_pool[i].ep_number, qhd_pool[i].pid) ) {
807 return &qhd_pool[i];
808 }
809 }
810
811 return NULL;
812}
813
814// Init queue head with endpoint descriptor
815static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
816{
817 // address 0 is used as async head, which always on the list --> cannot be cleared (ehci halted otherwise)
818 if (dev_addr != 0) {
819 tu_memclr(p_qhd, sizeof(ehci_qhd_t));
820 }
821
822 hcd_devtree_info_t devtree_info;
823 hcd_devtree_get_info(dev_addr, &devtree_info);
824
825 uint8_t const xfer_type = ep_desc->bmAttributes.xfer;
826 uint8_t const interval = ep_desc->bInterval;
827
828 p_qhd->dev_addr = dev_addr;
829 p_qhd->fl_inactive_next_xact = 0;
830 p_qhd->ep_number = tu_edpt_number(ep_desc->bEndpointAddress);
831 p_qhd->ep_speed = devtree_info.speed;
832 p_qhd->data_toggle_control= (xfer_type == TUSB_XFER_CONTROL) ? 1 : 0;
833 p_qhd->head_list_flag = (dev_addr == 0) ? 1 : 0; // addr0's endpoint is the static asyn list head
834 p_qhd->max_packet_size = tu_edpt_packet_size(ep_desc);
835 p_qhd->fl_ctrl_ep_flag = ((xfer_type == TUSB_XFER_CONTROL) && (p_qhd->ep_speed != TUSB_SPEED_HIGH)) ? 1 : 0;
836 p_qhd->nak_reload = 0;
837
838 // Bulk/Control -> smask = cmask = 0
839 // TODO Isochronous
840 if (TUSB_XFER_INTERRUPT == xfer_type)
841 {
842 if (TUSB_SPEED_HIGH == p_qhd->ep_speed)
843 {
844 TU_ASSERT( interval <= 16, );
845 if ( interval < 4) // sub millisecond interval
846 {
847 p_qhd->interval_ms = 0;
848 p_qhd->int_smask = (interval == 1) ? TU_BIN8(11111111) :
849 (interval == 2) ? TU_BIN8(10101010) : TU_BIN8(01000100);
850 }else
851 {
852 p_qhd->interval_ms = (uint8_t) tu_min16( 1 << (interval-4), 255 );
853 p_qhd->int_smask = TU_BIT(interval % 8);
854 }
855 }else
856 {
857 TU_ASSERT( 0 != interval, );
858 // Full/Low: 4.12.2.1 (EHCI) case 1 schedule start split at 1 us & complete split at 2,3,4 uframes
859 p_qhd->int_smask = 0x01;
860 p_qhd->fl_int_cmask = TU_BIN8(11100);
861 p_qhd->interval_ms = interval;
862 }
863 }else
864 {
865 p_qhd->int_smask = p_qhd->fl_int_cmask = 0;
866 }
867
868 p_qhd->fl_hub_addr = devtree_info.hub_addr;
869 p_qhd->fl_hub_port = devtree_info.hub_port;
870 p_qhd->mult = 1; // TODO not use high bandwidth/park mode yet
871
872 //------------- HCD Management Data -------------//
873 p_qhd->used = 1;
874 p_qhd->removing = 0;
875 p_qhd->attached_qtd = NULL;
876 p_qhd->pid = tu_edpt_dir(ep_desc->bEndpointAddress) ? EHCI_PID_IN : EHCI_PID_OUT; // PID for TD under this endpoint
877
878 //------------- active, but no TD list -------------//
879 p_qhd->qtd_overlay.halted = 0;
880 p_qhd->qtd_overlay.next.terminate = 1;
881 p_qhd->qtd_overlay.alternate.terminate = 1;
882
883 if (TUSB_XFER_BULK == xfer_type && p_qhd->ep_speed == TUSB_SPEED_HIGH && p_qhd->pid == EHCI_PID_OUT)
884 {
885 p_qhd->qtd_overlay.ping_err = 1; // do PING for Highspeed Bulk OUT, EHCI section 4.11
886 }
887}
888
889// Attach a TD to queue head
890static void qhd_attach_qtd(ehci_qhd_t *qhd, ehci_qtd_t *qtd) {
891 qhd->attached_qtd = qtd;
892 qhd->attached_buffer = qtd->buffer[0];
893
894 // clean and invalidate cache before physically write
896
897 qhd->qtd_overlay.next.address = (uint32_t) qtd;
899}
900
901// Remove an attached TD from queue head
902static void qhd_remove_qtd(ehci_qhd_t *qhd) {
903 ehci_qtd_t * volatile qtd = qhd->attached_qtd;
904
905 qhd->attached_qtd = NULL;
906 qhd->attached_buffer = 0;
907 hcd_dcache_clean(qhd, sizeof(ehci_qhd_t));
908
909 qtd->used = 0; // free QTD
910 hcd_dcache_clean(qtd, sizeof(ehci_qtd_t));
911}
912
913//--------------------------------------------------------------------+
914// Queue TD helper
915//--------------------------------------------------------------------+
916
917// Get TD for control transfer (always available)
918TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t* qtd_control(uint8_t dev_addr) {
919 return &ehci_data.control[dev_addr].qtd;
920}
921
922TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t *qtd_find_free(void) {
923 for (uint32_t i = 0; i < QTD_MAX; i++) {
924 if (!ehci_data.qtd_pool[i].used) return &ehci_data.qtd_pool[i];
925 }
926 return NULL;
927}
928
929static void qtd_init(ehci_qtd_t* qtd, void const* buffer, uint16_t total_bytes) {
930 tu_memclr(qtd, sizeof(ehci_qtd_t));
931 qtd->used = 1;
932
933 qtd->next.terminate = 1; // init to null
934 qtd->alternate.terminate = 1; // not used, always set to terminated
935 qtd->active = 1;
936 qtd->err_count = 3; // TODO 3 consecutive errors tolerance
937 qtd->data_toggle = 0;
938 qtd->int_on_complete = 1;
941
942 qtd->buffer[0] = (uint32_t) buffer;
943 for(uint8_t i=1; i<5; i++) {
944 qtd->buffer[i] |= tu_align4k(qtd->buffer[i - 1] ) + 4096;
945 }
946}
947
948#endif
static bool in_isr
uint16_t total_bytes
Definition: dcd_nuc505.c:113
uint8_t dev_addr
Definition: dcd_pic32mz.c:81
static void list_remove_qhd_by_daddr(ehci_link_t *list_head, uint8_t dev_addr)
Definition: ehci.c:736
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
Definition: ehci.c:424
static TU_ATTR_ALWAYS_INLINE ehci_qhd_t * qhd_find_free(void)
Definition: ehci.c:784
static TU_ATTR_ALWAYS_INLINE void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type)
Definition: ehci.c:729
static TU_ATTR_ALWAYS_INLINE ehci_qhd_t * qhd_next(ehci_qhd_t const *p_qhd)
Definition: ehci.c:792
static void qtd_init(ehci_qtd_t *qtd, void const *buffer, uint16_t total_bytes)
Definition: ehci.c:929
TU_ATTR_WEAK bool hcd_dcache_invalidate(void const *addr, uint32_t data_size)
Definition: ehci.c:132
static void init_periodic_list(uint8_t rhport)
Definition: ehci.c:256
static void ehci_stop(uint8_t rhport)
Definition: ehci.c:361
static ehci_qhd_t * qhd_get_from_addr(uint8_t dev_addr, uint8_t ep_addr)
Definition: ehci.c:797
static TU_ATTR_ALWAYS_INLINE ehci_link_t * list_get_period_head(uint8_t rhport, uint32_t interval_ms)
Definition: ehci.c:714
static void qhd_attach_qtd(ehci_qhd_t *qhd, ehci_qtd_t *qtd)
Definition: ehci.c:890
static TU_ATTR_ALWAYS_INLINE ehci_link_t * list_next(ehci_link_t const *p_link)
Definition: ehci.c:725
static TU_ATTR_ALWAYS_INLINE void qhd_xfer_complete_isr(ehci_qhd_t *qhd)
Definition: ehci.c:570
static TU_ATTR_ALWAYS_INLINE void port_connect_status_change_isr(uint8_t rhport)
Definition: ehci.c:557
static TU_ATTR_ALWAYS_INLINE ehci_qhd_t * qhd_control(uint8_t dev_addr)
Definition: ehci.c:779
TU_ATTR_WEAK bool hcd_dcache_clean_invalidate(void const *addr, uint32_t data_size)
Definition: ehci.c:133
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen)
Definition: ehci.c:447
bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t daddr, uint8_t ep_addr)
Definition: ehci.c:525
bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr)
Definition: ehci.c:493
static TU_ATTR_ALWAYS_INLINE void async_advance_isr(uint8_t rhport)
Definition: ehci.c:543
void hcd_port_reset_end(uint8_t rhport)
Definition: ehci.c:207
static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const *ep_desc)
Definition: ehci.c:815
bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
Definition: ehci.c:295
void hcd_port_reset(uint8_t rhport)
Definition: ehci.c:185
static void print_intr(uint32_t intr)
Definition: ehci.c:110
bool hcd_port_connect_status(uint8_t rhport)
Definition: ehci.c:224
static TU_ATTR_ALWAYS_INLINE void proccess_async_xfer_isr(ehci_qhd_t *const list_head)
Definition: ehci.c:615
uint32_t hcd_frame_number(uint8_t rhport)
Definition: ehci.c:179
static TU_ATTR_ALWAYS_INLINE void process_period_xfer_isr(uint8_t rhport, uint32_t interval_ms)
Definition: ehci.c:626
tusb_speed_t hcd_port_speed_get(uint8_t rhport)
Definition: ehci.c:230
void hcd_int_handler(uint8_t rhport, bool in_isr)
Definition: ehci.c:659
static TU_ATTR_ALWAYS_INLINE ehci_qtd_t * qtd_control(uint8_t dev_addr)
Definition: ehci.c:918
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const *ep_desc)
Definition: ehci.c:378
void hcd_device_close(uint8_t rhport, uint8_t daddr)
Definition: ehci.c:237
CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(4096)
Definition: ehci.c:90
TU_ATTR_WEAK bool hcd_dcache_clean(void const *addr, uint32_t data_size)
Definition: ehci.c:131
static TU_ATTR_ALWAYS_INLINE ehci_qtd_t * qtd_find_free(void)
Definition: ehci.c:922
static void ehci_enable_schedule(ehci_registers_t *regs, bool is_period)
Definition: ehci.c:164
static TU_ATTR_ALWAYS_INLINE ehci_qhd_t * list_get_async_head(uint8_t rhport)
Definition: ehci.c:720
static void qhd_remove_qtd(ehci_qhd_t *qhd)
Definition: ehci.c:902
static void ehci_disable_schedule(ehci_registers_t *regs, bool is_period)
Definition: ehci.c:153
@ EHCI_INT_MASK_PCI_HOST_SYSTEM_ERROR
Definition: ehci.h:271
@ EHCI_INT_MASK_ALL
Definition: ehci.h:281
@ EHCI_INT_MASK_USB
Definition: ehci.h:267
@ EHCI_INT_MASK_FRAMELIST_ROLLOVER
Definition: ehci.h:270
@ EHCI_INT_MASK_HC_HALTED
Definition: ehci.h:276
@ EHCI_INT_MASK_ERROR
Definition: ehci.h:268
@ EHCI_INT_MASK_PORT_CHANGE
Definition: ehci.h:269
@ EHCI_INT_MASK_ASYNC_ADVANCE
Definition: ehci.h:272
@ EHCI_QTYPE_FSTN
Definition: ehci.h:64
@ EHCI_QTYPE_SITD
Definition: ehci.h:63
@ EHCI_QTYPE_QHD
Definition: ehci.h:62
@ EHCI_QTYPE_ITD
Definition: ehci.h:61
@ EHCI_PORTSC_MASK_PORT_POWER
Definition: ehci.h:310
@ EHCI_PORTSC_MASK_PORT_EANBLED
Definition: ehci.h:304
@ EHCI_PORTSC_MASK_W1C
Definition: ehci.h:312
@ EHCI_PORTSC_MASK_PORT_RESET
Definition: ehci.h:309
@ EHCI_PID_IN
Definition: ehci.h:71
@ EHCI_PID_OUT
Definition: ehci.h:70
@ EHCI_PID_SETUP
Definition: ehci.h:72
@ EHCI_USBCMD_ASYNC_SCHEDULE_ENABLE
Definition: ehci.h:297
@ EHCI_USBCMD_RUN_STOP
Definition: ehci.h:294
@ EHCI_USBCMD_PERIOD_SCHEDULE_ENABLE
Definition: ehci.h:296
ehci_qhd_t
Definition: ehci.h:173
static TU_ATTR_ALWAYS_INLINE void hcd_event_device_remove(uint8_t rhport, bool in_isr)
Definition: hcd.h:209
static TU_ATTR_ALWAYS_INLINE void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred_bytes, xfer_result_t result, bool in_isr)
Definition: hcd.h:221
static TU_ATTR_ALWAYS_INLINE void hcd_event_device_attach(uint8_t rhport, bool in_isr)
Definition: hcd.h:197
void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t *devtree_info)
Definition: usbh.c:949
uint8_t const * buffer
Definition: midi_device.h:100
AUDIO Channel Cluster Descriptor (4.1)
Definition: audio.h:647
uint8_t bInterval
Definition: tusb_types.h:372
uint8_t bmAttributes
See: audio_clock_source_attribute_t.
Definition: audio.h:672
uint8_t bEndpointAddress
Definition: video.h:306
volatile uint32_t uframe_number
Definition: ehci.c:86
ehci_qtd_t qtd
Definition: ehci.c:77
ehci_cap_registers_t * cap_regs
Definition: ehci.c:84
ehci_registers_t * regs
Definition: ehci.c:83
ehci_qtd_t qtd_pool[QTD_MAX] TU_ATTR_ALIGNED(32)
ehci_qhd_t qhd
Definition: ehci.c:76
volatile uint32_t buffer_err
Data overrun/underrun error.
Definition: ehci.h:110
ehci_link_t alternate
Definition: ehci.h:95
uint32_t used
Definition: ehci.h:98
ehci_link_t next
Definition: ehci.h:91
uint32_t pid
0: OUT, 1: IN, 2 Setup
Definition: ehci.h:114
volatile uint32_t babble_err
Babble detected, also set Halted bit to 1.
Definition: ehci.h:109
volatile uint32_t active
Start transfer, clear by HC when complete.
Definition: ehci.h:112
uint32_t buffer[5]
Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address....
Definition: ehci.h:123
volatile uint32_t total_bytes
Transfer bytes, decreased during transaction.
Definition: ehci.h:118
uint32_t expected_bytes
Definition: ehci.h:100
volatile uint32_t halted
Serious error or STALL received.
Definition: ehci.h:111
volatile uint32_t data_toggle
Data Toggle bit.
Definition: ehci.h:119
volatile uint32_t xact_err
Error (Timeout, CRC, Bad PID ... )
Definition: ehci.h:108
volatile uint32_t err_count
Error Counter of consecutive errors.
Definition: ehci.h:115
uint32_t int_on_complete
Interrupt on complete.
Definition: ehci.h:117
uint32_t status
Definition: ehci.h:341
uint32_t port_power
12: 0= power off, 1= power on
Definition: ehci.h:406
uint32_t async_list_addr
0x18 Address of next async QHD to be executed
Definition: ehci.h:386
uint32_t connect_status_change
01: [R/WC] Change in Current Connect Status
Definition: ehci.h:396
uint32_t port_reset
08: 1=Port is in Reset. 0=Port is not in Reset
Definition: ehci.h:403
const struct ehci_registers_t::@169::@174 portsc_bm
uint32_t async_status
Async schedule status.
Definition: ehci.h:356
uint32_t port_enabled
02: Ports can only be enabled by HC as a part of the reset and enable. SW can write 0 to disable
Definition: ehci.h:397
uint32_t async_enable
This bit controls whether the host controller skips processing the Asynchronous Schedule....
Definition: ehci.h:328
uint32_t run_stop
1=Run. 0=Stop
Definition: ehci.h:324
uint32_t suspend
07: Port in suspend state
Definition: ehci.h:402
uint32_t force_port_resume
06: Resume detected/driven on port. This functionality defined for manipulating this bit depends on t...
Definition: ehci.h:401
uint32_t over_current_change
05: [R/WC] Change to Over-current Active
Definition: ehci.h:400
uint32_t port_enable_change
03: [R/WC] Port Enabled has changed
Definition: ehci.h:398
uint32_t periodic_enable
This bit controls whether the host controller skips processing the Periodic Schedule....
Definition: ehci.h:327
uint32_t nxp_tt_control
nxp embedded transaction translator (reserved by EHCI specs)
Definition: ehci.h:387
uint32_t hc_halted
Opposite value to run_stop bit.
Definition: ehci.h:353
struct ehci_registers_t::@163::@171 command_bm
uint32_t periodic_status
Periodic schedule status.
Definition: ehci.h:355
struct ehci_registers_t::@165::@172 status_bm
uint32_t current_connect_status
00: 0: No device, 1: Device is present on port
Definition: ehci.h:395
uint32_t command
Definition: ehci.h:321
uint32_t portsc
0x44 port status and control
Definition: ehci.h:393
uint32_t periodic_list_base
0x14 Beginning address of perodic frame list
Definition: ehci.h:385
uint32_t inten
Definition: ehci.h:365
uint8_t hub_port
Definition: hcd.h:96
uint8_t hub_addr
Definition: hcd.h:95
uint8_t speed
Definition: hcd.h:97
static TU_ATTR_ALWAYS_INLINE uint32_t tu_align32(uint32_t value)
Definition: tusb_common.h:171
static TU_ATTR_ALWAYS_INLINE uint16_t tu_min16(uint16_t x, uint16_t y)
Definition: tusb_common.h:155
static TU_ATTR_ALWAYS_INLINE uint32_t tu_align4k(uint32_t value)
Definition: tusb_common.h:172
static TU_ATTR_ALWAYS_INLINE uint32_t tu_min32(uint32_t x, uint32_t y)
Definition: tusb_common.h:156
static uint8_t tu_log2(uint32_t value)
Definition: tusb_common.h:183
tusb_speed_t
defined base on EHCI specs value for Endpoint Speed
Definition: tusb_types.h:49
@ TUSB_SPEED_HIGH
Definition: tusb_types.h:52
static TU_ATTR_ALWAYS_INLINE uint8_t tu_edpt_number(uint8_t addr)
Definition: tusb_types.h:507
xfer_result_t
Definition: tusb_types.h:236
@ XFER_RESULT_FAILED
Definition: tusb_types.h:238
@ XFER_RESULT_SUCCESS
Definition: tusb_types.h:237
@ XFER_RESULT_STALLED
Definition: tusb_types.h:239
static TU_ATTR_ALWAYS_INLINE uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const *desc_ep)
Definition: tusb_types.h:515
@ TUSB_XFER_CONTROL
Definition: tusb_types.h:59
@ TUSB_XFER_ISOCHRONOUS
Definition: tusb_types.h:60
@ TUSB_XFER_INTERRUPT
Definition: tusb_types.h:62
@ TUSB_XFER_BULK
Definition: tusb_types.h:61
TU_ATTR_PACKED_END TU_ATTR_BIT_FIELD_ORDER_END static TU_ATTR_ALWAYS_INLINE tusb_dir_t tu_edpt_dir(uint8_t addr)
Definition: tusb_types.h:502
static TU_ATTR_ALWAYS_INLINE uint8_t tu_edpt_addr(uint8_t num, uint8_t dir)
Definition: tusb_types.h:511
uint8_t daddr
Definition: usbh.c:264