123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- #!/usr/bin/perl
- use strict;
- use integer;
- use Digest::MD5;
- my $FW_MAGIC = 0x7a07fbd6;
- my %datatypes = (
- 'end' => 0, # End of data
- 'data' => 1, # FPGA flash data
- 'target' => 2, # Firmware target string
- 'note' => 3, # Informative string
- 'espota' => 4, # ESP32 OTA image
- 'fpgainit' => 5 # FPGA bypass (transient) image during update
- );
- my $FDF_OPTIONAL = 0x0001;
- my $STRING_MAX_LEN = 4095;
- sub getint($) {
- my($s) = @_;
- return undef
- unless ($s =~ /^(([1-9][0-9]+)|(0(x[0-9a-f]+|[0-7]*)))([kmgtpe]?)$/i);
- my $o = oct($3) + $2;
- my $p = lc($5);
- if ($p eq 'k') {
- $o <<= 10;
- } elsif ($p eq 'm') {
- $o <<= 20;
- } elsif ($p eq 'g') {
- $o <<= 30;
- } elsif ($p eq 't') {
- $o <<= 40;
- } elsif ($p eq 'p') {
- $o <<= 50;
- } elsif ($p eq 'e') {
- $o <<= 60;
- }
- return $o;
- }
- sub filelen($) {
- my($f) = @_;
- my @s = stat($f);
- return $s[7];
- }
- sub output_chunk($$$) {
- my($out,$data,$options) = @_;
- print $out pack("VvvVV", $FW_MAGIC,
- $options->{'type'}, $options->{'flags'},
- length($data), $options->{'addr'});
- printf STDERR "chunk: type %u flags 0x%x length %u addr 0x%x\n",
- $options->{'type'}, $options->{'flags'},
- length($data), $options->{'addr'};
- print $out $data;
- }
- if (!scalar(@ARGV)) {
- die "Usage: $0 [-o outfile] [options command]...\n".
- "Options:\n".
- "\t-type datatype\n".
- "\t-addr address (or equivalent)\n".
- "\t-optional\n".
- "\t-required\n".
- "Commands:\n".
- "\t-file data_file\n".
- "\t-str data_string\n";
- }
- our $outfile;
- our $out;
- sub delete_out {
- close($out);
- unlink($outfile);
- }
- if ($ARGV[0] eq '-o') {
- shift @ARGV;
- $outfile = shift @ARGV;
- }
- if ($outfile ne '' && $outfile ne '-') {
- open($out, '>', $outfile) or
- die "$0: $outfile: $!\n";
- $SIG{'INT'} = \&delete_out;
- $SIG{'QUIT'} = \&delete_out;
- $SIG{'TERM'} = \&delete_out;
- $SIG{'__DIE__'} = \&delete_out;
- } else {
- $outfile = '-';
- $out = \*STDOUT;
- }
- binmode $out;
- my %default_options = {
- 'type' => $datatypes{'data'},
- 'addr' => 0,
- 'flags' => 0
- };
- my $err;
- my %options = %default_options;
- while (1) {
- my $what = shift @ARGV;
- last if (!defined($what));
- if ($what eq '-type') {
- my $arg = lc(shift @ARGV);
- $options{'type'} = $datatypes{$arg} || getint($arg);
- if (!$arg) {
- die "$0: invalid data type: $arg";
- }
- } elsif ($what eq '-addr') {
- my $arg = shift @ARGV;
- $options{'addr'} = getint($arg);
- } elsif ($what eq '-optional') {
- $options{'flags'} |= $FDF_OPTIONAL;
- } elsif ($what eq '-required') {
- $options{'flags'} &= ~$FDF_OPTIONAL;
- } elsif ($what eq '-file') {
- my $infile = shift @ARGV;
- my $in;
- if (!open($in, '<', $infile)) {
- die "$0: $infile: $!\n";
- }
- binmode($in);
- my @is = stat($in);
- my $data;
- my $dlen = read($in, $data, $is[7]);
- close($in);
- output_chunk($out, $data, \%options);
- undef $data;
- %options = %default_options;
- } elsif ($what eq '-str') {
- my $str = shift @ARGV;
- if (length($str) > $STRING_MAX_LEN) {
- die "$0: string too long\n";
- }
-
- output_chunk($out, $str, \%options);
- %options = %default_options;
- } else {
- die "$0: unknown argument: $what\n";
- }
- }
- output_chunk($out, '', {'type' => $datatypes{'end'}});
- close($out);
|