48
48
#ifdef TUP_USBIP_CHIPIDEA_HS
49
49
// NXP Transdimension: 8 elements
50
50
#define FRAMELIST_SIZE_BIT_VALUE 7u
51
- #define FRAMELIST_SIZE_USBCMD_VALUE (((FRAMELIST_SIZE_BIT_VALUE & 3) << EHCI_USBCMD_POS_FRAMELIST_SIZE ) | \
52
- ((FRAMELIST_SIZE_BIT_VALUE >> 2) << EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB ))
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
53
#else
54
54
// STD EHCI: 256 elements
55
55
#define FRAMELIST_SIZE_BIT_VALUE 2u
@@ -132,24 +132,46 @@ TU_ATTR_WEAK void hcd_dcache_clean(void const* addr, uint32_t data_size) { (void
132
132
TU_ATTR_WEAK void hcd_dcache_invalidate (void const * addr , uint32_t data_size ) { (void ) addr ; (void ) data_size ; }
133
133
TU_ATTR_WEAK void hcd_dcache_clean_invalidate (void const * addr , uint32_t data_size ) { (void ) addr ; (void ) data_size ; }
134
134
135
- TU_ATTR_ALWAYS_INLINE static inline ehci_link_t * list_get_period_head (uint8_t rhport , uint32_t interval_ms );
136
- TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t * list_get_async_head (uint8_t rhport );
137
-
138
135
TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t * qhd_control (uint8_t dev_addr );
139
136
TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t * qhd_next (ehci_qhd_t const * p_qhd );
140
137
TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t * qhd_find_free (void );
141
138
static ehci_qhd_t * qhd_get_from_addr (uint8_t dev_addr , uint8_t ep_addr );
142
139
static void qhd_init (ehci_qhd_t * p_qhd , uint8_t dev_addr , tusb_desc_endpoint_t const * ep_desc );
143
140
static void qhd_attach_qtd (ehci_qhd_t * qhd , ehci_qtd_t * qtd );
141
+ static void qhd_remove_qtd (ehci_qhd_t * qhd );
144
142
145
143
TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t * qtd_control (uint8_t dev_addr );
146
144
TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t * qtd_find_free (void );
147
145
static void qtd_init (ehci_qtd_t * qtd , void const * buffer , uint16_t total_bytes );
148
146
149
- static inline void list_insert (ehci_link_t * current , ehci_link_t * new , uint8_t new_type );
150
- static inline ehci_link_t * list_next (ehci_link_t const * p_link );
147
+ TU_ATTR_ALWAYS_INLINE static inline ehci_link_t * list_get_period_head (uint8_t rhport , uint32_t interval_ms );
148
+ TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t * list_get_async_head (uint8_t rhport );
149
+ TU_ATTR_ALWAYS_INLINE static inline void list_insert (ehci_link_t * current , ehci_link_t * new , uint8_t new_type );
150
+ TU_ATTR_ALWAYS_INLINE static inline ehci_link_t * list_next (ehci_link_t const * p_link );
151
151
static void list_remove_qhd_by_daddr (ehci_link_t * list_head , uint8_t dev_addr );
152
152
153
+ static 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
+
164
+ static 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
+
153
175
//--------------------------------------------------------------------+
154
176
// HCD API
155
177
//--------------------------------------------------------------------+
@@ -204,43 +226,6 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport)
204
226
return (tusb_speed_t ) ehci_data .regs -> portsc_bm .nxp_port_speed ; // NXP specific port speed
205
227
}
206
228
207
- static void list_remove_qhd_by_daddr (ehci_link_t * list_head , uint8_t dev_addr ) {
208
- ehci_link_t * prev = list_head ;
209
-
210
- while (prev && !prev -> terminate ) {
211
- ehci_qhd_t * qhd = (ehci_qhd_t * ) (uintptr_t ) list_next (prev );
212
-
213
- // done if loop back to head
214
- if ( (uintptr_t ) qhd == (uintptr_t ) list_head ) {
215
- break ;
216
- }
217
-
218
- if ( qhd -> dev_addr == dev_addr ) {
219
- // TODO deactivate all TD, wait for QHD to inactive before removal
220
- prev -> address = qhd -> next .address ;
221
-
222
- // EHCI 4.8.2 link the removed qhd's next to async head (which always reachable by Host Controller)
223
- qhd -> next .address = ((uint32_t ) list_head ) | (EHCI_QTYPE_QHD << 1 );
224
-
225
- if ( qhd -> int_smask )
226
- {
227
- // period list queue element is guarantee to be free in the next frame (1 ms)
228
- qhd -> used = 0 ;
229
- }else
230
- {
231
- // async list use async advance handshake
232
- // mark as removing, will completely re-usable when async advance isr occurs
233
- qhd -> removing = 1 ;
234
- }
235
-
236
- hcd_dcache_clean (qhd , sizeof (ehci_qhd_t ));
237
- hcd_dcache_clean (prev , sizeof (ehci_qhd_t ));
238
- }else {
239
- prev = list_next (prev );
240
- }
241
- }
242
- }
243
-
244
229
// Close all opened endpoint belong to this device
245
230
void hcd_device_close (uint8_t rhport , uint8_t daddr )
246
231
{
@@ -347,8 +332,7 @@ bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
347
332
regs -> nxp_tt_control = 0 ;
348
333
349
334
//------------- USB CMD Register -------------//
350
- regs -> command |= TU_BIT (EHCI_USBCMD_POS_RUN_STOP ) | TU_BIT (EHCI_USBCMD_POS_ASYNC_ENABLE ) |
351
- TU_BIT (EHCI_USBCMD_POS_PERIOD_ENABLE ) | // TODO enable period list only there is int/iso endpoint
335
+ regs -> command |= EHCI_USBCMD_RUN_STOP | EHCI_USBCMD_PERIOD_SCHEDULE_ENABLE | EHCI_USBCMD_ASYNC_SCHEDULE_ENABLE |
352
336
FRAMELIST_SIZE_USBCMD_VALUE ;
353
337
354
338
//------------- ConfigFlag Register (skip) -------------//
@@ -358,7 +342,7 @@ bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
358
342
if (ehci_data .cap_regs -> hcsparams_bm .port_power_control ) {
359
343
// mask out all change bits since they are Write 1 to clear
360
344
uint32_t portsc = (regs -> portsc & ~EHCI_PORTSC_MASK_W1C );
361
- portsc |= ECHI_PORTSC_MASK_PORT_POWER ;
345
+ portsc |= EHCI_PORTSC_MASK_PORT_POWER ;
362
346
363
347
regs -> portsc = portsc ;
364
348
}
@@ -455,20 +439,17 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
455
439
uint8_t const epnum = tu_edpt_number (ep_addr );
456
440
uint8_t const dir = tu_edpt_dir (ep_addr );
457
441
458
- ehci_qhd_t * qhd ;
442
+ ehci_qhd_t * qhd = qhd_get_from_addr ( dev_addr , ep_addr ) ;
459
443
ehci_qtd_t * qtd ;
460
444
461
445
if (epnum == 0 ) {
462
- qhd = qhd_control (dev_addr );
463
446
qtd = qtd_control (dev_addr );
464
-
465
447
qtd_init (qtd , buffer , buflen );
466
448
467
449
// first data toggle is always 1 (data & setup stage)
468
450
qtd -> data_toggle = 1 ;
469
451
qtd -> pid = dir ? EHCI_PID_IN : EHCI_PID_OUT ;
470
452
} else {
471
- qhd = qhd_get_from_addr (dev_addr , ep_addr );
472
453
qtd = qtd_find_free ();
473
454
TU_ASSERT (qtd );
474
455
@@ -491,20 +472,34 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
491
472
492
473
bool hcd_edpt_abort_xfer (uint8_t rhport , uint8_t dev_addr , uint8_t ep_addr ) {
493
474
(void ) rhport ;
494
- (void ) dev_addr ;
495
- (void ) ep_addr ;
496
475
497
- return false;
476
+ // TODO ISO not supported yet
477
+ ehci_qhd_t * qhd = qhd_get_from_addr (dev_addr , ep_addr );
478
+ ehci_qtd_t * volatile qtd = qhd -> attached_qtd ;
479
+ TU_VERIFY (qtd != NULL ); // no queued transfer
480
+
481
+ hcd_dcache_invalidate (qtd , sizeof (ehci_qtd_t ));
482
+ TU_VERIFY (qtd -> active ); // transfer is already complete
483
+
484
+ // HC is still processing, disable HC list schedule before making changes
485
+ bool const is_period = (qhd -> interval_ms > 0 );
486
+
487
+ ehci_disable_schedule (ehci_data .regs , is_period );
488
+
489
+ // check active bit again just in case HC has just processed the TD
490
+ bool const still_active = qtd -> active ;
491
+ if (still_active ) {
492
+ // remove TD from QH overlay
493
+ qhd -> qtd_overlay .next .terminate = 1 ;
494
+ hcd_dcache_clean (qhd , sizeof (ehci_qhd_t ));
495
+
496
+ // remove TD from QH software list
497
+ qhd_remove_qtd (qhd );
498
+ }
498
499
499
- // uint8_t const epnum = tu_edpt_number(ep_addr);
500
- // ehci_qhd_t* qhd;
501
- //
502
- // // TODO ISO not supported
503
- // if (epnum == 0) {
504
- // qhd = qhd_control(dev_addr);
505
- // }else {
506
- // qhd = qhd_get_from_addr(dev_addr, ep_addr);
507
- // }
500
+ ehci_enable_schedule (ehci_data .regs , is_period );
501
+
502
+ return still_active ; // true if removed an active transfer
508
503
}
509
504
510
505
bool hcd_edpt_clear_stall (uint8_t daddr , uint8_t ep_addr )
@@ -554,8 +549,12 @@ void port_connect_status_change_isr(uint8_t rhport)
554
549
TU_ATTR_ALWAYS_INLINE static inline
555
550
void qhd_xfer_complete_isr (ehci_qhd_t * qhd ) {
556
551
// examine TD attached to queue head
557
- ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile ) qhd -> attached_qtd ;
558
- if (qtd == NULL ) return ; // no TD attached
552
+ ehci_qtd_t * volatile qtd = qhd -> attached_qtd ;
553
+
554
+ if (qtd == NULL ) {
555
+ return ; // no TD attached
556
+ }
557
+
559
558
hcd_dcache_invalidate (qtd , sizeof (ehci_qtd_t ));
560
559
561
560
// TD is still active, no need to process
@@ -572,9 +571,7 @@ void qhd_xfer_complete_isr(ehci_qhd_t * qhd) {
572
571
}
573
572
574
573
// remove and free TD before invoking callback
575
- qhd -> attached_qtd = NULL ;
576
- qhd -> attached_buffer = 0 ;
577
- qtd -> used = 0 ; // free QTD
574
+ qhd_remove_qtd (qhd );
578
575
579
576
// notify usbh
580
577
uint8_t const ep_addr = tu_edpt_addr (qhd -> ep_number , dir );
@@ -677,9 +674,7 @@ void qhd_xfer_error_isr(ehci_qhd_t * qhd)
677
674
}
678
675
679
676
// remove and free TD before invoking callback
680
- qhd -> attached_qtd = NULL ;
681
- qhd -> attached_buffer = 0 ;
682
- qtd -> used = 0 ; // free QTD
677
+ qhd_remove_qtd (qhd );
683
678
684
679
if (0 == qhd -> ep_number ) {
685
680
// control cannot be halted
@@ -822,18 +817,55 @@ TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* list_get_async_head(uint8_t rhpo
822
817
return qhd_control (0 ); // control qhd of dev0 is used as async head
823
818
}
824
819
825
- // insert at head
826
- static inline void list_insert (ehci_link_t * current , ehci_link_t * new , uint8_t new_type )
820
+ TU_ATTR_ALWAYS_INLINE static inline ehci_link_t * list_next (ehci_link_t const * p_link ) {
821
+ return (ehci_link_t * ) tu_align32 (p_link -> address );
822
+ }
823
+
824
+ TU_ATTR_ALWAYS_INLINE static inline void list_insert (ehci_link_t * current , ehci_link_t * new , uint8_t new_type )
827
825
{
828
826
new -> address = current -> address ;
829
827
current -> address = ((uint32_t ) new ) | (new_type << 1 );
830
828
}
831
829
832
- static inline ehci_link_t * list_next (ehci_link_t const * p_link )
833
- {
834
- return (ehci_link_t * ) tu_align32 (p_link -> address );
830
+ // Remove all queue head belong to this device address
831
+ static void list_remove_qhd_by_daddr (ehci_link_t * list_head , uint8_t dev_addr ) {
832
+ ehci_link_t * prev = list_head ;
833
+
834
+ while (prev && !prev -> terminate ) {
835
+ ehci_qhd_t * qhd = (ehci_qhd_t * ) (uintptr_t ) list_next (prev );
836
+
837
+ // done if loop back to head
838
+ if ( (uintptr_t ) qhd == (uintptr_t ) list_head ) {
839
+ break ;
840
+ }
841
+
842
+ if ( qhd -> dev_addr == dev_addr ) {
843
+ // TODO deactivate all TD, wait for QHD to inactive before removal
844
+ prev -> address = qhd -> next .address ;
845
+
846
+ // EHCI 4.8.2 link the removed qhd's next to async head (which always reachable by Host Controller)
847
+ qhd -> next .address = ((uint32_t ) list_head ) | (EHCI_QTYPE_QHD << 1 );
848
+
849
+ if ( qhd -> int_smask )
850
+ {
851
+ // period list queue element is guarantee to be free in the next frame (1 ms)
852
+ qhd -> used = 0 ;
853
+ }else
854
+ {
855
+ // async list use async advance handshake
856
+ // mark as removing, will completely re-usable when async advance isr occurs
857
+ qhd -> removing = 1 ;
858
+ }
859
+
860
+ hcd_dcache_clean (qhd , sizeof (ehci_qhd_t ));
861
+ hcd_dcache_clean (prev , sizeof (ehci_qhd_t ));
862
+ }else {
863
+ prev = list_next (prev );
864
+ }
865
+ }
835
866
}
836
867
868
+
837
869
//--------------------------------------------------------------------+
838
870
// Queue Header helper
839
871
//--------------------------------------------------------------------+
@@ -856,8 +888,12 @@ TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t *qhd_next(ehci_qhd_t const *p_qhd
856
888
return (ehci_qhd_t * ) tu_align32 (p_qhd -> next .address );
857
889
}
858
890
859
- // Get queue head from address
891
+ // Get queue head from device + endpoint address
860
892
static ehci_qhd_t * qhd_get_from_addr (uint8_t dev_addr , uint8_t ep_addr ) {
893
+ if ( 0 == tu_edpt_number (ep_addr ) ) {
894
+ return qhd_control (dev_addr );
895
+ }
896
+
861
897
ehci_qhd_t * qhd_pool = ehci_data .qhd_pool ;
862
898
863
899
for ( uint32_t i = 0 ; i < QHD_MAX ; i ++ ) {
@@ -957,6 +993,16 @@ static void qhd_attach_qtd(ehci_qhd_t *qhd, ehci_qtd_t *qtd) {
957
993
hcd_dcache_clean_invalidate (qhd , sizeof (ehci_qhd_t ));
958
994
}
959
995
996
+ // Remove an attached TD from queue head
997
+ static void qhd_remove_qtd (ehci_qhd_t * qhd ) {
998
+ ehci_qtd_t * volatile qtd = qhd -> attached_qtd ;
999
+
1000
+ qhd -> attached_qtd = NULL ;
1001
+ qhd -> attached_buffer = 0 ;
1002
+
1003
+ qtd -> used = 0 ; // free QTD
1004
+ }
1005
+
960
1006
//--------------------------------------------------------------------+
961
1007
// Queue TD helper
962
1008
//--------------------------------------------------------------------+
0 commit comments