From 990090a4cc9070837d31e66b58d40f0c3d038741 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Sun, 16 Mar 2014 14:41:11 +0200 Subject: Add usblib and utils --- usblib/device/usbddfu-rt.c | 661 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 661 insertions(+) create mode 100644 usblib/device/usbddfu-rt.c (limited to 'usblib/device/usbddfu-rt.c') diff --git a/usblib/device/usbddfu-rt.c b/usblib/device/usbddfu-rt.c new file mode 100644 index 0000000..24adb26 --- /dev/null +++ b/usblib/device/usbddfu-rt.c @@ -0,0 +1,661 @@ +//***************************************************************************** +// +// usbddfu-rt.c - USB Device Firmware Update runtime device class driver. +// +// Copyright (c) 2010-2014 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.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "inc/hw_nvic.h" +#include "driverlib/debug.h" +#include "driverlib/usb.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. +//! @} +// +//***************************************************************************** -- cgit v1.3.1