openblt/Target/Demo/ARMCM4_TM4C_DK_TM4C123G_IAR/Boot/lib/usblib/device/usbdcomp.c

1507 lines
50 KiB
C

//****************************************************************************
//
// usbdcomp.c - USB composite device class driver.
//
// Copyright (c) 2010-2013 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 1.1 of the Tiva USB Library.
//
//****************************************************************************
#include <stdbool.h>
#include <stdint.h>
#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/usbdrv.h"
#include "usblib/usblib.h"
#include "usblib/usblibpriv.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 uint8_t g_pui8CompDeviceDescriptor[] =
{
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 uint8_t g_pui8CompConfigDescriptor[] =
{
//
// 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.pui32DeviceWorkspace). 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
//****************************************************************************
//
// Various internal handlers needed by this class.
//
//****************************************************************************
static void HandleDisconnect(void *pvCompositeInstance);
static void InterfaceChange(void *pvCompositeInstance, uint8_t ui8InterfaceNum,
uint8_t ui8AlternateSetting);
static void ConfigChangeHandler(void *pvCompositeInstance, uint32_t ui32Value);
static void DataSent(void *pvCompositeInstance, uint32_t ui32Info);
static void DataReceived(void *pvCompositeInstance, uint32_t ui32Info);
static void HandleEndpoints(void *pvCompositeInstance, uint32_t ui32Status);
static void HandleRequests(void *pvCompositeInstance, tUSBRequest *psUSBRequest);
static void SuspendHandler(void *pvCompositeInstance);
static void ResumeHandler(void *pvCompositeInstance);
static void ResetHandler(void *pvCompositeInstance);
static void HandleDevice(void *pvCompositeInstance, uint32_t ui32Request,
void *pvRequestData);
static void GetDescriptor(void *pvCompositeInstance, tUSBRequest *psUSBRequest);
//****************************************************************************
//
// Configuration Descriptor.
//
//****************************************************************************
tConfigHeader *g_ppCompConfigDescriptors[1];
//****************************************************************************
//
// The device information structure for the USB Composite device.
//
//****************************************************************************
const tCustomHandlers g_sCompHandlers =
{
//
// GetDescriptor
//
GetDescriptor,
//
// RequestHandler
//
HandleRequests,
//
// InterfaceChange
//
InterfaceChange,
//
// ConfigChange
//
ConfigChangeHandler,
//
// DataReceived
//
DataReceived,
//
// DataSentCallback
//
DataSent,
//
// ResetHandler
//
ResetHandler,
//
// SuspendHandler
//
SuspendHandler,
//
// ResumeHandler
//
ResumeHandler,
//
// DisconnectHandler
//
HandleDisconnect,
//
// EndpointHandler
//
HandleEndpoints,
//
// DeviceHandler
//
HandleDevice,
};
//****************************************************************************
//
// Use the lookup table from the field pui32DeviceWorkspace 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 uint32_t
InterfaceToIndex(tUSBDCompositeDevice *psDevice, uint32_t ui32Interface)
{
uint32_t ui32Loop;
uint32_t ui32Lookup;
//
// Check each lookup entry in turn.
//
for(ui32Loop = 0; ui32Loop < psDevice->ui32NumDevices; ui32Loop++)
{
//
// Get the look up value from the device.
//
ui32Lookup = psDevice->psDevices[ui32Loop].ui32DeviceWorkspace;
ui32Lookup = (ui32Lookup >> (8 * LOOKUP_INTERFACE_BYTE)) & 0xff;
//
// 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(ui32Interface < ui32Lookup)
{
return(ui32Loop);
}
}
//
// 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 pui32DeviceWorkspace 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 uint32_t
EndpointToIndex(tUSBDCompositeDevice *psDevice, uint32_t ui32Endpoint,
bool bInEndpoint)
{
uint32_t ui32Loop, ui32EndpointByte, ui32Lookup;
//
// Are we considering an IN or OUT endpoint?
//
ui32EndpointByte = bInEndpoint ? LOOKUP_IN_END_BYTE : LOOKUP_OUT_END_BYTE;
//
// Check each lookup entry in turn.
//
for(ui32Loop = 0; ui32Loop < psDevice->ui32NumDevices; ui32Loop++)
{
//
// Get the look up byte from the device.
//
ui32Lookup = psDevice->psDevices[ui32Loop].ui32DeviceWorkspace;
ui32Lookup = (ui32Lookup >> (ui32EndpointByte * 8)) & 0xff;
//
// 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(ui32Endpoint < ui32Lookup)
{
return(ui32Loop);
}
}
//
// 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 *pvCompositeInstance, tUSBRequest *psUSBRequest)
{
uint32_t ui32Idx;
const tDeviceInfo *psDeviceInfo;
tUSBDCompositeDevice *psCompDevice;
//
// Create the composite device pointer.
//
psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance;
//
// 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(psUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M)
{
case USB_RTYPE_INTERFACE:
{
ui32Idx = InterfaceToIndex(psCompDevice,
(psUSBRequest->wIndex & 0xFF));
break;
}
case USB_RTYPE_ENDPOINT:
{
ui32Idx = EndpointToIndex(psCompDevice,
(psUSBRequest->wIndex & 0x0F),
(psUSBRequest->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:
{
ui32Idx = INVALID_DEVICE_INDEX;
break;
}
}
//
// Did we find a device class to pass the request to?
//
if(ui32Idx != INVALID_DEVICE_INDEX)
{
//
// Get a pointer to the individual device instance.
//
psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo;
//
// Does this device have a GetDescriptor callback?
//
if(psDeviceInfo->psCallbacks->pfnGetDescriptor)
{
//
// Remember this device index so that we can correctly route any
// data notification callbacks to it.
//
psCompDevice->sPrivateData.ui32EP0Owner = ui32Idx;
//
// Call the device to retrieve the descriptor.
//
psDeviceInfo->psCallbacks->pfnGetDescriptor(
psCompDevice->psDevices[ui32Idx].pvInstance, psUSBRequest);
}
else
{
//
// Oops - we can't satisfy the request so stall EP0 to indicate
// an error.
//
USBDCDStallEP0(USBBaseToIndex(
psCompDevice->sPrivateData.ui32USBBase));
}
}
else
{
//
// We are unable to satisfy the descriptor request so stall EP0 to
// indicate an error.
//
USBDCDStallEP0(USBBaseToIndex(
psCompDevice->sPrivateData.ui32USBBase));
}
}
//****************************************************************************
//
// This function will check if any device classes need an suspend handler
// called.
//
//****************************************************************************
static void
SuspendHandler(void *pvCompositeInstance)
{
uint32_t ui32Idx;
tUSBDCompositeDevice *psCompDevice;
const tDeviceInfo *psDeviceInfo;
void *pvDeviceInst;
ASSERT(pvCompositeInstance != 0);
//
// Create the device instance pointer.
//
psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance;
//
// Inform the application that the device has resumed.
//
if(psCompDevice->pfnCallback)
{
psCompDevice->pfnCallback(pvCompositeInstance, USB_EVENT_SUSPEND,
0, 0);
}
for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++)
{
psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo;
pvDeviceInst = psCompDevice->psDevices[ui32Idx].pvInstance;
if(psDeviceInfo->psCallbacks->pfnSuspendHandler)
{
psDeviceInfo->psCallbacks->pfnSuspendHandler(pvDeviceInst);
}
}
}
//****************************************************************************
//
// This function will check if any device classes need an resume handler
// called.
//
//****************************************************************************
static void
ResumeHandler(void *pvCompositeInstance)
{
uint32_t ui32Idx;
tUSBDCompositeDevice *psCompDevice;
const tDeviceInfo *psDeviceInfo;
void *pvDeviceInst;
ASSERT(pvCompositeInstance != 0);
//
// Create the device instance pointer.
//
psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance;
//
// Inform the application that the device has resumed.
//
if(psCompDevice->pfnCallback)
{
psCompDevice->pfnCallback(pvCompositeInstance, USB_EVENT_RESUME,
0, 0);
}
for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++)
{
psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo;
pvDeviceInst = psCompDevice->psDevices[ui32Idx].pvInstance;
if(psDeviceInfo->psCallbacks->pfnResumeHandler)
{
psDeviceInfo->psCallbacks->pfnResumeHandler(pvDeviceInst);
}
}
}
//****************************************************************************
//
// This function will check if any device classes need an reset handler
// called.
//
//****************************************************************************
static void
ResetHandler(void *pvCompositeInstance)
{
uint32_t ui32Idx;
tUSBDCompositeDevice *psCompDevice;
const tDeviceInfo *psDeviceInfo;
void *pvDeviceInst;
ASSERT(pvCompositeInstance != 0);
//
// Create the device instance pointer.
//
psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance;
//
// Inform the application that the device has been connected.
//
if(psCompDevice->pfnCallback)
{
psCompDevice->pfnCallback(pvCompositeInstance,
USB_EVENT_CONNECTED, 0, 0);
}
for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++)
{
psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo;
pvDeviceInst = psCompDevice->psDevices[ui32Idx].pvInstance;
if(psDeviceInfo->psCallbacks->pfnResetHandler)
{
psDeviceInfo->psCallbacks->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 *pvCompositeInstance, uint32_t ui32Info)
{
uint32_t ui32Idx;
const tDeviceInfo *psDeviceInfo;
tUSBDCompositeDevice *psCompDevice;
//
// Create the device instance pointer.
//
psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance;
//
// Pass this notification on to the device which last handled a
// transaction on endpoint 0 (assuming we know who that was).
//
ui32Idx = psCompDevice->sPrivateData.ui32EP0Owner;
if(ui32Idx != INVALID_DEVICE_INDEX)
{
psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo;
if(psDeviceInfo->psCallbacks->pfnDataSent)
{
psDeviceInfo->psCallbacks->pfnDataSent(
psCompDevice->psDevices[ui32Idx].pvInstance, ui32Info);
}
}
}
//****************************************************************************
//
// 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 *pvCompositeInstance, uint32_t ui32Info)
{
uint32_t ui32Idx;
const tDeviceInfo *psDeviceInfo;
tUSBDCompositeDevice *psCompDevice;
//
// Create the device instance pointer.
//
psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance;
//
// Pass this notification on to the device which last handled a
// transaction on endpoint 0 (assuming we know who that was).
//
ui32Idx = psCompDevice->sPrivateData.ui32EP0Owner;
if(ui32Idx != INVALID_DEVICE_INDEX)
{
psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo;
if(psDeviceInfo->psCallbacks->pfnDataReceived)
{
psDeviceInfo->psCallbacks->pfnDataReceived(
psCompDevice->psDevices[ui32Idx].pvInstance, ui32Info);
}
}
}
//****************************************************************************
//
// This function will check if any device classes need an endpoint handler
// called.
//
//****************************************************************************
static void
HandleEndpoints(void *pvCompositeInstance, uint32_t ui32Status)
{
uint32_t ui32Idx;
const tDeviceInfo *psDeviceInfo;
tUSBDCompositeDevice *psCompDevice;
ASSERT(pvCompositeInstance != 0);
//
// Create the device instance pointer.
//
psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance;
//
// 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 ui32Status will be 0). Since the handlers are
// set up to ignore any callback that is not for them, this is safe.
//
for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++)
{
psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo;
if(psDeviceInfo->psCallbacks->pfnEndpointHandler)
{
psDeviceInfo->psCallbacks->pfnEndpointHandler(
psCompDevice->psDevices[ui32Idx].pvInstance, ui32Status);
}
}
}
//*****************************************************************************
//
// Device instance specific handler.
//
//*****************************************************************************
static void
HandleDevice(void *pvCompositeInstance, uint32_t ui32Request,
void *pvRequestData)
{
uint32_t ui32Idx;
tUSBDCompositeDevice *psCompDevice;
const tDeviceInfo *psDeviceInfo;
ASSERT(pvCompositeInstance != 0);
//
// Create the device instance pointer.
//
psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance;
for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++)
{
psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo;
if(psDeviceInfo->psCallbacks->pfnDeviceHandler)
{
psDeviceInfo->psCallbacks->pfnDeviceHandler(
psCompDevice->psDevices[ui32Idx].pvInstance, ui32Request,
pvRequestData);
}
}
}
//****************************************************************************
//
// This function is called by the USB device stack whenever the device is
// disconnected from the host.
//
//****************************************************************************
static void
HandleDisconnect(void *pvCompositeInstance)
{
uint32_t ui32Idx;
const tDeviceInfo *psDeviceInfo;
tUSBDCompositeDevice *psCompDevice;
ASSERT(pvCompositeInstance != 0);
//
// Create the device instance pointer.
//
psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance;
//
// Inform the application that the device has been disconnected.
//
if(psCompDevice->pfnCallback)
{
psCompDevice->pfnCallback(pvCompositeInstance,
USB_EVENT_DISCONNECTED, 0, 0);
}
for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++)
{
psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo;
if(psDeviceInfo->psCallbacks->pfnDisconnectHandler)
{
psDeviceInfo->psCallbacks->pfnDisconnectHandler(
psCompDevice->psDevices[ui32Idx].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 *pvCompositeInstance, uint8_t ui8InterfaceNum,
uint8_t ui8AlternateSetting)
{
uint32_t ui32Idx;
const tDeviceInfo *psDeviceInfo;
tUSBDCompositeDevice *psCompDevice;
ASSERT(pvCompositeInstance != 0);
//
// Create the device instance pointer.
//
psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance;
for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++)
{
psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo;
if(psDeviceInfo->psCallbacks->pfnInterfaceChange)
{
psDeviceInfo->psCallbacks->pfnInterfaceChange(
psCompDevice->psDevices[ui32Idx].pvInstance,
ui8InterfaceNum, ui8AlternateSetting);
}
}
}
//****************************************************************************
//
// 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 *pvCompositeInstance, uint32_t ui32Value)
{
uint32_t ui32Idx;
const tDeviceInfo *psDeviceInfo;
tUSBDCompositeDevice *psCompDevice;
ASSERT(pvCompositeInstance != 0);
//
// Create the device instance pointer.
//
psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance;
for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++)
{
psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo;
if(psDeviceInfo->psCallbacks->pfnConfigChange)
{
psDeviceInfo->psCallbacks->pfnConfigChange(
psCompDevice->psDevices[ui32Idx].pvInstance, ui32Value);
}
}
}
//****************************************************************************
//
// This function is called by the USB device stack whenever a non-standard
// request is received.
//
// \param pvCompositeInstance
// \param psUSBRequest 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 *pvCompositeInstance, tUSBRequest *psUSBRequest)
{
uint32_t ui32Idx;
const tDeviceInfo *psDeviceInfo;
tUSBDCompositeDevice *psCompDevice;
//
// Create the device instance pointer.
//
psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance;
//
// 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(psUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M)
{
case USB_RTYPE_INTERFACE:
{
ui32Idx = InterfaceToIndex(psCompDevice,
(psUSBRequest->wIndex & 0xFF));
break;
}
case USB_RTYPE_ENDPOINT:
{
ui32Idx = EndpointToIndex(psCompDevice,
(psUSBRequest->wIndex & 0x0F),
(psUSBRequest->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:
{
ui32Idx = INVALID_DEVICE_INDEX;
break;
}
}
//
// Did we find a device class to pass the request to?
//
if(ui32Idx != INVALID_DEVICE_INDEX)
{
//
// Get a pointer to the individual device instance.
//
psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo;
//
// Does this device have a RequestHandler callback?
//
if(psDeviceInfo->psCallbacks->pfnRequestHandler)
{
//
// Remember this device index so that we can correctly route any
// data notification callbacks to it.
//
psCompDevice->sPrivateData.ui32EP0Owner = ui32Idx;
//
// Yes - call the device to retrieve the descriptor.
//
psDeviceInfo->psCallbacks->pfnRequestHandler(
psCompDevice->psDevices[ui32Idx].pvInstance,
psUSBRequest);
}
else
{
//
// Oops - we can't satisfy the request so stall EP0 to indicate
// an error.
//
USBDCDStallEP0(USBBaseToIndex(
psCompDevice->sPrivateData.ui32USBBase));
}
}
else
{
//
// We are unable to satisfy the descriptor request so stall EP0 to
// indicate an error.
//
USBDCDStallEP0(USBBaseToIndex(
psCompDevice->sPrivateData.ui32USBBase));
}
}
//****************************************************************************
//
// This function handles sending interface number changes to device instances.
//
//****************************************************************************
static void
CompositeIfaceChange(tCompositeEntry *psCompDevice, uint8_t ui8Old,
uint8_t ui8New)
{
uint8_t pui8Interfaces[2];
if(psCompDevice->psDevInfo->psCallbacks->pfnDeviceHandler)
{
//
// Create the data to pass to the device handler.
//
pui8Interfaces[0] = ui8Old;
pui8Interfaces[1] = ui8New;
//
// Call the device handler to inform the class of the interface number
// change.
//
psCompDevice->psDevInfo->psCallbacks->pfnDeviceHandler(
psCompDevice->pvInstance, USB_EVENT_COMP_IFACE_CHANGE,
(void *)pui8Interfaces);
}
}
//****************************************************************************
//
// This function handles sending endpoint number changes to device instances.
//
//****************************************************************************
static void
CompositeEPChange(tCompositeEntry *psCompDevice, uint8_t ui8Old,
uint8_t ui8New)
{
uint8_t pui8Interfaces[2];
if(psCompDevice->psDevInfo->psCallbacks->pfnDeviceHandler)
{
//
// Create the data to pass to the device handler.
//
pui8Interfaces[0] = ui8Old;
pui8Interfaces[1] = ui8New;
ui8New--;
//
// Call the device handler to inform the class of the interface number
// change.
//
psCompDevice->psDevInfo->psCallbacks->pfnDeviceHandler(
psCompDevice->pvInstance, USB_EVENT_COMP_EP_CHANGE,
(void *)pui8Interfaces);
}
}
//****************************************************************************
//
// This function merges the configuration descriptors into a single multiple
// instance device.
//
//****************************************************************************
uint32_t
BuildCompositeDescriptor(tUSBDCompositeDevice *psCompDevice)
{
uint32_t ui32Idx, ui32Offset, ui32CPIdx, ui32FixINT, ui32Dev;
uint16_t ui16TotalLength, ui16Bytes;
uint8_t ui8Interface, ui8INEndpoint, ui8OUTEndpoint;
uint8_t *pui8Data, *pui8Config;
const tConfigHeader *psConfigHeader;
tDescriptorHeader *psHeader;
const uint8_t *pui8Descriptor;
tInterfaceDescriptor *psInterface;
tEndpointDescriptor *psEndpoint;
const tDeviceInfo *psDevice;
//
// Save the number of devices to look through.
//
ui32Dev = 0;
ui32Idx = 0;
ui8Interface = 0;
ui8INEndpoint = 1;
ui8OUTEndpoint = 1;
ui32Offset = 0;
ui32FixINT = 0;
//
// This puts the first section pointer in the first entry in the list
// of sections.
//
psCompDevice->sPrivateData.ppsCompSections[0] =
&psCompDevice->sPrivateData.psCompSections[0];
//
// Put the pointer to this instances configuration descriptor into the
// front of the list.
//
psCompDevice->sPrivateData.ppsCompSections[0]->pui8Data =
(uint8_t *)&psCompDevice->sPrivateData.sConfigDescriptor;
psCompDevice->sPrivateData.ppsCompSections[0]->ui16Size =
psCompDevice->sPrivateData.sConfigDescriptor.bLength;
//
// The configuration descriptor is 9 bytes so initialize the total length
// to 9 bytes.
//
ui16TotalLength = 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->sPrivateData.ppsCompSections[1] =
&psCompDevice->sPrivateData.psCompSections[1];
//
// Copy the pointer to the application supplied space into the section
// list.
//
psCompDevice->sPrivateData.ppsCompSections[1]->ui16Size = 0;
psCompDevice->sPrivateData.ppsCompSections[1]->pui8Data =
psCompDevice->sPrivateData.pui8Data;
//
// Create a local pointer to the data that is used to copy data from
// the other devices into the composite descriptor.
//
pui8Data = psCompDevice->sPrivateData.pui8Data;
//
// Consider each device in turn.
//
while(ui32Dev < psCompDevice->ui32NumDevices)
{
//
// Save the current starting address of this descriptor.
//
pui8Config = pui8Data + ui32Offset;
//
// Create a local pointer to the configuration header.
//
psDevice = psCompDevice->psDevices[ui32Dev].psDevInfo;
psConfigHeader = psDevice->ppsConfigDescriptors[0];
//
// Loop through each of the sections in this device's configuration
// descriptor.
//
for(ui32Idx = 0; ui32Idx < psConfigHeader->ui8NumSections; ui32Idx++)
{
//
// Initialize the local offset in this descriptor. We include
// a special case here to ignore the initial 9 byte configuration
// descriptor since this has already been handled.
//
if(ui32Idx)
{
//
// This is not the first section so we handle everything in
// it.
//
ui16Bytes = 0;
}
else
{
//
// This is the first section for this device so skip the 9
// byte configuration descriptor since we've already handled
// this.
//
ui16Bytes = 9;
//
// If this section includes only the configuration descriptor,
// skip it entirely.
//
if(psConfigHeader->psSections[ui32Idx]->ui16Size <= ui16Bytes)
{
continue;
}
}
//
// Get a pointer to the configuration descriptor.
//
pui8Descriptor = psConfigHeader->psSections[ui32Idx]->pui8Data;
//
// Bounds check the allocated space and return if there is not
// enough space.
//
if(ui32Offset > psCompDevice->sPrivateData.ui32DataSize)
{
return(1);
}
//
// Copy the descriptor from the device into the descriptor list.
//
for(ui32CPIdx = 0;
ui32CPIdx < psConfigHeader->psSections[ui32Idx]->ui16Size;
ui32CPIdx++)
{
pui8Data[ui32CPIdx + ui32Offset] = pui8Descriptor[ui32CPIdx];
}
//
// Read out the descriptors in this section.
//
while(ui16Bytes < psConfigHeader->psSections[ui32Idx]->ui16Size)
{
//
// Create a descriptor header pointer.
//
psHeader = (tDescriptorHeader *)&pui8Data[ui32Offset +
ui16Bytes];
//
// 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 = ui8Interface - 1;
}
else
{
//
// Notify the class that it's interface number has
// changed.
//
CompositeIfaceChange(
&psCompDevice->psDevices[ui32Dev],
psInterface->bInterfaceNumber,
ui8Interface);
//
// This was the non-alternate setting so save the
// value and move to the next interface number.
//
psInterface->bInterfaceNumber = ui8Interface;
//
// No strings allowed on interface descriptors for
// composite devices.
//
psInterface->iInterface = 0;
ui8Interface++;
}
}
//
// 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->ui16PID == USB_PID_COMP_SERIAL))
{
//
// Check if the Fixed Interrupt endpoint has been
// set yet.
//
if(ui32FixINT == 0)
{
//
// Allocate the fixed interrupt endpoint and
// save its number.
//
ui32FixINT = ui8INEndpoint++;
}
CompositeEPChange(
&psCompDevice->psDevices[ui32Dev],
psEndpoint->bEndpointAddress,
ui32FixINT);
psEndpoint->bEndpointAddress = ui32FixINT |
USB_RTYPE_DIR_IN;
}
else
{
//
// Notify the class that it's interface number has
// changed.
//
CompositeEPChange(
&psCompDevice->psDevices[ui32Dev],
psEndpoint->bEndpointAddress,
ui8INEndpoint);
psEndpoint->bEndpointAddress = ui8INEndpoint++ |
USB_RTYPE_DIR_IN;
}
}
else
{
//
// Notify the class that it's interface number has
// changed.
//
CompositeEPChange(&psCompDevice->psDevices[ui32Dev],
psEndpoint->bEndpointAddress,
ui8OUTEndpoint);
psEndpoint->bEndpointAddress = ui8OUTEndpoint++;
}
}
//
// Move on to the next descriptor.
//
ui16Bytes += psHeader->bLength;
}
ui32Offset += psConfigHeader->psSections[ui32Idx]->ui16Size;
ui16TotalLength += ui16Bytes;
}
//
// Allow the device class to make adjustments to the configuration
// descriptor.
//
psCompDevice->psDevices[ui32Dev].psDevInfo->psCallbacks->pfnDeviceHandler(
psCompDevice->psDevices[ui32Dev].pvInstance,
USB_EVENT_COMP_CONFIG, (void *)pui8Config);
//
// Add an entry into the device workspace array to allow us to quickly
// map interface and endpoint numbers to device instances later.
//
psCompDevice->psDevices[ui32Dev].ui32DeviceWorkspace =
(ui32Dev << (LOOKUP_INDEX_BYTE * 8)) |
(ui8Interface << (LOOKUP_INTERFACE_BYTE * 8)) |
(ui8OUTEndpoint << (LOOKUP_OUT_END_BYTE * 8)) |
(ui8INEndpoint << (LOOKUP_IN_END_BYTE * 8));
//
// Move on to the next device.
//
ui32Dev++;
}
//
// Modify the configuration descriptor to match the number of interfaces
// and the new total size.
//
psCompDevice->sPrivateData.sCompConfigHeader.ui8NumSections = 2;
psCompDevice->sPrivateData.ppsCompSections[1]->ui16Size = ui32Offset;
psCompDevice->sPrivateData.sConfigDescriptor.bNumInterfaces =
ui8Interface;
psCompDevice->sPrivateData.sConfigDescriptor.wTotalLength =
ui16TotalLength;
return(0);
}
//****************************************************************************
//
//! This function should be called once for the composite class device to
//! initialize basic operation and prepare for enumeration.
//!
//! \param ui32Index 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 ui32Size is the size in bytes of the data pointed to by the
//! \e pui8Data parameter.
//! \param pui8Data 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 ui32Size and \e pui8Data
//! parameters should be large enough to hold all of the class instances
//! passed in via the \e 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(uint32_t ui32Index, tUSBDCompositeDevice *psDevice,
uint32_t ui32Size, uint8_t *pui8Data)
{
tCompositeInstance *psInst;
int32_t i32Idx;
uint8_t *pui8Temp;
//
// Check parameter validity.
//
ASSERT(ui32Index == 0);
ASSERT(psDevice);
ASSERT(psDevice->ppui8StringDescriptors);
//
// Initialize the work space in the passed instance structure.
//
psInst = &psDevice->sPrivateData;
psInst->ui32DataSize = ui32Size;
psInst->pui8Data = pui8Data;
//
// Save the base address of the USB controller.
//
psInst->ui32USBBase = USBIndexToBase(ui32Index);
//
// No device is currently transferring data on EP0.
//
psInst->ui32EP0Owner = INVALID_DEVICE_INDEX;
//
// Initialize the device information structure.
//
psInst->sDevInfo.psCallbacks = &g_sCompHandlers;
psInst->sDevInfo.pui8DeviceDescriptor = g_pui8CompDeviceDescriptor;
psInst->sDevInfo.ppsConfigDescriptors =
(const tConfigHeader * const *)g_ppCompConfigDescriptors;
psInst->sDevInfo.ppui8StringDescriptors = 0;
psInst->sDevInfo.ui32NumStringDescriptors = 0;
//
// Initialize the device info structure for the composite device.
//
USBDCDDeviceInfoInit(0, &psInst->sDevInfo);
g_ppCompConfigDescriptors[0] = &psInst->sCompConfigHeader;
g_ppCompConfigDescriptors[0]->ui8NumSections = 0;
g_ppCompConfigDescriptors[0]->psSections =
(const tConfigSection * const *)psDevice->sPrivateData.ppsCompSections;
//
// Create a byte pointer to use with the copy.
//
pui8Temp = (uint8_t *)&psInst->sConfigDescriptor;
//
// Copy the default configuration descriptor into the instance data.
//
for(i32Idx = 0; i32Idx < g_pui8CompConfigDescriptor[0]; i32Idx++)
{
pui8Temp[i32Idx] = g_pui8CompConfigDescriptor[i32Idx];
}
//
// Create a byte pointer to use with the copy.
//
pui8Temp = (uint8_t *)&psInst->sDeviceDescriptor;
//
// Copy the default configuration descriptor into the instance data.
//
for(i32Idx = 0; i32Idx < g_pui8CompDeviceDescriptor[0]; i32Idx++)
{
pui8Temp[i32Idx] = g_pui8CompDeviceDescriptor[i32Idx];
}
//
// Fix up the device descriptor with the client-supplied values.
//
psInst->sDeviceDescriptor.idVendor = psDevice->ui16VID;
psInst->sDeviceDescriptor.idProduct = psDevice->ui16PID;
//
// Fix up the configuration descriptor with client-supplied values.
//
psInst->sConfigDescriptor.bmAttributes = psDevice->ui8PwrAttributes;
psInst->sConfigDescriptor.bMaxPower =
(uint8_t)(psDevice->ui16MaxPowermA>>1);
psInst->sDevInfo.pui8DeviceDescriptor =
(const uint8_t *)&psInst->sDeviceDescriptor;
//
// Plug in the client's string table to the device information
// structure.
//
psInst->sDevInfo.ppui8StringDescriptors =
psDevice->ppui8StringDescriptors;
psInst->sDevInfo.ui32NumStringDescriptors =
psDevice->ui32NumStringDescriptors;
//
// 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);
}
//
// All is well so now pass the descriptors to the lower layer and put
// the bulk device on the bus.
//
USBDCDInit(ui32Index, &psInst->sDevInfo, (void *)psDevice);
//
// Return the pointer to the instance indicating that everything went
// well.
//
return((void *)psDevice);
}
//****************************************************************************
//
//! Shuts down the composite device.
//!
//! \param pvCompositeInstance is the pointer to the device instance structure
//! as returned by USBDCompositeInit().
//!
//! This function terminates composite device interface for the instance
//! not me supplied. Following this call, the \e pvCompositeInstance instance
//! should not be used in any other calls.
//!
//! \return None.
//
//****************************************************************************
void
USBDCompositeTerm(void *pvCompositeInstance)
{
ASSERT(pvCompositeInstance != 0);
}
//****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//****************************************************************************