flashesp.pl 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #!/usr/bin/perl
  2. use strict;
  3. use integer;
  4. use PerlIO::gzip;
  5. use File::Temp;
  6. use File::Spec;
  7. my $esptool = ($^O eq 'MSWin32') ? 'esptool.exe' : 'esptool.py';
  8. $esptool = $ENV{'ESPTOOL'} || $esptool;
  9. my $FW_MAGIC = 0x7a07fbd6;
  10. my %datatypes = (
  11. 'end' => 0, # End of data
  12. 'data' => 1, # FPGA flash data
  13. 'target' => 2, # Firmware target string
  14. 'note' => 3, # Informative string
  15. 'espota' => 4, # ESP32 OTA image
  16. 'fpgainit' => 5, # FPGA bypass (transient) image during update
  17. 'esppart' => 6, # ESP32 partition table
  18. 'espsys' => 7, # ESP32 boot loader, OTA control partition...
  19. 'esptool' => 8 # esptool.py options for flashing
  20. );
  21. my @type;
  22. foreach my $t (keys(%datatypes)) {
  23. $type[$datatypes{$t}] = $t;
  24. }
  25. my $FDF_OPTIONAL = 0x0001;
  26. my $STRING_MAX_LEN = 4095;
  27. sub getint($) {
  28. my($s) = @_;
  29. return undef
  30. unless ($s =~ /^(([1-9][0-9]+)|(0(x[0-9a-f]+|[0-7]*)))([kmgtpe]?)$/i);
  31. my $o = oct($3) + $2;
  32. my $p = lc($5);
  33. if ($p eq 'k') {
  34. $o <<= 10;
  35. } elsif ($p eq 'm') {
  36. $o <<= 20;
  37. } elsif ($p eq 'g') {
  38. $o <<= 30;
  39. } elsif ($p eq 't') {
  40. $o <<= 40;
  41. } elsif ($p eq 'p') {
  42. $o <<= 50;
  43. } elsif ($p eq 'e') {
  44. $o <<= 60;
  45. }
  46. return $o;
  47. }
  48. sub filelen($) {
  49. my($f) = @_;
  50. my @s = stat($f);
  51. return $s[7];
  52. }
  53. sub unquote_cmd($) {
  54. my($s) = @_;
  55. my @a;
  56. $s =~ s/[\r\n]+/ /g;
  57. while ($s =~ /^\s*(?:\"((?:[^\"]+|\"\")*)\"|(\S+))(\s.*)?$/) {
  58. push(@a, $1.$2);
  59. $s = $3;
  60. }
  61. return @a;
  62. }
  63. my @args = @ARGV;
  64. my $file = shift(@args);
  65. if (!defined($file)) {
  66. die "Usage: $0 fwfile [esptool options...]\n";
  67. }
  68. open(my $fw, '<:gzip', $file)
  69. or die "$0: $file: $!\n";
  70. my @chunks = ();
  71. my $err = 0;
  72. my $hdr;
  73. while (read($fw, $hdr, 16) == 16) {
  74. # magic type flags data_len addr
  75. my @h = unpack('VvvVV', $hdr);
  76. my $c = { 'hdr' => $hdr, 'magic' => $h[0], 'type' => $h[1],
  77. 'flags' => $h[2], 'len' => $h[3], 'addr' => $h[4] };
  78. if ($c->{'magic'} != $FW_MAGIC) {
  79. print STDERR "$0: $file: bad chunk magic\n";
  80. $err = 1;
  81. last;
  82. }
  83. my $t = $type[$c->{'type'}];
  84. last if ($t eq 'end'); # End of stream
  85. my $d;
  86. if (read($fw, $d, $c->{'len'}) != $c->{'len'}) {
  87. print STDERR "$0: $file: short chunk read\n";
  88. $err = 1;
  89. last;
  90. }
  91. $c->{'data'} = $d;
  92. push(@chunks, $c);
  93. }
  94. close($fw);
  95. exit $err if ($err);
  96. my $td = File::Temp->newdir(CLEANUP => 1);
  97. my @espfiles = ();
  98. my %espopt = ('before' => 'default_reset', 'after' => 'hard_reset',
  99. 'baud' => 115200, 'port' => undef, 'chip' => undef,
  100. 'flash_mode' => undef, 'flash_freq' => undef,
  101. 'flash_size' => undef);
  102. my $nc = 0;
  103. foreach my $c ( @chunks ) {
  104. my $t = $type[$c->{'type'}];
  105. if ($t eq 'esptool') {
  106. my $s = $c->{'data'};
  107. $s =~ s/[\r\n]+/ /g;
  108. while ($s =~ /^\s*(\S+)\s+(\S+)(.*)/) {
  109. $espopt{$1} = $2;
  110. $s = $3;
  111. }
  112. } elsif ($t =~ /^esp/ && $c->{'addr'}) {
  113. my $addr = sprintf('%x', $c->{'addr'});
  114. my $tff = File::Spec->catfile($td, $t.$nc.'_'.$addr.'.bin');
  115. open(my $tf, '>', $tff) or die "$0: $tff: $!\n";
  116. print $tf $c->{'data'};
  117. close($tf);
  118. push(@espfiles, '0x'.$addr, $tff);
  119. }
  120. $nc++;
  121. }
  122. foreach my $e (keys %espopt) {
  123. my $ev = $ENV{"ESP\U$e"};
  124. if (defined($ev)) {
  125. $espopt{$e} = $ev;
  126. }
  127. }
  128. my @espcmd = unquote_cmd($esptool);
  129. foreach my $o (sort grep (!/^flash_/, keys %espopt)) {
  130. if (defined($espopt{$o})) {
  131. push(@espcmd, "--$o", $espopt{$o});
  132. }
  133. }
  134. push(@espcmd, @args);
  135. push(@espcmd, 'write_flash', '-z');
  136. foreach my $o (sort grep (/^flash_/, keys %espopt)) {
  137. if (defined($espopt{$o})) {
  138. push(@espcmd, "--$o", $espopt{$o});
  139. }
  140. }
  141. push(@espcmd, @espfiles);
  142. if (defined($ENV{'VERBOSE'})) {
  143. print STDERR join(' ', @espcmd), "\n";
  144. }
  145. $err = system(@espcmd);
  146. if ($err == -1) {
  147. print STDERR "$0: $espcmd[0]: $!\n";
  148. }
  149. exit !!$err;