local fork (temporary)

Dependents:   VodafoneUSBModem_bleedingedge2

Fork of USBHostWANDongle_bleedingedge by Donatien Garnier

Revision:
0:ae46a0638b2c
Child:
1:49df46e3295c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBHost/USBHALHost.cpp	Thu May 24 16:39:35 2012 +0000
@@ -0,0 +1,404 @@
+/* Copyright (c) 2010-2011 mbed.org, MIT License
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+* and associated documentation files (the "Software"), to deal in the Software without
+* restriction, including without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or
+* substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
+
+
+#define __DEBUG__ 0 //WARN: ENABLING DEBUGGING HERE WILL PRINTF IN IRQS!! UNEXPECTED BEHAVIOUR MAY RESULT...
+#ifndef __MODULE__
+#define __MODULE__ "USBHALHost.cpp"
+#endif
+
+#include "dbg.h"
+#include <cstdint>
+
+#include "mbed.h"
+#include "USBHALHost.h"
+
+// bits of the USB/OTG clock control register
+#define HOST_CLK_EN     (1<<0)
+#define DEV_CLK_EN      (1<<1)
+#define PORTSEL_CLK_EN  (1<<3)
+#define AHB_CLK_EN      (1<<4)
+
+// bits of the USB/OTG clock status register
+#define HOST_CLK_ON     (1<<0)
+#define DEV_CLK_ON      (1<<1)
+#define PORTSEL_CLK_ON  (1<<3)
+#define AHB_CLK_ON      (1<<4)
+
+// we need host clock, OTG/portsel clock and AHB clock
+#define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN)
+
+
+
+#define HCCA_SIZE 0x100
+#define ED_SIZE 0x10
+#define TD_SIZE 0x10
+
+#define TOTAL_SIZE (HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE) + (MAX_TD*TD_SIZE))
+
+static volatile __align(256) uint8_t usb_buf[TOTAL_SIZE] __attribute((section("AHBSRAM0"),aligned));  //256 bytes aligned!
+
+USBHALHost * USBHALHost::instHost;
+
+USBHALHost::USBHALHost() : thread(USBHALHost::staticCb, (void*)this, osPriorityNormal, 4*128) {
+    instHost = this;
+    memInit();
+    memset((void*)usb_hcca, 0, HCCA_SIZE);
+    for (int i = 0; i < MAX_ENDPOINT; i++) {
+        edBufAlloc[i] = false;
+    }
+    for (int i = 0; i < MAX_TD; i++) {
+        tdBufAlloc[i] = false;
+    }
+}
+
+
+void USBHALHost::init() {
+  thread.signal_set(USBHALHOST_SIG_INIT);
+}
+
+
+uint32_t USBHALHost::controlHeadED() {
+    return LPC_USB->HcControlHeadED;
+}
+
+uint32_t USBHALHost::bulkHeadED() {
+    return LPC_USB->HcBulkHeadED;
+}
+
+uint32_t USBHALHost::interruptHeadED() {
+    return usb_hcca->IntTable[0];
+}
+
+void USBHALHost::updateBulkHeadED(uint32_t addr) {
+    LPC_USB->HcBulkHeadED = addr;
+}
+
+
+void USBHALHost::updateControlHeadED(uint32_t addr) {
+    LPC_USB->HcControlHeadED = addr;
+}
+
+void USBHALHost::updateInterruptHeadED(uint32_t addr) {
+    usb_hcca->IntTable[0] = addr;
+}
+
+
+void USBHALHost::enableControlList() {
+    LPC_USB->HcCommandStatus = OR_CMD_STATUS_CLF;
+    LPC_USB->HcControl       |= OR_CONTROL_CLE; //Enable control list
+}
+
+void USBHALHost::enableBulkList() {
+    LPC_USB->HcCommandStatus = OR_CMD_STATUS_BLF;
+    LPC_USB->HcControl       |= OR_CONTROL_BLE; //Enable bulk list
+}
+
+void USBHALHost::enableInterruptList() {
+    LPC_USB->HcControl |= OR_CONTROL_PLE;
+}
+
+bool USBHALHost::disableControlList() {
+    if(LPC_USB->HcControl & OR_CONTROL_CLE)
+    {
+      LPC_USB->HcControl       &= ~OR_CONTROL_CLE; //Disable control list
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+}
+
+bool USBHALHost::disableBulkList() {
+    if(LPC_USB->HcControl & OR_CONTROL_BLE)
+    {
+      LPC_USB->HcControl       &= ~OR_CONTROL_BLE; //Disable bulk list
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+}
+
+bool USBHALHost::disableInterruptList() {
+    if(LPC_USB->HcControl & OR_CONTROL_PLE)
+    {
+      LPC_USB->HcControl       &= ~OR_CONTROL_PLE; //Disable interrupt list
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+}
+
+//Lock processing
+void USBHALHost::lock()
+{
+  mtx.lock();
+}
+
+void USBHALHost::unlock()
+{
+  mtx.unlock();
+}
+
+void USBHALHost::memInit() {
+    usb_hcca = (volatile HCCA *)usb_buf;
+    usb_edBuf = usb_buf + HCCA_SIZE;
+    usb_tdBuf = usb_buf + HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE);
+}
+
+volatile uint8_t * USBHALHost::getED() {
+    for (int i = 0; i < MAX_ENDPOINT; i++) {
+        if ( !edBufAlloc[i] ) {
+            edBufAlloc[i] = true;
+            return (volatile uint8_t *)(usb_edBuf + i*ED_SIZE);
+        }
+    }
+    perror("Could not allocate ED\r\n");
+    return NULL; //Could not alloc ED
+}
+
+volatile uint8_t * USBHALHost::getTD() {
+    int i;
+    for (i = 0; i < MAX_TD; i++) {
+        if ( !tdBufAlloc[i] ) {
+            tdBufAlloc[i] = true;
+            return (volatile uint8_t *)(usb_tdBuf + i*TD_SIZE);
+        }
+    }
+    perror("Could not allocate TD\r\n");
+    return NULL; //Could not alloc TD
+}
+
+
+void USBHALHost::freeED(volatile uint8_t * ed) {
+    int i;
+    i = (ed - usb_edBuf) / ED_SIZE;
+    edBufAlloc[i] = false;
+}
+
+void USBHALHost::freeTD(volatile uint8_t * td) {
+    int i;
+    i = (td - usb_tdBuf) / TD_SIZE;
+    tdBufAlloc[i] = false;
+}
+
+
+void USBHALHost::resetPort(int hub, int port) {
+    DBG("Resetting hub %d, port %d\n", hub, port);
+    if (hub == 0) { //Root hub
+        // USB 2.0 spec says at least 50ms delay before port reset
+        Thread::wait(200);
+        LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS; // Initiate port reset
+        while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS);
+        LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; // ...and clear port reset signal
+        // Wait for 100 MS after port reset
+        Thread::wait(200);
+    } else {
+        //TODO: Hubs
+    }
+}
+
+
+void USBHALHost::_usbisr(void) {
+    if (instHost) {
+        instHost->UsbIrqhandler();
+    }
+}
+
+void USBHALHost::UsbIrqhandler() {
+  if( LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable ) //Is there something to actually process?
+  {
+    NVIC_DisableIRQ(USB_IRQn);
+    NVIC_ClearPendingIRQ(USB_IRQn);
+    thread.signal_set(USBHALHOST_SIG_IRQ); //Signal processing thread
+  }
+
+}
+
+void USBHALHost::process()
+{
+  DBG("USB Process started");
+
+  lock();
+  Thread::signal_wait(USBHALHOST_SIG_INIT);
+
+  NVIC_DisableIRQ(USB_IRQn);                           // Disable the USB interrupt source
+
+  LPC_SC->PCONP       &= ~(1UL<<31); //Cut power
+  Thread::wait(200);
+
+  // turn on power for USB
+  LPC_SC->PCONP       |= (1UL<<31);
+
+  // Enable USB host clock, port selection and AHB clock
+  LPC_USB->USBClkCtrl |= CLOCK_MASK;
+
+  // Wait for clocks to become available
+  while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK);
+
+  // it seems the bits[0:1] mean the following
+  // 0: U1=device, U2=host
+  // 1: U1=host, U2=host
+  // 2: reserved
+  // 3: U1=host, U2=device
+  // NB: this register is only available if OTG clock (aka "port select") is enabled!!
+  // since we don't care about port 2, set just bit 0 to 1 (U1=host)
+  LPC_USB->OTGStCtrl |= 1;
+
+  // now that we've configured the ports, we can turn off the portsel clock
+  LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN;
+
+  // configure USB D+/D- pins
+  // P0[29] = USB_D+, 01
+  // P0[30] = USB_D-, 01
+  LPC_PINCON->PINSEL1 &= ~((3<<26) | (3<<28));
+  LPC_PINCON->PINSEL1 |=  ((1<<26)|(1<<28));     // 0x14000000
+
+  LPC_USB->HcControl       = 0;                       // HARDWARE RESET
+  LPC_USB->HcControlHeadED = 0;                       // Initialize Control list head to Zero
+  LPC_USB->HcBulkHeadED    = 0;                       // Initialize Bulk list head to Zero
+
+  //wait_ms(100);                                   // Wait 50 ms before apply reset
+  Thread::wait(100);
+
+  // SOFTWARE RESET
+  LPC_USB->HcCommandStatus = OR_CMD_STATUS_HCR;
+  LPC_USB->HcFmInterval    = DEFAULT_FMINTERVAL;      // Write Fm Interval and Largest Data Packet Counter
+  LPC_USB->HcPeriodicStart = FI * 90 / 100;
+
+  // Put HC in operational state
+  LPC_USB->HcControl  = (LPC_USB->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
+  LPC_USB->HcRhStatus = OR_RH_STATUS_LPSC;            // Set Global Power
+
+  LPC_USB->HcHCCA = (uint32_t)(usb_hcca);
+  LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus;                   // Clear Interrrupt Status
+
+  LPC_USB->HcInterruptEnable  = OR_INTR_ENABLE_MIE | OR_INTR_ENABLE_WDH | OR_INTR_ENABLE_RHSC;
+
+  //DG: Do not set prio
+  //NVIC_SetPriority(USB_IRQn, 0);       // highest priority
+  // Enable the USB Interrupt
+  NVIC_SetVector(USB_IRQn, (uint32_t)(_usbisr));
+  LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
+  LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
+
+  NVIC_EnableIRQ(USB_IRQn);
+
+
+  // Check for any connected devices
+  if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS) { //Root device connected
+      //Device connected
+      Thread::wait(500);
+      DBG("Device connected (%08x)\n\r", LPC_USB->HcRhPortStatus1);
+      deviceConnected(0, 1, LPC_USB->HcRhPortStatus1 & OR_RH_PORT_LSDA); //Hub 0 (root hub), Port 1 (count starts at 1), Low or High speed
+  }
+
+  unlock();
+
+
+  for(;;)
+  {
+    Thread::signal_wait(USBHALHOST_SIG_IRQ); //Wait for IRQ to process
+
+    lock();
+    DBG("Locked");
+
+    WARN("isr %08x [EN %08x]", LPC_USB->HcInterruptStatus, LPC_USB->HcInterruptEnable);
+
+    //Now process IRQ
+    uint32_t int_status = LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable;
+
+    if (int_status & OR_INTR_STATUS_RHSC)
+    { // Root hub status change interrupt
+      WARN("Port status %08x", LPC_USB->HcRhPortStatus1);
+      if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CSC)
+      {
+        if (LPC_USB->HcRhStatus & OR_RH_STATUS_DRWE)
+        {
+
+          // When DRWE is on, Connect Status Change
+          // means a remote wakeup event.
+
+        }
+        else
+        {
+
+          // When DRWE is off, Connect Status Change
+          // is NOT a remote wakeup event
+
+          if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS)
+          { //Root device connected
+            //Device connected
+            WARN("Device connected!!");
+           // Thread::wait(500);
+            deviceConnected(0, 1, LPC_USB->HcRhPortStatus1 & OR_RH_PORT_LSDA); //Hub 0 (root hub), Port 1 (count starts at 1), Low or High speed
+          }
+          else
+          { //Root device disconnected
+            //Device disconnected
+            WARN("Device disconnected!!");
+            Thread::wait(500);
+            if (!(int_status & OR_INTR_STATUS_WDH))
+            {
+              usb_hcca->DoneHead = 0;
+            }
+            deviceDisconnected(0, 1, usb_hcca->DoneHead & 0xFFFFFFFE);
+            if (int_status & OR_INTR_STATUS_WDH)
+            {
+              usb_hcca->DoneHead = 0;
+              LPC_USB->HcInterruptStatus = OR_INTR_STATUS_WDH;
+            }
+
+          }
+          //TODO: HUBS
+        }
+        LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
+      }
+      if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRSC)
+      {
+        LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
+        //int_status &= ~OR_RH_PORT_PRSC;
+      }
+      LPC_USB->HcInterruptStatus = OR_INTR_STATUS_RHSC;
+    }
+
+    if (int_status & OR_INTR_STATUS_WDH)
+    { // Writeback Done Head interrupt
+      transferCompleted(usb_hcca->DoneHead & 0xFFFFFFFE);
+      LPC_USB->HcInterruptStatus = OR_INTR_STATUS_WDH;
+    }
+
+    //IRQ Processed
+
+    DBG("Unlocked");
+
+    NVIC_EnableIRQ(USB_IRQn);
+
+    unlock();
+
+  }
+}
+
+/*static*/ void USBHALHost::staticCb(void const* p)
+{
+  ((USBHALHost*)p)->process();
+}