Skip to content

Commit 330d9d7

Browse files
committed
ohci: Re-implement TD allocation, matching the specification
The initial motivation for doing this is to remove the `used` flag in the TD. If we use this flag, we end up being required to read from TDs that the OHCI controller might be modifying (i.e. the OHCI controller logically owns the TD). This happens when we try to allocate a new, empty TD while the OHCI host controller is working on a transfer. Move the `used` flag to `gtd_extra_data_t`. This data is only used by the CPU, and the OHCI controller never accesses it. The existing allocation method for TDs does *not* put an empty TD onto each ED (i.e it does *not* do what is shown in Figure 5-6 of the OHCI specification). Instead, the NextTD field of the last TD is set to 0. The TailP field of the ED is also set to 0. This works in many cases. However, this implementation means that the CPU may end up trying to write to the NextTD field of an in-progress transfer while the OHCI host controller logically owns it. Change the implementation to use an empty TD, as suggested by the specification, for endpoints other than EP0. This avoids the above issue. It is not necessary to make the change for EP0 because only at most one TD can ever be pending at a time. The above change should also remove the need for the stall workaround. In the future, we want to modify the code to access EDs through an uncached mapping. Because uncached mappings are slow, we want to access EDs as little as possible. Currently, when a TD completes, we access an ED in order to figure out the device address and endpoint number of the TD which was completed. Because moving `used` to `gtd_extra_data_t` necessitates expanding it, we have enough room to also store the device address and endpoint number of the TD. This patch does so. With the above two changes, we no longer need to access an ED when a TD completes. Also remove the `index` field from TDs as it is no longer necessary.
1 parent 839149c commit 330d9d7

File tree

2 files changed

+35
-55
lines changed

2 files changed

+35
-55
lines changed

src/portable/ohci/ohci.c

Lines changed: 30 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,6 @@ static void ed_init(ohci_ed_t *p_ed, uint8_t dev_addr, uint16_t ep_size, uint8_t
355355
static void gtd_init(ohci_gtd_t *p_td, uint8_t *data_ptr, uint16_t total_bytes) {
356356
tu_memclr(p_td, sizeof(ohci_gtd_t));
357357

358-
p_td->used = 1;
359358
gtd_get_extra_data(p_td)->expected_bytes = total_bytes;
360359

361360
p_td->buffer_rounding = 1; // less than queued length is not a error
@@ -439,25 +438,15 @@ static ohci_gtd_t * gtd_find_free(void)
439438
{
440439
for(uint8_t i=0; i < GTD_MAX; i++)
441440
{
442-
if ( !ohci_data.gtd_pool[i].used ) return &ohci_data.gtd_pool[i];
441+
if ( !ohci_data.gtd_extra[i].used ) {
442+
ohci_data.gtd_extra[i].used = 1;
443+
return &ohci_data.gtd_pool[i];
444+
}
443445
}
444446

445447
return NULL;
446448
}
447449

448-
static void td_insert_to_ed(ohci_ed_t* p_ed, ohci_gtd_t * p_gtd)
449-
{
450-
// tail is always NULL
451-
if ( tu_align16(p_ed->td_head.address) == 0 )
452-
{ // TD queue is empty --> head = TD
453-
p_ed->td_head.address |= (uint32_t) _phys_addr(p_gtd);
454-
}
455-
else
456-
{ // TODO currently only support queue up to 2 TD each endpoint at a time
457-
((ohci_gtd_t*) tu_align16((uint32_t)_virt_addr((void *)p_ed->td_head.address)))->next = (uint32_t) _phys_addr(p_gtd);
458-
}
459-
}
460-
461450
//--------------------------------------------------------------------+
462451
// Endpoint API
463452
//--------------------------------------------------------------------+
@@ -490,6 +479,16 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
490479
return true;
491480
}
492481

482+
if ( tu_edpt_number(ep_desc->bEndpointAddress) != 0 ) {
483+
// Get an empty TD and use it as the end-of-list marker.
484+
// This marker TD will be used when a transfer is made on this EP
485+
// (and a new, empty TD will be allocated for the next-next transfer).
486+
ohci_gtd_t* gtd = gtd_find_free();
487+
TU_ASSERT(gtd);
488+
hcd_dcache_uncached(p_ed->td_head).address = (uint32_t)_phys_addr(gtd);
489+
hcd_dcache_uncached(p_ed->td_tail) = (uint32_t)_phys_addr(gtd);
490+
}
491+
493492
ed_list_insert( p_ed_head[ep_desc->bmAttributes.xfer], p_ed );
494493

495494
return true;
@@ -508,7 +507,8 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet
508507
ohci_gtd_t *qtd = &ohci_data.control[dev_addr].gtd;
509508

510509
gtd_init(qtd, (uint8_t*)(uintptr_t) setup_packet, 8);
511-
qtd->index = dev_addr;
510+
gtd_get_extra_data(qtd)->dev_addr = dev_addr;
511+
gtd_get_extra_data(qtd)->ep_addr = tu_edpt_addr(0, TUSB_DIR_OUT);
512512
qtd->pid = PID_SETUP;
513513
qtd->data_toggle = GTD_DT_DATA0;
514514
qtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
@@ -534,8 +534,9 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
534534
ohci_gtd_t* gtd = &ohci_data.control[dev_addr].gtd;
535535

536536
gtd_init(gtd, buffer, buflen);
537+
gtd_get_extra_data(gtd)->dev_addr = dev_addr;
538+
gtd_get_extra_data(gtd)->ep_addr = ep_addr;
537539

538-
gtd->index = dev_addr;
539540
gtd->pid = dir ? PID_IN : PID_OUT;
540541
gtd->data_toggle = GTD_DT_DATA1; // Both Data and Ack stage start with DATA1
541542
gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
@@ -546,15 +547,20 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
546547
}else
547548
{
548549
ohci_ed_t * ed = ed_from_addr(dev_addr, ep_addr);
549-
ohci_gtd_t* gtd = gtd_find_free();
550-
551-
TU_ASSERT(gtd);
550+
ohci_gtd_t *gtd = (ohci_gtd_t *)_virt_addr((void *)hcd_dcache_uncached(ed->td_tail));
552551

553552
gtd_init(gtd, buffer, buflen);
554-
gtd->index = ed-ohci_data.ed_pool;
553+
gtd_get_extra_data(gtd)->dev_addr = dev_addr;
554+
gtd_get_extra_data(gtd)->ep_addr = ep_addr;
555555
gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
556556

557-
td_insert_to_ed(ed, gtd);
557+
// Insert a new, empty TD at the tail, to be used by the next transfer
558+
ohci_gtd_t* new_gtd = gtd_find_free();
559+
TU_ASSERT(new_gtd);
560+
561+
gtd->next = (uint32_t)_phys_addr(new_gtd);
562+
563+
hcd_dcache_uncached(ed->td_tail) = (uint32_t)_phys_addr(new_gtd);
558564

559565
tusb_xfer_type_t xfer_type = ed_get_xfer_type( ed_from_addr(dev_addr, ep_addr) );
560566
if (TUSB_XFER_BULK == xfer_type) OHCI_REG->command_status_bit.bulk_list_filled = 1;
@@ -614,17 +620,6 @@ static inline bool gtd_is_control(ohci_gtd_t const * const p_qtd)
614620
return ((uint32_t) p_qtd) < ((uint32_t) ohci_data.gtd_pool); // check ohci_data_t for memory layout
615621
}
616622

617-
static inline ohci_ed_t* gtd_get_ed(ohci_gtd_t const * const p_qtd)
618-
{
619-
if ( gtd_is_control(p_qtd) )
620-
{
621-
return &ohci_data.control[p_qtd->index].ed;
622-
}else
623-
{
624-
return &ohci_data.ed_pool[p_qtd->index];
625-
}
626-
}
627-
628623
static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd) {
629624
if ( gtd_is_control(gtd) ) {
630625
uint8_t idx = ((uintptr_t)gtd - (uintptr_t)&ohci_data.control->gtd) / sizeof(ohci_data.control[0]);
@@ -661,29 +656,12 @@ static void done_queue_isr(uint8_t hostid)
661656
xfer_result_t const event = (qtd->condition_code == OHCI_CCODE_NO_ERROR) ? XFER_RESULT_SUCCESS :
662657
(qtd->condition_code == OHCI_CCODE_STALL) ? XFER_RESULT_STALLED : XFER_RESULT_FAILED;
663658

664-
qtd->used = 0; // free TD
659+
gtd_get_extra_data(qtd)->used = 0; // free TD
665660
if ( (qtd->delay_interrupt == OHCI_INT_ON_COMPLETE_YES) || (event != XFER_RESULT_SUCCESS) )
666661
{
667-
ohci_ed_t * const ed = gtd_get_ed(qtd);
668662
uint32_t const xferred_bytes = gtd_get_extra_data(qtd)->expected_bytes - gtd_xfer_byte_left((uint32_t) qtd->buffer_end, (uint32_t) qtd->current_buffer_pointer);
669663

670-
// NOTE Assuming the current list is BULK and there is no other EDs in the list has queued TDs.
671-
// When there is a error resulting this ED is halted, and this EP still has other queued TD
672-
// --> the Bulk list only has this halted EP queueing TDs (remaining)
673-
// --> Bulk list will be considered as not empty by HC !!! while there is no attempt transaction on this list
674-
// --> HC will not process Control list (due to service ratio when Bulk list not empty)
675-
// To walk-around this, the halted ED will have TailP = HeadP (empty list condition), when clearing halt
676-
// the TailP must be set back to NULL for processing remaining TDs
677-
if (event != XFER_RESULT_SUCCESS)
678-
{
679-
ed->td_tail &= 0x0Ful;
680-
ed->td_tail |= tu_align16(ed->td_head.address); // mark halted EP as empty queue
681-
if ( event == XFER_RESULT_STALLED ) ed->is_stalled = 1;
682-
}
683-
684-
uint8_t dir = (ed->ep_number == 0) ? (qtd->pid == PID_IN) : (ed->pid == PID_IN);
685-
686-
hcd_event_xfer_complete(ed->dev_addr, tu_edpt_addr(ed->ep_number, dir), xferred_bytes, event, true);
664+
hcd_event_xfer_complete(gtd_get_extra_data(qtd)->dev_addr, gtd_get_extra_data(qtd)->ep_addr, xferred_bytes, event, true);
687665
}
688666

689667
td_head = (ohci_td_item_t*) _virt_addr((void *)td_head->next);

src/portable/ohci/ohci.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,7 @@ typedef struct TU_ATTR_ALIGNED(16) {
9797
typedef struct TU_ATTR_ALIGNED(CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 16)
9898
{
9999
// Word 0
100-
uint32_t used : 1;
101-
uint32_t index : 8; // endpoint index the gtd belongs to, or device address in case of control xfer
102-
uint32_t : 9; // can be used
100+
uint32_t : 18; // can be used
103101
uint32_t buffer_rounding : 1;
104102
uint32_t pid : 2;
105103
uint32_t delay_interrupt : 3;
@@ -181,7 +179,11 @@ TU_VERIFY_STATIC( sizeof(ochi_itd_t) == CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_
181179

182180
typedef struct {
183181
uint16_t expected_bytes; // up to 8192 bytes so max is 13 bits
182+
uint8_t dev_addr : 7;
183+
uint8_t used : 1;
184+
uint8_t ep_addr;
184185
} gtd_extra_data_t;
186+
TU_VERIFY_STATIC( sizeof(gtd_extra_data_t) == 4, "size is not correct" );
185187

186188
// structure with member alignment required from large to small
187189
typedef struct TU_ATTR_ALIGNED(256) {

0 commit comments

Comments
 (0)