//****************************************************************************
//
// 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.
//! @}
//
//****************************************************************************

