local fork (temporary)

Dependents:   VodafoneUSBModem_bleedingedge2

Fork of USBHostWANDongle_bleedingedge by Donatien Garnier

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers USBHALHost.cpp Source File

USBHALHost.cpp

00001 /* Copyright (c) 2010-2012 mbed.org, MIT License
00002 *
00003 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00004 * and associated documentation files (the "Software"), to deal in the Software without
00005 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
00006 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
00007 * Software is furnished to do so, subject to the following conditions:
00008 *
00009 * The above copyright notice and this permission notice shall be included in all copies or
00010 * substantial portions of the Software.
00011 *
00012 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00013 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00014 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00015 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00016 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00017 */
00018 
00019 #define __DEBUG__ 0 //WARN: ENABLING DEBUGGING HERE WILL PRINTF IN IRQS!! UNEXPECTED BEHAVIOUR MAY RESULT...
00020 #ifndef __MODULE__
00021 #define __MODULE__ "USBHALHost.cpp"
00022 #endif
00023 
00024 #include "core/dbg.h"
00025 #include <cstdint>
00026 
00027 #include "mbed.h"
00028 #include "USBHALHost.h"
00029 
00030 // bits of the USB/OTG clock control register
00031 #define HOST_CLK_EN     (1<<0)
00032 #define DEV_CLK_EN      (1<<1)
00033 #define PORTSEL_CLK_EN  (1<<3)
00034 #define AHB_CLK_EN      (1<<4)
00035 
00036 // bits of the USB/OTG clock status register
00037 #define HOST_CLK_ON     (1<<0)
00038 #define DEV_CLK_ON      (1<<1)
00039 #define PORTSEL_CLK_ON  (1<<3)
00040 #define AHB_CLK_ON      (1<<4)
00041 
00042 // we need host clock, OTG/portsel clock and AHB clock
00043 #define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN)
00044 
00045 
00046 
00047 #define HCCA_SIZE 0x100
00048 #define ED_SIZE 0x10
00049 #define TD_SIZE 0x10
00050 
00051 #define TOTAL_SIZE (HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE) + (MAX_TD*TD_SIZE))
00052 
00053 static volatile uint8_t usb_buf[TOTAL_SIZE] __attribute((section("AHBSRAM1"),aligned(256)));  //256 bytes aligned!
00054 
00055 USBHALHost * USBHALHost::instHost;
00056 
00057 USBHALHost::USBHALHost() : thread(USBHALHost::staticCb, (void*)this, osPriorityNormal, 4*128) {
00058     instHost = this;
00059     memInit();
00060     memset((void*)usb_hcca, 0, HCCA_SIZE);
00061     for (int i = 0; i < MAX_ENDPOINT; i++) {
00062         edBufAlloc[i] = false;
00063     }
00064     for (int i = 0; i < MAX_TD; i++) {
00065         tdBufAlloc[i] = false;
00066     }
00067 }
00068 
00069 
00070 void USBHALHost::init() {
00071   thread.signal_set(USBHALHOST_SIG_INIT);
00072 }
00073 
00074 
00075 uint32_t USBHALHost::controlHeadED() {
00076     return LPC_USB->HcControlHeadED;
00077 }
00078 
00079 uint32_t USBHALHost::bulkHeadED() {
00080     return LPC_USB->HcBulkHeadED;
00081 }
00082 
00083 uint32_t USBHALHost::interruptHeadED() {
00084     return usb_hcca->IntTable[0];
00085 }
00086 
00087 void USBHALHost::updateBulkHeadED(uint32_t addr) {
00088     LPC_USB->HcBulkHeadED = addr;
00089 }
00090 
00091 
00092 void USBHALHost::updateControlHeadED(uint32_t addr) {
00093     LPC_USB->HcControlHeadED = addr;
00094 }
00095 
00096 void USBHALHost::updateInterruptHeadED(uint32_t addr) {
00097     usb_hcca->IntTable[0] = addr;
00098 }
00099 
00100 
00101 void USBHALHost::enableControlList() {
00102     LPC_USB->HcCommandStatus = OR_CMD_STATUS_CLF;
00103     LPC_USB->HcControl       |= OR_CONTROL_CLE; //Enable control list
00104 }
00105 
00106 void USBHALHost::enableBulkList() {
00107     LPC_USB->HcCommandStatus = OR_CMD_STATUS_BLF;
00108     LPC_USB->HcControl       |= OR_CONTROL_BLE; //Enable bulk list
00109 }
00110 
00111 void USBHALHost::enableInterruptList() {
00112     LPC_USB->HcControl |= OR_CONTROL_PLE;
00113 }
00114 
00115 bool USBHALHost::disableControlList() {
00116     if(LPC_USB->HcControl & OR_CONTROL_CLE)
00117     {
00118       LPC_USB->HcControl       &= ~OR_CONTROL_CLE; //Disable control list
00119       return true;
00120     }
00121     else
00122     {
00123       return false;
00124     }
00125 }
00126 
00127 bool USBHALHost::disableBulkList() {
00128     if(LPC_USB->HcControl & OR_CONTROL_BLE)
00129     {
00130       LPC_USB->HcControl       &= ~OR_CONTROL_BLE; //Disable bulk list
00131       return true;
00132     }
00133     else
00134     {
00135       return false;
00136     }
00137 }
00138 
00139 bool USBHALHost::disableInterruptList() {
00140     if(LPC_USB->HcControl & OR_CONTROL_PLE)
00141     {
00142       LPC_USB->HcControl       &= ~OR_CONTROL_PLE; //Disable interrupt list
00143       return true;
00144     }
00145     else
00146     {
00147       return false;
00148     }
00149 }
00150 
00151 //Lock processing
00152 void USBHALHost::lock()
00153 {
00154   mtx.lock();
00155 }
00156 
00157 void USBHALHost::unlock()
00158 {
00159   mtx.unlock();
00160 }
00161 
00162 void USBHALHost::memInit() {
00163     usb_hcca = (volatile HCCA *)usb_buf;
00164     usb_edBuf = usb_buf + HCCA_SIZE;
00165     usb_tdBuf = usb_buf + HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE);
00166 }
00167 
00168 volatile uint8_t * USBHALHost::getED() {
00169     for (int i = 0; i < MAX_ENDPOINT; i++) {
00170         if ( !edBufAlloc[i] ) {
00171             edBufAlloc[i] = true;
00172             return (volatile uint8_t *)(usb_edBuf + i*ED_SIZE);
00173         }
00174     }
00175     perror("Could not allocate ED\r\n");
00176     return NULL; //Could not alloc ED
00177 }
00178 
00179 volatile uint8_t * USBHALHost::getTD() {
00180     int i;
00181     for (i = 0; i < MAX_TD; i++) {
00182         if ( !tdBufAlloc[i] ) {
00183             tdBufAlloc[i] = true;
00184             return (volatile uint8_t *)(usb_tdBuf + i*TD_SIZE);
00185         }
00186     }
00187     perror("Could not allocate TD\r\n");
00188     return NULL; //Could not alloc TD
00189 }
00190 
00191 
00192 void USBHALHost::freeED(volatile uint8_t * ed) {
00193     int i;
00194     i = (ed - usb_edBuf) / ED_SIZE;
00195     edBufAlloc[i] = false;
00196 }
00197 
00198 void USBHALHost::freeTD(volatile uint8_t * td) {
00199     int i;
00200     i = (td - usb_tdBuf) / TD_SIZE;
00201     tdBufAlloc[i] = false;
00202 }
00203 
00204 
00205 void USBHALHost::resetPort(int hub, int port) {
00206     DBG("Resetting hub %d, port %d\n", hub, port);
00207     if (hub == 0) { //Root hub
00208         // USB 2.0 spec says at least 50ms delay before port reset
00209         Thread::wait(200);
00210         LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS; // Initiate port reset
00211         while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS);
00212         LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; // ...and clear port reset signal
00213         // Wait for 100 MS after port reset
00214         Thread::wait(200);
00215     } else {
00216         //TODO: Hubs
00217     }
00218 }
00219 
00220 
00221 void USBHALHost::_usbisr(void) {
00222     if (instHost) {
00223         instHost->UsbIrqhandler();
00224     }
00225 }
00226 
00227 void USBHALHost::UsbIrqhandler() {
00228   if( LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable ) //Is there something to actually process?
00229   {
00230     NVIC_DisableIRQ(USB_IRQn);
00231     NVIC_ClearPendingIRQ(USB_IRQn);
00232     thread.signal_set(USBHALHOST_SIG_IRQ); //Signal processing thread
00233   }
00234 
00235 }
00236 
00237 void USBHALHost::process()
00238 {
00239   DBG("USB Process started");
00240 
00241   lock();
00242   Thread::signal_wait(USBHALHOST_SIG_INIT);
00243 
00244   NVIC_DisableIRQ(USB_IRQn);                           // Disable the USB interrupt source
00245 
00246   LPC_SC->PCONP       &= ~(1UL<<31); //Cut power
00247   Thread::wait(200);
00248 
00249   // turn on power for USB
00250   LPC_SC->PCONP       |= (1UL<<31);
00251 
00252   // Enable USB host clock, port selection and AHB clock
00253   LPC_USB->USBClkCtrl |= CLOCK_MASK;
00254 
00255   // Wait for clocks to become available
00256   while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK);
00257 
00258   // it seems the bits[0:1] mean the following
00259   // 0: U1=device, U2=host
00260   // 1: U1=host, U2=host
00261   // 2: reserved
00262   // 3: U1=host, U2=device
00263   // NB: this register is only available if OTG clock (aka "port select") is enabled!!
00264   // since we don't care about port 2, set just bit 0 to 1 (U1=host)
00265   LPC_USB->OTGStCtrl |= 1;
00266 
00267   // now that we've configured the ports, we can turn off the portsel clock
00268   LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN;
00269 
00270   // configure USB D+/D- pins
00271   // P0[29] = USB_D+, 01
00272   // P0[30] = USB_D-, 01
00273   LPC_PINCON->PINSEL1 &= ~((3<<26) | (3<<28));
00274   LPC_PINCON->PINSEL1 |=  ((1<<26)|(1<<28));     // 0x14000000
00275 
00276   LPC_USB->HcControl       = 0;                       // HARDWARE RESET
00277   LPC_USB->HcControlHeadED = 0;                       // Initialize Control list head to Zero
00278   LPC_USB->HcBulkHeadED    = 0;                       // Initialize Bulk list head to Zero
00279 
00280   //wait_ms(100);                                   // Wait 50 ms before apply reset
00281   Thread::wait(100);
00282 
00283   // SOFTWARE RESET
00284   LPC_USB->HcCommandStatus = OR_CMD_STATUS_HCR;
00285   LPC_USB->HcFmInterval    = DEFAULT_FMINTERVAL;      // Write Fm Interval and Largest Data Packet Counter
00286   LPC_USB->HcPeriodicStart = FI * 90 / 100;
00287 
00288   // Put HC in operational state
00289   LPC_USB->HcControl  = (LPC_USB->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
00290   LPC_USB->HcRhStatus = OR_RH_STATUS_LPSC;            // Set Global Power
00291 
00292   LPC_USB->HcHCCA = (uint32_t)(usb_hcca);
00293   LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus;                   // Clear Interrrupt Status
00294 
00295   LPC_USB->HcInterruptEnable  = OR_INTR_ENABLE_MIE | OR_INTR_ENABLE_WDH | OR_INTR_ENABLE_RHSC;
00296 
00297   //DG: Do not set prio
00298   //NVIC_SetPriority(USB_IRQn, 0);       // highest priority
00299   // Enable the USB Interrupt
00300   NVIC_SetVector(USB_IRQn, (uint32_t)(_usbisr));
00301   LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
00302   LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
00303 
00304   NVIC_EnableIRQ(USB_IRQn);
00305 
00306 
00307   // Check for any connected devices
00308   if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS) { //Root device connected
00309       //Device connected
00310       Thread::wait(500);
00311       DBG("Device connected (%08x)\n\r", LPC_USB->HcRhPortStatus1);
00312       deviceConnected(0, 1, LPC_USB->HcRhPortStatus1 & OR_RH_PORT_LSDA); //Hub 0 (root hub), Port 1 (count starts at 1), Low or High speed
00313   }
00314 
00315   unlock();
00316 
00317 
00318   for(;;)
00319   {
00320     Thread::signal_wait(USBHALHOST_SIG_IRQ); //Wait for IRQ to process
00321 
00322     lock();
00323     DBG("Locked");
00324 
00325     WARN("isr %08x [EN %08x]", LPC_USB->HcInterruptStatus, LPC_USB->HcInterruptEnable);
00326 
00327     //Now process IRQ
00328     uint32_t int_status = LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable;
00329 
00330     if (int_status & OR_INTR_STATUS_RHSC)
00331     { // Root hub status change interrupt
00332       WARN("Port status %08x", LPC_USB->HcRhPortStatus1);
00333       if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CSC)
00334       {
00335         if (LPC_USB->HcRhStatus & OR_RH_STATUS_DRWE)
00336         {
00337 
00338           // When DRWE is on, Connect Status Change
00339           // means a remote wakeup event.
00340 
00341         }
00342         else
00343         {
00344 
00345           // When DRWE is off, Connect Status Change
00346           // is NOT a remote wakeup event
00347 
00348           if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS)
00349           { //Root device connected
00350             //Device connected
00351             WARN("Device connected!!");
00352            // Thread::wait(500);
00353             deviceConnected(0, 1, LPC_USB->HcRhPortStatus1 & OR_RH_PORT_LSDA); //Hub 0 (root hub), Port 1 (count starts at 1), Low or High speed
00354           }
00355           else
00356           { //Root device disconnected
00357             //Device disconnected
00358             WARN("Device disconnected!!");
00359             Thread::wait(500);
00360             if (!(int_status & OR_INTR_STATUS_WDH))
00361             {
00362               usb_hcca->DoneHead = 0;
00363             }
00364             deviceDisconnected(0, 1, usb_hcca->DoneHead & 0xFFFFFFFE);
00365             if (int_status & OR_INTR_STATUS_WDH)
00366             {
00367               usb_hcca->DoneHead = 0;
00368               LPC_USB->HcInterruptStatus = OR_INTR_STATUS_WDH;
00369             }
00370 
00371           }
00372           //TODO: HUBS
00373         }
00374         LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
00375       }
00376       if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRSC)
00377       {
00378         LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
00379         //int_status &= ~OR_RH_PORT_PRSC;
00380       }
00381       LPC_USB->HcInterruptStatus = OR_INTR_STATUS_RHSC;
00382     }
00383 
00384     if (int_status & OR_INTR_STATUS_WDH)
00385     { // Writeback Done Head interrupt
00386       transferCompleted(usb_hcca->DoneHead & 0xFFFFFFFE);
00387       LPC_USB->HcInterruptStatus = OR_INTR_STATUS_WDH;
00388     }
00389 
00390     //IRQ Processed
00391 
00392     DBG("Unlocked");
00393 
00394     NVIC_EnableIRQ(USB_IRQn);
00395 
00396     unlock();
00397 
00398   }
00399 }
00400 
00401 /*static*/ void USBHALHost::staticCb(void const* p)
00402 {
00403   ((USBHALHost*)p)->process();
00404 }