/* 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));
}
Vorgefundene Kodierung: UTF-8 | 0
|