ea98880dff7af0fb2bf8f2845e65c990af4343c7
[openwrt/svn-archive/archive.git] / openwrt / target / linux / package / bcm43xx-dscape / fwcutter / fwcutter.c
1 /*
2 * firmware cutter for broadcom 43xx wireless driver files
3 *
4 * Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
5 * 2005 Michael Buesch <mbuesch@freenet.de>
6 * 2005 Alex Beregszaszi
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24
25
26 #include <stdlib.h>
27 #include <ctype.h>
28 #include <string.h>
29 #include <stdio.h>
30
31 typedef unsigned char byte;
32
33 #define DRIVER_UNSUPPORTED 0x01 /* no support for this driver file */
34 #define BYTE_ORDER_BIG_ENDIAN 0x02 /* ppc driver files */
35 #define BYTE_ORDER_LITTLE_ENDIAN 0x04 /* x86, mips driver files */
36
37 #define MISSING_INITVAL_08 0x10 /* initval 8 is missing */
38 #define MISSING_INITVAL_80211_A 0x20 /* initvals 3,7,9,10 (802.11a cards) are empty */
39
40 #define FIRMWARE_UCODE_OFFSET 100
41 #define FIRMWARE_UNDEFINED 0
42 #define FIRMWARE_PCM_4 4
43 #define FIRMWARE_PCM_5 5
44 #define FIRMWARE_UCODE_2 (FIRMWARE_UCODE_OFFSET + 2)
45 #define FIRMWARE_UCODE_4 (FIRMWARE_UCODE_OFFSET + 4)
46 #define FIRMWARE_UCODE_5 (FIRMWARE_UCODE_OFFSET + 5)
47 #define FIRMWARE_UCODE_11 (FIRMWARE_UCODE_OFFSET + 11)
48
49
50 #define fwcutter_stringify_1(x) #x
51 #define fwcutter_stringify(x) fwcutter_stringify_1(x)
52 #define FWCUTTER_VERSION fwcutter_stringify(FWCUTTER_VERSION_)
53
54 #include "md5.h"
55 #include "fwcutter_list.h"
56
57
58 struct cmdline_args {
59 const char *infile;
60 const char *postfix;
61 const char *target_dir;
62 int identify_only;
63 };
64
65 static struct cmdline_args cmdargs;
66 int big_endian_cpu;
67
68
69 static void write_little_endian(FILE *f, byte *buffer, int len)
70 {
71 byte swapbuf[4];
72
73 while (len > 0) {
74 swapbuf[0] = buffer[3]; swapbuf[1] = buffer[2];
75 swapbuf[2] = buffer[1]; swapbuf[3] = buffer[0];
76 fwrite(swapbuf, 4, 1, f);
77 buffer = buffer + 4;
78 len = len - 4;
79 }
80 }
81
82 static void write_big_endian(FILE *f, byte *buffer, int len)
83 {
84 while (len > 0) {
85 fwrite(buffer, 4, 1, f);
86 buffer = buffer + 4;
87 len = len - 4;
88 }
89 }
90
91 static void write_fw(const char *outfilename, uint8_t flags, byte *data, int len)
92 {
93 FILE* fw;
94 char outfile[2048];
95
96 snprintf(outfile, sizeof(outfile),
97 "%s/%s", cmdargs.target_dir, outfilename);
98
99 fw = fopen(outfile, "w");
100 if (!fw) {
101 perror(outfile);
102 exit(1);
103 }
104
105 if (flags & BYTE_ORDER_LITTLE_ENDIAN)
106 write_little_endian(fw, data, len);
107 else if (flags & BYTE_ORDER_BIG_ENDIAN)
108 write_big_endian(fw, data, len);
109 else
110 printf("unknown byteorder...\n");
111
112 fflush(fw);
113 fclose(fw);
114 }
115
116 static void write_iv(uint8_t flags, byte *data)
117 {
118 FILE* fw;
119 char ivfilename[2048];
120 int i;
121
122 for (i = 1; i <= 10; i++) {
123
124 if ((flags & MISSING_INITVAL_08) && (i==8)) {
125 printf("*****: Sorry, initval08 is not available in driver file \"%s\".\n", cmdargs.infile);
126 printf("*****: Extracting firmware from an old driver is bad. Choose a more recent one.\n");
127 printf("*****: Luckily bcm43xx driver doesn't include initval08 uploads at the moment.\n");
128 printf("*****: But this can be added in the future...\n");
129 i++;
130 }
131
132 snprintf(ivfilename, sizeof(ivfilename),
133 "%s/bcm43xx_initval%02d%s.fw",
134 cmdargs.target_dir, i, cmdargs.postfix);
135 fw = fopen(ivfilename, "w");
136
137 if (!fw) {
138 perror(ivfilename);
139 exit(1);
140 }
141
142 printf("extracting bcm43xx_initval%02d%s.fw ...\n", i, cmdargs.postfix);
143
144 while (1) {
145
146 if ((data[0]==0xff) && (data[1]==0xff) && (data[2]==0x00) && (data[3]==0x00)) {
147 data = data + 8;
148 break;
149 }
150
151 if (flags & BYTE_ORDER_LITTLE_ENDIAN)
152 fprintf(fw, "%c%c%c%c%c%c%c%c",
153 data[1], data[0], /* offset */
154 data[3], data[2], /* size */
155 data[7], data[6], data[5], data[4]); /* value */
156 else if (flags & BYTE_ORDER_BIG_ENDIAN)
157 fprintf(fw, "%c%c%c%c%c%c%c%c",
158 data[0], data[1], /* offset */
159 data[2], data[3], /* size */
160 data[4], data[5], data[6], data[7]); /* value */
161 else {
162 printf("unknown byteorder...\n");
163 exit(1);
164 }
165
166 data = data + 8;
167 }
168 fflush(fw);
169 fclose(fw);
170 }
171 }
172
173 static byte* read_file(const char* filename)
174 {
175 FILE* file;
176 long len;
177 byte* data;
178
179 file = fopen(filename, "rb");
180 if (!file) {
181 perror(filename);
182 exit(1);
183 }
184 if (fseek(file, 0, SEEK_END)) {
185 perror("cannot seek");
186 exit(1);
187 }
188 len = ftell(file);
189 fseek(file, 0, SEEK_SET);
190 data = malloc(len);
191 if (!data) {
192 fputs("out of memory\n", stderr);
193 exit(1);
194 }
195 if (fread(data, 1, len, file) != len) {
196 perror("cannot read");
197 exit(1);
198 }
199 fclose(file);
200 return data;
201 }
202
203 static void extract_fw(uint8_t fwtype, uint8_t flags, uint32_t pos, uint32_t length)
204 {
205 byte* filedata;
206 char outfile[1024];
207
208 switch (fwtype) {
209 case FIRMWARE_UCODE_2:
210 case FIRMWARE_UCODE_4:
211 case FIRMWARE_UCODE_5:
212 case FIRMWARE_UCODE_11:
213 snprintf(outfile, sizeof(outfile), "bcm43xx_microcode%i%s.fw",
214 fwtype - FIRMWARE_UCODE_OFFSET, cmdargs.postfix);
215 break;
216 case FIRMWARE_PCM_4:
217 case FIRMWARE_PCM_5:
218 snprintf(outfile, sizeof(outfile), "bcm43xx_pcm%i%s.fw",
219 fwtype, cmdargs.postfix);
220 break;
221 default:
222 snprintf(outfile, sizeof(outfile), "bcm43xx_unknown.fw");
223 }
224
225 if (length > 0) {
226 printf("extracting %s ...\n", outfile);
227 filedata = read_file(cmdargs.infile);
228 write_fw(outfile, flags, filedata + pos, length);
229 free(filedata);
230 } else {
231 printf("*****: Sorry, it's not posible to extract \"%s\".\n", outfile);
232 printf("*****: Extracting firmware from an old driver is bad. Choose a more recent one.\n");
233
234 switch (fwtype) {
235 case FIRMWARE_UCODE_2:
236 printf("*****: bcm43xx driver will not work with with core revision 2.\n");
237 break;
238 case FIRMWARE_UCODE_4:
239 printf("*****: bcm43xx driver will not work with with core revision 4.\n");
240 break;
241 case FIRMWARE_UCODE_5:
242 printf("*****: bcm43xx driver will not work with with core revision 5 or higher.\n");
243 break;
244 case FIRMWARE_UCODE_11:
245 printf("*****: Luckily bcm43xx driver doesn't include microcode11 uploads at the moment.\n");
246 printf("*****: But this can be added in the future...\n");
247 break;
248 case FIRMWARE_PCM_4:
249 printf("*****: bcm43xx driver will not work with with core revision 4 or smaller.\n");
250 break;
251 case FIRMWARE_PCM_5:
252 printf("*****: bcm43xx driver will not work with with core revision 5 or higher.\n");
253 break;
254 }
255 }
256 }
257
258 static void extract_iv(uint8_t flags, uint32_t pos)
259 {
260 byte* filedata;
261
262 if (pos > 0) {
263 filedata = read_file(cmdargs.infile);
264 write_iv(flags, filedata + pos);
265 free(filedata);
266 }
267 }
268
269 static void print_banner(void)
270 {
271 printf("fwcutter " FWCUTTER_VERSION "\n");
272 }
273
274 static void print_file(const struct file *file)
275 {
276 printf("%s\t", file->name);
277 if (strlen(file->name) < 8)
278 printf("\t");
279
280 printf("%s\t", file->version);
281 if (strlen(file->version) < 8)
282 printf("\t");
283 if (strlen(file->version) < 16)
284 printf("\t");
285
286 if (!(file->flags & DRIVER_UNSUPPORTED)) {
287 if (file->flags & MISSING_INITVAL_80211_A)
288 printf("b/g ");
289 else
290 printf("a/b/g");
291 }
292
293 printf(" %s", file->md5);
294 printf("\n");
295 }
296
297 static void print_supported_files(void)
298 {
299 int i;
300
301 print_banner();
302 printf("\nExtracting firmware is possible from these binary driver files:\n\n");
303 printf("<filename>\t<version>\t <802.11><MD5 checksum>\n\n");
304 for (i = 0; i < FILES; i++) {
305 if (files[i].flags & DRIVER_UNSUPPORTED)
306 continue;
307 print_file(&files[i]);
308 }
309 printf("\n\nExtracting firmware is IMPOSSIBLE from these binary driver files:\n\n");
310 printf("<filename>\t<version>\t <MD5 checksum>\n\n");
311 for (i = 0; i < FILES; i++) {
312 if (!(files[i].flags & DRIVER_UNSUPPORTED))
313 continue;
314 print_file(&files[i]);
315 }
316 }
317
318 static const struct file * find_file(FILE *fd)
319 {
320 unsigned char buffer[16384], signature[16];
321 struct MD5Context md5c;
322 char md5sig[33];
323 int i;
324
325 MD5Init(&md5c);
326 while ((i = (int) fread(buffer, 1, sizeof(buffer), fd)) > 0)
327 MD5Update(&md5c, buffer, (unsigned) i);
328 MD5Final(signature, &md5c);
329
330 snprintf(md5sig, sizeof(md5sig),
331 "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
332 signature[0], signature[1], signature[2], signature[3],
333 signature[4], signature[5], signature[6], signature[7],
334 signature[8], signature[9], signature[10], signature[11],
335 signature[12], signature[13], signature[14], signature[15]);
336
337 for (i = 0; i < FILES; ++i) {
338 if (strcasecmp(md5sig, files[i].md5) == 0) {
339 if (files[i].flags & DRIVER_UNSUPPORTED) {
340 printf("Extracting firmware from this file is IMPOSSIBLE. (too old)\n");
341 return 0;
342 }
343 printf("fwcutter can cut the firmware out of %s\n", cmdargs.infile);
344 printf(" filename : %s\n", files[i].name);
345 printf(" version : %s\n", files[i].version);
346 printf(" MD5 : %s\n\n", files[i].md5);
347 if (files[i].flags & MISSING_INITVAL_80211_A) {
348 printf("WARNING! This firmware doesn't include support for 802.11a cards.\n");
349 printf("WARNING! Use this firmware only for 802.11b/g cards.\n\n");
350 }
351 return &(files[i]);
352 }
353 }
354 printf("Sorry, the input file is either wrong or not supported by fwcutter.\n");
355 printf("I can't find the MD5sum %s :(\n", md5sig);
356
357 return 0;
358 }
359
360 static void get_endianess(void)
361 {
362 const unsigned char x[] = { 0xde, 0xad, 0xbe, 0xef, };
363 const uint32_t *p = (uint32_t *)x;
364
365 if (*p == 0xdeadbeef) {
366 big_endian_cpu = 1;
367 } else if (*p == 0xefbeadde) {
368 big_endian_cpu = 0;
369 } else {
370 printf("Confused: NUXI endian machine??\n");
371 exit(-1);
372 }
373 }
374
375 static void print_usage(int argc, char *argv[])
376 {
377 print_banner();
378 printf("\nUsage: %s [OPTION] [driver.sys]\n", argv[0]);
379 printf(" -l|--list List supported driver versions\n");
380 printf(" -i|--identify Only identify the driver file (don't extract)\n");
381 printf(" -w|--target-dir DIR Extract and write firmware to DIR\n");
382 printf(" -p|--postfix \".FOO\" Postfix for firmware filenames (.FOO.fw)\n");
383 printf(" -v|--version Print fwcutter version\n");
384 printf(" -h|--help Print this help\n");
385 printf("\nExample: %s bcmwl5.sys\n"
386 " to extract the firmware blobs from bcmwl5.sys\n", argv[0]);
387 }
388
389 #define ARG_MATCH 0
390 #define ARG_NOMATCH 1
391 #define ARG_ERROR -1
392
393 static int do_cmp_arg(char **argv, int *pos,
394 const char *template,
395 int allow_merged,
396 char **param)
397 {
398 char *arg;
399 char *next_arg;
400 size_t arg_len, template_len;
401
402 arg = argv[*pos];
403 next_arg = argv[*pos + 1];
404 arg_len = strlen(arg);
405 template_len = strlen(template);
406
407 if (param) {
408 /* Maybe we have a merged parameter here.
409 * A merged parameter is "-pfoobar" for example.
410 */
411 if (allow_merged && arg_len > template_len) {
412 if (memcmp(arg, template, template_len) == 0) {
413 *param = arg + template_len;
414 return ARG_MATCH;
415 }
416 return ARG_NOMATCH;
417 } else if (arg_len != template_len)
418 return ARG_NOMATCH;
419 *param = next_arg;
420 }
421 if (strcmp(arg, template) == 0) {
422 if (param) {
423 /* Skip the parameter on the next iteration. */
424 (*pos)++;
425 if (*param == 0) {
426 printf("%s needs a parameter\n", arg);
427 return ARG_ERROR;
428 }
429 }
430 return ARG_MATCH;
431 }
432
433 return ARG_NOMATCH;
434 }
435
436 /* Simple and lean command line argument parsing. */
437 static int cmp_arg(char **argv, int *pos,
438 const char *long_template,
439 const char *short_template,
440 char **param)
441 {
442 int err;
443
444 if (long_template) {
445 err = do_cmp_arg(argv, pos, long_template, 0, param);
446 if (err == ARG_MATCH || err == ARG_ERROR)
447 return err;
448 }
449 err = ARG_NOMATCH;
450 if (short_template)
451 err = do_cmp_arg(argv, pos, short_template, 1, param);
452 return err;
453 }
454
455 static int parse_args(int argc, char *argv[])
456 {
457 int i, res;
458 char *param;
459
460 if (argc < 2)
461 goto out_usage;
462 for (i = 1; i < argc; i++) {
463 res = cmp_arg(argv, &i, "--list", "-l", 0);
464 if (res == ARG_MATCH) {
465 print_supported_files();
466 return 1;
467 } else if (res == ARG_ERROR)
468 goto out;
469
470 res = cmp_arg(argv, &i, "--version", "-v", 0);
471 if (res == ARG_MATCH) {
472 print_banner();
473 return 1;
474 } else if (res == ARG_ERROR)
475 goto out;
476
477 res = cmp_arg(argv, &i, "--help", "-h", 0);
478 if (res == ARG_MATCH)
479 goto out_usage;
480 else if (res == ARG_ERROR)
481 goto out;
482
483 res = cmp_arg(argv, &i, "--identify", "-i", 0);
484 if (res == ARG_MATCH) {
485 cmdargs.identify_only = 1;
486 continue;
487 } else if (res == ARG_ERROR)
488 goto out;
489
490 res = cmp_arg(argv, &i, "--target-dir", "-w", &param);
491 if (res == ARG_MATCH) {
492 cmdargs.target_dir = param;
493 continue;
494 } else if (res == ARG_ERROR)
495 goto out;
496
497 res = cmp_arg(argv, &i, "--postfix", "-p", &param);
498 if (res == ARG_MATCH) {
499 cmdargs.postfix = param;
500 continue;
501 } else if (res == ARG_ERROR)
502 goto out;
503
504 cmdargs.infile = argv[i];
505 break;
506 }
507
508 if (!cmdargs.infile)
509 goto out_usage;
510 return 0;
511
512 out_usage:
513 print_usage(argc, argv);
514 out:
515 return -1;
516 }
517
518 int main(int argc, char *argv[])
519 {
520 FILE *fd;
521 const struct file *file;
522 int err;
523
524 get_endianess();
525
526 cmdargs.target_dir = ".";
527 cmdargs.postfix = "";
528 err = parse_args(argc, argv);
529 if (err == 1)
530 return 0;
531 else if (err != 0)
532 return err;
533
534 fd = fopen(cmdargs.infile, "rb");
535 if (!fd) {
536 fprintf(stderr, "Cannot open input file %s\n", cmdargs.infile);
537 return 2;
538 }
539
540 err = -1;
541 file = find_file(fd);
542 if (!file)
543 goto out_close;
544 if (cmdargs.identify_only) {
545 err = 0;
546 goto out_close;
547 }
548
549 extract_fw(FIRMWARE_UCODE_2, file->flags, file->uc2_pos, file->uc2_length);
550 extract_fw(FIRMWARE_UCODE_4, file->flags, file->uc4_pos, file->uc4_length);
551 extract_fw(FIRMWARE_UCODE_5, file->flags, file->uc5_pos, file->uc5_length);
552 extract_fw(FIRMWARE_UCODE_11, file->flags, file->uc11_pos, file->uc11_length);
553 extract_fw(FIRMWARE_PCM_4, file->flags, file->pcm4_pos, file->pcm4_length);
554 extract_fw(FIRMWARE_PCM_5, file->flags, file->pcm5_pos, file->pcm5_length);
555 extract_iv(file->flags, file->iv_pos);
556
557 err = 0;
558 out_close:
559 fclose(fd);
560
561 return err;
562 }