Takao Aratani / uvchost

Dependents:   WebCamera_SD

Fork of uvchost by Norimasa Okamoto

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers UsbHostMgr.cpp Source File

UsbHostMgr.cpp

00001 
00002 /*
00003 Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
00004  
00005 Permission is hereby granted, free of charge, to any person obtaining a copy
00006 of this software and associated documentation files (the "Software"), to deal
00007 in the Software without restriction, including without limitation the rights
00008 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00009 copies of the Software, and to permit persons to whom the Software is
00010 furnished to do so, subject to the following conditions:
00011  
00012 The above copyright notice and this permission notice shall be included in
00013 all copies or substantial portions of the Software.
00014  
00015 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00016 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00017 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00018 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00019 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00020 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00021 THE SOFTWARE.
00022 */
00023 #include "UsbHostMgr.h"
00024 #include "usb_mem.h"
00025 #include "string.h" //For memcpy, memmove, memset
00026 //#define __DEBUG
00027 //#define __DEBUG3
00028 //#include "dbg/dbg.h"
00029 #include "mydbg.h"
00030 
00031 // bits of the USB/OTG clock control register
00032 #define HOST_CLK_EN     (1<<0)
00033 #define DEV_CLK_EN      (1<<1)
00034 #define PORTSEL_CLK_EN  (1<<3)
00035 #define AHB_CLK_EN      (1<<4)
00036 
00037 // bits of the USB/OTG clock status register
00038 #define HOST_CLK_ON     (1<<0)
00039 #define DEV_CLK_ON      (1<<1)
00040 #define PORTSEL_CLK_ON  (1<<3)
00041 #define AHB_CLK_ON      (1<<4)
00042 
00043 // we need host clock, OTG/portsel clock and AHB clock
00044 #define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN)
00045 
00046 static UsbHostMgr* pMgr = NULL;
00047 
00048 extern "C" void sUsbIrqhandler(void) __irq
00049 {
00050   DBG("\n+Int\n");
00051   if(pMgr)
00052     pMgr->UsbIrqhandler();
00053   DBG("\n-Int\n");
00054   return;
00055 }
00056 
00057 UsbHostMgr::UsbHostMgr() : m_lpDevices()
00058 {
00059   /*if(!pMgr)*/ //Assume singleton
00060   pMgr = this;
00061   usb_mem_init();
00062   for(int i = 0; i < USB_HOSTMGR_MAX_DEVS; i++) {
00063       m_lpDevices[i] = NULL;
00064   }
00065   m_pHcca = (HCCA*) usb_get_hcca();
00066   memset((void*)m_pHcca, 0, 0x100);
00067   m_hardware_init = false;
00068   DBG("Host manager at %p\n", this);
00069 }
00070 
00071 UsbHostMgr::~UsbHostMgr()
00072 {
00073   if(pMgr == this)
00074     pMgr = NULL;
00075 }
00076 
00077 UsbErr UsbHostMgr::init() //Initialize host
00078 {
00079   DBG("m_hardware_init=%d\n", m_hardware_init);
00080   if(m_hardware_init) {
00081       return USBERR_OK;
00082   }
00083 
00084   NVIC_DisableIRQ(USB_IRQn);                           /* Disable the USB interrupt source           */
00085   
00086   LPC_SC->PCONP       &= ~(1UL<<31); //Cut power
00087   wait(1);
00088   
00089   
00090   // turn on power for USB
00091   LPC_SC->PCONP       |= (1UL<<31);
00092   // Enable USB host clock, port selection and AHB clock
00093   LPC_USB->USBClkCtrl |= CLOCK_MASK;
00094   // Wait for clocks to become available
00095   while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK)
00096       ;
00097   
00098   // it seems the bits[0:1] mean the following
00099   // 0: U1=device, U2=host
00100   // 1: U1=host, U2=host
00101   // 2: reserved
00102   // 3: U1=host, U2=device
00103   // NB: this register is only available if OTG clock (aka "port select") is enabled!!
00104   // since we don't care about port 2, set just bit 0 to 1 (U1=host)
00105   LPC_USB->OTGStCtrl |= 1;
00106   
00107   // now that we've configured the ports, we can turn off the portsel clock
00108   LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN;
00109   
00110   // power pins are not connected on mbed, so we can skip them
00111   /* P1[18] = USB_UP_LED, 01 */
00112   /* P1[19] = /USB_PPWR,     10 */
00113   /* P1[22] = USB_PWRD, 10 */
00114   /* P1[27] = /USB_OVRCR, 10 */
00115   /*LPC_PINCON->PINSEL3 &= ~((3<<4) | (3<<6) | (3<<12) | (3<<22));  
00116   LPC_PINCON->PINSEL3 |=  ((1<<4)|(2<<6) | (2<<12) | (2<<22));   // 0x00802080
00117   */
00118 
00119   // configure USB D+/D- pins
00120   /* P0[29] = USB_D+, 01 */
00121   /* P0[30] = USB_D-, 01 */
00122   LPC_PINCON->PINSEL1 &= ~((3<<26) | (3<<28));  
00123   LPC_PINCON->PINSEL1 |=  ((1<<26)|(1<<28));     // 0x14000000
00124       
00125   DBG("Initializing Host Stack\n");
00126   
00127   wait_ms(100);                                   /* Wait 50 ms before apply reset              */
00128   LPC_USB->HcControl       = 0;                       /* HARDWARE RESET                             */
00129   LPC_USB->HcControlHeadED = 0;                       /* Initialize Control list head to Zero       */
00130   LPC_USB->HcBulkHeadED    = 0;                       /* Initialize Bulk list head to Zero          */
00131   
00132                                                       /* SOFTWARE RESET                             */
00133   LPC_USB->HcCommandStatus = OR_CMD_STATUS_HCR;
00134   LPC_USB->HcFmInterval    = DEFAULT_FMINTERVAL;      /* Write Fm Interval and Largest Data Packet Counter */
00135   LPC_USB->HcPeriodicStart = FI*90/100;
00136 
00137                                                       /* Put HC in operational state                */
00138   LPC_USB->HcControl  = (LPC_USB->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
00139   LPC_USB->HcRhStatus = OR_RH_STATUS_LPSC;            /* Set Global Power                           */
00140   
00141   LPC_USB->HcHCCA = (uint32_t)(m_pHcca);
00142   LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus;                   /* Clear Interrrupt Status                    */
00143 
00144 
00145   LPC_USB->HcInterruptEnable  = OR_INTR_ENABLE_MIE |
00146                        OR_INTR_ENABLE_WDH |
00147                        OR_INTR_ENABLE_RHSC;
00148 
00149   NVIC_SetPriority(USB_IRQn, 0);       /* highest priority */
00150   /* Enable the USB Interrupt */
00151   NVIC_SetVector(USB_IRQn, (uint32_t)(sUsbIrqhandler));
00152   LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
00153   LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
00154   
00155   
00156   /* Check for any connected devices */
00157   //if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS)  //Root device connected
00158   //{
00159   //  //Device connected
00160   //  wait(1);
00161   //  DBG("Device connected (%08x)\n", LPC_USB->HcRhPortStatus1);
00162   //  onUsbDeviceConnected(0, 1); //Hub 0 (root hub), Port 1 (count starts at 1)
00163   //}
00164     
00165   DBG("Enabling IRQ\n");
00166   NVIC_EnableIRQ(USB_IRQn);
00167   DBG("End of host stack initialization\n");
00168   m_hardware_init = true;
00169   return USBERR_OK;
00170 }
00171 
00172 UsbErr UsbHostMgr::poll() //Enumerate connected devices, etc
00173 {
00174   /* Check for any connected devices */
00175   if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS)  //Root device connected
00176   {
00177     //Device connected
00178     wait(1);
00179     DBG("Device connected (%08x)\n", LPC_USB->HcRhPortStatus1);
00180     bool lowspeed = false;
00181     if (LPC_USB->HcRhPortStatus1 & 0x0200) { // lowspeed?
00182         lowspeed = true;
00183     }
00184     onUsbDeviceConnected(0, 1, lowspeed); //Hub 0 (root hub), Port 1 (count starts at 1)
00185   }
00186   
00187   for(int i = 0; i < USB_HOSTMGR_MAX_DEVS; i++)
00188   {
00189     UsbDevice* dev = m_lpDevices[i];
00190     if (dev == NULL) {
00191       continue;
00192     }
00193     DBG3("%d dev=%p %d %d addr=%d\n", i, dev, dev->m_connected, dev->m_enumerated, dev->m_addr);
00194     if(dev->m_connected) {
00195       if (!dev->m_enumerated) {
00196           dev->enumerate();
00197           return USBERR_PROCESSING;
00198       }
00199     }
00200   }
00201   for(int i = 0; i < USB_HOSTMGR_MAX_DEVS; i++) {
00202     UsbDevice* dev = m_lpDevices[i];
00203     if (dev == NULL) {
00204       continue;
00205     }   
00206     if (dev->m_connected && dev->m_enumerated) {
00207       if (dev->m_DeviceClass == 0x09) { // HUB
00208         UsbErr rc = dev->hub_poll();
00209         if (rc == USBERR_PROCESSING) {
00210           return USBERR_PROCESSING;
00211         } 
00212       }
00213     }
00214   }
00215   return USBERR_OK;
00216 }
00217 
00218 int UsbHostMgr::devicesCount()
00219 {
00220   int i;
00221   for(i = 0; i < USB_HOSTMGR_MAX_DEVS; i++)
00222   {
00223     if (m_lpDevices[i] == NULL) {
00224       return i;
00225     }
00226   }
00227   return i;
00228 }
00229 
00230 UsbDevice* UsbHostMgr::getDevice(int item)
00231 {
00232   UsbDevice* pDev = m_lpDevices[item];
00233   if(!pDev)
00234     return NULL;
00235     
00236   pDev->m_refs++;
00237   return pDev;
00238 }
00239 
00240 void UsbHostMgr::releaseDevice(UsbDevice* pDev)
00241 {
00242   pDev->m_refs--;
00243   if(pDev->m_refs > 0)
00244     return;
00245   //If refs count = 0, delete
00246   //Find & remove from list
00247   int i;
00248   for(i = 0; i < USB_HOSTMGR_MAX_DEVS; i++)
00249   {
00250     if (m_lpDevices[i] == pDev)
00251       break;
00252   }
00253   if(i!=USB_HOSTMGR_MAX_DEVS)
00254     memmove(&m_lpDevices[i], &m_lpDevices[i+1], sizeof(UsbDevice*) * (USB_HOSTMGR_MAX_DEVS - (i + 1))); //Safer than memcpy because of overlapping mem
00255   m_lpDevices[USB_HOSTMGR_MAX_DEVS - 1] = NULL;
00256   delete pDev;
00257 }
00258 
00259 void UsbHostMgr::UsbIrqhandler()
00260 {
00261   uint32_t   int_status;
00262   uint32_t   ie_status;
00263   
00264   int_status    = LPC_USB->HcInterruptStatus;                          /* Read Interrupt Status                */
00265   ie_status     = LPC_USB->HcInterruptEnable;                          /* Read Interrupt enable status         */
00266 
00267   if (!(int_status & ie_status))
00268   {
00269     return;
00270   }
00271   else
00272   {
00273     int_status = int_status & ie_status;
00274     if (int_status & OR_INTR_STATUS_RHSC) /* Root hub status change interrupt     */
00275     {
00276       DBG("LPC_USB->HcRhPortStatus1 = %08x\n", LPC_USB->HcRhPortStatus1);
00277       if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CSC)
00278       {
00279         if (LPC_USB->HcRhStatus & OR_RH_STATUS_DRWE)
00280         {
00281             /*
00282              * When DRWE is on, Connect Status Change
00283              * means a remote wakeup event.
00284             */
00285             //HOST_RhscIntr = 1;// JUST SOMETHING FOR A BREAKPOINT
00286         }
00287         else
00288         {
00289           /*
00290            * When DRWE is off, Connect Status Change
00291            * is NOT a remote wakeup event
00292           */
00293           if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS)  //Root device connected
00294           {
00295             //Device connected
00296             DBG("Device connected (%08x)\n", LPC_USB->HcRhPortStatus1);
00297             onUsbDeviceConnected(0, 1); //Hub 0 (root hub), Port 1 (count starts at 1)
00298           }
00299           else //Root device disconnected
00300           {
00301             //Device disconnected
00302             DBG("Device disconnected\n");
00303             onUsbDeviceDisconnected(0, 1);
00304           }
00305           //TODO: HUBS
00306         }
00307         LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
00308       }
00309       if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRSC)
00310       {
00311         LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
00312       }
00313     }  
00314     if (int_status & OR_INTR_STATUS_WDH) /* Writeback Done Head interrupt        */
00315     {                  
00316       //UsbEndpoint::sOnCompletion((LPC_USB->HccaDoneHead) & 0xFE);
00317       if(m_pHcca->DoneHead)
00318       {
00319         UsbEndpoint::sOnCompletion(m_pHcca->DoneHead);
00320         m_pHcca->DoneHead = 0;
00321         LPC_USB->HcInterruptStatus = OR_INTR_STATUS_WDH;
00322         if(m_pHcca->DoneHead)
00323           DBG("??????????????????????????????\n\n\n");
00324       }
00325       else
00326       {
00327         //Probably an error
00328         int_status = LPC_USB->HcInterruptStatus;
00329         DBG("HcInterruptStatus = %08x\n", int_status);
00330         if (int_status & OR_INTR_STATUS_UE) //Unrecoverable error, disconnect devices and resume
00331         {
00332           onUsbDeviceDisconnected(0, 1);
00333           LPC_USB->HcInterruptStatus = OR_INTR_STATUS_UE;
00334           LPC_USB->HcCommandStatus = 0x01; //Host Controller Reset
00335         }
00336       }
00337     }
00338     LPC_USB->HcInterruptStatus = int_status; /* Clear interrupt status register      */
00339   }
00340   return;
00341 }
00342 
00343 void UsbHostMgr::onUsbDeviceDisconnected(int hub, int port)
00344 {
00345   for(int i = 0; i < devicesCount(); i++)
00346   {
00347      if( (m_lpDevices[i]->m_hub == hub)
00348      &&  (m_lpDevices[i]->m_port == port) )
00349      {
00350        m_lpDevices[i]->m_connected = false;
00351        if(!m_lpDevices[i]->m_enumerated)
00352        {
00353          delete m_lpDevices[i];
00354          m_lpDevices[i] = NULL;
00355        }
00356        return;
00357      }
00358   }
00359 }
00360 
00361 void UsbHostMgr::resetPort(int hub, int port)
00362 {
00363   DBG3("hub=%d port=%d\n", hub, port);
00364   if(hub == 0) //Root hub
00365   {
00366     wait_ms(100); /* USB 2.0 spec says at least 50ms delay before port reset */
00367     LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS; // Initiate port reset
00368     DBG("Before loop\n");
00369     while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS)
00370       ;
00371     LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; // ...and clear port reset signal
00372     DBG("After loop\n");
00373     wait_ms(200); /* Wait for 100 MS after port reset  */
00374   }
00375   else
00376   {
00377     for(int i = 0; i < USB_HOSTMGR_MAX_DEVS; i++) {
00378       UsbDevice* dev = m_lpDevices[i];
00379       if (dev == NULL) {
00380         continue;
00381       }
00382       if (dev->m_addr == hub) {
00383         DBG("%d dev=%p\n", i, dev);
00384         dev->hub_PortReset(port);
00385         return;
00386       }
00387     }
00388     DBG_ASSERT(0);
00389   }
00390 }