From c241dbd7e78c50327781a35d88d9f2db7ff2b271 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Mon, 29 Oct 2012 23:08:53 +0200 Subject: Added usblib --- usblib/host/usbhaudio.c | 1528 +++++++++++ usblib/host/usbhaudio.h | 162 ++ usblib/host/usbhhid.c | 723 ++++++ usblib/host/usbhhid.h | 164 ++ usblib/host/usbhhidkeyboard.c | 719 ++++++ usblib/host/usbhhidkeyboard.h | 77 + usblib/host/usbhhidmouse.c | 415 +++ usblib/host/usbhhidmouse.h | 68 + usblib/host/usbhhub.c | 1251 +++++++++ usblib/host/usbhhub.h | 206 ++ usblib/host/usbhmsc.c | 713 ++++++ usblib/host/usbhmsc.h | 95 + usblib/host/usbhost.h | 348 +++ usblib/host/usbhostenum.c | 5694 +++++++++++++++++++++++++++++++++++++++++ usblib/host/usbhostpriv.h | 257 ++ usblib/host/usbhscsi.c | 778 ++++++ usblib/host/usbhscsi.h | 102 + 17 files changed, 13300 insertions(+) create mode 100644 usblib/host/usbhaudio.c create mode 100644 usblib/host/usbhaudio.h create mode 100644 usblib/host/usbhhid.c create mode 100644 usblib/host/usbhhid.h create mode 100644 usblib/host/usbhhidkeyboard.c create mode 100644 usblib/host/usbhhidkeyboard.h create mode 100644 usblib/host/usbhhidmouse.c create mode 100644 usblib/host/usbhhidmouse.h create mode 100644 usblib/host/usbhhub.c create mode 100644 usblib/host/usbhhub.h create mode 100644 usblib/host/usbhmsc.c create mode 100644 usblib/host/usbhmsc.h create mode 100644 usblib/host/usbhost.h create mode 100644 usblib/host/usbhostenum.c create mode 100644 usblib/host/usbhostpriv.h create mode 100644 usblib/host/usbhscsi.c create mode 100644 usblib/host/usbhscsi.h (limited to 'usblib/host') diff --git a/usblib/host/usbhaudio.c b/usblib/host/usbhaudio.c new file mode 100644 index 0000000..fcaa9c3 --- /dev/null +++ b/usblib/host/usbhaudio.c @@ -0,0 +1,1528 @@ +//***************************************************************************** +// +// usbhaudio.c - USB host audio driver. +// +// Copyright (c) 2010-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_types.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usbaudio.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhaudio.h" + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// These defines are used with the USBHostAudioFormatSet() +// USBHostAudioFormatGet() to parse out interface number and alternate +// setting number for an interface. +// +//***************************************************************************** +#define INTERFACE_NUM_M 0x000000FF +#define INTERFACE_ALTSETTING_M 0x0000FF00 +#define INTERFACE_ALTSETTING_S 8 + +//***************************************************************************** +// +// Used to indicate an invalid interface descriptor number. +// +//***************************************************************************** +#define INVALID_INTERFACE 0xffffffff + +//***************************************************************************** +// +// Forward declarations for the driver open and close calls. +// +//***************************************************************************** +static void *USBAudioOpen(tUSBHostDevice *pDevice); +static void USBAudioClose(void *pvInstance); + +//***************************************************************************** +// +// This is the structure for an instance of a USB host audio driver. +// +//***************************************************************************** +typedef struct +{ + // + // Save the device instance. + // + tUSBHostDevice *pDevice; + + // + // Used to save the call back. + // + tUSBHostAudioCallback pfnCallback; + + // + // This is the control interface. + // + unsigned char ucIControl; + + // + // This is the output streaming interface. + // + unsigned char ucOutInterface; + + // + // This is the currently selected active output interface used with + // ucOutInterface interface. + // + unsigned char ucOutAltSetting; + + // + // This is the streaming interface. + // + unsigned char ucInInterface; + + // + // This is the currently selected active input interface used with + // ucInInterface interface. + // + unsigned char ucInAltSetting; + + // + // The Isochronous endpoint addresses. + // + unsigned char ucIsochInAddress; + unsigned char ucIsochOutAddress; + + tACInputTerminal *pInTerminal; + tACOutputTerminal *pOutTerminal; + + // + // Holds the identifier for the Feature Unit for controlling volume. + // + unsigned char ucVolumeID; + + tACFeatureUnit *pFeatureUnit; + + // + // Holds what types of controls are enabled on the device. + // + unsigned short pusControls[3]; + + // + // Isochronous IN pipe. + // + unsigned long ulIsochInPipe; + unsigned short usPipeSizeIn; + tUSBHostAudioCallback pfnInCallback; + void *pvInBuffer; + + // + // Isochronous OUT pipe. + // + unsigned long ulIsochOutPipe; + unsigned short usPipeSizeOut; + tUSBHostAudioCallback pfnOutCallback; + void *pvOutBuffer; + + // + // State flags for this audio instance. + // + unsigned long ulFlags; +} +tUSBHostAudioInstance; + +//***************************************************************************** +// +// The internal flags for an audio interface. +// +//***************************************************************************** +#define AUDIO_FLAG_OUT_ACTIVE 1 // Audio output is active. +#define AUDIO_FLAG_IN_ACTIVE 2 // Audio input is active. + +//***************************************************************************** +// +// The USB Host audio instance. +// +//***************************************************************************** +static tUSBHostAudioInstance g_AudioDevice = +{ + 0 +}; + +//***************************************************************************** +// +//! This constant global structure defines the Audio Class Driver that is +//! provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_USBHostAudioClassDriver = +{ + USB_CLASS_AUDIO, + USBAudioOpen, + USBAudioClose, + 0 +}; + +//***************************************************************************** +// +// This is the internal function that handles callbacks from the USB IN pipe. +// +//***************************************************************************** +static void +PipeCallbackIN(unsigned long ulPipe, unsigned long ulEvent) +{ + // + // Only handle the data available callback and pass it on to the + // application. + // + if(ulEvent == USB_EVENT_RX_AVAILABLE) + { + if(g_AudioDevice.pfnInCallback) + { + g_AudioDevice.pfnInCallback( + g_AudioDevice.pvInBuffer, 0, USB_EVENT_RX_AVAILABLE); + } + } +} + +//***************************************************************************** +// +// This is the internal function that handles callbacks from the USB OUT pipe. +// +//***************************************************************************** +static void +PipeCallbackOUT(unsigned long ulPipe, unsigned long ulEvent) +{ + // + // Only handle the transmit complete callback and pass it on to the + // application. + // + if(ulEvent == USB_EVENT_TX_COMPLETE) + { + if(g_AudioDevice.pfnOutCallback) + { + g_AudioDevice.pfnOutCallback( + g_AudioDevice.pvOutBuffer, 0, USB_EVENT_TX_COMPLETE); + } + } +} + +//***************************************************************************** +// +// Finds a given terminal and type in an audio configuration descriptor. +// +//***************************************************************************** +static tDescriptorHeader * +AudioTerminalGet(tConfigDescriptor *pConfigDesc, unsigned long ulTerminal, + unsigned long ulTerminalType) +{ + tACOutputTerminal *pOutput; + tDescriptorHeader *pHeader; + long lBytesRemaining; + + pHeader = (tDescriptorHeader *)pConfigDesc; + lBytesRemaining = pConfigDesc->wTotalLength; + + while(lBytesRemaining > 0) + { + // + // Output and input terminals are the same past the bDescriptorSubtype + // and wTerminalType that are being searched for. + // + pOutput = (tACOutputTerminal *)pHeader; + + // + // Only CS_INTERFACE descriptors can be a terminal. + // + if((pHeader->bDescriptorType == USB_DTYPE_CS_INTERFACE) && + (ulTerminal == pOutput->bDescriptorSubtype)) + { + if((pOutput->bDescriptorSubtype == USB_AI_OUTPUT_TERMINAL) || + (pOutput->bDescriptorSubtype == USB_AI_INPUT_TERMINAL)) + + { + // + // If this was the terminal type that was requested, the + // return it. + // + if(pOutput->wTerminalType == ulTerminalType) + { + return(pHeader); + } + } + else if(pOutput->bDescriptorSubtype == USB_AI_FEATURE_UNIT) + { + return(pHeader); + } + } + + // + // Decrease the bytes remaining by the size of this descriptor. + // + lBytesRemaining -= pHeader->bLength; + + // + // Move the pointer to the next header. + // + pHeader = (tDescriptorHeader*)((unsigned long)pHeader + + pHeader->bLength); + } + return((tDescriptorHeader *)0); +} + +//***************************************************************************** +// +// This function returns the interface number for the control interface +// in the structure passed in the pConfigDesc. +// +// \param pConfigDescriptor is a pointer to the memory containing a valid +// configuration descriptor for a device. +// +// This function searches a configuration descriptor for a control interface +// descriptor. The function only search for the first descriptor and then +// returns when it finds one. +// +// \return The first control interface descriptor number for an audio device +// or INVALID_INTERFACE if no control interface descriptor was found. +// +//***************************************************************************** +static unsigned long +AudioControlGet(tConfigDescriptor *pConfigDesc) +{ + tDescriptorHeader *pHeader; + tInterfaceDescriptor *pInterface; + unsigned long ulInterface; + long lBytes; + + pHeader = (tDescriptorHeader *)pConfigDesc; + lBytes = pConfigDesc->wTotalLength; + + // + // Initialize the interface number to an invalid value. + // + ulInterface = INVALID_INTERFACE; + + // + // Search the whole configuration descriptor. + // + while(lBytes > 0) + { + // + // Find an interface descriptor and see if it is a control interface. + // + if(pHeader->bDescriptorType == USB_DTYPE_INTERFACE) + { + pInterface = (tInterfaceDescriptor *)pHeader; + + // + // If this is the control interface then return the value to the + // caller. + // + if(pInterface->bInterfaceSubClass == USB_ASC_AUDIO_CONTROL) + { + ulInterface = pInterface->bInterfaceNumber; + + break; + } + } + + // + // Decrease the bytes remaining by the size of this descriptor. + // + lBytes -= pHeader->bLength; + + // + // Move the pointer to the next header. + // + pHeader = (tDescriptorHeader*)((unsigned long)pHeader + + pHeader->bLength); + } + return(ulInterface); +} + +//***************************************************************************** +// +// If it exists, finds the correct audio interface for a given audio format. +// +//***************************************************************************** +static unsigned long +AudioGetInterface(tUSBHostAudioInstance *pAudioDevice, + unsigned short usFormat, unsigned long ulSampleRate, + unsigned long ulBytes, unsigned long ulChannels, + unsigned long ulFlags) +{ + tDescriptorHeader *pHeader; + tInterfaceDescriptor *pInterface; + tEndpointDescriptor *pINEndpoint, *pOUTEndpoint; + tACHeader *pACHeader; + tACGeneral *pGeneral; + tASFormat *pFormat; + tEndpointDescriptor *pEndpoint; + unsigned char *pucValue; + unsigned long ulValue; + long lBytes, lIdx; + + // + // Initialize the Interface pointer to null. + // + pInterface = 0; + pINEndpoint = 0; + pOUTEndpoint = 0; + + // + // Start at the top of the configuration descriptor. + // + pHeader = (tDescriptorHeader *)pAudioDevice->pDevice->pConfigDescriptor; + + lBytes = pAudioDevice->pDevice->pConfigDescriptor->wTotalLength; + + while(lBytes > 0) + { + if(pHeader->bDescriptorType == USB_DTYPE_INTERFACE) + { + // + // If a new interface was found and the last one satisfied all + // requirements then a valid interface was found so break out. + // + if(pInterface) + { + break; + } + + // + // Get the new interface pointer. + // + pInterface = (tInterfaceDescriptor *)pHeader; + + // + // Reset the endpoints on finding a new interface descriptor. + // + pINEndpoint = 0; + pOUTEndpoint = 0; + + // + // If this is not a valid audio streaming interface then reset + // the interface pointer to null. + // + if((pInterface->bNumEndpoints == 0) || + (pInterface->bInterfaceClass != USB_CLASS_AUDIO) || + (pInterface->bInterfaceSubClass != USB_ASC_AUDIO_STREAMING)) + { + pInterface = 0; + } + } + if((pInterface) && (pHeader->bDescriptorType == USB_DTYPE_CS_INTERFACE)) + { + pACHeader = (tACHeader *)pHeader; + + // + // If this is a General descriptor the check if the format matches. + // + if(pACHeader->bDescriptorSubtype == USB_AS_GENERAL) + { + // + // Just save the pointer to the format descriptor. + // + pGeneral = (tACGeneral *)pHeader; + + // + // If this interface has the wrong format then set it to null + // so that the rest of this interface is ignored. + // + if(pGeneral->wFormatTag != usFormat) + { + pInterface = 0; + } + } + else if(pACHeader->bDescriptorSubtype == USB_AS_FORMAT_TYPE) + { + pFormat = (tASFormat *)pHeader; + + // + // If the number of bytes per sample and number of channels do + // not match then reset the interface pointer so that the rest + // of this interface is ignored. + // + if((pFormat->bNrChannels != ulChannels) || + (pFormat->bSubFrameSize != ulBytes)) + { + pInterface = 0; + } + else + { + pucValue = &pFormat->tSamFreq; + + // + // Attempt to find the sample rate in the sample rate + // table for this interface. + // + for(lIdx = 0; lIdx < pFormat->bSamFreqType; lIdx++) + { + ulValue = (*((unsigned long *)&pucValue[lIdx * 3]) & + 0xffffff); + + if(ulValue == ulSampleRate) + { + break; + } + } + + // + // If the sample rate was not found then set the interface + // pointer to null so that the rest of this interface is + // ignored. + // + if(lIdx == pFormat->bSamFreqType) + { + pInterface = 0; + } + } + } + } + else if((pInterface) && + (pHeader->bDescriptorType == USB_DTYPE_ENDPOINT)) + { + pEndpoint = (tEndpointDescriptor *)pHeader; + + // + // See what direction is being requested. + // + if(ulFlags & USBH_AUDIO_FORMAT_IN) + { + // + // If this is an input endpoint and is just a feed back input + // then ignore it. + // + if(pEndpoint->bEndpointAddress & USB_EP_DESC_IN) + { + if((pEndpoint->bmAttributes & USB_EP_ATTR_USAGE_M) + == USB_EP_ATTR_USAGE_FEEDBACK) + { + pInterface = 0; + } + else + { + // + // Save this endpoint as a possible valid endpoint + // + pINEndpoint = pEndpoint; + } + } + } + else + { + // + // If this is an output endpoint and is just a feed back input + // then ignore it. + // + if((pEndpoint->bEndpointAddress & USB_EP_DESC_IN) == 0) + { + if((pEndpoint->bmAttributes & USB_EP_ATTR_USAGE_M) + == USB_EP_ATTR_USAGE_FEEDBACK) + { + pInterface = 0; + } + else + { + // + // Save this endpoint as a possible valid endpoint; + // + pOUTEndpoint = pEndpoint; + } + } + } + } + + // + // Decrease the bytes remaining by the size of this descriptor. + // + lBytes -= pHeader->bLength; + + // + // Move the pointer to the next header. + // + pHeader = (tDescriptorHeader*)((unsigned long)pHeader + + pHeader->bLength); + } + + // + // If there is still a valid interface then return the values. + // + if(pInterface) + { + // + // Check a valid IN endpoint descriptor. + // + if(pINEndpoint) + { + // + // Save the endpoint address. + // + g_AudioDevice.ucIsochInAddress = pINEndpoint->bEndpointAddress & + USB_EP_DESC_NUM_M; + + // + // If there is no current pipe then just allocate a new one with + // the settings for this interface. + // + if(g_AudioDevice.ulIsochInPipe == 0) + { + // + // Allocate the USB Pipe for this Isochronous IN end point. + // + g_AudioDevice.ulIsochInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_IN_DMA, + g_AudioDevice.pDevice, + pINEndpoint->wMaxPacketSize, + PipeCallbackIN); + } + else if(g_AudioDevice.usPipeSizeIn < pINEndpoint->wMaxPacketSize) + { + // + // Free the old endpoint and allocate a new one. + // + USBHCDPipeFree(g_AudioDevice.ulIsochInPipe); + + // + // Allocate the USB Pipe for this Isochronous IN end point. + // + g_AudioDevice.ulIsochInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_IN_DMA, + g_AudioDevice.pDevice, + pINEndpoint->wMaxPacketSize, + PipeCallbackIN); + + // + // Save the new size of the maximum packet size for this + // USB pipe. + // + g_AudioDevice.usPipeSizeIn = pINEndpoint->wMaxPacketSize; + } + + // + // Configure the USB pipe as a Isochronous IN end point. + // + USBHCDPipeConfig(g_AudioDevice.ulIsochInPipe, + pINEndpoint->wMaxPacketSize, + 0, + g_AudioDevice.ucIsochInAddress); + } + + // + // Check a valid OUT endpoint descriptor. + // + if(pOUTEndpoint) + { + // + // Save the endpoint address. + // + g_AudioDevice.ucIsochOutAddress = pOUTEndpoint->bEndpointAddress & + USB_EP_DESC_NUM_M; + + // + // If there is no current pipe then just allocate a new one with + // the settings for this interface. + // + if(g_AudioDevice.ulIsochOutPipe == 0) + { + // + // Allocate the USB Pipe for this Isochronous OUT end point. + // + g_AudioDevice.ulIsochOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_OUT_DMA, + g_AudioDevice.pDevice, + pOUTEndpoint->wMaxPacketSize, + PipeCallbackOUT); + } + else if(g_AudioDevice.usPipeSizeOut < pOUTEndpoint->wMaxPacketSize) + { + // + // Free the old endpoint and allocate a new one. + // + USBHCDPipeFree(g_AudioDevice.ulIsochOutPipe); + + // + // Allocate the USB Pipe for this Isochronous OUT end point. + // + g_AudioDevice.ulIsochOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_OUT_DMA, + g_AudioDevice.pDevice, + pOUTEndpoint->wMaxPacketSize, + PipeCallbackOUT); + + // + // Save the new size of the maximum packet size for this + // USB pipe. + // + g_AudioDevice.usPipeSizeOut = pOUTEndpoint->wMaxPacketSize; + } + + // + // Configure the USB pipe as a Isochronous OUT end point. + // + USBHCDPipeConfig(g_AudioDevice.ulIsochOutPipe, + pOUTEndpoint->wMaxPacketSize, 0, + g_AudioDevice.ucIsochOutAddress); + } + + return(pInterface->bInterfaceNumber | + (pInterface->bAlternateSetting << INTERFACE_ALTSETTING_S)); + } + return(INVALID_INTERFACE); +} + +//***************************************************************************** +// +// This function is used to open an instance of the USB host audio driver. +// +// \param pDevice is a pointer to the device information structure. +// +// This function attempts to open an instance of the USB host audio driver +// based on the information contained in the pDevice structure. This call +// fails if there are not sufficient resources to open the device. The +// function returns a value that should be passed back into USBHostAudioClose() +// when the driver is no longer needed. +// +// \return The function returns a pointer to a USB host audio driver +// instance. +// +//***************************************************************************** +static void * +USBAudioOpen(tUSBHostDevice *pDevice) +{ + unsigned long ulTemp; + tConfigDescriptor *pConfigDesc; + + // + // Don't allow the device to be opened without closing first. + // + if(g_AudioDevice.pDevice) + { + return(0); + } + + g_AudioDevice.pDevice = pDevice; + + // + // Save a shorter name for the configuration descriptor. + // + pConfigDesc = pDevice->pConfigDescriptor; + + // + // Find the input terminal. + // + g_AudioDevice.pInTerminal = + (tACInputTerminal *)AudioTerminalGet(pConfigDesc, + USB_AI_INPUT_TERMINAL, + USB_TTYPE_STREAMING); + + // + // Find the output terminal. + // + g_AudioDevice.pOutTerminal = + (tACOutputTerminal *)AudioTerminalGet(pConfigDesc, + USB_AI_OUTPUT_TERMINAL, + USB_TTYPE_STREAMING); + + // + // Find the feature unit. + g_AudioDevice.pFeatureUnit = + (tACFeatureUnit *)AudioTerminalGet(pConfigDesc, + USB_AI_FEATURE_UNIT, + 0); + + // + // Need some kind of terminal to send or receive audio from. + // + if((g_AudioDevice.pOutTerminal == 0) && + (g_AudioDevice.pInTerminal == 0)) + { + return(0); + } + + // + // Find the Audio control interface. + // + ulTemp = AudioControlGet(pConfigDesc); + + if(ulTemp == INVALID_INTERFACE) + { + return(0); + } + + // + // Save the control interface index and increment the number + // of interfaces that have been found. + // + g_AudioDevice.ucIControl = (unsigned char)ulTemp; + + // + // If the call back exists, call it with an Open event. + // + if(g_AudioDevice.pfnCallback != 0) + { + g_AudioDevice.pfnCallback((void *)&g_AudioDevice, + 0, USBH_AUDIO_EVENT_OPEN); + } + + // + // If a feature unit was found, save the ID + // + if(g_AudioDevice.pFeatureUnit != 0) + { + g_AudioDevice.ucVolumeID = g_AudioDevice.pFeatureUnit->bUnitID; + } + + // + // Save the device pointer. + // + g_AudioDevice.pDevice = pDevice; + + // + // Allocate the USB Pipe for this Isochronous IN end point. + // + g_AudioDevice.ulIsochInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_IN_DMA, + g_AudioDevice.pDevice, 256, + PipeCallbackIN); + g_AudioDevice.usPipeSizeIn = 256; + + // + // Allocate the USB Pipe for this Isochronous OUT end point. + // + g_AudioDevice.ulIsochOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_OUT_DMA, + g_AudioDevice.pDevice, 256, + PipeCallbackOUT); + g_AudioDevice.usPipeSizeOut = 256; + + // + // Clear the flags. + // + g_AudioDevice.ulFlags = 0; + + // + // Return the only instance of this device. + // + return(&g_AudioDevice); +} + +//***************************************************************************** +// +// This function is used to release an instance of the USB host audio driver. +// +// \param pvInstance is an instance pointer that needs to be released. +// +// This function frees up any resources in use by the USB host audio +// driver instance that is passed in. The \e pvInstance pointer should be a +// valid value that was returned from a call to USBHostAudioOpen(). +// +// \return None. +// +//***************************************************************************** +static void +USBAudioClose(void *pvInstance) +{ + // + // Do nothing if there is not a driver open. + // + if(g_AudioDevice.pDevice == 0) + { + return; + } + + // + // Reset the device pointer. + // + g_AudioDevice.pDevice = 0; + + // + // Free the Isochronous IN pipe. + // + if(g_AudioDevice.ulIsochInPipe != 0) + { + USBHCDPipeFree(g_AudioDevice.ulIsochInPipe); + } + + // + // Free the Isochronous OUT pipe. + // + if(g_AudioDevice.ulIsochOutPipe != 0) + { + USBHCDPipeFree(g_AudioDevice.ulIsochOutPipe); + } + + // + // If the call back exists then call it. + // + if(g_AudioDevice.pfnCallback != 0) + { + g_AudioDevice.pfnCallback((void *)&g_AudioDevice, + 0, USBH_AUDIO_EVENT_CLOSE); + } +} + +//***************************************************************************** +// +//! This function should be called before any devices are present to enable +//! the host audio class driver. +//! +//! \param ulIndex is the audio device to open (currently only 0 is supported). +//! \param pfnCallback is the driver call back for host audio events. +//! +//! This function is called to open an instance of a host audio device and +//! should provide a valid callback function for host audio events in the +//! \e pfnCallback parameter. This function must be called before the USB +//! host code can successfully enumerate an audio device. +//! +//! \return This function returns the driver instance to use for the other +//! host audio functions. If there is no instance available at the time of +//! this call, this function returns zero. +// +//***************************************************************************** +unsigned long +USBHostAudioOpen(unsigned long ulIndex, tUSBHostAudioCallback pfnCallback) +{ + // + // Only one audio device is supported at this time and on one instance + // is supported so if there is already a call back then fail. + // + if((ulIndex != 0) || (g_AudioDevice.pfnCallback)) + { + return(0); + } + + // + // Save the call back. + // + g_AudioDevice.pfnCallback = pfnCallback; + + // + // Return the requested device instance. + // + return((unsigned long)&g_AudioDevice); +} + +//***************************************************************************** +// +//! This function should be called to release an audio device instance. +//! +//! \param ulInstance is the device instance that is to be released. +//! +//! This function is called when a host audio device needs to be released. +//! This could be in preparation for shutdown or a switch to USB device mode, +//! for example. Following this call, the audio device is available and can +//! be opened again using a call to USBHostAudioOpen(). After calling this +//! function, the host audio driver will no longer provide any callbacks or +//! accept calls to other audio driver APIs. +//! +//! \return None. +// +//***************************************************************************** +void +USBHostAudioClose(unsigned long ulInstance) +{ + tUSBHostAudioInstance *pAudioDevice; + + // + // Get a pointer to the device instance data from the handle. + // + pAudioDevice = (tUSBHostAudioInstance *)ulInstance; + + // + // Close the audio device. + // + USBAudioClose((void *)pAudioDevice); + + // + // Clear the call back indicating that the device is now closed. + // + pAudioDevice->pfnCallback = 0; +} + +//***************************************************************************** +// +// This function is used to request settings from a given audio interface. +// +// \param ulInstance is an instance value for the audio device to access. +// \param ulInterface is the interface to access. +// \param ulChannel is the channel number to access. +// \param ulRequest is the audio device request. +// +// This function is used to get volume control parameters from a given +// interface and on a given channel. The \e ulInterface is the interface to +// make the request specified by \e ulChannel and \e ulRequest. The +// \e ulRequest parameter must be one of the USB_AC_GET_* values. +// +// \return This function returns the requested value. +// +//***************************************************************************** +static unsigned long +VolumeSettingGet(unsigned long ulInstance, unsigned long ulInterface, + unsigned long ulChannel, unsigned long ulRequest) +{ + unsigned long ulValue; + tUSBHostAudioInstance *pAudioDevice; + tUSBRequest SetupPacket; + + pAudioDevice = (tUSBHostAudioInstance *)ulInstance; + + ulValue = 0; + + // + // This is a Class specific Interface IN request. + // + SetupPacket.bmRequestType = + USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | USB_RTYPE_INTERFACE; + + // + // Request a Device Descriptor. + // + SetupPacket.bRequest = (ulRequest & 0xff); + + // + // Request for a string descriptor. + // + SetupPacket.wValue = VOLUME_CONTROL | (ulChannel & 0xff); + + // + // Set the language ID. + // + SetupPacket.wIndex = (pAudioDevice->ucVolumeID << 8) | + (ulInterface & 0xff); + + // + // Only request the space available. + // + SetupPacket.wLength = 2; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &SetupPacket, pAudioDevice->pDevice, + (unsigned char *)&ulValue, 4, + pAudioDevice->pDevice->DeviceDescriptor.bMaxPacketSize0); + + return(ulValue); +} + +//***************************************************************************** +// +//! This function is used to get the current volume setting for a given +//! audio device. +//! +//! \param ulInstance is an instance of the USB audio device. +//! \param ulInterface is the interface number to use to query the current +//! volume setting. +//! \param ulChannel is the 0 based channel number to query. +//! +//! The function is used to retrieve the current volume setting for an audio +//! device on the channel specified by \e ulChannel. The \e ulInterface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ulChannel value starts with 0 which is the master audio +//! volume control interface. The remaining \e ulChannel values provide +//! access to various other audio channels, with 1 and 2 being left and right +//! audio channels. +//! +//! \note On devices that do not support volume control interfaces, this +//! call returns 0, indicating a 0db setting. +//! +//! \return Returns the current volume setting for the requested interface. +// +//***************************************************************************** +unsigned long +USBHostAudioVolumeGet(unsigned long ulInstance, unsigned long ulInterface, + unsigned long ulChannel) +{ + return(VolumeSettingGet(ulInstance, ulInterface, ulChannel, + USB_AC_GET_CUR)); +} + +//***************************************************************************** +// +//! This function is used to get the maximum volume setting for a given +//! audio device. +//! +//! \param ulInstance is an instance of the USB audio device. +//! \param ulInterface is the interface number to use to query the maximum +//! volume control value. +//! \param ulChannel is the 0 based channel number to query. +//! +//! The function is used to retrieve the maximum volume setting for an audio +//! device on the channel specified by \e ulChannel. The \e ulInterface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ulChannel value starts with 0 which is the master audio +//! volume control interface. The remaining \e ulChannel values provide +//! access to various other audio channels, with 1 and 2 being left and right +//! audio channels. +//! +//! \note On devices that do not support volume control interfaces, this +//! call returns 0, indicating a 0db setting. +//! +//! \return Returns the maximum volume setting for the requested interface. +// +//***************************************************************************** +unsigned long +USBHostAudioVolumeMaxGet(unsigned long ulInstance, unsigned long ulInterface, + unsigned long ulChannel) +{ + return(VolumeSettingGet(ulInstance, ulInterface, ulChannel, + USB_AC_GET_MAX)); +} + +//***************************************************************************** +// +//! This function is used to get the minimum volume setting for a given +//! audio device. +//! +//! \param ulInstance is an instance of the USB audio device. +//! \param ulInterface is the interface number to use to query the minimum +//! volume control value. +//! \param ulChannel is the 0 based channel number to query. +//! +//! The function is used to retrieve the minimum volume setting for an audio +//! device on the channel specified by \e ulChannel. The \e ulInterface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ulChannel value starts with 0 which is the master audio +//! volume control interface. The remaining \e ulChannel values provide +//! access to various other audio channels, with 1 and 2 being left and right +//! audio channels. +//! +//! \note On devices that do not support volume control interfaces, this +//! call returns 0, indicating a 0db setting. +//! +//! \return Returns the minimum volume setting for the requested interface. +// +//***************************************************************************** +unsigned long +USBHostAudioVolumeMinGet(unsigned long ulInstance, unsigned long ulInterface, + unsigned long ulChannel) +{ + return(VolumeSettingGet(ulInstance, ulInterface, ulChannel, + USB_AC_GET_MIN)); +} + +//***************************************************************************** +// +//! This function is used to get the volume control resolution for a given +//! audio device. +//! +//! \param ulInstance is an instance of the USB audio device. +//! \param ulInterface is the interface number to use to query the resolution +//! for the volume control. +//! \param ulChannel is the 0 based channel number to query. +//! +//! The function is used to retrieve the volume control resolution for an audio +//! device on the channel specified by \e ulChannel. The \e ulInterface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ulChannel value starts with 0 which is the master audio +//! volume control interface. The remaining \e ulChannel values provide +//! access to various other audio channels, with 1 and 2 being left and right +//! audio channels. +//! +//! \note On devices that do not support volume control interfaces, this +//! call returns 0, indicating a 0db setting. +//! +//! \return Returns the volume control resolution for the requested interface. +// +//***************************************************************************** +unsigned long +USBHostAudioVolumeResGet(unsigned long ulInstance, unsigned long ulInterface, + unsigned long ulChannel) +{ + return(VolumeSettingGet(ulInstance, ulInterface, ulChannel, + USB_AC_GET_RES)); +} + +//***************************************************************************** +// +//! This function is used to set the current volume setting for a given +//! audio device. +//! +//! \param ulInstance is an instance of the USB audio device. +//! \param ulInterface is the interface number to use to set the current +//! volume setting. +//! \param ulChannel is the 0 based channel number to query. +//! \param ulValue is the value to write to the USB audio device. +//! +//! The function is used to set the current volume setting for an audio +//! device on the channel specified by \e ulChannel. The \e ulInterface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ulChannel value starts with 0 which is the master audio +//! volume control interface. The remaining \e ulChannel values provide +//! access to various other audio channels, with 1 and 2 being left and right +//! audio channels. +//! +//! \note On devices that do not support volume control interfaces, this +//! call returns 0, indicating a 0db setting. +//! +//! \return None. +// +//***************************************************************************** +void +USBHostAudioVolumeSet(unsigned long ulInstance, unsigned ulInterface, + unsigned long ulChannel, unsigned long ulValue) +{ + tUSBHostAudioInstance *pAudioDevice; + tUSBRequest SetupPacket; + + // + // Create an audio instance pointer. + // + pAudioDevice = (tUSBHostAudioInstance *)ulInstance; + + // + // This is a Class specific Interface OUT request. + // + SetupPacket.bmRequestType = + USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | USB_RTYPE_INTERFACE; + + // + // Request is to set the current value. + // + SetupPacket.bRequest = USB_AC_SET_CUR; + + // + // Request the volume control. + // + SetupPacket.wValue = VOLUME_CONTROL | (ulChannel & 0xff); + + // + // Set Volume control ID and interface to 0. + // + SetupPacket.wIndex = pAudioDevice->ucVolumeID << 8; + + // + // Only request the space available. + // + SetupPacket.wLength = 2; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &SetupPacket, pAudioDevice->pDevice, + (unsigned char *)&ulValue, 2, + pAudioDevice->pDevice->DeviceDescriptor.bMaxPacketSize0); +} + +//***************************************************************************** +// +//! This function is called to determine if an audio format is supported by the +//! connected USB Audio device. +//! +//! \param ulInstance is the device instance for this call. +//! \param ulSampleRate is the sample rate of the audio stream. +//! \param ulBits is the number of bits per sample in the audio stream. +//! \param ulChannels is the number of channels in the audio stream. +//! \param ulFlags is a set of flags to determine what type of interface to +//! retrieve. +//! +//! This function is called when an application needs to determine which audio +//! formats are supported by a USB audio device that has been connected. The +//! \e ulInstance value that is used with this call is the value that was +//! returned from the USBHostAudioOpen() function. This call checks the +//! USB audio device to determine if it can support the values provided in the +//! \e ulSampleRate, \e ulBits, and \e ulChannels values. The \e ulFlags +//! currently only supports either the \b USBH_AUDIO_FORMAT_IN or +//! \b USBH_AUDIO_FORMAT_OUT values that indicates if a request is for an +//! audio input and an audio output. If the format is supported this +//! function returns zero, and this function returns a non-zero value if the +//! format is not supported. This function does not set the current output or +//! input format. +//! +//! \return A value of zero indicates the supplied format is supported and +//! a non-zero value indicates that the format is not supported. +// +//***************************************************************************** +unsigned long +USBHostAudioFormatGet(unsigned long ulInstance, unsigned long ulSampleRate, + unsigned long ulBits, unsigned long ulChannels, + unsigned long ulFlags) +{ + tUSBHostAudioInstance *pAudioDevice; + + // + // Get a pointer to the device instance data from the handle. + // + pAudioDevice = (tUSBHostAudioInstance *)ulInstance; + + // + // Look for the requested format. + // + if(AudioGetInterface(pAudioDevice, USB_ADF_PCM, ulSampleRate, ulBits>>3, + ulChannels, ulFlags) != INVALID_INTERFACE) + { + return(0); + } + return(1); +} + +//***************************************************************************** +// +//! This function is called to set the current sample rate on an audio +//! interface. +//! +//! \param ulInstance specifies the device instance for this call. +//! \param ulSampleRate is the sample rate in Hz. +//! \param ulBits is the number of bits per sample. +//! \param ulChannels is then number of audio channels. +//! \param ulFlags is a set of flags that determine the access type. +//! +//! This function is called when to set the current audio output or input format +//! for a USB audio device. The \e ulInstance value that is used with this +//! call is the value that was returned from the USBHostAudioOpen() function. +//! The application can use this call to insure that the audio format is +//! supported and set the format at the same time. If the application is +//! just checking for supported rates, then it should call the +//! USBHostAudioFormatGet(). +//! +//! \note This function must be called before attempting to send or receive +//! audio with the USBHostAudioPlay() or USBHostAudioRecord() functions. +//! +//! \return A non-zero value indicates the supplied format is not supported and +//! a zero value indicates that the format was supported and has been +//! configured. +// +//***************************************************************************** +unsigned long +USBHostAudioFormatSet(unsigned long ulInstance, unsigned long ulSampleRate, + unsigned long ulBits, unsigned long ulChannels, + unsigned long ulFlags) +{ + tUSBHostAudioInstance *pAudioDevice; + unsigned long ulInterface; + + // + // Get a pointer to the device instance data from the handle. + // + pAudioDevice = (tUSBHostAudioInstance *)ulInstance; + + // + // Look for the requested format. + // + ulInterface = AudioGetInterface(pAudioDevice, USB_ADF_PCM, ulSampleRate, + ulBits>>3, ulChannels, ulFlags); + + if(ulInterface == INVALID_INTERFACE) + { + return(1); + } + + // + // Determine if this is an input or output request. + // + if(ulFlags & USBH_AUDIO_FORMAT_IN) + { + // + // Get the active interface number and alternate setting for this + // format. + // + pAudioDevice->ucInInterface = + (unsigned char)(ulInterface & INTERFACE_NUM_M); + pAudioDevice->ucInAltSetting = + (unsigned char)((ulInterface & INTERFACE_ALTSETTING_M) >> + INTERFACE_ALTSETTING_S); + } + else + { + // + // Get the active interface number and alternate setting for this + // format. + // + pAudioDevice->ucOutInterface = + (unsigned char)(ulInterface & INTERFACE_NUM_M); + pAudioDevice->ucOutAltSetting = + (unsigned char)((ulInterface & INTERFACE_ALTSETTING_M) >> + INTERFACE_ALTSETTING_S); + } + return(0); +} + +//***************************************************************************** +// +//! This function is called to send an audio buffer to the USB audio device. +//! +//! \param ulInstance specifies the device instance for this call. +//! \param pvBuffer is the audio buffer to send. +//! \param ulSize is the size of the buffer in bytes. +//! \param pfnCallback is a pointer to a callback function that is called +//! when the buffer can be used again. +//! +//! This function is called when an application needs to schedule a new buffer +//! for output to the USB audio device. Since this call schedules the transfer +//! and returns immediately, the application should provide a \e pfnCallback +//! function to be notified when the buffer can be used again by the +//! application. The \e pfnCallback function provided is called with the +//! \e pvBuffer parameter set to the \e pvBuffer provided by this call, the +//! \e ulParam can be ignored and the \e ulEvent parameter is +//! \b USB_EVENT_TX_COMPLETE. +//! +//! \return This function returns the number of bytes that were scheduled +//! to be sent. If this function returns zero then there was no USB audio +//! device present or the request could not be satisfied at this time. +// +//***************************************************************************** +long +USBHostAudioPlay(unsigned long ulInstance, void *pvBuffer, + unsigned long ulSize, tUSBHostAudioCallback pfnCallback) +{ + tUSBHostAudioInstance *pAudioDevice; + unsigned long ulBytes; + + // + // Make sure that there is a device present. + // + if(g_AudioDevice.pDevice == 0) + { + return(0); + } + + // + // Get a pointer to the device instance data from the handle. + // + pAudioDevice = (tUSBHostAudioInstance *)ulInstance; + + // + // If the audio output interface is not active then select the current + // active audio interface. + // + if(HWREGBITW(&pAudioDevice->ulFlags, AUDIO_FLAG_OUT_ACTIVE) == 0) + { + // + // Indicate the active audio interface has been selected. + // + HWREGBITW(&pAudioDevice->ulFlags, AUDIO_FLAG_OUT_ACTIVE) = 1; + + // + // Configure the USB audio device to use the selected audio interface. + // + USBHCDSetInterface(0, (unsigned long)pAudioDevice->pDevice, + pAudioDevice->ucOutInterface, + pAudioDevice->ucOutAltSetting); + } + + // + // Save the callback function and the buffer pointer. + // + pAudioDevice->pfnOutCallback = pfnCallback; + pAudioDevice->pvOutBuffer = (void *)pvBuffer; + + // + // Schedule the data to be written out to the FIFO. + // + ulBytes = USBHCDPipeSchedule(pAudioDevice->ulIsochOutPipe, pvBuffer, + ulSize); + + // + // Return the number of bytes scheduled to be sent. + // + return(ulBytes); +} + +//***************************************************************************** +// +//! This function is called to provide an audio buffer to the USB audio device +//! for audio input. +//! +//! \param ulInstance specifies the device instance for this call. +//! \param pvBuffer is the audio buffer to send. +//! \param ulSize is the size of the buffer in bytes. +//! \param pfnCallback is a pointer to a callback function that is called +//! when the buffer has been filled. +//! +//! This function is called when an application needs to schedule a new buffer +//! for input from the USB audio device. Since this call schedules the +//! transfer and returns immediately, the application should provide a +//! \e pfnCallback function to be notified when the buffer has been filled with +//! audio data. When the \e pfnCallback function is called, the \e pvBuffer +//! parameter is set to \e pvBuffer provided in this call, the \e ulParam is +//! the number of valid bytes in the pvBuffer and the \e ulEvent is set to +//! \b USB_EVENT_RX_AVAILABLE. +//! +//! \return This function returns the number of bytes that were scheduled +//! to be sent. If this function returns zero then there was no USB audio +//! device present or the device does not support audio input. +// +//***************************************************************************** +long +USBHostAudioRecord(unsigned long ulInstance, void *pvBuffer, + unsigned long ulSize, tUSBHostAudioCallback pfnCallback) +{ + tUSBHostAudioInstance *pAudioDevice; + unsigned long ulBytes; + + // + // Make sure that there is a device present. + // + if(g_AudioDevice.pDevice == 0) + { + return(0); + } + + // + // Get a pointer to the device instance data from the handle. + // + pAudioDevice = (tUSBHostAudioInstance *)ulInstance; + + // + // If the audio input interface is not active then select the current + // active audio interface. + // + if(HWREGBITW(&pAudioDevice->ulFlags, AUDIO_FLAG_IN_ACTIVE) == 0) + { + // + // Indicate the active audio interface has been selected. + // + HWREGBITW(&pAudioDevice->ulFlags, AUDIO_FLAG_IN_ACTIVE) = 1; + + // + // Configure the USB audio device to use the selected audio interface. + // + USBHCDSetInterface(0, (unsigned long)pAudioDevice->pDevice, + pAudioDevice->ucInInterface, + pAudioDevice->ucInAltSetting); + } + + // + // Save the callback function and the buffer pointer. + // + pAudioDevice->pfnInCallback = pfnCallback; + pAudioDevice->pvInBuffer = (void *)pvBuffer; + + // + // Schedule the data to be written out to the FIFO. + // + ulBytes = USBHCDPipeSchedule(pAudioDevice->ulIsochInPipe, pvBuffer, ulSize); + + // + // Return the number of bytes scheduled to be sent. + // + return(ulBytes); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + diff --git a/usblib/host/usbhaudio.h b/usblib/host/usbhaudio.h new file mode 100644 index 0000000..a12e97e --- /dev/null +++ b/usblib/host/usbhaudio.h @@ -0,0 +1,162 @@ +//***************************************************************************** +// +// usbhaudio.h - USB host audio class driver. +// +// Copyright (c) 2010-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. +// +//***************************************************************************** + +#ifndef __USBHAUDIO_H__ +#define __USBHAUDIO_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +typedef void (* tUSBHostAudioCallback)(void *pvBuffer, + unsigned long ulParam, + unsigned long ulEvent); + +//***************************************************************************** +// +//! This is the size in bytes of the private data for the host audio class. +// +//***************************************************************************** +#define USB_HOST_AUDIO_INSTANCE_SIZE sizeof(tHostAudioInstance); + +//***************************************************************************** +// +// USB host audio specific events +// +//***************************************************************************** + +//***************************************************************************** +// +//! This USB host audio event indicates that the device is connected and +//! ready to send or receive buffers. The \e pvBuffer and \e ulParam +//! values are not used in this event. +// +//***************************************************************************** +#define USBH_AUDIO_EVENT_OPEN (USBH_AUDIO_EVENT_BASE + 0) + +//***************************************************************************** +// +//! This USB host audio event indicates that the previously connected device +//! has been disconnected. The \e pvBuffer and \e ulParam values are not used +//! in this event. +// +//***************************************************************************** +#define USBH_AUDIO_EVENT_CLOSE (USBH_AUDIO_EVENT_BASE + 1) + +//***************************************************************************** +// +// This definition is used with the USBHostAudioFormatGet() and +// USBHostAudioFormatSet() API's to determine if the audio input is being +// accesses(USBH_AUDIO_FORMAT_IN set) or audio output(USBH_AUDIO_FORMAT clear). +// +//***************************************************************************** +#define USBH_AUDIO_FORMAT_IN 0x00000001 +#define USBH_AUDIO_FORMAT_OUT 0x00000000 + +typedef struct +{ + unsigned char ucChannels; + unsigned char ucBits; + unsigned long ulSampleRate; +} tUSBAudioFormat; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern unsigned long USBHostAudioOpen(unsigned long ulIndex, + tUSBHostAudioCallback pfnCallback); +extern void USBHostAudioClose(unsigned long ulInstance); +extern long USBHostAudioPlay(unsigned long ulInstance, void *pvBuffer, + unsigned long ulSize, + tUSBHostAudioCallback pfnCallback); + +extern unsigned long USBHostAudioFormatGet(unsigned long ulInstance, + unsigned long ulSampleRate, + unsigned long ulBits, + unsigned long ulChannels, + unsigned long ulFlags); +extern unsigned long USBHostAudioFormatSet(unsigned long ulInstance, + unsigned long ulSampleRate, + unsigned long ulBits, + unsigned long ulChannels, + unsigned long ulFlags); + +extern long USBHostAudioRecord(unsigned long ulInstance, void *pvBuffer, + unsigned long ulSize, + tUSBHostAudioCallback); + +extern unsigned long USBHostAudioVolumeGet(unsigned long ulInstance, + unsigned long ulInterface, + unsigned long ulChannel); + +extern void USBHostAudioVolumeSet(unsigned long ulInstance, + unsigned ulInterface, + unsigned long ulChannel, + unsigned long ulValue); + +extern unsigned long USBHostAudioVolumeMaxGet(unsigned long ulInstance, + unsigned long ulInterface, + unsigned long ulChannel); + +extern unsigned long USBHostAudioVolumeMinGet(unsigned long ulInstance, + unsigned long ulInterface, + unsigned long ulChannel); + +extern unsigned long USBHostAudioVolumeResGet(unsigned long ulInstance, + unsigned long ulInterface, + unsigned long ulChannel); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/usblib/host/usbhhid.c b/usblib/host/usbhhid.c new file mode 100644 index 0000000..5e3fa50 --- /dev/null +++ b/usblib/host/usbhhid.c @@ -0,0 +1,723 @@ +//***************************************************************************** +// +// usbhhid.c - This file contains the host HID driver. +// +// 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_types.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usbhid.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhhid.h" + +static void * HIDDriverOpen(tUSBHostDevice *pDevice); +static void HIDDriverClose(void *pvInstance); + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// If the user has not explicitly stated the maximum number of HID devices to +// support, we assume that we need to support up to the maximum number of USB +// devices that the build is configured for. +// +//***************************************************************************** +#ifndef MAX_HID_DEVICES +#define MAX_HID_DEVICES MAX_USB_DEVICES +#endif + +//***************************************************************************** +// +// This is the structure that holds all of the data for a given instance of +// a HID device. +// +//***************************************************************************** +typedef struct +{ + // + // Save the device instance. + // + tUSBHostDevice *pDevice; + + // + // Used to save the callback. + // + tUSBCallback pfnCallback; + + // + // Callback data provided by caller. + // + unsigned long ulCBData; + + // + // Used to remember what type of device was registered. + // + tHIDSubClassProtocol eDeviceType; + + // + // Interrupt IN pipe. + // + unsigned long ulIntInPipe; +} +tHIDInstance; + +//***************************************************************************** +// +// The instance data storage for attached hid devices. +// +//***************************************************************************** +static tHIDInstance g_pHIDDevice[MAX_HID_DEVICES]; + +//***************************************************************************** +// +//! This constant global structure defines the HID Class Driver that is +//! provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_USBHIDClassDriver = +{ + USB_CLASS_HID, + HIDDriverOpen, + HIDDriverClose, + 0 +}; + +//***************************************************************************** +// +//! This function is used to open an instance of a HID device. +//! +//! \param eDeviceType is the type of device that should be loaded for this +//! instance of the HID device. +//! \param pfnCallback is the function that will be called whenever changes +//! are detected for this device. +//! \param ulCBData is the data that will be returned in when the pfnCallback +//! function is called. +//! +//! This function creates an instance of an specific type of HID device. The +//! \e eDeviceType parameter is one subclass/protocol values of the types +//! specified in enumerated types tHIDSubClassProtocol. Only devices that +//! enumerate with this type will be called back via the \e pfnCallback +//! function. The \e pfnCallback parameter is the callback function for any +//! events that occur for this device type. The \e pfnCallback function must +//! point to a valid function of type \e tUSBCallback for this call to complete +//! successfully. To release this device instance the caller of USBHHIDOpen() +//! should call USBHHIDClose() and pass in the value returned from the +//! USBHHIDOpen() call. +//! +//! \return This function returns and instance value that should be used with +//! any other APIs that require an instance value. If a value of 0 is returned +//! then the device instance could not be created. +// +//***************************************************************************** +unsigned long +USBHHIDOpen(tHIDSubClassProtocol eDeviceType, tUSBCallback pfnCallback, + unsigned long ulCBData) +{ + unsigned long ulLoop; + + // + // Find a free device instance structure. + // + for(ulLoop = 0; ulLoop < MAX_HID_DEVICES; ulLoop++) + { + if(g_pHIDDevice[ulLoop].eDeviceType == USBH_HID_DEV_NONE) + { + // + // Save the instance data for this device. + // + g_pHIDDevice[ulLoop].pfnCallback = pfnCallback; + g_pHIDDevice[ulLoop].eDeviceType = eDeviceType; + g_pHIDDevice[ulLoop].ulCBData = ulCBData; + + // + // Return the device instance pointer. + // + return((unsigned long)&g_pHIDDevice[ulLoop]); + } + } + + // + // If we get here, there are no space device slots so return NULL to + // indicate a problem. + // + return(0); +} + +//***************************************************************************** +// +//! This function is used to release an instance of a HID device. +//! +//! \param ulHIDInstance is the instance value for a HID device to release. +//! +//! This function releases an instance of a HID device that was created by a +//! call to USBHHIDOpen(). This call is required to allow other HID devices +//! to be enumerated after another HID device has been disconnected. The +//! \e ulHIDInstance parameter should hold the value that was returned from the +//! previous call to USBHHIDOpen(). +//! +//! \return None. +// +//***************************************************************************** +void +USBHHIDClose(unsigned long ulHIDInstance) +{ + tHIDInstance *pInst; + + // + // Get our instance pointer. + // + pInst = (tHIDInstance *)ulHIDInstance; + + // + // Disable any more notifications from the HID layer. + // + pInst->pfnCallback = 0; + + // + // Mark this device slot as free. + // + pInst->eDeviceType = USBH_HID_DEV_NONE; +} + +//***************************************************************************** +// +// This function handles callbacks for the interrupt IN endpoint. +// +//***************************************************************************** +static void +HIDIntINCallback(unsigned long ulPipe, unsigned long ulEvent) +{ + long lDev; + + switch (ulEvent) + { + // + // Handles a request to schedule a new request on the interrupt IN + // pipe. + // + case USB_EVENT_SCHEDULER: + { + USBHCDPipeSchedule(ulPipe, 0, 1); + break; + } + // + // Called when new data is available on the interrupt IN pipe. + // + case USB_EVENT_RX_AVAILABLE: + { + // + // Determine which device this notification is intended for. + // + for(lDev = 0; lDev < MAX_HID_DEVICES; lDev++) + { + // + // Does this device own the pipe we've been passed? + // + if(g_pHIDDevice[lDev].ulIntInPipe == ulPipe) + { + // + // Yes - send the report data to the USB host HID device + // class driver. + // + g_pHIDDevice[lDev].pfnCallback( + (void *)g_pHIDDevice[lDev].ulCBData, + USB_EVENT_RX_AVAILABLE, + ulPipe, + 0); + } + } + + break; + } + } +} + +//***************************************************************************** +// +//! This function is used to open an instance of the HID driver. +//! +//! \param pDevice is a pointer to the device information structure. +//! +//! This function will attempt to open an instance of the HID driver based on +//! the information contained in the pDevice structure. This call can fail if +//! there are not sufficient resources to open the device. The function will +//! return a value that should be passed back into USBHIDClose() when the +//! driver is no longer needed. +//! +//! \return The function will return a pointer to a HID driver instance. +// +//***************************************************************************** +static void * +HIDDriverOpen(tUSBHostDevice *pDevice) +{ + long lIdx, lDev; + tEndpointDescriptor *pEndpointDescriptor; + tInterfaceDescriptor *pInterface; + + // + // Get the interface descriptor. + // + pInterface = USBDescGetInterface(pDevice->pConfigDescriptor, 0, 0); + + // + // Search the currently open instances for one that supports the protocol + // of this device. + // + for(lDev = 0; lDev < MAX_HID_DEVICES; lDev++) + { + if(g_pHIDDevice[lDev].eDeviceType == pInterface->bInterfaceProtocol) + { + // + // Save the device pointer. + // + g_pHIDDevice[lDev].pDevice = pDevice; + + for(lIdx = 0; lIdx < 3; lIdx++) + { + // + // Get the first endpoint descriptor. + // + pEndpointDescriptor = USBDescGetInterfaceEndpoint(pInterface, + lIdx, 256); + + // + // If no more endpoints then break out. + // + if(pEndpointDescriptor == 0) + { + break; + } + + // + // Interrupt + // + if((pEndpointDescriptor->bmAttributes & USB_EP_ATTR_TYPE_M) == + USB_EP_ATTR_INT) + { + // + // Interrupt IN. + // + if(pEndpointDescriptor->bEndpointAddress & USB_EP_DESC_IN) + { + g_pHIDDevice[lDev].ulIntInPipe = USBHCDPipeAlloc(0, + USBHCD_PIPE_INTR_IN, + pDevice, + HIDIntINCallback); + USBHCDPipeConfig(g_pHIDDevice[lDev].ulIntInPipe, + pEndpointDescriptor->wMaxPacketSize, + pEndpointDescriptor->bInterval, + (pEndpointDescriptor->bEndpointAddress & + USB_EP_DESC_NUM_M)); + } + } + } + + // + // If there is a callback function call it to inform the application that + // the device has been enumerated. + // + if(g_pHIDDevice[lDev].pfnCallback != 0) + { + g_pHIDDevice[lDev].pfnCallback( + (void *)g_pHIDDevice[lDev].ulCBData, + USB_EVENT_CONNECTED, + (unsigned long)&g_pHIDDevice[lDev], 0); + } + + // + // Save the device pointer. + // + g_pHIDDevice[lDev].pDevice = pDevice; + + return (&g_pHIDDevice[lDev]); + } + } + + // + // If we get here, no user has registered an interest in this particular + // HID device so we return an error. + // + return(0); +} + +//***************************************************************************** +// +//! This function is used to release an instance of the HID driver. +//! +//! \param pvInstance is an instance pointer that needs to be released. +//! +//! This function will free up any resources in use by the HID driver instance +//! that is passed in. The \e pvInstance pointer should be a valid value that +//! was returned from a call to USBHIDOpen(). +//! +//! \return None. +// +//***************************************************************************** +static void +HIDDriverClose(void *pvInstance) +{ + tHIDInstance *pInst; + + // + // Get our instance pointer. + // + pInst = (tHIDInstance *)pvInstance; + + // + // Reset the device pointer. + // + pInst->pDevice = 0; + + // + // Free the Interrupt IN pipe. + // + if(pInst->ulIntInPipe != 0) + { + USBHCDPipeFree(pInst->ulIntInPipe); + } + + // + // If the callback exists, call it with a DISCONNECTED event. + // + if(pInst->pfnCallback != 0) + { + pInst->pfnCallback((void *)pInst->ulCBData, + USB_EVENT_DISCONNECTED, + (unsigned long)pvInstance, 0); + } +} + +//***************************************************************************** +// +//! This function is used to set the idle timeout for a HID device. +//! +//! \param ulInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ucDuration is the duration of the timeout in milliseconds. +//! \param ucReportID is the report identifier to set the timeout on. +//! +//! This function will send the Set Idle command to a HID device to set the +//! idle timeout for a given report. The length of the timeout is specified +//! by the \e ucDuration parameter and the report the timeout for is in the +//! \e ucReportID value. +//! +//! \return Always returns 0. +// +//***************************************************************************** +unsigned long +USBHHIDSetIdle(unsigned long ulInstance, unsigned char ucDuration, + unsigned char ucReportID) +{ + tUSBRequest SetupPacket; + tHIDInstance *pHIDInstance; + + pHIDInstance = (tHIDInstance *)ulInstance; + + // + // This is a Class specific interface OUT request. + // + SetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS + | USB_RTYPE_INTERFACE; + + // + // Request a Device Descriptor. + // + SetupPacket.bRequest = USBREQ_SET_IDLE; + SetupPacket.wValue = (ucDuration << 8) | ucReportID; + + // + // Set this on interface 1. + // + SetupPacket.wIndex = 0; + + // + // This is always 0 for this request. + // + SetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + return(USBHCDControlTransfer(0, &SetupPacket, pHIDInstance->pDevice, + 0, 0, MAX_PACKET_SIZE_EP0)); +} + +//***************************************************************************** +// +//! This function can be used to retrieve the report descriptor for a given +//! device instance. +//! +//! \param ulInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param pucBuffer is the memory buffer to use to store the report +//! descriptor. +//! \param ulSize is the size in bytes of the buffer pointed to by +//! \e pucBuffer. +//! +//! This function is used to return a report descriptor from a HID device +//! instance so that it can determine how to interpret reports that are +//! returned from the device indicated by the \e ulInstance parameter. +//! This call is blocking and will return the number of bytes read into the +//! \e pucBuffer. +//! +//! \return Returns the number of bytes read into the \e pucBuffer. +// +//***************************************************************************** +unsigned long +USBHHIDGetReportDescriptor(unsigned long ulInstance, unsigned char *pucBuffer, + unsigned long ulSize) +{ + tUSBRequest SetupPacket; + unsigned long ulBytes; + tHIDInstance *pHIDInstance; + + pHIDInstance = (tHIDInstance *)ulInstance; + + // + // This is a Standard Device IN request. + // + SetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_STANDARD + | USB_RTYPE_INTERFACE; + + // + // Request a Report Descriptor. + // + SetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + SetupPacket.wValue = USB_HID_DTYPE_REPORT << 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 = ulSize; + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + ulBytes = USBHCDControlTransfer( + 0, + &SetupPacket, + pHIDInstance->pDevice, + pucBuffer, + ulSize, + pHIDInstance->pDevice->DeviceDescriptor.bMaxPacketSize0); + + return(ulBytes); +} + +//***************************************************************************** +// +//! This function is used to set or clear the boot protocol state of a device. +//! +//! \param ulInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ulBootProtocol is either zero or non-zero to indicate which protocol +//! to use for the device. +//! +//! A USB host device can use this function to set the protocol for a connected +//! HID device. This is commonly used to set keyboards and mice into their +//! simplified boot protocol modes to fix the report structure to a know +//! state. +//! +//! \return This function returns 0. +// +//***************************************************************************** +unsigned long +USBHHIDSetProtocol(unsigned long ulInstance, unsigned long ulBootProtocol) +{ + tUSBRequest SetupPacket; + tHIDInstance *pHIDInstance; + + pHIDInstance = (tHIDInstance *)ulInstance; + + // + // This is a Standard Device IN request. + // + SetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS + | USB_RTYPE_INTERFACE; + + // + // Request a Report Descriptor. + // + SetupPacket.bRequest = USBREQ_SET_PROTOCOL; + + if(ulBootProtocol) + { + // + // Boot Protocol. + // + SetupPacket.wValue = 0; + } + else + { + // + // Report Protocol. + // + SetupPacket.wValue = 1; + } + + // + // Index is always 0 for device requests. + // + SetupPacket.wIndex = 0; + + // + // Always 0. + // + SetupPacket.wLength = 0; + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + USBHCDControlTransfer( + 0, + &SetupPacket, + pHIDInstance->pDevice, + 0, + 0, + pHIDInstance->pDevice->DeviceDescriptor.bMaxPacketSize0); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to retrieve a report from a HID device. +//! +//! \param ulInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ulInterface is the interface to retrieve the report from. +//! \param pucData is the memory buffer to use to store the report. +//! \param ulSize is the size in bytes of the buffer pointed to by +//! \e pucBuffer. +//! +//! This function is used to retrieve a report from a USB pipe. It is usually +//! called when the USB HID layer has detected a new data available in a USB +//! pipe. The USB HID host device code will receive a +//! \b USB_EVENT_RX_AVAILABLE event when data is available, allowing the +//! callback function to retrieve the data. +//! +//! \return Returns the number of bytes read from report. +// +//***************************************************************************** +unsigned long +USBHHIDGetReport(unsigned long ulInstance, + unsigned long ulInterface, + unsigned char *pucData, + unsigned long ulSize) +{ + tHIDInstance *pHIDInstance; + + // + // Cast the instance pointer to the correct type for ease of use. + // + pHIDInstance = (tHIDInstance *)ulInstance; + + // + // Read the Data out. + // + ulSize = USBHCDPipeReadNonBlocking(pHIDInstance->ulIntInPipe, pucData, + ulSize); + + // + // Return the number of bytes read from the interrupt in pipe. + // + return(ulSize); +} + +//***************************************************************************** +// +//! This function is used to send a report to a HID device. +//! +//! \param ulInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ulInterface is the interface to send the report to. +//! \param pucData is the memory buffer to use to store the report. +//! \param ulSize is the size in bytes of the buffer pointed to by +//! \e pucBuffer. +//! +//! This function is used to send a report to a USB HID device. It can be +//! only be called from outside the callback context as this function will not +//! return from the call until the data has been sent successfully. +//! +//! \return Returns the number of bytes sent to the device. +// +//***************************************************************************** +unsigned long +USBHHIDSetReport(unsigned long ulInstance, unsigned long ulInterface, + unsigned char *pucData, unsigned long ulSize) +{ + tUSBRequest SetupPacket; + tHIDInstance *pHIDInstance; + + pHIDInstance = (tHIDInstance *)ulInstance; + + // + // This is a Standard Device IN request. + // + SetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS + | USB_RTYPE_INTERFACE; + + // + // Request a Report Descriptor. + // + SetupPacket.bRequest = USBREQ_SET_REPORT; + SetupPacket.wValue = USB_HID_REPORT_OUTPUT << 8; + + // + // Index is always 0 for device requests. + // + SetupPacket.wIndex = (unsigned short)ulInterface; + + // + // Always 0. + // + SetupPacket.wLength = ulSize; + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + USBHCDControlTransfer(0, &SetupPacket, pHIDInstance->pDevice, + pucData, ulSize, + pHIDInstance->pDevice->DeviceDescriptor.bMaxPacketSize0); + + return(ulSize); +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhhid.h b/usblib/host/usbhhid.h new file mode 100644 index 0000000..2088c1e --- /dev/null +++ b/usblib/host/usbhhid.h @@ -0,0 +1,164 @@ +//***************************************************************************** +// +// usbhhid.h - This hold the host driver for hid class. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBHHID_H__ +#define __USBHHID_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// These defines are the the events that will be passed in the ulEvent +// parameter of the callback from the driver. +// +//***************************************************************************** +#define USBH_EVENT_HID_SETRPT USBH_HID_EVENT_BASE + 0 +#define USBH_EVENT_HID_REPORT USBH_HID_EVENT_BASE + 1 + +// +//! The HID keyboard detected a key being pressed. +// +#define USBH_EVENT_HID_KB_PRESS USBH_HID_EVENT_BASE + 16 + +// +//! The HID keyboard detected a key being released. +// +#define USBH_EVENT_HID_KB_REL USBH_HID_EVENT_BASE + 17 + +// +//! The HID keyboard detected one of the keyboard modifiers being pressed. +// +#define USBH_EVENT_HID_KB_MOD USBH_HID_EVENT_BASE + 18 + +// +//! A button was pressed on a HID mouse. +// +#define USBH_EVENT_HID_MS_PRESS USBH_HID_EVENT_BASE + 32 + +// +//! A button was released on a HID mouse. +// +#define USBH_EVENT_HID_MS_REL USBH_HID_EVENT_BASE + 33 + +// +//! The HID mouse detected movement in the X direction. +// +#define USBH_EVENT_HID_MS_X USBH_HID_EVENT_BASE + 34 + +// +//! The HID mouse detected movement in the Y direction. +// +#define USBH_EVENT_HID_MS_Y USBH_HID_EVENT_BASE + 35 + +//***************************************************************************** +// +//! The following values are used to register callbacks to the USB HOST HID +//! device class layer. +// +//***************************************************************************** +typedef enum +{ + // + //! No device should be used. This value should not be used by + //! applications. + // + USBH_HID_DEV_NONE = 0, + + // + //! This is a keyboard device. + // + USBH_HID_DEV_KEYBOARD, + + // + //! This is a mouse device. + // + USBH_HID_DEV_MOUSE, + + // + //! This is a vendor specific device. + // + USBH_HID_DEV_VENDOR +} +tHIDSubClassProtocol; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototypes. +// +//***************************************************************************** +extern unsigned long USBHHIDOpen(tHIDSubClassProtocol eDeviceType, + tUSBCallback pfnCallback, + unsigned long ulCBData); +extern void USBHHIDClose(unsigned long ulInstance); +extern unsigned long USBHHIDGetReportDescriptor(unsigned long ulInstance, + unsigned char *pucBuffer, + unsigned long ulSize); +extern unsigned long USBHHIDSetIdle(unsigned long ulInstance, + unsigned char ucDuration, + unsigned char ucReportID); +extern unsigned long USBHHIDSetProtocol(unsigned long ulInstance, + unsigned long ulBootProtocol); +extern unsigned long USBHHIDSetReport(unsigned long ulInstance, + unsigned long ulInterface, + unsigned char *pucData, + unsigned long ulSize); +extern unsigned long USBHHIDGetReport(unsigned long ulInstance, + unsigned long ulInterface, + unsigned char *pucData, + unsigned long ulSize); +extern const tUSBHostClassDriver g_USBHIDClassDriver; + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBHHID_H__ diff --git a/usblib/host/usbhhidkeyboard.c b/usblib/host/usbhhidkeyboard.c new file mode 100644 index 0000000..7d356a3 --- /dev/null +++ b/usblib/host/usbhhidkeyboard.c @@ -0,0 +1,719 @@ +//***************************************************************************** +// +// usbhhidkeyboard.c - This file holds the application interfaces for USB +// keyboard devices. +// +// 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_types.h" +#include "usblib/usblib.h" +#include "usblib/host/usbhost.h" +#include "usblib/usbhid.h" +#include "usblib/host/usbhhid.h" +#include "usblib/host/usbhhidkeyboard.h" + +//***************************************************************************** +// +//! \addtogroup usblib_host_device +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototypes for local functions. +// +//***************************************************************************** +static unsigned long USBHKeyboardCallback(void *pvCBData, + unsigned long ulEvent, + unsigned long ulMsgParam, + void *pvMsgData); + +//***************************************************************************** +// +// The size of a USB keyboard report. +// +//***************************************************************************** +#define USBHKEYB_REPORT_SIZE 8 + +//***************************************************************************** +// +// These are the flags for the tUSBHKeyboard.ulHIDFlags member variable. +// +//***************************************************************************** +#define USBHKEYB_DEVICE_PRESENT 0x00000001 + +//***************************************************************************** +// +// This is the structure definition for a keyboard device instance. +// +//***************************************************************************** +typedef struct +{ + // + // Global flags for an instance of a keyboard. + // + unsigned long ulHIDFlags; + + // + // The applications registered callback. + // + tUSBCallback pfnCallback; + + // + // The HID instance pointer for this keyboard instance. + // + unsigned long ulHIDInstance; + + // + // NUM_LOCK, CAPS_LOCK, SCROLL_LOCK, COMPOSE or KANA keys. + // + unsigned char ucKeyModSticky; + + // + // This is the current state of the keyboard modifier keys. + // + unsigned char ucKeyModState; + + // + // This holds the keyboard usage codes for keys that are being held down. + // + unsigned char pucKeyState[6]; + + // + // This is a local buffer to hold the current HID report that comes up + // from the HID driver layer. + // + unsigned char pucBuffer[USBHKEYB_REPORT_SIZE]; +} +tUSBHKeyboard; + +//***************************************************************************** +// +// This is the per instance information for a keyboard device. +// +//***************************************************************************** +static tUSBHKeyboard g_sUSBHKeyboard = +{ + 0 +}; + +//***************************************************************************** +// +//! This function is used open an instance of a keyboard. +//! +//! \param pfnCallback is the callback function to call when new events occur +//! with the keyboard returned. +//! \param pucBuffer is the memory used by the keyboard to interact with the +//! USB keyboard. +//! \param ulSize is the size of the buffer provided by \e pucBuffer. +//! +//! This function is used to open an instance of the keyboard. The value +//! returned from this function should be used as the instance identifier for +//! all other USBHKeyboard calls. The \e pucBuffer memory buffer is used to +//! access the keyboard. The buffer size required is at least enough to hold +//! a normal report descriptor for the device. If there is not enough space +//! only a partial report descriptor will be read out. +//! +//! \return Returns the instance identifier for the keyboard that is attached. +//! If there is no keyboard present this will return 0. +// +//***************************************************************************** +unsigned long +USBHKeyboardOpen(tUSBCallback pfnCallback, unsigned char *pucBuffer, + unsigned long ulSize) +{ + // + // Save the callback and data pointers. + // + g_sUSBHKeyboard.pfnCallback = pfnCallback; + + // + // Save the instance pointer for the HID device that was opened. + // + g_sUSBHKeyboard.ulHIDInstance = + USBHHIDOpen(USBH_HID_DEV_KEYBOARD, USBHKeyboardCallback, + (unsigned long)&g_sUSBHKeyboard); + + return((unsigned long)&g_sUSBHKeyboard); +} + +//***************************************************************************** +// +//! This function is used close an instance of a keyboard. +//! +//! \param ulInstance is the instance value for this keyboard. +//! +//! This function is used to close an instance of the keyboard that was opened +//! with a call to USBHKeyboardOpen(). The \e ulInstance value is the value +//! that was returned when the application called USBHKeyboardOpen(). +//! +//! \return This function returns 0 to indicate success any non-zero value +//! indicates an error condition. +// +//***************************************************************************** +unsigned long +USBHKeyboardClose(unsigned long ulInstance) +{ + tUSBHKeyboard *pUSBHKeyboard; + + // + // Recover the pointer to the instance data. + // + pUSBHKeyboard = (tUSBHKeyboard *)ulInstance; + + // + // Reset the callback to null. + // + pUSBHKeyboard->pfnCallback = 0; + + // + // Call the HID driver layer to close out this instance. + // + USBHHIDClose(pUSBHKeyboard->ulHIDInstance); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to map a USB usage ID to a printable character. +//! +//! \param ulInstance is the instance value for this keyboard. +//! \param pTable is the table to use to map the usage ID to characters. +//! \param ucUsageID is the USB usage ID to map to a character. +//! +//! This function is used to map a USB usage ID to a character. The provided +//! \e pTable is used to perform the mapping and is described by the +//! tHIDKeyboardUsageTable type defined structure. See the documentation on +//! the tHIDKeyboardUsageTable structure for more details on the internals of +//! this structure. This function uses the current state of the shift keys +//! and the Caps Lock key to modify the data returned by this function. The +//! pTable structure has values indicating which keys are modified by Caps Lock +//! and alternate values for shifted cases. The number of bytes returned from +//! this function depends on the \e pTable structure passed in as it holds the +//! number of bytes per character in the table. +//! +//! \return Returns the character value for the given usage id. +// +//***************************************************************************** +unsigned long +USBHKeyboardUsageToChar(unsigned long ulInstance, + const tHIDKeyboardUsageTable *pTable, + unsigned char ucUsageID) +{ + unsigned long ulValue; + const unsigned char *pucKeyBoardMap; + const unsigned short *pusKeyBoardMap; + unsigned long ulOffset; + unsigned long ulShift; + tUSBHKeyboard *pUSBHKeyboard; + + // + // Recover the pointer to the instance data. + // + pUSBHKeyboard = (tUSBHKeyboard *)ulInstance; + + // + // The added offset for the shifted character value. + // + ulShift = 0; + + // + // Offset in the table for the character. + // + ulOffset = (ucUsageID * pTable->ucBytesPerChar * 2); + + // + // Handle the case where CAPS lock has been set. + // + if(pUSBHKeyboard->ucKeyModSticky &= HID_KEYB_CAPS_LOCK) + { + // + // See if this usage ID is modified by Caps Lock by checking the packed + // bit array in the pulShiftState member of the pTable array. + // + if((pTable->pulCapsLock[ucUsageID >> 5]) >> (ucUsageID & 0x1f) & 1) + { + ulShift = pTable->ucBytesPerChar; + } + } + + // + // Now handle if a shift key is being held. + // + if((pUSBHKeyboard->ucKeyModState & 0x22) != 0) + { + // + // Not shifted yet so we need to shift. + // + if(ulShift == 0) + { + ulShift = pTable->ucBytesPerChar; + } + else + { + // + // Unshift because CAPS LOCK and shift were presed. + // + ulShift = 0; + } + } + + // + // One byte per character. + // + if(pTable->ucBytesPerChar == 1) + { + // + // Get the base address of the table. + // + pucKeyBoardMap = pTable->pCharMapping; + + ulValue = pucKeyBoardMap[ulOffset + ulShift]; + } + // + // Two bytes per character. + // + else if(pTable->ucBytesPerChar == 2) + { + // + // Get the base address of the table. + // + pusKeyBoardMap = (unsigned short *)pTable->pCharMapping; + + ulValue = pusKeyBoardMap[ulOffset + ulShift]; + } + // + // All other sizes are unsupported for now. + // + else + { + ulValue = 0; + } + + return(ulValue); +} + +//***************************************************************************** +// +//! This function is used to set one of the fixed modifier keys on a keyboard. +//! +//! \param ulInstance is the instance value for this keyboard. +//! \param ulModifiers is a bit mask of the modifiers to set on the keyboard. +//! +//! This function is used to set the modifier key states on a keyboard. The +//! \e ulModifiers value is a bitmask of the following set of values: +//! - HID_KEYB_NUM_LOCK +//! - HID_KEYB_CAPS_LOCK +//! - HID_KEYB_SCROLL_LOCK +//! - HID_KEYB_COMPOSE +//! - HID_KEYB_KANA +//! +//! Not all of these will be supported on all keyboards however setting values +//! on a keyboard that does not have them should have no effect. The +//! \e ulInstance value is the value that was returned when the application +//! called USBHKeyboardOpen(). If the value \b HID_KEYB_CAPS_LOCK is used it +//! will modify the values returned from the USBHKeyboardUsageToChar() +//! function. +//! +//! \return This function returns 0 to indicate success any non-zero value +//! indicates an error condition. +// +//***************************************************************************** +unsigned long +USBHKeyboardModifierSet(unsigned long ulInstance, unsigned long ulModifiers) +{ + tUSBHKeyboard *pUSBHKeyboard; + + // + // Recover the pointer to the instance data. + // + pUSBHKeyboard = (tUSBHKeyboard *)ulInstance; + + // + // Remeber the fact that this is set. + // + pUSBHKeyboard->ucKeyModSticky = (unsigned char)ulModifiers; + + // + // Set the LEDs on the keyboard. + // + USBHHIDSetReport(pUSBHKeyboard->ulHIDInstance, 0, + (unsigned char *)&ulModifiers, 1); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to initialize a keyboard interface after a keyboard +//! has been detected. +//! +//! \param ulInstance is the instance value for this keyboard. +//! +//! This function should be called after receiving a \b USB_EVENT_CONNECTED +//! event in the callback function provided by USBHKeyboardOpen(), however this +//! function should only be called outside the callback function. This will +//! initialize the keyboard interface and determine the keyboard's +//! layout and how it reports keys to the USB host controller. The +//! \e ulInstance value is the value that was returned when the application +//! called USBHKeyboardOpen(). This function only needs to be called once +//! per connection event but it should be called every time a +//! \b USB_EVENT_CONNECTED event occurs. +//! +//! \return This function returns 0 to indicate success any non-zero value +//! indicates an error condition. +// +//***************************************************************************** +unsigned long +USBHKeyboardInit(unsigned long ulInstance) +{ + unsigned char ucModData; + tUSBHKeyboard *pUSBHKeyboard; + + // + // Recover the pointer to the instance data. + // + pUSBHKeyboard = (tUSBHKeyboard *)ulInstance; + + // + // Set the initial rate to only update on keyboard state changes. + // + USBHHIDSetIdle(pUSBHKeyboard->ulHIDInstance, 0, 0); + + // + // Read out the Report Descriptor from the keyboard and parse it for + // the format of the reports coming back from the keyboard. + // + USBHHIDGetReportDescriptor(pUSBHKeyboard->ulHIDInstance, + pUSBHKeyboard->pucBuffer, + USBHKEYB_REPORT_SIZE); + + // + // Set the keyboard to boot protocol. + // + USBHHIDSetProtocol(pUSBHKeyboard->ulHIDInstance, 1); + + // + // Used to clear the initial state of all on keyboard modifiers. + // + ucModData = 0; + + // + // Update the keyboard LED state. + // + USBHHIDSetReport(pUSBHKeyboard->ulHIDInstance, 0, &ucModData, 1); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to set the automatic poll rate of the keyboard. +//! +//! \param ulInstance is the instance value for this keyboard. +//! \param ulPollRate is the rate in ms to cause the keyboard to update the +//! host regardless of no change in key state. +//! +//! This function will allow an application to tell the keyboard how often it +//! should send updates to the USB host controller regardless of any changes +//! in keyboard state. The \e ulInstance value is the value that was returned +//! when the application called USBHKeyboardOpen(). The \e ulPollRate is the +//! new value in ms for the update rate on the keyboard. This value is +//! initially set to 0 which indicates that the keyboard should only to update +//! when the keyboard state changes. Any value other than 0 can be used to +//! force the keyboard to generate auto-repeat sequences for the application. +//! +//! \return This function returns 0 to indicate success any non-zero value +//! indicates an error condition. +// +//***************************************************************************** +unsigned long +USBHKeyboardPollRateSet(unsigned long ulInstance, unsigned long ulPollRate) +{ + tUSBHKeyboard *pUSBHKeyboard; + + // + // Recover the pointer to the instance data. + // + pUSBHKeyboard = (tUSBHKeyboard *)ulInstance; + + // + // Send the Set Idle command to the USB keyboard. + // + USBHHIDSetIdle(pUSBHKeyboard->ulHIDInstance, ulPollRate, 0); + + return(0); +} + +//***************************************************************************** +// +// This is an internal function used to modify the current keyboard state. +// +// This function checks for changes in the keyboard state due to a new report +// being received from the device. It first checks if this is a "roll-over" +// case by seeing if 0x01 is in the first position of the new keyboard report. +// This indicates that too many keys were pressed to handle and to ignore this +// report. Next the keyboard modifier state is stored and if any changes are +// detected a \b USBH_EVENT_HID_KB_MOD event is sent back to the application. +// Then this function will check for any keys that have been released and send +// a \b USBH_EVENT_HID_KB_REL even for each of these keys. The last check is +// for any new keys that are pressed and a \b USBH_EVENT_HID_KB_PRESS event +// will be sent for each new key pressed. +// +// \return None. +// +//***************************************************************************** +static void +UpdateKeyboardState(tUSBHKeyboard *pUSBHKeyboard) +{ + long lNewKey, lOldKey; + + // + // rollover code so ignore this buffer. + // + if(pUSBHKeyboard->pucBuffer[2] == 0x01) + { + return; + } + + // + // Handle the keyboard modifier states. + // + if(pUSBHKeyboard->ucKeyModState != pUSBHKeyboard->pucBuffer[0]) + { + // + // Notify the application of the event. + // + pUSBHKeyboard->pfnCallback(0, USBH_EVENT_HID_KB_MOD, + pUSBHKeyboard->pucBuffer[0], 0); + + // + // Save the new state of the modifier keys. + // + pUSBHKeyboard->ucKeyModState = pUSBHKeyboard->pucBuffer[0]; + } + + // + // This loop checks for keys that have been released to make room for new + // ones that may have been pressed. + // + for(lOldKey = 2; lOldKey < 8; lOldKey++) + { + // + // If there is no old key pressed in this entry go to the next one. + // + if(pUSBHKeyboard->pucKeyState[lOldKey] == 0) + { + continue; + } + + // + // Check if this old key is still in the list of currently pressed + // keys. + // + for(lNewKey = 2; lNewKey < 8; lNewKey++) + { + // + // Break out if the key is still present. + // + if(pUSBHKeyboard->pucBuffer[lNewKey] + == pUSBHKeyboard->pucKeyState[lOldKey]) + { + break; + } + } + // + // If the old key was no longer in the list of pressed keys then + // notify the application of the key release. + // + if(lNewKey == 8) + { + // + // Send the key release notification to the application. + // + pUSBHKeyboard->pfnCallback(0, + USBH_EVENT_HID_KB_REL, + pUSBHKeyboard->pucKeyState[lOldKey], + 0); + // + // Remove the old key from the currently held key list. + // + pUSBHKeyboard->pucKeyState[lOldKey] = 0; + + } + } + + // + // This loop checks for new keys that have been pressed. + // + for(lNewKey = 2; lNewKey < 8; lNewKey++) + { + // + // The new list is empty so no new keys are pressed. + // + if(pUSBHKeyboard->pucBuffer[lNewKey] == 0) + { + break; + } + + // + // This loop checks if the current key was already pressed. + // + for(lOldKey = 2; lOldKey < 8; lOldKey++) + { + // + // If it is in both lists then it was already pressed so ignore it. + // + if(pUSBHKeyboard->pucBuffer[lNewKey] + == pUSBHKeyboard->pucKeyState[lOldKey]) + { + break; + } + } + // + // The key in the new list was not found so it is new. + // + if(lOldKey == 8) + { + // + // Look for a free location to store this key usage code. + // + for(lOldKey = 2; lOldKey < 8; lOldKey++) + { + // + // If an empty location is found, store it and notify the + // application. + // + if(pUSBHKeyboard->pucKeyState[lOldKey] == 0) + { + // + // Save the newly pressed key. + // + pUSBHKeyboard->pucKeyState[lOldKey] + = pUSBHKeyboard->pucBuffer[lNewKey]; + + // + // Notify the application of the new key that has been + // pressed. + // + pUSBHKeyboard->pfnCallback( + 0, + USBH_EVENT_HID_KB_PRESS, + pUSBHKeyboard->pucBuffer[lNewKey], + 0); + + break; + } + } + } + } +} + +//***************************************************************************** +// +//! This function handles event callbacks from the USB HID driver layer. +//! +//! \param pvCBData is the pointer that was passed in to the USBHHIDOpen() +//! call. +//! \param ulEvent is the event that has been passed up from the HID driver. +//! \param ulMsgParam has meaning related to the \e ulEvent that occurred. +//! \param pvMsgData has meaning related to the \e ulEvent that occurred. +//! +//! This function will receive all event updates from the HID driver layer. +//! The keyboard driver itself will mostly be concerned with report callbacks +//! from the HID driver layer and parsing them into keystrokes for the +//! application that has registered for callbacks with the USBHKeyboardOpen() +//! call. +//! +//! \return Non-zero values should be assumed to indicate an error condition. +// +//***************************************************************************** +unsigned long +USBHKeyboardCallback(void *pvCBData, unsigned long ulEvent, + unsigned long ulMsgParam, void *pvMsgData) +{ + tUSBHKeyboard *pUSBHKeyboard; + + // + // Recover the pointer to the instance data. + // + pUSBHKeyboard = (tUSBHKeyboard *)pvCBData; + + switch (ulEvent) + { + // + // New keyboard has been connected so notify the application. + // + case USB_EVENT_CONNECTED: + { + // + // Remember that a keyboard is present. + // + pUSBHKeyboard->ulHIDFlags |= USBHKEYB_DEVICE_PRESENT; + + // + // Notify the application that a new keyboard was connected. + // + pUSBHKeyboard->pfnCallback(0, ulEvent, ulMsgParam, pvMsgData); + + break; + } + case USB_EVENT_DISCONNECTED: + { + // + // No keyboard is present. + // + pUSBHKeyboard->ulHIDFlags &= ~USBHKEYB_DEVICE_PRESENT; + + // + // Notify the application that the keyboard was disconnected. + // + pUSBHKeyboard->pfnCallback(0, ulEvent, ulMsgParam, pvMsgData); + + break; + } + case USB_EVENT_RX_AVAILABLE: + { + // + // New keyboard report structure was received. + // + USBHHIDGetReport(pUSBHKeyboard->ulHIDInstance, 0, + pUSBHKeyboard->pucBuffer, + USBHKEYB_REPORT_SIZE); + + // + // Update the application on the changes in the keyboard state. + // + UpdateKeyboardState(pUSBHKeyboard); + + break; + } + } + return(0); +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhhidkeyboard.h b/usblib/host/usbhhidkeyboard.h new file mode 100644 index 0000000..a84ff28 --- /dev/null +++ b/usblib/host/usbhhidkeyboard.h @@ -0,0 +1,77 @@ +//***************************************************************************** +// +// usbhhidkeyboard.h - This file holds the application interfaces for USB +// keyboard devices. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBHHIDKEYBOARD_H__ +#define __USBHHIDKEYBOARD_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_host_device +//! @{ +// +//***************************************************************************** + +extern unsigned long USBHKeyboardOpen(tUSBCallback pfnCallback, + unsigned char *pucBuffer, + unsigned long ulBufferSize); +extern unsigned long USBHKeyboardClose(unsigned long ulInstance); +extern unsigned long USBHKeyboardInit(unsigned long ulInstance); +extern unsigned long USBHKeyboardModifierSet(unsigned long ulInstance, + unsigned long ulModifiers); +extern unsigned long USBHKeyboardPollRateSet(unsigned long ulInstance, + unsigned long ulPollRate); + +extern unsigned long USBHKeyboardUsageToChar( + unsigned long ulInstance, + const tHIDKeyboardUsageTable *pTable, + unsigned char ucUsageID); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usblib/host/usbhhidmouse.c b/usblib/host/usbhhidmouse.c new file mode 100644 index 0000000..d337965 --- /dev/null +++ b/usblib/host/usbhhidmouse.c @@ -0,0 +1,415 @@ +//***************************************************************************** +// +// usbhhidmouse.c - This file holds the application interfaces for USB +// mouse devices. +// +// 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_types.h" +#include "usblib/usblib.h" +#include "usblib/host/usbhost.h" +#include "usblib/usbhid.h" +#include "usblib/host/usbhhid.h" +#include "usblib/host/usbhhidmouse.h" + +//***************************************************************************** +// +//! \addtogroup usblib_host_device +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototypes for local functions. +// +//***************************************************************************** +static unsigned long USBHMouseCallback(void *pvCBData, + unsigned long ulEvent, + unsigned long ulMsgParam, + void *pvMsgData); + +//***************************************************************************** +// +// The size of a USB mouse report. +// +//***************************************************************************** +#define USBHMS_REPORT_SIZE 4 + +//***************************************************************************** +// +// These are the flags for the tUSBHMouse.ulHIDFlags member variable. +// +//***************************************************************************** +#define USBHMS_DEVICE_PRESENT 0x00000001 + +//***************************************************************************** +// +// This is the structure definition for a mouse device instance. +// +//***************************************************************************** +typedef struct +{ + // + // Global flags for an instance of a mouse. + // + unsigned long ulHIDFlags; + + // + // The applications registered callback. + // + tUSBCallback pfnCallback; + + // + // The current state of the buttons. + // + unsigned char ucButtons; + + // + // This is a local buffer to hold the current HID report that comes up + // from the HID driver layer. + // + unsigned char pucBuffer[USBHMS_REPORT_SIZE]; + + // + // Heap data for the mouse currently used to read the HID Report + // Descriptor. + // + unsigned char *pucHeap; + + // + // Size of the heap in bytes. + // + unsigned long ulHeapSize; + + // + // This is the instance value for the HID device that will be used for the + // mouse. + // + unsigned long ulMouseInstance; +} +tUSBHMouse; + +//***************************************************************************** +// +// This is the per instance information for a mouse device. +// +//***************************************************************************** +static tUSBHMouse g_sUSBHMouse = +{ + 0 +}; + +//***************************************************************************** +// +//! This function is used open an instance of a mouse. +//! +//! \param pfnCallback is the callback function to call when new events occur +//! with the mouse returned. +//! \param pucBuffer is the memory used by the driver to interact with the +//! USB mouse. +//! \param ulSize is the size of the buffer provided by \e pucBuffer. +//! +//! This function is used to open an instance of the mouse. The value +//! returned from this function should be used as the instance identifier for +//! all other USBHMouse calls. The \e pucBuffer memory buffer is used to +//! access the mouse. The buffer size required is at least enough to hold +//! a normal report descriptor for the device. +//! +//! \return Returns the instance identifier for the mouse that is attached. +//! If there is no mouse present this will return 0. +// +//***************************************************************************** +unsigned long +USBHMouseOpen(tUSBCallback pfnCallback, unsigned char *pucBuffer, + unsigned long ulSize) +{ + // + // Save the callback and data pointers. + // + g_sUSBHMouse.pfnCallback = pfnCallback; + + // + // Save the instance pointer for the HID device that was opened. + // + g_sUSBHMouse.ulMouseInstance = USBHHIDOpen(USBH_HID_DEV_MOUSE, + USBHMouseCallback, + (unsigned long)&g_sUSBHMouse); + + // + // Save the heap buffer and size. + // + g_sUSBHMouse.pucHeap = pucBuffer; + g_sUSBHMouse.ulHeapSize = ulSize; + + return((unsigned long)&g_sUSBHMouse); +} + +//***************************************************************************** +// +//! This function is used close an instance of a mouse. +//! +//! \param ulInstance is the instance value for this mouse. +//! +//! This function is used to close an instance of the mouse that was opened +//! with a call to USBHMouseOpen(). The \e ulInstance value is the value +//! that was returned when the application called USBHMouseOpen(). +//! +//! \return Returns 0. +// +//***************************************************************************** +unsigned long +USBHMouseClose(unsigned long ulInstance) +{ + tUSBHMouse *pUSBHMouse; + + // + // Recover the pointer to the instance data. + // + pUSBHMouse = (tUSBHMouse *)ulInstance; + + // + // Reset the callback to null. + // + pUSBHMouse->pfnCallback = 0; + + // + // Call the HID driver layer to close out this instance. + // + USBHHIDClose(pUSBHMouse->ulMouseInstance); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to initialize a mouse interface after a mouse has +//! been detected. +//! +//! \param ulInstance is the instance value for this mouse. +//! +//! This function should be called after receiving a \b USB_EVENT_CONNECTED +//! event in the callback function provided by USBHMouseOpen(), however it +//! should only be called outside of the callback function. This will +//! initialize the mouse interface and determine how it reports events to the +//! USB host controller. The \e ulInstance value is the value that was +//! returned when the application called USBHMouseOpen(). This function only +//! needs to be called once per connection event but it should be called every +//! time a \b USB_EVENT_CONNECTED event occurs. +//! +//! \return Non-zero values should be assumed to indicate an error condition. +// +//***************************************************************************** +unsigned long +USBHMouseInit(unsigned long ulInstance) +{ + tUSBHMouse *pUSBHMouse; + + // + // Recover the pointer to the instance data. + // + pUSBHMouse = (tUSBHMouse *)ulInstance; + + // + // Set the initial rate to only update on mouse state changes. + // + USBHHIDSetIdle(pUSBHMouse->ulMouseInstance, 0, 0); + + // + // Read out the Report Descriptor from the mouse and parse it for + // the format of the reports coming back from the mouse. + // + USBHHIDGetReportDescriptor(pUSBHMouse->ulMouseInstance, + pUSBHMouse->pucHeap, + pUSBHMouse->ulHeapSize); + + // + // Set the mouse to boot protocol. + // + USBHHIDSetProtocol(pUSBHMouse->ulMouseInstance, 1); + + return(0); +} + +//***************************************************************************** +// +// This function handles updating the state of the mouse buttons and axis. +// +// \param pUSBHMouse is the pointer to an instance of the mouse data. +// +// This function will check for updates to buttons or X/Y movements and send +// callbacks to the mouse callback function. +// +// \return None. +// +//***************************************************************************** +static void +UpdateMouseState(tUSBHMouse *pUSBHMouse) +{ + unsigned long ulButton; + + if(pUSBHMouse->pucBuffer[0] != pUSBHMouse->ucButtons) + { + for(ulButton = 1; ulButton <= 0x4; ulButton <<= 1) + { + if(((pUSBHMouse->pucBuffer[0] & ulButton) != 0) && + ((pUSBHMouse->ucButtons & ulButton) == 0)) + { + // + // Send the mouse button press notification to the application. + // + pUSBHMouse->pfnCallback(0, + USBH_EVENT_HID_MS_PRESS, + ulButton, + 0); + } + if(((pUSBHMouse->pucBuffer[0] & ulButton) == 0) && + ((pUSBHMouse->ucButtons & ulButton) != 0)) + { + // + // Send the mouse button release notification to the + // application. + // + pUSBHMouse->pfnCallback(0, + USBH_EVENT_HID_MS_REL, + ulButton, + 0); + } + } + + // + // Save the new state. + // + pUSBHMouse->ucButtons = pUSBHMouse->pucBuffer[0]; + } + if(pUSBHMouse->pucBuffer[1] != 0) + { + // + // Send the mouse button release notification to the + // application. + // + pUSBHMouse->pfnCallback(0, + USBH_EVENT_HID_MS_X, + (unsigned long)pUSBHMouse->pucBuffer[1], + 0); + } + if(pUSBHMouse->pucBuffer[2] != 0) + { + // + // Send the mouse button release notification to the + // application. + // + pUSBHMouse->pfnCallback(0, + USBH_EVENT_HID_MS_Y, + (unsigned long)pUSBHMouse->pucBuffer[2], + 0); + } +} + +//***************************************************************************** +// +//! This function handles event callbacks from the USB HID driver layer. +//! +//! \param pvCBData is the pointer that was passed in to the USBHHIDOpen() +//! call. +//! \param ulEvent is the event that has been passed up from the HID driver. +//! \param ulMsgParam has meaning related to the \e ulEvent that occurred. +//! \param pvMsgData has meaning related to the \e ulEvent that occurred. +//! +//! This function will receive all event updates from the HID driver layer. +//! The mouse driver itself will mostly be concerned with report callbacks +//! from the HID driver layer and parsing them into keystrokes for the +//! application that has registered for callbacks with the USBHMouseOpen() +//! call. +//! +//! \return Non-zero values should be assumed to indicate an error condition. +// +//***************************************************************************** +unsigned long +USBHMouseCallback(void *pvCBData, unsigned long ulEvent, + unsigned long ulMsgParam, void *pvMsgData) +{ + tUSBHMouse *pUSBHMouse; + + // + // Recover the pointer to the instance data. + // + pUSBHMouse = (tUSBHMouse *)pvCBData; + + switch(ulEvent) + { + // + // New mouse has been connected so notify the application. + // + case USB_EVENT_CONNECTED: + { + // + // Remember that a mouse is present. + // + pUSBHMouse->ulHIDFlags |= USBHMS_DEVICE_PRESENT; + + // + // Notify the application that a new mouse was connected. + // + pUSBHMouse->pfnCallback(0, ulEvent, ulMsgParam, pvMsgData); + + break; + } + case USB_EVENT_DISCONNECTED: + { + // + // No mouse is present. + // + pUSBHMouse->ulHIDFlags &= ~USBHMS_DEVICE_PRESENT; + + // + // Notify the application that the mouse was disconnected. + // + pUSBHMouse->pfnCallback(0, ulEvent, ulMsgParam, pvMsgData); + + break; + } + case USB_EVENT_RX_AVAILABLE: + { + // + // New mouse report structure was received. + // + USBHHIDGetReport(pUSBHMouse->ulMouseInstance, 0, + pUSBHMouse->pucBuffer, + USBHMS_REPORT_SIZE); + + // + // Update the current state of the mouse and notify the application + // of any changes. + // + UpdateMouseState(pUSBHMouse); + + break; + } + } + return(0); +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhhidmouse.h b/usblib/host/usbhhidmouse.h new file mode 100644 index 0000000..4c4fb12 --- /dev/null +++ b/usblib/host/usbhhidmouse.h @@ -0,0 +1,68 @@ +//***************************************************************************** +// +// usbhhidmouse.h - This file holds the application interfaces for USB +// mouse devices. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBHHIDMOUSE_H__ +#define __USBHHIDMOUSE_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_host_device +//! @{ +// +//***************************************************************************** + +extern unsigned long USBHMouseOpen(tUSBCallback pfnCallback, + unsigned char *pucBuffer, + unsigned long ulBufferSize); +extern unsigned long USBHMouseClose(unsigned long ulInstance); +extern unsigned long USBHMouseInit(unsigned long ulInstance); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usblib/host/usbhhub.c b/usblib/host/usbhhub.c new file mode 100644 index 0000000..62dcd80 --- /dev/null +++ b/usblib/host/usbhhub.c @@ -0,0 +1,1251 @@ +//***************************************************************************** +// +// usbhhub.c - This file contains the host HID driver. +// +// Copyright (c) 2011-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_types.h" +#include "inc/hw_ints.h" +#include "driverlib/usb.h" +#include "driverlib/interrupt.h" +#include "driverlib/rom_map.h" +#include "driverlib/rtos_bindings.h" +#include "usblib/usblib.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_host_class +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Forward references to the hub class driver functions. +// +//***************************************************************************** +static void *HubDriverOpen(tUSBHostDevice *pDevice); +static void HubDriverClose(void *pvInstance); + +//***************************************************************************** +// +//! This constant global structure defines the Hub Class Driver that is +//! provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_USBHubClassDriver = +{ + USB_CLASS_HUB, + HubDriverOpen, + HubDriverClose, + 0 +}; + +//***************************************************************************** +// +// The instance data storage for attached hub. +// +//***************************************************************************** +static tHubInstance *g_pRootHub; + +//***************************************************************************** +// +// Hub and port state change flags as reported via the hub's IN endpoint. +// +//***************************************************************************** +static volatile unsigned long g_ulChangeFlags; + +// +// Note: The following assumes ROOT_HUB_MAX_PORTS is less than 32! +// +static unsigned long g_ulHubChanges; + +//***************************************************************************** +// +// This function is called to send a request to the hub to set a feature on +// a given port. +// +// \param ulInstance is the hub device instance. +// \param ucPort is the port number for this request. +// \param usFeature is one of the HUB_FEATURE_PORT_* values. +// +// This function will send the set feature request to the hub indicated by the +// \e ulInstance parameter. The \e ucPort value indicates which port number +// to send this request to and can range from 0 to the number of valid ports +// on the given hub. A \e ucPort value of 0 is an access to the hub itself and +// not one of the hub ports. The \e usFeature is the feature request to set +// on the given port. For example, a \e usFeature value of +// \e HUB_FEATURE_PORT_RESET and \e ucPort value of 1 will cause reset +// signaling to hub port 1. +// +// \return None. +// +//***************************************************************************** +static void +HubSetPortFeature(unsigned long ulInstance, unsigned char ucPort, + unsigned short usFeature) +{ + tUSBRequest SetupPacket; + tHubInstance *pHubInstance; + tUSBHostDevice *pDevice; + + // + // Retrieve the hub instance and device pointer. + // + pHubInstance = (tHubInstance *)ulInstance; + pDevice = pHubInstance->pDevice; + + // + // This is a standard OUT request. + // + SetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_OTHER; + + // + // Set the field to clear the requested port feature. + // + SetupPacket.bRequest = USBREQ_SET_FEATURE; + SetupPacket.wValue = usFeature; + SetupPacket.wIndex = ucPort; + SetupPacket.wLength = 0; + + // + // Send the request. + // + USBHCDControlTransfer(0, + &SetupPacket, + pDevice, + 0, + 0, + pDevice->DeviceDescriptor.bMaxPacketSize0); +} + +//***************************************************************************** +// +// This function is called to send a request to the hub to clear a feature on +// a given port. +// +// \param ulInstance is the hub device instance. +// \param ucPort is the port number for this request. +// \param usFeature is one of the HUB_FEATURE_PORT_* values. +// +// This function will send the clear feature request to the hub indicated by +// the \e ulInstance parameter. The \e ucPort value indicates which port +// number to send this request to and can range from 0 to the number of valid +// ports on the given hub. A \e ucPort value of 0 is an access to the hub +// itself and not one of the hub ports. The \e usFeature is the feature +// request to clear on the given port. For example, a \e usFeature value of +// \e HUB_FEATURE_C_PORT_RESET and \e ucPort value of 1 will clear the reset +// complete signaling on hub port 1. Values like the reset feature will +// remain set until actively cleared by this function. +// +// \return None. +// +//***************************************************************************** +static void +HubClearPortFeature(unsigned long ulInstance, unsigned char ucPort, + unsigned short usFeature) +{ + tUSBRequest SetupPacket; + tHubInstance *pHubInstance; + tUSBHostDevice *pDevice; + + // + // Retrieve the hub instance and device pointer. + // + pHubInstance = (tHubInstance *)ulInstance; + pDevice = pHubInstance->pDevice; + + // + // This is a standard OUT request. + // + SetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_OTHER; + + // + // Set the field to clear the requested port feature. + // + SetupPacket.bRequest = USBREQ_CLEAR_FEATURE; + SetupPacket.wValue = usFeature; + SetupPacket.wIndex = ucPort; + SetupPacket.wLength = 0; + + // + // Send the request. + // + USBHCDControlTransfer(0, + &SetupPacket, + pDevice, + 0, + 0, + pDevice->DeviceDescriptor.bMaxPacketSize0); +} + +//***************************************************************************** +// +// This function is used to retrieve the current status of a port on the +// hub. +// +// \param ulInstance is the hub device instance. +// \param ucPort is the port number for this request. +// \param pusPortStatus is a pointer to the memory to store the current status +// of the port. +// \param pusPortChange is a pointer to the memory to store the current change +// status of the ports. +// +// This function is used to retrieve the current overall status and change +// status for the port given in the \e ucPort parameter. The \e ucPort value +// indicates which port number to send this request to and can range from 0 to +// the number of valid ports on the given hub. A \e ucPort value of 0 is an +// access to the hub itself and not one of the hub ports. +// +// \return None. +// +//***************************************************************************** +static tBoolean +HubGetPortStatus(unsigned long ulInstance, unsigned char ucPort, + unsigned short *pusPortStatus, unsigned short *pusPortChange) +{ + unsigned long ulData, ulRead; + tUSBRequest SetupPacket; + tHubInstance *pHubInstance; + tUSBHostDevice *pDevice; + + // + // Retrieve the hub instance and device pointer. + // + pHubInstance = (tHubInstance *)ulInstance; + pDevice = pHubInstance->pDevice; + + // + // This is a standard OUT request. + // + SetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | + USB_RTYPE_OTHER; + + // + // Set the fields to get the hub status. + // + SetupPacket.bRequest = USBREQ_GET_STATUS; + SetupPacket.wValue = 0; + SetupPacket.wIndex = (unsigned short)ucPort; + SetupPacket.wLength = 4; + + // + // Send the request. + // + ulRead = USBHCDControlTransfer(0, &SetupPacket, + pDevice, + (unsigned char *)&ulData, 4, + pDevice->DeviceDescriptor.bMaxPacketSize0); + + // + // Check that we received the correct number of bytes. + // + if(ulRead != 4) + { + return(false); + } + else + { + // + // We got 4 bytes from the device. Now translate these into the 2 + // unsigned shorts we pass back to the caller. + // + *pusPortStatus = (unsigned short)(ulData & 0xFFFF); + *pusPortChange = (unsigned short)(ulData >> 16); + + DEBUG_OUTPUT("Port %d, status 0x%04x, change 0x%04x\n", ucPort, + *pusPortStatus, *pusPortChange); + } + + // + // All is well. + // + return(true); +} + +//***************************************************************************** +// +// This function handles callbacks for the interrupt IN endpoint for the hub +// device. +// +//***************************************************************************** +static void +HubIntINCallback(unsigned long ulPipe, unsigned long ulEvent) +{ + switch (ulEvent) + { + // + // Handles a request to schedule a new request on the interrupt IN + // pipe. + // + case USB_EVENT_SCHEDULER: + { + // + // Set things up to read the next change indication from the hub. + // + USBHCDPipeSchedule(ulPipe, (unsigned char *)&g_ulHubChanges, + (unsigned long)g_pRootHub->ucReportSize); + break; + } + + // + // Called when new data is available on the interrupt IN pipe. + // + case USB_EVENT_RX_AVAILABLE: + { + // + // For data transfers on INT IN endpoints, we need to acknowledge + // the data from this callback. + // + USBHCDPipeDataAck(ulPipe); + + // + // Update our global "ports needing service" flags with the latest + // information we've just received. + // + g_ulChangeFlags |= g_ulHubChanges; + + // + // Send the report data to the USB host hub device class driver if + // we have been given a callback function. + // + if(g_pRootHub->pfnCallback) + { + g_pRootHub->pfnCallback((void *)g_pRootHub->ulCBData, + USB_EVENT_RX_AVAILABLE, + ulPipe, + &g_ulHubChanges); + } + + break; + } + case USB_EVENT_ERROR: + { + break; + } + } +} + +//***************************************************************************** +// +// Query the class-specific hub descriptor. +// +//***************************************************************************** +static tBoolean +GetHubDescriptor(tUsbHubDescriptor *psDesc) +{ + unsigned long ulRead; + tUSBRequest SetupPacket; + tUSBHostDevice *pDevice; + + // + // Retrieve the device pointer. + // + pDevice = g_pRootHub->pDevice; + + // + // This is a standard OUT request. + // + SetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | + USB_RTYPE_DEVICE; + + // + // Set the fields to get the hub descriptor. Initially, we request only + // the first 4 bytes of the descriptor. This will give us the size which + // we use to determine how many bytes to read to get the full descriptor. + // This is necessary since we don't know how many ports the hub can support + // and we only support up to MAX_USB_DEVICES. + // + SetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + SetupPacket.wValue = (USB_DTYPE_HUB << 8); + SetupPacket.wIndex = 0; + SetupPacket.wLength = sizeof(tUsbHubDescriptor); + + // + // Send the request. + // + ulRead = USBHCDControlTransfer(0, &SetupPacket, + pDevice, + (void *)psDesc, sizeof(tUsbHubDescriptor), + pDevice->DeviceDescriptor.bMaxPacketSize0); + + // + // Make sure we got at least some data. + // + if(ulRead == 0) + { + return(false); + } + + // + // All is well. + // + return(true); +} + +//***************************************************************************** +// +// Open an instance of the hub driver. This is called when the USB host +// has enumerated a new hub device. +// +//***************************************************************************** +static void * +HubDriverOpen(tUSBHostDevice *pDevice) +{ + tEndpointDescriptor *pEndpointDescriptor; + tInterfaceDescriptor *pInterface; + tUsbHubDescriptor sHubDesc; + tBoolean bRetcode; + unsigned long ulLoop; + + // + // If we are already talking to a hub, fail the call. We only support + // a single hub. + // + if(g_pRootHub->bHubActive) + { + return(0); + } + + // + // Get pointers to the device descriptors we need to look at. + // + pInterface = USBDescGetInterface(pDevice->pConfigDescriptor, 0, 0); + pEndpointDescriptor = USBDescGetInterfaceEndpoint(pInterface, 0, + pDevice->ulConfigDescriptorSize); + + // + // If there are no endpoints, something is wrong since a hub must have + // a single INT endpoint for signaling. + // + if(pEndpointDescriptor == 0) + { + return 0; + } + + // + // Make sure we really are talking to a hub. + // + if((pInterface->bInterfaceClass != USB_CLASS_HUB) || + pInterface -> bInterfaceSubClass || pInterface -> bInterfaceProtocol) + { + // + // Something is wrong - this isn't a hub or, if it is, we don't + // understand the protocol it is using. + // + return(0); + } + + // + // Remember the device information for later. + // + g_pRootHub->pDevice = pDevice; + + // + // A hub must support an interrupt endpoint so check this. + // + if((pEndpointDescriptor->bmAttributes & USB_EP_ATTR_TYPE_M) == + USB_EP_ATTR_INT) + { + // + // The endpoint is the correct type. Is it an IN endpoint? + // + if(pEndpointDescriptor->bEndpointAddress & USB_EP_DESC_IN) + { + // + // Yes - all is well with the hub endpoint so allocate a pipe to + // handle traffic from the hub. + // + g_pRootHub->ulIntInPipe = USBHCDPipeAlloc(0,USBHCD_PIPE_INTR_IN, + pDevice, + HubIntINCallback); + USBHCDPipeConfig(g_pRootHub->ulIntInPipe, + pEndpointDescriptor->wMaxPacketSize, + pEndpointDescriptor->bInterval, + pEndpointDescriptor->bEndpointAddress & + USB_EP_DESC_NUM_M); + } + } + + // + // Did we allocate the endpoint successfully? + // + if(!g_pRootHub->ulIntInPipe) + { + // + // No - return an error. + // + return 0; + } + + // + // Assuming we have a callback, call it to tell the owner that a hub is + // now connected. + // + if(g_pRootHub->pfnCallback != 0) + { + g_pRootHub->pfnCallback((void *)g_pRootHub->ulCBData, + USB_EVENT_CONNECTED, + (unsigned long)g_pRootHub, 0); + } + + // + // Get the hub descriptor and store information we'll need for later. + // + bRetcode = GetHubDescriptor(&sHubDesc); + if(bRetcode) + { + + // + // We read the descriptor successfully so extract the parts we need. + // + g_pRootHub->ucNumPorts = sHubDesc.bNbrPorts; + g_pRootHub->usHubCharacteristics = sHubDesc.wHubCharacteristics; + g_pRootHub->ucNumPortsInUse = (sHubDesc.bNbrPorts > MAX_USB_DEVICES) ? + MAX_USB_DEVICES : sHubDesc.bNbrPorts; + + // + // The size of the status change report that the hub sends is dependent + // upon the number of ports that the hub supports. Calculate this by + // adding 1 to the number of ports (bit 0 of the report is the hub + // status, higher bits are one per port) then dividing by 8 (bits per + // byte) and rounding up. + // + g_pRootHub->ucReportSize = ((sHubDesc.bNbrPorts + 1) + 7) / 8; + + // + // Enable power to all ports on the hub. + // + for(ulLoop = 1; ulLoop <= sHubDesc.bNbrPorts; ulLoop++) + { + // + // Turn on power to this port. + // + HubSetPortFeature((unsigned long )g_pRootHub, ulLoop, + HUB_FEATURE_PORT_POWER); + } + + // + // Clear out our port state structures. + // + for(ulLoop = 0; ulLoop < MAX_USB_DEVICES; ulLoop++) + { + g_pRootHub->psPorts[ulLoop].bChanged = false; + g_pRootHub->psPorts[ulLoop].sState = PORT_IDLE; + } + } + else + { + // + // Oops - we can't read the hub descriptor! Tidy up and return + // an error. + // + USBHCDPipeFree(g_pRootHub->ulIntInPipe); + g_pRootHub->pfnCallback = 0; + g_pRootHub->bHubActive = false; + return(0); + } + + // + // If we get here, all is well so remember that the hub is connected and + // active. + // + g_pRootHub->bHubActive = true; + + // + // Return our instance data pointer to the caller to use as a handle. + // + return((void *)g_pRootHub); +} + +//***************************************************************************** +// +// Close an instance of the hub driver. +// +//***************************************************************************** +static void +HubDriverClose(void *pvInstance) +{ + unsigned long ulLoop; + + // + // No device so just exit. + // + if(g_pRootHub->pDevice == 0) + { + return; + } + + // + // Disconnect any devices that are currently connected to the hub. + // + for(ulLoop = 0; ulLoop < MAX_USB_DEVICES; ulLoop++) + { + // + // Does this port have a device connected to it that we have previously + // reported to the host control layer? + // + if((g_pRootHub->psPorts[ulLoop].sState == PORT_ACTIVE) || + (g_pRootHub->psPorts[ulLoop].sState == PORT_RESET_WAIT) || + (g_pRootHub->psPorts[ulLoop].sState == PORT_ENUMERATED) || + (g_pRootHub->psPorts[ulLoop].sState == PORT_ERROR)) + { + // + // Yes - tell the host controller to disconnect the device. + // + USBHCDHubDeviceDisconnected(0, + g_pRootHub->psPorts[ulLoop].ulDevHandle); + + } + + // + // Make sure that the state returns to idle. + // + g_pRootHub->psPorts[ulLoop].sState = PORT_IDLE; + + } + + // + // Reset the device pointer. + // + g_pRootHub->pDevice = 0; + + // + // Mark the hub as absent. + // + g_pRootHub->bHubActive = false; + + // + // Note that we are not in the middle of enumerating anything. + // + g_pRootHub->bEnumerationBusy = false; + + // + // Free the Interrupt IN pipe. + // + if(g_pRootHub->ulIntInPipe != 0) + { + USBHCDPipeFree(g_pRootHub->ulIntInPipe); + } + + // + // If the callback exists, call it with a DISCONNECTED event. + // + if(g_pRootHub->pfnCallback != 0) + { + g_pRootHub->pfnCallback((void *)g_pRootHub->ulCBData, + USB_EVENT_DISCONNECTED, + (unsigned long)g_pRootHub, 0); + } +} + +//***************************************************************************** +// +// Perform any processing required as a result of a change in the reset +// signaling for a given port. +// +//***************************************************************************** +static void +HubDriverReset(unsigned char ucPort, tBoolean bResetActive) +{ + // + // Did the reset sequence end or begin? + // + if(!bResetActive) + { + // + // The reset ended. Now wait for at least 10ms before signaling + // USB enumeration code that a new device is waiting to be enumerated. + // + g_pRootHub->psPorts[ucPort].sState = PORT_RESET_WAIT; + + // + // Set the wait to 10ms (10 frames) from now. + // + g_pRootHub->psPorts[ucPort].ulCount = 10; + } + else + { + // + // Was this device previously active? + // + if(g_pRootHub->psPorts[ucPort].sState == PORT_ACTIVE) + { + USBHCDHubDeviceDisconnected(0, + g_pRootHub->psPorts[ucPort].ulDevHandle); + } + + // + // The reset is active so mark our port as in reset. + // + g_pRootHub->psPorts[ucPort].sState = PORT_RESET_ACTIVE; + } +} + +//***************************************************************************** +// +// Start the process of enumerating a new device by issuing a reset to the +// appropriate downstream port. +// +//***************************************************************************** +static void +HubDriverDeviceReset(unsigned char ucPort) +{ + DEBUG_OUTPUT("Starting enumeration for port %d\n", ucPort); + + // + // Record the fact that we are in the process of enumerating a device. + // + g_pRootHub->bEnumerationBusy = true; + + // + // Save the port that is being enumerated. + // + g_pRootHub->ucEnumIdx = ucPort; + + // + // Mark the port as being reset. + // + g_pRootHub->psPorts[ucPort].sState = PORT_RESET_ACTIVE; + + // + // Initiate a reset on the relevant port to start the enumeration process. + // + HubSetPortFeature((unsigned long)g_pRootHub, + ucPort, + HUB_FEATURE_PORT_RESET); +} + +//***************************************************************************** +// +// A new device has been connected to the hub. Allocate resources to manage +// it and pass details back to the main USB host enumeration code to have the +// device enumerated. +// +//***************************************************************************** +static void +HubDriverDeviceConnect(unsigned char ucPort, tBoolean bLowSpeed) +{ + DEBUG_OUTPUT("HubDriverDeviceConnect\n"); + + // + // We've allocated a port table entry so fill it in then initiate a reset + // on the device. + // + g_pRootHub->psPorts[ucPort].bChanged = false; + g_pRootHub->psPorts[ucPort].bLowSpeed = bLowSpeed; + + // + // Mark the port as having a device present but not enumerated. + // + DEBUG_OUTPUT("Deferring enumeration for port %d\n", ucPort); + g_pRootHub->psPorts[ucPort].sState = PORT_CONNECTED; + + // + // Wait 100ms to reset the device. + // + g_pRootHub->psPorts[ucPort].ulCount = 100; +} + +//***************************************************************************** +// +// An existing device has been removed from the hub. Tidy up and let the main +// USB host code know so that it can free device resources. +// +//***************************************************************************** +static void +HubDriverDeviceDisconnect(unsigned char ucPort) +{ + // + // This is a device we are currently managing. Have we already informed + // the host controller that it is present? + // + if((g_pRootHub->psPorts[ucPort].sState == PORT_ACTIVE) || + (g_pRootHub->psPorts[ucPort].sState == PORT_RESET_WAIT) || + (g_pRootHub->psPorts[ucPort].sState == PORT_ENUMERATED) || + (g_pRootHub->psPorts[ucPort].sState == PORT_ERROR)) + { + // + // Yes - tell the host controller that the device is not longer + // connected. + // + USBHCDHubDeviceDisconnected(0, g_pRootHub->psPorts[ucPort].ulDevHandle); + } + + // + // If the device was being enumerated, make sure we clear the flag + // indicating that an enumeration is still ongoing. + // + if((g_pRootHub->psPorts[ucPort].sState == PORT_RESET_ACTIVE) || + (g_pRootHub->psPorts[ucPort].sState == PORT_RESET_WAIT) || + (g_pRootHub->psPorts[ucPort].sState == PORT_ACTIVE)) + { + g_pRootHub->bEnumerationBusy = false; + } + + // + // Free up the port state structure. + // + g_pRootHub->psPorts[ucPort].sState = PORT_IDLE; +} + +//***************************************************************************** +// +// This function is called periodically by USBHCDMain(). We use it to handle +// the hub port state machine. +// +//***************************************************************************** +void +USBHHubMain(void) +{ + unsigned short usStatus, usChanged; + unsigned char ucPort; + tBoolean bRetcode; + + // + // If the hub isn't present, just return. + // + if((g_pRootHub == 0) || (!g_pRootHub->bHubActive)) + { + return; + } + + // + // Initialize the status variables. + // + usStatus = 0; + usChanged = 0; + + // + // The hub is active and something changed. Check to see which port changed + // state and handle as necessary. + // + for(ucPort = 0; ucPort <= g_pRootHub->ucNumPortsInUse; ucPort++) + { + // + // Decrement any wait counter if there is one present. + // + if(g_pRootHub->psPorts[ucPort].ulCount != 0) + { + g_pRootHub->psPorts[ucPort].ulCount--; + } + + // + // Is this port waiting to be enumerated and is the last device + // enumeration finished? + // + if((g_pRootHub->psPorts[ucPort].sState == PORT_CONNECTED) && + (!g_pRootHub->bEnumerationBusy) && + (g_pRootHub->psPorts[ucPort].ulCount == 0)) + { + // + // Yes - start the enumeration processing for this device. + // + HubDriverDeviceReset(ucPort); + } + + // + // If the state is PORT_RESET_WAIT then the hub is waiting before + // accessing device as the USB 2.0 specification requires. + // + if((g_pRootHub->psPorts[ucPort].sState == PORT_RESET_WAIT) && + (g_pRootHub->psPorts[ucPort].ulCount == 0)) + { + // + // Start the enumeration process if the timeout has passed and + // the hub is waiting to start enumerating the device. + // + g_pRootHub->psPorts[ucPort].sState = PORT_ACTIVE; + + // + // Call the main host controller layer to have it enumerate the newly + // connected device. + // + g_pRootHub->psPorts[ucPort].ulDevHandle = + USBHCDHubDeviceConnected(0, 1, ucPort, + g_pRootHub->psPorts[ucPort].bLowSpeed, + g_pRootHub->psPorts[ucPort].pucConfigDesc, + g_pRootHub->psPorts[ucPort].ulConfigSize); + } + + // + // If an enumeration is in progress and the loop is not on the port + // being enumerated then skip the port. + // + if(g_pRootHub->bEnumerationBusy && (g_pRootHub->ucEnumIdx != ucPort)) + { + continue; + } + + // + // Did something change for this particular port? + // + if(g_ulChangeFlags & (1 << ucPort)) + { + // + // Yes - query the port status. + // + bRetcode = HubGetPortStatus((unsigned long)g_pRootHub, ucPort, + &usStatus, &usChanged); + + // + // Clear this change with the USB interrupt temporarily disabled to + // ensure that we do not clear a flag that the interrupt routine + // has just set. + // + OS_INT_DISABLE(INT_USB0); + g_ulChangeFlags &= ~(1 << ucPort); + OS_INT_ENABLE(INT_USB0); + + // + // If there was an error, go on and look at the next bit. + // + if(!bRetcode) + { + continue; + } + + // + // Now consider what changed and handle it as necessary. + // + + // + // Was a device connected to or disconnected from the port? + // + if(usChanged & HUB_PORT_CHANGE_DEVICE_PRESENT) + { + DEBUG_OUTPUT("Connection change on port %d\n", ucPort); + + // + // Clear the condition. + // + HubClearPortFeature((unsigned long)g_pRootHub, ucPort, + HUB_FEATURE_C_PORT_CONNECTION); + + // + // Was a device connected or disconnected? + // + if(usStatus & HUB_PORT_STATUS_DEVICE_PRESENT) + { + DEBUG_OUTPUT("Connected\n"); + + // + // A device was connected. + // + HubDriverDeviceConnect(ucPort, + ((usStatus & HUB_PORT_STATUS_LOW_SPEED) ? + true : false)); + } + else + { + DEBUG_OUTPUT("Disconnected\n"); + + // + // A device was disconnected. + // + HubDriverDeviceDisconnect(ucPort); + } + } + + // + // Did a reset on the port complete? + // + if(usChanged & HUB_PORT_CHANGE_RESET) + { + // + // Clear the condition. + // + HubClearPortFeature((unsigned long)g_pRootHub, ucPort, + HUB_FEATURE_C_PORT_RESET); + + // + // Yes - query the port status. + // + bRetcode = HubGetPortStatus((unsigned long)g_pRootHub, ucPort, + &usStatus, &usChanged); + + DEBUG_OUTPUT("Reset %s for port %d\n", + ((usStatus & HUB_PORT_STATUS_RESET) ? "asserted" : + "deasserted"), ucPort); + + // + // Handle the reset case. + // + HubDriverReset(ucPort, (usStatus & HUB_PORT_STATUS_RESET) ? + true : false); + } + + // + // Did an over-current reset on the port complete? + // + if(usChanged & HUB_PORT_CHANGE_OVER_CURRENT) + { + DEBUG_OUTPUT("Port %d over current.\n", ucPort); + + // + // Currently we ignore this and just clear the condition. + // + HubClearPortFeature((unsigned long)g_pRootHub, ucPort, + HUB_FEATURE_C_PORT_OVER_CURRENT); + } + + // + // Has the port been enabled or disabled? + // + if(usChanged & HUB_PORT_CHANGE_ENABLED) + { + DEBUG_OUTPUT("Enable change for port %d.\n", ucPort); + + // + // Currently we ignore this and just clear the condition. + // + HubClearPortFeature((unsigned long)g_pRootHub, ucPort, + HUB_FEATURE_C_PORT_ENABLE); + } + + // + // Has the port been suspended or resumed? + // + if(usChanged & HUB_PORT_CHANGE_SUSPENDED) + { + DEBUG_OUTPUT("Suspend change for port %d.\n", ucPort); + + // + // Currently we ignore this and just clear the condition. + // + HubClearPortFeature((unsigned long)g_pRootHub, ucPort, + HUB_FEATURE_C_PORT_SUSPEND); + } + } + } +} + +//***************************************************************************** +// +//! Informs the hub class driver that a downstream device has been enumerated. +//! +//! \param ucHub is the address of the hub to which the downstream device +//! is attached. +//! \param ucPort is the port on the hub to which the downstream device is +//! attached. +//! +//! This function is called by the host controller driver to inform the hub +//! class driver that a downstream device has been enumerated successfully. +//! The hub driver then moves on and continues enumeration of any other newly +//! connected devices. +//! +//! \return None. +// +//***************************************************************************** +void +USBHHubEnumerationComplete(unsigned char ucHub, unsigned char ucPort) +{ + DEBUG_OUTPUT("Enumeration complete for hub %d, port %d\n", ucHub, ucPort); + + // + // Record the fact that the device is up and running. + // + g_pRootHub->psPorts[ucPort].sState = PORT_ENUMERATED; + + // + // Clear the flag we use to defer further enumerations. This will cause + // the next connected device (if any) to start enumeration on the next + // call to USBHHubMain(). + // + g_pRootHub->bEnumerationBusy = false; +} + +//***************************************************************************** +// +//! Informs the hub class driver that a downstream device failed to enumerate. +//! +//! \param ucHub is the address of the hub to which the downstream device +//! is attached. +//! \param ucPort is the port on the hub to which the downstream device is +//! attached. +//! +//! This function is called by the host controller driver to inform the hub +//! class driver that an attempt to enumerate a downstream device has failed. +//! The hub driver then cleans up and continues enumeration of any other newly +//! connected devices. +//! +//! \return None. +// +//***************************************************************************** +void +USBHHubEnumerationError(unsigned char ucHub, unsigned char ucPort) +{ + DEBUG_OUTPUT("Enumeration error for hub %d, port %d\n", ucHub, ucPort); + + // + // Record the fact that the device is not working correctly. + // + g_pRootHub->psPorts[ucPort].sState = PORT_ERROR; + + // + // Clear the flag we use to defer further enumerations. This will cause + // the next connected device (if any) to start enumeration on the next + // call to USBHHubMain(). + // + g_pRootHub->bEnumerationBusy = false; +} + +//***************************************************************************** +// +//! This function is used to enable the host hub class driver before any +//! devices are present. +//! +//! \param pfnCallback is the driver call back for host hub events. +//! \param pucHubPool is the memory pool allocated to the USB hub class. +//! \param ulPoolSize is the size in bytes of the memory pool provided by the +//! \e pucHubPool parameter. +//! \param psHubInstance is a pointer to an instance of the private hub data. +//! \param ulNumHubs is the number of hubs to support. +//! +//! This function is called to open an instance of a host hub device and +//! provides a valid callback function for host hub events in the +//! \e pfnCallback parameter. This function must be called before the USB +//! host code can successfully enumerate a hub device or any devices attached +//! to the hub. The \e pucHubPool is memory provided to the hub class to +//! manage the devices that are connected to the hub. The \e ulPoolSize is +//! the number of bytes and should be at least 32 bytes per device including +//! the hub device itself. A simple formula for providing memory to the hub +//! class is \b MAX_USB_DEVICES * 32 bytes of data to allow for proper +//! enumeration of connected devices. The value for \b MAX_USB_DEVICES is +//! defined in the usblib.h file and controls the number of devices +//! supported by the USB library. The \e ulNumHubs parameter +//! defaults to one and only one buffer of size tHubInstance is required to +//! be passed in the \e psHubInstance parameter. +//! +//! \note Changing the value of \b MAX_USB_DEVICES requires a rebuild of the +//! USB library to have an effect on the library. +//! +//! \return This function returns the driver instance to use for the other +//! host hub functions. If there is no instance available at the time of +//! this call, this function returns zero. +// +//***************************************************************************** +unsigned long +USBHHubOpen(tUSBCallback pfnCallback, unsigned char *pucHubPool, + unsigned long ulPoolSize, tHubInstance *psHubInstance, + unsigned long ulNumHubs) +{ + unsigned long ulLoop, ulBlockSize; + + // + // Only one hub is supported. + // + if(g_pRootHub) + { + DEBUG_OUTPUT("USBHHubOpen failed - already connected.\n"); + return(0); + } + + // + // Save this instance. + // + g_pRootHub = psHubInstance; + + // + // Save the instance data for this device. + // + g_pRootHub->pfnCallback = pfnCallback; + + // + // Divide the pool up into blocks, one for each supported port. We make + // sure that each block is a multiple of 4 bytes. + // + ulBlockSize = (ulPoolSize / MAX_USB_DEVICES) & ~3; + for(ulLoop = 0; ulLoop < MAX_USB_DEVICES; ulLoop++) + { + g_pRootHub->psPorts[ulLoop].pucConfigDesc = (pucHubPool + + (ulLoop * ulBlockSize)); + g_pRootHub->psPorts[ulLoop].ulConfigSize = ulBlockSize; + } + + DEBUG_OUTPUT("USBHHubOpen completed.\n"); + + // + // Return the device instance pointer. + // + return((unsigned long)g_pRootHub); +} + +//***************************************************************************** +// +//! This function is used to release a hub device instance. +//! +//! \param ulInstance is the hub device instance that is to be released. +//! +//! This function is called when an instance of the hub device must be +//! released. This function is typically made in preparation for shutdown or a switch +//! to function as a USB device when in OTG mode. Following this call, the hub device is +//! no longer available, but it can be opened again using a call to +//! USBHHubOpen(). After calling USBHHubClose(), the host hub driver no +//! longer provides any callbacks or accepts calls to other hub driver APIs. +//! +//! \return None. +// +//***************************************************************************** +void +USBHHubClose(unsigned long ulInstance) +{ + // + // Forget the instance pointer. + // + g_pRootHub = 0; + + DEBUG_OUTPUT("USBHHubClose completed.\n"); +} + +//***************************************************************************** +// +// This function is used to initialize the Hub driver. This is an internal +// function that should not be called by the application. +// +//***************************************************************************** +void +USBHHubInit(void) +{ + // + // Initialize Hub state. + // + g_pRootHub = 0; + g_ulChangeFlags = 0; + g_ulHubChanges = 0; +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhhub.h b/usblib/host/usbhhub.h new file mode 100644 index 0000000..e79965e --- /dev/null +++ b/usblib/host/usbhhub.h @@ -0,0 +1,206 @@ +//***************************************************************************** +// +// usbhhub.h - This hold the host driver for hid class. +// +// Copyright (c) 2011-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. +// +//***************************************************************************** + +#ifndef __USBHHUB_H__ +#define __USBHHUB_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +extern const tUSBHostClassDriver g_USBHubClassDriver; + +//***************************************************************************** +// +//! The USB standard hub descriptor structure. Full documentation for the +//! contents of this structure can be found in chapter 11.23.2.1 of the USB +//! 2.0 specification. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack(1) +#endif + +//***************************************************************************** +// +// The USB standard allows for up to 127 downstream ports on a single hub. This +// would require rather more memory than we would like to set aside so the +// default configuration of the hub driver supports hubs with up to 7 +// downstream-facing ports. In practice, this should be more than enough +// since this covers the vast majority of consumer hubs. Note that, by default, +// we will only support 4 devices so you can't fully populate a 7 port hub and +// have everything work. +// +// Feel free to change this but bad things will happen if you increase it above +// 31 since we assume the reports will always fit inside a 4 byte buffer. +// +//***************************************************************************** +#define ROOT_HUB_MAX_PORTS 7 + +typedef struct +{ + // + //! The total number of bytes in the descriptor (including this field). + // + unsigned char bLength; + + // + //! The descriptor type. For a hub descriptor, this will be USB_DTYPE_HUB + //! (0x29 or 41 decimal). + // + unsigned char bDescType; + + // + //! The number of downstream-facing ports that the hub supports. + // + unsigned char bNbrPorts; + + // + //! Characteristics of the hub device including its power switching + //! capabilities and overcurrent protection mode. + // + unsigned short wHubCharacteristics; + + // + //! The time between the start of the power-on sequence for a port and + //! the power to the port becoming stable. This is expressed in 2mS units. + // + unsigned char bPwrOn2PwrGood; + + // + //! The maximum current requirement for the hub circuitry in mA. + // + unsigned char bHubContrCurrent; + + // + //! The last two fields in the structure are bit masks indicating which + //! downstream ports support removable devices and, following this, another + //! obsolete field from USB1.0 related to port power control. Each field + //! is byte aligned and contains a bit for each hub port. This structure + //! definition is set up with enough storage to handle ROOT_HUB_MAX_PORTS + //! ports but beware that the actual size of each field is dependent upon + //! the bNbrPorts field above. + // + unsigned char PortInfo[((ROOT_HUB_MAX_PORTS + 7) / 8) * 2]; +} +PACKED tUsbHubDescriptor; + +#ifdef ewarm +#pragma pack() +#endif + +//***************************************************************************** +// +// Values used as the usFeature parameter to USBHHubClearHubFeature. +// +//***************************************************************************** +#define HUB_FEATURE_C_HUB_LOCAL_POWER 0 +#define HUB_FEATURE_C_HUB_OVER_CURRENT 1 + +//***************************************************************************** +// +// Values used as the usFeature parameter to USBHHubSetPortFeature and +// USBHHubClearPortFeature. +// +//***************************************************************************** +#define HUB_FEATURE_PORT_CONNECTION 0 +#define HUB_FEATURE_PORT_ENABLE 1 +#define HUB_FEATURE_PORT_SUSPEND 2 +#define HUB_FEATURE_PORT_OVER_CURRENT 3 +#define HUB_FEATURE_PORT_RESET 4 +#define HUB_FEATURE_PORT_POWER 8 +#define HUB_FEATURE_PORT_LOW_SPEED 9 +#define HUB_FEATURE_C_PORT_CONNECTION 16 +#define HUB_FEATURE_C_PORT_ENABLE 17 +#define HUB_FEATURE_C_PORT_SUSPEND 18 +#define HUB_FEATURE_C_PORT_OVER_CURRENT 19 +#define HUB_FEATURE_C_PORT_RESET 20 +#define HUB_FEATURE_PORT_TEST 21 +#define HUB_FEATURE_PORT_INDICATOR 22 + +//***************************************************************************** +// +// Values returned via the *pusHubStatus and *pusHubChange parameters passed to +// USBHHubGetHubStatus. These may be ORed together into the returned status +// value. +// +//***************************************************************************** +#define HUB_STATUS_PWR_LOST 1 +#define HUB_STATUS_OVER_CURRENT 2 + +//***************************************************************************** +// +// Values returned via the *pusPortStatus parameter passed to +// USBHHubGetPortStatus. These may be ORed together into the returned status +// value. +// +//***************************************************************************** +#define HUB_PORT_STATUS_DEVICE_PRESENT 0x0001 +#define HUB_PORT_STATUS_ENABLED 0x0002 +#define HUB_PORT_STATUS_SUSPENDED 0x0004 +#define HUB_PORT_STATUS_OVER_CURRENT 0x0008 +#define HUB_PORT_STATUS_RESET 0x0010 +#define HUB_PORT_STATUS_POWERED 0x0100 +#define HUB_PORT_STATUS_LOW_SPEED 0x0200 +#define HUB_PORT_STATUS_HIGH_SPEED 0x0400 +#define HUB_PORT_STATUS_TEST_MODE 0x0800 +#define HUB_PORT_STATUS_INDICATOR_CONTROL 0x1000 + +//***************************************************************************** +// +// Values returned via the *pusPortChange parameter passed to +// USBHHubGetPortStatus. These may be ORed together into the returned status +// value. +// +//***************************************************************************** +#define HUB_PORT_CHANGE_DEVICE_PRESENT 0x0001 +#define HUB_PORT_CHANGE_ENABLED 0x0002 +#define HUB_PORT_CHANGE_SUSPENDED 0x0004 +#define HUB_PORT_CHANGE_OVER_CURRENT 0x0008 +#define HUB_PORT_CHANGE_RESET 0x0010 + +//***************************************************************************** +// +// Public function prototypes for the HUB class driver. +// +//***************************************************************************** +extern unsigned long USBHHubOpen(tUSBCallback pfnCallback, + unsigned char *pucHubPool, + unsigned long ulPoolSize, + tHubInstance *psHubInstance, + unsigned long ulNumHubs); +extern void USBHHubClose(unsigned long ulInstance); + +#ifdef __cplusplus +} +#endif + +#endif // __USBHHUB_H__ diff --git a/usblib/host/usbhmsc.c b/usblib/host/usbhmsc.c new file mode 100644 index 0000000..7a2756e --- /dev/null +++ b/usblib/host/usbhmsc.c @@ -0,0 +1,713 @@ +//***************************************************************************** +// +// usbhmsc.c - USB MSC host driver. +// +// 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_types.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usbmsc.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhmsc.h" +#include "usblib/host/usbhscsi.h" + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Forward declarations for the driver open and close calls. +// +//***************************************************************************** +static void *USBHMSCOpen(tUSBHostDevice *pDevice); +static void USBHMSCClose(void *pvInstance); + +//***************************************************************************** +// +// This is the structure for an instance of a USB MSC host driver. +// +//***************************************************************************** +typedef struct +{ + // + // Save the device instance. + // + tUSBHostDevice *pDevice; + + // + // Used to save the callback. + // + tUSBHMSCCallback pfnCallback; + + // + // The Maximum LUNs + // + unsigned long ulMaxLUN; + + // + // The total number of blocks associated with this device. + // + unsigned long ulNumBlocks; + + // + // The size of the blocks associated with this device. + // + unsigned long ulBlockSize; + + // + // Bulk IN pipe. + // + unsigned long ulBulkInPipe; + + // + // Bulk OUT pipe. + // + unsigned long ulBulkOutPipe; +} +tUSBHMSCInstance; + +//***************************************************************************** +// +// The array of USB MSC host drivers. +// +//***************************************************************************** +static tUSBHMSCInstance g_USBHMSCDevice = +{ + 0 +}; + +//***************************************************************************** +// +//! This constant global structure defines the Mass Storage Class Driver that +//! is provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_USBHostMSCClassDriver = +{ + USB_CLASS_MASS_STORAGE, + USBHMSCOpen, + USBHMSCClose, + 0 +}; + +//***************************************************************************** +// +//! This function is used to open an instance of the MSC driver. +//! +//! \param pDevice is a pointer to the device information structure. +//! +//! This function will attempt to open an instance of the MSC driver based on +//! the information contained in the pDevice structure. This call can fail if +//! there are not sufficient resources to open the device. The function will +//! return a value that should be passed back into USBMSCClose() when the +//! driver is no longer needed. +//! +//! \return The function will return a pointer to a MSC driver instance. +// +//***************************************************************************** +static void * +USBHMSCOpen(tUSBHostDevice *pDevice) +{ + long lIdx; + tEndpointDescriptor *pEndpointDescriptor; + tInterfaceDescriptor *pInterface; + + // + // Don't allow the device to be opened without closing first. + // + if(g_USBHMSCDevice.pDevice) + { + return(0); + } + + // + // Save the device pointer. + // + g_USBHMSCDevice.pDevice = pDevice; + + // + // Get the interface descriptor. + // + pInterface = USBDescGetInterface(pDevice->pConfigDescriptor, 0, 0); + + // + // Loop through the endpoints of the device. + // + for(lIdx = 0; lIdx < 3; lIdx++) + { + // + // Get the first endpoint descriptor. + // + pEndpointDescriptor = + USBDescGetInterfaceEndpoint(pInterface, lIdx, + pDevice->ulConfigDescriptorSize); + + // + // If no more endpoints then break out. + // + if(pEndpointDescriptor == 0) + { + break; + } + + // + // See if this is a bulk endpoint. + // + if((pEndpointDescriptor->bmAttributes & USB_EP_ATTR_TYPE_M) == + USB_EP_ATTR_BULK) + { + // + // See if this is bulk IN or bulk OUT. + // + if(pEndpointDescriptor->bEndpointAddress & USB_EP_DESC_IN) + { + // + // Allocate the USB Pipe for this Bulk IN endpoint. + // + g_USBHMSCDevice.ulBulkInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_BULK_IN_DMA, + pDevice, + pEndpointDescriptor->wMaxPacketSize, + 0); + // + // Configure the USB pipe as a Bulk IN endpoint. + // + USBHCDPipeConfig(g_USBHMSCDevice.ulBulkInPipe, + pEndpointDescriptor->wMaxPacketSize, + 0, + (pEndpointDescriptor->bEndpointAddress & + USB_EP_DESC_NUM_M)); + } + else + { + // + // Allocate the USB Pipe for this Bulk OUT endpoint. + // + g_USBHMSCDevice.ulBulkOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_BULK_OUT_DMA, + pDevice, + pEndpointDescriptor->wMaxPacketSize, + 0); + // + // Configure the USB pipe as a Bulk OUT endpoint. + // + USBHCDPipeConfig(g_USBHMSCDevice.ulBulkOutPipe, + pEndpointDescriptor->wMaxPacketSize, + 0, + (pEndpointDescriptor->bEndpointAddress & + USB_EP_DESC_NUM_M)); + } + } + } + + // + // If the callback exists, call it with an Open event. + // + if(g_USBHMSCDevice.pfnCallback != 0) + { + g_USBHMSCDevice.pfnCallback((unsigned long)&g_USBHMSCDevice, + MSC_EVENT_OPEN, 0); + } + + + g_USBHMSCDevice.ulMaxLUN = 0xffffffff; + + // + // Return the only instance of this device. + // + return(&g_USBHMSCDevice); +} + +//***************************************************************************** +// +//! This function is used to release an instance of the MSC driver. +//! +//! \param pvInstance is an instance pointer that needs to be released. +//! +//! This function will free up any resources in use by the MSC driver instance +//! that is passed in. The \e pvInstance pointer should be a valid value that +//! was returned from a call to USBMSCOpen(). +//! +//! \return None. +// +//***************************************************************************** +static void +USBHMSCClose(void *pvInstance) +{ + // + // Do nothing if there is not a driver open. + // + if(g_USBHMSCDevice.pDevice == 0) + { + return; + } + + // + // Reset the device pointer. + // + g_USBHMSCDevice.pDevice = 0; + + // + // Free the Bulk IN pipe. + // + if(g_USBHMSCDevice.ulBulkInPipe != 0) + { + USBHCDPipeFree(g_USBHMSCDevice.ulBulkInPipe); + } + + // + // Free the Bulk OUT pipe. + // + if(g_USBHMSCDevice.ulBulkOutPipe != 0) + { + USBHCDPipeFree(g_USBHMSCDevice.ulBulkOutPipe); + } + + // + // If the callback exists then call it. + // + if(g_USBHMSCDevice.pfnCallback != 0) + { + g_USBHMSCDevice.pfnCallback((unsigned long)&g_USBHMSCDevice, + MSC_EVENT_CLOSE, 0); + } +} + +//***************************************************************************** +// +//! This function retrieves the maximum number of the logical units on a +//! mass storage device. +//! +//! \param pDevice is the device instance pointer for this request. +//! \param ulInterface is the interface number on the device specified by the +//! \e ulAddress parameter. +//! \param pucMaxLUN is the byte value returned from the device for the +//! device's maximum logical unit. +//! +//! The device will return one byte of data that contains the maximum LUN +//! supported by the device. For example, if the device supports four LUNs +//! then the LUNs would be numbered from 0 to 3 and the return value would be +//! 3. If no LUN is associated with the device, the value returned shall be 0. +//! +//! \return None. +// +//***************************************************************************** +static void +USBHMSCGetMaxLUN(tUSBHostDevice *pDevice, unsigned long ulInterface, + unsigned char *pucMaxLUN) +{ + tUSBRequest SetupPacket; + + // + // This is a Class specific interface IN request. + // + SetupPacket.bmRequestType = + USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | USB_RTYPE_INTERFACE; + + // + // Request a the Max LUN for this interface. + // + SetupPacket.bRequest = USBREQ_GET_MAX_LUN; + SetupPacket.wValue = 0; + + // + // Indicate the interface to use. + // + SetupPacket.wIndex = (unsigned short)ulInterface; + + // + // Only request a single byte of data. + // + SetupPacket.wLength = 1; + + // + // Put the setup packet in the buffer and send the command. + // + if(USBHCDControlTransfer(0, &SetupPacket, pDevice, pucMaxLUN, 1, + MAX_PACKET_SIZE_EP0) != 1) + { + *pucMaxLUN = 0; + } +} + +//***************************************************************************** +// +//! This function checks if a drive is ready to be accessed. +//! +//! \param ulInstance is the device instance to use for this read. +//! +//! This function checks if the current device is ready to be accessed. +//! It uses the \e ulInstance parameter to determine which device to check and +//! will return zero when the device is ready. Any non-zero return code +//! indicates that the device was not ready. +//! +//! \return This function will return zero if the device is ready and it will +//! return a other value if the device is not ready or if an error occurred. +// +//***************************************************************************** +long +USBHMSCDriveReady(unsigned long ulInstance) +{ + unsigned char ucMaxLUN, pBuffer[SCSI_INQUIRY_DATA_SZ]; + unsigned long ulSize; + tUSBHMSCInstance *pMSCDevice; + + // + // Get the instance pointer in a more usable form. + // + pMSCDevice = (tUSBHMSCInstance *)ulInstance; + + // + // If there is no device present then return an error. + // + if(pMSCDevice->pDevice == 0) + { + return(-1); + } + + // + // Only request the maximum number of LUNs once. + // + if(g_USBHMSCDevice.ulMaxLUN == 0xffffffff) + { + // + // Get the Maximum LUNs on this device. + // + USBHMSCGetMaxLUN(g_USBHMSCDevice.pDevice, + g_USBHMSCDevice.pDevice->ulInterface, &ucMaxLUN); + + // + // Save the Maximum number of LUNs on this device. + // + g_USBHMSCDevice.ulMaxLUN = ucMaxLUN; + } + + // + // Just return if the device is returning not present. + // + ulSize = SCSI_REQUEST_SENSE_SZ; + if(USBHSCSIRequestSense(pMSCDevice->ulBulkInPipe, pMSCDevice->ulBulkOutPipe, + pBuffer, &ulSize) != SCSI_CMD_STATUS_PASS) + { + return(-1); + } + + if((pBuffer[SCSI_RS_SKEY] == SCSI_RS_KEY_UNIT_ATTN) && + (pBuffer[SCSI_RS_SKEY_AD_SKEY] == SCSI_RS_KEY_NOTPRSNT)) + { + return(-1); + } + + // + // Issue a SCSI Inquiry to get basic information on the device + // + ulSize = SCSI_INQUIRY_DATA_SZ; + if((USBHSCSIInquiry(pMSCDevice->ulBulkInPipe, pMSCDevice->ulBulkOutPipe, + pBuffer, &ulSize) != SCSI_CMD_STATUS_PASS)) + { + return(-1); + } + + // + // Get the size of the drive. + // + ulSize = SCSI_INQUIRY_DATA_SZ; + if(USBHSCSIReadCapacity(pMSCDevice->ulBulkInPipe, pMSCDevice->ulBulkOutPipe, + pBuffer, &ulSize) != SCSI_CMD_STATUS_PASS) + { + // + // Get the current sense data from the device to see why it failed + // the Read Capacity command. + // + ulSize = SCSI_REQUEST_SENSE_SZ; + USBHSCSIRequestSense(pMSCDevice->ulBulkInPipe, + pMSCDevice->ulBulkOutPipe, pBuffer, &ulSize); + + // + // If the read capacity failed then check if the drive is ready. + // + if(USBHSCSITestUnitReady(pMSCDevice->ulBulkInPipe, + pMSCDevice->ulBulkOutPipe) != SCSI_CMD_STATUS_PASS) + { + // + // Get the current sense data from the device to see why it failed + // the Test Unit Ready command. + // + ulSize = SCSI_REQUEST_SENSE_SZ; + USBHSCSIRequestSense(pMSCDevice->ulBulkInPipe, + pMSCDevice->ulBulkOutPipe, pBuffer, &ulSize); + } + + return(-1); + } + else + { + // + // Read the block size out, value is stored big endian. + // + pMSCDevice->ulBlockSize = + (pBuffer[7] | (pBuffer[6] << 8) | pBuffer[5] << 16 | + (pBuffer[4] << 24)); + + // + // Read the block size out. + // + pMSCDevice->ulNumBlocks = + (pBuffer[3] | (pBuffer[2] << 8) | pBuffer[1] << 16 | + (pBuffer[0] << 24)); + } + + // + // See if the drive is ready to use. + // + if(USBHSCSITestUnitReady(pMSCDevice->ulBulkInPipe, + pMSCDevice->ulBulkOutPipe) != SCSI_CMD_STATUS_PASS) + { + // + // Get the current sense data from the device to see why it failed + // the Test Unit Ready command. + // + ulSize = SCSI_REQUEST_SENSE_SZ; + USBHSCSIRequestSense(pMSCDevice->ulBulkInPipe, + pMSCDevice->ulBulkOutPipe, pBuffer, &ulSize); + + return(-1); + } + + // + // Success. + // + return(0); +} + +//***************************************************************************** +// +//! This function should be called before any devices are present to enable +//! the mass storage device class driver. +//! +//! \param ulDrive is the drive number to open. +//! \param pfnCallback is the driver callback for any mass storage events. +//! +//! This function is called to open an instance of a mass storage device. It +//! should be called before any devices are connected to allow for proper +//! notification of drive connection and disconnection. The \e ulDrive +//! parameter is a zero based index of the drives present in the system. +//! There are a constant number of drives, and this number should only +//! be greater than 0 if there is a USB hub present in the system. The +//! application should also provide the \e pfnCallback to be notified of mass +//! storage related events like device enumeration and device removal. +//! +//! \return This function will return the driver instance to use for the other +//! mass storage functions. If there is no driver available at the time of +//! this call, this function will return zero. +// +//***************************************************************************** +unsigned long +USBHMSCDriveOpen(unsigned long ulDrive, tUSBHMSCCallback pfnCallback) +{ + // + // Only the first drive is supported and only one callback is supported. + // + if((ulDrive != 0) || (g_USBHMSCDevice.pfnCallback)) + { + return(0); + } + + // + // Save the callback. + // + g_USBHMSCDevice.pfnCallback = pfnCallback; + + // + // Return the requested device instance. + // + return((unsigned long)&g_USBHMSCDevice); +} + +//***************************************************************************** +// +//! This function should be called to release a drive instance. +//! +//! \param ulInstance is the device instance that is to be released. +//! +//! This function is called when an MSC drive is to be released in preparation +//! for shutdown or a switch to USB device mode, for example. Following this +//! call, the drive is available for other clients who may open it again using +//! a call to USBHMSCDriveOpen(). +//! +//! \return None. +// +//***************************************************************************** +void +USBHMSCDriveClose(unsigned long ulInstance) +{ + tUSBHMSCInstance *pMSCDevice; + + // + // Get a pointer to the device instance data from the handle. + // + pMSCDevice = (tUSBHMSCInstance *)ulInstance; + + // + // Close the drive (if it is already open) + // + USBHMSCClose((void *)pMSCDevice); + + // + // Clear the callback indicating that the device is now closed. + // + pMSCDevice->pfnCallback = 0; +} + +//***************************************************************************** +// +//! This function performs a block read to an MSC device. +//! +//! \param ulInstance is the device instance to use for this read. +//! \param ulLBA is the logical block address to read on the device. +//! \param pucData is a pointer to the returned data buffer. +//! \param ulNumBlocks is the number of blocks to read from the device. +//! +//! This function will perform a block sized read from the device associated +//! with the \e ulInstance parameter. The \e ulLBA parameter specifies the +//! logical block address to read on the device. This function will only +//! perform \e ulNumBlocks block sized reads. In most cases this is a read +//! of 512 bytes of data. The \e *pucData buffer should be at least +//! \e ulNumBlocks * 512 bytes in size. +//! +//! \return The function returns zero for success and any negative value +//! indicates a failure. +// +//***************************************************************************** +long +USBHMSCBlockRead(unsigned long ulInstance, unsigned long ulLBA, + unsigned char *pucData, unsigned long ulNumBlocks) +{ + tUSBHMSCInstance *pMSCDevice; + unsigned long ulSize; + + // + // Get the instance pointer in a more usable form. + // + pMSCDevice = (tUSBHMSCInstance *)ulInstance; + + // + // If there is no device present then return an error. + // + if(pMSCDevice->pDevice == 0) + { + return(-1); + } + + // + // Calculate the actual byte size of the read. + // + ulSize = pMSCDevice->ulBlockSize * ulNumBlocks; + + // + // Perform the SCSI read command. + // + if(USBHSCSIRead10(pMSCDevice->ulBulkInPipe, pMSCDevice->ulBulkOutPipe, + ulLBA, pucData, &ulSize, + ulNumBlocks) != SCSI_CMD_STATUS_PASS) + { + return(-1); + } + + // + // Success. + // + return(0); +} + +//***************************************************************************** +// +//! This function performs a block write to an MSC device. +//! +//! \param ulInstance is the device instance to use for this write. +//! \param ulLBA is the logical block address to write on the device. +//! \param pucData is a pointer to the data to write out. +//! \param ulNumBlocks is the number of blocks to write to the device. +//! +//! This function will perform a block sized write to the device associated +//! with the \e ulInstance parameter. The \e ulLBA parameter specifies the +//! logical block address to write on the device. This function will only +//! perform \e ulNumBlocks block sized writes. In most cases this is a write +//! of 512 bytes of data. The \e *pucData buffer should contain at least +//! \e ulNumBlocks * 512 bytes in size to prevent unwanted data being written +//! to the device. +//! +//! \return The function returns zero for success and any negative value +//! indicates a failure. +// +//***************************************************************************** +long +USBHMSCBlockWrite(unsigned long ulInstance, unsigned long ulLBA, + unsigned char *pucData, unsigned long ulNumBlocks) +{ + tUSBHMSCInstance *pMSCDevice; + unsigned long ulSize; + + // + // Get the instance pointer in a more usable form. + // + pMSCDevice = (tUSBHMSCInstance *)ulInstance; + + // + // If there is no device present then return an error. + // + if(pMSCDevice->pDevice == 0) + { + return(-1); + } + + // + // Calculate the actual byte size of the write. + // + ulSize = pMSCDevice->ulBlockSize * ulNumBlocks; + + // + // Perform the SCSI write command. + // + if(USBHSCSIWrite10(pMSCDevice->ulBulkInPipe, pMSCDevice->ulBulkOutPipe, + ulLBA, pucData, &ulSize, + ulNumBlocks) != SCSI_CMD_STATUS_PASS) + { + return(-1); + } + + // + // Success. + // + return(0); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhmsc.h b/usblib/host/usbhmsc.h new file mode 100644 index 0000000..f15ffc6 --- /dev/null +++ b/usblib/host/usbhmsc.h @@ -0,0 +1,95 @@ +//***************************************************************************** +// +// usbhmsc.h - Definitions for the USB MSC host driver. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBHMSC_H__ +#define __USBHMSC_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// These defines are the the events that will be passed in the \e ulEvent +// parameter of the callback from the driver. +// +//***************************************************************************** +#define MSC_EVENT_OPEN 1 +#define MSC_EVENT_CLOSE 2 + +//***************************************************************************** +// +// The prototype for the USB MSC host driver callback function. +// +//***************************************************************************** +typedef void (*tUSBHMSCCallback)(unsigned long ulInstance, + unsigned long ulEvent, + void *pvEventData); + +//***************************************************************************** +// +// Prototypes for the USB MSC host driver APIs. +// +//***************************************************************************** +extern unsigned long USBHMSCDriveOpen(unsigned long ulDrive, + tUSBHMSCCallback pfnCallback); +extern void USBHMSCDriveClose(unsigned long ulInstance); +extern long USBHMSCDriveReady(unsigned long ulInstance); +extern long USBHMSCBlockRead(unsigned long ulInstance, unsigned long ulLBA, + unsigned char *pucData, + unsigned long ulNumBlocks); +extern long USBHMSCBlockWrite(unsigned long ulInstance, unsigned long ulLBA, + unsigned char *pucData, + unsigned long ulNumBlocks); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBHMSC_H__ diff --git a/usblib/host/usbhost.h b/usblib/host/usbhost.h new file mode 100644 index 0000000..072cf9f --- /dev/null +++ b/usblib/host/usbhost.h @@ -0,0 +1,348 @@ +//***************************************************************************** +// +// usbhost.h - Host specific definitions 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. +// +//***************************************************************************** + +#ifndef __USBHOST_H__ +#define __USBHOST_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_hcd +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// This is the type used to identify what the pipe is currently in use for. +// +//***************************************************************************** +#define USBHCD_PIPE_UNUSED 0x00100000 +#define USBHCD_PIPE_CONTROL 0x00130000 +#define USBHCD_PIPE_BULK_OUT 0x00210000 +#define USBHCD_PIPE_BULK_IN 0x00220000 +#define USBHCD_PIPE_INTR_OUT 0x00410000 +#define USBHCD_PIPE_INTR_IN 0x00420000 +#define USBHCD_PIPE_ISOC_OUT 0x00810000 +#define USBHCD_PIPE_ISOC_IN 0x00820000 +#define USBHCD_PIPE_ISOC_OUT_DMA 0x01810000 +#define USBHCD_PIPE_ISOC_IN_DMA 0x01820000 +#define USBHCD_PIPE_BULK_OUT_DMA 0x01210000 +#define USBHCD_PIPE_BULK_IN_DMA 0x01220000 + +//***************************************************************************** +// +// These are the defines that are used with USBHCDPowerConfigInit(). +// +//***************************************************************************** +#define USBHCD_FAULT_LOW 0x00000010 +#define USBHCD_FAULT_HIGH 0x00000030 +#define USBHCD_FAULT_VBUS_NONE 0x00000000 +#define USBHCD_FAULT_VBUS_TRI 0x00000140 +#define USBHCD_FAULT_VBUS_DIS 0x00000400 +#define USBHCD_VBUS_MANUAL 0x00000004 +#define USBHCD_VBUS_AUTO_LOW 0x00000002 +#define USBHCD_VBUS_AUTO_HIGH 0x00000003 +#define USBHCD_VBUS_FILTER 0x00010000 + +//***************************************************************************** +// +//! This macro is used to declare an instance of an Event driver for the USB +//! library. +//! +//! \param VarName is the name of the variable. +//! \param pfnOpen is the callback for the Open call to this driver. This +//! value is currently reserved and should be set to 0. +//! \param pfnClose is the callback for the Close call to this driver. This +//! value is currently reserved and should be set to 0. +//! \param pfnEvent is the callback that will be called for various USB events. +//! +//! The first parameter is the actual name of the variable that will +//! be declared by this macro. The second and third parameter are reserved +//! for future functionality and are unused and should be set to zero. The +//! last parameter is the actual callback function and is specified as +//! a function pointer of the type: +//! +//! void (*pfnEvent)(void *pvData); +//! +//! When the \e pfnEvent function is called the void pointer that is passed in +//! as a parameter should be cast to a pointer to a structure of type +//! tEventInfo. This will contain the event that caused the pfnEvent function +//! to be called. +// +//***************************************************************************** +#define DECLARE_EVENT_DRIVER(VarName, pfnOpen, pfnClose, pfnEvent) \ +void IntFn(void *pvData); \ +const tUSBHostClassDriver VarName = \ +{ \ + USB_CLASS_EVENTS, \ + 0, \ + 0, \ + pfnEvent \ +} + +//***************************************************************************** +// +// This is the type definition a call back for events on USB Pipes allocated +// by USBHCDPipeAlloc(). +// +// \param ulPipe is well the pipe +// \param ulEvent is well the event +// +// longer def thand may need more text in order to be recogized what should +// this really say about ourselves. +// +// \return None. +// +//***************************************************************************** +typedef void (* tHCDPipeCallback)(unsigned long ulPipe, + unsigned long ulEvent); + +//***************************************************************************** +// +//! This is the structure that holds all of the information for devices +//! that are enumerated in the system. It is passed in to Open function of +//! USB host class drivers so that they can allocate any endpoints and parse +//! out other information that the device class needs to complete enumeration. +// +//***************************************************************************** +typedef struct +{ + // + //! The current device address for this device. + // + unsigned long ulAddress; + + // + //! The current interface for this device. + // + unsigned long ulInterface; + + // + //! A flag used to determine whether we need to pass an interrupt + //! notification on to this device as a result of endpoint activity. + // + tBoolean bNotifyInt; + + // + //! A flag used to record whether this is a low-speed or a full-speed + //! device. + // + tBoolean bLowSpeed; + + // + //! A flag indicating whether or not we have read the device's + //! configuration descriptor yet. + // + tBoolean bConfigRead; + + // + //! The hub number to which this device is attached. + // + unsigned char ucHub; + + // + //! The hub port number to which the device is attached. + // + unsigned char ucHubPort; + + // + //! The device descriptor for this device. + // + tDeviceDescriptor DeviceDescriptor; + + // + //! A pointer to the configuration descriptor for this device. + // + tConfigDescriptor *pConfigDescriptor; + + // + //! The size of the buffer allocated to pConfigDescriptor. + // + unsigned long ulConfigDescriptorSize; +} +tUSBHostDevice; + +//***************************************************************************** +// +//! This structure defines a USB host class driver interface, it is parsed to +//! find a USB class driver once a USB device is enumerated. +// +//***************************************************************************** +typedef struct +{ + // + //! The interface class that this device class driver supports. + // + unsigned long ulInterfaceClass; + + // + //! The function is called when this class of device has been detected. + // + void * (*pfnOpen)(tUSBHostDevice *pDevice); + + // + //! The function is called when the device, originally opened with a call + //! to the pfnOpen function, is disconnected. + // + void (*pfnClose)(void *pvInstance); + + // + //! This is the optional interrupt handler that will be called when an + //! endpoint associated with this device instance generates an interrupt. + // + void (*pfnIntHandler)(void *pvInstance); +} +tUSBHostClassDriver; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// If the g_USBEventDriver is included in the host controller driver list then +// this function must be provided by the application. +// +//***************************************************************************** +void USBHCDEvents(void *pvData); + +//***************************************************************************** +// +// Prototypes for the USB Host controller APIs. +// +//***************************************************************************** +extern void USBHCDMain(void); +extern long USBHCDEventEnable(unsigned long ulIndex, void *pvEventDriver, + unsigned long ulEvent); +extern long USBHCDEventDisable(unsigned long ulIndex, void *pvEventDriver, + unsigned long ulEvent); +extern void USBHCDInit(unsigned long ulIndex, void *pData, + unsigned long ulSize); +extern void USBHCDPowerConfigInit(unsigned long ulIndex, + unsigned long ulFlags); +extern unsigned long USBHCDPowerConfigGet(unsigned long ulIndex); +extern unsigned long USBHCDPowerConfigSet(unsigned long ulIndex, + unsigned long ulConfig); +extern unsigned long USBHCDPowerAutomatic(unsigned long ulIndex); +extern void + USBHCDRegisterDrivers(unsigned long ulIndex, + const tUSBHostClassDriver * const *ppHClassDrvrs, + unsigned long ulNumDrivers); +extern void USBHCDTerm(unsigned long ulIndex); +extern void USBHCDSetConfig(unsigned long ulIndex, unsigned long ulDevice, + unsigned long ulConfiguration); +extern void USBHCDSetInterface(unsigned long ulIndex, unsigned long ulDevice, + unsigned long ulInterface, + unsigned ulAltSetting); +extern unsigned long USBHCDHubDeviceConnected(unsigned long ulIndex, + unsigned char ucHub, + unsigned char ucPort, + tBoolean bLowSpeed, + unsigned char *pucConfigPool, + unsigned long ulConfigSize); +extern void USBHCDHubDeviceDisconnected(unsigned long ulIndex, + unsigned long ulDevIndex); +extern void USBHCDSuspend(unsigned long ulIndex); +extern void USBHCDResume(unsigned long ulIndex); +extern void USBHCDReset(unsigned long ulIndex); +extern void USBHCDPipeFree(unsigned long ulPipe); +extern unsigned long USBHCDPipeAlloc(unsigned long ulIndex, + unsigned long ulEndpointType, + tUSBHostDevice *psDevice, + tHCDPipeCallback pCallback); +extern unsigned long USBHCDPipeAllocSize(unsigned long ulIndex, + unsigned long ulEndpointType, + tUSBHostDevice *psDevice, + unsigned long ulFIFOSize, + tHCDPipeCallback pCallback); +extern unsigned long USBHCDPipeConfig(unsigned long ulPipe, + unsigned long ulMaxPayload, + unsigned long ulInterval, + unsigned long ulTargetEndpoint); +extern unsigned long USBHCDPipeStatus(unsigned long ulPipe); +extern unsigned long USBHCDPipeWrite(unsigned long ulPipe, + unsigned char *pData, + unsigned long ulSize); +extern unsigned long USBHCDPipeRead(unsigned long ulPipe, unsigned char *pData, + unsigned long ulSize); +extern unsigned long USBHCDPipeSchedule(unsigned long ulPipe, + unsigned char *pucData, + unsigned long ulSize); +extern void USBHCDPipeDataAck(unsigned long ulPipe); +extern unsigned long USBHCDPipeReadNonBlocking(unsigned long ulPipe, + unsigned char *pucData, + unsigned long ulSize); +extern unsigned long USBHCDControlTransfer(unsigned long ulIndex, + tUSBRequest *pSetupPacket, + tUSBHostDevice *pDevice, + unsigned char *pData, + unsigned long ulSize, + unsigned long ulMaxPacketSize); +extern void USB0HostIntHandler(void); + +extern unsigned char USBHCDDevHubPort(unsigned long ulInstance); +extern unsigned char USBHCDDevAddress(unsigned long ulInstance); +extern unsigned char USBHCDDevClass(unsigned long ulInstance, + unsigned long ulInterface); +extern unsigned char USBHCDDevSubClass(unsigned long ulInstance, + unsigned long ulInterface); +extern unsigned char USBHCDDevProtocol(unsigned long ulInstance, + unsigned long ulInterface); + + +//***************************************************************************** +// +// The host class drivers supported by the USB library. +// +//***************************************************************************** +extern const tUSBHostClassDriver g_USBHostMSCClassDriver; +extern const tUSBHostClassDriver g_USBHIDClassDriver; +extern const tUSBHostClassDriver g_USBHostAudioClassDriver; + +#include "usbhostpriv.h" + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBHOST_H__ 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. +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhostpriv.h b/usblib/host/usbhostpriv.h new file mode 100644 index 0000000..44c0a73 --- /dev/null +++ b/usblib/host/usbhostpriv.h @@ -0,0 +1,257 @@ +//***************************************************************************** +// +// usbhostpriv.h - Internal header file for USB host functions. +// +// Copyright (c) 2011-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. +// +//***************************************************************************** + +#ifndef __USBHOSTPRIV_H__ +#define __USBHOSTPRIV_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The states a hub port can be in during device connection. +// +//***************************************************************************** +typedef enum +{ + // + // The port has no device connected. + // + PORT_IDLE, + + // + // The port has a device present and is waiting for the enumeration + // sequence to begin. + // + PORT_CONNECTED, + + // + // A device connection notification has been received and we have initiated + // a reset to the port. We are waiting for the reset to complete. + // + PORT_RESET_ACTIVE, + + // + // The Port reset has completed but now the hub is waiting the required + // 10ms before accessing the device. + // + PORT_RESET_WAIT, + + // + // A device is connected and the port has been reset. Control has been + // passed to the main host handling portion of USBLib to enumerate the + // device. + // + PORT_ACTIVE, + + // + // A device has completed enumeration. + // + PORT_ENUMERATED, + + // + // A device is attached to the port but enumeration failed. + // + PORT_ERROR +} +tHubPortState; + +//***************************************************************************** +// +// The list of valid event flags in the g_sUSBHCD.ulEventEnables member +// variable. +// +//***************************************************************************** +#define USBHCD_EVFLAG_SOF 0x00000001 +#define USBHCD_EVFLAG_CONNECT 0x00000002 +#define USBHCD_EVFLAG_UNKCNCT 0x00000004 +#define USBHCD_EVFLAG_DISCNCT 0x00000008 +#define USBHCD_EVFLAG_PWRFAULT 0x00000010 +#define USBHCD_EVFLAG_PWRDIS 0x00000020 +#define USBHCD_EVFLAG_PWREN 0x00000040 + +//***************************************************************************** +// +// This structure holds all data specific to a single hub port. +// +//***************************************************************************** +typedef struct +{ + // + // A pointer to storage for the configuration descriptor of a device + // attached to this port. + // + unsigned char *pucConfigDesc; + + // + // The size of the storage pointed to by pucConfigDesc. + // + unsigned long ulConfigSize; + + // + // The handle used by the HCD layer to identify this device. + // + unsigned long ulDevHandle; + + // + // The current state of the port. + // + volatile tHubPortState sState; + + // + // General counter used in various states. + // + volatile unsigned long ulCount; + + // + // A flag used to indicate that the downstream device is a low speed + // device. + // + tBoolean bLowSpeed; + + // + // This flag is set if the hub reports that a change is pending on this + // port. + // + volatile tBoolean bChanged; +} +tHubPort; + +//***************************************************************************** +// +// This is the structure that holds all of the data for a given instance of +// a Hub device. +// +//***************************************************************************** +typedef struct +{ + // + // Save the device instance. + // + tUSBHostDevice *pDevice; + + // + // Used to save the callback function pointer. + // + tUSBCallback pfnCallback; + + // + // Callback data provided by caller. + // + unsigned long ulCBData; + + // + // Interrupt IN pipe. + // + unsigned long ulIntInPipe; + + // + // Hub characteristics as reported in the class-specific hub descriptor. + // + unsigned short usHubCharacteristics; + + // + // The number of downstream-facing ports the hub supports. + // + unsigned char ucNumPorts; + + // + // The number of ports on the hub that we can actually talk to. This will + // be the smaller of the number of ports on the hub and MAX_USB_DEVICES. + // + unsigned char ucNumPortsInUse; + + // + // The size of a status change packet sent by the hub. This is determined + // from the number of ports supported by the hub. + // + unsigned char ucReportSize; + + // + // Flag indicating whether the hub is connected. + // + tBoolean bHubActive; + + // + // Flag indicating that a device is currently in process of being + // enumerated. + // + volatile tBoolean bEnumerationBusy; + + // + // This is valid if bEnumerationBusy is set and indicates the port + // that is in the process of enumeration. + // + unsigned char ucEnumIdx; + + // + // The state of each of the ports we support on the hub. + // + tHubPort psPorts[MAX_USB_DEVICES]; +} +tHubInstance; + +//***************************************************************************** +// +// Functions within the host controller that are called by the hub class driver +// +//***************************************************************************** +extern unsigned long USBHCDHubDeviceConnected(unsigned long ulIndex, + unsigned char ucHub, + unsigned char ucPort, + tBoolean bLowSpeed, + unsigned char *pucConfigPool, + unsigned long ulConfigSize); +extern void USBHCDHubDeviceDisconnected(unsigned long ulIndex, + unsigned long ulDevIndex); + +//***************************************************************************** +// +// Functions in the hub class driver that are called by the host controller. +// +//***************************************************************************** +extern void USBHHubMain(void); +extern void USBHHubInit(void); +extern void USBHHubEnumerationComplete(unsigned char ucHub, + unsigned char ucPort); +extern void USBHHubEnumerationError(unsigned char ucHub, unsigned char ucPort); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBHOSTPRIV_H__ diff --git a/usblib/host/usbhscsi.c b/usblib/host/usbhscsi.c new file mode 100644 index 0000000..9199514 --- /dev/null +++ b/usblib/host/usbhscsi.c @@ -0,0 +1,778 @@ +//***************************************************************************** +// +// usbhscsi.c - USB host SCSI layer used by the USB host MSC driver. +// +// 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_types.h" +#include "usblib/usblib.h" +#include "usblib/usbmsc.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhmsc.h" +#include "usblib/host/usbhscsi.h" + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// This is the data verify tag passed between requests. +// +//***************************************************************************** +#define CBW_TAG_VALUE 0x54231990 + +//***************************************************************************** +// +//! This function is used to issue SCSI commands via USB. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! \param pSCSICmd is the SCSI command structure to send. +//! \param pucData is pointer to the command data to be sent. +//! \param pulSize is the number of bytes is the number of bytes expected or +//! sent by the command. +//! +//! This internal function is used to handle SCSI commands sent by other +//! functions. It serves as a layer between the SCSI command and the USB +//! interface being used to send the command. The \e pSCSI parameter contains +//! the SCSI command to send. For commands that expect data back, the +//! \e pucData is the buffer to store the data into and \e pulSize is used to +//! store the amount of data to request as well as used to indicate how many +//! bytes were filled into the \e pucData buffer on return. For commands that +//! are sending data, \e pucData is the data to be sent and \e pulSize is the +//! number of bytes to send. +//! +//! \return This function returns the SCSI status from the command. The value +//! will be either \b SCSI_CMD_STATUS_PASS or \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +static unsigned long +USBHSCSISendCommand(unsigned long ulInPipe, unsigned long ulOutPipe, + tMSCCBW *pSCSICmd, unsigned char *pucData, + unsigned long *pulSize) +{ + tMSCCSW CmdStatus; + unsigned long ulBytes; + + // + // Initialize the command status. + // + CmdStatus.dCSWSignature = 0; + CmdStatus.dCSWTag = 0; + CmdStatus.bCSWStatus = SCSI_CMD_STATUS_FAIL; + + // + // Set the CBW signature and tag. + // + pSCSICmd->dCBWSignature = CBW_SIGNATURE; + pSCSICmd->dCBWTag = CBW_TAG_VALUE; + + // + // Set the size of the data to be returned by the device. + // + pSCSICmd->dCBWDataTransferLength = *pulSize; + + // + // Send the command. + // + ulBytes = USBHCDPipeWrite(ulOutPipe, + (unsigned char*)pSCSICmd, sizeof(tMSCCBW)); + + // + // If no bytes went out then the command failed. + // + if(ulBytes == 0) + { + return(SCSI_CMD_STATUS_FAIL); + } + + // + // Only request data if there is data to request. + // + if(pSCSICmd->dCBWDataTransferLength != 0) + { + // + // See if this is a read or a write. + // + if(pSCSICmd->bmCBWFlags & CBWFLAGS_DIR_IN) + { + // + // Read the data back. + // + *pulSize = USBHCDPipeRead(ulInPipe, pucData, *pulSize); + } + else + { + // + // Write the data out. + // + *pulSize = USBHCDPipeWrite(ulOutPipe, pucData, *pulSize); + } + } + + // + // Get the status of the command. + // + ulBytes = USBHCDPipeRead(ulInPipe, (unsigned char *)&CmdStatus, + sizeof(tMSCCSW)); + + + // + // If the status was invalid or did not have the correct signature then + // indicate a failure. + // + if((ulBytes == 0) || (CmdStatus.dCSWSignature != CSW_SIGNATURE) || + (CmdStatus.dCSWTag != CBW_TAG_VALUE)) + { + return(SCSI_CMD_STATUS_FAIL); + } + + // + // Return the status. + // + return((unsigned long)CmdStatus.bCSWStatus); +} + +//***************************************************************************** +// +//! This will issue the SCSI inquiry command to a device. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! \param pucData is the data buffer to return the results into. +//! \param pulSize is the size of buffer that was passed in on entry and the +//! number of bytes returned. +//! +//! This function should be used to issue a SCSI Inquiry command to a mass +//! storage device. To allow for multiple devices, the \e ulInPipe and +//! \e ulOutPipe parameters indicate which USB pipes to use for this call. +//! +//! \note The \e pucData buffer pointer should have at least +//! \b SCSI_INQUIRY_DATA_SZ bytes of data or this function will overflow the +//! buffer. +//! +//! \return This function returns the SCSI status from the command. The value +//! will be either \b SCSI_CMD_STATUS_PASS or \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +unsigned long +USBHSCSIInquiry(unsigned long ulInPipe, unsigned long ulOutPipe, + unsigned char *pucData, unsigned long *pulSize) +{ + tMSCCBW SCSICmd; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)SCSICmd.CBWCB; + + // + // The number of bytes of data that the host expects to transfer on the + // Bulk-In or Bulk-Out endpoint (as indicated by the Direction bit) during + // the execution of this command. If this field is zero, the device and + // the host shall transfer no data between the CBW and the associated CSW, + // and the device shall ignore the value of the Direction bit in + // bmCBWFlags. + // + *pulSize = SCSI_INQUIRY_DATA_SZ; + + // + // This is an IN request. + // + SCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + SCSICmd.bCBWLUN = 0; + + // + // This is the length of the command itself. + // + SCSICmd.bCBWCBLength = 6; + + // + // Send Inquiry command with no request for vital product data. + // + pulData[0] = SCSI_INQUIRY_CMD; + + // + // Allocation length. + // + pulData[1] = SCSI_INQUIRY_DATA_SZ; + pulData[2] = 0; + pulData[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ulInPipe, ulOutPipe, &SCSICmd, pucData, + pulSize)); +} + +//***************************************************************************** +// +//! This will issue the SCSI read capacity command to a device. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! \param pucData is the data buffer to return the results into. +//! \param pulSize is the size of buffer that was passed in on entry and the +//! number of bytes returned. +//! +//! This function should be used to issue a SCSI Read Capacity command +//! to a mass storage device that is connected. To allow for multiple devices, +//! the \e ulInPipe and \e ulOutPipe parameters indicate which USB pipes to +//! use for this call. +//! +//! \note The \e pucData buffer pointer should have at least +//! \b SCSI_READ_CAPACITY_SZ bytes of data or this function will overflow the +//! buffer. +//! +//! \return This function returns the SCSI status from the command. The value +//! will be either \b SCSI_CMD_STATUS_PASS or \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +unsigned long +USBHSCSIReadCapacity(unsigned long ulInPipe, unsigned long ulOutPipe, + unsigned char *pucData, unsigned long *pulSize) +{ + tMSCCBW SCSICmd; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)SCSICmd.CBWCB; + + // + // Set the size of the command data. + // + *pulSize = SCSI_READ_CAPACITY_SZ; + + // + // This is an IN request. + // + SCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + SCSICmd.bCBWLUN = 0; + + // + // Set the length of the command itself. + // + SCSICmd.bCBWCBLength = 12; + + // + // Only use the first byte and set it to the Read Capacity command. The + // rest are set to 0. + // + pulData[0] = SCSI_READ_CAPACITY; + pulData[1] = 0; + pulData[2] = 0; + pulData[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ulInPipe, ulOutPipe, &SCSICmd, pucData, + pulSize)); +} + +//***************************************************************************** +// +//! This will issue the SCSI read capacities command to a device. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! \param pucData is the data buffer to return the results into. +//! \param pulSize is the size of buffer that was passed in on entry and the +//! number of bytes returned. +//! +//! This function should be used to issue a SCSI Read Capacities command +//! to a mass storage device that is connected. To allow for multiple devices, +//! the \e ulInPipe and \e ulOutPipe parameters indicate which USB pipes to +//! use for this call. +//! +//! \return This function returns the SCSI status from the command. The value +//! will be either \b SCSI_CMD_STATUS_PASS or \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +unsigned long +USBHSCSIReadCapacities(unsigned long ulInPipe, unsigned long ulOutPipe, + unsigned char *pucData, unsigned long *pulSize) +{ + tMSCCBW SCSICmd; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)SCSICmd.CBWCB; + + // + // This is an IN request. + // + SCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + SCSICmd.bCBWLUN = 0; + + // + // Set the length of the command itself. + // + SCSICmd.bCBWCBLength = 12; + + // + // Only use the first byte and set it to the Read Capacity command. The + // rest are set to 0. + // + pulData[0] = SCSI_READ_CAPACITIES; + pulData[1] = 0; + pulData[2] = 0; + pulData[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ulInPipe, ulOutPipe, &SCSICmd, pucData, + pulSize)); +} + +//***************************************************************************** +// +//! This will issue the SCSI Mode Sense(6) command to a device. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! \param ulFlags is a combination of flags defining the exact query that is +//! to be made. +//! \param pucData is the data buffer to return the results into. +//! \param pulSize is the size of the buffer on entry and number of bytes read +//! on exit. +//! +//! This function should be used to issue a SCSI Mode Sense(6) command +//! to a mass storage device. To allow for multiple devices,the \e ulInPipe +//! and \e ulOutPipe parameters indicate which USB pipes to use for this call. +//! The call will return at most the number of bytes in the \e pulSize +//! parameter, however it can return less and change the \e pulSize parameter +//! to the number of valid bytes in the \e *pulSize buffer. +//! +//! The \e ulFlags parameter is a combination of the following three sets of +//! definitions: +//! +//! One of the following values must be specified: +//! +//! - \b SCSI_MS_PC_CURRENT request for current settings. +//! - \b SCSI_MS_PC_CHANGEABLE request for changeable settings. +//! - \b SCSI_MS_PC_DEFAULT request for default settings. +//! - \b SCSI_MS_PC_SAVED request for the saved values. +//! +//! One of these following values must also be specified to determine the page +//! code for the request: +//! +//! - \b SCSI_MS_PC_VENDOR is the vendor specific page code. +//! - \b SCSI_MS_PC_DISCO is the disconnect/reconnect page code. +//! - \b SCSI_MS_PC_CONTROL is the control page code. +//! - \b SCSI_MS_PC_LUN is the protocol specific LUN page code. +//! - \b SCSI_MS_PC_PORT is the protocol specific port page code. +//! - \b SCSI_MS_PC_POWER is the power condition page code. +//! - \b SCSI_MS_PC_INFORM is the informational exceptions page code. +//! - \b SCSI_MS_PC_ALL will request all pages codes supported by the device. +//! +//! The last value is optional and supports the following global flag: +//! - \b SCSI_MS_DBD disables returning block descriptors. +//! +//! Example: Request for all current settings. +//! +//! \verbatim +//! SCSIModeSense6(ulInPipe, ulOutPipe, +//! SCSI_MS_PC_CURRENT | SCSI_MS_PC_ALL, +//! pucData, pulSize); +//! \endverbatim +//! +//! \return This function returns the SCSI status from the command. The value +//! will be either \b SCSI_CMD_STATUS_PASS or \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +unsigned long +USBHSCSIModeSense6(unsigned long ulInPipe, unsigned long ulOutPipe, + unsigned long ulFlags, unsigned char *pucData, + unsigned long *pulSize) +{ + tMSCCBW SCSICmd; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)SCSICmd.CBWCB; + + // + // This is an IN request. + // + SCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + SCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + SCSICmd.bCBWCBLength = 6; + + // + // Set the options for the Mode Sense Command (6). + // + pulData[0] = (SCSI_MODE_SENSE_6 | ulFlags); + pulData[1] = (unsigned char)*pulSize; + pulData[2] = 0; + pulData[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ulInPipe, ulOutPipe, &SCSICmd, pucData, + pulSize)); +} + +//***************************************************************************** +// +//! This function issues a SCSI Test Unit Ready command to a device. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! +//! This function is used to issue a SCSI Test Unit Ready command to a device. +//! This call will simply return the results of issuing this command. +//! +//! \return This function returns the results of the SCSI Test Unit Ready +//! command. The value will be either \b SCSI_CMD_STATUS_PASS or +//! \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +unsigned long +USBHSCSITestUnitReady(unsigned long ulInPipe, unsigned long ulOutPipe) +{ + tMSCCBW SCSICmd; + unsigned long ulSize; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)SCSICmd.CBWCB; + + // + // No data in this command. + // + ulSize = 0; + + // + // This is an IN request. + // + SCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + SCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + SCSICmd.bCBWCBLength = 6; + + // + // Set the parameter options. + // + pulData[0] = SCSI_TEST_UNIT_READY; + pulData[1] = 0; + pulData[2] = 0; + pulData[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ulInPipe, ulOutPipe, &SCSICmd, 0, &ulSize)); +} + +//***************************************************************************** +// +//! This function issues a SCSI Request Sense command to a device. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! \param pucData is the data buffer to return the results into. +//! \param pulSize is the size of the buffer on entry and number of bytes read +//! on exit. +//! +//! This function is used to issue a SCSI Request Sense command to a device. +//! It will return the data in the buffer pointed to by \e pucData. The +//! parameter \e pulSize should have the allocation size in bytes of the buffer +//! pointed to by pucData. +//! +//! \return This function returns the results of the SCSI Request Sense +//! command. The value will be either \b SCSI_CMD_STATUS_PASS or +//! \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +unsigned long +USBHSCSIRequestSense(unsigned long ulInPipe, unsigned long ulOutPipe, + unsigned char *pucData, unsigned long *pulSize) +{ + tMSCCBW SCSICmd; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)SCSICmd.CBWCB; + + // + // This is an IN request. + // + SCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + SCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + SCSICmd.bCBWCBLength = 12; + + // + // Set the parameter options. + // + pulData[0] = SCSI_REQUEST_SENSE; + pulData[1] = 18; + pulData[2] = 0; + pulData[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ulInPipe, ulOutPipe, &SCSICmd, pucData, + pulSize)); +} + +//***************************************************************************** +// +//! This function issues a SCSI Read(10) command to a device. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! \param ulLBA is the logical block address to read. +//! \param pucData is the data buffer to return the data. +//! \param pulSize is the size of the buffer on entry and number of bytes read +//! on exit. +//! \param ulNumBlocks is the number of contiguous blocks to read from the +//! device. +//! +//! This function is used to issue a SCSI Read(10) command to a device. The +//! \e ulLBA parameter specifies the logical block address to read from the +//! device. The data from this block will be returned in the buffer pointed to +//! by \e pucData. The parameter \e pulSize should indicate enough space to +//! hold a full block size, or only the first pulSize bytes of the LBA will +//! be returned. +//! +//! \return This function returns the results of the SCSI Read(10) command. +//! The value will be either \b SCSI_CMD_STATUS_PASS or +//! \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +unsigned long +USBHSCSIRead10(unsigned long ulInPipe, unsigned long ulOutPipe, + unsigned long ulLBA, unsigned char *pucData, + unsigned long *pulSize, unsigned long ulNumBlocks) +{ + tMSCCBW SCSICmd; + + // + // This is an IN request. + // + SCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + SCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + SCSICmd.bCBWCBLength = 10; + + // + // Set the parameter options. + // + SCSICmd.CBWCB[0] = SCSI_READ_10; + + // + // Clear the reserved field. + // + SCSICmd.CBWCB[1] = 0; + + // + // LBA starts at offset 2. + // + SCSICmd.CBWCB[2] = (unsigned char)(ulLBA >> 24); + SCSICmd.CBWCB[3] = (unsigned char)(ulLBA >> 16); + SCSICmd.CBWCB[4] = (unsigned char)(ulLBA >> 8); + SCSICmd.CBWCB[5] = (unsigned char)ulLBA; + + // + // Clear the reserved field. + // + SCSICmd.CBWCB[6] = 0; + + // + // Transfer length in blocks starts at offset 2. + // This also sets the Control value to 0 at offset 9. + // + SCSICmd.CBWCB[7] = (ulNumBlocks & 0xFF00) >> 8; + *((unsigned long *)&SCSICmd.CBWCB[8]) = (ulNumBlocks & 0xFF); + *((unsigned long *)&SCSICmd.CBWCB[12]) = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ulInPipe, ulOutPipe, &SCSICmd, pucData, + pulSize)); +} + +//***************************************************************************** +// +//! This function issues a SCSI Write(10) command to a device. +//! +//! This function is used to issue a SCSI Write(10) command to a device. The +//! \e ulLBA parameter specifies the logical block address on the device. The +//! data to write to this block should be in the buffer pointed to by +//! \e pucData parameter. The parameter \e pulSize should indicate the amount +//! of data to write to the specified LBA. +//! +//! \param ulInPipe is the USB IN pipe to use for this command. +//! \param ulOutPipe is the USB OUT pipe to use for this command. +//! \param ulLBA is the logical block address to read. +//! \param pucData is the data buffer to write out. +//! \param pulSize is the size of the buffer. +//! \param ulNumBlocks is the number of contiguous blocks to write to the +//! device. +//! +//! \return This function returns the results of the SCSI Write(10) command. +//! The value will be either \b SCSI_CMD_STATUS_PASS or +//! \b SCSI_CMD_STATUS_FAIL. +// +//***************************************************************************** +unsigned long +USBHSCSIWrite10(unsigned long ulInPipe, unsigned long ulOutPipe, + unsigned long ulLBA, unsigned char *pucData, + unsigned long *pulSize, unsigned long ulNumBlocks) +{ + tMSCCBW SCSICmd; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)SCSICmd.CBWCB; + + // + // This is an IN request. + // + SCSICmd.bmCBWFlags = CBWFLAGS_DIR_OUT; + + // + // Only handle LUN 0. + // + SCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + SCSICmd.bCBWCBLength = 10; + + // + // Set the parameter options. + // + SCSICmd.CBWCB[0] = SCSI_WRITE_10; + + // + // Clear the reserved field. + // + SCSICmd.CBWCB[1] = 0; + + // + // LBA starts at offset 2. + // + SCSICmd.CBWCB[2] = (unsigned char)(ulLBA >> 24); + SCSICmd.CBWCB[3] = (unsigned char)(ulLBA >> 16); + SCSICmd.CBWCB[4] = (unsigned char)(ulLBA >> 8); + SCSICmd.CBWCB[5] = (unsigned char)ulLBA; + + // + // Clear the reserved field. + // + SCSICmd.CBWCB[6] = 0; + + // + // Set the transfer length in blocks. + // This also sets the Control value to 0 at offset 9. + // + SCSICmd.CBWCB[7] = (ulNumBlocks & 0xFF00) >> 8; + + // + // The blocks go into is byte offset 8 or word address 2. + // + pulData[2] = (ulNumBlocks & 0xFF); + + // + // The blocks go into is byte offset 12 or word address 3. + // + pulData[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ulInPipe, ulOutPipe, &SCSICmd, pucData, + pulSize)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhscsi.h b/usblib/host/usbhscsi.h new file mode 100644 index 0000000..21897eb --- /dev/null +++ b/usblib/host/usbhscsi.h @@ -0,0 +1,102 @@ +//***************************************************************************** +// +// usbhscsi.h - Definitions for the USB host SCSI layer. +// +// 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. +// +//***************************************************************************** + +#ifndef __USBHSCSI_H__ +#define __USBHSCSI_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototypes for the APIs exported by the USB SCSI layer. +// +//***************************************************************************** +extern unsigned long USBHSCSIInquiry(unsigned long ulInPipe, + unsigned long ulOutPipe, + unsigned char *pucBuffer, + unsigned long *pulSize); +extern unsigned long USBHSCSIReadCapacity(unsigned long ulInPipe, + unsigned long ulOutPipe, + unsigned char *pData, + unsigned long *pulSize); +extern unsigned long USBHSCSIReadCapacities(unsigned long ulInPipe, + unsigned long ulOutPipe, + unsigned char *pData, + unsigned long *pulSize); +extern unsigned long USBHSCSIModeSense6(unsigned long ulInPipe, + unsigned long ulOutPipe, + unsigned long ulFlags, + unsigned char *pData, + unsigned long *pulSize); +extern unsigned long USBHSCSITestUnitReady(unsigned long ulInPipe, + unsigned long ulOutPipe); +extern unsigned long USBHSCSIRequestSense(unsigned long ulInPipe, + unsigned long ulOutPipe, + unsigned char *pucData, + unsigned long *pulSize); +extern unsigned long USBHSCSIRead10(unsigned long ulInPipe, + unsigned long ulOutPipe, + unsigned long ulLBA, + unsigned char *pucData, + unsigned long *pulSize, + unsigned long ulNumBlocks); +extern unsigned long USBHSCSIWrite10(unsigned long ulInPipe, + unsigned long ulOutPipe, + unsigned long ulLBA, + unsigned char *pucData, + unsigned long *pulSize, + unsigned long ulNumBlocks); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBHSCSI_H__ -- cgit v1.3.1