Merge pull request #2175 from hathach/hcd-abort-xfer

Add HCD abort xfer API
This commit is contained in:
Ha Thach 2023-07-24 22:08:53 +07:00 committed by GitHub
commit 9554283b03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 311 additions and 170 deletions

View File

@ -1,3 +1,4 @@
board:mimxrt1060_evk
board:mimxrt1064_evk
board:mcb1800
mcu:RP2040

View File

@ -54,19 +54,19 @@
// default to pin on Adafruit Feather rp2040 USB Host or Tester if defined
//--------------------------------------------------------------------+
// #define USE_ADAFRUIT_RP2040_TESTER
#ifdef USE_ADAFRUIT_RP2040_TESTER
#define PICO_DEFAULT_PIO_USB_DP_PIN 20
#define PICO_DEFAULT_PIO_USB_VBUSEN_PIN 22
// #define USE_ADAFRUIT_FEATHER_RP2040_USBHOST
#ifdef USE_ADAFRUIT_FEATHER_RP2040_USBHOST
#define PICO_DEFAULT_PIO_USB_DP_PIN 16
#define PICO_DEFAULT_PIO_USB_VBUSEN_PIN 18
#endif
#ifndef PICO_DEFAULT_PIO_USB_DP_PIN
#define PICO_DEFAULT_PIO_USB_DP_PIN 16
#define PICO_DEFAULT_PIO_USB_DP_PIN 20
#endif
// VBUS enable pin and its active state
#ifndef PICO_DEFAULT_PIO_USB_VBUSEN_PIN
#define PICO_DEFAULT_PIO_USB_VBUSEN_PIN 18
#define PICO_DEFAULT_PIO_USB_VBUSEN_PIN 22
#endif
// VBUS enable state

View File

@ -53,6 +53,8 @@
#define U32_TO_U8S_LE(_u32) TU_U32_BYTE0(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE3(_u32)
#define TU_BIT(n) (1UL << (n))
// Generate a mask with bit from high (31) to low (0) set, e.g TU_GENMASK(3, 0) = 0b1111
#define TU_GENMASK(h, l) ( (UINT32_MAX << (l)) & (UINT32_MAX >> (31 - (h))) )
//--------------------------------------------------------------------+

View File

@ -171,6 +171,10 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen);
// Abort a queued transfer. Note: it can only abort transfer that has not been started
// Return true if a queued transfer is aborted, false if there is no transfer to abort
bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr);
// Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]);

View File

@ -710,6 +710,25 @@ bool tuh_edpt_xfer(tuh_xfer_t* xfer)
return true;
}
bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr) {
usbh_device_t* dev = get_device(daddr);
TU_VERIFY(dev);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
// skip if not busy
TU_VERIFY(dev->ep_status[epnum][dir].busy);
bool const ret = hcd_edpt_abort_xfer(dev->rhport, daddr, ep_addr);
if (ret) {
// mark as ready if transfer is aborted
dev->ep_status[epnum][dir].busy = false;
}
return ret;
}
//--------------------------------------------------------------------+
// USBH API For Class Driver
//--------------------------------------------------------------------+
@ -741,7 +760,7 @@ void usbh_int_set(bool enabled)
// Endpoint API
//--------------------------------------------------------------------+
// TODO has some duplication code with device, refactor later
// Claim an endpoint for transfer
bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr)
{
// Note: addr0 only use tuh_control_xfer
@ -757,7 +776,7 @@ bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr)
return true;
}
// TODO has some duplication code with device, refactor later
// Release an claimed endpoint due to failed transfer attempt
bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr)
{
// Note: addr0 only use tuh_control_xfer
@ -773,7 +792,7 @@ bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr)
return true;
}
// TODO has some duplication code with device, refactor later
// Submit an transfer
bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes,
tuh_xfer_cb_t complete_cb, uintptr_t user_data)
{
@ -840,14 +859,13 @@ bool tuh_edpt_open(uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep)
return hcd_edpt_open(usbh_get_rhport(dev_addr), dev_addr, desc_ep);
}
bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) {
usbh_device_t* dev = get_device(dev_addr);
TU_VERIFY(dev);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
return dev->ep_status[epnum][dir].busy;
}

View File

@ -172,8 +172,12 @@ bool tuh_control_xfer(tuh_xfer_t* xfer);
// - sync : blocking if complete callback is NULL.
bool tuh_edpt_xfer(tuh_xfer_t* xfer);
// Open an non-control endpoint
bool tuh_edpt_open(uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep);
// Open a non-control endpoint
bool tuh_edpt_open(uint8_t daddr, tusb_desc_endpoint_t const * desc_ep);
// Abort a queued transfer. Note: it can only abort transfer that has not been started
// Return true if a queued transfer is aborted, false if there is no transfer to abort
bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr);
// Set Configuration (control transfer)
// config_num = 0 will un-configure device. Note: config_num = config_descriptor_index + 1

View File

@ -48,8 +48,8 @@
#ifdef TUP_USBIP_CHIPIDEA_HS
// NXP Transdimension: 8 elements
#define FRAMELIST_SIZE_BIT_VALUE 7u
#define FRAMELIST_SIZE_USBCMD_VALUE (((FRAMELIST_SIZE_BIT_VALUE & 3) << EHCI_USBCMD_POS_FRAMELIST_SIZE) | \
((FRAMELIST_SIZE_BIT_VALUE >> 2) << EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB))
#define FRAMELIST_SIZE_USBCMD_VALUE (((FRAMELIST_SIZE_BIT_VALUE & 3) << EHCI_USBCMD_FRAMELIST_SIZE_SHIFT) | \
((FRAMELIST_SIZE_BIT_VALUE >> 2) << EHCI_USBCMD_CHIPIDEA_FRAMELIST_SIZE_MSB_SHIFT))
#else
// STD EHCI: 256 elements
#define FRAMELIST_SIZE_BIT_VALUE 2u
@ -126,55 +126,50 @@ static inline void print_intr(uint32_t intr) {
//--------------------------------------------------------------------+
// PROTOTYPE
//--------------------------------------------------------------------+
static inline ehci_link_t* get_period_head(uint8_t rhport, uint32_t interval_ms)
{
(void) rhport;
return (ehci_link_t*) &ehci_data.period_head_arr[ tu_log2( tu_min32(FRAMELIST_SIZE, interval_ms) ) ];
}
static inline ehci_qhd_t* qhd_control(uint8_t dev_addr)
{
return &ehci_data.control[dev_addr].qhd;
}
// weak dcache for non-cacheable MCU
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; }
TU_ATTR_WEAK bool hcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) { (void) addr; (void) data_size; return true; }
static inline ehci_qhd_t* qhd_async_head(uint8_t rhport)
{
(void) rhport;
// control qhd of dev0 is used as async head
return qhd_control(0);
}
static inline ehci_qtd_t* qtd_control(uint8_t dev_addr)
{
return &ehci_data.control[dev_addr].qtd;
}
static inline ehci_qhd_t* qhd_next (ehci_qhd_t const * p_qhd);
static inline ehci_qhd_t* qhd_find_free (void);
static inline ehci_qhd_t* qhd_get_from_addr (uint8_t dev_addr, uint8_t ep_addr);
TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* qhd_control(uint8_t dev_addr);
TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* qhd_next (ehci_qhd_t const * p_qhd);
TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* qhd_find_free (void);
static ehci_qhd_t* qhd_get_from_addr (uint8_t dev_addr, uint8_t ep_addr);
static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc);
static void qhd_attach_qtd(ehci_qhd_t *qhd, ehci_qtd_t *qtd);
static void qhd_remove_qtd(ehci_qhd_t *qhd);
static inline ehci_qtd_t* qtd_find_free (void);
TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t* qtd_control(uint8_t dev_addr);
TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t* qtd_find_free (void);
static void qtd_init (ehci_qtd_t* qtd, void const* buffer, uint16_t total_bytes);
static inline void list_insert (ehci_link_t *current, ehci_link_t *new, uint8_t new_type);
static inline ehci_link_t* list_next (ehci_link_t const *p_link);
TU_ATTR_ALWAYS_INLINE static inline ehci_link_t* list_get_period_head(uint8_t rhport, uint32_t interval_ms);
TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* list_get_async_head(uint8_t rhport);
TU_ATTR_ALWAYS_INLINE static inline void list_insert (ehci_link_t *current, ehci_link_t *new, uint8_t new_type);
TU_ATTR_ALWAYS_INLINE static inline ehci_link_t* list_next (ehci_link_t const *p_link);
static void list_remove_qhd_by_daddr(ehci_link_t* list_head, uint8_t dev_addr);
TU_ATTR_WEAK bool hcd_dcache_clean(void const* addr, uint32_t data_size) {
(void) addr; (void) data_size;
return true;
static void ehci_disable_schedule(ehci_registers_t* regs, bool is_period) {
// maybe have a timeout for status
if (is_period) {
regs->command_bm.periodic_enable = 0;
while(regs->status_bm.periodic_status) {}
} else {
regs->command_bm.async_enable = 0;
while(regs->status_bm.async_status) {} // should have a timeout
}
}
TU_ATTR_WEAK bool hcd_dcache_invalidate(void const* addr, uint32_t data_size) {
(void) addr; (void) data_size;
return true;
}
TU_ATTR_WEAK bool hcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
(void) addr; (void) data_size;
return true;
static void ehci_enable_schedule(ehci_registers_t* regs, bool is_period) {
// maybe have a timeout for status
if (is_period) {
regs->command_bm.periodic_enable = 1;
while ( 0 == regs->status_bm.periodic_status ) {}
} else {
regs->command_bm.async_enable = 1;
while( 0 == regs->status_bm.async_status ) {}
}
}
//--------------------------------------------------------------------+
@ -231,43 +226,6 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport)
return (tusb_speed_t) ehci_data.regs->portsc_bm.nxp_port_speed; // NXP specific port speed
}
static void list_remove_qhd_by_daddr(ehci_link_t* list_head, uint8_t dev_addr) {
ehci_link_t* prev = list_head;
while (prev && !prev->terminate) {
ehci_qhd_t* qhd = (ehci_qhd_t*) (uintptr_t) list_next(prev);
// done if loop back to head
if ( (uintptr_t) qhd == (uintptr_t) list_head) {
break;
}
if ( qhd->dev_addr == dev_addr ) {
// TODO deactivate all TD, wait for QHD to inactive before removal
prev->address = qhd->next.address;
// EHCI 4.8.2 link the removed qhd's next to async head (which always reachable by Host Controller)
qhd->next.address = ((uint32_t) list_head) | (EHCI_QTYPE_QHD << 1);
if ( qhd->int_smask )
{
// period list queue element is guarantee to be free in the next frame (1 ms)
qhd->used = 0;
}else
{
// async list use async advance handshake
// mark as removing, will completely re-usable when async advance isr occurs
qhd->removing = 1;
}
hcd_dcache_clean(qhd, sizeof(ehci_qhd_t));
hcd_dcache_clean(prev, sizeof(ehci_qhd_t));
}else {
prev = list_next(prev);
}
}
}
// Close all opened endpoint belong to this device
void hcd_device_close(uint8_t rhport, uint8_t daddr)
{
@ -277,7 +235,7 @@ void hcd_device_close(uint8_t rhport, uint8_t daddr)
}
// Remove from async list
list_remove_qhd_by_daddr((ehci_link_t *) qhd_async_head(rhport), daddr);
list_remove_qhd_by_daddr((ehci_link_t *) list_get_async_head(rhport), daddr);
// Remove from all interval period list
for(uint8_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++) {
@ -289,37 +247,42 @@ void hcd_device_close(uint8_t rhport, uint8_t daddr)
}
static void init_periodic_list(uint8_t rhport) {
(void) rhport;
// Build the polling interval tree with 1 ms, 2 ms, 4 ms and 8 ms (framesize) only
for ( uint32_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++ ) {
ehci_data.period_head_arr[i].int_smask = 1; // queue head in period list must have smask non-zero
ehci_data.period_head_arr[i].qtd_overlay.halted = 1; // dummy node, always inactive
}
ehci_link_t * const framelist = ehci_data.period_framelist;
ehci_link_t * const period_1ms = get_period_head(rhport, 1u);
// TODO EHCI_FRAMELIST_SIZE with other size than 8
// all links --> period_head_arr[0] (1ms)
// 0, 2, 4, 6 etc --> period_head_arr[1] (2ms)
// 1, 5 --> period_head_arr[2] (4ms)
// 3 --> period_head_arr[3] (8ms)
// TODO EHCI_FRAMELIST_SIZE with other size than 8
ehci_link_t * const framelist = ehci_data.period_framelist;
ehci_link_t * const head_1ms = (ehci_link_t *) &ehci_data.period_head_arr[0];
ehci_link_t * const head_2ms = (ehci_link_t *) &ehci_data.period_head_arr[1];
ehci_link_t * const head_4ms = (ehci_link_t *) &ehci_data.period_head_arr[2];
ehci_link_t * const head_8ms = (ehci_link_t *) &ehci_data.period_head_arr[3];
for (uint32_t i = 0; i < FRAMELIST_SIZE; i++) {
framelist[i].address = (uint32_t) period_1ms;
framelist[i].address = (uint32_t) head_1ms;
framelist[i].type = EHCI_QTYPE_QHD;
}
for (uint32_t i = 0; i < FRAMELIST_SIZE; i += 2) {
list_insert(framelist + i, get_period_head(rhport, 2u), EHCI_QTYPE_QHD);
list_insert(framelist + i, head_2ms, EHCI_QTYPE_QHD);
}
for (uint32_t i = 1; i < FRAMELIST_SIZE; i += 4) {
list_insert(framelist + i, get_period_head(rhport, 4u), EHCI_QTYPE_QHD);
list_insert(framelist + i, head_4ms, EHCI_QTYPE_QHD);
}
list_insert(framelist + 3, get_period_head(rhport, 8u), EHCI_QTYPE_QHD);
list_insert(framelist + 3, head_8ms, EHCI_QTYPE_QHD);
period_1ms->terminate = 1;
head_1ms->terminate = 1;
}
bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
@ -348,7 +311,7 @@ bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
EHCI_INT_MASK_NXP_PERIODIC | EHCI_INT_MASK_NXP_ASYNC | EHCI_INT_MASK_FRAMELIST_ROLLOVER;
//------------- Asynchronous List -------------//
ehci_qhd_t * const async_head = qhd_async_head(rhport);
ehci_qhd_t * const async_head = list_get_async_head(rhport);
tu_memclr(async_head, sizeof(ehci_qhd_t));
async_head->next.address = (uint32_t) async_head; // circular list, next is itself
@ -369,8 +332,7 @@ bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
regs->nxp_tt_control = 0;
//------------- USB CMD Register -------------//
regs->command |= TU_BIT(EHCI_USBCMD_POS_RUN_STOP) | TU_BIT(EHCI_USBCMD_POS_ASYNC_ENABLE) |
TU_BIT(EHCI_USBCMD_POS_PERIOD_ENABLE) | // TODO enable period list only there is int/iso endpoint
regs->command |= EHCI_USBCMD_RUN_STOP | EHCI_USBCMD_PERIOD_SCHEDULE_ENABLE | EHCI_USBCMD_ASYNC_SCHEDULE_ENABLE |
FRAMELIST_SIZE_USBCMD_VALUE;
//------------- ConfigFlag Register (skip) -------------//
@ -380,7 +342,7 @@ bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
if (ehci_data.cap_regs->hcsparams_bm.port_power_control) {
// mask out all change bits since they are Write 1 to clear
uint32_t portsc = (regs->portsc & ~EHCI_PORTSC_MASK_W1C);
portsc |= ECHI_PORTSC_MASK_PORT_POWER;
portsc |= EHCI_PORTSC_MASK_PORT_POWER;
regs->portsc = portsc;
}
@ -429,11 +391,11 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
{
case TUSB_XFER_CONTROL:
case TUSB_XFER_BULK:
list_head = (ehci_link_t*) qhd_async_head(rhport);
list_head = (ehci_link_t*) list_get_async_head(rhport);
break;
case TUSB_XFER_INTERRUPT:
list_head = get_period_head(rhport, p_qhd->interval_ms);
list_head = list_get_period_head(rhport, p_qhd->interval_ms);
break;
case TUSB_XFER_ISOCHRONOUS:
@ -442,10 +404,8 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
default: break;
}
TU_ASSERT(list_head);
// TODO might need to disable async/period list
list_insert(list_head, (ehci_link_t*) p_qhd, EHCI_QTYPE_QHD);
hcd_dcache_clean(p_qhd, sizeof(ehci_qhd_t));
@ -479,20 +439,17 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
ehci_qhd_t* qhd;
ehci_qhd_t* qhd = qhd_get_from_addr(dev_addr, ep_addr);
ehci_qtd_t* qtd;
if (epnum == 0) {
qhd = qhd_control(dev_addr);
qtd = qtd_control(dev_addr);
qtd_init(qtd, buffer, buflen);
// first data toggle is always 1 (data & setup stage)
qtd->data_toggle = 1;
qtd->pid = dir ? EHCI_PID_IN : EHCI_PID_OUT;
} else {
qhd = qhd_get_from_addr(dev_addr, ep_addr);
qtd = qtd_find_free();
TU_ASSERT(qtd);
@ -513,6 +470,38 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
return true;
}
bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
(void) rhport;
// TODO ISO not supported yet
ehci_qhd_t* qhd = qhd_get_from_addr(dev_addr, ep_addr);
ehci_qtd_t * volatile qtd = qhd->attached_qtd;
TU_VERIFY(qtd != NULL); // no queued transfer
hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t));
TU_VERIFY(qtd->active); // transfer is already complete
// HC is still processing, disable HC list schedule before making changes
bool const is_period = (qhd->interval_ms > 0);
ehci_disable_schedule(ehci_data.regs, is_period);
// check active bit again just in case HC has just processed the TD
bool const still_active = qtd->active;
if (still_active) {
// remove TD from QH overlay
qhd->qtd_overlay.next.terminate = 1;
hcd_dcache_clean(qhd, sizeof(ehci_qhd_t));
// remove TD from QH software list
qhd_remove_qtd(qhd);
}
ehci_enable_schedule(ehci_data.regs, is_period);
return still_active; // true if removed an active transfer
}
bool hcd_edpt_clear_stall(uint8_t daddr, uint8_t ep_addr)
{
ehci_qhd_t *qhd = qhd_get_from_addr(daddr, ep_addr);
@ -560,8 +549,12 @@ void port_connect_status_change_isr(uint8_t rhport)
TU_ATTR_ALWAYS_INLINE static inline
void qhd_xfer_complete_isr(ehci_qhd_t * qhd) {
// examine TD attached to queue head
ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile) qhd->attached_qtd;
if (qtd == NULL) return; // no TD attached
ehci_qtd_t * volatile qtd = qhd->attached_qtd;
if (qtd == NULL) {
return; // no TD attached
}
hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t));
// TD is still active, no need to process
@ -578,9 +571,7 @@ void qhd_xfer_complete_isr(ehci_qhd_t * qhd) {
}
// remove and free TD before invoking callback
qhd->attached_qtd = NULL;
qhd->attached_buffer = 0;
qtd->used = 0; // free QTD
qhd_remove_qtd(qhd);
// notify usbh
uint8_t const ep_addr = tu_edpt_addr(qhd->ep_number, dir);
@ -607,8 +598,8 @@ void async_list_xfer_complete_isr(ehci_qhd_t * const async_head)
TU_ATTR_ALWAYS_INLINE static inline
void period_list_xfer_complete_isr(uint8_t rhport, uint32_t interval_ms)
{
uint32_t const period_1ms_addr = (uint32_t) get_period_head(rhport, 1u);
ehci_link_t next_link = * get_period_head(rhport, interval_ms);
uint32_t const period_1ms_addr = (uint32_t) list_get_period_head(rhport, 1u);
ehci_link_t next_link = *list_get_period_head(rhport, interval_ms);
while (!next_link.terminate) {
if (interval_ms > 1 && period_1ms_addr == tu_align32(next_link.address)) {
@ -683,9 +674,7 @@ void qhd_xfer_error_isr(ehci_qhd_t * qhd)
}
// remove and free TD before invoking callback
qhd->attached_qtd = NULL;
qhd->attached_buffer = 0;
qtd->used = 0; // free QTD
qhd_remove_qtd(qhd);
if (0 == qhd->ep_number ) {
// control cannot be halted
@ -706,7 +695,7 @@ TU_ATTR_ALWAYS_INLINE static inline
void xfer_error_isr(uint8_t rhport)
{
//------------- async list -------------//
ehci_qhd_t * const async_head = qhd_async_head(rhport);
ehci_qhd_t * const async_head = list_get_async_head(rhport);
ehci_qhd_t *p_qhd = async_head;
do
{
@ -716,10 +705,10 @@ void xfer_error_isr(uint8_t rhport)
}while(p_qhd != async_head); // async list traversal, stop if loop around
//------------- TODO refractor period list -------------//
uint32_t const period_1ms_addr = (uint32_t) get_period_head(rhport, 1u);
uint32_t const period_1ms_addr = (uint32_t) list_get_period_head(rhport, 1u);
for (uint32_t interval_ms=1; interval_ms <= FRAMELIST_SIZE; interval_ms *= 2)
{
ehci_link_t next_item = * get_period_head(rhport, interval_ms);
ehci_link_t next_item = *list_get_period_head(rhport, interval_ms);
// TODO abstract max loop guard for period
while( !next_item.terminate &&
@ -786,7 +775,7 @@ void hcd_int_handler(uint8_t rhport)
//------------- some QTD/SITD/ITD with IOC set is completed -------------//
if (int_status & EHCI_INT_MASK_NXP_ASYNC) {
async_list_xfer_complete_isr(qhd_async_head(rhport));
async_list_xfer_complete_isr(list_get_async_head(rhport));
regs->status = EHCI_INT_MASK_NXP_ASYNC; // Acknowledge
}
@ -813,35 +802,103 @@ void hcd_int_handler(uint8_t rhport)
}
//--------------------------------------------------------------------+
// HELPER
// List Managing Helper
//--------------------------------------------------------------------+
// Get head of periodic list
TU_ATTR_ALWAYS_INLINE static inline ehci_link_t* list_get_period_head(uint8_t rhport, uint32_t interval_ms) {
(void) rhport;
return (ehci_link_t*) &ehci_data.period_head_arr[ tu_log2( tu_min32(FRAMELIST_SIZE, interval_ms) ) ];
}
//------------- queue head helper -------------//
static inline ehci_qhd_t* qhd_find_free (void)
// Get head of async list
TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* list_get_async_head(uint8_t rhport) {
(void) rhport;
return qhd_control(0); // control qhd of dev0 is used as async head
}
TU_ATTR_ALWAYS_INLINE static inline ehci_link_t* list_next(ehci_link_t const *p_link) {
return (ehci_link_t*) tu_align32(p_link->address);
}
TU_ATTR_ALWAYS_INLINE static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type)
{
for (uint32_t i=0; i<QHD_MAX; i++)
{
new->address = current->address;
current->address = ((uint32_t) new) | (new_type << 1);
}
// Remove all queue head belong to this device address
static void list_remove_qhd_by_daddr(ehci_link_t* list_head, uint8_t dev_addr) {
ehci_link_t* prev = list_head;
while (prev && !prev->terminate) {
ehci_qhd_t* qhd = (ehci_qhd_t*) (uintptr_t) list_next(prev);
// done if loop back to head
if ( (uintptr_t) qhd == (uintptr_t) list_head) {
break;
}
if ( qhd->dev_addr == dev_addr ) {
// TODO deactivate all TD, wait for QHD to inactive before removal
prev->address = qhd->next.address;
// EHCI 4.8.2 link the removed qhd's next to async head (which always reachable by Host Controller)
qhd->next.address = ((uint32_t) list_head) | (EHCI_QTYPE_QHD << 1);
if ( qhd->int_smask )
{
// period list queue element is guarantee to be free in the next frame (1 ms)
qhd->used = 0;
}else
{
// async list use async advance handshake
// mark as removing, will completely re-usable when async advance isr occurs
qhd->removing = 1;
}
hcd_dcache_clean(qhd, sizeof(ehci_qhd_t));
hcd_dcache_clean(prev, sizeof(ehci_qhd_t));
}else {
prev = list_next(prev);
}
}
}
//--------------------------------------------------------------------+
// Queue Header helper
//--------------------------------------------------------------------+
// Get queue head for control transfer (always available)
TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* qhd_control(uint8_t dev_addr) {
return &ehci_data.control[dev_addr].qhd;
}
// Find a free queue head
TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t *qhd_find_free(void) {
for ( uint32_t i = 0; i < QHD_MAX; i++ ) {
if ( !ehci_data.qhd_pool[i].used ) return &ehci_data.qhd_pool[i];
}
return NULL;
}
static inline ehci_qhd_t* qhd_next(ehci_qhd_t const * p_qhd)
{
return (ehci_qhd_t*) tu_align32(p_qhd->next.address);
// Next queue head link
TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t *qhd_next(ehci_qhd_t const *p_qhd) {
return (ehci_qhd_t *) tu_align32(p_qhd->next.address);
}
static inline ehci_qhd_t* qhd_get_from_addr(uint8_t dev_addr, uint8_t ep_addr)
{
ehci_qhd_t* qhd_pool = ehci_data.qhd_pool;
// Get queue head from device + endpoint address
static ehci_qhd_t *qhd_get_from_addr(uint8_t dev_addr, uint8_t ep_addr) {
if ( 0 == tu_edpt_number(ep_addr) ) {
return qhd_control(dev_addr);
}
for(uint32_t i=0; i<QHD_MAX; i++)
{
ehci_qhd_t *qhd_pool = ehci_data.qhd_pool;
for ( uint32_t i = 0; i < QHD_MAX; i++ ) {
if ( (qhd_pool[i].dev_addr == dev_addr) &&
ep_addr == tu_edpt_addr(qhd_pool[i].ep_number, qhd_pool[i].pid) )
{
ep_addr == tu_edpt_addr(qhd_pool[i].ep_number, qhd_pool[i].pid) ) {
return &qhd_pool[i];
}
}
@ -849,6 +906,7 @@ static inline ehci_qhd_t* qhd_get_from_addr(uint8_t dev_addr, uint8_t ep_addr)
return NULL;
}
// Init queue head with endpoint descriptor
static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
{
// address 0 is used as async head, which always on the list --> cannot be cleared (ehci halted otherwise)
@ -923,6 +981,7 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c
}
}
// Attach a TD to queue head
static void qhd_attach_qtd(ehci_qhd_t *qhd, ehci_qtd_t *qtd) {
qhd->attached_qtd = qtd;
qhd->attached_buffer = qtd->buffer[0];
@ -934,9 +993,26 @@ static void qhd_attach_qtd(ehci_qhd_t *qhd, ehci_qtd_t *qtd) {
hcd_dcache_clean_invalidate(qhd, sizeof(ehci_qhd_t));
}
// Remove an attached TD from queue head
static void qhd_remove_qtd(ehci_qhd_t *qhd) {
ehci_qtd_t * volatile qtd = qhd->attached_qtd;
//------------- TD helper -------------//
static inline ehci_qtd_t *qtd_find_free(void) {
qhd->attached_qtd = NULL;
qhd->attached_buffer = 0;
qtd->used = 0; // free QTD
}
//--------------------------------------------------------------------+
// Queue TD helper
//--------------------------------------------------------------------+
// Get TD for control transfer (always available)
TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t* qtd_control(uint8_t dev_addr) {
return &ehci_data.control[dev_addr].qtd;
}
TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t *qtd_find_free(void) {
for (uint32_t i = 0; i < QTD_MAX; i++) {
if (!ehci_data.qtd_pool[i].used) return &ehci_data.qtd_pool[i];
}
@ -964,18 +1040,4 @@ static void qtd_init(ehci_qtd_t* qtd, void const* buffer, uint16_t total_bytes)
}
}
//------------- List Managing Helper -------------//
// insert at head
static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type)
{
new->address = current->address;
current->address = ((uint32_t) new) | (new_type << 1);
}
static inline ehci_link_t* list_next(ehci_link_t const *p_link)
{
return (ehci_link_t*) tu_align32(p_link->address);
}
#endif

View File

@ -289,12 +289,17 @@ enum {
};
enum {
EHCI_USBCMD_POS_RUN_STOP = 0,
EHCI_USBCMD_POS_FRAMELIST_SIZE = 2,
EHCI_USBCMD_POS_PERIOD_ENABLE = 4,
EHCI_USBCMD_POS_ASYNC_ENABLE = 5,
EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB = 15,
EHCI_USBCMD_POS_INTERRUPT_THRESHOLD = 16
EHCI_USBCMD_FRAMELIST_SIZE_SHIFT = 2, // [2..3]
EHCI_USBCMD_CHIPIDEA_FRAMELIST_SIZE_MSB_SHIFT = 15,
EHCI_USBCMD_INTERRUPT_THRESHOLD_SHIFT = 16
};
enum {
EHCI_USBCMD_RUN_STOP = TU_BIT(0), // [0..0] 1 = Run, 0 = Stop
EHCI_USBCMD_HCRESET = TU_BIT(1), // [1..1] SW write 1 to reset HC, clear by HC when complete
EHCI_USBCMD_PERIOD_SCHEDULE_ENABLE = TU_BIT(4), // [4..4] Enable periodic schedule
EHCI_USBCMD_ASYNC_SCHEDULE_ENABLE = TU_BIT(5), // [5..5] Enable async schedule
EHCI_USBCMD_INTR_ON_ASYNC_ADVANCE_DOORBELL = TU_BIT(6), // [6..6] Tell HC to interrupt next time it advances async list. Clear by HC
};
enum {
@ -306,7 +311,7 @@ enum {
EHCI_PORTSC_MASK_FORCE_RESUME = TU_BIT(6),
EHCI_PORTSC_MASK_PORT_SUSPEND = TU_BIT(7),
EHCI_PORTSC_MASK_PORT_RESET = TU_BIT(8),
ECHI_PORTSC_MASK_PORT_POWER = TU_BIT(12),
EHCI_PORTSC_MASK_PORT_POWER = TU_BIT(12),
EHCI_PORTSC_MASK_W1C =
EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE |

View File

@ -822,6 +822,14 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b
return ret;
}
bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
(void) rhport;
(void) dev_addr;
(void) ep_addr;
// TODO not implemented yet
return false;
}
// clear stall, data toggle is also reset to DATA0
bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
{

View File

@ -562,6 +562,14 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
return true;
}
bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
(void) rhport;
(void) dev_addr;
(void) ep_addr;
// TODO not implemented yet
return false;
}
bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
{
if (!tu_edpt_number(ep_addr)) return true;

View File

@ -548,6 +548,14 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
return true;
}
bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
(void) rhport;
(void) dev_addr;
(void) ep_addr;
// TODO not implemented yet
return false;
}
bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
{
ohci_ed_t * const p_ed = ed_from_addr(dev_addr, ep_addr);

View File

@ -138,6 +138,11 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
return pio_usb_host_endpoint_transfer(pio_rhport, dev_addr, ep_addr, buffer, buflen);
}
bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
uint8_t const pio_rhport = RHPORT_PIO(rhport);
return pio_usb_host_endpoint_abort_transfer(pio_rhport, dev_addr, ep_addr);
}
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
{
uint8_t const pio_rhport = RHPORT_PIO(rhport);

View File

@ -576,6 +576,14 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
return true;
}
bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
(void) rhport;
(void) dev_addr;
(void) ep_addr;
// TODO not implemented yet
return false;
}
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
{
(void) rhport;

View File

@ -710,6 +710,14 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b
return r;
}
bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
(void) rhport;
(void) dev_addr;
(void) ep_addr;
// TODO not implemented yet
return false;
}
bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
{
uint16_t volatile *ctr = addr_to_pipectr(dev_addr, ep_addr);

View File

@ -54,7 +54,7 @@ deps_optional = {
'950819b7de9b32f92c3edf396bc5ffb8d66e7009',
'kinetis_k32l2 kinetis_kl lpc51 lpc54 lpc55 mcx imxrt'],
'hw/mcu/raspberry_pi/Pico-PIO-USB': ['https://github.com/sekigon-gonnoc/Pico-PIO-USB.git',
'58879cfa0eca5725d8db6443ec17f8896a321042',
'd00a10a8c425d0d40f81b87169102944b01f3bb3',
'rp2040'],
'hw/mcu/renesas/fsp': ['https://github.com/renesas/fsp.git',
'8dc14709f2a6518b43f71efad70d900b7718d9f1',