28
28
29
29
#if CFG_TUH_ENABLED && defined(TUP_USBIP_OHCI )
30
30
31
+ #ifndef TUP_OHCI_RHPORTS
32
+ #error OHCI is enabled, but TUP_OHCI_RHPORTS is not defined.
33
+ #endif
34
+
31
35
//--------------------------------------------------------------------+
32
36
// INCLUDE
33
37
//--------------------------------------------------------------------+
@@ -157,6 +161,21 @@ static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr);
157
161
//--------------------------------------------------------------------+
158
162
// USBH-HCD API
159
163
//--------------------------------------------------------------------+
164
+
165
+ // If your system requires separation of virtual and physical memory, implement
166
+ // tusb_app_virt_to_phys and tusb_app_virt_to_phys in your application.
167
+ TU_ATTR_ALWAYS_INLINE static inline void * _phys_addr (void * virtual_address )
168
+ {
169
+ if (tusb_app_virt_to_phys ) return tusb_app_virt_to_phys (virtual_address );
170
+ return virtual_address ;
171
+ }
172
+
173
+ TU_ATTR_ALWAYS_INLINE static inline void * _virt_addr (void * physical_address )
174
+ {
175
+ if (tusb_app_phys_to_virt ) return tusb_app_phys_to_virt (physical_address );
176
+ return physical_address ;
177
+ }
178
+
160
179
// Initialization according to 5.1.1.4
161
180
bool hcd_init (uint8_t rhport )
162
181
{
@@ -166,37 +185,65 @@ bool hcd_init(uint8_t rhport)
166
185
tu_memclr (& ohci_data , sizeof (ohci_data_t ));
167
186
for (uint8_t i = 0 ; i < 32 ; i ++ )
168
187
{ // assign all interrupt pointers to period head ed
169
- ohci_data .hcca .interrupt_table [i ] = (uint32_t ) & ohci_data .period_head_ed ;
188
+ ohci_data .hcca .interrupt_table [i ] = (uint32_t ) _phys_addr ( & ohci_data .period_head_ed ) ;
170
189
}
171
190
172
191
ohci_data .control [0 ].ed .skip = 1 ;
173
192
ohci_data .bulk_head_ed .skip = 1 ;
174
193
ohci_data .period_head_ed .skip = 1 ;
175
194
195
+ //If OHCI hardware is in SMM mode, gain ownership (Ref OHCI spec 5.1.1.3.3)
196
+ if (OHCI_REG -> control_bit .interrupt_routing == 1 )
197
+ {
198
+ OHCI_REG -> command_status_bit .ownership_change_request = 1 ;
199
+ while (OHCI_REG -> control_bit .interrupt_routing == 1 ) {}
200
+ }
201
+
202
+ //If OHCI hardware has come from warm-boot, signal resume (Ref OHCI spec 5.1.1.3.4)
203
+ else if (OHCI_REG -> control_bit .hc_functional_state != OHCI_CONTROL_FUNCSTATE_RESET &&
204
+ OHCI_REG -> control_bit .hc_functional_state != OHCI_CONTROL_FUNCSTATE_OPERATIONAL )
205
+ {
206
+ //Wait 20 ms. (Ref Usb spec 7.1.7.7)
207
+ OHCI_REG -> control_bit .hc_functional_state = OHCI_CONTROL_FUNCSTATE_RESUME ;
208
+
209
+ #if CFG_TUSB_OS != OPT_OS_NONE
210
+ // os_none implement task delay using usb frame counter which is not started yet
211
+ // therefore cause infinite delay.
212
+ // TODO find a way to delay in case of os none e.g __nop
213
+ osal_task_delay (20 );
214
+ #endif
215
+ }
216
+
176
217
// reset controller
177
218
OHCI_REG -> command_status_bit .controller_reset = 1 ;
178
219
while ( OHCI_REG -> command_status_bit .controller_reset ) {} // should not take longer than 10 us
179
220
180
221
//------------- init ohci registers -------------//
181
- OHCI_REG -> control_head_ed = (uint32_t ) & ohci_data .control [0 ].ed ;
182
- OHCI_REG -> bulk_head_ed = (uint32_t ) & ohci_data .bulk_head_ed ;
183
- OHCI_REG -> hcca = (uint32_t ) & ohci_data .hcca ;
222
+ OHCI_REG -> control_head_ed = (uint32_t ) _phys_addr ( & ohci_data .control [0 ].ed ) ;
223
+ OHCI_REG -> bulk_head_ed = (uint32_t ) _phys_addr ( & ohci_data .bulk_head_ed ) ;
224
+ OHCI_REG -> hcca = (uint32_t ) _phys_addr ( & ohci_data .hcca ) ;
184
225
185
226
OHCI_REG -> interrupt_disable = OHCI_REG -> interrupt_enable ; // disable all interrupts
186
227
OHCI_REG -> interrupt_status = OHCI_REG -> interrupt_status ; // clear current set bits
187
228
OHCI_REG -> interrupt_enable = OHCI_INT_WRITEBACK_DONEHEAD_MASK | OHCI_INT_RESUME_DETECTED_MASK |
188
229
OHCI_INT_UNRECOVERABLE_ERROR_MASK | OHCI_INT_FRAME_OVERFLOW_MASK | OHCI_INT_RHPORT_STATUS_CHANGE_MASK |
189
230
OHCI_INT_MASTER_ENABLE_MASK ;
190
231
191
- OHCI_REG -> control | = OHCI_CONTROL_CONTROL_BULK_RATIO | OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK |
232
+ OHCI_REG -> control = OHCI_CONTROL_CONTROL_BULK_RATIO | OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK |
192
233
OHCI_CONTROL_LIST_BULK_ENABLE_MASK | OHCI_CONTROL_LIST_PERIODIC_ENABLE_MASK ; // TODO Isochronous
193
234
194
235
OHCI_REG -> frame_interval = (OHCI_FMINTERVAL_FSMPS << 16 ) | OHCI_FMINTERVAL_FI ;
236
+ OHCI_REG -> frame_interval ^= (1 << 31 ); //Must toggle when frame_interval is updated.
195
237
OHCI_REG -> periodic_start = (OHCI_FMINTERVAL_FI * 9 ) / 10 ; // Periodic start is 90% of frame interval
196
238
197
239
OHCI_REG -> control_bit .hc_functional_state = OHCI_CONTROL_FUNCSTATE_OPERATIONAL ; // make HC's state to operational state TODO use this to suspend (save power)
198
240
OHCI_REG -> rh_status_bit .local_power_status_change = 1 ; // set global power for ports
199
241
242
+ #if CFG_TUSB_OS != OPT_OS_NONE
243
+ // TODO as above delay
244
+ osal_task_delay (OHCI_REG -> rh_descriptorA_bit .power_on_to_good_time * 2 ); // Wait POTG after power up
245
+ #endif
246
+
200
247
return true;
201
248
}
202
249
@@ -212,8 +259,7 @@ uint32_t hcd_frame_number(uint8_t rhport)
212
259
//--------------------------------------------------------------------+
213
260
void hcd_port_reset (uint8_t hostid )
214
261
{
215
- (void ) hostid ;
216
- OHCI_REG -> rhport_status [0 ] = RHPORT_PORT_RESET_STATUS_MASK ;
262
+ OHCI_REG -> rhport_status [hostid ] = RHPORT_PORT_RESET_STATUS_MASK ;
217
263
}
218
264
219
265
void hcd_port_reset_end (uint8_t rhport )
@@ -223,14 +269,12 @@ void hcd_port_reset_end(uint8_t rhport)
223
269
224
270
bool hcd_port_connect_status (uint8_t hostid )
225
271
{
226
- (void ) hostid ;
227
- return OHCI_REG -> rhport_status_bit [0 ].current_connect_status ;
272
+ return OHCI_REG -> rhport_status_bit [hostid ].current_connect_status ;
228
273
}
229
274
230
275
tusb_speed_t hcd_port_speed_get (uint8_t hostid )
231
276
{
232
- (void ) hostid ;
233
- return OHCI_REG -> rhport_status_bit [0 ].low_speed_device_attached ? TUSB_SPEED_LOW : TUSB_SPEED_FULL ;
277
+ return OHCI_REG -> rhport_status_bit [hostid ].low_speed_device_attached ? TUSB_SPEED_LOW : TUSB_SPEED_FULL ;
234
278
}
235
279
236
280
// endpoints are tied to an address, which only reclaim after a long delay when enumerating
@@ -308,8 +352,8 @@ static void gtd_init(ohci_gtd_t* p_td, uint8_t* data_ptr, uint16_t total_bytes)
308
352
p_td -> delay_interrupt = OHCI_INT_ON_COMPLETE_NO ;
309
353
p_td -> condition_code = OHCI_CCODE_NOT_ACCESSED ;
310
354
311
- p_td -> current_buffer_pointer = data_ptr ;
312
- p_td -> buffer_end = total_bytes ? (data_ptr + total_bytes - 1 ) : data_ptr ;
355
+ p_td -> current_buffer_pointer = _phys_addr ( data_ptr ) ;
356
+ p_td -> buffer_end = total_bytes ? (_phys_addr ( data_ptr + total_bytes - 1 )) : ( uint8_t * ) p_td -> current_buffer_pointer ;
313
357
}
314
358
315
359
static ohci_ed_t * ed_from_addr (uint8_t dev_addr , uint8_t ep_addr )
@@ -345,7 +389,7 @@ static ohci_ed_t * ed_find_free(void)
345
389
static void ed_list_insert (ohci_ed_t * p_pre , ohci_ed_t * p_ed )
346
390
{
347
391
p_ed -> next = p_pre -> next ;
348
- p_pre -> next = (uint32_t ) p_ed ;
392
+ p_pre -> next = (uint32_t ) _phys_addr ( p_ed ) ;
349
393
}
350
394
351
395
static void ed_list_remove_by_addr (ohci_ed_t * p_head , uint8_t dev_addr )
@@ -354,20 +398,24 @@ static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr)
354
398
355
399
while ( p_prev -> next )
356
400
{
357
- ohci_ed_t * ed = (ohci_ed_t * ) p_prev -> next ;
401
+ ohci_ed_t * ed = (ohci_ed_t * ) _virt_addr (( void * ) p_prev -> next ) ;
358
402
359
403
if (ed -> dev_addr == dev_addr )
360
404
{
361
- // unlink ed
405
+ // Prevent Host Controller from processing this ED while we remove it
406
+ ed -> skip = 1 ;
407
+
408
+ // unlink ed, will also move up p_prev
362
409
p_prev -> next = ed -> next ;
363
410
364
411
// point the removed ED's next pointer to list head to make sure HC can always safely move away from this ED
365
- ed -> next = (uint32_t ) p_head ;
412
+ ed -> next = (uint32_t ) _phys_addr ( p_head ) ;
366
413
ed -> used = 0 ;
414
+ ed -> skip = 0 ;
415
+ }else
416
+ {
417
+ p_prev = (ohci_ed_t * ) _virt_addr ((void * )p_prev -> next );
367
418
}
368
-
369
- // check next valid since we could remove it
370
- if (p_prev -> next ) p_prev = (ohci_ed_t * ) p_prev -> next ;
371
419
}
372
420
}
373
421
@@ -386,11 +434,11 @@ static void td_insert_to_ed(ohci_ed_t* p_ed, ohci_gtd_t * p_gtd)
386
434
// tail is always NULL
387
435
if ( tu_align16 (p_ed -> td_head .address ) == 0 )
388
436
{ // TD queue is empty --> head = TD
389
- p_ed -> td_head .address |= (uint32_t ) p_gtd ;
437
+ p_ed -> td_head .address |= (uint32_t ) _phys_addr ( p_gtd ) ;
390
438
}
391
439
else
392
440
{ // TODO currently only support queue up to 2 TD each endpoint at a time
393
- ((ohci_gtd_t * ) tu_align16 (p_ed -> td_head .address ))-> next = (uint32_t ) p_gtd ;
441
+ ((ohci_gtd_t * ) tu_align16 (( uint32_t ) _virt_addr (( void * ) p_ed -> td_head .address ))) -> next = (uint32_t ) _phys_addr ( p_gtd ) ;
394
442
}
395
443
}
396
444
@@ -443,10 +491,10 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet
443
491
qtd -> index = dev_addr ;
444
492
qtd -> pid = PID_SETUP ;
445
493
qtd -> data_toggle = GTD_DT_DATA0 ;
446
- qtd -> delay_interrupt = 0 ;
494
+ qtd -> delay_interrupt = OHCI_INT_ON_COMPLETE_YES ;
447
495
448
496
//------------- Attach TDs list to Control Endpoint -------------//
449
- ed -> td_head .address = (uint32_t ) qtd ;
497
+ ed -> td_head .address = (uint32_t ) _phys_addr ( qtd ) ;
450
498
451
499
OHCI_REG -> command_status_bit .control_list_filled = 1 ;
452
500
@@ -470,9 +518,9 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
470
518
gtd -> index = dev_addr ;
471
519
gtd -> pid = dir ? PID_IN : PID_OUT ;
472
520
gtd -> data_toggle = GTD_DT_DATA1 ; // Both Data and Ack stage start with DATA1
473
- gtd -> delay_interrupt = 0 ;
521
+ gtd -> delay_interrupt = OHCI_INT_ON_COMPLETE_YES ;
474
522
475
- ed -> td_head .address = (uint32_t ) gtd ;
523
+ ed -> td_head .address = (uint32_t ) _phys_addr ( gtd ) ;
476
524
477
525
OHCI_REG -> command_status_bit .control_list_filled = 1 ;
478
526
}else
@@ -484,7 +532,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
484
532
485
533
gtd_init (gtd , buffer , buflen );
486
534
gtd -> index = ed - ohci_data .ed_pool ;
487
- gtd -> delay_interrupt = 0 ;
535
+ gtd -> delay_interrupt = OHCI_INT_ON_COMPLETE_YES ;
488
536
489
537
td_insert_to_ed (ed , gtd );
490
538
@@ -520,16 +568,17 @@ static ohci_td_item_t* list_reverse(ohci_td_item_t* td_head)
520
568
521
569
while (td_head != NULL )
522
570
{
571
+ td_head = _virt_addr (td_head );
523
572
uint32_t next = td_head -> next ;
524
573
525
574
// make current's item become reverse's first item
526
575
td_head -> next = (uint32_t ) td_reverse_head ;
527
- td_reverse_head = td_head ;
576
+ td_reverse_head = _phys_addr ( td_head ) ;
528
577
529
578
td_head = (ohci_td_item_t * ) next ; // advance to next item
530
579
}
531
580
532
- return td_reverse_head ;
581
+ return _virt_addr ( td_reverse_head ) ;
533
582
}
534
583
535
584
static inline bool gtd_is_control (ohci_gtd_t const * const p_qtd )
@@ -565,6 +614,7 @@ static void done_queue_isr(uint8_t hostid)
565
614
566
615
// done head is written in reversed order of completion --> need to reverse the done queue first
567
616
ohci_td_item_t * td_head = list_reverse ( (ohci_td_item_t * ) tu_align16 (ohci_data .hcca .done_head ) );
617
+ ohci_data .hcca .done_head = 0 ;
568
618
569
619
while ( td_head != NULL )
570
620
{
@@ -600,7 +650,7 @@ static void done_queue_isr(uint8_t hostid)
600
650
hcd_event_xfer_complete (ed -> dev_addr , tu_edpt_addr (ed -> ep_number , dir ), xferred_bytes , event , true);
601
651
}
602
652
603
- td_head = (ohci_td_item_t * ) td_head -> next ;
653
+ td_head = (ohci_td_item_t * ) _virt_addr (( void * ) td_head -> next ) ;
604
654
}
605
655
}
606
656
@@ -611,6 +661,9 @@ void hcd_int_handler(uint8_t hostid)
611
661
612
662
if (int_status == 0 ) return ;
613
663
664
+ // Disable MIE as per OHCI spec 5.3
665
+ OHCI_REG -> interrupt_disable = OHCI_INT_MASTER_ENABLE_MASK ;
666
+
614
667
// Frame number overflow
615
668
if ( int_status & OHCI_INT_FRAME_OVERFLOW_MASK )
616
669
{
@@ -620,29 +673,30 @@ void hcd_int_handler(uint8_t hostid)
620
673
//------------- RootHub status -------------//
621
674
if ( int_status & OHCI_INT_RHPORT_STATUS_CHANGE_MASK )
622
675
{
623
- uint32_t const rhport_status = OHCI_REG -> rhport_status [0 ] & RHPORT_ALL_CHANGE_MASK ;
624
-
625
- // TODO dual port is not yet supported
626
- if ( rhport_status & RHPORT_CONNECT_STATUS_CHANGE_MASK )
676
+ for (int i = 0 ; i < TUP_OHCI_RHPORTS ; i ++ )
627
677
{
628
- // TODO check if remote wake-up
629
- if ( OHCI_REG -> rhport_status_bit [ 0 ]. current_connect_status )
678
+ uint32_t const rhport_status = OHCI_REG -> rhport_status [ i ] & RHPORT_ALL_CHANGE_MASK ;
679
+ if ( rhport_status & RHPORT_CONNECT_STATUS_CHANGE_MASK )
630
680
{
631
- // TODO reset port immediately, without this controller will got 2-3 (debouncing connection status change)
632
- OHCI_REG -> rhport_status [0 ] = RHPORT_PORT_RESET_STATUS_MASK ;
633
- hcd_event_device_attach (hostid , true);
634
- }else
635
- {
636
- hcd_event_device_remove (hostid , true);
681
+ // TODO check if remote wake-up
682
+ if ( OHCI_REG -> rhport_status_bit [i ].current_connect_status )
683
+ {
684
+ // TODO reset port immediately, without this controller will got 2-3 (debouncing connection status change)
685
+ OHCI_REG -> rhport_status [i ] = RHPORT_PORT_RESET_STATUS_MASK ;
686
+ hcd_event_device_attach (i , true);
687
+ }else
688
+ {
689
+ hcd_event_device_remove (i , true);
690
+ }
637
691
}
638
- }
639
692
640
- if ( rhport_status & RHPORT_PORT_SUSPEND_CHANGE_MASK )
641
- {
693
+ if ( rhport_status & RHPORT_PORT_SUSPEND_CHANGE_MASK )
694
+ {
642
695
643
- }
696
+ }
644
697
645
- OHCI_REG -> rhport_status [0 ] = rhport_status ; // acknowledge all interrupt
698
+ OHCI_REG -> rhport_status [i ] = rhport_status ; // acknowledge all interrupt
699
+ }
646
700
}
647
701
648
702
//------------- Transfer Complete -------------//
@@ -652,6 +706,8 @@ void hcd_int_handler(uint8_t hostid)
652
706
}
653
707
654
708
OHCI_REG -> interrupt_status = int_status ; // Acknowledge handled interrupt
709
+
710
+ OHCI_REG -> interrupt_enable = OHCI_INT_MASTER_ENABLE_MASK ; // Enable MIE
655
711
}
656
712
//--------------------------------------------------------------------+
657
713
// HELPER
0 commit comments