3 # parse_prism_ap_fw - convert binary tertiary firmware to S-Record format
5 # Copyright (C) 2004 Pavel Roskin <proski@gnu.org>
7 # This script is Free Software, and it can be copied, distributed and
8 # modified as defined in the GNU General Public License. A copy of
9 # its license can be downloaded from http://www.gnu.org/copyleft/gpl.html
12 # parse_prism_ap_fw infile
15 use open IN
=> ":raw", OUT
=> ":raw";
20 return (($word & 0xff) << 8) + (($word & 0xff00) >> 8);
21 my $radix = 0x1000000;
24 $res += $radix * ($word & 0xff);
34 my $radix = 0x1000000;
37 $res += $radix * ($word & 0xff);
47 read INFILE
,$byte_a,1;
49 read INFILE
,$byte_b,1;
50 return (ord($byte_a) << 8) + ord($byte_b);
56 read INFILE
,$byte_a,1;
58 read INFILE
,$byte_b,1;
60 read INFILE
,$byte_c,1;
62 read INFILE
,$byte_d,1;
63 return (ord($byte_c) << 24) + (ord($byte_d) << 16) + (ord($byte_a) << 8) + ord($byte_b);
68 my $line = sprintf(shift (@_), @_);
70 # Split, clean empty elements.
71 my @twochars = split (/(..)/, $line);
72 foreach my $index (0 .. $#twochars) {
73 unless ($twochars[$index] =~ m/../) {
74 splice (@twochars, $index, 1);
76 last if ($index >= $#twochars);
79 # Print Sn, find record length
80 printf OUTFILE
"%s", shift(@twochars);
81 my $bytelen = hex $twochars[0];
83 # Calculate CRC, print the line
85 foreach my $index (0 .. $bytelen - 1) {
86 $crc += hex($twochars[$index]);
87 printf OUTFILE
"$twochars[$index]";
89 printf OUTFILE
"%02X\n", (0xFF - $crc) & 0xFF;
102 while (defined $bl_addr[$block]) {
103 if ( ($bl_addr[$block] <= $a) &&
104 ($bl_addr[$block] + $bl_len[$block] > $a) ) {
105 return ($bl_offset[$block] + $a - $bl_addr[$block]);
112 # Print message and exit (like "die", but without raising an exception).
113 # Newline is added at the end.
116 printf STDERR
"ERROR: ";
165 error
("Usage: parse_prism_ap_fw infile");
168 unless (open (INFILE
, "< $ARGV[0]")) {
169 error
("couldn't open $ARGV[0] for reading: $!");
172 # Skip to the beginning of firmware
175 last if (/reserved\./);
184 my $min_addr = 0x7fffffff;
186 while (!eof(INFILE
)) {
187 $addr = readnum_cdab
();
188 $len = 2 * readnum_ab
();
194 push @bl_offset, tell(INFILE
);
195 push @bl_addr, $addr;
198 $min_addr = $addr if ($addr < $min_addr);
199 $max_addr = ($addr + $len) if ($addr + $len > $max_addr);
201 seek (INFILE
, $len, 1);
204 # Find first non-zero word
205 while (readnum_ab
() == 0) {}
206 my $pdroffset = tell(INFILE
) - 2;
207 seek(INFILE
, $pdroffset, 0);
210 while (!eof(INFILE
)) {
211 my $pdtype = readnum_ab
();
213 $pdr6addr = readnum_cdab
();
216 } elsif ($pdtype == 0x107) {
217 $fwidaddr = readnum_cdab
() + 0x2a;
220 } elsif ($pdtype == 0) {
226 if ($pdr6addr == 0) {
227 error
"Cannot identify firmware - no PDR 6";
230 my $pdr6offset = find_offset
($pdr6addr);
231 if ($pdr6offset == 0) {
232 error
"Cannot identify firmware - PDR 6 doesn't point to the image";
235 my $fwidoffset = find_offset
($fwidaddr);
236 if ($fwidoffset == 0) {
237 error
"Cannot identify firmware - Firmware ID string is not in the image";
240 seek(INFILE
, $fwidoffset, 0);
241 my $tertmagic = readnum_ab
();
242 if ($tertmagic != 0x6554) {
243 error
"Firmware ID doesn't start with \"Te\"";
246 seek(INFILE
, $pdr6offset + 14, 0);
247 my $hwver = readnum_ab
();
248 seek(INFILE
, $pdr6offset + 20, 0);
249 my $compid = readnum_ab
();
250 my $variant = readnum_ab
();
251 my $major = readnum_ab
();
252 my $minor = readnum_ab
();
254 if ($compid != 0x014b) {
255 error
"Component ID %04X - not tertiary firmware?\n", $compid;
262 $hwtext = "hfa3841 (Prism 2)";
263 $nictable = $nic3841;
266 } elsif ($hwver == 2) {
267 $hwtext = "hfa3842 (Prism 2.5 and 3)";
268 $nictable = $nic3842;
271 error
"Unknown hardware version %d\n", $hwver;
274 printf "Found tertiary firmware %d.%d.%d for %s\n",
275 $major, $minor, $variant, $hwtext;
280 $varstr = sprintf("c%01x", $variant);
282 $varstr = sprintf("%02x", $variant);
285 $intname = sprintf("t%s%02x%02x%s.hex", $hwchar, $major, $minor, $varstr);
286 printf "Firmware internal name: %s\n", $intname;
288 unless (open(OUTFILE
, "> $intname")) {
289 error
"Cannot open %s for writing: $!", $intname;
299 while (defined $bl_addr[$block]) {
300 seek(INFILE
, $bl_offset[$block], 0);
301 my $len = $bl_len[$block];
306 if ($count % 16 == 0) {
308 $rest = $len if ($len < 16);
309 $line = sprintf("S3%02X%08X", $rest + 5,
310 $bl_addr[$block] + $count);
312 read INFILE
, $byte1, 1;
313 read INFILE
, $byte2, 1;
314 $line .= sprintf("%02X%02X", ord($byte2), ord($byte1));
318 if ($count % 16 == 0 && $len) {
326 # FWID PDR - firmware file name
327 crc_print
("S311FF000000FFFFFFFF%08X0E000000", swap32
($fwidaddr));
330 seek(INFILE
, $pdroffset, 0);
331 while (!eof(INFILE
)) {
332 my $pdtype = readnum_ab
();
333 last if ($pdtype == 0);
334 my $pdaddr = readnum_cdab
();
335 my $pdlen = readnum_ab
();
336 crc_print
("S311FF000000%08X%08X%08X",
337 swap32
($pdtype), swap32
($pdaddr), swap32
($pdlen));
340 # CRC record - cover whole image, don't program CRC
341 crc_print
("S311FF100000%08X%08X00000000", swap32
($min_addr),
342 swap32
($max_addr - $min_addr));
345 crc_print
"S311FF20000005000100%04X%04X%04X%04X", swap16
($compid),
346 swap16
($variant), swap16
($major), swap16
($minor);
348 # Compatibility ranges
350 crc_print
"S313FF2000000600020001000100010001000100";
351 crc_print
"S313FF2000000600020001000200010001000100";
352 crc_print
"S313FF2000000600020001000300010002000200";
354 } elsif ($hwver == 2) {
355 crc_print
"S313FF2000000600020001000100010001000100";
356 crc_print
"S313FF2000000600020001000200020001000100";
357 if ((($major << 8) + $minor) >= 0x0102) {
358 # tertiary f/w 1.2.x and newer - primary f/w 1.1.x
359 crc_print
"S313FF2000000600020001000300010004000400";
361 # tertiary f/w 1.1.x and older - primary f/w 1.0.x
362 crc_print
"S313FF2000000600020001000300010002000200";
367 $line = "S317FF20000007000180";
368 my @namechars = split(//, $intname);
369 foreach (@namechars) {
370 $line .= sprintf("%02X", ord($_));
377 while (defined $nictable->[$nic][0]) {
378 crc_print
("S311FF20000005000400%04X%04X%04X%04X",
379 swap16
($nictable->[$nic][0]), swap16
($nictable->[$nic][3]),
380 swap16
($nictable->[$nic][1]), swap16
($nictable->[$nic][2]));
384 # At last, write starting address
385 crc_print
"S705%08X", $start_addr;