#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <shlwapi.h>
#include <setupapi.h>
extern "C" {
#include <hidsdi.h>
#include <hidpi.h>
}
#include <stdio.h>
#include "BSL_Command_Definitions_5xx.h"
#include "fileio.h"
int uputs(PCTSTR s,int l=-1) {
if (l==-1) l=lstrlen(s);
char b[1024];
CharToOemBuff(s,b,l);
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),b,l,(PDWORD)&l,NULL);
return l;
}
int uvprintf(PCTSTR t, va_list va) {
TCHAR s[1024];
DWORD l=_vsntprintf(s,elemof(s),t,va); // No need for NULL termination here
return uputs(s,l);
}
int _cdecl uprintf(PCTSTR t,...) {
va_list va;
va_start(va,t);
int r=uvprintf(t,va);
va_end(va);
return r;
}
int uvprintf(UINT id,va_list va) {
TCHAR t[1024];
LoadString(0,id,t,elemof(t));
return uvprintf(t,va);
}
int _cdecl uprintf(UINT id,...) {
va_list va;
va_start(va,id);
int r=uvprintf(id,va);
va_end(va);
return r;
}
// CRC calculation as in MSP430 bootloader code
struct CRC16{
WORD crc;
inline void Init(WORD init) {crc=init;};
void Input(BYTE b);
inline WORD Get() const {return crc;};
};
// f(x) = x**16 + x**12 + x**5 + 1
void CRC16::Input(BYTE data) {
WORD x;
x = ((crc>>8) ^ data) & 0xff;
x ^= x>>4;
crc = crc<<8 ^ x<<12 ^ x<<5 ^ x;
}
/**********************************
* Layer 1: Universal HID routine *
**********************************/
// Opens the n'th HID device that has matching VID (LOWORD) and PID (HIWORD),
// Use CloseHandle() to free the handle returned.
// Returns 0 when failing.
static HANDLE OpenUsbHid(DWORD vid_pid, int n) {
HANDLE h=0;
DWORD i;
HDEVINFO devs;
GUID hidGuid; // GUID for HID driver
HidD_GetHidGuid(&hidGuid);
devs = SetupDiGetClassDevs(&hidGuid, NULL, 0, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if (devs==INVALID_HANDLE_VALUE) return h;
for (i=0;;i++) {
DWORD size = 0;
PSP_DEVICE_INTERFACE_DETAIL_DATA interface_detail;
union{
struct{
SP_DEVICE_INTERFACE_DATA devinterface;
SP_DEVINFO_DATA devinfo;
};
HIDD_ATTRIBUTES deviceAttributes;
}s; // some shared buffers not used the same time (does the optimizer see that?)
if (h) CloseHandle(h), h=0;
s.devinterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
if (!SetupDiEnumDeviceInterfaces(devs, 0, &hidGuid, i, &s.devinterface)) break;
// See how large a buffer we require for the device interface details
SetupDiGetDeviceInterfaceDetail(devs, &s.devinterface, NULL, 0, &size, 0);
s.devinfo.cbSize = sizeof(SP_DEVINFO_DATA);
interface_detail = (PSP_DEVICE_INTERFACE_DETAIL_DATA) new BYTE[size];
if (!interface_detail) continue;
interface_detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
s.devinfo.cbSize = sizeof(SP_DEVINFO_DATA);
if (!SetupDiGetDeviceInterfaceDetail(devs, &s.devinterface, interface_detail, size, 0, &s.devinfo)) {
delete[] interface_detail; // ignore this entry in case of error
continue;
}
h = CreateFile(interface_detail->DevicePath,GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL);
delete[] interface_detail;
if (h == INVALID_HANDLE_VALUE) h = 0;
if (!h) continue;
if (!HidD_GetAttributes(h, &s.deviceAttributes)) continue;
if (*(DWORD*)&s.deviceAttributes.VendorID != vid_pid) continue;
if (!n) break; // found my device
n--; // iterate to next device
}
SetupDiDestroyDeviceInfoList(devs);
return h;
}
/***********************************************************************************
* Layer 2: Basic HID routines for MSP430 communication wrappers (see PDF page 24) *
***********************************************************************************/
// All these functions return negative error codes.
// Non-negative return values indicate number of bytes transferred.
#pragma pointers_to_members(best_case,single_inheritance)
struct PI{ // PI = Peripheral Interface (Texas Instruments nomenclature)
HANDLE hCom;
virtual int Open()=0;
virtual int Write(const BYTE*, int)=0;
virtual int Read(BYTE*, int)=0;
virtual void Close()=0 {}
virtual ~PI() {Close();}
};
struct USBPI:PI{
DWORD VID_PID; // defaults to 0x02002047
int n; // number of attached MSP430F5xx device in bootloader mode (most often: zero)
OVERLAPPED ovl; // for handling timeouts
HIDP_CAPS caps; // to have buffer sizes
USBPI(PTSTR initspec=NULL);// constructor with string initializer from command line
virtual int Open();
virtual int Write(const BYTE*buf, int siz);
virtual int Read(BYTE*buf, int siz);
virtual void Close();
};
// Constructor
USBPI::USBPI(PTSTR initspec) {
ZeroMemory(&hCom,(BYTE*)&caps-(BYTE*)&hCom);
VID_PID=0x02002047; // TODO: Extracting VID&PID out of <initspec>
n=initspec?StrToInt(initspec):0;
}
// Open the USB device using preset <HID_VID> and <n> members
int USBPI::Open() {
// if (hCom) Close();
if (hCom) return 0;
hCom=OpenUsbHid(VID_PID,n);
if (!hCom) return -3;
PHIDP_PREPARSED_DATA pd;
if (!HidD_GetPreparsedData(hCom,&pd)) return -4;
if (!HidP_GetCaps(pd,&caps)) return -4;
HidD_FreePreparsedData(pd);
ovl.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
return 0;
}
// Write bytes to HID device with report ID = 63
// Returns number of bytes sent, i.e. equals <siz>, or a negative error code
// If <siz>=0, this routine sends one packet with null-byte payload (unused)
int USBPI::Write(const BYTE*buf, int siz) {
if (!hCom) return -2;
if ((unsigned)siz>(unsigned)(caps.OutputReportByteLength-2)) return -1;
BYTE*report = new BYTE[caps.OutputReportByteLength];
report[0]=63; // report ID
report[1]=(BYTE)siz; // payload length field
memcpy(report+2,buf,siz);
DWORD bw;
if (!WriteFile(hCom,report,caps.OutputReportByteLength,&bw,&ovl)
&& (GetLastError()!=ERROR_IO_PENDING
|| WaitForSingleObject(ovl.hEvent,1000)
|| !GetOverlappedResult(hCom,&ovl,&bw,FALSE))) {
CancelIo(hCom);
siz=-5; // TimeOut
}
delete[] report;
return siz;
}
// Read bytes from HID device ignoring report ID
// report[0] = report ID
// report[1] = length of data, including CMD byte
// report[2] = "CMD" byte, either BSL_DATA_REPLY or BSL_MESSAGE_REPLY
// In case of BSL_MESSAGE_REPLY and report[3]!=0, an error code will be returned.
int USBPI::Read(BYTE*buf, int siz) {
if (!hCom) return -2;
if (siz<0) return -1;
BYTE *report = new BYTE[caps.InputReportByteLength];
DWORD br;
int l;
if (!ReadFile(hCom,report,caps.InputReportByteLength,&br,&ovl)
&& (GetLastError()!=ERROR_IO_PENDING
|| WaitForSingleObject(ovl.hEvent,1000)
|| !GetOverlappedResult(hCom,&ovl,&br,FALSE))) {
CancelIo(hCom);
l=-5;
goto exi;
}
l=report[1];
if (--l<0) {l=-10; goto exi;} // malformed structure
switch (report[2]) {
case BSL_DATA_REPLY: break;
case BSL_MESSAGE_REPLY: {
if (l!=1) {l=-10; goto exi;} // invalid structure
if (report[3]) {l=report[3]-256; goto exi;} // return numerical error report, shifted by -256
else if (siz) {l=-11; goto exi;} // success code given, but more data expected
}break; // OK answer (return value would be 0)
default: l=-10; goto exi; // unknown CMD
}
if (l>siz) l=siz; // if buffer too small...
if (buf) memcpy(buf,report+3,l); // If no buffer address given, discard received bytes
exi:
delete[] report;
return l;
}
void USBPI::Close() {
if (hCom && CloseHandle(hCom)) hCom=0;
if (ovl.hEvent && CloseHandle(ovl.hEvent)) ovl.hEvent=0;
}
/**************************************************************************
* Layer 2a: Helper functions around one instance of two possible objects *
**************************************************************************/
// Implements one of both interfaces, either UARTPI or USBPI
PI*pi;
// Opens the communication port, if not yet done
// Returns error code -3 when failing
int OpenCom() {
if (!pi) pi = new USBPI; // initializing VTABLE for USB
if (!pi) return -8;
return pi->Open();
}
// Simply one command byte (RX Password, Unlock/Lock Info, Mass Erase, TX BSL Version, TX Buffer Size)
int HidWrite1(BYTE cmd) {
int e=OpenCom();
if (e<0) return e;
return pi->Write(&cmd,1);
}
// Command and address (Erase Segment, Load PC)
int HidWrite4(BYTE cmd, DWORD addr) {
if (addr>=1<<24) return -1; // invalid address
int e=OpenCom();
if (e<0) return e;
addr<<=8;
*(BYTE*)&addr=cmd;
return pi->Write((BYTE*)&addr,4);
}
// Command, address, length (CRC Check, TX Data Block)
int HidWrite6(BYTE cmd, DWORD addr, int len) {
if (addr>=1<<24) return -1; // invalid address
if ((unsigned)len>=1<<16) return -1; // invalid length
int e=OpenCom();
if (e<0) return e;
BYTE b[6];
*(DWORD*)(b)=addr<<8|cmd; // Start address in 3 bytes, MSB first
*(WORD*)(b+4)=(WORD)len; // Length in 2 bytes, MSB first
return pi->Write(b,6);
}
// Reads the BSL response with no data and compares the report[2] value
int HidRead0() {
return pi->Read(NULL,0);
}
/**********************************************************
* Layer 3: All commands wrapped with dedicated functions *
**********************************************************/
// All these functions return negative error codes.
bool NoWrite;
bool NoVerify;
// Again, a base class is declared which can either filled with
// ROMBSL (for ROM based bootloaders) or FLASHBSL (for FLASH based).
struct BSL{
};
// Currently, there is not ROMBSL interface implemented, so no virtual functions are used.
struct FLASHBSL:BSL{
static int RxDataBlock(DWORD addr, int len, const BYTE*buf);
static int RxPassword(const BYTE passwd[32]);
static int EraseSegment(DWORD addr);
static int ToggleInfo();
static int EraseBlock();
static int MassErase();
static int CrcCheck(DWORD addr, int len, WORD*crc);
static int LoadPc(DWORD addr);
static int TxDataBlock(DWORD addr, int len, BYTE*buf);
static int TxBslVersion(BYTE buf[4]);
static int TxBufferSize(int*siz);
static int RxDataBlockFast(DWORD addr, int len, const BYTE*buf, BYTE cmd=RX_DATA_BLOCK_FAST);
};
// [0x10] Write flash memory
int FLASHBSL::RxDataBlock(DWORD addr, int len, const BYTE*buf) {
return RxDataBlockFast(addr,len,buf,RX_DATA_BLOCK);
}
// [0x11] Send the password (i.e. interrupt vector table of old firmware)
int FLASHBSL::RxPassword(const BYTE passwd[32]) {
int e=OpenCom();
if (e<0) return e;
if (NoWrite) return 0;
BYTE *b=new BYTE[33];
if (!b) return -8;
b[0]=RX_PASSWORD;
memcpy(b+1,passwd,32);
e=pi->Write(b,33);
delete[] b;
if (e<0) return e;
return HidRead0();
}
// [0x12] Erase one flash segment
int FLASHBSL::EraseSegment(DWORD addr) {
if (NoWrite) return 0;
int e=HidWrite4(ERASE_SEGMENT,addr);
if (e<0) return e;
return HidRead0();
}
// [0x13] Unlock/Lock Info (???)
int FLASHBSL::ToggleInfo() {
int e=HidWrite1(TOGGLE_INFO);
if (e<0) return e;
return HidRead0();
}
// [0x14] undocumented
int FLASHBSL::EraseBlock() {
int e=HidWrite1(ERASE_BLOCK);
if (e<0) return e;
return HidRead0();
}
// [0x15] Erase entire flash
int FLASHBSL::MassErase() {
if (NoWrite) return 0;
int e=HidWrite1(MASS_ERASE);
if (e<0) return e;
return HidRead0();
}
// [0x16] Make a CRC calculation over flash bytes and return the hash
int FLASHBSL::CrcCheck(DWORD addr, int len, WORD*crc) {
int e;
e=HidWrite6(CRC_CHECK,addr,len);
if (e<0) return e;
return pi->Read((BYTE*)crc,2);
}
// [0x17] set the pointer to the next MSP430 instruction (i.e. do a jump)
int FLASHBSL::LoadPc(DWORD addr) {
return HidWrite4(LOAD_PC,addr);
}
// [0x18] read-in the flash memory content
int FLASHBSL::TxDataBlock(DWORD addr, int len, BYTE*buf) {
int e;
e=HidWrite6(TX_DATA_BLOCK,addr,len);
if (e<0) return e;
return pi->Read(buf,len);
}
// [0x19] Read BSL version
int FLASHBSL::TxBslVersion(BYTE buf[4]) {
int e=HidWrite1(TX_BSL_VERSION);
if (e<0) return e;
return pi->Read(buf,4);
}
// [0x1A] Query buffer size (this code intentionally makes an INT value)
int FLASHBSL::TxBufferSize(int*siz) {
if (!siz) return -8;
*siz=0;
int e=HidWrite1(TX_BSL_VERSION);
if (e<0) return e;
return pi->Read((BYTE*)&siz,2);
}
// [0x1B] Write flash memory too
int FLASHBSL::RxDataBlockFast(DWORD addr, int len, const BYTE*buf, BYTE cmd) {
int e=OpenCom();
if (e<0) return e;
if (NoWrite) return 0;
if (addr>=1<<24) return -1; // invalid address
if ((unsigned)len>=1<<16) return -1; // invalid length
BYTE *b=new BYTE[4+len];
if (!b) return -8;
*(DWORD*)b=addr<<8|cmd;
memcpy(b+4,buf,len);
e=pi->Write(b,4+len);
delete[] b;
if (e<0) return e;
if (cmd==RX_DATA_BLOCK_FAST) return 0; // no answer
return HidRead0();
}
/************************************
* Layer 4: Command-line processing *
************************************/
static void SetExitState() {
uprintf(T("SetExitState: Todo\n"));
}
int SendPassword(PTSTR arg=NULL) {
BYTE pwd[32];
FillMemory(pwd,32,0xFF);
//DEBUG
// pwd[0]=42;
int e=FLASHBSL::RxPassword(pwd);
#if 0
if (e<0) return e;
BYTE ver[4];
e=FLASHBSL::TxBslVersion(ver);
if (e<0) return e;
uprintf(14,ver[0],ver[1],ver[2],ver[3]);
int siz;
e=FLASHBSL::TxBufferSize(&siz);
if (e<0) return e;
uprintf(15,siz);
#endif
return e;
}
struct TRANSFERDATA{
BYTE target;
BYTE action;
BYTE mode;
PTSTR FileName;
int Start(PTSTR);
int Do();
int Write(FILEIO*, PTSTR, BYTE, BYTE);
}td;
int TRANSFERDATA::Start(PTSTR spec) {
PTSTR p=StrChr(spec,':');
if (!p || p==spec+1) {
action='w';
mode='a';
FileName=spec;
return Do();
}
*p=0;
if (StrCmp(spec,T("flash"))) return 2;
spec=p+1;
p=StrChr(spec,':');
if (!p) return 1;
if (p-spec!=1) return 2;
switch (*spec) {
case 'r':
case 'w':
case 'v': action=(BYTE)*spec;
default: return 2;
}
spec=p+1;
p=StrChr(spec,':');
if (p) *p=0;
PTSTR FileName=spec;
if (p) switch (p[1]) {
case 'm': // hexadecimal notation
case 'i': mode=(BYTE)p[1]; // intel hex file
default: return 2;
}
return Do();
}
int TRANSFERDATA::Write(FILEIO*io, PTSTR Name, BYTE cmd=RX_DATA_BLOCK_FAST, BYTE HandleStart=1) {
int e=io->Open(Name);
if (e<0) return e;
int l=((USBPI*)pi)->caps.OutputReportByteLength-6;
if (l&1) l--; // round down to word-aligned length
BYTE *b=new BYTE[l];
DWORD addr;
while (e=io->Read(b,l,addr)) {
if (e<0) break;
// uprintf(T("Fetched %d (0x%X) Data Bytes for Address %X ..."),e,e,addr);
e=FLASHBSL::RxDataBlockFast(addr,e,b,cmd);
if (e<0) return e;
uprintf(T("*"));
}
delete[] b;
uprintf(T("\n"));
switch (HandleStart) {
case 1: if (io->havestart) {
uprintf(2,io->start);
e=FLASHBSL::LoadPc(io->start);
if (e<0) return e;
}break;
case 2: uprintf(3);
e=FLASHBSL::RxDataBlockFast(0x120,2,(BYTE*)"\0");
if (e<0) return e;
break;
}
e=HidRead0(); // Any message?
if (e==-5) e=0; // ignore timeout
if (e<0) return e;
return io->Close();
}
int TRANSFERDATA::Do() {
int e;
// printf(T("Todo (target=%c, action=%c, filename=%s, mode=%c\n"),
// target, action, FileName, mode);
e=OpenCom();
if (e<0) return e;
// 1. Set password
e=SendPassword();
if (e==BSL_PASSWORD_ERROR-256) {
// 2. Set password again
uprintf(4);
e=SendPassword();
}
if (e<0) return e;
// 3. Download and start RAM BSL
uprintf(5);
FILEIO *io=new RESIO;
e=Write(io,MAKEINTRESOURCE(1));
if (e<0) return e;
pi->Close(); // let re-enumerate
for (int i=0; i<5; i++) {
Sleep(500);
e=OpenCom();
if (e>=0) break;
}
if (e<0) return e;
// for short: program the flash
const TCHAR*type;
rep:
switch (mode) {
case 0:
case 'a': {
FILE *f=_tfopen(FileName,T("r"));
if (!f) return -2;
char c=(char)fgetc(f); // fetch first character
fclose(f);
switch (c) {
case ':': mode='i'; goto rep;
case 'S': mode='s'; goto rep;
case '@': mode='t'; goto rep;
case 0x7F:mode='e'; goto rep;
default: mode='b'; goto rep;
}
}
case 'i': io = new IHEXFILEIO; type=T("Intel Hex"); break;
case 's': io = new SRECFILEIO; type=T("Motorola SREC"); break;
case 't': io = new TEXTFILEIO; type=T("TI Text"); break;
case 'e': io = new ELFFILEIO; type=T("ELF"); break;
case 'b': io = new BINFILEIO; type=T("binary dump"); io->addr=0x4400; break;
case 'm': io = new NOFILEIO; type=T("plain bytes"); io->addr=0xFFE0; break;
default: return -1;
}
uprintf(6,FileName,type);
e=Write(io,FileName,RX_DATA_BLOCK_FAST,2);
io->Close();
delete io;
if (e<0) return e;
#if 0
BYTE b[4];
int size;
e=FLASHBSL::TxBslVersion(b);
if (e<0) return e;
FLASHBSL::TxBufferSize(&size);
if (e<0) return e;
uprintf(T("Version=(%d %d %d %d) Buffersize=%d\n"),b[0],b[1],b[2],b[3],size);
#endif
return e;
}
TCHAR ProgName[32]; // in general, "msp430-usbbsl"
// Chooses the number of USB device
static void setPort(PTSTR s) {
if (pi) delete pi;
pi = new USBPI(s);
}
_declspec(noreturn) static void OutUsage() {
uprintf(10,ProgName);
ExitProcess(1);
}
_declspec(noreturn) static void OutUnknownOpt(TCHAR ch) {
uprintf(0x22,ProgName,ch);
OutUsage();
}
_declspec(noreturn) static void _cdecl Error(int code,...) {
DWORD err=GetLastError();
uprintf(8,ProgName,code,code&0xFF);
if (code<-128) {
uvprintf(code+512,(va_list)(&code+1));
uputs(T("\n"),1);
}
TCHAR s[128];
int l=LoadString(0,code+256,s,elemof(s)); // Description available?
if (l) uprintf(T("%s\n"),s); // Emit description for error
if (err) {
l=FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,err,0,s,elemof(s),NULL);
uputs(s,l);
}
ExitProcess(code);
}
#ifdef UNICODE
# define CommandLineToArgv CommandLineToArgvW
#else
static PSTR* CommandLineToArgv(PSTR s,int*pargc) {
int alloc=8; // start with 32 bytes
PSTR* argv=new PSTR[alloc];
int argc=0;
while (*s) {
if (argc>=alloc-1) {
alloc+=8; // grow by 32 bytes
argv=(PSTR*)realloc(argv,alloc*sizeof(PSTR));
}
PSTR e=PathGetArgs(s);
if (*e) e[-1]=0; // set string end at space before
PathUnquoteSpaces(s); // remove surrounding quotes (if any)
StrTrim(s,T(" ")); // remove trailing spaces (it really happens on argv[0])
argv[argc++]=s;
s=e;
}
argv[argc]=NULL; // terminate the list
if (pargc) *pargc=argc;
return argv;
}
#endif
extern "C" void WINAPI mainCRTStartup() {
PTSTR*argv;
int argc;
argv=CommandLineToArgv(GetCommandLine(),&argc);
lstrcpyn(ProgName,PathFindFileName(argv[0]),elemof(ProgName));
*PathFindExtension(ProgName)=0;
if (argc<2) OutUsage();
int i,e;
for (i=1; i<argc; i++) {
PTSTR arg=argv[i];
if (arg[0]=='-') switch (arg[1]) { // this is an option
case 'P': setPort(arg[2]?arg+2:argv[++i]); break;
case 'U': e=td.Start(arg[2]?arg+2:argv[++i]); if (e<0) Error(e); break;
case 'e': e=FLASHBSL::MassErase(); if (e<0) Error(e); break;
case 'z': e=SendPassword(arg[2]?arg+2:argv[++i]); if (e<0) Error(e); break;
case 'n': NoWrite=true; break;
case 'V': NoVerify=true; break;
case '?': OutUsage();
default: OutUnknownOpt(arg[1]);
}else{
e=td.Start(arg); if (e<0) Error(e);
}
}
ExitProcess(0);
}
extern "C" int _cdecl _purecall() {
ExitProcess(-1);
}
Vorgefundene Kodierung: ASCII (7 bit) | 2
|