//*****************************************************************************
//
// usbdbulk.c - USB bulk device class driver.
//
// Copyright (c) 2008-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/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/usb.h"
#include "usblib/usblib.h"
#include "usblib/usblibpriv.h"
#include "usblib/device/usbdevice.h"
#include "usblib/device/usbdbulk.h"
#include "usblib/device/usbdcomp.h"
#include "usblib/usblibpriv.h"

//*****************************************************************************
//
//! \addtogroup bulk_device_class_api
//! @{
//
//*****************************************************************************

//*****************************************************************************
//
// The subset of endpoint status flags that we consider to be reception
// errors.  These are passed to the client via USB_EVENT_ERROR if seen.
//
//*****************************************************************************
#define USB_RX_ERROR_FLAGS      (USBERR_DEV_RX_DATA_ERROR |                   \
                                 USBERR_DEV_RX_OVERRUN |                      \
                                 USBERR_DEV_RX_FIFO_FULL)

//*****************************************************************************
//
// Flags that may appear in ui16DeferredOpFlags to indicate some operation that
// has been requested but could not be processed at the time it was received.
// Each deferred operation is defined as the bit number that should be set in
// tBulkInstance->ui16DeferredOpFlags to indicate that the operation is
// pending.
//
//*****************************************************************************
#define BULK_DO_PACKET_RX       5

//*****************************************************************************
//
// Endpoints to use for each of the required endpoints in the driver.'
//
//*****************************************************************************
#define DATA_IN_ENDPOINT        USB_EP_1
#define DATA_OUT_ENDPOINT       USB_EP_1

//*****************************************************************************
//
// Maximum packet size for the bulk endpoints used for bulk data
// transmission and reception and the associated FIFO sizes to set aside
// for each endpoint.
//
//*****************************************************************************
#define DATA_IN_EP_MAX_SIZE     USBFIFOSizeToBytes(USB_FIFO_SZ_64)
#define DATA_OUT_EP_MAX_SIZE    USBFIFOSizeToBytes(USB_FIFO_SZ_64)

#define DATA_IN_EP_MAX_SIZE_HS  USBFIFOSizeToBytes(USB_FIFO_SZ_512)
#define DATA_OUT_EP_MAX_SIZE_HS USBFIFOSizeToBytes(USB_FIFO_SZ_512)

//*****************************************************************************
//
// Device Descriptor.  This is stored in RAM to allow several fields to be
// changed at runtime based on the client's requirements.
//
//*****************************************************************************
uint8_t g_pui8BulkDeviceDescriptor[] =
{
    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_VEND_SPECIFIC,    // USB Device Class
    0,                          // USB Device Sub-class
    0,                          // USB Device protocol
    64,                         // Maximum packet size for default pipe.
    USBShort(0),                // Vendor ID (VID).
    USBShort(0),                // Product ID (PID).
    USBShort(0x100),            // Device Version BCD.
    1,                          // Manufacturer string identifier.
    2,                          // Product string identifier.
    3,                          // Product serial number.
    1                           // Number of configurations.
};

//*****************************************************************************
//
// Bulk 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.
//
//*****************************************************************************
uint8_t g_pui8BulkDescriptor[] =
{
    //
    // Configuration descriptor header.
    //
    9,                          // Size of the configuration descriptor.
    USB_DTYPE_CONFIGURATION,    // Type of this descriptor.
    USBShort(32),               // The total size of this full structure.
    1,                          // The number of interfaces in this
                                // configuration.
    1,                          // The unique value for this configuration.
    5,                          // The string identifier that describes this
                                // configuration.
    USB_CONF_ATTR_SELF_PWR,     // Bus Powered, Self Powered, remote wake up.
    250,                        // The maximum power in 2mA increments.
};

//*****************************************************************************
//
// The remainder of the configuration descriptor is stored in flash since we
// don't need to modify anything in it at runtime.
//
//*****************************************************************************
const uint8_t g_pui8BulkInterface[BULKINTERFACE_SIZE] =
{
    //
    // Vendor-specific Interface Descriptor.
    //
    9,                              // Size of the interface descriptor.
    USB_DTYPE_INTERFACE,            // Type of this descriptor.
    0,                              // The index for this interface.
    0,                              // The alternate setting for this
                                    // interface.
    2,                              // The number of endpoints used by this
                                    // interface.
    USB_CLASS_VEND_SPECIFIC,        // The interface class
    0,                              // The interface sub-class.
    0,                              // The interface protocol for the sub-class
                                    // specified above.
    4,                              // The string index for this interface.

    //
    // Endpoint Descriptor
    //
    7,                              // The size of the endpoint descriptor.
    USB_DTYPE_ENDPOINT,             // Descriptor type is an endpoint.
    USB_EP_DESC_IN | USBEPToIndex(DATA_IN_ENDPOINT),
    USB_EP_ATTR_BULK,               // Endpoint is a bulk endpoint.
    USBShort(DATA_IN_EP_MAX_SIZE),  // The maximum packet size.
    0,                              // The polling interval for this endpoint.

    //
    // Endpoint Descriptor
    //
    7,                               // The size of the endpoint descriptor.
    USB_DTYPE_ENDPOINT,              // Descriptor type is an endpoint.
    USB_EP_DESC_OUT | USBEPToIndex(DATA_OUT_ENDPOINT),
    USB_EP_ATTR_BULK,                // Endpoint is a bulk endpoint.
    USBShort(DATA_OUT_EP_MAX_SIZE),  // The maximum packet size.
    0,                               // The polling interval for this endpoint.
};

const uint8_t g_pui8BulkInterfaceHS[BULKINTERFACE_SIZE] =
{
    //
    // Vendor-specific Interface Descriptor.
    //
    9,                              // Size of the interface descriptor.
    USB_DTYPE_INTERFACE,            // Type of this descriptor.
    0,                              // The index for this interface.
    0,                              // The alternate setting for this
                                    // interface.
    2,                              // The number of endpoints used by this
                                    // interface.
    USB_CLASS_VEND_SPECIFIC,        // The interface class
    0,                              // The interface sub-class.
    0,                              // The interface protocol for the sub-class
                                    // specified above.
    4,                              // The string index for this interface.

    //
    // Endpoint Descriptor
    //
    7,                              // The size of the endpoint descriptor.
    USB_DTYPE_ENDPOINT,             // Descriptor type is an endpoint.
    USB_EP_DESC_IN | USBEPToIndex(DATA_IN_ENDPOINT),
    USB_EP_ATTR_BULK,               // Endpoint is a bulk endpoint.
    USBShort(DATA_IN_EP_MAX_SIZE_HS),  // The maximum packet size.
    0,                              // The polling interval for this endpoint.

    //
    // Endpoint Descriptor
    //
    7,                               // The size of the endpoint descriptor.
    USB_DTYPE_ENDPOINT,              // Descriptor type is an endpoint.
    USB_EP_DESC_OUT | USBEPToIndex(DATA_OUT_ENDPOINT),
    USB_EP_ATTR_BULK,                // Endpoint is a bulk endpoint.
    USBShort(DATA_OUT_EP_MAX_SIZE_HS),  // The maximum packet size.
    0,                               // The polling interval for this endpoint.
};

//*****************************************************************************
//
// The bulk configuration descriptor is defined as two sections, one
// containing just the 9 byte USB configuration descriptor and the other
// containing everything else that is sent to the host along with it.
//
//*****************************************************************************
const tConfigSection g_sBulkConfigSection =
{
    sizeof(g_pui8BulkDescriptor),
    g_pui8BulkDescriptor
};

const tConfigSection g_sBulkInterfaceSection =
{
    sizeof(g_pui8BulkInterface),
    g_pui8BulkInterface
};

const tConfigSection g_sBulkInterfaceSectionHS =
{
    sizeof(g_pui8BulkInterfaceHS),
    g_pui8BulkInterfaceHS
};

//*****************************************************************************
//
// This array lists all the sections that must be concatenated to make a
// single, complete bulk device configuration descriptor.
//
//*****************************************************************************
const tConfigSection *g_psBulkSections[] =
{
    &g_sBulkConfigSection,
    &g_sBulkInterfaceSection
};

const tConfigSection *g_psBulkSectionsHS[] =
{
    &g_sBulkConfigSection,
    &g_sBulkInterfaceSectionHS
};

#define NUM_BULK_SECTIONS       (sizeof(g_psBulkSections) /                   \
                                 sizeof(g_psBulkSections[0]))

//*****************************************************************************
//
// The header for the single configuration we support.  This is the root of
// the data structure that defines all the bits and pieces that are pulled
// together to generate the configuration descriptor.
//
//*****************************************************************************
const tConfigHeader g_sBulkConfigHeader =
{
    NUM_BULK_SECTIONS,
    g_psBulkSections
};

const tConfigHeader g_sBulkConfigHeaderHS =
{
    NUM_BULK_SECTIONS,
    g_psBulkSectionsHS
};

//*****************************************************************************
//
// Configuration Descriptor.
//
//*****************************************************************************
const tConfigHeader * const g_ppBulkConfigDescriptors[] =
{
    &g_sBulkConfigHeader
};

const tConfigHeader * const g_ppBulkConfigDescriptorsHS[] =
{
    &g_sBulkConfigHeaderHS
};

//*****************************************************************************
//
// Variable to get the maximum packet size for the interface
//
//*****************************************************************************
static uint16_t g_ui16MaxPacketSize = USBFIFOSizeToBytes(USB_FIFO_SZ_64);

//*****************************************************************************
//
// Forward references for device handler callbacks
//
//*****************************************************************************
static void HandleConfigChange(void *pvBulkDevice, uint32_t ui32Info);
static void HandleDisconnect(void *pvBulkDevice);
static void HandleEndpoints(void *pvBulkDevice, uint32_t ui32Status);
static void HandleSuspend(void *pvBulkDevice);
static void HandleResume(void *pvBulkDevice);
static void HandleDevice(void *pvBulkDevice, uint32_t ui32Request,
                         void *pvRequestData);

//*****************************************************************************
//
// Device event handler callbacks.
//
//*****************************************************************************
const tCustomHandlers g_sBulkHandlers =
{
    //
    // GetDescriptor
    //
    0,

    //
    // RequestHandler
    //
    0,

    //
    // InterfaceChange
    //
    0,

    //
    // ConfigChange
    //
    HandleConfigChange,

    //
    // DataReceived
    //
    0,

    //
    // DataSentCallback
    //
    0,

    //
    // ResetHandler
    //
    0,

    //
    // SuspendHandler
    //
    HandleSuspend,

    //
    // ResumeHandler
    //
    HandleResume,

    //
    // DisconnectHandler
    //
    HandleDisconnect,

    //
    // EndpointHandler
    //
    HandleEndpoints,

    //
    // Device handler
    //
    HandleDevice
};

//*****************************************************************************
//
// Set or clear deferred operation flags in an "atomic" manner.
//
// \param pui16DeferredOp points to the flags variable which is to be modified.
// \param ui16Bit indicates which bit number is to be set or cleared.
// \param bSet indicates the state that the flag must be set to.  If \b true,
// the flag is set, if \b false, the flag is cleared.
//
// This function safely sets or clears a bit in a flag variable.  The operation
// makes use of bitbanding to ensure that the operation is atomic (no read-
// modify-write is required).
//
// \return None.
//
//*****************************************************************************
static void
SetDeferredOpFlag(volatile uint16_t *pui16DeferredOp, uint16_t ui16Bit,
                  bool bSet)
{
    //
    // Set the flag bit to 1 or 0 using a bitband access.
    //
    HWREGBITH(pui16DeferredOp, ui16Bit) = bSet ? 1 : 0;
}

//*****************************************************************************
//
// Receives notifications related to data received from the host.
//
// \param psBulkDevice is the device instance whose endpoint is to be
// processed.
// \param ui32Status is the USB interrupt status that caused this function to
// be called.
//
// This function is called from HandleEndpoints for all interrupts signaling
// the arrival of data on the bulk OUT endpoint (in other words, whenever the
// host has sent us a packet of data).  We inform the client that a packet
// is available and, on return, check to see if the packet has been read.  If
// not, we schedule another notification to the client for a later time.
//
// \return Returns \b true on success or \b false on failure.
//
//*****************************************************************************
static bool
ProcessDataFromHost(tUSBDBulkDevice *psBulkDevice, uint32_t ui32Status)
{
    uint32_t ui32EPStatus;
    uint32_t ui32Size;
    tBulkInstance *psInst;

    //
    // Get a pointer to the bulk device instance data pointer
    //
    psInst = &psBulkDevice->sPrivateData;

    //
    // Get the endpoint status to see why we were called.
    //
    ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ui8OUTEndpoint);

    //
    // Clear the status bits.
    //
    MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ui8OUTEndpoint,
                                  ui32EPStatus);

    //
    // Has a packet been received?
    //
    if(ui32EPStatus & USB_DEV_RX_PKT_RDY)
    {
        //
        // Set the flag we use to indicate that a packet read is pending.  This
        // will be cleared if the packet is read.  If the client does not read
        // the packet in the context of the USB_EVENT_RX_AVAILABLE callback,
        // the event will be signaled later during tick processing.
        //
        SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, BULK_DO_PACKET_RX,
                          true);

        //
        // How big is the packet we have just received?
        //
        ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase,
                                            psInst->ui8OUTEndpoint);

        //
        // The receive channel is not blocked so let the caller know
        // that a packet is waiting.  The parameters are set to indicate
        // that the packet has not been read from the hardware FIFO yet.
        //
        psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData,
                                    USB_EVENT_RX_AVAILABLE,
                                    ui32Size, (void *)0);
    }
    else
    {
        //
        // No packet was received.  Some error must have been reported.  Check
        // and pass this on to the client if necessary.
        //
        if(ui32EPStatus & USB_RX_ERROR_FLAGS)
        {
            //
            // This is an error we report to the client so allow the callback
            // to handle it.
            //
            psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData,
                                        USB_EVENT_ERROR,
                                        (ui32EPStatus & USB_RX_ERROR_FLAGS),
                                        (void *)0);
        }
        return(false);
    }

    return(true);
}

//*****************************************************************************
//
// Receives notifications related to data sent to the host.
//
// \param psBulkDevice is the device instance whose endpoint is to be
// processed.
// \param ui32Status is the USB interrupt status that caused this function to
// be called.
//
// This function is called from HandleEndpoints for all interrupts originating
// from the bulk IN endpoint (in other words, whenever data has been
// transmitted to the USB host).  We examine the cause of the interrupt and,
// if due to completion of a transmission, notify the client.
//
// \return Returns \b true on success or \b false on failure.
//
//*****************************************************************************
static bool
ProcessDataToHost(tUSBDBulkDevice *psBulkDevice, uint32_t ui32Status)
{
    tBulkInstance *psInst;
    uint32_t ui32EPStatus, ui32Size;

    //
    // Get a pointer to the bulk device instance data pointer
    //
    psInst = &psBulkDevice->sPrivateData;

    //
    // Get the endpoint status to see why we were called.
    //
    ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase,
                                         psInst->ui8INEndpoint);

    //
    // Clear the status bits.
    //
    MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, psInst->ui8INEndpoint,
                                  ui32EPStatus);

    //
    // Our last transmission completed.  Clear our state back to idle and
    // see if we need to send any more data.
    //
    psInst->iBulkTxState = eBulkStateIdle;

    //
    // Notify the client that the last transmission completed.
    //
    ui32Size = psInst->ui16LastTxSize;
    psInst->ui16LastTxSize = 0;
    psBulkDevice->pfnTxCallback(psBulkDevice->pvTxCBData,
                                USB_EVENT_TX_COMPLETE, ui32Size, (void *)0);

    return(true);
}

//*****************************************************************************
//
// Called by the USB stack for any activity involving one of our endpoints
// other than EP0.  This function is a fan out that merely directs the call to
// the correct handler depending upon the endpoint and transaction direction
// signaled in ui32Status.
//
//*****************************************************************************
static void
HandleEndpoints(void *pvBulkDevice, uint32_t ui32Status)
{
    tUSBDBulkDevice *psBulkDevice;
    tBulkInstance *psInst;

    ASSERT(pvBulkDevice != 0);

    //
    // The bulk device structure pointer.
    //
    psBulkDevice = (tUSBDBulkDevice *)pvBulkDevice;

    //
    // Get a pointer to the bulk device instance data pointer
    //
    psInst = &psBulkDevice->sPrivateData;

    //
    // Handler for the bulk OUT data endpoint.
    //
    if(ui32Status & (0x10000 << USBEPToIndex(psInst->ui8OUTEndpoint)))
    {
        //
        // Data is being sent to us from the host.
        //
        ProcessDataFromHost(psBulkDevice, ui32Status);
    }

    //
    // Handler for the bulk IN data endpoint.
    //
    if(ui32Status & (1 << USBEPToIndex(psInst->ui8INEndpoint)))
    {
        ProcessDataToHost(psBulkDevice, ui32Status);
    }
}

//*****************************************************************************
//
// Called by the USB stack whenever a configuration change occurs.
//
//*****************************************************************************
static void
HandleConfigChange(void *pvBulkDevice, uint32_t ui32Info)
{
    tBulkInstance *psInst;
    tUSBDBulkDevice *psBulkDevice;

    ASSERT(pvBulkDevice != 0);

    //
    // The bulk device structure pointer.
    //
    psBulkDevice = (tUSBDBulkDevice *)pvBulkDevice;

    //
    // Get a pointer to the bulk device instance data pointer
    //
    psInst = &psBulkDevice->sPrivateData;

    //
    // Set all our endpoints to idle state.
    //
    psInst->iBulkRxState = eBulkStateIdle;
    psInst->iBulkTxState = eBulkStateIdle;

    //
    // If we have a control callback, let the client know we are open for
    // business.
    //
    if(psBulkDevice->pfnRxCallback)
    {
        //
        // Pass the connected event to the client.
        //
        psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData,
                                    USB_EVENT_CONNECTED, 0, (void *)0);
    }

    //
    // Remember that we are connected.
    //
    psInst->bConnected = true;
}

//*****************************************************************************
//
// Device instance specific handler.
//
//*****************************************************************************
static void
HandleDevice(void *pvBulkDevice, uint32_t ui32Request, void *pvRequestData)
{
    tBulkInstance *psInst;
    uint8_t *pui8Data;
    tUSBDBulkDevice *psBulkDevice;

    //
    // The bulk device structure pointer.
    //
    psBulkDevice = (tUSBDBulkDevice *)pvBulkDevice;

    //
    // Get a pointer to the bulk device instance data pointer
    //
    psInst = &psBulkDevice->sPrivateData;

    //
    // Create the 8-bit array used by the events supported by the USB Bulk
    // class.
    //
    pui8Data = (uint8_t *)pvRequestData;

    switch(ui32Request)
    {
        //
        // This was an interface change event.
        //
        case USB_EVENT_COMP_IFACE_CHANGE:
        {
            psInst->ui8Interface = pui8Data[1];
            break;
        }

        //
        // This was an endpoint change event.
        //
        case USB_EVENT_COMP_EP_CHANGE:
        {
            //
            // Determine if this is an IN or OUT endpoint that has changed.
            //
            if(pui8Data[0] & USB_EP_DESC_IN)
            {
                psInst->ui8INEndpoint = IndexToUSBEP((pui8Data[1] & 0x7f));
            }
            else
            {
                //
                // Extract the new endpoint number.
                //
                psInst->ui8OUTEndpoint = IndexToUSBEP(pui8Data[1] & 0x7f);
            }
            break;
        }
        case USB_EVENT_LPM_RESUME:
        {
            if(psBulkDevice->pfnRxCallback)
            {
                //
                // Pass the LPM resume event to the client.
                //
                psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData,
                                            USB_EVENT_LPM_RESUME, 0,
                                            (void *)0);
            }
            break;
        }
        case USB_EVENT_LPM_SLEEP:
        {
            if(psBulkDevice->pfnRxCallback)
            {
                //
                // Pass the LPM sleep event to the client.
                //
                psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData,
                                            USB_EVENT_LPM_SLEEP, 0, (void *)0);
            }
            break;
        }
        case USB_EVENT_LPM_ERROR:
        {
            if(psBulkDevice->pfnRxCallback)
            {
                //
                // Pass the LPM error event to the client.
                //
                psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData,
                                            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 *pvBulkDevice)
{
    tUSBDBulkDevice *psBulkDevice;
    tBulkInstance *psInst;

    ASSERT(pvBulkDevice != 0);

    //
    // The bulk device structure pointer.
    //
    psBulkDevice = (tUSBDBulkDevice *)pvBulkDevice;

    //
    // Get a pointer to the bulk device instance data pointer
    //
    psInst = &psBulkDevice->sPrivateData;

    //
    // If we are not currently connected so let the client know we are open
    // for business.
    //
    if(psInst->bConnected)
    {
        //
        // Pass the disconnected event to the client.
        //
        psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData,
                                    USB_EVENT_DISCONNECTED, 0, (void *)0);
    }

    //
    // Remember that we are no longer connected.
    //
    psInst->bConnected = false;
}

//*****************************************************************************
//
// This function is called by the USB device stack whenever the bus is put into
// suspend state.
//
//*****************************************************************************
static void
HandleSuspend(void *pvBulkDevice)
{
    const tUSBDBulkDevice *psBulkDevice;

    ASSERT(pvBulkDevice != 0);

    //
    // The bulk device structure pointer.
    //
    psBulkDevice = (const tUSBDBulkDevice *)pvBulkDevice;

    //
    // Pass the event on to the client.
    //
    psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, USB_EVENT_SUSPEND, 0,
                                (void *)0);
}

//*****************************************************************************
//
// This function is called by the USB device stack whenever the bus is taken
// out of suspend state.
//
//*****************************************************************************
static void
HandleResume(void *pvBulkDevice)
{
    const tUSBDBulkDevice *psBulkDevice;

    ASSERT(pvBulkDevice != 0);

    //
    // The bulk device structure pointer.
    //
    psBulkDevice = (const tUSBDBulkDevice *)pvBulkDevice;

    //
    // Pass the event on to the client.
    //
    psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, USB_EVENT_RESUME, 0,
                                (void *)0);
}

//*****************************************************************************
//
// This function is called periodically and provides us with a time reference
// and method of implementing delayed or time-dependent operations.
//
// \param ui32Index is the index of the USB controller for which this tick
// is being generated.
// \param ui32TimemS is the elapsed time in milliseconds since the last call
// to this function.
//
// \return None.
//
//*****************************************************************************
static void
BulkTickHandler(void *pvBulkDevice, uint32_t ui32TimemS)
{
    tBulkInstance *psInst;
    uint32_t ui32Size;
    tUSBDBulkDevice *psBulkDevice;

    ASSERT(pvBulkDevice != 0);

    //
    // The bulk device structure pointer.
    //
    psBulkDevice = (tUSBDBulkDevice *)pvBulkDevice;

    //
    // Get a pointer to the bulk device instance data pointer
    //
    psInst = &psBulkDevice->sPrivateData;

    //
    // Do we have a deferred receive waiting
    //
    if(psInst->ui16DeferredOpFlags & (1 << BULK_DO_PACKET_RX))
    {
        //
        // Yes - how big is the waiting packet?
        //
        ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase,
                                            psInst->ui8OUTEndpoint);

        //
        // Tell the client that there is a packet waiting for it.
        //
        psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData,
                                    USB_EVENT_RX_AVAILABLE, ui32Size,
                                    (void *)0);
    }

    return;
}

//*****************************************************************************
//
//! Initializes bulk device operation for a given USB controller.
//!
//! \param ui32Index is the index of the USB controller which is to be
//! initialized for bulk device operation.
//! \param psBulkDevice points to a structure containing parameters customizing
//! the operation of the bulk device.
//!
//! An application wishing to make use of a USB bulk communication channel
//! must call this function to initialize the USB controller and attach the
//! device to the USB bus.  This function performs all required USB
//! initialization.
//!
//! On successful completion, this function will return the \e psBulkDevice
//! pointer passed to it.  This must be passed on all future calls to the
//! device driver related to this device.
//!
//! The USBDBulk interface offers packet-based transmit and receive operation.
//! If the application would rather use block based communication with
//! transmit and receive buffers, USB buffers may be used above the bulk
//! transmit and receive channels to offer this functionality.
//!
//! Transmit Operation:
//!
//! Calls to USBDBulkPacketWrite() must send no more than 64 bytes of data for
//! FS USB and 512 bytes for HS USB at a time and may only be made when no
//! other transmission is currently outstanding.
//!
//! Once a packet of data has been acknowledged by the USB host, a
//! \b USB_EVENT_TX_COMPLETE event is sent to the application callback to
//! inform it that another packet may be transmitted.
//!
//! Receive Operation:
//!
//! An incoming USB data packet will result in a call to the application
//! callback with event \b USBD_EVENT_RX_AVAILABLE.  The application must then
//! call USBDBulkPacketRead(), passing a buffer capable of holding 64 bytes for
//! FS USB and 512 bytes for HS USB, to retrieve the data and acknowledge
//! reception to the USB host.
//!
//! \note The application must not make any calls to the low level USB Device
//! API if interacting with USB via the USB bulk device class API.  Doing so
//! will cause unpredictable (though almost certainly unpleasant) behavior.
//!
//! \return Returns NULL on failure or void pointer that should be used with
//! the remaining USB bulk class APSs.
//
//*****************************************************************************
void *
USBDBulkInit(uint32_t ui32Index, tUSBDBulkDevice *psBulkDevice)
{
    void *pvBulkDevice;
    tDeviceDescriptor *psDevDesc;
    tConfigDescriptor *psConfigDesc;

    //
    // Check parameter validity.
    //
    ASSERT(ui32Index == 0);
    ASSERT(psBulkDevice);

    pvBulkDevice = USBDBulkCompositeInit(ui32Index, psBulkDevice, 0);

    if(pvBulkDevice)
    {
        //
        // Fix up the device descriptor with the client-supplied values.
        //
        psDevDesc = (tDeviceDescriptor *)g_pui8BulkDeviceDescriptor;
        psDevDesc->idVendor = psBulkDevice->ui16VID;
        psDevDesc->idProduct = psBulkDevice->ui16PID;

        //
        // Fix up the configuration descriptor with client-supplied values.
        //
        psConfigDesc = (tConfigDescriptor *)g_pui8BulkDescriptor;
        psConfigDesc->bmAttributes = psBulkDevice->ui8PwrAttributes;
        psConfigDesc->bMaxPower = (uint8_t)(psBulkDevice->ui16MaxPowermA / 2);

        //
        // All is well so now pass the descriptors to the lower layer and put
        // the bulk device on the bus.
        //
        USBDCDInit(ui32Index, &psBulkDevice->sPrivateData.sDevInfo,
                   (void *)psBulkDevice);
    }

    //
    // Return the pointer to the instance indicating that everything went well.
    //
    return(pvBulkDevice);
}

//*****************************************************************************
//
//! Initializes bulk device operation for a given USB controller.
//!
//! \param ui32Index is the index of the USB controller which is to be
//! initialized for bulk device operation.
//! \param psBulkDevice points to a structure containing parameters customizing
//! the operation of the bulk device.
//! \param psCompEntry is the composite device entry to initialize when
//! creating a composite device.
//!
//! This call is very similar to USBDBulkInit() except that it is used for
//! initializing an instance of the bulk device for use in a composite device.
//! When this bulk device is part of a composite device, then the
//! \e psCompEntry should point to the composite device entry to initialize.
//! This is part of the array that is passed to the USBDCompositeInit()
//! function.
//!
//! \return Returns zero on failure or a non-zero value that should be
//! used with the remaining USB Bulk APIs.
//
//*****************************************************************************
void *
USBDBulkCompositeInit(uint32_t ui32Index, tUSBDBulkDevice *psBulkDevice,
                      tCompositeEntry *psCompEntry)
{
    tBulkInstance *psInst;
    uint32_t ui32ulpiFeature = 0;

    //
    // Check parameter validity.
    //
    ASSERT(ui32Index == 0);
    ASSERT(psBulkDevice);
    ASSERT(psBulkDevice->ppui8StringDescriptors);
    ASSERT(psBulkDevice->pfnRxCallback);
    ASSERT(psBulkDevice->pfnTxCallback);

    //
    // Initialize the workspace in the passed instance structure.
    //
    psInst = &psBulkDevice->sPrivateData;

    //
    // Initialize the composite entry that is used by the composite device
    // class.
    //
    if(psCompEntry != 0)
    {
        psCompEntry->psDevInfo = &psInst->sDevInfo;
        psCompEntry->pvInstance = (void *)psBulkDevice;
    }

    //
    // Get the ULPI Feature
    //
    USBDCDFeatureGet(0, USBLIB_FEATURE_USBULPI, &ui32ulpiFeature);

    g_ui16MaxPacketSize = USBFIFOSizeToBytes(USB_FIFO_SZ_64);

    //
    // Initialize the device information structure.
    //
    psInst->sDevInfo.psCallbacks = &g_sBulkHandlers;
    psInst->sDevInfo.pui8DeviceDescriptor = g_pui8BulkDeviceDescriptor;
    psInst->sDevInfo.ppsConfigDescriptors = g_ppBulkConfigDescriptors;
    if (USBLIB_FEATURE_ULPI_HS == ui32ulpiFeature)
    {
        psInst->sDevInfo.ppsConfigDescriptors = g_ppBulkConfigDescriptorsHS;
        g_ui16MaxPacketSize = USBFIFOSizeToBytes(USB_FIFO_SZ_512);
    }
    psInst->sDevInfo.ppui8StringDescriptors = 0;
    psInst->sDevInfo.ui32NumStringDescriptors = 0;

    //
    // Set the basic state information for the class.
    //
    psInst->ui32USBBase = USB0_BASE;
    psInst->iBulkRxState = eBulkStateUnconfigured;
    psInst->iBulkTxState = eBulkStateUnconfigured;
    psInst->ui16DeferredOpFlags = 0;
    psInst->bConnected = false;

    //
    // Initialize the device info structure for the Bulk device.
    //
    USBDCDDeviceInfoInit(0, &psInst->sDevInfo);

    //
    // Set the default endpoint and interface assignments.
    //
    psInst->ui8INEndpoint = DATA_IN_ENDPOINT;
    psInst->ui8OUTEndpoint = DATA_OUT_ENDPOINT;
    psInst->ui8Interface = 0;

    //
    // Plug in the client's string stable to the device information
    // structure.
    //
    psInst->sDevInfo.ppui8StringDescriptors =
                                        psBulkDevice->ppui8StringDescriptors;
    psInst->sDevInfo.ui32NumStringDescriptors =
                                        psBulkDevice->ui32NumStringDescriptors;

    //
    // Initialize the USB tick module, this will prevent it from being
    // initialized later in the call to USBDCDInit();
    //
    InternalUSBTickInit();

    //
    // Register our tick handler (this must be done after USBDCDInit).
    //
    InternalUSBRegisterTickHandler(BulkTickHandler, (void *)psBulkDevice);

    //
    // Return the pointer to the instance indicating that everything went well.
    //
    return((void *)psBulkDevice);
}

//*****************************************************************************
//
//! Shut down the bulk device.
//!
//! \param pvBulkDevice is the pointer to the device instance structure as
//! returned by USBDBulkInit().
//!
//! This function terminates device operation for the instance supplied and
//! removes the device from the USB bus.  This function should not be called
//! if the bulk device is part of a composite device and instead the
//! USBDCompositeTerm() function should be called for the full composite
//! device.
//!
//! Following this call, the \e pvBulkDevice instance should not me used in any
//! other calls.
//!
//! \return None.
//
//*****************************************************************************
void
USBDBulkTerm(void *pvBulkDevice)
{
    tBulkInstance *psInst;

    ASSERT(pvBulkDevice);

    //
    // Get a pointer to the bulk device instance data pointer
    //
    psInst = &((tUSBDBulkDevice *)pvBulkDevice)->sPrivateData;

    //
    // Terminate the requested instance.
    //
    USBDCDTerm(USBBaseToIndex(psInst->ui32USBBase));

    psInst->ui32USBBase = 0;

    return;
}

//*****************************************************************************
//
//! Sets the client-specific pointer parameter for the receive channel
//! callback.
//!
//! \param pvBulkDevice is the pointer to the device instance structure as
//! returned by USBDBulkInit().
//! \param pvCBData is the pointer that client wishes to be provided on each
//! event sent to the receive channel callback function.
//!
//! The client uses this function to change the callback pointer passed in
//! the first parameter on all callbacks to the \e pfnRxCallback function
//! passed on USBDBulkInit().
//!
//! If a client wants to make runtime changes in the callback pointer, it must
//! ensure that the \e pvBulkDevice structure passed to USBDBulkInit() resides
//! in RAM.  If this structure is in flash, callback pointer changes are not
//! possible.
//!
//! \return Returns the previous callback pointer that was being used for
//! this instance's receive callback.
//
//*****************************************************************************
void *
USBDBulkSetRxCBData(void *pvBulkDevice, void *pvCBData)
{
    void *pvOldValue;

    ASSERT(pvBulkDevice);

    //
    // Set the callback data for the receive channel after remembering the
    // previous value.
    //
    pvOldValue = ((tUSBDBulkDevice *)pvBulkDevice)->pvRxCBData;
    ((tUSBDBulkDevice *)pvBulkDevice)->pvRxCBData = pvCBData;

    //
    // Return the previous callback pointer.
    //
    return(pvOldValue);
}

//*****************************************************************************
//
//! Sets the client-specific pointer parameter for the transmit callback.
//!
//! \param pvBulkDevice is the pointer to the device instance structure as
//! returned by USBDBulkInit().
//! \param pvCBData is the pointer that client wishes to be provided on each
//! event sent to the transmit channel callback function.
//!
//! The client uses this function to change the callback pointer passed in
//! the first parameter on all callbacks to the \e pfnTxCallback function
//! passed on USBDBulkInit().
//!
//! If a client wants to make runtime changes in the callback pointer, it must
//! ensure that the \e pvBulkDevice structure passed to USBDBulkInit() resides
//! in RAM.  If this structure is in flash, callback pointer changes are not
//! possible.
//!
//! \return Returns the previous callback pointer that was being used for
//! this instance's transmit callback.
//
//*****************************************************************************
void *
USBDBulkSetTxCBData(void *pvBulkDevice, void *pvCBData)
{
    void *pvOldValue;

    ASSERT(pvBulkDevice);

    //
    // Set the callback pointer for the transmit channel after remembering the
    // previous value.
    //
    pvOldValue = ((tUSBDBulkDevice *)pvBulkDevice)->pvTxCBData;
    ((tUSBDBulkDevice *)pvBulkDevice)->pvTxCBData = pvCBData;

    //
    // Return the previous callback pointer value.
    //
    return(pvOldValue);
}

//*****************************************************************************
//
//! Transmits a packet of data to the USB host via the bulk data interface.
//!
//! \param pvBulkDevice is the pointer to the device instance structure as
//! returned by USBDBulkInit().
//! \param pi8Data points to the first byte of data which is to be transmitted.
//! \param ui32Length is the number of bytes of data to transmit.
//! \param bLast indicates whether more data is to be written before a packet
//! should be scheduled for transmission.  If \b true, the client will make
//! a further call to this function.  If \b false, no further call will be
//! made and the driver should schedule transmission of a short packet.
//!
//! This function schedules the supplied data for transmission to the USB
//! host in a single USB packet.  If no transmission is currently ongoing,
//! the data is immediately copied to the relevant USB endpoint FIFO for
//! transmission.  Whenever a USB packet is acknowledged by the host, a
//! \b USB_EVENT_TX_COMPLETE event will be sent to the transmit channel
//! callback indicating that more data can now be transmitted.
//!
//! The maximum value for \e ui32Length is 64 bytes (the maximum USB packet
//! size for the bulk endpoints in use by the device).  Attempts to send more
//! data than this will result in a return code of 0 indicating that the data
//! cannot be sent.
//!
//! The \e bLast parameter allows a client to make multiple calls to this
//! function before scheduling transmission of the packet to the host.  This
//! can be helpful if, for example, constructing a packet on the fly or
//! writing a packet which spans the wrap point in a ring buffer.
//!
//! \return Returns the number of bytes actually sent.  At this level, this
//! will either be the number of bytes passed (if less than or equal to the
//! maximum packet size for the USB endpoint in use and no outstanding
//! transmission ongoing) or 0 to indicate a failure.
//
//*****************************************************************************
uint32_t
USBDBulkPacketWrite(void *pvBulkDevice, uint8_t *pi8Data, uint32_t ui32Length,
                    bool bLast)
{
    tBulkInstance *psInst;
    int32_t i32Retcode;

    ASSERT(pvBulkDevice);

    //
    // Get a pointer to the bulk device instance data pointer
    //
    psInst = &((tUSBDBulkDevice *)pvBulkDevice)->sPrivateData;

    //
    // Can we send the data provided?
    //
    if((ui32Length > g_ui16MaxPacketSize) ||
       (psInst->iBulkTxState != eBulkStateIdle))
    {
        //
        // Either the packet was too big or we are in the middle of sending
        // another packet.  Return 0 to indicate that we can't send this data.
        //
        return(0);
    }

    //
    // Copy the data into the USB endpoint FIFO.
    //
    i32Retcode = MAP_USBEndpointDataPut(psInst->ui32USBBase,
                                        psInst->ui8INEndpoint,
                                        pi8Data, ui32Length);

    //
    // Did we copy the data successfully?
    //
    if(i32Retcode != -1)
    {
        //
        // Remember how many bytes we sent.
        //
        psInst->ui16LastTxSize += (uint16_t)ui32Length;

        //
        // If this is the last call for this packet, schedule transmission.
        //
        if(bLast)
        {
            //
            // Send the packet to the host if we have received all the data we
            // can expect for this packet.
            //
            psInst->iBulkTxState = eBulkStateWaitData;
            i32Retcode = MAP_USBEndpointDataSend(psInst->ui32USBBase,
                                                 psInst->ui8INEndpoint,
                                                 USB_TRANS_IN);
        }
    }

    //
    // Did an error occur while trying to send the data?
    //
    if(i32Retcode != -1)
    {
        //
        // No - tell the caller we sent all the bytes provided.
        //
        return(ui32Length);
    }
    else
    {
        //
        // Yes - tell the caller we could not send the data.
        //
        return(0);
    }
}

//*****************************************************************************
//
//! Reads a packet of data received from the USB host via the bulk data
//! interface.
//!
//! \param pvBulkDevice is the pointer to the device instance structure as
//! returned by USBDBulkInit().
//! \param pi8Data points to a buffer into which the received data will be
//! written.
//! \param ui32Length is the size of the buffer pointed to by pi8Data.
//! \param bLast indicates whether the client will make a further call to
//! read additional data from the packet.
//!
//! This function reads up to \e ui32Length bytes of data received from the USB
//! host into the supplied application buffer.  If the driver detects that the
//! entire packet has been read, it is acknowledged to the host.
//!
//! The \e bLast parameter is ignored in this implementation since the end of
//! a packet can be determined without relying upon the client to provide
//! this information.
//!
//! \return Returns the number of bytes of data read.
//
//*****************************************************************************
uint32_t
USBDBulkPacketRead(void *pvBulkDevice, uint8_t *pi8Data, uint32_t ui32Length,
                   bool bLast)
{
    uint32_t ui32EPStatus, ui32Count, ui32Pkt;
    tBulkInstance *psInst;
    int32_t i32Retcode;

    ASSERT(pvBulkDevice);

    //
    // Get our instance data pointer
    //
    psInst = &((tUSBDBulkDevice *)pvBulkDevice)->sPrivateData;

    //
    // Does the relevant endpoint FIFO have a packet waiting for us?
    //
    ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase,
                                         psInst->ui8OUTEndpoint);

    if(ui32EPStatus & USB_DEV_RX_PKT_RDY)
    {
        //
        // How many bytes are available for us to receive?
        //
        ui32Pkt = MAP_USBEndpointDataAvail(psInst->ui32USBBase,
                                           psInst->ui8OUTEndpoint);

        //
        // Get as much data as we can.
        //
        ui32Count = ui32Length;
        i32Retcode = MAP_USBEndpointDataGet(psInst->ui32USBBase,
                                            psInst->ui8OUTEndpoint,
                                            pi8Data, &ui32Count);

        //
        // Did we read the last of the packet data?
        //
        if(ui32Count == ui32Pkt)
        {
            //
            // Clear the endpoint status so that we know no packet is
            // waiting.
            //
            MAP_USBDevEndpointStatusClear(psInst->ui32USBBase,
                                          psInst->ui8OUTEndpoint,
                                          ui32EPStatus);

            //
            // Acknowledge the data, thus freeing the host to send the
            // next packet.
            //
            MAP_USBDevEndpointDataAck(psInst->ui32USBBase,
                                      psInst->ui8OUTEndpoint, true);

            //
            // Clear the flag we set to indicate that a packet read is
            // pending.
            //
            SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, BULK_DO_PACKET_RX,
                              false);
        }

        //
        // If all went well, tell the caller how many bytes they got.
        //
        if(i32Retcode != -1)
        {
            return(ui32Count);
        }
    }

    //
    // No packet was available or an error occurred while reading so tell
    // the caller no bytes were returned.
    //
    return(0);
}

//*****************************************************************************
//
//! Returns the number of free bytes in the transmit buffer.
//!
//! \param pvBulkDevice is the pointer to the device instance structure as
//! returned by USBDBulkInit().
//!
//! This function returns the maximum number of bytes that can be passed on a
//! call to USBDBulkPacketWrite() and accepted for transmission.  The value
//! returned will be the maximum USB packet size (64) if no transmission is
//! currently outstanding or 0 if a transmission is in progress.
//!
//! \return Returns the number of bytes available in the transmit buffer.
//
//*****************************************************************************
uint32_t
USBDBulkTxPacketAvailable(void *pvBulkDevice)
{
    tBulkInstance *psInst;

    ASSERT(pvBulkDevice);

    //
    // Get our instance data pointer.
    //
    psInst = &((tUSBDBulkDevice *)pvBulkDevice)->sPrivateData;

    //
    // Do we have a packet transmission currently ongoing?
    //
    if(psInst->iBulkTxState != eBulkStateIdle)
    {
        //
        // We are not ready to receive a new packet so return 0.
        //
        return(0);
    }
    else
    {
        //
        // We can receive a packet so return the max packet size for the
        // relevant endpoint.
        //
        return(g_ui16MaxPacketSize);
    }
}

//*****************************************************************************
//
//! Determines whether a packet is available and, if so, the size of the
//! buffer required to read it.
//!
//! \param pvBulkDevice is the pointer to the device instance structure as
//! returned by USBDBulkInit().
//!
//! This function may be used to determine if a received packet remains to be
//! read and allows the application to determine the buffer size needed to
//! read the data.
//!
//! \return Returns 0 if no received packet remains unprocessed or the
//! size of the packet if a packet is waiting to be read.
//
//*****************************************************************************
uint32_t
USBDBulkRxPacketAvailable(void *pvBulkDevice)
{
    uint32_t ui32EPStatus, ui32Size;
    tBulkInstance *psInst;

    ASSERT(pvBulkDevice);

    //
    // Get a pointer to the bulk device instance data pointer
    //
    psInst = &((tUSBDBulkDevice *)pvBulkDevice)->sPrivateData;

    //
    // Does the relevant endpoint FIFO have a packet waiting for us?
    //
    ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase,
                                         psInst->ui8OUTEndpoint);

    if(ui32EPStatus & USB_DEV_RX_PKT_RDY)
    {
        //
        // Yes - a packet is waiting.  How big is it?
        //
        ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase,
                                            psInst->ui8OUTEndpoint);

        return(ui32Size);
    }
    else
    {
        //
        // There is no packet waiting to be received.
        //
        return(0);
    }
}
#ifndef DEPRECATED

//*****************************************************************************
//
//! Reports the device power status (bus- or self-powered) to the USB library.
//!
//! \param pvBulkDevice is the pointer to the bulk device instance structure.
//! \param ui8Power indicates the current power status, either
//! \b USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR.
//!
//! Applications which support switching between bus- or self-powered
//! operation should call this function whenever the power source changes
//! to indicate the current power status to the USB library.  This information
//! is required by the USB library to allow correct responses to be provided
//! when the host requests status from the device.
//!
//! \return None.
//
//*****************************************************************************
void
USBDBulkPowerStatusSet(void *pvBulkDevice, uint8_t ui8Power)
{
    ASSERT(pvBulkDevice);

    //
    // Pass the request through to the lower layer.
    //
    USBDCDPowerStatusSet(0, ui8Power);
}
#endif

//*****************************************************************************
//
//! Requests a remote wake up to resume communication when in suspended state.
//!
//! \param pvBulkDevice is the pointer to the bulk device instance structure.
//!
//! When the bus is suspended, an application which supports remote wake up
//! (advertised to the host via the configuration descriptor) may call this
//! function to initiate remote wake up signaling to the host.  If the remote
//! wake up feature has not been disabled by the host, this will cause the bus
//! to resume operation within 20mS.  If the host has disabled remote wake up,
//! \b false will be returned to indicate that the wake up request was not
//! successful.
//!
//! \return Returns \b true if the remote wake up is not disabled and the
//! signaling was started or \b false if remote wake up is disabled or if
//! signaling is currently ongoing following a previous call to this function.
//
//*****************************************************************************
bool
USBDBulkRemoteWakeupRequest(void *pvBulkDevice)
{
    ASSERT(pvBulkDevice);

    //
    // Pass the request through to the lower layer.
    //
    return(USBDCDRemoteWakeupRequest(0));
}

//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************
