//***************************************************************************** // // 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 #include #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. //! @} // //*****************************************************************************