From c241dbd7e78c50327781a35d88d9f2db7ff2b271 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Mon, 29 Oct 2012 23:08:53 +0200 Subject: Added usblib --- usblib/device/usbdcomp.c | 1543 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1543 insertions(+) create mode 100644 usblib/device/usbdcomp.c (limited to 'usblib/device/usbdcomp.c') diff --git a/usblib/device/usbdcomp.c b/usblib/device/usbdcomp.c new file mode 100644 index 0000000..feb22bf --- /dev/null +++ b/usblib/device/usbdcomp.c @@ -0,0 +1,1543 @@ +//**************************************************************************** +// +// usbdcomp.c - USB composite device class driver. +// +// Copyright (c) 2010-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//**************************************************************************** + +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/sysctl.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usb-ids.h" +#include "usblib/usbcdc.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdcdc.h" +#include "usblib/device/usbdcomp.h" + +//**************************************************************************** +// +//! \addtogroup composite_device_class_api +//! @{ +// +//**************************************************************************** + +//**************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//**************************************************************************** +static unsigned char g_pCompDeviceDescriptor[] = +{ + 18, // Size of this structure. + USB_DTYPE_DEVICE, // Type of this structure. + USBShort(0x110), // USB version 1.1 (if we say 2.0, hosts assume + // high-speed - see USB 2.0 spec 9.2.6.6) + USB_CLASS_MISC, // USB Device Class (spec 5.1.1) + USB_MISC_SUBCLASS_COMMON, // USB Device Sub-class (spec 5.1.1) + USB_MISC_PROTOCOL_IAD, // USB Device protocol (spec 5.1.1) + 64, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (filled in during USBDCompositeInit). + USBShort(0), // Product ID (filled in during USBDCompositeInit). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//**************************************************************************** +// +// Composite class device configuration descriptor. +// +// It is vital that the configuration descriptor bConfigurationValue field +// (byte 6) is 1 for the first configuration and increments by 1 for each +// additional configuration defined here. This relationship is assumed in the +// device stack for simplicity even though the USB 2.0 specification imposes +// no such restriction on the bConfigurationValue values. +// +// Note that this structure is deliberately located in RAM since we need to +// be able to patch some values in it based on client requirements. +// +//**************************************************************************** +static const unsigned char g_pCompConfigDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(0), // The total size of this full structure. + 0, // The number of interfaces in this + // configuration, this will be filled by + // the class as it discovers all classes + // supported. + 1, // The unique value for this configuration. + 0, // The string identifier that describes this + // configuration. + USB_CONF_ATTR_BUS_PWR, // . + 250, // The maximum power in 2mA increments. +}; + +//**************************************************************************** +// +// Byte offsets used to access various fields in our index/interface/endpoint +// lookup table (tUSBDCompositeDevice.pulDeviceWorkspace). This workspace +// contains one 4 byte entry per device. The LSB is the device index, next byte +// is the number of the first interface not within this device, next byte is the +// number of the first IN endpoint not within this device and the final byte is +// the number of the first OUT endpoint not within this device. Using this +// simple table we can reasonably quickly cross-reference index with interface +// and endpoint numbers. +// +//**************************************************************************** +#define LOOKUP_INDEX_BYTE 0 +#define LOOKUP_INTERFACE_BYTE 1 +#define LOOKUP_IN_END_BYTE 2 +#define LOOKUP_OUT_END_BYTE 3 + +//**************************************************************************** +// +// A marker used to indicate an invalid index into the device table. +// +//**************************************************************************** +#define INVALID_DEVICE_INDEX 0xFFFFFFFF + +//***************************************************************************** +// +// Macros to convert between USB controller base address and an index. These +// are currently trivial but are included to allow for the possibility of +// supporting more than one controller in the future. +// +//***************************************************************************** +#define USB_BASE_TO_INDEX(BaseAddr) (0) +#define USB_INDEX_TO_BASE(Index) (USB0_BASE) + +//**************************************************************************** +// +// Various internal handlers needed by this class. +// +//**************************************************************************** +static void HandleDisconnect(void *pvInstance); +static void InterfaceChange(void *pvInstance, unsigned char ucInterfaceNum, + unsigned char ucAlternateSetting); +static void ConfigChangeHandler(void *pvInstance, unsigned long ulValue); +static void DataSent(void *pvInstance, unsigned long ulInfo); +static void DataReceived(void *pvInstance, unsigned long ulInfo); +static void HandleEndpoints(void *pvInstance, unsigned long ulStatus); +static void HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest); +static void SuspendHandler(void *pvInstance); +static void ResumeHandler(void *pvInstance); +static void ResetHandler(void *pvInstance); +static void GetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest); + +//**************************************************************************** +// +// Configuration Descriptor. +// +//**************************************************************************** +tConfigHeader *g_pCompConfigDescriptors[1]; + +//**************************************************************************** +// +// The FIFO configuration for USB mass storage class device. +// +//**************************************************************************** +tFIFOConfig g_sUSBCompositeFIFOConfig = +{ + // + // IN endpoints. + // + { + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN } + }, + // + // OUT endpoints. + // + { + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT } + }, +}; + +//**************************************************************************** +// +// The device information structure for the USB Composite device. +// +//**************************************************************************** +tDeviceInfo g_sCompositeDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + // + // GetDescriptor + // + GetDescriptor, + + // + // RequestHandler + // + HandleRequests, + + // + // InterfaceChange + // + InterfaceChange, + + // + // ConfigChange + // + ConfigChangeHandler, + + // + // DataReceived + // + DataReceived, + + // + // DataSentCallback + // + DataSent, + + // + // ResetHandler + // + ResetHandler, + + // + // SuspendHandler + // + SuspendHandler, + + // + // ResumeHandler + // + ResumeHandler, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints + }, + g_pCompDeviceDescriptor, + (const tConfigHeader **)g_pCompConfigDescriptors, + 0, + 0, + &g_sUSBCompositeFIFOConfig +}; + +//**************************************************************************** +// +// Use the lookup table from the field pulDeviceWorkspace in the +// tUSBDCompositeDevice structure to determine which device to call given a +// particular composite device interface number. +// +// The returned value is the index into psDevice->tCompositeEntry indicating +// the device which contains this interface or INVALID_DEVICE_INDEX if no +// device contains the passed interface number. +// +//**************************************************************************** +static unsigned long +InterfaceToIndex(tUSBDCompositeDevice *psDevice, unsigned long ulInterface) +{ + unsigned long ulLoop; + unsigned char *pucLookupEntry; + + // + // Get a pointer to the lookup table. + // + pucLookupEntry = (unsigned char *)psDevice->pulDeviceWorkspace; + + // + // Check each lookup entry in turn. + // + for(ulLoop = 0; ulLoop < psDevice->ulNumDevices; ulLoop++) + { + // + // If the desired interface number is lower than the value in the + // current lookup table entry, we have found the desired device so + // return its index. + // + if(ulInterface < (unsigned long)pucLookupEntry[LOOKUP_INTERFACE_BYTE]) + { + return(ulLoop); + } + + // + // Move to the next lookup table entry. + // + pucLookupEntry += sizeof(unsigned long); + } + + // + // If we get here, an invalid interface number was passed so return a + // marker to indicate this. + // + return(INVALID_DEVICE_INDEX); +} + +//**************************************************************************** +// +// Use the lookup table from the field pulDeviceWorkspace in the +// tUSBDCompositeDevice structure to determine which device to call given a +// particular composite device endpoint number. +// +// The returned value is the index into psDevice->tCompositeEntry indicating +// the device which contains this endpoint or INVALID_DEVICE_INDEX if no +// device contains the passed endpoint number. +// +//**************************************************************************** +static unsigned long +EndpointToIndex(tUSBDCompositeDevice *psDevice, unsigned long ulEndpoint, + tBoolean bInEndpoint) +{ + unsigned long ulLoop, ulEndpointByte; + unsigned char *pucLookupEntry; + + // + // Get a pointer to the lookup table. + // + pucLookupEntry = (unsigned char *)psDevice->pulDeviceWorkspace; + + // + // Are we considering an IN or OUT endpoint? + // + ulEndpointByte = bInEndpoint ? LOOKUP_IN_END_BYTE : LOOKUP_OUT_END_BYTE; + + // + // Check each lookup entry in turn. + // + for(ulLoop = 0; ulLoop < psDevice->ulNumDevices; ulLoop++) + { + // + // If the desired endpoint number is lower than the value in the + // current lookup table entry, we have found the desired device so + // return its index. + // + if(ulEndpoint < (unsigned long)pucLookupEntry[ulEndpointByte]) + { + return(ulLoop); + } + + // + // Move to the next lookup table entry. + // + pucLookupEntry += sizeof(unsigned long); + } + + // + // If we get here, an invalid endpoint number was passed so return a + // marker to indicate this. + // + return(INVALID_DEVICE_INDEX); +} + + +//**************************************************************************** +// +// This function will check if any device classes need a get descriptor +// handler called. +// +//**************************************************************************** +static void +GetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Determine which device this request is intended for. We have to be + // careful here to send this to the callback for the correct device + // depending upon whether it is a request sent to the device, the interface + // or the endpoint. + // + switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + case USB_RTYPE_INTERFACE: + { + ulIdx = InterfaceToIndex(psDevice, (pUSBRequest->wIndex & 0xFF)); + break; + } + + case USB_RTYPE_ENDPOINT: + { + ulIdx = EndpointToIndex(psDevice, (pUSBRequest->wIndex & 0x0F), + (pUSBRequest->wIndex & 0x80) ? true : false); + break; + } + + // + // Requests sent to the device or any other recipient can't be + // handled here since we have no way of telling where they are + // supposed to be handled. As a result, we just stall them. + // + // If your composite device has some device-specific descriptors, + // you should add code here to handle them. + // + case USB_RTYPE_DEVICE: + case USB_RTYPE_OTHER: + default: + { + ulIdx = INVALID_DEVICE_INDEX; + break; + } + } + + // + // Did we find a device class to pass the request to? + // + if(ulIdx != INVALID_DEVICE_INDEX) + { + // + // Get a pointer to the individual device instance. + // + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + // + // Does this device have a GetDescriptor callback? + // + if(pDeviceInfo->sCallbacks.pfnGetDescriptor) + { + // + // Remember this device index so that we can correctly route any + // data notification callbacks to it. + // + psDevice->psPrivateData->ulEP0Owner = ulIdx; + + // + // Call the device to retrieve the descriptor. + // + pDeviceInfo->sCallbacks.pfnGetDescriptor( + psDevice->psDevices[ulIdx].pvInstance, pUSBRequest); + } + else + { + // + // Oops - we can't satisfy the request so stall EP0 to indicate + // an error. + // + USBDCDStallEP0( + USB_BASE_TO_INDEX(psDevice->psPrivateData->ulUSBBase)); + } + } + else + { + // + // We are unable to satisfy the descriptor request so stall EP0 to + // indicate an error. + // + USBDCDStallEP0(USB_BASE_TO_INDEX(psDevice->psPrivateData->ulUSBBase)); + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an suspend handler +// called. +// +//**************************************************************************** +static void +SuspendHandler(void *pvInstance) +{ + unsigned long ulIdx; + tUSBDCompositeDevice *psDevice; + const tDeviceInfo *pDeviceInfo; + void *pvDeviceInst; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Inform the application that the device has resumed. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(pvInstance, USB_EVENT_SUSPEND, 0, 0); + } + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + pvDeviceInst = psDevice->psDevices[ulIdx].pvInstance; + + if(pDeviceInfo->sCallbacks.pfnSuspendHandler) + { + pDeviceInfo->sCallbacks.pfnSuspendHandler(pvDeviceInst); + } + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an resume handler +// called. +// +//**************************************************************************** +static void +ResumeHandler(void *pvInstance) +{ + unsigned long ulIdx; + tUSBDCompositeDevice *psDevice; + const tDeviceInfo *pDeviceInfo; + void *pvDeviceInst; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Inform the application that the device has resumed. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(pvInstance, USB_EVENT_RESUME, 0, 0); + } + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + pvDeviceInst = psDevice->psDevices[ulIdx].pvInstance; + + if(pDeviceInfo->sCallbacks.pfnResumeHandler) + { + pDeviceInfo->sCallbacks.pfnResumeHandler(pvDeviceInst); + } + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an reset handler +// called. +// +//**************************************************************************** +static void +ResetHandler(void *pvInstance) +{ + unsigned long ulIdx; + tUSBDCompositeDevice *psDevice; + const tDeviceInfo *pDeviceInfo; + void *pvDeviceInst; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Inform the application that the device has been connected. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(pvInstance, USB_EVENT_CONNECTED, 0, 0); + } + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + pvDeviceInst = psDevice->psDevices[ulIdx].pvInstance; + + if(pDeviceInfo->sCallbacks.pfnResetHandler) + { + pDeviceInfo->sCallbacks.pfnResetHandler(pvDeviceInst); + } + } +} + +//**************************************************************************** +// +// This function is called to handle data being set to the host so that the +// application callback can be called when the data has been transferred. +// +//**************************************************************************** +static void +DataSent(void *pvInstance, unsigned long ulInfo) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Pass this notification on to the device which last handled a + // transaction on endpoint 0 (assuming we know who that was). + // + ulIdx = psDevice->psPrivateData->ulEP0Owner; + + if(ulIdx != INVALID_DEVICE_INDEX) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnDataSent) + { + pDeviceInfo->sCallbacks.pfnDataSent( + psDevice->psDevices[ulIdx].pvInstance, ulInfo); + } + } +} + +//**************************************************************************** +// +// This function is called to handle data being received back from the host so +// that the application callback can be called when the new data is ready. +// +//**************************************************************************** +static void +DataReceived(void *pvInstance, unsigned long ulInfo) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Pass this notification on to the device which last handled a + // transaction on endpoint 0 (assuming we know who that was). + // + ulIdx = psDevice->psPrivateData->ulEP0Owner; + + if(ulIdx != INVALID_DEVICE_INDEX) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnDataReceived) + { + pDeviceInfo->sCallbacks.pfnDataReceived( + psDevice->psDevices[ulIdx].pvInstance, ulInfo); + } + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an endpoint handler +// called. +// +//**************************************************************************** +static void +HandleEndpoints(void *pvInstance, unsigned long ulStatus) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Call each of the endpoint handlers. This may seem odd since we should + // only call the handler whose endpoint needs service. Unfortunately, if + // the device class driver is using uDMA, we have no way of knowing which + // handler to call (since ulStatus will be 0). Since the handlers are + // set up to ignore any callback that isn't for them, this is safe. + // + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnEndpointHandler) + { + pDeviceInfo->sCallbacks.pfnEndpointHandler( + psDevice->psDevices[ulIdx].pvInstance, ulStatus); + } + } +} + +//**************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//**************************************************************************** +static void +HandleDisconnect(void *pvInstance) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Inform the application that the device has been disconnected. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(pvInstance, USB_EVENT_DISCONNECTED, 0, 0); + } + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnDisconnectHandler) + { + pDeviceInfo->sCallbacks.pfnDisconnectHandler( + psDevice->psDevices[ulIdx].pvInstance); + } + } +} + +//**************************************************************************** +// +// This function is called by the USB device stack whenever the device +// interface changes. It will be passed on to the device classes if they have +// a handler for this function. +// +//**************************************************************************** +static void +InterfaceChange(void *pvInstance, unsigned char ucInterfaceNum, + unsigned char ucAlternateSetting) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnInterfaceChange) + { + pDeviceInfo->sCallbacks.pfnInterfaceChange( + psDevice->psDevices[ulIdx].pvInstance, ucInterfaceNum, + ucAlternateSetting); + } + } +} + +//**************************************************************************** +// +// This function is called by the USB device stack whenever the device +// configuration changes. It will be passed on to the device classes if they +// have a handler for this function. +// +//**************************************************************************** +static void +ConfigChangeHandler(void *pvInstance, unsigned long ulValue) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnConfigChange) + { + pDeviceInfo->sCallbacks.pfnConfigChange( + psDevice->psDevices[ulIdx].pvInstance, ulValue); + } + } +} + +//**************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvInstance +// \param pUSBRequest points to the request received. +// +// This call will be passed on to the device classes if they have a handler +// for this function. +// +// \return None. +// +//**************************************************************************** +static void +HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Determine which device this request is intended for. We have to be + // careful here to send this to the callback for the correct device + // depending upon whether it is a request sent to the device, the interface + // or the endpoint. + // + switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + case USB_RTYPE_INTERFACE: + { + ulIdx = InterfaceToIndex(psDevice, (pUSBRequest->wIndex & 0xFF)); + break; + } + + case USB_RTYPE_ENDPOINT: + { + ulIdx = EndpointToIndex(psDevice, (pUSBRequest->wIndex & 0x0F), + (pUSBRequest->wIndex & 0x80) ? true : false); + break; + } + + // + // Requests sent to the device or any other recipient can't be + // handled here since we have no way of telling where they are + // supposed to be handled. As a result, we just stall them. + // + // If your composite device has some device-specific requests that need + // to be handled at the device (rather than interface or endpoint) + // level, you should add code here to handle them. + // + case USB_RTYPE_DEVICE: + case USB_RTYPE_OTHER: + default: + { + ulIdx = INVALID_DEVICE_INDEX; + break; + } + } + + // + // Did we find a device class to pass the request to? + // + if(ulIdx != INVALID_DEVICE_INDEX) + { + // + // Get a pointer to the individual device instance. + // + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + // + // Does this device have a RequestHandler callback? + // + if(pDeviceInfo->sCallbacks.pfnRequestHandler) + { + // + // Remember this device index so that we can correctly route any + // data notification callbacks to it. + // + psDevice->psPrivateData->ulEP0Owner = ulIdx; + + // + // Yes - call the device to retrieve the descriptor. + // + pDeviceInfo->sCallbacks.pfnRequestHandler( + psDevice->psDevices[ulIdx].pvInstance, pUSBRequest); + } + else + { + // + // Oops - we can't satisfy the request so stall EP0 to indicate + // an error. + // + USBDCDStallEP0( + USB_BASE_TO_INDEX(psDevice->psPrivateData->ulUSBBase)); + } + } + else + { + // + // We are unable to satisfy the descriptor request so stall EP0 to + // indicate an error. + // + USBDCDStallEP0(USB_BASE_TO_INDEX(psDevice->psPrivateData->ulUSBBase)); + } + +} + +//**************************************************************************** +// +// This function handles sending interface number changes to device instances. +// +//**************************************************************************** +static void +CompositeIfaceChange(tCompositeEntry *pCompDevice, unsigned char ucOld, + unsigned char ucNew) +{ + unsigned char pucInterfaces[2]; + + if(pCompDevice->psDevice->sCallbacks.pfnDeviceHandler) + { + // + // Create the data to pass to the device handler. + // + pucInterfaces[0] = ucOld; + pucInterfaces[1] = ucNew; + + // + // Call the device handler to inform the class of the interface number + // change. + // + pCompDevice->psDevice->sCallbacks.pfnDeviceHandler( + pCompDevice->pvInstance, USB_EVENT_COMP_IFACE_CHANGE, + (void *)pucInterfaces); + } +} + +//**************************************************************************** +// +// This function handles sending endpoint number changes to device instances. +// +//**************************************************************************** +static void +CompositeEPChange(tCompositeEntry *pCompDevice, unsigned char ucOld, + unsigned char ucNew) +{ + unsigned char pucInterfaces[2]; + unsigned char ucIndex; + + if(pCompDevice->psDevice->sCallbacks.pfnDeviceHandler) + { + // + // Create the data to pass to the device handler. + // + pucInterfaces[0] = ucOld; + pucInterfaces[1] = ucNew; + + ucNew--; + + if(ucOld & USB_RTYPE_DIR_IN) + { + ucIndex = (ucOld & ~USB_RTYPE_DIR_IN) - 1; + + g_sUSBCompositeFIFOConfig.sIn[ucNew].bDoubleBuffer = + pCompDevice->psDevice->psFIFOConfig->sIn[ucIndex].bDoubleBuffer; + + g_sUSBCompositeFIFOConfig.sIn[ucNew].usEPFlags = + pCompDevice->psDevice->psFIFOConfig->sIn[ucIndex].usEPFlags; + } + else + { + ucIndex = ucOld - 1; + + g_sUSBCompositeFIFOConfig.sOut[ucNew].bDoubleBuffer = + pCompDevice->psDevice->psFIFOConfig->sOut[ucIndex].bDoubleBuffer; + + g_sUSBCompositeFIFOConfig.sOut[ucNew].usEPFlags = + pCompDevice->psDevice->psFIFOConfig->sOut[ucIndex].usEPFlags; + } + // + // Call the device handler to inform the class of the interface number + // change. + // + pCompDevice->psDevice->sCallbacks.pfnDeviceHandler( + pCompDevice->pvInstance, USB_EVENT_COMP_EP_CHANGE, + (void *)pucInterfaces); + } +} + +//**************************************************************************** +// +// This function merges the configuration descriptors into a single multiple +// instance device. +// +//**************************************************************************** +unsigned long +BuildCompositeDescriptor(tUSBDCompositeDevice *psCompDevice) +{ + unsigned long ulIdx, ulOffset, ulCPIdx, ulFixINT, ulDev; + unsigned short usTotalLength, usBytes; + unsigned char ucInterface, ucINEndpoint, ucOUTEndpoint; + unsigned char *pucData, *pucConfig; + const tConfigHeader *pConfigHeader; + tDescriptorHeader *psHeader; + const unsigned char *pucDescriptor; + tInterfaceDescriptor *psInterface; + tEndpointDescriptor *psEndpoint; + const tDeviceInfo *psDevice; + + // + // Save the number of devices to look through. + // + ulDev = 0; + ulIdx = 0; + ucInterface = 0; + ucINEndpoint = 1; + ucOUTEndpoint = 1; + ulOffset = 0; + ulFixINT = 0; + + // + // This puts the first section pointer in the first entry in the list + // of sections. + // + psCompDevice->psPrivateData->ppsCompSections[0] = + &psCompDevice->psPrivateData->psCompSections[0]; + + // + // Put the pointer to this instances configuration descriptor into the + // front of the list. + // + psCompDevice->psPrivateData->ppsCompSections[0]->pucData = + (unsigned char *)&psCompDevice->psPrivateData->sConfigDescriptor; + + psCompDevice->psPrivateData->ppsCompSections[0]->usSize = + psCompDevice->psPrivateData->sConfigDescriptor.bLength; + + // + // The configuration descriptor is 9 bytes so initialize the total length + // to 9 bytes. + // + usTotalLength = 9; + + // + // Copy the section pointer into the section array for the composite + // device. This is awkward but is required given the definition + // of the structures. + // + psCompDevice->psPrivateData->ppsCompSections[1] = + &psCompDevice->psPrivateData->psCompSections[1]; + + // + // Copy the pointer to the application supplied space into the section + // list. + // + psCompDevice->psPrivateData->ppsCompSections[1]->usSize = 0; + psCompDevice->psPrivateData->ppsCompSections[1]->pucData = + psCompDevice->psPrivateData->pucData; + + // + // Create a local pointer to the data that is used to copy data from + // the other devices into the composite descriptor. + // + pucData = psCompDevice->psPrivateData->pucData; + + // + // Consider each device in turn. + // + while(ulDev < psCompDevice->ulNumDevices) + { + // + // Save the current starting address of this descriptor. + // + pucConfig = pucData + ulOffset; + + // + // Create a local pointer to the configuration header. + // + psDevice = psCompDevice->psDevices[ulDev].psDevice; + pConfigHeader = psDevice->ppConfigDescriptors[0]; + + // + // Loop through each of the sections in this device's configuration + // descriptor. + // + for(ulIdx = 0; ulIdx < pConfigHeader->ucNumSections; ulIdx++) + { + // + // Initialize the local offset in this descriptor. We include + // a special case here to ignore the initial 9 byte config + // descriptor since this has already been handled. + // + if(ulIdx) + { + // + // This is not the first section so we handle everything in + // it. + // + usBytes = 0; + } + else + { + // + // This is the first section for this device so skip the 9 + // byte config descriptor since we've already handled this. + // + usBytes = 9; + + // + // If this section includes only the config descriptor, skip + // it entirely. + // + if(pConfigHeader->psSections[ulIdx]->usSize <= usBytes) + { + continue; + } + } + + // + // Get a pointer to the configuration descriptor. + // + pucDescriptor = pConfigHeader->psSections[ulIdx]->pucData; + + // + // Bounds check the allocated space and return if there is not + // enough space. + // + if(ulOffset > psCompDevice->psPrivateData->ulDataSize) + { + return(1); + } + + // + // Copy the descriptor from the device into the descriptor list. + // + for(ulCPIdx = 0; + ulCPIdx < pConfigHeader->psSections[ulIdx]->usSize; + ulCPIdx++) + { + pucData[ulCPIdx + ulOffset] = pucDescriptor[ulCPIdx]; + } + + // + // Read out the descriptors in this section. + // + while(usBytes < pConfigHeader->psSections[ulIdx]->usSize) + { + // + // Create a descriptor header pointer. + // + psHeader = (tDescriptorHeader *)&pucData[ulOffset + usBytes]; + + // + // Check for interface descriptors and modify the numbering to + // match the composite device. + // + if(psHeader->bDescriptorType == USB_DTYPE_INTERFACE) + { + psInterface = (tInterfaceDescriptor *)psHeader; + + // + // See if this is an alternate setting or the initial + // setting. + // + if(psInterface->bAlternateSetting != 0) + { + // + // If this is an alternate setting then use the + // previous interface number because the current one + // has already been incremented. + // + psInterface->bInterfaceNumber = ucInterface - 1; + } + else + { + // + // Notify the class that it's interface number has + // changed. + // + CompositeIfaceChange(&psCompDevice->psDevices[ulDev], + psInterface->bInterfaceNumber, + ucInterface); + // + // This was the non-alternate setting so save the + // value and move to the next interface number. + // + psInterface->bInterfaceNumber = ucInterface; + + // + // No strings allowed on interface descriptors for + // composite devices. + // + psInterface->iInterface = 0; + + ucInterface++; + } + } + // + // Check for endpoint descriptors and modify the numbering to + // match the composite device. + // + else if(psHeader->bDescriptorType == USB_DTYPE_ENDPOINT) + { + psEndpoint = (tEndpointDescriptor *)psHeader; + + // + // Check if this is an IN or OUT endpoint. + // + if(psEndpoint->bEndpointAddress & USB_RTYPE_DIR_IN) + { + // + // Check if this is the special Fixed Interrupt class + // and this is the interrupt endpoint. + // + if(((psEndpoint->bmAttributes & USB_EP_ATTR_TYPE_M) == + USB_EP_ATTR_INT) && + (psCompDevice->usPID == USB_PID_COMP_SERIAL)) + { + // + // Check if the Fixed Interrupt endpoint has been + // set yet. + // + if(ulFixINT == 0) + { + // + // Allocate the fixed interrupt endpoint and + // save its number. + // + ulFixINT = ucINEndpoint++; + } + + CompositeEPChange(&psCompDevice->psDevices[ulDev], + psEndpoint->bEndpointAddress, + ulFixINT); + + psEndpoint->bEndpointAddress = ulFixINT | + USB_RTYPE_DIR_IN; + } + else + { + // + // Notify the class that it's interface number has + // changed. + // + CompositeEPChange(&psCompDevice->psDevices[ulDev], + psEndpoint->bEndpointAddress, + ucINEndpoint); + + psEndpoint->bEndpointAddress = ucINEndpoint++ | + USB_RTYPE_DIR_IN; + } + } + else + { + // + // Notify the class that it's interface number has + // changed. + // + CompositeEPChange(&psCompDevice->psDevices[ulDev], + psEndpoint->bEndpointAddress, + ucOUTEndpoint); + psEndpoint->bEndpointAddress = ucOUTEndpoint++; + } + } + + // + // Move on to the next descriptor. + // + usBytes += psHeader->bLength; + } + + ulOffset += pConfigHeader->psSections[ulIdx]->usSize; + + usTotalLength += usBytes; + } + + // + // Allow the device class to make adjustments to the configuration + // descriptor. + // + psCompDevice->psDevices[ulDev].psDevice->sCallbacks.pfnDeviceHandler( + psCompDevice->psDevices[ulDev].pvInstance, + USB_EVENT_COMP_CONFIG, (void *)pucConfig); + + // + // Add an entry into the device workspace array to allow us to quickly + // map interface and endpoint numbers to device instances later. + // + psCompDevice->pulDeviceWorkspace[ulDev] = + (ulDev << (LOOKUP_INDEX_BYTE * 8)) | + (ucInterface << (LOOKUP_INTERFACE_BYTE * 8)) | + (ucOUTEndpoint << (LOOKUP_OUT_END_BYTE * 8)) | + (ucINEndpoint << (LOOKUP_IN_END_BYTE * 8)); + + // + // Move on to the next device. + // + ulDev++; + } + + // + // Modify the configuration descriptor to match the number of interfaces + // and the new total size. + // + psCompDevice->psPrivateData->sCompConfigHeader.ucNumSections = 2; + psCompDevice->psPrivateData->ppsCompSections[1]->usSize = ulOffset; + psCompDevice->psPrivateData->sConfigDescriptor.bNumInterfaces = + ucInterface; + psCompDevice->psPrivateData->sConfigDescriptor.wTotalLength = + usTotalLength; + + + return(0); +} + +//**************************************************************************** +// +//! This function should be called once for the composite class device to +//! initialize basic operation and prepare for enumeration. +//! +//! \param ulIndex is the index of the USB controller to initialize for +//! composite device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the composite device. +//! \param ulSize is the size in bytes of the data pointed to by the +//! \e pucData parameter. +//! \param pucData is the data area that the composite class can use to build +//! up descriptors. +//! +//! In order for an application to initialize the USB composite device class, +//! it must first call this function with the a valid composite device class +//! structure in the \e psDevice parameter. This allows this function to +//! initialize the USB controller and device code to be prepared to enumerate +//! and function as a USB composite device. The \e ulSize and \e pucData +//! parameters should be large enough to hold all of the class instances +//! passed in via the psDevice structure. This is typically the full size of +//! the configuration descriptor for a device minus its configuration +//! header(9 bytes). +//! +//! This function returns a void pointer that must be passed in to all other +//! APIs used by the composite class. +//! +//! See the documentation on the tUSBDCompositeDevice structure for more +//! information on how to properly fill the structure members. +//! +//! \return This function returns 0 on failure or a non-zero void pointer on +//! success. +// +//**************************************************************************** +void * +USBDCompositeInit(unsigned long ulIndex, tUSBDCompositeDevice *psDevice, + unsigned long ulSize, unsigned char *pucData) +{ + tCompositeInstance *psInst; + long lIdx; + unsigned char *pucTemp; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateData); + + // + // Initialize the work space in the passed instance structure. + // + psInst = psDevice->psPrivateData; + psInst->ulDataSize = ulSize; + psInst->pucData = pucData; + + // + // Save the base address of the USB controller. + // + psInst->ulUSBBase = USB_INDEX_TO_BASE(ulIndex); + + // + // No device is currently transfering data on EP0. + // + psInst->ulEP0Owner = INVALID_DEVICE_INDEX; + + // + // Set the device information for the composite device. + // + psInst->psDevInfo = &g_sCompositeDeviceInfo; + + g_pCompConfigDescriptors[0] = &psInst->sCompConfigHeader; + g_pCompConfigDescriptors[0]->ucNumSections = 0; + g_pCompConfigDescriptors[0]->psSections = + (const tConfigSection * const *)psDevice->psPrivateData->ppsCompSections; + + // + // Create a byte pointer to use with the copy. + // + pucTemp = (unsigned char *)&psInst->sConfigDescriptor; + + // + // Copy the default configuration descriptor into the instance data. + // + for(lIdx = 0; lIdx < g_pCompConfigDescriptor[0]; lIdx++) + { + pucTemp[lIdx] = g_pCompConfigDescriptor[lIdx]; + } + + // + // Create a byte pointer to use with the copy. + // + pucTemp = (unsigned char *)&psInst->sDeviceDescriptor; + + // + // Copy the default configuration descriptor into the instance data. + // + for(lIdx = 0; lIdx < g_pCompDeviceDescriptor[0]; lIdx++) + { + pucTemp[lIdx] = g_pCompDeviceDescriptor[lIdx]; + } + + // + // Fix up the device descriptor with the client-supplied values. + // + psInst->sDeviceDescriptor.idVendor = psDevice->usVID; + psInst->sDeviceDescriptor.idProduct = psDevice->usPID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->sConfigDescriptor.bmAttributes = psDevice->ucPwrAttributes; + psInst->sConfigDescriptor.bMaxPower = + (unsigned char)(psDevice->usMaxPowermA>>1); + + g_sCompositeDeviceInfo.pDeviceDescriptor = + (const unsigned char *)&psInst->sDeviceDescriptor; + + // + // Plug in the client's string table to the device information + // structure. + // + psInst->psDevInfo->ppStringDescriptors = psDevice->ppStringDescriptors; + psInst->psDevInfo->ulNumStringDescriptors = + psDevice->ulNumStringDescriptors; + + // + // Enable Clocking to the USB controller so that changes to the USB + // controller can be made in the BuildCompositeDescriptor() function. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); + + // + // Create the combined descriptors. + // + if(BuildCompositeDescriptor(psDevice)) + { + return(0); + } + + // + // Set the instance data for this device. + // + psInst->psDevInfo->pvInstance = (void *)psDevice; + + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ulIndex, psInst->psDevInfo); + + // + // Return the pointer to the instance indicating that everything went + // well. + // + return((void *)psDevice); +} + +//**************************************************************************** +// +//! Shuts down the composite device. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCompositeInit(). +//! +//! This function terminates composite device interface for the instance +//! supplied. Following this call, the \e pvInstance instance should not me +//! used in any other calls. +//! +//! \return None. +// +//**************************************************************************** +void +USBDCompositeTerm(void *pvInstance) +{ + ASSERT(pvInstance != 0); + +} + +//**************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//**************************************************************************** + -- cgit v1.3.1