USB host library, support isochronous,bulk,interrupt and control.

Dependents:   BaseUsbHost_example BaseJpegDecode_example SimpleJpegDecode_example

Import programBaseUsbHost_example

BaseUsbHost example program

Revision:
0:b7d6879637a8
Child:
3:ae77d63a1eda
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BaseUsbHost.cpp	Tue Dec 04 13:29:41 2012 +0000
@@ -0,0 +1,283 @@
+// BaseUsbHost.cpp 2012/12/4
+#include "mbed.h"
+#include "rtos.h"
+#include "BaseUsbHost.h"
+#define DEBUG
+#include "BaseUsbHostDebug.h"
+#define TEST
+#include "BaseUsbHostTest.h"
+
+// bits of the USB/OTG clock control register
+#define HOST_CLK_EN     (1<<0)
+#define DEV_CLK_EN      (1<<1)
+#define PORTSEL_CLK_EN  (1<<3)
+#define AHB_CLK_EN      (1<<4)
+
+// bits of the USB/OTG clock status register
+#define HOST_CLK_ON     (1<<0)
+#define DEV_CLK_ON      (1<<1)
+#define PORTSEL_CLK_ON  (1<<3)
+#define AHB_CLK_ON      (1<<4)
+
+// we need host clock, OTG/portsel clock and AHB clock
+#define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN)
+
+#define  FI                     0x2EDF           /* 12000 bits per frame (-1) */
+#define  DEFAULT_FMINTERVAL     ((((6 * (FI - 210)) / 7) << 16) | FI)
+
+static BaseUsbHost* pHost = NULL;
+
+extern "C" void USB_IRQHandler(void) __irq;
+void USB_IRQHandler(void) __irq
+{
+    if (pHost) {
+        pHost->irqHandler();
+    }
+}
+
+inline static void* malloc_align(size_t size, size_t alignment)
+{
+    void* p;
+    int r = posix_memalign(&p, alignment, size);
+    if (r == 0) {
+        return p;
+    }
+    return NULL;
+}
+
+BaseUsbHost::BaseUsbHost()
+{
+    if (pHost) {
+        TEST_ASSERT(pHost == NULL);
+        return;
+    }
+    pHost = this;
+    NVIC_DisableIRQ(USB_IRQn);
+    m_pHcca = reinterpret_cast<HCCA*>(malloc_align(sizeof(HCCA), 256));
+    TEST_ASSERT(m_pHcca);
+    init_hw_ohci(m_pHcca);
+    ResetRootHub();
+    NVIC_SetPriority(USB_IRQn, 0);
+    NVIC_EnableIRQ(USB_IRQn);
+}
+
+void BaseUsbHost::init_hw_ohci(HCCA* pHcca)
+{
+    LPC_SC->PCONP &= ~(1UL<<31);    //Cut power
+    wait(1);
+    LPC_SC->PCONP |= (1UL<<31);     // turn on power for USB
+    LPC_USB->USBClkCtrl |= CLOCK_MASK; // Enable USB host clock, port selection and AHB clock
+    // Wait for clocks to become available
+    while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK)
+        ;
+    LPC_USB->OTGStCtrl |= 1;
+    LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN;
+    
+    LPC_PINCON->PINSEL1 &= ~((3<<26) | (3<<28));  
+    LPC_PINCON->PINSEL1 |=  ((1<<26)|(1<<28));     // 0x14000000
+      
+    DBG("initialize OHCI\n");
+    wait_ms(100);                                   /* Wait 50 ms before apply reset              */
+    TEST_ASSERT((LPC_USB->HcRevision&0xff) == 0x10); // check revision
+    LPC_USB->HcControl       = 0;                       /* HARDWARE RESET                             */
+    LPC_USB->HcControlHeadED = 0;                       /* Initialize Control list head to Zero       */
+    LPC_USB->HcBulkHeadED    = 0;                       /* Initialize Bulk list head to Zero          */
+                                                        /* SOFTWARE RESET                             */
+    LPC_USB->HcCommandStatus = OR_CMD_STATUS_HCR;
+    LPC_USB->HcFmInterval    = DEFAULT_FMINTERVAL;      /* Write Fm Interval and Largest Data Packet Counter */
+    LPC_USB->HcPeriodicStart = FI*90/100;
+                                                      /* Put HC in operational state                */
+    LPC_USB->HcControl  = (LPC_USB->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
+    LPC_USB->HcRhStatus = OR_RH_STATUS_LPSC;            /* Set Global Power */
+    TEST_ASSERT(pHcca);
+    for (int i = 0; i < 32; i++) {
+        pHcca->InterruptTable[i] = NULL;
+    }
+    LPC_USB->HcHCCA = (uint32_t)pHcca;
+    LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus; /* Clear Interrrupt Status */
+
+    //LPC_USB->HcInterruptEnable  = MIE|WDH|RHSC|FNO;
+    LPC_USB->HcInterruptEnable  = OR_INTR_ENABLE_MIE|OR_INTR_ENABLE_WDH|OR_INTR_ENABLE_FNO;
+
+    LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
+    LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
+}
+
+void BaseUsbHost::ResetRootHub()
+{
+    wait_ms(100); /* USB 2.0 spec says at least 50ms delay before port reset */
+    LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS; // Initiate port reset
+    DBG("Before loop\n");
+    while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS)
+      ;
+    LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; // ...and clear port reset signal
+    DBG("After loop\n");
+    wait_ms(200); /* Wait for 100 MS after port reset  */
+}
+
+HCTD* td_reverse(HCTD* td)
+{
+    HCTD* result = NULL;
+    HCTD* next;
+    while(td) {
+        next = reinterpret_cast<HCTD*>(td->Next);
+        td->Next = reinterpret_cast<uint32_t>(result);
+        result = td;
+        td = next;
+    }
+    return result;
+}
+
+void BaseUsbHost::irqHandler()
+{
+    if (!(LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable)) {
+        return;
+    }
+    m_report_irq++;
+    uint32_t status = LPC_USB->HcInterruptStatus;
+    if (status & OR_INTR_STATUS_FNO) {
+        m_report_FNO++;
+    }
+    if (status & OR_INTR_STATUS_WDH) {
+        uint32_t done_td = m_pHcca->DoneHead;
+        TEST_ASSERT(done_td);
+        m_pHcca->DoneHead = NULL; // reset
+        if (done_td & 1) { // error ?
+            done_td &= ~1;
+        }
+        HCTD* td = td_reverse(reinterpret_cast<HCTD*>(done_td));
+        while(td) {
+            BaseEp* ep = reinterpret_cast<BaseEp*>(td->ep);
+            TEST_ASSERT(ep);
+            ep->irqWdhHandler(td);
+            td = reinterpret_cast<HCTD*>(td->Next);
+        }
+        m_report_WDH++;
+    }
+    LPC_USB->HcInterruptStatus = status; // Clear Interrrupt Status
+}
+
+BaseEp::BaseEp(int addr, uint8_t ep, uint16_t size, int lowSpeed):m_td_queue_count(0)
+{
+    DBG("%p FA=%d EN=%02x MPS=%d S=%d\n", this, addr, ep, size, lowSpeed);
+    m_pED  = reinterpret_cast<HCED*>(malloc_align(sizeof(HCED), 16));
+    TEST_ASSERT(m_pED);
+    TEST_ASSERT(size >= 8 && size <= 1023);    
+    TEST_ASSERT(lowSpeed == 0 || lowSpeed == 1);
+    m_pED->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 */
+    m_pED->Next = NULL;
+}
+
+int BaseEp::GetAddr()
+{
+    HCED* ed = m_pED;
+    TEST_ASSERT(ed);
+    if (ed) {
+        return ed->Control & 0x7f;
+    }
+    return 0;
+}
+
+int BaseEp::GetLowSpeed()
+{
+    HCED* ed = m_pED;
+    TEST_ASSERT(ed);
+    if (ed) {
+        if (ed->Control & (1<<13)) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+void BaseEp::update_FunctionAddress(int addr)
+{
+    TEST_ASSERT(addr >= 0 && addr <= 127);
+    HCED* ed = m_pED;
+    TEST_ASSERT(ed);
+    ed->Control &= ~0x7f;
+    ed->Control |= addr;
+}
+
+void BaseEp::update_MaxPacketSize(uint16_t size)
+{
+    TEST_ASSERT(size >= 8 && size <= 1023);
+    HCED* ed = m_pED;
+    TEST_ASSERT(ed);
+    ed->Control &= ~0xffff0000;
+    ed->Control |= size<<16;
+}
+
+HCTD* BaseEp::new_HCTD(int buf_size)
+{
+    HCTD* td;
+    int r = posix_memalign(reinterpret_cast<void**>(&td), 16, sizeof(HCTD) + buf_size);
+    if (r == 0) {
+        td->Control = TD_CC|TD_ROUNDING;
+        td->Next = NULL;
+        td->ep = this;
+        td->buf_top = NULL;
+        td->buf_size = buf_size;
+        if (buf_size == 0) {
+            td->CurrBufPtr = NULL;
+            td->BufEnd = NULL;
+        } else {
+            td->CurrBufPtr = td->buf;
+            td->BufEnd = const_cast<uint8_t*>(td->buf)+buf_size-1;
+        }
+        return td;
+    }
+    return NULL;
+}
+
+void BaseEp::delete_HCTD(HCTD* p)
+{
+    free(p);
+}
+
+HCTD* BaseEp::get_queue_HCTD(uint32_t millisec)
+{
+    for(int i = 0; i < 16; i++) {
+        osEvent evt = m_queue.get(millisec);
+        if (evt.status == osEventMessage) {
+            HCTD* td = reinterpret_cast<HCTD*>(evt.value.p);
+            TEST_ASSERT(td);
+            if (td->Control & TD_CC) {
+                m_ConditionCode = td->Control>>28;
+                DBG_TD(td);
+            }
+            return td;
+        } else if (evt.status == osOK) {
+            continue;
+        } else if (evt.status == osEventTimeout) {
+            return NULL;
+        } else {
+            DBG("evt.status: %02x\n", evt.status);
+            TEST_ASSERT(evt.status == osEventMessage);
+        }
+    }
+    return NULL;
+}
+
+int BaseEp::wait_queue_HCTD(HCTD* wait_td, uint32_t millisec)
+{
+    for(int i = 0; i < 16; i++) {
+        HCTD* td = get_queue_HCTD(millisec);
+        if (td) {
+            if (td->Control & TD_CC) {
+                DBG_TD(td);
+                delete_HCTD(td);
+                return USB_ERROR;
+            }
+            delete_HCTD(td);
+            if (td == wait_td) {
+                return USB_OK;
+            }
+        }
+    }
+    return USB_ERROR;
+}