Simple USBHost library for LPC4088. Backward compatibility of official-USBHost.

Dependencies:   FATFileSystem mbed-rtos

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers USBHALHost.cpp Source File

USBHALHost.cpp

00001 /* mbed USBHost Library
00002  * Copyright (c) 2006-2013 ARM Limited
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016  
00017 #include "USBHALHost.h"
00018 //#define _USB_DBG
00019 #include "BaseUsbHostDebug.h"
00020 #include "BaseUsbHostTest.h"
00021 
00022 #ifndef CTASSERT
00023 template <bool>struct CtAssert;
00024 template <>struct CtAssert<true> {};
00025 #define CTASSERT(A) CtAssert<A>();
00026 #endif
00027 
00028 #ifdef _USB_DBG
00029 #define USB_DBG(...) do{fprintf(stderr,"[%s@%d] ",__PRETTY_FUNCTION__,__LINE__);fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\n");} while(0);
00030 #define USB_DBG_HEX(A,B) debug_hex(A,B)
00031 void debug_hex(uint8_t* buf, int size);
00032 #else
00033 #define USB_DBG(...) while(0)
00034 #define USB_DBG_HEX(A,B) while(0)
00035 #endif
00036 
00037 #ifdef _USB_TEST
00038 #define USB_TEST_ASSERT(A) while(!(A)){fprintf(stderr,"\n\n%s@%d %s ASSERT!\n\n",__PRETTY_FUNCTION__,__LINE__,#A);exit(1);};
00039 #define USB_TEST_ASSERT_FALSE(A) USB_TEST_ASSERT(!(A))
00040 #else
00041 #define USB_TEST_ASSERT(A) while(0)
00042 #define USB_TEST_ASSERT_FALSE(A) while(0)
00043 #endif
00044 
00045 #define USB_INFO(...) do{fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\n");}while(0);
00046 
00047 // bits of the USB/OTG clock control register
00048 #define HOST_CLK_EN     (1<<0)
00049 #define DEV_CLK_EN      (1<<1)
00050 #define PORTSEL_CLK_EN  (1<<3)
00051 #define AHB_CLK_EN      (1<<4)
00052 
00053 // bits of the USB/OTG clock status register
00054 #define HOST_CLK_ON     (1<<0)
00055 #define DEV_CLK_ON      (1<<1)
00056 #define PORTSEL_CLK_ON  (1<<3)
00057 #define AHB_CLK_ON      (1<<4)
00058 
00059 // we need host clock, OTG/portsel clock and AHB clock
00060 #define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN)
00061 #define  FI                     0x2EDF           /* 12000 bits per frame (-1) */
00062 #define  DEFAULT_FMINTERVAL     ((((6 * (FI - 210)) / 7) << 16) | FI)
00063 
00064 USBHALHost* USBHALHost::instHost;
00065 
00066 USBHALHost::USBHALHost() {
00067     instHost = this;
00068 }
00069 
00070 void USBHALHost::init() {
00071     NVIC_DisableIRQ(USB_IRQn);
00072     m_pHcca = new HCCA();
00073     init_hw_ohci(m_pHcca);
00074     ResetRootHub();
00075     NVIC_SetVector(USB_IRQn, (uint32_t)_usbisr);
00076     NVIC_SetPriority(USB_IRQn, 0);
00077     NVIC_EnableIRQ(USB_IRQn);
00078     
00079 #ifndef BASE_USBHOST
00080     USB_INFO("Simple USBHost Library for EA LPC4088 QSB");
00081     bool lowSpeed = wait_attach();
00082     addDevice(NULL, 0, lowSpeed);
00083 #endif // BASE_USBHOST
00084 }
00085 
00086 void USBHALHost::init_hw_ohci(HCCA* pHcca) {
00087     LPC_SC->PCONP &= ~(1UL<<31);    //Cut power
00088     wait_ms(1000);
00089     LPC_SC->PCONP |= (1UL<<31);     // turn on power for USB
00090     LPC_USB->USBClkCtrl |= CLOCK_MASK; // Enable USB host clock, port selection and AHB clock
00091     // Wait for clocks to become available
00092     while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK)
00093         ;
00094     LPC_USB->OTGStCtrl |= 1;
00095     LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN;
00096     
00097 #if defined(TARGET_LPC1768)
00098     LPC_PINCON->PINSEL1 &= ~((3<<26) | (3<<28));  
00099     LPC_PINCON->PINSEL1 |=  ((1<<26)|(1<<28));     // 0x14000000
00100 #elif defined(TARGET_LPC4088)
00101     LPC_IOCON->P0_29 = 0x01; // USB_D+1
00102     LPC_IOCON->P0_30 = 0x01; // USB_D-1
00103     // DO NOT CHANGE P1_19.
00104 #else
00105 #error "target error"
00106 #endif
00107     USB_DBG("initialize OHCI\n");
00108     wait_ms(100);                                   /* Wait 50 ms before apply reset              */
00109     TEST_ASSERT((LPC_USB->HcRevision&0xff) == 0x10); // check revision
00110     LPC_USB->HcControl       = 0;                       /* HARDWARE RESET                             */
00111     LPC_USB->HcControlHeadED = 0;                       /* Initialize Control list head to Zero       */
00112     LPC_USB->HcBulkHeadED    = 0;                       /* Initialize Bulk list head to Zero          */
00113                                                         /* SOFTWARE RESET                             */
00114     LPC_USB->HcCommandStatus = OR_CMD_STATUS_HCR;
00115     LPC_USB->HcFmInterval    = DEFAULT_FMINTERVAL;      /* Write Fm Interval and Largest Data Packet Counter */
00116     LPC_USB->HcPeriodicStart = FI*90/100;
00117                                                       /* Put HC in operational state                */
00118     LPC_USB->HcControl  = (LPC_USB->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
00119     LPC_USB->HcRhStatus = OR_RH_STATUS_LPSC;            /* Set Global Power */
00120     TEST_ASSERT(pHcca);
00121     for (int i = 0; i < 32; i++) {
00122         pHcca->InterruptTable[i] = NULL;
00123     }
00124     LPC_USB->HcHCCA = reinterpret_cast<uint32_t>(pHcca);
00125     LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus; /* Clear Interrrupt Status */
00126     LPC_USB->HcInterruptEnable  = OR_INTR_ENABLE_MIE|OR_INTR_ENABLE_WDH|OR_INTR_ENABLE_FNO;
00127 
00128     LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
00129     LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
00130 }
00131 
00132 void USBHALHost::ResetRootHub() {
00133     wait_ms(100); /* USB 2.0 spec says at least 50ms delay before port reset */
00134     LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS; // Initiate port reset
00135     USB_DBG("Before loop\n");
00136     while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS)
00137       ;
00138     LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; // ...and clear port reset signal
00139     USB_DBG("After loop\n");
00140     wait_ms(200); /* Wait for 100 MS after port reset  */
00141 }
00142 
00143 bool USBHALHost::wait_attach() {
00144     bool lowSpeed = false;
00145     uint32_t status = LPC_USB->HcRhPortStatus1;
00146     if (status & OR_RH_PORT_LSDA) { // lowSpeedDeviceAttached
00147         lowSpeed = true;
00148     }
00149     return lowSpeed;
00150 }
00151 
00152 void USBHALHost::enable(ENDPOINT_TYPE type) {
00153     switch(type) {
00154         case CONTROL_ENDPOINT:
00155             LPC_USB->HcCommandStatus |= OR_CMD_STATUS_CLF;
00156             LPC_USB->HcControl |= OR_CONTROL_CLE;
00157             break;
00158         case ISOCHRONOUS_ENDPOINT:
00159             LPC_USB->HcControl |= OR_CONTROL_PLE;
00160             break;
00161         case BULK_ENDPOINT:
00162             LPC_USB->HcCommandStatus |= OR_CMD_STATUS_BLF;
00163             LPC_USB->HcControl |= OR_CONTROL_BLE;
00164             break;
00165         case INTERRUPT_ENDPOINT:
00166             LPC_USB->HcControl |= OR_CONTROL_PLE;
00167             break;
00168     }
00169 }
00170 
00171 void USBHALHost::token_init(USBEndpoint* ep) {
00172     if (ep->m_pED == NULL) {
00173         ep->m_pED = new HCED(ep);
00174         USB_DBG_ED(ep->m_pED);
00175     }
00176     HCED* ed = ep->m_pED;
00177     USBDeviceConnected* dev = ep->getDevice();
00178     USB_TEST_ASSERT(dev);
00179     if (dev) {
00180         uint8_t devAddr = dev->getAddress();
00181         USB_DBG("devAddr=%02x", devAddr);
00182         ed->setFunctionAddress(devAddr);
00183     }
00184     uint16_t size = ep->getSize();
00185     USB_DBG("MaxPacketSize=%d", size);
00186     ed->setMaxPacketSize(size);
00187     if (ed->HeadTd == NULL) {
00188         HCTD* td = new HCTD(ep);
00189         ed->TailTd = td;
00190         ed->HeadTd = td;
00191         switch(ep->getType()) {
00192             case CONTROL_ENDPOINT:
00193                 ed->Next = reinterpret_cast<HCED*>(LPC_USB->HcControlHeadED);
00194                 LPC_USB->HcControlHeadED = reinterpret_cast<uint32_t>(ed);
00195                 break;
00196             case BULK_ENDPOINT:
00197                 ed->Next = reinterpret_cast<HCED*>(LPC_USB->HcBulkHeadED);
00198                 LPC_USB->HcBulkHeadED = reinterpret_cast<uint32_t>(ed);
00199                 break;
00200             case INTERRUPT_ENDPOINT:
00201                 HCCA* pHcca = reinterpret_cast<HCCA*>(LPC_USB->HcHCCA);
00202                 ed->Next = pHcca->InterruptTable[0];
00203                 pHcca->InterruptTable[0] = ed;
00204                 break;
00205         }
00206         USB_DBG_ED(ed);
00207     }
00208 }
00209 
00210 int USBHALHost::token_setup(USBEndpoint* ep, SETUP_PACKET* setup, uint16_t wLength) {
00211     token_init(ep);
00212     HCED* ed = ep->m_pED;
00213     HCTD* td = ed->TailTd;
00214     setup->wLength = wLength;
00215     TBUF* tbuf = new(sizeof(SETUP_PACKET))TBUF(setup, sizeof(SETUP_PACKET));
00216     td->transfer(tbuf, sizeof(SETUP_PACKET));
00217     td->Control |= TD_TOGGLE_0|TD_SETUP; 
00218     HCTD* blank = new HCTD(ep);
00219     ed->enqueue(blank);
00220     //DBG_ED(ed);
00221     enable(ep->getType());
00222 
00223     td = ep->get_queue_HCTD();
00224     TEST_ASSERT(td);
00225     int result = td->getLengthTransferred();
00226     DBG_TD(td);
00227     delete tbuf;
00228     delete td;
00229     return result;
00230 }
00231 
00232 int USBHALHost::token_in(USBEndpoint* ep, uint8_t* data, int size, int retryLimit) {
00233     token_init(ep);
00234     HCED* ed = ep->m_pED;
00235     HCTD* td = ed->TailTd;
00236     TBUF* tbuf = NULL;
00237     if (data != NULL) {
00238         tbuf = new(size)TBUF();
00239         td->transfer(tbuf, size);
00240     }
00241     td->Control |= TD_IN;
00242     HCTD* blank = new HCTD(ep);
00243     ed->enqueue(blank);
00244     USB_DBG_ED_IF(ed->EndpointNumber()==0, ed); // control transfer
00245     enable(ep->getType());
00246 
00247     td = ep->get_queue_HCTD();
00248     TEST_ASSERT(td);
00249     if (data != NULL) {
00250         memcpy(data, tbuf->buf, size);
00251         delete tbuf;
00252     }
00253     int result = td->getLengthTransferred();
00254     delete td;
00255     return result;
00256 }
00257 
00258 int USBHALHost::token_out(USBEndpoint* ep, const uint8_t* data, int size, int retryLimit) {
00259     token_init(ep);
00260     HCED* ed = ep->m_pED;
00261     HCTD* td = ed->TailTd;
00262     TBUF* tbuf = NULL;
00263     if (data != NULL) {
00264         tbuf = new(size)TBUF(data, size);
00265         td->transfer(tbuf, size);
00266     }
00267     td->Control |= TD_OUT;
00268     HCTD* blank = new HCTD(ep);
00269     ed->enqueue(blank);
00270     DBG_ED(ed);
00271     enable(ep->getType());
00272 
00273     td = ep->get_queue_HCTD();
00274     TEST_ASSERT(td);
00275     if (data != NULL) {
00276         delete tbuf;
00277     }
00278     int result = td->getLengthTransferred();
00279     delete td;
00280     return result;
00281 }
00282 
00283 void USBHALHost::token_inNB(USBEndpoint* ep, uint8_t* data, int size) {
00284     token_init(ep);
00285     HCED* ed = ep->m_pED;
00286     HCTD* td = ed->TailTd;
00287     TBUF* tbuf = new(size)TBUF();
00288     td->transfer(tbuf, size);
00289     td->Control |= TD_IN;
00290     HCTD* blank = new HCTD(ep);
00291     ed->enqueue(blank);
00292     enable(ep->getType());
00293 }
00294 
00295 USB_TYPE USBHALHost::token_inNB_result(USBEndpoint* ep) {
00296     HCTD* td = ep->get_queue_HCTD(0);
00297     if (td) {
00298         int len = td->getLengthTransferred();
00299         TBUF* tbuf = (TBUF*)td->buf_top;
00300         memcpy(ep->getBufStart(), tbuf->buf, len);
00301         ep->setLengthTransferred(len);
00302         ep->setState((USB_TYPE)td->ConditionCode());
00303         delete td;
00304         delete tbuf;
00305         return USB_TYPE_OK;
00306     }
00307     return USB_TYPE_PROCESSING;
00308 }
00309 
00310 void USBHALHost::_usbisr(void) {
00311     if (instHost) {
00312         instHost->UsbIrqhandler();
00313     }
00314 }
00315 
00316 HCTD* td_reverse(HCTD* td)
00317 {
00318     HCTD* result = NULL;
00319     HCTD* next;
00320     while(td) {
00321         next = const_cast<HCTD*>(td->Next);
00322         td->Next = result;
00323         result = td;
00324         td = next;
00325     }
00326     return result;
00327 }
00328 
00329 void USBHALHost::UsbIrqhandler() {
00330     if (!(LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable)) {
00331         return;
00332     }
00333     m_report_irq++;
00334     uint32_t status = LPC_USB->HcInterruptStatus;
00335     if (status & OR_INTR_STATUS_FNO) {
00336         m_report_FNO++;
00337     }
00338     if (status & OR_INTR_STATUS_WDH) {
00339         union {
00340             HCTD* done_td;
00341             uint32_t lsb;
00342         };
00343         done_td = const_cast<HCTD*>(m_pHcca->DoneHead);
00344         TEST_ASSERT(done_td);
00345         m_pHcca->DoneHead = NULL; // reset
00346         if (lsb & 1) { // error ?
00347             lsb &= ~1;
00348         }
00349         HCTD* td = td_reverse(done_td);
00350         while(td) {
00351             USBEndpoint* ep = td->ep;
00352             TEST_ASSERT(ep);
00353             if (ep) {
00354                 ep->irqWdhHandler(td);
00355             }
00356             td = td->Next;
00357         }
00358         m_report_WDH++;
00359     }
00360     LPC_USB->HcInterruptStatus = status; // Clear Interrrupt Status
00361 }
00362 
00363 TBUF::TBUF(const void* data, int size) {
00364     if (size > 0) {
00365         memcpy(buf, data, size);
00366     }
00367 }
00368 
00369 HCTD::HCTD(USBEndpoint* obj) {
00370     CTASSERT(sizeof(HCTD) == 36);
00371     TEST_ASSERT(obj);
00372     Control = TD_CC|TD_ROUNDING;
00373     CurrBufPtr = NULL;
00374     Next = NULL;
00375     BufEnd = NULL;
00376     buf_top = NULL;
00377     buf_size = 0;
00378     ep = obj;
00379 }
00380 
00381 HCITD::HCITD(USBEndpoint* obj, uint16_t FrameNumber, int FrameCount, uint16_t PacketSize) {
00382     Control = 0xe0000000           | // CC ConditionCode NOT ACCESSED
00383              ((FrameCount-1) << 24)| // FC FrameCount
00384                   TD_DELAY_INT(0)  | // DI DelayInterrupt
00385                  FrameNumber;        // SF StartingFrame
00386     BufferPage0 = const_cast<uint8_t*>(buf);
00387     BufferEnd = const_cast<uint8_t*>(buf) + PacketSize * FrameCount - 1;
00388     Next = NULL; 
00389     ep = obj;
00390     uint32_t addr = reinterpret_cast<uint32_t>(buf);
00391     for(int i = 0; i < FrameCount; i++) {
00392         uint16_t offset = addr & 0x0fff;
00393         if ((addr&0xfffff000) == (reinterpret_cast<uint32_t>(BufferEnd)&0xfffff000)) {
00394             offset |= 0x1000;
00395         }
00396         OffsetPSW[i] = 0xe000|offset;
00397         addr += PacketSize;
00398     }
00399 }
00400 
00401 HCED::HCED(USBEndpoint* ep) {
00402     USBDeviceConnected* dev = ep->getDevice();
00403     int devAddr = 0;
00404     bool lowSpeed = false;
00405     if (dev) {
00406         devAddr = dev->getAddress();
00407         lowSpeed = dev->getSpeed();
00408     }
00409     int ep_number = ep->getAddress();
00410     int MaxPacketSize = ep->getSize();
00411         Control =  devAddr        | /* USB address */
00412         ((ep_number & 0x7F) << 7) | /* Endpoint address */
00413         (ep_number!=0?(((ep_number&0x80)?2:1) << 11):0)| /* direction : Out = 1, 2 = In */
00414         ((lowSpeed?1:0) << 13)    | /* speed full=0 low=1 */
00415         (MaxPacketSize << 16);      /* MaxPkt Size */
00416         TailTd = NULL;
00417         HeadTd = NULL;
00418         Next = NULL;
00419 }
00420 
00421 bool HCED::enqueue(HCTD* td) {
00422     if (td) {
00423         HCTD* tail = reinterpret_cast<HCTD*>(TailTd);
00424         if (tail) {
00425             tail->Next = td;
00426             TailTd = reinterpret_cast<HCTD*>(td);
00427             return true;
00428         }
00429     }
00430     return false;
00431 }
00432 
00433 void HCED::init_queue(HCTD* td) {
00434     TailTd = reinterpret_cast<HCTD*>(td);
00435     HeadTd = reinterpret_cast<HCTD*>(td); 
00436 }
00437 
00438 void HCCA::enqueue(HCED* ed) {
00439     for(int i = 0; i < 32; i++) {
00440         if (InterruptTable[i] == NULL) {
00441             InterruptTable[i] = ed;
00442         } else {
00443             HCED* nextEd = InterruptTable[i];
00444             while(nextEd->Next && nextEd->Next != ed) {
00445                 nextEd = nextEd->Next;
00446             }
00447             nextEd->Next = ed;
00448         }
00449     }
00450 }
00451 
00452