Source file: /~heha/hs/bl/msp430-usbbsl.zip/src/msp430usbbsl.cpp

#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);
}
Detected encoding: ASCII (7 bit)2