Revert "svn-1276"
[orinoco_usb.git] / drivers / net / wireless / firmware / parse_prism_ap_fw
blob3373d8cce19605d9dba27a92a02d246579a4ed8a
1 #!/usr/bin/perl -w
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
11 # Usage:
12 # parse_prism_ap_fw infile
14 use strict;
15 use open IN => ":raw", OUT => ":raw";
17 sub swap16 ($)
19 my $word = shift(@_);
20 return (($word & 0xff) << 8) + (($word & 0xff00) >> 8);
21 my $radix = 0x1000000;
22 my $res = 0;
23 while ($word != 0) {
24 $res += $radix * ($word & 0xff);
25 $word >>= 8;
26 $radix >>= 8;
28 return $res;
31 sub swap32 ($)
33 my $word = shift(@_);
34 my $radix = 0x1000000;
35 my $res = 0;
36 while ($word != 0) {
37 $res += $radix * ($word & 0xff);
38 $word >>= 8;
39 $radix >>= 8;
41 return $res;
44 sub readnum_ab ()
46 my $byte_a;
47 read INFILE,$byte_a,1;
48 my $byte_b;
49 read INFILE,$byte_b,1;
50 return (ord($byte_a) << 8) + ord($byte_b);
53 sub readnum_cdab ()
55 my $byte_a;
56 read INFILE,$byte_a,1;
57 my $byte_b;
58 read INFILE,$byte_b,1;
59 my $byte_c;
60 read INFILE,$byte_c,1;
61 my $byte_d;
62 read INFILE,$byte_d,1;
63 return (ord($byte_c) << 24) + (ord($byte_d) << 16) + (ord($byte_a) << 8) + ord($byte_b);
66 sub crc_print
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
84 my $crc = 0;
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;
94 my @bl_offset;
95 my @bl_addr;
96 my @bl_len;
98 sub find_offset ($)
100 my $a = shift(@_);
101 my $block = 0;
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]);
107 $block++;
109 return 0;
112 # Print message and exit (like "die", but without raising an exception).
113 # Newline is added at the end.
114 sub error
116 printf STDERR "ERROR: ";
117 printf STDERR @_;
118 printf STDERR "\n";
119 exit 1;
123 my $addr;
124 my $len;
125 my $line;
126 my $start_addr = 0;
127 my $pdr6addr = 0;
128 my $fwidaddr = 0;
130 my $nic3841 = [
131 [0x8002, 1, 0, 0],
132 [0x8002, 1, 0, 1],
133 [0x8003, 1, 0, 0],
134 [0x8003, 1, 0, 1],
135 [0x8004, 1, 0, 0],
136 [0x8008, 1, 0, 0],
139 my $nic3842 = [
140 [0x800A, 1, 0, 0],
141 [0x800B, 1, 0, 0],
142 [0x800C, 1, 0, 0],
143 [0x800D, 1, 0, 0],
144 [0x800E, 1, 0, 0],
145 [0x8012, 1, 0, 0],
146 [0x8013, 1, 0, 0],
147 [0x8014, 1, 0, 0],
148 [0x8015, 1, 0, 0],
149 [0x8016, 1, 0, 0],
150 [0x8017, 1, 0, 0],
151 [0x8018, 1, 0, 0],
152 [0x8019, 1, 0, 0],
153 [0x801A, 1, 0, 0],
154 [0x801B, 1, 0, 0],
155 [0x801C, 1, 0, 0],
156 [0x801D, 1, 0, 0],
157 [0x8021, 1, 0, 0],
158 [0x8022, 1, 0, 0],
159 [0x8023, 1, 0, 0],
160 [0x8024, 1, 0, 0],
164 if ($#ARGV != 0) {
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
173 $/ = "\x00";
174 while (<INFILE>) {
175 last if (/reserved\./);
180 # First pass
183 # Data blocks
184 my $min_addr = 0x7fffffff;
185 my $max_addr = 0;
186 while (!eof(INFILE)) {
187 $addr = readnum_cdab();
188 $len = 2 * readnum_ab();
189 if ($len == 0) {
190 $start_addr = $addr;
191 last;
194 push @bl_offset, tell(INFILE);
195 push @bl_addr, $addr;
196 push @bl_len, $len;
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);
209 # PDRs
210 while (!eof(INFILE)) {
211 my $pdtype = readnum_ab();
212 if ($pdtype == 6) {
213 $pdr6addr = readnum_cdab();
214 seek (INFILE, 2, 1);
215 next;
216 } elsif ($pdtype == 0x107) {
217 $fwidaddr = readnum_cdab() + 0x2a;
218 seek (INFILE, 2, 1);
219 next;
220 } elsif ($pdtype == 0) {
221 last;
223 seek (INFILE, 6, 1);
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;
258 my $nictable;
259 my $hwchar;
260 my $hwtext;
261 if ($hwver == 1) {
262 $hwtext = "hfa3841 (Prism 2)";
263 $nictable = $nic3841;
264 $hwchar = "1";
266 } elsif ($hwver == 2) {
267 $hwtext = "hfa3842 (Prism 2.5 and 3)";
268 $nictable = $nic3842;
269 $hwchar = "f";
270 } else {
271 error "Unknown hardware version %d\n", $hwver;
274 printf "Found tertiary firmware %d.%d.%d for %s\n",
275 $major, $minor, $variant, $hwtext;
277 my $intname;
278 my $varstr;
279 if ($major == 0) {
280 $varstr = sprintf("c%01x", $variant);
281 } else {
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;
294 # Second pass
297 # Data blocks
298 my $block = 0;
299 while (defined $bl_addr[$block]) {
300 seek(INFILE, $bl_offset[$block], 0);
301 my $len = $bl_len[$block];
302 my $count = 0;
303 while (1) {
304 my $byte1;
305 my $byte2;
306 if ($count % 16 == 0) {
307 my $rest = 16;
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));
315 $count += 2;
316 $len -= 2;
317 last if ($len <= 0);
318 if ($count % 16 == 0 && $len) {
319 crc_print ($line);
322 crc_print ($line);
323 $block++;
326 # FWID PDR - firmware file name
327 crc_print("S311FF000000FFFFFFFF%08X0E000000", swap32($fwidaddr));
329 # Other PDRs
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));
344 # Firmware version
345 crc_print "S311FF20000005000100%04X%04X%04X%04X", swap16($compid),
346 swap16($variant), swap16($major), swap16($minor);
348 # Compatibility ranges
349 if ($hwver == 1) {
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";
360 } else {
361 # tertiary f/w 1.1.x and older - primary f/w 1.0.x
362 crc_print "S313FF2000000600020001000300010002000200";
366 # Included name
367 $line = "S317FF20000007000180";
368 my @namechars = split(//, $intname);
369 foreach (@namechars) {
370 $line .= sprintf("%02X", ord($_));
372 $line .= "0000";
373 crc_print $line;
375 # NIC ID table
376 my $nic = 0;
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]));
381 $nic++;
384 # At last, write starting address
385 crc_print "S705%08X", $start_addr;
387 close(INFILE);
388 close(OUTFILE);