Source file: /~heha/enas/Convac-Ätzer/uba.zip/src/uba.cpp

/* uba: Zugriffssoftware für ubaboot = 512-Byte-USB-Urlader für ATmega32U4
 */

#include <stdio.h>
#include "usb.h"
#include <windows.h>
#include <shlwapi.h>
#include "loadelf.h"
#include "loadhex.h"

#ifdef _DEBUG
#define assume(x,y) {int z; if ((z=(x))!=y) fprintf(stderr,"%s:%d:Assume failed:%s is not %d but %d\n",__FILE__,__LINE__,#x,y,z);}
#else
#define assume(x,y) (x)
#endif
#if _MSC_VER <= 1200
# define override
typedef long LONG_PTR;
#endif
//		id		flash page (B)	eeprom page
// ATmega32U4:	1E 95 87	128	7	4	2
// ATmega16U4:	1E 94 88	128	7	4	2
// AT90USB162:	1E 94 82	128	7	4	2
// ATmega8U2:	1E 93 89	64	6	4	2
// ATmega16U2:	1E 94 89	128	7	4	2
// ATmega32U2:	1E 95 8A	128	7	4	2
// EEPROM-Seitengröße ist für Urlader irrelevant!

static byte*codebuf,*databuf,
  fusebuf[4]={0xDE,0xDF,0xC3,0xEF},
  signaturebuf[3]={0x1E,0x95,0x87};
static unsigned codelen,datalen;
template<class T>struct Range{
 T start,end;
 Range():start(0),end(0) {}
 void expand(T a, T e) {if (start>a) start=a; if (end<e) end=e;}
};
static Range<unsigned> codeRange,dataRange;
static bool havefuse,havesig;

static int findstart(const byte*buf,int len) {	// find index to first used (non-0xFF) byte
 int i;
 for(i=0;i<len;++i) if (buf[i]!=0xFF) break;
 return i;	// i==len: not found, flash/eeprom empty
}
static int findend(const byte*buf,int len) {	// find index beyond last used (non-0xFF) byte
 int i;
 for(i=len;i>0;--i) if (buf[i-1]!=0xFF) break;
 return i;	// i==0: not found, flash/eeprom empty
}
static int firstdiff(const byte*b1,const byte*b2,int len) {
 int i; for (i=0;i<len;i++) if (*b1!=*b2) break; return i;
}
static FILE*myfopen(const char*name,bool wr) {	// UTF-8
#ifdef _WIN32
 wchar_t name2[MAX_PATH];
 MultiByteToWideChar(CP_UTF8,0,name,-1,name2,MAX_PATH);
 return _wfopen(name2,wr?L"wb":L"rb");
#else
 return fopen(name,wr?"wb":"rb");
#endif
}

static struct args{
 bool read,verify,erase,jump;
 const char*fname,*ename;
 void loadfile();
 static void loadfile(const char*name,bool eeprom=false);
}args;

void args::loadfile() {
 if (fname) loadfile(fname);
 if (ename) loadfile(ename,true);
}
void args::loadfile(const char*name,bool eeprom) {
 unsigned filesize;
 if (firstdiff((const byte*)name,(const byte*)"random",6)==6 && (filesize=atoi(name+6))) {
  srand(GetTickCount());
  for (unsigned i=0; i<filesize; i++) codebuf[i]=rand();
  codeRange.expand(0,filesize);
  return;
 }
 FILE*f=myfopen(name,false);
 if (!f) return;
 fseek(f,0,SEEK_END);
 filesize=ftell(f);
 fseek(f,0,SEEK_SET);
 byte*filedata=new byte[filesize];
 fread(filedata,1,filesize,f);
 fclose(f);
 const char*ext=strrchr(name,'.');
 bool okay=false;
 if (!ext) fprintf(stderr,"File name needs extension!\n");
 else{
  ++ext;
  if (!_stricmp(ext,"elf")) okay=loadelf(filedata,filesize);
  else if (!_stricmp(ext,"hex") || !_stricmp(ext,"eep")) okay=loadhex(filedata,filesize,eeprom);
  else if (!_stricmp(ext,"bin")) okay=(store_buffer(filedata,filesize,eeprom?0x810000:0),true);
  else fprintf(stderr,"Unsupported file extension %s\n",ext);
 }
 if (!okay) fprintf(stderr,"Failed to load file %s\n",name);
 delete[] filedata;
}

void store_buffer(const byte*b,unsigned l,unsigned a) {
 byte section=a>>16; unsigned a16=a&0xFFFF;
 switch (a>>16) {
  case 0x81: {
   if (a16<datalen) memcpy(databuf+a16,b,min(l,datalen-a16));
   dataRange.expand(a16,a16+l);
  }break;
  case 0x82: {
   if (a16<3) memcpy(fusebuf+a16,b,min(l,3-a16));
   havefuse=true;
  }break;
  case 0x83: {
   if (a16<1) fusebuf[3]=*b;
   havefuse=true;
  }break;
  case 0x84: {
   for (;a16<3; a16++) signaturebuf[2-a16]=*b++;
   havesig=true; 
  }break;
  default: {
   if (a<codelen) memcpy(codebuf+a,b,min(l,codelen-a));
   codeRange.expand(a,a+l);
  }
 }
}

struct progress{
 int len;
 virtual void init(int l)	{len=l;}
 virtual void show(int pos)	=0;
 virtual void done(bool ok)	{};
};

static struct uba{
 usb_dev_handle*handle;			// von libusb
 byte iface;				// stets 0
 byte flashsectorshift;			// je nach Chip, meist 7 = 128 Bytes
 enum{
  eepromsectorshift=4,	// nicht zuviel (16 Byte = 160 ms) um Timeout nicht zu stressen
  readshift=9		// 512 Bytes am Stück lesen (geht schneller)
 };
 WORD idProduct;			// Gefundene Produkt-ID (des Device-Deskriptors)
 progress*pr;				// Klassenzeiger mit 3 virtuellen Methoden zur Fortschrittsanzeige
 int timeout;
 int get_signature(byte b[3])			const	{return read(1,b,3);}
 int read_flash(byte*b,int l,int a=0)		const	{return readmem(2,b,l,a);}
 int write_flash(const byte*b,int l,int a=0)	const	{return writemem(3,b,l,flashsectorshift,a);}
 int reboot()					const	{return write(4);}
 int read_eeprom(byte*b,int l,int a=0)		const	{return readmem(5,b,l,a);}
 int write_eeprom(const byte*b,int l,int a=0)	const	{return writemem(6,b,l,eepromsectorshift,a);}
 int get_lock(byte b[4]) const	{int r=read(7,b,4); byte t=b[1];b[1]=b[3];b[3]=t; return r;}
 int init(int n=0);
 int done();
private:
 int read(byte,byte*,int,int=0)			const;
 int write(byte,const byte* =0,int=0,int=0)	const;
 int readmem(byte,byte*,int,int)		const;
 int writemem(byte,const byte*,int,byte,int)	const;
}uba;
 

int uba::write(byte cmd,const byte*data,int len,int wValue) const{
 return ::usb.control_msg(handle,0x40,cmd,
   LOWORD(wValue),HIWORD(wValue),(char*)data,len,timeout);
}
int uba::read(byte cmd,byte*data,int len,int wValue) const{
 return ::usb.control_msg(handle,0xC0,cmd,
   LOWORD(wValue),HIWORD(wValue),(char*)data,len,timeout);
}
int uba::readmem(byte cmd,byte*b,int len,int a) const{
 int ret=0;
 if (pr) pr->init(len);
 do{
  int ll=min(len,1<<readshift);
  int r=read(cmd,b,ll,a);
  if (r>ll) r=-1;	// kann nicht sein
  if (r<=0) {
   if (pr) pr->done(false);
   return r;	// raus bei Fehler oder 0-Byte-Lesen
  }
  b+=r; a+=r; ret+=ll; len-=ll;
  if (pr) pr->show(ret);
 }while(len);
 if (pr) pr->done(true);
 return ret;		// Gesamtanzahl gelesener Bytes
}
int uba::writemem(byte cmd,const byte*b,int len,byte sectorshift,int a) const{
 int ret=0;
 if (pr) pr->init(len);
 do{
  int ll=min(len,1<<sectorshift);
  int r=write(cmd,b,ll,a);
  if (r>ll) r=-1;
  if (r<=0) {
   if (pr) pr->done(false);
   return r;	// raus bei Fehler oder 0-Byte-Schreiben
  }
  b+=r; a+=r; ret+=ll; len-=ll;
  if (pr) pr->show(ret);
 }while(len);
 if (pr) pr->done(true);
 return ret;		// Gesamtanzahl geschriebener Bytes
}

static void dump(const byte*buf,int len) {
 if (len) do{
  printf("%02X%c",*buf++,len==1?'\n':' ');
 }while(--len);
}
static int getalen(int glen) {
 int alen=0; for(--glen;glen;glen>>=4) ++alen;
 return alen;
}

static void hdump(int glen,const byte*buf,int len,int a=0,bool wordwise=false,int w=16) {
 int alen=getalen(glen);
 while (len>0){
  printf("%0*X",alen,a);
  int i, ww=w; if (ww>len) ww=len;
  if (wordwise) {
   for (i=0; i<ww; i+=2) printf(" %04X",*(WORD*)(buf+i));
   for (;i<w;i+=2) printf("     ");
  }else{
   for (i=0; i<ww; i++) printf(" %02X",buf[i]);
   for (;i<w;i++) printf("   ");
  }
  putchar(' ');
  for (i=0; i<ww; i++) {
   byte c=buf[i]; if (c<32 || c>=127) c='.'; putchar(c);
  }
  putchar('\n');
  a+=w;
  buf+=w;
  len-=w;
 }
}
static void savebin(const char*fname,const byte*buf,unsigned len) {
 printf("Save %u bytes to binary file %s...",len,fname);
 FILE*f=myfopen(fname,true);
 bool okay=false;
 if (f) {
  if (fwrite(buf,1,len,f)==len) okay=true;
  fclose(f);
 }
 printf("%s\n",okay?"okay":"FAIL");
}

static struct progress_percent:public progress{
 DWORD lastTick;		// Nicht zu oft aktualisieren!
 enum{space=5};	// Benötigte Zeichenzahl (Platz) für Darstellung
 void init(int l)   override	{progress::init(l); loesch(); punkt(0); lastTick=GetTickCount();}
 void show(int p)   override	{DWORD t; if ((t=GetTickCount())>lastTick+100) {lastTick=t; punkt(p);}}
 void done(bool ok) override 	{zurueck();loesch();zurueck();}
 void punkt(int pos) {
  static const char Drehstrich[]="/-\\|";
  static byte dreh;
  zurueck();
  printf("%c%3d%%",Drehstrich[dreh],pos*100/len);
  dreh=dreh+1&3;
 }
 static void loesch()		{strstr(' ',space);}
 static void zurueck()		{strstr('\b',space);}
 static void strstr(char c,int rept) {do putchar(c); while(--rept);}
}progress_percent;

static void readall(bool all=true) {
 assume(uba.get_signature(signaturebuf),3);
 assume(uba.get_lock(fusebuf),4);
 printf("idProduct = %04X, Signature = %02X %02X %02X\n",uba.idProduct,signaturebuf[0],signaturebuf[1],signaturebuf[2]);
 printf("LFuse = %02X, HFuse = %02X, EFuse = %02X, Lock = %02X\n",fusebuf[0],fusebuf[1],fusebuf[2],fusebuf[3]);
 if (!all) return;
 uba.pr=&progress_percent;
 printf("Flash (%d bytes): read...",codelen);
 assume(uba.read_flash(codebuf,codelen),int(codelen));
 int e=findend(codebuf,codelen);
 int a=findstart(codebuf,e);
 if (e) {
  printf(" filled with %d (0x%X) bytes (%d %%):\n",e-a,e-a,(e-a)*100/codelen);
  if (args.fname) savebin(args.fname,codebuf,args.erase?codelen:e);	// Option -e speichert ganzen Flash, sonst nur bis zum letzten Nicht-0xFF-Byte
  else hdump(codelen,codebuf+a,e-a,a,true);
 }else printf(" empty\n",codelen);
 printf("Eeprom (%d bytes): read...",datalen);
 assume(uba.read_eeprom(databuf,datalen),int(datalen));
 e=findend(databuf,datalen);
 a=findstart(databuf,e);
 if (e) {
  printf(" filled with %d (0x%X) bytes (%d %%):\n",e-a,e-a,(e-a)*100/datalen);
  if (args.ename) savebin(args.ename,databuf,args.erase?datalen:e);	// Option -e speichert ganzen Eeprom, sonst nur bis zum letzten Nicht-0xFF-Byte
  else hdump(datalen,databuf,e-a);
 }else printf(" empty\n");
}

// bei args.verify==true nur Vergleich mit Datei
static bool writeall() {
 bool ret=true;
 printf("Signature: ");
 if (havesig) {
  printf("verify...");
  byte*cmpbuf=new byte[3];
  assume(uba.get_signature(cmpbuf),3);
  if (firstdiff(signaturebuf,cmpbuf,3)==3) printf("okay");
  else{
   printf("FAIL\n");
   printf("In Device: "); dump(cmpbuf,3);
   printf("In File  : "); dump(signaturebuf,3);
   ret=false;
  }
  delete[] cmpbuf;
 }else printf("not in file, nothing to do");
 putchar('\n');
 if (!ret) return ret;
 uba.pr=&progress_percent;
 int mask=(1<<uba.flashsectorshift)-1;
 int l=args.erase?codelen:codeRange.end; l+=mask; l&=~mask;	// aufrunden
 int a=args.erase?0:codeRange.start; a&=~mask;	// abrunden
 ++mask;
 printf("Flash: ");
 if (l) {
  printf("%u (0x%X) bytes in %u pages of %u words: ",l-a,l-a,(l-a)>>uba.flashsectorshift,mask>>1);
  if (!args.verify) {
   printf("%s...",codeRange.end?"program":"erase");
   assume(uba.write_flash(codebuf,l),l);
  }
  printf("%s...",codeRange.end?"verify":"blank check");
  byte*cmpbuf=new byte[l];
  assume(uba.read_flash(cmpbuf,l),l);
  int d=firstdiff(codebuf,cmpbuf,l);
  if (d==l) printf("okay");
  else {printf("FAIL at address 0x%0*X, written 0x%02X read 0x%02X",getalen(codelen),d,codebuf[d],cmpbuf[d]); ret=false;}
  delete[] cmpbuf;
 }else {printf("not in file, nothing to do"); ret=false;}
 putchar('\n');
 l=args.erase?datalen:dataRange.end;
 a=args.erase?0:dataRange.start;
 printf("Eeprom: ");
 if (l) {
  printf("%u (0x%X) bytes: ",l,l);
  if (!args.verify) {
   printf("%s...",dataRange.end?"program":"erase");
   assume(uba.write_eeprom(databuf,l),l);
  }
  printf("%s...",dataRange.end?"verify":"blank check");
  byte*cmpbuf=new byte[l];
  assume(uba.read_eeprom(cmpbuf,l),l);
  int d=firstdiff(databuf,cmpbuf,l);
  if (d==l) printf("okay");
  else {printf("FAIL at address 0x%0*X, written 0x%02X, read 0x%02X",getalen(datalen),d,databuf[d],cmpbuf[d]); ret=false;}
  delete[] cmpbuf;
 }else printf("not in file, nothing to do");
 putchar('\n');
 printf("Fuse: ");
 if (havefuse) {
  printf("verify...");
  byte*cmpbuf=new byte[4];
  assume(uba.get_lock(cmpbuf),4);
  if (firstdiff(fusebuf,cmpbuf,4)==4) printf("okay");
  else{
   printf("FAIL\n");
   printf("In Device: "); dump(cmpbuf,4);
   printf("In File  : "); dump(fusebuf,4);
  }
  delete[] cmpbuf;
 }else printf("not in file, nothing to do");
 putchar('\n');
 return ret;
}

int uba::init(int n) {
 ::usb.init();
 ::usb.find_busses();
 ::usb.find_devices();
 for (usb_bus*bus=::usb.get_busses();bus;bus=bus->next) {
  for (usb_device*dev=bus->devices;dev;dev=dev->next) {
   if (dev->descriptor.idVendor==0x03EB) {	// Atmel, ATmega32U4 PID == 0x2FF4
    if (n) --n; else{		// try nth device
     handle=::usb.open(dev);
     if (!handle) return -3;	// cannot usb_open()
     idProduct=dev->descriptor.idProduct;
     timeout=250;
     if (::usb.set_configuration(handle,1)) {
      ::usb.close(handle); handle=0; return -4;	// cannot usb_set_configuration()
     }
     iface=0;
     if (::usb.claim_interface(handle,iface)) {
      ::usb.release_interface(handle,iface);
      ::usb.close(handle); handle=0; return -5;	// cannot usb_claim_interface()
     }
     return 0;			// opened with okay
    }
   }
  }
 }
 return -1;			// cannot find nth atmel device (under libusb control)
}

int uba::done() {
 usb.release_interface(handle,iface);
 assume(::usb.close(handle),0);
 return 0;
}

static void usage(const char*progname) {
 printf(
       "AVR ATmega32U4 Flash/Eeprom programmer for 'ubaboot' boot loader, heha 190514\n"
       "%s [options] [file1] [file2]\n"
       "-r	readout the chip (to binary file[s])\n"
       "-e	erase all unused flash/EEPROM space, or assume erased\n"
       "-v	verify chip with file, or blank check when no file given\n"
       "-j	jump to application\n"
       "file1	autodetect ELF file for all sections, or binary file for Flash. No HEX\n"
       "file2	binary file for Eeprom\n"
       "If neither option nor files are given, signature + fuses are shown\n",
   progname
 );
}

int __cdecl main(int argc, char **argv ) {
 const char *progname = PathFindFileName(argv[0]);
 int ret = 0;
 for (;--argc && *++argv;) {
  const char*arg=*argv;
  switch (*arg) {
   case '-':
   case '/': for(;;) switch(*++arg) {
    case 0: goto raus;
    case 'e': args.erase=true; break;
    case 'r': args.read=true; break;
    case 'v': args.verify=true; break;
    case 'j': args.jump=true; break;
    case 'h':
    case '?': usage(progname); return 0;
    default: printf("Unknown option %c!\n",*arg); return 3;
   }raus:break;
   default: if (args.fname) args.ename=arg; else args.fname=arg;
  }
 }
 if (args.read&&args.verify) {printf("Illegal option combination"); return 3;}
 if (!((dynaprocs*)&usb)->dynaload(libusbEntries)) {	// load library
  fprintf(stderr,"Cannot load %s\n",libusbEntries);
  return 2;
 }
// Fest für ATmega32U4
 codelen=codeRange.start=32768-512;
 codebuf=new byte[codelen];
 memset(codebuf,0xFF,codelen);
 datalen=dataRange.start=1024;
 databuf=new byte[datalen];
 memset(databuf,0xFF,datalen);
 uba.flashsectorshift=7;

 if (uba.init()<0) {
  fprintf(stderr,"%s: no device present.\n",progname);
  return 1;
 }
 if (args.read) readall();
 else if (args.fname || args.erase || args.verify) {
  args.loadfile();
  if (!(args.jump|=writeall()))
    printf("NOT jumping to application, keeping bootloader running\n");
 }else readall(false);
 if (args.jump) {
  printf("jumping to application...");
  uba.reboot();
  putchar('\n');
 }
 uba.done();
 delete[] databuf;
 delete[] codebuf;
 return 0;
}

#ifndef _M_X64
// For removing fat from executable (i.e. standard library)
// Redeclaration (with linker warning) needed for VS2005+
// TODO: Compile with MSVC6
#pragma warning(disable:4273)
extern "C" _CRTIMP FILE _iob[];
FILE*__cdecl __iob_func() {
 return _iob;
}
#endif
// Needed for float-point support
int _fltused;
#pragma data_seg(".CRT$XCA")
 LONG_PTR Anfang = 0;	// Initialisierung ist wichtig, sonst geht's nach .bss!
#pragma data_seg(".CRT$XCZ")
 LONG_PTR Ende = 0;	// irgendetwas
#pragma data_seg()	//zurückschalten
EXTERN_C _CRTIMP void _cdecl _initterm(LONG_PTR*,LONG_PTR*);

// Needed as entry point: Convert command line arguments to UTF-8
void mainCRTStartup() {
 _initterm(&Anfang,&Ende);	// ruft alle statischen Konstruktoren: Setzt virtuelle Methodenzeiger
 int argc,i;
 char**argv=(char**)CommandLineToArgvW(GetCommandLineW(),&argc);
 for(i=0;i<argc;i++) {
  int space=(lstrlenW((PWSTR)argv[i])+1)*2;
  char buf[MAX_PATH];
  WideCharToMultiByte(CP_UTF8,0,(PWSTR)argv[i],-1,buf,sizeof buf,0,0);
  lstrcpyn(argv[i],buf,space);
 }
 ExitProcess(main(argc,argv));
}
Detected encoding: UTF-80