This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

EK-TM4C1294XL: Unable to make the bootloader work for USB DFU mode using the internal oscillator of the microcontroller.

Part Number: EK-TM4C1294XL

Hi all,

I'm trying to use the bootloader provided by Texas Instruments with the example project boot_serial for the board EK-TM4C1294XL to work with the USB in DFU mode using the internal oscillator. I have used the example and made it work with the external oscillator using a crystal of 25 MHz. However when I'm trying to use the internal oscillator of 16MHz everything works but the operating system, in this case Windows 10, is unable to detect the device in DFU mode.  The device appears as Unknown USB Device (Device Descriptor Request Failed) in my operating system. I believe that it is a clock problem, that the USB is working with a wrong clock since it should run at 60MHz. So far I have updated in file bl_config.h, the macro #define CRYSTAL_FREQ to 16000000 Hz. In the bl_usb.c file, I have also updated the register SYSCTL_RSCLKCFG to work with the internal oscillator by using the following line of code: HWREG(SYSCTL_RSCLKCFG) = (SYSCTL_RSCLKCFG_PLLSRC_PIOSC |SYSCTL_RSCLKCFG_OSCSRC_PIOSC); Also in the file bl_usbfuncs.c I have changed the function USBBLInit to have the following line HWREG(USB0_BASE + USB_O_CC) = (USB_CC_CLKEN | (7 << USB_CC_CLKDIV_S)); I believe that this line of code divides the internal PLL that is working at 480 MHz by 8 giving a clock of 60 MHz. However even after all these changes I'm unable to make the bootloader work in USB DUF mode. Please find attached to this thread the files that I have mentioned. What am I doing wrong?

Also, I believe it is possible to used the internal oscillator to generate the clock for the USB. Please also find attached to this thread the image that is present in the datasheet of the microcontroller.

As you can see in the clock tree there is a path from PIOSC to the USB CLOCK signal.

Thank you very much for your help,

Best regards,

6763.bl_config.h

0576.bl_usbfuncs.c
//*****************************************************************************
//
// bl_usbfuncs.c - The subset of USB library functions required by the USB DFU
//                 boot loader.
//
// 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 Firmware Development Package.
//
//*****************************************************************************

#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "inc/hw_usb.h"
#include "inc/hw_sysctl.h"
#include "inc/hw_nvic.h"
#include "inc/hw_ints.h"
#include "inc/hw_gpio.h"
#include "bl_config.h"
#include "boot_loader/bl_usbfuncs.h"

//Define GPIO Pins
#define GPIO_PIN_0              0x00000001  // GPIO pin 0
#define GPIO_PIN_1              0x00000002  // GPIO pin 1
#define GPIO_PIN_2              0x00000004  // GPIO pin 2
#define GPIO_PIN_3              0x00000008  // GPIO pin 3
#define GPIO_PIN_4              0x00000010  // GPIO pin 4
#define GPIO_PIN_5              0x00000020  // GPIO pin 5
#define GPIO_PIN_6              0x00000040  // GPIO pin 6
#define GPIO_PIN_7              0x00000080  // GPIO pin 7

//*****************************************************************************
//
//! \addtogroup bl_usb_api
//! @{
//
//*****************************************************************************
#if defined(USB_ENABLE_UPDATE) || defined(DOXYGEN)

//*****************************************************************************
//
// A prototype for the function (in the startup code) for a predictable length
// delay.
//
//*****************************************************************************
extern void Delay(uint32_t ui32Count);

//*****************************************************************************
//
// Local functions prototypes.
//
//*****************************************************************************
static void USBDGetStatus(tUSBRequest *pUSBRequest);
static void USBDClearFeature(tUSBRequest *pUSBRequest);
static void USBDSetFeature(tUSBRequest *pUSBRequest);
static void USBDSetAddress(tUSBRequest *pUSBRequest);
static void USBDGetDescriptor(tUSBRequest *pUSBRequest);
static void USBDSetDescriptor(tUSBRequest *pUSBRequest);
static void USBDGetConfiguration(tUSBRequest *pUSBRequest);
static void USBDSetConfiguration(tUSBRequest *pUSBRequest);
static void USBDGetInterface(tUSBRequest *pUSBRequest);
static void USBDSetInterface(tUSBRequest *pUSBRequest);
static void USBDEP0StateTx(void);
static int32_t USBDStringIndexFromRequest(uint16_t ui16Lang,
                                          uint16_t ui16Index);

//*****************************************************************************
//
// This structure holds the full state for the device enumeration.
//
//*****************************************************************************
typedef struct
{
    //
    // The devices current address, this also has a change pending bit in the
    // MSB of this value specified by DEV_ADDR_PENDING.
    //
    volatile uint32_t ui32DevAddress;

    //
    // This holds the current active configuration for this device.
    //
    uint32_t ui32Configuration;

    //
    // This holds the current alternate interface for this device. We only have
    // 1 interface so only need to hold 1 setting.
    //
    uint8_t ui8AltSetting;

    //
    // This is the pointer to the current data being sent out or received
    // on endpoint zero.
    //
    uint8_t *pui8EP0Data;

    //
    // This is the number of bytes that remain to be sent from or received
    // into the g_sUSBDeviceState.pui8EP0Data data buffer.
    //
    volatile uint32_t ui32EP0DataRemain;

    //
    // The amount of data being sent/received due to a custom request.
    //
    uint32_t ui32OUTDataSize;

    //
    // Holds the current device status.
    //
    uint8_t ui8Status;

    //
    // This flag indicates whether or not remote wakeup signalling is in
    // progress.
    //
    bool bRemoteWakeup;

    //
    // During remote wakeup signalling, this counter is used to track the
    // number of milliseconds since the signalling was initiated.
    //
    uint8_t ui8RemoteWakeupCount;
}
tDeviceState;

//*****************************************************************************
//
// The states for endpoint zero during enumeration.
//
//*****************************************************************************
typedef enum
{
    //
    // The USB device is waiting on a request from the host controller on
    // endpoint zero.
    //
    USB_STATE_IDLE,

    //
    // The USB device is sending data back to the host due to an IN request.
    //
    USB_STATE_TX,

    //
    // The USB device is receiving data from the host due to an OUT
    // request from the host.
    //
    USB_STATE_RX,

    //
    // The USB device has completed the IN or OUT request and is now waiting
    // for the host to acknowledge the end of the IN/OUT transaction.  This
    // is the status phase for a USB control transaction.
    //
    USB_STATE_STATUS,

    //
    // This endpoint has signaled a stall condition and is waiting for the
    // stall to be acknowledged by the host controller.
    //
    USB_STATE_STALL
}
tEP0State;

//*****************************************************************************
//
// Define the max packet size for endpoint zero.
//
//*****************************************************************************
#define EP0_MAX_PACKET_SIZE     64

//*****************************************************************************
//
// This is a flag used with g_sUSBDeviceState.ui32DevAddress to indicate that a
// device address change is pending.
//
//*****************************************************************************
#define DEV_ADDR_PENDING        0x80000000

//*****************************************************************************
//
// This label defines the default configuration number to use after a bus
// reset.
//
//*****************************************************************************
#define DEFAULT_CONFIG_ID       1

//*****************************************************************************
//
// This label defines the number of milliseconds that the remote wakeup signal
// must remain asserted before removing it. Section 7.1.7.7 of the USB 2.0 spec
// states that "the remote wakeup device must hold the resume signaling for at
// least 1ms but for no more than 15ms" so 10mS seems a reasonable choice.
//
//*****************************************************************************
#define REMOTE_WAKEUP_PULSE_MS 10

//*****************************************************************************
//
// This label defines the number of milliseconds between the point where we
// assert the remote wakeup signal and calling the client back to tell it that
// bus operation has been resumed.  This value is based on the timings provided
// in section 7.1.7.7 of the USB 2.0 specification which indicates that the host
// (which takes over resume signalling when the device's initial signal is
// detected) must hold the resume signalling for at least 20mS.
//
//*****************************************************************************
#define REMOTE_WAKEUP_READY_MS 20

//*****************************************************************************
//
// The buffer for reading data coming into EP0
//
//*****************************************************************************
static uint8_t g_pui8DataBufferIn[EP0_MAX_PACKET_SIZE];

//*****************************************************************************
//
// This global holds the current state information for the USB device.
//
//*****************************************************************************
static volatile tDeviceState g_sUSBDeviceState;

//*****************************************************************************
//
// This global holds the current state of endpoint zero.
//
//*****************************************************************************
static volatile tEP0State g_eUSBDEP0State = USB_STATE_IDLE;

//*****************************************************************************
//
// Function table to handle standard requests.
//
//*****************************************************************************
static const tStdRequest g_ppfnUSBDStdRequests[] =
{
    USBDGetStatus,
    USBDClearFeature,
    0,
    USBDSetFeature,
    0,
    USBDSetAddress,
    USBDGetDescriptor,
    USBDSetDescriptor,
    USBDGetConfiguration,
    USBDSetConfiguration,
    USBDGetInterface,
    USBDSetInterface,
};

//*****************************************************************************
//
// Amount to shift the RX interrupt sources by in the flags used in the
// interrupt calls.
//
//*****************************************************************************
#define USB_INT_RX_SHIFT        8

//*****************************************************************************
//
// Amount to shift the status interrupt sources by in the flags used in the
// interrupt calls.
//
//*****************************************************************************
#define USB_INT_STATUS_SHIFT    24

//*****************************************************************************
//
// Amount to shift the RX endpoint status sources by in the flags used in the
// calls.
//
//*****************************************************************************
#define USB_RX_EPSTATUS_SHIFT   16

//*****************************************************************************
//
// Converts from an endpoint specifier to the offset of the endpoint's
// control/status registers.
//
//*****************************************************************************
#define EP_OFFSET(Endpoint)     (Endpoint - 0x10)

//*****************************************************************************
//
// Retrieves data from endpoint 0's FIFO.
//
// \param pui8Data is a pointer to the data area used to return the data from
// the FIFO.
// \param pui32Size is initially the size of the buffer passed into this call
// via the \e pui8Data parameter.  It will be set to the amount of data
// returned in the buffer.
//
// This function will return the data from the FIFO for endpoint 0.
// The \e pui32Size parameter should indicate the size of the buffer passed in
// the \e pui32Data parameter.  The data in the \e pui32Size parameter will be
// changed to match the amount of data returned in the \e pui8Data parameter.
// If a zero byte packet was received this call will not return a error but
// will instead just return a zero in the \e pui32Size parameter.  The only
// error case occurs when there is no data packet available.
//
// \return This call will return 0, or -1 if no packet was received.
//
//*****************************************************************************
int32_t
USBEndpoint0DataGet(uint8_t *pui8Data, uint32_t *pui32Size)
{
    uint32_t ui32ByteCount;

    //
    // Don't allow reading of data if the RxPktRdy bit is not set.
    //
    if((HWREGH(USB0_BASE + USB_O_CSRL0) & USB_CSRL0_RXRDY) == 0)
    {
        //
        // Can't read the data because none is available.
        //
        *pui32Size = 0;

        //
        // Return a failure since there is no data to read.
        //
        return(-1);
    }

    //
    // Get the byte count in the FIFO.
    //
    ui32ByteCount = HWREGH(USB0_BASE + USB_O_COUNT0 + USB_EP_0);

    //
    // Determine how many bytes we will actually copy.
    //
    ui32ByteCount = (ui32ByteCount < *pui32Size) ? ui32ByteCount : *pui32Size;

    //
    // Return the number of bytes we are going to read.
    //
    *pui32Size = ui32ByteCount;

    //
    // Read the data out of the FIFO.
    //
    for(; ui32ByteCount > 0; ui32ByteCount--)
    {
        //
        // Read a byte at a time from the FIFO.
        //
        *pui8Data++ = HWREGB(USB0_BASE + USB_O_FIFO0 + (USB_EP_0 >> 2));
    }

    //
    // Success.
    //
    return(0);
}

//*****************************************************************************
//
// Acknowledge that data was read from endpoint 0's FIFO.
//
// \param bIsLastPacket indicates if this is the last packet.
//
// This function acknowledges that the data was read from the endpoint 0's
// FIFO.  The \e bIsLastPacket parameter is set to a \b true value if this is
// the last in a series of data packets.  This call can be used if processing
// is required between reading the data and acknowledging that the data has
// been read.
//
// \return None.
//
//*****************************************************************************
void
USBDevEndpoint0DataAck(bool bIsLastPacket)
{
    //
    // Clear RxPktRdy, and optionally DataEnd, on endpoint zero.
    //
    HWREGB(USB0_BASE + USB_O_CSRL0) =
        USB_CSRL0_RXRDYC | (bIsLastPacket ? USB_CSRL0_DATAEND : 0);

}

//*****************************************************************************
//
// Puts data into endpoint 0's FIFO.
//
// \param pui8Data is a pointer to the data area used as the source for the
// data to put into the FIFO.
// \param ui32Size is the amount of data to put into the FIFO.
//
// This function will put the data from the \e pui8Data parameter into the FIFO
// for endpoint 0.  If a packet is already pending for transmission then
// this call will not put any of the data into the FIFO and will return -1.
//
// \return This call will return 0 on success, or -1 to indicate that the FIFO
// is in use and cannot be written.
//
//*****************************************************************************
int32_t
USBEndpoint0DataPut(uint8_t *pui8Data, uint32_t ui32Size)
{
    //
    // Don't allow transmit of data if the TxPktRdy bit is already set.
    //
    if(HWREGB(USB0_BASE + USB_O_CSRL0 + USB_EP_0) & USB_CSRL0_TXRDY)
    {
        return(-1);
    }

    //
    // Write the data to the FIFO.
    //
    for(; ui32Size > 0; ui32Size--)
    {
        HWREGB(USB0_BASE + USB_O_FIFO0 + (USB_EP_0 >> 2)) = *pui8Data++;
    }

    //
    // Success.
    //
    return(0);
}

//*****************************************************************************
//
// Starts the transfer of data from endpoint 0's FIFO.
//
// \param ui32TransType is set to indicate what type of data is being sent.
//
// This function will start the transfer of data from the FIFO for
// endpoint 0.  This is necessary if the \b USB_EP_AUTO_SET bit was not enabled
// for the endpoint.  Setting the \e ui32TransType parameter will allow the
// appropriate signaling on the USB bus for the type of transaction being
// requested.  The \e ui32TransType parameter should be one of the following:
//
// - USB_TRANS_OUT for OUT transaction on any endpoint in host mode.
// - USB_TRANS_IN for IN transaction on any endpoint in device mode.
// - USB_TRANS_IN_LAST for the last IN transactions on endpoint zero in a
//   sequence of IN transactions.
// - USB_TRANS_SETUP for setup transactions on endpoint zero.
// - USB_TRANS_STATUS for status results on endpoint zero.
//
// \return This call will return 0 on success, or -1 if a transmission is
// already in progress.
//
//*****************************************************************************
int32_t
USBEndpoint0DataSend(uint32_t ui32TransType)
{
    //
    // Don't allow transmit of data if the TxPktRdy bit is already set.
    //
    if(HWREGB(USB0_BASE + USB_O_CSRL0 + USB_EP_0) & USB_CSRL0_TXRDY)
    {
        return(-1);
    }

    //
    // Set TxPktRdy in order to send the data.
    //
    HWREGB(USB0_BASE + USB_O_CSRL0 + USB_EP_0) = ui32TransType & 0xff;

    //
    // Success.
    //
    return(0);
}

#if defined(USB_VBUS_CONFIG) || defined(USB_ID_CONFIG) || \
    defined(USB_DP_CONFIG) || defined(USB_DM_CONFIG) || defined(DOXYGEN)
//*****************************************************************************
//
//! Initialize the pins used by USB functions.
//!
//! This function configures the pins for USB functions depending on defines
//! from the bl_config.h file.
//!
//! \return None.
//
//*****************************************************************************
void
USBConfigurePins(void)
{
    //
    // Enable the clocks to the GPIOs.
    //
    HWREG(SYSCTL_RCGCGPIO) |=
        (0x0 |
#if defined(USB_VBUS_CONFIG)
         USB_VBUS_PERIPH |
#endif
#if defined(USB_ID_CONFIG)
         USB_ID_PERIPH |
#endif
#if defined(USB_DP_CONFIG)
         USB_DP_PERIPH |
#endif
#if defined(USB_DM_CONFIG)
         USB_DM_PERIPH
#endif
        );

    //
    // Wait for the Peripherals to be Ready before accessing the register
    // address space.
    //
    while((HWREG(SYSCTL_PRGPIO) &
          (0x0 |
#if defined(USB_VBUS_CONFIG)
           USB_VBUS_PERIPH |
#endif
#if defined(USB_ID_CONFIG)
           USB_ID_PERIPH |
#endif
#if defined(USB_DP_CONFIG)
           USB_DP_PERIPH |
#endif
#if defined(USB_DM_CONFIG)
           USB_DM_PERIPH
#endif
        )) !=
            (0x0 |
#if defined(USB_VBUS_CONFIG)
             USB_VBUS_PERIPH |
#endif
#if defined(USB_ID_CONFIG)
             USB_ID_PERIPH |
#endif
#if defined(USB_DP_CONFIG)
             USB_DP_PERIPH |
#endif
#if defined(USB_DM_CONFIG)
             USB_DM_PERIPH
#endif
            ));

    //
    // Setup the pins based on bl_config.h
    //
#if defined(USB_VBUS_CONFIG)
    //
    // Set the VBUS pin to be an analog input.
    //
    HWREG(USB_VBUS_PORT + GPIO_O_DIR) &= ~(1 << USB_VBUS_PIN);
    HWREG(USB_VBUS_PORT + GPIO_O_AMSEL) |= (1 << USB_VBUS_PIN);
#endif

#if defined(USB_ID_CONFIG)
    //
    // Set the ID pin to be an analog input.
    //
    HWREG(USB_ID_PORT + GPIO_O_DIR) &= ~(1 << USB_ID_PIN);
    HWREG(USB_ID_PORT + GPIO_O_AMSEL) |= (1 << USB_ID_PIN);
#endif

#if defined(USB_DP_CONFIG)
    //
    // Set the DP pin to be an analog input.
    //
    HWREG(USB_DP_PORT + GPIO_O_DIR) &= ~(1 << USB_DP_PIN);
    HWREG(USB_DP_PORT + GPIO_O_AMSEL) |= (1 << USB_DP_PIN);
#endif

#if defined(USB_DM_CONFIG)
    //
    // Set the DM pin to be an analog input.
    //
    HWREG(USB_DM_PORT + GPIO_O_DIR) &= ~(1 << USB_DM_PIN);
    HWREG(USB_DM_PORT + GPIO_O_AMSEL) |= (1 << USB_DM_PIN);
#endif

#if defined(TAGAIO_TESTE)
    volatile unsigned char ucToggleCount, ucDelayCount;
    // Enable GPIO port B
    HWREG(SYSCTL_RCGCGPIO) |= SYSCTL_RCGCGPIO_R1;
    while((!HWREG(SYSCTL_PRGPIO) & SYSCTL_PRGPIO_R1));

    // Enable GPIO port N
    HWREG(SYSCTL_RCGCGPIO) |= SYSCTL_RCGCGPIO_R12;
    while((!HWREG(SYSCTL_PRGPIO) & SYSCTL_RCGCGPIO_R12));

    /// Turn on the digital enable for PB2
    HWREG(GPIO_PORTB_BASE + GPIO_O_DEN) |= 1 << 2;
    for(ucDelayCount=0; ucDelayCount<100; ucDelayCount++)
    {
    }
    HWREG(GPIO_PORTB_BASE + GPIO_O_DIR) |= (1 << 2);
    for(ucDelayCount=0; ucDelayCount<100; ucDelayCount++)
    {
    }

    /// Turn on the digital enable for PN1
    HWREG(GPIO_PORTN_BASE + GPIO_O_DEN) |= 1 << 1;
    for(ucDelayCount=0; ucDelayCount<100; ucDelayCount++)
    {
    }
    HWREG(GPIO_PORTN_BASE + GPIO_O_DIR) |= (1 << 1);
    for(ucDelayCount=0; ucDelayCount<100; ucDelayCount++)
    {
    }

    HWREG(GPIO_PORTB_BASE+(GPIO_O_DATA + 0xFF<<2)) |= GPIO_PIN_2;
    HWREG(GPIO_PORTN_BASE+(GPIO_O_DATA + 0xFF<<2)) |= GPIO_PIN_1;


    //HWREG(GPIO_PORTB_BASE+(GPIO_O_DATA + 0xFF<<2)) &= ~GPIO_PIN_2);
    for(ucDelayCount=0; ucDelayCount<100; ucDelayCount++)
    {
    }
#endif
}
#endif

//*****************************************************************************
//
//! Initialize the boot loader USB functions.
//!
//! This function initializes the boot loader USB functions and places the DFU
//! device onto the USB bus.
//!
//! \return None.
//
//*****************************************************************************
void
USBBLInit(void)
{
    //
    // Configure the USB Pins based on the bl_config.h settings.
    //
#if defined(USB_VBUS_CONFIG) || defined(USB_ID_CONFIG) || \
    defined(USB_DP_CONFIG) || defined(USB_DM_CONFIG)
    USBConfigurePins();
#endif

    //
    // Initialize a couple of fields in the device state structure.
    //
    g_sUSBDeviceState.ui32Configuration = DEFAULT_CONFIG_ID;

    //
    // Enable the USB controller.
    //
    HWREG(SYSCTL_RCGCUSB) = SYSCTL_RCGCUSB_R0;

    //
    // Wait for the peripheral ready
    //
    while((HWREG(SYSCTL_PRUSB) & SYSCTL_PRUSB_R0) != SYSCTL_PRUSB_R0)
    {
    }

#if defined(TARGET_IS_TM4C129_RA0) ||                                         \
    defined(TARGET_IS_TM4C129_RA1) ||                                         \
    defined(TARGET_IS_TM4C129_RA2)
    //
    // Turn on USB Phy clock from PLL VCO
    //
    //HWREG(USB0_BASE + USB_O_CC) = (8 - 1) | USB_CC_CLKEN;

    HWREG(USB0_BASE + USB_O_CC) = (USB_CC_CLKEN | (3 << USB_CC_CLKDIV_S));
#else
    //
    // Turn on USB Phy clock.
    //
    HWREG(SYSCTL_RCC2) &= ~SYSCTL_RCC2_USBPWRDN;
#endif

    //
    // Clear any pending interrupts.
    //
    HWREGH(USB0_BASE + USB_O_TXIS);
    HWREGB(USB0_BASE + USB_O_IS);

    //
    // Enable USB Interrupts.
    //
    HWREGH(USB0_BASE + USB_O_TXIE) = USB_TXIS_EP0;
    HWREGB(USB0_BASE + USB_O_IE) = (USB_IS_DISCON | USB_IS_RESET);

    //
    // Default to the state where remote wakeup is disabled.
    //
    g_sUSBDeviceState.ui8Status = 0;
    g_sUSBDeviceState.bRemoteWakeup = false;

    //
    // Determine the self- or bus-powered state based on bl_config.h setting.
    //
#if USB_BUS_POWERED
    g_sUSBDeviceState.ui8Status &= ~USB_STATUS_SELF_PWR;
#else
    g_sUSBDeviceState.ui8Status |= USB_STATUS_SELF_PWR;
#endif

    //
    // Attach the device using the soft connect.
    //
    HWREGB(USB0_BASE + USB_O_POWER) |= USB_POWER_SOFTCONN;

    //
    // Enable the USB interrupt.
    //
    HWREG(NVIC_EN1) = 1 << (INT_USB0 - 48);
}

//*****************************************************************************
//
// This function starts the request for data from the host on endpoint zero.
//
// \param pui8Data is a pointer to the buffer to fill with data from the USB
// host.
// \param ui32Size is the size of the buffer or data to return from the USB
// host.
//
// This function handles retrieving data from the host when a custom command
// has been issued on endpoint zero.  When the requested data is received,
// the function HandleEP0Data() will be called.
//
// \return None.
//
//*****************************************************************************
void
USBBLRequestDataEP0(uint8_t *pui8Data, uint32_t ui32Size)
{
    //
    // Enter the RX state on end point 0.
    //
    g_eUSBDEP0State = USB_STATE_RX;

    //
    // Save the pointer to the data.
    //
    g_sUSBDeviceState.pui8EP0Data = pui8Data;

    //
    // Location to save the current number of bytes received.
    //
    g_sUSBDeviceState.ui32OUTDataSize = ui32Size;

    //
    // Bytes remaining to be received.
    //
    g_sUSBDeviceState.ui32EP0DataRemain = ui32Size;
}

//*****************************************************************************
//
//! This function requests transfer of data to the host on endpoint zero.
//!
//! \param pui8Data is a pointer to the buffer to send via endpoint zero.
//! \param ui32Size is the amount of data to send in bytes.
//!
//! This function handles sending data to the host when a custom command is
//! issued or non-standard descriptor has been requested on endpoint zero.
//!
//! \return None.
//
//*****************************************************************************
void
USBBLSendDataEP0(uint8_t *pui8Data, uint32_t ui32Size)
{
    //
    // Return the externally provided device descriptor.
    //
    g_sUSBDeviceState.pui8EP0Data = pui8Data;

    //
    // The size of the device descriptor is in the first byte.
    //
    g_sUSBDeviceState.ui32EP0DataRemain = ui32Size;

    //
    // Save the total size of the data sent.
    //
    g_sUSBDeviceState.ui32OUTDataSize = ui32Size;

    //
    // Now in the transmit data state.
    //
    USBDEP0StateTx();
}

//*****************************************************************************
//
//! This function generates a stall condition on endpoint zero.
//!
//! This function is typically called to signal an error condition to the host
//! when an unsupported request is received by the device.  It should be
//! called from within the callback itself (in interrupt context) and not
//! deferred until later since it affects the operation of the endpoint zero
//! state machine.
//!
//! \return None.
//
//*****************************************************************************
void
USBBLStallEP0(void)
{
    //
    // Perform a stall on endpoint zero.
    //
    HWREGB(USB0_BASE + USB_O_CSRL0) |= (USB_CSRL0_STALL | USB_CSRL0_RXRDYC);

    //
    // Enter the stalled state.
    //
    g_eUSBDEP0State = USB_STATE_STALL;
}

//*****************************************************************************
//
// This internal function reads a request data packet and dispatches it to
// either a standard request handler or the registered device request
// callback depending upon the request type.
//
// \return None.
//
//*****************************************************************************
static void
USBDReadAndDispatchRequest(void)
{
    uint32_t ui32Size;
    tUSBRequest *pRequest;

    //
    // Cast the buffer to a request structure.
    //
    pRequest = (tUSBRequest *)g_pui8DataBufferIn;

    //
    // Set the buffer size.
    //
    ui32Size = EP0_MAX_PACKET_SIZE;

    //
    // Get the data from the USB controller end point 0.
    //
    USBEndpoint0DataGet(g_pui8DataBufferIn, &ui32Size);

    if(!ui32Size)
    {
        return;
    }

    //
    // See if this is a standard request or not.
    //
    if((pRequest->bmRequestType & USB_RTYPE_TYPE_M) != USB_RTYPE_STANDARD)
    {
        //
        // Pass this non-standard request on to the DFU handler
        //
        HandleRequests(pRequest);
    }
    else
    {
        //
        // Assure that the jump table is not out of bounds.
        //
        if((pRequest->bRequest <
            (sizeof(g_ppfnUSBDStdRequests) / sizeof(tStdRequest))) &&
           (g_ppfnUSBDStdRequests[pRequest->bRequest] != 0))
        {
            //
            // Jump table to the appropriate handler.
            //
            g_ppfnUSBDStdRequests[pRequest->bRequest](pRequest);
        }
        else
        {
            //
            // If there is no handler then stall this request.
            //
            USBBLStallEP0();
        }
    }
}

//*****************************************************************************
//
// This is the low level interrupt handler for endpoint zero.
//
// This function handles all interrupts on endpoint zero in order to maintain
// the state needed for the control endpoint on endpoint zero.  In order to
// successfully enumerate and handle all USB standard requests, all requests
// on endpoint zero must pass through this function.  The endpoint has the
// following states: \b USB_STATE_IDLE, \b USB_STATE_TX, \b USB_STATE_RX,
// \b USB_STATE_STALL, and \b USB_STATE_STATUS.  In the \b USB_STATE_IDLE
// state the USB controller has not received the start of a request, and once
// it does receive the data for the request it will either enter the
// \b USB_STATE_TX, \b USB_STATE_RX, or \b USB_STATE_STALL depending on the
// command.  If the controller enters the \b USB_STATE_TX or \b USB_STATE_RX
// then once all data has been sent or received, it must pass through the
// \b USB_STATE_STATUS state to allow the host to acknowledge completion of
// the request.  The \b USB_STATE_STALL is entered from \b USB_STATE_IDLE in
// the event that the USB request was not valid.  Both the \b USB_STATE_STALL
// and \b USB_STATE_STATUS are transitional states that return to the
// \b USB_STATE_IDLE state.
//
// \return None.
//
// USB_STATE_IDLE -*--> USB_STATE_TX -*-> USB_STATE_STATUS -*->USB_STATE_IDLE
//                 |                  |                     |
//                 |--> USB_STATE_RX -                      |
//                 |                                        |
//                 |--> USB_STATE_STALL ---------->---------
//
//  ----------------------------------------------------------------
// | Current State       | State 0           | State 1              |
// | --------------------|-------------------|----------------------
// | USB_STATE_IDLE      | USB_STATE_TX/RX   | USB_STATE_STALL      |
// | USB_STATE_TX        | USB_STATE_STATUS  |                      |
// | USB_STATE_RX        | USB_STATE_STATUS  |                      |
// | USB_STATE_STATUS    | USB_STATE_IDLE    |                      |
// | USB_STATE_STALL     | USB_STATE_IDLE    |                      |
//  ----------------------------------------------------------------
//
//*****************************************************************************
void
USBDeviceEnumHandler(void)
{
    uint32_t ui32EPStatus;

    //
    // Get the TX portion of the endpoint status.
    //
    ui32EPStatus = HWREGH(USB0_BASE + EP_OFFSET(USB_EP_0) + USB_O_TXCSRL1);

    //
    // Get the RX portion of the endpoint status.
    //
    ui32EPStatus |=
        ((HWREGH(USB0_BASE + EP_OFFSET(USB_EP_0) + USB_O_RXCSRL1)) <<
         USB_RX_EPSTATUS_SHIFT);

    //
    // What state are we currently in?
    //
    switch(g_eUSBDEP0State)
    {
        //
        // Handle the status state, this is a transitory state from
        // USB_STATE_TX or USB_STATE_RX back to USB_STATE_IDLE.
        //
        case USB_STATE_STATUS:
        {
            //
            // Just go back to the idle state.
            //
            g_eUSBDEP0State = USB_STATE_IDLE;

            //
            // If there is a pending address change then set the address.
            //
            if(g_sUSBDeviceState.ui32DevAddress & DEV_ADDR_PENDING)
            {
                //
                // Clear the pending address change and set the address.
                //
                g_sUSBDeviceState.ui32DevAddress &= ~DEV_ADDR_PENDING;
                HWREGB(USB0_BASE + USB_O_FADDR) =
                    (uint8_t)g_sUSBDeviceState.ui32DevAddress;
            }

            //
            // If a new packet is already pending, we need to read it
            // and handle whatever request it contains.
            //
            if(ui32EPStatus & USB_DEV_EP0_OUT_PKTRDY)
            {
                //
                // Process the newly arrived packet.
                //
                USBDReadAndDispatchRequest();
            }
            break;
        }

        //
        // In the IDLE state the code is waiting to receive data from the host.
        //
        case USB_STATE_IDLE:
        {
            //
            // Is there a packet waiting for us?
            //
            if(ui32EPStatus & USB_DEV_EP0_OUT_PKTRDY)
            {
                //
                // Yes - process it.
                //
                USBDReadAndDispatchRequest();
            }
            break;
        }

        //
        // Data is still being sent to the host so handle this in the
        // EP0StateTx() function.
        //
        case USB_STATE_TX:
        {
            USBDEP0StateTx();
            break;
        }

        //
        // Handle the receive state for commands that are receiving data on
        // endpoint zero.
        //
        case USB_STATE_RX:
        {
            uint32_t ui32DataSize;

            //
            // Set the number of bytes to get out of this next packet.
            //
            if(g_sUSBDeviceState.ui32EP0DataRemain > EP0_MAX_PACKET_SIZE)
            {
                //
                // Don't send more than EP0_MAX_PACKET_SIZE bytes.
                //
                ui32DataSize = EP0_MAX_PACKET_SIZE;
            }
            else
            {
                //
                // There was space so send the remaining bytes.
                //
                ui32DataSize = g_sUSBDeviceState.ui32EP0DataRemain;
            }

            //
            // Get the data from the USB controller end point 0.
            //
            USBEndpoint0DataGet(g_sUSBDeviceState.pui8EP0Data, &ui32DataSize);

            //
            // If there we not more that EP0_MAX_PACKET_SIZE or more bytes
            // remaining then this transfer is complete.  If there were exactly
            // EP0_MAX_PACKET_SIZE remaining then there still needs to be
            // null packet sent before this is complete.
            //
            if(g_sUSBDeviceState.ui32EP0DataRemain < EP0_MAX_PACKET_SIZE)
            {
                //
                // Need to ack the data on end point 0 in this case
                // without setting data end.
                //
                USBDevEndpoint0DataAck(true);

                //
                // Return to the idle state.
                //
                g_eUSBDEP0State =  USB_STATE_IDLE;

                //
                // If there is a receive callback then call it.
                //
                if(g_sUSBDeviceState.ui32OUTDataSize != 0)
                {
                    //
                    // Call the receive handler to handle the data
                    // that was received.
                    //
                    HandleEP0Data(g_sUSBDeviceState.ui32OUTDataSize);

                    //
                    // Indicate that there is no longer any data being waited
                    // on.
                    //
                    g_sUSBDeviceState.ui32OUTDataSize = 0;
                }
            }
            else
            {
                //
                // Need to ack the data on end point 0 in this case
                // without setting data end.
                //
                USBDevEndpoint0DataAck(false);
            }

            //
            // Advance the pointer.
            //
            g_sUSBDeviceState.pui8EP0Data += ui32DataSize;

            //
            // Decrement the number of bytes that are being waited on.
            //
            g_sUSBDeviceState.ui32EP0DataRemain -= ui32DataSize;

            break;
        }
        //
        // The device stalled endpoint zero so check if the stall needs to be
        // cleared once it has been successfully sent.
        //
        case USB_STATE_STALL:
        {
            //
            // If we sent a stall then acknowledge this interrupt.
            //
            if(ui32EPStatus & USB_DEV_EP0_SENT_STALL)
            {
                //
                // Clear the stall condition.
                //
                HWREGB(USB0_BASE + USB_O_CSRL0) &= ~(USB_DEV_EP0_SENT_STALL);

                //
                // Reset the global end point 0 state to IDLE.
                //
                g_eUSBDEP0State = USB_STATE_IDLE;

            }
            break;
        }
        //
        // Halt on an unknown state, but only in DEBUG mode builds.
        //
        default:
        {
#ifdef DEBUG
            while(1);
#endif
            break;
        }
    }
}

//*****************************************************************************
//
// This function handles bus reset notifications.
//
// This function is called from the low level USB interrupt handler whenever
// a bus reset is detected.  It performs tidy-up as required and resets the
// configuration back to defaults in preparation for descriptor queries from
// the host.
//
// \return None.
//
//*****************************************************************************
void
USBDeviceEnumResetHandler(void)
{
    //
    // Disable remote wakeup signalling (as per USB 2.0 spec 9.1.1.6).
    //
    g_sUSBDeviceState.ui8Status &= ~USB_STATUS_REMOTE_WAKE;
    g_sUSBDeviceState.bRemoteWakeup = false;

    //
    // Call the device dependent code to indicate a bus reset has occurred.
    //
    HandleReset();

    //
    // Reset the default configuration identifier and alternate function
    // selections.
    //
    g_sUSBDeviceState.ui32Configuration = DEFAULT_CONFIG_ID;
    g_sUSBDeviceState.ui8AltSetting = 0;
}

//*****************************************************************************
//
// This function handles the GET_STATUS standard USB request.
//
// \param pUSBRequest holds the request type and endpoint number if endpoint
// status is requested.
//
// This function handles responses to a Get Status request from the host
// controller.  A status request can be for the device, an interface or an
// endpoint.  If any other type of request is made this function will cause
// a stall condition to indicate that the command is not supported.  The
// \e pUSBRequest structure holds the type of the request in the
// bmRequestType field.  If the type indicates that this is a request for an
// endpoint's status, then the wIndex field holds the endpoint number.
//
// \return None.
//
//*****************************************************************************
static void
USBDGetStatus(tUSBRequest *pUSBRequest)
{
    uint16_t ui16Data;

    //
    // Determine what type of status was requested.
    //
    switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M)
    {
        //
        // This was a Device Status request.
        //
        case USB_RTYPE_DEVICE:
        {
            //
            // Return the current status for the device.
            //
            ui16Data = g_sUSBDeviceState.ui8Status;

            break;
        }

        //
        // This was a Interface status request.
        //
        case USB_RTYPE_INTERFACE:
        {
            //
            // Interface status always returns 0.
            //
            ui16Data = 0;

            break;
        }

        //
        // This was an unknown request or a request for an endpoint (of which
        // we have none) so set a stall.
        //
        case USB_RTYPE_ENDPOINT:
        default:
        {
            //
            // Anything else causes a stall condition to indicate that the
            // command was not supported.
            //
            USBBLStallEP0();
            return;
        }
    }

    //
    // Send the two byte status response.
    //
    g_sUSBDeviceState.ui32EP0DataRemain = 2;
    g_sUSBDeviceState.pui8EP0Data = (uint8_t *)&ui16Data;

    //
    // Send the response.
    //
    USBDEP0StateTx();
}

//*****************************************************************************
//
// This function handles the CLEAR_FEATURE standard USB request.
//
// \param pUSBRequest holds the options for the Clear Feature USB request.
//
// This function handles device or endpoint clear feature requests.  The
// \e pUSBRequest structure holds the type of the request in the bmRequestType
// field and the feature is held in the wValue field.  For device, the only
// clearable feature is the Remote Wake feature.  This device request
// should only be made if the descriptor indicates that Remote Wake is
// implemented by the device.  For endpoint requests the only clearable
// feature is the ability to clear a halt on a given endpoint.  If any other
// requests are made, then the device will stall the request to indicate to
// the host that the command was not supported.
//
// \return None.
//
//*****************************************************************************
static void
USBDClearFeature(tUSBRequest *pUSBRequest)
{
    //
    // Determine what type of status was requested.
    //
    switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M)
    {
        //
        // This is a clear feature request at the device level.
        //
        case USB_RTYPE_DEVICE:
        {
            //
            // Only remote wake is clearable by this function.
            //
            if(USB_FEATURE_REMOTE_WAKE & pUSBRequest->wValue)
            {
                //
                // Clear the remote wake up state.
                //
                g_sUSBDeviceState.ui8Status &= ~USB_STATUS_REMOTE_WAKE;

                //
                // Need to ack the data on end point 0.
                //
                USBDevEndpoint0DataAck(true);
            }
            else
            {
                USBBLStallEP0();
            }
            break;
        }

        //
        // This is an unknown request or one destined for an invalid endpoint.
        //
        case USB_RTYPE_ENDPOINT:
        default:
        {
            USBBLStallEP0();
            return;
        }
    }
}

//*****************************************************************************
//
// This function handles the SET_FEATURE standard USB request.
//
// \param pUSBRequest holds the feature in the wValue field of the USB
// request.
//
// This function handles device or endpoint set feature requests.  The
// \e pUSBRequest structure holds the type of the request in the bmRequestType
// field and the feature is held in the wValue field.  For device, the only
// settable feature is the Remote Wake feature.  This device request
// should only be made if the descriptor indicates that Remote Wake is
// implemented by the device.  For endpoint requests the only settable feature
// is the ability to issue a halt on a given endpoint.  If any other requests
// are made, then the device will stall the request to indicate to the host
// that the command was not supported.
//
// \return None.
//
//*****************************************************************************
static void
USBDSetFeature(tUSBRequest *pUSBRequest)
{
    //
    // Determine what type of status was requested.
    //
    switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M)
    {
        //
        // This is a set feature request at the device level.
        //
        case USB_RTYPE_DEVICE:
        {
            //
            // Only remote wake is setable by this function.
            //
            if(USB_FEATURE_REMOTE_WAKE & pUSBRequest->wValue)
            {
                //
                // Set the remote wakeup state.
                //
                g_sUSBDeviceState.ui8Status |= USB_STATUS_REMOTE_WAKE;

                //
                // Need to ack the data on end point 0.
                //
                USBDevEndpoint0DataAck(true);
            }
            else
            {
                USBBLStallEP0();
            }
            break;
        }

        //
        // This is an unknown request or one destined for an invalid endpoint.
        //
        case USB_RTYPE_ENDPOINT:
        default:
        {
            USBBLStallEP0();
            return;
        }
    }
}

//*****************************************************************************
//
// This function handles the SET_ADDRESS standard USB request.
//
// \param pUSBRequest holds the new address to use in the wValue field of the
// USB request.
//
// This function is called to handle the change of address request from the
// host controller.  This can only start the sequence as the host must
// acknowledge that the device has changed address.  Thus this function sets
// the address change as pending until the status phase of the request has
// been completed successfully.  This prevents the devices address from
// changing and not properly responding to the status phase.
//
// \return None.
//
//*****************************************************************************
static void
USBDSetAddress(tUSBRequest *pUSBRequest)
{
    //
    // The data needs to be acknowledged on end point 0 without setting data
    // end because there is no data coming.
    //
    USBDevEndpoint0DataAck(true);

    //
    // Save the device address as we cannot change address until the status
    // phase is complete.
    //
    g_sUSBDeviceState.ui32DevAddress = pUSBRequest->wValue | DEV_ADDR_PENDING;

    //
    // Transition directly to the status state since there is no data phase
    // for this request.
    //
    g_eUSBDEP0State = USB_STATE_STATUS;

    //
    // Clear the DFU status just in case we were in an error state last time
    // the device was accessed and we were unplugged and replugged (for a self-
    // powered implementation, of course).
    //
    HandleSetAddress();
}

//*****************************************************************************
//
// This function handles the GET_DESCRIPTOR standard USB request.
//
// \param pUSBRequest holds the data for this request.
//
// This function will return all configured standard USB descriptors to the
// host - device, config and string descriptors.  Any request for a descriptor
// which is not available will result in endpoint 0 being stalled.
//
// \return None.
//
//*****************************************************************************
static void
USBDGetDescriptor(tUSBRequest *pUSBRequest)
{
    uint32_t ui32Stall;

    //
    // Default to no stall.
    //
    ui32Stall = 0;

    //
    // Which descriptor are we being asked for?
    //
    switch(pUSBRequest->wValue >> 8)
    {
        //
        // This request was for a device descriptor.
        //
        case USB_DTYPE_DEVICE:
        {
            //
            // Return the externally provided device descriptor.
            //
            g_sUSBDeviceState.pui8EP0Data =
                (uint8_t *)g_pui8DFUDeviceDescriptor;

            //
            // The size of the device descriptor is in the first byte.
            //
            g_sUSBDeviceState.ui32EP0DataRemain =
                g_pui8DFUDeviceDescriptor[0];
            break;
        }

        //
        // This request was for a configuration descriptor.
        //
        case USB_DTYPE_CONFIGURATION:
        {
            uint8_t ui8Index;

            //
            // Which configuration are we being asked for?
            //
            ui8Index = (uint8_t)(pUSBRequest->wValue & 0xFF);

            //
            // Is this valid?
            //
            if(ui8Index != 0)
            {
                //
                // This is an invalid configuration index.  Stall EP0 to
                // indicate a request error.
                //
                USBBLStallEP0();
                g_sUSBDeviceState.pui8EP0Data = 0;
                g_sUSBDeviceState.ui32EP0DataRemain = 0;
            }
            else
            {
                //
                // Start by sending data from the beginning of the first
                // descriptor.
                //

                g_sUSBDeviceState.pui8EP0Data =
                    (uint8_t *)g_pui8DFUConfigDescriptor;

                //
                // Get the size of the config descriptor (remembering that in
                // this case, we only have a single section)
                //
                g_sUSBDeviceState.ui32EP0DataRemain =
                    *(uint16_t *)&(g_pui8DFUConfigDescriptor[2]);
            }
            break;
        }

        //
        // This request was for a string descriptor.
        //
        case USB_DTYPE_STRING:
        {
            int32_t i32Index;

            //
            // Determine the correct descriptor index based on the requested
            // language ID and index.
            //
            i32Index = USBDStringIndexFromRequest(pUSBRequest->wIndex,
                                                  pUSBRequest->wValue & 0xFF);

            //
            // If the mapping function returned -1 then stall the request to
            // indicate that the request was not valid.
            //
            if(i32Index == -1)
            {
                USBBLStallEP0();
                break;
            }

            //
            // Return the externally specified configuration descriptor.
            //
            g_sUSBDeviceState.pui8EP0Data =
                (uint8_t *)g_ppui8StringDescriptors[i32Index];

            //
            // The total size of a string descriptor is in byte 0.
            //
            g_sUSBDeviceState.ui32EP0DataRemain =
                g_ppui8StringDescriptors[i32Index][0];

            break;
        }

        //
        // Any other request is not handled by the default enumeration handler
        // so see if it needs to be passed on to another handler.
        //
        default:
        {
            //
            // All other requests are not handled.
            //
            USBBLStallEP0();
            ui32Stall = 1;
            break;
        }
    }

    //
    // If there was no stall, ACK the data and see if data needs to be sent.
    //
    if(ui32Stall == 0)
    {
        //
        // Need to ack the data on end point 0 in this case without
        // setting data end.
        //
        USBDevEndpoint0DataAck(false);

        //
        // If this request has data to send, then send it.
        //
        if(g_sUSBDeviceState.pui8EP0Data)
        {
            //
            // If there is more data to send than is requested then just
            // send the requested amount of data.
            //
            if(g_sUSBDeviceState.ui32EP0DataRemain > pUSBRequest->wLength)
            {
                g_sUSBDeviceState.ui32EP0DataRemain = pUSBRequest->wLength;
            }

            //
            // Now in the transmit data state.  Be careful to call the correct
            // function since we need to handle the config descriptor
            // differently from the others.
            //
            USBDEP0StateTx();
        }
    }
}

//*****************************************************************************
//
// This function determines which string descriptor to send to satisfy a
// request for a given index and language.
//
// \param ui16Lang is the requested string language ID.
// \param ui16Index is the requested string descriptor index.
//
// When a string descriptor is requested, the host provides a language ID and
// index to identify the string ("give me string number 5 in French").  This
// function maps these two parameters to an index within our device's string
// descriptor array which is arranged as multiple groups of strings with
// one group for each language advertised via string descriptor 0.
//
// We assume that there are an equal number of strings per language and
// that the first descriptor is the language descriptor and use this fact to
// perform the mapping.
//
// \return The index of the string descriptor to return or -1 if the string
// could not be found.
//
//*****************************************************************************
static int32_t
USBDStringIndexFromRequest(uint16_t ui16Lang, uint16_t ui16Index)
{
    tString0Descriptor *pLang;
    uint32_t ui32NumLangs;
    uint32_t ui32NumStringsPerLang;
    uint32_t ui32Loop;

    //
    // First look for the trivial case where descriptor 0 is being
    // requested.  This is the special case since descriptor 0 contains the
    // language codes supported by the device.
    //
    if(ui16Index == 0)
    {
        return(0);
    }

    //
    // How many languages does this device support?  This is determined by
    // looking at the length of the first descriptor in the string table,
    // subtracting 2 for the header and dividing by two (the size of each
    // language code).
    //
    ui32NumLangs = (g_ppui8StringDescriptors[0][0] - 2) / 2;

    //
    // We assume that the table includes the same number of strings for each
    // supported language.  We know the number of entries in the string table,
    // so how many are there for each language?  This may seem an odd way to
    // do this (why not just have the application tell us in the device info
    // structure?) but it's needed since we didn't want to change the API
    // after the first release which did not support multiple languages.
    //
    ui32NumStringsPerLang = ((NUM_STRING_DESCRIPTORS - 1) / ui32NumLangs);

    //
    // Just to be sure, make sure that the calculation indicates an equal
    // number of strings per language.  We expect the string table to contain
    // (1 + (strings_per_language * languages)) entries.
    //
    if((1 + (ui32NumStringsPerLang * ui32NumLangs)) != NUM_STRING_DESCRIPTORS)
    {
        return(-1);
    }

    //
    // Now determine which language we are looking for.  It is assumed that
    // the order of the groups of strings per language in the table is the
    // same as the order of the language IDs listed in the first descriptor.
    //
    pLang = (tString0Descriptor *)(g_ppui8StringDescriptors[0]);

    //
    // Look through the supported languages looking for the one we were asked
    // for.
    //
    for(ui32Loop = 0; ui32Loop < ui32NumLangs; ui32Loop++)
    {
        //
        // Have we found the requested language?
        //
        if(pLang->wLANGID[ui32Loop] == ui16Lang)
        {
            //
            // Yes - calculate the index of the descriptor to send.
            //
            return((ui32NumStringsPerLang * ui32Loop) + ui16Index);
        }
    }

    //
    // If we drop out of the loop, the requested language was not found so
    // return -1 to indicate the error.
    //
    return(-1);
}

//*****************************************************************************
//
// This function handles the SET_DESCRIPTOR standard USB request.
//
// \param pUSBRequest holds the data for this request.
//
// This function currently is not supported and will respond with a Stall
// to indicate that this command is not supported by the device.
//
// \return None.
//
//*****************************************************************************
static void
USBDSetDescriptor(tUSBRequest *pUSBRequest)
{
    //
    // This function is not handled by default.
    //
    USBBLStallEP0();
}

//*****************************************************************************
//
// This function handles the GET_CONFIGURATION standard USB request.
//
// \param pUSBRequest holds the data for this request.
//
// This function responds to a host request to return the current
// configuration of the USB device.  The function will send the configuration
// response to the host and return.  This value will either be 0 or the last
// value received from a call to SetConfiguration().
//
// \return None.
//
//*****************************************************************************
static void
USBDGetConfiguration(tUSBRequest *pUSBRequest)
{
    uint8_t ui8Value;

    //
    // If we still have an address pending then the device is still not
    // configured.
    //
    if(g_sUSBDeviceState.ui32DevAddress & DEV_ADDR_PENDING)
    {
        ui8Value = 0;
    }
    else
    {
        ui8Value = (uint8_t)g_sUSBDeviceState.ui32Configuration;
    }

    g_sUSBDeviceState.ui32EP0DataRemain = 1;
    g_sUSBDeviceState.pui8EP0Data = &ui8Value;

    //
    // Send the single byte response.
    //
    USBDEP0StateTx();
}

//*****************************************************************************
//
// This function handles the SET_CONFIGURATION standard USB request.
//
// \param pUSBRequest holds the data for this request.
//
// This function responds to a host request to change the current
// configuration of the USB device.  The actual configuration number is taken
// from the structure passed in via \e pUSBRequest.
//
// \return None.
//
//*****************************************************************************
static void
USBDSetConfiguration(tUSBRequest *pUSBRequest)
{
    //
    // Cannot set the configuration to one that does not exist so check the
    // enumeration structure to see how many valid configurations are present.
    //
    if(pUSBRequest->wValue > 1)
    {
        //
        // The passed configuration number is not valid.  Stall the endpoint to
        // signal the error to the host.
        //
        USBBLStallEP0();
    }
    else
    {
        //
        // Need to ack the data on end point 0.
        //
        USBDevEndpoint0DataAck(true);

        //
        // Save the configuration.
        //
        g_sUSBDeviceState.ui32Configuration = pUSBRequest->wValue;

        //
        // If passed a configuration other than 0 (which tells us that we are
        // not currently configured), configure the endpoints (other than EP0)
        // appropriately.
        //
        if(g_sUSBDeviceState.ui32Configuration)
        {
            //
            // Set the power state
            //
#if USB_BUS_POWERED
            g_sUSBDeviceState.ui8Status &= ~USB_STATUS_SELF_PWR;
#else
            g_sUSBDeviceState.ui8Status |= USB_STATUS_SELF_PWR;
#endif
        }

        //
        // Do whatever needs to be done as a result of the config change.
        //
        HandleConfigChange(g_sUSBDeviceState.ui32Configuration);
    }
}

//*****************************************************************************
//
// This function handles the GET_INTERFACE standard USB request.
//
// \param pUSBRequest holds the data for this request.
//
// This function is called when the host controller request the current
// interface that is in use by the device.  This simply returns the value set
// by the last call to SetInterface().
//
// \return None.
//
//*****************************************************************************
static void
USBDGetInterface(tUSBRequest *pUSBRequest)
{
    uint8_t ui8Value;

    //
    // If we still have an address pending then the device is still not
    // configured.
    //
    if(g_sUSBDeviceState.ui32DevAddress & DEV_ADDR_PENDING)
    {
        ui8Value = 0;
    }
    else
    {
        //
        // Is the interface number valid?
        //
        if(pUSBRequest->wIndex == 0)
        {
            //
            // Read the current alternate setting for the required interface.
            //
            ui8Value = g_sUSBDeviceState.ui8AltSetting;
        }
        else
        {
            //
            // An invalid interface number was specified.
            //
            USBBLStallEP0();
            return;
        }
    }

    //
    // Send the single byte response.
    //
    g_sUSBDeviceState.ui32EP0DataRemain = 1;
    g_sUSBDeviceState.pui8EP0Data = &ui8Value;

    //
    // Send the single byte response.
    //
    USBDEP0StateTx();
}

//*****************************************************************************
//
// This function handles the SET_INTERFACE standard USB request.
//
// \param pUSBRequest holds the data for this request.
//
// The DFU device supports a single interface with no alternate settings so
// this handler is hardcoded assuming this configuration.
//
// \return None.
//
//*****************************************************************************
static void
USBDSetInterface(tUSBRequest *pUSBRequest)
{
    if((pUSBRequest->wIndex == 0) && (pUSBRequest->wValue == 0))
    {
        //
        // We were passed a valid interface number so acknowledge the request.
        //
        USBDevEndpoint0DataAck(true);
    }
    else
    {
        //
        // The values passed were not valid so stall endpoint 0.
        //
        USBBLStallEP0();
    }
}

//*****************************************************************************
//
// This internal function handles sending data on endpoint zero.
//
// \return None.
//
//*****************************************************************************
static void
USBDEP0StateTx(void)
{
    uint32_t ui32NumBytes;
    uint8_t *pui8Data;

    //
    // In the TX state on endpoint zero.
    //
    g_eUSBDEP0State = USB_STATE_TX;

    //
    // Set the number of bytes to send this iteration.
    //
    ui32NumBytes = g_sUSBDeviceState.ui32EP0DataRemain;

    //
    // Limit individual transfers to 64 bytes.
    //
    if(ui32NumBytes > EP0_MAX_PACKET_SIZE)
    {
        ui32NumBytes = EP0_MAX_PACKET_SIZE;
    }

    //
    // Save the pointer so that it can be passed to the USBEndpointDataPut()
    // function.
    //
    pui8Data = g_sUSBDeviceState.pui8EP0Data;

    //
    // Advance the data pointer and counter to the next data to be sent.
    //
    g_sUSBDeviceState.ui32EP0DataRemain -= ui32NumBytes;
    g_sUSBDeviceState.pui8EP0Data += ui32NumBytes;

    //
    // Put the data in the correct FIFO.
    //
    USBEndpoint0DataPut(pui8Data, ui32NumBytes);

    //
    // If this is exactly 64 then don't set the last packet yet.
    //
    if(ui32NumBytes == EP0_MAX_PACKET_SIZE)
    {
        //
        // There is more data to send or exactly 64 bytes were sent, this
        // means that there is either more data coming or a null packet needs
        // to be sent to complete the transaction.
        //
        USBEndpoint0DataSend(USB_TRANS_IN);
    }
    else
    {
        //
        // Now go to the status state and wait for the transmit to complete.
        //
        g_eUSBDEP0State = USB_STATE_STATUS;

        //
        // Send the last bit of data.
        //
        USBEndpoint0DataSend(USB_TRANS_IN_LAST);
        g_sUSBDeviceState.ui32OUTDataSize = 0;
    }
}

//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************
#endif // USB_ENABLE_UPDATE
bl_usb.c

  • Hi,

      I have seen this problem before. However, you seem to suggest that if you use main oscillator then it works which is different from my understanding. Please make the below two changes and see if they work. 

    1. In the bl_usbfuncs.c file make the below change from 7 to 3 on line 619.

    2. In the bl_config.h file, change the FLASH_PAGE_SIZE from 0x400 to 0x4000. 

  • Hi Charles,

    Thank you for your reply.

    My FLASH_PAGE_SIZE is 0x00004000 so I didn't have to change that parameter in my code. However I have change in bl_usbfuncs.c the value of 7 to 3 making the line looking like this: HWREG(USB0_BASE + USB_O_CC) = (USB_CC_CLKEN | (3 << USB_CC_CLKDIV_S)); However it still doesn't work. Could you please explained this line? I'm struggling with the lack of documentation explaining the workings of the bootloader.

    Once again, thank you,

    Best regards

  • Hi,

      I did some experiments. First, with the mentioned changes of code suggested in my last reply, I load the program and run on the EK-TM4C1294XL LaunchPad. I use the dfuprog.exe to scan for DFU devices. It will see two of them. See below. The first device name is the Device Firmware Upgrade and the second one is the In-Circuit Debug Interface. The second one is the debug probe. These two are expected to be detected.

    To avoid upgrading firmware to the wrong device, I disconnect power to the ICDI (the Debug USB port) device. On the LaunchPad, I switch JP1 jumper to take power from OTG which is the USB device port. With this change, I only detect Device Firmware Upgrade which is expected since I remove power from the ICDI. 

    I use dfuprog.exe to program a simple blinky program to address at 0x20000.

    There is no hard requirement to program at this address 0x20000. Of course, the blinky program needs to built and linked to 0x20000 as well.  You can program to 0x4000 by changing the bl_config.h for the below two macros. 

  • However I have change in bl_usbfuncs.c the value of 7 to 3 making the line looking like this: HWREG(USB0_BASE + USB_O_CC) = (USB_CC_CLKEN | (3 << USB_CC_CLKDIV_S)); However it still doesn't work. Could you please explained this line? I'm struggling with the lack of documentation explaining the workings of the bootloader.

    The VCO was supposedly 480Mhz but due to an errata the VCO is actually 240Mhz. Therefore, you would need to divide by 4 as in (3 << USB_CC_CLKDIV_S) rather than divide by 8 as in (7 << USB_CC_CLKDIV_S).

    Here is the project I use. 4643.boot_usb_with_app_at_0x20000.zip

  • Hi Charles,

    Thank you for your reply and support.

    The project that you have attached to your last reply contains links so I’m unable to check the bl_usb.c and bl_usbfuncs.c. I have downloaded your project and I have run it on my machine and it is working. However it is using the external oscillator (MOSC) instead of the internal oscillator (PIOSC). What I’m trying to achieve is to use the internal oscillator (PIOSC). For that I have change the bl_usb.c file in order to set the source of the PLL to the PIOSC and disabled the external crystal oscillator, since I don’t need it. Please find attached to this reply my whole project. If possible, run it in your machine and see if the operating system detects the device in USB DUF mode.

    In my case, as I have previously said, when I use the external oscillator I able to make the bootloader work but when I change to the internal oscillator the operating system is not able to detect the device in DFU mode and it appears as Unknown USB Device (Device Descriptor Request Failed). It seems to me that I have a problem with the clock of the USB.

    Finally my final product board does not have a crystal since is using the internal oscillator for economy reasons.

    Best regards,

    boot_usb_piosc.zip

  • Thank you for your explanation.

  • Hi,

      I see you make quite some changes to bl_usb.c and bl_usbfuncs.c files. I have not much experience with DFU running from an internal PIOSC. You are probably the first one I have come across to use PIOSC for DFU mode. This is what I will suggest. 

      Run any simple example such as blinky and use the below statement to run SYSCLK at 120Mhz with PIOSC as the input source.

    ui32SysClock = SysCtlClockFreqSet(SYSCTL_OSC_INT | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_240,
    120000000);

      In CCS register window, look at the settings for registers SYSCTL_MOSCCTL, SYSCTL_RSCLKCFG, SYSCTL_PLLFREQ1,  and SYSCTL_PLLFREQ0 and SYSCTL_MEMTIM0. Compare the settings with the settings you modify in bl_usb.c. Are there any differences?