NetServices Stack source

Dependents:   HelloWorld ServoInterfaceBoardExample1 4180_Lab4

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 
00024 #include "UsbHostMgr.h"
00025 
00026 #include "usb_mem.h"
00027 
00028 #include "string.h" //For memcpy, memmove, memset
00029 
00030 #include "netCfg.h"
00031 #if NET_USB
00032 
00033 //#define __DEBUG
00034 #include "dbg/dbg.h"
00035 
00036 // bits of the USB/OTG clock control register
00037 #define HOST_CLK_EN     (1<<0)
00038 #define DEV_CLK_EN      (1<<1)
00039 #define PORTSEL_CLK_EN  (1<<3)
00040 #define AHB_CLK_EN      (1<<4)
00041 
00042 // bits of the USB/OTG clock status register
00043 #define HOST_CLK_ON     (1<<0)
00044 #define DEV_CLK_ON      (1<<1)
00045 #define PORTSEL_CLK_ON  (1<<3)
00046 #define AHB_CLK_ON      (1<<4)
00047 
00048 // we need host clock, OTG/portsel clock and AHB clock
00049 #define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN)
00050 
00051 static UsbHostMgr* pMgr = NULL;
00052 
00053 extern "C" void sUsbIrqhandler(void) __irq
00054 {
00055   DBG("\n+Int\n");
00056   if(pMgr)
00057     pMgr->UsbIrqhandler();
00058   DBG("\n-Int\n");
00059   return;
00060 }
00061 
00062 UsbHostMgr::UsbHostMgr() : m_lpDevices()
00063 {
00064   /*if(!pMgr)*/ //Assume singleton
00065     pMgr = this;
00066   usb_mem_init();
00067   memset(m_lpDevices, NULL, sizeof(UsbDevice*) * USB_HOSTMGR_MAX_DEVS);
00068   m_pHcca = (HCCA*) usb_get_hcca();
00069   memset((void*)m_pHcca, 0, 0x100);
00070   DBG("Host manager at %p\n", this);
00071 }
00072 
00073 UsbHostMgr::~UsbHostMgr()
00074 {
00075   if(pMgr == this)
00076     pMgr = NULL;
00077 }
00078 
00079 UsbErr UsbHostMgr::init() //Initialize host
00080 {
00081   NVIC_DisableIRQ(USB_IRQn);                           /* Disable the USB interrupt source           */
00082   
00083   LPC_SC->PCONP       &= ~(1UL<<31); //Cut power
00084   wait(1);
00085   
00086   
00087   // turn on power for USB
00088   LPC_SC->PCONP       |= (1UL<<31);
00089   // Enable USB host clock, port selection and AHB clock
00090   LPC_USB->USBClkCtrl |= CLOCK_MASK;
00091   // Wait for clocks to become available
00092   while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK)
00093       ;
00094   
00095   // it seems the bits[0:1] mean the following
00096   // 0: U1=device, U2=host
00097   // 1: U1=host, U2=host
00098   // 2: reserved
00099   // 3: U1=host, U2=device
00100   // NB: this register is only available if OTG clock (aka "port select") is enabled!!
00101   // since we don't care about port 2, set just bit 0 to 1 (U1=host)
00102   LPC_USB->OTGStCtrl |= 1;
00103   
00104   // now that we've configured the ports, we can turn off the portsel clock
00105   LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN;
00106   
00107   // power pins are not connected on mbed, so we can skip them
00108   /* P1[18] = USB_UP_LED, 01 */
00109   /* P1[19] = /USB_PPWR,     10 */
00110   /* P1[22] = USB_PWRD, 10 */
00111   /* P1[27] = /USB_OVRCR, 10 */
00112   /*LPC_PINCON->PINSEL3 &= ~((3<<4) | (3<<6) | (3<<12) | (3<<22));  
00113   LPC_PINCON->PINSEL3 |=  ((1<<4)|(2<<6) | (2<<12) | (2<<22));   // 0x00802080
00114   */
00115 
00116   // configure USB D+/D- pins
00117   /* P0[29] = USB_D+, 01 */
00118   /* P0[30] = USB_D-, 01 */
00119   LPC_PINCON->PINSEL1 &= ~((3<<26) | (3<<28));  
00120   LPC_PINCON->PINSEL1 |=  ((1<<26)|(1<<28));     // 0x14000000
00121       
00122   DBG("Initializing Host Stack\n");
00123   
00124   wait_ms(100);                                   /* Wait 50 ms before apply reset              */
00125   LPC_USB->HcControl       = 0;                       /* HARDWARE RESET                             */
00126   LPC_USB->HcControlHeadED = 0;                       /* Initialize Control list head to Zero       */
00127   LPC_USB->HcBulkHeadED    = 0;                       /* Initialize Bulk list head to Zero          */
00128   
00129                                                       /* SOFTWARE RESET                             */
00130   LPC_USB->HcCommandStatus = OR_CMD_STATUS_HCR;
00131   LPC_USB->HcFmInterval    = DEFAULT_FMINTERVAL;      /* Write Fm Interval and Largest Data Packet Counter */
00132 
00133                                                       /* Put HC in operational state                */
00134   LPC_USB->HcControl  = (LPC_USB->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
00135   LPC_USB->HcRhStatus = OR_RH_STATUS_LPSC;            /* Set Global Power                           */
00136   
00137   LPC_USB->HcHCCA = (uint32_t)(m_pHcca);
00138   LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus;                   /* Clear Interrrupt Status                    */
00139 
00140 
00141   LPC_USB->HcInterruptEnable  = OR_INTR_ENABLE_MIE |
00142                        OR_INTR_ENABLE_WDH |
00143                        OR_INTR_ENABLE_RHSC;
00144 
00145   NVIC_SetPriority(USB_IRQn, 0);       /* highest priority */
00146   /* Enable the USB Interrupt */
00147   NVIC_SetVector(USB_IRQn, (uint32_t)(sUsbIrqhandler));
00148   LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
00149   LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
00150   
00151   /* Check for any connected devices */
00152   if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS)  //Root device connected
00153   {
00154     //Device connected
00155     wait(1);
00156     DBG("Device connected (%08x)\n", LPC_USB->HcRhPortStatus1);
00157     onUsbDeviceConnected(0, 1); //Hub 0 (root hub), Port 1 (count starts at 1)
00158   }
00159   
00160   DBG("Enabling IRQ\n");
00161   NVIC_EnableIRQ(USB_IRQn);
00162   DBG("End of host stack initialization\n");
00163   return USBERR_OK;
00164 }
00165 
00166 void UsbHostMgr::poll() //Enumerate connected devices, etc
00167 {
00168   /* Check for any connected devices */
00169   if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS)  //Root device connected
00170   {
00171     //Device connected
00172     wait(1);
00173     DBG("Device connected (%08x)\n", LPC_USB->HcRhPortStatus1);
00174     onUsbDeviceConnected(0, 1); //Hub 0 (root hub), Port 1 (count starts at 1)
00175   }
00176   
00177   for(int i = 0; i < devicesCount(); i++)
00178   {
00179     if( (m_lpDevices[i]->m_connected)
00180     &&  !(m_lpDevices[i]->m_enumerated) )
00181     {
00182       m_lpDevices[i]->enumerate();
00183       return;
00184     }
00185   }
00186 }
00187 
00188 int UsbHostMgr::devicesCount()
00189 {
00190   int i;
00191   for(i = 0; i < USB_HOSTMGR_MAX_DEVS; i++)
00192   {
00193     if (m_lpDevices[i] == NULL)
00194       break;
00195   }
00196   return i;
00197 }
00198 
00199 UsbDevice* UsbHostMgr::getDevice(int item)
00200 {
00201   UsbDevice* pDev = m_lpDevices[item];
00202   if(!pDev)
00203     return NULL;
00204     
00205   pDev->m_refs++;
00206   return pDev;
00207 }
00208 
00209 void UsbHostMgr::releaseDevice(UsbDevice* pDev)
00210 {
00211   pDev->m_refs--;
00212   if(pDev->m_refs > 0)
00213     return;
00214   //If refs count = 0, delete
00215   //Find & remove from list
00216   int i;
00217   for(i = 0; i < USB_HOSTMGR_MAX_DEVS; i++)
00218   {
00219     if (m_lpDevices[i] == pDev)
00220       break;
00221   }
00222   if(i!=USB_HOSTMGR_MAX_DEVS)
00223     memmove(&m_lpDevices[i], &m_lpDevices[i+1], sizeof(UsbDevice*) * (USB_HOSTMGR_MAX_DEVS - (i + 1))); //Safer than memcpy because of overlapping mem
00224   m_lpDevices[USB_HOSTMGR_MAX_DEVS - 1] = NULL;
00225   delete pDev;
00226 }
00227 
00228 void UsbHostMgr::UsbIrqhandler()
00229 {
00230   uint32_t   int_status;
00231   uint32_t   ie_status;
00232   
00233   int_status    = LPC_USB->HcInterruptStatus;                          /* Read Interrupt Status                */
00234   ie_status     = LPC_USB->HcInterruptEnable;                          /* Read Interrupt enable status         */
00235 
00236   if (!(int_status & ie_status))
00237   {
00238     return;
00239   }
00240   else
00241   {
00242     int_status = int_status & ie_status;
00243     if (int_status & OR_INTR_STATUS_RHSC) /* Root hub status change interrupt     */
00244     {
00245       DBG("LPC_USB->HcRhPortStatus1 = %08x\n", LPC_USB->HcRhPortStatus1);
00246       if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CSC)
00247       {
00248         if (LPC_USB->HcRhStatus & OR_RH_STATUS_DRWE)
00249         {
00250             /*
00251              * When DRWE is on, Connect Status Change
00252              * means a remote wakeup event.
00253             */
00254             //HOST_RhscIntr = 1;// JUST SOMETHING FOR A BREAKPOINT
00255         }
00256         else
00257         {
00258           /*
00259            * When DRWE is off, Connect Status Change
00260            * is NOT a remote wakeup event
00261           */
00262           if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS)  //Root device connected
00263           {
00264             //Device connected
00265             DBG("Device connected (%08x)\n", LPC_USB->HcRhPortStatus1);
00266             onUsbDeviceConnected(0, 1); //Hub 0 (root hub), Port 1 (count starts at 1)
00267           }
00268           else //Root device disconnected
00269           {
00270             //Device disconnected
00271             DBG("Device disconnected\n");
00272             onUsbDeviceDisconnected(0, 1);
00273           }
00274           //TODO: HUBS
00275         }
00276         LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
00277       }
00278       if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRSC)
00279       {
00280         LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
00281       }
00282     }  
00283     if (int_status & OR_INTR_STATUS_WDH) /* Writeback Done Head interrupt        */
00284     {                  
00285       //UsbEndpoint::sOnCompletion((LPC_USB->HccaDoneHead) & 0xFE);
00286       if(m_pHcca->DoneHead)
00287       {
00288         UsbEndpoint::sOnCompletion(m_pHcca->DoneHead);
00289         m_pHcca->DoneHead = 0;
00290         LPC_USB->HcInterruptStatus = OR_INTR_STATUS_WDH;
00291         if(m_pHcca->DoneHead)
00292           DBG("??????????????????????????????\n\n\n");
00293       }
00294       else
00295       {
00296         //Probably an error
00297         int_status = LPC_USB->HcInterruptStatus;
00298         DBG("HcInterruptStatus = %08x\n", int_status);
00299         if (int_status & OR_INTR_STATUS_UE) //Unrecoverable error, disconnect devices and resume
00300         {
00301           onUsbDeviceDisconnected(0, 1);
00302           LPC_USB->HcInterruptStatus = OR_INTR_STATUS_UE;
00303           LPC_USB->HcCommandStatus = 0x01; //Host Controller Reset
00304         }
00305       }
00306     }
00307     LPC_USB->HcInterruptStatus = int_status; /* Clear interrupt status register      */
00308   }
00309   return;
00310 }
00311 
00312 void UsbHostMgr::onUsbDeviceConnected(int hub, int port)
00313 {
00314   int item = devicesCount();
00315   if( item == USB_HOSTMGR_MAX_DEVS )
00316     return; //List full...
00317   //Find a free address (not optimized, but not really important)
00318   int i;
00319   int addr = 1;
00320   for(i = 0; i < item; i++)
00321   {
00322     addr = MAX( addr, m_lpDevices[i]->m_addr + 1 );
00323   }
00324   m_lpDevices[item] = new UsbDevice( this, hub, port, addr );
00325   m_lpDevices[item]->m_connected = true;
00326 }
00327 
00328 void UsbHostMgr::onUsbDeviceDisconnected(int hub, int port)
00329 {
00330   for(int i = 0; i < devicesCount(); i++)
00331   {
00332      if( (m_lpDevices[i]->m_hub == hub)
00333      &&  (m_lpDevices[i]->m_port == port) )
00334      {
00335        m_lpDevices[i]->m_connected = false;
00336        if(!m_lpDevices[i]->m_enumerated)
00337        {
00338          delete m_lpDevices[i];
00339          m_lpDevices[i] = NULL;
00340        }
00341        return;
00342      }
00343   }
00344 }
00345 
00346 void UsbHostMgr::resetPort(int hub, int port)
00347 {
00348   DBG("Resetting hub %d, port %d\n", hub, port);
00349   if(hub == 0) //Root hub
00350   {
00351     wait_ms(100); /* USB 2.0 spec says at least 50ms delay before port reset */
00352     LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS; // Initiate port reset
00353     DBG("Before loop\n");
00354     while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS)
00355       ;
00356     LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; // ...and clear port reset signal
00357     DBG("After loop\n");
00358     wait_ms(200); /* Wait for 100 MS after port reset  */
00359   }
00360   else
00361   {
00362     //TODO: Hubs
00363   }
00364   DBG("Port reset OK\n");
00365 }
00366 
00367 #endif