//****************************************************************************
//
// usbdcomp.c - USB composite device class driver.
//
// Copyright (c) 2010-2020 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
// 
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
// 
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
// 
// This is part of revision 2.2.0.295 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/usb.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);
        }
    }

    if(psCompDevice->pfnCallback)
    {
        switch(ui32Request)
        {
            case USB_EVENT_LPM_RESUME:
            {
                //
                // Pass the LPM resume event to the client.
                //
                psCompDevice->pfnCallback(0, USB_EVENT_LPM_RESUME, 0,
                                               (void *)0);
                break;
            }
            case USB_EVENT_LPM_SLEEP:
            {
                //
                // Pass the LPM sleep event to the client.
                //
                psCompDevice->pfnCallback(0, USB_EVENT_LPM_SLEEP, 0,
                                               (void *)0);
                break;
            }
            case USB_EVENT_LPM_ERROR:
            {
                //
                // Pass the LPM error event to the client.
                //
                psCompDevice->pfnCallback(0, USB_EVENT_LPM_ERROR, 0,
                                               (void *)0);
                break;
            }
            default:
            {
                break;
            }
        }
    }
}

//****************************************************************************
//
// 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;

    //
    // Inform the application that the device configuration has changed.
    //
    if(psCompDevice->pfnCallback)
    {
        psCompDevice->pfnCallback(pvCompositeInstance, USB_EVENT_CONFIG_CHANGE,
                                  ui32Value, 0);
    }

    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;
	bool bINEndpointSet = false;
	bool bOUTEndpointSet = false;

    //
    // Save the number of devices to look through.
    //
    ui32Dev = 0;
    ui32Idx = 0;
    ui8Interface = 0;
    ui8INEndpoint = 1;
    ui8OUTEndpoint = 1;
    ui32Offset = 0;
    ui32FixINT = 0;
	bINEndpointSet = false;
	bOUTEndpointSet = false;

    //
    // 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];
            }

        	bINEndpointSet = false;
        	bOUTEndpointSet = false;
            //
            // 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)
                    {
                        //
                    	// TivaWare Update 2.2.x - Removed check for Fixed Interrupt
                    	// class which caused multiple Windows 10 issues with USB
                    	// composite device enumeration.
                        //

                    	// Check if an OUT Endpoint was set during this loop
                    	if (bOUTEndpointSet)
                            {
                    		// Set IN Endpoint Number to be one less than the OUT endpoint
                    		// since it was incremented when setting the IN Endpoint.

                    		ui8INEndpoint = ui8OUTEndpoint-1;
                        }

                            //
                            // Notify the class that it's interface number has
                            // changed.
                            //
                            CompositeEPChange(
                                        &psCompDevice->psDevices[ui32Dev],
                                        psEndpoint->bEndpointAddress,
                                        ui8INEndpoint);

                            psEndpoint->bEndpointAddress = ui8INEndpoint++ |
                                                           USB_RTYPE_DIR_IN;
                    	// Check if a BULK Endpoint for Data transfer is being set.
                    	if(((psEndpoint->bmAttributes & USB_EP_ATTR_TYPE_M) ==
                    			USB_EP_ATTR_BULK) &&
                    			(psCompDevice->ui16PID == USB_PID_COMP_SERIAL))
                    	{
                    		// Set the boolean that an IN Endpoint for Data transfer is set.
                    		bINEndpointSet = true;
                        }
                    }
                    else
                    {
                    	// Check if an IN Endpoint was set during this loop
                    	if (bINEndpointSet)
                    	{
                    		// Set OUT Endpoint Number to be one less than the IN endpoint
                    		// since it was incremented when setting the IN Endpoint.
                    		ui8OUTEndpoint = ui8INEndpoint-1;
                    	}
                        //
                        // Notify the class that it's interface number has
                        // changed.
                        //
                        CompositeEPChange(&psCompDevice->psDevices[ui32Dev],
                                          psEndpoint->bEndpointAddress,
                                          ui8OUTEndpoint);
                        psEndpoint->bEndpointAddress = ui8OUTEndpoint++;
                        // Check if a BULK Endpoint for Data transfer is being set.
                        if(((psEndpoint->bmAttributes & USB_EP_ATTR_TYPE_M) ==
                        		USB_EP_ATTR_BULK) &&
                        		(psCompDevice->ui16PID == USB_PID_COMP_SERIAL))
                        {
                        	// Set the boolean that an OUT Endpoint for Data transfer is set.
                        	bOUTEndpointSet = true;
                        }
                    }
                }

                //
                // 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.
//! @}
//
//****************************************************************************

