Source file: /~heha/hsn/esptool.zip/espcom.cpp

#include "esptool.h"
#include "espcom.h"
#include "image.h"
#include "miniz.h"
//#include <stdio.h>
#include <malloc.h>	// _alloca, realloc

// Return a/b rounded up to nearest integer,
static unsigned div_ceil(unsigned a,unsigned b) {
 return (a+b-1)/b;
}

// Konstruktor
ESP::ESP(unsigned port):chip(esp::undefined),info1(0),info2(0),lastError(0),statusbytes(0) {
 printf(0x209,port+1);	// "Open COM%u:"
 _port=ComSel::Open(port);
 putchar(' ');		// in case Open() takes a while, you get a clue looking at cursor movement
 if (!_port) {
  println(0x20A);	// "Failure"
  return;
 }
 if (!changeHostBaud(currentBaud=115200)) {
  CloseHandle(_port);
  _port=0;
 }
}

bool ESP::changeHostBaud(uint32 baud) {
 DCB dcb;
 dcb.DCBlength=sizeof dcb;
 GetCommState(_port,&dcb);
 dcb.BaudRate=baud;
 ((DWORD*)&dcb)[2]=1;	// alle Flags=0 außer fBinary
 dcb.ByteSize=8;
 dcb.Parity=NOPARITY;
 dcb.StopBits=ONESTOPBIT;
 if (!SetCommState(_port,&dcb)) {
  println(0x20B,baud);	// "Could not set baudrate %u"
  return false;
 }
 return true;
}

/* Checks status bytes of answer data */
byte ESP::HDR::getStatus(const ESP*o) const{
 byte distFromEnd=o->statusbytes;
 if (!distFromEnd)		// unbekannt, Initialisierungsphase
  distFromEnd = siz>=4 && !(data()[siz-4]&0xFE) ? 4 : 2;
 int i = siz - distFromEnd;
 if (i<0) return 1;		// Antwort zu kurz
 byte error=data()[i];		// (Hier ist der Fehlerkode vom Stubben!)
 switch (error) {
  case 0: return 0;		// okay, evtl. Fehlerkode nicht auswerten
  case 1: return data()[i+1];	// Fehlerkode 5..0x0B (ROM-Lader), 0xC*, 0xFF (Stub-Lader)
  default: return error>=0xC0?error:2;	// Falsches Statusbyte (muss 0 oder 1 sein)
 }
}

int ESP::readByte() {
 int c=0;
 DWORD br;
 if (!ReadFile(_port,&c,1,&br,0)) {ComError("Failure");}
 if (br!=1) {c=-1; setError(3);} //3 = read timeout
 return c;
}

unsigned ESP::readSlip(void*buf,unsigned length,byte&flags) {
 unsigned i=0;
 byte*bu=reinterpret_cast<byte*>(buf);
 int c;
 if (flags&1<<1) {
  while ((c=readByte())!=0xC0) if (c<0) {flags|=1<<4; goto reti;}
  flags&=~(1<<1);
 }
 if (flags&1<<2) {
  c=readByte();
  if (c<0) {flags|=1<<4; goto reti;}	// Timeout: Raus!
  if (c==0xC0) flags&=~(1<<2);		// nur 1×
  else if (length) goto jumpin;
  else{	// only happens if empty SLIP packet expected and flags&1<<2 on entry and read character != C0
   setError(5);
   flags|=1<<5;
   if (flags&1<<6) ComError("Invalid zero-length SLIP packet");
   goto reti;
  }
 }
 for (; i<length; i++) {
  c=readByte();
  if (c<0) {flags|=1<<4; goto reti;}	// Timeout: Raus!
jumpin:
  switch (c) {
   case 0xDB: {
    c=readByte();
    if (c<0) {flags|=1<<4; goto reti;}	// Timeout: Raus!
    switch (c) {
     case 0xDC: c = 0xC0; break;
     case 0xDD: c = 0xDB; break;
     default:
     setError(5);	// "Empfangenes Paket ungültig"
     flags|=1<<5;
     if (flags&1<<6) ComError("Invalid SLIP escape DB-%02X",c);
     goto reti;
    }
   }break;
   case 0xC0: flags&=~(1<<3); goto reti;// Voreilendes SLIP-Ende
  }
  *bu++=c;
 }
 if (flags&1<<3) {
  if (readByte()==0xC0) flags&=~(1<<3);
  else if (flags&1<<6) ComError("Invalid SLIP termination");
 }
 if (i==length) flags|=1;		// complete
reti:
 flags&=~(1<<6);
 return i;
}

bool ESP::writeByte(byte c) {
 DWORD bw;
 if (!WriteFile(_port,&c,1,&bw,0)) {ComError("wFailure"); return false;}
 if (bw!=1) return setError(4); // 4 = write timeout
 return true;
}

/*  Write bytes to the serial port while performing SLIP escaping  */
bool ESP::writeSlip(const void*data, unsigned dlen) {
 const byte*da=reinterpret_cast<const byte*>(data);
 if (!writeByte(0xC0)) return false;
 if (dlen) do{
  byte c=*da++;
  switch (c) {
   case 0xDB: if (!writeByte(c)||!writeByte(0xDD)) return false; break;
   case 0xC0: if (!writeByte(0xDB)||!writeByte(0xDC)) return false; break;
   default: if (!writeByte(c)) return false;
  }
 }while(--dlen);
 return writeByte(0xC0);
}
// Same but length given by header
bool ESP::writeSlip(const HDR*packet) {
 return writeSlip(packet,sizeof*packet+packet->siz);
}

/*  Receive a response to a command  */
ESP::autoHDR ESP::readSlip(const HDR*sent,uint32 to_ms) {
 HDR*recv=0;
 setReadTimeout(to_ms);	// fürs Löschen für das ganze Paket erforderlich: Blockiert vor den Statusbytes
// Read header of response and parse
 do{
  recv=(HDR*)realloc(recv,sizeof*recv);
  byte flags=6;		// Lesebytes ignorieren bis 0xC0 kommt, dann darf ein zweites 0xC0 kommen
  readSlip(recv,sizeof*recv,flags);
  if (flags!=1 && flags!=5) goto err;	// Kein Fehler (= normal) wenn kein zweites 0xC0 kam
  if (recv->dir!=1) {setError(5)/* "Invalid direction of response" */; goto err;}
// The variable-length data and terminating byte
  if (recv->siz) recv=(HDR*)realloc(recv,sizeof*recv+recv->siz);
  flags=1<<3;	// 0xC0 am Ende erwarten
  readSlip(recv->data(),recv->siz,flags);
  if (flags!=1) goto err;
 }while (sent && sent->cmd!=recv->cmd);	// solange lesen bis die Antwort stimmt oder ein Timeout auftritt
 return recv;
err:
 delete recv; return 0;
}

/*  Send a request and read the response  */
ESP::autoHDR ESP::command(byte op, const void*data, int dlen, uint32 chk, uint32 to_ms,bool check_status) {
 HDR*h = (HDR*)_alloca(sizeof*h+dlen);
 h->set(op,data,dlen,chk);
 if (!writeSlip(h)) return 0;
 autoHDR r(readSlip(h,to_ms));
 if (r && check_status) {
  byte sta=r->getStatus(this);
  if (sta) {setError(sta); r=0;}
 }
 return r;
}
// tries to get a response until that response has the
// same operation as the request or a retries limit has exceeded.
// This is needed for some esp8266s that
// reply with more sync responses than expected.
//  for (int retries = 5; retries > 0; --retries) {
//   silent = retries>1;		// Beim letzten Versuch bei Fehler aussteigen
//   rh=readSlip(h,body,to_ms);
//   if (silent==1) break;	// fehlerfrei durchgelaufen
//  }
//  silent=0;
//  return rh;
// }

void _cdecl ESP::ComError(const char*msg,...) {
//   PurgeComm(_port,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
 bool writing = *msg=='w';
 if (writing) ++msg;
 fprintf(stderr," Error %s COM data: ",writing?"writing":"reading");
 va_list va;
 va_start(va,msg);
 vFatal(msg,va);
}

/*  Perform a connection test  */
bool ESP::sync(uint32 trial) {
 byte pack[36];
 pack[0]=7;
 pack[1]=7;
 pack[2]=0x12;
 pack[3]=0x20;
 memset(pack+4,0x55,32);
 autoHDR recv(0);
 for (; trial; trial--) {
  lastError=0;
  printf(".");
  recv=command(SYNC,pack,sizeof pack);
  if (recv) break;	// keine Fehler
  if (lastError==4 && trial>2) trial=2; // Reduzierte Versuche bei Timeout beim Schreiben (vermutlich COM2COM)
 }
 if (!recv) return false;
/* Zitat aus esptool/loader.py:
# ROM bootloaders send some non-zero "val" response. The flasher stub sends 0.
# If we receive 0 then it probably indicates that the chip wasn't or couldn't be
# reseted (sic!) properly and esptool is talking to the flasher stub.
 */
 if (stub_active = !recv->val) statusbytes=2;
 return true;
}

bool ESP::tryBaud(uint32 baud, uint32 syncs) {
 if (currentBaud==baud) return false;	// schon versucht
 if (!baud) return false;		// Keine Angabe
 if (!changeHostBaud(baud)) return false; // Kann nicht
 putchar('*'); Sleep(50); PurgeComm(_port,PURGE_RXCLEAR);
 if (!sync(syncs)) return false;	// gibt Punkte aus
 lastB=currentBaud=baud;		// merken wenn's klappt
 return true;
}

bool ESP::changeDeviceBaud(uint32 baud) {
 if (info1->image_chip_id==66) return true;	// nichts tun!
 uint32 a[2]={baud,stub_active?currentBaud:0};
 if (!command(CHANGE_BAUDRATE,a,sizeof a)) {
// Kommt die Antwort in alter oder neuer Baudrate??
  println(0x20B,baud);	// "Could not set baudrate %u"
// kann schiefgehen, muss aber nicht
 }
 if (!tryBaud(baud,20)){// sollte niemals false liefern
  println(0x20B,baud);	// "Could not set baudrate %u"
  changeHostBaud(currentBaud);	// Änderung auf der Hostseite rückgängig machen
  return false;
 }
 currentBaud=baud;
 return true;
}

/*  Try connecting repeatedly until successful, or giving up  */
bool ESP::connect(unsigned baud) {
 printf(0x20C);		// "Connect"
// issue reset-to-bootloader:
// RTS = either CH_PD or nRESET (both active low = chip in reset)
// DTR = GPIO0 (low = boot to flasher)	// Reset Boot
 EscapeCommFunction(_port,CLRDTR);	// H	 H
 EscapeCommFunction(_port,SETRTS);	// L	 H
 Sleep(50);				// L	 H
 printf(".");
 EscapeCommFunction(_port,SETDTR);	// L	 L
 EscapeCommFunction(_port,CLRRTS);	// H	 L
 Sleep(50);				// H	 L
 printf(".");
 EscapeCommFunction(_port,CLRDTR);	// H	 H
 setReadTimeout(50);
 Sleep(100);
 if (!sync(8)		// Erster Versuch mit 115200 Baud (40-MHz-Quarz)
 && !tryBaud(lastB,8)	// Zweiter Versuch mit persistent gespeicherter Baudrate vom letzten Mal
 && !tryBaud(74800,8)	// Dritter Versuch mit 74800 Baud (26-MHz-Quarz)
 && !tryBaud(baud,8)){	// Vierter Versuch mit angegebener Baudrate
  println(0x20D);	// "Failed to connect to ESP microcontroller\n"
  return false;
 }
 autoHDR recv(command(GET_SECURITY_INFO));
 if (recv) {	// ESP32S2 oder höher
  if (recv->siz>=20) chip = esp::Chipmagic(((uint32*)recv->data())[3]);
 }
 lastError=0;
// ESP32 oder ESP8266?
 if (chip==esp::undefined) chip = esp::Chipmagic(read_reg(0x40001000));
 info1=esp::find1(chip);
 if (!info1) {
  println(0x200);
  return false;
 }
 if (!statusbytes)	// Die Statusbytes werden bei ESP::sync() auf 2 gesetzt, wenn der RAM-Urlader detektiert wurde
	statusbytes = chip==esp::ESP8266 ? 2 : 4;
 info2=esp::find2(info1->image_chip_id);
 println(0x201,info1->name,get_chip_description().c_str());
 if (currentBaud!=baud) {
  tty.x=printf("Change baudrate from %u to %u",currentBaud,baud);
  putchar(' ');
  if (!changeDeviceBaud(baud)) return false;	// may take a while!
  putchar(' ');
  println("done.");
 }else println("Current baudrate is %u",baud);
 println(0x202,features().c_str());
 println(0x203,get_crystal_freq());
 println(0x204,read_mac().toString().c_str());
 return true;
}

void ESP::setReadTimeout(DWORD ms) const{
 COMMTIMEOUTS to={0,0,ms,0,50};
 SetCommTimeouts(_port,&to);
}

/*
 static const char*statusToString(byte sta) {
  switch (sta) {
   case 0: return "Okay";
   case 1: return "Answer too short";
   case 2: return "Wrong status byte";
   case 3: return "Read Timeout";
   case 4: return "Write Timeout";
   case 5: return "Received message is invalid";
   case 6: return "Failed to act on received message";
   case 7: return "Invalid CRC in message";
   case 8: return "Flash write error";
   case 9: return "Flash read error";
   case 10: return "Flash read length error";
   case 11: return "Deflate error";
   case 0xC0: return "Bad data length";
   case 0xC1: return "Bad data checksum";
   case 0xC2: return "Bad blocksize";
   case 0xC3: return "Invalid command";
   case 0xC4: return "Failed SPI operation";
   case 0xC5: return "Failed SPI unlock";
   case 0xC6: return "Not in flash mode";
   case 0xC7: return "Inflate error";
   case 0xC8: return "Not enough data";
   case 0xC9: return "Too much data";
   case 0xFF: return "Command not implemented";
   default: return "Unknown error code";
  }
 }
*/

/*  Read memory address in target  */
uint32 ESP::read_reg(uint32 addr) {
 autoHDR recv(command(READ_REG,&addr,4));
 if (!recv) Fatal(0x110,lastError);	// "Failed to read target memory (%02X)"
 return recv->val;
}

void ESP::write_reg(uint32 addr, uint32 value, uint32 mask, uint32 delay_us) {
 uint32 a[4]={addr,value,mask,delay_us};
 autoHDR recv(command(WRITE_REG,a,4*4));
 if (!recv) Fatal(0x111,lastError);	// "Failed to write target memory (%02X)"
}

void _cdecl Printbuf::operator()(const char*t,...) {
 va_list va;
 va_start(va,t);
 k+=_vsnprintf(s+k,n-k-1,t,va);
 va_end(va);
 s[k]=0;	// Nullterminierung sicherstellen
};

String<32>ESP::get_chip_description() {
 MAKEPRINTBUF(32)
 switch (chip) {
  case esp::ESP32: {
   uint32 efuse3 = read_reg(info2->efuse_rd_reg_base+3*4),
	 efuse5 = read_reg(info2->efuse_rd_reg_base+5*4),
	 c_date = read_reg(0x3FF6607C),
	 pkg_v = efuse3>>9&7 | efuse3<<1&8,
         major = efuse3>>15&1 | efuse5>>19&2 | c_date>>29&4,
	 minor = efuse5>>24&3;
   switch (major) {
    case 1: break;
    case 3: major=2; break;
    case 7: major=3; break;
    default: major=0;
   }
   bool rev3 = major==3,
        single_core = efuse3&1;
   const char*suffix;
   switch (pkg_v) {
    case 0: suffix = single_core ? "S0WDQ6" : "D0WDQ6"; break;
    case 1: suffix = single_core ? "S0WD" : "D0WD"; break;
    case 2: suffix = "D2WD"; break;
    case 4: suffix = "U4WDH"; break;
    case 5: suffix = rev3 ? "PICO-V3" : "PICO-D4"; break;
    case 6: suffix = "PICO-V3-02"; break;
    case 7: suffix = "D0WDR2"; break;
    default: suffix = "??";
   }
   print("ESP32-%s%s (revision v%u.%u)",
	suffix,
	!single_core && pkg_v<2 && rev3 || pkg_v==7 ? "-V3" : "",
	major,
	minor);
  }break;
  default: print("no description");
 }
 return buf;
}

String<100>ESP::features() {
 MAKEPRINTBUF(100)
 size_t k=0;
 switch (chip) {
  case esp::ESP32: {
   print("WiFi");
   uint32 efuse3 = read_reg(info2->efuse_rd_reg_base+3*4),
         efuse4 = read_reg(info2->efuse_rd_reg_base+4*4),
         efuse6 = read_reg(info2->efuse_rd_reg_base+6*4);
   if (!(efuse3&2)) print(", Bluetooth");
   print(", %s Core",efuse3&1?"Single":"Dual");
   if (efuse3&1<<13)
	print(", %u MHz",efuse3&1<<12 ? 160 : 240);
   switch (efuse3 & 0x0E04) {
    case 0xC00: print(", Embedded %s","PSRAM"); nobreak;
    case 0xA00:
    case 0x800:
    case 0x400: print(", Embedded %s","Flash");
   }
   if (efuse4&0x1F00)
	print(", VRef calibration in efuse");
   if (efuse3&0x4000)
	print(", BLK3 partially reserved");
   const char*scheme;
   switch (efuse6&3) {
    case 0: scheme = "None"; break;
    case 1: scheme = "3/4"; break;
    case 2: scheme = "Repeat (UNSUPPORTED)"; break;
    default: scheme = "Invalid";
   }
   print(", Coding Scheme: %s",scheme);
  }break;
 }
 return buf;
}

int ESP::get_crystal_freq() {
 uint32 UART_CLKDIV_REG=0,
	XTAL_CLK_DIVIDER=0;
 switch (chip) {
  case esp::ESP8266: UART_CLKDIV_REG=0x60000014; XTAL_CLK_DIVIDER=2; break;
  case esp::ESP32:   UART_CLKDIV_REG=0x3FF40014; XTAL_CLK_DIVIDER=1; break;
 }
 if (!UART_CLKDIV_REG) return 0;
 DCB dcb;
 dcb.DCBlength=sizeof dcb;
 GetCommState(_port,&dcb);
 uint32 f = read_reg(UART_CLKDIV_REG) & 0xFFFFF;
 f*= dcb.BaudRate/XTAL_CLK_DIVIDER;
 return f > 33000000 ? 40 : 26;
}

/*  Start downloading an application image to RAM  */
bool ESP::mem_begin(BeginInfo&bi) {
 autoHDR recv(command(MEM_BEGIN,&bi,sizeof bi));
 if (!recv) Fatal(0x112,lastError);	//"Failed to enter RAM download mode (%02X)"
 return true;
}

/*  Send a block of an image to RAM  */
bool ESP::mem_block(DataInfo&di,const void*data) {
 uint32*a=(uint32*)_alloca(sizeof di+di.sblk);
 memcpy(a,&di,sizeof di); memcpy(a+4,data,di.sblk);
 autoHDR recv(command(MEM_DATA,a,sizeof di+di.sblk,esp::xor8(data,di.sblk)));
 if (!recv) Fatal(0x113,lastError);	//"Failed to write to target RAM (%02X)"
 return true;
}

/*  Leave download mode and run the application  */
bool ESP::mem_finish(uint32 entrypoint) {
 uint32 a[2]={!entrypoint,entrypoint};
 autoHDR recv(command(MEM_END,a,sizeof a));
 if (!recv) Fatal(0x114,lastError);	//"Failed to leave RAM download mode (%02X)"
 return true;
}

/*  Start downloading to Flash (performs an erase)  */
bool ESP::flash_begin(BeginInfo&bi,byte compress) {
 if (!stub_active && info1->image_chip_id==66) {
  unsigned sectors_per_block = 16;
  unsigned sector_size = 4096;
  unsigned num_sectors = div_ceil(bi.size,sector_size);
  unsigned start_sector = bi.addr / sector_size;
  unsigned head_sectors = sectors_per_block - (start_sector % sectors_per_block);
  if (num_sectors < head_sectors) head_sectors = num_sectors;
  if (num_sectors < 2 * head_sectors) bi.size = (num_sectors + 1) / 2 * sector_size;
  else bi.size = (num_sectors - head_sectors) * sector_size;
 }
 DWORD t = GetTickCount();
 if (!command(FLASH_BEGIN+compress,&bi,sizeof bi)) {
  println("Failed to enter Flash download mode: %s",loadString(lastError).c_str());
  return false;
 }
 tty.x+=printf("Took %u ms to erase flash block",GetTickCount() - t);
 return true;
}

/*  Write block to flash  */
bool ESP::flash_block(DataInfo&di,const void*data,byte compress) {
 uint32 tlen=sizeof di+di.sblk;
 void*t=_alloca(tlen);
 memcpy(t,&di,sizeof di);
 memcpy((byte*)t+sizeof di,data,di.sblk);
 if (!command(FLASH_DATA+compress,t,tlen,esp::xor8(data,di.sblk),1000)) {
  if (tty.x) newline();
  println("Failed to write to target Flash after seq %d: %s",di.iblk,loadString(lastError).c_str());
  return false;
 }
 putchar('.');
 return true;
}

/*  Leave flash mode and run/reboot  */
bool ESP::flash_finish(bool reboot,byte compress) {
 uint32 pkt = !reboot;
 if (!command(FLASH_END+compress,&pkt,sizeof pkt)) {
  println("Failed to run/reboot after flash write: %s",loadString(lastError).c_str());
  return false;
 }
 return true;
}

// So stellt der Aufrufer den Platz für den String auf dem Stack bereit!
// Allerdings ist das Ergebnis ungeeignet als Argument für ...!
// (C++ kopiert dann den String als Ganzes auf den Stack!)
// Daher hat String<> die Memberfunktion c_str() der daraus den Zeiger macht.
// Vorteil: Man kommt ohne Heap und ohne Hilfsvariablen aus.
// Erstaunlich: MSVC kann klaglos auf __$ReturnUdt zugreifen.
String<18>ESP::MAC::toString() {
 MAKEPRINTBUF(18)
 byte*m=((byte*)&mac)+6;	// So nur für Little Endian
 for(int i=6;;){
  print("%02X",*--m);
  if (!--i) break;
  print(":");
 }
 return buf;
}

    /*  Read MAC from OTP ROM  */
ESP::MAC ESP::read_mac(int) {
 switch (chip) {
  case esp::ESP8266: {
   uint32 mac0 = read_reg(OTP_MAC+0),	// MMMM.MMMM 0000.0000 0000.0000 0000.0000
          mac1 = read_reg(OTP_MAC+4),	// 0000.0000 UUUU.UUUU MMMM.MMMM MMMM.MMMM
          mac2 = read_reg(OTP_MAC+8),	// 0000.SSSF 0000.0000 0000.0000 0000.0000
          mac3 = read_reg(OTP_MAC+12),	// 0000.0000 MMMM.MMMM MMMM.MMMM MMMM.MMMM
	  maclo= (uint16)mac1<<8 | mac0>>24;	// low 24 bit
            
   switch (mac2>>13&7) {	// "S"-Bits
    case 5: printf("THIS IS ESP8266EX"); break;	//ESP8266EX
    case 4: printf("THIS IS ESP8266"); break; //ESP8266
    default: Fatal("OTHER CHIPS...");
   }
            
   if (mac2>>12&1) return (uint64)mac3<<24 | maclo;  // "F"-Bit: Full 48 bit MAC address format
   //a fixed 3 bytes MAC header + 3 bytes MAC
   uint32 oui;
   switch (mac1>>16&0xff) {
    case 0: oui = 0x18FE34; break;
    case 1: oui = 0xACD074; break;
    default: Fatal("Unknown OUI");
   }
   return (uint64)oui<<24|maclo;
  }break;

  case esp::ESP32: {
// Keine Doku gefunden und liefert Nullen. Was soll der Quatsch? Alles Fehler!
//    unsigned word16 = read_reg(EFUSE_RDATA16+0),	// MMMM.MMMM MMMM.MMMM MMMM.MMMM 0000.0000
//	     word17 = read_reg(EFUSE_RDATA16+4),	// 0000.0000 MMMM.MMMM MMMM.MMMM MMMM.MMMM
//	     word18 = read_reg(EFUSE_RDATA16+8),	// BBBB.BBBB BBBB.BBBB BBBB.BBBB 0000.0000
//	     word19 = read_reg(EFUSE_RDATA16+12);	// 0000.0000 BBBB.BBBB BBBB.BBBB BBBB.BBBB
//    macs[1](word17>>16,word17>>8,word17,word16>>24,word16>>16,word16>>8);
//    macs[2](word19>>16,word19>>8,word19,word18>>24,word18>>16,word18>>8);
   const unsigned EFUSE_BLK0_RDATA1_REG = 0x3FF5A004;
// So wie ich das lese ist das die MAC-Adresse für (hypothetisches) Kabel-Ethernet.
// Die sich ergebende Adresse unterscheidet sich (etwas) von der WLAN-MAC-Adresse!
   uint32 w1 = read_reg(EFUSE_BLK0_RDATA1_REG),
	  w2 = read_reg(EFUSE_BLK0_RDATA1_REG+4);
   return (uint64)w2<<32|w1;
  }break;
 }
 return 0;
}

/*  Read SPI flash manufacturer and device id  */
uint32 ESP::flash_id() {
 if (chip!=esp::ESP8266) return 0;	// Scheiße!
 BeginInfo bi={0};
 flash_begin(bi,0);
 write_reg(0x60000240,0,0xffffffff);
 write_reg(0x60000200,0x10000000,0xffffffff);
 uint32 flash_id = read_reg(0x60000240);
 flash_finish(false,0);
 return flash_id;
}

bool ESP::chunk2ram(const Dumpfile&df) {
 BeginInfo bi={
  df.size,
  div_ceil(df.size,RAM_BLOCK),
  RAM_BLOCK,
  df.addr};
 if (!bi.size) return true;	// nichts zu tun
 const byte*d=reinterpret_cast<const byte*>(df.data());
 if (!mem_begin(bi)) return false;
 printf(" @%X",df.addr);
 DataInfo di={bi.sblk};
 do{
  if (di.sblk>bi.size) di.sblk=bi.size;
  if (!mem_block(di,d)) return false;
  printf("L%X",di.sblk);
  d+=di.sblk;
  bi.size-=di.sblk;
  di.iblk++;
 }while (bi.size);
// return mem_finish(0);
 return true;
}

bool ESP::chunk2ram(void*cbd,const Dumpfile&df) {
 return reinterpret_cast<ESP*>(cbd)->chunk2ram(df);
}

bool ESP::load_stub() {
 if (stub_active) {
  println(0x205);	// "Stub is already running. No upload necessary."
  return true;
 }
 printf(0x206);		// "Load stub"
 if (!info2) return false;
 Dumpfile sub(info2->image_chip_id);
 if (!sub.data()) return false;
 uint32 start;
 if (!ESPFirmwareImage::parse(sub,chunk2ram,this,&start)) return false;
 printf(0x207,start);	// " done, ^%X"
 DWORD ohai;	// Der Stub sendet nach dem Start ein Slip-Paket mit 4 Byte Kennung
 byte flags=1<<2|1<<3;	// ecpect full packet
 bool ok=mem_finish(start) && readSlip(&ohai,4,flags)==4 && flags==1 && ohai=='IAHO';
 if (ok) {
  printf(0x208);	// " done."
  stub_active=true;
  statusbytes=2;
 }
 putchar('\n');
 return ok; 
}

/*  Read or verify SPI flash  */
bool ESP::flash2file(const char*fname) {
 Dumpfile df(fname);
 if (!(df.flags&5)) {
  printf("Address and (possibly) length must be given!\n");
  return false;
 }
// Leselänge von Adresse abhängig machen, für typische Szenarien!
 if (df.flags&10) {
  if (!df.size) {
   printf("Zero-length file %s!\n",df.name());
   return false;
  }
 }else switch (df.addr) {
  case 0x1000: df.size=0x7000; break;	// Zweiter Urlader
  case 0x8000: df.size=3072; break;	// Partitionstabelle
  case 0x10000: df.size=1024*0x1000; break;	// „Exerner“ Flash: 4 MByte ansetzen
  default: df.size=1024*1024; break;	// Alles andere: 1 MByte
 }
 tty.x=printf("Reading %u (0x%X) bytes",df.size,df.size); fflush(stdout);
 byte*buf=new byte[df.size];
 BeginInfo bi={df.addr,df.size,0x400,4};
 command(0xD2,&bi,sizeof bi,0,200);
 uint32 truncated=df.size;
 bool checkimage=true;
 bool aborted=false;
 uint32 i;
 for(i=0;i<df.size;) {		// Muss alles lesen, vorzeitiges Abbrechen nicht möglich
  uint32 l=df.size-i; if (l>bi.sblk) l=bi.sblk;
  byte flags=1<<2|1<<3;		// Start- und Endrahmen
  uint32 ll=readSlip(buf+i,l,flags);
  if (flags!=1) {
   if (tty.x) newline();
   println("Read aborted at %u (%X) bytes with %u bytes: %s",i,i,ll,loadString(lastError).c_str());
   aborted=true;
   break;
  }
  i+=l;
  const uint32 deadbeef=0xDEADBEEF;
  if (!writeSlip(i>truncated?&deadbeef:&i,4)) {delete[]buf; return false;}
  putchar('.'); fflush(stdout); ++tty.x;
  if (checkimage) {
   uint32 j = ESPFirmwareImage::detectSize(buf,i);
   switch (j) {
    case 0: checkimage=false;	// Kein FirmwareImage, nicht mehr testen
    printf(" Not a firmware image ");
    break;
    case 1: break;		// FirmwareImage, Länge (noch) unbekannt
    default: checkimage=false; truncated=j;
    if (j<df.size) {
     if (tty.x) newline();
     tty.x+=printf("Detected firmware image has %u (%X) bytes, no idea how to stop reading ",j,j);
    }
   }
  }
 }
 if (tty.x) newline();
 if (!aborted) {	// Bei vorzeitigem Abbruch kommt kein MD5-Hash mehr.
  Md5::Digest md5;
  byte flags=1<<2|1<<3;	// Erwarte genau 16 Bytes
  readSlip(&md5,sizeof md5,flags);
  if (flags!=1) {
   println("Error (%02X) getting MD5 hash: %s",flags,loadString(lastError).c_str());
   lastError=0;
  }else{
   println("MD5 hash from ESP%s = %s",info1->name,md5.toString().c_str());
  }
 }
 bool ret=false, doSave=true;
 if (df.data()) {	// Wenn Datei bereits vorhanden, dann Vergleich
  printf("Verify: ");
  for (i=0; i<truncated; i++) if (buf[i]!=df.data()[i]) break;
  if (i==truncated) {
   println("okay.");
   doSave=false;
   ret=true;
  }else{
   println("failed at offset %u!",i);
   if (MessageBoxA(0,"Überschreiben mit Flash-Daten?","esptool",MB_YESNO|MB_ICONQUESTION)!=IDYES) doSave=false;
  }
 }
 if (doSave) {
  printf("Save: ");
//  struct{	// Create a custom stub
//   uint32 offset,size,count;
//   byte assembly[sizeof SFLASH_STUB];
//  }stub;
//  stub.offset=offset;
//  stub.size=size;
//  stub.count=count;
//  memcpy(stub.assembly,SFLASH_STUB,sizeof SFLASH_STUB);

// Trick ROM to initialize SFlash
//  flash_begin(0,0);
// Download stub
//  mem_begin(sizeof stub,1,sizeof stub,0x40100000);
//  mem_block((byte*)&stub,sizeof stub,0);
//  mem_finish(0x4010001c);

// Fetch the data
//  if (readByte() != 0xC0) Fatal("Invalid head of packet (sflash read)");
// for (uint32 i=0; i<fsize; i++) {
//  if ((buf[i]=readByte()) != 0xC0) Fatal("Invalid end of packet (sflash read)");
// }
  FILE*f=fopen(df.name(),"wb");
  if (f) {
   ret = fwrite(buf,1,truncated,f)==truncated;
   ret = !fclose(f) && ret;
   f = 0;
   printf(ret?"okay.":"Could not write all bytes, disk full?");
  }else printf("Could not create file %s!",df.name());
 }
 putchar('\n');
 delete[]buf;
 return ret;
}

bool ESP::flash_verify(const char*fname) {
 Dumpfile df(fname);
 if (!df.data()) {
  println(0x20E,df.name());	// "File %s could not be open for reading!"
  return false;
 }
 if (!(df.flags&5)) {
  println(0x20F);		// "Address must be given!"
  return false;
 }
 if (df.flags&1<<4) {
  println(0x215,df.name());	// "Verify with compressed file yet unsupported"
  return false;
 }
 Md5 md5;
 md5.update(df.data(),df.size);
 Md5::Digest filehash = md5.final(), flashhash;
 println(0x211,filehash.toString().c_str(),df.name());	// "%s = MD5 hash for file %s"
 uint32 cmdargs[4]={df.addr,df.size};
 if (stub_active) {
  unsigned to=100+(df.size>>8);
  printf("wait upto %u ms while firmware calculates MD5 hash...",to);
  autoHDR r(command(SPI_FLASH_MD5,cmdargs,sizeof cmdargs,0,to));
  putchar('\r');
  if (!r) Fatal(0x115,lastError);	// "SPI_FLASH_MD5 command failed (%02X)"
  if (r->siz!=18) Fatal(0x116,r->siz);	// "Wrong answer length (%u)"
  flashhash.fromData(r->data());
 }else{	// same command but different timeout and answer
// Geht nicht, keine Antwort! Also (sowieso besser) Stub laden
  autoHDR r(command(SPI_FLASH_MD5,cmdargs,sizeof cmdargs,0,1000,false));
  if (!r) Fatal(0x115,lastError);
  if (r->siz!=32) Fatal(0x116,r->siz);
  flashhash.fromString(reinterpret_cast<const char*>(r->data()));
 }
 println(0x212,	// "%s = MD5 hash for flash from %X to %X, %s"
	flashhash.toString().c_str(),
	df.addr,
	df.addr+df.size-1,
	loadString(flashhash==filehash ? 0x214 : 0x213).c_str());	// "equal, okay." : "not equal!"
 return true;
}

/* Perform a chip erase of SPI flash. Stub is loaded */
bool ESP::flash_erase() {
 printf("Flash erase");
 DWORD t=GetTickCount();
 autoHDR r(command(ERASE_FLASH,0,0,0,5000));
 if (!r) {
  printf(" failed (%u)!\n",lastError);
  return false;
 }
 t = GetTickCount()-t;
 printf("d, %u ms\n",t);
 return true;
/*

        // Trick ROM to initialize SFlash
  flash_begin(0,0);	// der Bootcode verabschiedet sich in die ewigen Jagdgründe!

        // This is hacky: we don't have a custom stub, instead we trick
        // the bootloader to jump to the SPIEraseChip() routine and then halt/crash
        // when it tries to boot an unconfigured system.
  mem_begin(0,0,0,0x40100000);
  mem_finish(0x40004984);
*/
}
        // Yup - there's no good way to detect if we succeeded.
        // It it on the other hand unlikely to fail.

    /* Set the flash params for ESP booter */
bool ESP::flash_spi_param_set() {
 printf("Set flash params");
 uint32 a[6]={0, (128/8)*1024*1024, 64*1024, 4*1024, 256, 0xffff};
 autoHDR recv(command(SPI_SET_PARAMS,a,sizeof a,0,1000));
 if (!recv) {
  printf(" failed (%u)\n",lastError);
  return false;
 }
 printf(" done\n");
 return true;
}
    
byte ESP::crc8(byte abyte) {
 byte b = 0;
 for (int i=0; i<8; i++) {
  if ((b^abyte)&1) {
   b ^= 0x18;
   b >>= 1;
   b |= 0x80;
  }else b >>= 1;
  abyte >>= 1;
 }
 return b;
}
    
byte ESP::crc8(const void*data,int len) {
 const byte*dlist=reinterpret_cast<const byte*>(data);
 byte crc = 0;
 for (int i=0; i<len; i++) crc = crc8(crc ^ dlist[i]);
 return crc;
}
    
unsigned ESP::crc16(const void*data,int dlen) {
 const byte*dlist=reinterpret_cast<const byte*>(data);
 const unsigned CRC_CCITT = 0x1021;
 unsigned crc = 0;
 for (int i=0; i<dlen; i++) {
  for (byte m=0x80; m; m>>=1) {
   crc<<=1;
   if (crc&0x10000) crc^=0x10000|CRC_CCITT;
   if (dlist[i]&m) crc^= CRC_CCITT;
  }
 }
 return crc;
}

bool ESP::check_crc() {
 switch (chip) {
  case esp::ESP8266: {
   uint32 mac0 = read_reg(OTP_MAC+0),
         mac1 = read_reg(OTP_MAC+4),
         mac2 = read_reg(OTP_MAC+8),
         mac3 = read_reg(OTP_MAC+12);
//            efuse = (mac3<<96)|(mac2<<64)|(mac1<<32)|(mac0<<0)
   byte t[3]={byte(mac3),byte(mac3>>8),byte(mac3>>16)};
   byte crc8_high_cal = crc8(t,sizeof t);
   byte crc8_high = byte(mac2>>24);
   if (crc8_high_cal!=crc8_high) {
    printf("CRC8_H ERROR...");
    return false;
   }
   byte crc_low_version = byte(mac1>>24&0xf);
   printf("crc_low_version: 0x%X",crc_low_version);
   byte crc8_low = byte(mac0>>16);
   if (!crc_low_version) {
    if (crc8_low) {
     printf("CRC8_L V0 ERROR...");
     return false;
    }else if (crc_low_version==1 || crc_low_version==2) {
     byte t[4]={byte(mac0>>24),byte(mac1),byte(mac1>>8),byte(mac1>>16)};
     byte crc8_low_cal = crc8(t,sizeof t);
     if (crc8_low_cal!=crc8_low) {
      printf("CRC8_L V1 ERROR... ");
      return false;
     }
    }
   }
  }return true;
  case esp::ESP32: {
   uint32 word16 = read_reg(EFUSE_RDATA16+0),
         word17 = read_reg(EFUSE_RDATA16+4),
         word18 = read_reg(EFUSE_RDATA16+8),
         word19 = read_reg(EFUSE_RDATA16+12);
   byte wifi_crc_data[6] = {
    byte(word16>>8),
    byte(word16>>16),
    byte(word16>>24),
    byte(word17),
    byte(word17>>8),
    byte(word17>>16)},
	bt_crc_data[6] = {
    byte(word18>>8),
    byte(word18>>16),
    byte(word18>>24),
    byte(word19),
    byte(word19>>8),
    byte(word19>>16)};
   byte efuse_crc_wifi = byte(word16),
	efuse_crc_bt   = byte(word18);
   byte wifi_crc_cal = byte(crc16(wifi_crc_data,6)),
	bt_crc_cal   = byte(crc16(bt_crc_data,6));
   if (wifi_crc_cal!=efuse_crc_wifi) {
    printf("WIFI CRC ERROR.");
    return false;
   }
   if (bt_crc_cal != efuse_crc_bt) {
    printf("BT CRC ERROR");
    return false;
   }
  }return true;
  default: return false;
 }
}

    /*  GPIO Change  */
void ESP::HSPI_INIT() {
 switch (chip) {
  case esp::ESP32: {
   printf("hspi by gpio");
   write_reg(0x60008474,0xfc,0xffffffff);//hold spi sig output
	     //hspi gpio init
   uint32 val=read_reg(0x60009030);
   write_reg(0x60009030, 2<<12|val, 0xFFFFFFFF);
   val=read_reg(0x60009034);
   write_reg(0x60009034, 2<<12|val, 0xFFFFFFFF);
   val=read_reg(0x60009038);
   write_reg(0x60009038, 2<<12|val, 0xFFFFFFFF);
   val=read_reg(0x6000903c);
   write_reg(0x6000903c, 2<<12|val, 0xFFFFFFFF);
   val=read_reg(0x60009040);
   write_reg(0x60009040, 2<<12|val, 0xFFFFFFFF);
   val=read_reg(0x60009048);
   write_reg(0x60009048, 2<<12|val, 0xFFFFFFFF);
	    //gpio enable
   val=read_reg(0x60004020);
   write_reg(0x60004020, 0xf014|val, 0xFFFFFFFF);
	    //gpio out enable 
   write_reg(0x600041a4, 5<<24|2<<8|1, 0xFFFFFFFF);
   write_reg(0x6000419c, 3, 0xFFFFFFFF);
   write_reg(0x60004198, 4<<16, 0xFFFFFFFF);
	    //gpio input enable
   write_reg(0x600041c0, 0x3f, 0xFFFFFFFF);
   write_reg(0x60004130, 2<<24|4<<18|13<<12|12<<6|14, 0xFFFFFFFF);
   write_reg(0x60004134, 15, 0xFFFFFFFF);
  }break;
  default: printf("Support ESP%s LATER",info1?info1->name:"??");
  break;
 }
}

bool ESP::write_flash_cb(void*cbd,const Dumpfile&df) {
 return ((ESP*)cbd)->write_flash_cb(df);
}

bool ESP::write_flash_cb(const Dumpfile&df) {
 byte compress = stub_active ? DEFL_ADD : 0;
 if (chip==esp::ESP8266) compress=0;	// not capable handling compressed data
 const byte*data=df.data();
 if (compress && !(df.flags&1<<4)) {
  const_cast<Dumpfile&>(df).usize=df.size;
  size_t csize;
  data=reinterpret_cast<const byte*>(Deflate::mem_to_heap(data,df.usize,
	csize,Deflate::WRITE_ZLIB_HEADER));
  if (!data) return false;
  const_cast<Dumpfile&>(df).size=(uint32)csize;
 }
 if (!df.size) return true;
 uint32 blocksize=compress?FLASH_BLOCK>>2:FLASH_BLOCK;
 BeginInfo bi={
  df.usize,
  div_ceil(df.size,blocksize),
  blocksize,
  df.addr};
 printf("Erasing flash...");
 DWORD t=GetTickCount();
 if (!flash_begin(bi,compress)) return false;
// Schleife über FLASH_BLOCK-Chunks
 DataInfo di={bi.sblk};
 for (uint32 i=0; i<df.size; i+=di.sblk,di.iblk++) {
  putchar('\r'); tty.x=0;
  tty.x+=printf("Writing at %#x... (%d %%)",bi.addr + di.iblk * FLASH_BLOCK, 100 * (di.iblk + 1) / bi.nblk);
  fflush(stdout);
//  if (di.sblk>df.size-i) di.sblk=df.size-i;
// Pad the last block (das macht gefälligst der Lader!)
  if (!flash_block(di,data+i,compress)) return false;
  bi.addr+=uint32(di.sblk*(uint64)df.usize/df.size);	// Adresse etwa, nur zur Anzeige
 }
 t=GetTickCount()-t;
 putchar('\r');
 println("Wrote %d bytes at %#x in %u ms (%u kbit/s)",bi.size,df.addr,t,bi.size*8/(t?t:1));
 if (compress && !(df.flags&1<<4)) free(const_cast<byte*>(data));	// Komprimierte Daten löschen
 return true;
}

bool ESP::verboseDownloadCb(void*cbd,const Dumpfile&df) {
 return reinterpret_cast<ESP*>(cbd)->verboseDownloadCb(df);
}

bool ESP::verboseDownloadCb(const Dumpfile&df) {
 printf("Downloading %d bytes at %08x",df.size,df.addr);
 fflush(stdout);
 BeginInfo bi={
  df.size,
  div_ceil(df.size,RAM_BLOCK),
  RAM_BLOCK,
  df.addr};
 if (!bi.size) return true;
 if (!mem_begin(bi)) return false;
 DataInfo di={bi.sblk};
 for (uint32 i=0; i<bi.size; i+=di.sblk,di.iblk++) {
  if (di.sblk>bi.size-i) di.sblk=bi.size-i;
  if (!mem_block(di,df.data()+i)) return false;
  printf(".");
 }
 println("done!");
 return true;
}

ESP::~ESP() {
 CloseHandle(_port);
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded