Support Isochronous transfer additionally

Dependents:   USBHostC270_example_GR-PEACH USBHostDac_example USBHostDac_Audio_in_out

Fork of USBHost_custom by Renesas

Files at this revision

API Documentation at this revision

Comitter:
HinoNaka
Date:
Fri Apr 21 07:23:33 2017 +0000
Parent:
40:cdc0d2ab4678
Commit message:
Support Isochronous transfer

Changed in this revision

USBHost/TARGET_RENESAS/TARGET_RZ_A1H/usb_host_setting.h Show annotated file Show diff for this revision Revisions of this file
USBHost/USBHALHost.h Show annotated file Show diff for this revision Revisions of this file
USBHost/USBHALHost_LPC17.cpp Show annotated file Show diff for this revision Revisions of this file
USBHost/USBHALHost_RZ_A1.cpp Show annotated file Show diff for this revision Revisions of this file
USBHost/USBHost.cpp Show annotated file Show diff for this revision Revisions of this file
USBHost/USBHost.h Show annotated file Show diff for this revision Revisions of this file
USBisochronous/USBIsochronous.cpp Show annotated file Show diff for this revision Revisions of this file
USBisochronous/USBIsochronous.h Show annotated file Show diff for this revision Revisions of this file
diff -r cdc0d2ab4678 -r 5c3ebf7372ee USBHost/TARGET_RENESAS/TARGET_RZ_A1H/usb_host_setting.h
--- a/USBHost/TARGET_RENESAS/TARGET_RZ_A1H/usb_host_setting.h	Fri Mar 17 01:29:52 2017 +0000
+++ b/USBHost/TARGET_RENESAS/TARGET_RZ_A1H/usb_host_setting.h	Fri Apr 21 07:23:33 2017 +0000
@@ -32,7 +32,7 @@
 #define USB_HOST_HISPEED                      1
 
 #define INT_TRANS_MAX_NUM                     4    /* min:1 max:4 */
-#define ISO_TRANS_MAX_NUM                     0    /* min:0 max:2 */
+#define ISO_TRANS_MAX_NUM                     2    /* min:0 max:2 */
 
 #if (USB_HOST_CH == 0)
 #include "usb0_host.h"
diff -r cdc0d2ab4678 -r 5c3ebf7372ee USBHost/USBHALHost.h
--- a/USBHost/USBHALHost.h	Fri Mar 17 01:29:52 2017 +0000
+++ b/USBHost/USBHALHost.h	Fri Apr 21 07:23:33 2017 +0000
@@ -135,6 +135,9 @@
     * @returns the address of the new TD
     */
     volatile uint8_t * getTD();
+#if(1) /* Isochronous */
+    bool isTD(volatile uint8_t* td);
+#endif
 
     /**
     * Release a previous memory section reserved for an ED
diff -r cdc0d2ab4678 -r 5c3ebf7372ee USBHost/USBHALHost_LPC17.cpp
--- a/USBHost/USBHALHost_LPC17.cpp	Fri Mar 17 01:29:52 2017 +0000
+++ b/USBHost/USBHALHost_LPC17.cpp	Fri Apr 21 07:23:33 2017 +0000
@@ -234,6 +234,14 @@
     return NULL; //Could not alloc TD
 }
 
+#if(1) /* Isochronous */
+bool USBHALHost::isTD(volatile uint8_t* td) {
+    if (td >= usb_tdBuf && td < (usb_tdBuf+TD_SIZE*MAX_TD)) {
+        return true;
+    }
+    return false;
+}
+#endif
 
 void USBHALHost::freeED(volatile uint8_t * ed) {
     int i;
diff -r cdc0d2ab4678 -r 5c3ebf7372ee USBHost/USBHALHost_RZ_A1.cpp
--- a/USBHost/USBHALHost_RZ_A1.cpp	Fri Mar 17 01:29:52 2017 +0000
+++ b/USBHost/USBHALHost_RZ_A1.cpp	Fri Apr 21 07:23:33 2017 +0000
@@ -204,6 +204,14 @@
     return NULL; //Could not alloc TD
 }
 
+#if(1) /* Isochronous */
+bool USBHALHost::isTD(volatile uint8_t* td) {
+    if (td >= usb_tdBuf && td < (usb_tdBuf+TD_SIZE*MAX_TD)) {
+        return true;
+    }
+    return false;
+}
+#endif
 
 void USBHALHost::freeED(volatile uint8_t * ed) {
     int i;
diff -r cdc0d2ab4678 -r 5c3ebf7372ee USBHost/USBHost.cpp
--- a/USBHost/USBHost.cpp	Fri Mar 17 01:29:52 2017 +0000
+++ b/USBHost/USBHost.cpp	Fri Apr 21 07:23:33 2017 +0000
@@ -17,6 +17,9 @@
 
 #include "USBHost.h"
 #include "USBHostHub.h"
+#if(1) /* Isochronous */
+#include "USBIsochronous.h"
+#endif
 
 USBHost * USBHost::instHost = NULL;
 
@@ -311,6 +314,16 @@
     while(tdList != NULL) {
         volatile HCTD* td = tdList;
         tdList = (volatile HCTD*)td->nextTD; //Dequeue element now as it could be modified below
+#if(1) /* Isochronous */
+        if (!isTD((uint8_t*)td)) { // ITD?
+            HCITD* itd = (HCITD*)td;
+            IsochronousEp* ep = itd->ep;
+            if (ep) {
+                ep->irqWdhHandler(itd);
+            }
+            continue;
+        }
+#endif
         if (td->ep != NULL) {
             USBEndpoint * ep = (USBEndpoint *)(td->ep);
 
@@ -912,6 +925,11 @@
     bool parsing_intf = false;
     uint8_t current_intf = 0;
 
+#if(1) /* Isochronous */
+    lenCnfdDescr = len;
+    indexCnfdDescr = 0;
+#endif
+    
     while (index < len) {
         len_desc = conf_descr[index];
         id = conf_descr[index+1];
@@ -966,6 +984,9 @@
                 break;
         }
         index += len_desc;
+#if(1) /* Isochronous */
+       indexCnfdDescr = index;
+#endif
     }
 }
 
diff -r cdc0d2ab4678 -r 5c3ebf7372ee USBHost/USBHost.h
--- a/USBHost/USBHost.h	Fri Mar 17 01:29:52 2017 +0000
+++ b/USBHost/USBHost.h	Fri Apr 21 07:23:33 2017 +0000
@@ -127,6 +127,16 @@
     */
     USB_TYPE enumerate(USBDeviceConnected * dev, IUSBEnumerator* pEnumerator);
 
+#if(1) /* Isochronous */
+    inline uint8_t * getConfDescrCurPtr() {
+        return &data[indexCnfdDescr];
+    };
+
+    inline uint16_t getConfDescrRestLen() {
+        return lenCnfdDescr - indexCnfdDescr;
+    };
+#endif
+
     /**
     * reset a specific device
     *
@@ -235,6 +245,10 @@
     USBHost();
     static USBHost * instHost;
     uint16_t  lenReportDescr;
+#if(1) /* Isochronous */
+    uint16_t  lenCnfdDescr;
+    uint16_t  indexCnfdDescr;
+#endif
 
     // endpoints
     void unqueueEndpoint(USBEndpoint * ep) ;
@@ -284,7 +298,11 @@
     Mutex td_mutex;
 
     // buffer for conf descriptor
+#if(1) /* Isochronous */
+    uint8_t data[1024];
+#else
     uint8_t data[415];
+#endif
 
     /**
     * Add a transfer on the TD linked list associated to an ED
diff -r cdc0d2ab4678 -r 5c3ebf7372ee USBisochronous/USBIsochronous.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBisochronous/USBIsochronous.cpp	Fri Apr 21 07:23:33 2017 +0000
@@ -0,0 +1,226 @@
+// USBIsochronous.cpp
+#include "USBHostConf.h"
+#include "USBHost.h"
+#include "USBIsochronous.h"
+#if defined(TARGET_RZ_A1H)
+#include "ohci_wrapp_RZ_A1.h"
+#endif
+
+#define  OR_CONTROL_PLE                 0x00000004
+#define  OR_CONTROL_IE                  0x00000008
+
+//#define ISO_DEBUG 1
+#ifdef ISO_DEBUG
+#define ISO_DBG(x, ...) std::printf("[%s:%d]"x"\r\n", __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
+#else
+#define ISO_DBG(...)  while(0);
+#endif
+
+#define TEST_ASSERT(A) while(!(A)){fprintf(stderr,"\n\n%s@%d %s ASSERT!\n\n",__PRETTY_FUNCTION__,__LINE__,#A);exit(1);};
+
+HCITD::HCITD(IsochronousEp* obj, uint16_t FrameNumber, int FrameCount, uint16_t PacketSize) {
+    Control = 0xe0000000           | // CC ConditionCode NOT ACCESSED
+             ((FrameCount-1) << 24)| // FC FrameCount
+                  TD_DELAY_INT(0)  | // DI DelayInterrupt
+                 FrameNumber;        // SF StartingFrame
+    BufferPage0 = const_cast<uint8_t*>(buf);
+    BufferEnd = const_cast<uint8_t*>(buf) + PacketSize * FrameCount - 1;
+    Next = NULL; 
+    ep = obj;
+    uint32_t addr = reinterpret_cast<uint32_t>(buf);
+    for(int i = 0; i < FrameCount; i++) {
+        uint16_t offset = addr & 0x0fff;
+        if ((addr&0xfffff000) == (reinterpret_cast<uint32_t>(BufferEnd)&0xfffff000)) {
+            offset |= 0x1000;
+        }
+        OffsetPSW[i] = 0xe000|offset;
+        addr += PacketSize;
+    }
+}
+
+void IsochronousEp::init(int addr, uint8_t ep, uint16_t size, uint8_t frameCount, uint8_t queueLimit) {
+    //ISO_DBG("%p FA:%d EP:%02X MPS:%d\n", this, addr, ep, size);
+    TEST_ASSERT(addr >= 1);    
+    TEST_ASSERT(size >= 8 && size <= 1023);
+    m_pED  = new _HCED(addr, ep, size);
+    TEST_ASSERT(m_pED);
+
+    m_pED->setFormat(); // F Format ITD
+
+    m_PacketSize = size;
+    TEST_ASSERT(frameCount >= 1 && frameCount <= 8);
+    m_FrameCount = frameCount;
+    TEST_ASSERT(queueLimit >= 1 && queueLimit <= HCITD_QUEUE_SIZE);
+    m_itd_queue_limit = queueLimit;
+
+    m_itd_queue_count = 0;
+    reset();
+    HCITD* itd = new_HCITD(this);
+    m_pED->init_queue<HCITD>(itd); 
+    TEST_ASSERT(itd);
+    if (itd == NULL) {
+        return;
+    }
+#if defined(TARGET_LPC1768)
+    _HCCA* hcca = reinterpret_cast<_HCCA*>(LPC_USB->HcHCCA);
+#elif defined(TARGET_RZ_A1H)
+    _HCCA* hcca = reinterpret_cast<_HCCA*>(ohciwrapp_reg_r(OHCI_REG_HCCA));
+#endif
+    TEST_ASSERT(hcca);
+    if (hcca == NULL) {
+        return;
+    }
+    hcca->enqueue(m_pED);
+}
+
+void IsochronousEp::reset(int delay_ms)
+{
+#if defined(TARGET_LPC1768)
+    m_FrameNumber = LPC_USB->HcFmNumber + delay_ms;
+#elif defined(TARGET_RZ_A1H)
+    m_FrameNumber = ohciwrapp_reg_r(OHCI_REG_FMNUMBER) + delay_ms;
+#endif
+}
+
+HCITD* IsochronousEp::new_HCITD(IsochronousEp* obj) {
+    HCITD* itd = new(m_PacketSize*m_FrameCount)HCITD(obj, 0, m_FrameCount, m_PacketSize);
+    if (itd == NULL) {
+        return NULL;
+    } 
+    return itd;
+}
+
+HCITD* IsochronousEp::isochronousReceive(int timeout_ms) {
+    TEST_ASSERT(m_itd_queue_count >= 0);
+    while(m_itd_queue_count < m_itd_queue_limit) {
+        if (m_pED == NULL) {
+            ISO_DBG("m_pED is NULL");
+            break;
+        }
+        if (m_pED->Skip()) {
+            break;
+        }
+        HCITD* blank_itd = new_HCITD(this);
+        ((HCITD *)m_pED->TailTd)->SetStartingFrame(m_FrameNumber);
+        m_FrameNumber += m_FrameCount;
+        TEST_ASSERT(blank_itd);
+        if (m_pED->enqueue<HCITD>(blank_itd)) {
+            m_itd_queue_count++;
+        }
+        enable(); // Enable Periodic
+    }
+
+    HCITD* itd = get_queue_HCITD(timeout_ms);
+    if (itd) {
+        m_itd_queue_count--;
+    }
+    return itd;
+}
+
+int IsochronousEp::isochronousSend(uint8_t* buf, int len, int timeout_ms) {
+    //ISO_DBG("buf: %p, len: %d", buf, len);
+    HCITD* itd;
+
+    if (m_itd_queue_count >= m_itd_queue_limit) {
+        itd = get_queue_HCITD(timeout_ms);
+    } else {
+        itd = get_queue_HCITD(0);
+    }
+    if (itd) {
+        delete itd;
+        m_itd_queue_count--;
+        TEST_ASSERT(m_itd_queue_count >= 0);
+    }
+    TEST_ASSERT(m_itd_queue_count >= 0);
+    if(m_itd_queue_count < m_itd_queue_limit) {
+        if (m_pED == NULL) {
+            ISO_DBG("m_pED is NULL");
+            return 0;
+        }
+        if (m_pED->Skip()) {
+            return 0;
+        }
+        itd = new_HCITD(this);
+        TEST_ASSERT(itd);
+        //ISO_DBG("m_pED: %p itd: %p", m_pED, itd);
+        ((HCITD *)m_pED->TailTd)->SetStartingFrame(m_FrameNumber);
+        m_FrameNumber += m_FrameCount;
+        memcpy(const_cast<uint8_t*>(((HCITD *)m_pED->TailTd)->buf), buf, len);
+        if (m_pED->enqueue<HCITD>(itd)) {
+            m_itd_queue_count++;
+        }
+        enable(); // Enable Periodic
+        //ISO_DBG("m_itd_queue_count: %d", m_itd_queue_count);
+        return len;
+    }
+    return 0;
+}
+
+HCITD* IsochronousEp::get_queue_HCITD(int timeout_ms) {
+    osEvent evt = m_queue.get(timeout_ms);
+    if (evt.status == osEventMessage) {
+        HCITD* itd = reinterpret_cast<HCITD*>(evt.value.p);
+        TEST_ASSERT(itd);
+        return itd;
+    }
+    return NULL;
+}
+
+void IsochronousEp::enable() {
+#if defined(TARGET_LPC1768)
+    LPC_USB->HcControl |= (OR_CONTROL_PLE | OR_CONTROL_IE);
+#elif defined(TARGET_RZ_A1H)
+    uint32_t data;
+
+    data = ohciwrapp_reg_r(OHCI_REG_CONTROL) | OR_CONTROL_PLE | OR_CONTROL_IE;
+    ohciwrapp_reg_w(OHCI_REG_CONTROL, data);
+#endif
+}
+
+void IsochronousEp::disconnect() {
+    m_pED->setSkip(); // skip bit on
+    ISO_DBG("rtos-queue: %d", m_itd_queue_count);
+    int queue_count = m_itd_queue_count;
+    Timer t;
+    t.reset();
+    t.start();
+    do {
+        HCITD* itd = get_queue_HCITD(10);
+        if (itd) {
+            ISO_DBG("delete ITD:%p from rtos-queue %d ms", itd, t.read_ms());
+            delete itd;
+            queue_count--;
+            t.reset();
+        }
+    } while(t.read_ms() < 50);
+    ISO_DBG("rtos-queue: %d, %d ms", queue_count, t.read_ms());
+    TEST_ASSERT(queue_count >= 0);
+    while(1) {
+        HCITD* itd = m_pED->dequeue<HCITD>();
+        if (itd == NULL) {
+            break;
+        }
+        ISO_DBG("delete ITD:%p from ED(%p)-queue", itd, m_pED);        
+        delete itd;
+        TEST_ASSERT(queue_count > 0);
+        queue_count--;
+    }            
+    TEST_ASSERT(queue_count == 0);
+    HCITD* tail = reinterpret_cast<HCITD*>(m_pED->TailTd);
+    ISO_DBG("delete ITD:%p from ED(%p)-tail", tail, m_pED);
+    TEST_ASSERT(tail);
+    delete tail;
+    m_pED->init_queue<HCITD>(NULL);
+    
+#if defined(TARGET_LPC1768)
+    _HCCA* hcca = reinterpret_cast<_HCCA*>(LPC_USB->HcHCCA);
+#elif defined(TARGET_RZ_A1H)
+    _HCCA* hcca = reinterpret_cast<_HCCA*>(ohciwrapp_reg_r(OHCI_REG_HCCA));
+#endif
+    TEST_ASSERT(hcca);
+    hcca->dequeue(m_pED);
+    ISO_DBG("delete ED:%p", m_pED);
+    delete m_pED;
+    m_pED = NULL;
+}
+
diff -r cdc0d2ab4678 -r 5c3ebf7372ee USBisochronous/USBIsochronous.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBisochronous/USBIsochronous.h	Fri Apr 21 07:23:33 2017 +0000
@@ -0,0 +1,250 @@
+// USBIsochronous.h
+#pragma once
+#if !defined (__CC_ARM) && (!defined (_POSIX_C_SOURCE) || (_POSIX_C_SOURCE < 200112L))
+#if defined(__ICCARM__)
+#include <iar_dlmalloc.h>
+#define memalign   __iar_dlmemalign
+#else
+#include <malloc.h>
+#endif
+#endif
+
+class IsochronousEp;
+struct HCITD {    // HostController Isochronous Transfer Descriptor
+    __IO uint32_t Control;      // +0 Transfer descriptor control
+    uint8_t*  BufferPage0;      // +4 Buffer Page 0
+    HCITD* Next;                // +8 Physical pointer to next Isochronous Transfer Descriptor
+    uint8_t*  BufferEnd;        // +12 buffer End
+    __IO uint16_t OffsetPSW[8]; // +16 Offset/PSW
+    IsochronousEp* ep;          // +32 endpoint object
+    uint8_t*  buf;              // +36 buffer
+                                // +40
+    HCITD(IsochronousEp* obj, uint16_t FrameNumber, int FrameCount, uint16_t PacketSize);
+    inline void* operator new(size_t size, int buf_size) {
+        void* p;
+        struct HCITD * wk_hcitd;
+        void* q;
+#if !defined (__CC_ARM) && (!defined (_POSIX_C_SOURCE) || (_POSIX_C_SOURCE < 200112L))
+        p = memalign(0x20, size);
+        if (p != NULL) {
+            q = memalign(0x1000, buf_size);
+            if (q != NULL) {
+                wk_hcitd = (struct HCITD *)p;
+                wk_hcitd->buf = (uint8_t*)q;
+            } else {
+                free(p);
+                p = NULL;
+            }
+        }
+        return p;
+#else
+        if (posix_memalign(&p, 0x20, size) == 0) {
+            if (posix_memalign(&q, 0x1000, buf_size) == 0) {
+                wk_hcitd = (struct HCITD *)p;
+                wk_hcitd->buf = (uint8_t*)q;
+            } else {
+                free(p);
+                p = NULL;
+            }
+            return p;
+        }
+        return NULL;
+#endif
+    }
+
+    inline void operator delete(void* p) {
+        struct HCITD * wk_hcitd = (struct HCITD *)p;
+        free((void *)wk_hcitd->buf);
+        free(p);
+    }
+
+    inline uint16_t StartingFrame() {
+        return Control & 0xffff;
+    }
+
+    inline void SetStartingFrame(uint16_t FrameNumber) {
+        Control = (Control & 0xffff0000) | FrameNumber;
+    }
+
+    inline uint8_t FrameCount() {
+        return ((Control>>24)&7)+1;
+    }    
+
+    inline uint8_t ConditionCode() {
+        return Control>>28;
+    }
+};
+
+struct _HCED {    // HostController EndPoint Descriptor
+    __IO uint32_t Control; // +0 Endpoint descriptor control
+    HCTD* TailTd;          // +4 Physical address of tail in Transfer descriptor list
+    __IO HCTD* HeadTd;     // +8 Physcial address of head in Transfer descriptor list
+    _HCED* Next;           // +12 Physical address of next Endpoint descriptor
+                           // +16
+    _HCED(int addr, uint8_t ep, uint16_t size, int lowSpeed = 0) {
+        Control =  addr            | /* USB address */
+        ((ep & 0x7F) << 7)         | /* Endpoint address */
+        (ep!=0?(((ep&0x80)?2:1) << 11):0)| /* direction : Out = 1, 2 = In */
+        ((lowSpeed?1:0) << 13)     | /* speed full=0 low=1 */
+        (size << 16);                /* MaxPkt Size */
+        Next = NULL;
+    }
+
+    inline void* operator new(size_t size) {
+        void* p;
+#if !defined (__CC_ARM) && (!defined (_POSIX_C_SOURCE) || (_POSIX_C_SOURCE < 200112L))
+        p = memalign(16, size);
+        return p;
+#else
+        if (posix_memalign(&p, 16, size) == 0) {
+            return p;
+        }
+        return NULL;
+#endif
+    }
+
+    inline void operator delete(void* p) {
+        free(p);
+    }
+
+    inline uint8_t FunctionAddress() {
+        return Control & 0x7f;
+    }
+
+    inline int Speed() {
+        return (Control>>13)&1;
+    }
+
+    inline void setFunctionAddress(int addr) {
+        Control &= ~0x7f;
+        Control |= addr;
+    }
+
+    inline void setMaxPacketSize(uint16_t size) {
+        Control &= ~0xffff0000;
+        Control |= size<<16;
+    }
+
+    int Skip() {
+        return (Control>>14) & 1;
+    }
+
+    void setSkip() {
+        Control |= (1<<14);
+    }
+
+    void setFormat() {
+        Control |= (1<<15);
+    }
+
+    template<typename T>
+    inline bool enqueue(T* td) {
+        if (td) {
+            T* tail = reinterpret_cast<T*>(TailTd);
+            if (tail) {
+                tail->Next = td;
+                TailTd = reinterpret_cast<HCTD*>(td);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    template<typename T>
+    inline T* dequeue() {
+        T* head = reinterpret_cast<T*>(reinterpret_cast<uint32_t>(HeadTd)&~3); // delete Halted and Toggle Carry bit
+        T* tail = reinterpret_cast<T*>(TailTd);
+        if (head == NULL || tail == NULL || head == tail) {
+            return NULL;
+        }
+        HeadTd = reinterpret_cast<HCTD*>(head->Next);
+        return head;
+    }
+    template<typename T>
+    void init_queue(T* td) {
+        TailTd = reinterpret_cast<HCTD*>(td);
+        HeadTd = reinterpret_cast<HCTD*>(td); 
+    }
+};
+
+struct _HCCA {    // Host Controller Communication Area
+    _HCED* InterruptTable[32]; // +0 Interrupt Table
+    __IO uint16_t FrameNumber;// +128 Frame Number
+    __IO uint16_t Pad1;       // +130
+    __IO HCTD* DoneHead;      // +132 Done Head
+    uint8_t Reserved[116];    // +136 Reserved for future use
+    uint8_t Unknown[4];       // +252 Unused
+                              // +256
+    inline void* operator new(size_t size) {
+        void* p;
+#if !defined (__CC_ARM) && (!defined (_POSIX_C_SOURCE) || (_POSIX_C_SOURCE < 200112L))
+        p = memalign(256, size);
+        return p;
+#else
+        if (posix_memalign(&p, 256, size) == 0) {
+            return p;
+        }
+        return NULL;
+#endif
+    }
+
+    inline void operator delete(void* p) {
+        free(p);
+    }
+    
+    inline void enqueue(_HCED* ed) {
+        for(int i = 0; i < 32; i++) {
+            if (InterruptTable[i] == NULL) {
+                InterruptTable[i] = ed;
+            } else {
+                _HCED* nextEd = InterruptTable[i];
+                while(nextEd->Next && nextEd->Next != ed) {
+                    nextEd = nextEd->Next;
+                }
+                nextEd->Next = ed;
+            }
+        }
+    }
+    
+    inline void dequeue(_HCED* ed) {
+         for(int i = 0; i < 32; i++) {
+            if (InterruptTable[i] == ed) {
+                InterruptTable[i] = ed->Next;
+            } else if (InterruptTable[i]) {
+                _HCED* nextEd = InterruptTable[i];
+                while(nextEd) {
+                    if (nextEd->Next == ed) {
+                        nextEd->Next = ed->Next;
+                        break;
+                    }
+                    nextEd = nextEd->Next;
+                }
+            }
+         }
+    }
+};
+
+#define HCITD_QUEUE_SIZE 24
+
+class IsochronousEp {
+public:
+    void init(int addr, uint8_t ep, uint16_t size, uint8_t frameCount = 4, uint8_t queueLimit = 3);
+    void reset(int delay_ms = 100);
+    HCITD* isochronousReceive(int timeout_ms);
+    int isochronousSend(uint8_t* buf, int len, int timeout_ms);
+    HCITD* get_queue_HCITD(int timeout_ms);
+    uint16_t m_PacketSize;
+    void disconnect();
+    void irqWdhHandler(HCITD* itd) {m_queue.put(itd);} // WDH
+    int getQueueNum() {return m_itd_queue_count;}
+private:
+    HCITD* new_HCITD(IsochronousEp* obj);
+    Queue<HCITD, HCITD_QUEUE_SIZE> m_queue; // ITD done queue
+    int m_itd_queue_count;
+    int m_itd_queue_limit;
+    uint16_t m_FrameNumber;
+    int m_FrameCount; // 1-8
+    void enable();
+    _HCED* m_pED;
+};
+