summaryrefslogtreecommitdiff
path: root/usblib/host/usbhostenum.c
diff options
context:
space:
mode:
authorYuval Adam <yuv.adm@gmail.com>2012-10-29 23:08:53 +0200
committerYuval Adam <yuv.adm@gmail.com>2012-10-29 23:08:53 +0200
commitc241dbd7e78c50327781a35d88d9f2db7ff2b271 (patch)
tree2c84fe512c0cd3ee328244bca2f8ed4f9622074d /usblib/host/usbhostenum.c
parent4ba8614c006f9828f0796c140bc3e13c9e67938c (diff)
Added usblib
Diffstat (limited to 'usblib/host/usbhostenum.c')
-rw-r--r--usblib/host/usbhostenum.c5694
1 files changed, 5694 insertions, 0 deletions
diff --git a/usblib/host/usbhostenum.c b/usblib/host/usbhostenum.c
new file mode 100644
index 0000000..f3c8e81
--- /dev/null
+++ b/usblib/host/usbhostenum.c
@@ -0,0 +1,5694 @@
+//*****************************************************************************
+//
+// usbhostenum.c - Device enumeration code for the USB host library.
+//
+// Copyright (c) 2008-2012 Texas Instruments Incorporated. All rights reserved.
+// Software License Agreement
+//
+// Texas Instruments (TI) is supplying this software for use solely and
+// exclusively on TI's microcontroller products. The software is owned by
+// TI and/or its suppliers, and is protected under applicable copyright
+// laws. You may not combine this software with "viral" open-source
+// software in order to form a larger program.
+//
+// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
+// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
+// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
+// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+// DAMAGES, FOR ANY REASON WHATSOEVER.
+//
+// This is part of revision 9453 of the Stellaris USB Library.
+//
+//*****************************************************************************
+
+#include "inc/hw_ints.h"
+#include "inc/hw_memmap.h"
+#include "inc/hw_sysctl.h"
+#include "inc/hw_types.h"
+#include "driverlib/rom.h"
+#include "driverlib/rom_map.h"
+#include "driverlib/debug.h"
+#include "driverlib/interrupt.h"
+#include "driverlib/sysctl.h"
+#include "driverlib/udma.h"
+#include "driverlib/usb.h"
+#include "driverlib/rtos_bindings.h"
+#include "usblib/usblib.h"
+#include "usblib/usblibpriv.h"
+#include "usblib/host/usbhost.h"
+#include "usblib/host/usbhostpriv.h"
+#include "usblib/host/usbhhub.h"
+
+#ifdef INCLUDE_DEBUG_OUTPUT
+#include "utils/uartstdio.h"
+#define DEBUG_OUTPUT UARTprintf
+#else
+#define DEBUG_OUTPUT while(0)((int (*)(char *, ...))0)
+#endif
+
+//*****************************************************************************
+//
+//! \addtogroup usblib_hcd
+//! @{
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// External prototypes.
+//
+//*****************************************************************************
+extern tUSBMode g_eUSBMode;
+
+extern void OTGDeviceDisconnect(unsigned long ulIndex);
+
+//*****************************************************************************
+//
+// Internal function prototypes.
+//
+//*****************************************************************************
+static void USBHCDEP0StateTx(void);
+static void USBHCDEnumHandler(void);
+static void USBHCDClearFeature(unsigned long ulDevAddress,
+ unsigned long ulEndpoint,
+ unsigned long ulFeature);
+
+//*****************************************************************************
+//
+// Automatic power enable.
+//
+//*****************************************************************************
+#define USB_HOST_PWREN_AUTO 0x00000002
+
+//*****************************************************************************
+//
+// Flags used to signal between the interrupt handler and USBHCDMain().
+//
+//*****************************************************************************
+#define INT_EVENT_VBUS_ERR 0x01
+#define INT_EVENT_CONNECT 0x02
+#define INT_EVENT_DISCONNECT 0x04
+#define INT_EVENT_POWER_FAULT 0x08
+#define INT_EVENT_SOF 0x10
+#define INT_EVENT_ENUM 0x20
+
+volatile unsigned long g_ulUSBHIntEvents;
+
+//*****************************************************************************
+//
+// Flags used to indicate that a uDMA transfer is pending on a pipe.
+//
+//*****************************************************************************
+#define DMA_PEND_TRANSMIT_FLAG 0x10000
+#define DMA_PEND_RECEIVE_FLAG 0x1
+
+volatile unsigned long g_ulDMAPending = 0;
+
+//*****************************************************************************
+//
+// Flag used to indicate that a workaround should be applied when using
+// uDMA with USB. The uDMA transfers must match the USB FIFO size when
+// with Rev A0 silicon.
+//
+//*****************************************************************************
+static unsigned long g_bUseDMAWA = 0;
+
+//*****************************************************************************
+//
+// This holds the current power configuration that is used when USBHCDInit()
+// is called.
+//
+//*****************************************************************************
+static unsigned long g_ulPowerConfig = USBHCD_VBUS_AUTO_HIGH;
+
+//*****************************************************************************
+//
+// The states for endpoint 0 during enumeration.
+//
+//*****************************************************************************
+typedef enum
+{
+ //
+ // The USB device is waiting on a request from the host controller on
+ // endpoint 0.
+ //
+ EP0_STATE_IDLE,
+
+ //
+ // Setup packet is expecting data IN.
+ //
+ EP0_STATE_SETUP_IN,
+
+ //
+ // Setup packet is sending data OUT.
+ //
+ EP0_STATE_SETUP_OUT,
+
+ //
+ // The USB device is receiving data from the device due to an SETUP IN
+ // request.
+ //
+ EP0_STATE_RX,
+
+ //
+ // The USB device has completed the IN or OUT request and is now waiting
+ // for the host to acknowledge the end of the IN/OUT transaction. This
+ // is the status phase for a USB control transaction.
+ //
+ EP0_STATE_STATUS,
+
+ //
+ // This state is for when a response only has a status phase and no
+ // data phase.
+ //
+ EP0_STATE_STATUS_IN,
+
+ //
+ // This endpoint has signaled a stall condition and is waiting for the
+ // stall to be acknowledged by the host controller.
+ //
+ EP0_STATE_STALL,
+
+ //
+ // An error has occurred on endpoint 0.
+ //
+ EP0_STATE_ERROR
+}
+tEP0State;
+
+//*****************************************************************************
+//
+// This structure holds the full state for the device enumeration.
+//
+//*****************************************************************************
+typedef struct
+{
+ //
+ // This is the pointer to the current data being sent out or received
+ // on endpoint 0.
+ //
+ unsigned char *pData;
+
+ //
+ // This is the number of bytes that remain to be sent from or received
+ // into the g_DeviceState.pEP0Data data buffer.
+ //
+ volatile unsigned long ulBytesRemaining;
+
+ //
+ // The amount of data being sent/received due to a request.
+ //
+ unsigned long ulDataSize;
+
+ //
+ // This is the current device address in use by endpoint 0.
+ //
+ unsigned long ulDevAddress;
+
+ //
+ // The maximum packet size for the device responding to the setup packet.
+ //
+ unsigned long ulMaxPacketSize;
+
+ //
+ // The host controller's state.
+ //
+ tEP0State eState;
+}
+tHostState;
+
+//*****************************************************************************
+//
+// This variable holds the current state of endpoint 0.
+//
+//*****************************************************************************
+static volatile tHostState g_sUSBHEP0State =
+{
+ 0, // pData
+ 0, // ulBytesRemaining
+ 0, // ulDataSize
+ 0, // ulDevAddress
+ 0, // ulMaxPacketSize
+ EP0_STATE_IDLE // eState
+};
+
+//*****************************************************************************
+//
+// The global delay time for use by SysCtlDelay() function. This is
+// initialized to an appropriate value for a 50MHz clock. The correct value
+// will be set in USBHCDInit().
+//
+//*****************************************************************************
+static unsigned long g_ulTickms = (50000000 / 3000);
+static volatile unsigned long g_ulCurrentTick = 0;
+
+//*****************************************************************************
+//
+// The current active drivers.
+//
+//*****************************************************************************
+static long g_lUSBHActiveDriver[MAX_USB_DEVICES + 1];
+static void *g_pvDriverInstance[MAX_USB_DEVICES + 1];
+
+//*****************************************************************************
+//
+// This is the structure used to hold the information for a given USB pipe
+// that is attached to a device.
+//
+//*****************************************************************************
+typedef struct
+{
+ //
+ // The current address for this pipe.
+ //
+ tUSBHostDevice *psDevice;
+
+ //
+ // The current address for this pipe.
+ //
+ unsigned char ucEPNumber;
+
+ //
+ // The DMA channel assigned to this endpoint.
+ //
+ unsigned char ucDMAChannel;
+
+ //
+ // The current type for this pipe.
+ //
+ unsigned long ulType;
+
+ //
+ // The millisecond interval for this pipe.
+ //
+ unsigned long ulInterval;
+
+ //
+ // The next tick value to trigger and event on this pipe.
+ //
+ unsigned long ulNextEventTick;
+
+ //
+ // The current call back for this pipe.
+ //
+ tHCDPipeCallback pfnCallback;
+
+ //
+ // The pointer to which IN data must be copied.
+ //
+ unsigned char *pucReadPtr;
+
+ //
+ // The number of bytes of read data to copy.
+ //
+ unsigned long ulReadSize;
+
+ //
+ // The state of a given USB pipe.
+ //
+ volatile enum
+ {
+ PIPE_READING,
+ PIPE_DATA_READY,
+ PIPE_DATA_SENT,
+ PIPE_WRITING,
+ PIPE_STALLED,
+ PIPE_ERROR,
+ PIPE_IDLE,
+ PIPE_DISABLED
+ }
+ eState;
+
+ //
+ // The actual FIFO offset allocated to this endpoint.
+ //
+ unsigned short usFIFOAddr;
+
+ //
+ // The size of the FIFO entry based on the size parameter. These are
+ // equivalent to the USB_FIFO_SZ_* values in usb.h.
+ //
+ unsigned char ucFIFOSize;
+
+ //
+ // The bit offset in the allocation structure.
+ //
+ unsigned char ucFIFOBitOffset;
+}
+tUSBHCDPipe;
+
+//*****************************************************************************
+//
+// The internal state of the device.
+//
+//*****************************************************************************
+typedef enum
+{
+ HCD_DEV_DISCONNECTED,
+ HCD_DEV_CONNECTED,
+ HCD_DEV_REQUEST,
+ HCD_DEV_RESET,
+ HCD_DEV_ADDRESSED,
+ HCD_DEV_CONFIGURED,
+ HCD_DEV_GETSTRINGS,
+ HCD_DEV_ERROR,
+ HCD_VBUS_ERROR,
+ HCD_POWER_FAULT,
+ HCD_IDLE
+}
+tUSBHDeviceState;
+
+static void ProcessUSBDeviceStateMachine(tUSBHDeviceState eOldState,
+ unsigned long ulDevIndex);
+
+//*****************************************************************************
+//
+// This is a fixed number as it relates to the maximum number of USB pipes
+// available on any USB controller. The actual number on a given device may
+// be less than this number.
+//
+//*****************************************************************************
+#define MAX_NUM_PIPES 15
+
+//*****************************************************************************
+//
+// This is a fixed number as it relates to the number of USB pipes available
+// in the USB controller.
+//
+//*****************************************************************************
+#define MAX_NUM_DMA_CHANNELS 6
+
+//*****************************************************************************
+//
+// Marker for an unused DMA channel slot.
+//
+//*****************************************************************************
+#define USBHCD_DMA_UNUSED 0xff
+
+//*****************************************************************************
+//
+// These definitions are used to manipulate the values returned as allocated
+// USB pipes.
+//
+//*****************************************************************************
+#define EP_PIPE_TYPE_LOW_SPEED 0x02000000
+#define EP_PIPE_USE_UDMA 0x01000000
+#define EP_PIPE_TYPE_ISOC 0x00800000
+#define EP_PIPE_TYPE_INTR 0x00400000
+#define EP_PIPE_TYPE_BULK 0x00200000
+#define EP_PIPE_TYPE_CONTROL 0x00100000
+#define EP_PIPE_TYPE_IN 0x00020000
+#define EP_PIPE_TYPE_OUT 0x00010000
+#define EP_PIPE_IDX_M 0x0000ffff
+
+//*****************************************************************************
+//
+// This creates a USB pipe handle from an index.
+//
+//*****************************************************************************
+#define OUT_PIPE_HANDLE(ulIdx) (g_sUSBHCD.USBOUTPipes[ulIdx].ulType | ulIdx)
+#define IN_PIPE_HANDLE(ulIdx) (g_sUSBHCD.USBINPipes[ulIdx].ulType | ulIdx)
+
+//*****************************************************************************
+//
+// Converts from an endpoint specifier to the offset of the endpoint's
+// control/status registers.
+//
+//*****************************************************************************
+#define EP_OFFSET(Endpoint) (Endpoint - 0x10)
+
+//*****************************************************************************
+//
+// This structure holds the state information for a given host controller.
+//
+//*****************************************************************************
+typedef struct
+{
+ unsigned long ulUSBBase;
+
+ tUSBHCDPipe USBControlPipe;
+ tUSBHCDPipe USBOUTPipes[MAX_NUM_PIPES];
+ tUSBHCDPipe USBINPipes[MAX_NUM_PIPES];
+ unsigned char ucDMAChannels[MAX_NUM_DMA_CHANNELS];
+
+ //
+ // Each devices state. We support a total of (MAX_USB_DEVICES + 1) devices
+ // to allow for the use if MAX_USB_DEVICES through a single hub (which is
+ // itself a device).
+ //
+ tUSBHostDevice USBDevice[MAX_USB_DEVICES + 1];
+
+ //
+ // Holds the current state of the device.
+ //
+ volatile tUSBHDeviceState eDeviceState[MAX_USB_DEVICES + 1];
+
+ //
+ // Pointer to the memory pool for this controller.
+ //
+ void *pvPool;
+
+ //
+ // The pool size for this controller.
+ //
+ unsigned long ulPoolSize;
+
+ //
+ // The number of endpoint pairs supported by the controller.
+ //
+ unsigned long ulNumEndpoints;
+
+ //
+ // The class drivers for this controller.
+ //
+ const tUSBHostClassDriver * const *pClassDrivers;
+
+ //
+ // The number of class drivers.
+ //
+ unsigned long ulNumClassDrivers;
+
+ //
+ // This is the index in the driver list of the event driver.
+ //
+ long lEventDriver;
+
+ //
+ // These are the generic event information used by the event driver.
+ //
+ unsigned long ulEventEnables;
+
+ unsigned long ulClass;
+}
+tUSBHCD;
+
+//*****************************************************************************
+//
+// The global to hold all of the state information for a given host controller.
+//
+//*****************************************************************************
+static tUSBHCD g_sUSBHCD;
+
+//*****************************************************************************
+//
+// Return the device index from a ulInstance value passed from an external
+// source.
+//
+//*****************************************************************************
+static unsigned char
+HCDInstanceToDevIndex(unsigned long ulInstance)
+{
+ unsigned long ulDevIndex;
+
+ //
+ // Get the device instance from the instance value.
+ //
+ ulDevIndex = (ulInstance & 0xff);
+
+ //
+ // If the above math went negative or is too large just return 0xff.
+ //
+ if(ulDevIndex > MAX_USB_DEVICES)
+ {
+ ulDevIndex = 0xff;
+ }
+
+ return(ulDevIndex);
+}
+
+//=============================================================================
+//
+// This is the internal function that will map an event to a valid event flag.
+//
+// \param ulEvent specifies which event flag to retrieve.
+//
+// \return The event flag or 0 if there is no support event flag for the
+// event specified by the \e ulEvent parameter.
+//
+//=============================================================================
+static unsigned long
+GetEventFlag(unsigned long ulEvent)
+{
+ unsigned long ulEventFlag;
+
+ ulEventFlag = 0;
+
+ //
+ // Search for a valid event flag for the requested event.
+ //
+ switch(ulEvent)
+ {
+ case USB_EVENT_SOF:
+ {
+ ulEventFlag |= USBHCD_EVFLAG_SOF;
+ break;
+ }
+ case USB_EVENT_CONNECTED:
+ {
+ ulEventFlag |= USBHCD_EVFLAG_CONNECT;
+ break;
+ }
+ case USB_EVENT_DISCONNECTED:
+ {
+ ulEventFlag |= USBHCD_EVFLAG_DISCNCT;
+ break;
+ }
+ case USB_EVENT_UNKNOWN_CONNECTED:
+ {
+ ulEventFlag |= USBHCD_EVFLAG_UNKCNCT;
+ break;
+ }
+ case USB_EVENT_POWER_FAULT:
+ {
+ ulEventFlag |= USBHCD_EVFLAG_PWRFAULT;
+ break;
+ }
+ case USB_EVENT_POWER_DISABLE:
+ {
+ ulEventFlag |= USBHCD_EVFLAG_PWRDIS;
+ break;
+ }
+ case USB_EVENT_POWER_ENABLE:
+ {
+ ulEventFlag |= USBHCD_EVFLAG_PWREN;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ return(ulEventFlag);
+}
+
+//=============================================================================
+//
+//! This function is called to enable a specific USB HCD event notification.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//! \param pvEventDriver is the event driver structure that was passed into
+//! the USBHCDRegisterDrivers() function as part of the array of
+//! tUSBHostClassDriver structures.
+//! \param ulEvent is the event to enable.
+//!
+//! This function is called to enable event callbacks for a specific USB HCD
+//! event. The requested event is passed in the \e ulEvent parameter. Not
+//! all events can be enables so the function will return zero if the event
+//! provided cannot be enabled. The \e pvEventDriver is a pointer to the
+//! event driver structure that the caller passed into the
+//! USBHCDRegisterDrivers() function. This structure is typically declared
+//! with the DECLARE_EVENT_DRIVER() macro and included as part of the array
+//! of pointers to tUSBHostClassDriver structures that is passed to the
+//! USBHCDRegisterDrivers() function.
+//!
+//! \return This function returns a non-zero number if the event was
+//! successfully enabled and returns zero if the event cannot be enabled.
+//
+//=============================================================================
+long
+USBHCDEventEnable(unsigned long ulIndex, void *pvEventDriver,
+ unsigned long ulEvent)
+{
+ long lRet;
+ unsigned long ulEventFlag;
+
+ ASSERT(ulIndex == 0);
+
+ //
+ // Default the return to fail the call unless a valid event is found.
+ //
+ lRet = 0;
+
+ //
+ // Get the event flag for this event.
+ //
+ ulEventFlag = GetEventFlag(ulEvent);
+
+ //
+ // Check if there was an event flag for the corresponding event.
+ //
+ if(ulEventFlag)
+ {
+ //
+ // Set the enable for this event.
+ //
+ g_sUSBHCD.ulEventEnables |= ulEventFlag;
+
+ //
+ // Indicate that the event was valid and is now enabled.
+ //
+ lRet = 1;
+ }
+
+ return(lRet);
+}
+
+//=============================================================================
+//
+//! This function is called to disable a specific USB HCD event notification.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//! \param pvEventDriver is the event driver structure that was passed into
+//! the USBHCDRegisterDrivers() function as part of the array of
+//! tUSBHostClassDriver structures.
+//! \param ulEvent is the event to disable.
+//!
+//! This function is called to disable event callbacks for a specific USB HCD
+//! event. The requested event is passed in the \e ulEvent parameter. Not
+//! all events can be enables so the function will return zero if the event
+//! provided cannot be enabled. The \e pvEventDriver is a pointer to the
+//! event driver structure that the caller passed into the
+//! USBHCDRegisterDrivers() function. This structure is typically declared
+//! with the DECLARE_EVENT_DRIVER() macro and included as part of the array
+//! of pointers to tUSBHostClassDriver structures that is passed to the
+//! USBHCDRegisterDrivers() function.
+//!
+//! \return This function returns a non-zero number if the event was
+//! successfully disabled and returns zero if the event cannot be disabled.
+//
+//=============================================================================
+long
+USBHCDEventDisable(unsigned long ulIndex, void *pvEventDriver,
+ unsigned long ulEvent)
+{
+ long lRet;
+ unsigned long ulEventFlag;
+
+ ASSERT(ulIndex == 0);
+
+ //
+ // Default the return to fail the call unless a valid event is found.
+ //
+ lRet = 0;
+
+ //
+ // Get the event flag for this event.
+ //
+ ulEventFlag = GetEventFlag(ulEvent);
+
+ //
+ // Check if there was an event flag for the corresponding event.
+ //
+ if(ulEventFlag)
+ {
+ //
+ // Clear the enable for this event.
+ //
+ g_sUSBHCD.ulEventEnables &= ~ulEventFlag;
+
+ //
+ // Indicate that the event was valid and is now disabled.
+ //
+ lRet = 1;
+ }
+
+ return(lRet);
+}
+
+//*****************************************************************************
+//
+// If there is an event driver this function will send out a generic connection
+// event USB_EVENT_UNKNOWN_CONNECTED indicating that an unknown connection
+// event has occurred.
+//
+//*****************************************************************************
+static void
+SendUnknownConnect(unsigned long ulIndex, unsigned long ulClass)
+{
+ tEventInfo sEvent;
+
+ //
+ // If there is an event driver registered and it has a event handler and
+ // the USBHCD_EVFLAG_UNKCNCT is enabled then call the function.
+ //
+ sEvent.ulEvent = USB_EVENT_UNKNOWN_CONNECTED;
+ sEvent.ulInstance = ulClass;
+ InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_UNKCNCT);
+}
+
+//*****************************************************************************
+//
+// Internal memory allocation space is two unsigned long values where each
+// bit represents a 64 byte block in the FIFO. This requires 64 bits for
+// the 4096 bytes of FIFO available.
+//
+//*****************************************************************************
+static unsigned long g_ulAlloc[2];
+
+//*****************************************************************************
+//
+// This function handles freeing FIFO memory that has been allocated using the
+// FIFOAlloc() function.
+//
+//*****************************************************************************
+static void
+FIFOFree(tUSBHCDPipe *pUSBPipe)
+{
+ unsigned long ulMask;
+
+ //
+ // Calculate the mask value to use to clear off the allocated blocks used
+ // by the USB pipe specified by pUSBPipe.
+ //
+ ulMask = (1 << (pUSBPipe->ucFIFOSize - 2)) - 1;
+ ulMask = ulMask << pUSBPipe->ucFIFOBitOffset;
+
+ //
+ // Determine which 32 bit word to access based on the size.
+ //
+ if(pUSBPipe->ucFIFOSize > USB_FIFO_SZ_64)
+ {
+ //
+ // If the FIFO size is greater than 64 then use the upper 32 bits.
+ //
+ g_ulAlloc[1] &= ~ulMask;
+ }
+ else
+ {
+ //
+ // If the FIFO size is less than or equal to 64 then use the lower
+ // 32 bits.
+ //
+ g_ulAlloc[0] &= ~ulMask;
+ }
+}
+
+//*****************************************************************************
+//
+// This function is used to allocate FIFO memory to a given USB pipe.
+//
+// \param pUSBPipe is the USB pipe that needs FIFO memory allocated.
+// \param ulSize is the minimum size in bytes of the FIFO to allocate.
+//
+// This function will allocate \e ulSize bytes to the USB pipe in the
+// \e pUSBPipe parameter. The function will fill the pUSBPipe structure
+// members ucFIFOSize and ucFIFOAddr with values that can be used with the
+// USBFIFOConfigSet() API. This allocation uses a first fit algorithm.
+//
+// \return This function returns the size of the block allocated.
+//
+//*****************************************************************************
+static unsigned long
+FIFOAlloc(tUSBHCDPipe *pUSBPipe, unsigned long ulSize)
+{
+ unsigned long ulBlocks, ulStart, ulBlockSize;
+ unsigned short usFIFOAddr;
+ unsigned long ulTemp, ulIndex;
+
+ //
+ // Save which 32 bit value to access, the upper is for blocks greater
+ // than 64 and the lower is for block 64 or less.
+ //
+ if(ulSize > 64)
+ {
+ ulIndex = 1;
+ }
+ else
+ {
+ ulIndex = 0;
+ }
+
+ //
+ // Initial FIFO address is 0.
+ //
+ usFIFOAddr = 0;
+
+ //
+ // Initialize the bit pattern and bit location.
+ //
+ ulBlocks = 1;
+ ulStart = 0;
+
+ //
+ // The initial block size is always the minimum size of 64 bytes.
+ //
+ ulBlockSize = 64;
+
+ //
+ // The initial size and offset are 64 and 0.
+ //
+ pUSBPipe->ucFIFOBitOffset = 0;
+ pUSBPipe->ucFIFOSize = 3;
+
+ //
+ // Scan through 32 bits looking for a memory block large enough to fill
+ // the request.
+ //
+ while(usFIFOAddr <= 32)
+ {
+ //
+ // If the pattern is zero then it is a possible match.
+ //
+ if((g_ulAlloc[ulIndex] & ulBlocks) == 0)
+ {
+ //
+ // If the size is large enough then save it and break out of the
+ // loop.
+ //
+ if(ulBlockSize >= ulSize)
+ {
+ //
+ // Mark the memory as allocated.
+ //
+ g_ulAlloc[ulIndex] |= ulBlocks;
+
+ break;
+ }
+
+ //
+ // Increment the size of the FIFO block.
+ //
+ pUSBPipe->ucFIFOSize++;
+
+ //
+ // Add in a new bit to the size of the allocation.
+ //
+ ulBlocks = ulBlocks | (ulBlocks << 1) ;
+
+ //
+ // Double the current size.
+ //
+ ulBlockSize <<= 1;
+
+ }
+ else
+ {
+ //
+ // Need to start over looking because the last allocation match
+ // failed, so reset the bit offset to the current location and the
+ // size to 64 bytes.
+ //
+ pUSBPipe->ucFIFOBitOffset = usFIFOAddr;
+ pUSBPipe->ucFIFOSize = 3;
+
+ //
+ // Reset the block size to the minimum (64 bytes).
+ //
+ ulBlockSize = 64;
+
+ //
+ // Store the current starting bit location and set the block mask
+ // to this value.
+ //
+ ulStart = 1 << usFIFOAddr;
+ ulBlocks = ulStart;
+ }
+
+ //
+ // Increase the address of the FIFO offset.
+ //
+ usFIFOAddr++;
+ }
+
+ //
+ // If there was no block large enough then fail this call.
+ //
+ if(usFIFOAddr > 32)
+ {
+ ulBlockSize = 0;
+ pUSBPipe->usFIFOAddr = 0;
+ pUSBPipe->ucFIFOBitOffset = 0;
+ pUSBPipe->ucFIFOSize = 0;
+ }
+ else
+ {
+ //
+ // Calculate the offset in the FIFO.
+ //
+ ulTemp = pUSBPipe->ucFIFOBitOffset * 64;
+
+ //
+ // Sizes greater than 64 are allocated in the second half of the FIFO
+ // memory space.
+ //
+ if(ulSize > 64)
+ {
+ ulTemp += 2048;
+ }
+
+ //
+ // Convert this to the value that can be set in the USB controller.
+ //
+ pUSBPipe->usFIFOAddr = (unsigned short)ulTemp;
+ }
+ return(ulBlockSize);
+}
+
+//*****************************************************************************
+//
+//! This function is used to allocate a USB HCD pipe.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//! \param ulEndpointType is the type of endpoint that this pipe will be
+//! communicating with.
+//! \param psDevice is the device instance associated with this endpoint.
+//! \param ulSize is the size of the FIFO in bytes.
+//! \param pfnCallback is the function that will be called when events occur on
+//! this USB Pipe.
+//!
+//! Since there are a limited number of USB HCD pipes that can be used in the
+//! host controller, this function is used to temporarily or permanently
+//! acquire one of the endpoints. Unlike the USBHCDPipeAlloc() function this
+//! function allows the caller to specify the size of the FIFO allocated to
+//! this endpoint in the \e ulSize parameter. This function also provides a
+//! method to register a callback for status changes on this endpoint. If no
+//! callbacks are desired then the \e pfnCallback function should be set to 0.
+//! The callback should be used when using the USBHCDPipeSchedule() function
+//! so that the caller is notified when the action is complete.
+//!
+//! \return This function returns a value indicating which pipe was reserved.
+//! If the value is 0 then there were no pipes currently available. This value
+//! should be passed to any USBHCDPipe APIs to indicate which pipe is being
+//! accessed.
+//
+//*****************************************************************************
+unsigned long
+USBHCDPipeAllocSize(unsigned long ulIndex, unsigned long ulEndpointType,
+ tUSBHostDevice *psDevice, unsigned long ulSize,
+ tHCDPipeCallback pfnCallback)
+{
+ long lIdx, lDMAIdx;
+ unsigned long ulHubAddr;
+
+ ASSERT(ulIndex == 0);
+
+ //
+ // Find a USB pipe that is free.
+ //
+ for(lIdx = 0; lIdx < MAX_NUM_PIPES; lIdx++)
+ {
+ //
+ // Handle OUT Pipes.
+ //
+ if(ulEndpointType & EP_PIPE_TYPE_OUT)
+ {
+ //
+ // A zero address indicates free.
+ //
+ if(g_sUSBHCD.USBOUTPipes[lIdx].psDevice == 0)
+ {
+ //
+ // Set up uDMA for the pipe.
+ //
+ if(ulEndpointType & EP_PIPE_USE_UDMA)
+ {
+ //
+ // First three endpoints have fixed channels on some
+ // parts so bias the pipes to match this as best is
+ // possible.
+ //
+ if(lIdx < 3)
+ {
+ //
+ // Check if the fixed channel is available for this
+ // USB pipe.
+ //
+ if((g_sUSBHCD.ucDMAChannels[1 + (lIdx * 2)] ==
+ USBHCD_DMA_UNUSED))
+ {
+ //
+ // The default channel was available so use it.
+ //
+ g_sUSBHCD.ucDMAChannels[1 + (lIdx * 2)] = lIdx;
+ g_sUSBHCD.USBOUTPipes[lIdx].ucDMAChannel =
+ 1 + (lIdx * 2);
+ }
+ else
+ {
+ //
+ // Go to the next USB pipe if the fixed one was not
+ // available.
+ //
+ continue;
+ }
+ }
+ else
+ {
+ //
+ // Either the fixed channel was not available or the
+ // pipe index was more than the first 3 pipes that are
+ // available on all parts.
+ //
+ for(lDMAIdx = 1; lDMAIdx < MAX_NUM_DMA_CHANNELS;
+ lDMAIdx += 2)
+ {
+ //
+ // Find any available channel.
+ //
+ if(g_sUSBHCD.ucDMAChannels[lDMAIdx] ==
+ USBHCD_DMA_UNUSED)
+ {
+ //
+ // Save the index and the DMA channel
+ // information.
+ //
+ g_sUSBHCD.ucDMAChannels[lDMAIdx] = lIdx;
+ g_sUSBHCD.USBOUTPipes[lIdx].ucDMAChannel =
+ lDMAIdx;
+ }
+ }
+ }
+
+ //
+ // If no DMA channel was available then just disable DMA
+ // on this pipe.
+ //
+ if(g_sUSBHCD.USBOUTPipes[lIdx].ucDMAChannel ==
+ USBHCD_DMA_UNUSED)
+ {
+ ulEndpointType &= ~EP_PIPE_USE_UDMA;
+ }
+ else
+ {
+ //
+ // Set the DMA channel for this endpoint, this has no
+ // effect on parts without configurable DMA.
+ //
+ MAP_USBEndpointDMAChannel(
+ USB0_BASE, INDEX_TO_USB_EP(lIdx + 1),
+ g_sUSBHCD.USBOUTPipes[lIdx].ucDMAChannel);
+
+ //
+ // Clear all the attributes for the channel
+ //
+ MAP_uDMAChannelAttributeDisable(
+ g_sUSBHCD.USBOUTPipes[lIdx].ucDMAChannel,
+ UDMA_ATTR_ALL);
+
+ //
+ // Configure the uDMA channel for the pipe
+ //
+ MAP_uDMAChannelControlSet(
+ g_sUSBHCD.USBOUTPipes[lIdx].ucDMAChannel,
+ (UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE |
+ UDMA_ARB_64));
+ }
+ }
+
+ //
+ // Save the endpoint type and device address and callback
+ // function.
+ //
+ g_sUSBHCD.USBOUTPipes[lIdx].ulType = ulEndpointType;
+ g_sUSBHCD.USBOUTPipes[lIdx].psDevice = psDevice;
+ g_sUSBHCD.USBOUTPipes[lIdx].pfnCallback = pfnCallback;
+
+ //
+ // Clear out any pending status on this endpoint in case it
+ // was in use before a allowing a new device class to use it.
+ //
+ MAP_USBHostEndpointStatusClear(USB0_BASE,
+ INDEX_TO_USB_EP(lIdx + 1),
+ USB_HOST_OUT_STATUS);
+
+ //
+ // Initialize the endpoint as idle.
+ //
+ g_sUSBHCD.USBOUTPipes[lIdx].eState = PIPE_IDLE;
+
+ //
+ // Allocate space in the FIFO for this endpoint.
+ //
+ if(FIFOAlloc(&g_sUSBHCD.USBOUTPipes[lIdx], ulSize) != 0)
+ {
+ //
+ // Configure the FIFO.
+ //
+ MAP_USBFIFOConfigSet(USB0_BASE, INDEX_TO_USB_EP(lIdx + 1),
+ g_sUSBHCD.USBOUTPipes[lIdx].usFIFOAddr,
+ g_sUSBHCD.USBOUTPipes[lIdx].ucFIFOSize,
+ USB_EP_HOST_OUT);
+ }
+
+ //
+ // Set the function address for this endpoint.
+ //
+ MAP_USBHostAddrSet(USB0_BASE, INDEX_TO_USB_EP(lIdx + 1),
+ psDevice->ulAddress, USB_EP_HOST_OUT);
+
+ //
+ // Set the hub and port address for the endpoint.
+ //
+ ulHubAddr = (psDevice->ucHub << 8) |
+ psDevice->ucHubPort;
+ USBHostHubAddrSet(USB0_BASE, INDEX_TO_USB_EP(lIdx + 1),
+ ulHubAddr, (USB_EP_HOST_OUT |
+ (psDevice->bLowSpeed ?
+ USB_EP_SPEED_LOW : USB_EP_SPEED_FULL)));
+
+ break;
+ }
+ }
+ //
+ // Handle IN Pipes.
+ //
+ else if(ulEndpointType & EP_PIPE_TYPE_IN)
+ {
+ //
+ // A zero address indicates free.
+ //
+ if(g_sUSBHCD.USBINPipes[lIdx].psDevice == 0)
+ {
+ //
+ // Set up uDMA for the pipe.
+ //
+ if(ulEndpointType & EP_PIPE_USE_UDMA)
+ {
+ //
+ // First three endpoints have fixed channels on some
+ // parts so bias the pipes to match this as best is
+ // possible.
+ //
+ if(lIdx < 3)
+ {
+ //
+ // Check if the fixed channel is available for this
+ // USB pipe.
+ //
+ if(g_sUSBHCD.ucDMAChannels[lIdx * 2] ==
+ USBHCD_DMA_UNUSED)
+ {
+ //
+ // The default channel was available so use it.
+ //
+ g_sUSBHCD.ucDMAChannels[lIdx * 2] = lIdx;
+ g_sUSBHCD.USBINPipes[lIdx].ucDMAChannel = lIdx * 2;
+ }
+ else
+ {
+ //
+ // Go to the next USB pipe if the fixed one was not
+ // available.
+ //
+ continue;
+ }
+ }
+ else
+ {
+ //
+ // Either the fixed channel was not available or the
+ // pipe index was more than the first 3 pipes that are
+ // available on all parts.
+ //
+ for(lDMAIdx = 0; lDMAIdx < MAX_NUM_DMA_CHANNELS;
+ lDMAIdx += 2)
+ {
+ //
+ // Find any available channel.
+ //
+ if(g_sUSBHCD.ucDMAChannels[lDMAIdx] ==
+ USBHCD_DMA_UNUSED)
+ {
+ //
+ // Save the index and the DMA channel
+ // information.
+ //
+ g_sUSBHCD.ucDMAChannels[lDMAIdx] = lIdx;
+ g_sUSBHCD.USBINPipes[lIdx].ucDMAChannel =
+ lDMAIdx;
+ }
+ }
+ }
+
+ //
+ // If no DMA channel was available then just disable DMA
+ // on this pipe.
+ //
+ if(g_sUSBHCD.USBINPipes[lIdx].ucDMAChannel ==
+ USBHCD_DMA_UNUSED)
+ {
+ ulEndpointType &= ~EP_PIPE_USE_UDMA;
+ }
+ else
+ {
+ //
+ // Set the DMA channel for this endpoint, this has no
+ // effect on parts without configurable DMA.
+ //
+ //
+ MAP_USBEndpointDMAChannel(
+ USB0_BASE, INDEX_TO_USB_EP(lIdx + 1),
+ g_sUSBHCD.USBINPipes[lIdx].ucDMAChannel);
+
+ //
+ // Clear all the attributes for the channel
+ //
+ MAP_uDMAChannelAttributeDisable(
+ g_sUSBHCD.USBINPipes[lIdx].ucDMAChannel,
+ UDMA_ATTR_ALL);
+
+ //
+ // Configure the uDMA channel for the pipe
+ //
+ MAP_uDMAChannelControlSet(
+ g_sUSBHCD.USBINPipes[lIdx].ucDMAChannel,
+ (UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
+ UDMA_ARB_64));
+ }
+ }
+
+ //
+ // Save the endpoint type and device address and callback
+ // function.
+ //
+ g_sUSBHCD.USBINPipes[lIdx].ulType = ulEndpointType;
+ g_sUSBHCD.USBINPipes[lIdx].psDevice = psDevice;
+ g_sUSBHCD.USBINPipes[lIdx].pfnCallback = pfnCallback;
+
+ //
+ // Clear out any pending status on this endpoint in case it
+ // was in use before a allowing a new device class to use it.
+ //
+ MAP_USBHostEndpointStatusClear(USB0_BASE,
+ INDEX_TO_USB_EP(lIdx + 1),
+ USB_HOST_IN_STATUS);
+
+ //
+ // Allocate space in the FIFO for this endpoint.
+ //
+ if(FIFOAlloc(&g_sUSBHCD.USBINPipes[lIdx], ulSize) != 0)
+ {
+ //
+ // Configure the FIFO.
+ //
+ MAP_USBFIFOConfigSet(USB0_BASE, INDEX_TO_USB_EP(lIdx + 1),
+ g_sUSBHCD.USBINPipes[lIdx].usFIFOAddr,
+ g_sUSBHCD.USBINPipes[lIdx].ucFIFOSize,
+ USB_EP_HOST_IN);
+ }
+
+ //
+ // Set the function address for this endpoint.
+ //
+ MAP_USBHostAddrSet(USB0_BASE, INDEX_TO_USB_EP(lIdx + 1),
+ psDevice->ulAddress, USB_EP_HOST_IN);
+
+ //
+ // Set the hub and port address for the endpoint.
+ //
+ ulHubAddr = (psDevice->ucHub << 8) |
+ psDevice->ucHubPort;
+ USBHostHubAddrSet(USB0_BASE, INDEX_TO_USB_EP(lIdx + 1),
+ ulHubAddr, (USB_EP_HOST_IN |
+ (psDevice->bLowSpeed ?
+ USB_EP_SPEED_LOW : USB_EP_SPEED_FULL)));
+
+ //
+ // Reset the state of the pipe to idle.
+ //
+ g_sUSBHCD.USBINPipes[lIdx].eState = PIPE_IDLE;
+
+ break;
+ }
+ }
+ }
+
+ //
+ // Did not find a free pipe.
+ //
+ if(lIdx == MAX_NUM_PIPES)
+ {
+ return(0);
+ }
+
+ //
+ // Return the pipe index and type that was allocated.
+ //
+ return(ulEndpointType | lIdx);
+}
+
+//*****************************************************************************
+//
+//! This function is used to allocate a USB HCD pipe.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//! \param ulEndpointType is the type of endpoint that this pipe will be
+//! communicating with.
+//! \param psDevice is the device instance associated with this endpoint.
+//! \param pfnCallback is the function that will be called when events occur on
+//! this USB Pipe.
+//!
+//! Since there are a limited number of USB HCD pipes that can be used in the
+//! host controller, this function is used to temporarily or permanently
+//! acquire one of the endpoints. It also provides a method to register a
+//! callback for status changes on this endpoint. If no callbacks are desired
+//! then the \e pfnCallback function should be set to 0. The callback should
+//! be used when using the USBHCDPipeSchedule() function so that the caller is
+//! notified when the action is complete.
+//!
+//! \return This function returns a value indicating which pipe was reserved.
+//! If the value is 0 then there were no pipes currently available. This value
+//! should be passed to any USBHCDPipe APIs to indicate which pipe is being
+//! accessed.
+//
+//*****************************************************************************
+unsigned long
+USBHCDPipeAlloc(unsigned long ulIndex, unsigned long ulEndpointType,
+ tUSBHostDevice *psDevice, tHCDPipeCallback pfnCallback)
+{
+ //
+ // The old API allocated only 64 bytes to each endpoint.
+ //
+ return(USBHCDPipeAllocSize(ulIndex, ulEndpointType, psDevice, 64,
+ pfnCallback));
+}
+
+//*****************************************************************************
+//
+//! This function is used to configure a USB HCD pipe.
+//!
+//! This should be called after allocating a USB pipe with a call to
+//! USBHCDPipeAlloc(). It is used to set the configuration associated with an
+//! endpoint like the max payload and target endpoint. The \e ulMaxPayload
+//! parameter is typically read directly from the devices endpoint descriptor
+//! and is expressed in bytes.
+//!
+//! Setting the \e ulInterval parameter depends on the type of endpoint being
+//! configured. For endpoints that do not need to use the \e ulInterval
+//! parameter \e ulInterval should be set to 0. For Bulk \e ulInterval is a
+//! value from 2-16 and will set the NAK timeout value as 2^(\e ulInterval-1)
+//! frames. For interrupt endpoints \e ulInterval is a value from 1-255 and
+//! is the count in frames between polling the endpoint. For isochronous
+//! endpoints \e ulInterval ranges from 1-16 and is the polling interval in
+//! frames represented as 2^(\e ulInterval-1) frames.
+//!
+//! \param ulPipe is the allocated endpoint to modify.
+//! \param ulMaxPayload is maximum data that can be handled per transaction.
+//! \param ulInterval is the polling interval for data transfers expressed in
+//! frames.
+//! \param ulTargetEndpoint is the target endpoint on the device to communicate
+//! with.
+//!
+//! \return If the call was successful, this function returns zero any other
+//! value indicates an error.
+//
+//*****************************************************************************
+unsigned long
+USBHCDPipeConfig(unsigned long ulPipe, unsigned long ulMaxPayload,
+ unsigned long ulInterval, unsigned long ulTargetEndpoint)
+{
+ unsigned long ulFlags;
+ unsigned long ulIndex;
+
+ //
+ // Get the index number from the allocated pipe.
+ //
+ ulIndex = (ulPipe & EP_PIPE_IDX_M);
+
+ //
+ // Set the direction.
+ //
+ if(ulPipe & EP_PIPE_TYPE_OUT)
+ {
+ //
+ // Set the mode for this endpoint.
+ //
+ if(g_sUSBHCD.USBOUTPipes[ulIndex].ulType & EP_PIPE_TYPE_BULK)
+ {
+ ulFlags = USB_EP_MODE_BULK;
+ }
+ else if(g_sUSBHCD.USBOUTPipes[ulIndex].ulType & EP_PIPE_TYPE_INTR)
+ {
+ ulFlags = USB_EP_MODE_INT;
+ }
+ else if(g_sUSBHCD.USBOUTPipes[ulIndex].ulType & EP_PIPE_TYPE_ISOC)
+ {
+ ulFlags = USB_EP_MODE_ISOC;
+ }
+ else
+ {
+ ulFlags = USB_EP_MODE_CTRL;
+ }
+
+ ulFlags |= USB_EP_HOST_OUT;
+
+ g_sUSBHCD.USBOUTPipes[ulIndex].ucEPNumber =
+ (unsigned char)ulTargetEndpoint;
+
+ //
+ // Save the interval and the next tick to trigger a scheduler event.
+ //
+ g_sUSBHCD.USBOUTPipes[ulIndex].ulInterval = ulInterval;
+ g_sUSBHCD.USBOUTPipes[ulIndex].ulNextEventTick =
+ ulInterval + g_ulCurrentTick;
+
+ //
+ // Set the device speed.
+ //
+ ulFlags |= (g_sUSBHCD.USBOUTPipes[ulIndex].psDevice->bLowSpeed ?
+ USB_EP_SPEED_LOW : USB_EP_SPEED_FULL);
+ }
+ else
+ {
+ //
+ // Set the mode for this endpoint.
+ //
+ if(g_sUSBHCD.USBINPipes[ulIndex].ulType & EP_PIPE_TYPE_BULK)
+ {
+ ulFlags = USB_EP_MODE_BULK;
+ }
+ else if(g_sUSBHCD.USBINPipes[ulIndex].ulType & EP_PIPE_TYPE_INTR)
+ {
+ ulFlags = USB_EP_MODE_INT;
+ }
+ else if(g_sUSBHCD.USBINPipes[ulIndex].ulType & EP_PIPE_TYPE_ISOC)
+ {
+ ulFlags = USB_EP_MODE_ISOC;
+ }
+ else
+ {
+ ulFlags = USB_EP_MODE_CTRL;
+ }
+ ulFlags |= USB_EP_HOST_IN;
+
+ g_sUSBHCD.USBINPipes[ulIndex].ucEPNumber =
+ (unsigned char)ulTargetEndpoint;
+
+ //
+ // Save the interval and the next tick to trigger a scheduler event.
+ //
+ g_sUSBHCD.USBINPipes[ulIndex].ulInterval = ulInterval;
+ g_sUSBHCD.USBINPipes[ulIndex].ulNextEventTick =
+ ulInterval + g_ulCurrentTick;
+
+ //
+ // Set the device speed.
+ //
+ ulFlags |= (g_sUSBHCD.USBINPipes[ulIndex].psDevice->bLowSpeed ?
+ USB_EP_SPEED_LOW : USB_EP_SPEED_FULL);
+ }
+
+ //
+ // Set up the appropriate flags if uDMA is used.
+ //
+ if(ulPipe & EP_PIPE_USE_UDMA)
+ {
+ ulFlags |= USB_EP_DMA_MODE_0;
+ }
+
+ //
+ // Configure the endpoint according to the flags determined above.
+ //
+ USBHostEndpointConfig(USB0_BASE,
+ INDEX_TO_USB_EP((ulPipe & EP_PIPE_IDX_M) + 1),
+ ulMaxPayload, ulInterval, ulTargetEndpoint,
+ ulFlags);
+
+ return(0);
+}
+
+//*****************************************************************************
+//
+//! This function is used to return the current status of a USB HCD pipe.
+//!
+//! This function will return the current status for a given USB pipe. If
+//! there is no status to report this call will simply return
+//! \b USBHCD_PIPE_NO_CHANGE.
+//!
+//! \param ulPipe is the USB pipe for this status request.
+//!
+//! \return This function returns the current status for the given endpoint.
+//! This will be one of the \b USBHCD_PIPE_* values.
+//
+//*****************************************************************************
+unsigned long
+USBHCDPipeStatus(unsigned long ulPipe)
+{
+ return(0);
+}
+
+//*****************************************************************************
+//
+//! This function is used to write data to a USB HCD pipe.
+//!
+//! \param ulPipe is the USB pipe to put data into.
+//! \param pucData is a pointer to the data to send.
+//! \param ulSize is the amount of data to send.
+//!
+//! This function will block until it has sent as much data as was
+//! requested using the USB pipe's FIFO. The caller should have registered a
+//! callback with the USBHCDPipeAlloc() call in order to be informed when the
+//! data has been transmitted. The value returned by this function can be less
+//! than the \e ulSize requested if the USB pipe has less space available than
+//! this request is making.
+//!
+//! \return This function returns the number of bytes that were scheduled to
+//! be sent on the given USB pipe.
+//
+//*****************************************************************************
+unsigned long
+USBHCDPipeWrite(unsigned long ulPipe, unsigned char *pucData,
+ unsigned long ulSize)
+{
+ unsigned long ulEndpoint;
+ unsigned long ulRemainingBytes;
+ unsigned long ulByteToSend;
+ unsigned long ulPipeIdx;
+ unsigned long ulEPStatus;
+
+ //
+ // Determine which endpoint interface that this pipe is using.
+ //
+ ulEndpoint = INDEX_TO_USB_EP((EP_PIPE_IDX_M & ulPipe) + 1);
+
+ //
+ // Get index used for looking up pipe data
+ //
+ ulPipeIdx = ulPipe & EP_PIPE_IDX_M;
+
+ //
+ // Set the total number of bytes to send out.
+ //
+ ulRemainingBytes = ulSize;
+
+ if(ulSize > 64)
+ {
+ //
+ // Only send 64 bytes at a time.
+ //
+ ulByteToSend = 64;
+ }
+ else
+ {
+ //
+ // Send the requested number of bytes.
+ //
+ ulByteToSend = ulSize;
+ }
+
+ //
+ // Send all of the requested data.
+ //
+ while(ulRemainingBytes != 0)
+ {
+ //
+ // Start a write request.
+ //
+ g_sUSBHCD.USBOUTPipes[ulPipeIdx].eState = PIPE_WRITING;
+
+ //
+ // If uDMA is not enabled for this pipe, or if the uDMA workaround
+ // is applied, then don't use uDMA for this transfer.
+ //
+ if(!(ulPipe & EP_PIPE_USE_UDMA) ||
+ (g_bUseDMAWA && (ulByteToSend != 64)))
+ {
+ //
+ // Disable uDMA on the USB endpoint
+ //
+ MAP_USBEndpointDMADisable(USB0_BASE, ulEndpoint, USB_EP_HOST_OUT);
+
+ //
+ // Put the data in the buffer.
+ //
+ MAP_USBEndpointDataPut(USB0_BASE, ulEndpoint, pucData,
+ ulByteToSend);
+
+ //
+ // Schedule the data to be sent.
+ //
+ MAP_USBEndpointDataSend(USB0_BASE, ulEndpoint, USB_TRANS_OUT);
+ }
+
+ //
+ // Otherwise, uDMA should be used for this transfer
+ //
+ else
+ {
+ //
+ // Set up the uDMA transfer.
+ //
+ MAP_uDMAChannelTransferSet(UDMA_CHANNEL_USBEP1TX + (ulPipeIdx * 2),
+ UDMA_MODE_AUTO, pucData,
+ (void *)USBFIFOAddrGet(USB0_BASE,
+ ulEndpoint),
+ ulByteToSend);
+
+ //
+ // Enable uDMA on the USB endpoint
+ //
+ MAP_USBEndpointDMAEnable(USB0_BASE, ulEndpoint, USB_EP_HOST_OUT);
+
+ //
+ // Disable the USB interrupt.
+ //
+ OS_INT_DISABLE(INT_USB0);
+
+ //
+ // Set pending transmit DMA flag
+ //
+ g_ulDMAPending |= DMA_PEND_TRANSMIT_FLAG << ulPipeIdx;
+
+ //
+ // Enable the uDMA channel to start the transfer
+ //
+ MAP_uDMAChannelEnable(UDMA_CHANNEL_USBEP1TX + (ulPipeIdx * 2));
+
+ //
+ // Enable the USB interrupt.
+ //
+ OS_INT_ENABLE(INT_USB0);
+ }
+
+ //
+ // Wait for a status change.
+ //
+ while(g_sUSBHCD.USBOUTPipes[ulPipeIdx].eState == PIPE_WRITING)
+ {
+ //
+ // Read the status of the endpoint connected to this pipe.
+ //
+ ulEPStatus = MAP_USBEndpointStatus(USB0_BASE,
+ INDEX_TO_USB_EP(ulPipeIdx + 1));
+
+ //
+ // Check if the device stalled the request.
+ //
+ if(ulEPStatus & USB_HOST_OUT_STALL)
+ {
+ //
+ // If uDMA is being used, then disable the channel.
+ //
+ if(ulPipe & EP_PIPE_USE_UDMA)
+ {
+ MAP_uDMAChannelDisable(UDMA_CHANNEL_USBEP1TX +
+ (ulPipeIdx * 2));
+ }
+ }
+
+ //
+ // If a disconnect event occurs the exit out of the loop.
+ //
+ if(g_ulUSBHIntEvents & INT_EVENT_DISCONNECT)
+ {
+ //
+ // Set the pipe state to error.
+ //
+ g_sUSBHCD.USBOUTPipes[ulPipeIdx].eState = PIPE_ERROR;
+ }
+ }
+
+ //
+ // If the data was successfully sent then decrement the count and
+ // continue.
+ //
+ if(g_sUSBHCD.USBOUTPipes[ulPipeIdx].eState == PIPE_DATA_SENT)
+ {
+ //
+ // Decrement the remaining data and advance the pointer.
+ //
+ ulRemainingBytes -= ulByteToSend;
+ pucData += ulByteToSend;
+ }
+ else if(g_sUSBHCD.USBOUTPipes[ulPipeIdx].eState == PIPE_STALLED)
+ {
+ //
+ // Zero out the size so that the caller knows that no data was
+ // written.
+ //
+ ulSize = 0;
+
+ //
+ // If uDMA is being used, then disable the channel.
+ //
+ if(ulPipe & EP_PIPE_USE_UDMA)
+ {
+ //
+ // Disable the DMA channel.
+ //
+ MAP_uDMAChannelDisable(UDMA_CHANNEL_USBEP1TX + (ulPipeIdx * 2));
+ }
+
+ //
+ // This is the actual endpoint number.
+ //
+ USBHCDClearFeature(
+ g_sUSBHCD.USBOUTPipes[ulPipeIdx].psDevice->ulAddress,
+ ulPipe, USB_FEATURE_EP_HALT);
+
+ //
+ // If there was a stall, then no more data is coming so break out.
+ //
+ break;
+ }
+
+ //
+ // If there are less than 64 bytes to send then this is the last
+ // of the data to go out.
+ //
+ if(ulRemainingBytes < 64)
+ {
+ ulByteToSend = ulRemainingBytes;
+ }
+ else if(g_sUSBHCD.USBOUTPipes[ulPipeIdx].eState == PIPE_ERROR)
+ {
+ //
+ // An error occurred so stop this transaction and set the number
+ // of bytes to zero.
+ //
+ ulSize = 0;
+ break;
+ }
+ }
+
+ //
+ // Go Idle once this state has been reached.
+ //
+ g_sUSBHCD.USBOUTPipes[ulPipeIdx].eState = PIPE_IDLE;
+
+ return(ulSize);
+}
+
+//*****************************************************************************
+//
+//! This function is used to schedule and IN transaction on a USB HCD pipe.
+//!
+//! \param ulPipe is the USB pipe to read data from.
+//! \param pucData is a pointer to store the data that is received.
+//! \param ulSize is the size in bytes of the buffer pointed to by pucData.
+//!
+//! This function will not block depending on the type of pipe passed in will
+//! schedule either a send of data to the device or a read of data from the
+//! device. In either case the amount of data will be limited to what will
+//! fit in the FIFO for a given endpoint.
+//!
+//! \return This function returns the number of bytes that were sent in the case
+//! of a transfer of data or it will return 0 for a request on a USB IN pipe.
+//
+//*****************************************************************************
+unsigned long
+USBHCDPipeSchedule(unsigned long ulPipe, unsigned char *pucData,
+ unsigned long ulSize)
+{
+ unsigned long ulEndpoint;
+ unsigned long ulPipeIdx;
+
+ //
+ // Get index used for looking up pipe data
+ //
+ ulPipeIdx = ulPipe & EP_PIPE_IDX_M;
+
+ //
+ // Determine which endpoint interface that this pipe is using.
+ //
+ ulEndpoint = INDEX_TO_USB_EP((EP_PIPE_IDX_M & ulPipe) + 1);
+
+ if(ulPipe & EP_PIPE_TYPE_OUT)
+ {
+ //
+ // Start a write request.
+ //
+ g_sUSBHCD.USBOUTPipes[EP_PIPE_IDX_M & ulPipe].eState = PIPE_WRITING;
+
+ //
+ // Check if uDMA is enabled on this pipe.
+ //
+ if(ulPipe & EP_PIPE_USE_UDMA)
+ {
+ //
+ // Set up the uDMA transfer.
+ //
+ MAP_uDMAChannelTransferSet(UDMA_CHANNEL_USBEP1TX + (ulPipeIdx * 2),
+ UDMA_MODE_AUTO, pucData,
+ (void *)USBFIFOAddrGet(USB0_BASE,
+ ulEndpoint),
+ ulSize);
+
+ //
+ // Enable uDMA on the USB endpoint
+ //
+ MAP_USBEndpointDMAEnable(USB0_BASE, ulEndpoint, USB_EP_HOST_OUT);
+
+ //
+ // Disable the USB interrupt.
+ //
+ OS_INT_DISABLE(INT_USB0);
+
+ //
+ // Set pending transmit DMA flag
+ //
+ g_ulDMAPending |= DMA_PEND_TRANSMIT_FLAG << ulPipeIdx;
+
+ //
+ // Enable the uDMA channel to start the transfer
+ //
+ MAP_uDMAChannelEnable(UDMA_CHANNEL_USBEP1TX + (ulPipeIdx * 2));
+
+ //
+ // Enable the USB interrupt.
+ //
+ OS_INT_ENABLE(INT_USB0);
+ }
+ else
+ {
+ //
+ // Put the data in the buffer.
+ //
+ MAP_USBEndpointDataPut(USB0_BASE, ulEndpoint, pucData, ulSize);
+
+ //
+ // Schedule the data to be sent.
+ //
+ MAP_USBEndpointDataSend(USB0_BASE, ulEndpoint, USB_TRANS_OUT);
+ }
+ }
+ else
+ {
+ //
+ // Start a read request.
+ //
+ g_sUSBHCD.USBINPipes[EP_PIPE_IDX_M & ulPipe].eState = PIPE_READING;
+
+ //
+ // If uDMA is not enabled for this pipe, or if the uDMA workaround
+ // is applied, then do not use uDMA for this transfer.
+ //
+ if((ulPipe & EP_PIPE_USE_UDMA) == 0)
+ {
+ //
+ // Disable uDMA on the endpoint
+ //
+ MAP_USBEndpointDMADisable(USB0_BASE, ulEndpoint, USB_EP_HOST_IN);
+ }
+ //
+ // Otherwise, uDMA should be used for this transfer, so set up
+ // the uDMA channel in advance of triggering the IN request.
+ //
+ else
+ {
+ //
+ // Compute bytes to transfer and set up transfer
+ //
+ MAP_uDMAChannelTransferSet(UDMA_CHANNEL_USBEP1RX + (ulPipeIdx * 2),
+ UDMA_MODE_AUTO,
+ (void *)USBFIFOAddrGet(USB0_BASE,
+ ulEndpoint),
+ pucData, ulSize);
+
+ //
+ // Enable uDMA on the endpoint
+ //
+ MAP_USBEndpointDMAEnable(USB0_BASE, ulEndpoint, USB_EP_HOST_IN);
+
+ //
+ // Disable the USB interrupt.
+ //
+ OS_INT_DISABLE(INT_USB0);
+
+ //
+ // Set pending DMA flag
+ //
+ g_ulDMAPending |= DMA_PEND_RECEIVE_FLAG << ulPipeIdx;
+
+ //
+ // Enable the uDMA channel to start the transfer
+ //
+ MAP_uDMAChannelEnable(UDMA_CHANNEL_USBEP1RX + (ulPipeIdx * 2));
+
+ //
+ // Enable the USB interrupt.
+ //
+ OS_INT_ENABLE(INT_USB0);
+ }
+
+ //
+ // Remember details of the buffer into which the data will be read.
+ //
+ g_sUSBHCD.USBINPipes[ulPipeIdx].pucReadPtr = pucData;
+ g_sUSBHCD.USBINPipes[ulPipeIdx].ulReadSize = ulSize;
+
+ //
+ // Trigger a request for data from the device.
+ //
+ MAP_USBHostRequestIN(USB0_BASE, ulEndpoint);
+
+ //
+ // No data was put into or read from the buffer.
+ //
+ ulSize = 0;
+ }
+ return(ulSize);
+}
+
+//*****************************************************************************
+//
+//! This function is used to read data from a USB HCD pipe.
+//!
+//! \param ulPipe is the USB pipe to read data from.
+//! \param pucData is a pointer to store the data that is received.
+//! \param ulSize is the size in bytes of the buffer pointed to by pucData.
+//!
+//! This function will not block and will only read as much data as requested
+//! or as much data is currently available from the USB pipe. The caller
+//! should have registered a callback with the USBHCDPipeAlloc() call in order
+//! to be informed when the data has been received. The value returned by this
+//! function can be less than the \e ulSize requested if the USB pipe has less
+//! data available than was requested.
+//!
+//! \return This function returns the number of bytes that were returned in the
+//! \e pucData buffer.
+//
+//*****************************************************************************
+unsigned long
+USBHCDPipeReadNonBlocking(unsigned long ulPipe, unsigned char *pucData,
+ unsigned long ulSize)
+{
+ unsigned long ulEndpoint;
+
+ //
+ // Determine which endpoint interface that this pipe is using.
+ //
+ ulEndpoint = INDEX_TO_USB_EP((EP_PIPE_IDX_M & ulPipe) + 1);
+
+ //
+ // Read the data out of the USB endpoint interface.
+ //
+ MAP_USBEndpointDataGet(USB0_BASE, ulEndpoint, pucData, &ulSize);
+
+ //
+ // Acknowledge that the data was read from the endpoint.
+ //
+ MAP_USBHostEndpointDataAck(USB0_BASE, ulEndpoint);
+
+ //
+ // Go Idle once this state has been reached.
+ //
+ g_sUSBHCD.USBINPipes[EP_PIPE_IDX_M & ulPipe].eState = PIPE_IDLE;
+
+ return(ulSize);
+}
+
+//*****************************************************************************
+//
+//! This function acknowledges data received via an interrupt IN pipe.
+//!
+//! \param ulPipe is the USB INT pipe whose last packet is to be acknowledged.
+//!
+//! This function is used to acknowledge reception of data on an interrupt IN
+//! pipe. A transfer on an interrupt IN endpoint is scheduled via a call to
+//! USBHCDPipeSchedule() and the application is notified when data is received
+//! using a USB_EVENT_RX_AVAILABLE event. In the handler for this event, the
+//! application must call USBHCDPipeDataAck() to have the USB controller ACK
+//! the data from the device and complete the transaction.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBHCDPipeDataAck(unsigned long ulPipe)
+{
+ unsigned long ulEndpoint;
+
+ //
+ // Determine which endpoint interface that this pipe is using.
+ //
+ ulEndpoint = INDEX_TO_USB_EP((EP_PIPE_IDX_M & ulPipe) + 1);
+
+ //
+ // Acknowledge that the data was read from the endpoint.
+ //
+ USBHostEndpointDataAck(USB0_BASE, ulEndpoint);
+
+ //
+ // Go Idle once this state has been reached.
+ //
+ g_sUSBHCD.USBINPipes[EP_PIPE_IDX_M & ulPipe].eState = PIPE_IDLE;
+}
+
+//*****************************************************************************
+//
+//! This function is used to read data from a USB HCD pipe.
+//!
+//! \param ulPipe is the USB pipe to read data from.
+//! \param pucData is a pointer to store the data that is received.
+//! \param ulSize is the size in bytes of the buffer pointed to by pucData.
+//!
+//! This function will block and will only return when it has read as much data
+//! as requested from the USB pipe. The caller should have registered a
+//! callback with the USBHCDPipeAlloc() call in order to be informed when the
+//! data has been received. The value returned by this function can be less
+//! than the \e ulSize requested if the USB pipe has less data available than
+//! was requested.
+//!
+//! \return This function returns the number of bytes that were returned in the
+//! \e pucData buffer.
+//
+//*****************************************************************************
+unsigned long
+USBHCDPipeRead(unsigned long ulPipe, unsigned char *pucData,
+ unsigned long ulSize)
+{
+ unsigned long ulEndpoint;
+ unsigned long ulRemainingBytes;
+ unsigned long ulBytesRead;
+ unsigned long ulPipeIdx;
+ unsigned long ulEPStatus;
+
+ //
+ // Get index used for looking up pipe data
+ //
+ ulPipeIdx = ulPipe & EP_PIPE_IDX_M;
+
+ //
+ // Initialized the number of bytes read.
+ //
+ ulBytesRead = 0;
+
+ //
+ // Determine which endpoint interface that this pipe is using.
+ //
+ ulEndpoint = INDEX_TO_USB_EP(ulPipeIdx + 1);
+
+ //
+ // Set the remaining bytes to received.
+ //
+ ulRemainingBytes = ulSize;
+
+ //
+ // Continue until all data requested has been received.
+ //
+ while(ulRemainingBytes != 0)
+ {
+ //
+ // Start a read request.
+ //
+ g_sUSBHCD.USBINPipes[ulPipeIdx].eState = PIPE_READING;
+
+ //
+ // If uDMA is not enabled for this pipe, or if the uDMA workaround
+ // is applied, then do not use uDMA for this transfer.
+ //
+ if(!(ulPipe & EP_PIPE_USE_UDMA) ||
+ (g_bUseDMAWA && (ulRemainingBytes < 64)))
+ {
+ //
+ // Disable uDMA on the endpoint
+ //
+ MAP_USBEndpointDMADisable(USB0_BASE, ulEndpoint, USB_EP_HOST_IN);
+ }
+
+ //
+ // Otherwise, uDMA should be used for this transfer, so set up
+ // the uDMA channel in advance of triggering the IN request.
+ //
+ else
+ {
+ //
+ // Compute bytes to transfer and set up transfer
+ //
+ ulBytesRead = ulRemainingBytes > 64 ? 64 : ulRemainingBytes;
+
+ MAP_uDMAChannelTransferSet(UDMA_CHANNEL_USBEP1RX + (ulPipeIdx * 2),
+ UDMA_MODE_AUTO,
+ (void *)USBFIFOAddrGet(USB0_BASE,
+ ulEndpoint),
+ pucData, ulBytesRead);
+
+ //
+ // Enable uDMA on the endpoint
+ //
+ MAP_USBEndpointDMAEnable(USB0_BASE, ulEndpoint, USB_EP_HOST_IN);
+
+ //
+ // Disable the USB interrupt.
+ //
+ OS_INT_DISABLE(INT_USB0);
+
+ //
+ // Set pending DMA flag
+ //
+ g_ulDMAPending |= DMA_PEND_RECEIVE_FLAG << ulPipeIdx;
+
+ //
+ // Enable the uDMA channel to start the transfer
+ //
+ MAP_uDMAChannelEnable(UDMA_CHANNEL_USBEP1RX + (ulPipeIdx * 2));
+
+ //
+ // Enable the USB interrupt.
+ //
+ OS_INT_ENABLE(INT_USB0);
+ }
+
+ //
+ // Set up for the next transaction.
+ //
+ g_sUSBHCD.USBINPipes[ulPipeIdx].pucReadPtr = pucData;
+ g_sUSBHCD.USBINPipes[ulPipeIdx].ulReadSize = (ulRemainingBytes < 64) ?
+ ulRemainingBytes : 64;
+
+ //
+ // Trigger a request for data from the device.
+ //
+ MAP_USBHostRequestIN(USB0_BASE, ulEndpoint);
+
+ //
+ // Wait for a status change.
+ //
+ while(g_sUSBHCD.USBINPipes[ulPipeIdx].eState == PIPE_READING)
+ {
+ //
+ // Read the status of the endpoint connected to this pipe.
+ //
+ ulEPStatus = MAP_USBEndpointStatus(USB0_BASE,
+ INDEX_TO_USB_EP(ulPipeIdx + 1));
+
+ //
+ // Check if the device stalled the request.
+ //
+ if(ulEPStatus & USB_HOST_IN_STALL)
+ {
+ //
+ // If uDMA is being used, then disable the channel.
+ //
+ if(ulPipe & EP_PIPE_USE_UDMA)
+ {
+ //
+ // Disable the DMA channel.
+ //
+ MAP_uDMAChannelDisable(UDMA_CHANNEL_USBEP1RX +
+ (ulPipeIdx * 2));
+ }
+ }
+
+ //
+ // If a disconnect event occurs the exit out of the loop.
+ //
+ if(g_ulUSBHIntEvents & INT_EVENT_DISCONNECT)
+ {
+ //
+ // Set the pipe state to error.
+ //
+ g_sUSBHCD.USBINPipes[ulPipeIdx].eState = PIPE_ERROR;
+ }
+ }
+
+ //
+ // If data is ready then return it.
+ //
+ if(g_sUSBHCD.USBINPipes[ulPipeIdx].eState == PIPE_DATA_READY)
+ {
+ //
+ // If not using uDMA then read the data from the USB. Otherwise
+ // the data will already be in the buffer.
+ //
+ if(!(ulPipe & EP_PIPE_USE_UDMA) ||
+ (g_bUseDMAWA && (ulRemainingBytes < 64)))
+ {
+ //
+ // Compute bytes to transfer and set up transfer
+ //
+ ulBytesRead = ulRemainingBytes > 64 ? 64 : ulRemainingBytes;
+
+ //
+ // Acknowledge that the data was read from the endpoint.
+ //
+ MAP_USBHostEndpointDataAck(USB0_BASE, ulEndpoint);
+ }
+
+ //
+ // Subtract the number of bytes read from the bytes remaining.
+ //
+ ulRemainingBytes -= ulBytesRead;
+
+ //
+ // If there were less than 64 bytes read, then this was a short
+ // packet and no more data will be returned.
+ //
+ if(ulBytesRead < 64)
+ {
+ //
+ // Subtract off the bytes that were not received and exit the
+ // loop.
+ //
+ ulSize = ulSize - ulRemainingBytes;
+ break;
+ }
+ else
+ {
+ //
+ // Move the buffer ahead to receive more data into the buffer.
+ //
+ pucData += 64;
+ }
+ }
+ else if(g_sUSBHCD.USBINPipes[ulPipeIdx].eState == PIPE_STALLED)
+ {
+ //
+ // Zero out the size so that the caller knows that no data was read.
+ //
+ ulSize = 0;
+
+ //
+ // If uDMA is being used, then disable the channel.
+ //
+ if(ulPipe & EP_PIPE_USE_UDMA)
+ {
+ MAP_uDMAChannelDisable(UDMA_CHANNEL_USBEP1RX + (ulPipeIdx * 2));
+ }
+
+ //
+ // This is the actual endpoint number.
+ //
+ USBHCDClearFeature(
+ g_sUSBHCD.USBINPipes[ulPipeIdx].psDevice->ulAddress,
+ ulPipe, USB_FEATURE_EP_HALT);
+
+ //
+ // If there was a stall, then no more data is coming so break out.
+ //
+ break;
+ }
+ else if(g_sUSBHCD.USBINPipes[ulPipeIdx].eState == PIPE_ERROR)
+ {
+ //
+ // An error occurred so stop this transaction and set the number
+ // of bytes to zero.
+ //
+ ulSize = 0;
+ break;
+ }
+ }
+
+ //
+ // Go Idle once this state has been reached.
+ //
+ g_sUSBHCD.USBINPipes[ulPipeIdx].eState = PIPE_IDLE;
+
+ return(ulSize);
+}
+
+//*****************************************************************************
+//
+//! This function is used to release a USB pipe.
+//!
+//! \param ulPipe is the allocated USB pipe to release.
+//!
+//! This function is used to release a USB pipe that was allocated by a call to
+//! USBHCDPipeAlloc() for use by some other device endpoint in the system.
+//! Freeing an unallocated or invalid pipe will not generate an error and will
+//! instead simply return.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBHCDPipeFree(unsigned long ulPipe)
+{
+ unsigned long ulDMAIdx;
+ unsigned long ulIndex;
+
+ //
+ // Get the index number from the allocated pipe.
+ //
+ ulIndex = (ulPipe & EP_PIPE_IDX_M);
+
+ if(ulPipe & EP_PIPE_TYPE_OUT)
+ {
+ //
+ // Clear the address and type for this endpoint to free it up.
+ //
+ g_sUSBHCD.USBOUTPipes[ulPipe & EP_PIPE_IDX_M].psDevice = 0;
+ g_sUSBHCD.USBOUTPipes[ulPipe & EP_PIPE_IDX_M].ulType = 0;
+ g_sUSBHCD.USBOUTPipes[ulPipe & EP_PIPE_IDX_M].pfnCallback = 0;
+
+ //
+ // Check if this pipe has allocated a DMA channel.
+ //
+ if(g_sUSBHCD.USBOUTPipes[ulPipe & EP_PIPE_IDX_M].ucDMAChannel !=
+ USBHCD_DMA_UNUSED)
+ {
+ //
+ // Get the DMA channel used by this pipe.
+ //
+ ulDMAIdx =
+ g_sUSBHCD.USBOUTPipes[ulPipe & EP_PIPE_IDX_M].ucDMAChannel;
+
+ //
+ // This is a debug check that will prevent accessing beyond the
+ // buffer allocated to the DMA channels.
+ //
+ ASSERT(ulDMAIdx < MAX_NUM_DMA_CHANNELS);
+
+ //
+ // Mark the channel as free for use.
+ //
+ g_sUSBHCD.ucDMAChannels[ulDMAIdx] = USBHCD_DMA_UNUSED;
+
+ //
+ // Clear out the current channel in use by this pipe.
+ //
+ g_sUSBHCD.USBOUTPipes[ulPipe & EP_PIPE_IDX_M].ucDMAChannel =
+ USBHCD_DMA_UNUSED;
+ }
+
+ //
+ // Free up the FIFO memory used by this endpoint.
+ //
+ if(g_sUSBHCD.USBOUTPipes[ulPipe & EP_PIPE_IDX_M].ucFIFOSize)
+ {
+ FIFOFree(&g_sUSBHCD.USBOUTPipes[ulPipe & EP_PIPE_IDX_M]);
+ }
+
+ //
+ // Set the function address for this endpoint back to zero.
+ //
+ USBHostAddrSet(USB0_BASE, INDEX_TO_USB_EP(ulIndex + 1),
+ 0, USB_EP_HOST_OUT);
+
+ //
+ // Set the hub and port address for the endpoint back to zero and the
+ // speed back to LOW.
+ //
+ USBHostHubAddrSet(USB0_BASE, INDEX_TO_USB_EP(ulIndex + 1),
+ 0, (USB_EP_HOST_OUT | USB_EP_SPEED_LOW));
+ }
+ else if(ulPipe & EP_PIPE_TYPE_IN)
+ {
+ //
+ // Clear the address and type for this endpoint to free it up.
+ //
+ g_sUSBHCD.USBINPipes[ulPipe & EP_PIPE_IDX_M].psDevice = 0;
+ g_sUSBHCD.USBINPipes[ulPipe & EP_PIPE_IDX_M].ulType = 0;
+ g_sUSBHCD.USBINPipes[ulPipe & EP_PIPE_IDX_M].pfnCallback = 0;
+
+ //
+ // Check if this pipe has allocated a DMA channel.
+ //
+ if(g_sUSBHCD.USBINPipes[ulPipe & EP_PIPE_IDX_M].ucDMAChannel !=
+ USBHCD_DMA_UNUSED)
+ {
+ //
+ // Get the DMA channel used by this pipe.
+ //
+ ulDMAIdx = g_sUSBHCD.USBINPipes[ulPipe & EP_PIPE_IDX_M].ucDMAChannel;
+
+ //
+ // This is a debug check that will prevent accessing beyond the
+ // buffer allocated to the DMA channels.
+ //
+ ASSERT(ulDMAIdx < MAX_NUM_DMA_CHANNELS);
+
+ //
+ // Mark the channel as free for use.
+ //
+ g_sUSBHCD.ucDMAChannels[ulDMAIdx] = USBHCD_DMA_UNUSED;
+
+ //
+ // Clear out the current channel in use by this pipe.
+ //
+ g_sUSBHCD.USBINPipes[ulPipe & EP_PIPE_IDX_M].ucDMAChannel =
+ USBHCD_DMA_UNUSED;
+ }
+
+ //
+ // Free up the FIFO memory used by this endpoint.
+ //
+ if(g_sUSBHCD.USBINPipes[ulPipe & EP_PIPE_IDX_M].ucFIFOSize)
+ {
+ FIFOFree(&g_sUSBHCD.USBINPipes[ulPipe & EP_PIPE_IDX_M]);
+ }
+
+ //
+ // Set the function address for this endpoint back to zero.
+ //
+ USBHostAddrSet(USB0_BASE, INDEX_TO_USB_EP(ulIndex + 1),
+ 0, USB_EP_HOST_IN);
+
+ //
+ // Set the hub and port address for the endpoint back to zero and the
+ // speed back to LOW.
+ //
+ USBHostHubAddrSet(USB0_BASE, INDEX_TO_USB_EP(ulIndex + 1),
+ 0, (USB_EP_HOST_IN | USB_EP_SPEED_LOW));
+
+ //
+ // Clear any pending IN transactions.
+ //
+ USBHostRequestINClear(USB0_BASE, INDEX_TO_USB_EP(ulIndex + 1));
+ }
+}
+
+//*****************************************************************************
+//
+// This internal function initializes the HCD code.
+//
+// \param ulIndex specifies which USB controller to use.
+// \param pvPool is a pointer to the data to use as a memory pool for this
+// controller.
+// \param ulPoolSize is the size in bytes of the buffer passed in as pvPool.
+//
+// This function will perform all the necessary operations to allow the USB
+// host controller to begin enumeration and communication with a device. This
+// function should typically be called once at the start of an application
+// before any other calls are made to the host controller.
+//
+// \return None.
+//
+//*****************************************************************************
+static void
+USBHCDInitInternal(unsigned long ulIndex, void *pvPool,
+ unsigned long ulPoolSize)
+{
+ long lIdx;
+
+ ASSERT(ulIndex == 0);
+
+ //
+ // Get the number of endpoints supported by this device.
+ //
+ g_sUSBHCD.ulNumEndpoints = USBNumEndpointsGet(USB0_BASE);
+
+ //
+ // The first 64 Bytes are allocated to endpoint 0.
+ //
+ g_ulAlloc[0] = 1;
+ g_ulAlloc[1] = 0;
+
+ //
+ // Save the base address for this controller.
+ //
+ g_sUSBHCD.ulUSBBase = USB0_BASE;
+
+ //
+ // All Pipes are unused at start.
+ //
+ for(lIdx = 0; lIdx < MAX_NUM_PIPES; lIdx++)
+ {
+ g_sUSBHCD.USBINPipes[lIdx].psDevice = 0;
+ g_sUSBHCD.USBINPipes[lIdx].ulType = USBHCD_PIPE_UNUSED;
+ g_sUSBHCD.USBINPipes[lIdx].ucDMAChannel = USBHCD_DMA_UNUSED;
+ g_sUSBHCD.USBOUTPipes[lIdx].psDevice = 0;
+ g_sUSBHCD.USBOUTPipes[lIdx].ulType = USBHCD_PIPE_UNUSED;
+ g_sUSBHCD.USBOUTPipes[lIdx].ucDMAChannel = USBHCD_DMA_UNUSED;
+ }
+
+ //
+ // Make sure that the hub driver is initialized since it is called even
+ // if it is not present in the system.
+ //
+ USBHHubInit();
+
+ //
+ // All DMA channels are unused at start.
+ //
+ for(lIdx = 0; lIdx < MAX_NUM_DMA_CHANNELS; lIdx++)
+ {
+ g_sUSBHCD.ucDMAChannels[lIdx] = USBHCD_DMA_UNUSED;
+ }
+
+ //
+ // Initialized the device structures.
+ //
+ for(lIdx = 0; lIdx <= MAX_USB_DEVICES; lIdx++)
+ {
+ //
+ // Clear the config descriptor and state.
+ //
+ g_sUSBHCD.eDeviceState[lIdx] = HCD_IDLE;
+ g_sUSBHCD.USBDevice[lIdx].pConfigDescriptor = 0;
+ g_sUSBHCD.USBDevice[lIdx].bConfigRead = false;
+
+ //
+ // Initialize the device descriptor.
+ //
+ g_sUSBHCD.USBDevice[lIdx].DeviceDescriptor.bLength = 0;
+ g_sUSBHCD.USBDevice[lIdx].DeviceDescriptor.bMaxPacketSize0 = 0;
+
+ //
+ // Initialize the device address.
+ //
+ g_sUSBHCD.USBDevice[lIdx].ulAddress = 0;
+
+ //
+ // Set the current interface to 0.
+ //
+ g_sUSBHCD.USBDevice[lIdx].ulInterface = 0;
+
+ //
+ // Clear the active driver for the device.
+ //
+ g_lUSBHActiveDriver[lIdx] = -1;
+ }
+
+ //
+ // Allocate the memory needed for reading descriptors.
+ //
+ g_sUSBHCD.pvPool = pvPool;
+ g_sUSBHCD.ulPoolSize = ulPoolSize;
+
+ //
+ // Initialize the device class.
+ //
+ g_sUSBHCD.ulClass = USB_CLASS_EVENTS;
+
+ //
+ // Default enable connect, disconnect, unknown device and power fault
+ // event notifications.
+ //
+ g_sUSBHCD.ulEventEnables = USBHCD_EVFLAG_CONNECT | USBHCD_EVFLAG_UNKCNCT |
+ USBHCD_EVFLAG_DISCNCT | USBHCD_EVFLAG_PWRFAULT |
+ USBHCD_EVFLAG_PWREN | USBHCD_EVFLAG_PWRDIS;
+
+ //
+ // Initialize the USB tick module.
+ //
+ InternalUSBTickInit();
+
+ //
+ // Only do hardware update if the stack is in Host mode, do not touch the
+ // hardware for OTG mode operation.
+ //
+ if((g_eUSBMode == USB_MODE_HOST) || (g_eUSBMode == USB_MODE_FORCE_HOST))
+ {
+ //
+ // Configure the End point 0.
+ //
+ USBHostEndpointConfig(USB0_BASE, USB_EP_0, 64, 0, 0,
+ (USB_EP_MODE_CTRL | USB_EP_SPEED_FULL |
+ USB_EP_HOST_OUT));
+
+ //
+ // Enable USB Interrupts.
+ //
+ MAP_USBIntEnableControl(USB0_BASE, USB_INTCTRL_RESET |
+ USB_INTCTRL_DISCONNECT |
+ USB_INTCTRL_SOF |
+ USB_INTCTRL_SESSION |
+ USB_INTCTRL_BABBLE |
+ USB_INTCTRL_CONNECT |
+ USB_INTCTRL_RESUME |
+ USB_INTCTRL_SUSPEND |
+ USB_INTCTRL_VBUS_ERR |
+ USB_INTCTRL_MODE_DETECT |
+ USB_INTCTRL_POWER_FAULT);
+
+ MAP_USBIntEnableEndpoint(USB0_BASE, USB_INTEP_ALL);
+
+ //
+ // Enable the USB interrupt.
+ //
+ OS_INT_ENABLE(INT_USB0);
+
+ //
+ // There is no automatic power in pure host mode.
+ //
+ USBHCDPowerConfigSet(ulIndex, g_ulPowerConfig & ~USB_HOST_PWREN_AUTO);
+
+ //
+ // Force the power on as well as this point.
+ //
+ MAP_USBHostPwrEnable(USB0_BASE);
+
+ //
+ // This is required to get into host mode on some parts.
+ //
+ USBOTGSessionRequest(USB0_BASE, true);
+ }
+}
+
+//*****************************************************************************
+//
+//! This function is used to set the power pin and power fault configuration.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//! \param ulPwrConfig is the power configuration to use for the application.
+//!
+//! This function must be called before HCDInit() is called so that the power
+//! pin configuration can be set before power is enabled. The \e ulPwrConfig
+//! flags specify the power fault level sensitivity, the power fault action,
+//! and the power enable pin level and source.
+//!
+//! One of the following can be selected as the power fault level sensitivity:
+//!
+//! - \b USBHCD_FAULT_LOW - An external power fault is indicated by the pin
+//! being driven low.
+//! - \b USBHCD_FAULT_HIGH - An external power fault is indicated by the pin
+//! being driven high.
+//!
+//! One of the following can be selected as the power fault action:
+//!
+//! - \b USBHCD_FAULT_VBUS_NONE - No automatic action when power fault
+//! detected.
+//! - \b USBHCD_FAULT_VBUS_TRI - Automatically Tri-state the USBnEPEN pin on a
+//! power fault.
+//! - \b USBHCD_FAULT_VBUS_DIS - Automatically drive the USBnEPEN pin to it's
+//! inactive state on a power fault.
+//!
+//! One of the following can be selected as the power enable level and source:
+//!
+//! - \b USBHCD_VBUS_MANUAL - Power control is completely managed by the
+//! application, the USB library will provide a
+//! power callback to request power state changes.
+//! - \b USBHCD_VBUS_AUTO_LOW - USBEPEN is driven low by the USB controller
+//! automatically if USBOTGSessionRequest() has
+//! enabled a session.
+//! - \b USBHCD_VBUS_AUTO_HIGH - USBEPEN is driven high by the USB controller
+//! automatically if USBOTGSessionRequest() has
+//! enabled a session.
+//!
+//! If USBHCD_VBUS_MANUAL is used then the application must provide an
+//! event driver to receive the USB_EVENT_POWER_ENABLE and
+//! USB_EVENT_POWER_DISABLE events and enable and disable power to VBUS when
+//! requested by the USB library. The application should respond to a power
+//! control callback by enabling or disabling VBUS as soon as possible and
+//! before returning from the callback function.
+//!
+//! \note The following values should no longer be used with the USB library:
+//! USB_HOST_PWRFLT_LOW, USB_HOST_PWRFLT_HIGH, USB_HOST_PWRFLT_EP_NONE,
+//! USB_HOST_PWRFLT_EP_TRI, USB_HOST_PWRFLT_EP_LOW, USB_HOST_PWRFLT_EP_HIGH,
+//! USB_HOST_PWREN_LOW, USB_HOST_PWREN_HIGH, USB_HOST_PWREN_VBLOW, and
+//! USB_HOST_PWREN_VBHIGH.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBHCDPowerConfigInit(unsigned long ulIndex, unsigned long ulPwrConfig)
+{
+ ASSERT(ulIndex == 0);
+
+ //
+ // Save the value as it will be used later.
+ //
+ g_ulPowerConfig = ulPwrConfig;
+}
+
+//*****************************************************************************
+//
+//! This function is used to get the power pin and power fault configuration.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//!
+//! This function will return the current power control pin configuration as
+//! set by the USBHCDPowerConfigInit() function or the defaults if not yet set.
+//! See the USBHCDPowerConfigInit() documentation for the meaning of the bits
+//! that are returned by this function.
+//!
+//! \return The configuration of the power control pins.
+//!
+//*****************************************************************************
+unsigned long
+USBHCDPowerConfigGet(unsigned long ulIndex)
+{
+ ASSERT(ulIndex == 0);
+
+ //
+ // Save the value as it will be used later.
+ //
+ return(g_ulPowerConfig);
+}
+
+//*****************************************************************************
+//
+//! This function is used to set the power pin and power fault configuration.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//! \param ulConfig specifies which USB power configuration to use.
+//!
+//! This function will set the current power control pin configuration as
+//! set by the USBHCDPowerConfigInit() function or the defaults if not yet set.
+//! See the USBHCDPowerConfigInit() documentation for the meaning of the bits
+//! that are set by this function.
+//!
+//! \return Returns zero to indicate the power setting is now active.
+//!
+//*****************************************************************************
+unsigned long
+USBHCDPowerConfigSet(unsigned long ulIndex, unsigned long ulConfig)
+{
+ ASSERT(ulIndex == 0);
+
+ //
+ // Remember the current setting.
+ //
+ g_ulPowerConfig = ulConfig;
+
+ //
+ // Clear out the two flag bits.
+ //
+ ulConfig = g_ulPowerConfig & ~(USBHCD_VBUS_MANUAL | USBHCD_FAULT_VBUS_DIS);
+
+ //
+ // If there is an automatic disable power action specified then set the
+ // polarity of the signal to match EPEN.
+ //
+ if(g_ulPowerConfig & USBHCD_FAULT_VBUS_DIS)
+ {
+ //
+ // Insure that the assumption below is true.
+ //
+ ASSERT((USBHCD_VBUS_AUTO_HIGH & 1) == 1);
+ ASSERT((USBHCD_VBUS_AUTO_LOW & 1) == 0);
+
+ //
+ // This is taking advantage of the difference between
+ // USBHCD_VBUS_AUTO_LOW and USBHCD_VBUS_AUTO_HIGH being that bit
+ // one is set when EPEN is active high.
+ //
+ if(g_ulPowerConfig & 1)
+ {
+ g_ulPowerConfig |= USB_HOST_PWRFLT_EP_LOW;
+ ulConfig |= USB_HOST_PWRFLT_EP_LOW;
+ }
+ else
+ {
+ g_ulPowerConfig |= USB_HOST_PWRFLT_EP_HIGH;
+ ulConfig |= USB_HOST_PWRFLT_EP_HIGH;
+ }
+ }
+
+ //
+ // Initialize the power configuration.
+ //
+ MAP_USBHostPwrConfig(USB0_BASE, ulConfig);
+
+ //
+ // If not in manual mode then just turn on power.
+ //
+ if((g_ulPowerConfig & USBHCD_VBUS_MANUAL) == 0)
+ {
+ //
+ // Power the USB bus.
+ //
+ MAP_USBHostPwrEnable(USB0_BASE);
+ }
+
+ //
+ // Return success.
+ //
+ return(0);
+}
+
+//*****************************************************************************
+//
+//! This function returns if the current power settings will automatically
+//! handle enabling and disabling VBUS power.
+//!
+//! \param ulIndex specifies which USB controller to query.
+//!
+//! This function returns if the current power control pin configuration will
+//! automatically apply power or whether it will be left to the application
+//! to turn on power when it is notified.
+//!
+//! \return A non-zero value indicates that power is automatically applied and
+//! a value of zero indicates that the application must manually apply power.
+//!
+//*****************************************************************************
+unsigned long
+USBHCDPowerAutomatic(unsigned long ulIndex)
+{
+ //
+ // Check if the controller is automatically applying power or not.
+ //
+ if(g_ulPowerConfig & USBHCD_VBUS_MANUAL)
+ {
+ return(0);
+ }
+ return(1);
+}
+
+//*****************************************************************************
+//
+//! This function is used to initialize the HCD code.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//! \param pvPool is a pointer to the data to use as a memory pool for this
+//! controller.
+//! \param ulPoolSize is the size in bytes of the buffer passed in as pvPool.
+//!
+//! This function will perform all the necessary operations to allow the USB
+//! host controller to begin enumeration and communication with devices. This
+//! function should typically be called once at the start of an application
+//! once all of the device and class drivers are ready for normal operation.
+//! This call will start up the USB host controller and any connected device
+//! will immediately start the enumeration sequence.
+//!
+//! The USBStackModeSet() function can be called with USB_MODE_HOST in order to
+//! cause the USB library to force the USB operating mode to a host controller.
+//! This allows the application to used the USBVBUS and USBID pins as GPIOs on
+//! devices that support forcing OTG to operate as a host only controller. By
+//! default the USB library will assume that the USBVBUS and USBID pins are
+//! configured as USB pins and not GPIOs.
+//!
+//! \note Forcing of the USB controller mode feature is not available on all
+//! Stellaris microcontrollers. Consult the data sheet for the microcontroller
+//! that the application is using to determine if this feature is available.
+//!
+//! The memory pool passed to this function must be at least as large as a
+//! typical configuration descriptor for devices that are to be supported. This
+//! value is application-dependent however it should never be less than 32
+//! bytes and, in most cases, should be at least 64 bytes. If there is not
+//! sufficient memory to load a configuration descriptor from a device, the
+//! device will not be recognized by the USB library's host controller driver.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBHCDInit(unsigned long ulIndex, void *pvPool, unsigned long ulPoolSize)
+{
+ long lDriver;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(ulIndex == 0);
+
+ //
+ // Make sure there is at least enough to read the configuration descriptor.
+ //
+ ASSERT(ulPoolSize >= sizeof(tConfigDescriptor));
+
+ //
+ // Should not call this if the stack is in device mode.
+ //
+ ASSERT(g_eUSBMode != USB_MODE_DEVICE);
+ ASSERT(g_eUSBMode != USB_MODE_FORCE_DEVICE);
+
+ //
+ // If the mode was not set then default to USB_MODE_HOST.
+ //
+ if(g_eUSBMode == USB_MODE_NONE)
+ {
+ g_eUSBMode = USB_MODE_HOST;
+ }
+
+ //
+ // Reset the USB controller.
+ //
+ MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_USB0);
+
+ //
+ // Enable Clocking to the USB controller.
+ //
+ MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0);
+
+ //
+ // Turn on USB Phy clock.
+ //
+ MAP_SysCtlUSBPLLEnable();
+
+ //
+ // If the application not requesting OTG mode then set the mode to forced
+ // host mode. If the mode is actually USB_MODE_HOST, this will be switched
+ // off when ID pin detection is complete and the ID is no longer in use.
+ //
+ if(g_eUSBMode != USB_MODE_OTG)
+ {
+ //
+ // Force Host mode on devices that support force host mode.
+ //
+ MAP_USBHostMode(USB0_BASE);
+ }
+
+ //
+ // Call our internal function to perform the initialization.
+ //
+ USBHCDInitInternal(ulIndex, pvPool, ulPoolSize);
+
+ //
+ // No event driver is present by default.
+ //
+ g_sUSBHCD.lEventDriver = -1;
+
+ //
+ // Search through the Host Class driver list for the devices class.
+ //
+ for(lDriver = 0; lDriver < g_sUSBHCD.ulNumClassDrivers; lDriver++)
+ {
+ if(g_sUSBHCD.pClassDrivers[lDriver]->ulInterfaceClass ==
+ USB_CLASS_EVENTS)
+ {
+ //
+ // Event driver was found so remember it.
+ //
+ g_sUSBHCD.lEventDriver = lDriver;
+ }
+ }
+
+ //
+ // Get the number of ticks per millisecond, this is only used by blocking
+ // delays using the SysCtlDelay() function.
+ //
+ g_ulTickms = MAP_SysCtlClockGet() / 3000;
+
+ //
+ // Check to see if uDMA workaround is needed.
+ //
+ if(CLASS_IS_DUSTDEVIL && REVISION_IS_A0)
+ {
+ g_bUseDMAWA = 1;
+ }
+}
+
+//*****************************************************************************
+//
+//! This function is used to initialize the HCD class driver list.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//! \param ppHClassDrvs is an array of host class drivers that are
+//! supported on this controller.
+//! \param ulNumDrivers is the number of entries in the \e pHostClassDrivers
+//! array.
+//!
+//! This function will set the host classes supported by the host controller
+//! specified by the \e ulIndex parameter. This function should be called
+//! before enabling the host controller driver with the USBHCDInit() function.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBHCDRegisterDrivers(unsigned long ulIndex,
+ const tUSBHostClassDriver * const *ppHClassDrvs,
+ unsigned long ulNumDrivers)
+{
+ ASSERT(ulIndex == 0);
+
+ //
+ // Save the class drivers.
+ //
+ g_sUSBHCD.pClassDrivers = ppHClassDrvs;
+
+ //
+ // Save the number of class drivers.
+ //
+ g_sUSBHCD.ulNumClassDrivers = ulNumDrivers;
+}
+
+//*****************************************************************************
+//
+//! This function is used to terminate the HCD code.
+//!
+//! \param ulIndex specifies which USB controller to release.
+//!
+//! This function will clean up the USB host controller and disable it in
+//! preparation for shutdown or a switch to USB device mode. Once this call is
+//! made, \e USBHCDInit() may be called to reinitialize the controller and
+//! prepare for host mode operation.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBHCDTerm(unsigned long ulIndex)
+{
+ ASSERT(ulIndex == 0);
+
+ //
+ // End the session.
+ //
+ USBOTGSessionRequest(USB0_BASE, false);
+
+ //
+ // Remove power from the USB bus.
+ //
+ MAP_USBHostPwrDisable(USB0_BASE);
+
+ //
+ // Disable USB interrupts.
+ //
+ OS_INT_DISABLE(INT_USB0);
+
+ MAP_USBIntDisableControl(USB0_BASE, USB_INTCTRL_ALL);
+
+ MAP_USBIntDisableEndpoint(USB0_BASE, USB_INTEP_ALL);
+
+ //
+ // Set the host controller state back to it's initial values.
+ //
+ g_sUSBHCD.USBINPipes[0].ulType = USBHCD_PIPE_UNUSED;
+ g_sUSBHCD.USBINPipes[1].ulType = USBHCD_PIPE_UNUSED;
+ g_sUSBHCD.USBINPipes[2].ulType = USBHCD_PIPE_UNUSED;
+ g_sUSBHCD.USBOUTPipes[0].ulType = USBHCD_PIPE_UNUSED;
+ g_sUSBHCD.USBOUTPipes[1].ulType = USBHCD_PIPE_UNUSED;
+ g_sUSBHCD.USBOUTPipes[2].ulType = USBHCD_PIPE_UNUSED;
+ g_sUSBHCD.eDeviceState[0] = HCD_IDLE;
+ g_sUSBHCD.USBDevice[0].pConfigDescriptor = 0;
+ g_sUSBHCD.USBDevice[0].bConfigRead = false;
+ g_sUSBHCD.USBDevice[0].DeviceDescriptor.bLength = 0;
+ g_sUSBHCD.USBDevice[0].DeviceDescriptor.bMaxPacketSize0 = 0;
+ g_sUSBHCD.USBDevice[0].ulAddress = 0;
+ g_sUSBHCD.USBDevice[0].ulInterface = 0;
+ g_sUSBHCD.pvPool = 0;
+ g_sUSBHCD.ulPoolSize = 0;
+}
+
+//*****************************************************************************
+//
+//! This function generates reset signaling on the USB bus.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//!
+//! This function handles sending out reset signaling on the USB bus. After
+//! returning from this function, any attached device on the USB bus should
+//! have returned to it's reset state.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBHCDReset(unsigned long ulIndex)
+{
+ ASSERT(ulIndex == 0);
+
+ //
+ // Start the reset signaling.
+ //
+ MAP_USBHostReset(USB0_BASE, 1);
+
+ //
+ // Wait 20ms
+ //
+ OS_DELAY(g_ulTickms * 20);
+
+ //
+ // End reset signaling on the bus.
+ //
+ MAP_USBHostReset(USB0_BASE, 0);
+
+ //
+ // Need to wait at least 10ms to let the device recover from
+ // the reset. This is the delay specified in the USB 2.0 spec.
+ // We will hold the reset for 20ms.
+ //
+ OS_DELAY(g_ulTickms * 20);
+}
+
+//*****************************************************************************
+//
+//! This function will generate suspend signaling on the USB bus.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//!
+//! This function is used to generate suspend signaling on the USB bus. In
+//! order to leave the suspended state, the application should call
+//! USBHCDResume().
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBHCDSuspend(unsigned long ulIndex)
+{
+ ASSERT(ulIndex == 0);
+
+ //
+ // Start the suspend signaling.
+ //
+ MAP_USBHostSuspend(USB0_BASE);
+}
+
+//*****************************************************************************
+//
+//! This function will generate resume signaling on the USB bus.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//!
+//! This function is used to generate resume signaling on the USB bus in order
+//! to cause USB devices to leave their suspended state. This call should
+//! not be made unless a preceding call to USBHCDSuspend() has been made.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBHCDResume(unsigned long ulIndex)
+{
+ ASSERT(ulIndex == 0);
+
+ //
+ // Start the resume signaling.
+ //
+ MAP_USBHostResume(USB0_BASE, 1);
+
+ //
+ // Wait 100ms
+ //
+ OS_DELAY(g_ulTickms * 100);
+
+ //
+ // End reset signaling on the bus.
+ //
+ MAP_USBHostResume(USB0_BASE, 0);
+}
+
+//*****************************************************************************
+//
+//! This function issues a request for the current configuration descriptor
+//! from a device.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//! \param pDevice is a pointer to the device structure that holds the buffer
+//! to store the configuration descriptor.
+//!
+//! This function will request the configuration descriptor from the device.
+//! The \e pDevice->ConfigDescriptor member variable is used to hold the data
+//! for this request. This buffer will be allocated from the pool provided by
+//! the HCDInit() function. \e pDevice->DeviceDescriptor.bMaxPacketSize0
+//! should be valid prior to this call in order to correctly receive the
+//! configuration descriptor. If this variable is not valid then this call
+//! will not return accurate data.
+//!
+//! \return The number of bytes returned due to the request. This value can be
+//! zero if the device did not respond.
+//
+//*****************************************************************************
+static unsigned long
+USBHCDGetConfigDescriptor(unsigned long ulIndex, tUSBHostDevice *pDevice)
+{
+ tUSBRequest SetupPacket;
+ unsigned long ulBytes;
+
+ ASSERT(ulIndex == 0);
+
+ ulBytes = 0;
+
+ //
+ // This is a Standard Device IN request.
+ //
+ SetupPacket.bmRequestType =
+ USB_RTYPE_DIR_IN | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE;
+
+ //
+ // Request a Device Descriptor.
+ //
+ SetupPacket.bRequest = USBREQ_GET_DESCRIPTOR;
+ SetupPacket.wValue = USB_DTYPE_CONFIGURATION << 8;
+
+ //
+ // Index is always 0 for device configurations requests.
+ //
+ SetupPacket.wIndex = 0;
+
+ //
+ // Only ask for the configuration header first to see how big the
+ // whole thing is.
+ //
+ if(!pDevice->bConfigRead)
+ {
+ //
+ // Only request the space available.
+ //
+ SetupPacket.wLength = sizeof(tConfigDescriptor);
+
+ //
+ // Put the setup packet in the buffer.
+ //
+ ulBytes =
+ USBHCDControlTransfer(0, &SetupPacket, pDevice,
+ (unsigned char *)pDevice->pConfigDescriptor,
+ sizeof(tConfigDescriptor),
+ pDevice->DeviceDescriptor.bMaxPacketSize0);
+ }
+
+ //
+ // If the Configuration header was successfully returned then get the
+ // full configuration descriptor.
+ //
+ if(ulBytes == sizeof(tConfigDescriptor))
+ {
+ //
+ // Save the total size and request the full configuration descriptor.
+ //
+ SetupPacket.wLength =
+ pDevice->pConfigDescriptor->wTotalLength;
+
+ //
+ // Don't allow the buffer to be larger than was allocated.
+ //
+ if(SetupPacket.wLength > g_sUSBHCD.ulPoolSize)
+ {
+ SetupPacket.wLength = g_sUSBHCD.ulPoolSize;
+ }
+
+ //
+ // Put the setup packet in the buffer.
+ //
+ ulBytes =
+ USBHCDControlTransfer(0, &SetupPacket, pDevice,
+ (unsigned char *)pDevice->pConfigDescriptor,
+ SetupPacket.wLength,
+ pDevice->DeviceDescriptor.bMaxPacketSize0);
+
+ //
+ // If we read the descriptor, remember the fact.
+ //
+ if(ulBytes)
+ {
+ pDevice->bConfigRead = true;
+ }
+ }
+
+ return(ulBytes);
+}
+
+//*****************************************************************************
+//
+//! This function issues a request for a device descriptor from a device.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//! \param pDevice is a pointer to the device structure that holds the buffer
+//! to store the device descriptor into.
+//!
+//! This function will request the device descriptor from the device. The
+//! \e pDevice->DeviceDescriptor descriptor is used to hold the data for this
+//! request. \e pDevice->DeviceDescriptor.bMaxPacketSize0 should be
+//! initialized to zero or to the valid maximum packet size if it is known. If
+//! this variable is not set to zero, then this call will determine the maximum
+//! packet size for endpoint 0 and save it in the structure member
+//! bMaxPacketSize0.
+//!
+//! \return The number of bytes returned due to the request. This value can be
+//! zero if the device did not respond.
+//
+//*****************************************************************************
+static unsigned long
+USBHCDGetDeviceDescriptor(unsigned long ulIndex, tUSBHostDevice *pDevice)
+{
+ tUSBRequest SetupPacket;
+ unsigned long ulBytes;
+
+ ASSERT(ulIndex == 0);
+
+ //
+ // This is a Standard Device IN request.
+ //
+ SetupPacket.bmRequestType =
+ USB_RTYPE_DIR_IN | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE;
+
+ //
+ // Request a Device Descriptor.
+ //
+ SetupPacket.bRequest = USBREQ_GET_DESCRIPTOR;
+ SetupPacket.wValue = USB_DTYPE_DEVICE << 8;
+
+ //
+ // Index is always 0 for device requests.
+ //
+ SetupPacket.wIndex = 0;
+
+ //
+ // All devices must have at least an 8 byte max packet size so just ask
+ // for 8 bytes to start with.
+ //
+ SetupPacket.wLength = sizeof(tDeviceDescriptor);
+
+ ulBytes = 0;
+
+ //
+ // Discover the max packet size for endpoint 0.
+ //
+ if(pDevice->DeviceDescriptor.bMaxPacketSize0 == 0)
+ {
+ //
+ // Put the setup packet in the buffer.
+ //
+ ulBytes =
+ USBHCDControlTransfer(ulIndex, &SetupPacket, pDevice,
+ (unsigned char *)&(pDevice->DeviceDescriptor),
+ sizeof(tDeviceDescriptor),
+ 8);
+ }
+
+ //
+ // Now get the full descriptor now that the actual maximum packet size
+ // is known.
+ //
+ if(ulBytes < sizeof(tDeviceDescriptor))
+ {
+ SetupPacket.wLength = (unsigned short)sizeof(tDeviceDescriptor);
+
+ ulBytes =
+ USBHCDControlTransfer(ulIndex, &SetupPacket, pDevice,
+ (unsigned char *)&(pDevice->DeviceDescriptor),
+ sizeof(tDeviceDescriptor),
+ pDevice->DeviceDescriptor.bMaxPacketSize0);
+ }
+
+ return(ulBytes);
+}
+
+//*****************************************************************************
+//
+//! This function is used to send the set address command to a device.
+//!
+//! \param ulDevIndex is the index of the device whose address is to be
+//! set. This value must be 0 to indicate that the device is connected
+//! directly to the host controller. Higher values indicate devices connected
+//! via a hub.
+//! \param ulDevAddress is the new device address to use for a device.
+//!
+//! The USBHCDSetAddress() function is used to set the USB device address, once
+//! a device has been discovered on the bus. This call is typically issued
+//! following a USB reset triggered by a call the USBHCDReset(). The
+//! address passed into this function via the \e ulDevAddress parameter is used
+//! for all further communications with the device after this function
+//! returns.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBHCDSetAddress(unsigned long ulDevIndex, unsigned long ulDevAddress)
+{
+ tUSBRequest SetupPacket;
+
+ //
+ // This is a Standard Device OUT request.
+ //
+ SetupPacket.bmRequestType =
+ USB_RTYPE_DIR_OUT | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE;
+
+ //
+ // Request a Device Descriptor.
+ //
+ SetupPacket.bRequest = USBREQ_SET_ADDRESS;
+ SetupPacket.wValue = ulDevAddress;
+
+ //
+ // Index is always 0 for device requests.
+ //
+ SetupPacket.wIndex = 0;
+
+ //
+ // Only request the space available.
+ //
+ SetupPacket.wLength = 0;
+
+ //
+ // Put the setup packet in the buffer.
+ //
+ USBHCDControlTransfer(0, &SetupPacket, &g_sUSBHCD.USBDevice[ulDevIndex], 0,
+ 0, MAX_PACKET_SIZE_EP0);
+
+ //
+ // Must delay 2ms after setting the address.
+ //
+ OS_DELAY(g_ulTickms * 2);
+}
+
+//*****************************************************************************
+//
+//! This function is used to send a Clear Feature request to a device.
+//!
+//! \param ulDevAddress is the USB bus address of the device that will receive
+//! this request.
+//! \param ulPipe is the pipe that will be used to send the request.
+//! \param ulFeature is one of the USB_FEATURE_* definitions.
+//!
+//! This function will issue a Clear Feature request to the device indicated
+//! by the \e ulDevAddress parameter. The \e ulPipe parameter is the USB pipe
+//! that should be used to send this request. The \e ulFeature parameter
+//! should be one of the following values:
+//!
+//! * \b USB_FEATURE_EP_HALT is used to end a HALT condition on a devices
+//! endpoint.
+//! * \b USB_FEATURE_REMOTE_WAKE is used to disable a device's remote wake
+//! feature.
+//! * \b USB_FEATURE_TEST_MODE is used take the USB device out of test mode.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBHCDClearFeature(unsigned long ulDevAddress, unsigned long ulPipe,
+ unsigned long ulFeature)
+{
+ tUSBRequest SetupPacket;
+ unsigned long ulIndex;
+
+ //
+ // Get the index number from the allocated pipe.
+ //
+ ulIndex = (ulPipe & EP_PIPE_IDX_M);
+
+ //
+ // This is a Standard Device OUT request.
+ //
+ SetupPacket.bmRequestType =
+ USB_RTYPE_DIR_OUT | USB_RTYPE_STANDARD | USB_RTYPE_ENDPOINT;
+
+ //
+ // Request a Device Descriptor.
+ //
+ SetupPacket.bRequest = USBREQ_CLEAR_FEATURE;
+ SetupPacket.wValue = ulFeature;
+
+ //
+ // Set the endpoint to access.
+ //
+ if(ulPipe & EP_PIPE_TYPE_IN)
+ {
+ SetupPacket.wIndex = g_sUSBHCD.USBINPipes[ulIndex].ucEPNumber | 0x80;
+ }
+ else
+ {
+ SetupPacket.wIndex = g_sUSBHCD.USBOUTPipes[ulIndex].ucEPNumber;
+ }
+
+ //
+ // This is always 0.
+ //
+ SetupPacket.wLength = 0;
+
+ //
+ // Put the setup packet in the buffer.
+ //
+ USBHCDControlTransfer(0, &SetupPacket,
+ &g_sUSBHCD.USBDevice[ulDevAddress - 1], 0, 0,
+ MAX_PACKET_SIZE_EP0);
+
+ //
+ // Set the endpoint to access.
+ //
+ if(ulPipe & EP_PIPE_TYPE_IN)
+ {
+ MAP_USBEndpointDataToggleClear(USB0_BASE, INDEX_TO_USB_EP(ulIndex + 1),
+ USB_EP_HOST_IN);
+ }
+ else
+ {
+ MAP_USBEndpointDataToggleClear(USB0_BASE, INDEX_TO_USB_EP(ulIndex + 1),
+ USB_EP_HOST_OUT);
+ }
+
+ //
+ // Must delay 2ms after clearing the feature.
+ //
+ OS_DELAY(g_ulTickms * 2);
+}
+
+//*****************************************************************************
+//
+//! This function is used to set the current configuration for a device.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//! \param ulDevice is the USB device for this function.
+//! \param ulConfiguration is one of the devices valid configurations.
+//!
+//! This function is used to set the current device configuration for a USB
+//! device. The \e ulConfiguration value must be one of the configuration
+//! indexes that was returned in the configuration descriptor from the device,
+//! or a value of 0. If 0 is passed in, the device will return to it's
+//! addressed state and no longer be in a configured state. If the value is
+//! non-zero then the device will change to the requested configuration.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBHCDSetConfig(unsigned long ulIndex, unsigned long ulDevice,
+ unsigned long ulConfiguration)
+{
+ tUSBRequest SetupPacket;
+ tUSBHostDevice *pDevice;
+
+ ASSERT(ulIndex == 0);
+
+ pDevice = (tUSBHostDevice *)ulDevice;
+
+ //
+ // This is a Standard Device OUT request.
+ //
+ SetupPacket.bmRequestType =
+ USB_RTYPE_DIR_OUT | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE;
+
+ //
+ // Request a Device Descriptor.
+ //
+ SetupPacket.bRequest = USBREQ_SET_CONFIG;
+ SetupPacket.wValue = ulConfiguration;
+
+ //
+ // Index is always 0 for device requests.
+ //
+ SetupPacket.wIndex = 0;
+
+ //
+ // Only request the space available.
+ //
+ SetupPacket.wLength = 0;
+
+ //
+ // Put the setup packet in the buffer.
+ //
+ USBHCDControlTransfer(0, &SetupPacket, pDevice, 0, 0,
+ MAX_PACKET_SIZE_EP0);
+}
+
+//*****************************************************************************
+//
+//! This function is used to set the current interface and alternate setting
+//! for an interface on a device.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//! \param ulDevice is the USB device for this function.
+//! \param ulInterface is one of the valid interface numbers for a device.
+//! \param ulAltSetting is one of the valid alternate interfaces for the
+//! ulInterface number.
+//!
+//! This function is used to change the alternate setting for one of the valid
+//! interfaces on a USB device. The \e ulDevice specifies the device instance
+//! that was returned when the device was connected. This call will set the
+//! USB device's interface based on the \e ulInterface and \e ulAltSetting.
+//!
+//! \b Example: Set the USB device interface 2 to alternate setting 1.
+//!
+//! \verbatim
+//! USBHCDSetInterface(0, ulDevice, 2, 1);
+//! \endverbatim
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBHCDSetInterface(unsigned long ulIndex, unsigned long ulDevice,
+ unsigned long ulInterface, unsigned ulAltSetting)
+{
+ tUSBRequest SetupPacket;
+ tUSBHostDevice *pDevice;
+
+ ASSERT(ulIndex == 0);
+
+ pDevice = (tUSBHostDevice *)ulDevice;
+
+ //
+ // This is a Standard Device OUT request.
+ //
+ SetupPacket.bmRequestType =
+ USB_RTYPE_DIR_OUT | USB_RTYPE_STANDARD | USB_RTYPE_INTERFACE;
+
+ //
+ // Request a Device Descriptor.
+ //
+ SetupPacket.bRequest = USBREQ_SET_INTERFACE;
+
+ //
+ // Index is the interface to access.
+ //
+ SetupPacket.wIndex = ulInterface;
+
+ //
+ // wValue is the alternate setting.
+ //
+ SetupPacket.wValue = ulAltSetting;
+
+
+ //
+ // Only request the space available.
+ //
+ SetupPacket.wLength = 0;
+
+ //
+ // Put the setup packet in the buffer.
+ //
+ USBHCDControlTransfer(0, &SetupPacket, pDevice, 0, 0,
+ MAX_PACKET_SIZE_EP0);
+}
+
+//*****************************************************************************
+//
+// The internal function to see if a new schedule event should occur.
+//
+// This function is called by the main interrupt handler due to start of frame
+// interrupts to determine if a new scheduler event should be sent to the USB
+// pipe.
+//
+// \return None.
+//
+//*****************************************************************************
+void
+USBHostCheckPipes(void)
+{
+ long lIdx;
+
+ g_ulCurrentTick++;
+
+ for(lIdx = 0; lIdx < g_sUSBHCD.ulNumEndpoints; lIdx++)
+ {
+ //
+ // Skip unused pipes.
+ //
+ if(g_sUSBHCD.USBINPipes[lIdx].ulType == USBHCD_PIPE_UNUSED)
+ {
+ continue;
+ }
+
+ //
+ // If the tick has expired and it has an interval then update it.
+ //
+ if((g_sUSBHCD.USBINPipes[lIdx].ulInterval != 0) &&
+ (g_sUSBHCD.USBINPipes[lIdx].ulNextEventTick == g_ulCurrentTick))
+ {
+ //
+ // Schedule the next event.
+ //
+ g_sUSBHCD.USBINPipes[lIdx].ulNextEventTick +=
+ g_sUSBHCD.USBINPipes[lIdx].ulInterval;
+
+ //
+ // If the pipe is IDLE and there is a callback, let the higher
+ // level drivers know that a new transfer can be scheduled.
+ //
+ if((g_sUSBHCD.USBINPipes[lIdx].eState == PIPE_IDLE) &&
+ (g_sUSBHCD.USBINPipes[lIdx].pfnCallback))
+ {
+ g_sUSBHCD.USBINPipes[lIdx].pfnCallback(IN_PIPE_HANDLE(lIdx),
+ USB_EVENT_SCHEDULER);
+ }
+ }
+ }
+}
+
+//*****************************************************************************
+//
+// The internal USB host mode interrupt handler.
+//
+// \param ulIndex is the USB controller associated with this interrupt.
+// \param ulStatus is the current interrupt status as read via a call to
+// \e USBIntStatusControl().
+//
+// This the main USB interrupt handler called when operating in host mode.
+// This handler will branch the interrupt off to the appropriate handlers
+// depending on the current status of the USB controller.
+//
+// The two-tiered structure for the interrupt handler ensures that it is
+// possible to use the same handler code in both host and OTG modes and
+// means that device code can be excluded from applications that only require
+// support for USB host mode operation.
+//
+// \return None.
+//
+//*****************************************************************************
+void
+USBHostIntHandlerInternal(unsigned long ulIndex, unsigned long ulStatus)
+{
+ unsigned long ulEPStatus;
+ static unsigned long ulSOFDivide = 0;
+ unsigned long ulEvent;
+ unsigned long ulIdx;
+ unsigned long ulDevIndex;
+ long lClassDrvr;
+
+ //
+ // By default, assume we are dealing with the device directly connected
+ // to the host controller and that we need to notify its class driver of
+ // this interrupt.
+ //
+ g_sUSBHCD.USBDevice[0].bNotifyInt = true;
+
+ if(ulStatus & USB_INTCTRL_SOF)
+ {
+ //
+ // Indicate that a start of frame has occurred.
+ //
+ g_ulUSBHIntEvents |= INT_EVENT_SOF;
+ }
+
+ //
+ // A power fault has occurred so notify the application.
+ //
+ if(ulStatus & USB_INTCTRL_POWER_FAULT)
+ {
+ //
+ // Indicate that a power fault has occurred.
+ //
+ g_ulUSBHIntEvents |= INT_EVENT_POWER_FAULT;
+
+ //
+ // Turn off power to the bus.
+ //
+ MAP_USBHostPwrDisable(USB0_BASE);
+
+ //
+ // Disable USB interrupts.
+ //
+ OS_INT_DISABLE(INT_USB0);
+
+ return;
+ }
+
+ //
+ // In the event of a USB VBUS error, end the session and remove power to
+ // the device.
+ //
+ if(ulStatus & USB_INTCTRL_VBUS_ERR)
+ {
+ //
+ // Set the VBUS error event. We deliberately clear all other events
+ // since this one means anything else that is outstanding is
+ // irrelevant.
+ //
+ g_ulUSBHIntEvents = INT_EVENT_VBUS_ERR;
+ return;
+ }
+
+ //
+ // Received a reset from the host.
+ //
+ if(ulStatus & USB_INTCTRL_BABBLE)
+ {
+ }
+
+ //
+ // Suspend was signaled on the bus.
+ //
+ if(ulStatus & USB_INTCTRL_SUSPEND)
+ {
+ }
+
+ //
+ // Start the session.
+ //
+ if(ulStatus & USB_INTCTRL_SESSION)
+ {
+ //
+ // Power the USB bus.
+ //
+ MAP_USBHostPwrEnable(USB0_BASE);
+
+ USBOTGSessionRequest(USB0_BASE, true);
+ }
+
+ //
+ // Resume was signaled on the bus.
+ //
+ if(ulStatus & USB_INTCTRL_RESUME)
+ {
+ }
+
+ //
+ // Device connected so tell the main routine to issue a reset.
+ //
+ if(ulStatus & USB_INTCTRL_CONNECT)
+ {
+ //
+ // Set the connect flag and clear disconnect if it happens to be set.
+ //
+ g_ulUSBHIntEvents |= INT_EVENT_CONNECT;
+ g_ulUSBHIntEvents &= ~INT_EVENT_DISCONNECT;
+
+ //
+ // Power the USB bus.
+ //
+ MAP_USBHostPwrEnable(USB0_BASE);
+ }
+
+ //
+ // Handle the ID detection so that the ID pin can be used as a
+ // GPIO in USB_MODE_HOST.
+ //
+ if(ulStatus & USB_INTCTRL_MODE_DETECT)
+ {
+ //
+ // If in USB_MODE_HOST mode then switch back to OTG detection
+ // so that VBUS can be monitored but free up the ID pin.
+ //
+ if(g_eUSBMode == USB_MODE_HOST)
+ {
+ USBOTGMode(USB0_BASE);
+ }
+ }
+
+ //
+ // Device was unplugged.
+ //
+ if(ulStatus & USB_INTCTRL_DISCONNECT)
+ {
+ //
+ // Set the disconnect flag and clear connect if it happens to be set.
+ //
+ g_ulUSBHIntEvents |= INT_EVENT_DISCONNECT;
+ g_ulUSBHIntEvents &= ~INT_EVENT_CONNECT;
+ }
+
+ //
+ // Start of Frame was received.
+ //
+ if(ulStatus & USB_INTCTRL_SOF)
+ {
+ //
+ // Increment the global Start of Frame counter.
+ //
+ g_ulUSBSOFCount++;
+
+ //
+ // Increment our SOF divider.
+ //
+ ulSOFDivide++;
+
+ //
+ // Have we counted enough SOFs to allow us to call the tick function?
+ //
+ if(ulSOFDivide == USB_SOF_TICK_DIVIDE)
+ {
+ //
+ // Yes - reset the divider and call the SOF tick handler.
+ //
+ ulSOFDivide = 0;
+ InternalUSBStartOfFrameTick(USB_SOF_TICK_DIVIDE);
+ }
+ }
+
+ //
+ // Get the current endpoint interrupt status.
+ //
+ ulStatus = MAP_USBIntStatusEndpoint(USB0_BASE);
+
+ //
+ // Handle end point 0 interrupts.
+ //
+ if(ulStatus & USB_INTEP_0)
+ {
+ //
+ // Indicate that a start of frame has occurred.
+ //
+ g_ulUSBHIntEvents |= INT_EVENT_ENUM;
+ }
+
+ //
+ // Check to see if any uDMA transfers are pending
+ //
+ for(ulIdx = 0; ulIdx < MAX_NUM_PIPES; ulIdx++)
+ {
+ if((g_ulDMAPending == 0) && (ulStatus == 0))
+ {
+ break;
+ }
+
+ //
+ // Check each pipe to see if uDMA is pending
+ //
+ if(g_ulDMAPending & (DMA_PEND_RECEIVE_FLAG << ulIdx))
+ {
+ //
+ // Handle the case where the pipe is reading
+ //
+ if(g_sUSBHCD.USBINPipes[ulIdx].eState == PIPE_READING)
+ {
+ //
+ // If the DMA channel transfer is complete, send an ack.
+ //
+ if(uDMAChannelModeGet(UDMA_CHANNEL_USBEP1RX + (ulIdx * 2))
+ == UDMA_MODE_STOP)
+ {
+ MAP_USBHostEndpointDataAck(USB0_BASE,
+ INDEX_TO_USB_EP(ulIdx + 1));
+ g_ulDMAPending &= ~(DMA_PEND_RECEIVE_FLAG << ulIdx);
+
+ //
+ // If using uDMA then the endpoint status int will not
+ // occur. So process the data ready event here.
+ //
+ g_sUSBHCD.USBINPipes[ulIdx].eState = PIPE_DATA_READY;
+ ulEvent = USB_EVENT_RX_AVAILABLE;
+
+ //
+ // Only call a handler if one is present.
+ //
+ if(g_sUSBHCD.USBINPipes[ulIdx].pfnCallback)
+ {
+ g_sUSBHCD.USBINPipes[ulIdx].pfnCallback(
+ IN_PIPE_HANDLE(ulIdx), ulEvent);
+ }
+
+ //
+ // Remember that we need to notify this device's class
+ // driver that an interrupt occurred.
+ //
+ g_sUSBHCD.USBINPipes[ulIdx].psDevice->bNotifyInt = true;
+ }
+ }
+ }
+
+ //
+ // Check for a pending DMA transmit transaction.
+ //
+ if(g_ulDMAPending & (DMA_PEND_TRANSMIT_FLAG << ulIdx))
+ {
+ //
+ // Handle the case where the pipe is writing
+ //
+ if(g_sUSBHCD.USBOUTPipes[ulIdx].eState == PIPE_WRITING)
+ {
+ //
+ // If the uDMA channel transfer is complete, then tell
+ // the USB controller to go ahead and send the data
+ //
+ if(uDMAChannelModeGet(UDMA_CHANNEL_USBEP1TX + (ulIdx * 2))
+ == UDMA_MODE_STOP)
+ {
+ MAP_USBEndpointDataSend(USB0_BASE,
+ INDEX_TO_USB_EP(ulIdx + 1),
+ USB_TRANS_OUT);
+ g_ulDMAPending &= ~(DMA_PEND_TRANSMIT_FLAG << ulIdx);
+ }
+ }
+ }
+
+ //
+ // Check the next pipe, the first time through this will clear out
+ // any interrupts dealing with endpoint zero since it was handled above.
+ //
+ ulStatus >>= 1;
+
+ //
+ // Check the status of the transmit(OUT) pipes.
+ //
+ if(ulStatus & 1)
+ {
+ //
+ // Read the status of the endpoint connected to this pipe.
+ //
+ ulEPStatus = MAP_USBEndpointStatus(USB0_BASE,
+ INDEX_TO_USB_EP(ulIdx + 1));
+
+ //
+ // Check if the device stalled the request.
+ //
+ if(ulEPStatus & USB_HOST_OUT_STALL)
+ {
+ //
+ // Clear the stall condition on this endpoint pipe.
+ //
+ MAP_USBHostEndpointStatusClear(USB0_BASE,
+ INDEX_TO_USB_EP(ulIdx + 1),
+ USB_HOST_OUT_STALL);
+
+ //
+ // Save the STALLED state.
+ //
+ g_sUSBHCD.USBOUTPipes[ulIdx].eState = PIPE_STALLED;
+
+ //
+ // Notify the pipe that it was stalled.
+ //
+ ulEvent = USB_EVENT_STALL;
+ }
+ else if(ulEPStatus & USB_HOST_OUT_ERROR)
+ {
+ //
+ // Clear the error condition on this endpoint pipe.
+ //
+ MAP_USBHostEndpointStatusClear(USB0_BASE,
+ INDEX_TO_USB_EP(ulIdx + 1),
+ USB_HOST_OUT_ERROR);
+
+ //
+ // Save the Pipes error state.
+ //
+ g_sUSBHCD.USBOUTPipes[ulIdx].eState = PIPE_ERROR;
+
+ //
+ // Notify the pipe that had an error.
+ //
+ ulEvent = USB_EVENT_ERROR;
+ }
+ else
+ {
+ //
+ // Data was transmitted successfully.
+ //
+ g_sUSBHCD.USBOUTPipes[ulIdx].eState = PIPE_DATA_SENT;
+
+ //
+ // Notify the pipe that its last transaction was completed.
+ //
+ ulEvent = USB_EVENT_TX_COMPLETE;
+ }
+
+ //
+ // Clear the stall condition on this endpoint pipe.
+ //
+ MAP_USBHostEndpointStatusClear(USB0_BASE,
+ INDEX_TO_USB_EP(ulIdx + 1),
+ ulEPStatus);
+
+ //
+ // Only call a handler if one is present.
+ //
+ if(g_sUSBHCD.USBOUTPipes[ulIdx].pfnCallback)
+ {
+ g_sUSBHCD.USBOUTPipes[ulIdx].pfnCallback(OUT_PIPE_HANDLE(ulIdx),
+ ulEvent);
+ }
+
+ //
+ // Remember that we need to notify this device's class
+ // driver that an interrupt occurred.
+ //
+ g_sUSBHCD.USBOUTPipes[ulIdx].psDevice->bNotifyInt = true;
+ }
+
+ //
+ // Check the status of the receive(IN) pipes.
+ //
+ if(ulStatus & 0x10000)
+ {
+ //
+ // Clear the status flag for the IN Pipe.
+ //
+ ulStatus &= ~0x10000;
+
+ //
+ // Read the status of the endpoint connected to this pipe.
+ //
+ ulEPStatus = MAP_USBEndpointStatus(USB0_BASE,
+ INDEX_TO_USB_EP(ulIdx + 1));
+
+ //
+ // Check if the device stalled the request.
+ //
+ if(ulEPStatus & USB_HOST_IN_STALL)
+ {
+ //
+ // Clear the stall condition on this endpoint pipe.
+ //
+ MAP_USBHostEndpointStatusClear(USB0_BASE,
+ INDEX_TO_USB_EP(ulIdx + 1),
+ USB_HOST_IN_STALL);
+
+ //
+ // Save the STALLED state.
+ //
+ g_sUSBHCD.USBINPipes[ulIdx].eState = PIPE_STALLED;
+
+ //
+ // Notify the pipe that it was stalled.
+ //
+ ulEvent = USB_EVENT_STALL;
+ }
+ else if(ulEPStatus & USB_HOST_IN_ERROR)
+ {
+ //
+ // We can no longer communicate with this device for some
+ // reason. It may have been disconnected from a hub, for
+ // example. Merely clear the status and continue.
+ //
+ USBHostEndpointStatusClear(USB0_BASE,
+ INDEX_TO_USB_EP(ulIdx + 1),
+ USB_HOST_IN_ERROR);
+
+ //
+ // Save the STALLED state.
+ //
+ g_sUSBHCD.USBINPipes[ulIdx].eState = PIPE_ERROR;
+
+ //
+ // Notify the pipe that it was stalled.
+ //
+ ulEvent = USB_EVENT_ERROR;
+ }
+ else
+ {
+ //
+ // Data is available.
+ //
+ g_sUSBHCD.USBINPipes[ulIdx].eState = PIPE_DATA_READY;
+
+ //
+ // Read the data out of the USB endpoint interface into the
+ // buffer provided by the caller to USBHCDPipeRead() or
+ // USBHCDPipeSchedule() if a buffer was provided already.
+ //
+ if(g_sUSBHCD.USBINPipes[ulIdx].pucReadPtr)
+ {
+ USBEndpointDataGet(USB0_BASE, INDEX_TO_USB_EP(ulIdx + 1),
+ g_sUSBHCD.USBINPipes[ulIdx].pucReadPtr,
+ &g_sUSBHCD.USBINPipes[ulIdx].ulReadSize);
+ }
+
+ //
+ // Notify the pipe that its last transaction was completed.
+ //
+ ulEvent = USB_EVENT_RX_AVAILABLE;
+ }
+
+ //
+ // Only call a handler if one is present.
+ //
+ if(g_sUSBHCD.USBINPipes[ulIdx].pfnCallback)
+ {
+ g_sUSBHCD.USBINPipes[ulIdx].pfnCallback(IN_PIPE_HANDLE(ulIdx),
+ ulEvent);
+ }
+
+ //
+ // Remember that we need to notify this device's class
+ // driver that an interrupt occurred.
+ //
+ g_sUSBHCD.USBINPipes[ulIdx].psDevice->bNotifyInt = true;
+ }
+ }
+
+ //
+ // Send back notifications to any class driver whose endpoint required
+ // service during the handler.
+ //
+ for(ulDevIndex = 0; ulDevIndex <= MAX_USB_DEVICES; ulDevIndex++)
+ {
+ //
+ // Which class driver does this device use?
+ //
+ lClassDrvr = g_lUSBHActiveDriver[ulDevIndex];
+
+ //
+ // If a class driver is in use, and one of its endpoints was serviced
+ // and the class driver has an interrupt callback...
+ //
+ if((lClassDrvr >= 0) && g_sUSBHCD.USBDevice[ulDevIndex].bNotifyInt &&
+ (g_sUSBHCD.pClassDrivers[lClassDrvr]->pfnIntHandler))
+ {
+ //
+ // ...call the class driver's interrupt notification callback.
+ //
+ g_sUSBHCD.pClassDrivers[lClassDrvr]->pfnIntHandler(
+ g_pvDriverInstance[ulDevIndex]);
+ }
+ }
+}
+
+//*****************************************************************************
+//
+//! The USB host mode interrupt handler for controller index 0.
+//!
+//! This the main USB interrupt handler entry point. This handler will branch
+//! the interrupt off to the appropriate handlers depending on the current
+//! status of the USB controller. This function must be placed in the
+//! interrupt table in order for the USB Library host stack to function.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USB0HostIntHandler(void)
+{
+ unsigned long ulStatus;
+
+ //
+ // Get the control interrupt status.
+ //
+ ulStatus = MAP_USBIntStatusControl(USB0_BASE);
+
+ //
+ // Call the internal handler to process the interrupts.
+ //
+ USBHostIntHandlerInternal(0, ulStatus);
+}
+
+//*****************************************************************************
+//
+//! This function opens the class driver.
+//!
+//! \param ulIndex specifies which USB controller to use.
+//! \param ulDeviceNum is the device number for the driver to load.
+//!
+//! This function opens the driver needed based on the class value found in
+//! the device's interface descriptor.
+//!
+//! \return This function returns -1 if no driver is found, or it returns the
+//! index of the driver found in the list of host class drivers.
+//
+//*****************************************************************************
+static long
+USBHCDOpenDriver(unsigned long ulIndex, unsigned long ulDeviceNum)
+{
+ long lDriver;
+ unsigned long ulClass;
+ tInterfaceDescriptor *pInterface;
+ tEventInfo sEvent;
+
+ ASSERT(ulIndex == 0);
+
+ //
+ // Get the interface descriptor.
+ //
+ pInterface = USBDescGetInterface(
+ g_sUSBHCD.USBDevice[ulDeviceNum].pConfigDescriptor,
+ g_sUSBHCD.USBDevice[ulDeviceNum].ulInterface,
+ USB_DESC_ANY);
+
+ //
+ // Read the interface class.
+ //
+ ulClass = pInterface->bInterfaceClass;
+
+ //
+ // Search through the Host Class driver list for the devices class.
+ //
+ for(lDriver = 0; lDriver < g_sUSBHCD.ulNumClassDrivers; lDriver++)
+ {
+ //
+ // If a driver was found call the open for this driver and save which
+ // driver is in use.
+ //
+ if(g_sUSBHCD.pClassDrivers[lDriver]->ulInterfaceClass == ulClass)
+ {
+ //
+ // Call the open function for the class driver.
+ //
+ g_pvDriverInstance[ulDeviceNum] =
+ g_sUSBHCD.pClassDrivers[lDriver]->pfnOpen(
+ &g_sUSBHCD.USBDevice[ulDeviceNum]);
+
+ //
+ // If the driver was successfully loaded then break out of the
+ // loop.
+ //
+ if(g_pvDriverInstance[ulDeviceNum] != 0)
+ {
+ break;
+ }
+ }
+ }
+
+ //
+ // If no drivers were found then return -1 to indicate an invalid
+ // driver instance.
+ //
+ if(lDriver == g_sUSBHCD.ulNumClassDrivers)
+ {
+ //
+ // Send an unknown connection event.
+ //
+ SendUnknownConnect(ulIndex, ulClass);
+
+ //
+ // Indicate that no driver was found.
+ //
+ lDriver = -1;
+ }
+
+ //
+ // If the connect event is enabled then send the event.
+ //
+ sEvent.ulEvent = USB_EVENT_CONNECTED;
+ sEvent.ulInstance = (ulIndex << 16) | ulDeviceNum;
+ InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_CONNECT);
+
+ return(lDriver);
+}
+
+//*****************************************************************************
+//
+// This function will send an event to a registered event driver.
+//
+// \param ulIndex specifies which USB controller to use.
+// \param psEvent is a pointer to the event structure to send.
+//
+// This function is only used internally to the USB library and will check
+// if an event driver is registered and send on the event.
+//
+// Note: This function should not be called outside of the USB library.
+//
+// \return None.
+//
+//*****************************************************************************
+void
+InternalUSBHCDSendEvent(unsigned long ulIndex, tEventInfo *psEvent,
+ unsigned long ulEvFlag)
+{
+ //
+ // Make sure that an event driver has been registered.
+ //
+ if((g_sUSBHCD.lEventDriver != -1) &&
+ (g_sUSBHCD.pClassDrivers[g_sUSBHCD.lEventDriver]->pfnIntHandler) &&
+ (g_sUSBHCD.ulEventEnables & ulEvFlag))
+ {
+ g_sUSBHCD.pClassDrivers[g_sUSBHCD.lEventDriver]->pfnIntHandler(psEvent);
+ }
+}
+
+//*****************************************************************************
+//
+// This function handles the necessary clean up for device disconnect.
+//
+// \param ulIndex is the device number for the device that was disconnected.
+//
+// This function handles all of the necessary clean up after a device
+// disconnect has been detected by the stack. This includes calling back the
+// appropriate driver if necessary.
+//
+// \return None.
+//
+//*****************************************************************************
+static void
+USBHCDDeviceDisconnected(unsigned long ulIndex, unsigned long ulDevIndex)
+{
+ tEventInfo sEvent;
+
+ ASSERT(ulIndex == 0);
+ ASSERT(ulDevIndex <= MAX_USB_DEVICES);
+
+ //
+ // If there is an event driver with a valid event handler and the
+ // USBHCD_EVFLAG_DISCNCT is enabled, then call the registered event handler.
+ //
+ sEvent.ulEvent = USB_EVENT_DISCONNECTED;
+ sEvent.ulInstance = (ulIndex << 16) | ulDevIndex;
+ InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_DISCNCT);
+
+ //
+ // Reset the class.
+ //
+ g_sUSBHCD.ulClass = USB_CLASS_EVENTS;
+
+ if(g_sUSBHCD.USBDevice[ulDevIndex].pConfigDescriptor)
+ {
+ //
+ // Invalidate the configuration descriptor.
+ //
+ g_sUSBHCD.USBDevice[ulDevIndex].pConfigDescriptor = 0;
+ g_sUSBHCD.USBDevice[ulDevIndex].bConfigRead = false;
+
+ }
+
+ //
+ // Reset the max packet size so that this will be re-read from new devices.
+ //
+ g_sUSBHCD.USBDevice[ulDevIndex].DeviceDescriptor.bMaxPacketSize0 = 0;
+
+ //
+ // No longer have a device descriptor.
+ //
+ g_sUSBHCD.USBDevice[ulDevIndex].DeviceDescriptor.bLength = 0;
+
+ //
+ // No longer addressed.
+ //
+ g_sUSBHCD.USBDevice[ulDevIndex].ulAddress = 0;
+
+ //
+ // If this was an active driver then close it out.
+ //
+ if(g_lUSBHActiveDriver[ulDevIndex] >= 0)
+ {
+ //
+ // Call the driver Close entry point.
+ //
+ g_sUSBHCD.pClassDrivers[g_lUSBHActiveDriver[ulDevIndex]]->
+ pfnClose(g_pvDriverInstance[ulDevIndex]);
+
+ //
+ // No active driver now present.
+ //
+ g_lUSBHActiveDriver[ulDevIndex] = -1;
+ g_pvDriverInstance[ulDevIndex] = 0;
+ }
+
+ //
+ // This call is necessary for OTG controllers to know that the host
+ // stack has completed handling the disconnect of the device before
+ // removing power and returning to a state that can allow OTG
+ // negotiations once again.
+ // We only do this if the disconnected device
+ // was attached directly to us (device index 0).
+ //
+ if((ulDevIndex == 0) && (g_eUSBMode == USB_MODE_OTG))
+ {
+ OTGDeviceDisconnect(0);
+ }
+}
+
+//*****************************************************************************
+//
+//! This function is the main routine for the Host Controller Driver.
+//!
+//! This function is the main routine for the host controller driver, and must
+//! be called periodically by the main application outside of a callback
+//! context. This allows for a simple cooperative system to access the the
+//! host controller driver interface without the need for an RTOS. All time
+//! critical operations are handled in interrupt context but all blocking
+//! operations are run from the this function to allow them to block and wait
+//! for completion without holding off other interrupts.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBHCDMain(void)
+{
+ tUSBHDeviceState eOldState;
+ unsigned long ulLoop;
+ tEventInfo sEvent;
+
+ //
+ // Save the old state to detect changes properly.
+ //
+ eOldState = g_sUSBHCD.eDeviceState[0];
+
+ //
+ // Fix up the state if any important interrupt events occurred.
+ //
+ if(g_ulUSBHIntEvents)
+ {
+ //
+ // Disable the USB interrupt.
+ //
+ OS_INT_DISABLE(INT_USB0);
+
+ if(g_ulUSBHIntEvents & INT_EVENT_POWER_FAULT)
+ {
+ //
+ // A power fault has occurred so notify the application if there
+ // is an event handler and the event has been enabled.
+ //
+ sEvent.ulEvent = USB_EVENT_POWER_FAULT;
+ sEvent.ulInstance = 0;
+ InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_PWRFAULT);
+
+ g_sUSBHCD.eDeviceState[0] = HCD_POWER_FAULT;
+ }
+ else if(g_ulUSBHIntEvents & INT_EVENT_VBUS_ERR)
+ {
+ //
+ // A VBUS error has occurred. This event trumps connect and
+ // disconnect since it will cause a controller reset.
+ //
+ g_sUSBHCD.eDeviceState[0] = HCD_VBUS_ERROR;
+ }
+ else
+ {
+ //
+ // Has a device connected?
+ //
+ if(g_ulUSBHIntEvents & INT_EVENT_CONNECT)
+ {
+ g_sUSBHCD.eDeviceState[0] = HCD_DEV_RESET;
+ g_sUSBHCD.USBDevice[0].ucHub = 0;
+ g_sUSBHCD.USBDevice[0].ucHubPort = 0;
+ }
+ else
+ {
+ //
+ // Has a device disconnected?
+ //
+ if(g_ulUSBHIntEvents & INT_EVENT_DISCONNECT)
+ {
+ g_sUSBHCD.eDeviceState[0] = HCD_DEV_DISCONNECTED;
+ }
+ }
+
+ //
+ // Handle the start of frame event
+ //
+ if(g_ulUSBHIntEvents & INT_EVENT_SOF)
+ {
+ //
+ // If the connect event is enabled then send the event.
+ //
+ sEvent.ulEvent = USB_EVENT_SOF;
+ sEvent.ulInstance = 0;
+ InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_SOF);
+
+ USBHostCheckPipes();
+
+ //
+ // Call the hub driver to have it perform any necessary processing to
+ // handle downstream devices.
+ //
+ USBHHubMain();
+ }
+ }
+
+ //
+ // Clear the flags.
+ //
+ g_ulUSBHIntEvents = 0;
+
+ //
+ // Enable the USB interrupt.
+ //
+ OS_INT_ENABLE(INT_USB0);
+ }
+
+ //
+ // Process the state machine for each connected device. Yes, the exit
+ // condition for this loop is correct since we support (MAX_USB_DEVICES+1)
+ // devices (the hub counts as one).
+ //
+ for(ulLoop = 0; ulLoop <= MAX_USB_DEVICES; ulLoop++)
+ {
+ //
+ // If this is not the first device (i.e. the one directly connected to
+ // the host controller) then set the old state to the current state
+ // since we won't have mucked with it in any of the previous code.
+ //
+ if(ulLoop != 0)
+ {
+ eOldState = g_sUSBHCD.eDeviceState[ulLoop];
+ }
+
+ //
+ // Process the state machine for this device.
+ //
+ ProcessUSBDeviceStateMachine(eOldState, ulLoop);
+ }
+}
+
+static void
+ProcessUSBDeviceStateMachine(tUSBHDeviceState eOldState,
+ unsigned long ulDevIndex)
+{
+ switch(g_sUSBHCD.eDeviceState[ulDevIndex])
+ {
+ //
+ // There was a power fault condition so shut down and wait for the
+ // application to re-initialized the system.
+ //
+ case HCD_POWER_FAULT:
+ {
+ break;
+ }
+
+ //
+ // There was a VBUS error so handle it.
+ //
+ case HCD_VBUS_ERROR:
+ {
+ //
+ // Disable USB interrupts.
+ //
+ OS_INT_DISABLE(INT_USB0);
+
+ //
+ // If there was a device in any state of connection then indicate
+ // that it has been disconnected.
+ //
+ if((eOldState != HCD_IDLE) && (eOldState != HCD_POWER_FAULT))
+ {
+ //
+ // Handle device disconnect.
+ //
+ USBHCDDeviceDisconnected(0, ulDevIndex);
+ }
+
+ //
+ // Reset the controller.
+ //
+ MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_USB0);
+
+ //
+ // Wait for 100ms before trying to re-power the device.
+ //
+ OS_DELAY(g_ulTickms * 100);
+
+ //
+ // Re-initialize the HCD.
+ //
+ USBHCDInitInternal(0, g_sUSBHCD.pvPool, g_sUSBHCD.ulPoolSize);
+
+ break;
+ }
+ //
+ // Trigger a reset to the connected device.
+ //
+ case HCD_DEV_RESET:
+ {
+ if(!ulDevIndex)
+ {
+ //
+ // Trigger a Reset. This is only ever done for devices attached
+ // directly to the controller.
+ //
+ DEBUG_OUTPUT("USB reset.\n");
+ USBHCDReset(0);
+ }
+
+ //
+ // The state moves to connected but not configured.
+ //
+ g_sUSBHCD.eDeviceState[0] = HCD_DEV_CONNECTED;
+
+ //
+ // Set the memory to use for the config descriptor and save the
+ // size.
+ //
+ g_sUSBHCD.USBDevice[0].pConfigDescriptor = g_sUSBHCD.pvPool;
+ g_sUSBHCD.USBDevice[0].ulConfigDescriptorSize =
+ g_sUSBHCD.ulPoolSize;
+
+ //
+ // Remember that we don't have a valid configuration descriptor
+ // yet.
+ //
+ g_sUSBHCD.USBDevice[0].bConfigRead = false;
+
+ break;
+ }
+ //
+ // Device connection has been established now start enumerating
+ // the device.
+ //
+ case HCD_DEV_CONNECTED:
+ {
+ //
+ // First check if we have read the device descriptor at all
+ // before proceeding.
+ //
+ if(g_sUSBHCD.USBDevice[ulDevIndex].DeviceDescriptor.bLength == 0)
+ {
+ //
+ // Initialize a request for the device descriptor.
+ //
+ DEBUG_OUTPUT("Connection %d - getting device descriptor\n",
+ ulDevIndex);
+
+ if(USBHCDGetDeviceDescriptor(0,
+ &g_sUSBHCD.USBDevice[ulDevIndex]) == 0)
+ {
+ //
+ // If the device descriptor cannot be read then the device
+ // will be treated as unknown.
+ //
+ g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_DEV_ERROR;
+
+ DEBUG_OUTPUT("Connection %d - failed to get descriptor\n",
+ ulDevIndex);
+
+ //
+ // Send an unknown connection event.
+ //
+ SendUnknownConnect(0, 1);
+
+ //
+ // If the device is connected via a hub, tell the hub
+ // driver that we experienced an error enumerating the
+ // device.
+ //
+ if(g_sUSBHCD.USBDevice[ulDevIndex].ucHub)
+ {
+ USBHHubEnumerationError(
+ g_sUSBHCD.USBDevice[ulDevIndex].ucHub,
+ g_sUSBHCD.USBDevice[ulDevIndex].ucHubPort);
+ }
+ }
+ }
+ //
+ // If we have the device descriptor then move on to setting
+ // the address of the device.
+ //
+ else if(g_sUSBHCD.USBDevice[ulDevIndex].ulAddress == 0)
+ {
+ DEBUG_OUTPUT("Connection %d - setting address %d\n",
+ ulDevIndex, ulDevIndex + 1);
+
+ //
+ // Send the set address command.
+ //
+ USBHCDSetAddress(ulDevIndex, (ulDevIndex + 1));
+
+ //
+ // Save the address.
+ //
+ g_sUSBHCD.USBDevice[ulDevIndex].ulAddress = (ulDevIndex + 1);
+
+ //
+ // Move on to the addressed state.
+ //
+ g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_DEV_ADDRESSED;
+ }
+ break;
+ }
+ case HCD_DEV_ADDRESSED:
+ {
+ //
+ // First check if we have read the configuration descriptor.
+ //
+ if(!g_sUSBHCD.USBDevice[ulDevIndex].bConfigRead)
+ {
+ DEBUG_OUTPUT("Connection %d - getting config descriptor\n",
+ ulDevIndex);
+
+ //
+ // Initialize a request for the config descriptor.
+ //
+ if(USBHCDGetConfigDescriptor(0,
+ &g_sUSBHCD.USBDevice[ulDevIndex]) == 0)
+ {
+ //
+ // If the device descriptor cannot be read then the device
+ // will be treated as unknown.
+ //
+ g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_DEV_ERROR;
+
+ DEBUG_OUTPUT("Connection %d - failed to get descriptor\n",
+ ulDevIndex);
+
+ //
+ // Send an unknown connection event.
+ //
+ SendUnknownConnect(0, 0);
+
+ //
+ // If the device is connected via a hub, tell the hub
+ // driver that we experienced an error enumerating the
+ // device.
+ //
+ if(g_sUSBHCD.USBDevice[ulDevIndex].ucHub)
+ {
+ USBHHubEnumerationError(
+ g_sUSBHCD.USBDevice[ulDevIndex].ucHub,
+ g_sUSBHCD.USBDevice[ulDevIndex].ucHubPort);
+ }
+ }
+ }
+ //
+ // Now have addressed and received the device configuration,
+ // so get ready to set the device configuration.
+ //
+ else
+ {
+ DEBUG_OUTPUT("Connection %d - setting configuration.\n",
+ ulDevIndex);
+
+ //
+ // Use the first configuration to set the device
+ // configuration.
+ //
+ USBHCDSetConfig(0,
+ (unsigned long)&g_sUSBHCD.USBDevice[ulDevIndex], 1);
+
+ //
+ // Move on to the configured state.
+ //
+ g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_DEV_CONFIGURED;
+
+ //
+ // Open the driver for the device.
+ //
+ g_lUSBHActiveDriver[ulDevIndex] = USBHCDOpenDriver(0,
+ ulDevIndex);
+
+ //
+ // If the device is connected via a hub, tell the hub
+ // driver that enumeration is complete.
+ //
+ if(g_sUSBHCD.USBDevice[ulDevIndex].ucHub)
+ {
+ USBHHubEnumerationComplete(
+ g_sUSBHCD.USBDevice[ulDevIndex].ucHub,
+ g_sUSBHCD.USBDevice[ulDevIndex].ucHubPort);
+ }
+ }
+ break;
+ }
+ //
+ // The device was making a request and is now complete.
+ //
+ case HCD_DEV_REQUEST:
+ {
+ g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_DEV_CONNECTED;
+ break;
+ }
+ //
+ // The strings are currently not accessed.
+ //
+ case HCD_DEV_GETSTRINGS:
+ {
+ break;
+ }
+ //
+ // Basically Idle at this point.
+ //
+ case HCD_DEV_DISCONNECTED:
+ {
+ DEBUG_OUTPUT("Connection %d - disconnected.\n",
+ ulDevIndex);
+
+ //
+ // Handle device disconnect.
+ //
+ USBHCDDeviceDisconnected(0, ulDevIndex);
+
+ //
+ // Return to the Idle state.
+ //
+ g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_IDLE;
+ break;
+ }
+
+ //
+ // Connection and enumeration is complete so allow this function
+ // to exit.
+ //
+ case HCD_DEV_CONFIGURED:
+ {
+ break;
+ }
+
+ //
+ // Poorly behaving device are in limbo in this state until removed.
+ //
+ case HCD_DEV_ERROR:
+ {
+ DEBUG_OUTPUT("Connection %d - Error!\n", ulDevIndex);
+
+ //
+ // If this device is connected directly to us, tidy up and ignore
+ // it until it is removed. If the device is connected to a hub,
+ // we just leave it in the error state until it is removed.
+ //
+ if(ulDevIndex == 0)
+ {
+ g_ulUSBHIntEvents |= INT_EVENT_DISCONNECT;
+ g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_IDLE;
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+//*****************************************************************************
+//
+//! This function completes a control transaction to a device.
+//!
+//! \param ulIndex is the controller index to use for this transfer.
+//! \param pSetupPacket is the setup request to be sent.
+//! \param pDevice is the device instance pointer for this request.
+//! \param pData is the data to send for OUT requests or the receive buffer
+//! for IN requests.
+//! \param ulSize is the size of the buffer in pData.
+//! \param ulMaxPacketSize is the maximum packet size for the device for this
+//! request.
+//!
+//! This function handles the state changes necessary to send a control
+//! transaction to a device. This function should not be called from within
+//! an interrupt callback as it is a blocking function.
+//!
+//! \return The number of bytes of data that were sent or received as a result
+//! of this request.
+//
+//*****************************************************************************
+unsigned long
+USBHCDControlTransfer(unsigned long ulIndex, tUSBRequest *pSetupPacket,
+ tUSBHostDevice *pDevice, unsigned char *pData,
+ unsigned long ulSize, unsigned long ulMaxPacketSize)
+{
+ unsigned long ulRemaining;
+ unsigned long ulDataSize;
+
+ //
+ // Debug sanity check.
+ //
+ ASSERT(g_sUSBHEP0State.eState == EP0_STATE_IDLE);
+ ASSERT(ulIndex == 0);
+
+ //
+ // Initialize the state of the data for this request.
+ //
+ g_sUSBHEP0State.pData = pData;
+ g_sUSBHEP0State.ulBytesRemaining = ulSize;
+ g_sUSBHEP0State.ulDataSize = ulSize;
+
+ //
+ // Set the maximum packet size.
+ //
+ g_sUSBHEP0State.ulMaxPacketSize = ulMaxPacketSize;
+
+ //
+ // Save the current address.
+ //
+ g_sUSBHEP0State.ulDevAddress = pDevice->ulAddress;
+
+ //
+ // Set the address the host will used to communicate with the device.
+ //
+ MAP_USBHostAddrSet(USB0_BASE, USB_EP_0, g_sUSBHEP0State.ulDevAddress,
+ USB_EP_HOST_OUT);
+
+ //
+ // Put the data in the correct FIFO.
+ //
+ MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, (unsigned char *)pSetupPacket,
+ sizeof(tUSBRequest));
+
+ //
+ // If this is an IN request, change to that state.
+ //
+ if(pSetupPacket->bmRequestType & USB_RTYPE_DIR_IN)
+ {
+ g_sUSBHEP0State.eState = EP0_STATE_SETUP_IN;
+ }
+ else
+ {
+ //
+ // If there is no data then this is not an OUT request.
+ //
+ if(ulSize != 0)
+ {
+ //
+ // Since there is data, this is an OUT request.
+ //
+ g_sUSBHEP0State.eState = EP0_STATE_SETUP_OUT;
+ }
+ else
+ {
+ //
+ // Otherwise this request has no data and just a status phase.
+ //
+ g_sUSBHEP0State.eState = EP0_STATE_STATUS_IN;
+ }
+ }
+
+ //
+ // Make sure we are talking to the correct device.
+ //
+ USBHostHubAddrSet(USB0_BASE, USB_EP_0,
+ ((pDevice->ucHub << 8) | (pDevice->ucHubPort)),
+ USB_EP_HOST_OUT | (pDevice->bLowSpeed ?
+ USB_EP_SPEED_LOW : USB_EP_SPEED_FULL));
+
+ //
+ // Send the Setup packet.
+ //
+ MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_SETUP);
+
+ //
+ // Block until endpoint 0 returns to the IDLE state.
+ //
+ while(g_sUSBHEP0State.eState != EP0_STATE_IDLE)
+ {
+ OS_INT_DISABLE(INT_USB0);
+
+ if((g_ulUSBHIntEvents & (INT_EVENT_ENUM | INT_EVENT_SOF)) == (INT_EVENT_ENUM | INT_EVENT_SOF))
+ {
+ g_ulUSBHIntEvents &= ~(INT_EVENT_ENUM | INT_EVENT_SOF);
+
+ USBHCDEnumHandler();
+ }
+
+ OS_INT_ENABLE(INT_USB0);
+
+ if(g_sUSBHEP0State.eState == EP0_STATE_ERROR)
+ {
+ return(0xffffffff);
+ }
+
+ //
+ // If we aborted the transfer due to an error, tell the caller
+ // that no bytes were transferred.
+ //
+ if(g_ulUSBHIntEvents & (INT_EVENT_VBUS_ERR | INT_EVENT_DISCONNECT))
+ {
+ return(0xffffffff);
+ }
+ }
+
+ //
+ // Calculate and return the number of bytes that were sent or received.
+ // The extra copy into local variables is required to prevent some
+ // compilers from warning about undefined order of volatile access.
+ //
+ ulDataSize = g_sUSBHEP0State.ulDataSize;
+ ulRemaining = g_sUSBHEP0State.ulBytesRemaining;
+
+ return(ulDataSize - ulRemaining);
+}
+
+//*****************************************************************************
+//
+// Starts enumerating a new device connected via the hub.
+//
+// \param ulIndex is the index of the USB controller to use.
+// \param ulHub is the hub address from which the connection is being made.
+// \param ulPort is the hub port number that the new device is connected to.
+// \param pucConfigPool is memory to be used to store the device's config
+// descriptor.
+// \param ulConfigSize is the number of bytes available in the buffer pointed
+// to by pucConfigPool.
+//
+// This function is called by the hub class driver after it has detected a new
+// device connection and reset the device.
+//
+// \return Returns the index of the device allocated or 0 if no resources are
+// available. Device index 0 is the hub itself.
+//
+//*****************************************************************************
+unsigned long
+USBHCDHubDeviceConnected(unsigned long ulIndex, unsigned char ucHub,
+ unsigned char ucPort, tBoolean bLowSpeed,
+ unsigned char *pucConfigPool,
+ unsigned long ulConfigSize)
+{
+ unsigned long ulDevIndex;
+
+ //
+ // Debug sanity checks.
+ //
+ ASSERT(ulIndex == 0);
+ ASSERT(pucConfigPool);
+ ASSERT(ulConfigSize);
+ ASSERT(ucPort);
+
+ DEBUG_OUTPUT("Connection from hub %d, port %d.\n", ucHub, ucPort);
+
+ //
+ // Look for a free slot in the device table.
+ //
+ for(ulDevIndex = 1; ulDevIndex <= MAX_USB_DEVICES; ulDevIndex++)
+ {
+ if(g_sUSBHCD.USBDevice[ulDevIndex].pConfigDescriptor == 0)
+ {
+ //
+ // We found one. Set the state to ensure that it gets enumerated.
+ //
+ g_sUSBHCD.USBDevice[ulDevIndex].pConfigDescriptor =
+ (tConfigDescriptor *)pucConfigPool;
+ g_sUSBHCD.USBDevice[ulDevIndex].ulConfigDescriptorSize =
+ ulConfigSize;
+ g_sUSBHCD.USBDevice[ulDevIndex].pConfigDescriptor->bLength = 0;
+ g_sUSBHCD.USBDevice[ulDevIndex].ucHub = ucHub;
+ g_sUSBHCD.USBDevice[ulDevIndex].ucHubPort = ucPort;
+ g_sUSBHCD.USBDevice[ulDevIndex].bLowSpeed = bLowSpeed;
+ g_sUSBHCD.USBDevice[ulDevIndex].DeviceDescriptor.bLength = 0;
+
+ //
+ // Set the state to ensure enumeration begins.
+ //
+ g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_DEV_CONNECTED;
+
+ DEBUG_OUTPUT("Allocating device %d\n", ulDevIndex);
+
+ //
+ // Pass the device index back to the hub driver.
+ //
+ return(ulDevIndex);
+ }
+ }
+
+ //
+ // If we get here, there are device slots available so send back an invalid
+ // device index to tell the caller to ignore this device.
+ //
+ return(0);
+}
+
+//*****************************************************************************
+//
+// TODO: Documentation
+//
+//*****************************************************************************
+void
+USBHCDHubDeviceDisconnected(unsigned long ulIndex, unsigned long ulDevIndex)
+{
+ //
+ // Debug sanity checks.
+ //
+ ASSERT(ulIndex == 0);
+ ASSERT(ulDevIndex && (ulDevIndex <= MAX_USB_DEVICES));
+
+ DEBUG_OUTPUT("Disconnection from hub %d, port %d, device %d\n",
+ g_sUSBHCD.USBDevice[ulDevIndex].ucHub,
+ g_sUSBHCD.USBDevice[ulDevIndex].ucHubPort, ulDevIndex);
+
+ //
+ // Set the device state to ensure that USBHCDMain cleans it up.
+ //
+ g_sUSBHCD.eDeviceState[ulDevIndex] = HCD_DEV_DISCONNECTED;
+}
+
+//*****************************************************************************
+//
+// This is the endpoint 0 interrupt handler.
+//
+// \return None.
+//
+//*****************************************************************************
+static void
+USBHCDEnumHandler(void)
+{
+ unsigned long ulEPStatus;
+ unsigned long ulDataSize;
+
+ //
+ // Get the end point 0 status.
+ //
+ ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, USB_EP_0);
+
+ //
+ // If there was an error then go to the error state.
+ //
+ if(ulEPStatus == USB_HOST_EP0_ERROR)
+ {
+ //
+ // Clear this status indicating that the status packet was
+ // received.
+ //
+ MAP_USBHostEndpointStatusClear(USB0_BASE, USB_EP_0, USB_HOST_EP0_ERROR);
+ MAP_USBFIFOFlush(USB0_BASE, USB_EP_0, 0);
+
+ //
+ // Just go back to the idle state.
+ //
+ g_sUSBHEP0State.eState = EP0_STATE_ERROR;
+
+ return;
+ }
+
+ switch(g_sUSBHEP0State.eState)
+ {
+ //
+ // Handle the status state, this is a transitory state from
+ // USB_STATE_TX or USB_STATE_RX back to USB_STATE_IDLE.
+ //
+ case EP0_STATE_STATUS:
+ {
+ //
+ // Handle the case of a received status packet.
+ //
+ if(ulEPStatus & (USB_HOST_EP0_RXPKTRDY | USB_HOST_EP0_STATUS))
+ {
+ //
+ // Clear this status indicating that the status packet was
+ // received.
+ //
+ MAP_USBHostEndpointStatusClear(USB0_BASE, USB_EP_0,
+ (USB_HOST_EP0_RXPKTRDY |
+ USB_HOST_EP0_STATUS));
+ }
+
+ //
+ // Just go back to the idle state.
+ //
+ g_sUSBHEP0State.eState = EP0_STATE_IDLE;
+
+ break;
+ }
+
+ //
+ // This state triggers a STATUS IN request from the device.
+ //
+ case EP0_STATE_STATUS_IN:
+ {
+ //
+ // Generate an IN request from the device.
+ //
+ MAP_USBHostRequestStatus(USB0_BASE);
+
+ //
+ // Change to the status phase and wait for the response.
+ //
+ g_sUSBHEP0State.eState = EP0_STATE_STATUS;
+
+ break;
+ }
+
+ //
+ // In the IDLE state the code is waiting to receive data from the host.
+ //
+ case EP0_STATE_IDLE:
+ {
+ break;
+ }
+
+ //
+ // Data is still being sent to the host so handle this in the
+ // EP0StateTx() function.
+ //
+ case EP0_STATE_SETUP_OUT:
+ {
+ //
+ // Send remaining data if necessary.
+ //
+ USBHCDEP0StateTx();
+
+ break;
+ }
+
+ //
+ // Handle the receive state for commands that are receiving data on
+ // endpoint 0.
+ //
+ case EP0_STATE_SETUP_IN:
+ {
+ //
+ // Generate a new IN request to the device.
+ //
+ MAP_USBHostRequestIN(USB0_BASE, USB_EP_0);
+
+ //
+ // Proceed to the RX state to receive the requested data.
+ //
+ g_sUSBHEP0State.eState = EP0_STATE_RX;
+
+ break;
+ }
+
+ //
+ // The endpoint remains in this state until all requested data has
+ // been received.
+ //
+ case EP0_STATE_RX:
+ {
+ //
+ // There was a stall on endpoint 0 so go back to the idle state
+ // as this command has been terminated.
+ //
+ if(ulEPStatus & USB_HOST_EP0_RX_STALL)
+ {
+ g_sUSBHEP0State.eState = EP0_STATE_IDLE;
+
+ //
+ // Clear the stalled state on endpoint 0.
+ //
+ MAP_USBHostEndpointStatusClear(USB0_BASE, USB_EP_0, ulEPStatus);
+ break;
+ }
+
+ //
+ // Set the number of bytes to get out of this next packet.
+ //
+ if(g_sUSBHEP0State.ulBytesRemaining >
+ g_sUSBHEP0State.ulMaxPacketSize)
+ {
+ //
+ // Don't send more than EP0_MAX_PACKET_SIZE bytes.
+ //
+ ulDataSize = MAX_PACKET_SIZE_EP0;
+ }
+ else
+ {
+ //
+ // There was space so send the remaining bytes.
+ //
+ ulDataSize = g_sUSBHEP0State.ulBytesRemaining;
+ }
+
+ if(ulDataSize != 0)
+ {
+ //
+ // Get the data from the USB controller end point 0.
+ //
+ MAP_USBEndpointDataGet(USB0_BASE, USB_EP_0,
+ g_sUSBHEP0State.pData,
+ &ulDataSize);
+ }
+
+ //
+ // Advance the pointer.
+ //
+ g_sUSBHEP0State.pData += ulDataSize;
+
+ //
+ // Decrement the number of bytes that are being waited on.
+ //
+ g_sUSBHEP0State.ulBytesRemaining -= ulDataSize;
+
+ //
+ // Need to ack the data on end point 0 in this case
+ // without setting data end.
+ //
+ MAP_USBHostEndpointDataAck(USB0_BASE, USB_EP_0);
+
+ //
+ // If there was not more than the maximum packet size bytes of data
+ // the this was a short packet and indicates that this transfer is
+ // complete. If there were exactly g_sUSBHEP0State.ulMaxPacketSize
+ // remaining then there still needs to be null packet sent before
+ // this transfer is complete.
+ //
+ if((ulDataSize < g_sUSBHEP0State.ulMaxPacketSize) ||
+ (g_sUSBHEP0State.ulBytesRemaining == 0))
+ {
+ //
+ // Return to the idle state.
+ //
+ g_sUSBHEP0State.eState = EP0_STATE_STATUS;
+
+ //
+ // No more data.
+ //
+ g_sUSBHEP0State.pData = 0;
+
+ //
+ // Send a null packet to acknowledge that all data was received.
+ //
+ MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_STATUS);
+ }
+ else
+ {
+ //
+ // Request more data.
+ //
+ MAP_USBHostRequestIN(USB0_BASE, USB_EP_0);
+ }
+ break;
+ }
+
+ //
+ // The device stalled endpoint zero so check if the stall needs to be
+ // cleared once it has been successfully sent.
+ //
+ case EP0_STATE_STALL:
+ {
+ //
+ // Reset the global end point 0 state to IDLE.
+ //
+ g_sUSBHEP0State.eState = EP0_STATE_IDLE;
+
+ break;
+ }
+
+ //
+ // Halt on an unknown state, but only in DEBUG builds.
+ //
+ default:
+ {
+ ASSERT(0);
+ break;
+ }
+ }
+}
+
+//*****************************************************************************
+//
+// This internal function handles sending data on endpoint 0.
+//
+// \return None.
+//
+//*****************************************************************************
+static void
+USBHCDEP0StateTx(void)
+{
+ unsigned long ulNumBytes;
+ unsigned char *pData;
+
+ //
+ // In the TX state on endpoint 0.
+ //
+ g_sUSBHEP0State.eState = EP0_STATE_SETUP_OUT;
+
+ //
+ // Set the number of bytes to send this iteration.
+ //
+ ulNumBytes = g_sUSBHEP0State.ulBytesRemaining;
+
+ //
+ // Limit individual transfers to 64 bytes.
+ //
+ if(ulNumBytes > 64)
+ {
+ ulNumBytes = 64;
+ }
+
+ //
+ // Save the pointer so that it can be passed to the USBEndpointDataPut()
+ // function.
+ //
+ pData = (unsigned char *)g_sUSBHEP0State.pData;
+
+ //
+ // Advance the data pointer and counter to the next data to be sent.
+ //
+ g_sUSBHEP0State.ulBytesRemaining -= ulNumBytes;
+ g_sUSBHEP0State.pData += ulNumBytes;
+
+ //
+ // Put the data in the correct FIFO.
+ //
+ MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, pData, ulNumBytes);
+
+ //
+ // If this is exactly 64 then don't set the last packet yet.
+ //
+ if(ulNumBytes == 64)
+ {
+ //
+ // There is more data to send or exactly 64 bytes were sent, this
+ // means that there is either more data coming or a null packet needs
+ // to be sent to complete the transaction.
+ //
+ MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_OUT);
+ }
+ else
+ {
+ //
+ // Send the last bit of data.
+ //
+ MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_OUT);
+
+ //
+ // Now go to the status state and wait for the transmit to complete.
+ //
+ g_sUSBHEP0State.eState = EP0_STATE_STATUS_IN;
+ }
+}
+
+//*****************************************************************************
+//
+//! This function returns the USB hub port for the requested device instance.
+//!
+//! \param ulInstance is a unique value indicating which device to query.
+//!
+//! This function returns the USB hub port for the device that is associated
+//! with the \e ulInstance parameter. The caller must use the value for
+//! \e ulInstance was passed to the application when it receives a
+//! USB_EVENT_CONNECTED event. The function returns the USB hub port for
+//! the interface number specified by the \e ulInterface parameter.
+//!
+//! \return The USB hub port for the requested interface.
+//
+//*****************************************************************************
+unsigned char
+USBHCDDevHubPort(unsigned long ulInstance)
+{
+ unsigned long ulDevIndex;
+
+ ulDevIndex = HCDInstanceToDevIndex(ulInstance);
+
+ if(ulDevIndex == 0xff)
+ {
+ return(ulDevIndex);
+ }
+
+ return(g_sUSBHCD.USBDevice[ulDevIndex].ucHubPort);
+}
+
+//*****************************************************************************
+//
+//! This function will return the USB address for the requested device
+//! instance.
+//!
+//! \param ulInstance is a unique value indicating which device to query.
+//!
+//! This function returns the USB address for the device that is associated
+//! with the \e ulInstance parameter. The caller must use a value for
+//! \e ulInstance have been passed to the application when it receives a
+//! USB_EVENT_CONNECTED event. The function will return the USB address for
+//! the interface number specified by the \e ulInterface parameter.
+//!
+//! \return The USB address for the requested interface.
+//
+//*****************************************************************************
+unsigned char
+USBHCDDevAddress(unsigned long ulInstance)
+{
+ unsigned long ulDevIndex;
+
+ ulDevIndex = HCDInstanceToDevIndex(ulInstance);
+
+ if(ulDevIndex == 0xff)
+ {
+ return(ulDevIndex);
+ }
+
+ return(g_sUSBHCD.USBDevice[ulDevIndex].ulAddress);
+}
+
+//*****************************************************************************
+//
+//! This function will return the USB class for the requested device
+//! instance.
+//!
+//! \param ulInstance is a unique value indicating which device to query.
+//! \param ulInterface is the interface number to query for the USB class.
+//!
+//! This function returns the USB class for the device that is associated
+//! with the \e ulInstance parameter. The caller must use a value for
+//! \e ulInstance have been passed to the application when it receives a
+//! USB_EVENT_CONNECTED event. The function will return the USB class for
+//! the interface number specified by the \e ulInterface parameter. If
+//! \e ulInterface is set to 0xFFFFFFFF then the function will return the USB
+//! class for the first interface that is found in the device's USB
+//! descriptors.
+//!
+//! \return The USB class for the requested interface.
+//
+//*****************************************************************************
+unsigned char
+USBHCDDevClass(unsigned long ulInstance, unsigned long ulInterface)
+{
+ unsigned long ulDevIndex;
+ tInterfaceDescriptor *pInterface;
+
+ ulDevIndex = HCDInstanceToDevIndex(ulInstance);
+
+ //
+ // If the instance was not valid return an undefined class.
+ //
+ if(ulDevIndex == 0xff)
+ {
+ return(USB_CLASS_DEVICE);
+ }
+
+ //
+ // Get the interface descriptor.
+ //
+ pInterface = USBDescGetInterface(
+ g_sUSBHCD.USBDevice[ulDevIndex].pConfigDescriptor,
+ g_sUSBHCD.USBDevice[ulDevIndex].ulInterface,
+ ulInterface);
+
+ //
+ // Make sure that the interface requested actually exists.
+ //
+ if(pInterface)
+ {
+ //
+ // Return the interface class.
+ //
+ return(pInterface->bInterfaceClass);
+ }
+
+ //
+ // No valid interface so return an undefined class.
+ //
+ return(USB_CLASS_DEVICE);
+}
+
+//*****************************************************************************
+//
+//! This function will return the USB subclass for the requested device
+//! instance.
+//!
+//! \param ulInstance is a unique value indicating which device to query.
+//! \param ulInterface is the interface number to query for the USB subclass.
+//!
+//! This function returns the USB subclass for the device that is associated
+//! with the \e ulInstance parameter. The caller must use a value for
+//! \e ulInstance have been passed to the application when it receives a
+//! USB_EVENT_CONNECTED event. The function will return the USB subclass for
+//! the interface number specified by the \e ulInterface parameter. If
+//! \e ulInterface is set to 0xFFFFFFFF then the function will return the USB
+//! subclass for the first interface that is found in the device's USB
+//! descriptors.
+//!
+//! \return The USB subclass for the requested interface.
+//
+//*****************************************************************************
+unsigned char
+USBHCDDevSubClass(unsigned long ulInstance, unsigned long ulInterface)
+{
+ unsigned long ulDevIndex;
+ tInterfaceDescriptor *pInterface;
+
+ ulDevIndex = HCDInstanceToDevIndex(ulInstance);
+
+ //
+ // If the instance was not valid return an undefined subclass.
+ //
+ if(ulDevIndex == 0xff)
+ {
+ return(USB_SUBCLASS_UNDEFINED);
+ }
+
+ //
+ // Get the interface descriptor.
+ //
+ pInterface = USBDescGetInterface(
+ g_sUSBHCD.USBDevice[ulDevIndex].pConfigDescriptor,
+ g_sUSBHCD.USBDevice[ulDevIndex].ulInterface,
+ ulInterface);
+
+ //
+ // Make sure that the interface requested actually exists.
+ //
+ if(pInterface)
+ {
+ //
+ // Return the interface subclass.
+ //
+ return(pInterface->bInterfaceSubClass);
+ }
+
+ //
+ // No valid interface so return an undefined subclass.
+ //
+ return(USB_SUBCLASS_UNDEFINED);
+}
+
+//*****************************************************************************
+//
+//! This function will return the USB protocol for the requested device
+//! instance.
+//!
+//! \param ulInstance is a unique value indicating which device to query.
+//! \param ulInterface is the interface number to query for the USB protocol.
+//!
+//! This function returns the USB protocol for the device that is associated
+//! with the \e ulInstance parameter. The caller must use a value for
+//! \e ulInstance have been passed to the application when it receives a
+//! USB_EVENT_CONNECTED event. The function will return the USB protocol for
+//! the interface number specified by the \e ulInterface parameter. If
+//! \e ulInterface is set to 0xFFFFFFFF then the function will return the USB
+//! protocol for the first interface that is found in the device's USB
+//! descriptors.
+//!
+//! \return The USB protocol for the requested interface.
+//
+//*****************************************************************************
+unsigned char
+USBHCDDevProtocol(unsigned long ulInstance, unsigned long ulInterface)
+{
+ unsigned long ulDevIndex;
+ tInterfaceDescriptor *pInterface;
+
+ ulDevIndex = HCDInstanceToDevIndex(ulInstance);
+
+ //
+ // If the instance was not valid return an undefined protocol.
+ //
+ if(ulDevIndex == 0xff)
+ {
+ return(USB_PROTOCOL_UNDEFINED);
+ }
+
+ //
+ // Get the interface descriptor.
+ //
+ pInterface = USBDescGetInterface(
+ g_sUSBHCD.USBDevice[ulDevIndex].pConfigDescriptor,
+ g_sUSBHCD.USBDevice[ulDevIndex].ulInterface,
+ ulInterface);
+
+ //
+ // Make sure that the interface requested actually exists.
+ //
+ if(pInterface)
+ {
+ //
+ // Return the interface protocol.
+ //
+ return(pInterface->bInterfaceProtocol);
+ }
+
+ //
+ // No valid interface so return an undefined protocol.
+ //
+ return(USB_PROTOCOL_UNDEFINED);
+}
+
+//*****************************************************************************
+//
+// Close the Doxygen group.
+//! @}
+//
+//*****************************************************************************