diff options
| author | hathach <[email protected]> | 2025-10-09 18:18:08 +0700 |
|---|---|---|
| committer | hathach <[email protected]> | 2025-10-09 18:18:08 +0700 |
| commit | d4374bb0dbb0e0eddd4459219f981fd8f62365a1 (patch) | |
| tree | d6e6fbf12dcc115d4700996f22ab232cb26ccd05 | |
| parent | 970384d6dbb012e5b27095b0f065fd73d00ebf4c (diff) | |
centralize hcd_dcache_uncached() for ED
| -rw-r--r-- | src/portable/ohci/ohci.c | 359 | ||||
| -rw-r--r-- | src/portable/ohci/ohci.h | 55 |
2 files changed, 181 insertions, 233 deletions
diff --git a/src/portable/ohci/ohci.c b/src/portable/ohci/ohci.c index 340d8c56a..ee85f1b6a 100644 --- a/src/portable/ohci/ohci.c +++ b/src/portable/ohci/ohci.c @@ -109,17 +109,17 @@ enum { enum { OHCI_CCODE_NO_ERROR = 0, OHCI_CCODE_CRC = 1, - OHCI_CCODE_BIT_STUFFING = 2, - OHCI_CCODE_DATA_TOGGLE_MISMATCH = 3, - OHCI_CCODE_STALL = 4, - OHCI_CCODE_DEVICE_NOT_RESPONDING = 5, - OHCI_CCODE_PID_CHECK_FAILURE = 6, - OHCI_CCODE_UNEXPECTED_PID = 7, - OHCI_CCODE_DATA_OVERRUN = 8, - OHCI_CCODE_DATA_UNDERRUN = 9, - OHCI_CCODE_BUFFER_OVERRUN = 12, - OHCI_CCODE_BUFFER_UNDERRUN = 13, - OHCI_CCODE_NOT_ACCESSED = 14, + OHCI_CCODE_BIT_STUFFING = 2, + OHCI_CCODE_DATA_TOGGLE_MISMATCH = 3, + OHCI_CCODE_STALL = 4, + OHCI_CCODE_DEVICE_NOT_RESPONDING = 5, + OHCI_CCODE_PID_CHECK_FAILURE = 6, + OHCI_CCODE_UNEXPECTED_PID = 7, + OHCI_CCODE_DATA_OVERRUN = 8, + OHCI_CCODE_DATA_UNDERRUN = 9, + OHCI_CCODE_BUFFER_OVERRUN = 12, + OHCI_CCODE_BUFFER_UNDERRUN = 13, + OHCI_CCODE_NOT_ACCESSED = 14, }; enum { @@ -148,6 +148,8 @@ enum { //--------------------------------------------------------------------+ TU_ATTR_WEAK bool hcd_dcache_clean(void const* addr, uint32_t data_size) { (void) addr; (void) data_size; return true; } TU_ATTR_WEAK bool hcd_dcache_invalidate(void const* addr, uint32_t data_size) { (void) addr; (void) data_size; return true; } + +// Optional macro to access ED in uncached way #ifndef hcd_dcache_uncached #define hcd_dcache_uncached(x) (x) #endif @@ -157,17 +159,21 @@ TU_ATTR_WEAK bool hcd_dcache_invalidate(void const* addr, uint32_t data_size) { //--------------------------------------------------------------------+ CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(256) static ohci_data_t ohci_data; -static ohci_ed_t * const p_ed_head[] = -{ - [TUSB_XFER_CONTROL] = &ohci_data.control[0].ed, - [TUSB_XFER_BULK ] = &ohci_data.bulk_head_ed, - [TUSB_XFER_INTERRUPT] = &ohci_data.period_head_ed, +static ohci_ed_t * const p_ed_head[] = { + [TUSB_XFER_CONTROL] = hcd_dcache_uncached(&ohci_data.control[0].ed), + [TUSB_XFER_BULK ] = hcd_dcache_uncached(&ohci_data.bulk_head_ed), + [TUSB_XFER_INTERRUPT] = hcd_dcache_uncached(&ohci_data.period_head_ed), [TUSB_XFER_ISOCHRONOUS] = NULL // TODO Isochronous }; static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed); static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr); static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd); +static ohci_ed_t* ed_from_addr(uint8_t dev_addr, uint8_t ep_addr); + +TU_ATTR_ALWAYS_INLINE static inline ohci_ed_t* ed_control(uint8_t daddr) { + return hcd_dcache_uncached(&ohci_data.control[daddr].ed); +} //--------------------------------------------------------------------+ // USBH-HCD API @@ -175,13 +181,11 @@ static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd); // If your system requires separation of virtual and physical memory, implement // tusb_app_virt_to_phys and tusb_app_virt_to_phys in your application. -TU_ATTR_ALWAYS_INLINE static inline void *_phys_addr(void *virtual_address) -{ +TU_ATTR_ALWAYS_INLINE static inline void *_phys_addr(void *virtual_address) { return tusb_app_virt_to_phys(virtual_address); } -TU_ATTR_ALWAYS_INLINE static inline void *_virt_addr(void *physical_address) -{ +TU_ATTR_ALWAYS_INLINE static inline void *_virt_addr(void *physical_address) { return tusb_app_phys_to_virt(physical_address); } @@ -192,8 +196,8 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { //------------- Data Structure init -------------// tu_memclr(&ohci_data, sizeof(ohci_data_t)); - for(uint8_t i=0; i<32; i++) - { // assign all interrupt pointers to period head ed + // assign all interrupt pointers to period head ed + for(uint8_t i=0; i<32; i++) { ohci_data.hcca.interrupt_table[i] = (uint32_t) _phys_addr(&ohci_data.period_head_ed); } @@ -202,19 +206,14 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { ohci_data.period_head_ed.w0.skip = 1; //If OHCI hardware is in SMM mode, gain ownership (Ref OHCI spec 5.1.1.3.3) - if (OHCI_REG->control_bit.interrupt_routing == 1) - { + if (OHCI_REG->control_bit.interrupt_routing == 1) { OHCI_REG->command_status_bit.ownership_change_request = 1; while (OHCI_REG->control_bit.interrupt_routing == 1) {} - } - - //If OHCI hardware has come from warm-boot, signal resume (Ref OHCI spec 5.1.1.3.4) - else if (OHCI_REG->control_bit.hc_functional_state != OHCI_CONTROL_FUNCSTATE_RESET && - OHCI_REG->control_bit.hc_functional_state != OHCI_CONTROL_FUNCSTATE_OPERATIONAL) - { + } else if (OHCI_REG->control_bit.hc_functional_state != OHCI_CONTROL_FUNCSTATE_RESET && + OHCI_REG->control_bit.hc_functional_state != OHCI_CONTROL_FUNCSTATE_OPERATIONAL) { + //If OHCI hardware has come from warm-boot, signal resume (Ref OHCI spec 5.1.1.3.4) //Wait 20 ms. (Ref Usb spec 7.1.7.7) OHCI_REG->control_bit.hc_functional_state = OHCI_CONTROL_FUNCSTATE_RESUME; - tusb_time_delay_ms_api(20); } @@ -256,7 +255,6 @@ uint32_t hcd_frame_number(uint8_t rhport) return (ohci_data.frame_number_hi << 16) | OHCI_REG->frame_number; } - //--------------------------------------------------------------------+ // PORT API //--------------------------------------------------------------------+ @@ -282,26 +280,18 @@ tusb_speed_t hcd_port_speed_get(uint8_t hostid) // endpoints are tied to an address, which only reclaim after a long delay when enumerating // thus there is no need to make sure ED is not in HC's cahed as it will not for sure -void hcd_device_close(uint8_t rhport, uint8_t dev_addr) -{ +void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { // TODO OHCI (void) rhport; // addr0 serves as static head --> only set skip bit - if ( dev_addr == 0 ) - { - hcd_dcache_uncached(ohci_data.control[0].ed.w0).skip = 1; - }else - { - // remove control - ed_list_remove_by_addr( p_ed_head[TUSB_XFER_CONTROL], dev_addr); - - // remove bulk - ed_list_remove_by_addr(p_ed_head[TUSB_XFER_BULK], dev_addr); - - // remove interrupt - ed_list_remove_by_addr(p_ed_head[TUSB_XFER_INTERRUPT], dev_addr); - + if (dev_addr == 0) { + ohci_ed_t* ed = ed_control(0); + ed->w0.skip = 1; + } else { + ed_list_remove_by_addr(p_ed_head[TUSB_XFER_CONTROL], dev_addr); // remove control + ed_list_remove_by_addr(p_ed_head[TUSB_XFER_BULK], dev_addr); // remove bulk + ed_list_remove_by_addr(p_ed_head[TUSB_XFER_INTERRUPT], dev_addr); // remove interrupt // TODO remove ISO } } @@ -313,29 +303,26 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) //--------------------------------------------------------------------+ // List Helper //--------------------------------------------------------------------+ -static inline tusb_xfer_type_t ed_get_xfer_type(ohci_ed_word0 w0) -{ +static inline tusb_xfer_type_t ed_get_xfer_type(ohci_ed_word0_t w0) { return (w0.ep_number == 0 ) ? TUSB_XFER_CONTROL : (w0.is_iso ) ? TUSB_XFER_ISOCHRONOUS : (w0.is_interrupt_xfer) ? TUSB_XFER_INTERRUPT : TUSB_XFER_BULK; } -static void ed_init(ohci_ed_t *p_ed, uint8_t dev_addr, uint16_t ep_size, uint8_t ep_addr, uint8_t xfer_type, uint8_t interval) -{ +static void ed_init(ohci_ed_t *p_ed, uint8_t dev_addr, uint16_t ep_size, uint8_t ep_addr, uint8_t xfer_type, uint8_t interval) { (void) interval; // address 0 is used as async head, which always on the list --> cannot be cleared - if (dev_addr != 0) - { - hcd_dcache_uncached(p_ed->td_tail) = 0; - hcd_dcache_uncached(p_ed->td_head).address = 0; - hcd_dcache_uncached(p_ed->next) = 0; + if (dev_addr != 0) { + p_ed->td_tail = 0; + p_ed->td_head.address = 0; + p_ed->next = 0; } tuh_bus_info_t bus_info; tuh_bus_info_get(dev_addr, &bus_info); - ohci_ed_word0 w0 = {.u = 0}; + ohci_ed_word0_t w0 = {.value = 0}; w0.dev_addr = dev_addr; w0.ep_number = ep_addr & 0x0F; w0.pid = (xfer_type == TUSB_XFER_CONTROL) ? PID_FROM_TD : (tu_edpt_dir(ep_addr) ? PID_IN : PID_OUT); @@ -345,12 +332,13 @@ static void ed_init(ohci_ed_t *p_ed, uint8_t dev_addr, uint16_t ep_size, uint8_t w0.used = 1; w0.is_interrupt_xfer = (xfer_type == TUSB_XFER_INTERRUPT ? 1 : 0); - hcd_dcache_uncached(p_ed->w0) = w0; + p_ed->w0 = w0; } static void gtd_init(ohci_gtd_t *p_td, uint8_t *data_ptr, uint16_t total_bytes) { tu_memclr(p_td, sizeof(ohci_gtd_t)); + p_td->used = 1; gtd_get_extra_data(p_td)->expected_bytes = total_bytes; p_td->buffer_rounding = 1; // less than queued length is not a error @@ -367,130 +355,109 @@ static void gtd_init(ohci_gtd_t *p_td, uint8_t *data_ptr, uint16_t total_bytes) } } -static ohci_ed_t * ed_from_addr(uint8_t dev_addr, uint8_t ep_addr) -{ - if ( tu_edpt_number(ep_addr) == 0 ) return &ohci_data.control[dev_addr].ed; +static ohci_ed_t* ed_from_addr(uint8_t dev_addr, uint8_t ep_addr) { + if (tu_edpt_number(ep_addr) == 0) { + return ed_control(dev_addr); + } ohci_ed_t* ed_pool = ohci_data.ed_pool; - - for(uint32_t i=0; i<ED_MAX; i++) - { - ohci_ed_word0 w0 = hcd_dcache_uncached(ed_pool[i].w0); - if ( (w0.dev_addr == dev_addr) && - ep_addr == tu_edpt_addr(w0.ep_number, w0.pid == PID_IN) ) - { - return &ed_pool[i]; + for (size_t i = 0; i < ED_MAX; i++) { + ohci_ed_t* qhd = hcd_dcache_uncached(&ed_pool[i]); + if ((qhd->w0.dev_addr == dev_addr) && + ep_addr == tu_edpt_addr(qhd->w0.ep_number, qhd->w0.pid == PID_IN)) { + return qhd; } } return NULL; } -static ohci_ed_t * ed_find_free(void) -{ +static ohci_ed_t* ed_find_free(void) { ohci_ed_t* ed_pool = ohci_data.ed_pool; - - for(uint8_t i = 0; i < ED_MAX; i++) - { - if ( !hcd_dcache_uncached(ed_pool[i].w0).used ) return &ed_pool[i]; + for (size_t i = 0; i < ED_MAX; i++) { + ohci_ed_t* qhd = hcd_dcache_uncached(&ed_pool[i]); + if (!qhd->w0.used) { + return qhd; + } } - return NULL; } -static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed) -{ - hcd_dcache_uncached(p_ed->next) = hcd_dcache_uncached(p_pre->next); - hcd_dcache_uncached(p_pre->next) = (uint32_t) _phys_addr(p_ed); +static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed) { + p_ed->next = p_pre->next; + p_pre->next = (uint32_t) _phys_addr(p_ed); } -static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr) -{ +static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr) { ohci_ed_t* p_prev = p_head; - uint32_t ed_pa; - while( (ed_pa = hcd_dcache_uncached(p_prev->next)) ) - { - ohci_ed_t* ed = (ohci_ed_t*) _virt_addr((void *)ed_pa); + while (p_prev->next) { + ohci_ed_t* ed = (ohci_ed_t*)_virt_addr((void*)p_prev->next); - if (hcd_dcache_uncached(ed->w0).dev_addr == dev_addr) - { + if (ed->w0.dev_addr == dev_addr) { // Prevent Host Controller from processing this ED while we remove it - hcd_dcache_uncached(ed->w0).skip = 1; + ed->w0.skip = 1; // unlink ed, will also move up p_prev - hcd_dcache_uncached(p_prev->next) = hcd_dcache_uncached(ed->next); + p_prev->next = ed->next; // point the removed ED's next pointer to list head to make sure HC can always safely move away from this ED - hcd_dcache_uncached(ed->next) = (uint32_t) _phys_addr(p_head); - ohci_ed_word0 w0 = hcd_dcache_uncached(ed->w0); - w0.used = 0; - w0.skip = 0; - hcd_dcache_uncached(ed->w0) = w0; - }else - { - p_prev = (ohci_ed_t*) _virt_addr((void *)ed_pa); + ed->next = (uint32_t)_phys_addr(p_head); + ed->w0.used = 0; + ed->w0.skip = 0; + } else { + p_prev = (ohci_ed_t*)_virt_addr((void*)p_prev->next); } } } -static ohci_gtd_t * gtd_find_free(void) -{ - for(uint8_t i=0; i < GTD_MAX; i++) - { - if ( !ohci_data.gtd_extra[i].used ) { - ohci_data.gtd_extra[i].used = 1; +static ohci_gtd_t* gtd_find_free(void) { + for (uint8_t i = 0; i < GTD_MAX; i++) { + if (!ohci_data.gtd_pool[i].used) { return &ohci_data.gtd_pool[i]; } } - return NULL; } //--------------------------------------------------------------------+ // Endpoint API //--------------------------------------------------------------------+ -bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) -{ - (void) rhport; +bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const* ep_desc) { + (void)rhport; // TODO iso support TU_ASSERT(ep_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS); //------------- Prepare Queue Head -------------// - ohci_ed_t * p_ed; - - if ( ep_desc->bEndpointAddress == 0 ) - { - p_ed = &ohci_data.control[dev_addr].ed; - }else - { + ohci_ed_t* p_ed; + if (ep_desc->bEndpointAddress == 0) { + p_ed = ed_control(dev_addr); + } else { p_ed = ed_find_free(); } TU_ASSERT(p_ed); - ed_init( p_ed, dev_addr, tu_edpt_packet_size(ep_desc), ep_desc->bEndpointAddress, - ep_desc->bmAttributes.xfer, ep_desc->bInterval ); + ed_init(p_ed, dev_addr, tu_edpt_packet_size(ep_desc), ep_desc->bEndpointAddress, + ep_desc->bmAttributes.xfer, ep_desc->bInterval); // control of dev0 is used as static async head - if ( dev_addr == 0 ) - { - hcd_dcache_uncached(p_ed->w0).skip = 0; // only need to clear skip bit + if (dev_addr == 0) { + p_ed->w0.skip = 0; // only need to clear skip bit return true; } - if ( tu_edpt_number(ep_desc->bEndpointAddress) != 0 ) { + if (tu_edpt_number(ep_desc->bEndpointAddress) != 0) { // Get an empty TD and use it as the end-of-list marker. // This marker TD will be used when a transfer is made on this EP // (and a new, empty TD will be allocated for the next-next transfer). ohci_gtd_t* gtd = gtd_find_free(); TU_ASSERT(gtd); - hcd_dcache_uncached(p_ed->td_head).address = (uint32_t)_phys_addr(gtd); - hcd_dcache_uncached(p_ed->td_tail) = (uint32_t)_phys_addr(gtd); + p_ed->td_head.address = (uint32_t)_phys_addr(gtd); + p_ed->td_tail = (uint32_t)_phys_addr(gtd); } - ed_list_insert( p_ed_head[ep_desc->bmAttributes.xfer], p_ed ); - + ed_list_insert(p_ed_head[ep_desc->bmAttributes.xfer], p_ed); return true; } @@ -499,25 +466,23 @@ bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) { return false; // TODO not implemented yet } -bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) -{ +bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) { (void) rhport; - ohci_ed_t* ed = &ohci_data.control[dev_addr].ed; + ohci_ed_t* ed = ed_control(dev_addr); ohci_gtd_t *qtd = &ohci_data.control[dev_addr].gtd; hcd_dcache_clean(setup_packet, 8); gtd_init(qtd, (uint8_t*)(uintptr_t) setup_packet, 8); - gtd_get_extra_data(qtd)->dev_addr = dev_addr; - gtd_get_extra_data(qtd)->ep_addr = tu_edpt_addr(0, TUSB_DIR_OUT); + qtd->index = dev_addr; qtd->pid = PID_SETUP; qtd->data_toggle = GTD_DT_DATA0; qtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES; hcd_dcache_clean(qtd, sizeof(ohci_gtd_t)); //------------- Attach TDs list to Control Endpoint -------------// - hcd_dcache_uncached(ed->td_head.address) = (uint32_t) _phys_addr(qtd); + ed->td_head.address = (uint32_t) _phys_addr(qtd); OHCI_REG->command_status_bit.control_list_filled = 1; @@ -538,32 +503,27 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * hcd_dcache_clean(buffer, buflen); } - if ( epnum == 0 ) - { - ohci_ed_t* ed = &ohci_data.control[dev_addr].ed; + ohci_ed_t * ed = ed_from_addr(dev_addr, ep_addr); + if (epnum == 0) { ohci_gtd_t* gtd = &ohci_data.control[dev_addr].gtd; gtd_init(gtd, buffer, buflen); - gtd_get_extra_data(gtd)->dev_addr = dev_addr; - gtd_get_extra_data(gtd)->ep_addr = ep_addr; - gtd->pid = dir ? PID_IN : PID_OUT; - gtd->data_toggle = GTD_DT_DATA1; // Both Data and Ack stage start with DATA1 + gtd->index = dev_addr; + gtd->pid = dir ? PID_IN : PID_OUT; + gtd->data_toggle = GTD_DT_DATA1; // Both Data and Ack stage start with DATA1 gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES; hcd_dcache_clean(gtd, sizeof(ohci_gtd_t)); - hcd_dcache_uncached(ed->td_head).address = (uint32_t) _phys_addr(gtd); + ed->td_head.address = (uint32_t)_phys_addr(gtd); OHCI_REG->command_status_bit.control_list_filled = 1; - }else - { - ohci_ed_t * ed = ed_from_addr(dev_addr, ep_addr); - tusb_xfer_type_t xfer_type = ed_get_xfer_type( hcd_dcache_uncached(ed->w0) ); - ohci_gtd_t *gtd = (ohci_gtd_t *)_virt_addr((void *)hcd_dcache_uncached(ed->td_tail)); + } else { + tusb_xfer_type_t xfer_type = ed_get_xfer_type(ed->w0); + ohci_gtd_t* gtd = (ohci_gtd_t*)_virt_addr((void*)ed->td_tail); gtd_init(gtd, buffer, buflen); - gtd_get_extra_data(gtd)->dev_addr = dev_addr; - gtd_get_extra_data(gtd)->ep_addr = ep_addr; + gtd->index = ed-ohci_data.ed_pool; gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES; // Insert a new, empty TD at the tail, to be used by the next transfer @@ -573,9 +533,11 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * gtd->next = (uint32_t)_phys_addr(new_gtd); hcd_dcache_clean(gtd, sizeof(ohci_gtd_t)); - hcd_dcache_uncached(ed->td_tail) = (uint32_t)_phys_addr(new_gtd); + ed->td_tail = (uint32_t)_phys_addr(new_gtd); - if (TUSB_XFER_BULK == xfer_type) OHCI_REG->command_status_bit.bulk_list_filled = 1; + if (TUSB_XFER_BULK == xfer_type) { + OHCI_REG->command_status_bit.bulk_list_filled = 1; + } } return true; @@ -593,12 +555,14 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { (void) rhport; ohci_ed_t * const p_ed = ed_from_addr(dev_addr, ep_addr); - ohci_ed_td_head td_head = hcd_dcache_uncached(p_ed->td_head); + ohci_ed_word2_t td_head = p_ed->td_head; td_head.toggle = 0; // reset data toggle td_head.halted = 0; - hcd_dcache_uncached(p_ed->td_head) = td_head; + p_ed->td_head = td_head; - if ( TUSB_XFER_BULK == ed_get_xfer_type(hcd_dcache_uncached(p_ed->w0)) ) OHCI_REG->command_status_bit.bulk_list_filled = 1; + if (TUSB_XFER_BULK == ed_get_xfer_type(p_ed->w0)) { + OHCI_REG->command_status_bit.bulk_list_filled = 1; + } return true; } @@ -632,13 +596,22 @@ static ohci_td_item_t* list_reverse(ohci_td_item_t* td_head) return _virt_addr(td_reverse_head); } -static inline bool gtd_is_control(ohci_gtd_t const * const p_qtd) -{ +TU_ATTR_ALWAYS_INLINE static inline bool gtd_is_control(ohci_gtd_t const * const p_qtd) { return ((uint32_t) p_qtd) < ((uint32_t) ohci_data.gtd_pool); // check ohci_data_t for memory layout } +TU_ATTR_ALWAYS_INLINE static inline ohci_ed_t* gtd_get_ed(ohci_gtd_t const* const p_qtd) { + ohci_ed_t* ed; + if (gtd_is_control(p_qtd)) { + ed = &ohci_data.control[p_qtd->index].ed; + } else { + ed = &ohci_data.ed_pool[p_qtd->index]; + } + return hcd_dcache_uncached(ed); +} + static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd) { - if ( gtd_is_control(gtd) ) { + if (gtd_is_control(gtd)) { uint8_t idx = ((uintptr_t)gtd - (uintptr_t)&ohci_data.control->gtd) / sizeof(ohci_data.control[0]); return &ohci_data.gtd_extra_control[idx]; }else { @@ -646,85 +619,77 @@ static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd) { } } -static inline uint32_t gtd_xfer_byte_left(uint32_t buffer_end, uint32_t current_buffer) -{ +TU_ATTR_ALWAYS_INLINE static inline uint32_t gtd_xfer_byte_left(uint32_t buffer_end, uint32_t current_buffer) { // 5.2.9 OHCI sample code - // CBP is 0 mean all data is transferred - if (current_buffer == 0) return 0; + if (current_buffer == 0) { + return 0; + } return (tu_align4k(buffer_end ^ current_buffer) ? 0x1000 : 0) + - tu_offset4k(buffer_end) - tu_offset4k(current_buffer) + 1; + tu_offset4k(buffer_end) - tu_offset4k(current_buffer) + 1; } -static void done_queue_isr(uint8_t hostid) -{ - (void) hostid; +static void done_queue_isr(uint8_t hostid) { + (void)hostid; // done head is written in reversed order of completion --> need to reverse the done queue first - ohci_td_item_t* td_head = list_reverse ( (ohci_td_item_t*) tu_align16(hcd_dcache_uncached(ohci_data.hcca).done_head) ); - hcd_dcache_uncached(ohci_data.hcca).done_head = 0; + ohci_td_item_t* td_head = list_reverse((ohci_td_item_t*)tu_align16(ohci_data.hcca.done_head)); + ohci_data.hcca.done_head = 0; - while( td_head != NULL ) - { + while (td_head != NULL) { // TODO check if td_head is iso td //------------- Non ISO transfer -------------// - ohci_gtd_t * const qtd = (ohci_gtd_t *) td_head; + ohci_gtd_t* const qtd = (ohci_gtd_t*) td_head; xfer_result_t const event = (qtd->condition_code == OHCI_CCODE_NO_ERROR) ? XFER_RESULT_SUCCESS : (qtd->condition_code == OHCI_CCODE_STALL) ? XFER_RESULT_STALLED : XFER_RESULT_FAILED; - - gtd_get_extra_data(qtd)->used = 0; // free TD - if ( (qtd->delay_interrupt == OHCI_INT_ON_COMPLETE_YES) || (event != XFER_RESULT_SUCCESS) ) - { - 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); - - hcd_event_xfer_complete(gtd_get_extra_data(qtd)->dev_addr, gtd_get_extra_data(qtd)->ep_addr, xferred_bytes, event, true); + qtd->used = 0; // free TD + if ((qtd->delay_interrupt == OHCI_INT_ON_COMPLETE_YES) || (event != XFER_RESULT_SUCCESS)) { + const ohci_ed_t* ed = gtd_get_ed(qtd); + const ohci_ed_word0_t ed_w0 = ed->w0; + const uint32_t xferred_bytes = gtd_get_extra_data(qtd)->expected_bytes - gtd_xfer_byte_left((uint32_t)qtd->buffer_end, (uint32_t)qtd->current_buffer_pointer); + uint8_t dir = (ed_w0.ep_number == 0) ? (qtd->pid == PID_IN) : (ed_w0.pid == PID_IN); + const uint8_t ep_addr = tu_edpt_addr(ed_w0.ep_number, dir); + hcd_event_xfer_complete(ed_w0.dev_addr, ep_addr, xferred_bytes, event, true); } - td_head = (ohci_td_item_t*) _virt_addr((void *)td_head->next); + td_head = (ohci_td_item_t*)_virt_addr((void*)td_head->next); } } void hcd_int_handler(uint8_t hostid, bool in_isr) { - (void) in_isr; - - uint32_t const int_en = OHCI_REG->interrupt_enable; + (void)in_isr; + uint32_t const int_en = OHCI_REG->interrupt_enable; uint32_t const int_status = OHCI_REG->interrupt_status & int_en; - if (int_status == 0) return; + if (int_status == 0) { + return; + } // Disable MIE as per OHCI spec 5.3 OHCI_REG->interrupt_disable = OHCI_INT_MASTER_ENABLE_MASK; // Frame number overflow - if ( int_status & OHCI_INT_FRAME_OVERFLOW_MASK ) - { + if (int_status & OHCI_INT_FRAME_OVERFLOW_MASK) { ohci_data.frame_number_hi++; } //------------- RootHub status -------------// - if ( int_status & OHCI_INT_RHPORT_STATUS_CHANGE_MASK ) - { - for (int i = 0; i < TUP_OHCI_RHPORTS; i++) - { + if (int_status & OHCI_INT_RHPORT_STATUS_CHANGE_MASK) { + for (int i = 0; i < TUP_OHCI_RHPORTS; i++) { uint32_t const rhport_status = OHCI_REG->rhport_status[i] & RHPORT_ALL_CHANGE_MASK; - if ( rhport_status & RHPORT_CONNECT_STATUS_CHANGE_MASK ) - { + if (rhport_status & RHPORT_CONNECT_STATUS_CHANGE_MASK) { // TODO check if remote wake-up - if ( OHCI_REG->rhport_status_bit[i].current_connect_status ) - { + if (OHCI_REG->rhport_status_bit[i].current_connect_status) { // TODO reset port immediately, without this controller will got 2-3 (debouncing connection status change) OHCI_REG->rhport_status[i] = RHPORT_PORT_RESET_STATUS_MASK; hcd_event_device_attach(i, true); - }else - { + } else { hcd_event_device_remove(i, true); } } - if ( rhport_status & RHPORT_PORT_SUSPEND_CHANGE_MASK) - { - + if (rhport_status & RHPORT_PORT_SUSPEND_CHANGE_MASK) { } OHCI_REG->rhport_status[i] = rhport_status; // acknowledge all interrupt @@ -732,13 +697,11 @@ void hcd_int_handler(uint8_t hostid, bool in_isr) { } //------------- Transfer Complete -------------// - if (int_status & OHCI_INT_WRITEBACK_DONEHEAD_MASK) - { + if (int_status & OHCI_INT_WRITEBACK_DONEHEAD_MASK) { done_queue_isr(hostid); } OHCI_REG->interrupt_status = int_status; // Acknowledge handled interrupt - OHCI_REG->interrupt_enable = OHCI_INT_MASTER_ENABLE_MASK; // Enable MIE } //--------------------------------------------------------------------+ diff --git a/src/portable/ohci/ohci.h b/src/portable/ohci/ohci.h index 78cac664e..ac5d2881b 100644 --- a/src/portable/ohci/ohci.h +++ b/src/portable/ohci/ohci.h @@ -79,12 +79,6 @@ TU_VERIFY_STATIC( sizeof(ohci_hcca_t) == 256, "size is not correct" ); // the _smallest_ amount that can be read/written at a time. If there were to be multiple TDs // in the same cache line, they would be required to always have the same logical ownership. // This ends up being impossible to guarantee, so we choose a design which avoids the situation entirely. -// -// TDs have a minimum alignment requirement according to the OHCI specification. This is 16 bytes for -// a general TD but 32 bytes for an isochronous TD. It happens that typical CPU cache line sizes are usually -// a power of 2 at least 32. In order to simplify code later in this file, we assume this -// as an additional requirement. -TU_VERIFY_STATIC( (CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 0) % 32 == 0, "cache line not multiple of 32" ); // common link item for gtd and itd for list travel // use as pointer only @@ -94,10 +88,12 @@ typedef struct TU_ATTR_ALIGNED(16) { uint32_t reserved2; }ohci_td_item_t; -typedef struct TU_ATTR_ALIGNED(CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 16) +typedef struct TU_ATTR_ALIGNED(TU_MAX(CFG_TUH_MEM_DCACHE_LINE_SIZE, 16)) { // Word 0 - uint32_t : 18; // can be used + uint32_t used : 1; + uint32_t index : 8; // endpoint index the gtd belongs to, or device address in case of control xfer + uint32_t : 9; // can be used uint32_t buffer_rounding : 1; uint32_t pid : 2; uint32_t delay_interrupt : 3; @@ -114,8 +110,7 @@ typedef struct TU_ATTR_ALIGNED(CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LI // Word 3 uint8_t* buffer_end; } ohci_gtd_t; - -TU_VERIFY_STATIC( sizeof(ohci_gtd_t) == CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 16, "size is not correct" ); +TU_VERIFY_STATIC(sizeof(ohci_gtd_t) == TU_MAX(CFG_TUH_MEM_DCACHE_LINE_SIZE,16), "size is not correct" ); typedef union { struct { @@ -126,13 +121,14 @@ typedef union { uint32_t skip : 1; uint32_t is_iso : 1; uint32_t max_packet_size : 11; - // HCD: make use of 5 reserved bits + // HCD: make use of 5 reserved bits uint32_t used : 1; uint32_t is_interrupt_xfer : 1; uint32_t : 3; }; - uint32_t u; -} ohci_ed_word0; + uint32_t value; +} ohci_ed_word0_t; +TU_VERIFY_STATIC(sizeof(ohci_ed_word0_t) == 4, "size is not correct" ); typedef union { uint32_t address; @@ -141,26 +137,18 @@ typedef union { uint32_t toggle : 1; uint32_t : 30; }; -} ohci_ed_td_head; - -typedef struct TU_ATTR_ALIGNED(16) -{ - // Word 0 - ohci_ed_word0 w0; +} ohci_ed_word2_t; +TU_VERIFY_STATIC(sizeof(ohci_ed_word2_t) == 4, "size is not correct" ); - // Word 1 - uint32_t td_tail; - - // Word 2 - volatile ohci_ed_td_head td_head; - - // Word 3: next ED - uint32_t next; +typedef struct TU_ATTR_ALIGNED(TU_MAX(CFG_TUH_MEM_DCACHE_LINE_SIZE, 16)) { + ohci_ed_word0_t w0; // Word 0 + uint32_t td_tail; // Word 1 + volatile ohci_ed_word2_t td_head; // Word 2 + uint32_t next; // Word 3 } ohci_ed_t; +TU_VERIFY_STATIC(sizeof(ohci_ed_t) == TU_MAX(CFG_TUH_MEM_DCACHE_LINE_SIZE, 16), "size is not correct" ); -TU_VERIFY_STATIC( sizeof(ohci_ed_t) == 16, "size is not correct" ); - -typedef struct TU_ATTR_ALIGNED(CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 32) +typedef struct TU_ATTR_ALIGNED(TU_MAX(CFG_TUH_MEM_DCACHE_LINE_SIZE, 32)) { /*---------- Word 1 ----------*/ uint32_t starting_frame : 16; @@ -183,15 +171,12 @@ typedef struct TU_ATTR_ALIGNED(CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LI volatile uint16_t offset_packetstatus[8]; } ochi_itd_t; -TU_VERIFY_STATIC( sizeof(ochi_itd_t) == CFG_TUH_MEM_DCACHE_ENABLE ? CFG_TUH_MEM_DCACHE_LINE_SIZE : 32, "size is not correct" ); +TU_VERIFY_STATIC(sizeof(ochi_itd_t) == TU_MAX(CFG_TUH_MEM_DCACHE_LINE_SIZE, 32), "size is not correct" ); typedef struct { uint16_t expected_bytes; // up to 8192 bytes so max is 13 bits - uint8_t dev_addr : 7; - uint8_t used : 1; - uint8_t ep_addr; } gtd_extra_data_t; -TU_VERIFY_STATIC( sizeof(gtd_extra_data_t) == 4, "size is not correct" ); +TU_VERIFY_STATIC(sizeof(gtd_extra_data_t) == 2, "size is not correct" ); // structure with member alignment required from large to small typedef struct TU_ATTR_ALIGNED(256) { |
