Skip to content

Commit c122e9d

Browse files
committed
implement hcd_edpt_abort_xfer() for EHCI, also move thing around a bit
1 parent 14c98dd commit c122e9d

File tree

6 files changed

+147
-86
lines changed

6 files changed

+147
-86
lines changed

src/common/tusb_common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353
#define U32_TO_U8S_LE(_u32) TU_U32_BYTE0(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE3(_u32)
5454

5555
#define TU_BIT(n) (1UL << (n))
56+
57+
// Generate a mask with bit from high (31) to low (0) set, e.g TU_GENMASK(3, 0) = 0b1111
5658
#define TU_GENMASK(h, l) ( (UINT32_MAX << (l)) & (UINT32_MAX >> (31 - (h))) )
5759

5860
//--------------------------------------------------------------------+

src/host/hcd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
172172
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen);
173173

174174
// Abort a queued transfer. Note: it can only abort transfer that has not been started
175+
// Return true if a queued transfer is aborted, false if there is no transfer to abort
175176
bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr);
176177

177178
// Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked

src/host/usbh.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -718,9 +718,15 @@ bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr) {
718718
uint8_t const dir = tu_edpt_dir(ep_addr);
719719

720720
// skip if not busy
721-
if (!dev->ep_status[epnum][dir].busy) return true;
721+
TU_VERIFY(dev->ep_status[epnum][dir].busy);
722722

723-
return hcd_edpt_abort_xfer(dev->rhport, daddr, ep_addr);
723+
bool const ret = hcd_edpt_abort_xfer(dev->rhport, daddr, ep_addr);
724+
if (ret) {
725+
// mark as ready if transfer is aborted
726+
dev->ep_status[epnum][dir].busy = false;
727+
}
728+
729+
return ret;
724730
}
725731

726732
//--------------------------------------------------------------------+

src/host/usbh.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ bool tuh_edpt_xfer(tuh_xfer_t* xfer);
176176
bool tuh_edpt_open(uint8_t daddr, tusb_desc_endpoint_t const * desc_ep);
177177

178178
// Abort a queued transfer. Note: it can only abort transfer that has not been started
179+
// Return true if a queued transfer is aborted, false if there is no transfer to abort
179180
bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr);
180181

181182
// Set Configuration (control transfer)

src/portable/ehci/ehci.c

Lines changed: 123 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@
4848
#ifdef TUP_USBIP_CHIPIDEA_HS
4949
// NXP Transdimension: 8 elements
5050
#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))
5353
#else
5454
// STD EHCI: 256 elements
5555
#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
132132
TU_ATTR_WEAK void hcd_dcache_invalidate(void const* addr, uint32_t data_size) { (void) addr; (void) data_size; }
133133
TU_ATTR_WEAK void hcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) { (void) addr; (void) data_size; }
134134

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-
138135
TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* qhd_control(uint8_t dev_addr);
139136
TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* qhd_next (ehci_qhd_t const * p_qhd);
140137
TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* qhd_find_free (void);
141138
static ehci_qhd_t* qhd_get_from_addr (uint8_t dev_addr, uint8_t ep_addr);
142139
static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc);
143140
static void qhd_attach_qtd(ehci_qhd_t *qhd, ehci_qtd_t *qtd);
141+
static void qhd_remove_qtd(ehci_qhd_t *qhd);
144142

145143
TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t* qtd_control(uint8_t dev_addr);
146144
TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t* qtd_find_free (void);
147145
static void qtd_init (ehci_qtd_t* qtd, void const* buffer, uint16_t total_bytes);
148146

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);
151151
static void list_remove_qhd_by_daddr(ehci_link_t* list_head, uint8_t dev_addr);
152152

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+
153175
//--------------------------------------------------------------------+
154176
// HCD API
155177
//--------------------------------------------------------------------+
@@ -204,43 +226,6 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport)
204226
return (tusb_speed_t) ehci_data.regs->portsc_bm.nxp_port_speed; // NXP specific port speed
205227
}
206228

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-
244229
// Close all opened endpoint belong to this device
245230
void hcd_device_close(uint8_t rhport, uint8_t daddr)
246231
{
@@ -347,8 +332,7 @@ bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
347332
regs->nxp_tt_control = 0;
348333

349334
//------------- 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 |
352336
FRAMELIST_SIZE_USBCMD_VALUE;
353337

354338
//------------- ConfigFlag Register (skip) -------------//
@@ -358,7 +342,7 @@ bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
358342
if (ehci_data.cap_regs->hcsparams_bm.port_power_control) {
359343
// mask out all change bits since they are Write 1 to clear
360344
uint32_t portsc = (regs->portsc & ~EHCI_PORTSC_MASK_W1C);
361-
portsc |= ECHI_PORTSC_MASK_PORT_POWER;
345+
portsc |= EHCI_PORTSC_MASK_PORT_POWER;
362346

363347
regs->portsc = portsc;
364348
}
@@ -455,20 +439,17 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
455439
uint8_t const epnum = tu_edpt_number(ep_addr);
456440
uint8_t const dir = tu_edpt_dir(ep_addr);
457441

458-
ehci_qhd_t* qhd;
442+
ehci_qhd_t* qhd = qhd_get_from_addr(dev_addr, ep_addr);
459443
ehci_qtd_t* qtd;
460444

461445
if (epnum == 0) {
462-
qhd = qhd_control(dev_addr);
463446
qtd = qtd_control(dev_addr);
464-
465447
qtd_init(qtd, buffer, buflen);
466448

467449
// first data toggle is always 1 (data & setup stage)
468450
qtd->data_toggle = 1;
469451
qtd->pid = dir ? EHCI_PID_IN : EHCI_PID_OUT;
470452
} else {
471-
qhd = qhd_get_from_addr(dev_addr, ep_addr);
472453
qtd = qtd_find_free();
473454
TU_ASSERT(qtd);
474455

@@ -491,20 +472,34 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
491472

492473
bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
493474
(void) rhport;
494-
(void) dev_addr;
495-
(void) ep_addr;
496475

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+
}
498499

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
508503
}
509504

510505
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)
554549
TU_ATTR_ALWAYS_INLINE static inline
555550
void qhd_xfer_complete_isr(ehci_qhd_t * qhd) {
556551
// 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+
559558
hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t));
560559

561560
// TD is still active, no need to process
@@ -572,9 +571,7 @@ void qhd_xfer_complete_isr(ehci_qhd_t * qhd) {
572571
}
573572

574573
// 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);
578575

579576
// notify usbh
580577
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)
677674
}
678675

679676
// 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);
683678

684679
if (0 == qhd->ep_number ) {
685680
// control cannot be halted
@@ -822,18 +817,55 @@ TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* list_get_async_head(uint8_t rhpo
822817
return qhd_control(0); // control qhd of dev0 is used as async head
823818
}
824819

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)
827825
{
828826
new->address = current->address;
829827
current->address = ((uint32_t) new) | (new_type << 1);
830828
}
831829

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+
}
835866
}
836867

868+
837869
//--------------------------------------------------------------------+
838870
// Queue Header helper
839871
//--------------------------------------------------------------------+
@@ -856,8 +888,12 @@ TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t *qhd_next(ehci_qhd_t const *p_qhd
856888
return (ehci_qhd_t *) tu_align32(p_qhd->next.address);
857889
}
858890

859-
// Get queue head from address
891+
// Get queue head from device + endpoint address
860892
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+
861897
ehci_qhd_t *qhd_pool = ehci_data.qhd_pool;
862898

863899
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) {
957993
hcd_dcache_clean_invalidate(qhd, sizeof(ehci_qhd_t));
958994
}
959995

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+
9601006
//--------------------------------------------------------------------+
9611007
// Queue TD helper
9621008
//--------------------------------------------------------------------+

src/portable/ehci/ehci.h

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -289,12 +289,17 @@ enum {
289289
};
290290

291291
enum {
292-
EHCI_USBCMD_POS_RUN_STOP = 0,
293-
EHCI_USBCMD_POS_FRAMELIST_SIZE = 2,
294-
EHCI_USBCMD_POS_PERIOD_ENABLE = 4,
295-
EHCI_USBCMD_POS_ASYNC_ENABLE = 5,
296-
EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB = 15,
297-
EHCI_USBCMD_POS_INTERRUPT_THRESHOLD = 16
292+
EHCI_USBCMD_FRAMELIST_SIZE_SHIFT = 2, // [2..3]
293+
EHCI_USBCMD_CHIPIDEA_FRAMELIST_SIZE_MSB_SHIFT = 15,
294+
EHCI_USBCMD_INTERRUPT_THRESHOLD_SHIFT = 16
295+
};
296+
297+
enum {
298+
EHCI_USBCMD_RUN_STOP = TU_BIT(0), // [0..0] 1 = Run, 0 = Stop
299+
EHCI_USBCMD_HCRESET = TU_BIT(1), // [1..1] SW write 1 to reset HC, clear by HC when complete
300+
EHCI_USBCMD_PERIOD_SCHEDULE_ENABLE = TU_BIT(4), // [4..4] Enable periodic schedule
301+
EHCI_USBCMD_ASYNC_SCHEDULE_ENABLE = TU_BIT(5), // [5..5] Enable async schedule
302+
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
298303
};
299304

300305
enum {
@@ -306,7 +311,7 @@ enum {
306311
EHCI_PORTSC_MASK_FORCE_RESUME = TU_BIT(6),
307312
EHCI_PORTSC_MASK_PORT_SUSPEND = TU_BIT(7),
308313
EHCI_PORTSC_MASK_PORT_RESET = TU_BIT(8),
309-
ECHI_PORTSC_MASK_PORT_POWER = TU_BIT(12),
314+
EHCI_PORTSC_MASK_PORT_POWER = TU_BIT(12),
310315

311316
EHCI_PORTSC_MASK_W1C =
312317
EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE |

0 commit comments

Comments
 (0)