Adaptation of the official mbed USBHost repository to work with the LPC4088 Display Module

Dependents:   DMSupport DMSupport DMSupport DMSupport

Fork of DM_USBHost by EmbeddedArtists AB

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 "mbed.h"
00018 #include "USBHALHost.h"
00019 #include "dbg.h"
00020 
00021 // bits of the USB/OTG clock control register
00022 #define HOST_CLK_EN     (1<<0)
00023 #define DEV_CLK_EN      (1<<1)
00024 #define PORTSEL_CLK_EN  (1<<3)
00025 #define AHB_CLK_EN      (1<<4)
00026 
00027 // bits of the USB/OTG clock status register
00028 #define HOST_CLK_ON     (1<<0)
00029 #define DEV_CLK_ON      (1<<1)
00030 #define PORTSEL_CLK_ON  (1<<3)
00031 #define AHB_CLK_ON      (1<<4)
00032 
00033 // we need host clock, OTG/portsel clock and AHB clock
00034 #define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN)
00035 
00036 #define HCCA_SIZE sizeof(HCCA)
00037 #define ED_SIZE sizeof(HCED)
00038 #define TD_SIZE sizeof(HCTD)
00039 
00040 #define TOTAL_SIZE (HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE) + (MAX_TD*TD_SIZE))
00041 
00042 // Put the USB structures in the only memory accessible by the USB stack - the AHBSRAM1
00043 static volatile uint8_t usb_buf[TOTAL_SIZE] __attribute__((section("AHBSRAM1"),aligned(256)));// __attribute__((section("AHBSRAM0"),aligned(256)));  //256 bytes aligned!
00044 
00045 // A very basic implementation of malloc to allocate in the AHBSRAM1
00046 #define SAFE_MEM_TO_USE     (10*1024)
00047 #define SAFE_MEM_BLOCK_SIZE (512)
00048 #define SAFE_MEM_NUM_BLOCKS (SAFE_MEM_TO_USE/SAFE_MEM_BLOCK_SIZE)
00049 typedef struct {
00050     volatile uint8_t* ptr;
00051     bool used;
00052 } safe_mem_info_t;
00053 static safe_mem_info_t safe_mem_list[SAFE_MEM_NUM_BLOCKS];
00054 static uint8_t safe_mem_data[SAFE_MEM_TO_USE] __attribute__((section("AHBSRAM1"),aligned(256)));//__attribute__((section("AHBSRAM0"),aligned));  //256 bytes aligned!
00055 
00056 // To detect when memory outside of the AHBSRAM0 is passed to the USB stack
00057 void assert_mem_region(uint32_t ptr)
00058 {
00059     if (( ptr != 0) && ((ptr & 0xff000000) != 0x20000000)) {
00060         USB_ERR("0x%08x not in USB MEM", ptr);
00061         mbed_die();
00062     }
00063 }
00064 
00065 USBHALHost * USBHALHost::instHost;
00066 
00067 USBHALHost::USBHALHost() {
00068     instHost = this;
00069     memInit();
00070     memset((void*)usb_hcca, 0, HCCA_SIZE);
00071     for (int i = 0; i < MAX_ENDPOINT; i++) {
00072         edBufAlloc[i] = false;
00073     }
00074     for (int i = 0; i < MAX_TD; i++) {
00075         tdBufAlloc[i] = false;
00076     }
00077     for (int i = 0; i < SAFE_MEM_NUM_BLOCKS; i++) {
00078         safe_mem_list[i].used = false;
00079         safe_mem_list[i].ptr = safe_mem_data + SAFE_MEM_BLOCK_SIZE*i;
00080     }
00081     
00082 }
00083 
00084 uint8_t* USBHALHost::getSafeMem(uint32_t size) {
00085     uint8_t* result = NULL;
00086     if (size > 512) {
00087         USB_ERR("getSafeMem(%u) not supported", size);
00088     } else {
00089         safemem_mutex.lock();
00090         for (int i = 0; i < SAFE_MEM_NUM_BLOCKS; i++) {
00091             if (!safe_mem_list[i].used) {
00092                 safe_mem_list[i].used = true;
00093                 result = (uint8_t*)safe_mem_list[i].ptr;
00094                 break;
00095             }
00096         }
00097         safemem_mutex.unlock();
00098     }
00099     if (result == NULL) {
00100         USB_ERR("getSafeMem(%u) failed to allocate", size);
00101     }
00102     return result;
00103 }
00104 
00105 void USBHALHost::returnSafeMem(uint8_t* mem) {
00106     safemem_mutex.lock();
00107     int i;
00108     for (i = 0; i < SAFE_MEM_NUM_BLOCKS; i++) {
00109         if (safe_mem_list[i].ptr == mem) {
00110             safe_mem_list[i].used = false;
00111             break;
00112         }
00113     }
00114     safemem_mutex.unlock();
00115     if (i == SAFE_MEM_NUM_BLOCKS) {
00116       USB_ERR("returnSafeMem(%p) not allocated", mem);
00117     }
00118 }
00119 
00120 
00121 void USBHALHost::init() {
00122     NVIC_DisableIRQ(USB_IRQn);
00123 
00124     //Cut power
00125     LPC_SC->PCONP &= ~(1UL<<31);
00126     ThisThread::sleep_for(1000);
00127 
00128     // turn on power for USB
00129     LPC_SC->PCONP       |= (1UL<<31);
00130 
00131     // Enable USB host clock, port selection and AHB clock
00132     LPC_USB->USBClkCtrl = 0x19;
00133 
00134     // Wait for clocks to become available
00135     while ((LPC_USB->USBClkSt & 0x19) != 0x19);
00136 
00137     // it seems the bits[0:1] mean the following
00138     // 0: U1=device, U2=host
00139     // 1: U1=host, U2=host
00140     // 2: reserved
00141     // 3: U1=host, U2=device
00142     // NB: this register is only available if OTG clock (aka "port select") is enabled!!
00143     LPC_USB->OTGStCtrl = 1;
00144 
00145     // configure USB D+/D- pins
00146 
00147     LPC_IOCON->P0_29 &= ~0x9F; // USB_D+1
00148     LPC_IOCON->P0_29 |=  0x01; // USB_D+1
00149     LPC_IOCON->P0_30 &= ~0x9F; // USB_D-1
00150     LPC_IOCON->P0_30 |=  0x01; // USB_D-1
00151 
00152     LPC_IOCON->P0_31 &= ~0x9F; // USB_D+2
00153     LPC_IOCON->P0_31 |=  0x01; // USB_D+2
00154 
00155     LPC_IOCON->P1_30 &= ~0x1F; // USB_PWRD2
00156     LPC_IOCON->P1_30 |=  0x01; // USB_PWRD2
00157     LPC_IOCON->P1_31 &= ~0x1F; // USB_OVRCR2
00158     LPC_IOCON->P1_31 |=  0x01; // USB_OVRCR2
00159 
00160     LPC_IOCON->P0_14 &= ~0x1F; // USB ID Pin as GPIO, output, high
00161     LPC_GPIO0->DIR   |= (1<<14);
00162     LPC_GPIO0->SET    = (1<<14);
00163     LPC_IOCON->P4_31 &= ~0x1F; // USB CONN Pin as GPIO, output, low
00164     LPC_GPIO4->DIR   |= (1UL<<31);
00165     LPC_GPIO4->CLR    = (1UL<<31);
00166 
00167     USB_DBG("initialize OHCI\n");
00168 
00169     // Wait 100 ms before apply reset
00170     ThisThread::sleep_for(100);
00171 
00172     LPC_USB->HcControl       = 0; // HARDWARE RESET
00173     LPC_USB->HcControlHeadED = 0; // Initialize Control list head to Zero
00174     LPC_USB->HcBulkHeadED    = 0; // Initialize Bulk list head to Zero
00175 
00176     // software reset
00177     LPC_USB->HcCommandStatus = OR_CMD_STATUS_HCR;
00178 
00179     // Write Fm Interval and Largest Data Packet Counter
00180     LPC_USB->HcFmInterval    = DEFAULT_FMINTERVAL;
00181     LPC_USB->HcPeriodicStart = FI * 90 / 100;
00182 
00183     // Put HC in operational state
00184     LPC_USB->HcControl  = (LPC_USB->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
00185 
00186     // Set Global Power
00187     LPC_USB->HcRhStatus = OR_RH_STATUS_LPSC;
00188 
00189     LPC_USB->HcHCCA = (uint32_t)(usb_hcca);
00190 
00191     // Clear Interrrupt Status
00192     LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus;
00193 
00194     LPC_USB->HcInterruptEnable  = OR_INTR_ENABLE_MIE | OR_INTR_ENABLE_WDH | OR_INTR_ENABLE_RHSC;
00195 
00196     // Enable the USB Interrupt
00197     NVIC_SetVector(USB_IRQn, (uint32_t)(_usbisr));
00198     LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
00199     LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
00200     LPC_USB->HcRhPortStatus2 = OR_RH_PORT_CSC;
00201     LPC_USB->HcRhPortStatus2 = OR_RH_PORT_PRSC;
00202     resetRootHub();
00203     NVIC_SetPriority(USB_IRQn, 0);
00204     NVIC_EnableIRQ(USB_IRQn);
00205 
00206     if (LPC_USB->HcRhPortStatus2 & OR_RH_PORT_CCS) {
00207         //Device connected
00208         ThisThread::sleep_for(150);
00209         USB_DBG("Device connected (%08x)\n\r", LPC_USB->HcRhPortStatus2);
00210         deviceConnected(0, 2, LPC_USB->HcRhPortStatus2 & OR_RH_PORT_LSDA);
00211     }
00212 }
00213 
00214 uint32_t USBHALHost::controlHeadED() {
00215     return LPC_USB->HcControlHeadED;
00216 }
00217 
00218 uint32_t USBHALHost::bulkHeadED() {
00219     return LPC_USB->HcBulkHeadED;
00220 }
00221 
00222 uint32_t USBHALHost::interruptHeadED() {
00223     return usb_hcca->IntTable[0];
00224 }
00225 
00226 void USBHALHost::updateBulkHeadED(uint32_t addr) {
00227     LPC_USB->HcBulkHeadED = addr;
00228 }
00229 
00230 
00231 void USBHALHost::updateControlHeadED(uint32_t addr) {
00232     LPC_USB->HcControlHeadED = addr;
00233 }
00234 
00235 void USBHALHost::updateInterruptHeadED(uint32_t addr) {
00236     usb_hcca->IntTable[0] = addr;
00237 }
00238 
00239 
00240 void USBHALHost::enableList(ENDPOINT_TYPE type) {
00241     switch(type) {
00242         case CONTROL_ENDPOINT:
00243             LPC_USB->HcCommandStatus = OR_CMD_STATUS_CLF;
00244             LPC_USB->HcControl |= OR_CONTROL_CLE;
00245             break;
00246         case ISOCHRONOUS_ENDPOINT:
00247             break;
00248         case BULK_ENDPOINT:
00249             LPC_USB->HcCommandStatus = OR_CMD_STATUS_BLF;
00250             LPC_USB->HcControl |= OR_CONTROL_BLE;
00251             break;
00252         case INTERRUPT_ENDPOINT:
00253             LPC_USB->HcControl |= OR_CONTROL_PLE;
00254             break;
00255     }
00256 }
00257 
00258 
00259 bool USBHALHost::disableList(ENDPOINT_TYPE type) {
00260     switch(type) {
00261         case CONTROL_ENDPOINT:
00262             if(LPC_USB->HcControl & OR_CONTROL_CLE) {
00263                 LPC_USB->HcControl &= ~OR_CONTROL_CLE;
00264                 return true;
00265             }
00266             return false;
00267         case ISOCHRONOUS_ENDPOINT:
00268             return false;
00269         case BULK_ENDPOINT:
00270             if(LPC_USB->HcControl & OR_CONTROL_BLE){
00271                 LPC_USB->HcControl &= ~OR_CONTROL_BLE;
00272                 return true;
00273             }
00274             return false;
00275         case INTERRUPT_ENDPOINT:
00276             if(LPC_USB->HcControl & OR_CONTROL_PLE) {
00277                 LPC_USB->HcControl &= ~OR_CONTROL_PLE;
00278                 return true;
00279             }
00280             return false;
00281     }
00282     return false;
00283 }
00284 
00285 
00286 void USBHALHost::memInit() {
00287     usb_hcca = (volatile HCCA *)usb_buf;
00288     usb_edBuf = usb_buf + HCCA_SIZE;
00289     usb_tdBuf = usb_buf + HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE);
00290 }
00291 
00292 volatile uint8_t * USBHALHost::getED() {
00293     for (int i = 0; i < MAX_ENDPOINT; i++) {
00294         if ( !edBufAlloc[i] ) {
00295             edBufAlloc[i] = true;
00296             return (volatile uint8_t *)(usb_edBuf + i*ED_SIZE);
00297         }
00298     }
00299     USB_ERR("Could not allocate ED\r\n");
00300     return NULL;
00301 }
00302 
00303 volatile uint8_t * USBHALHost::getTD() {
00304     int i;
00305     for (i = 0; i < MAX_TD; i++) {
00306         if ( !tdBufAlloc[i] ) {
00307             tdBufAlloc[i] = true;
00308             return (volatile uint8_t *)(usb_tdBuf + i*TD_SIZE);
00309         }
00310     }
00311     USB_ERR("Could not allocate TD\r\n");
00312     return NULL;
00313 }
00314 
00315 
00316 void USBHALHost::freeED(volatile uint8_t * ed) {
00317     int i;
00318     i = (ed - usb_edBuf) / ED_SIZE;
00319     edBufAlloc[i] = false;
00320 }
00321 
00322 void USBHALHost::freeTD(volatile uint8_t * td) {
00323     int i;
00324     i = (td - usb_tdBuf) / TD_SIZE;
00325     tdBufAlloc[i] = false;
00326 }
00327 
00328 
00329 void USBHALHost::resetRootHub() {
00330 
00331     DigitalOut usb2_vbus_en(P0_12);
00332     usb2_vbus_en = 1;
00333     ThisThread::sleep_for(100); /* USB 2.0 spec says at least 50ms delay before port reset */
00334 
00335     // Initiate port reset
00336     LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS;
00337 
00338     while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS);
00339 
00340     // ...and clear port reset signal
00341     LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
00342 
00343     usb2_vbus_en = 0;
00344 
00345     // Initiate port reset
00346     LPC_USB->HcRhPortStatus2 = OR_RH_PORT_PRS;
00347 
00348     while (LPC_USB->HcRhPortStatus2 & OR_RH_PORT_PRS);
00349     
00350     // ...and clear port reset signal
00351     LPC_USB->HcRhPortStatus2 = OR_RH_PORT_PRSC;
00352     
00353     usb2_vbus_en = 1;
00354     ThisThread::sleep_for(200); /* Wait for at least 100 MS after port reset */
00355 }
00356 
00357 
00358 void USBHALHost::_usbisr(void) {
00359     if (instHost) {
00360         instHost->UsbIrqhandler();        
00361     }
00362 }
00363 
00364 void USBHALHost::UsbIrqhandler() {
00365 
00366     if( LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable ) //Is there something to actually process?
00367     {
00368         uint32_t int_status = LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable;
00369         
00370         // Root hub status change interrupt
00371         if (int_status & OR_INTR_STATUS_RHSC) {
00372             if (LPC_USB->HcRhPortStatus2 & OR_RH_PORT_CSC) {
00373                 if (LPC_USB->HcRhStatus & OR_RH_STATUS_DRWE) {
00374                     // When DRWE is on, Connect Status Change
00375                     // means a remote wakeup event.
00376                 } else {
00377 
00378                     //Root device connected
00379                     if (LPC_USB->HcRhPortStatus2 & OR_RH_PORT_CCS) {
00380 
00381                         // wait 150ms to avoid bounce (removed since wait is not allowed in isr)
00382                         //ThisThread::sleep_for(150);
00383 
00384                         //Hub 0 (root hub), Port 2 (count starts at 1), Low or High speed
00385                         deviceConnected(0, 2, LPC_USB->HcRhPortStatus2 & OR_RH_PORT_LSDA);
00386                     }
00387 
00388                     //Root device disconnected
00389                     else {
00390 
00391                         if (!(int_status & OR_INTR_STATUS_WDH)) {
00392                             usb_hcca->DoneHead = 0;
00393                         }
00394 
00395                         // wait 200ms to avoid bounce (removed since wait is not allowed in isr)
00396                         //wait_ms(200);
00397 
00398                         deviceDisconnected(0, 2, NULL, usb_hcca->DoneHead & 0xFFFFFFFE);
00399 
00400                         if (int_status & OR_INTR_STATUS_WDH) {
00401                             usb_hcca->DoneHead = 0;
00402                             LPC_USB->HcInterruptStatus = OR_INTR_STATUS_WDH;
00403                         }
00404                     }
00405                 }
00406                 LPC_USB->HcRhPortStatus2 = OR_RH_PORT_CSC;
00407             }
00408             if (LPC_USB->HcRhPortStatus2 & OR_RH_PORT_PRSC) {
00409                 LPC_USB->HcRhPortStatus2 = OR_RH_PORT_PRSC;
00410             }
00411             LPC_USB->HcInterruptStatus = OR_INTR_STATUS_RHSC;
00412         }
00413 
00414         // Writeback Done Head interrupt
00415         if (int_status & OR_INTR_STATUS_WDH) {
00416             transferCompleted(usb_hcca->DoneHead & 0xFFFFFFFE);
00417             LPC_USB->HcInterruptStatus = OR_INTR_STATUS_WDH;
00418         }
00419     }
00420 }