c2000ware-core-sdk/utilities/tools/f2806x/tidfu/tidfu.cpp
2023-06-26 16:14:08 +05:30

4285 lines
136 KiB
C++

//
// TIDFU.cpp : A library providing Device Firmware Upgrade function for use
// with the TI TI USB boot loader.
//
//
// Included Files
//
#include "stdafx.h"
#include <windows.h>
#include <stdint.h>
#include <errno.h>
#include <initguid.h>
#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
//