openblt/Target/Demo/ARMCM4_TM4C_DK_TM4C123G_IAR/Boot/lib/usblib/device/usbddfu-rt.c

662 lines
21 KiB
C

//*****************************************************************************
//
// usbddfu-rt.c - USB Device Firmware Update runtime device class driver.
//
// Copyright (c) 2010-2013 Texas Instruments Incorporated. All rights reserved.
// Software License Agreement
//
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
//
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
//
// This is part of revision 1.1 of the Tiva USB Library.
//
//*****************************************************************************
#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_nvic.h"
#include "driverlib/debug.h"
#include "driverlib/usbdrv.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/interrupt.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "usblib/usblib.h"
#include "usblib/usblibpriv.h"
#include "usblib/usbdfu.h"
#include "usblib/usb-ids.h"
#include "usblib/device/usbdevice.h"
#include "usblib/device/usbddfu-rt.h"
#include "usblib/usblibpriv.h"
//*****************************************************************************
//
//! \addtogroup dfu_device_class_api
//! @{
//
//*****************************************************************************
//*****************************************************************************
//
// DFU Device Descriptor. This is a dummy structure since runtime DFU must be
// a part of a composite device and cannot be instantiated on its own.
//
//*****************************************************************************
const uint8_t g_pui8DFUDeviceDescriptor[] =
{
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(0), // Device Release Number BCD.
0, // Manufacturer string identifier.
0, // Product string identifier.
0, // Product serial number.
1 // Number of configurations.
};
//*****************************************************************************
//
// DFU device runtime configuration descriptor. This is also a dummy structure
// since the primary device class configuration will be used when DFU is added
// to the composite device.
//
//*****************************************************************************
uint8_t g_pui8DFUConfigDescriptor[] =
{
//
// Configuration descriptor header.
//
9, // Size of the configuration descriptor.
USB_DTYPE_CONFIGURATION, // Type of this descriptor.
USBShort(27), // The total size of this full structure.
1, // The number of interfaces in this
// configuration.
1, // The unique value for this configuration.
0, // 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 DFU runtime interface descriptor.
//
//*****************************************************************************
uint8_t g_pui8DFUInterface[DFUINTERFACE_SIZE] =
{
//
// Interface descriptor for runtime DFU operation.
//
9, // Length of this descriptor.
USB_DTYPE_INTERFACE, // This is an interface descriptor.
0, // Interface number .
0, // Alternate setting number.
0, // Number of endpoints (only endpoint 0
// used)
USB_CLASS_APP_SPECIFIC, // Application specific interface class
USB_DFU_SUBCLASS, // Device Firmware Upgrade subclass
USB_DFU_RUNTIME_PROTOCOL, // DFU runtime protocol
0, // No string descriptor for this interface.
};
//*****************************************************************************
//
// The DFU functional descriptor.
//
//*****************************************************************************
uint8_t g_pui8DFUFunctionalDesc[DFUFUNCTIONALDESC_SIZE] =
{
//
// Device Firmware Upgrade functional descriptor.
//
9, // Length of this descriptor.
USB_DFU_FUNC_DESCRIPTOR_TYPE, // DFU Functional descriptor type
(DFU_ATTR_CAN_DOWNLOAD | // DFU attributes.
DFU_ATTR_CAN_UPLOAD |
DFU_ATTR_WILL_DETACH |
DFU_ATTR_MANIFEST_TOLERANT),
USBShort(0xFFFF), // Detach timeout (set to maximum).
USBShort(DFU_TRANSFER_SIZE), // Transfer size 1KB.
USBShort(0x0110) // DFU Version 1.1
};
//*****************************************************************************
//
// The DFU runtime configuration descriptor is defined as two sections.
// These sections are:
//
// 1. The 9 byte configuration descriptor.
// 2. The interface descriptor + DFU functional descriptor.
//
//*****************************************************************************
const tConfigSection g_sDFUConfigSection =
{
sizeof(g_pui8DFUConfigDescriptor),
g_pui8DFUConfigDescriptor
};
const tConfigSection g_sDFUInterfaceSection =
{
sizeof(g_pui8DFUInterface),
g_pui8DFUInterface
};
const tConfigSection g_sDFUFunctionalDescSection =
{
sizeof(g_pui8DFUFunctionalDesc),
g_pui8DFUFunctionalDesc
};
//*****************************************************************************
//
// This array lists all the sections that must be concatenated to make a
// single, complete DFU runtime configuration descriptor.
//
//*****************************************************************************
const tConfigSection *g_psDFUSections[] =
{
&g_sDFUConfigSection,
&g_sDFUInterfaceSection,
&g_sDFUFunctionalDescSection
};
#define NUM_DFU_SECTIONS (sizeof(g_psDFUSections) / \
sizeof(g_psDFUSections[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.
//
//*****************************************************************************
tConfigHeader g_sDFUConfigHeader =
{
NUM_DFU_SECTIONS,
g_psDFUSections
};
//*****************************************************************************
//
// Configuration Descriptor.
//
//*****************************************************************************
const tConfigHeader * const g_ppsDFUConfigDescriptors[] =
{
&g_sDFUConfigHeader
};
//*****************************************************************************
//
// Forward references for device handler callbacks
//
//*****************************************************************************
static void HandleGetDescriptor(void *pvDFUInstance, tUSBRequest *psUSBRequest);
static void HandleRequest(void *pvDFUInstance, tUSBRequest *psUSBRequest);
static void HandleDevice(void *pvDFUInstance, uint32_t ui32Request,
void *pvRequestData);
//*****************************************************************************
//
// The device information structure for the USB DFU devices.
//
//*****************************************************************************
static const tCustomHandlers g_sDFUHandlers =
{
//
// GetDescriptor
//
HandleGetDescriptor,
//
// RequestHandler
//
HandleRequest,
//
// InterfaceChange
//
0,
//
// ConfigChange
//
0,
//
// DataReceived
//
0,
//
// DataSentCallback
//
0,
//
// ResetHandler
//
0,
//
// SuspendHandler
//
0,
//
//
//
// ResumeHandler
//
0,
//
// DisconnectHandler
//
0,
//
// EndpointHandler
//
0,
//
// Device handler.
//
HandleDevice,
};
//*****************************************************************************
//
// Device instance specific handler. This callback received notifications of
// events related to handling interface, endpoint and string identifiers when
// a device is part of a composite device. In this case, the only resource we
// need which may be renumbered is the DFU runtime interface.
//
//*****************************************************************************
static void
HandleDevice(void *pvDFUInstance, uint32_t ui32Request, void *pvRequestData)
{
tDFUInstance *psInst;
uint8_t *pui8Data;
//
// Get a pointer to the DFU device instance data pointer
//
psInst = &((tUSBDDFUDevice *)pvDFUInstance)->sPrivateData;
//
// Get a byte pointer to the data.
//
pui8Data = (uint8_t *)pvRequestData;
//
// Which request event have we been passed?
//
switch(ui32Request)
{
//
// This was an interface change event.
//
case USB_EVENT_COMP_IFACE_CHANGE:
{
//
// Save the change to the interface number.
//
psInst->ui8Interface = pui8Data[1];
break;
}
//
// We are not interested in any other event.
//
default:
{
break;
}
}
}
//*****************************************************************************
//
// This function is called by the USB device stack whenever a request for a
// non-standard descriptor is received.
//
// \param pvDFUInstance is the instance data for this request.
// \param psUSBRequest points to the request received.
//
// This call parses the provided request structure and determines which
// descriptor is being requested. Assuming the descriptor can be found, it is
// scheduled for transmission via endpoint zero. If the descriptor cannot be
// found, the endpoint is stalled to indicate an error to the host.
//
//*****************************************************************************
static void
HandleGetDescriptor(void *pvDFUInstance, tUSBRequest *psUSBRequest)
{
uint32_t ui32Size;
ASSERT(pvDFUInstance != 0);
//
// Which type of class descriptor are we being asked for? We only support
// 1 type - the DFU functional descriptor.
//
if(((psUSBRequest->wValue >> 8) == USB_DFU_FUNC_DESCRIPTOR_TYPE) &&
((psUSBRequest->wValue & 0xFF) == 0))
{
//
// If there is more data to send than the host requested then just
// send the requested amount of data.
//
if((uint16_t)g_pui8DFUFunctionalDesc[0] > psUSBRequest->wLength)
{
ui32Size = (uint32_t)psUSBRequest->wLength;
}
else
{
ui32Size = (uint32_t)g_pui8DFUFunctionalDesc[0];
}
//
// Send the data via endpoint 0.
//
USBDCDSendDataEP0(0, g_pui8DFUFunctionalDesc, ui32Size);
}
else
{
//
// This was an unknown or invalid request so stall.
//
USBDCDStallEP0(0);
}
}
//*****************************************************************************
//
// This function is called by the USB device stack whenever a non-standard
// request is received.
//
// \param pvDFUInstance is the instance data for this HID device.
// \param psUSBRequest points to the request received.
//
// This call parses the provided request structure. Assuming the request is
// understood, it is handled and any required response generated. If the
// request cannot be handled by this device class, endpoint zero is stalled to
// indicate an error to the host.
//
//*****************************************************************************
static void
HandleRequest(void *pvDFUInstance, tUSBRequest *psUSBRequest)
{
tDFUInstance *psInst;
tUSBDDFUDevice *psDevice;
ASSERT(pvDFUInstance != 0);
//
// Get a pointer to the DFU device structure
//
psDevice = pvDFUInstance;
//
// Get a pointer to the DFU device instance data pointer
//
psInst = &psDevice->sPrivateData;
//
// Make sure the request was for this interface.
//
if(psUSBRequest->wIndex != psInst->ui8Interface)
{
return;
}
//
// Determine the type of request.
//
switch(psUSBRequest->bRequest)
{
//
// We have been asked to detach. In this case, we call back to the
// application telling it to tidy up and re-enter the boot loader. We
// rely upon it doing this on our behalf since this must be done from a
// non-interrupt context and this call is most likely in interrupt
// context.
//
case USBD_DFU_REQUEST_DETACH:
{
//
// Tell the application it's time to reenter the boot loader.
//
psDevice->pfnCallback(psDevice->pvCBData, USBD_DFU_EVENT_DETACH,
0, (void *)0);
break;
}
//
// This request was not recognized so stall.
//
default:
{
USBDCDStallEP0(0);
break;
}
}
}
//*****************************************************************************
//
//! Initializes DFU device operation for a given USB controller.
//!
//! \param ui32Index is the index of the USB controller which is to be
//! initialized for DFU runtime device operation.
//! \param psDFUDevice points to a structure containing parameters customizing
//! the operation of the DFU device.
//! \param psCompEntry is the composite device entry to initialize when
//! creating a composite device.
//!
//! 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 instance value that should be
//! used with the remaining USB DFU APIs.
//
//*****************************************************************************
void *
USBDDFUCompositeInit(uint32_t ui32Index, tUSBDDFUDevice *psDFUDevice,
tCompositeEntry *psCompEntry)
{
tDFUInstance *psInst;
//
// Check parameter validity.
//
ASSERT(ui32Index == 0);
ASSERT(psDFUDevice);
ASSERT(psCompEntry != 0);
//
// Get a pointer to the DFU device instance data pointer
//
psInst = &psDFUDevice->sPrivateData;
//
// Initialize the composite entry that is used by the composite device
// class.
//
if(psCompEntry != 0)
{
psCompEntry->psDevInfo = &psInst->sDevInfo;
psCompEntry->pvInstance = (void *)psDFUDevice;
}
//
// Initialize the device information structure.
//
psInst->sDevInfo.psCallbacks = &g_sDFUHandlers;
psInst->sDevInfo.pui8DeviceDescriptor = g_pui8DFUDeviceDescriptor;
psInst->sDevInfo.ppsConfigDescriptors = g_ppsDFUConfigDescriptors;
psInst->sDevInfo.ppui8StringDescriptors = 0;
psInst->sDevInfo.ui32NumStringDescriptors = 0;
psInst->ui32USBBase = USB0_BASE;
psInst->bConnected = false;
psInst->ui8Interface = 0;
//
// Initialize the device info structure for the DFU device.
//
USBDCDDeviceInfoInit(0, &psInst->sDevInfo);
//
// Return the pointer to the instance indicating that everything went well.
//
return((void *)psDFUDevice);
}
//*****************************************************************************
//
//! Shuts down the DFU device.
//!
//! \param pvDFUInstance is the pointer to the device instance structure as
//! returned by USBDDFUCompositeInit().
//!
//! This function terminates DFU operation for the instance supplied and
//! removes the device from the USB bus.
//!
//! Following this call, the \e pvDFUInstance instance should not me used in
//! any other calls.
//!
//! \return None.
//
//*****************************************************************************
void
USBDDFUCompositeTerm(void *pvDFUInstance)
{
tDFUInstance *psInst;
ASSERT(pvDFUInstance);
//
// Get a pointer to our instance data.
//
psInst = &((tUSBDDFUDevice *)pvDFUInstance)->sPrivateData;
//
// Terminate the requested instance.
//
USBDCDTerm(0);
psInst->ui32USBBase = 0;
}
//*****************************************************************************
//
//! Removes the current USB device from the bus and transfers control to the
//! DFU boot loader.
//!
//! This function should be called from the application's main loop (i.e. not
//! in interrupt context) following a callback to the USB DFU callback function
//! notifying the application of a DETACH request from the host. The function
//! will prepare the system to switch to DFU mode and transfer control to the
//! boot loader in preparation for a firmware upgrade from the host.
//!
//! The application must ensure that it has completed all necessary shutdown
//! activities (saved any required data, etc.) before making this call since
//! the function will not return.
//!
//! \return This function does not return.
//
//*****************************************************************************
void
USBDDFUUpdateBegin(void)
{
//
// Terminate the USB device and take us off the bus.
//
USBDCDTerm(0);
//
// Disable all interrupts.
//
MAP_IntMasterDisable();
//
// We must make sure we turn off SysTick and its interrupt
// before entering the boot loader!
//
MAP_SysTickIntDisable();
MAP_SysTickDisable();
//
// Disable all processor interrupts. Instead of disabling them
// one at a time, a direct write to NVIC is done to disable all
// peripheral interrupts.
//
HWREG(NVIC_DIS0) = 0xffffffff;
HWREG(NVIC_DIS1) = 0xffffffff;
//
// Reset the USB peripheral
//
MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0);
MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_USB0);
MAP_SysCtlPeripheralDisable(SYSCTL_PERIPH_USB0);
//
// Wait for about a second.
//
MAP_SysCtlDelay(MAP_SysCtlClockGet() / 3);
//
// Re-enable interrupts at the NVIC level.
//
MAP_IntMasterEnable();
//
// Return control to the boot loader. This is a call to the SVC
// handler in the boot loader.
//
(*((void (*)(void))(*(uint32_t *)0x2c)))();
//
// Should never get here, but just in case.
//
while(1)
{
}
}
//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************