USB host library, support isochronous,bulk,interrupt and control.
Dependents: BaseUsbHost_example BaseJpegDecode_example SimpleJpegDecode_example
Import programBaseUsbHost_example
BaseUsbHost example program
BaseUsbHost.cpp
- Committer:
- va009039
- Date:
- 2013-02-11
- Revision:
- 5:8a2d056e9b38
- Parent:
- 4:d931d24c2f81
File content as of revision 5:8a2d056e9b38:
// BaseUsbHost.cpp 2013/1/25 #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(); } } BaseUsbHost::BaseUsbHost() { if (pHost) { TEST_ASSERT(pHost == NULL); return; } pHost = this; NVIC_DisableIRQ(USB_IRQn); m_pHcca = new HCCA(); 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 = reinterpret_cast<uint32_t>(pHcca); LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus; /* Clear Interrrupt Status */ 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 = const_cast<HCTD*>(td->Next); td->Next = 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) { union { HCTD* done_td; uint32_t lsb; }; done_td = const_cast<HCTD*>(m_pHcca->DoneHead); TEST_ASSERT(done_td); m_pHcca->DoneHead = NULL; // reset if (lsb & 1) { // error ? lsb &= ~1; } HCTD* td = td_reverse(done_td); while(td) { BaseEp* ep = td->ep; TEST_ASSERT(ep); if (ep) { ep->irqWdhHandler(td); } td = 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); TEST_ASSERT(size >= 8 && size <= 1023); TEST_ASSERT(lowSpeed == 0 || lowSpeed == 1); m_pED = new HCED(addr, ep, size, lowSpeed); TEST_ASSERT(m_pED); } int BaseEp::GetAddr() { TEST_ASSERT(m_pED); if (m_pED) { return m_pED->FunctionAddress(); } return 0; } int BaseEp::GetLowSpeed() { TEST_ASSERT(m_pED); if (m_pED) { return m_pED->Speed(); } return 0; } void BaseEp::update_FunctionAddress(int addr) { TEST_ASSERT(addr >= 0 && addr <= 127); TEST_ASSERT(m_pED); if (m_pED) { m_pED->setFunctionAddress(addr); } } void BaseEp::update_MaxPacketSize(uint16_t size) { TEST_ASSERT(size >= 8 && size <= 1023); TEST_ASSERT(m_pED); if (m_pED) { m_pED->setMaxPacketSize(size); } } int BaseEp::transfer(uint8_t* buf, int len) { HCTD* data_td = m_pED->TailTd; TEST_ASSERT(data_td); if (data_td == NULL) { return USB_ERROR; } data_td->transfer(buf, len); HCTD* blank_td = new HCTD(this); TEST_ASSERT(blank_td); if (blank_td == NULL) { return USB_ERROR_MEMORY; } data_td->Next = blank_td; m_pED->TailTd = blank_td; enable(); return USB_PROCESSING; } int BaseEp::status(uint32_t millisec) { HCTD* td = get_queue_HCTD(millisec); if (td == NULL) { return USB_PROCESSING; } uint8_t cc = td->ConditionCode(); int ret; switch(cc) { case 0: case 8: case 9: ret = td->status(); break; case 4: ret = USB_ERROR_STALL; break; case 5: ret = USB_ERROR_DEVICE_NOT_RESPONDING; break; default: DBG("ConditionCode=%d\n", cc); ret = USB_ERROR; break; } delete td; return ret; } int BaseEp::send_receive(uint8_t* buf, int len, int millisec) { if (m_td_queue_count == 0) { int rc = transfer(buf, len); if (rc != USB_PROCESSING) { return rc; } m_td_queue_count++; } int ret = status(millisec); if (ret != USB_PROCESSING) { m_td_queue_count--; } return ret; } 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); uint8_t cc = td->ConditionCode(); if (cc != 0) { m_ConditionCode = cc; 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) { uint8_t cc = td->ConditionCode(); if (cc != 0) { DBG_TD(td); delete td; switch(cc) { case 4: return USB_ERROR_STALL; default: return USB_ERROR; } } delete td; if (td == wait_td) { return USB_OK; } } } return USB_ERROR; }