Quelltext /~heha/hsn/bl/hid-flash.zip/src/main.cpp

/*
 STM32 HID Bootloader - USB HID bootloader for STM32F10x and STM32F4xx
 Copyright 2018	Bruno Freitas - bruno@brunofreitas.com
 20 April 2018	Vassilis Serasidis <avrsite@yahoo.gr>
*211007	Reduced code size + smell, COM port now optional
+240201	Support for STM32F401 preprogrammed WeAct BL added and verified
 This HID bootloader work with bluepill + STM32duino + Arduino IDE <http://www.stm32duino.com/>
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "rs232.h"
#include "hidapi.h"

#define SECTOR_SIZE 1024

#define VID_PID_103 0xBEBA1209	// My STM32F103 HID bootloader
#define VID_PID_401 0x572A0483	// STM32F401 on black pill bootloader
#define FIRMWARE_VER  0x0300

#ifndef _WIN32
# define _cdecl
# include <unistd.h>
# define _snprintf snprintf
void Sleep(unsigned ms) {usleep(ms*1000);}
#endif

static const char*gComName;

#ifdef _WIN32
static UINT consoleCP=CP_OEMCP;
#endif

static void hyphen() {
#ifdef _WIN32
 printf(consoleCP==CP_UTF8 ? " — " : " -- ");
#else
 printf(" — ");
#endif
}

static void done(bool ok) {
 hyphen();
 puts(ok?"Done.":"Error!");
}

// The sole usage for the serial port is resetting the STM32
// and to activate the HID bootloader.
// This saves user interaction, otherwise, RESET button must be pressed.
static bool serial_init(char*name) {
 unsigned ms=0;
 char*dp=strrchr(name,':');
 if (dp) {
  *dp=0;		// make COM port name without ':' part
  ms=strtoul(++dp,0,0);	// get delay value
 }
 gComName=name;
 printf("> Open \"%s\"\n",name);
 RS232 port(name);
 if (!port) return false;
 printf("> Toggling DTR...");
 port.disableRTS();
 port.enableDTR();
 Sleep(200);
 port.disableDTR();
 Sleep(200);
 port.enableDTR();
 Sleep(200);
 port.disableDTR();
 Sleep(200);
 port.send_magic();
 Sleep(200);
 Sleep(ms);
 done(true);
 return true;
}

static int memstrcmp(const void*mem, const char*str) {return memcmp(mem,str,strlen(str));}
static void memstrcpy(void*mem,const char*str,char suffix) {
 size_t l=strlen(str);
 memcpy(mem,str,l);
 ((char*)mem)[l]=suffix;
}

typedef unsigned char byte;

// subclass to fill the virtual match() procedure
class MyHidDev:public HidDev{
 bool match() const;
public:
 void retry_msg() const;
 bool write(byte*buffer);
};

bool MyHidDev::match() const{
 static const uint32_t vidpids[]={VID_PID_103,VID_PID_401};
    //wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID);
 for (int i=0; i<elemof(vidpids); i++)
	if (vidpid()==vidpids[i]) return true;
 return false;
}

bool MyHidDev::write(byte*buffer) {
 return setOutputReport(buffer,100);	// many attempts
}

void MyHidDev::retry_msg() const{
 static byte k;
 putchar("\\|/-"[k&3]); k++; putchar('\b'); fflush(stdout);
 Sleep(100);
}

int _cdecl main(int argc, char *argv[]) {
 static const char BTLDCMD[] = "BTLDCMD";	// original (not mine) Serasidis / Arduino bootloader on STM32F103
 static const char WEACT[]   = "WeAct:";	// magic seen on STM32F401 black pill board: Chinese boot loader
#ifdef _WIN32
 static const char COM1[] = "COM";
 static const char COM2[] = "com";
 if (SetConsoleOutputCP(CP_UTF8)) consoleCP=CP_UTF8;
#else
 static const char COM1[] = "ttyS";
 static const char COM2[] = "ttyUSB";
#endif
 int error = 0;
 MyHidDev dev;
 byte*bufO=0,*bufI=0,*bufF=0;
 if (argc<2) {	// this box contain tabs, tab size should be 8 in your damn editor, what else!
 fputs("┌",stdout); int l=63; do fputs("─",stdout); while (--l);  puts("┐");
  puts("│ HID-Flash v2.2.4 - STM32 driverless HID Bootloader Flash Tool	│\n"
       "│ (c)		2018 - Bruno Freitas	www.brunofreitas.com	│\n"
       "│ (c) 2018-2019 - Vassilis Serasidis	www.serasidis.gr	│\n"
       "│ Customized for STM32duino ecosystem	www.stm32duino.com	│\n"
       "│	 2021-2024 - Henrik Haftmann	www.tu-chemnitz.de/~heha│");
 fputs("└",stdout);	l=63; do fputs("─",stdout); while (--l);  puts("┘");
  puts("Usage:  hid-flash <binary_firmware_file> [comport[:delay-ms]]");
  puts("	hid-flash start | erase");
  return 1;
 }
 setbuf(stdout,0);
 FILE*firmware_file = 0;
 bool start=false, erase=false, weact=false, resetpagesSent=false;
 for (char**argp=argv+1;;argp++) {
  char*arg=*argp;
  if (!arg) break;	// no more command-line arguments
  if (!strcmp(arg,"start")) start=true;
  else if (!strcmp(arg,"erase")) erase=true;
  else{
#ifdef UNICODE	// arg may contain non-ASCII characters, so use wide-char open function
   int l=MultiByteToWideChar(CP_UTF8,0,arg,-1,0,0);	// l counts '\0' too
   wchar_t*n=new wchar_t[l];
   MultiByteToWideChar(CP_UTF8,0,arg,-1,n,l);
   firmware_file = _wfopen(n,L"rb");
   delete[]n;
#else
   firmware_file = fopen(arg,"rb");
#endif
   if (!firmware_file) {
    printf("> Error opening firmware file %s\n", arg);
    return error;
   }
   if (argp[1]		// next argument given, and looking like a COM port?
   && (strstr(COM1,argp[1]) || strstr(COM2,argp[1]))) {
    arg=*++argp;	// eat up
    if (!serial_init(arg)) printf("> Unable to open %s\n",arg);
   }
  }
// 240201: Now, multiple arguments may be given on command line
// The first processed arg opens the HID device.
  if (!dev) {
   printf("> Searching for STM32 device");
   if (dev.connect()) {			// search first occurence
    if (dev.vidpid()==VID_PID_103
    && (!dev.caps.FeatureReportByteLength   // Feature reports show new firmware
    || dev.VersionNumber() < FIRMWARE_VER)) { //The STM32 board has firmware lower than 3.00
     hyphen();
     puts("Error!");
     puts("\tPlease update the firmware to the latest version (v3.00+)");
     goto exit;
    }
   }else{
    hyphen();
    puts("Not found!");
    error = 1;
    goto exit;
   }
   hyphen();
   printf("found [%04X:%04X].\n",LOWORD(dev.vidpid()),HIWORD(dev.vidpid()));
   if (dev.caps.OutputReportByteLength!=65) {
    puts("Output report size must be 64 (plus one for the report ID byte)!");
    goto exit;
   }
   bufO=new byte[dev.caps.OutputReportByteLength];
   memset(bufO,0,sizeof bufO);
   bufI=new byte[dev.caps.InputReportByteLength];
   bufF=new byte[dev.caps.FeatureReportByteLength];
   if (dev.vidpid()==VID_PID_401) {
    memstrcpy(bufO+1,WEACT,2);	// 2 = get version string
    dev.write(bufO);
    if (dev.getInputReport(bufI,1,200)
    && !memstrcmp(bufI+1,WEACT)) {
     printf("> Version string is \"%s\".\n",bufI+1);
     weact=true;
    }
   }
  }
  if (erase) {
   if (weact) {
    printf("> Erase flash");
    memstrcpy(bufO+1,WEACT,4);
    dev.write(bufO);
    bool ok=dev.getInputReport(bufI,50,200);	// may take a while, emits '.' on each retry
    done(ok);
   }else puts("> \"erase\" is only available on WeAct boot loader, parameter ignored.");
   erase=false;
  }
  if (firmware_file) {
   bool ok=true;
   unsigned filesize = 0;	// filesize zero denotes binary data from pipe
   if (!fseek(firmware_file,0,SEEK_END)) {
    filesize = ftell(firmware_file);
    fseek(firmware_file,0,SEEK_SET);
    if (filesize<16) {
     printf("> firmware file %s is empty!\n",arg);
     ok=false;
    }
   }
// Only for the first binary file
   if (ok && !resetpagesSent) {
    printf("> Initialize flashing");
// Send RESET PAGES command to put HID bootloader in initial stage...
    if (dev.caps.FeatureReportByteLength) {	// Firmware with feature reports
     bufF[0]=0;
     bufF[1]=1;
     ok=dev.setFeatureReport(bufF);
    }else{
     memstrcpy(bufO+1,weact?WEACT:BTLDCMD,0);
// Flash is unavailable when writing to it, so USB interrupt may fail here
     ok=dev.write(bufO);
    }
    resetpagesSent=ok;
    done(ok);
   }
   if(ok) {
// Send Firmware File data
    const char*sizestr="unknown";
    int w=6;	// field width (= digits to reserve) for progress
    if (filesize) {
     char buf[8];
     w=(int)_snprintf(buf,sizeof buf,"%u",(filesize+SECTOR_SIZE-1)&~(SECTOR_SIZE-1));
     _snprintf(buf,sizeof buf,"%u",filesize);
     sizestr=buf;
    }
    printf("> Flashing firmware, size = %s, current = %*u",sizestr,w,0);
    fflush(stdout);
    for (int bw=0;;) {
     int br,k;
     for(br=0;br<64;br+=k) {		// For stream input read until chunk is full or EOF occurs
      k=(int)fread(bufO+1+br,1,64-br,firmware_file);
      if (k<0) {br=k; break;}		// error, bail out with negative result
      if (!k) break;			// end-of-stream, return bytes read so far
     }
     if (br<0) break;			// error reading file
     memset(bufO+1+br,0xFF,64-br);	// clear rest-of-buffer
     if (!dev.write(bufO)) break;
     bw+=64;
     if (!(bw&SECTOR_SIZE-1)) {		// reached one sector: expect answer
      for(int k=0;k<w;k++) putchar('\b');
      printf("%*u",w,bw);	// update byte count in sector intervals (may be larger than file size)
      fflush(stdout);
      ok=dev.getInputReport(bufI,1,500);	// wait upto 0.5 s for answer after flashing
      if (!ok) break;
      if (dev.caps.FeatureReportByteLength) {	// modern driver? 1-Byte answer
       if (bufI[1]!=1) {ok=false; break;}	// bail out when wrong
      }else if (weact) {			// Serasidis driver? 8-Byte answer
       if (memstrcmp(bufI+1,WEACT) || bufI[7]!=3) {ok=false; break;}
      }else{
       if (memstrcmp(bufI+1,BTLDCMD) || bufI[8]!=2) {ok=false; break;}	// bail out when wrong
      }
      if (br<64) break;	// end of file or inputstream reached, leave loop
     }
    }
    done(ok);
   }
   if (ok) start=true; else error = 1;
   fclose(firmware_file);
   firmware_file=0;
  }
 }// for loop
 if (dev && start) {
// Send CMD_REBOOT_MCU command to reboot the microcontroller...
  bool ok;
  printf("> Reboot STM32 controller and start application");
  if (dev.caps.FeatureReportByteLength) {
   bufF[0]=0;
   bufF[1]=2;
   ok=dev.setFeatureReport(bufF);
  }else{
   memstrcpy(bufO+1,weact?WEACT:BTLDCMD,1);
// Flash is unavailable when writing to it, so USB interrupt may fail here
   ok=dev.write(bufO);
  }
  done(ok);
 }
  
exit:
 if (bufF)delete[]bufF;
 if (bufI)delete[]bufI;
 if (bufO)delete[]bufO;
// Dirty incomprehensible hack
 if (gComName) {
  printf("> Searching for %s",gComName);
  int i;
  for (i=0;i<5;i++){
   RS232 port(gComName);
   if (port) {
    hyphen();
    puts("found.");
    break;
   }
   dev.retry_msg();
  }
  if(i==5) {
   hyphen();
   puts("Not found!");
  }
 }
 return error;
}

#ifdef _WIN32
// To reduce executable file size by whopping 30 KB,
// this startup code feeds main() with arguments.
// Initializing RAM with DATA, BSS, HEAP and STACK
// is done by Windows executable loader.
// As this program doesn't use static objects,
// invoking their constructors is not needed here.

# ifdef UNICODE	// NT based systems, 32 and 64 bit
void mainCRTStartup() {
 int argc;
 wchar_t**argvW=CommandLineToArgvW(GetCommandLine(),&argc);
 char**argv=new char*[argc+1],**argvP=argv;
 for (int i=0; i<argc; i++,argvP++) {
  int len=WideCharToMultiByte(CP_UTF8,0,argvW[i],-1,0,0,0,0);
  *argvP=new char[len];
  WideCharToMultiByte(CP_UTF8,0,argvW[i],-1,*argvP,len,0,0);
 }
 *argvP=0;
 LocalFree(argvW); 
 ExitProcess(main(argc,argv));
}
# else
// In case of Windows 98/Me, there is no Windows helper
// function to dissect the command line.
// So use the dissect routine inside msvcrt.dll.
EXTERN_C _CRTIMP int __cdecl __getmainargs(int&,char**&,char**&,int);
void mainCRTStartup() {
 int argc;
 char**argv,**envp;
 __getmainargs(argc,argv,envp,TRUE);
 ExitProcess(main(argc,argv));
}
# endif
#endif
Vorgefundene Kodierung: UTF-80