// // TIDFU.cpp : A library providing Device Firmware Upgrade function for use // with the TI TI USB boot loader. // // // Included Files // #include "stdafx.h" #include #include #include #include #include "tidfuprot.h" #include "tidfu.h" #include "dfu_guids.h" #include "tiusbdll.h" #ifdef _DEBUG #define new DEBUG_NEW #endif #define BLOCK_BUFFER_LENGTH 0x40000 //**************************************************************************** // // DFU Interface expected class and subclass. // //**************************************************************************** #define USB_CLASS_APP_SPECIFIC 0xFE #define USB_DFU_SUBCLASS 0x01 //**************************************************************************** // // Interface protocols for DFU mode and runtime mode. // //**************************************************************************** #define DFU_PROTOCOL 0x02 #define RUNTIME_PROTOCOL 0x01 //**************************************************************************** // // DFU functional descriptor type // //**************************************************************************** #define USB_DESC_TYPE_DFU 0x21 //**************************************************************************** // // Some standard USB request codes. // //**************************************************************************** #define GET_DESCRIPTOR 6 //**************************************************************************** // // The sizes, in bytes, of several standard USB descriptor structures. // //**************************************************************************** #define SIZE_DEVICE_DESCRIPTOR 18 #define SIZE_CONFIG_DESCRIPTOR 9 #define SIZE_STRING_DESC_HEADER 2 //**************************************************************************** // // Values used with GET_DESCRIPTOR to retrieve various descriptor types. // //**************************************************************************** #define DEVICE_DESCRIPTOR_VALUE 0x01 #define CONFIG_DESCRIPTOR_VALUE 0x02 #define STRING_DESCRIPTOR_VALUE 0x03 #define INTERFACE_DESCRIPTOR_VALUE 0x04 //**************************************************************************** // // Macros to extract various fields from the DFU functional descriptor. In // each case, parameter p is a pointer to the first byte (length byte) of the // descriptor. // //**************************************************************************** #define DFU_ATTRIBUTES(p) (*(((unsigned char *)(p)) + 2)) #define DFU_DETACHTIMEOUT(p) ((*(((unsigned char *)(p)) + 3)) + \ ((*(((unsigned char *)(p)) + 4)) << 8)) #define DFU_MAXTRANSFER(p) ((*(((unsigned char *)(p)) + 5)) + \ ((*(((unsigned char *)(p)) + 6)) << 8)) #define DFU_VERSION(p) ((*(((unsigned char *)(p)) + 7)) + \ ((*(((unsigned char *)(p)) + 8)) << 8)) //**************************************************************************** // // The instance structure hidden behind a tTIDFUHandle. // //**************************************************************************** typedef struct { TIUSB_HANDLE hUSB; bool bSupportsLMProtocol; bool bRuntimeMode; unsigned short usVID; unsigned short usPID; unsigned short usDevice; unsigned short usTransferSize; unsigned char ucDFUAttributes; unsigned short usInterface; unsigned short usLastFlashBlock; unsigned short usFirstFlashBlock; unsigned short usBlockNum; unsigned long ulClassInfo; unsigned long ulPartInfo; } tTIDFUDeviceState; //***************************************************************************** // // Structure mapping values from the device information ulPartInfo field to // TI LM3Sxxxx part numbers. // //***************************************************************************** typedef struct { unsigned char ucPartNo; unsigned long ulLM3SPart; char *pcPartString; } tPartNumMapping; tPartNumMapping g_sPartNumMap[] = { // // USB Host + Device parts // { 0x44, 0x3739, "lm3s3739" }, { 0x49, 0x3748, "lm3s3748" }, { 0x45, 0x3749, "lm3s3749" }, { 0x81, 0x5632, "lm3s5632" }, { 0x96, 0x5732, "lm3s5732" }, { 0x97, 0x5737, "lm3s5737" }, { 0xA0, 0x5739, "lm3s5739" }, { 0x99, 0x5747, "lm3s5747" }, { 0xA7, 0x5749, "lm3s5749" }, // // USB Device parts // { 0x41, 0x3026, "lm3s3j26" }, { 0x40, 0x3026, "lm3s3n26" }, { 0x3F, 0x3026, "lm3s3w26" }, { 0x3E, 0x3026, "lm3s3z26" }, { 0x09, 0x5031, "lm3s5k31" }, { 0x4A, 0x5036, "lm3s5k36" }, { 0x0A, 0x5031, "lm3s5p31" }, { 0x48, 0x5036, "lm3s5p36" }, { 0x07, 0x5031, "lm3s5r31" }, { 0x4B, 0x5036, "lm3s5r36" }, { 0x47, 0x5036, "lm3s5t36" }, { 0x46, 0x5036, "lm3s5y36" }, { 0x41, 0x3826, "lm3s3826" }, // // USB OTG parts // { 0x43, 0x3651, "lm3s3651" }, { 0x46, 0x3759, "lm3s3759" }, { 0x48, 0x3768, "lm3s3768" }, { 0x8A, 0x5652, "lm3s5652" }, { 0x91, 0x5662, "lm3s5662" }, { 0x9A, 0x5752, "lm3s5752" }, { 0x9B, 0x5757, "lm3s5757" }, { 0x9C, 0x5762, "lm3s5762" }, { 0x9D, 0x5767, "lm3s5767" }, { 0xA8, 0x5769, "lm3s5769" }, { 0xA9, 0x5768, "lm3s5768" }, { 0x68, 0x5091, "lm3s5b91" }, { 0x0D, 0x5051, "lm3s5p51" }, { 0x4C, 0x5056, "lm3s5p56" }, { 0x66, 0x9090, "lm3s9b90" }, { 0x6A, 0x9092, "lm3s9b92" }, { 0x6E, 0x9095, "lm3s9b95" }, { 0x6F, 0x9096, "lm3s9b96" }, // { 0x?? , 0x9097, "lm3s9l97" }, { 0x4D, 0x5656, "lm3s5656" }, { 0x69, 0x5791, "lm3s5791" }, { 0x0B, 0x5951, "lm3s5951" }, { 0x4E, 0x5956, "lm3s5956" } }; #define NUM_TI_PARTS (sizeof(g_sPartNumMap) / sizeof(tPartNumMapping)) //***************************************************************************** // // Structure mapping DFU status values to human readable strings for debug // purposes. // //***************************************************************************** typedef struct { tDFUStatus eStatus; char *pcStatus; } tStatusMapping; tStatusMapping g_psStatusMap[] = { {STATUS_OK, "STATUS_OK"}, {STATUS_ERR_TARGET, "STATUS_ERR_TARGET"}, {STATUS_ERR_FILE, "STATUS_ERR_FILE"}, {STATUS_ERR_WRITE, "STATUS_ERR_WRITE"}, {STATUS_ERR_ERASE, "STATUS_ERR_ERASE"}, {STATUS_ERR_CHECK_ERASED, "STATUS_ERR_CHECK_ERASED"}, {STATUS_ERR_PROG, "STATUS_ERR_PROG"}, {STATUS_ERR_VERIFY, "STATUS_ERR_VERIFY"}, {STATUS_ERR_ADDRESS, "STATUS_ERR_ADDRESS"}, {STATUS_ERR_NOTDONE, "STATUS_ERR_NOTDONE"}, {STATUS_ERR_FIRMWARE, "STATUS_ERR_FIRMWARE"}, {STATUS_ERR_VENDOR, "STATUS_ERR_VENDOR"}, {STATUS_ERR_USBR, "STATUS_ERR_USBR"}, {STATUS_ERR_POR, "STATUS_ERR_POR"}, {STATUS_ERR_UNKNOWN, "STATUS_ERR_UNKNOWN"}, {STATUS_ERR_STALLEDPKT, "STATUS_ERR_STALLEDPKT"} }; #define NUM_STATUS_MAP (sizeof(g_psStatusMap) / sizeof(tStatusMapping)) //***************************************************************************** // // Structure mapping DFU state values to human readable strings for debug // purposes. // //***************************************************************************** typedef struct { tDFUState eState; char *pcState; } tStateMapping; tStateMapping g_psStateMap[] = { {STATE_APP_IDLE, "APP_IDLE"}, {STATE_APP_DETACH, "APP_DETACH"}, {STATE_IDLE, "IDLE"}, {STATE_DNLOAD_SYNC, "DNLOAD_SYNC"}, {STATE_DNBUSY, "DNBUSY"}, {STATE_DNLOAD_IDLE, "DNLOAD_IDLE"}, {STATE_MANIFEST_SYNC, "MANIFEST_SYNC"}, {STATE_MANIFEST, "MANIFEST"}, {STATE_MANIFEST_WAIT_RESET, "MANIFEST_WAIT_RESET"}, {STATE_UPLOAD_IDLE, "UPLOAD_IDLE"}, {STATE_ERROR, "ERROR"}, }; #define NUM_STATE_MAP (sizeof(g_psStateMap) / sizeof(tStateMapping)) //***************************************************************************** // // Storage for the CRC32 calculation lookup table. // //***************************************************************************** unsigned long g_pulCRC32Table[256]; //***************************************************************************** // // Macros for accessing multi-byte fields in the DFU suffix and prefix. // //***************************************************************************** #define WRITE_LONG(num, ptr) \ { \ *((unsigned char *)(ptr)) = (unsigned char)((num) & 0xFF); \ *(((unsigned char *)(ptr)) + 1) = (unsigned char)(((num) >> 8) & 0xFF); \ *(((unsigned char *)(ptr)) + 2) = (unsigned char)(((num) >> 16) & 0xFF); \ *(((unsigned char *)(ptr)) + 3) = (unsigned char)(((num) >> 24) & 0xFF); \ } #define WRITE_SHORT(num, ptr) \ { \ *((unsigned char *)(ptr)) = (unsigned char)((num) & 0xFF); \ *(((unsigned char *)(ptr)) + 1) = (unsigned char)(((num) >> 8) & 0xFF); \ } #define READ_SHORT(ptr) \ (*((unsigned char *)(ptr)) | (*(((unsigned char *)(ptr)) + 1) << 8)) #define READ_LONG(ptr) \ (*((unsigned char *)(ptr)) | \ (*(((unsigned char *)(ptr)) + 1) << 8) | \ (*(((unsigned char *)(ptr)) + 2) << 16) | \ (*(((unsigned char *)(ptr)) + 3) << 24)) //***************************************************************************** // // Internal function prototypes. // //***************************************************************************** static tTIDFUErr DFUDownloadTransfer(tTIDFUDeviceState *pState, bool bCheckStatus, unsigned char *pcData, int iLength); static tTIDFUErr DFUUploadTransfer(tTIDFUDeviceState *pState, unsigned char *pcData, int iLength); //***************************************************************************** // // Initialize the CRC32 calculation table for the polynomial required by the // DFU specification. This code is based on an example found at // // http://www.createwindow.com/programming/crc32/index.htm. // //***************************************************************************** static unsigned long Reflect(unsigned long ulRef, char ucCh) { unsigned long ulValue; int iLoop; ulValue = 0; // // Swap bit 0 for bit 7, bit 1 for bit 6, etc. // for(iLoop = 1; iLoop < (ucCh + 1); iLoop++) { if(ulRef & 1) ulValue |= 1 << (ucCh - iLoop); ulRef >>= 1; } return ulValue; } static void InitCRC32Table() { unsigned long ulPolynomial; int i, j; // This is the ANSI X 3.66 polynomial as required by the DFU // specification. // ulPolynomial = 0x04c11db7; for(i = 0; i <= 0xFF; i++) { g_pulCRC32Table[i]=Reflect(i, 8) << 24; for (j = 0; j < 8; j++) { g_pulCRC32Table[i] = (g_pulCRC32Table[i] << 1) ^ (g_pulCRC32Table[i] & (1 << 31) ? ulPolynomial : 0); } g_pulCRC32Table[i] = Reflect(g_pulCRC32Table[i], 32); } } //***************************************************************************** // // Calculate the CRC for the supplied block of data. // //***************************************************************************** static unsigned long CalculateCRC32(unsigned char *pcData, unsigned long ulLength, unsigned long ulStart) { unsigned long ulCRC; unsigned long ulCount; unsigned char* pcBuffer; unsigned char ucChar; // // Initialize the CRC to the start value specified. // ulCRC = ulStart; // // Get a pointer to the start of the data and the number of bytes to // process. // pcBuffer = pcData; ulCount = ulLength; // // Perform the algorithm on each byte in the supplied buffer using the // lookup table values calculated in InitCRC32Table(). // while(ulCount--) { ucChar = *pcBuffer++; ulCRC = (ulCRC >> 8) ^ g_pulCRC32Table[(ulCRC & 0xFF) ^ ucChar]; } // Return the result. return (ulCRC); } //**************************************************************************** // // Maps a Windows system error code into the matching tLMDUErr return code. // // This function maps a Windows system error code into the set of values // that function calls to this DLL are speced to return. Callers can still // use GetLastError() to retrieve the Windows error codes but the original // version of this DLL (based on libusb-win32 rather than WinUSB/tiusbdll) // returned these values so we do this for backwards compatibility. // //**************************************************************************** static tTIDFUErr MapRetcode(ULONG ulWinErr) { switch(ulWinErr) { // // There was no error. // case ERROR_SUCCESS: return(DFU_OK); // // TODO: Add other mappings here as we determine what they are. // Windows defines about 16000 and doesn't document exactly which // may be returned from particular API calls :-( // // // All other unspecified errors. // default: return(DFU_ERR_UNKNOWN); } } //**************************************************************************** // // Gets the device descriptor for the device whose handle is passed. // // Returns a pointer to the descriptor if found or NULL otherwise. The client // must free the pointer by calling free() once it is finished using the data. // //**************************************************************************** static PUCHAR GetDeviceDescriptor(TIUSB_HANDLE hUSB) { PUCHAR pucDevice; USHORT usCount; BOOL bRetcode; // // Allocate the 18 bytes we need to hold the device descriptor. // pucDevice = (PUCHAR)malloc(SIZE_DEVICE_DESCRIPTOR); if(!pucDevice) { return(NULL); } // // Send a standard request to get the device descriptor. // bRetcode = Endpoint0Transfer(hUSB, (REQUEST_TRANSFER_IN | REQUEST_TYPE_STANDARD | REQUEST_RECIPIENT_DEVICE), GET_DESCRIPTOR, (DEVICE_DESCRIPTOR_VALUE << 8), 0, SIZE_DEVICE_DESCRIPTOR, pucDevice, &usCount); // // Sanity check the result. // if(bRetcode && (usCount == SIZE_DEVICE_DESCRIPTOR) && (pucDevice[0] == SIZE_DEVICE_DESCRIPTOR)) { // // We read the expected number of bytes and the descriptor tells us it // is the expected size so assume it's good and return it to the // caller. // return(pucDevice); } else { // // Either we got the wrong number of bytes back or the descriptor // seems to be indicating the wrong size. Either way, we consider this // an error. // free(pucDevice); return(NULL); } } //**************************************************************************** // // Gets the current configuration descriptor for the device whose handle is // passed. // // Returns a pointer to the descriptor if found or NULL otherwise. The client // must free the pointer by calling free() once it is finished using the data. // //**************************************************************************** static PUCHAR GetConfigDescriptor(TIUSB_HANDLE hUSB) { PUCHAR pucConfig; USHORT usCount; UCHAR pucCfgHeader[SIZE_CONFIG_DESCRIPTOR]; BOOL bRetcode; // // Send a standard request to get the header of the config descriptor. // bRetcode = Endpoint0Transfer(hUSB, (REQUEST_TRANSFER_IN | REQUEST_TYPE_STANDARD | REQUEST_RECIPIENT_DEVICE), GET_DESCRIPTOR, (CONFIG_DESCRIPTOR_VALUE << 8), 0, SIZE_CONFIG_DESCRIPTOR, pucCfgHeader, &usCount); // // Sanity check the result. // if(!bRetcode || (usCount != SIZE_CONFIG_DESCRIPTOR) || (pucCfgHeader[0] != SIZE_CONFIG_DESCRIPTOR)) { // // Either we got the wrong number of bytes back or the descriptor // seems to be indicating the wrong size. Either way, we consider this // an error and bomb out. // return(NULL); } // // Now that we have the header, allocate enough space for the whole // descriptor and get it. // pucConfig = (PUCHAR)malloc(READ_SHORT(pucCfgHeader + 2)); if(!pucConfig) { return(NULL); } // // Now ask for the whole configuration descriptor. // bRetcode = Endpoint0Transfer(hUSB, (REQUEST_TRANSFER_IN | REQUEST_TYPE_STANDARD | REQUEST_RECIPIENT_DEVICE), GET_DESCRIPTOR, (CONFIG_DESCRIPTOR_VALUE << 8), 0, READ_SHORT(pucCfgHeader + 2), pucConfig, &usCount); // // Sanity check the result once again. // if(!bRetcode || (usCount != READ_SHORT(pucCfgHeader + 2)) || (pucConfig[0] != SIZE_CONFIG_DESCRIPTOR) || (READ_SHORT(pucConfig + 2) != READ_SHORT(pucCfgHeader + 2))) { // // Either we got the wrong number of bytes back or the descriptor // seems to be indicating a wrong size. Either way, we consider this // an error and bomb out. // free(pucConfig); return(NULL); } else { // // Everything seems OK so return the descriptor we read. // return(pucConfig); } } //**************************************************************************** // // Gets a particular string descriptor for the device whose handle is passed. // // Returns a pointer to the descriptor if found or NULL otherwise. The client // must free the pointer by calling free() once it is finished using the data. // //**************************************************************************** static PUCHAR GetStringDescriptor(TIUSB_HANDLE hUSB, UCHAR ucStringIndex, USHORT usLanguageID) { PUCHAR pucString; USHORT usCount; UCHAR pucHeader[SIZE_STRING_DESC_HEADER]; BOOL bRetcode; // // Send a standard request to get the header of the config descriptor. // bRetcode = Endpoint0Transfer(hUSB, (REQUEST_TRANSFER_IN | REQUEST_TYPE_STANDARD | REQUEST_RECIPIENT_DEVICE), GET_DESCRIPTOR, ((STRING_DESCRIPTOR_VALUE << 8) | ucStringIndex), usLanguageID, SIZE_STRING_DESC_HEADER, pucHeader, &usCount); // // Sanity check the result. // if(!bRetcode || (usCount != SIZE_STRING_DESC_HEADER) || (pucHeader[0] < (SIZE_STRING_DESC_HEADER + 2))) { // // We got the wrong number of bytes back or the descriptor seems to be // indicating the wrong size. Either way, we consider this an error // and bomb out. // return(NULL); } // // Now that we have the header, allocate enough space for the whole // descriptor and get it. // pucString = (PUCHAR)malloc(*pucHeader); if(!pucString) { return(NULL); } // // Now ask for the whole string descriptor. // bRetcode = Endpoint0Transfer(hUSB, (REQUEST_TRANSFER_IN | REQUEST_TYPE_STANDARD | REQUEST_RECIPIENT_DEVICE), GET_DESCRIPTOR, ((STRING_DESCRIPTOR_VALUE << 8) | ucStringIndex), usLanguageID, (USHORT)pucHeader[0], pucString, &usCount); // // Sanity check the result once again. // if(!bRetcode || (usCount != (USHORT)pucHeader[0]) || (pucString[0] != (USHORT)pucHeader[0])) { // // Either we got the wrong number of bytes back or the descriptor // seems to be indicating a wrong size. Either way, we consider this // an error and bomb out. // free(pucString); return(NULL); } else { // // Everything seems OK so return the descriptor we read. // return(pucString); } } //**************************************************************************** // // Find the first descriptor with the given type in a block of descriptors. // // This function must be called before any other entry point in the library. // It initalizes global data required to access DFU devices. // // Returns a pointer to the descriptor if found or NULL otherwise. // //**************************************************************************** static unsigned char * FindDescriptor(unsigned char ucType, unsigned char *pcStart, unsigned long ulLength) { unsigned char *pcEnd; // // Weed out completely bogus data. A valid descriptor must be at least 2 // bytes long. // if(ulLength < 2) { return(NULL); } // // Where's the last place we could sensibly look? // pcEnd = pcStart + (ulLength - 1); // // Walk the list of descriptors looking for one of the specified type. // while(pcStart < pcEnd) { // // Did we find a descriptor with the requested type? // if(*(pcStart + 1) == ucType) { // // Yes - return its pointer. // return(pcStart); } else { // // No - skip over this descriptor by adding the length (byte 1) to // the current descriptor start pointer. // pcStart += *pcStart; } } // // We did not find the requested descriptor. // return(NULL); } //**************************************************************************** // // Populate a DFU file suffix structure with values appropriate for the // passed image data. Parameter pcData points to ulImageLen bytes of data // with and a 16 byte block of free space at the end into which the suffix is // written. // //**************************************************************************** static void DFUSuffixPopulate(tTIDFUDeviceState *pState, unsigned char *pcData, unsigned long ulImageLen) { unsigned long ulCRC; // // Fill in the "static" fields in the DFU suffix at the end of the // supplied data. // pcData[ulImageLen + 11] = 16; pcData[ulImageLen + 10] = 'D'; pcData[ulImageLen + 9] = 'F'; pcData[ulImageLen + 8] = 'U'; WRITE_SHORT(0x100, &(pcData[ulImageLen + 6])); WRITE_SHORT(pState->usVID, &(pcData[ulImageLen + 4])); WRITE_SHORT(pState->usPID, &(pcData[ulImageLen + 2])); WRITE_SHORT(pState->usDevice, &(pcData[ulImageLen])); // // Calculate the CRC of the whole block including the first 12 bytes of // the DFU suffix. // ulCRC = CalculateCRC32(pcData, ulImageLen + 12, 0xFFFFFFFF); // // Write the CRC to the final 4 bytes of the buffer. // WRITE_LONG(ulCRC, &(pcData[ulImageLen + 12])); } //**************************************************************************** // // Query the DFU device information structure (for TI-compatible // devices). // //**************************************************************************** static tTIDFUErr DFUDeviceInfoGet(tTIDFUDeviceState *pState, tDFUDeviceInfo *psDevInfo) { tDFUDownloadInfoHeader sInfoCmd; tTIDFUErr eRetcode; // // Bomb out on bad parameters. // if(!psDevInfo || !pState) { return(DFU_ERR_INVALID_ADDR); } // // Set up the command we need to send to request the device information. // memset(&sInfoCmd, 0, sizeof(tDFUDownloadInfoHeader)); sInfoCmd.ucCommand = TI_CMD_INFO; // // Send a download request containing our info read command. // eRetcode = DFUDownloadTransfer(pState, true, (unsigned char *)&sInfoCmd, sizeof(tDFUDownloadInfoHeader)); // // Did we send the download command successfully? // if(eRetcode == DFU_OK) { // // Yes - now upload the device information structure. // eRetcode = DFUUploadTransfer(pState, (unsigned char *)psDevInfo, sizeof(tDFUDeviceInfo)); } return(eRetcode); } //**************************************************************************** // // Determine whether the connected device supports TI's DFU binary // protocol and, if so, query the device information structure and fill in // various fields in the state structure. // //**************************************************************************** static bool CheckForTIProtocol(tTIDFUDeviceState *pState) { USHORT usCount; BOOL bRetcode; tTIDFUErr eRetcode; tDFUQueryTIProtocol sProt; tDFUDeviceInfo sDevInfo; // // Clear out the TI-specific fields in the state structure. // pState->bSupportsLMProtocol = false; pState->ulClassInfo = 0; pState->ulPartInfo = 0; pState->usFirstFlashBlock = 0; pState->usLastFlashBlock = 0; // // Send our special request to the device and see if it responds. // bRetcode = Endpoint0Transfer(pState->hUSB, (REQUEST_TRANSFER_IN | REQUEST_TYPE_CLASS | REQUEST_RECIPIENT_INTERFACE), USBD_DFU_REQUEST_TI, REQUEST_TI_VALUE, pState->usInterface, sizeof(tDFUQueryTIProtocol), (PUCHAR)&sProt, &usCount); // // If we got back the amount of data we expected and the values match // those we expect, we can be pretty safe in assuming that this device // supports our protocol. // if(bRetcode && (usCount == sizeof(tDFUQueryTIProtocol)) && (sProt.usMarker == LM_DFU_PROTOCOL_MARKER) && (sProt.usVersion == LM_DFU_PROTOCOL_VERSION_1)) { // // We can safely use TI extensions when talking to this device. // pState->bSupportsLMProtocol = true; // // Now get the device information structure so that we know the // bounds of the writeable area of the device flash and the part // number we are talking to. // eRetcode = DFUDeviceInfoGet(pState, &sDevInfo); if(eRetcode == DFU_OK) { // // Remember the writeable flash bounds. // pState->usFirstFlashBlock = (unsigned short)(sDevInfo.ulAppStartAddr / 1024); pState->usLastFlashBlock = (unsigned short)(sDevInfo.ulFlashTop / 1024); pState->ulClassInfo = sDevInfo.ulClassInfo; pState->ulPartInfo = sDevInfo.ulPartInfo; } } // // Tell the caller whether the device supports our extensions or not. // return(pState->bSupportsLMProtocol); } //**************************************************************************** // // Given the ulPartInfo value as returned in the DFU device information // structure, return an integer representing the TI part number in // use. For example, if running on an LM3S3748, this function will return // value 0x3748 (hex). The function also writes the part number string into // the supplied buffer which is assumed to be at least NUM_PART_STRING_CHARS // bytes long. // //**************************************************************************** static unsigned long MapPartInfo(unsigned long ulPartInfo, char *strPart) { int iLoop; unsigned char ucPartNo; // // Extract the part number from the part info value passed. // ucPartNo = (unsigned char)((ulPartInfo & TI_PART_M) >> TI_PART_SHIFT); // // Search our part number mapping table for the LM3S part number // that is represented by the value in ulPartInfo. // for(iLoop = 0; iLoop < NUM_TI_PARTS; iLoop++) { if(g_sPartNumMap[iLoop].ucPartNo == ucPartNo) { // // We found it - copy the part number string and return the // part number. // strcpy_s(strPart, NUM_PART_STRING_CHARS, g_sPartNumMap[iLoop].pcPartString); return(g_sPartNumMap[iLoop].ulLM3SPart); } } // // If we drop out, the part number we were passed doesn't exist in the // table so return 0 to indicate an unknown part. // return(0); } //**************************************************************************** // // From the part and class information stored in the provided state structure, // determine the actual TI part number and revision we are talking to // and fill in the appropriate values in the device info structure passed. // //**************************************************************************** void GetPartNumberAndRev(tTIDFUDeviceState *pState, tTIDFUDeviceInfo *pDevInfo) { // // Make sure we tell the caller whether this device supports the // TI protocol or not. // pDevInfo->bSupportsTIExtensions = pState->bSupportsLMProtocol; // // Is the protocol supported? // if(pState->bSupportsLMProtocol) { // // Yes - fill in the part number and revision information. // //pDevInfo->ulPartNumber = MapPartInfo(pState->ulPartInfo, // pDevInfo->pcPartNumber); pDevInfo->ulPartNumber = pState->ulPartInfo; //pDevInfo->cRevisionMajor = (char)((pState->ulClassInfo & // TI_INFO_MAJ_M)>> // TI_INFO_MAJ_SHIFT); pDevInfo->cRevisionMajor = 0; //pDevInfo->cRevisionMinor = (char)((pState->ulClassInfo & // TI_INFO_MIN_M) >> // TI_INFO_MIN_SHIFT); pDevInfo->cRevisionMinor = (char)pState->ulClassInfo; } else { // // No - clear the part number and revision fields. // pDevInfo->ulPartNumber = 0; pDevInfo->cRevisionMajor = 0; pDevInfo->cRevisionMinor = 0; } } //**************************************************************************** // // Maps a DFU status value to a human-readable string for debug use. // //**************************************************************************** static char * MapStatusToString(tDFUStatus eStatus) { int iLoop; for(iLoop = 0; iLoop < NUM_STATUS_MAP; iLoop++) { if(g_psStatusMap[iLoop].eStatus == eStatus) { return(g_psStatusMap[iLoop].pcStatus); } } return("**UNKNOWN**"); } //**************************************************************************** // // Maps a DFU state to a human-readable string for debug use. // //**************************************************************************** static char * MapStateToString(tDFUState eState) { int iLoop; for(iLoop = 0; iLoop < NUM_STATE_MAP; iLoop++) { if(g_psStateMap[iLoop].eState == eState) { return(g_psStateMap[iLoop].pcState); } } return("**UNKNOWN**"); } //**************************************************************************** // // Maps a DFU status value into the equivalent return code in tTIDFUErr. // //**************************************************************************** static tTIDFUErr MapDFUStatus(tDFUStatus eStatus) { switch(eStatus) { case STATUS_OK: return(DFU_OK); case STATUS_ERR_FILE: case STATUS_ERR_TARGET: return(DFU_ERR_UNSUPPORTED); case STATUS_ERR_NOTDONE: case STATUS_ERR_WRITE: case STATUS_ERR_ERASE: case STATUS_ERR_CHECK_ERASED: case STATUS_ERR_PROG: case STATUS_ERR_VERIFY: return(DFU_ERR_DNLOAD_FAIL); case STATUS_ERR_ADDRESS: return(DFU_ERR_INVALID_ADDR); case STATUS_ERR_FIRMWARE: case STATUS_ERR_VENDOR: case STATUS_ERR_USBR: case STATUS_ERR_POR: case STATUS_ERR_UNKNOWN: return(DFU_ERR_UNKNOWN); case STATUS_ERR_STALLEDPKT: return(DFU_ERR_STALL); default: return(DFU_ERR_UNKNOWN); } } //**************************************************************************** // // Perform a single DFU download transfer and optionally block until it // completes. // // \param pState is the instance data structure for the connection. // \param bCheckState is \b true if the function should poll the device state // and block until the state leaves DNLOAD_SYNC or DNBUSY, or \b false if // the function should return immediately after posting the download request. // \param pcData points to the download data to send. // \param iLength is the number of bytes to send. This must be less than // or equal to the maximum transfer size for the target device. // // This function performs a single DFU download request and sends the supplied // data as the request payload. If bCheckState is \b true, the function will // poll the device using GET_STATUS requests until the state is no longer // reported as DNLOAD_SYNC or DNBUSY (indicating that the transfer completed // or an error is being reported). // // \return Returns DFU_OK if the operation completed without any errors // reported, DFU_ERR_TIMEOUT if the control transaction timed out, // DFU_ERR_DISCONNECTED if the device was disconnected, DFU_ERR_STALL if the // device stalled endpoint 0 to indicate an error, DFU_ERR_INVALID_SIZE if // \e iLength is larger than the maximum transfer size for the target or // DFU_ERR_UNKNOWN if some other low level USB error occurred. // //**************************************************************************** static tTIDFUErr DFUDownloadTransfer(tTIDFUDeviceState *pState, bool bCheckStatus, unsigned char *pcData, int iLength) { USHORT usCount; BOOL bRetcode; tDFUGetStatusResponse sStatus; TRACE("Downloading %d bytes from 0x%08x\n", iLength, pcData); // // Make sure the size provided can be sent in a single DFU transfer. // if(iLength > (int)pState->usTransferSize) { TRACE("Bad size %d passed.\n", iLength); return(DFU_ERR_INVALID_SIZE); } // // Send the download request with the supplied payload. // bRetcode = Endpoint0Transfer(pState->hUSB, (REQUEST_TRANSFER_OUT | REQUEST_TYPE_CLASS | REQUEST_RECIPIENT_INTERFACE), USBD_DFU_REQUEST_DNLOAD, pState->usBlockNum++, pState->usInterface, iLength, (PUCHAR)pcData, &usCount); // // Did the transfer complete successfully? // if(!bRetcode) { TRACE("Error %d from Endpoint0Transfer\n", GetLastError()); return(MapRetcode(GetLastError())); } // // Did we process the amount of data we expected? // if((int)usCount != iLength) { TRACE("Error - processed %d bytes, expected %d\n", (int)usCount, iLength); return(DFU_ERR_UNKNOWN); } if(bCheckStatus) { // // Keep checking the device status until we see a problem reported // or the device tells us that the previous download request completed. // do { bRetcode = Endpoint0Transfer(pState->hUSB, (REQUEST_TRANSFER_IN | REQUEST_TYPE_CLASS | REQUEST_RECIPIENT_INTERFACE), USBD_DFU_REQUEST_GETSTATUS, 0, pState->usInterface, sizeof(tDFUGetStatusResponse), (PUCHAR)&sStatus, &usCount); // // Did the transaction succeed? // if(!bRetcode) { TRACE("Error %d from Endpoint0Transfer\n", GetLastError()); return(MapRetcode(GetLastError())); } else { // // Did we get the data we expected? // if(usCount != sizeof(tDFUGetStatusResponse)) { TRACE("Error - read %d bytes, expected %d\n", (int)usCount, sizeof(tDFUGetStatusResponse)); return(DFU_ERR_UNKNOWN); } // // If the device is still busy, we need to wait a while before // polling again. // if((sStatus.bState == STATE_DNLOAD_SYNC) || (sStatus.bState == STATE_DNBUSY)) { DWORD dwTimeout; // // Read the timeout in milliseconds from the 3 byte field // in the status structure. // dwTimeout = sStatus.bwPollTimeout[0] + (sStatus.bwPollTimeout[1] << 8) + (sStatus.bwPollTimeout[2] << 16); // // Twiddle our thumbs until this number of milliseconds has // elapsed. // Sleep(dwTimeout); } } } while((sStatus.bState == STATE_DNLOAD_SYNC) || (sStatus.bState == STATE_DNBUSY)); // // Return the appropriate return code depending upon the device status. // TRACE("State on completion %s, status %s\n", MapStateToString((tDFUState)sStatus.bState), MapStatusToString((tDFUStatus)sStatus.bStatus)); return(MapDFUStatus((tDFUStatus)sStatus.bStatus)); } else { // // We didn't have to check the device status so tell the caller all is // apparently well. // return(DFU_OK); } } //**************************************************************************** // // Download a block of data to the device using multiple transfers if necessary. // // \param pState is the instance data structure for the connection. // \param pcData points to the download data to send. // \param ulLength is the number of bytes to send. If this is greater than the // maximum transfer size for the device, multiple transfers will be used to // complete the download. // // This function downloads a block of data to the DFU device using as many // block transfers as are necessary to complete the operation. It is assumed // that the device is in a state ready to receive the data and that any // necessary preamble commands have been sent prior to this call being made. // // \return Returns DFU_OK if the operation completed without any errors // reported, DFU_ERR_TIMEOUT if the control transaction timed out, // DFU_ERR_DISCONNECTED if the device was disconnected, DFU_ERR_STALL if the // device stalled endpoint 0 to indicate an error, DFU_ERR_INVALID_SIZE if // \e iLength is larger than the maximum transfer size for the target or // DFU_ERR_UNKNOWN if some other low level USB error occurred. // //**************************************************************************** static tTIDFUErr DFUDownloadBlock(tTIDFUDeviceState *pState, unsigned char *pcData, unsigned long ulImageLen, HWND hwndNotify) { unsigned long ulCount; unsigned long ulToSend; unsigned long ulTransfers; unsigned char *pcSending; tTIDFUErr eRetcode; // // Now we really are ready to download the image! Send a message to // indicate that we are about to start. // if(hwndNotify) { // // How many transfers will this download take? // ulTransfers = ((ulImageLen + (pState->usTransferSize - 1)) / pState->usTransferSize) + 1; PostMessage(hwndNotify, WM_DFU_DOWNLOAD, (WPARAM)ulTransfers, (LPARAM)pState); } // // Set up for the download loop. // ulCount = ulImageLen; pcSending = pcData; ulTransfers = 0; // // Do we have any more data to send? // while(ulCount) { // // How many bytes can we send this transfer? // ulToSend = ((ulCount > pState->usTransferSize) ? pState->usTransferSize : ulCount); // // Perform a single transfer. // eRetcode = DFUDownloadTransfer(pState, true, pcSending, ulToSend); // // If it failed, exit the loop. // if(eRetcode != DFU_OK) break; // // Move on to the next block. // pcSending += ulToSend; ulCount -= ulToSend; ulTransfers++; // // Send a progress update if required. // if(hwndNotify) { PostMessage(hwndNotify, WM_DFU_PROGRESS, (WPARAM)ulTransfers, (LPARAM)pState); } } // // If we get here, either we exited the download loop due to an error or // the transfer is complete and we need to tell the device that we are // done. Which is it? // if(eRetcode == DFU_OK) { // // We are done so send a 0 length download request as a signal. // eRetcode = DFUDownloadTransfer(pState, true, NULL, 0); ulTransfers++; } // // Send a message telling the caller either that we finished successfully // or that an error was notified. // if(hwndNotify) { PostMessage(hwndNotify, (eRetcode == DFU_OK) ? WM_DFU_COMPLETE : WM_DFU_ERROR, (WPARAM)ulTransfers, (LPARAM)pState); } return(eRetcode); } //**************************************************************************** // // Perform a single DFU upload transfer. // // \param pState is the instance data structure for the connection. // \param pcData points to a buffer which will receive the uploaded data. // \param iLength is the number of bytes to receive. This must be less than // or equal to the maximum transfer size for the target device. // // This function performs a single DFU upload request to read a block of data // back from the DFU device. // // \return Returns DFU_OK if the operation completed without any errors // reported, DFU_ERR_TIMEOUT if the control transaction timed out, // DFU_ERR_DISCONNECTED if the device was disconnected, DFU_ERR_STALL if the // device stalled endpoint 0 to indicate an error, DFU_ERR_INVALID_SIZE if // \e iLength is larger than the maximum transfer size for the target or // DFU_ERR_UNKNOWN if some other low level USB error occurred. // //**************************************************************************** static tTIDFUErr DFUUploadTransfer(tTIDFUDeviceState *pState, unsigned char *pcData, int iLength) { BOOL bRetcode; USHORT usCount; // // Make sure the size provided can be sent in a single DFU transfer. // if(iLength > (int)pState->usTransferSize) { return(DFU_ERR_INVALID_SIZE); } TRACE("Uploading %d bytes to 0x%08x\n", iLength, pcData); // // Send the download request with the supplied payload. // bRetcode = Endpoint0Transfer(pState->hUSB, (REQUEST_TRANSFER_IN | REQUEST_TYPE_CLASS | REQUEST_RECIPIENT_INTERFACE), USBD_DFU_REQUEST_UPLOAD, pState->usBlockNum++, pState->usInterface, (USHORT)iLength, (PUCHAR)pcData, &usCount); // // Did the transfer complete successfully? // if(!bRetcode) { // // The transfer failed for some reason. // return(MapRetcode(GetLastError())); } else { // // The transfer succeeded. Did the amount of data processed match the // amount we expected. // if((int)usCount == iLength) return DFU_OK; else return DFU_ERR_UNKNOWN; //return(((int)usCount == iLength) ? DFU_OK : DFU_ERR_UNKNOWN); } } //**************************************************************************** // // Upload a block of data from the device using multiple transfers if necessary. // // \param pState is the instance data structure for the connection. // \param pcData points to a buffer into which uploaded data will be written. // \param ulLength is the number of bytes to read. If this is greater than the // maximum transfer size for the device, multiple transfers will be used to // complete the upload. // \param bVerifying indicates whether the WM_DFU_UPLOAD or WM_DFU_VERIFY // message should be sent to hwndNotify at the start of the operation. // \param hwndNotify is the handle of a window which will receive periodic // WM_DFU_PROGRESS messages during the transfer. If NULL, no messages will be // sent. // // This function uploads a block of data from the DFU device using as many // block transfers as are necessary to complete the operation. It is assumed // that the device is in a state ready to provide the data and that any // necessary preamble commands have been sent prior to this call being made. // // \return Returns DFU_OK if the operation completed without any errors // reported, DFU_ERR_TIMEOUT if the control transaction timed out, // DFU_ERR_DISCONNECTED if the device was disconnected, DFU_ERR_STALL if the // device stalled endpoint 0 to indicate an error or DFU_ERR_UNKNOWN if some // other low level USB error occurred. // //**************************************************************************** static tTIDFUErr DFUUploadBlock(tTIDFUDeviceState *pState, unsigned char *pcData, int iLength, bool bVerifying, HWND hwndNotify) { unsigned long ulCount; unsigned long ulToRead; unsigned long ulTransfers; unsigned char *pcReading; tTIDFUErr eRetcode; // // Have we been passed a window handle? If so, send the message // indicating that we are about to start a new operation. // if(hwndNotify) { // // How many transfers will this upload take? // ulTransfers = ((iLength + (pState->usTransferSize - 1)) / pState->usTransferSize) + 1; PostMessage(hwndNotify, bVerifying ? WM_DFU_VERIFY : WM_DFU_UPLOAD, (WPARAM)ulTransfers, (LPARAM)pState); } // // Set up for the upload loop. // ulCount = (unsigned long)iLength; pcReading = pcData; ulTransfers = 0; // // Do we have any more data to send? // while(ulCount) { // // How many bytes can we send this transfer? // ulToRead = ((ulCount > pState->usTransferSize) ? pState->usTransferSize : ulCount); // // Perform a single transfer. // eRetcode = DFUUploadTransfer(pState, pcReading, ulToRead); // // If it failed, exit the loop. // if(eRetcode != DFU_OK) break; // // Move on to the next block. // pcReading += ulToRead; ulCount -= ulToRead; ulTransfers++; // // Send a progress update if required. // if(hwndNotify) { PostMessage(hwndNotify, WM_DFU_PROGRESS, (WPARAM)ulTransfers, (LPARAM)pState); } } // // Send a message telling the caller either that we finished successfully // or that an error was notified. // if(hwndNotify) { PostMessage(hwndNotify, (eRetcode == DFU_OK) ? WM_DFU_COMPLETE : WM_DFU_ERROR, (WPARAM)ulTransfers, (LPARAM)pState); } return(eRetcode); } //**************************************************************************** // // Verify that the content of a block of flash matches the data passed. // // \param pState is the instance data structure for the connection. // \param pcData points to the first byte of data that is to be verified. // \param iLength is the number of bytes to verify. // \param hwndNotify is the handle of a window which will receive periodic // WM_DFU_PROGRESS messages during the transfer. If NULL, no messages will be // sent. // // This function uploads a block of data from the DFU device and checks that the // returned data matches the content of the buffer passed to this function. It // is assumed that the device has previously been sent any necessary preamble // commands to inform it of the range of flash to be read back during the // upload operation. // // \return Returns DFU_OK if the operation completed without any errors // reported, DFU_ERR_VERIFY_FAIL if the data read back did not match the // contents of pcData, DFU_ERR_TIMEOUT if a control transaction timed out, // DFU_ERR_DISCONNECTED if the device was disconnected, DFU_ERR_STALL if the // device stalled endpoint 0 to indicate an error or DFU_ERR_UNKNOWN if some // other low level USB error occurred. // //**************************************************************************** static tTIDFUErr DFUVerifyBlock(tTIDFUDeviceState *pState, unsigned char *pcData, int iLength, HWND hwndNotify) { unsigned char *pcBuffer; tTIDFUErr eRetcode; int iLoop; int errorCounter = 0; // // Allocate a buffer large enough to hold the downloaded image. // pcBuffer = (unsigned char *)malloc(iLength); if(!pcBuffer) { return(DFU_ERR_MEMORY); } // // Upload into the newly allocated buffer. // eRetcode = DFUUploadBlock(pState, (unsigned char *)pcBuffer, iLength, true, hwndNotify); // // Did the upload complete successfully? // if(eRetcode == DFU_OK) { // // Yes - now compare the uploaded block with the original data. // for(iLoop = 0; iLoop < iLength && iLoop < 0x3DF00; iLoop++) { if(pcBuffer[iLoop] != pcData[iLoop]) { errorCounter++; } if(errorCounter > 4){ eRetcode = DFU_ERR_VERIFY_FAIL; TRACE("Verification failure at offset 0x%x - read 0x%02x, " "expected 0x%02x\n", iLoop, pcBuffer[iLoop], pcData[iLoop]); break; } } } // // Free up our buffer and tell the caller whether or not the verification // succeeded. // free(pcBuffer); return(eRetcode); } //**************************************************************************** // // Gets the DFU device status. // // \param pState is the instance data structure for the connection. // \param pStatus is a pointer which will be written with the status structure // returned by the DFU device. // // This function issues a USBD_DFU_REQUEST_GETSTATUS to the DFU device and // returns the data that this provides. // // \return Returns DFU_OK if the operation completed without any errors // reported, DFU_ERR_TIMEOUT if the control transaction timed out, // DFU_ERR_DISCONNECTED if the device was disconnected, DFU_ERR_STALL if the // device stalled endpoint 0 to indicate an error or DFU_ERR_UNKNOWN if some // other low level USB error occurred. // //**************************************************************************** static tTIDFUErr DFUDeviceStatusGet(tTIDFUDeviceState *pState, tDFUGetStatusResponse *pStatus) { BOOL bRetcode; USHORT usCount; // // Request the status information via a control transaction on EP0. // bRetcode = Endpoint0Transfer(pState->hUSB, (REQUEST_TRANSFER_IN | REQUEST_TYPE_CLASS | REQUEST_RECIPIENT_INTERFACE), USBD_DFU_REQUEST_GETSTATUS, 0, pState->usInterface, sizeof(tDFUGetStatusResponse), (PUCHAR)pStatus, &usCount); // // Did the transfer complete successfully? // if(!bRetcode && (usCount != sizeof(tDFUGetStatusResponse))) { return(MapRetcode(GetLastError())); } else { return(DFU_OK); } } //**************************************************************************** // // Aborts a current operation and returns the device to idle state. // // \param pState is the instance data structure for the connection. // // This function issues a USBD_DFU_REQUEST_ABORT to the DFU device. // // \return Returns DFU_OK if the operation completed without any errors // reported, DFU_ERR_TIMEOUT if the control transaction timed out, // DFU_ERR_DISCONNECTED if the device was disconnected, DFU_ERR_STALL if the // device stalled endpoint 0 to indicate an error or DFU_ERR_UNKNOWN if some // other low level USB error occurred. // //**************************************************************************** static tTIDFUErr DFUDeviceAbort(tTIDFUDeviceState *pState) { BOOL bRetcode; USHORT usCount; // // Send the download request with the supplied payload. // bRetcode = Endpoint0Transfer(pState->hUSB, (REQUEST_TRANSFER_OUT | REQUEST_TYPE_CLASS | REQUEST_RECIPIENT_INTERFACE), USBD_DFU_REQUEST_ABORT, 0, pState->usInterface, 0, (PUCHAR)NULL, &usCount); // // Did the transfer complete successfully? // if(!bRetcode) { return(MapRetcode(GetLastError())); } else { return(DFU_OK); } } //**************************************************************************** // // Clears any device error status. // // \param pState is the instance data structure for the connection. // // This function issues a USBD_DFU_REQUEST_CLRSTATUS to the DFU device. // // \return Returns DFU_OK if the operation completed without any errors // reported, DFU_ERR_TIMEOUT if the control transaction timed out, // DFU_ERR_DISCONNECTED if the device was disconnected, DFU_ERR_STALL if the // device stalled endpoint 0 to indicate an error or DFU_ERR_UNKNOWN if some // other low level USB error occurred. // //**************************************************************************** static tTIDFUErr DFUDeviceStatusClear(tTIDFUDeviceState *pState) { BOOL bRetcode; USHORT usCount; // // Send the download request with the supplied payload. // bRetcode = Endpoint0Transfer(pState->hUSB, (REQUEST_TRANSFER_OUT | REQUEST_TYPE_CLASS | REQUEST_RECIPIENT_INTERFACE), USBD_DFU_REQUEST_CLRSTATUS, 0, pState->usInterface, 0, (PUCHAR)NULL, &usCount); // // Did the transfer complete successfully? // if(!bRetcode && (usCount != sizeof(tDFUGetStatusResponse))) { return(MapRetcode(GetLastError())); } else { return(DFU_OK); } } //**************************************************************************** // // Checks the status of the DFU device and does what is necessary to make it // idle in preparation for a new upload or download. // // \param pState is the instance data structure for the connection. // // This function read the DFU device status and, if it's not in state IDLE, // performs an ABORT and/or CLRSTATUS to get it back into IDLE state. // // \return Returns DFU_OK if the operation completed without any errors // reported, DFU_ERR_TIMEOUT if the control transaction timed out, // DFU_ERR_DISCONNECTED if the device was disconnected, DFU_ERR_STALL if the // device stalled endpoint 0 to indicate an error, DFU_ERR_UNSUPPORTED if the // device is in APP_IDLE, APP_DETACH or MANIFEST_WAIT_SYNC states or is not // manifest tolerant and is currently in MANIFEST or MANIFEST_SYNC state, // or DFU_ERR_UNKNOWN if some other low level USB error occurred. // //**************************************************************************** static tTIDFUErr DFUMakeDeviceIdle(tTIDFUDeviceState *pState) { tDFUGetStatusResponse sStatus; tTIDFUErr eRetcode; // // Get the current device status. // eRetcode = DFUDeviceStatusGet(pState, &sStatus); // // Make sure we got the status successfully. // if(eRetcode == DFU_OK) { switch(sStatus.bState) { // // Are we already idle? // case STATE_IDLE: break; // // Are we in the error state? // case STATE_ERROR: { // // Yes - we need to clear the status to get back to idle. // eRetcode = DFUDeviceStatusClear(pState); } break; case STATE_MANIFEST: case STATE_MANIFEST_SYNC: { // // If the device is manifest tolerant, we can proceed to wait // a while then abort to get back to IDLE. If, however, it is // not, we can't get back to IDLE without a reset (which we // don't do here). // if(!(pState->ucDFUAttributes && DFU_ATTR_MANIFEST_TOLERANT)) { return(DFU_ERR_UNSUPPORTED); } } // // Drop through. // // // Is the device currently busy? // case STATE_DNBUSY: { DWORD dwTimeout; // // The device is busy. We need to wait for the polling timeout // then send an abort. // dwTimeout = sStatus.bwPollTimeout[0] + (sStatus.bwPollTimeout[1] << 8) + (sStatus.bwPollTimeout[2] << 8); Sleep(dwTimeout); } // // Drop through. // case STATE_DNLOAD_SYNC: case STATE_DNLOAD_IDLE: case STATE_UPLOAD_IDLE: { eRetcode = DFUDeviceAbort(pState); } break; // // In these states, it is not possible to get back to IDLE without // a device reset which we don't do here. These are, therefore, // considered error cases. // case STATE_MANIFEST_WAIT_RESET: case STATE_APP_IDLE: case STATE_APP_DETACH: return(DFU_ERR_UNSUPPORTED); } } // // Tell the caller how things went. // return(eRetcode); } //**************************************************************************** // // Reset the attached DFU device. // // This function sends a command to the attached device informing it that it // should initiate a system reset. Only devices supporting the TI // protocol extensions will be affected by this function call. Use of a // protocol extension is required since WinUSB does not provide any method for // an application to reset a USB device. // // \return Returns DFU_ERR_UNSUPPORTED if the device does not support TI // protocol extensions. // //**************************************************************************** static tTIDFUErr DFUDeviceReset(tTIDFUDeviceState *pState) { tDFUDownloadHeader sHeader; tTIDFUErr eRetcode; // // This operation is only supported if the target device supports the // TI protocol. // if(!pState->bSupportsLMProtocol) { TRACE("Device does not support TI protocol.\n"); return(DFU_ERR_UNSUPPORTED); } // // First, make sure the target is idle and ready to receive a new // command. // eRetcode = DFUMakeDeviceIdle(pState); if(eRetcode != DFU_OK) { TRACE("Error %s (%d) attempting to get device into IDLE state.\n", TIDFUErrorStringGet(eRetcode), eRetcode); return(eRetcode); } // // Send the target the command telling it to reset. If this succeeds, the // device will not be accessible after this function returns. // sHeader.ucCommand = TI_CMD_RESET; eRetcode = DFUDownloadTransfer(pState, true, (unsigned char *)&sHeader, sizeof(tDFUDownloadHeader)); // // Tell the caller how things went. // return(eRetcode); } //**************************************************************************** // //! Initialize the DFU library. //! //! This function must be called before any other entry point in the library. //! It initalizes global data required to access DFU devices. //! //! \return Returns DFU_OK. // //**************************************************************************** tTIDFUErr __stdcall TIDFUInit(void) { TRACE("TIDFUInit\n"); // // Initialize the DFU CRC32 lookup table. // InitCRC32Table(); return(DFU_OK); } //**************************************************************************** // //! Opens a DFU device in preparation for download or upload operations. //! //! \param iDeviceIndex is a zero-based index indicating which DFU-capable //! device is to be opened. //! \param psDevInfo is the structure which will be filled in with information //! relating to the DFU device which has been opened. //! \param phHandle points to storage which will be written with a valid //! DFU device handle on success. //! //! This function opens a DFU device and returns a handle allowing further //! DFU operations to be performed on the device and also information on the //! device state and capabilities. //! //! Note that this function will open DFU devices which are currently in //! runtime mode. The caller must ensure that a device is in DFU mode prior //! to making any requests which are not supported in runtime mode. Currently, //! this library does not contain a function to cause a DFU device to switch //! from runtime to DFU mode. //! //! Handles allocated by this function must be closed using a matching call to //! TIDFUDeviceClose(). //! //! \return Returns DFU_OK on success (in which case *phHandle and *psDevInfo //! will also be written with valid information), DFU_ERR_NOT_FOUND if a DFU //! device with index iDeviceIndex cannot be found, or DFU_ERR_INVALID_ADDR if //! a NULL pointer is passed in any parameter. // //**************************************************************************** tTIDFUErr __stdcall TIDFUDeviceOpen(int iDeviceIndex, tTIDFUDeviceInfo *psDevInfo, tTIDFUHandle *phHandle) { int iCount = 0; int iIntCount; TIUSB_HANDLE hHandle; tTIDFUDeviceState *pState; PUCHAR pucDevice; PUCHAR pucConfig; PUCHAR pucInterface; PUCHAR pucDFU; BOOL bInstalled; TRACE("TIDFUDeviceOpen %d\n", iDeviceIndex); // // Check for a bad pointers. // if(!psDevInfo || !phHandle) { return(DFU_ERR_INVALID_ADDR); } // // Search for the required device and open it if found. Note that this // call will only find devices identified by the TI DFU GUID. // hHandle = InitializeDeviceByIndex(DFU_VID, DFU_PID, (LPGUID)&(GUID_DEVINTERFACE_TI_DFU), iDeviceIndex, FALSE, &bInstalled); // // Did we find a device supporting the TI DFU class? // if(!hHandle) { // // We couldn't find the device so return an appropriate error. // return(DFU_ERR_NOT_FOUND); } // // Allocate a state structure for this instance. // pState = (tTIDFUDeviceState *) malloc(sizeof(tTIDFUDeviceState)); if(!pState) { return(DFU_ERR_MEMORY); } // // Remember our device handle. // pState->hUSB = hHandle; // // Get the device descriptor. // pucDevice = GetDeviceDescriptor(hHandle); if(!pucDevice) { // // For some reason, we can't query the device descriptor. Without this, // we can't get some vital information so return an error. // free(pState); return(DFU_ERR_UNKNOWN); } // // Clear the device information structure. // memset(psDevInfo, 0, sizeof(tTIDFUDeviceInfo)); // // Extract fields we are interested in from the device descriptor. // psDevInfo->usVID = READ_SHORT(pucDevice + 8); psDevInfo->usPID = READ_SHORT(pucDevice + 10); psDevInfo->usDevice = READ_SHORT(pucDevice + 12); psDevInfo->ucManufacturerString = pucDevice[14]; psDevInfo->ucProductString = pucDevice[15]; psDevInfo->ucSerialString = pucDevice[16]; // // Get the configuration descriptor. We assume the default configuration // is in use. // pucConfig = GetConfigDescriptor(hHandle); if(!pucConfig) { // // For some reason, we can't query the device descriptor. Without this, // we can't get some vital information so return an error. // free(pucDevice); free(pState); return(DFU_ERR_UNKNOWN); } // // Find the interface offering DFU functionality. We start the search at // the end of the configuration descriptor itself. // pucInterface = pucConfig + pucConfig[0]; for(iIntCount = 0; iIntCount < pucConfig[4]; iIntCount++) { // // Search from the current position in the config descriptor looking // for the next interface descriptor. // pucInterface = FindDescriptor(INTERFACE_DESCRIPTOR_VALUE, pucInterface, ((unsigned long)READ_SHORT(pucConfig + 2) - (unsigned long)(pucInterface - pucConfig))); if(!pucInterface) { // // We ran off the end of the config descriptor without finding // the interface descriptor we are looking for. This suggests // the device isn't offering a DFU interface after all. // free(pucConfig); free(pucDevice); free(pState); return(DFU_ERR_UNKNOWN); } else { // // We found an interface descriptor. Is it the DFU one? // if((pucInterface[5] == USB_CLASS_APP_SPECIFIC) && (pucInterface[6] == USB_DFU_SUBCLASS)) { // // Yes - break out of the search. // break; } else { // // No - skip to the next descriptor to continue our search. // pucInterface += pucInterface[0]; } } } // // Did we hit the end of the search without finding a DFU interface? // if(iIntCount == pucConfig[4]) { // // Yes - this is an error. // free(pucConfig); free(pucDevice); free(pState); return(DFU_ERR_UNKNOWN); } // // Remember the interface string descriptor ID and whether or not it is // in DFU mode. // psDevInfo->ucDFUInterfaceString = pucInterface[8]; psDevInfo->bDFUMode = ((pucInterface[7] == DFU_PROTOCOL) ? true : false); // // Find the DFU functional descriptor. It will follow the interface // descriptor we just found. // pucDFU = FindDescriptor(USB_DESC_TYPE_DFU, pucInterface + pucInterface[0], ((unsigned long)(READ_SHORT(pucConfig + 2) - (pucInterface - pucConfig)) - (unsigned long)pucInterface[0])); // // Was one there? // if(pucDFU) { // // Yes - extract the useful stuff from it. // psDevInfo->usDetachTimeOut = DFU_DETACHTIMEOUT(pucDFU); psDevInfo->usTransferSize = DFU_MAXTRANSFER(pucDFU); psDevInfo->ucDFUAttributes = DFU_ATTRIBUTES(pucDFU); } else { // // This naughty DFU device doesn't publish a DFU functional descriptor! // psDevInfo->usDetachTimeOut = 0; psDevInfo->usTransferSize = 0; psDevInfo->ucDFUAttributes = 0; } // // Remember various important pieces of info about the device then tell // the caller we found it, giving them back the handle. // pState->usVID = psDevInfo->usVID; pState->usPID = psDevInfo->usPID; pState->usTransferSize = psDevInfo->usTransferSize; pState->ucDFUAttributes = psDevInfo->ucDFUAttributes; pState->usInterface = (unsigned short)pucInterface[2]; pState->usBlockNum = 0; // // Is the device in DFU mode? If we are in runtime mode, skip these // queries since they are only supported in DFU mode. // if(psDevInfo->bDFUMode) { // // Make sure the device is idle before we do anything that may be // illegal in various error states. // DFUMakeDeviceIdle(pState); // // Determine whether this device supports the TI-specific binary // protocol and, if so, read the device information structure. // CheckForTIProtocol(pState); // // If possible, determine the specific TI part we are talking to. // GetPartNumberAndRev(pState, psDevInfo); } // // Free the device and configuration descriptor storage. // free(pucDevice); free(pucConfig); // // If we get here, all is well so return the handle to the caller. // *phHandle = (tTIDFUHandle)pState; return(DFU_OK); } //**************************************************************************** // //! Closes a DFU device and, optionally, returns it to its run time //! configuration. //! //! \param hHandle is the handle of the DFU device as returned from a //! previous call to TIDFUDeviceOpen(). //! \param bReset indicates whether to leave the device in DFU mode (\b false) //! or reset it and return to run time mode (\b true). //! //! This function closes a DFU device previously opened using a call to //! TIDFUDeviceOpen(). If the \e bReset parameter is \b true, the device //! is reset and returned to its run time mode of operation. If \e bReset //! is false, the device is left in DFU mode and can be reopened again //! without the need to perform a mode switch. //! //! \return Returns DFU_OK on success or other error codes on failure. // //**************************************************************************** tTIDFUErr __stdcall TIDFUDeviceClose(tTIDFUHandle hHandle, bool bReset) { BOOL bRetcode; tTIDFUDeviceState *pState; TRACE("TIDFUDeviceClose 0x%08x\n", hHandle); if(!hHandle) { return(DFU_ERR_HANDLE); } // // Get our state information. // pState = (tTIDFUDeviceState *)hHandle; // // Have we been asked to reset the device? // if(bReset) { // // Yes - do it. // DFUDeviceReset(pState); } // // Close the device handle. // bRetcode = TerminateDevice(pState->hUSB); // // Free our state data. // free(pState); // // Tell the caller if all is well. // return(bRetcode ? DFU_OK : DFU_ERR_HANDLE); } //**************************************************************************** // //! Retrieves a string descriptor from a DFU device. //! //! \param hHandle is the handle of the DFU device from which the string is //! being queried. //! \param ucStringIndex is the index of the string that is being queried. //! \param ucLanguageID is the identifier of the language that the string is //! to be returned in. //! \param pcString points to a buffer into which the returned string is to //! be written. //! \param pusStringLen points to a variable containing the size of the //! pcString buffer (in bytes) on entry. If the string is read, this variable //! is updated to show the number of bytes written into pcString. //! //! This function retrieves Unicode strings from the DFU device. If the //! requested string is available, DFU_OK is returned and the string is //! written into the supplied \e pcString buffer. //! //! \return Returns DFU_OK on success or DFU_ERR_NOT_FOUND if the supplied //! string ID or language codes are invalid. If a NULL pointer is passed, //! DFU_ERR_INVALID_ADDR is returned. // //**************************************************************************** tTIDFUErr __stdcall TIDFUDeviceStringGet(tTIDFUHandle hHandle, unsigned char ucStringIndex, unsigned short usLanguageID, char *pcString, unsigned short *pusStringLen) { int iCount; tTIDFUDeviceState *pState; PUCHAR pucString; TRACE("TIDFUDeviceStringGet 0x%08x %d %d\n", hHandle, ucStringIndex, usLanguageID); // // Bomb out on bad parameters. // if(!hHandle) { TRACE("Bad handle.\n"); return(DFU_ERR_HANDLE); } if(!pcString || !pusStringLen) { TRACE("NULL pointer.\n"); return(DFU_ERR_INVALID_ADDR); } // // Get our state information. // pState = (tTIDFUDeviceState *)hHandle; // // Get the requested string descriptor. // pucString = GetStringDescriptor(pState->hUSB, ucStringIndex, usLanguageID); // // Was a string returned? // if(pucString == (PUCHAR)NULL) { TRACE("Failed to read string descriptor %d, language 0x%04x.\n", ucStringIndex, usLanguageID); return(DFU_ERR_NOT_FOUND); } // // How many bytes to we need to copy? // iCount = (int)pucString[0] - SIZE_STRING_DESC_HEADER; // // Is the passed buffer large enough to hold this? // if(iCount > (int)*pusStringLen) { // // No - truncate the string to the length passed. // iCount =(int)*pusStringLen; } // // Copy the UNICODE string into the client's buffer. // memcpy(pcString, pucString + SIZE_STRING_DESC_HEADER, iCount); // // Update the client's length to indicate the number fof bytes // actually written. // *pusStringLen = (USHORT)iCount; // // Free the string descriptor. // free(pucString); // // All is well so tell the caller they got their string. // return(DFU_OK); } //**************************************************************************** // //! Retrieves an ASCII string descriptor from a DFU device. //! //! \param psDevInfo is the structure defining the device whose string is //! being queried. //! \param ucStringIndex is the index of the string that is being queried. //! \param pcString points to a buffer into which the returned string is to //! be written. //! \param pusStringLen points to a variable containing the size of the //! pcString buffer (in bytes) on entry. If the string is read, this variable //! is updated to show the number of bytes written into pcString. //! //! This function retrieves a string descriptor from the device and converts it //! from UNICODE into 8-bit-per-character ASCII, writing the result into the //! supplied pcString buffer. The string returned is in the first language //! supported by the device. //! //! \return Returns DFU_OK on success or DFU_ERR_NOT_FOUND if the supplied //! string ID is invalid. If a NULL pointer is passed, DFU_ERR_INVALID_ADDR //! is returned. // //**************************************************************************** tTIDFUErr __stdcall TIDFUDeviceASCIIStringGet(tTIDFUHandle hHandle, unsigned char ucStringIndex, char *pcString, unsigned short *pusStringLen) { int iNumChars; tTIDFUDeviceState *pState; PUCHAR pucLangTable; PUCHAR pucDesc; errno_t eError; TRACE("TIDFUDeviceASCIIStringGet 0x%08x %d\n", hHandle, ucStringIndex); // // Bomb out on bad parameters. // if(!hHandle) { TRACE("Bad handle\n"); return(DFU_ERR_HANDLE); } if(!pcString || !pusStringLen) { TRACE("NULL pointer\n"); return(DFU_ERR_INVALID_ADDR); } // // We don't allow the caller to query string descriptor 0 via this call. // if(!ucStringIndex) { TRACE("String index 0 cannot be read via this call.\n"); return(DFU_ERR_NOT_FOUND); } // // Get our state information. // pState = (tTIDFUDeviceState *)hHandle; // // Get the language table so that we can determine the language code for // the first supported language. // pucLangTable = GetStringDescriptor(pState->hUSB, 0, 0); // // Did the table exist? If not, there are no strings offered by the device. // if(!pucLangTable) { TRACE("Can't read language table.\n"); return(DFU_ERR_NOT_FOUND); } // // Make sure the table is long enough. It has to have at least 1 language // entry in it. // if(pucLangTable[0] < (SIZE_STRING_DESC_HEADER + 2)) { TRACE("Language table appears corrupted.\n"); free(pucLangTable); return(DFU_ERR_NOT_FOUND); } // // Now that we have the language table, query the actual descriptor that // we need. // pucDesc = GetStringDescriptor(pState->hUSB, ucStringIndex, READ_SHORT(pucLangTable + 2)); // // Free the language table since we don't need it any more. // free(pucLangTable); // // Did we get the requested string descriptor? // if(!pucDesc) { TRACE("Can't read string descriptor %d for language 0x%04x.\n", ucStringIndex, READ_SHORT(pucLangTable + 2)); return(DFU_ERR_NOT_FOUND); } // // Is it a sensible length? // if(pucDesc[0] <= (SIZE_STRING_DESC_HEADER + 1)) { // // String must be corrupt - it's too short. // TRACE("String descriptor appears corrupted.\n"); free(pucDesc); return(DFU_ERR_NOT_FOUND); } // // We got the descriptor and the string is not zero length so now we need // to convert it into an ASCII string. First, how long is it? // iNumChars = (pucDesc[0] - SIZE_STRING_DESC_HEADER) / 2; // // Now create a UNICODE string object containing the string from the // descriptor. // CStringW strUnicode((WCHAR *)(pucDesc + SIZE_STRING_DESC_HEADER), iNumChars); // // If that worked, we now create a new ASCII string based on the Unicode // one. // CStringA strASCII(strUnicode); // // Now determine the length of the ASCII string and copy the content out // to the buffer provided by the caller. // iNumChars = strASCII.GetLength(); // // Can this string fit in the buffer provided by the caller? // if(iNumChars >= (int)*pusStringLen) { // // No - we need to truncate the string to the size of the buffer, // remembering to leave a place free for the terminating NULL. // iNumChars = *pusStringLen - 1; } // // Now copy the string into the buffer. // eError = strcpy_s(pcString, *pusStringLen, (LPCSTR)strASCII); // // Free our descriptor. // free(pucDesc); // // Tell the caller all is well (assuming it is) // if(eError) { TRACE("Error converting string from UNICODE to ASCII.\n"); return(DFU_ERR_UNKNOWN); } else { *pusStringLen = iNumChars; return(DFU_OK); } } //**************************************************************************** // //! Query DFU download-related parameters from a TI DFU device. //! //! \param hHandle is the handle of the DFU device whose information is to be //! read. //! \param psParams points to a structure which will be written with DFU //! parameters related to the device. //! //! This function retrieves various parameters related to the download from //! a TI device. These parameters include the device flash block size, //! the number of flash blocks (hence the size of the flash), the programmable //! address range, and the ID of the TI part itself. //! //! This function makes use of a TI-specific extension to the //! DFU protocol so is only available on TI devices. //! //! \return Returns DFU_OK on success or other error codes on failure. // //**************************************************************************** tTIDFUErr __stdcall TIDFUParamsGet(tTIDFUHandle hHandle, tTIDFUParams *psParams) { tTIDFUDeviceState *pState; tDFUDownloadInfoHeader sInfoCmd; tDFUDeviceInfo sDevInfo; tTIDFUErr eRetcode; TRACE("TIDFUDeviceParamsGet 0x%08x\n", hHandle); // // Bomb out on bad parameters. // if(!hHandle) { TRACE("Bad handle.\n"); return(DFU_ERR_HANDLE); } if(!psParams) { TRACE("NULL pointer.\n"); return(DFU_ERR_INVALID_ADDR); } // // Get our state information. // pState = (tTIDFUDeviceState *)hHandle; eRetcode = DFUDeviceInfoGet(pState, &sDevInfo); // // Set up the command we need to send to request the device information. // memset(&sInfoCmd, 0, sizeof(tDFUDownloadInfoHeader)); sInfoCmd.ucCommand = TI_CMD_INFO; // // Did the request complete successfully? // if(eRetcode == DFU_OK) { // // Copy the relevant fields from the device information structure // into the client's storage. // psParams->ulAppStartAddr = sDevInfo.ulAppStartAddr; psParams->ulFlashTop = sDevInfo.ulFlashTop; psParams->usFlashBlockSize = sDevInfo.usFlashBlockSize; psParams->usNumFlashBlocks = sDevInfo.usNumFlashBlocks; } return(eRetcode); } //**************************************************************************** // //! Determines whether the supplied data is a correctly formatted DFU image. //! //! \param hHandle is the handle of the DFU device which the image is destined //! to be used with. //! \param pcDFUImage is a pointer to the first byte of the image to check. //! \param ulImageLen is the number of bytes in the image data pointed to by //! \e pcDFUImage. //! \param pbTIFormat is a pointer which will be written to \e true if //! the supplied data appears to start with a valid TI DFU prefix. //! If the data ends in a valid DFU suffix structure but does not contain the //! TI prefix, this value will be written to \e false. //! //! This function checks a provided binary to determine whether it is a //! correctly formatted DFU image or not. A valid image contains a 16 byte //! suffix with a checksum and the IDs of the intended target device. An image //! is considered to be valid if the following criteria are met: //! //! 1. The CRC of the whole block calculates to 0 (i.e. the CRC of all but the //! last 4 bytes equals the CRC stored in the last 4 bytes). //! 2. The "DFU" suffix marker exists at the correct place at the end of the //! data block. //! 3. The vendor and products IDs read from the expected positions in the //! DFU suffix match the VID and PID of the device whose handle is passed. //! //! Additionally, if these conditions are met, the data is examined for the //! presence of a TI DFU prefix. This structure contains the //! address at which to flash the image and also the length of the payload. //! The pbTIFormat pointer is written to \e true if the following //! additional criteria are met: //! //! 1. The first byte of the data block is 0x01. //! 2. The unsigned long in bytes 4 through 7 of the image matches the //! value of the length of the block minus the DFU suffix (length read from //! the suffix itself) and TI prefix (8 bytes). //! 3. The unsigned short in bytes 2 and 3 of the image forms a sensible //! flash block number (address / 1024) for a TI device. //! //! \return Returns DFU_OK if a valid DFU suffix is found and the IDs it //! contains match those of the target device. If the suffix is valid but the //! IDs do not match, DFU_ERR_UNSUPPORTED is returned. If the suffix is //! invalid, DFU_ERR_INVALID_FORMAT is returned. Parameter errors are //! indicated by return codes DFU_ERR_INVALID_ADDR and DFU_ERR_HANDLE. // //**************************************************************************** tTIDFUErr __stdcall TIDFUIsValidImage(tTIDFUHandle hHandle, unsigned char *pcDFUImage, unsigned long ulImageLen, bool *pbTIFormat) { tTIDFUDeviceState *pState; unsigned long ulCRC; TRACE("TIDFUDeviceIsValidImage 0x%08x 0x%08x %d\n", hHandle, pcDFUImage, ulImageLen); // // Catch obviously bad parameters. // if(!hHandle) { return(DFU_ERR_HANDLE); } if(!pcDFUImage || !pbTIFormat) { return(DFU_ERR_INVALID_ADDR); } // // Get our state information. // pState = (tTIDFUDeviceState *)hHandle; // // Catch images that are obviously too small to contain the DFU suffix. // The suffix will be at least 16 bytes long and contains its length in // the 5th last byte of the block. // if((ulImageLen < 16) || (ulImageLen < pcDFUImage[ulImageLen - 5])) { return(DFU_ERR_INVALID_FORMAT); } // // First calculate the CRC of the data we've been passed. If this doesn't // come out to zero, it's not a valid DFU image. // ulCRC = CalculateCRC32(pcDFUImage, ulImageLen, 0xFFFFFFFF); if(ulCRC) { return(DFU_ERR_INVALID_FORMAT); } // // The CRC is valid so now check for the expected "DFU" marker in the // suffix structure we assume is at the end of the data. // if((pcDFUImage[ulImageLen - 6] != 'D') || (pcDFUImage[ulImageLen - 7] != 'F') || (pcDFUImage[ulImageLen - 8] != 'U')) { // // The marker is not correct. // return(DFU_ERR_INVALID_FORMAT); } // // Here we know that we have a valid DFU file intended for use with the // device we intend using it with. Does it have a TI prefix? It // does if the first byte is 0x01 and the length read from the supposed // prefix matches the expected length. Note that we do not check for a // valid target address here - we leave the device to do this for us. // if((pcDFUImage[0] != 0x01) || (READ_LONG(pcDFUImage + 4) != (ulImageLen - (pcDFUImage[ulImageLen - 5] + 8)))) { *pbTIFormat = false; } else { *pbTIFormat = true; } // // The DFU suffix looks valid but does it indicate that the image is for // the device whose handle we have been passed? // if((pState->usVID != READ_SHORT(&(pcDFUImage[ulImageLen - 12]))) || (pState->usPID != READ_SHORT(&(pcDFUImage[ulImageLen - 14])))) { // // The VID and PID in the file don't match the current device. // return(DFU_ERR_UNSUPPORTED); } else { // // At this point, we know the image is fine. // return(DFU_OK); } } //**************************************************************************** // //! Download a DFU-formatted binary image to device flash. //! //! \param hHandle is the handle of the DFU device whose flash is to be written. //! \param pcDFUImage is a pointer to the first byte of the image to download. //! \param ulImageLen is the number of bytes in the image data pointed to by //! \e pcDFUImage. //! \param bVerify should be set to \b true if the download is to be verified //! (by reading back the image and checking it against the original data) or //! \b false if verification is not necessary. //! \param bIgnoreIDs should be set to \b true if the DFU image is to be //! downloaded regardless of the fact that the DFU suffix contains a VID or PID //! that differs from the target device. If set to \b false, the call will //! fail with DFU_ERR_UNSUPPORTED if the device VID and PID do not match the //! values in the DFU suffix. //! \param hwndNotify is the handle of a window to which periodic notifications //! will be sent indicating the progress of the operation. If NULL, no status //! notifications will be sent. //! //! This function downloads a DFU-formatted binary to the device. A valid //! binary contains both the standard DFU footer suffix structure and also a //! TI-specific header informing the device of the address to //! which the image is to be written. If the data passed does not appear to //! contain this information,\b DFU_ERR_INVALID_FORMAT will be returned and //! the image will not be written to the device flash. //! //! This function is synchronous and will not return until the operation is //! complete. To receive periodic status updates during the operation, a //! window handle may be provided. This window will receive WM_TIDFU_STATUS //! messages during the download operation allowing an application to update //! its user interface accordingly. //! //! To flash a pure binary image without the DFU suffix or TI //! prefix, use TIDFUDownloadBin() instead of this function. //! //! Note that the TI-specific prefix structure contains the address at //! which the image is to be flashed so no parameter exists here to provide //! this information. //! //! This function uses no TI-specific extensions to the DFU protocol and //! should, therefore, allow a correctly formatted firmware image to be //! downloaded to any DFU device (though, frankly, this has not been tested). //! //! \return Returns DFU_OK on success or other error codes on failure. // //**************************************************************************** tTIDFUErr __stdcall TIDFUDownload(tTIDFUHandle hHandle, unsigned char *pcDFUImage, unsigned long ulImageLen, bool bVerify, bool bIgnoreIDs, HWND hwndNotify) { tTIDFUDeviceState *pState; tDFUGetStatusResponse sStatus; tTIDFUErr eRetcode; bool bExtensions; TRACE("TIDFUDownload 0x%08x 0x%08x %d %s, %s\n", hHandle, pcDFUImage, ulImageLen, (bVerify ? "Verify" : "Don't verify"), (bIgnoreIDs ? "Ignore IDs" : "Check IDs")); // // Bomb out on bad parameters. // if(!hHandle) { return(DFU_ERR_HANDLE); } if(!pcDFUImage) { return(DFU_ERR_INVALID_ADDR); } // // Get our state information. // pState = (tTIDFUDeviceState *)hHandle; // // Make sure this DFU device supports downloads. // if(!(pState->ucDFUAttributes && DFU_ATTR_CAN_DOWNLOAD)) { return(DFU_ERR_UNSUPPORTED); } // // Make sure the DFU device is in idle state and ready to receive // a new image download. // eRetcode = DFUMakeDeviceIdle(pState); if(eRetcode != DFU_OK) { return(eRetcode); } // // We should be all set to start the download now. Check to make sure that // the provided image is valid. // eRetcode = TIDFUIsValidImage(hHandle, pcDFUImage, ulImageLen, &bExtensions); // // Is the image valid? // if(!((eRetcode == DFU_OK) || (bIgnoreIDs && (eRetcode == DFU_ERR_UNSUPPORTED)))) { // // Either the image is invalid or it is for a device other than the // one we are talking to and we have not been told to ignore the // embedded IDs. Either way, we bomb out without doing anything. // return(DFU_ERR_INVALID_FORMAT); } // // The image is valid. We remove the DFU suffix since this is not sent // to the target. // ulImageLen -= pcDFUImage[ulImageLen - 5]; // // Download the contents of the file minus the DFU suffix which we don't // send to the device. // eRetcode = DFUDownloadBlock(pState, pcDFUImage, ulImageLen, hwndNotify); if((eRetcode == DFU_OK) && bVerify) { // // We finished the download so now we need to determine whether or not // we can verify the operation. If the device is manifest tolerant, // we query its status and expect it to go back to state IDLE at this // point. If this happens, we can read back the image to verify that // it is as expected. // eRetcode = DFUDeviceStatusGet(pState, &sStatus); if((eRetcode == DFU_OK) && (sStatus.bState == STATE_IDLE)) { // // The device is in IDLE state so we can proceed to verify the // downloaded image. // eRetcode = DFUVerifyBlock(pState, pcDFUImage, ulImageLen, hwndNotify); } else { // // We can't verify the image since the device didn't go back into // IDLE state. // TRACE("Can't verify download. Device is not IDLE.\n"); return(DFU_ERR_CANT_VERIFY); } } return(eRetcode); } //**************************************************************************** // //! Download a binary image to device flash. //! //! \param hHandle is the handle of the DFU device whose flash is to be written. //! \param pcBinaryImage is a pointer to the first byte of the image to //! download. //! \param ulImageLen is the number of bytes in the image data pointed to by //! \e pcDFUImage. //! \param ulStartAddr is the flash address at which the image is to be //! written. If this parameter is set to 0 and the target device supports //! TI extensions, the binary image will be downloaded to the //! currently-configured application start address. //! \param bVerify should be set to \b true if the download is to be verified //! (by reading back the image and checking it against the original data) or //! \b false if verification is not necessary. //! \param hwndNotify is the handle of a window to which periodic notifications //! will be send indicating the progress of the operation. If NULL, no status //! notifications will be sent. //! //! This function downloads a pure binary image containing no DFU suffix or //! TI header to the device at an address supplied by the caller. //! //! This function is synchronous and will not return until the operation is //! complete. To receive periodic status updates during the operation, a //! window handle may be provided. This window will receive WM_TIDFU_STATUS //! messages during the download operation allowing an application to update //! its user interface accordingly. //! //! To flash a DFU-formatted image, use TIDFUDownload() instead of this //! function. //! //! This function makes use of a TI-specific extension to the //! DFU protocol so is only available on TI devices. //! //! \return Returns DFU_OK on success, DFU_ERR_INVALID_ADDR if the start address //! is invalid or if the image is too large to fit within the available flash, //! or appropriate error codes on other failures. // //**************************************************************************** tTIDFUErr __stdcall TIDFUDownloadBin(tTIDFUHandle hHandle, unsigned char *pcBinaryImage, unsigned long ulImageLen, unsigned long ulStartAddr, bool bVerify, HWND hwndNotify) { tTIDFUDeviceState *pState; tDFUDownloadProgHeader sProg; tDFUGetStatusResponse sStatus; tTIDFUErr eRetcode; TRACE("TIDFUDownloadBin 0x%08x 0x%08x %d %s\n", hHandle, pcBinaryImage, ulImageLen, bVerify ? "Verify" : "Don't verify"); // // Bomb out on bad parameters. // if(!hHandle) { return(DFU_ERR_HANDLE); } if(!pcBinaryImage) { return(DFU_ERR_INVALID_ADDR); } // // Get our state information. // pState = (tTIDFUDeviceState *)hHandle; // // We only support binary downloads for TI targets since we // need to use a protocol extension to tell the board where to flash the // image. // if(!pState->bSupportsLMProtocol) { TRACE("Target does not support TI DFU protocol.\n"); return(DFU_ERR_UNSUPPORTED); } // // Make sure that the start address and length are valid and that the image // will fit into the programmable area of flash. // if((ulStartAddr + ulImageLen) > (unsigned long)(pState->usLastFlashBlock * 1024)) { TRACE("Image is located outside writeable area of flash.\n"); return(DFU_ERR_INVALID_ADDR); } // // First, make sure the target is idle and ready to receive a new // download. // eRetcode = DFUMakeDeviceIdle(pState); if(eRetcode != DFU_OK) { TRACE("Error %s (%d) attempting to get device into IDLE state.\n", TIDFUErrorStringGet(eRetcode), eRetcode); return(eRetcode); } // // First send the download header that tells the board where to put the // image. // sProg.ucCommand = TI_CMD_PROG; sProg.ucReserved = 0; sProg.usStartAddr = (unsigned short)(ulStartAddr / 1024); sProg.ulLength = ulImageLen; eRetcode = DFUDownloadTransfer(pState, true, (unsigned char *)&sProg, sizeof(tDFUDownloadProgHeader)); if(eRetcode != DFU_OK) { TRACE("Error %s (%d) sending PROG command to device.\n", TIDFUErrorStringGet(eRetcode), eRetcode); return(eRetcode); } // // Now we really are ready to download the image! // eRetcode = DFUDownloadBlock(pState, pcBinaryImage, ulImageLen, hwndNotify); if((eRetcode == DFU_OK) && bVerify) { // // We finished the download so now we need to determine whether or not // we can verify the operation. If the device is manifest tolerant, // we query its satus and expect it to go back to state IDLE at this // point. If this happens, we can read back the image to verify that // it is as expected. // eRetcode = DFUDeviceStatusGet(pState, &sStatus); if((eRetcode == DFU_OK) && (sStatus.bState == STATE_IDLE)) { tDFUDownloadBinHeader sBin; tDFUDownloadReadCheckHeader sRead; // // The device is in IDLE state so we can proceed to verify the // downloaded image. First we tell the device that we want it to // pass us raw data without any DFU header structures added. // memset(&sBin, 0, sizeof(tDFUDownloadBinHeader)); sBin.ucCommand = TI_CMD_BIN; sBin.bBinary = 1; eRetcode = DFUDownloadTransfer(pState, true, (unsigned char *)&sBin, sizeof(tDFUDownloadBinHeader)); if(eRetcode == DFU_OK) { // // Now tell it which area of flash we want to read. // memset(&sRead, 0, sizeof(tDFUDownloadReadCheckHeader)); sRead.ucCommand = TI_CMD_READ; sRead.usStartAddr = (unsigned short)(ulStartAddr / 1024); sRead.ulLength = ulImageLen; eRetcode = DFUDownloadTransfer(pState, true, (unsigned char *)&sRead, sizeof(tDFUDownloadReadCheckHeader)); if(eRetcode == DFU_OK) { // // Finally, read back what we downloaded and make sure that it // agrees with what was sent. // eRetcode = DFUVerifyBlock(pState, pcBinaryImage, ulImageLen, hwndNotify); } // // Revert to non-binary transfers // sBin.bBinary = 0; DFUDownloadTransfer(pState, true, (unsigned char *)&sBin, sizeof(tDFUDownloadBinHeader)); } } else { // // We can't verify the image since the device didn't go back into // IDLE state. // TRACE("Can't verify download. Device is not IDLE.\n"); return(DFU_ERR_CANT_VERIFY); } } return(eRetcode); } // // Hex Line Structure // typedef struct{ uint8_t record_type; uint32_t address; uint8_t word_count; uint8_t *buf; } hex_t; unsigned char process_line ( unsigned char *line, unsigned int len, hex_t *hex) { unsigned char cnt,i; unsigned char tmp[64]; static unsigned long addressH; unsigned long addressL; unsigned char line_cksum=0; unsigned long hex_cksum; /* If first character isn't a ':' line is invalid */ cnt = 0; if(line[cnt++] != ':') return 1; /* Grab byte count */ tmp[0] = line[cnt++]; tmp[1] = line[cnt++]; tmp[2] = 0; sscanf_s((const char *)tmp, "%X", &(hex->word_count)); //Accumulate Checksum line_cksum += hex->word_count & 0xFF; //Divide byte count by two to get word count hex->word_count = hex->word_count / 2; /* Grab Address */ tmp[0] = line[cnt++]; tmp[1] = line[cnt++]; tmp[2] = line[cnt++]; tmp[3] = line[cnt++]; tmp[4] = 0; sscanf_s((const char *)tmp, "%X", &(addressL)); //signed_tmp = atol((const char *)tmp); //addressL = signed_tmp; //Accumulate Checksum line_cksum += (addressL & 0xFF) + ((addressL & 0xFF00) >> 8); /* Grab Record Type */ tmp[0] = line[cnt++]; tmp[1] = line[cnt++]; tmp[2] = 0; sscanf_s((const char *)tmp, "%X", &(hex->record_type)); //signed_tmp = atol((const char *)tmp); //hex->record_type = signed_tmp; //Accumulate Checksum line_cksum += hex->record_type & 0xFF; /* Grab Data */ for(i=0 ; i < hex->word_count * 2 ; i++){ tmp[0] = line[cnt++]; tmp[1] = line[cnt++]; tmp[2] = 0; sscanf_s((const char *)tmp, "%X", &(hex->buf[i])); //signed_tmp = atol((const char *)tmp); //hex->buf[i] = signed_tmp; line_cksum += hex->buf[i]; } /* Grab Checksum */ tmp[0] = line[cnt++]; tmp[1] = line[cnt++]; tmp[2] = 0; sscanf_s((const char *)tmp, "%X", &(hex_cksum)); //signed_tmp = atol((const char *)tmp); //hex_cksum = signed_tmp; /* Calculate Checksum */ line_cksum = 0x100-line_cksum; /* Check checksums and length */ if((line_cksum != hex_cksum) | (cnt > len)){ return 1; }else{ /* Take action based on record type */ switch(hex->record_type){ case 0: hex->address = (addressH & 0xFFFF0000) | (addressL & 0xFFFF); break; case 1: break; case 2: break; case 3: break; case 4: /* Extended Linear Address Record */ addressH = (hex->buf[0] << 24) | (hex->buf[1] << 16); hex->address = (addressH & 0xFFFF0000) | (addressL & 0xFFFF); break; case 5: break; } } #ifdef PRINT_HEX printf("HEX %u @ 0x%08X: ", hex->record_type, hex->address ); for( i = 0; i < hex->word_count; i++ ) printf("%02X ", hex->buf[i] ); printf("\n"); #endif return (unsigned char) 0; } //**************************************************************************** // //! Download a binary image to device flash. //! //! \param hHandle is the handle of the DFU device whose flash is to be written. //! \param pcBinaryImage is a pointer to the first byte of the image to //! download. //! \param ulImageLen is the number of bytes in the image data pointed to by //! \e pcDFUImage. //! \param ulStartAddr is the flash address at which the image is to be //! written. If this parameter is set to 0 and the target device supports //! TI extensions, the binary image will be downloaded to the //! currently-configured application start address. //! \param bVerify should be set to \b true if the download is to be verified //! (by reading back the image and checking it against the original data) or //! \b false if verification is not necessary. //! \param hwndNotify is the handle of a window to which periodic notifications //! will be send indicating the progress of the operation. If NULL, no status //! notifications will be sent. //! //! This function downloads a pure binary image containing no DFU suffix or //! TI header to the device at an address supplied by the caller. //! //! This function is synchronous and will not return until the operation is //! complete. To receive periodic status updates during the operation, a //! window handle may be provided. This window will receive WM_TIDFU_STATUS //! messages during the download operation allowing an application to update //! its user interface accordingly. //! //! To flash a DFU-formatted image, use TIDFUDownload() instead of this //! function. //! //! This function makes use of a TI-specific extension to the //! DFU protocol so is only available on TI devices. //! //! \return Returns DFU_OK on success, DFU_ERR_INVALID_ADDR if the start address //! is invalid or if the image is too large to fit within the available flash, //! or appropriate error codes on other failures. // //**************************************************************************** tTIDFUErr __stdcall TIDFUDownloadHex(tTIDFUHandle hHandle, unsigned char *pcBinaryImage, unsigned long ulImageLen, bool bVerify, HWND hwndNotify) { tTIDFUDeviceState *pState; tDFUDownloadProgHeader sProg; tDFUGetStatusResponse sStatus; tTIDFUErr eRetcode; hex_t tCurrentHex; unsigned char pucHexArray[128]; unsigned long ulOldAddress = 0; unsigned char pucBlockBuffer[BLOCK_BUFFER_LENGTH]; unsigned long pulBBIndex = 0; unsigned short * pusBlockAddress; unsigned char ucFirst = 1; unsigned char ucTemp; unsigned char ucFailed = 0; unsigned char pucLineArray[128]; unsigned long ulCount = 0; tCurrentHex.buf = pucHexArray; TRACE("TIDFUDownloadHex 0x%08x 0x%08x %d %s\n", hHandle, pcBinaryImage, ulImageLen, bVerify ? "Verify" : "Don't verify"); // // Bomb out on bad parameters. // if(!hHandle) { return(DFU_ERR_HANDLE); } if(!pcBinaryImage) { return(DFU_ERR_INVALID_ADDR); } // // Get our state information. // pState = (tTIDFUDeviceState *)hHandle; // // We only support binary downloads for TI targets since we // need to use a protocol extension to tell the board where to flash the // image. // if(!pState->bSupportsLMProtocol) { TRACE("Target does not support TI DFU protocol.\n"); return(DFU_ERR_UNSUPPORTED); } // // Make sure that the start address and length are valid and that the image // will fit into the programmable area of flash. // //if((ulStartAddr + ulImageLen) > // (unsigned long)(pState->usLastFlashBlock * 1024)) //{ // TRACE("Image is located outside writeable area of flash.\n"); // return(DFU_ERR_INVALID_ADDR); //} // // First, make sure the target is idle and ready to receive a new // download. // eRetcode = DFUMakeDeviceIdle(pState); if(eRetcode != DFU_OK) { TRACE("Error %s (%d) attempting to get device into IDLE state.\n", TIDFUErrorStringGet(eRetcode), eRetcode); return(eRetcode); } // // read character from file until end of file or user hits q or Q // while(((ucTemp = *(unsigned char *)pcBinaryImage++) != EOF) && (ucFailed == 0)) { pucLineArray[ulCount++] = ucTemp; if( ucTemp == 10 ) { // // Parse line from Hex record // if( process_line(pucLineArray, ulCount, &tCurrentHex) ) { printf("error: line process failed\n"); ucFailed = 1; } ulCount = 0; if(tCurrentHex.record_type == 0) { // // Parse hex record into can frames and transmit appropriately // // // download command // if(ucFirst) { pusBlockAddress = (unsigned short *) tCurrentHex.address; } if(tCurrentHex.address != ulOldAddress && !ucFirst) { // // First send the download header that tells the board where to // put the image. // sProg.ucCommand = TI_CMD_PROG; sProg.ucReserved = 0; sProg.usStartAddr = (unsigned short)((unsigned long)pusBlockAddress / 1024); sProg.ulLength = pulBBIndex / 2; eRetcode = DFUDownloadTransfer(pState, true, (unsigned char *)&sProg, sizeof(tDFUDownloadProgHeader)); if(eRetcode != DFU_OK) { TRACE("Error %s (%d) sending PROG command to device.\n", TIDFUErrorStringGet(eRetcode), eRetcode); return(eRetcode); } // // Now we really are ready to download the image! // eRetcode = DFUDownloadBlock(pState, pucBlockBuffer, pulBBIndex, hwndNotify); if(eRetcode != DFU_OK) { TRACE("Error %s (%d) downloading data to device.\n", TIDFUErrorStringGet(eRetcode), eRetcode); return(eRetcode); } // // Reset block buffer // pusBlockAddress = (unsigned short *)tCurrentHex.address; pulBBIndex = 0; } memcpy(pucBlockBuffer + pulBBIndex, tCurrentHex.buf, tCurrentHex.word_count * 2); pulBBIndex += 2 * tCurrentHex.word_count; ulOldAddress = tCurrentHex.address + tCurrentHex.word_count; ucFirst = 0; } else if(tCurrentHex.record_type == 1) { // // End of file // // // Flush any remaining data // // // First send the download header that tells the board where // to put the image. // sProg.ucCommand = TI_CMD_PROG; sProg.ucReserved = 0; sProg.usStartAddr = (unsigned short)((unsigned long)pusBlockAddress / 1024); sProg.ulLength = pulBBIndex / 2; eRetcode = DFUDownloadTransfer(pState, true, (unsigned char *)&sProg, sizeof(tDFUDownloadProgHeader)); if(eRetcode != DFU_OK) { TRACE("Error %s (%d) sending PROG command to device.\n", TIDFUErrorStringGet(eRetcode), eRetcode); return(eRetcode); } // // Now we really are ready to download the image! // eRetcode = DFUDownloadBlock(pState, pucBlockBuffer, pulBBIndex, hwndNotify); if(eRetcode != DFU_OK) { TRACE("Error %s (%d) downloading data to device.\n", TIDFUErrorStringGet(eRetcode), eRetcode); return(eRetcode); } break; //return(eRetcode); } if( ulCount >= 128 ) { printf("error: max line length exceeded\n"); ucFailed = 1; } }//end hex file line } //end read from file if((eRetcode == DFU_OK) && bVerify) { // // We finished the download so now we need to determine whether or not // we can verify the operation. If the device is manifest tolerant, // we query its satus and expect it to go back to state IDLE at this // point. If this happens, we can read back the image to verify that // it is as expected. // eRetcode = DFUDeviceStatusGet(pState, &sStatus); if((eRetcode == DFU_OK) && (sStatus.bState == STATE_IDLE)) { tDFUDownloadBinHeader sBin; tDFUDownloadReadCheckHeader sRead; // // The device is in IDLE state so we can proceed to verify the // downloaded image. First we tell the device that we want it to // pass us raw data without any DFU header structures added. // memset(&sBin, 0, sizeof(tDFUDownloadBinHeader)); sBin.ucCommand = TI_CMD_BIN; sBin.bBinary = 1; eRetcode = DFUDownloadTransfer(pState, true, (unsigned char *)&sBin, sizeof(tDFUDownloadBinHeader)); if(eRetcode == DFU_OK) { // // Now tell it which area of flash we want to read. // memset(&sRead, 0, sizeof(tDFUDownloadReadCheckHeader)); sRead.ucCommand = TI_CMD_READ; sRead.usStartAddr = (unsigned short)((unsigned long)pusBlockAddress / 1024); sRead.ulLength = pulBBIndex / 2; eRetcode = DFUDownloadTransfer(pState, true, (unsigned char *)&sRead, sizeof(tDFUDownloadReadCheckHeader)); if(eRetcode == DFU_OK) { // // Finally, read back what we downloaded and make sure that // it agrees with what was sent. // eRetcode = DFUVerifyBlock(pState, pucBlockBuffer, pulBBIndex, hwndNotify); } // // Revert to non-binary transfers // sBin.bBinary = 0; DFUDownloadTransfer(pState, true, (unsigned char *)&sBin, sizeof(tDFUDownloadBinHeader)); } } else { // // We can't verify the image since the device didn't go back into // IDLE state. // TRACE("Can't verify download. Device is not IDLE.\n"); return(DFU_ERR_CANT_VERIFY); } } return(eRetcode); } //**************************************************************************** // //! Erases a section of the device flash. //! //! \param hHandle is the handle of the DFU device whose flash is to be erased. //! \param ulStartAddr is the address of the first byte of flash to be erased. //! This must correspond to a flash block boundary, typically multiples of 1024 //! bytes. If this parameter is set to 0 the entire writeable flash region //! will be erased. //! \param ulEraseLen is the number of bytes of flash to erase. This must be a //! multiple of the flash block size, typically 1024 bytes. //! \param bVerify should be set to \b true if the erase is to be verified //! (by reading back the flash blocks and ensuring that all bytes contain 0xFF) //! or \b false if verification is not necessary. //! \param hwndNotify is the handle of a window to which periodic notifications //! will be send indicating the progress of the operation. If NULL, no status //! notifications will be sent. //! //! This function erases a section of the device flash and, optionally, checks //! that the resulting area has been correctly erased before returning. //! //! The function is synchronous and will not return until the operation is //! complete. To receive periodic status updates during the operation, a //! window handle may be provided. This window will receive WM_TIDFU_STATUS //! messages during the erase operation allowing an application to update //! its user interface accordingly. //! //! This function makes use of a TI-specific extension to the //! DFU protocol so is only available on TI devices. //! //! \return Returns DFU_OK on success or DFU_ERR_INVALID_ADDR if the start //! address is not on a flash block boundary or if the erase length is not a //! multiple of the flash block size. Appropriate return codes will be used //! to indicate other errors. // //**************************************************************************** tTIDFUErr __stdcall TIDFUErase(tTIDFUHandle hHandle, unsigned long ulStartAddr, unsigned long ulEraseLen, bool bVerify, HWND hwndNotify) { tTIDFUDeviceState *pState; tDFUDownloadEraseHeader sErase; tTIDFUErr eRetcode; TRACE("TIDFUErase 0x%08x 0x%08x %d %s\n", hHandle, ulStartAddr, ulEraseLen, bVerify ? "Verify" : "Don't verify"); // // Bomb out on bad parameters. // if(!hHandle) { return(DFU_ERR_HANDLE); } // // Get our state information. // pState = (tTIDFUDeviceState *)hHandle; // // This operation is only supported if the target device supports the // TI protocol. // if(!pState->bSupportsLMProtocol) { TRACE("Device does not support TI protocol.\n"); return(DFU_ERR_UNSUPPORTED); } // // Make sure we were passed a block start address and a length that is an // integer multiple of the block size. // if(ulStartAddr == 0) { // // If the start address passed is 0, we erase the whole writable region // of flash. // ulStartAddr = pState->usFirstFlashBlock * 1024; ulEraseLen = (pState->usLastFlashBlock - pState->usFirstFlashBlock) * 1024; TRACE("Erasing entire writeable region. %dKB from 0x%x\n", ulEraseLen / 1024, ulStartAddr); } else { // // Is the start address correctly aligned? // if(ulStartAddr & (1024 - 1)) { TRACE("Address is not a multiple of 1024\n"); return(DFU_ERR_INVALID_ADDR); } if(!ulEraseLen || (ulEraseLen & (1024 - 1))) { TRACE("Size is not a multiple of 1024 or is zero.\n"); return(DFU_ERR_INVALID_SIZE); } } // // First, make sure the target is idle and ready to receive a new // command. // eRetcode = DFUMakeDeviceIdle(pState); if(eRetcode != DFU_OK) { TRACE("Error %s (%d) attempting to get device into IDLE state.\n", TIDFUErrorStringGet(eRetcode), eRetcode); return(eRetcode); } // // Send the target the command to erase the relevant section of memory. // sErase.ucCommand = TI_CMD_ERASE; sErase.ucReserved = 0; sErase.usStartAddr = (unsigned short)(ulStartAddr / 1024); sErase.usNumBlocks = (unsigned short)(ulEraseLen / 1023); sErase.ucReserved2[0] = 0; sErase.ucReserved2[1] = 0; eRetcode = DFUDownloadTransfer(pState, true, (unsigned char *)&sErase, sizeof(tDFUDownloadEraseHeader)); // // Did the operation complete successfully and do we want to verify the // erase operation? // if((eRetcode == DFU_OK) && bVerify) { tDFUDownloadReadCheckHeader sCheck; // // Yes - now go ahead and verify that the flash is indeed erased. // sCheck.ucCommand = TI_CMD_CHECK; sCheck.ucReserved = 0; sCheck.usStartAddr = (unsigned short)(ulStartAddr / 1024); sCheck.ulLength = ulEraseLen; eRetcode = DFUDownloadTransfer(pState, true, (unsigned char *)&sCheck, sizeof(tDFUDownloadReadCheckHeader)); } return(eRetcode); } //**************************************************************************** // //! Verify that a section of the device flash is blank. //! //! \param hHandle is the handle of the DFU device whose flash is to be checked. //! \param ulStartAddr is the address of the first byte of flash to be checked. //! This must correspond to a flash block boundary, typically multiples of 1024 //! bytes. If this parameter is set to 0 the entire writeable flash region //! will be checked. //! \param ulLen is the number of bytes of flash to check. //! //! This function checks a region of the device flash and reports whether or // not it is blank (with all bytes containing value 0xFF). //! //! The function is synchronous and will not return until the operation is //! complete. //! //! This function makes use of a TI-specific extension to the //! DFU protocol so is only available on TI devices. //! //! \return Returns DFU_OK on success or DFU_ERR_INVALID_ADDR if the start //! address is not on a flash block boundary or if the erase length is not a //! multiple of the flash block size. If the region is not blank, the return //! code will be DFU_ERR_DNLOAD_FAIL. // //**************************************************************************** tTIDFUErr __stdcall TIDFUBlankCheck(tTIDFUHandle hHandle, unsigned long ulStartAddr, unsigned long ulLen) { tTIDFUDeviceState *pState; tDFUDownloadReadCheckHeader sCheck; tTIDFUErr eRetcode; TRACE("TIDFUBlankCheck 0x%08x 0x%08x %d\n", hHandle, ulStartAddr, ulLen); // // Bomb out on bad parameters. // if(!hHandle) { return(DFU_ERR_HANDLE); } // // Get our state information. // pState = (tTIDFUDeviceState *)hHandle; // // This operation is only supported if the target device supports the // TI protocol. // if(!pState->bSupportsLMProtocol) { TRACE("Device does not support TI protocol.\n"); return(DFU_ERR_UNSUPPORTED); } // // Make sure we were passed a block start address and a length that is an // integer multiple of the block size. // if(ulStartAddr == 0) { // // If the start address passed is 0, we check the whole writable region // of flash. // ulStartAddr = pState->usFirstFlashBlock * 1024; ulLen = (pState->usLastFlashBlock - pState->usFirstFlashBlock) * 1024; TRACE("Erasing entire writeable region. %dKB from 0x%x\n", ulLen / 1024, ulStartAddr); } else { // // Is the start address correctly aligned? // if(ulStartAddr & (1024 - 1)) { TRACE("Address is not a multiple of 1024\n"); return(DFU_ERR_INVALID_ADDR); } if(!ulLen || (ulLen & 3)) { TRACE("Size is not a multiple of 4 or is zero.\n"); return(DFU_ERR_INVALID_SIZE); } } // // First, make sure the target is idle and ready to receive a new // command. // eRetcode = DFUMakeDeviceIdle(pState); if(eRetcode != DFU_OK) { TRACE("Error %s (%d) attempting to get device into IDLE state.\n", TIDFUErrorStringGet(eRetcode), eRetcode); return(eRetcode); } // // Verify that the flash is indeed erased. // sCheck.ucCommand = TI_CMD_CHECK; sCheck.ucReserved = 0; sCheck.usStartAddr = (unsigned short)(ulStartAddr / 1024); sCheck.ulLength = ulLen; eRetcode = DFUDownloadTransfer(pState, true, (unsigned char *)&sCheck, sizeof(tDFUDownloadReadCheckHeader)); // // Tell the caller whether the flash is blank or not. // return(eRetcode); } //**************************************************************************** // //! Read back a section of the device flash. //! //! \param hHandle is the handle of the DFU device whose flash is to be read. //! \param pcBuffer points to a buffer of at least \e ulImageLen bytes into //! which the returned data will be written. If \e bRaw is set to \b false, //! the buffer must be 24 bytes longer than the actual data requested to //! accommodate the DFU prefix and suffix which are added during the upload //! process. //! \param ulStartAddr is the address of the first byte of flash to be read. //! \param ulImageLen is the number of bytes of flash to read. If \e bRaw is //! set to \b false, this length must be increased by 24 bytes to accommodate //! the DFU prefix and suffix added during the upload process. //! \param bRaw indicates whether the returned image will be wrapped in a //! TI-specific prefix and DFU standard suffix. If \e false, the wrappers //! will be omitted and the raw data returned. If \e true, the wrappers will //! be included allowing the returned image to be written to a device later //! by calling TIDFUDownload(). //! \param hwndNotify is the handle of a window to which periodic notifications //! will be send indicating the progress of the operation. If NULL, no status //! notifications will be sent. //! //! This function reads back a section of the device flash into a buffer //! supplied by the caller. The data returned may be either raw data containing //! no DFU control prefix and suffix or a DFU-wrapped image suitable for //! later download via a call to TIDFUDownload(). If a DFU-wrapped image is //! requested, the buffer pointed to by pcBuffer must be 24 bytes larger than //! the number of bytes of device flash which is to be read. For example, to //! read 1024 bytes of flash wrapped as a DFU image, \e ulImageLen must be //! set to (1024 + 24) and \e pcBuffer allocated accordingly. //! //! The function is synchronous and will not return until the operation is //! complete. To receive periodic status updates during the operation, a //! window handle may be provided. This window will receive WM_DFU_PROGRESS //! messages during the erase operation allowing an application to update //! its user interface accordingly. //! //! This function makes use of a TI-specific extension to the //! DFU protocol so is only available on TI devices. //! //! \return Returns DFU_OK on success or DFU_ERR_INVALID_ADDR if the start //! address is not on a flash block boundary or if the erase length is not a //! multiple of the flash block size. Appropriate return codes will be used //! to indicate other errors. // //**************************************************************************** tTIDFUErr __stdcall TIDFUUpload(tTIDFUHandle hHandle, unsigned char *pcBuffer, unsigned long ulStartAddr, unsigned long ulImageLen, bool bRaw, HWND hwndNotify) { tTIDFUDeviceState *pState; tDFUDownloadReadCheckHeader sRead; tDFUDownloadBinHeader sBin; tTIDFUErr eRetcode; TRACE("TIDFUUpload 0x%08x 0x%08x %d %s\n", hHandle, ulStartAddr, ulImageLen, bRaw ? "Raw" : "DFU"); // // Bomb out on bad parameters. // if(!hHandle) { return(DFU_ERR_HANDLE); } // // Get our state information. // pState = (tTIDFUDeviceState *)hHandle; // // This operation is only supported if the target device supports the // TI protocol. // if(!pState->bSupportsLMProtocol) { TRACE("Device does not support TI protocol.\n"); return(DFU_ERR_UNSUPPORTED); } // // Make sure we were passed a start address that is an integer multiple of // 1024. // if(ulStartAddr & (1024 - 1)) { TRACE("Start address is not a multiple of 1024.\n"); return(DFU_ERR_INVALID_ADDR); } // // Make sure that the buffer passed is large enough to hold the DFU prefix // and suffix if asked for a DFU-formatted image. // if(!bRaw && (ulImageLen <= (16 + sizeof(tDFUDownloadProgHeader)))) { TRACE("Buffer too small for prefix and suffix.\n"); return(DFU_ERR_INVALID_SIZE); } // // First, make sure the target is idle and ready to receive a new // command. // eRetcode = DFUMakeDeviceIdle(pState); if(eRetcode != DFU_OK) { TRACE("Error %s (%d) attempting to get device into IDLE state.\n", TIDFUErrorStringGet(eRetcode), eRetcode); return(eRetcode); } // // Now we can go ahead and initiate the upoad. First we need to tell // the target what region of flash we want to read. Note that we adjust // the image length to remove the DFU prefix and suffix. // sRead.ucCommand = TI_CMD_READ; sRead.ucReserved = 0; sRead.usStartAddr = (unsigned short)(ulStartAddr / 1024); sRead.ulLength = ulImageLen - (bRaw ? 0 : 16 + sizeof(tDFUDownloadProgHeader)); eRetcode = DFUDownloadTransfer(pState, true, (unsigned char *)&sRead, sizeof(tDFUDownloadReadCheckHeader)); // // Only do the next operation if the last one succeeded. // if(eRetcode == DFU_OK) { // // Now tell the target whether to download an image with a DFU header or // just raw binary. // memset(&sBin, 0, sizeof(tDFUDownloadBinHeader)); sBin.ucCommand = TI_CMD_BIN; sBin.bBinary = bRaw ? 1 : 0; eRetcode = DFUDownloadTransfer(pState, true, (unsigned char *)&sBin, sizeof(tDFUDownloadBinHeader)); if(eRetcode == DFU_OK) { // // If no errors were reported, go ahead and perform the upload. // if(eRetcode == DFU_OK) { // // Actually read back the data from the device. The number of // bytes we read is the image size passed minus the 16 bytes // for the DFU suffix if we have been asked for a DFU image. // The device automatically adds the 8 byte prefix structure // to the length of data we request via the TI_CMD_READ // sent earlier so we don't need to adjust for the prefix at // this point. // eRetcode = DFUUploadBlock(pState, pcBuffer, (int)ulImageLen - (bRaw ? 0 : 16), false, hwndNotify); // // Revert to the default DFU format for future uploads. // sBin.bBinary = 0; DFUDownloadTransfer(pState, true, (unsigned char *)&sBin, sizeof(tDFUDownloadBinHeader)); // // If the upload completed successfully and we have been asked // for a DFU-format image, we need to populate the DFU // suffix structure at the end of the downloaded data. // if((eRetcode == DFU_OK) && !bRaw) { DFUSuffixPopulate(pState, pcBuffer, ulImageLen - 16); } } } } return(eRetcode); } //**************************************************************************** // //! Queries the current status of the DFU device. //! //! \param hHandle is the handle of the DFU device which is to be queried. //! \param pStatus points to storage which will be written with the DFU status //! returned by the device. //! //! This call may be made to receive detailed error status from the //! connected DFU device. //! //! \return Returns DFU_OK if the operation completed without any errors //! reported, DFU_ERR_TIMEOUT if the control transaction timed out, //! DFU_ERR_DISCONNECTED if the device was disconnected, DFU_ERR_STALL if the //! device stalled endpoint 0 to indicate an error or DFU_ERR_UNKNOWN if //! some other low level USB error occurred. // //**************************************************************************** tTIDFUErr __stdcall TIDFUStatusGet(tTIDFUHandle hHandle, tDFUStatus *pStatus) { tTIDFUErr eRetcode; tDFUGetStatusResponse sStatus; tTIDFUDeviceState *pState; TRACE("TIDFUStatusGet 0x%08x\n", hHandle); // // Bomb out on bad parameters. // if(!hHandle) { return(DFU_ERR_HANDLE); } if(!pStatus) { return(DFU_ERR_INVALID_ADDR); } // // Get our state information. // pState = (tTIDFUDeviceState *)hHandle; // // Get the current device status structure. // eRetcode = DFUDeviceStatusGet(pState, &sStatus); // // Did the request succeed? // if(eRetcode == DFU_OK) { // // Yes - write the status to the caller's storage. // TRACE("Status is %d\n", sStatus.bStatus); *pStatus = (tDFUStatus)sStatus.bStatus; } else { TRACE("Error %d getting status\n", eRetcode); } // // Tell the caller how we got on. // return(eRetcode); } //**************************************************************************** // //! Returns a string describing the passed error code. //! //! \param eError is the error code whose human-readable description is being //! queried. //! //! This call is provided for debug purposes. It maps the return code from an //! TIDFU function into a human readable string suitable for, for example, //! debug trace output. //! //! \return Returns a pointer to a string describing the passed return code. // //**************************************************************************** char * __stdcall TIDFUErrorStringGet(tTIDFUErr eError) { switch(eError) { case DFU_ERR_VERIFY_FAIL: return("DFU_ERR_VERIFY_FAIL"); case DFU_ERR_CANT_VERIFY: return("DFU_ERR_CANT_VERIFY"); case DFU_ERR_DNLOAD_FAIL: return("DFU_ERR_DNLOAD_FAIL"); case DFU_ERR_STALL: return("DFU_ERR_STALL"); case DFU_ERR_TIMEOUT: return("DFU_ERR_TIMEOUT"); case DFU_ERR_DISCONNECTED: return("DFU_ERR_DISCONNECTED"); case DFU_ERR_INVALID_SIZE: return("DFU_ERR_INVALID_SIZE"); case DFU_ERR_INVALID_ADDR: return("DFU_ERR_INVALID_ADDR"); case DFU_ERR_INVALID_FORMAT: return("DFU_ERR_INVALID_FORMAT"); case DFU_ERR_UNSUPPORTED: return("DFU_ERR_UNSUPPORTED"); case DFU_ERR_UNKNOWN: return("DFU_ERR_UNKNOWN"); case DFU_ERR_MEMORY: return("DFU_ERR_MEMORY"); case DFU_ERR_NOT_FOUND: return("DFU_ERR_NOT_FOUND"); case DFU_ERR_HANDLE: return("DFU_ERR_HANDLE"); case DFU_OK: return("DFU_OK"); default: return("*** Invalid ***"); } } //**************************************************************************** // //! Instructs a runtime DFU device to switch into DFU mode. //! //! \param hHandle is the handle of the DFU device which is to be switched //! into DFU mode (from runtime mode). //! //! This call may be used to signal a runtime DFU device that it is to switch //! into DFU mode in preparation for a firmware upgrade. On return, handle //! hHandle will have been closed since the device will detach from the USB //! bus as a result of receiving the signal. Callers must reenumerate and //! reopen the device after this function has been executed. //! //! \return Returns DFU_OK on success or other error codes on failure. // //**************************************************************************** tTIDFUErr __stdcall TIDFUModeSwitch(tTIDFUHandle hHandle) { tTIDFUDeviceState *pState; USHORT usCount; TRACE("TIDFUModeSwitch 0x%08x\n", hHandle); // // Bomb out on bad parameters. // if(!hHandle) { return(DFU_ERR_HANDLE); } // // Get our state information. // pState = (tTIDFUDeviceState *)hHandle; // // Send a DETACH message to the DFU device. The device will detach // as a result of this request so we expect a bad return code here. // Endpoint0Transfer(pState->hUSB, (REQUEST_TRANSFER_OUT | REQUEST_TYPE_CLASS | REQUEST_RECIPIENT_INTERFACE), USBD_DFU_REQUEST_DETACH, 0, pState->usInterface, 0, (PUCHAR)NULL, &usCount); // // Close the device handle. // TerminateDevice(pState->hUSB); // // Free our state data. // free(pState); // // Tell the caller if all is well. // return(DFU_OK); } // // End of File //