From 990090a4cc9070837d31e66b58d40f0c3d038741 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Sun, 16 Mar 2014 14:41:11 +0200 Subject: Add usblib and utils --- usblib/host/usbhaudio.c | 1557 ++++++++++ usblib/host/usbhaudio.h | 163 ++ usblib/host/usbhhid.c | 746 +++++ usblib/host/usbhhid.h | 166 ++ usblib/host/usbhhidkeyboard.c | 752 +++++ usblib/host/usbhhidkeyboard.h | 89 + usblib/host/usbhhidmouse.c | 452 +++ usblib/host/usbhhidmouse.h | 81 + usblib/host/usbhhub.c | 1522 ++++++++++ usblib/host/usbhhub.h | 174 ++ usblib/host/usbhmsc.c | 756 +++++ usblib/host/usbhmsc.h | 99 + usblib/host/usbhost.h | 295 ++ usblib/host/usbhostenum.c | 6284 +++++++++++++++++++++++++++++++++++++++++ usblib/host/usbhostpriv.h | 201 ++ usblib/host/usbhscsi.c | 777 +++++ usblib/host/usbhscsi.h | 87 + 17 files changed, 14201 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..129130c --- /dev/null +++ b/usblib/host/usbhaudio.c @@ -0,0 +1,1557 @@ +//***************************************************************************** +// +// usbhaudio.c - USB host audio driver. +// +// Copyright (c) 2010-2014 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 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/usbaudio.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhostpriv.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 *psDevice); +static void USBAudioClose(void *pvInstance); + +//***************************************************************************** +// +// This is the structure for an instance of a USB host audio driver. +// +//***************************************************************************** +struct tUSBHostAudioInstance +{ + // + // Save the device instance. + // + tUSBHostDevice *psDevice; + + // + // Used to save the call back. + // + tUSBHostAudioCallback pfnCallback; + + // + // This is the control interface. + // + uint8_t ui8IControl; + + // + // This is the output streaming interface. + // + uint8_t ui8OutInterface; + + // + // This is the currently selected active output interface used with + // ui8OutInterface interface. + // + uint8_t ui8OutAltSetting; + + // + // This is the streaming interface. + // + uint8_t ui8InInterface; + + // + // This is the currently selected active input interface used with + // ui8InInterface interface. + // + uint8_t ui8InAltSetting; + + // + // The Isochronous endpoint addresses. + // + uint8_t ui8IsochInAddress; + uint8_t ui8IsochOutAddress; + + tACInputTerminal *psInTerminal; + tACOutputTerminal *psOutTerminal; + + // + // Holds the identifier for the Feature Unit for controlling volume. + // + uint8_t ui8VolumeID; + + tACFeatureUnit *psFeatureUnit; + + // + // Holds what types of controls are enabled on the device. + // + uint16_t pui16Controls[3]; + + // + // Isochronous IN pipe. + // + uint32_t ui32IsochInPipe; + uint16_t ui16PipeSizeIn; + tUSBHostAudioCallback pfnInCallback; + void *pvInBuffer; + + // + // Isochronous OUT pipe. + // + uint32_t ui32IsochOutPipe; + uint16_t ui16PipeSizeOut; + tUSBHostAudioCallback pfnOutCallback; + void *pvOutBuffer; + + // + // State flags for this audio instance. + // + uint32_t ui32Flags; +}; + +//***************************************************************************** +// +// 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_sAudioDevice = +{ + 0 +}; + +//***************************************************************************** +// +//! This constant global structure defines the Audio Class Driver that is +//! provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_sUSBHostAudioClassDriver = +{ + USB_CLASS_AUDIO, + USBAudioOpen, + USBAudioClose, + 0 +}; + +//***************************************************************************** +// +// This is the internal function that handles callbacks from the USB IN pipe. +// +//***************************************************************************** +static void +PipeCallbackIN(uint32_t ui32Pipe, uint32_t ui32Event) +{ + // + // Only handle the data available callback and pass it on to the + // application. + // + if(ui32Event == USB_EVENT_RX_AVAILABLE) + { + if(g_sAudioDevice.pfnInCallback) + { + g_sAudioDevice.pfnInCallback(&g_sAudioDevice, + USB_EVENT_RX_AVAILABLE, + USBHCDPipeTransferSizeGet(ui32Pipe), + g_sAudioDevice.pvInBuffer); + } + } +} + +//***************************************************************************** +// +// This is the internal function that handles callbacks from the USB OUT pipe. +// +//***************************************************************************** +static void +PipeCallbackOUT(uint32_t ui32Pipe, uint32_t ui32Event) +{ + // + // Only handle the transmit complete callback and pass it on to the + // application. + // + if(ui32Event == USB_EVENT_TX_COMPLETE) + { + if(g_sAudioDevice.pfnOutCallback) + { + g_sAudioDevice.pfnOutCallback(&g_sAudioDevice, + USB_EVENT_TX_COMPLETE, 0, + g_sAudioDevice.pvOutBuffer); + } + } +} + +//***************************************************************************** +// +// Finds a given terminal and type in an audio configuration descriptor. +// +//***************************************************************************** +static tDescriptorHeader * +AudioTerminalGet(tConfigDescriptor *psConfigDesc, uint32_t ui32Terminal, + uint32_t ui32TerminalType) +{ + tACOutputTerminal *psOutput; + tDescriptorHeader *psHeader; + int32_t i32BytesRemaining; + + psHeader = (tDescriptorHeader *)psConfigDesc; + i32BytesRemaining = psConfigDesc->wTotalLength; + + while(i32BytesRemaining > 0) + { + // + // Output and input terminals are the same past the bDescriptorSubtype + // and wTerminalType that are being searched for. + // + psOutput = (tACOutputTerminal *)psHeader; + + // + // Only CS_INTERFACE descriptors can be a terminal. + // + if((psHeader->bDescriptorType == USB_DTYPE_CS_INTERFACE) && + (ui32Terminal == psOutput->bDescriptorSubtype)) + { + if((psOutput->bDescriptorSubtype == USB_AI_OUTPUT_TERMINAL) || + (psOutput->bDescriptorSubtype == USB_AI_INPUT_TERMINAL)) + + { + // + // If this was the terminal type that was requested, the + // return it. + // + if(psOutput->wTerminalType == ui32TerminalType) + { + return(psHeader); + } + } + else if(psOutput->bDescriptorSubtype == USB_AI_FEATURE_UNIT) + { + return(psHeader); + } + } + + // + // Decrease the bytes remaining by the size of this descriptor. + // + i32BytesRemaining -= psHeader->bLength; + + // + // Move the pointer to the next header. + // + psHeader = (tDescriptorHeader *)((uint32_t)psHeader + + psHeader->bLength); + } + return((tDescriptorHeader *)0); +} + +//***************************************************************************** +// +// This function returns the interface number for the control interface +// in the structure passed in the psConfigDesc. +// +// \param psConfigDescriptor 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 uint32_t +AudioControlGet(tConfigDescriptor *psConfigDesc) +{ + tDescriptorHeader *psHeader; + tInterfaceDescriptor *psInterface; + uint32_t ui32Interface; + int32_t i32Bytes; + + psHeader = (tDescriptorHeader *)psConfigDesc; + i32Bytes = psConfigDesc->wTotalLength; + + // + // Initialize the interface number to an invalid value. + // + ui32Interface = INVALID_INTERFACE; + + // + // Search the whole configuration descriptor. + // + while(i32Bytes > 0) + { + // + // Find an interface descriptor and see if it is a control interface. + // + if(psHeader->bDescriptorType == USB_DTYPE_INTERFACE) + { + psInterface = (tInterfaceDescriptor *)psHeader; + + // + // If this is the control interface then return the value to the + // caller. + // + if(psInterface->bInterfaceSubClass == USB_ASC_AUDIO_CONTROL) + { + ui32Interface = psInterface->bInterfaceNumber; + + break; + } + } + + // + // Decrease the bytes remaining by the size of this descriptor. + // + i32Bytes -= psHeader->bLength; + + // + // Move the pointer to the next header. + // + psHeader = (tDescriptorHeader*)((uint32_t)psHeader + + psHeader->bLength); + } + return(ui32Interface); +} + +//***************************************************************************** +// +// If it exists, finds the correct audio interface for a given audio format. +// +//***************************************************************************** +static uint32_t +AudioGetInterface(tUSBHostAudioInstance *psAudioDevice, uint16_t ui16Format, + uint32_t ui32SampleRate, uint32_t ui32Bytes, + uint32_t ui32Channels, uint32_t ui32Flags) +{ + tDescriptorHeader *psHeader; + tInterfaceDescriptor *psInterface; + tEndpointDescriptor *pINEndpoint, *pOUTEndpoint; + tACHeader *pACHeader; + tACGeneral *pGeneral; + tASFormat *pFormat; + tEndpointDescriptor *pEndpoint; + uint8_t *pui8Value; + uint32_t ui32Value; + int32_t i32Bytes, i32Idx; + + // + // Initialize the Interface pointer to null. + // + psInterface = 0; + pINEndpoint = 0; + pOUTEndpoint = 0; + + // + // Start at the top of the configuration descriptor. + // + psHeader = (tDescriptorHeader *)psAudioDevice->psDevice->psConfigDescriptor; + + i32Bytes = psAudioDevice->psDevice->psConfigDescriptor->wTotalLength; + + while(i32Bytes > 0) + { + if(psHeader->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(psInterface) + { + break; + } + + // + // Get the new interface pointer. + // + psInterface = (tInterfaceDescriptor *)psHeader; + + // + // 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((psInterface->bNumEndpoints == 0) || + (psInterface->bInterfaceClass != USB_CLASS_AUDIO) || + (psInterface->bInterfaceSubClass != USB_ASC_AUDIO_STREAMING)) + { + psInterface = 0; + } + } + if((psInterface) && + (psHeader->bDescriptorType == USB_DTYPE_CS_INTERFACE)) + { + pACHeader = (tACHeader *)psHeader; + + // + // 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 *)psHeader; + + // + // If this interface has the wrong format then set it to null + // so that the rest of this interface is ignored. + // + if(pGeneral->wFormatTag != ui16Format) + { + psInterface = 0; + } + } + else if(pACHeader->bDescriptorSubtype == USB_AS_FORMAT_TYPE) + { + pFormat = (tASFormat *)psHeader; + + // + // 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 != ui32Channels) || + (pFormat->bSubFrameSize != ui32Bytes)) + { + psInterface = 0; + } + else + { + pui8Value = &pFormat->tSamFreq; + + // + // Attempt to find the sample rate in the sample rate + // table for this interface. + // + for(i32Idx = 0; i32Idx < pFormat->bSamFreqType; i32Idx++) + { + ui32Value = (*((uint32_t *)&pui8Value[i32Idx * 3]) & + 0xffffff); + + if(ui32Value == ui32SampleRate) + { + 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(i32Idx == pFormat->bSamFreqType) + { + psInterface = 0; + } + } + } + } + else if((psInterface) && + (psHeader->bDescriptorType == USB_DTYPE_ENDPOINT)) + { + pEndpoint = (tEndpointDescriptor *)psHeader; + + // + // See what direction is being requested. + // + if(ui32Flags & 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) + { + psInterface = 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) + { + psInterface = 0; + } + else + { + // + // Save this endpoint as a possible valid endpoint; + // + pOUTEndpoint = pEndpoint; + } + } + } + } + + // + // Decrease the bytes remaining by the size of this descriptor. + // + i32Bytes -= psHeader->bLength; + + // + // Move the pointer to the next header. + // + psHeader = (tDescriptorHeader*)((uint32_t)psHeader + + psHeader->bLength); + } + + // + // If there is still a valid interface then return the values. + // + if(psInterface) + { + // + // Check a valid IN endpoint descriptor. + // + if(pINEndpoint) + { + // + // Save the endpoint address. + // + g_sAudioDevice.ui8IsochInAddress = 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_sAudioDevice.ui32IsochInPipe == 0) + { + // + // Allocate the USB Pipe for this Isochronous IN end point. + // + g_sAudioDevice.ui32IsochInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_IN_DMA, + g_sAudioDevice.psDevice, + pINEndpoint->wMaxPacketSize, + PipeCallbackIN); + } + else if(g_sAudioDevice.ui16PipeSizeIn < pINEndpoint->wMaxPacketSize) + { + // + // Free the old endpoint and allocate a new one. + // + USBHCDPipeFree(g_sAudioDevice.ui32IsochInPipe); + + // + // Allocate the USB Pipe for this Isochronous IN end point. + // + g_sAudioDevice.ui32IsochInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_IN_DMA, + g_sAudioDevice.psDevice, + pINEndpoint->wMaxPacketSize, + PipeCallbackIN); + + // + // Save the new size of the maximum packet size for this + // USB pipe. + // + g_sAudioDevice.ui16PipeSizeIn = pINEndpoint->wMaxPacketSize; + } + + // + // Configure the USB pipe as a Isochronous IN end point. + // + USBHCDPipeConfig(g_sAudioDevice.ui32IsochInPipe, + pINEndpoint->wMaxPacketSize, + 0, + g_sAudioDevice.ui8IsochInAddress); + } + + // + // Check a valid OUT endpoint descriptor. + // + if(pOUTEndpoint) + { + // + // Save the endpoint address. + // + g_sAudioDevice.ui8IsochOutAddress = + 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_sAudioDevice.ui32IsochOutPipe == 0) + { + // + // Allocate the USB Pipe for this Isochronous OUT end point. + // + g_sAudioDevice.ui32IsochOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_OUT_DMA, + g_sAudioDevice.psDevice, + pOUTEndpoint->wMaxPacketSize, + PipeCallbackOUT); + } + else if(g_sAudioDevice.ui16PipeSizeOut < + pOUTEndpoint->wMaxPacketSize) + { + // + // Free the old endpoint and allocate a new one. + // + USBHCDPipeFree(g_sAudioDevice.ui32IsochOutPipe); + + // + // Allocate the USB Pipe for this Isochronous OUT end point. + // + g_sAudioDevice.ui32IsochOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_OUT_DMA, + g_sAudioDevice.psDevice, + pOUTEndpoint->wMaxPacketSize, + PipeCallbackOUT); + + // + // Save the new size of the maximum packet size for this + // USB pipe. + // + g_sAudioDevice.ui16PipeSizeOut = pOUTEndpoint->wMaxPacketSize; + } + + // + // Configure the USB pipe as a Isochronous OUT end point. + // + USBHCDPipeConfig(g_sAudioDevice.ui32IsochOutPipe, + pOUTEndpoint->wMaxPacketSize, 0, + g_sAudioDevice.ui8IsochOutAddress); + } + + return(psInterface->bInterfaceNumber | + (psInterface->bAlternateSetting << INTERFACE_ALTSETTING_S)); + } + return(INVALID_INTERFACE); +} + +//***************************************************************************** +// +// This function is used to open an instance of the USB host audio driver. +// +// \param psDevice 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 psDevice 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 *psDevice) +{ + uint32_t ui32Temp; + tConfigDescriptor *psConfigDesc; + + // + // Don't allow the device to be opened without closing first. + // + if(g_sAudioDevice.psDevice) + { + return(0); + } + + // + // Save the Host device pointer. + // + g_sAudioDevice.psDevice = psDevice; + + // + // Save a shorter name for the configuration descriptor. + // + psConfigDesc = psDevice->psConfigDescriptor; + + // + // Find the input terminal. + // + g_sAudioDevice.psInTerminal = + (tACInputTerminal *)AudioTerminalGet(psConfigDesc, + USB_AI_INPUT_TERMINAL, + USB_TTYPE_STREAMING); + + // + // Find the output terminal. + // + g_sAudioDevice.psOutTerminal = + (tACOutputTerminal *)AudioTerminalGet(psConfigDesc, + USB_AI_OUTPUT_TERMINAL, + USB_TTYPE_STREAMING); + + // + // Find the feature unit. + g_sAudioDevice.psFeatureUnit = + (tACFeatureUnit *)AudioTerminalGet(psConfigDesc, + USB_AI_FEATURE_UNIT, 0); + + // + // Need some kind of terminal to send or receive audio from. + // + if((g_sAudioDevice.psOutTerminal == 0) && + (g_sAudioDevice.psInTerminal == 0)) + { + return(0); + } + + // + // Find the Audio control interface. + // + ui32Temp = AudioControlGet(psConfigDesc); + + if(ui32Temp == INVALID_INTERFACE) + { + return(0); + } + + // + // Save the control interface index and increment the number + // of interfaces that have been found. + // + g_sAudioDevice.ui8IControl = (uint8_t)ui32Temp; + + // + // If the call back exists, call it with an Open event. + // + if(g_sAudioDevice.pfnCallback != 0) + { + g_sAudioDevice.pfnCallback(&g_sAudioDevice, + USBH_AUDIO_EVENT_OPEN, 0, 0); + } + + // + // If a feature unit was found, save the ID + // + if(g_sAudioDevice.psFeatureUnit != 0) + { + g_sAudioDevice.ui8VolumeID = g_sAudioDevice.psFeatureUnit->bUnitID; + } + + // + // Allocate the USB Pipe for this Isochronous IN end point. + // + g_sAudioDevice.ui32IsochInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_IN_DMA, + g_sAudioDevice.psDevice, 256, PipeCallbackIN); + g_sAudioDevice.ui16PipeSizeIn = 256; + + // + // Allocate the USB Pipe for this Isochronous OUT end point. + // + g_sAudioDevice.ui32IsochOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_OUT_DMA, + g_sAudioDevice.psDevice, 256, PipeCallbackOUT); + g_sAudioDevice.ui16PipeSizeOut = 256; + + // + // Clear the flags. + // + g_sAudioDevice.ui32Flags = 0; + + // + // Return the only instance of this device. + // + return(&g_sAudioDevice); +} + +//***************************************************************************** +// +// This function is used to release an instance of the USB host audio driver. +// +// \param pvAudioDevice 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 pvAudioDevice pointer should be a +// valid value that was returned from a call to USBHostAudioOpen(). +// +// \return None. +// +//***************************************************************************** +static void +USBAudioClose(void *pvAudioDevice) +{ + tUSBHostAudioInstance *psAudioDevice; + + psAudioDevice = (tUSBHostAudioInstance *)pvAudioDevice; + + // + // Do nothing if there is not a driver open. + // + if(psAudioDevice->psDevice == 0) + { + return; + } + + // + // Reset the device pointer. + // + psAudioDevice->psDevice = 0; + + // + // Free the Isochronous IN pipe. + // + if(psAudioDevice->ui32IsochInPipe != 0) + { + USBHCDPipeFree(psAudioDevice->ui32IsochInPipe); + } + + // + // Free the Isochronous OUT pipe. + // + if(psAudioDevice->ui32IsochOutPipe != 0) + { + USBHCDPipeFree(psAudioDevice->ui32IsochOutPipe); + } + + // + // If the call back exists then call it. + // + if(psAudioDevice->pfnCallback != 0) + { + psAudioDevice->pfnCallback(psAudioDevice, USBH_AUDIO_EVENT_CLOSE, 0, + 0); + } +} + +//***************************************************************************** +// +//! This function should be called before any devices are present to enable +//! the host audio class driver. +//! +//! \param ui32Index 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. +// +//***************************************************************************** +tUSBHostAudioInstance * +USBHostAudioOpen(uint32_t ui32Index, 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((ui32Index != 0) || (g_sAudioDevice.pfnCallback)) + { + return(0); + } + + // + // Save the call back. + // + g_sAudioDevice.pfnCallback = pfnCallback; + + // + // Return the requested device instance. + // + return(&g_sAudioDevice); +} + +//***************************************************************************** +// +//! This function should be called to release an audio device instance. +//! +//! \param psAudioInstance 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(tUSBHostAudioInstance *psAudioInstance) +{ + // + // Close the audio device. + // + USBAudioClose(psAudioInstance); + + // + // Clear the call back indicating that the device is now closed. + // + psAudioInstance->pfnCallback = 0; +} + +//***************************************************************************** +// +// This function is used to request settings from a given audio interface. +// +// \param psAudioDevice is the audio device instance to access. +// \param ui32Interface is the interface to access. +// \param ui32Channel is the channel number to access. +// \param ui32Request 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 ui32Interface is the interface to +// make the request specified by \e ui32Channel and \e ui32Request. The +// \e ui32Request parameter must be one of the USB_AC_GET_* values. +// +// \return This function returns the requested value. +// +//***************************************************************************** +static uint32_t +VolumeSettingGet(tUSBHostAudioInstance *psAudioDevice, uint32_t ui32Interface, + uint32_t ui32Channel, uint32_t ui32Request) +{ + uint32_t ui32Value; + tUSBRequest sSetupPacket; + + ui32Value = 0; + + // + // This is a Class specific Interface IN request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = (ui32Request & 0xff); + + // + // Request for a string descriptor. + // + sSetupPacket.wValue = VOLUME_CONTROL | (ui32Channel & 0xff); + + // + // Set the language ID. + // + sSetupPacket.wIndex = (psAudioDevice->ui8VolumeID << 8) | + (ui32Interface & 0xff); + + // + // Only request the space available. + // + sSetupPacket.wLength = 2; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &sSetupPacket, psAudioDevice->psDevice, + (uint8_t *)&ui32Value, 4, + psAudioDevice->psDevice->sDeviceDescriptor.bMaxPacketSize0); + + return(ui32Value); +} + +//***************************************************************************** +// +//! This function is used to get the current volume setting for a given +//! audio device. +//! +//! \param psAudioInstance is an instance of the USB audio device. +//! \param ui32Interface is the interface number to use to query the current +//! volume setting. +//! \param ui32Channel 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 ui32Channel. The \e ui32Interface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ui32Channel value starts with 0 which is the master +//! audio volume control interface. The remaining \e ui32Channel 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. +// +//***************************************************************************** +uint32_t +USBHostAudioVolumeGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel) +{ + return(VolumeSettingGet(psAudioInstance, ui32Interface, ui32Channel, + USB_AC_GET_CUR)); +} + +//***************************************************************************** +// +//! This function is used to get the maximum volume setting for a given +//! audio device. +//! +//! \param psAudioInstance is an instance of the USB audio device. +//! \param ui32Interface is the interface number to use to query the maximum +//! volume control value. +//! \param ui32Channel 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 ui32Channel. The \e ui32Interface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ui32Channel value starts with 0 which is the master +//! audio volume control interface. The remaining \e ui32Channel 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. +// +//***************************************************************************** +uint32_t +USBHostAudioVolumeMaxGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel) +{ + return(VolumeSettingGet(psAudioInstance, ui32Interface, ui32Channel, + USB_AC_GET_MAX)); +} + +//***************************************************************************** +// +//! This function is used to get the minimum volume setting for a given +//! audio device. +//! +//! \param psAudioInstance is an instance of the USB audio device. +//! \param ui32Interface is the interface number to use to query the minimum +//! volume control value. +//! \param ui32Channel 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 ui32Channel. The \e ui32Interface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ui32Channel value starts with 0 which is the master +//! audio volume control interface. The remaining \e ui32Channel 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. +// +//***************************************************************************** +uint32_t +USBHostAudioVolumeMinGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel) +{ + return(VolumeSettingGet(psAudioInstance, ui32Interface, ui32Channel, + USB_AC_GET_MIN)); +} + +//***************************************************************************** +// +//! This function is used to get the volume control resolution for a given +//! audio device. +//! +//! \param psAudioInstance is an instance of the USB audio device. +//! \param ui32Interface is the interface number to use to query the resolution +//! for the volume control. +//! \param ui32Channel 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 ui32Channel. The \e ui32Interface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ui32Channel value starts with 0 which is the master +//! audio volume control interface. The remaining \e ui32Channel 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. +// +//***************************************************************************** +uint32_t +USBHostAudioVolumeResGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel) +{ + return(VolumeSettingGet(psAudioInstance, ui32Interface, ui32Channel, + USB_AC_GET_RES)); +} + +//***************************************************************************** +// +//! This function is used to set the current volume setting for a given +//! audio device. +//! +//! \param psAudioInstance is an instance of the USB audio device. +//! \param ui32Interface is the interface number to use to set the current +//! volume setting. +//! \param ui32Channel is the 0 based channel number to query. +//! \param ui32Value 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 ui32Channel. The \e ui32Interface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ui32Channel value starts with 0 which is the master +//! audio volume control interface. The remaining \e ui32Channel 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(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel, + uint32_t ui32Value) +{ + tUSBRequest sSetupPacket; + + // + // This is a Class specific Interface OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE; + + // + // Request is to set the current value. + // + sSetupPacket.bRequest = USB_AC_SET_CUR; + + // + // Request the volume control. + // + sSetupPacket.wValue = VOLUME_CONTROL | (ui32Channel & 0xff); + + // + // Set Volume control ID and interface to 0. + // + sSetupPacket.wIndex = psAudioInstance->ui8VolumeID << 8; + + // + // Only request the space available. + // + sSetupPacket.wLength = 2; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &sSetupPacket, psAudioInstance->psDevice, + (uint8_t *)&ui32Value, 2, + psAudioInstance->psDevice->sDeviceDescriptor.bMaxPacketSize0); +} + +//***************************************************************************** +// +//! This function is called to determine if an audio format is supported by the +//! connected USB Audio device. +//! +//! \param psAudioInstance is the device instance for this call. +//! \param ui32SampleRate is the sample rate of the audio stream. +//! \param ui32Bits is the number of bits per sample in the audio stream. +//! \param ui32Channels is the number of channels in the audio stream. +//! \param ui32Flags 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 psAudioInstance 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 ui32SampleRate, \e ui32Bits, and \e ui32Channels values. The +//! \e ui32Flags 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. +// +//***************************************************************************** +uint32_t +USBHostAudioFormatGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32SampleRate, uint32_t ui32Bits, + uint32_t ui32Channels, uint32_t ui32Flags) +{ + // + // Look for the requested format. + // + if(AudioGetInterface(psAudioInstance, USB_ADF_PCM, ui32SampleRate, + ui32Bits >> 3, ui32Channels, ui32Flags) != + INVALID_INTERFACE) + { + return(0); + } + return(1); +} + +//***************************************************************************** +// +//! This function is called to set the current sample rate on an audio +//! interface. +//! +//! \param psAudioInstance specifies the device instance for this call. +//! \param ui32SampleRate is the sample rate in Hz. +//! \param ui32Bits is the number of bits per sample. +//! \param ui32Channels is then number of audio channels. +//! \param ui32Flags 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 psAudioInstance 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. +// +//***************************************************************************** +uint32_t +USBHostAudioFormatSet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32SampleRate, uint32_t ui32Bits, + uint32_t ui32Channels, uint32_t ui32Flags) +{ + uint32_t ui32Interface; + + // + // Look for the requested format. + // + ui32Interface = AudioGetInterface(psAudioInstance, USB_ADF_PCM, + ui32SampleRate, ui32Bits >> 3, + ui32Channels, ui32Flags); + + if(ui32Interface == INVALID_INTERFACE) + { + return(1); + } + + // + // Determine if this is an input or output request. + // + if(ui32Flags & USBH_AUDIO_FORMAT_IN) + { + // + // Get the active interface number and alternate setting for this + // format. + // + psAudioInstance->ui8InInterface = + (uint8_t)(ui32Interface & INTERFACE_NUM_M); + psAudioInstance->ui8InAltSetting = + (uint8_t)((ui32Interface & INTERFACE_ALTSETTING_M) >> + INTERFACE_ALTSETTING_S); + } + else + { + // + // Get the active interface number and alternate setting for this + // format. + // + psAudioInstance->ui8OutInterface = + (uint8_t)(ui32Interface & INTERFACE_NUM_M); + psAudioInstance->ui8OutAltSetting = + (uint8_t)((ui32Interface & INTERFACE_ALTSETTING_M) >> + INTERFACE_ALTSETTING_S); + } + return(0); +} + +//***************************************************************************** +// +//! This function is called to send an audio buffer to the USB audio device. +//! +//! \param psAudioInstance specifies the device instance for this call. +//! \param pvBuffer is the audio buffer to send. +//! \param ui32Size 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 ui32Param can be ignored and the \e ui32Event 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. +// +//***************************************************************************** +int32_t +USBHostAudioPlay(tUSBHostAudioInstance *psAudioInstance, void *pvBuffer, + uint32_t ui32Size, tUSBHostAudioCallback pfnCallback) +{ + uint32_t ui32Bytes; + + // + // Make sure that there is a device present. + // + if(psAudioInstance->psDevice == 0) + { + return(0); + } + + // + // If the audio output interface is not active then select the current + // active audio interface. + // + if(HWREGBITW(&psAudioInstance->ui32Flags, AUDIO_FLAG_OUT_ACTIVE) == 0) + { + // + // Indicate the active audio interface has been selected. + // + HWREGBITW(&psAudioInstance->ui32Flags, AUDIO_FLAG_OUT_ACTIVE) = 1; + + // + // Configure the USB audio device to use the selected audio interface. + // + USBHCDSetInterface(0, (uint32_t)psAudioInstance->psDevice, + psAudioInstance->ui8OutInterface, + psAudioInstance->ui8OutAltSetting); + } + + // + // Save the callback function and the buffer pointer. + // + psAudioInstance->pfnOutCallback = pfnCallback; + psAudioInstance->pvOutBuffer = (void *)pvBuffer; + + // + // Schedule the data to be written out to the FIFO. + // + ui32Bytes = USBHCDPipeSchedule(psAudioInstance->ui32IsochOutPipe, pvBuffer, + ui32Size); + + // + // Return the number of bytes scheduled to be sent. + // + return(ui32Bytes); +} + +//***************************************************************************** +// +//! This function is called to provide an audio buffer to the USB audio device +//! for audio input. +//! +//! \param psAudioInstance specifies the device instance for this call. +//! \param pvBuffer is the audio buffer to send. +//! \param ui32Size 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 ui32Param is +//! the number of valid bytes in the pvBuffer and the \e ui32Event 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. +// +//***************************************************************************** +int32_t +USBHostAudioRecord(tUSBHostAudioInstance *psAudioInstance, void *pvBuffer, + uint32_t ui32Size, tUSBHostAudioCallback pfnCallback) +{ + uint32_t ui32Bytes; + + // + // Make sure that there is a device present. + // + if(psAudioInstance->psDevice == 0) + { + return(0); + } + + // + // If the audio input interface is not active then select the current + // active audio interface. + // + if(HWREGBITW(&psAudioInstance->ui32Flags, AUDIO_FLAG_IN_ACTIVE) == 0) + { + // + // Indicate the active audio interface has been selected. + // + HWREGBITW(&psAudioInstance->ui32Flags, AUDIO_FLAG_IN_ACTIVE) = 1; + + // + // Configure the USB audio device to use the selected audio interface. + // + USBHCDSetInterface(0, (uint32_t)psAudioInstance->psDevice, + psAudioInstance->ui8InInterface, + psAudioInstance->ui8InAltSetting); + } + + // + // Save the callback function and the buffer pointer. + // + psAudioInstance->pfnInCallback = pfnCallback; + psAudioInstance->pvInBuffer = (void *)pvBuffer; + + // + // Schedule the data to be read from the FIFO. + // + ui32Bytes = USBHCDPipeSchedule(psAudioInstance->ui32IsochInPipe, pvBuffer, + ui32Size); + + // + // Return the number of bytes scheduled to be sent. + // + return(ui32Bytes); +} + +//***************************************************************************** +// +//! This function forwards an LPM request for a device to enter L1 sleep state. +//! +//! \param psAudioInstance is the audio device instance that was returned +//! from the call to USBHostAudioOpen(). +//! +//! This function forwards a request from an application to the audio device +//! class to request that a device enter the LPM L1 sleep state. The +//! caller must check the return value to see if the request can be +//! attempted at this time. If another LPM transaction is busy on this or +//! another device, then this function returns \b USBHCD_LPM_PENDING. If +//! the LPM request was scheduled to be sent the function returns +//! \b USBHCD_LPM_AVAIL. The caller should check the USBHCDLPMStatus() +//! function to determine if the request completed successfully or if there +//! was an error. +//! +//! \return This function returns the following values: +//! - \b USBHCD_LPM_AVAIL - The transition to L1 state is scheduled to be sent. +//! - \b USBHCD_LPM_PENDING - There is already an LPM request pending. +// +//***************************************************************************** +uint32_t +USBHostAudioLPMSleep(tUSBHostAudioInstance *psAudioInstance) +{ + // + // Call the host controller function to send the sleep command. + // + return(USBHCDLPMSleep(psAudioInstance->psDevice)); +} + +//***************************************************************************** +// +//! This function returns the current status of an LPM request. +//! +//! \param psAudioInstance is the audio device instance that was returned +//! from the call to USBHostAudioOpen(). +//! +//! This function returns the current status of LPM requests for a given +//! device. This is called to determine if a previous request completed +//! successfully or if there was an error. +//! +//! \return This function returns the following values: +//! - \b USBHCD_LPM_AVAIL - There are no pending LPM requests on this specific +//! device or the last request completed successfully. +//! - \b USBHCD_LPM_ERROR - The last LPM request for this device did not +//! complete successfully. +//! - \b USBHCD_LPM_PENDING - The last LPM request has not completed. +// +//***************************************************************************** +uint32_t +USBHostAudioLPMStatus(tUSBHostAudioInstance *psAudioInstance) +{ + // + // Call the host controller function to get the current LPM status. + // + return(USBHCDLPMStatus(psAudioInstance->psDevice)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + diff --git a/usblib/host/usbhaudio.h b/usblib/host/usbhaudio.h new file mode 100644 index 0000000..fc768ee --- /dev/null +++ b/usblib/host/usbhaudio.h @@ -0,0 +1,163 @@ +//***************************************************************************** +// +// usbhaudio.h - USB host audio class driver. +// +// Copyright (c) 2010-2014 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 2.1.0.12573 of the Tiva 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 +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// 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 ui32Param +//! 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 ui32Param 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 +{ + uint8_t ui8Channels; + uint8_t ui8Bits; + uint32_t ui32SampleRate; +} +tUSBAudioFormat; + +typedef struct tUSBHostAudioInstance tUSBHostAudioInstance; + +//***************************************************************************** +// +// The prototype for the host USB Audio driver callback function. +// +//***************************************************************************** +typedef void (*tUSBHostAudioCallback)(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Event, + uint32_t ui32MsgParam, + void *pvMsgData); + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern tUSBHostAudioInstance * USBHostAudioOpen(uint32_t ui32Index, + tUSBHostAudioCallback pfnCallback); +extern void USBHostAudioClose(tUSBHostAudioInstance *psAudioInstance); +extern int32_t USBHostAudioPlay(tUSBHostAudioInstance *psAudioInstance, + void *pvBuffer, uint32_t ui32Size, + tUSBHostAudioCallback pfnCallback); + +extern uint32_t USBHostAudioFormatGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32SampleRate, + uint32_t ui32Bits, uint32_t ui32Channels, + uint32_t ui32Flags); +extern uint32_t USBHostAudioFormatSet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32SampleRate, + uint32_t ui32Bits, uint32_t ui32Channels, + uint32_t ui32Flags); + +extern int32_t USBHostAudioRecord(tUSBHostAudioInstance *psAudioInstance, + void *pvBuffer, uint32_t ui32Size, + tUSBHostAudioCallback pfnAudioCallback); + +extern uint32_t USBHostAudioVolumeGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, + uint32_t ui32Channel); + +extern void USBHostAudioVolumeSet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel, + uint32_t ui32Value); + +extern uint32_t USBHostAudioVolumeMaxGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, + uint32_t ui32Channel); + +extern uint32_t USBHostAudioVolumeMinGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, + uint32_t ui32Channel); + +extern uint32_t USBHostAudioVolumeResGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, + uint32_t ui32Channel); +extern uint32_t USBHostAudioLPMSleep(tUSBHostAudioInstance *psAudioInstance); +extern uint32_t USBHostAudioLPMStatus(tUSBHostAudioInstance *psAudioInstance); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// 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..bfbea85 --- /dev/null +++ b/usblib/host/usbhhid.c @@ -0,0 +1,746 @@ +//***************************************************************************** +// +// usbhhid.c - This file contains the host HID driver. +// +// Copyright (c) 2008-2014 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 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/usbhid.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhostpriv.h" +#include "usblib/host/usbhhid.h" + +static void * HIDDriverOpen(tUSBHostDevice *psDevice); +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. +// +//***************************************************************************** +struct tHIDInstance +{ + // + // Save the device instance. + // + tUSBHostDevice *psDevice; + + // + // Used to save the callback. + // + tUSBCallback pfnCallback; + + // + // Callback data provided by caller. + // + void *pvCBData; + + // + // Used to remember what type of device was registered. + // + tHIDSubClassProtocol iDeviceType; + + // + // Interrupt IN pipe. + // + uint32_t ui32IntInPipe; +}; + +//***************************************************************************** +// +// The instance data storage for attached hid devices. +// +//***************************************************************************** +static tHIDInstance g_psHIDDevice[MAX_HID_DEVICES]; + +//***************************************************************************** +// +//! This constant global structure defines the HID Class Driver that is +//! provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_sUSBHIDClassDriver = +{ + USB_CLASS_HID, + HIDDriverOpen, + HIDDriverClose, + 0 +}; + +//***************************************************************************** +// +//! This function is used to open an instance of a HID device. +//! +//! \param iDeviceType 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 pvCBData is the data that will be returned in when the +//! \e pfnCallback function is called. +//! +//! This function creates an instance of an specific type of HID device. The +//! \e iDeviceType 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. +// +//***************************************************************************** +tHIDInstance * +USBHHIDOpen(tHIDSubClassProtocol iDeviceType, tUSBCallback pfnCallback, + void *pvCBData) +{ + uint32_t ui32Loop; + + // + // Find a free device instance structure. + // + for(ui32Loop = 0; ui32Loop < MAX_HID_DEVICES; ui32Loop++) + { + if(g_psHIDDevice[ui32Loop].iDeviceType == eUSBHHIDClassNone) + { + // + // Save the instance data for this device. + // + g_psHIDDevice[ui32Loop].pfnCallback = pfnCallback; + g_psHIDDevice[ui32Loop].iDeviceType = iDeviceType; + g_psHIDDevice[ui32Loop].pvCBData = pvCBData; + + // + // Return the device instance pointer. + // + return(&g_psHIDDevice[ui32Loop]); + } + } + + // + // 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 psHIDInstance 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 psHIDInstance parameter should hold the value that was returned from +//! the previous call to USBHHIDOpen(). +//! +//! \return None. +// +//***************************************************************************** +void +USBHHIDClose(tHIDInstance *psHIDInstance) +{ + // + // Disable any more notifications from the HID layer. + // + psHIDInstance->pfnCallback = 0; + + // + // Mark this device slot as free. + // + psHIDInstance->iDeviceType = eUSBHHIDClassNone; +} + +//***************************************************************************** +// +// This function handles callbacks for the interrupt IN endpoint. +// +//***************************************************************************** +static void +HIDIntINCallback(uint32_t ui32Pipe, uint32_t ui32Event) +{ + int32_t i32Dev; + + switch (ui32Event) + { + // + // Handles a request to schedule a new request on the interrupt IN + // pipe. + // + case USB_EVENT_SCHEDULER: + { + USBHCDPipeSchedule(ui32Pipe, 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(i32Dev = 0; i32Dev < MAX_HID_DEVICES; i32Dev++) + { + // + // Does this device own the pipe we have been passed? + // + if(g_psHIDDevice[i32Dev].ui32IntInPipe == ui32Pipe) + { + // + // Yes - send the report data to the USB host HID device + // class driver. + // + g_psHIDDevice[i32Dev].pfnCallback( + g_psHIDDevice[i32Dev].pvCBData, + USB_EVENT_RX_AVAILABLE, ui32Pipe, 0); + } + } + + break; + } + } +} + +//***************************************************************************** +// +//! This function is used to open an instance of the HID driver. +//! +//! \param psDevice 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 psDevice 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 *psDevice) +{ + int32_t i32Idx, i32Dev; + tEndpointDescriptor *psEndpointDescriptor; + tInterfaceDescriptor *psInterface; + + // + // Get the interface descriptor. + // + psInterface = USBDescGetInterface(psDevice->psConfigDescriptor, 0, 0); + + // + // Search the currently open instances for one that supports the protocol + // of this device. + // + for(i32Dev = 0; i32Dev < MAX_HID_DEVICES; i32Dev++) + { + if(g_psHIDDevice[i32Dev].iDeviceType == + psInterface->bInterfaceProtocol) + { + // + // Save the device pointer. + // + g_psHIDDevice[i32Dev].psDevice = psDevice; + + for(i32Idx = 0; i32Idx < 3; i32Idx++) + { + // + // Get the first endpoint descriptor. + // + psEndpointDescriptor = USBDescGetInterfaceEndpoint(psInterface, + i32Idx, + 256); + + // + // If no more endpoints then break out. + // + if(psEndpointDescriptor == 0) + { + break; + } + + // + // Interrupt + // + if((psEndpointDescriptor->bmAttributes & USB_EP_ATTR_TYPE_M) == + USB_EP_ATTR_INT) + { + // + // Interrupt IN. + // + if(psEndpointDescriptor->bEndpointAddress & USB_EP_DESC_IN) + { + g_psHIDDevice[i32Dev].ui32IntInPipe = + USBHCDPipeAlloc(0, USBHCD_PIPE_INTR_IN, + psDevice, HIDIntINCallback); + USBHCDPipeConfig(g_psHIDDevice[i32Dev].ui32IntInPipe, + psEndpointDescriptor->wMaxPacketSize, + psEndpointDescriptor->bInterval, + (psEndpointDescriptor->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_psHIDDevice[i32Dev].pfnCallback != 0) + { + g_psHIDDevice[i32Dev].pfnCallback( + g_psHIDDevice[i32Dev].pvCBData, + USB_EVENT_CONNECTED, + (uint32_t)&g_psHIDDevice[i32Dev], 0); + } + + // + // Save the device pointer. + // + g_psHIDDevice[i32Dev].psDevice = psDevice; + + return (&g_psHIDDevice[i32Dev]); + } + } + + // + // 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 *psInst; + + // + // Get our instance pointer. + // + psInst = (tHIDInstance *)pvInstance; + + // + // Reset the device pointer. + // + psInst->psDevice = 0; + + // + // Free the Interrupt IN pipe. + // + if(psInst->ui32IntInPipe != 0) + { + USBHCDPipeFree(psInst->ui32IntInPipe); + } + + // + // If the callback exists, call it with a DISCONNECTED event. + // + if(psInst->pfnCallback != 0) + { + psInst->pfnCallback(psInst->pvCBData, USB_EVENT_DISCONNECTED, + (uint32_t)pvInstance, 0); + } +} + +//***************************************************************************** +// +//! This function is used to set the idle timeout for a HID device. +//! +//! \param psHIDInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ui8Duration is the duration of the timeout in milliseconds. +//! \param ui8ReportID 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 ui8Duration parameter and the report the timeout for is in the +//! \e ui8ReportID value. +//! +//! \return Always returns 0. +// +//***************************************************************************** +uint32_t +USBHHIDSetIdle(tHIDInstance *psHIDInstance, uint8_t ui8Duration, + uint8_t ui8ReportID) +{ + tUSBRequest sSetupPacket; + + // + // This is a Class specific interface OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_SET_IDLE; + sSetupPacket.wValue = (ui8Duration << 8) | ui8ReportID; + + // + // Set this on interface 1. + // + sSetupPacket.wIndex = 0; + + // + // This is always 0 for this request. + // + sSetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + return(USBHCDControlTransfer(0, &sSetupPacket, psHIDInstance->psDevice, + 0, 0, MAX_PACKET_SIZE_EP0)); +} + +//***************************************************************************** +// +//! This function can be used to retrieve the report descriptor for a given +//! device instance. +//! +//! \param psHIDInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param pui8Buffer is the memory buffer to use to store the report +//! descriptor. +//! \param ui32Size is the size in bytes of the buffer pointed to by +//! \e pui8Buffer. +//! +//! 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 psHIDInstance parameter. +//! This call is blocking and will return the number of bytes read into the +//! \e pui8Buffer. +//! +//! \return Returns the number of bytes read into the \e pui8Buffer. +// +//***************************************************************************** +uint32_t +USBHHIDGetReportDescriptor(tHIDInstance *psHIDInstance, uint8_t *pui8Buffer, + uint32_t ui32Size) +{ + tUSBRequest sSetupPacket; + uint32_t ui32Bytes; + + // + // This is a Standard Device IN request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_STANDARD | + USB_RTYPE_INTERFACE; + + // + // Request a Report Descriptor. + // + sSetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + sSetupPacket.wValue = USB_HID_DTYPE_REPORT << 8; + + // + // Index is always 0 for device requests. + // + sSetupPacket.wIndex = 0; + + // + // All devices must have at least an 8 byte max packet size so just ask + // for 8 bytes to start with. + // + sSetupPacket.wLength = ui32Size; + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + ui32Bytes = USBHCDControlTransfer(0, &sSetupPacket, + psHIDInstance->psDevice, pui8Buffer, ui32Size, + psHIDInstance->psDevice->sDeviceDescriptor.bMaxPacketSize0); + + return(ui32Bytes); +} + +//***************************************************************************** +// +//! This function is used to set or clear the boot protocol state of a device. +//! +//! \param psHIDInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ui32BootProtocol 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. +// +//***************************************************************************** +uint32_t +USBHHIDSetProtocol(tHIDInstance *psHIDInstance, uint32_t ui32BootProtocol) +{ + tUSBRequest sSetupPacket; + + // + // This is a Standard Device IN request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE; + + // + // Request a Report Descriptor. + // + sSetupPacket.bRequest = USBREQ_SET_PROTOCOL; + + if(ui32BootProtocol) + { + // + // Boot Protocol. + // + sSetupPacket.wValue = 0; + } + else + { + // + // Report Protocol. + // + sSetupPacket.wValue = 1; + } + + // + // Index is always 0 for device requests. + // + sSetupPacket.wIndex = 0; + + // + // Always 0. + // + sSetupPacket.wLength = 0; + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + USBHCDControlTransfer(0, &sSetupPacket, psHIDInstance->psDevice, 0, 0, + psHIDInstance->psDevice->sDeviceDescriptor.bMaxPacketSize0); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to retrieve a report from a HID device. +//! +//! \param psHIDInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ui32Interface is the interface to retrieve the report from. +//! \param pui8Data is the memory buffer to use to store the report. +//! \param ui32Size is the size in bytes of the buffer pointed to by +//! \e pui8Buffer. +//! +//! 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. +// +//***************************************************************************** +uint32_t +USBHHIDGetReport(tHIDInstance *psHIDInstance, uint32_t ui32Interface, + uint8_t *pui8Data, uint32_t ui32Size) +{ + // + // Read the Data out. + // + ui32Size = USBHCDPipeReadNonBlocking(psHIDInstance->ui32IntInPipe, + pui8Data, ui32Size); + + // + // Return the number of bytes read from the interrupt in pipe. + // + return(ui32Size); +} + +//***************************************************************************** +// +//! This function is used to send a report to a HID device. +//! +//! \param psHIDInstance is the value that was returned from the call to +//! USBHHIDOpen(). +//! \param ui32Interface is the interface to send the report to. +//! \param pui8Data is the memory buffer to use to store the report. +//! \param ui32Size is the size in bytes of the buffer pointed to by +//! \e pui8Buffer. +//! +//! 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. +// +//***************************************************************************** +uint32_t +USBHHIDSetReport(tHIDInstance *psHIDInstance, uint32_t ui32Interface, + uint8_t *pui8Data, uint32_t ui32Size) +{ + tUSBRequest sSetupPacket; + + // + // This is a class specific OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE; + + // + // Request a Report Descriptor. + // + sSetupPacket.bRequest = USBREQ_SET_REPORT; + sSetupPacket.wValue = USB_HID_REPORT_OUTPUT << 8; + + // + // Index is always 0 for device requests. + // + sSetupPacket.wIndex = (uint16_t)ui32Interface; + + // + // Always 0. + // + sSetupPacket.wLength = ui32Size; + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + USBHCDControlTransfer(0, &sSetupPacket, psHIDInstance->psDevice, + pui8Data, ui32Size, + psHIDInstance->psDevice->sDeviceDescriptor.bMaxPacketSize0); + + return(ui32Size); +} + +//***************************************************************************** +// +//! This function forwards an LPM request for a device to enter L1 sleep state. +//! +//! \param psHIDInstance is the HID instance that was returned from the call +//! to USBHHIDOpen(). +//! +//! This function forwards a request from a HID device class to the host +//! controller to request that a device enter the LPM L1 sleep state. The +//! caller must check the return value to see if the request can be +//! attempted at this time. If another LPM transaction is busy on this or +//! another device, then this function returns \b USBHCD_LPM_PENDING. If +//! the LPM request was scheduled to be sent the function returns +//! \b USBHCD_LPM_AVAIL. The caller should check the USBHCDLPMStatus() +//! function to determine if the request completed successfully or if there +//! was an error. +//! +//! \return This function returns the following values: +//! - USBHCD_LPM_AVAIL - The transition to L1 state is scheduled to be sent. +//! - USBHCD_LPM_PENDING - There is already an LPM request pending. +// +//***************************************************************************** +uint32_t +USBHHIDLPMSleep(tHIDInstance *psHIDInstance) +{ + // + // Forward the request to the control endpoint of the device. + // + return(USBHCDLPMSleep(psHIDInstance->psDevice)); +} + +//***************************************************************************** +// +//! This function returns the current status of an LPM request. +//! +//! \param psHIDInstance is the HID instance that was returned from the call +//! to USBHHIDOpen(). +//! +//! This function returns the current status of LPM requests for a given +//! device. This is called to determine if a previous request completed +//! successfully or if there was an error. +//! +//! \return This function returns the following values: +//! - \b USBHCD_LPM_AVAIL - There are no pending LPM requests on this specific +//! device or the last request completed successfully. +//! - \b USBHCD_LPM_ERROR - The last LPM request for this device did not +//! complete successfully. +//! - \b USBHCD_LPM_PENDING - The last LPM request has not completed. +// +//***************************************************************************** +uint32_t +USBHHIDLPMStatus(tHIDInstance *psHIDInstance) +{ + // + // Call the host controller function to get the current LPM status. + // + return(USBHCDLPMStatus(psHIDInstance->psDevice)); +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhhid.h b/usblib/host/usbhhid.h new file mode 100644 index 0000000..f2df536 --- /dev/null +++ b/usblib/host/usbhhid.h @@ -0,0 +1,166 @@ +//***************************************************************************** +// +// usbhhid.h - This hold the host driver for hid class. +// +// Copyright (c) 2008-2014 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 2.1.0.12573 of the Tiva 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 +//! @{ +// +//***************************************************************************** + +typedef struct tHIDInstance tHIDInstance; + +//***************************************************************************** +// +// These defines are the the events that will be passed in the ui32Event +// 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. + // + eUSBHHIDClassNone = 0, + + // + //! This is a keyboard device. + // + eUSBHHIDClassKeyboard, + + // + //! This is a mouse device. + // + eUSBHHIDClassMouse, + + // + //! This is a vendor specific device. + // + eUSBHHIDClassVendor +} +tHIDSubClassProtocol; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Prototypes. +// +//***************************************************************************** +extern tHIDInstance * USBHHIDOpen(tHIDSubClassProtocol iDeviceType, + tUSBCallback pfnCallback, + void *pvCBData); +extern void USBHHIDClose(tHIDInstance *psHIDInstance); +extern uint32_t USBHHIDGetReportDescriptor(tHIDInstance *psHIDInstance, + uint8_t *pui8Buffer, + uint32_t ui32Size); +extern uint32_t USBHHIDSetIdle(tHIDInstance *psHIDInstance, uint8_t ui8Duration, + uint8_t ui8ReportID); +extern uint32_t USBHHIDSetProtocol(tHIDInstance *psHIDInstance, + uint32_t ui32BootProtocol); +extern uint32_t USBHHIDSetReport(tHIDInstance *psHIDInstance, + uint32_t ui32Interface, uint8_t *pui8Data, + uint32_t ui32Size); +extern uint32_t USBHHIDGetReport(tHIDInstance *psHIDInstance, + uint32_t ui32Interface, uint8_t *pui8Data, + uint32_t ui32Size); +extern uint32_t USBHHIDLPMSleep(tHIDInstance *psHIDInstance); +extern uint32_t USBHHIDLPMStatus(tHIDInstance *psHIDInstance); + +extern const tUSBHostClassDriver g_sUSBHIDClassDriver; + +//***************************************************************************** +// +// 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..08f2b6a --- /dev/null +++ b/usblib/host/usbhhidkeyboard.c @@ -0,0 +1,752 @@ +//***************************************************************************** +// +// usbhhidkeyboard.c - This file holds the application interfaces for USB +// keyboard devices. +// +// Copyright (c) 2008-2014 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 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#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 uint32_t USBHKeyboardCallback(void *pvKeyboard, uint32_t ui32Event, + uint32_t ui32MsgParam, void *pvMsgData); + +//***************************************************************************** +// +// The size of a USB keyboard report. +// +//***************************************************************************** +#define USBHKEYB_REPORT_SIZE 8 + +//***************************************************************************** +// +// These are the flags for the tUSBHKeyboard.ui32HIDFlags member variable. +// +//***************************************************************************** +#define USBHKEYB_DEVICE_PRESENT 0x00000001 + +//***************************************************************************** +// +// This is the structure definition for a keyboard device instance. +// +//***************************************************************************** +struct tUSBHKeyboard +{ + // + // Global flags for an instance of a keyboard. + // + uint32_t ui32HIDFlags; + + // + // The applications registered callback. + // + tUSBHIDKeyboardCallback pfnCallback; + + // + // The HID instance pointer for this keyboard instance. + // + tHIDInstance *psHIDInstance; + + // + // NUM_LOCK, CAPS_LOCK, SCROLL_LOCK, COMPOSE or KANA keys. + // + uint8_t ui8KeyModSticky; + + // + // This is the current state of the keyboard modifier keys. + // + uint8_t ui8KeyModState; + + // + // This holds the keyboard usage codes for keys that are being held down. + // + uint8_t pui8KeyState[6]; + + // + // This is a local buffer to hold the current HID report that comes up + // from the HID driver layer. + // + uint8_t pui8Buffer[USBHKEYB_REPORT_SIZE]; +}; + +//***************************************************************************** +// +// 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 pui8Buffer is the memory used by the keyboard to interact with the +//! USB keyboard. +//! \param ui32Size is the size of the buffer provided by \e pui8Buffer. +//! +//! 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 pui8Buffer 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. +// +//***************************************************************************** +tUSBHKeyboard * +USBHKeyboardOpen(tUSBHIDKeyboardCallback pfnCallback, uint8_t *pui8Buffer, + uint32_t ui32Size) +{ + // + // Save the callback and data pointers. + // + g_sUSBHKeyboard.pfnCallback = pfnCallback; + + // + // Save the instance pointer for the HID device that was opened. + // + g_sUSBHKeyboard.psHIDInstance = + USBHHIDOpen(eUSBHHIDClassKeyboard, USBHKeyboardCallback, + (void *)&g_sUSBHKeyboard); + + return(&g_sUSBHKeyboard); +} + +//***************************************************************************** +// +//! This function is used close an instance of a keyboard. +//! +//! \param psKbInstance 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 psKbInstance 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. +// +//***************************************************************************** +uint32_t +USBHKeyboardClose(tUSBHKeyboard *psKbInstance) +{ + // + // Reset the callback to null. + // + psKbInstance->pfnCallback = 0; + + // + // Call the HID driver layer to close out this instance. + // + USBHHIDClose(psKbInstance->psHIDInstance); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to map a USB usage ID to a printable character. +//! +//! \param psKbInstance is the instance value for this keyboard. +//! \param psTable is the table to use to map the usage ID to characters. +//! \param ui8UsageID 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 psTable 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 +//! psTable structure has values indicating which keys are modified by Caps +//! and alternate values for shifted cases. The number of bytes returned from +//! Lock this function depends on the \e psTable 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. +// +//***************************************************************************** +uint32_t +USBHKeyboardUsageToChar(tUSBHKeyboard *psKbInstance, + const tHIDKeyboardUsageTable *psTable, + uint8_t ui8UsageID) +{ + uint32_t ui32Value, ui32Offset, ui32Shift; + const uint8_t *pui8KeyBoardMap; + const uint16_t *pui16KeyBoardMap; + + // + // The added offset for the shifted character value. + // + ui32Shift = 0; + + // + // Offset in the table for the character. + // + ui32Offset = (ui8UsageID * psTable->ui8BytesPerChar * 2); + + // + // Handle the case where CAPS lock has been set. + // + if(psKbInstance->ui8KeyModSticky &= HID_KEYB_CAPS_LOCK) + { + // + // See if this usage ID is modified by Caps Lock by checking the packed + // bit array in the pui32ShiftState member of the psTable array. + // + if((psTable->pui32CapsLock[ui8UsageID >> 5]) >> + (ui8UsageID & 0x1f) & 1) + { + ui32Shift = psTable->ui8BytesPerChar; + } + } + + // + // Now handle if a shift key is being held. + // + if((psKbInstance->ui8KeyModState & 0x22) != 0) + { + // + // Not shifted yet so we need to shift. + // + if(ui32Shift == 0) + { + ui32Shift = psTable->ui8BytesPerChar; + } + else + { + // + // Unshift because CAPS LOCK and shift were pressed. + // + ui32Shift = 0; + } + } + + // + // One byte per character. + // + if(psTable->ui8BytesPerChar == 1) + { + // + // Get the base address of the table. + // + pui8KeyBoardMap = psTable->pvCharMapping; + + ui32Value = pui8KeyBoardMap[ui32Offset + ui32Shift]; + } + // + // Two bytes per character. + // + else if(psTable->ui8BytesPerChar == 2) + { + // + // Get the base address of the table. + // + pui16KeyBoardMap = (uint16_t *)psTable->pvCharMapping; + + ui32Value = pui16KeyBoardMap[ui32Offset + ui32Shift]; + } + // + // All other sizes are unsupported for now. + // + else + { + ui32Value = 0; + } + + return(ui32Value); +} + +//***************************************************************************** +// +//! This function is used to set one of the fixed modifier keys on a keyboard. +//! +//! \param psKbInstance is the instance value for this keyboard. +//! \param ui32Modifiers 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 ui32Modifiers value is a bitmask of the following set of values: +//! - \b HID_KEYB_NUM_LOCK +//! - \b HID_KEYB_CAPS_LOCK +//! - \b HID_KEYB_SCROLL_LOCK +//! - \b HID_KEYB_COMPOSE +//! - \b 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 psKbInstance 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. +// +//***************************************************************************** +uint32_t +USBHKeyboardModifierSet(tUSBHKeyboard *psKbInstance, uint32_t ui32Modifiers) +{ + // + // Remember the fact that this is set. + // + psKbInstance->ui8KeyModSticky = (uint8_t)ui32Modifiers; + + // + // Set the LEDs on the keyboard. + // + USBHHIDSetReport(psKbInstance->psHIDInstance, 0, + (uint8_t *)&ui32Modifiers, 1); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to initialize a keyboard interface after a keyboard +//! has been detected. +//! +//! \param psKbInstance 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 psKbInstance 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. +// +//***************************************************************************** +uint32_t +USBHKeyboardInit(tUSBHKeyboard *psKbInstance) +{ + uint8_t ui8ModData; + int32_t i32Idx; + + // + // Set the initial rate to only update on keyboard state changes. + // + USBHHIDSetIdle(psKbInstance->psHIDInstance, 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(psKbInstance->psHIDInstance, + psKbInstance->pui8Buffer, + USBHKEYB_REPORT_SIZE); + + // + // Set the keyboard to boot protocol. + // + USBHHIDSetProtocol(psKbInstance->psHIDInstance, 1); + + // + // Used to clear the initial state of all on keyboard modifiers. + // + ui8ModData = 0; + + // + // Update the keyboard LED state. + // + USBHHIDSetReport(psKbInstance->psHIDInstance, 0, &ui8ModData, 1); + + // + // Reset the key state. + // + for(i32Idx = 0; + i32Idx < sizeof(psKbInstance->pui8KeyState) / sizeof(uint8_t); + i32Idx++) + { + psKbInstance->pui8KeyState[i32Idx] =0; + } + + return(0); +} + +//***************************************************************************** +// +//! This function is used to set the automatic poll rate of the keyboard. +//! +//! \param psKbInstance is the instance value for this keyboard. +//! \param ui32PollRate 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 psKbInstance value is the value that was +//! returned when the application called USBHKeyboardOpen(). The +//! \e ui32PollRate 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. +// +//***************************************************************************** +uint32_t +USBHKeyboardPollRateSet(tUSBHKeyboard *psKbInstance, uint32_t ui32PollRate) +{ + // + // Send the Set Idle command to the USB keyboard. + // + USBHHIDSetIdle(psKbInstance->psHIDInstance, ui32PollRate, 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 *psKbInstance) +{ + int32_t i32NewKey, i32OldKey; + + // + // rollover code so ignore this buffer. + // + if(psKbInstance->pui8Buffer[2] == 0x01) + { + return; + } + + // + // Handle the keyboard modifier states. + // + if(psKbInstance->ui8KeyModState != psKbInstance->pui8Buffer[0]) + { + // + // Notify the application of the event. + // + psKbInstance->pfnCallback(0, USBH_EVENT_HID_KB_MOD, + psKbInstance->pui8Buffer[0], 0); + + // + // Save the new state of the modifier keys. + // + psKbInstance->ui8KeyModState = psKbInstance->pui8Buffer[0]; + } + + // + // This loop checks for keys that have been released to make room for new + // ones that may have been pressed. + // + for(i32OldKey = 0; i32OldKey < 6; i32OldKey++) + { + // + // If there is no old key pressed in this entry go to the next one. + // + if(psKbInstance->pui8KeyState[i32OldKey] == 0) + { + continue; + } + + // + // Check if this old key is still in the list of currently pressed + // keys. + // + for(i32NewKey = 2; i32NewKey < 8; i32NewKey++) + { + // + // Break out if the key is still present. + // + if(psKbInstance->pui8Buffer[i32NewKey] == + psKbInstance->pui8KeyState[i32OldKey]) + { + break; + } + } + // + // If the old key was no longer in the list of pressed keys then + // notify the application of the key release. + // + if(i32NewKey == 8) + { + // + // Send the key release notification to the application. + // + psKbInstance->pfnCallback(0, USBH_EVENT_HID_KB_REL, + psKbInstance->pui8KeyState[i32OldKey], + 0); + // + // Remove the old key from the currently held key list. + // + psKbInstance->pui8KeyState[i32OldKey] = 0; + + } + } + + // + // This loop checks for new keys that have been pressed. + // + for(i32NewKey = 2; i32NewKey < 8; i32NewKey++) + { + // + // The new list is empty so no new keys are pressed. + // + if(psKbInstance->pui8Buffer[i32NewKey] == 0) + { + break; + } + + // + // This loop checks if the current key was already pressed. + // + for(i32OldKey = 0; i32OldKey < 6; i32OldKey++) + { + // + // If it is in both lists then it was already pressed so ignore it. + // + if(psKbInstance->pui8Buffer[i32NewKey] == + psKbInstance->pui8KeyState[i32OldKey]) + { + break; + } + } + // + // The key in the new list was not found so it is new. + // + if(i32OldKey == 6) + { + // + // Look for a free location to store this key usage code. + // + for(i32OldKey = 0; i32OldKey < 6; i32OldKey++) + { + // + // If an empty location is found, store it and notify the + // application. + // + if(psKbInstance->pui8KeyState[i32OldKey] == 0) + { + // + // Save the newly pressed key. + // + psKbInstance->pui8KeyState[i32OldKey] = + psKbInstance->pui8Buffer[i32NewKey]; + + // + // Notify the application of the new key that has been + // pressed. + // + psKbInstance->pfnCallback( 0, USBH_EVENT_HID_KB_PRESS, + psKbInstance->pui8Buffer[i32NewKey], + 0); + + break; + } + } + } + } +} + +//***************************************************************************** +// +//! This function handles event callbacks from the USB HID driver layer. +//! +//! \param pvKeyboard is the pointer that was passed in to the USBHHIDOpen() +//! call. +//! \param ui32Event is the event that has been passed up from the HID driver. +//! \param ui32MsgParam has meaning related to the \e ui32Event that occurred. +//! \param pvMsgData has meaning related to the \e ui32Event 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. +// +//***************************************************************************** +static uint32_t +USBHKeyboardCallback(void *pvKeyboard, uint32_t ui32Event, + uint32_t ui32MsgParam, void *pvMsgData) +{ + tUSBHKeyboard *psKbInstance; + + // + // Recover the pointer to the instance data. + // + psKbInstance = (tUSBHKeyboard *)pvKeyboard; + + switch (ui32Event) + { + // + // New keyboard has been connected so notify the application. + // + case USB_EVENT_CONNECTED: + { + // + // Remember that a keyboard is present. + // + psKbInstance->ui32HIDFlags |= USBHKEYB_DEVICE_PRESENT; + + // + // Notify the application that a new keyboard was connected. + // + psKbInstance->pfnCallback(0, ui32Event, ui32MsgParam, pvMsgData); + + break; + } + case USB_EVENT_DISCONNECTED: + { + // + // No keyboard is present. + // + psKbInstance->ui32HIDFlags &= ~USBHKEYB_DEVICE_PRESENT; + + // + // Notify the application that the keyboard was disconnected. + // + psKbInstance->pfnCallback(0, ui32Event, ui32MsgParam, pvMsgData); + + break; + } + case USB_EVENT_RX_AVAILABLE: + { + // + // New keyboard report structure was received. + // + USBHHIDGetReport(psKbInstance->psHIDInstance, 0, + psKbInstance->pui8Buffer, + USBHKEYB_REPORT_SIZE); + + // + // Update the application on the changes in the keyboard state. + // + UpdateKeyboardState(psKbInstance); + + break; + } + } + return(0); +} + +//***************************************************************************** +// +//! This function forwards an LPM request for a device to enter L1 sleep state. +//! +//! \param psKbInstance is the HID keyboard instance that was +//! returned from the call to USBHKeyboardOpen(). +//! +//! This function forwards a request from an application to the HID device +//! class to request that a device enter the LPM L1 sleep state. The +//! caller must check the return value to see if the request can be +//! attempted at this time. If another LPM transaction is busy on this or +//! another device, then this function returns \b USBHCD_LPM_PENDING. If +//! the LPM request was scheduled to be sent the function returns +//! \b USBHCD_LPM_AVAIL. The caller should check the USBHCDLPMStatus() +//! function to determine if the request completed successfully or if there +//! was an error. +//! +//! \return This function returns the following values: +//! - \b USBHCD_LPM_AVAIL - The transition to L1 state is scheduled to be sent. +//! - \b USBHCD_LPM_PENDING - There is already an LPM request pending. +// +//***************************************************************************** +uint32_t +USBHKeyboardLPMSleep(tUSBHKeyboard *psKbInstance) +{ + // + // Call the HID function to send the sleep command. + // + return(USBHHIDLPMSleep(psKbInstance->psHIDInstance)); +} + +//***************************************************************************** +// +//! This function returns the current status of an LPM request. +//! +//! \param psKbInstance is the HID keyboard instance that was +//! returned from the call to USBHKeyboardOpen(). +//! +//! This function returns the current status of LPM requests for a given +//! device. This is called to determine if a previous request completed +//! successfully or if there was an error. +//! +//! \return This function returns the following values: +//! - \b USBHCD_LPM_AVAIL - There are no pending LPM requests on this specific +//! device or the last request completed successfully. +//! - \b USBHCD_LPM_ERROR - The last LPM request for this device did not +//! complete successfully. +//! - \b USBHCD_LPM_PENDING - The last LPM request has not completed. +// +//***************************************************************************** +uint32_t +USBHKeyboardLPMStatus(tUSBHKeyboard *psKbInstance) +{ + // + // Call the HID function to get the current LPM status. + // + return(USBHHIDLPMStatus(psKbInstance->psHIDInstance)); +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhhidkeyboard.h b/usblib/host/usbhhidkeyboard.h new file mode 100644 index 0000000..09852f1 --- /dev/null +++ b/usblib/host/usbhhidkeyboard.h @@ -0,0 +1,89 @@ +//***************************************************************************** +// +// usbhhidkeyboard.h - This file holds the application interfaces for USB +// keyboard devices. +// +// Copyright (c) 2008-2014 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 2.1.0.12573 of the Tiva 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 +//! @{ +// +//***************************************************************************** + +typedef struct tUSBHKeyboard tUSBHKeyboard; + +//***************************************************************************** +// +// The prototype for the host USB Keyboard driver callback function. +// +//***************************************************************************** +typedef void (*tUSBHIDKeyboardCallback)(tUSBHKeyboard *psKbInstance, + uint32_t ui32Event, + uint32_t ui32MsgParam, + void *pvMsgData); + +extern tUSBHKeyboard * USBHKeyboardOpen(tUSBHIDKeyboardCallback pfnCallback, + uint8_t *pui8Buffer, + uint32_t ui32BufferSize); +extern uint32_t USBHKeyboardClose(tUSBHKeyboard *psKbInstance); +extern uint32_t USBHKeyboardInit(tUSBHKeyboard *psKbInstance); +extern uint32_t USBHKeyboardModifierSet(tUSBHKeyboard *psKbInstance, + uint32_t ui32Modifiers); +extern uint32_t USBHKeyboardPollRateSet(tUSBHKeyboard *psKbInstance, + uint32_t ui32PollRate); +extern uint32_t USBHKeyboardLPMSleep(tUSBHKeyboard *psKbInstance); +extern uint32_t USBHKeyboardLPMStatus(tUSBHKeyboard *psKbInstance); +extern uint32_t USBHKeyboardUsageToChar(tUSBHKeyboard *psKbInstance, + const tHIDKeyboardUsageTable *psTable, + uint8_t ui8UsageID); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// 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..f1eb8d5 --- /dev/null +++ b/usblib/host/usbhhidmouse.c @@ -0,0 +1,452 @@ +//***************************************************************************** +// +// usbhhidmouse.c - This file holds the application interfaces for USB +// mouse devices. +// +// Copyright (c) 2008-2014 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 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#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 uint32_t USBHMouseCallback(void *pvMouse, uint32_t ui32Event, + uint32_t ui32MsgParam, void *pvMsgData); + +//***************************************************************************** +// +// The size of a USB mouse report. +// +//***************************************************************************** +#define USBHMS_REPORT_SIZE 4 + +//***************************************************************************** +// +// These are the flags for the tUSBHMouse.ui32HIDFlags member variable. +// +//***************************************************************************** +#define USBHMS_DEVICE_PRESENT 0x00000001 + +//***************************************************************************** +// +// This is the structure definition for a mouse device instance. +// +//***************************************************************************** +struct tUSBHMouse +{ + // + // Global flags for an instance of a mouse. + // + uint32_t ui32HIDFlags; + + // + // The applications registered callback. + // + tUSBHIDMouseCallback pfnCallback; + + // + // The current state of the buttons. + // + uint8_t ui8Buttons; + + // + // This is a local buffer to hold the current HID report that comes up + // from the HID driver layer. + // + uint8_t pui8Buffer[USBHMS_REPORT_SIZE]; + + // + // Heap data for the mouse currently used to read the HID Report + // Descriptor. + // + uint8_t *pui8Heap; + + // + // Size of the heap in bytes. + // + uint32_t ui32HeapSize; + + // + // This is the instance value for the HID device that will be used for the + // mouse. + // + tHIDInstance *psHIDInstance; +}; + +//***************************************************************************** +// +// 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 pui8Buffer is the memory used by the driver to interact with the +//! USB mouse. +//! \param ui32Size is the size of the buffer provided by \e pui8Buffer. +//! +//! 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 pui8Buffer 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. +// +//***************************************************************************** +tUSBHMouse * +USBHMouseOpen(tUSBHIDMouseCallback pfnCallback, uint8_t *pui8Buffer, + uint32_t ui32Size) +{ + // + // Save the callback and data pointers. + // + g_sUSBHMouse.pfnCallback = pfnCallback; + + // + // Save the instance pointer for the HID device that was opened. + // + g_sUSBHMouse.psHIDInstance = USBHHIDOpen(eUSBHHIDClassMouse, + USBHMouseCallback, + (void *)&g_sUSBHMouse); + + // + // Save the heap buffer and size. + // + g_sUSBHMouse.pui8Heap = pui8Buffer; + g_sUSBHMouse.ui32HeapSize = ui32Size; + + return(&g_sUSBHMouse); +} + +//***************************************************************************** +// +//! This function is used close an instance of a mouse. +//! +//! \param psMsInstance 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 psMsInstance value is the value +//! that was returned when the application called USBHMouseOpen(). +//! +//! \return Returns 0. +// +//***************************************************************************** +uint32_t +USBHMouseClose(tUSBHMouse *psMsInstance) +{ + // + // Reset the callback to null. + // + psMsInstance->pfnCallback = 0; + + // + // Call the HID driver layer to close out this instance. + // + USBHHIDClose(psMsInstance->psHIDInstance); + + return(0); +} + +//***************************************************************************** +// +//! This function is used to initialize a mouse interface after a mouse has +//! been detected. +//! +//! \param psMsInstance 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 psMsInstance 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. +// +//***************************************************************************** +uint32_t +USBHMouseInit(tUSBHMouse *psMsInstance) +{ + // + // Set the initial rate to only update on mouse state changes. + // + USBHHIDSetIdle(psMsInstance->psHIDInstance, 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(psMsInstance->psHIDInstance, + psMsInstance->pui8Heap, + psMsInstance->ui32HeapSize); + + // + // Set the mouse to boot protocol. + // + USBHHIDSetProtocol(psMsInstance->psHIDInstance, 1); + + return(0); +} + +//***************************************************************************** +// +// This function handles updating the state of the mouse buttons and axis. +// +// \param psMsInstance 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 *psMsInstance) +{ + uint32_t ui32Button; + + if(psMsInstance->pui8Buffer[0] != psMsInstance->ui8Buttons) + { + for(ui32Button = 1; ui32Button <= 0x4; ui32Button <<= 1) + { + if(((psMsInstance->pui8Buffer[0] & ui32Button) != 0) && + ((psMsInstance->ui8Buttons & ui32Button) == 0)) + { + // + // Send the mouse button press notification to the application. + // + psMsInstance->pfnCallback(0, USBH_EVENT_HID_MS_PRESS, + ui32Button, 0); + } + if(((psMsInstance->pui8Buffer[0] & ui32Button) == 0) && + ((psMsInstance->ui8Buttons & ui32Button) != 0)) + { + // + // Send the mouse button release notification to the + // application. + // + psMsInstance->pfnCallback(0, USBH_EVENT_HID_MS_REL, + ui32Button, 0); + } + } + + // + // Save the new state. + // + psMsInstance->ui8Buttons = psMsInstance->pui8Buffer[0]; + } + + if(psMsInstance->pui8Buffer[1] != 0) + { + // + // Send the mouse button release notification to the + // application. + // + psMsInstance->pfnCallback(0, USBH_EVENT_HID_MS_X, + (uint32_t)psMsInstance->pui8Buffer[1], 0); + } + + if(psMsInstance->pui8Buffer[2] != 0) + { + // + // Send the mouse button release notification to the + // application. + // + psMsInstance->pfnCallback(0, USBH_EVENT_HID_MS_Y, + (uint32_t)psMsInstance->pui8Buffer[2], 0); + } +} + +//***************************************************************************** +// +//! This function handles event callbacks from the USB HID driver layer. +//! +//! \param pvMouse is the pointer that was passed in to the USBHHIDOpen() +//! call. +//! \param ui32Event is the event that has been passed up from the HID driver. +//! \param ui32MsgParam has meaning related to the \e ui32Event that occurred. +//! \param pvMsgData has meaning related to the \e ui32Event 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. +// +//***************************************************************************** +uint32_t +USBHMouseCallback(void *pvMouse, uint32_t ui32Event, + uint32_t ui32MsgParam, void *pvMsgData) +{ + tUSBHMouse *psMsInstance; + + // + // Recover the pointer to the instance data. + // + psMsInstance = (tUSBHMouse *)pvMouse; + + switch(ui32Event) + { + // + // New mouse has been connected so notify the application. + // + case USB_EVENT_CONNECTED: + { + // + // Remember that a mouse is present. + // + psMsInstance->ui32HIDFlags |= USBHMS_DEVICE_PRESENT; + + // + // Notify the application that a new mouse was connected. + // + psMsInstance->pfnCallback(0, ui32Event, ui32MsgParam, pvMsgData); + + break; + } + case USB_EVENT_DISCONNECTED: + { + // + // No mouse is present. + // + psMsInstance->ui32HIDFlags &= ~USBHMS_DEVICE_PRESENT; + + // + // Notify the application that the mouse was disconnected. + // + psMsInstance->pfnCallback(0, ui32Event, ui32MsgParam, pvMsgData); + + break; + } + case USB_EVENT_RX_AVAILABLE: + { + // + // New mouse report structure was received. + // + USBHHIDGetReport(psMsInstance->psHIDInstance, 0, + psMsInstance->pui8Buffer, USBHMS_REPORT_SIZE); + + // + // Update the current state of the mouse and notify the application + // of any changes. + // + UpdateMouseState(psMsInstance); + + break; + } + } + return(0); +} + +//***************************************************************************** +// +//! This function forwards an LPM request for a device to enter L1 sleep state. +//! +//! \param psMsInstance is the HID keyboard instance that was returned +//! from the call to USBHMouseOpen(). +//! +//! This function forwards a request from an application to the HID device +//! class to request that a device enter the LPM L1 sleep state. The +//! caller must check the return value to see if the request can be +//! attempted at this time. If another LPM transaction is busy on this or +//! another device, then this function returns \b USBHCD_LPM_PENDING. If +//! the LPM request was scheduled to be sent the function returns +//! \b USBHCD_LPM_AVAIL. The caller should check the USBHCDLPMStatus() +//! function to determine if the request completed successfully or if there +//! was an error. +//! +//! \return This function returns the following values: +//! - \b USBHCD_LPM_AVAIL - The transition to L1 state is scheduled to be sent. +//! - \b USBHCD_LPM_PENDING - There is already an LPM request pending. +// +//***************************************************************************** +uint32_t +USBHMouseLPMSleep(tUSBHMouse *psMsInstance) +{ + // + // Call the HID function to send the sleep command. + // + return(USBHHIDLPMSleep(psMsInstance->psHIDInstance)); +} + +//***************************************************************************** +// +//! This function returns the current status of an LPM request. +//! +//! \param psMsInstance is the HID keyboard instance that was returned +//! from the call to USBHMouseOpen(). +//! +//! This function returns the current status of LPM requests for a given +//! device. This is called to determine if a previous request completed +//! successfully or if there was an error. +//! +//! \return This function returns the following values: +//! - \b USBHCD_LPM_AVAIL - There are no pending LPM requests on this specific +//! device or the last request completed successfully. +//! - \b USBHCD_LPM_ERROR - The last LPM request for this device did not +//! complete successfully. +//! - \b USBHCD_LPM_PENDING - The last LPM request has not completed. +// +//***************************************************************************** +uint32_t +USBHMouseLPMStatus(tUSBHMouse *psMsInstance) +{ + // + // Call the HID function to get the current LPM status. + // + return(USBHHIDLPMStatus(psMsInstance->psHIDInstance)); +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhhidmouse.h b/usblib/host/usbhhidmouse.h new file mode 100644 index 0000000..295f6c1 --- /dev/null +++ b/usblib/host/usbhhidmouse.h @@ -0,0 +1,81 @@ +//***************************************************************************** +// +// usbhhidmouse.h - This file holds the application interfaces for USB +// mouse devices. +// +// Copyright (c) 2008-2014 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 2.1.0.12573 of the Tiva 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 +//! @{ +// +//***************************************************************************** + +typedef struct tUSBHMouse tUSBHMouse; + +//***************************************************************************** +// +// The prototype for the host USB mouse driver callback function. +// +//***************************************************************************** +typedef void (*tUSBHIDMouseCallback)(tUSBHMouse *psMsInstance, + uint32_t ui32Event, + uint32_t ui32MsgParam, + void *pvMsgData); + +extern tUSBHMouse * USBHMouseOpen(tUSBHIDMouseCallback pfnCallback, + uint8_t *pui8Buffer, uint32_t ui32Size); +extern uint32_t USBHMouseClose(tUSBHMouse *psMsInstance); +extern uint32_t USBHMouseInit(tUSBHMouse *psMsInstance); +extern uint32_t USBHMouseLPMSleep(tUSBHMouse *psMsInstance); +extern uint32_t USBHMouseLPMStatus(tUSBHMouse *psMsInstance); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// 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..8042dfc --- /dev/null +++ b/usblib/host/usbhhub.c @@ -0,0 +1,1522 @@ +//***************************************************************************** +// +// usbhhub.c - This file contains the host HID driver. +// +// Copyright (c) 2011-2014 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 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "inc/hw_ints.h" +#include "inc/hw_sysctl.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 +//! @{ +// +//***************************************************************************** + +#ifdef ewarm +#pragma pack(1) +#endif + +//***************************************************************************** +// +//! 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. +// +//***************************************************************************** +typedef struct +{ + // + //! The total number of bytes in the descriptor (including this field). + // + uint8_t bLength; + + // + //! The descriptor type. For a hub descriptor, this will be USB_DTYPE_HUB + //! (0x29 or 41 decimal). + // + uint8_t bDescType; + + // + //! The number of downstream-facing ports that the hub supports. + // + uint8_t bNbrPorts; + + // + //! Characteristics of the hub device including its power switching + //! capabilities and over-current protection mode. + // + uint16_t 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. + // + uint8_t bPwrOn2PwrGood; + + // + //! The maximum current requirement for the hub circuitry in mA. + // + uint8_t 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. + // + uint8_t PortInfo[((ROOT_HUB_MAX_PORTS + 7) / 8) * 2]; +} +PACKED tUsbHubDescriptor; + +#ifdef ewarm +#pragma pack() +#endif + +//***************************************************************************** +// +// This structure holds all data specific to a single hub port. +// +//***************************************************************************** +typedef struct +{ + // + // The handle used by the HCD layer to identify this device. + // + uint32_t ui32DevHandle; + + // + // The current state of the port. + // + volatile tHubPortState iState; + + // + // General counter used in various states. + // + volatile uint32_t ui32Count; + + // + // A flag used to indicate that the downstream device is a low speed + // device. + // + bool bLowSpeed; + + // + // The speed of the device on this port. + // + uint32_t ui32Speed; + + // + // This flag is set if the hub reports that a change is pending on this + // port. + // + volatile bool bChanged; +} +tHubPort; + +//***************************************************************************** +// +// USB hub flags values for tHubInstance.ui32Flags. +// +//***************************************************************************** +#define USBLIB_HUB_ACTIVE 0x00000001 +#define USBLIB_HUB_HS 0x00000002 +#define USBLIB_HUB_MULTI_TT 0x00000004 + +//***************************************************************************** +// +// This is the structure that holds all of the data for a given instance of +// a Hub device. +// +//***************************************************************************** +struct tHubInstance +{ + // + // Save the device instance. + // + tUSBHostDevice *psDevice; + + // + // Used to save the callback function pointer. + // + tUSBHHubCallback pfnCallback; + + // + // Callback data provided by caller. + // + uint32_t ui32CBData; + + // + // Interrupt IN pipe. + // + uint32_t ui32IntInPipe; + + // + // Hub characteristics as reported in the class-specific hub descriptor. + // + uint16_t ui16HubCharacteristics; + + // + // The number of downstream-facing ports the hub supports. + // + uint8_t ui8NumPorts; + + // + // 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. + // + uint8_t ui8NumPortsInUse; + + // + // The size of a status change packet sent by the hub. This is determined + // from the number of ports supported by the hub. + // + uint8_t ui8ReportSize; + + // + // Flags indicating whether the hub is connected. + // + uint32_t ui32Flags; + + // + // Flag indicating that a device is currently in process of being + // enumerated. + // + volatile bool bEnumerationBusy; + + // + // This is valid if bEnumerationBusy is set and indicates the port + // that is in the process of enumeration. + // + uint8_t ui8EnumIdx; + + // + // The state of each of the ports we support on the hub. + // + tHubPort psPorts[MAX_USB_DEVICES]; + + // + // The interrupt number for this instance. + // + uint32_t ui32IntNum; +}; + +//***************************************************************************** +// +//! Forward references to the hub class driver functions. +// +//***************************************************************************** +static void *HubDriverOpen(tUSBHostDevice *psDevice); +static void HubDriverClose(void *pvHubDevice); + +//***************************************************************************** +// +//! This constant global structure defines the Hub Class Driver that is +//! provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_sUSBHubClassDriver = +{ + USB_CLASS_HUB, + HubDriverOpen, + HubDriverClose, + 0 +}; + +//***************************************************************************** +// +// The instance data storage for attached hub. +// +//***************************************************************************** +static tHubInstance g_sRootHub; + +//***************************************************************************** +// +// Hub and port state change flags as reported via the hub's IN endpoint. +// +//***************************************************************************** +static volatile uint32_t g_ui32ChangeFlags; + +// +// Note: The following assumes ROOT_HUB_MAX_PORTS is less than 32! +// +static uint32_t g_ui32HubChanges; + +//***************************************************************************** +// +// This function is called to set the operating speed of a given port. +// +// \param ui8Port is the port number for this request. +// \param ui32Speed is one of the HUB_FEATURE_PORT_* values. +// +// This function sets the operating speed of the hub port specified in the +// \e ui8Port parameter. A \e ui8Port value of 0 is an access to the hub +// itself and not one of the hub ports. The \e ui32Speed value is one of the +// \b USB_EP_SPEED_ values. +// +// \return None. +// +//***************************************************************************** +static void +USBHubPortSpeedSet(uint8_t ui8Port, uint32_t ui32Speed) +{ + g_sRootHub.psPorts[ui8Port].ui32Speed = ui32Speed; +} + +//***************************************************************************** +// +// This function is called to send a request to the hub to set a feature on +// a given port. +// +// \param psHubInstance is the hub device instance. +// \param ui8Port is the port number for this request. +// \param ui16Feature is one of the HUB_FEATURE_PORT_* values. +// +// This function will send the set feature request to the hub indicated by the +// \e psHubInstance parameter. The \e ui8Port 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 ui8Port value of 0 is an access to the hub +// itself and not one of the hub ports. The \e ui16Feature is the feature +// request toset on the given port. For example, a \e ui16Feature value of +// \e HUB_FEATURE_PORT_RESET and \e ui8Port value of 1 will cause reset +// signaling to hub port 1. +// +// \return None. +// +//***************************************************************************** +static void +HubSetPortFeature(tHubInstance *psHubInstance, uint8_t ui8Port, + uint16_t ui16Feature) +{ + tUSBRequest sSetupPacket; + tUSBHostDevice *psDevice; + + // + // Retrieve the hub instance and device pointer. + // + psDevice = psHubInstance->psDevice; + + // + // This is a standard OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_OTHER; + + // + // Set the field to clear the requested port feature. + // + sSetupPacket.bRequest = USBREQ_SET_FEATURE; + sSetupPacket.wValue = ui16Feature; + sSetupPacket.wIndex = ui8Port; + sSetupPacket.wLength = 0; + + // + // Send the request. + // + USBHCDControlTransfer(0, &sSetupPacket, psDevice, 0, 0, + psDevice->sDeviceDescriptor.bMaxPacketSize0); +} + +//***************************************************************************** +// +// This function is called to send a request to the hub to clear a feature on +// a given port. +// +// \param psHubInstance is the hub device instance. +// \param ui8Port is the port number for this request. +// \param ui16Feature is one of the HUB_FEATURE_PORT_* values. +// +// This function will send the clear feature request to the hub indicated by +// the \e psHubInstance parameter. The \e ui8Port 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 ui8Port value of 0 is an access to the hub +// itself and not one of the hub ports. The \e ui16Feature is the feature +// request to clear on the given port. For example, a \e ui16Feature value of +// \e HUB_FEATURE_C_PORT_RESET and \e ui8Port 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(tHubInstance *psHubInstance, uint8_t ui8Port, + uint16_t ui16Feature) +{ + tUSBRequest sSetupPacket; + tUSBHostDevice *psDevice; + + // + // Retrieve the hub instance and device pointer. + // + psDevice = psHubInstance->psDevice; + + // + // This is a standard OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_OTHER; + + // + // Set the field to clear the requested port feature. + // + sSetupPacket.bRequest = USBREQ_CLEAR_FEATURE; + sSetupPacket.wValue = ui16Feature; + sSetupPacket.wIndex = ui8Port; + sSetupPacket.wLength = 0; + + // + // Send the request. + // + USBHCDControlTransfer(0, &sSetupPacket, psDevice, 0, 0, + psDevice->sDeviceDescriptor.bMaxPacketSize0); +} + +//***************************************************************************** +// +// This function is used to retrieve the current status of a port on the +// hub. +// +// \param psHubInstance is the hub device instance. +// \param ui8Port is the port number for this request. +// \param pui16PortStatus is a pointer to the memory to store the current +// status of the port. +// \param pui16PortChange 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 ui8Port parameter. The \e ui8Port 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 ui8Port value of 0 is an +// access to the hub itself and not one of the hub ports. +// +// \return None. +// +//***************************************************************************** +static bool +HubGetPortStatus(tHubInstance *psHubInstance, uint8_t ui8Port, + uint16_t *pui16PortStatus, uint16_t *pui16PortChange) +{ + uint32_t ui32Data, ui32Read; + tUSBRequest sSetupPacket; + tUSBHostDevice *psDevice; + + // + // Retrieve the device pointer. + // + psDevice = psHubInstance->psDevice; + + // + // This is a standard OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | + USB_RTYPE_OTHER; + + // + // Set the fields to get the hub status. + // + sSetupPacket.bRequest = USBREQ_GET_STATUS; + sSetupPacket.wValue = 0; + sSetupPacket.wIndex = (uint16_t)ui8Port; + sSetupPacket.wLength = 4; + + // + // Send the request. + // + ui32Read = USBHCDControlTransfer(0, &sSetupPacket, psDevice, + (uint8_t *)&ui32Data, 4, + psDevice->sDeviceDescriptor.bMaxPacketSize0); + + // + // Check that we received the correct number of bytes. + // + if(ui32Read != 4) + { + return(false); + } + else + { + // + // We got 4 bytes from the device. Now translate these into the 2 + // 16-bit values we pass back to the caller. + // + *pui16PortStatus = (uint16_t)(ui32Data & 0xFFFF); + *pui16PortChange = (uint16_t)(ui32Data >> 16); + + DEBUG_OUTPUT("Port %d, status 0x%04x, change 0x%04x\n", ui8Port, + *pui16PortStatus, *pui16PortChange); + } + + // + // All is well. + // + return(true); +} + +//***************************************************************************** +// +// This function handles callbacks for the interrupt IN endpoint for the hub +// device. +// +//***************************************************************************** +static void +HubIntINCallback(uint32_t ui32Pipe, uint32_t ui32Event) +{ + switch (ui32Event) + { + // + // 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(ui32Pipe, (uint8_t *)&g_ui32HubChanges, + (uint32_t)g_sRootHub.ui8ReportSize); + 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(ui32Pipe); + + // + // Update our global "ports needing service" flags with the latest + // information we have just received. + // + g_ui32ChangeFlags |= g_ui32HubChanges; + + // + // Send the report data to the USB host hub device class driver if + // we have been given a callback function. + // + if(g_sRootHub.pfnCallback) + { + g_sRootHub.pfnCallback((void *)g_sRootHub.ui32CBData, + USB_EVENT_RX_AVAILABLE, + ui32Pipe, &g_ui32HubChanges); + } + + break; + } + case USB_EVENT_ERROR: + { + break; + } + } +} + +//***************************************************************************** +// +// Query the class-specific hub descriptor. +// +//***************************************************************************** +static bool +GetHubDescriptor(tUsbHubDescriptor *psDesc) +{ + uint32_t ui32Read; + tUSBRequest sSetupPacket; + tUSBHostDevice *psDevice; + + // + // Retrieve the device pointer. + // + psDevice = g_sRootHub.psDevice; + + // + // This is a standard OUT request. + // + sSetupPacket.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. + // + sSetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + sSetupPacket.wValue = (USB_DTYPE_HUB << 8); + sSetupPacket.wIndex = 0; + sSetupPacket.wLength = sizeof(tUsbHubDescriptor); + + // + // Send the request. + // + ui32Read = USBHCDControlTransfer(0, &sSetupPacket, psDevice, + (void *)psDesc, sizeof(tUsbHubDescriptor), + psDevice->sDeviceDescriptor.bMaxPacketSize0); + + // + // Make sure we got at least some data. + // + if(ui32Read == 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 *psDevice) +{ + tEndpointDescriptor *psEndpointDescriptor; + tInterfaceDescriptor *psInterface; + tUsbHubDescriptor sHubDesc; + bool bRetcode; + uint32_t ui32Loop; + + // + // If we are already talking to a hub, fail the call. We only support + // a single hub. + // + if(g_sRootHub.ui32Flags & USBLIB_HUB_ACTIVE) + { + return(0); + } + + // + // Get pointers to the device descriptors we need to look at. + // + psInterface = USBDescGetInterface(psDevice->psConfigDescriptor, 0, 0); + psEndpointDescriptor = USBDescGetInterfaceEndpoint(psInterface, 0, + psDevice->ui32ConfigDescriptorSize); + + // + // If there are no endpoints, something is wrong since a hub must have + // a single INT endpoint for signaling. + // + if(psEndpointDescriptor == 0) + { + return 0; + } + + // + // Make sure we really are talking to a hub. + // + if((psInterface->bInterfaceClass != USB_CLASS_HUB) || + (psInterface->bInterfaceSubClass != 0)) + { + // + // Something is wrong - this isn't a hub or, if it is, we don't + // understand the protocol it is using. + // + return(0); + } + + // + // Remember that this is a high speed hub with either single or multiple + // transaction translators. + // + if(psInterface->bInterfaceProtocol == USB_HUB_PROTOCOL_SINGLE) + { + g_sRootHub.ui32Flags |= USBLIB_HUB_HS; + } + else if(psInterface->bInterfaceProtocol == USB_HUB_PROTOCOL_MULTI) + { + g_sRootHub.ui32Flags |= USBLIB_HUB_HS | USBLIB_HUB_MULTI_TT; + } + + // + // Remember the device information for later. + // + g_sRootHub.psDevice = psDevice; + + // + // A hub must support an interrupt endpoint so check this. + // + if((psEndpointDescriptor->bmAttributes & USB_EP_ATTR_TYPE_M) == + USB_EP_ATTR_INT) + { + // + // The endpoint is the correct type. Is it an IN endpoint? + // + if(psEndpointDescriptor->bEndpointAddress & USB_EP_DESC_IN) + { + // + // Yes - all is well with the hub endpoint so allocate a pipe to + // handle traffic from the hub. + // + g_sRootHub.ui32IntInPipe = USBHCDPipeAlloc(0, USBHCD_PIPE_INTR_IN, + psDevice, + HubIntINCallback); + USBHCDPipeConfig(g_sRootHub.ui32IntInPipe, + psEndpointDescriptor->wMaxPacketSize, + psEndpointDescriptor->bInterval, + psEndpointDescriptor->bEndpointAddress & + USB_EP_DESC_NUM_M); + } + } + + // + // Did we allocate the endpoint successfully? + // + if(!g_sRootHub.ui32IntInPipe) + { + // + // 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_sRootHub.pfnCallback != 0) + { + g_sRootHub.pfnCallback((void *)g_sRootHub.ui32CBData, + USB_EVENT_CONNECTED, (uint32_t)&g_sRootHub, 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_sRootHub.ui8NumPorts = sHubDesc.bNbrPorts; + g_sRootHub.ui16HubCharacteristics = sHubDesc.wHubCharacteristics; + g_sRootHub.ui8NumPortsInUse = + (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_sRootHub.ui8ReportSize = ((sHubDesc.bNbrPorts + 1) + 7) / 8; + + // + // Enable power to all ports on the hub. + // + for(ui32Loop = 1; ui32Loop <= sHubDesc.bNbrPorts; ui32Loop++) + { + // + // Turn on power to this port. + // + HubSetPortFeature(&g_sRootHub, ui32Loop, + HUB_FEATURE_PORT_POWER); + } + + // + // Clear out our port state structures. + // + for(ui32Loop = 0; ui32Loop < MAX_USB_DEVICES; ui32Loop++) + { + g_sRootHub.psPorts[ui32Loop].bChanged = false; + g_sRootHub.psPorts[ui32Loop].iState = ePortIdle; + } + } + else + { + // + // Oops - we can't read the hub descriptor! Tidy up and return + // an error. + // + USBHCDPipeFree(g_sRootHub.ui32IntInPipe); + g_sRootHub.pfnCallback = 0; + g_sRootHub.ui32Flags &= ~USBLIB_HUB_ACTIVE; + return(0); + } + + // + // If we get here, all is well so remember that the hub is connected and + // active. + // + g_sRootHub.ui32Flags |= USBLIB_HUB_ACTIVE; + + // + // Return our instance data pointer to the caller to use as a handle. + // + return((void *)&g_sRootHub); +} + +//***************************************************************************** +// +// Close an instance of the hub driver. +// +//***************************************************************************** +static void +HubDriverClose(void *pvHubDevice) +{ + uint32_t ui32Loop; + + // + // No device so just exit. + // + if(g_sRootHub.psDevice == 0) + { + return; + } + + // + // Disconnect any devices that are currently connected to the hub. + // + for(ui32Loop = 0; ui32Loop < MAX_USB_DEVICES; ui32Loop++) + { + // + // Does this port have a device connected to it that we have previously + // reported to the host control layer?h + // + if((g_sRootHub.psPorts[ui32Loop].iState == ePortActive) || + (g_sRootHub.psPorts[ui32Loop].iState == ePortResetWait) || + (g_sRootHub.psPorts[ui32Loop].iState == ePortEnumerated) || + (g_sRootHub.psPorts[ui32Loop].iState == ePortError)) + { + // + // Yes - tell the host controller to disconnect the device. + // + USBHCDHubDeviceDisconnected(0, + g_sRootHub.psPorts[ui32Loop].ui32DevHandle); + + } + + // + // Make sure that the state returns to idle. + // + g_sRootHub.psPorts[ui32Loop].iState = ePortIdle; + + } + + // + // Reset the device pointer. + // + g_sRootHub.psDevice = 0; + + // + // Mark the hub as absent. + // + g_sRootHub.ui32Flags &= ~USBLIB_HUB_ACTIVE; + + // + // Note that we are not in the middle of enumerating anything. + // + g_sRootHub.bEnumerationBusy = false; + + // + // Free the Interrupt IN pipe. + // + if(g_sRootHub.ui32IntInPipe != 0) + { + USBHCDPipeFree(g_sRootHub.ui32IntInPipe); + } + + // + // If the callback exists, call it with a DISCONNECTED event. + // + if(g_sRootHub.pfnCallback != 0) + { + g_sRootHub.pfnCallback((void *)g_sRootHub.ui32CBData, + USB_EVENT_DISCONNECTED, (uint32_t)&g_sRootHub, + 0); + } +} + +//***************************************************************************** +// +// Perform any processing required as a result of a change in the reset +// signaling for a given port. +// +//***************************************************************************** +static void +HubDriverReset(uint8_t ui8Port, bool 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_sRootHub.psPorts[ui8Port].iState = ePortResetWait; + + // + // Set the wait to 10ms (10 frames) from now. + // + g_sRootHub.psPorts[ui8Port].ui32Count = 10; + } + else + { + // + // Was this device previously active? + // + if(g_sRootHub.psPorts[ui8Port].iState == ePortActive) + { + USBHCDHubDeviceDisconnected(0, + g_sRootHub.psPorts[ui8Port].ui32DevHandle); + } + + // + // The reset is active so mark our port as in reset. + // + g_sRootHub.psPorts[ui8Port].iState = ePortResetActive; + } +} + +//***************************************************************************** +// +// Start the process of enumerating a new device by issuing a reset to the +// appropriate downstream port. +// +//***************************************************************************** +static void +HubDriverDeviceReset(uint8_t ui8Port) +{ + DEBUG_OUTPUT("Starting enumeration for port %d\n", ui8Port); + + // + // Record the fact that we are in the process of enumerating a device. + // + g_sRootHub.bEnumerationBusy = true; + + // + // Save the port that is being enumerated. + // + g_sRootHub.ui8EnumIdx = ui8Port; + + // + // Mark the port as being reset. + // + g_sRootHub.psPorts[ui8Port].iState = ePortResetActive; + + // + // Initiate a reset on the relevant port to start the enumeration process. + // + HubSetPortFeature(&g_sRootHub, ui8Port, 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(uint8_t ui8Port) +{ + DEBUG_OUTPUT("HubDriverDeviceConnect\n"); + + // + // We've allocated a port table entry so fill it in then initiate a reset + // on the device. + // + g_sRootHub.psPorts[ui8Port].bChanged = false; + + // + // Mark the port as having a device present but not enumerated. + // + DEBUG_OUTPUT("Deferring enumeration for port %d\n", ui8Port); + g_sRootHub.psPorts[ui8Port].iState = ePortConnected; + + // + // Wait 100ms to reset the device. + // + g_sRootHub.psPorts[ui8Port].ui32Count = 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(uint8_t ui8Port) +{ + // + // This is a device we are currently managing. Have we already informed + // the host controller that it is present? + // + if((g_sRootHub.psPorts[ui8Port].iState == ePortActive) || + (g_sRootHub.psPorts[ui8Port].iState == ePortResetWait) || + (g_sRootHub.psPorts[ui8Port].iState == ePortEnumerated) || + (g_sRootHub.psPorts[ui8Port].iState == ePortError)) + { + // + // Yes - tell the host controller that the device is not longer + // connected. + // + USBHCDHubDeviceDisconnected(0, + g_sRootHub.psPorts[ui8Port].ui32DevHandle); + } + + // + // If the device was being enumerated, make sure we clear the flag + // indicating that an enumeration is still ongoing. + // + if((g_sRootHub.psPorts[ui8Port].iState == ePortResetActive) || + (g_sRootHub.psPorts[ui8Port].iState == ePortResetWait) || + (g_sRootHub.psPorts[ui8Port].iState == ePortActive)) + { + g_sRootHub.bEnumerationBusy = false; + } + + // + // Free up the port state structure. + // + g_sRootHub.psPorts[ui8Port].iState = ePortIdle; +} + +//***************************************************************************** +// +// This function is called periodically by USBHCDMain(). We use it to handle +// the hub port state machine. +// +//***************************************************************************** +void +USBHHubMain(void) +{ + uint16_t ui16Status, ui16Changed; + uint_fast8_t ui8Port; + bool bRetcode; + + // + // If the hub is not present, just return. + // + if((g_sRootHub.ui32Flags & USBLIB_HUB_ACTIVE) == 0) + { + return; + } + + // + // Initialize the status variables. + // + ui16Status = 0; + ui16Changed = 0; + + // + // The hub is active and something changed. Check to see which port changed + // state and handle as necessary. + // + for(ui8Port = 0; ui8Port <= g_sRootHub.ui8NumPortsInUse; ui8Port++) + { + // + // Decrement any wait counter if there is one present. + // + if(g_sRootHub.psPorts[ui8Port].ui32Count != 0) + { + g_sRootHub.psPorts[ui8Port].ui32Count--; + } + + // + // Is this port waiting to be enumerated and is the last device + // enumeration finished? + // + if((g_sRootHub.psPorts[ui8Port].iState == ePortConnected) && + (!g_sRootHub.bEnumerationBusy) && + (g_sRootHub.psPorts[ui8Port].ui32Count == 0)) + { + // + // Yes - start the enumeration processing for this device. + // + HubDriverDeviceReset(ui8Port); + } + + // + // If the state is ePortResetWait then the hub is waiting before + // accessing device as the USB 2.0 specification requires. + // + if((g_sRootHub.psPorts[ui8Port].iState == ePortResetWait) && + (g_sRootHub.psPorts[ui8Port].ui32Count == 0)) + { + // + // Start the enumeration process if the timeout has passed and + // the hub is waiting to start enumerating the device. + // + g_sRootHub.psPorts[ui8Port].iState = ePortActive; + + // + // Call the main host controller layer to have it enumerate the + // newly connected device. + // + g_sRootHub.psPorts[ui8Port].ui32DevHandle = + USBHCDHubDeviceConnected(0, 1, ui8Port, + g_sRootHub.psPorts[ui8Port].ui32Speed); + } + + // + // If an enumeration is in progress and the loop is not on the port + // being enumerated then skip the port. + // + if(g_sRootHub.bEnumerationBusy && + (g_sRootHub.ui8EnumIdx != ui8Port)) + { + continue; + } + + // + // Did something change for this particular port? + // + if(g_ui32ChangeFlags & (1 << ui8Port)) + { + // + // Yes - query the port status. + // + bRetcode = HubGetPortStatus(&g_sRootHub, ui8Port, &ui16Status, + &ui16Changed); + + // + // 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(g_sRootHub.ui32IntNum); + g_ui32ChangeFlags &= ~(1 << ui8Port); + OS_INT_ENABLE(g_sRootHub.ui32IntNum); + + // + // 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(ui16Changed & HUB_PORT_CHANGE_DEVICE_PRESENT) + { + DEBUG_OUTPUT("Connection change on port %d\n", ui8Port); + + // + // Clear the condition. + // + HubClearPortFeature(&g_sRootHub, ui8Port, + HUB_FEATURE_C_PORT_CONNECTION); + + // + // Was a device connected or disconnected? + // + if(ui16Status & HUB_PORT_STATUS_DEVICE_PRESENT) + { + DEBUG_OUTPUT("Connected\n"); + + // + // A device was connected. + // + HubDriverDeviceConnect(ui8Port); + } + else + { + DEBUG_OUTPUT("Disconnected\n"); + + // + // A device was disconnected. + // + HubDriverDeviceDisconnect(ui8Port); + } + } + + // + // Did a reset on the port complete? + // + if(ui16Changed & HUB_PORT_CHANGE_RESET) + { + // + // Clear the condition. + // + HubClearPortFeature(&g_sRootHub, ui8Port, + HUB_FEATURE_C_PORT_RESET); + + // + // Yes - query the port status. + // + bRetcode = HubGetPortStatus(&g_sRootHub, ui8Port, + &ui16Status, &ui16Changed); + + DEBUG_OUTPUT("Reset %s for port %d\n", + ((ui16Status & HUB_PORT_STATUS_RESET) ? "asserted" : + "deasserted"), ui8Port); + + // + // Handle the reset case. + // + HubDriverReset(ui8Port, (ui16Status & HUB_PORT_STATUS_RESET) ? + true : false); + + // + // A device was connected. + // + if(ui16Status & HUB_PORT_STATUS_LOW_SPEED) + { + USBHubPortSpeedSet(ui8Port, USB_EP_SPEED_LOW); + } + else if(ui16Status & HUB_PORT_STATUS_HIGH_SPEED) + { + USBHubPortSpeedSet(ui8Port, USB_EP_SPEED_HIGH); + } + else + { + USBHubPortSpeedSet(ui8Port, USB_EP_SPEED_FULL); + } + } + + // + // Did an over-current reset on the port complete? + // + if(ui16Changed & HUB_PORT_CHANGE_OVER_CURRENT) + { + DEBUG_OUTPUT("Port %d over current.\n", ui8Port); + + // + // Currently we ignore this and just clear the condition. + // + HubClearPortFeature(&g_sRootHub, ui8Port, + HUB_FEATURE_C_PORT_OVER_CURRENT); + } + + // + // Has the port been enabled or disabled? + // + if(ui16Changed & HUB_PORT_CHANGE_ENABLED) + { + DEBUG_OUTPUT("Enable change for port %d.\n", ui8Port); + + // + // Currently we ignore this and just clear the condition. + // + HubClearPortFeature(&g_sRootHub, ui8Port, + HUB_FEATURE_C_PORT_ENABLE); + } + + // + // Has the port been suspended or resumed? + // + if(ui16Changed & HUB_PORT_CHANGE_SUSPENDED) + { + DEBUG_OUTPUT("Suspend change for port %d.\n", ui8Port); + + // + // Currently we ignore this and just clear the condition. + // + HubClearPortFeature(&g_sRootHub, ui8Port, + HUB_FEATURE_C_PORT_SUSPEND); + } + } + } +} + +//***************************************************************************** +// +//! Informs the hub class driver that a downstream device has been enumerated. +//! +//! \param ui8Hub is the address of the hub to which the downstream device +//! is attached. +//! \param ui8Port 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(uint8_t ui8Hub, uint8_t ui8Port) +{ + DEBUG_OUTPUT("Enumeration complete for hub %d, port %d\n", ui8Hub, ui8Port); + + // + // Record the fact that the device is up and running. + // + g_sRootHub.psPorts[ui8Port].iState = ePortEnumerated; + + // + // 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_sRootHub.bEnumerationBusy = false; +} + +//***************************************************************************** +// +//! Informs the hub class driver that a downstream device failed to enumerate. +//! +//! \param ui8Hub is the address of the hub to which the downstream device +//! is attached. +//! \param ui8Port 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(uint8_t ui8Hub, uint8_t ui8Port) +{ + DEBUG_OUTPUT("Enumeration error for hub %d, port %d\n", ui8Hub, ui8Port); + + // + // Record the fact that the device is not working correctly. + // + g_sRootHub.psPorts[ui8Port].iState = ePortError; + + // + // 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_sRootHub.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. +//! +//! 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 pui8HubPool is memory provided to the hub class to +//! manage the devices that are connected to the hub. The \e ui32PoolSize 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 ui32NumHubs 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. +// +//***************************************************************************** +tHubInstance * +USBHHubOpen(tUSBHHubCallback pfnCallback) +{ + // + // Only one hub is supported. + // + if(g_sRootHub.pfnCallback) + { + DEBUG_OUTPUT("USBHHubOpen failed - already connected.\n"); + return(0); + } + + // + // Save the instance data for this device. + // + g_sRootHub.pfnCallback = pfnCallback; + + DEBUG_OUTPUT("USBHHubOpen completed.\n"); + + // + // Return the device instance pointer. + // + return(&g_sRootHub); +} + +//***************************************************************************** +// +//! This function is used to release a hub device instance. +//! +//! \param psHubInstance 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(tHubInstance *psHubInstance) +{ + // + // Forget the instance pointer and callback. + // + psHubInstance->psDevice = 0; + psHubInstance->pfnCallback = 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_ui32ChangeFlags = 0; + g_ui32HubChanges = 0; + + if(g_sRootHub.psDevice != 0) + { + // + // Save the USB interrupt number. + // + g_sRootHub.ui32IntNum = INT_USB0_TM4C123; + + // + // These devices have a different USB interrupt number. + // + if(CLASS_IS_TM4C129) + { + g_sRootHub.ui32IntNum = INT_USB0_TM4C129; + } + } +} + +//***************************************************************************** +// +//! This function forwards an LPM request for a device to enter L1 sleep state. +//! +//! \param psHubInstance is the hub device instance that was returned +//! from the call to USBHHubOpen(). +//! +//! This function forwards a request from an application to the hub device +//! class to request that a device enter the LPM L1 sleep state. The +//! caller must check the return value to see if the request can be +//! attempted at this time. If another LPM transaction is busy on this or +//! another device, then this function returns \b USBHCD_LPM_PENDING. If +//! the LPM request was scheduled to be sent the function returns +//! \b USBHCD_LPM_AVAIL. The caller should check the USBHCDLPMStatus() +//! function to determine if the request completed successfully or if there +//! was an error. +//! +//! \return This function returns the following values: +//! - \b USBHCD_LPM_AVAIL - The transition to L1 state is scheduled to be sent. +//! - \b USBHCD_LPM_PENDING - There is already an LPM request pending. +// +//***************************************************************************** +uint32_t +USBHHubLPMSleep(tHubInstance *psHubInstance) +{ + // + // Call the host controller function to send the sleep command. + // + return(USBHCDLPMSleep(psHubInstance->psDevice)); +} + +//***************************************************************************** +// +//! This function returns the current status of an LPM request. +//! +//! \param psHubInstance is the hub device instance that was returned +//! from the call to USBHHubOpen(). +//! +//! This function returns the current status of LPM requests for a given +//! device. This is called to determine if a previous request completed +//! successfully or if there was an error. +//! +//! \return This function returns the following values: +//! - \b USBHCD_LPM_AVAIL - There are no pending LPM requests on this specific +//! device or the last request completed successfully. +//! - \b USBHCD_LPM_ERROR - The last LPM request for this device did not +//! complete successfully. +//! - \b USBHCD_LPM_PENDING - The last LPM request has not completed. +// +//***************************************************************************** +uint32_t +USBHHubLPMStatus(tHubInstance *psHubInstance) +{ + // + // Call the host controller function to get the current LPM status. + // + return(USBHCDLPMStatus(psHubInstance->psDevice)); +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhhub.h b/usblib/host/usbhhub.h new file mode 100644 index 0000000..17a3c08 --- /dev/null +++ b/usblib/host/usbhhub.h @@ -0,0 +1,174 @@ +//***************************************************************************** +// +// usbhhub.h - This hold the host driver for hid class. +// +// Copyright (c) 2011-2014 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 2.1.0.12573 of the Tiva 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 + +typedef struct tHubInstance tHubInstance; + +extern const tUSBHostClassDriver g_sUSBHubClassDriver; + +//***************************************************************************** +// +// 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 + +//***************************************************************************** +// +// Values used as the ui16Feature parameter to USBHHubClearHubFeature(). +// +//***************************************************************************** +#define HUB_FEATURE_C_HUB_LOCAL_POWER \ + 0 +#define HUB_FEATURE_C_HUB_OVER_CURRENT \ + 1 + +//***************************************************************************** +// +// Values used as the ui16Feature 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 *pui16HubStatus and *pui16HubChange 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 *pui16PortStatus 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 *pui16PortChange 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 + +//***************************************************************************** +// +// The prototype for the USB Hub host driver callback function. +// +//***************************************************************************** +typedef void (*tUSBHHubCallback)(tHubInstance *psHubInstance, + uint32_t ui32Event, uint32_t ui32MsgParam, + void *pvMsgData); + +//***************************************************************************** +// +// Public function prototypes for the HUB class driver. +// +//***************************************************************************** +extern tHubInstance * USBHHubOpen(tUSBHHubCallback pfnCallback); +extern void USBHHubClose(tHubInstance *psHubInstance); +extern uint32_t USBHHubLPMSleep(tHubInstance *psHubInstance); +extern uint32_t USBHHubLPMStatus(tHubInstance *psHubInstance); + +#ifdef __cplusplus +} +#endif + +#endif // __USBHHUB_H__ diff --git a/usblib/host/usbhmsc.c b/usblib/host/usbhmsc.c new file mode 100644 index 0000000..9ef72fa --- /dev/null +++ b/usblib/host/usbhmsc.c @@ -0,0 +1,756 @@ +//***************************************************************************** +// +// usbhmsc.c - USB MSC host driver. +// +// Copyright (c) 2008-2014 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 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/usbmsc.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhostpriv.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 *psDevice); +static void USBHMSCClose(void *pvInstance); + +//***************************************************************************** +// +// This is the structure for an instance of a USB MSC host driver. +// +//***************************************************************************** +struct tUSBHMSCInstance +{ + // + // Save the device instance. + // + tUSBHostDevice *psDevice; + + // + // Used to save the callback. + // + tUSBHMSCCallback pfnCallback; + + // + // The Maximum LUNs + // + uint32_t ui32MaxLUN; + + // + // The total number of blocks associated with this device. + // + uint32_t ui32NumBlocks; + + // + // The size of the blocks associated with this device. + // + uint32_t ui32BlockSize; + + // + // Bulk IN pipe. + // + uint32_t ui32BulkInPipe; + + // + // Bulk OUT pipe. + // + uint32_t ui32BulkOutPipe; +}; + +//***************************************************************************** +// +// The array of USB MSC host drivers. +// +//***************************************************************************** +static tUSBHMSCInstance g_sUSBHMSCDevice = +{ + 0 +}; + +//***************************************************************************** +// +//! This constant global structure defines the Mass Storage Class Driver that +//! is provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_sUSBHostMSCClassDriver = +{ + USB_CLASS_MASS_STORAGE, + USBHMSCOpen, + USBHMSCClose, + 0 +}; + +//***************************************************************************** +// +//! This function is used to open an instance of the MSC driver. +//! +//! \param psDevice 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 \e psDevice structure. This call can fail +//! if there are not sufficient resources to open the device. The function +//! returns 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 *psDevice) +{ + int32_t i32Idx; + tEndpointDescriptor *psEndpointDescriptor; + tInterfaceDescriptor *psInterface; + + // + // Don't allow the device to be opened without closing first. + // + if(g_sUSBHMSCDevice.psDevice) + { + return(0); + } + + // + // Save the device pointer. + // + g_sUSBHMSCDevice.psDevice = psDevice; + + // + // Get the interface descriptor. + // + psInterface = USBDescGetInterface(psDevice->psConfigDescriptor, 0, 0); + + // + // Loop through the endpoints of the device. + // + for(i32Idx = 0; i32Idx < 3; i32Idx++) + { + // + // Get the first endpoint descriptor. + // + psEndpointDescriptor = + USBDescGetInterfaceEndpoint(psInterface, i32Idx, + psDevice->ui32ConfigDescriptorSize); + + // + // If no more endpoints then break out. + // + if(psEndpointDescriptor == 0) + { + break; + } + + // + // See if this is a bulk endpoint. + // + if((psEndpointDescriptor->bmAttributes & USB_EP_ATTR_TYPE_M) == + USB_EP_ATTR_BULK) + { + // + // See if this is bulk IN or bulk OUT. + // + if(psEndpointDescriptor->bEndpointAddress & USB_EP_DESC_IN) + { + // + // Allocate the USB Pipe for this Bulk IN endpoint. + // + g_sUSBHMSCDevice.ui32BulkInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_BULK_IN_DMA, + psDevice, + psEndpointDescriptor->wMaxPacketSize, + 0); + // + // Configure the USB pipe as a Bulk IN endpoint. + // + USBHCDPipeConfig(g_sUSBHMSCDevice.ui32BulkInPipe, + psEndpointDescriptor->wMaxPacketSize, + 0, + (psEndpointDescriptor->bEndpointAddress & + USB_EP_DESC_NUM_M)); + } + else + { + // + // Allocate the USB Pipe for this Bulk OUT endpoint. + // + g_sUSBHMSCDevice.ui32BulkOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_BULK_OUT_DMA, + psDevice, + psEndpointDescriptor->wMaxPacketSize, + 0); + // + // Configure the USB pipe as a Bulk OUT endpoint. + // + USBHCDPipeConfig(g_sUSBHMSCDevice.ui32BulkOutPipe, + psEndpointDescriptor->wMaxPacketSize, + 0, + (psEndpointDescriptor->bEndpointAddress & + USB_EP_DESC_NUM_M)); + } + } + } + + // + // If the callback exists, call it with an Open event. + // + if(g_sUSBHMSCDevice.pfnCallback != 0) + { + g_sUSBHMSCDevice.pfnCallback(&g_sUSBHMSCDevice, MSC_EVENT_OPEN, 0); + } + + + g_sUSBHMSCDevice.ui32MaxLUN = 0xffffffff; + + // + // Return the only instance of this device. + // + return(&g_sUSBHMSCDevice); +} + +//***************************************************************************** +// +//! 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_sUSBHMSCDevice.psDevice == 0) + { + return; + } + + // + // Reset the device pointer. + // + g_sUSBHMSCDevice.psDevice = 0; + + // + // Free the Bulk IN pipe. + // + if(g_sUSBHMSCDevice.ui32BulkInPipe != 0) + { + USBHCDPipeFree(g_sUSBHMSCDevice.ui32BulkInPipe); + } + + // + // Free the Bulk OUT pipe. + // + if(g_sUSBHMSCDevice.ui32BulkOutPipe != 0) + { + USBHCDPipeFree(g_sUSBHMSCDevice.ui32BulkOutPipe); + } + + // + // If the callback exists then call it. + // + if(g_sUSBHMSCDevice.pfnCallback != 0) + { + g_sUSBHMSCDevice.pfnCallback(&g_sUSBHMSCDevice, MSC_EVENT_CLOSE, 0); + } +} + +//***************************************************************************** +// +//! This function retrieves the maximum number of the logical units on a +//! mass storage device. +//! +//! \param psDevice is the device instance pointer for this request. +//! \param ui32Interface is the interface number on the device specified by the +//! \e ui32Address parameter. +//! \param pui8MaxLUN 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 *psDevice, uint32_t ui32Interface, + uint8_t *pui8MaxLUN) +{ + tUSBRequest sSetupPacket; + + // + // This is a Class specific interface IN request. + // + sSetupPacket.bmRequestType = + USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | USB_RTYPE_INTERFACE; + + // + // Request a the Max LUN for this interface. + // + sSetupPacket.bRequest = USBREQ_GET_MAX_LUN; + sSetupPacket.wValue = 0; + + // + // Indicate the interface to use. + // + sSetupPacket.wIndex = (uint16_t)ui32Interface; + + // + // Only request a single byte of data. + // + sSetupPacket.wLength = 1; + + // + // Put the setup packet in the buffer and send the command. + // + if(USBHCDControlTransfer(0, &sSetupPacket, psDevice, pui8MaxLUN, 1, + MAX_PACKET_SIZE_EP0) != 1) + { + *pui8MaxLUN = 0; + } +} + +//***************************************************************************** +// +//! This function checks if a drive is ready to be accessed. +//! +//! \param psMSCInstance 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 psMSCInstance parameter to determine which device to check +//! and returns zero when the device is ready. Any non-zero return code +//! indicates that the device was not ready. +//! +//! \return This function returns zero if the device is ready and it +//! returns a other value if the device is not ready or if an error occurred. +// +//***************************************************************************** +int32_t +USBHMSCDriveReady(tUSBHMSCInstance *psMSCInstance) +{ + uint8_t ui8MaxLUN, pui8Buffer[SCSI_INQUIRY_DATA_SZ]; + uint32_t ui32Size; + + // + // If there is no device present then return an error. + // + if(psMSCInstance->psDevice == 0) + { + return(-1); + } + + // + // Only request the maximum number of LUNs once. + // + if(g_sUSBHMSCDevice.ui32MaxLUN == 0xffffffff) + { + // + // Get the Maximum LUNs on this device. + // + USBHMSCGetMaxLUN(g_sUSBHMSCDevice.psDevice, + g_sUSBHMSCDevice.psDevice->ui32Interface, &ui8MaxLUN); + + // + // Save the Maximum number of LUNs on this device. + // + g_sUSBHMSCDevice.ui32MaxLUN = ui8MaxLUN; + } + + // + // Just return if the device is returning not present. + // + ui32Size = SCSI_REQUEST_SENSE_SZ; + if(USBHSCSIRequestSense(psMSCInstance->ui32BulkInPipe, + psMSCInstance->ui32BulkOutPipe, pui8Buffer, + &ui32Size) != SCSI_CMD_STATUS_PASS) + { + return(-1); + } + + if((pui8Buffer[SCSI_RS_SKEY] == SCSI_RS_KEY_UNIT_ATTN) && + (pui8Buffer[SCSI_RS_SKEY_AD_SKEY] == SCSI_RS_KEY_NOTPRSNT)) + { + return(-1); + } + + // + // Issue a SCSI Inquiry to get basic information on the device + // + ui32Size = SCSI_INQUIRY_DATA_SZ; + if((USBHSCSIInquiry(psMSCInstance->ui32BulkInPipe, + psMSCInstance->ui32BulkOutPipe, pui8Buffer, + &ui32Size) != SCSI_CMD_STATUS_PASS)) + { + return(-1); + } + + // + // Get the size of the drive. + // + ui32Size = SCSI_INQUIRY_DATA_SZ; + if(USBHSCSIReadCapacity(psMSCInstance->ui32BulkInPipe, + psMSCInstance->ui32BulkOutPipe, pui8Buffer, + &ui32Size) != SCSI_CMD_STATUS_PASS) + { + // + // Get the current sense data from the device to see why it failed + // the Read Capacity command. + // + ui32Size = SCSI_REQUEST_SENSE_SZ; + USBHSCSIRequestSense(psMSCInstance->ui32BulkInPipe, + psMSCInstance->ui32BulkOutPipe, pui8Buffer, + &ui32Size); + + // + // If the read capacity failed then check if the drive is ready. + // + if(USBHSCSITestUnitReady(psMSCInstance->ui32BulkInPipe, + psMSCInstance->ui32BulkOutPipe) != + SCSI_CMD_STATUS_PASS) + { + // + // Get the current sense data from the device to see why it failed + // the Test Unit Ready command. + // + ui32Size = SCSI_REQUEST_SENSE_SZ; + USBHSCSIRequestSense(psMSCInstance->ui32BulkInPipe, + psMSCInstance->ui32BulkOutPipe, pui8Buffer, + &ui32Size); + } + + return(-1); + } + else + { + // + // Read the block size out, value is stored big endian. + // + psMSCInstance->ui32BlockSize = + (pui8Buffer[7] | (pui8Buffer[6] << 8) | pui8Buffer[5] << 16 | + (pui8Buffer[4] << 24)); + + // + // Read the block size out. + // + psMSCInstance->ui32NumBlocks = + (pui8Buffer[3] | (pui8Buffer[2] << 8) | pui8Buffer[1] << 16 | + (pui8Buffer[0] << 24)); + } + + // + // See if the drive is ready to use. + // + if(USBHSCSITestUnitReady(psMSCInstance->ui32BulkInPipe, + psMSCInstance->ui32BulkOutPipe) != + SCSI_CMD_STATUS_PASS) + { + // + // Get the current sense data from the device to see why it failed + // the Test Unit Ready command. + // + ui32Size = SCSI_REQUEST_SENSE_SZ; + USBHSCSIRequestSense(psMSCInstance->ui32BulkInPipe, + psMSCInstance->ui32BulkOutPipe, pui8Buffer, + &ui32Size); + + return(-1); + } + + // + // Success. + // + return(0); +} + +//***************************************************************************** +// +//! This function should be called before any devices are present to enable +//! the mass storage device class driver. +//! +//! \param ui32Drive 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 ui32Drive +//! 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. +// +//***************************************************************************** +tUSBHMSCInstance * +USBHMSCDriveOpen(uint32_t ui32Drive, tUSBHMSCCallback pfnCallback) +{ + // + // Only the first drive is supported and only one callback is supported. + // + if((ui32Drive != 0) || (g_sUSBHMSCDevice.pfnCallback)) + { + return(0); + } + + // + // Save the callback. + // + g_sUSBHMSCDevice.pfnCallback = pfnCallback; + + // + // Return the requested device instance. + // + return(&g_sUSBHMSCDevice); +} + +//***************************************************************************** +// +//! This function should be called to release a drive instance. +//! +//! \param psMSCInstance 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(tUSBHMSCInstance *psMSCInstance) +{ + // + // Close the drive (if it is already open) + // + USBHMSCClose((void *)psMSCInstance); + + // + // Clear the callback indicating that the device is now closed. + // + psMSCInstance->pfnCallback = 0; +} + +//***************************************************************************** +// +//! This function performs a block read to an MSC device. +//! +//! \param psMSCInstance is the device instance to use for this read. +//! \param ui32LBA is the logical block address to read on the device. +//! \param pui8Data is a pointer to the returned data buffer. +//! \param ui32NumBlocks 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 psMSCInstance parameter. The \e ui32LBA parameter specifies +//! the logical block address to read on the device. This function will only +//! perform \e ui32NumBlocks block sized reads. In most cases this is a read +//! of 512 bytes of data. The \e *pui8Data buffer should be at least +//! \e ui32NumBlocks * 512 bytes in size. +//! +//! \return The function returns zero for success and any negative value +//! indicates a failure. +// +//***************************************************************************** +int32_t +USBHMSCBlockRead(tUSBHMSCInstance *psMSCInstance, uint32_t ui32LBA, + uint8_t *pui8Data, uint32_t ui32NumBlocks) +{ + uint32_t ui32Size; + + // + // If there is no device present then return an error. + // + if(psMSCInstance->psDevice == 0) + { + return(-1); + } + + // + // Calculate the actual byte size of the read. + // + ui32Size = psMSCInstance->ui32BlockSize * ui32NumBlocks; + + // + // Perform the SCSI read command. + // + if(USBHSCSIRead10(psMSCInstance->ui32BulkInPipe, + psMSCInstance->ui32BulkOutPipe, ui32LBA, pui8Data, + &ui32Size, ui32NumBlocks) != SCSI_CMD_STATUS_PASS) + { + return(-1); + } + + // + // Success. + // + return(0); +} + +//***************************************************************************** +// +//! This function performs a block write to an MSC device. +//! +//! \param psMSCInstance is the device instance to use for this write. +//! \param ui32LBA is the logical block address to write on the device. +//! \param pui8Data is a pointer to the data to write out. +//! \param ui32NumBlocks 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 psMSCInstance parameter. The \e ui32LBA parameter specifies +//! the logical block address to write on the device. This function will only +//! perform \e ui32NumBlocks block sized writes. In most cases this is a write +//! of 512 bytes of data. The \e *pui8Data buffer should contain at least +//! \e ui32NumBlocks * 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. +// +//***************************************************************************** +int32_t +USBHMSCBlockWrite(tUSBHMSCInstance *psMSCInstance, uint32_t ui32LBA, + uint8_t *pui8Data, uint32_t ui32NumBlocks) +{ + uint32_t ui32Size; + + // + // If there is no device present then return an error. + // + if(psMSCInstance->psDevice == 0) + { + return(-1); + } + + // + // Calculate the actual byte size of the write. + // + ui32Size = psMSCInstance->ui32BlockSize * ui32NumBlocks; + + // + // Perform the SCSI write command. + // + if(USBHSCSIWrite10(psMSCInstance->ui32BulkInPipe, + psMSCInstance->ui32BulkOutPipe, ui32LBA, pui8Data, + &ui32Size, ui32NumBlocks) != SCSI_CMD_STATUS_PASS) + { + return(-1); + } + + // + // Success. + // + return(0); +} + +//***************************************************************************** +// +//! This function forwards an LPM request for a device to enter L1 sleep state. +//! +//! \param psMSCInstance is the host mass storage class instance that was +//! returned from the call to USBHMSCDriveOpen(). +//! +//! This function forwards a request from an application to the mass storage +//! device class to request that a device enter the LPM L1 sleep state. The +//! caller must check the return value to see if the request can be +//! attempted at this time. If another LPM transaction is busy on this or +//! another device, then this function returns \b USBHCD_LPM_PENDING. If +//! the LPM request was scheduled to be sent the function returns +//! \b USBHCD_LPM_AVAIL. The caller should check the USBHCDLPMStatus() +//! function to determine if the request completed successfully or if there +//! was an error. +//! +//! \return This function returns the following values: +//! - USBHCD_LPM_AVAIL - The transition to L1 state is scheduled to be sent. +//! - USBHCD_LPM_PENDING - There is already an LPM request pending. +// +//***************************************************************************** +uint32_t +USBHMSCLPMSleep(tUSBHMSCInstance *psMSCInstance) +{ + // + // Call the host controller function to send the sleep command. + // + return(USBHCDLPMSleep(psMSCInstance->psDevice)); +} + +//***************************************************************************** +// +//! This function returns the current status of an LPM request. +//! +//! \param psMSCInstance is the host mass storage class instance that was +//! returned from the call to USBHMSCDriveOpen(). +//! +//! This function returns the current status of LPM requests for a given +//! device. This is called to determine if a previous request completed +//! successfully or if there was an error. +//! +//! \return This function returns the following values: +//! - \b USBHCD_LPM_AVAIL - There are no pending LPM requests on this specific +//! device or the last request completed successfully. +//! - \b USBHCD_LPM_ERROR - The last LPM request for this device did not +//! complete successfully. +//! - \b USBHCD_LPM_PENDING - The last LPM request has not completed. +// +//***************************************************************************** +uint32_t +USBHMSCLPMStatus(tUSBHMSCInstance *psMSCInstance) +{ + // + // Call the host controller function to get the current LPM status. + // + return(USBHCDLPMStatus(psMSCInstance->psDevice)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhmsc.h b/usblib/host/usbhmsc.h new file mode 100644 index 0000000..f163b71 --- /dev/null +++ b/usblib/host/usbhmsc.h @@ -0,0 +1,99 @@ +//***************************************************************************** +// +// usbhmsc.h - Definitions for the USB MSC host driver. +// +// Copyright (c) 2008-2014 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 2.1.0.12573 of the Tiva 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 +//! @{ +// +//***************************************************************************** + +typedef struct tUSBHMSCInstance tUSBHMSCInstance; + +//***************************************************************************** +// +// These defines are the the events that will be passed in the \e ui32Event +// 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)(tUSBHMSCInstance *psMSCInstance, + uint32_t ui32Event, + void *pvEventData); + +//***************************************************************************** +// +// Prototypes for the USB MSC host driver APIs. +// +//***************************************************************************** +extern tUSBHMSCInstance * USBHMSCDriveOpen(uint32_t ui32Drive, + tUSBHMSCCallback pfnCallback); +extern void USBHMSCDriveClose(tUSBHMSCInstance *psMSCInstance); +extern int32_t USBHMSCDriveReady(tUSBHMSCInstance *psMSCInstance); +extern int32_t USBHMSCBlockRead(tUSBHMSCInstance *psMSCInstance, + uint32_t ui32LBA, uint8_t *pui8Data, + uint32_t ui32NumBlocks); +extern int32_t USBHMSCBlockWrite(tUSBHMSCInstance *psMSCInstance, + uint32_t ui32LBA, uint8_t *pui8Data, + uint32_t ui32NumBlocks); +extern uint32_t USBHMSCLPMSleep(tUSBHMSCInstance *psMSCInstance); +extern uint32_t USBHMSCLPMStatus(tUSBHMSCInstance *psMSCInstance); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// 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..1d4c455 --- /dev/null +++ b/usblib/host/usbhost.h @@ -0,0 +1,295 @@ +//***************************************************************************** +// +// usbhost.h - Host specific definitions for the USB host library. +// +// Copyright (c) 2008-2014 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 2.1.0.12573 of the Tiva 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 + +//***************************************************************************** +// +// These are the defines that are used with USBHCDLPMStatus(). +// +//***************************************************************************** +#define USBHCD_LPM_AVAIL 0x00000000 +#define USBHCD_LPM_ERROR 0x00000001 +#define USBHCD_LPM_PENDING 0x00000002 + +//***************************************************************************** +// +//! 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: +//! +//! \verbatim +//! void (*pfnEvent)(void *pvData); +//! \endverbatim +//! +//! 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 callback for events on USB Pipes allocated +// by USBHCDPipeAlloc(). +// +// \param ui32Pipe is well the pipe +// \param ui32Event is well the event +// +// This prototype is used by any Pipe callbacks that are used in the host +// class drivers. These functions typically handle data events like +// USB_EVENT_RX_AVAILABLE or USB_EVENT_TX_COMPLETE but can be sent other events +// depending on the USB host class in use. See the documentation for the +// individual classes for the valid events for that class. +// +// \return None. +// +//***************************************************************************** +typedef void (* tHCDPipeCallback)(uint32_t ui32Pipe, uint32_t ui32Event); + +//***************************************************************************** +// +// Predeclare the private tUSBHostDevice structure. +// +//***************************************************************************** +typedef struct tUSBHostDevice 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. + // + uint32_t ui32InterfaceClass; + + // + //! The function is called when this class of device has been detected. + // + void *(*pfnOpen)(tUSBHostDevice *psDevice); + + // + //! 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. +// +//***************************************************************************** +extern void USBHCDEvents(void *pvData); + +//***************************************************************************** +// +// Prototypes for the USB Host controller APIs. +// +//***************************************************************************** +extern void USBHCDMain(void); +extern int32_t USBHCDEventEnable(uint32_t ui32Index, void *pvEventDriver, + uint32_t ui32Event); +extern int32_t USBHCDEventDisable(uint32_t ui32Index, void *pvEventDriver, + uint32_t ui32Event); +extern void USBHCDInit(uint32_t ui32Index, void *pvData, + uint32_t ui32Size); +extern void USBHCDPowerConfigInit(uint32_t ui32Index, + uint32_t ui32Flags); +extern uint32_t USBHCDPowerConfigGet(uint32_t ui32Index); +extern uint32_t USBHCDPowerConfigSet(uint32_t ui32Index, + uint32_t ui32Config); +extern uint32_t USBHCDPowerAutomatic(uint32_t ui32Index); +extern void USBHCDRegisterDrivers(uint32_t ui32Index, + const tUSBHostClassDriver * const *ppsHClassDrvrs, + uint32_t ui32NumDrivers); +extern void USBHCDTerm(uint32_t ui32Index); +extern void USBHCDSetConfig(uint32_t ui32Index, uint32_t ui32Device, + uint32_t ui32Configuration); +extern void USBHCDSetInterface(uint32_t ui32Index, uint32_t ui32Device, + uint32_t ui32Interface, + uint32_t ui32AltSetting); +extern void USBHCDSuspend(uint32_t ui32Index); +extern void USBHCDResume(uint32_t ui32Index); +extern void USBHCDReset(uint32_t ui32Index); +extern void USBHCDPipeFree(uint32_t ui32Pipe); +extern uint32_t USBHCDPipeAlloc(uint32_t ui32Index, + uint32_t ui32EndpointType, + tUSBHostDevice *psDevice, + tHCDPipeCallback pfnCallback); +extern uint32_t USBHCDPipeAllocSize(uint32_t ui32Index, + uint32_t ui32EndpointType, + tUSBHostDevice *psDevice, + uint32_t ui32FIFOSize, + tHCDPipeCallback pfnCallback); +extern uint32_t USBHCDPipeConfig(uint32_t ui32Pipe, uint32_t ui32MaxPayload, + uint32_t ui32Interval, + uint32_t ui32TargetEndpoint); +extern uint32_t USBHCDPipeStatus(uint32_t ui32Pipe); +extern uint32_t USBHCDPipeWrite(uint32_t ui32Pipe, uint8_t *pui8Data, + uint32_t ui32Size); +extern uint32_t USBHCDPipeRead(uint32_t ui32Pipe, uint8_t *pui8Data, + uint32_t ui32Size); +extern uint32_t USBHCDPipeSchedule(uint32_t ui32Pipe, uint8_t *pui8Data, + uint32_t ui32Size); +extern uint32_t USBHCDPipeTransferSizeGet(uint32_t ui32Pipe); +extern void USBHCDPipeDataAck(uint32_t ui32Pipe); +extern uint32_t USBHCDPipeReadNonBlocking(uint32_t ui32Pipe, uint8_t *pui8Data, + uint32_t ui32Size); +extern uint32_t USBHCDStringDescriptorGet(tUSBHostDevice *psDevice, + uint8_t *pui8Buffer, + uint32_t ui32Size, + uint32_t ui32LangID, + uint32_t ui32StringIndex); +extern uint32_t USBHCDControlTransfer(uint32_t ui32Index, + tUSBRequest *psSetupPacket, + tUSBHostDevice *psDevice, + uint8_t *pui8Data, uint32_t ui32Size, + uint32_t ui32MaxPacketSize); +extern void USB0HostIntHandler(void); + +extern uint8_t USBHCDDevHubPort(uint32_t ui32Instance); +extern uint8_t USBHCDDevAddress(uint32_t ui32Instance); +extern uint8_t USBHCDDevClass(uint32_t ui32Instance, uint32_t ui32Interface); +extern uint8_t USBHCDDevSubClass(uint32_t ui32Instance, + uint32_t ui32Interface); +extern uint8_t USBHCDDevProtocol(uint32_t ui32Instance, + uint32_t ui32Interface); +extern bool USBHCDFeatureSet(uint32_t ui32Index, uint32_t ui32Feature, + void *pvFeature); +extern uint32_t USBHCDLPMSleep(tUSBHostDevice *psDevice); +extern void USBHCDLPMResume(uint32_t ui32Index); + +//***************************************************************************** +// +// The host class drivers supported by the USB library. +// +//***************************************************************************** +extern const tUSBHostClassDriver g_sUSBHostMSCClassDriver; +extern const tUSBHostClassDriver g_sUSBHIDClassDriver; +extern const tUSBHostClassDriver g_sUSBHostAudioClassDriver; + +//***************************************************************************** +// +// 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..69b8c39 --- /dev/null +++ b/usblib/host/usbhostenum.c @@ -0,0 +1,6284 @@ +//***************************************************************************** +// +// usbhostenum.c - Device enumeration code for the USB host library. +// +// Copyright (c) 2008-2014 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 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#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/usb.h" +#include "driverlib/rtos_bindings.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/usbulpi.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)((int32_t (*)(char *, ...))0) +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_hcd +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// External prototypes. +// +//***************************************************************************** +extern tUSBMode g_iUSBMode; + +extern void OTGDeviceDisconnect(uint32_t ui32Index); + +//***************************************************************************** +// +// Internal function prototypes. +// +//***************************************************************************** +static void USBHCDEP0StateTx(void); +static void USBHCDEnumHandler(void); +static void USBHCDClearFeature(uint32_t ui32DevAddress, uint32_t ui32Endpoint, + uint32_t ui32Feature); + +//***************************************************************************** +// +// 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 +#define INT_EVENT_LPM_PEND 0x40 +#define INT_EVENT_LPM 0x80 + +//***************************************************************************** +// +// Flags used with the tUSBHostDevice.ui32Flags structure member. +// +//***************************************************************************** +#define USBHDEV_FLAG_NOTIFYINT 0x00000001 +#define USBHDEV_FLAG_LPMPEND 0x00000002 +#define USBHDEV_FLAG_LPMERROR 0x00000004 +#define USBHDEV_FLAG_ALLOCATED 0x80000000 + +//***************************************************************************** +// +// This holds the current power configuration that is used when USBHCDInit() +// is called. +// +//***************************************************************************** +static uint32_t g_ui32PowerConfig = 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. + // + eEP0StateIdle, + + // + // Setup packet is expecting data IN. + // + eEP0StateSetupIN, + + // + // Setup packet is sending data OUT. + // + eEP0StateSetupOUT, + + // + // The USB device is receiving data from the device due to an SETUP IN + // request. + // + eEP0StateRx, + + // + // 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. + // + eEP0StateStatus, + + // + // This state is for when a response only has a status phase and no + // data phase. + // + eEP0StateStatusIN, + + // + // This endpoint has signaled a stall condition and is waiting for the + // stall to be acknowledged by the host controller. + // + eEP0StateStall, + + // + // An error has occurred on endpoint 0. + // + eEP0StateError +} +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. + // + uint8_t *pui8Data; + + // + // This is the number of bytes that remain to be sent from or received + // into the g_DeviceState.pEP0Data data buffer. + // + volatile uint32_t ui32BytesRemaining; + + // + // The amount of data being sent/received due to a request. + // + uint32_t ui32DataSize; + + // + // This is the current device address in use by endpoint 0. + // + uint32_t ui32DevAddress; + + // + // The maximum packet size for the device responding to the setup packet. + // + uint32_t ui32MaxPacketSize; + + // + // The host controller's state. + // + tEP0State iState; +} +tHostState; + +//***************************************************************************** +// +// This variable holds the current state of endpoint 0. +// +//***************************************************************************** +static volatile tHostState g_sUSBHEP0State = +{ + 0, // pui8Data + 0, // ui32BytesRemaining + 0, // ui32DataSize + 0, // ui32DevAddress + 0, // ui32MaxPacketSize + eEP0StateIdle // iState +}; + +//***************************************************************************** +// +// 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 uint32_t g_ui32Tickms = 0; +static volatile uint32_t g_ui32CurrentTick = 0; + +//***************************************************************************** +// +// This is 480000000/60000000 or a PLL Divide of 8. +// +//***************************************************************************** +static uint32_t g_ui32PLLDiv = 8; + +//***************************************************************************** +// +// Holds the ULPI configuration. +// +//***************************************************************************** +static uint32_t g_ui32ULPISupport; + +//***************************************************************************** +// +// The current active drivers. +// +//***************************************************************************** +static int32_t g_pi32USBHActiveDriver[MAX_USB_DEVICES + 1]; +static void *g_ppvDriverInstance[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. + // + uint8_t ui8EPNumber; + + // + // The DMA channel assigned to this endpoint. + // + uint8_t ui8DMAChannel; + + // + // The current type for this pipe. + // + uint32_t ui32Type; + + // + // The millisecond interval for this pipe. + // + uint32_t ui32Interval; + + // + // The next tick value to trigger and event on this pipe. + // + uint32_t ui32NextEventTick; + + // + // The current call back for this pipe. + // + tHCDPipeCallback pfnCallback; + + // + // The pointer to which IN data must be copied. + // + uint8_t *pui8ReadPtr; + + // + // The size of the buffer pointed to by pui8ReadPtr. + // + uint32_t ui32ReadSize; + + // + // The number of bytes read, which can be less than ui32ReadSize. + // + uint32_t ui32DataRead; + + // + // The state of a given USB pipe. + // + volatile enum + { + ePipeReading, + ePipeReadDMA, + ePipeReadDMAWait, + ePipeReadSingle, + ePipeDataReady, + ePipeDataSent, + ePipeWriting, + ePipeWriteDMA, + ePipeWriteDMASend, + ePipeWriteDMAWait, + ePipeWriteSingle, + ePipeStalled, + ePipeError, + ePipeIdle, + ePipeDisabled + } + iState; + + // + // The actual FIFO offset allocated to this endpoint. + // + uint16_t ui16FIFOAddr; + + // + // The size of the FIFO entry based on the size parameter. These are + // equivalent to the USB_FIFO_SZ_* values in usb.h. + // + uint8_t ui8FIFOSize; + + // + // The bit offset in the allocation structure. + // + uint8_t ui8FIFOBitOffset; +} +tUSBHCDPipe; + +//***************************************************************************** +// +// The internal state of the device. +// +//***************************************************************************** +typedef enum +{ + eHCDDevDisconnected, + eHCDDevConnected, + eHCDDevConnectedHub, + eHCDDevRequest, + eHCDDevReset, + eHCDDevAddressed, + eHCDDevConfigured, + eHCDDevGetStrings, + eHCDDevError, + eHCDVBUSError, + eHCDPowerFault, + eHCDIdle +} +tUSBHDeviceState; + +static void ProcessUSBDeviceStateMachine(tUSBHDeviceState iOldState, + uint32_t ui32DevIndex); + +//***************************************************************************** +// +// 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(ui32Idx) \ + (g_sUSBHCD.psUSBOUTPipes[ui32Idx].ui32Type | \ + ui32Idx) +#define IN_PIPE_HANDLE(ui32Idx) (g_sUSBHCD.psUSBINPipes[ui32Idx].ui32Type | \ + ui32Idx) + +//***************************************************************************** +// +// 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 +{ + uint32_t ui32USBBase; + + tUSBHCDPipe sUSBControlPipe; + tUSBHCDPipe psUSBOUTPipes[MAX_NUM_PIPES]; + tUSBHCDPipe psUSBINPipes[MAX_NUM_PIPES]; + + // + // 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 psUSBDevice[MAX_USB_DEVICES + 1]; + + // + // Holds the current state of the device. + // + volatile tUSBHDeviceState piDeviceState[MAX_USB_DEVICES + 1]; + + // + // Pointer to the memory pool for this controller. + // + void *pvPool; + + // + // The pool size for this controller. + // + uint32_t ui32PoolSize; + + // + // The number of endpoint pairs supported by the controller. + // + uint32_t ui32NumEndpoints; + + // + // The class drivers for this controller. + // + const tUSBHostClassDriver * const *ppsClassDrivers; + + // + // The number of class drivers. + // + uint32_t ui32NumClassDrivers; + + // + // This is the index in the driver list of the event driver. + // + int32_t i32EventDriver; + + // + // These are the generic event information used by the event driver. + // + uint32_t ui32EventEnables; + + uint32_t ui32Class; + + // + // The DMA instance information for this class. + // + tUSBDMAInstance *psDMAInstance; + + // + // The interrupt number for this instance. + // + uint32_t ui32IntNum; + + // + // The pending interrupt events that are processed in USBHCDMain(). + // + uint32_t ui32IntEvents; + + // + // Features. + // + uint32_t ui32Features; + + // + // The host initiated resume duration in us. + // + uint32_t ui32LPMHIRD; +} +tUSBHCD; + +//***************************************************************************** +// +// The global to hold all of the state information for a given host controller. +// +//***************************************************************************** +static tUSBHCD g_sUSBHCD; + +//***************************************************************************** +// +// Allocates the memory needed to support configuration descriptors for +// devices. +// +//***************************************************************************** +uint32_t +ConfigDescAlloc(tUSBHostDevice *psDevice, uint32_t ui32Size) +{ + uint32_t ui32Idx, ui32BlockSize, ui32PoolSize; + uint8_t *pui8Pool; + + if(g_sUSBHCD.psUSBDevice[0].psConfigDescriptor == 0) + { + // + // 32 bit align the allocation. + // + ui32Size = (ui32Size + 3) & ~3; + + if(g_sUSBHCD.ui32PoolSize < ui32Size) + { + return(0); + } + + // + // Allocate the root connection to the device. + // + g_sUSBHCD.psUSBDevice[0].psConfigDescriptor = g_sUSBHCD.pvPool; + g_sUSBHCD.psUSBDevice[0].ui32ConfigDescriptorSize = ui32Size; + + // + // Allocate the hub memory pools (these can end up null). + // + pui8Pool = (uint8_t *)g_sUSBHCD.pvPool + ui32Size; + ui32PoolSize = g_sUSBHCD.ui32PoolSize - ui32Size; + + // + // Divide the pool up into blocks, one for each supported port. We make + // sure that each block is a multiple of 4 bytes. + // + ui32BlockSize = (ui32PoolSize / MAX_USB_DEVICES) & ~3; + for(ui32Idx = 1; ui32Idx < MAX_USB_DEVICES; ui32Idx++) + { + g_sUSBHCD.psUSBDevice[ui32Idx].psConfigDescriptor = + (tConfigDescriptor *)(pui8Pool + + (ui32Idx * ui32BlockSize)); + g_sUSBHCD.psUSBDevice[ui32Idx].ui32ConfigDescriptorSize = + ui32BlockSize; + } + } + return(ui32Size); +} + +//***************************************************************************** +// +// Frees the memory needed to support configuration descriptors for +// devices. +// +//***************************************************************************** +void +ConfigDescFree(tUSBHostDevice *psDevice) +{ + uint32_t ui32Idx; + + // + // If this is the root device then deallocate. + // + if(&g_sUSBHCD.psUSBDevice[0] == psDevice) + { + for(ui32Idx = 0; ui32Idx < MAX_USB_DEVICES; ui32Idx++) + { + g_sUSBHCD.psUSBDevice[ui32Idx].ui32Flags &= + ~USBHDEV_FLAG_ALLOCATED; + g_sUSBHCD.psUSBDevice[ui32Idx].psConfigDescriptor = 0; + g_sUSBHCD.psUSBDevice[ui32Idx].ui32ConfigDescriptorSize = 0; + } + } +} + +//***************************************************************************** +// +// Return the device index from a ui32Instance value passed from an external +// source. +// +//***************************************************************************** +static uint8_t +HCDInstanceToDevIndex(uint32_t ui32Instance) +{ + uint32_t ui32DevIndex; + + // + // Get the device instance from the instance value. + // + ui32DevIndex = (ui32Instance & 0xff); + + // + // If the above math went negative or is too large just return 0xff. + // + if(ui32DevIndex > MAX_USB_DEVICES) + { + ui32DevIndex = 0xff; + } + + return(ui32DevIndex); +} + +//============================================================================= +// +// This is the internal function that will map an event to a valid event flag. +// +// \param ui32Event 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 ui32Event parameter. +// +//============================================================================= +static uint32_t +GetEventFlag(uint32_t ui32Event) +{ + uint32_t ui32EventFlag; + + ui32EventFlag = 0; + + // + // Search for a valid event flag for the requested event. + // + switch(ui32Event) + { + case USB_EVENT_SOF: + { + ui32EventFlag |= USBHCD_EVFLAG_SOF; + break; + } + case USB_EVENT_CONNECTED: + { + ui32EventFlag |= USBHCD_EVFLAG_CONNECT; + break; + } + case USB_EVENT_DISCONNECTED: + { + ui32EventFlag |= USBHCD_EVFLAG_DISCNCT; + break; + } + case USB_EVENT_UNKNOWN_CONNECTED: + { + ui32EventFlag |= USBHCD_EVFLAG_UNKCNCT; + break; + } + case USB_EVENT_POWER_FAULT: + { + ui32EventFlag |= USBHCD_EVFLAG_PWRFAULT; + break; + } + case USB_EVENT_POWER_DISABLE: + { + ui32EventFlag |= USBHCD_EVFLAG_PWRDIS; + break; + } + case USB_EVENT_POWER_ENABLE: + { + ui32EventFlag |= USBHCD_EVFLAG_PWREN; + break; + } + default: + { + break; + } + } + return(ui32EventFlag); +} + +//============================================================================= +// +//! This function is called to enable a specific USB HCD event notification. +//! +//! \param ui32Index 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 ui32Event 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 ui32Event 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. +// +//============================================================================= +int32_t +USBHCDEventEnable(uint32_t ui32Index, void *pvEventDriver, uint32_t ui32Event) +{ + int32_t i32Ret; + uint32_t ui32EventFlag; + + ASSERT(ui32Index == 0); + + // + // Default the return to fail the call unless a valid event is found. + // + i32Ret = 0; + + // + // Get the event flag for this event. + // + ui32EventFlag = GetEventFlag(ui32Event); + + // + // Check if there was an event flag for the corresponding event. + // + if(ui32EventFlag) + { + // + // Set the enable for this event. + // + g_sUSBHCD.ui32EventEnables |= ui32EventFlag; + + // + // Indicate that the event was valid and is now enabled. + // + i32Ret = 1; + } + + return(i32Ret); +} + +//============================================================================= +// +//! This function is called to disable a specific USB HCD event notification. +//! +//! \param ui32Index 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 ui32Event 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 ui32Event 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. +// +//============================================================================= +int32_t +USBHCDEventDisable(uint32_t ui32Index, void *pvEventDriver, uint32_t ui32Event) +{ + int32_t i32Ret; + uint32_t ui32EventFlag; + + ASSERT(ui32Index == 0); + + // + // Default the return to fail the call unless a valid event is found. + // + i32Ret = 0; + + // + // Get the event flag for this event. + // + ui32EventFlag = GetEventFlag(ui32Event); + + // + // Check if there was an event flag for the corresponding event. + // + if(ui32EventFlag) + { + // + // Clear the enable for this event. + // + g_sUSBHCD.ui32EventEnables &= ~ui32EventFlag; + + // + // Indicate that the event was valid and is now disabled. + // + i32Ret = 1; + } + + return(i32Ret); +} + +//***************************************************************************** +// +// 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(uint32_t ui32Index, uint32_t ui32Class) +{ + 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.ui32Event = USB_EVENT_UNKNOWN_CONNECTED; + sEvent.ui32Instance = ui32Class; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_UNKCNCT); +} + +//***************************************************************************** +// +// Internal memory allocation space is two 32-bit values where each +// bit represents a 64 byte block in the FIFO. This requires 64 bits for +// the 4096 bytes of FIFO available. +// +//***************************************************************************** +static uint32_t g_pui32Alloc[2]; + +//***************************************************************************** +// +// This function handles freeing FIFO memory that has been allocated using the +// FIFOAlloc() function. +// +//***************************************************************************** +static void +FIFOFree(tUSBHCDPipe *psUSBPipe) +{ + uint32_t ui32Mask; + + // + // Calculate the mask value to use to clear off the allocated blocks used + // by the USB pipe specified by psUSBPipe. + // + ui32Mask = (1 << (psUSBPipe->ui8FIFOSize - 2)) - 1; + ui32Mask = ui32Mask << psUSBPipe->ui8FIFOBitOffset; + + // + // Determine which 32 bit word to access based on the size. + // + if(psUSBPipe->ui8FIFOSize > USB_FIFO_SZ_64) + { + // + // If the FIFO size is greater than 64 then use the upper 32 bits. + // + g_pui32Alloc[1] &= ~ui32Mask; + } + else + { + // + // If the FIFO size is less than or equal to 64 then use the lower + // 32 bits. + // + g_pui32Alloc[0] &= ~ui32Mask; + } +} + +//***************************************************************************** +// +// This function is used to allocate FIFO memory to a given USB pipe. +// +// \param psUSBPipe is the USB pipe that needs FIFO memory allocated. +// \param ui32Size is the minimum size in bytes of the FIFO to allocate. +// +// This function will allocate \e ui32Size bytes to the USB pipe in the +// \e psUSBPipe parameter. The function will fill the psUSBPipe structure +// members ui8FIFOSize and ui8FIFOAddr 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 uint32_t +FIFOAlloc(tUSBHCDPipe *psUSBPipe, uint32_t ui32Size) +{ + uint32_t ui32Blocks, ui32Start, ui32BlockSize, ui32Temp, ui32Index; + uint16_t ui16FIFOAddr; + + // + // 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(ui32Size > 64) + { + ui32Index = 1; + } + else + { + ui32Index = 0; + } + + // + // Initial FIFO address is 0. + // + ui16FIFOAddr = 0; + + // + // Initialize the bit pattern and bit location. + // + ui32Blocks = 1; + ui32Start = 0; + + // + // The initial block size is always the minimum size of 64 bytes. + // + ui32BlockSize = 64; + + // + // The initial size and offset are 64 and 0. + // + psUSBPipe->ui8FIFOBitOffset = 0; + psUSBPipe->ui8FIFOSize = 3; + + // + // Scan through 32 bits looking for a memory block large enough to fill + // the request. + // + while(ui16FIFOAddr <= 32) + { + // + // If the pattern is zero then it is a possible match. + // + if((g_pui32Alloc[ui32Index] & ui32Blocks) == 0) + { + // + // If the size is large enough then save it and break out of the + // loop. + // + if(ui32BlockSize >= ui32Size) + { + // + // Mark the memory as allocated. + // + g_pui32Alloc[ui32Index] |= ui32Blocks; + + break; + } + + // + // Increment the size of the FIFO block. + // + psUSBPipe->ui8FIFOSize++; + + // + // Add in a new bit to the size of the allocation. + // + ui32Blocks = ui32Blocks | (ui32Blocks << 1) ; + + // + // Double the current size. + // + ui32BlockSize <<= 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. + // + psUSBPipe->ui8FIFOBitOffset = ui16FIFOAddr; + psUSBPipe->ui8FIFOSize = 3; + + // + // Reset the block size to the minimum (64 bytes). + // + ui32BlockSize = 64; + + // + // Store the current starting bit location and set the block mask + // to this value. + // + ui32Start = 1 << ui16FIFOAddr; + ui32Blocks = ui32Start; + } + + // + // Increase the address of the FIFO offset. + // + ui16FIFOAddr++; + } + + // + // If there was no block large enough then fail this call. + // + if(ui16FIFOAddr > 32) + { + ui32BlockSize = 0; + psUSBPipe->ui16FIFOAddr = 0; + psUSBPipe->ui8FIFOBitOffset = 0; + psUSBPipe->ui8FIFOSize = 0; + } + else + { + // + // Calculate the offset in the FIFO. + // + ui32Temp = psUSBPipe->ui8FIFOBitOffset * 64; + + // + // Sizes greater than 64 are allocated in the second half of the FIFO + // memory space. + // + if(ui32Size > 64) + { + ui32Temp += 2048; + } + + // + // Convert this to the value that can be set in the USB controller. + // + psUSBPipe->ui16FIFOAddr = (uint16_t)ui32Temp; + } + return(ui32BlockSize); +} + +//***************************************************************************** +// +//! This function returns the current byte count of a USB HCD pipe. +//! +//! \param ui32Pipe is the allocated endpoint to modify. +//! +//! This call returns the current or last byte count for a transfer using the +//! pipe specified by the \e ui32Pipe parameter. This is typically used to +//! determine the actual byte count received when a \b USB_EVENT_RX_AVAILABLE +//! occurs. +//! +//! \return If the call was successful, this function returns the number of +//! bytes transfered by the USB pipe. +// +//***************************************************************************** +uint32_t +USBHCDPipeTransferSizeGet(uint32_t ui32Pipe) +{ + uint32_t ui32Index; + + // + // Get the index number from the allocated pipe. + // + ui32Index = (ui32Pipe & EP_PIPE_IDX_M); + + return(g_sUSBHCD.psUSBINPipes[ui32Index].ui32DataRead); +} + +//***************************************************************************** +// +//! This function is used to allocate a USB HCD pipe. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param ui32EndpointType is the type of endpoint that this pipe will be +//! communicating with. +//! \param psDevice is the device instance associated with this endpoint. +//! \param ui32Size 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 ui32Size 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. +// +//***************************************************************************** +uint32_t +USBHCDPipeAllocSize(uint32_t ui32Index, uint32_t ui32EndpointType, + tUSBHostDevice *psDevice, uint32_t ui32Size, + tHCDPipeCallback pfnCallback) +{ + int32_t i32Idx; + + uint32_t ui32HubAddr; + + ASSERT(ui32Index == 0); + + // + // Find a USB pipe that is free. + // + for(i32Idx = 0; i32Idx < MAX_NUM_PIPES; i32Idx++) + { + // + // Handle OUT Pipes. + // + if(ui32EndpointType & EP_PIPE_TYPE_OUT) + { + // + // A zero address indicates free. + // + if(g_sUSBHCD.psUSBOUTPipes[i32Idx].psDevice == 0) + { + // + // Set up uDMA for the pipe. + // + if(ui32EndpointType & EP_PIPE_USE_UDMA) + { + // + // Allocate a DMA channel to the endpoint. + // + g_sUSBHCD.psUSBOUTPipes[i32Idx].ui8DMAChannel = + USBLibDMAChannelAllocate(g_sUSBHCD.psDMAInstance, + IndexToUSBEP(i32Idx + 1), + ui32Size, + USB_DMA_EP_TX | + USB_DMA_EP_HOST); + + // + // If no DMA channel was available then just disable DMA + // on this pipe. + // + if(g_sUSBHCD.psUSBOUTPipes[i32Idx].ui8DMAChannel == 0) + { + ui32EndpointType &= ~EP_PIPE_USE_UDMA; + } + } + + // + // Save the endpoint type and device address and callback + // function. + // + g_sUSBHCD.psUSBOUTPipes[i32Idx].ui32Type = ui32EndpointType; + g_sUSBHCD.psUSBOUTPipes[i32Idx].psDevice = psDevice; + g_sUSBHCD.psUSBOUTPipes[i32Idx].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, + IndexToUSBEP(i32Idx + 1), + USB_HOST_OUT_STATUS); + + // + // Make sure to reset the data toggle. + // + USBEndpointDataToggleClear(USB0_BASE, IndexToUSBEP(i32Idx + 1), + USB_EP_HOST_OUT); + + // + // Initialize the endpoint as idle. + // + g_sUSBHCD.psUSBOUTPipes[i32Idx].iState = ePipeIdle; + + // + // Allocate space in the FIFO for this endpoint. + // + if(FIFOAlloc(&g_sUSBHCD.psUSBOUTPipes[i32Idx], ui32Size) != 0) + { + // + // Configure the FIFO. + // + MAP_USBFIFOConfigSet(USB0_BASE, + IndexToUSBEP(i32Idx + 1), + g_sUSBHCD.psUSBOUTPipes[i32Idx].ui16FIFOAddr, + g_sUSBHCD.psUSBOUTPipes[i32Idx].ui8FIFOSize, + USB_EP_HOST_OUT); + } + + // + // Set the function address for this endpoint. + // + MAP_USBHostAddrSet(USB0_BASE, IndexToUSBEP(i32Idx + 1), + psDevice->ui32Address, USB_EP_HOST_OUT); + + // + // Set the hub and port address for the endpoint. + // + ui32HubAddr = psDevice->ui8Hub | (psDevice->ui8HubPort << 8); + USBHostHubAddrSet(USB0_BASE, IndexToUSBEP(i32Idx + 1), + ui32HubAddr, (USB_EP_HOST_OUT | + psDevice->ui32Speed)); + break; + } + } + // + // Handle IN Pipes. + // + else if(ui32EndpointType & EP_PIPE_TYPE_IN) + { + // + // A zero address indicates free. + // + if(g_sUSBHCD.psUSBINPipes[i32Idx].psDevice == 0) + { + // + // Set up uDMA for the pipe. + // + if(ui32EndpointType & EP_PIPE_USE_UDMA) + { + // + // Allocate a DMA channel to the endpoint. + // + g_sUSBHCD.psUSBINPipes[i32Idx].ui8DMAChannel = + USBLibDMAChannelAllocate(g_sUSBHCD.psDMAInstance, + IndexToUSBEP(i32Idx + 1), + ui32Size, + USB_DMA_EP_RX | + USB_DMA_EP_HOST); + + // + // If no DMA channel was available then just disable DMA + // on this pipe. + // + if(g_sUSBHCD.psUSBINPipes[i32Idx].ui8DMAChannel == 0) + { + ui32EndpointType &= ~EP_PIPE_USE_UDMA; + } + } + + // + // Save the endpoint type and device address and callback + // function. + // + g_sUSBHCD.psUSBINPipes[i32Idx].ui32Type = ui32EndpointType; + g_sUSBHCD.psUSBINPipes[i32Idx].psDevice = psDevice; + g_sUSBHCD.psUSBINPipes[i32Idx].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, + IndexToUSBEP(i32Idx + 1), + USB_HOST_IN_STATUS); + + // + // Make sure to reset the data toggle. + // + USBEndpointDataToggleClear(USB0_BASE, IndexToUSBEP(i32Idx + 1), + USB_EP_HOST_IN); + + // + // Allocate space in the FIFO for this endpoint. + // + if(FIFOAlloc(&g_sUSBHCD.psUSBINPipes[i32Idx], ui32Size) != 0) + { + // + // Configure the FIFO. + // + MAP_USBFIFOConfigSet(USB0_BASE, + IndexToUSBEP(i32Idx + 1), + g_sUSBHCD.psUSBINPipes[i32Idx].ui16FIFOAddr, + g_sUSBHCD.psUSBINPipes[i32Idx].ui8FIFOSize, + USB_EP_HOST_IN); + } + + // + // Set the function address for this endpoint. + // + MAP_USBHostAddrSet(USB0_BASE, IndexToUSBEP(i32Idx + 1), + psDevice->ui32Address, USB_EP_HOST_IN); + + // + // Set the hub and port address for the endpoint. + // + ui32HubAddr = psDevice->ui8Hub | (psDevice->ui8HubPort << 8); + USBHostHubAddrSet(USB0_BASE, IndexToUSBEP(i32Idx + 1), + ui32HubAddr, (USB_EP_HOST_IN | + psDevice->ui32Speed)); + + // + // Reset the state of the pipe to idle. + // + g_sUSBHCD.psUSBINPipes[i32Idx].iState = ePipeIdle; + + break; + } + } + } + + // + // Did not find a free pipe. + // + if(i32Idx == MAX_NUM_PIPES) + { + return(0); + } + + // + // Return the pipe index and type that was allocated. + // + return(ui32EndpointType | i32Idx); +} + +//***************************************************************************** +// +//! This function is used to allocate a USB HCD pipe. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param ui32EndpointType 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. +// +//***************************************************************************** +uint32_t +USBHCDPipeAlloc(uint32_t ui32Index, uint32_t ui32EndpointType, + tUSBHostDevice *psDevice, tHCDPipeCallback pfnCallback) +{ + // + // The old API allocated only 64 bytes to each endpoint. + // + return(USBHCDPipeAllocSize(ui32Index, ui32EndpointType, 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 ui32MaxPayload +//! parameter is typically read directly from the devices endpoint descriptor +//! and is expressed in bytes. +//! +//! Setting the \e ui32Interval parameter depends on the type of endpoint being +//! configured. For endpoints that do not need to use the \e ui32Interval +//! parameter \e ui32Interval should be set to 0. For Bulk \e ui32Interval is +//! a value from 2-16 and will set the NAK timeout value as +//! 2^(\e ui32Interval-1) frames. For interrupt endpoints \e ui32Interval is a +//! value from 1-255 and is the count in frames between polling the endpoint. +//! For isochronous endpoints \e ui32Interval ranges from 1-16 and is the +//! polling interval in frames represented as 2^(\e ui32Interval-1) frames. +//! +//! \param ui32Pipe is the allocated endpoint to modify. +//! \param ui32MaxPayload is maximum data that can be handled per transaction. +//! \param ui32Interval is the polling interval for data transfers expressed in +//! frames. +//! \param ui32TargetEndpoint 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. +// +//***************************************************************************** +uint32_t +USBHCDPipeConfig(uint32_t ui32Pipe, uint32_t ui32MaxPayload, + uint32_t ui32Interval, uint32_t ui32TargetEndpoint) +{ + uint32_t ui32Flags; + uint32_t ui32Index; + + // + // Get the index number from the allocated pipe. + // + ui32Index = (ui32Pipe & EP_PIPE_IDX_M); + + // + // Set the direction. + // + if(ui32Pipe & EP_PIPE_TYPE_OUT) + { + // + // Set the mode for this endpoint. + // + if(g_sUSBHCD.psUSBOUTPipes[ui32Index].ui32Type & EP_PIPE_TYPE_BULK) + { + ui32Flags = USB_EP_MODE_BULK; + } + else if(g_sUSBHCD.psUSBOUTPipes[ui32Index].ui32Type & + EP_PIPE_TYPE_INTR) + { + ui32Flags = USB_EP_MODE_INT; + } + else if(g_sUSBHCD.psUSBOUTPipes[ui32Index].ui32Type & + EP_PIPE_TYPE_ISOC) + { + ui32Flags = USB_EP_MODE_ISOC; + } + else + { + ui32Flags = USB_EP_MODE_CTRL; + } + + ui32Flags |= USB_EP_HOST_OUT; + + g_sUSBHCD.psUSBOUTPipes[ui32Index].ui8EPNumber = + (uint8_t)ui32TargetEndpoint; + + // + // Save the interval and the next tick to trigger a scheduler event. + // + g_sUSBHCD.psUSBOUTPipes[ui32Index].ui32Interval = ui32Interval; + g_sUSBHCD.psUSBOUTPipes[ui32Index].ui32NextEventTick = + ui32Interval + g_ui32CurrentTick; + + // + // Set the device speed. + // + ui32Flags |= (g_sUSBHCD.psUSBOUTPipes[ui32Index].psDevice->ui32Speed); + + // + // Set up the appropriate flags if uDMA is used. + // + if(ui32Pipe & EP_PIPE_USE_UDMA) + { + ui32Flags |= USB_EP_DMA_MODE_0 | USB_EP_AUTO_SET; + } + } + else + { + // + // Set the mode for this endpoint. + // + if(g_sUSBHCD.psUSBINPipes[ui32Index].ui32Type & EP_PIPE_TYPE_BULK) + { + ui32Flags = USB_EP_MODE_BULK; + } + else if(g_sUSBHCD.psUSBINPipes[ui32Index].ui32Type & EP_PIPE_TYPE_INTR) + { + ui32Flags = USB_EP_MODE_INT; + } + else if(g_sUSBHCD.psUSBINPipes[ui32Index].ui32Type & EP_PIPE_TYPE_ISOC) + { + ui32Flags = USB_EP_MODE_ISOC; + } + else + { + ui32Flags = USB_EP_MODE_CTRL; + } + ui32Flags |= USB_EP_HOST_IN; + + g_sUSBHCD.psUSBINPipes[ui32Index].ui8EPNumber = + (uint8_t)ui32TargetEndpoint; + + // + // Save the interval and the next tick to trigger a scheduler event. + // + g_sUSBHCD.psUSBINPipes[ui32Index].ui32Interval = ui32Interval; + g_sUSBHCD.psUSBINPipes[ui32Index].ui32NextEventTick = + ui32Interval + g_ui32CurrentTick; + + // + // Set the device speed. + // + ui32Flags |= g_sUSBHCD.psUSBINPipes[ui32Index].psDevice->ui32Speed; + // + // Set up the appropriate flags if uDMA is used. + // + if(ui32Pipe & EP_PIPE_USE_UDMA) + { + ui32Flags |= USB_EP_DMA_MODE_1 | USB_EP_AUTO_CLEAR | + USB_EP_AUTO_REQUEST; + } + } + + + // + // Configure the endpoint according to the flags determined above. + // + USBHostEndpointConfig(USB0_BASE, + IndexToUSBEP((ui32Pipe & EP_PIPE_IDX_M) + 1), + ui32MaxPayload, ui32Interval, ui32TargetEndpoint, + ui32Flags); + + 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 ui32Pipe 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. +// +//***************************************************************************** +uint32_t +USBHCDPipeStatus(uint32_t ui32Pipe) +{ + return(0); +} + +//***************************************************************************** +// +//! This function is used to write data to a USB HCD pipe. +//! +//! \param ui32Pipe is the USB pipe to put data into. +//! \param pui8Data is a pointer to the data to send. +//! \param ui32Size 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 ui32Size 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. +// +//***************************************************************************** +uint32_t +USBHCDPipeWrite(uint32_t ui32Pipe, uint8_t *pui8Data, uint32_t ui32Size) +{ + uint32_t ui32Endpoint, ui32RemainingBytes, ui32ByteToSend, ui32PipeIdx; + bool bUseDMA; + + // + // Determine which endpoint interface that this pipe is using. + // + ui32Endpoint = IndexToUSBEP((EP_PIPE_IDX_M & ui32Pipe) + 1); + + // + // Get index used for looking up pipe data + // + ui32PipeIdx = ui32Pipe & EP_PIPE_IDX_M; + + // + // Set the total number of bytes to send out. + // + ui32RemainingBytes = ui32Size; + + // + // Default to using DMA. + // + bUseDMA = false; + + // + // Initialize the bytes to send to all of the remaining bytes. + // + ui32ByteToSend = ui32RemainingBytes; + + // + // Send all of the requested data. + // + while(ui32RemainingBytes != 0) + { + // + // If uDMA is not enabled for this pipe, or if the uDMA workaround + // is applied, then don't use uDMA for this transfer. + // + if(ui32Pipe & EP_PIPE_USE_UDMA) + { + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(g_sUSBHCD.ui32IntNum); + + // + // Start the DMA transfer. + // + if(USBLibDMATransfer(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].ui8DMAChannel, + pui8Data, ui32RemainingBytes) != 0) + { + if(ui32RemainingBytes < 64) + { + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState = + ePipeWriteDMASend; + } + else if((ui32RemainingBytes % 64) == 0) + { + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState = + ePipeWriteDMA; + } + else + { + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState = + ePipeWriteDMASend; + } + + bUseDMA = true; + } + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(g_sUSBHCD.ui32IntNum); + } + + if(bUseDMA == false) + { + // + // Only send 64 bytes at a time if not using DMA. + // + if(ui32ByteToSend > 64) + { + ui32ByteToSend = 64; + } + else + { + // + // Send the requested number of bytes. + // + ui32ByteToSend = ui32RemainingBytes; + } + + // + // Start a write request. + // + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState = ePipeWriting; + + // + // Disable uDMA on the USB endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, ui32Endpoint, + USB_EP_HOST_OUT); + + // + // Put the data in the buffer. + // + MAP_USBEndpointDataPut(USB0_BASE, ui32Endpoint, pui8Data, + ui32ByteToSend); + + // + // Schedule the data to be sent. + // + MAP_USBEndpointDataSend(USB0_BASE, ui32Endpoint, USB_TRANS_OUT); + } + + // + // Wait for a status change. + // + while(1) + { + // + // If an error event occurs then exit out of the loop. + // + if(g_sUSBHCD.ui32IntEvents & (INT_EVENT_DISCONNECT | + INT_EVENT_VBUS_ERR | + INT_EVENT_POWER_FAULT)) + { + // + // Set the pipe state to error. + // + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState = ePipeError; + + // + // Needs to be set to exit out of large while loop. + // + ui32RemainingBytes = 0; + + break; + } + // + // If the data was successfully sent then decrement the count and + // continue. + // + else if(g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState == + ePipeDataSent) + { + // + // Decrement the remaining data and advance the pointer. + // + ui32RemainingBytes -= ui32ByteToSend; + pui8Data += ui32ByteToSend; + + // + // If there are less than 64 bytes to send then this is the + // last of the data to go out. + // + if(ui32RemainingBytes < 64) + { + ui32ByteToSend = ui32RemainingBytes; + } + break; + } + else if(g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState == + ePipeStalled) + { + // + // Zero out the size so that the caller knows that no data was + // written. + // + ui32Size = 0; + + // + // Needs to be set to exit out of large while loop. + // + ui32RemainingBytes = 0; + + // + // If DMA is being used, then disable the channel. + // + if(bUseDMA == true) + { + // + // Disable the DMA channel. + // + USBLibDMAChannelDisable(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].ui8DMAChannel); + } + + // + // This is the actual endpoint number. + // + USBHCDClearFeature( + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].psDevice->ui32Address, + ui32Pipe, USB_FEATURE_EP_HALT); + + // + // If there was a stall, then no more data is coming so break + // out. + // + break; + } + else if(g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState == ePipeError) + { + // + // An error occurred so stop this transaction and set the + // number of bytes to zero. + // + ui32Size = 0; + + // + // Needs to be set to exit out of large while loop. + // + ui32RemainingBytes = 0; + + break; + } + } + } + + // + // Go Idle once this state has been reached. + // + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].iState = ePipeIdle; + + return(ui32Size); +} + +//***************************************************************************** +// +//! This function is used to schedule and IN transaction on a USB HCD pipe. +//! +//! \param ui32Pipe is the USB pipe to read data from. +//! \param pui8Data is a pointer to store the data that is received. +//! \param ui32Size is the size in bytes of the buffer pointed to by +//! \e pui8Data. +//! +//! 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. +// +//***************************************************************************** +uint32_t +USBHCDPipeSchedule(uint32_t ui32Pipe, uint8_t *pui8Data, uint32_t ui32Size) +{ + uint32_t ui32Endpoint, ui32PipeIdx; + + // + // Get index used for looking up pipe data + // + ui32PipeIdx = ui32Pipe & EP_PIPE_IDX_M; + + // + // Determine which endpoint interface that this pipe is using. + // + ui32Endpoint = IndexToUSBEP((EP_PIPE_IDX_M & ui32Pipe) + 1); + + if(ui32Pipe & EP_PIPE_TYPE_OUT) + { + // + // Check if uDMA is enabled on this pipe. + // + if(ui32Pipe & EP_PIPE_USE_UDMA) + { + // + // Start a write request. + // + g_sUSBHCD.psUSBOUTPipes[EP_PIPE_IDX_M & ui32Pipe].iState = + ePipeWriteDMASend; + + USBLibDMATransfer(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBOUTPipes[ui32PipeIdx].ui8DMAChannel, + pui8Data, ui32Size); + } + else + { + // + // Start a write request. + // + g_sUSBHCD.psUSBOUTPipes[EP_PIPE_IDX_M & ui32Pipe].iState = + ePipeWriting; + + // + // Put the data in the buffer. + // + MAP_USBEndpointDataPut(USB0_BASE, ui32Endpoint, pui8Data, + ui32Size); + + // + // Schedule the data to be sent. + // + MAP_USBEndpointDataSend(USB0_BASE, ui32Endpoint, USB_TRANS_OUT); + } + } + else + { + // + // If uDMA is not enabled for this pipe, or if the uDMA workaround + // is applied, then do not use uDMA for this transfer. + // + if((ui32Pipe & EP_PIPE_USE_UDMA) == 0) + { + // + // Start a read request. + // + g_sUSBHCD.psUSBINPipes[EP_PIPE_IDX_M & ui32Pipe].iState = + ePipeReading; + + // + // Disable uDMA on the endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, ui32Endpoint, 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 + { + g_sUSBHCD.psUSBINPipes[EP_PIPE_IDX_M & ui32Pipe].iState = + ePipeReadDMA; + + USBLibDMATransfer(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].ui8DMAChannel, + pui8Data, ui32Size); + } + + // + // Remember details of the buffer into which the data will be read. + // + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].pui8ReadPtr = pui8Data; + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].ui32ReadSize = ui32Size; + + // + // Trigger a request for data from the device. + // + MAP_USBHostRequestIN(USB0_BASE, ui32Endpoint); + + // + // No data was put into or read from the buffer. + // + ui32Size = 0; + } + return(ui32Size); +} + +//***************************************************************************** +// +//! This function is used to read data from a USB HCD pipe. +//! +//! \param ui32Pipe is the USB pipe to read data from. +//! \param pui8Data is a pointer to store the data that is received. +//! \param ui32Size is the size in bytes of the buffer pointed to by +//! \e pui8Data. +//! +//! 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 ui32Size 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 pui8Data buffer. +// +//***************************************************************************** +uint32_t +USBHCDPipeReadNonBlocking(uint32_t ui32Pipe, uint8_t *pui8Data, + uint32_t ui32Size) +{ + uint32_t ui32Endpoint; + + // + // Determine which endpoint interface that this pipe is using. + // + ui32Endpoint = IndexToUSBEP((EP_PIPE_IDX_M & ui32Pipe) + 1); + + // + // Read the data out of the USB endpoint interface. + // + MAP_USBEndpointDataGet(USB0_BASE, ui32Endpoint, pui8Data, &ui32Size); + + // + // Acknowledge that the data was read from the endpoint. + // + MAP_USBHostEndpointDataAck(USB0_BASE, ui32Endpoint); + + // + // Go Idle once this state has been reached. + // + g_sUSBHCD.psUSBINPipes[EP_PIPE_IDX_M & ui32Pipe].iState = ePipeIdle; + + return(ui32Size); +} + +//***************************************************************************** +// +//! This function acknowledges data received via an interrupt IN pipe. +//! +//! \param ui32Pipe 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 \b 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(uint32_t ui32Pipe) +{ + uint32_t ui32Endpoint; + + // + // Determine which endpoint interface that this pipe is using. + // + ui32Endpoint = IndexToUSBEP((EP_PIPE_IDX_M & ui32Pipe) + 1); + + // + // Acknowledge that the data was read from the endpoint. + // + USBHostEndpointDataAck(USB0_BASE, ui32Endpoint); + + // + // Go Idle once this state has been reached. + // + g_sUSBHCD.psUSBINPipes[EP_PIPE_IDX_M & ui32Pipe].iState = ePipeIdle; +} + +//***************************************************************************** +// +//! This function is used to read data from a USB HCD pipe. +//! +//! \param ui32Pipe is the USB pipe to read data from. +//! \param pui8Data is a pointer to store the data that is received. +//! \param ui32Size is the size in bytes of the buffer pointed to by +//! \e pui8Data. +//! +//! This function will block and will only return when it has read as much data +//! as requested from the USB pipe. The caller must register a callback with +//! the USBHCDPipeAlloc() call in order to be informed when the data has been +//! received. If the caller provides a non-zero pointer in the \e pui8Data +//! parameter then the data is copied into the buffer before the callback +//! occurs. If the caller provides a zero in \e pui8Data parameter +//! then the caller is responsible for reading the data out of the FIFO when +//! the \b USB_EVENT_RX_AVAILABLE callback event occurs. The value returned +//! by this function can be less than the \e ui32Size 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 pui8Data buffer. +// +//***************************************************************************** +uint32_t +USBHCDPipeRead(uint32_t ui32Pipe, uint8_t *pui8Data, uint32_t ui32Size) +{ + uint32_t ui32Endpoint, ui32RemainingBytes, ui32BytesRead, ui32PipeIdx; + bool bUseDMA; + + // + // Get index used for looking up pipe data + // + ui32PipeIdx = ui32Pipe & EP_PIPE_IDX_M; + + // + // Initialized the number of bytes read. + // + ui32BytesRead = 0; + + // + // Determine which endpoint interface that this pipe is using. + // + ui32Endpoint = IndexToUSBEP(ui32PipeIdx + 1); + + // + // Set the remaining bytes to received. + // + ui32RemainingBytes = ui32Size; + + // + // Default to using DMA. + // + bUseDMA = true; + + // + // Continue until all data requested has been received. + // + while(ui32RemainingBytes != 0) + { + // + // Start a read request. + // + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].iState = ePipeReading; + + // + // Try the DMA transfer should be used for this transfer, so set up + // the uDMA channel in advance of triggering the IN request. + // + if(ui32Pipe & EP_PIPE_USE_UDMA) + { + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(g_sUSBHCD.ui32IntNum); + + if(USBLibDMATransfer(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].ui8DMAChannel, + pui8Data, ui32Size) != 0) + { + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].iState = ePipeReadDMA; + + ui32BytesRead = ui32Size; + } + else + { + bUseDMA = false; + } + + // + // Disable the USB interrupt. + // + OS_INT_ENABLE(g_sUSBHCD.ui32IntNum); + } + + // + // If unable to use DMA then get ready to transfer without DMA. + // + if(bUseDMA == false) + { + // + // Disable uDMA on the endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, ui32Endpoint, USB_EP_HOST_IN); + + // + // Set up for the next transaction. + // + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].pui8ReadPtr = pui8Data; + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].ui32ReadSize = + (ui32RemainingBytes < 64) ? ui32RemainingBytes : 64; + } + + // + // Trigger a request for data from the device. + // + MAP_USBHostRequestIN(USB0_BASE, ui32Endpoint); + + // + // Wait for a status change. + // + while(1) + { + // + // Check if the device stalled the request. + // + if(g_sUSBHCD.psUSBINPipes[ui32PipeIdx].iState == ePipeStalled) + { + // + // Zero out the size so that the caller knows that no data was + // read. + // + ui32Size = 0; + + // + // There are also no remaining bytes to read. + // + ui32RemainingBytes = 0; + + // + // If DMA is being used, then disable the channel. + // + if(bUseDMA == true) + { + USBLibDMAChannelDisable( + g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].ui8DMAChannel); + } + + // + // This is the actual endpoint number. + // + USBHCDClearFeature( + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].psDevice->ui32Address, + ui32Pipe, USB_FEATURE_EP_HALT); + + // + // If there was a stall, then no more data is coming so break + // out. + // + break; + } + + // + // If any error event occurs then exit out of the loop. + // + if(g_sUSBHCD.ui32IntEvents & (INT_EVENT_DISCONNECT | + INT_EVENT_VBUS_ERR | + INT_EVENT_POWER_FAULT)) + { + // + // Set the pipe state to error. + // + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].iState = ePipeError; + break; + } + + // + // If data is ready then return it. + // + if(g_sUSBHCD.psUSBINPipes[ui32PipeIdx].iState == ePipeDataReady) + { + // + // If not using DMA then read the data from the USB. Otherwise + // the data will already be in the buffer. + // + if(bUseDMA == false) + { + // + // Compute bytes to transfer and set up transfer + // + ui32BytesRead = + ui32RemainingBytes > 64 ? 64 : ui32RemainingBytes; + + // + // Acknowledge that the data was read from the endpoint. + // + MAP_USBHostEndpointDataAck(USB0_BASE, ui32Endpoint); + } + + // + // Subtract the number of bytes read from the bytes remaining. + // + ui32RemainingBytes -= ui32BytesRead; + + // + // If there were less than 64 bytes read, then this was a short + // packet and no more data will be returned. + // + if(ui32BytesRead < 64) + { + // + // Subtract off the bytes that were not received and exit + // the loop. + // + ui32Size = ui32Size - ui32RemainingBytes; + break; + } + else + { + // + // Move the buffer ahead to receive more data into the + // buffer. + // + pui8Data += 64; + } + break; + } + else if(g_sUSBHCD.psUSBINPipes[ui32PipeIdx].iState == ePipeError) + { + // + // An error occurred so stop this transaction and set the + // number of bytes to zero. + // + ui32Size = 0; + ui32RemainingBytes = 0; + + break; + } + else if((g_sUSBHCD.psUSBINPipes[ui32PipeIdx].iState == + ePipeReadDMAWait) && + (USBLibDMAChannelStatus(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].ui8DMAChannel) & + USBLIBSTATUS_DMA_COMPLETE)) + { + break; + } + } + } + + // + // Go Idle once this state has been reached. + // + g_sUSBHCD.psUSBINPipes[ui32PipeIdx].iState = ePipeIdle; + + return(ui32Size); +} + +//***************************************************************************** +// +//! This function is used to release a USB pipe. +//! +//! \param ui32Pipe 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(uint32_t ui32Pipe) +{ + uint32_t ui32Index; + + // + // Get the index number from the allocated pipe. + // + ui32Index = (ui32Pipe & EP_PIPE_IDX_M); + + if(ui32Pipe & EP_PIPE_TYPE_OUT) + { + // + // Clear the address and type for this endpoint to free it up. + // + g_sUSBHCD.psUSBOUTPipes[ui32Index].psDevice = 0; + g_sUSBHCD.psUSBOUTPipes[ui32Index].ui32Type = 0; + g_sUSBHCD.psUSBOUTPipes[ui32Index].pfnCallback = 0; + + // + // Check if this pipe has allocated a DMA channel. + // + if(g_sUSBHCD.psUSBOUTPipes[ui32Index].ui8DMAChannel != + USBHCD_DMA_UNUSED) + { + // + // Release the DMA channel associated with this endpoint. + // + USBLibDMAChannelRelease(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBOUTPipes[ui32Index].ui8DMAChannel); + + // + // Clear out the current channel in use by this pipe. + // + g_sUSBHCD.psUSBOUTPipes[ui32Index].ui8DMAChannel = + USBHCD_DMA_UNUSED; + } + + // + // Free up the FIFO memory used by this endpoint. + // + if(g_sUSBHCD.psUSBOUTPipes[ui32Index].ui8FIFOSize) + { + FIFOFree(&g_sUSBHCD.psUSBOUTPipes[ui32Index]); + } + + // + // Set the function address for this endpoint back to zero. + // + USBHostAddrSet(USB0_BASE, IndexToUSBEP(ui32Index + 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, IndexToUSBEP(ui32Index + 1), + 0, (USB_EP_HOST_OUT | USB_EP_SPEED_LOW)); + } + else if(ui32Pipe & EP_PIPE_TYPE_IN) + { + // + // Clear the address and type for this endpoint to free it up. + // + g_sUSBHCD.psUSBINPipes[ui32Index].psDevice = 0; + g_sUSBHCD.psUSBINPipes[ui32Index].ui32Type = 0; + g_sUSBHCD.psUSBINPipes[ui32Index].pfnCallback = 0; + + // + // Check if this pipe has allocated a DMA channel. + // + if(g_sUSBHCD.psUSBINPipes[ui32Index].ui8DMAChannel != + USBHCD_DMA_UNUSED) + { + // + // Release the DMA channel associated with this endpoint. + // + USBLibDMAChannelRelease(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32Index].ui8DMAChannel); + + // + // Clear out the current channel in use by this pipe. + // + g_sUSBHCD.psUSBINPipes[ui32Index].ui8DMAChannel = + USBHCD_DMA_UNUSED; + } + + // + // Free up the FIFO memory used by this endpoint. + // + if(g_sUSBHCD.psUSBINPipes[ui32Pipe & EP_PIPE_IDX_M].ui8FIFOSize) + { + FIFOFree(&g_sUSBHCD.psUSBINPipes[ui32Pipe & EP_PIPE_IDX_M]); + } + + // + // Set the function address for this endpoint back to zero. + // + USBHostAddrSet(USB0_BASE, IndexToUSBEP(ui32Index + 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, IndexToUSBEP(ui32Index + 1), + 0, (USB_EP_HOST_IN | USB_EP_SPEED_LOW)); + + // + // Clear any pending IN transactions. + // + USBHostRequestINClear(USB0_BASE, IndexToUSBEP(ui32Index + 1)); + } +} + +//***************************************************************************** +// +// This internal function initializes the HCD code. +// +// \param ui32Index specifies which USB controller to use. +// \param pvPool is a pointer to the data to use as a memory pool for this +// controller. +// \param ui32PoolSize 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(uint32_t ui32Index, void *pvPool, uint32_t ui32PoolSize) +{ + int32_t i32Idx; + + ASSERT(ui32Index == 0); + + // + // Get the number of endpoints supported by this device. + // + g_sUSBHCD.ui32NumEndpoints = USBNumEndpointsGet(USB0_BASE); + + // + // The first 64 Bytes are allocated to endpoint 0. + // + g_pui32Alloc[0] = 1; + g_pui32Alloc[1] = 0; + + // + // Save the base address for this controller. + // + g_sUSBHCD.ui32USBBase = USB0_BASE; + + // + // Save the USB interrupt number. + // + g_sUSBHCD.ui32IntNum = INT_USB0_TM4C123; + + // + // These devices have a different USB interrupt number. + // + if(CLASS_IS_TM4C129) + { + g_sUSBHCD.ui32IntNum = INT_USB0_TM4C129; + } + + // + // All Pipes are unused at start. + // + for(i32Idx = 0; i32Idx < MAX_NUM_PIPES; i32Idx++) + { + g_sUSBHCD.psUSBINPipes[i32Idx].psDevice = 0; + g_sUSBHCD.psUSBINPipes[i32Idx].ui32Type = USBHCD_PIPE_UNUSED; + g_sUSBHCD.psUSBINPipes[i32Idx].ui8DMAChannel = USBHCD_DMA_UNUSED; + g_sUSBHCD.psUSBOUTPipes[i32Idx].psDevice = 0; + g_sUSBHCD.psUSBOUTPipes[i32Idx].ui32Type = USBHCD_PIPE_UNUSED; + g_sUSBHCD.psUSBOUTPipes[i32Idx].ui8DMAChannel = 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(); + + // + // Initialize the DMA interface. + // + g_sUSBHCD.psDMAInstance = USBLibDMAInit(g_sUSBHCD.ui32USBBase); + + // + // Initialized the device structures. + // + for(i32Idx = 0; i32Idx <= MAX_USB_DEVICES; i32Idx++) + { + // + // Clear the configuration descriptor and state. + // + g_sUSBHCD.piDeviceState[i32Idx] = eHCDIdle; + g_sUSBHCD.psUSBDevice[i32Idx].psConfigDescriptor = 0; + g_sUSBHCD.psUSBDevice[i32Idx].bConfigRead = false; + + // + // Initialize the device descriptor. + // + g_sUSBHCD.psUSBDevice[i32Idx].sDeviceDescriptor.bLength = 0; + g_sUSBHCD.psUSBDevice[i32Idx].sDeviceDescriptor.bMaxPacketSize0 = 0; + + // + // Initialize the device address. + // + g_sUSBHCD.psUSBDevice[i32Idx].ui32Address = 0; + + // + // Set the current interface to 0. + // + g_sUSBHCD.psUSBDevice[i32Idx].ui32Interface = 0; + + // + // Clear the active driver for the device. + // + g_pi32USBHActiveDriver[i32Idx] = -1; + + // + // Initialize the device flags. + // + g_sUSBHCD.psUSBDevice[i32Idx].ui32Flags = 0; + } + + // + // Allocate the memory needed for reading descriptors. + // + g_sUSBHCD.pvPool = pvPool; + g_sUSBHCD.ui32PoolSize = ui32PoolSize; + + // + // Initialize the device class. + // + g_sUSBHCD.ui32Class = USB_CLASS_EVENTS; + + // + // Default enable connect, disconnect, unknown device and power fault + // event notifications. + // + g_sUSBHCD.ui32EventEnables = 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_iUSBMode == eUSBModeHost) || (g_iUSBMode == eUSBModeForceHost)) + { + // + // 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_POWER_FAULT); + + MAP_USBIntEnableEndpoint(USB0_BASE, USB_INTEP_ALL); + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(g_sUSBHCD.ui32IntNum); + + // + // There is no automatic power in pure host mode. + // + USBHCDPowerConfigSet(ui32Index, (g_ui32PowerConfig & + ~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); + } + + // + // Configure LPM if it is enabled. + // + if(g_sUSBHCD.ui32Features & USBLIB_FEATURE_LPM_EN) + { + if(g_sUSBHCD.ui32Features & USBLIB_FEATURE_LPM_RMT_WAKE) + { + USBHostLPMConfig(USB0_BASE, g_sUSBHCD.ui32LPMHIRD, + USB_DEV_LPM_LS_L1 | USB_DEV_LPM_LS_RMTWAKE); + } + else + { + USBHostLPMConfig(USB0_BASE, g_sUSBHCD.ui32LPMHIRD, + USB_DEV_LPM_LS_L1); + } + + // + // Enable USB interrupts for LPM mode, these enables have no effect on + // devices that do not support LPM. + // + USBLPMIntEnable(USB0_BASE, USB_INTLPM_ERROR | USB_INTLPM_RESUME | + USB_INTLPM_INCOMPLETE | USB_INTLPM_ACK | + USB_INTLPM_NYET | USB_INTLPM_STALL); + } +} + +//***************************************************************************** +// +//! This function is used to set the power pin and power fault configuration. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param ui32PwrConfig 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 ui32PwrConfig +//! 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 \b USBHCD_VBUS_MANUAL is used then the application must provide an +//! event driver to receive the \b USB_EVENT_POWER_ENABLE and +//! \b 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: \b USB_HOST_PWRFLT_LOW, \b USB_HOST_PWRFLT_HIGH, +//! \b USB_HOST_PWRFLT_EP_NONE, \b USB_HOST_PWRFLT_EP_TRI, +//! \b USB_HOST_PWRFLT_EP_LOW, \b USB_HOST_PWRFLT_EP_HIGH, +//! \b USB_HOST_PWREN_LOW, \b USB_HOST_PWREN_HIGH, \b USB_HOST_PWREN_VBLOW, and +//! \b USB_HOST_PWREN_VBHIGH. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDPowerConfigInit(uint32_t ui32Index, uint32_t ui32PwrConfig) +{ + ASSERT(ui32Index == 0); + + // + // Save the value as it will be used later. + // + g_ui32PowerConfig = ui32PwrConfig; +} + +//***************************************************************************** +// +//! This function is used to get the power pin and power fault configuration. +//! +//! \param ui32Index 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. +//! +//***************************************************************************** +uint32_t +USBHCDPowerConfigGet(uint32_t ui32Index) +{ + ASSERT(ui32Index == 0); + + // + // Save the value as it will be used later. + // + return(g_ui32PowerConfig); +} + +//***************************************************************************** +// +//! This function is used to set the power pin and power fault configuration. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param ui32Config 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. +//! +//***************************************************************************** +uint32_t +USBHCDPowerConfigSet(uint32_t ui32Index, uint32_t ui32Config) +{ + ASSERT(ui32Index == 0); + + // + // Remember the current setting. + // + g_ui32PowerConfig = ui32Config; + + // + // Clear out the two flag bits. + // + ui32Config = g_ui32PowerConfig & ~(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_ui32PowerConfig & 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_ui32PowerConfig & 1) + { + g_ui32PowerConfig |= USB_HOST_PWRFLT_EP_LOW; + ui32Config |= USB_HOST_PWRFLT_EP_LOW; + } + else + { + g_ui32PowerConfig |= USB_HOST_PWRFLT_EP_HIGH; + ui32Config |= USB_HOST_PWRFLT_EP_HIGH; + } + } + + // + // Initialize the power configuration. + // + MAP_USBHostPwrConfig(USB0_BASE, ui32Config); + + // + // If not in manual mode then just turn on power. + // + if((g_ui32PowerConfig & 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 ui32Index 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. +//! +//***************************************************************************** +uint32_t +USBHCDPowerAutomatic(uint32_t ui32Index) +{ + // + // Check if the controller is automatically applying power or not. + // + if(g_ui32PowerConfig & USBHCD_VBUS_MANUAL) + { + return(0); + } + return(1); +} + +//***************************************************************************** +// +//! This function is used to initialize the HCD code. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param pvPool is a pointer to the data to use as a memory pool for this +//! controller. +//! \param ui32PoolSize is the size in bytes of the buffer passed in as +//! \e 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 eUSBModeHost 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. +//! +//! 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(uint32_t ui32Index, void *pvPool, uint32_t ui32PoolSize) +{ + int32_t i32Driver; + + // + // Check the arguments. + // + ASSERT(ui32Index == 0); + + // + // Make sure there is at least enough to read the configuration descriptor. + // + ASSERT(ui32PoolSize >= sizeof(tConfigDescriptor)); + + // + // Should not call this if the stack is in device mode. + // + ASSERT(g_iUSBMode != eUSBModeDevice); + ASSERT(g_iUSBMode != eUSBModeForceDevice); + + // + // If the mode was not set then default to eUSBModeHost. + // + if(g_iUSBMode == eUSBModeNone) + { + g_iUSBMode = eUSBModeHost; + } + + // + // 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(); + + // + // Set the PLL to USB clock divider. + // + if(g_ui32PLLDiv == 0) + { + USBClockEnable(USB0_BASE, g_ui32PLLDiv, USB_CLOCK_EXTERNAL); + } + else + { + USBClockEnable(USB0_BASE, g_ui32PLLDiv, USB_CLOCK_INTERNAL); + } + + // + // Configure ULPI support. + // + if(g_ui32ULPISupport != USBLIB_FEATURE_ULPI_NONE) + { + USBULPIEnable(USB0_BASE); + + if(g_ui32ULPISupport & USBLIB_FEATURE_ULPI_HS) + { + ULPIConfigSet(USB0_BASE, ULPI_CFG_HS); + } + else + { + ULPIConfigSet(USB0_BASE, ULPI_CFG_FS); + } + } + else + { + USBULPIDisable(USB0_BASE); + } + + // + // If the application not requesting OTG mode then set the mode to forced + // host mode. If the mode is actually eUSBModeHost, this will be switched + // off when ID pin detection is complete and the ID is no longer in use. + // + if(g_iUSBMode != eUSBModeOTG) + { + // + // Force Host mode on devices that support force host mode. + // + MAP_USBHostMode(USB0_BASE); + } + + // + // Call our internal function to perform the initialization. + // + USBHCDInitInternal(ui32Index, pvPool, ui32PoolSize); + + // + // No event driver is present by default. + // + g_sUSBHCD.i32EventDriver = -1; + + // + // Search through the Host Class driver list for the devices class. + // + for(i32Driver = 0; i32Driver < g_sUSBHCD.ui32NumClassDrivers; i32Driver++) + { + if(g_sUSBHCD.ppsClassDrivers[i32Driver]->ui32InterfaceClass == + USB_CLASS_EVENTS) + { + // + // Event driver was found so remember it. + // + g_sUSBHCD.i32EventDriver = i32Driver; + } + } + + // + // Get the number of ticks per millisecond, this is only used by blocking + // delays using the SysCtlDelay() function. + // + if(g_ui32Tickms == 0) + { + if(CLASS_IS_TM4C129) + { + g_ui32Tickms = 120000000 / 3000; + } + else + { + g_ui32Tickms = 80000000 / 3000; + } + } +} + +//***************************************************************************** +// +//! This function is used to initialize the HCD class driver list. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param ppsHClassDrvs is an array of host class drivers that are +//! supported on this controller. +//! \param ui32NumDrivers 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 ui32Index parameter. This function should be called +//! before enabling the host controller driver with the USBHCDInit() function. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDRegisterDrivers(uint32_t ui32Index, + const tUSBHostClassDriver * const *ppsHClassDrvs, + uint32_t ui32NumDrivers) +{ + ASSERT(ui32Index == 0); + + // + // Save the class drivers. + // + g_sUSBHCD.ppsClassDrivers = ppsHClassDrvs; + + // + // Save the number of class drivers. + // + g_sUSBHCD.ui32NumClassDrivers = ui32NumDrivers; +} + +//***************************************************************************** +// +//! This function is used to terminate the HCD code. +//! +//! \param ui32Index 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(uint32_t ui32Index) +{ + int32_t i32Idx; + + ASSERT(ui32Index == 0); + + // + // End the session. + // + USBOTGSessionRequest(USB0_BASE, false); + + // + // Remove power from the USB bus. + // + MAP_USBHostPwrDisable(USB0_BASE); + + // + // Disable USB interrupts. + // + OS_INT_DISABLE(g_sUSBHCD.ui32IntNum); + + 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. + // + for(i32Idx = 0; i32Idx < MAX_NUM_PIPES; i32Idx++) + { + g_sUSBHCD.psUSBINPipes[i32Idx].ui32Type = USBHCD_PIPE_UNUSED; + g_sUSBHCD.psUSBOUTPipes[i32Idx].ui32Type = USBHCD_PIPE_UNUSED; + } + + // + // Free the memory used by the configuration descriptor. + // + ConfigDescFree(&g_sUSBHCD.psUSBDevice[0]); + + g_sUSBHCD.piDeviceState[0] = eHCDIdle; + g_sUSBHCD.psUSBDevice[0].psConfigDescriptor = 0; + g_sUSBHCD.psUSBDevice[0].bConfigRead = false; + g_sUSBHCD.psUSBDevice[0].sDeviceDescriptor.bLength = 0; + g_sUSBHCD.psUSBDevice[0].sDeviceDescriptor.bMaxPacketSize0 = 0; + g_sUSBHCD.psUSBDevice[0].ui32Address = 0; + g_sUSBHCD.psUSBDevice[0].ui32Interface = 0; + g_sUSBHCD.pvPool = 0; + g_sUSBHCD.ui32PoolSize = 0; +} + +//***************************************************************************** +// +//! This function generates reset signaling on the USB bus. +//! +//! \param ui32Index 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(uint32_t ui32Index) +{ + ASSERT(ui32Index == 0); + + // + // Start the reset signaling. + // + MAP_USBHostReset(USB0_BASE, 1); + + // + // Wait 20ms + // + OS_DELAY(g_ui32Tickms * 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_ui32Tickms * 20); +} + +//***************************************************************************** +// +//! This function will generate suspend signaling on the USB bus. +//! +//! \param ui32Index 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(uint32_t ui32Index) +{ + ASSERT(ui32Index == 0); + + // + // Start the suspend signaling. + // + MAP_USBHostSuspend(USB0_BASE); +} + +//***************************************************************************** +// +//! This function will generate resume signaling on the USB bus. +//! +//! \param ui32Index 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(uint32_t ui32Index) +{ + ASSERT(ui32Index == 0); + + // + // Start the resume signaling. + // + MAP_USBHostResume(USB0_BASE, 1); + + // + // Wait 100ms + // + OS_DELAY(g_ui32Tickms * 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 ui32Index specifies which USB controller to use. +//! \param psDevice 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 psDevice->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 psDevice->sDeviceDescriptor.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 uint32_t +USBHCDGetConfigDescriptor(uint32_t ui32Index, tUSBHostDevice *psDevice) +{ + tUSBRequest sSetupPacket; + uint32_t ui32Bytes; + tConfigDescriptor sConfigDescriptor; + + ASSERT(ui32Index == 0); + + ui32Bytes = 0; + + // + // This is a Standard Device IN request. + // + sSetupPacket.bmRequestType = + USB_RTYPE_DIR_IN | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + sSetupPacket.wValue = USB_DTYPE_CONFIGURATION << 8; + + // + // Index is always 0 for device configurations requests. + // + sSetupPacket.wIndex = 0; + + // + // Only ask for the configuration header first to see how big the + // whole thing is. + // + if(!psDevice->bConfigRead) + { + // + // Only request the space available. + // + sSetupPacket.wLength = sizeof(tConfigDescriptor); + + // + // Put the setup packet in the buffer. + // + ui32Bytes = + USBHCDControlTransfer(0, &sSetupPacket, psDevice, + (uint8_t *)&sConfigDescriptor, + sizeof(tConfigDescriptor), + psDevice->sDeviceDescriptor.bMaxPacketSize0); + } + + // + // If the Configuration header was successfully returned then get the + // full configuration descriptor. + // + if(ui32Bytes == sizeof(tConfigDescriptor)) + { + // + // Save the total size and request the full configuration descriptor. + // + sSetupPacket.wLength = sConfigDescriptor.wTotalLength; + + // + // Not enough space to hold this configuration descriptor. + // + if(ConfigDescAlloc(psDevice, sConfigDescriptor.wTotalLength) == 0) + { + return(0); + } + + // + // Don't allow the buffer to be larger than was allocated. + // + if(sSetupPacket.wLength > psDevice->ui32ConfigDescriptorSize) + { + return(0); + } + + // + // Put the setup packet in the buffer. + // + ui32Bytes = + USBHCDControlTransfer(0, &sSetupPacket, psDevice, + (uint8_t *)psDevice->psConfigDescriptor, + sSetupPacket.wLength, + psDevice->sDeviceDescriptor.bMaxPacketSize0); + + // + // If we read the descriptor, remember the fact. + // + if(ui32Bytes) + { + psDevice->bConfigRead = true; + } + } + + return(ui32Bytes); +} + +//***************************************************************************** +// +//! This function issues a request for a device descriptor from a device. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param psDevice 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 psDevice->sDeviceDescriptor descriptor is used to hold the data for this +//! request. \e psDevice->sDeviceDescriptor.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 uint32_t +USBHCDGetDeviceDescriptor(uint32_t ui32Index, tUSBHostDevice *psDevice) +{ + tUSBRequest sSetupPacket; + uint32_t ui32Bytes; + + ASSERT(ui32Index == 0); + + // + // This is a Standard Device IN request. + // + sSetupPacket.bmRequestType = + USB_RTYPE_DIR_IN | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + sSetupPacket.wValue = USB_DTYPE_DEVICE << 8; + + // + // Index is always 0 for device requests. + // + sSetupPacket.wIndex = 0; + + // + // All devices must have at least an 8 byte max packet size so just ask + // for 8 bytes to start with. + // + sSetupPacket.wLength = sizeof(tDeviceDescriptor); + + ui32Bytes = 0; + + // + // Discover the max packet size for endpoint 0. + // + if(psDevice->sDeviceDescriptor.bMaxPacketSize0 == 0) + { + // + // Put the setup packet in the buffer. + // + ui32Bytes = + USBHCDControlTransfer(ui32Index, &sSetupPacket, psDevice, + (uint8_t *)&(psDevice->sDeviceDescriptor), + sizeof(tDeviceDescriptor), 8); + } + + // + // Now get the full descriptor now that the actual maximum packet size + // is known. + // + if(ui32Bytes < sizeof(tDeviceDescriptor)) + { + sSetupPacket.wLength = (uint16_t)sizeof(tDeviceDescriptor); + + ui32Bytes = + USBHCDControlTransfer(ui32Index, &sSetupPacket, psDevice, + (uint8_t *)&(psDevice->sDeviceDescriptor), + sizeof(tDeviceDescriptor), + psDevice->sDeviceDescriptor.bMaxPacketSize0); + } + + return(ui32Bytes); +} + +//***************************************************************************** +// +//! This function issues a request for a string descriptor from a device. +//! +//! \param psDevice is the device for this request. +//! \param pui8Buffer is the pointer to the buffer to store the requested +//! string descriptor. +//! \param ui32Size is the size of the buffer passed in the buffer that will +//! be used for this request. +//! \param ui32LangID is the ID of the language for the requested string. +//! \param ui32StringIndex is the index for the request. +//! +//! This function will request a string descriptor from the device of the type +//! specified in the \e ui32DescriptorType parameter. The \e pui8Descriptor +//! pointer is the location where the request results will be stored. The +//! \e ui32Size should be passed in to indicate the size of the +//! \e pui8Descriptor buffer. The \e ui32DevAddress parameter is used to +//! specify the device address to communicate with on the USB bus. This value +//! should be specified as 0 for any non-configured device on the USB bus and +//! be changed to the address set by a call to USBHCDSetAddress(). +//! +//! \return The number of bytes returned in the \e pui8Buffer due to the +//! request. This value can be zero if the device did not respond. +// +//***************************************************************************** +uint32_t +USBHCDStringDescriptorGet(tUSBHostDevice *psDevice, uint8_t *pui8Buffer, + uint32_t ui32Size, uint32_t ui32LangID, + uint32_t ui32StringIndex) +{ + uint32_t ui32BytesReturned; + tUSBRequest sSetupPacket; + + // + // Default the number of bytes to zero. + // + ui32BytesReturned = 0; + + // + // This is a Standard Device IN request. + // + sSetupPacket.bmRequestType = + USB_RTYPE_DIR_IN | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + + // + // Request for a string descriptor. + // + sSetupPacket.wValue = (USB_DTYPE_STRING << 8) | + (uint16_t)ui32StringIndex; + + // + // Set the language ID. + // + sSetupPacket.wIndex = ui32LangID; + + // + // Only request the space available. + // + sSetupPacket.wLength = (uint16_t)ui32Size; + + // + // Put the setup packet in the buffer. + // + ui32BytesReturned = + USBHCDControlTransfer(0, &sSetupPacket, psDevice, pui8Buffer, ui32Size, + psDevice->sDeviceDescriptor.bMaxPacketSize0); + + // + // Return the number of bytes in the string. + // + return(ui32BytesReturned); +} + +//***************************************************************************** +// +//! This function is used to send the set address command to a device. +//! +//! \param ui32DevIndex 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 ui32DevAddress 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 ui32DevAddress parameter is +//! used for all further communications with the device after this function +//! returns. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDSetAddress(uint32_t ui32DevIndex, uint32_t ui32DevAddress) +{ + tUSBRequest sSetupPacket; + + // + // This is a Standard Device OUT request. + // + sSetupPacket.bmRequestType = + USB_RTYPE_DIR_OUT | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_SET_ADDRESS; + sSetupPacket.wValue = ui32DevAddress; + + // + // Index is always 0 for device requests. + // + sSetupPacket.wIndex = 0; + + // + // Only request the space available. + // + sSetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &sSetupPacket, + &g_sUSBHCD.psUSBDevice[ui32DevIndex], 0, + 0, MAX_PACKET_SIZE_EP0); + + // + // Must delay 2ms after setting the address. + // + OS_DELAY(g_ui32Tickms * 2); +} + +//***************************************************************************** +// +//! This function is used to send a Clear Feature request to a device. +//! +//! \param ui32DevAddress is the USB bus address of the device that will +//! receive this request. +//! \param ui32Pipe is the pipe that will be used to send the request. +//! \param ui32Feature is one of the USB_FEATURE_* definitions. +//! +//! This function will issue a Clear Feature request to the device indicated +//! by the \e ui32DevAddress parameter. The \e ui32Pipe parameter is the USB +//! pipe that should be used to send this request. The \e ui32Feature +//! 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(uint32_t ui32DevAddress, uint32_t ui32Pipe, + uint32_t ui32Feature) +{ + tUSBRequest sSetupPacket; + uint32_t ui32Index; + + // + // Get the index number from the allocated pipe. + // + ui32Index = (ui32Pipe & EP_PIPE_IDX_M); + + // + // This is a Standard Device OUT request. + // + sSetupPacket.bmRequestType = + USB_RTYPE_DIR_OUT | USB_RTYPE_STANDARD | USB_RTYPE_ENDPOINT; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_CLEAR_FEATURE; + sSetupPacket.wValue = ui32Feature; + + // + // Set the endpoint to access. + // + if(ui32Pipe & EP_PIPE_TYPE_IN) + { + sSetupPacket.wIndex = g_sUSBHCD.psUSBINPipes[ui32Index].ui8EPNumber | + 0x80; + } + else + { + sSetupPacket.wIndex = g_sUSBHCD.psUSBOUTPipes[ui32Index].ui8EPNumber; + } + + // + // This is always 0. + // + sSetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &sSetupPacket, + &g_sUSBHCD.psUSBDevice[ui32DevAddress - 1], 0, 0, + MAX_PACKET_SIZE_EP0); + + // + // Set the endpoint to access. + // + if(ui32Pipe & EP_PIPE_TYPE_IN) + { + MAP_USBEndpointDataToggleClear(USB0_BASE, + IndexToUSBEP(ui32Index + 1), + USB_EP_HOST_IN); + } + else + { + MAP_USBEndpointDataToggleClear(USB0_BASE, + IndexToUSBEP(ui32Index + 1), + USB_EP_HOST_OUT); + } + + // + // Must delay 2ms after clearing the feature. + // + OS_DELAY(g_ui32Tickms * 2); +} + +//***************************************************************************** +// +//! This function is used to set the current configuration for a device. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param ui32Device is the USB device for this function. +//! \param ui32Configuration is one of the devices valid configurations. +//! +//! This function is used to set the current device configuration for a USB +//! device. The \e ui32Configuration 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(uint32_t ui32Index, uint32_t ui32Device, + uint32_t ui32Configuration) +{ + tUSBRequest sSetupPacket; + tUSBHostDevice *psDevice; + + ASSERT(ui32Index == 0); + + psDevice = (tUSBHostDevice *)ui32Device; + + // + // This is a Standard Device OUT request. + // + sSetupPacket.bmRequestType = + USB_RTYPE_DIR_OUT | USB_RTYPE_STANDARD | USB_RTYPE_DEVICE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_SET_CONFIG; + sSetupPacket.wValue = ui32Configuration; + + // + // Index is always 0 for device requests. + // + sSetupPacket.wIndex = 0; + + // + // Only request the space available. + // + sSetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &sSetupPacket, psDevice, 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 ui32Index specifies which USB controller to use. +//! \param ui32Device is the USB device for this function. +//! \param ui32Interface is one of the valid interface numbers for a device. +//! \param ui32AltSetting is one of the valid alternate interfaces for the +//! \e ui32Interface number. +//! +//! This function is used to change the alternate setting for one of the valid +//! interfaces on a USB device. The \e ui32Device 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 ui32Interface and +//! \e ui32AltSetting. +//! +//! \b Example: Set the USB device interface 2 to alternate setting 1. +//! +//! \verbatim +//! USBHCDSetInterface(0, ui32Device, 2, 1); +//! \endverbatim +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDSetInterface(uint32_t ui32Index, uint32_t ui32Device, + uint32_t ui32Interface, uint32_t ui32AltSetting) +{ + tUSBRequest sSetupPacket; + tUSBHostDevice *psDevice; + + ASSERT(ui32Index == 0); + + psDevice = (tUSBHostDevice *)ui32Device; + + // + // This is a Standard Device OUT request. + // + sSetupPacket.bmRequestType = + USB_RTYPE_DIR_OUT | USB_RTYPE_STANDARD | USB_RTYPE_INTERFACE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = USBREQ_SET_INTERFACE; + + // + // Index is the interface to access. + // + sSetupPacket.wIndex = ui32Interface; + + // + // wValue is the alternate setting. + // + sSetupPacket.wValue = ui32AltSetting; + + + // + // Only request the space available. + // + sSetupPacket.wLength = 0; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &sSetupPacket, psDevice, 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) +{ + int32_t i32Idx; + + g_ui32CurrentTick++; + + for(i32Idx = 0; i32Idx < g_sUSBHCD.ui32NumEndpoints; i32Idx++) + { + // + // Skip unused pipes. + // + if(g_sUSBHCD.psUSBINPipes[i32Idx].ui32Type == USBHCD_PIPE_UNUSED) + { + continue; + } + + // + // If the tick has expired and it has an interval then update it. + // + if((g_sUSBHCD.psUSBINPipes[i32Idx].ui32Interval != 0) && + (g_sUSBHCD.psUSBINPipes[i32Idx].ui32NextEventTick == + g_ui32CurrentTick)) + { + // + // Schedule the next event. + // + g_sUSBHCD.psUSBINPipes[i32Idx].ui32NextEventTick += + g_sUSBHCD.psUSBINPipes[i32Idx].ui32Interval; + + // + // 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.psUSBINPipes[i32Idx].iState == ePipeIdle) && + (g_sUSBHCD.psUSBINPipes[i32Idx].pfnCallback)) + { + g_sUSBHCD.psUSBINPipes[i32Idx].pfnCallback( + IN_PIPE_HANDLE(i32Idx), + USB_EVENT_SCHEDULER); + } + } + } +} + +//***************************************************************************** +// +// The internal USB host mode interrupt handler. +// +// \param ui32Index is the USB controller associated with this interrupt. +// \param ui32Status 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(uint32_t ui32Index, uint32_t ui32Status) +{ + uint32_t ui32EPStatus, ui32DMAIntStatus, ui32Idx, ui32DevIndex; + static uint32_t ui32SOFDivide = 0; + int32_t i32ClassDrvr; + + // + // 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.psUSBDevice[0].ui32Flags |= USBHDEV_FLAG_NOTIFYINT; + + if(ui32Status & USB_INTCTRL_SOF) + { + // + // Indicate that a start of frame has occurred. + // + g_sUSBHCD.ui32IntEvents |= INT_EVENT_SOF; + } + + // + // A power fault has occurred so notify the application. + // + if(ui32Status & USB_INTCTRL_POWER_FAULT) + { + // + // Indicate that a power fault has occurred. + // + g_sUSBHCD.ui32IntEvents |= INT_EVENT_POWER_FAULT; + + // + // Turn off power to the bus. + // + MAP_USBHostPwrDisable(USB0_BASE); + + // + // Disable USB interrupts. + // + OS_INT_DISABLE(g_sUSBHCD.ui32IntNum); + + return; + } + + // + // In the event of a USB VBUS error, end the session and remove power to + // the device. + // + if(ui32Status & 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_sUSBHCD.ui32IntEvents = INT_EVENT_VBUS_ERR; + return; + } + + // + // Received a reset from the host. + // + if(ui32Status & USB_INTCTRL_BABBLE) + { + } + + // + // Suspend was signaled on the bus. + // + if(ui32Status & USB_INTCTRL_SUSPEND) + { + } + + // + // Start the session. + // + if(ui32Status & USB_INTCTRL_SESSION) + { + // + // Power the USB bus. + // + MAP_USBHostPwrEnable(USB0_BASE); + + USBOTGSessionRequest(USB0_BASE, true); + } + + // + // Resume was signaled on the bus. + // + if(ui32Status & USB_INTCTRL_RESUME) + { + } + + // + // Device connected so tell the main routine to issue a reset. + // + if(ui32Status & USB_INTCTRL_CONNECT) + { + // + // Set the connect flag and clear disconnect if it happens to be set. + // + g_sUSBHCD.ui32IntEvents |= INT_EVENT_CONNECT; + g_sUSBHCD.ui32IntEvents &= ~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 eUSBModeHost. + // + if(ui32Status & USB_INTCTRL_MODE_DETECT) + { + // + // If in eUSBModeHost mode then switch back to OTG detection + // so that VBUS can be monitored but free up the ID pin. + // + if(g_iUSBMode == eUSBModeHost) + { + USBOTGMode(USB0_BASE); + } + } + + // + // Device was unplugged. + // + if(ui32Status & USB_INTCTRL_DISCONNECT) + { + // + // Set the disconnect flag and clear connect if it happens to be set. + // + g_sUSBHCD.ui32IntEvents |= INT_EVENT_DISCONNECT; + g_sUSBHCD.ui32IntEvents &= ~INT_EVENT_CONNECT; + } + + // + // Start of Frame was received. + // + if(ui32Status & USB_INTCTRL_SOF) + { + // + // Increment the global Start of Frame counter. + // + g_ui32USBSOFCount++; + + // + // Increment our SOF divider. + // + ui32SOFDivide++; + + // + // Have we counted enough SOFs to allow us to call the tick function? + // + if(ui32SOFDivide == USB_SOF_TICK_DIVIDE) + { + // + // Yes - reset the divider and call the SOF tick handler. + // + ui32SOFDivide = 0; + InternalUSBStartOfFrameTick(USB_SOF_TICK_DIVIDE); + } + } + + // + // Handle the LPM interrupt + // + ui32Status = USBLPMIntStatus(USB0_BASE); + + if(ui32Status) + { + // + // Set the LPM interrupt event and clear the pending event. + // + g_sUSBHCD.ui32IntEvents |= INT_EVENT_LPM; + g_sUSBHCD.ui32IntEvents &= ~INT_EVENT_LPM_PEND; + + // + // Anything other than and acknowledge means that the transfer + // was not complete for some reason. + // + for(ui32Idx = 0; ui32Idx < (MAX_USB_DEVICES + 1); ui32Idx++) + { + if((ui32Status != USB_INTLPM_ACK) && + (g_sUSBHCD.psUSBDevice[ui32Idx].ui32Flags & + USBHDEV_FLAG_LPMPEND)) + { + g_sUSBHCD.psUSBDevice[ui32Idx].ui32Flags |= + USBHDEV_FLAG_LPMERROR; + } + g_sUSBHCD.psUSBDevice[ui32Idx].ui32Flags &= ~USBHDEV_FLAG_LPMPEND; + } + } + + // + // Check to see if any DMA transfers are pending + // + ui32DMAIntStatus = USBLibDMAIntStatus(g_sUSBHCD.psDMAInstance); + + if(ui32DMAIntStatus) + { + // + // Handle any DMA interrupt processing. + // + USBLibDMAIntHandler(g_sUSBHCD.psDMAInstance, ui32DMAIntStatus); + + for(ui32Idx = 0; ui32Idx < MAX_NUM_PIPES; ui32Idx++) + { + if((g_sUSBHCD.psUSBINPipes[ui32Idx].iState == ePipeReadDMAWait) || + (g_sUSBHCD.psUSBINPipes[ui32Idx].iState == ePipeReadDMA)) + { + // + // If the DMA channel transfer is complete, send an ack. + // + if(USBLibDMAChannelStatus(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32Idx].ui8DMAChannel) == + USBLIBSTATUS_DMA_COMPLETE) + { + // + // Acknowledge the IN request. + // + MAP_USBHostEndpointDataAck(USB0_BASE, + IndexToUSBEP(ui32Idx + 1)); + + // + // If using uDMA then the endpoint status interrupt will + // not occur. So process the data ready event here. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].iState = ePipeDataReady; + + // + // Only call a handler if one is present. + // + if(g_sUSBHCD.psUSBINPipes[ui32Idx].pfnCallback) + { + g_sUSBHCD.psUSBINPipes[ui32Idx].pfnCallback( + IN_PIPE_HANDLE(ui32Idx), USB_EVENT_RX_AVAILABLE); + } + + // + // Remember that we need to notify this device's class + // driver that an interrupt occurred. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].psDevice->ui32Flags |= + USBHDEV_FLAG_NOTIFYINT; + } + } + else if(g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState == + ePipeWriteDMASend) + { + // + // If the uDMA channel transfer is complete, then tell + // the USB controller to go ahead and send the data + // + if(USBLibDMAChannelStatus(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBOUTPipes[ui32Idx].ui8DMAChannel) & + USBLIBSTATUS_DMA_COMPLETE) + { + MAP_USBEndpointDataSend(USB0_BASE, + IndexToUSBEP(ui32Idx + 1), + USB_TRANS_OUT); + + // + // Now waiting on the final endpoint interrupt. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState = + ePipeWriteDMAWait; + } + } + else if(g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState == ePipeWriteDMA) + { + // + // Data was transmitted successfully. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState = ePipeDataSent; + + // + // Only call a handler if one is present. + // + if(g_sUSBHCD.psUSBOUTPipes[ui32Idx].pfnCallback) + { + // + // Notify the pipe that its last transaction was completed. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].pfnCallback( + OUT_PIPE_HANDLE(ui32Idx), + USB_EVENT_TX_COMPLETE); + } + } + } + } + + // + // Get the current endpoint interrupt status. + // + ui32Status = MAP_USBIntStatusEndpoint(USB0_BASE); + + // + // Handle end point 0 interrupts. + // + if(ui32Status & USB_INTEP_0) + { + // + // Indicate that a start of frame has occurred. + // + g_sUSBHCD.ui32IntEvents |= INT_EVENT_ENUM; + } + + for(ui32Idx = 0; ui32Idx < MAX_NUM_PIPES; ui32Idx++) + { + // + // Check the next pipe, the first time through this will clear out + // any interrupts dealing with endpoint zero since it was handled + // above. + // + ui32Status >>= 1; + + // + // Break out if there are no more pending interrupts. + // + if(ui32Status == 0) + { + break; + } + + // + // Check the status of the receive(IN) pipes. + // + if(ui32Status & 0x10000) + { + // + // Clear the status flag for the IN Pipe. + // + ui32Status &= ~0x10000; + + // + // Read the status of the endpoint connected to this pipe. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, + IndexToUSBEP(ui32Idx + 1)); + + // + // Check if the device stalled the request. + // + if(ui32EPStatus & USB_HOST_IN_STALL) + { + // + // Clear the stall condition on this endpoint pipe. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, + IndexToUSBEP(ui32Idx + 1), + USB_HOST_IN_STALL); + + // + // Save the STALLED state. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].iState = ePipeStalled; + + // + // Notify the pipe that it was stalled. + // + if(g_sUSBHCD.psUSBINPipes[ui32Idx].pfnCallback) + { + g_sUSBHCD.psUSBINPipes[ui32Idx].pfnCallback( + IN_PIPE_HANDLE(ui32Idx), + USB_EVENT_STALL); + } + } + else if(ui32EPStatus & 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, + IndexToUSBEP(ui32Idx + 1), + USB_HOST_IN_ERROR); + + // + // Save the STALLED state. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].iState = ePipeError; + + // + // Notify the pipe that it was stalled. + // + if(g_sUSBHCD.psUSBINPipes[ui32Idx].pfnCallback) + { + g_sUSBHCD.psUSBINPipes[ui32Idx].pfnCallback( + IN_PIPE_HANDLE(ui32Idx), + USB_EVENT_ERROR); + } + } + // + // Handle the case where the pipe is reading a single packet. + // + else if(g_sUSBHCD.psUSBINPipes[ui32Idx].iState == ePipeReadDMA) + { + void *pvAddr; + + // + // Enable the DMA channel and wait for it to complete. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].iState = ePipeReadDMAWait; + + pvAddr = USBLibDMAAddrGet(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32Idx].ui8DMAChannel); + + // + // Save the amount of data available. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].ui32DataRead = + USBEndpointDataAvail(USB0_BASE, + IndexToUSBEP(ui32Idx + 1)); + + // + // Only request what is available. + // + if(g_sUSBHCD.psUSBINPipes[ui32Idx].ui32DataRead <= + g_sUSBHCD.psUSBINPipes[ui32Idx].ui32ReadSize) + { + // + // Reset the transfer size. + // + USBLibDMATransfer(g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32Idx].ui8DMAChannel, + pvAddr, + g_sUSBHCD.psUSBINPipes[ui32Idx].ui32DataRead); + } + else + { + // + // The transfer size did not change, this leaves some + // data in the FIFO. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].ui32DataRead = + g_sUSBHCD.psUSBINPipes[ui32Idx].ui32ReadSize; + } + + USBLibDMAChannelEnable( + g_sUSBHCD.psDMAInstance, + g_sUSBHCD.psUSBINPipes[ui32Idx].ui8DMAChannel); + } + else if(g_sUSBHCD.psUSBINPipes[ui32Idx].iState == ePipeReading) + { + // + // Data is available. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].iState = ePipeDataReady; + + // + // 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.psUSBINPipes[ui32Idx].pui8ReadPtr) + { + g_sUSBHCD.psUSBINPipes[ui32Idx].ui32DataRead = + g_sUSBHCD.psUSBINPipes[ui32Idx].ui32ReadSize; + + USBEndpointDataGet(USB0_BASE, IndexToUSBEP(ui32Idx + 1), + g_sUSBHCD.psUSBINPipes[ui32Idx].pui8ReadPtr, + &g_sUSBHCD.psUSBINPipes[ui32Idx].ui32DataRead); + } + + // + // Notify the pipe that its last transaction was completed. + // + if(g_sUSBHCD.psUSBINPipes[ui32Idx].pfnCallback) + { + g_sUSBHCD.psUSBINPipes[ui32Idx].pfnCallback( + IN_PIPE_HANDLE(ui32Idx), + USB_EVENT_RX_AVAILABLE); + } + + } + + // + // Remember that we need to notify this device's class + // driver that an interrupt occurred. + // + g_sUSBHCD.psUSBINPipes[ui32Idx].psDevice->ui32Flags |= + USBHDEV_FLAG_NOTIFYINT; + } + + // + // Check the status of the transmit(OUT) pipes. + // + if(ui32Status & 1) + { + // + // Read the status of the endpoint connected to this pipe. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, + IndexToUSBEP(ui32Idx + 1)); + + // + // Check if the device stalled the request. + // + if(ui32EPStatus & USB_HOST_OUT_STALL) + { + // + // Clear the stall condition on this endpoint pipe. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, + IndexToUSBEP(ui32Idx + 1), + USB_HOST_OUT_STALL); + + // + // Save the STALLED state. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState = ePipeStalled; + + // + // Only call a handler if one is present. + // + if(g_sUSBHCD.psUSBOUTPipes[ui32Idx].pfnCallback) + { + // + // Notify the pipe that it was stalled. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].pfnCallback( + OUT_PIPE_HANDLE(ui32Idx), + USB_EVENT_STALL); + } + } + else if(ui32EPStatus & USB_HOST_OUT_ERROR) + { + // + // Clear the error condition on this endpoint pipe. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, + IndexToUSBEP(ui32Idx + 1), + USB_HOST_OUT_ERROR); + + // + // Save the Pipes error state. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState = ePipeError; + + // + // Only call a handler if one is present. + // + if(g_sUSBHCD.psUSBOUTPipes[ui32Idx].pfnCallback) + { + // + // Notify the pipe that had an error. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].pfnCallback( + OUT_PIPE_HANDLE(ui32Idx), + USB_EVENT_ERROR); + } + } + else if((g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState == + ePipeWriting) || + (g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState == + ePipeWriteDMAWait)) + { + // + // Data was transmitted successfully. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].iState = ePipeDataSent; + + // + // Only call a handler if one is present. + // + if(g_sUSBHCD.psUSBOUTPipes[ui32Idx].pfnCallback) + { + // + // Notify the pipe that its last transaction was completed. + // + g_sUSBHCD.psUSBOUTPipes[ui32Idx].pfnCallback( + OUT_PIPE_HANDLE(ui32Idx), + USB_EVENT_TX_COMPLETE); + } + } + + // + // Clear the stall condition on this endpoint pipe. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, + IndexToUSBEP(ui32Idx + 1), + ui32EPStatus & USB_HOST_OUT_STATUS); + // + // Remember that we need to notify this device's class + // driver that an interrupt occurred. + // + if(g_sUSBHCD.psUSBOUTPipes[ui32Idx].psDevice) + { + g_sUSBHCD.psUSBINPipes[ui32Idx].psDevice->ui32Flags |= + USBHDEV_FLAG_NOTIFYINT; + } + } + } + + // + // Send back notifications to any class driver whose endpoint required + // service during the handler. + // + for(ui32DevIndex = 0; ui32DevIndex <= MAX_USB_DEVICES; ui32DevIndex++) + { + // + // Which class driver does this device use? + // + i32ClassDrvr = g_pi32USBHActiveDriver[ui32DevIndex]; + + // + // If a class driver is in use, and one of its endpoints was serviced + // and the class driver has an interrupt callback... + // + if((i32ClassDrvr >= 0) && + (g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Flags & + USBHDEV_FLAG_NOTIFYINT) && + (g_sUSBHCD.ppsClassDrivers[i32ClassDrvr]->pfnIntHandler)) + { + // + // ...call the class driver's interrupt notification callback. + // + g_sUSBHCD.ppsClassDrivers[i32ClassDrvr]->pfnIntHandler( + g_ppvDriverInstance[ui32DevIndex]); + } + } +} + +//***************************************************************************** +// +//! 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) +{ + uint32_t ui32Status; + + // + // Get the control interrupt status. + // + ui32Status = MAP_USBIntStatusControl(USB0_BASE); + + // + // Call the internal handler to process the interrupts. + // + USBHostIntHandlerInternal(0, ui32Status); +} + +//***************************************************************************** +// +//! This function opens the class driver. +//! +//! \param ui32Index specifies which USB controller to use. +//! \param ui32DeviceNum 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 int32_t +USBHCDOpenDriver(uint32_t ui32Index, uint32_t ui32DeviceNum) +{ + int32_t i32Driver; + uint32_t ui32Class; + tInterfaceDescriptor *psInterface; + tEventInfo sEvent; + + ASSERT(ui32Index == 0); + + // + // Get the interface descriptor. + // + psInterface = USBDescGetInterface( + g_sUSBHCD.psUSBDevice[ui32DeviceNum].psConfigDescriptor, + g_sUSBHCD.psUSBDevice[ui32DeviceNum].ui32Interface, + USB_DESC_ANY); + + // + // Read the interface class. + // + ui32Class = psInterface->bInterfaceClass; + + // + // Search through the Host Class driver list for the devices class. + // + for(i32Driver = 0; i32Driver < g_sUSBHCD.ui32NumClassDrivers; i32Driver++) + { + // + // If a driver was found call the open for this driver and save which + // driver is in use. + // + if(g_sUSBHCD.ppsClassDrivers[i32Driver]->ui32InterfaceClass == + ui32Class) + { + // + // Call the open function for the class driver. + // + g_ppvDriverInstance[ui32DeviceNum] = + g_sUSBHCD.ppsClassDrivers[i32Driver]->pfnOpen( + &g_sUSBHCD.psUSBDevice[ui32DeviceNum]); + + // + // If the driver was successfully loaded then break out of the + // loop. + // + if(g_ppvDriverInstance[ui32DeviceNum] != 0) + { + break; + } + } + } + + // + // If no drivers were found then return -1 to indicate an invalid + // driver instance. + // + if(i32Driver == g_sUSBHCD.ui32NumClassDrivers) + { + // + // Send an unknown connection event. + // + SendUnknownConnect(ui32Index, (ui32Index << 16) | ui32DeviceNum); + + // + // Indicate that no driver was found. + // + i32Driver = -1; + } + else + { + // + // If the connect event is enabled then send the event. + // + sEvent.ui32Event = USB_EVENT_CONNECTED; + sEvent.ui32Instance = (ui32Index << 16) | ui32DeviceNum; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_CONNECT); + } + + return(i32Driver); +} + +//***************************************************************************** +// +// This function will send an event to a registered event driver. +// +// \param ui32Index 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(uint32_t ui32Index, tEventInfo *psEvent, + uint32_t ui32EvFlag) +{ + // + // Make sure that an event driver has been registered. + // + if((g_sUSBHCD.i32EventDriver != -1) && + (g_sUSBHCD.ppsClassDrivers[g_sUSBHCD.i32EventDriver]->pfnIntHandler) && + (g_sUSBHCD.ui32EventEnables & ui32EvFlag)) + { + g_sUSBHCD.ppsClassDrivers[g_sUSBHCD.i32EventDriver]->pfnIntHandler( + psEvent); + } +} + +//***************************************************************************** +// +// This function handles the necessary clean up for device disconnect. +// +// \param ui32Index 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(uint32_t ui32Index, uint32_t ui32DevIndex) +{ + tEventInfo sEvent; + + ASSERT(ui32Index == 0); + ASSERT(ui32DevIndex <= 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.ui32Event = USB_EVENT_DISCONNECTED; + sEvent.ui32Instance = (ui32Index << 16) | ui32DevIndex; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_DISCNCT); + + // + // Reset the class. + // + g_sUSBHCD.ui32Class = USB_CLASS_EVENTS; + + // + // Free the memory used by the configuration descriptor. + // + ConfigDescFree(&g_sUSBHCD.psUSBDevice[ui32DevIndex]); + + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Flags = 0; + g_sUSBHCD.psUSBDevice[ui32DevIndex].bConfigRead = false; + + // + // Reset the max packet size so that this will be re-read from new devices. + // + g_sUSBHCD.psUSBDevice[ui32DevIndex].sDeviceDescriptor.bMaxPacketSize0 = 0; + + // + // No longer have a device descriptor. + // + g_sUSBHCD.psUSBDevice[ui32DevIndex].sDeviceDescriptor.bLength = 0; + + // + // No longer addressed. + // + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Address = 0; + + // + // If this was an active driver then close it out. + // + if(g_pi32USBHActiveDriver[ui32DevIndex] >= 0) + { + // + // Call the driver Close entry point. + // + g_sUSBHCD.ppsClassDrivers[g_pi32USBHActiveDriver[ui32DevIndex]]-> + pfnClose(g_ppvDriverInstance[ui32DevIndex]); + + // + // No active driver now present. + // + g_pi32USBHActiveDriver[ui32DevIndex] = -1; + g_ppvDriverInstance[ui32DevIndex] = 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((ui32DevIndex == 0) && (g_iUSBMode == eUSBModeOTG)) + { + 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 iOldState; + int32_t i32Dev; + tEventInfo sEvent; + + // + // Save the old state to detect changes properly. + // + iOldState = g_sUSBHCD.piDeviceState[0]; + + // + // Fix up the state if any important interrupt events occurred. + // + if(g_sUSBHCD.ui32IntEvents) + { + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(g_sUSBHCD.ui32IntNum); + + if(g_sUSBHCD.ui32IntEvents & 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.ui32Event = USB_EVENT_POWER_FAULT; + sEvent.ui32Instance = 0; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_PWRFAULT); + + g_sUSBHCD.piDeviceState[0] = eHCDPowerFault; + } + else if(g_sUSBHCD.ui32IntEvents & INT_EVENT_VBUS_ERR) + { + // + // A VBUS error has occurred. This event trumps connect and + // disconnect since it will cause a controller reset. + // + g_sUSBHCD.piDeviceState[0] = eHCDVBUSError; + } + else + { + // + // Has a device connected? + // + if(g_sUSBHCD.ui32IntEvents & INT_EVENT_CONNECT) + { + g_sUSBHCD.piDeviceState[0] = eHCDDevReset; + g_sUSBHCD.psUSBDevice[0].ui8Hub = 0; + g_sUSBHCD.psUSBDevice[0].ui8HubPort = 0; + } + else + { + // + // Has a device disconnected? + // + if(g_sUSBHCD.ui32IntEvents & INT_EVENT_DISCONNECT) + { + g_sUSBHCD.piDeviceState[0] = eHCDDevDisconnected; + } + } + + // + // Handle the start of frame event + // + if(g_sUSBHCD.ui32IntEvents & INT_EVENT_SOF) + { + // + // If the connect event is enabled then send the event. + // + sEvent.ui32Event = USB_EVENT_SOF; + sEvent.ui32Instance = 0; + InternalUSBHCDSendEvent(0, &sEvent, USBHCD_EVFLAG_SOF); + + USBHostCheckPipes(); + + // + // Call the hub driver to have it perform any necessary + // processing to handle downstream devices. + // + USBHHubMain(); + } + + // + // Handle LPM interrupt events. + // + if(g_sUSBHCD.ui32IntEvents & INT_EVENT_LPM) + { + // + // There should be a pending LPM request. + // + ASSERT((g_sUSBHCD.ui32IntEvents & INT_EVENT_LPM_PEND) != 0); + + for(i32Dev = 0; i32Dev < MAX_USB_DEVICES + 1; i32Dev++) + { + // + // Find the device with the pending LPM request. + // + if(g_sUSBHCD.psUSBDevice[i32Dev].ui32Flags & + USBHDEV_FLAG_LPMPEND) + { + // + // Clear the pending event at the device level, this + // leaves the error set if it was already set. + // + g_sUSBHCD.psUSBDevice[i32Dev].ui32Flags &= + ~USBHDEV_FLAG_LPMPEND; + + // + // Clear the pending request and event at the host + // controller level. + // + g_sUSBHCD.ui32IntEvents &= ~(INT_EVENT_LPM_PEND | + INT_EVENT_LPM); + } + } + } + } + + // + // Clear the flags. + // + g_sUSBHCD.ui32IntEvents = 0; + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(g_sUSBHCD.ui32IntNum); + } + + // + // 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(i32Dev = 0; i32Dev <= MAX_USB_DEVICES; i32Dev++) + { + // + // 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(i32Dev != 0) + { + iOldState = g_sUSBHCD.piDeviceState[i32Dev]; + } + + // + // Process the state machine for this device. + // + ProcessUSBDeviceStateMachine(iOldState, i32Dev); + } +} + +static void +ProcessUSBDeviceStateMachine(tUSBHDeviceState iOldState, + uint32_t ui32DevIndex) +{ + switch(g_sUSBHCD.piDeviceState[ui32DevIndex]) + { + // + // There was a power fault condition so shut down and wait for the + // application to re-initialized the system. + // + case eHCDPowerFault: + { + break; + } + + // + // There was a VBUS error so handle it. + // + case eHCDVBUSError: + { + // + // Disable USB interrupts. + // + OS_INT_DISABLE(g_sUSBHCD.ui32IntNum); + + // + // If there was a device in any state of connection then indicate + // that it has been disconnected. + // + if((iOldState != eHCDIdle) && (iOldState != eHCDPowerFault)) + { + // + // Handle device disconnect. + // + USBHCDDeviceDisconnected(0, ui32DevIndex); + } + + // + // Reset the controller. + // + MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_USB0); + + // + // Wait for 100ms before trying to re-power the device. + // + OS_DELAY(g_ui32Tickms * 100); + + // + // Re-initialize the HCD. + // + USBHCDInitInternal(0, g_sUSBHCD.pvPool, g_sUSBHCD.ui32PoolSize); + + break; + } + // + // Trigger a reset to the connected device. + // + case eHCDDevReset: + { + if(!ui32DevIndex) + { + // + // 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.piDeviceState[0] = eHCDDevConnected; + + // + // Remember that we don't have a valid configuration descriptor + // yet. + // + g_sUSBHCD.psUSBDevice[0].bConfigRead = false; + + break; + } + // + // Device connection has been established now start enumerating + // the device. + // + case eHCDDevConnected: + case eHCDDevConnectedHub: + { + // + // First check if we have read the device descriptor at all + // before proceeding. + // + if(g_sUSBHCD.psUSBDevice[ui32DevIndex].sDeviceDescriptor.bLength == + 0) + { + // + // Initialize a request for the device descriptor. + // + DEBUG_OUTPUT("Connection %d - getting device descriptor\n", + ui32DevIndex); + + // + // Hub enumeration has already set the speed so do not + // override the setting here. + // + if(g_sUSBHCD.piDeviceState[ui32DevIndex] == eHCDDevConnected) + { + // + // Remember the speed of this device to ensure endpoints + // are properly configured. + // + switch(USBHostSpeedGet(USB0_BASE)) + { + case USB_HIGH_SPEED: + { + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Speed = + USB_EP_SPEED_HIGH; + break; + } + case USB_FULL_SPEED: + { + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Speed = + USB_EP_SPEED_FULL; + break; + } + default: + { + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Speed = + USB_EP_SPEED_LOW; + break; + } + } + } + + if(USBHCDGetDeviceDescriptor(0, + &g_sUSBHCD.psUSBDevice[ui32DevIndex]) == 0) + { + // + // If the device descriptor cannot be read then the device + // will be treated as unknown. + // + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDDevError; + + DEBUG_OUTPUT("Connection %d - failed to get descriptor\n", + ui32DevIndex); + + // + // Send an unknown connection event to let the application + // know that there is a device connected but return no + // zero for the instance. + // + 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.psUSBDevice[ui32DevIndex].ui8Hub) + { + USBHHubEnumerationError( + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8Hub, + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8HubPort); + } + } + } + // + // If we have the device descriptor then move on to setting + // the address of the device. + // + else if(g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Address == 0) + { + DEBUG_OUTPUT("Connection %d - setting address %d\n", + ui32DevIndex, ui32DevIndex + 1); + + // + // Send the set address command. + // + USBHCDSetAddress(ui32DevIndex, (ui32DevIndex + 1)); + + // + // Save the address. + // + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Address = + (ui32DevIndex + 1); + + // + // Move on to the addressed state. + // + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDDevAddressed; + } + break; + } + case eHCDDevAddressed: + { + // + // First check if we have read the configuration descriptor. + // + if(!g_sUSBHCD.psUSBDevice[ui32DevIndex].bConfigRead) + { + DEBUG_OUTPUT("Connection %d - getting config descriptor\n", + ui32DevIndex); + + // + // Initialize a request for the configuration descriptor. + // + if(USBHCDGetConfigDescriptor(0, + &g_sUSBHCD.psUSBDevice[ui32DevIndex]) == 0) + { + // + // If the device descriptor cannot be read then the device + // will be treated as unknown. + // + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDDevError; + + DEBUG_OUTPUT("Connection %d - failed to get descriptor\n", + ui32DevIndex); + + // + // Send an unknown connection event to let the application + // know that there is a device connected but return no + // zero for the instance. + // + 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.psUSBDevice[ui32DevIndex].ui8Hub) + { + USBHHubEnumerationError( + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8Hub, + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8HubPort); + } + } + } + // + // Now have addressed and received the device configuration, + // so get ready to set the device configuration. + // + else + { + DEBUG_OUTPUT("Connection %d - setting configuration.\n", + ui32DevIndex); + + // + // Use the first configuration to set the device + // configuration. + // + USBHCDSetConfig(0, + (uint32_t)&g_sUSBHCD.psUSBDevice[ui32DevIndex], 1); + + // + // Move on to the configured state. + // + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDDevConfigured; + + // + // Open the driver for the device. + // + g_pi32USBHActiveDriver[ui32DevIndex] = USBHCDOpenDriver(0, + ui32DevIndex); + + // + // If the device is connected via a hub, tell the hub + // driver that enumeration is complete. + // + if(g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8Hub) + { + USBHHubEnumerationComplete( + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8Hub, + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8HubPort); + } + } + break; + } + // + // The device was making a request and is now complete. + // + case eHCDDevRequest: + { + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDDevConnected; + break; + } + // + // The strings are currently not accessed. + // + case eHCDDevGetStrings: + { + break; + } + // + // Basically Idle at this point. + // + case eHCDDevDisconnected: + { + DEBUG_OUTPUT("Connection %d - disconnected.\n", + ui32DevIndex); + + // + // Handle device disconnect. + // + USBHCDDeviceDisconnected(0, ui32DevIndex); + + // + // Return to the Idle state. + // + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDIdle; + break; + } + + // + // Connection and enumeration is complete so allow this function + // to exit. + // + case eHCDDevConfigured: + { + break; + } + + // + // Poorly behaving device are in limbo in this state until removed. + // + case eHCDDevError: + { + DEBUG_OUTPUT("Connection %d - Error!\n", ui32DevIndex); + + // + // 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(ui32DevIndex == 0) + { + g_sUSBHCD.ui32IntEvents |= INT_EVENT_DISCONNECT; + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDIdle; + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +//! This function completes a control transaction to a device. +//! +//! \param ui32Index is the controller index to use for this transfer. +//! \param psSetupPacket is the setup request to be sent. +//! \param psDevice is the device instance pointer for this request. +//! \param pui8Data is the data to send for OUT requests or the receive buffer +//! for IN requests. +//! \param ui32Size is the size of the buffer in \e pui8Data. +//! \param ui32MaxPacketSize 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. +// +//***************************************************************************** +uint32_t +USBHCDControlTransfer(uint32_t ui32Index, tUSBRequest *psSetupPacket, + tUSBHostDevice *psDevice, uint8_t *pui8Data, + uint32_t ui32Size, uint32_t ui32MaxPacketSize) +{ + uint32_t ui32Remaining; + uint32_t ui32DataSize; + + // + // Debug sanity check. + // + ASSERT(g_sUSBHEP0State.iState == eEP0StateIdle); + ASSERT(ui32Index == 0); + + // + // Initialize the state of the data for this request. + // + g_sUSBHEP0State.pui8Data = pui8Data; + g_sUSBHEP0State.ui32BytesRemaining = ui32Size; + g_sUSBHEP0State.ui32DataSize = ui32Size; + + // + // Set the maximum packet size. + // + g_sUSBHEP0State.ui32MaxPacketSize = ui32MaxPacketSize; + + // + // Save the current address. + // + g_sUSBHEP0State.ui32DevAddress = psDevice->ui32Address; + + // + // Set the address the host will used to communicate with the device. + // + MAP_USBHostAddrSet(USB0_BASE, USB_EP_0, g_sUSBHEP0State.ui32DevAddress, + USB_EP_HOST_OUT); + + // + // Make sure that endpoint 0 has the proper speed setting. + // + USBHostEndpointConfig(USB0_BASE, USB_EP_0, 64, 0, 0, + (USB_EP_MODE_CTRL | psDevice->ui32Speed | + USB_EP_HOST_OUT)); + + // + // Put the data in the correct FIFO. + // + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, (uint8_t *)psSetupPacket, + sizeof(tUSBRequest)); + + // + // If this is an IN request, change to that state. + // + if(psSetupPacket->bmRequestType & USB_RTYPE_DIR_IN) + { + g_sUSBHEP0State.iState = eEP0StateSetupIN; + } + else + { + // + // If there is no data then this is not an OUT request. + // + if(ui32Size != 0) + { + // + // Since there is data, this is an OUT request. + // + g_sUSBHEP0State.iState = eEP0StateSetupOUT; + } + else + { + // + // Otherwise this request has no data and just a status phase. + // + g_sUSBHEP0State.iState = eEP0StateStatusIN; + } + } + + // + // Make sure we are talking to the correct device. + // + if(psDevice->ui8Hub == 0) + { + USBHostHubAddrSet(USB0_BASE, USB_EP_0, 0, + USB_EP_HOST_OUT | psDevice->ui32Speed); + } + else + { + USBHostHubAddrSet(USB0_BASE, USB_EP_0, + (psDevice->ui8Hub | (psDevice->ui8HubPort << 8)), + USB_EP_HOST_OUT | psDevice->ui32Speed); + } + + // + // 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.iState != eEP0StateIdle) + { + OS_INT_DISABLE(g_sUSBHCD.ui32IntNum); + + if((g_sUSBHCD.ui32IntEvents & (INT_EVENT_ENUM | INT_EVENT_SOF)) == + (INT_EVENT_ENUM | INT_EVENT_SOF)) + { + g_sUSBHCD.ui32IntEvents &= ~(INT_EVENT_ENUM | INT_EVENT_SOF); + + USBHCDEnumHandler(); + } + + OS_INT_ENABLE(g_sUSBHCD.ui32IntNum); + + if(g_sUSBHEP0State.iState == eEP0StateError) + { + return(0xffffffff); + } + + // + // If we aborted the transfer due to an error, tell the caller + // that no bytes were transferred. + // + if(g_sUSBHCD.ui32IntEvents & (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. + // + ui32DataSize = g_sUSBHEP0State.ui32DataSize; + ui32Remaining = g_sUSBHEP0State.ui32BytesRemaining; + + return(ui32DataSize - ui32Remaining); +} + +//***************************************************************************** +// +// Starts enumerating a new device connected via the hub. +// +// \param ui32Index is the index of the USB controller to use. +// \param ui32Hub is the hub address from which the connection is being made. +// \param ui32Port is the hub port number that the new device is connected to. +// \param pui8ConfigPool is memory to be used to store the device's +// configuration descriptor. +// \param ui32ConfigSize is the number of bytes available in the buffer pointed +// to by pui8ConfigPool. +// +// 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. +// +//***************************************************************************** +uint32_t +USBHCDHubDeviceConnected(uint32_t ui32Index, uint8_t ui8Hub, + uint8_t ui8Port, uint32_t ui32Speed) +{ + uint32_t ui32DevIndex; + + // + // Debug sanity checks. + // + ASSERT(ui32Index == 0); + ASSERT(ui8Port); + + DEBUG_OUTPUT("Connection from hub %d, port %d.\n", ui8Hub, ui8Port); + + // + // Look for a free slot in the device table. + // + for(ui32DevIndex = 1; ui32DevIndex <= MAX_USB_DEVICES; ui32DevIndex++) + { + if((g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Flags & + USBHDEV_FLAG_ALLOCATED) == 0) + { + // + // We found one. Set the state to ensure that it gets enumerated. + // + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Flags = + USBHDEV_FLAG_ALLOCATED; + g_sUSBHCD.psUSBDevice[ui32DevIndex].psConfigDescriptor->bLength = 0; + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8Hub = ui8Hub; + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8HubPort = ui8Port; + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Speed = ui32Speed; + g_sUSBHCD.psUSBDevice[ui32DevIndex].sDeviceDescriptor.bLength = 0; + + // + // Set the state to ensure enumeration begins. + // + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDDevConnectedHub; + + DEBUG_OUTPUT("Allocating device %d\n", ui32DevIndex); + + // + // Pass the device index back to the hub driver. + // + return(ui32DevIndex); + } + } + + // + // 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); +} + +//***************************************************************************** +// +// Called when a device is disconnected from a hub. +// +// \param ui32Index is the index of the USB controller to use. +// \param ui32DevIndex is the device index for the USB device that was +// disconnected. +// +//***************************************************************************** +void +USBHCDHubDeviceDisconnected(uint32_t ui32Index, uint32_t ui32DevIndex) +{ + // + // Debug sanity checks. + // + ASSERT(ui32Index == 0); + ASSERT(ui32DevIndex && (ui32DevIndex <= MAX_USB_DEVICES)); + + DEBUG_OUTPUT("Disconnection from hub %d, port %d, device %d\n", + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8Hub, + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8HubPort, ui32DevIndex); + + // + // Set the device state to ensure that USBHCDMain cleans it up. + // + g_sUSBHCD.piDeviceState[ui32DevIndex] = eHCDDevDisconnected; +} + +//***************************************************************************** +// +// This is the endpoint 0 interrupt handler. +// +// \return None. +// +//***************************************************************************** +static void +USBHCDEnumHandler(void) +{ + uint32_t ui32EPStatus; + uint32_t ui32DataSize; + + // + // Get the end point 0 status. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, USB_EP_0); + + // + // If there was an error then go to the error state. + // + if(ui32EPStatus == 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.iState = eEP0StateError; + + return; + } + + switch(g_sUSBHEP0State.iState) + { + // + // Handle the status state, this is a transitory state from + // USB_STATE_TX or USB_STATE_RX back to USB_STATE_IDLE. + // + case eEP0StateStatus: + { + // + // Handle the case of a received status packet. + // + if(ui32EPStatus & (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.iState = eEP0StateIdle; + + break; + } + + // + // This state triggers a STATUS IN request from the device. + // + case eEP0StateStatusIN: + { + // + // Generate an IN request from the device. + // + MAP_USBHostRequestStatus(USB0_BASE); + + // + // Change to the status phase and wait for the response. + // + g_sUSBHEP0State.iState = eEP0StateStatus; + + break; + } + + // + // In the IDLE state the code is waiting to receive data from the host. + // + case eEP0StateIdle: + { + break; + } + + // + // Data is still being sent to the host so handle this in the + // EP0StateTx() function. + // + case eEP0StateSetupOUT: + { + // + // Send remaining data if necessary. + // + USBHCDEP0StateTx(); + + break; + } + + // + // Handle the receive state for commands that are receiving data on + // endpoint 0. + // + case eEP0StateSetupIN: + { + // + // 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.iState = eEP0StateRx; + + break; + } + + // + // The endpoint remains in this state until all requested data has + // been received. + // + case eEP0StateRx: + { + // + // There was a stall on endpoint 0 so go back to the idle state + // as this command has been terminated. + // + if(ui32EPStatus & USB_HOST_EP0_RX_STALL) + { + g_sUSBHEP0State.iState = eEP0StateIdle; + + // + // Clear the stalled state on endpoint 0. + // + MAP_USBHostEndpointStatusClear(USB0_BASE, USB_EP_0, + ui32EPStatus & USB_HOST_IN_STATUS); + break; + } + + // + // Set the number of bytes to get out of this next packet. + // + ui32DataSize = g_sUSBHEP0State.ui32BytesRemaining; + if(ui32DataSize > g_sUSBHEP0State.ui32MaxPacketSize) + { + // + // Don't send more than EP0_MAX_PACKET_SIZE bytes. + // + ui32DataSize = MAX_PACKET_SIZE_EP0; + } + + if(ui32DataSize != 0) + { + // + // Get the data from the USB controller end point 0. + // + MAP_USBEndpointDataGet(USB0_BASE, USB_EP_0, + g_sUSBHEP0State.pui8Data, + &ui32DataSize); + } + + // + // Advance the pointer. + // + g_sUSBHEP0State.pui8Data += ui32DataSize; + + // + // Decrement the number of bytes that are being waited on. + // + g_sUSBHEP0State.ui32BytesRemaining -= ui32DataSize; + + // + // 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 int16_t packet and indicates that this transfer + // is complete. If there were exactly + // g_sUSBHEP0State.ui32MaxPacketSize remaining then there still + // needs to be null packet sent before this transfer is complete. + // + if((ui32DataSize < g_sUSBHEP0State.ui32MaxPacketSize) || + (g_sUSBHEP0State.ui32BytesRemaining == 0)) + { + // + // Return to the idle state. + // + g_sUSBHEP0State.iState = eEP0StateStatus; + + // + // No more data. + // + g_sUSBHEP0State.pui8Data = 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 eEP0StateStall: + { + // + // Reset the global end point 0 state to IDLE. + // + g_sUSBHEP0State.iState = eEP0StateIdle; + + 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) +{ + uint32_t ui32NumBytes; + uint8_t *pui8Data; + + // + // In the TX state on endpoint 0. + // + g_sUSBHEP0State.iState = eEP0StateSetupOUT; + + // + // Set the number of bytes to send this iteration. + // + ui32NumBytes = g_sUSBHEP0State.ui32BytesRemaining; + + // + // Limit individual transfers to 64 bytes. + // + if(ui32NumBytes > 64) + { + ui32NumBytes = 64; + } + + // + // Save the pointer so that it can be passed to the USBEndpointDataPut() + // function. + // + pui8Data = (uint8_t *)g_sUSBHEP0State.pui8Data; + + // + // Advance the data pointer and counter to the next data to be sent. + // + g_sUSBHEP0State.ui32BytesRemaining -= ui32NumBytes; + g_sUSBHEP0State.pui8Data += ui32NumBytes; + + // + // Put the data in the correct FIFO. + // + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, pui8Data, ui32NumBytes); + + // + // If this is exactly 64 then don't set the last packet yet. + // + if(ui32NumBytes == 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.iState = eEP0StateStatusIN; + } +} + +//***************************************************************************** +// +//! This function returns the USB hub port for the requested device instance. +//! +//! \param ui32Instance 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 ui32Instance parameter. The caller must use the value for +//! \e ui32Instance was passed to the application when it receives a +//! \b USB_EVENT_CONNECTED event. The function returns the USB hub port for +//! the interface number specified by the \e ui32Interface parameter. +//! +//! \return The USB hub port for the requested interface. +// +//***************************************************************************** +uint8_t +USBHCDDevHubPort(uint32_t ui32Instance) +{ + uint32_t ui32DevIndex; + + ui32DevIndex = HCDInstanceToDevIndex(ui32Instance); + + if(ui32DevIndex == 0xff) + { + return(ui32DevIndex); + } + + return(g_sUSBHCD.psUSBDevice[ui32DevIndex].ui8HubPort); +} + +//***************************************************************************** +// +//! This function will return the USB address for the requested device +//! instance. +//! +//! \param ui32Instance is a unique value indicating which device to query. +//! +//! This function returns the USB address for the device that is associated +//! with the \e ui32Instance parameter. The caller must use a value for +//! \e ui32Instance have been passed to the application when it receives a +//! \b USB_EVENT_CONNECTED event. The function will return the USB address for +//! the interface number specified by the \e ui32Interface parameter. +//! +//! \return The USB address for the requested interface. +// +//***************************************************************************** +uint8_t +USBHCDDevAddress(uint32_t ui32Instance) +{ + uint32_t ui32DevIndex; + + ui32DevIndex = HCDInstanceToDevIndex(ui32Instance); + + if(ui32DevIndex == 0xff) + { + return(ui32DevIndex); + } + + return(g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Address); +} + +//***************************************************************************** +// +//! This function will return the USB class for the requested device +//! instance. +//! +//! \param ui32Instance is a unique value indicating which device to query. +//! \param ui32Interface 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 ui32Instance parameter. The caller must use a value for +//! \e ui32Instance have been passed to the application when it receives a +//! \b USB_EVENT_CONNECTED event. The function will return the USB class for +//! the interface number specified by the \e ui32Interface parameter. If +//! \e ui32Interface 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. +// +//***************************************************************************** +uint8_t +USBHCDDevClass(uint32_t ui32Instance, uint32_t ui32Interface) +{ + uint32_t ui32DevIndex; + tInterfaceDescriptor *psInterface; + + ui32DevIndex = HCDInstanceToDevIndex(ui32Instance); + + // + // If the instance was not valid return an undefined class. + // + if(ui32DevIndex == 0xff) + { + return(USB_CLASS_DEVICE); + } + + // + // Get the interface descriptor. + // + psInterface = USBDescGetInterface( + g_sUSBHCD.psUSBDevice[ui32DevIndex].psConfigDescriptor, + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Interface, + ui32Interface); + + // + // Make sure that the interface requested actually exists. + // + if(psInterface) + { + // + // Return the interface class. + // + return(psInterface->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 ui32Instance is a unique value indicating which device to query. +//! \param ui32Interface 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 ui32Instance parameter. The caller must use a value for +//! \e ui32Instance have been passed to the application when it receives a +//! \b USB_EVENT_CONNECTED event. The function will return the USB subclass +//! for the interface number specified by the \e ui32Interface parameter. If +//! \e ui32Interface 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. +// +//***************************************************************************** +uint8_t +USBHCDDevSubClass(uint32_t ui32Instance, uint32_t ui32Interface) +{ + uint32_t ui32DevIndex; + tInterfaceDescriptor *psInterface; + + ui32DevIndex = HCDInstanceToDevIndex(ui32Instance); + + // + // If the instance was not valid return an undefined subclass. + // + if(ui32DevIndex == 0xff) + { + return(USB_SUBCLASS_UNDEFINED); + } + + // + // Get the interface descriptor. + // + psInterface = USBDescGetInterface( + g_sUSBHCD.psUSBDevice[ui32DevIndex].psConfigDescriptor, + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Interface, + ui32Interface); + + // + // Make sure that the interface requested actually exists. + // + if(psInterface) + { + // + // Return the interface subclass. + // + return(psInterface->bInterfaceSubClass); + } + + // + // No valid interface so return an undefined subclass. + // + return(USB_SUBCLASS_UNDEFINED); +} + +//***************************************************************************** +// +//! This function returns the USB protocol for the requested device instance. +//! +//! \param ui32Instance is a unique value indicating which device to query. +//! \param ui32Interface 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 ui32Instance parameter. The caller must use a value for +//! \e ui32Instance have been passed to the application when it receives a +//! \b USB_EVENT_CONNECTED event. The function will return the USB protocol +//! for the interface number specified by the \e ui32Interface parameter. If +//! \e ui32Interface 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. +// +//***************************************************************************** +uint8_t +USBHCDDevProtocol(uint32_t ui32Instance, uint32_t ui32Interface) +{ + uint32_t ui32DevIndex; + tInterfaceDescriptor *psInterface; + + ui32DevIndex = HCDInstanceToDevIndex(ui32Instance); + + // + // If the instance was not valid return an undefined protocol. + // + if(ui32DevIndex == 0xff) + { + return(USB_PROTOCOL_UNDEFINED); + } + + // + // Get the interface descriptor. + // + psInterface = USBDescGetInterface( + g_sUSBHCD.psUSBDevice[ui32DevIndex].psConfigDescriptor, + g_sUSBHCD.psUSBDevice[ui32DevIndex].ui32Interface, + ui32Interface); + + // + // Make sure that the interface requested actually exists. + // + if(psInterface) + { + // + // Return the interface protocol. + // + return(psInterface->bInterfaceProtocol); + } + + // + // No valid interface so return an undefined protocol. + // + return(USB_PROTOCOL_UNDEFINED); +} + +//***************************************************************************** +// +//! This function sets one of the \b USBLIB_FEATURE_ requests. +//! +//! \param ui32Index is the index of the USB controller to access. +//! \param ui32Feature is one of the \b USBLIB_FEATURE_ defines. +//! \param pvFeature is a pointer to the data for the \b USBLIB_FEATURE +//! request. +//! +//! This function sends the requested feature request to the USB library. +//! Not all features are supported by all devices so see the documentation +//! for the \b USBLIB_FEATURE_ to determine if the feature is supported. +//! +//! \return Returns \b true if the feature was set and \b false if the feature +//! is not supported or could not be changed to the requested value. +// +//***************************************************************************** +bool +USBHCDFeatureSet(uint32_t ui32Index, uint32_t ui32Feature, + void *pvFeature) +{ + bool bRetCode; + tLPMFeature *psLPMFeature; + + bRetCode = true; + + switch(ui32Feature) + { + case USBLIB_FEATURE_CPUCLK: + { + // + // Set the ticks per millisecond. + // + g_ui32Tickms = (*(uint32_t *)pvFeature / 3000); + break; + } + case USBLIB_FEATURE_LPM: + { + psLPMFeature = (tLPMFeature *)pvFeature; + + if(psLPMFeature->ui32Features & USBLIB_FEATURE_LPM_EN) + { + g_sUSBHCD.ui32Features |= USBLIB_FEATURE_LPM_EN; + + if(psLPMFeature->ui32Features & USBLIB_FEATURE_LPM_RMT_WAKE) + { + g_sUSBHCD.ui32Features |= USBLIB_FEATURE_LPM_RMT_WAKE; + } + g_sUSBHCD.ui32LPMHIRD = psLPMFeature->ui32HIRD; + } + else + { + psLPMFeature->ui32Features &= ~USBLIB_FEATURE_LPM_EN; + } + break; + } + case USBLIB_FEATURE_USBPLL: + { + // + // If the PLL rate is not evenly divisible by 60MHz then + // do not set it. + // + if((*(uint32_t *)pvFeature % 60000000) != 0) + { + bRetCode = false; + } + else + { + // + // Save the new PLL rate. + // + g_ui32PLLDiv = (*(uint32_t *)pvFeature / 60000000); + } + break; + } + case USBLIB_FEATURE_USBULPI: + { + // + // Save the ULPI support level. + // + g_ui32ULPISupport = *(uint32_t *)pvFeature; + + break; + } + default: + { + bRetCode = false; + break; + } + } + return(bRetCode); +} + +//***************************************************************************** +// +//! This function returns the current status of an LPM request. +//! +//! \param psDevice is the device to query. +//! +//! This function returns the current status of LPM requests for a given +//! device. This is called to determine if a previous request completed +//! successfully or if there was an error. +//! +//! \return This function returns the following values: +//! - \b USBHCD_LPM_AVAIL - There are no pending LPM requests on this specific +//! device or the last request completed successfully. +//! - \b USBHCD_LPM_ERROR - The last LPM request for this device did not +//! complete successfully. +//! - \b USBHCD_LPM_PENDING - The last LPM request has not completed. +// +//***************************************************************************** +uint32_t +USBHCDLPMStatus(tUSBHostDevice *psDevice) +{ + uint32_t ui32Ret; + + ASSERT(psDevice != 0); + + // + // Should never have both USBHDEV_FLAG_LPMERROR and USBHDEV_FLAG_LPMPEND + // set at the same time. + // + ASSERT((psDevice->ui32Flags & + (USBHDEV_FLAG_LPMERROR | USBHDEV_FLAG_LPMPEND)) != + (USBHDEV_FLAG_LPMERROR | USBHDEV_FLAG_LPMPEND)); + + // + // Default to no pending transfers or errors. + // + ui32Ret = USBHCD_LPM_AVAIL; + + if(psDevice->ui32Flags & USBHDEV_FLAG_LPMERROR) + { + // + // An error occurred after the last call to send an LPM command. + // + ui32Ret = USBHCD_LPM_ERROR; + } + else if(psDevice->ui32Flags & USBHDEV_FLAG_LPMPEND) + { + // + // Still have a pending transfer. + // + ui32Ret = USBHCD_LPM_PENDING; + } + + return(ui32Ret); +} + +//***************************************************************************** +// +//! This function generates an LPM request for a device to enter L1 sleep +//! state. +//! +//! \param psDevice is the device to query. +//! +//! This function sends a request to a device to enter the LPM L1 sleep state. +//! The caller must check the return value to see if the request can be +//! attempted at this time. If another LPM transaction is busy on another +//! device this function will return \b USBHCD_LPM_PENDING or +//! \b USBHCD_LPM_AVAIL if the LPM request was scheduled to be sent. The +//! caller should check the USBHCDLPMStatus() function to determine if the +//! request has completed. +//! +//! \return This function returns the following values: +//! - USBHCD_LPM_AVAIL - The transition to L1 state is scheduled to be sent. +//! - USBHCD_LPM_PENDING - There is already an LPM request pending. +// +//***************************************************************************** +uint32_t +USBHCDLPMSleep(tUSBHostDevice *psDevice) +{ + uint32_t ui32Ret; + + ASSERT(psDevice != 0); + + // + // Disable the USB interrupt. + // + OS_INT_DISABLE(g_sUSBHCD.ui32IntNum); + + // + // If there is no current LPM pending then send the request. + // + if((g_sUSBHCD.ui32IntEvents & INT_EVENT_LPM_PEND) || + (psDevice->ui32Flags & USBHDEV_FLAG_LPMPEND)) + { + ui32Ret = USBHCD_LPM_PENDING; + } + else + { + // + // New pending LPM transfer at the host controller level. + // + g_sUSBHCD.ui32IntEvents |= INT_EVENT_LPM_PEND; + + // + // New pending request and clear any previous error for this + // device in case it was already set. + // + psDevice->ui32Flags |= USBHDEV_FLAG_LPMPEND; + psDevice->ui32Flags &= ~USBHDEV_FLAG_LPMERROR; + + USBHostLPMSend(USB0_BASE, psDevice->ui32Address, USB_EP_0); + + ui32Ret = USBHCD_LPM_AVAIL; + } + + // + // Disable the USB interrupt. + // + OS_INT_ENABLE(g_sUSBHCD.ui32IntNum); + + return(ui32Ret); +} + +//***************************************************************************** +// +//! This function generates an LPM request for a device to exit L1 sleep state. +//! +//! \param ui32Index specifies which USB controller to use. +//! +//! This function will start LPM resume signaling on the USB bus. This wakes +//! all devices and is similar to USBHCDResume() call but is triggered by an +//! LPM request. +//! +//! \return None. +// +//***************************************************************************** +void +USBHCDLPMResume(uint32_t ui32Index) +{ + ASSERT(ui32Index == 0); + + USBHostLPMResume(USB0_BASE); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhostpriv.h b/usblib/host/usbhostpriv.h new file mode 100644 index 0000000..1128749 --- /dev/null +++ b/usblib/host/usbhostpriv.h @@ -0,0 +1,201 @@ +//***************************************************************************** +// +// usbhostpriv.h - Internal header file for USB host functions. +// +// Copyright (c) 2011-2014 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 2.1.0.12573 of the Tiva 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. + // + ePortIdle, + + // + // The port has a device present and is waiting for the enumeration + // sequence to begin. + // + ePortConnected, + + // + // A device connection notification has been received and we have initiated + // a reset to the port. We are waiting for the reset to complete. + // + ePortResetActive, + + // + // The Port reset has completed but now the hub is waiting the required + // 10ms before accessing the device. + // + ePortResetWait, + + // + // 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. + // + ePortActive, + + // + // A device has completed enumeration. + // + ePortEnumerated, + + // + // A device is attached to the port but enumeration failed. + // + ePortError +} +tHubPortState; + +//***************************************************************************** +// +// The list of valid event flags in the g_sUSBHCD.ui32EventEnables 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 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. +// +//***************************************************************************** +struct tUSBHostDevice +{ + // + // The current device address for this device. + // + uint32_t ui32Address; + + // + // The current interface for this device. + // + uint32_t ui32Interface; + + // + // A flag used to record whether this is a low-speed or a full-speed + // device. + // + bool bLowSpeed; + + // + // The USB connection speed for this device. + // + uint32_t ui32Speed; + + // + // A flag indicating whether or not we have read the device's + // configuration descriptor yet. + // + bool bConfigRead; + + // + // The hub number to which this device is attached. + // + uint8_t ui8Hub; + + // + // The hub port number to which the device is attached. + // + uint8_t ui8HubPort; + + // + // The device descriptor for this device. + // + tDeviceDescriptor sDeviceDescriptor; + + // + // A pointer to the configuration descriptor for this device. + // + tConfigDescriptor *psConfigDescriptor; + + // + // The size of the buffer allocated to psConfigDescriptor. + // + uint32_t ui32ConfigDescriptorSize; + + // + // Internal flags used by the host controller driver. + // + uint32_t ui32Flags; +}; + +//***************************************************************************** +// +// Functions within the host controller that are called by the hub class driver +// +//***************************************************************************** +extern uint32_t USBHCDHubDeviceConnected(uint32_t ui32Index, uint8_t ui8Hub, + uint8_t ui8Port, uint32_t ui32Speed); +extern void USBHCDHubDeviceDisconnected(uint32_t ui32Index, + uint32_t ui32DevIndex); + +//***************************************************************************** +// +// Functions in the hub class driver that are called by the host controller. +// +//***************************************************************************** +extern void USBHHubMain(void); +extern void USBHHubInit(void); +extern void USBHHubEnumerationComplete(uint8_t ui8Hub, uint8_t ui8Port); +extern void USBHHubEnumerationError(uint8_t ui8Hub, uint8_t ui8Port); +extern uint32_t USBHCDLPMSleep(tUSBHostDevice *psDevice); +extern uint32_t USBHCDLPMStatus(tUSBHostDevice *psDevice); + +//***************************************************************************** +// +// 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..407dc2d --- /dev/null +++ b/usblib/host/usbhscsi.c @@ -0,0 +1,777 @@ +//***************************************************************************** +// +// usbhscsi.c - USB host SCSI layer used by the USB host MSC driver. +// +// Copyright (c) 2008-2014 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 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#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 ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! \param psSCSICmd is the SCSI command structure to send. +//! \param pui8Data is pointer to the command data to be sent. +//! \param pui32Size 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 pui8Data is the buffer to store the data into and \e pui32Size is used +//! to store the amount of data to request as well as used to indicate how many +//! bytes were filled into the \e pui8Data buffer on return. For commands that +//! are sending data, \e pui8Data is the data to be sent and \e pui32Size 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 uint32_t +USBHSCSISendCommand(uint32_t ui32InPipe, uint32_t ui32OutPipe, + tMSCCBW *psSCSICmd, uint8_t *pui8Data, uint32_t *pui32Size) +{ + tMSCCSW sCmdStatus; + uint32_t ui32Bytes; + + // + // Initialize the command status. + // + sCmdStatus.dCSWSignature = 0; + sCmdStatus.dCSWTag = 0; + sCmdStatus.bCSWStatus = SCSI_CMD_STATUS_FAIL; + + // + // Set the CBW signature and tag. + // + psSCSICmd->dCBWSignature = CBW_SIGNATURE; + psSCSICmd->dCBWTag = CBW_TAG_VALUE; + + // + // Set the size of the data to be returned by the device. + // + psSCSICmd->dCBWDataTransferLength = *pui32Size; + + // + // Send the command. + // + ui32Bytes = USBHCDPipeWrite(ui32OutPipe, (uint8_t*)psSCSICmd, + sizeof(tMSCCBW)); + + // + // If no bytes went out then the command failed. + // + if(ui32Bytes == 0) + { + return(SCSI_CMD_STATUS_FAIL); + } + + // + // Only request data if there is data to request. + // + if(psSCSICmd->dCBWDataTransferLength != 0) + { + // + // See if this is a read or a write. + // + if(psSCSICmd->bmCBWFlags & CBWFLAGS_DIR_IN) + { + // + // Read the data back. + // + *pui32Size = USBHCDPipeRead(ui32InPipe, pui8Data, *pui32Size); + } + else + { + // + // Write the data out. + // + *pui32Size = USBHCDPipeWrite(ui32OutPipe, pui8Data, *pui32Size); + } + } + + // + // Get the status of the command. + // + ui32Bytes = USBHCDPipeRead(ui32InPipe, (uint8_t *)&sCmdStatus, + sizeof(tMSCCSW)); + + + // + // If the status was invalid or did not have the correct signature then + // indicate a failure. + // + if((ui32Bytes == 0) || (sCmdStatus.dCSWSignature != CSW_SIGNATURE) || + (sCmdStatus.dCSWTag != CBW_TAG_VALUE)) + { + return(SCSI_CMD_STATUS_FAIL); + } + + // + // Return the status. + // + return((uint32_t)sCmdStatus.bCSWStatus); +} + +//***************************************************************************** +// +//! This will issue the SCSI inquiry command to a device. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! \param pui8Data is the data buffer to return the results into. +//! \param pui32Size 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 ui32InPipe and +//! \e ui32OutPipe parameters indicate which USB pipes to use for this call. +//! +//! \note The \e pui8Data 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. +// +//***************************************************************************** +uint32_t +USBHSCSIInquiry(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint8_t *pui8Data, uint32_t *pui32Size) +{ + tMSCCBW sSCSICmd; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)sSCSICmd.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. + // + *pui32Size = SCSI_INQUIRY_DATA_SZ; + + // + // This is an IN request. + // + sSCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + sSCSICmd.bCBWLUN = 0; + + // + // This is the length of the command itself. + // + sSCSICmd.bCBWCBLength = 6; + + // + // Send Inquiry command with no request for vital product data. + // + pui32Data[0] = SCSI_INQUIRY_CMD; + + // + // Allocation length. + // + pui32Data[1] = SCSI_INQUIRY_DATA_SZ; + pui32Data[2] = 0; + pui32Data[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ui32InPipe, ui32OutPipe, &sSCSICmd, pui8Data, + pui32Size)); +} + +//***************************************************************************** +// +//! This will issue the SCSI read capacity command to a device. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! \param pui8Data is the data buffer to return the results into. +//! \param pui32Size 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 ui32InPipe and \e ui32OutPipe parameters indicate which USB pipes to +//! use for this call. +//! +//! \note The \e pui8Data 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. +// +//***************************************************************************** +uint32_t +USBHSCSIReadCapacity(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint8_t *pui8Data, uint32_t *pui32Size) +{ + tMSCCBW sSCSICmd; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)sSCSICmd.CBWCB; + + // + // Set the size of the command data. + // + *pui32Size = SCSI_READ_CAPACITY_SZ; + + // + // This is an IN request. + // + sSCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + sSCSICmd.bCBWLUN = 0; + + // + // Set the length of the command itself. + // + sSCSICmd.bCBWCBLength = 12; + + // + // Only use the first byte and set it to the Read Capacity command. The + // rest are set to 0. + // + pui32Data[0] = SCSI_READ_CAPACITY; + pui32Data[1] = 0; + pui32Data[2] = 0; + pui32Data[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ui32InPipe, ui32OutPipe, &sSCSICmd, pui8Data, + pui32Size)); +} + +//***************************************************************************** +// +//! This will issue the SCSI read capacities command to a device. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! \param pui8Data is the data buffer to return the results into. +//! \param pui32Size 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 ui32InPipe and \e ui32OutPipe 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. +// +//***************************************************************************** +uint32_t +USBHSCSIReadCapacities(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint8_t *pui8Data, uint32_t *pui32Size) +{ + tMSCCBW sSCSICmd; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)sSCSICmd.CBWCB; + + // + // This is an IN request. + // + sSCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + sSCSICmd.bCBWLUN = 0; + + // + // Set the length of the command itself. + // + sSCSICmd.bCBWCBLength = 12; + + // + // Only use the first byte and set it to the Read Capacity command. The + // rest are set to 0. + // + pui32Data[0] = SCSI_READ_CAPACITIES; + pui32Data[1] = 0; + pui32Data[2] = 0; + pui32Data[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ui32InPipe, ui32OutPipe, &sSCSICmd, pui8Data, + pui32Size)); +} + +//***************************************************************************** +// +//! This will issue the SCSI Mode Sense(6) command to a device. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! \param ui32Flags is a combination of flags defining the exact query that is +//! to be made. +//! \param pui8Data is the data buffer to return the results into. +//! \param pui32Size 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 ui32InPipe +//! and \e ui32OutPipe parameters indicate which USB pipes to use for this +//! call. The call will return at most the number of bytes in the \e pui32Size +//! parameter, however it can return less and change the \e pui32Size parameter +//! to the number of valid bytes in the \e *pui32Size buffer. +//! +//! The \e ui32Flags 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(ui32InPipe, ui32OutPipe, +//! SCSI_MS_PC_CURRENT | SCSI_MS_PC_ALL, +//! pui8Data, pui32Size); +//! \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. +// +//***************************************************************************** +uint32_t +USBHSCSIModeSense6(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint32_t ui32Flags, uint8_t *pui8Data, + uint32_t *pui32Size) +{ + tMSCCBW sSCSICmd; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)sSCSICmd.CBWCB; + + // + // This is an IN request. + // + sSCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + sSCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + sSCSICmd.bCBWCBLength = 6; + + // + // Set the options for the Mode Sense Command (6). + // + pui32Data[0] = (SCSI_MODE_SENSE_6 | ui32Flags); + pui32Data[1] = (uint8_t)*pui32Size; + pui32Data[2] = 0; + pui32Data[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ui32InPipe, ui32OutPipe, &sSCSICmd, pui8Data, + pui32Size)); +} + +//***************************************************************************** +// +//! This function issues a SCSI Test Unit Ready command to a device. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe 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. +// +//***************************************************************************** +uint32_t +USBHSCSITestUnitReady(uint32_t ui32InPipe, uint32_t ui32OutPipe) +{ + tMSCCBW sSCSICmd; + uint32_t ui32Size; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)sSCSICmd.CBWCB; + + // + // No data in this command. + // + ui32Size = 0; + + // + // This is an IN request. + // + sSCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + sSCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + sSCSICmd.bCBWCBLength = 6; + + // + // Set the parameter options. + // + pui32Data[0] = SCSI_TEST_UNIT_READY; + pui32Data[1] = 0; + pui32Data[2] = 0; + pui32Data[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ui32InPipe, ui32OutPipe, &sSCSICmd, 0, + &ui32Size)); +} + +//***************************************************************************** +// +//! This function issues a SCSI Request Sense command to a device. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! \param pui8Data is the data buffer to return the results into. +//! \param pui32Size 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 pui8Data. The +//! parameter \e pui32Size should have the allocation size in bytes of the +//! buffer pointed to by \e pui8Data. +//! +//! \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. +// +//***************************************************************************** +uint32_t +USBHSCSIRequestSense(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint8_t *pui8Data, uint32_t *pui32Size) +{ + tMSCCBW sSCSICmd; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)sSCSICmd.CBWCB; + + // + // This is an IN request. + // + sSCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + sSCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + sSCSICmd.bCBWCBLength = 12; + + // + // Set the parameter options. + // + pui32Data[0] = SCSI_REQUEST_SENSE; + pui32Data[1] = 18; + pui32Data[2] = 0; + pui32Data[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ui32InPipe, ui32OutPipe, &sSCSICmd, pui8Data, + pui32Size)); +} + +//***************************************************************************** +// +//! This function issues a SCSI Read(10) command to a device. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! \param ui32LBA is the logical block address to read. +//! \param pui8Data is the data buffer to return the data. +//! \param pui32Size is the size of the buffer on entry and number of bytes +//! read on exit. +//! \param ui32NumBlocks 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 ui32LBA 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 pui8Data. The parameter \e pui32Size should indicate enough space to +//! hold a full block size, or only the first \e pui32Size bytes of the LBA are +//! 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. +// +//***************************************************************************** +uint32_t +USBHSCSIRead10(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint32_t ui32LBA, uint8_t *pui8Data, + uint32_t *pui32Size, uint32_t ui32NumBlocks) +{ + tMSCCBW sSCSICmd; + int32_t i32Idx; + + // + // Zero out the response data. + // + for(i32Idx = 0; i32Idx < sizeof(sSCSICmd.CBWCB); i32Idx++) + { + sSCSICmd.CBWCB[i32Idx] = 0; + } + + // + // This is an IN request. + // + sSCSICmd.bmCBWFlags = CBWFLAGS_DIR_IN; + + // + // Only handle LUN 0. + // + sSCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + sSCSICmd.bCBWCBLength = 10; + + // + // Set the parameter options. + // + sSCSICmd.CBWCB[0] = SCSI_READ_10; + + // + // LBA starts at offset 2. + // + sSCSICmd.CBWCB[2] = (uint8_t)(ui32LBA >> 24); + sSCSICmd.CBWCB[3] = (uint8_t)(ui32LBA >> 16); + sSCSICmd.CBWCB[4] = (uint8_t)(ui32LBA >> 8); + sSCSICmd.CBWCB[5] = (uint8_t)ui32LBA; + + // + // Transfer length in blocks starts at offset 7. + // + sSCSICmd.CBWCB[7] = (uint8_t)(ui32NumBlocks >> 8); + sSCSICmd.CBWCB[8] = (uint8_t)ui32NumBlocks; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ui32InPipe, ui32OutPipe, &sSCSICmd, pui8Data, + pui32Size)); +} + +//***************************************************************************** +// +//! 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 ui32LBA 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 pui8Data parameter. The parameter \e pui32Size should indicate the +//! amount of data to write to the specified LBA. +//! +//! \param ui32InPipe is the USB IN pipe to use for this command. +//! \param ui32OutPipe is the USB OUT pipe to use for this command. +//! \param ui32LBA is the logical block address to read. +//! \param pui8Data is the data buffer to write out. +//! \param pui32Size is the size of the buffer. +//! \param ui32NumBlocks 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. +// +//***************************************************************************** +uint32_t +USBHSCSIWrite10(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint32_t ui32LBA, uint8_t *pui8Data, + uint32_t *pui32Size, uint32_t ui32NumBlocks) +{ + tMSCCBW sSCSICmd; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)sSCSICmd.CBWCB; + + // + // This is an IN request. + // + sSCSICmd.bmCBWFlags = CBWFLAGS_DIR_OUT; + + // + // Only handle LUN 0. + // + sSCSICmd.bCBWLUN = 0; + + // + // Set the size of the command data. + // + sSCSICmd.bCBWCBLength = 10; + + // + // Set the parameter options. + // + sSCSICmd.CBWCB[0] = SCSI_WRITE_10; + + // + // Clear the reserved field. + // + sSCSICmd.CBWCB[1] = 0; + + // + // LBA starts at offset 2. + // + sSCSICmd.CBWCB[2] = (uint8_t)(ui32LBA >> 24); + sSCSICmd.CBWCB[3] = (uint8_t)(ui32LBA >> 16); + sSCSICmd.CBWCB[4] = (uint8_t)(ui32LBA >> 8); + sSCSICmd.CBWCB[5] = (uint8_t)ui32LBA; + + // + // Clear the reserved field. + // + sSCSICmd.CBWCB[6] = 0; + + // + // Set the transfer length in blocks. + // This also sets the Control value to 0 at offset 9. + // + sSCSICmd.CBWCB[7] = (ui32NumBlocks & 0xFF00) >> 8; + + // + // The blocks go into is byte offset 8 or word address 2. + // + pui32Data[2] = (ui32NumBlocks & 0xFF); + + // + // The blocks go into is byte offset 12 or word address 3. + // + pui32Data[3] = 0; + + // + // Send the command and get the results. + // + return(USBHSCSISendCommand(ui32InPipe, ui32OutPipe, &sSCSICmd, pui8Data, + pui32Size)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/host/usbhscsi.h b/usblib/host/usbhscsi.h new file mode 100644 index 0000000..29291e1 --- /dev/null +++ b/usblib/host/usbhscsi.h @@ -0,0 +1,87 @@ +//***************************************************************************** +// +// usbhscsi.h - Definitions for the USB host SCSI layer. +// +// Copyright (c) 2008-2014 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 2.1.0.12573 of the Tiva 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 uint32_t USBHSCSIInquiry(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint8_t *pui8Buffer, uint32_t *pui32Size); +extern uint32_t USBHSCSIReadCapacity(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint8_t *pui8Data, uint32_t *pui32Size); +extern uint32_t USBHSCSIReadCapacities(uint32_t ui32InPipe, + uint32_t ui32OutPipe, uint8_t *pui8Data, + uint32_t *pui32Size); +extern uint32_t USBHSCSIModeSense6(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint32_t ui32Flags, uint8_t *pui8Data, + uint32_t *pui32Size); +extern uint32_t USBHSCSITestUnitReady(uint32_t ui32InPipe, + uint32_t ui32OutPipe); +extern uint32_t USBHSCSIRequestSense(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint8_t *pui8Data, uint32_t *pui32Size); +extern uint32_t USBHSCSIRead10(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint32_t ui32LBA, uint8_t *pui8Data, + uint32_t *pui32Size, uint32_t ui32NumBlocks); +extern uint32_t USBHSCSIWrite10(uint32_t ui32InPipe, uint32_t ui32OutPipe, + uint32_t ui32LBA, uint8_t *pui8Data, + uint32_t *pui32Size, uint32_t ui32NumBlocks); + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBHSCSI_H__ -- cgit v1.3.1