b43-fwcutter: pack correct ucode for brcmsmac fw
[b43-tools.git] / 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-2007 Michael Buesch <m@bues.ch>
6  *               2005 Alex Beregszaszi
7  *               2007 Johannes Berg <johannes@sipsolutions.net>
8  *
9  *   Redistribution and use in source and binary forms, with or without
10  *   modification, are permitted provided that the following conditions
11  *   are met:
12  *
13  *     1. Redistributions of source code must retain the above copyright
14  *        notice, this list of conditions and the following disclaimer.
15  *     2. Redistributions in binary form must reproduce the above
16  *        copyright notice, this list of conditions and the following
17  *        disclaimer in the documentation and/or other materials provided
18  *        with the distribution.
19  *
20  *   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
21  *   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  *   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23  *   DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
24  *   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
26  *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
29  *   OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  *   EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <stdlib.h>
34 #include <ctype.h>
35 #include <string.h>
36 #include <stdio.h>
37 #include <errno.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40
41 #if defined(__DragonFly__) || defined(__FreeBSD__)
42 #include <sys/endian.h>
43 #else
44 #include <byteswap.h>
45 #endif
46
47 #include "md5.h"
48 #include "fwcutter.h"
49 #include "fwcutter_list.h"
50
51 #if defined(__DragonFly__) || defined(__FreeBSD__)
52 #define V3_FW_DIRNAME   "v3"
53 #define V4_FW_DIRNAME   "v4"
54 #else
55 #define V3_FW_DIRNAME   "b43legacy"
56 #define V4_FW_DIRNAME   "b43"
57 #endif
58
59 static struct cmdline_args cmdargs;
60
61
62 /* check whether file will be listed/extracted from */
63 static int file_ok(const struct file *f)
64 {
65         return !(f->flags & FW_FLAG_UNSUPPORTED) || cmdargs.unsupported;
66 }
67
68 /* Convert a Big-Endian 16bit integer to CPU-endian */
69 static uint16_t from_be16(be16_t v)
70 {
71         uint16_t ret = 0;
72
73         ret |= (uint16_t)(((uint8_t *)&v)[0]) << 8;
74         ret |= (uint16_t)(((uint8_t *)&v)[1]);
75
76         return ret;
77 }
78
79 /* Convert a CPU-endian 16bit integer to Big-Endian */
80 static be16_t to_be16(uint16_t v)
81 {
82         return (be16_t)from_be16((be16_t)v);
83 }
84
85 /* Convert a Big-Endian 32bit integer to CPU-endian */
86 static uint32_t from_be32(be32_t v)
87 {
88         uint32_t ret = 0;
89
90         ret |= (uint32_t)(((uint8_t *)&v)[0]) << 24;
91         ret |= (uint32_t)(((uint8_t *)&v)[1]) << 16;
92         ret |= (uint32_t)(((uint8_t *)&v)[2]) << 8;
93         ret |= (uint32_t)(((uint8_t *)&v)[3]);
94
95         return ret;
96 }
97
98 /* Convert a CPU-endian 32bit integer to Big-Endian */
99 static be32_t to_be32(uint32_t v)
100 {
101         return (be32_t)from_be32((be32_t)v);
102 }
103
104 /* Convert a Little-Endian 32bit integer to CPU-endian */
105 static uint32_t from_le32(le32_t v)
106 {
107         uint32_t ret = 0;
108
109         ret |= (uint32_t)(((uint8_t *)&v)[0]);
110         ret |= (uint32_t)(((uint8_t *)&v)[1]) << 8;
111         ret |= (uint32_t)(((uint8_t *)&v)[2]) << 16;
112         ret |= (uint32_t)(((uint8_t *)&v)[3]) << 24;
113
114         return ret;
115 }
116
117 /* Convert a CPU-endian 32bit integer to Little-Endian */
118 static le32_t to_le32(uint32_t v)
119 {
120         return (le32_t)from_le32((le32_t)v);
121 }
122
123 /* tiny disassembler */
124 static void print_ucode_version(struct insn *insn)
125 {
126         int val;
127
128         /*
129          * The instruction we're looking for is a store to memory
130          * offset insn->op3 of the constant formed like `val' below.
131          * 0x2de00 is the opcode for type 1, 0x378 is the opcode
132          * for type 2 and 3.
133          */
134         if (insn->opcode != 0x378 && insn->opcode != 0x2de00)
135                 return;
136
137         val = ((0xFF & insn->op1) << 8) | (0xFF & insn->op2);
138
139         /*
140          * Memory offsets are word-offsets, for the meaning
141          * see http://bcm-v4.sipsolutions.net/802.11/ObjectMemory
142          */
143         switch (insn->op3) {
144         case 0:
145                 printf("  ucode version:  %d\n", val);
146                 break;
147         case 1:
148                 printf("  ucode revision: %d\n", val);
149                 break;
150         case 2:
151                 printf("  ucode date:     %.4d-%.2d-%.2d\n",
152                        2000 + (val >> 12), (val >> 8) & 0xF, val & 0xFF);
153                 break;
154         case 3:
155                 printf("  ucode time:     %.2d:%.2d:%.2d\n",
156                        val >> 11, (val >> 5) & 0x3f, val & 0x1f);
157                 break;
158         }
159 }
160
161 static void disasm_ucode_1(uint64_t in, struct insn *out)
162 {
163         /* xxyyyzzz00oooooX -> ooooo Xxx yyy zzz
164          * if we swap the upper and lower 32-bits first it becomes easier:
165          * 00oooooxxxyyyzzz -> ooooo xxx yyy zzz
166          */
167         in = (in >> 32) | (in << 32);
168
169         out->op3        = in & 0xFFF;
170         out->op2        = (in >> 12) & 0xFFF;
171         out->op1        = (in >> 24) & 0xFFF;
172         out->opcode     = (in >> 36) & 0xFFFFF;
173         /* the rest of the in word should be zero */
174 }
175
176 static void disasm_ucode_2(uint64_t in, struct insn *out)
177 {
178         /* xxyyyzzz0000oooX -> ooo Xxx yyy zzz
179          * if we swap the upper and lower 32-bits first it becomes easier:
180          * 0000oooxxxyyyzzz -> ooo xxx yyy zzz
181          */
182         in = (in >> 32) | (in << 32);
183
184         out->op3        = in & 0xFFF;
185         out->op2        = (in >> 12) & 0xFFF;
186         out->op1        = (in >> 24) & 0xFFF;
187         out->opcode     = (in >> 36) & 0xFFF;
188         /* the rest of the in word should be zero */
189 }
190
191 static void disasm_ucode_3(uint64_t in, struct insn *out)
192 {
193         /*
194          * like 2, but each operand has one bit more; appears
195          * to use the same instruction set slightly extended
196          */
197         in = (in >> 32) | (in << 32);
198
199         out->op3        = in & 0x1FFF;
200         out->op2        = (in >> 13) & 0x1FFF;
201         out->op1        = (in >> 26) & 0x1FFF;
202         out->opcode     = (in >> 39) & 0xFFF;
203         /* the rest of the in word should be zero */
204 }
205
206 static void analyse_ucode(int ucode_rev, uint8_t *data, uint32_t len)
207 {
208         uint64_t *insns = (uint64_t*)data;
209         struct insn insn;
210         uint32_t i;
211
212         for (i=0; i<len/sizeof(*insns); i++) {
213                 switch (ucode_rev) {
214                 case 1:
215                         disasm_ucode_1(insns[i], &insn);
216                         print_ucode_version(&insn);
217                         break;
218                 case 2:
219                         disasm_ucode_2(insns[i], &insn);
220                         print_ucode_version(&insn);
221                         break;
222                 case 3:
223                         disasm_ucode_3(insns[i], &insn);
224                         print_ucode_version(&insn);
225                         break;
226                 }
227         }
228 }
229
230 static void swap_endianness_ucode(uint8_t *buf, uint32_t len)
231 {
232         uint32_t *buf32 = (uint32_t*)buf;
233         uint32_t i;
234
235         for (i=0; i<len/4; i++)
236                 buf32[i] = bswap_32(buf32[i]);
237 }
238
239 #define swap_endianness_pcm swap_endianness_ucode
240
241 static void swap_endianness_iv(struct iv *iv)
242 {
243         iv->reg = bswap_16(iv->reg);
244         iv->size = bswap_16(iv->size);
245         iv->val = bswap_32(iv->val);
246 }
247
248 static uint8_t *read_object(FILE *f, const struct extract *extract)
249 {
250         uint8_t *buf;
251
252         if (fseek(f, extract->offset, SEEK_SET)) {
253                 perror("failed to seek on file");
254                 exit(2);
255         }
256
257         buf = malloc(extract->length);
258         if (!buf) {
259                 perror("failed to allocate buffer");
260                 exit(3);
261         }
262         if (fread(buf, 1, extract->length, f) != extract->length) {
263                 perror("failed to read complete data");
264                 exit(3);
265         }
266         return buf;
267 }
268
269 static void build_ivs(struct b43_iv **_out, size_t *_out_size,
270                       struct iv *in, size_t in_size,
271                       struct fw_header *hdr,
272                       uint32_t flags)
273 {
274         struct iv *iv;
275         struct b43_iv *out;
276         uint32_t i;
277         size_t out_size = 0;
278
279         if (sizeof(struct b43_iv) != 6) {
280                 printf("sizeof(struct b43_iv) != 6\n");
281                 exit(255);
282         }
283
284         out = malloc(in_size);
285         if (!out) {
286                 perror("failed to allocate buffer");
287                 exit(1);
288         }
289         *_out = out;
290         for (i = 0; i < in_size / sizeof(*iv); i++, in++) {
291                 if (flags & FW_FLAG_LE)
292                         swap_endianness_iv(in);
293                 /* input-IV is BigEndian */
294                 if (in->reg & to_be16(~FW_IV_OFFSET_MASK)) {
295                         printf("Input file IV offset > 0x%X\n", FW_IV_OFFSET_MASK);
296                         exit(1);
297                 }
298                 out->offset_size = in->reg;
299                 if (in->size == to_be16(4)) {
300                         out->offset_size |= to_be16(FW_IV_32BIT);
301                         out->data.d32 = in->val;
302
303                         out_size += sizeof(be16_t) + sizeof(be32_t);
304                         out = (struct b43_iv *)((uint8_t *)out + sizeof(be16_t) + sizeof(be32_t));
305                 } else if (in->size == to_be16(2)) {
306                         if (in->val & to_be32(~0xFFFF)) {
307                                 printf("Input file 16bit IV value overflow\n");
308                                 exit(1);
309                         }
310                         out->data.d16 = to_be16(from_be32(in->val));
311
312                         out_size += sizeof(be16_t) + sizeof(be16_t);
313                         out = (struct b43_iv *)((uint8_t *)out + sizeof(be16_t) + sizeof(be16_t));
314                 } else {
315                         printf("Input file IV size != 2|4\n");
316                         exit(1);
317                 }
318         }
319         hdr->size = to_be32(i);
320         *_out_size = out_size;
321 }
322
323 static void write_file(const char *name, uint8_t *buf, uint32_t len,
324                        const struct fw_header *hdr, uint32_t flags)
325 {
326         FILE *f;
327         char nbuf[4096];
328         const char *dir;
329         int r;
330
331         if (flags & FW_FLAG_V4)
332                 dir = V4_FW_DIRNAME;
333         else
334                 dir = V3_FW_DIRNAME;
335
336         r = snprintf(nbuf, sizeof(nbuf),
337                      "%s/%s", cmdargs.target_dir, dir);
338         if (r >= sizeof(nbuf)) {
339                 fprintf(stderr, "name too long");
340                 exit(2);
341         }
342
343         r = mkdir(nbuf, 0770);
344         if (r && errno != EEXIST) {
345                 perror("failed to create output directory");
346                 exit(2);
347         }
348
349         r = snprintf(nbuf, sizeof(nbuf),
350                      "%s/%s/%s.fw", cmdargs.target_dir, dir, name);
351         if (r >= sizeof(nbuf)) {
352                 fprintf(stderr, "name too long");
353                 exit(2);
354         }
355         f = fopen(nbuf, "w");
356         if (!f) {
357                 perror("failed to open file");
358                 exit(2);
359         }
360         if (fwrite(hdr, sizeof(*hdr), 1, f) != 1) {
361                 perror("failed to write file");
362                 exit(2);
363         }
364         if (fwrite(buf, 1, len, f) != len) {
365                 perror("failed to write file");
366                 exit(2);
367         }
368         fclose(f);
369 }
370
371 static void extract_or_identify(FILE *f, const char *dir,
372                                 const struct extract *extract, uint32_t flags)
373 {
374         uint8_t *buf;
375         size_t data_length;
376         int ucode_rev = 0;
377         struct fw_header hdr;
378
379         memset(&hdr, 0, sizeof(hdr));
380         hdr.ver = FW_HDR_VER;
381
382         printf("%s %s/%s.fw\n",
383                cmdargs.mode == FWCM_IDENTIFY ? "Contains" : "Extracting",
384                dir, extract->name);
385
386         buf = read_object(f, extract);
387
388         switch (extract->type) {
389         case EXT_UCODE_3:
390                 ucode_rev += 1;
391         case EXT_UCODE_2:
392                 ucode_rev += 1;
393         case EXT_UCODE_1:
394                 ucode_rev += 1;
395                 data_length = extract->length;
396                 if (flags & FW_FLAG_LE)
397                         swap_endianness_ucode(buf, data_length);
398                 analyse_ucode(ucode_rev, buf, data_length);
399                 hdr.type = FW_TYPE_UCODE;
400                 hdr.size = to_be32(data_length);
401                 break;
402         case EXT_PCM:
403                 data_length = extract->length;
404                 if (flags & FW_FLAG_LE)
405                         swap_endianness_pcm(buf, data_length);
406                 hdr.type = FW_TYPE_PCM;
407                 hdr.size = to_be32(data_length);
408                 break;
409         case EXT_IV: {
410                 struct b43_iv *ivs;
411
412                 hdr.type = FW_TYPE_IV;
413                 build_ivs(&ivs, &data_length,
414                           (struct iv *)buf, extract->length,
415                           &hdr, flags);
416                 free(buf);
417                 buf = (uint8_t *)ivs;
418                 break;
419         }
420         default:
421                 exit(255);
422         }
423
424         if (cmdargs.mode == FWCM_EXTRACT_B43)
425                 write_file(extract->name, buf, data_length, &hdr, flags);
426
427         free(buf);
428 }
429
430 static int brcmsmac_name_to_idx(const char *name)
431 {
432         if (strcmp("lcn0bsinitvals24", name) == 0) {
433                 return D11LCN0BSINITVALS24;
434         } else if (strcmp("lcn0initvals24", name) == 0) {
435                 return D11LCN0INITVALS24;
436         } else if (strcmp("lcn1bsinitvals24", name) == 0) {
437                 return D11LCN1BSINITVALS24;
438         } else if (strcmp("lcn1initvals24", name) == 0) {
439                 return D11LCN1INITVALS24;
440         } else if (strcmp("lcn2bsinitvals24", name) == 0) {
441                 return D11LCN2BSINITVALS24;
442         } else if (strcmp("lcn2initvals24", name) == 0) {
443                 return D11LCN2INITVALS24;
444         } else if (strcmp("n0absinitvals16", name) == 0) {
445                 return D11N0ABSINITVALS16;
446         } else if (strcmp("n0bsinitvals16", name) == 0) {
447                 return D11N0BSINITVALS16;
448         } else if (strcmp("n0initvals16", name) == 0) {
449                 return D11N0INITVALS16;
450         } else if (strcmp("ucode16_mimo", name) == 0) {
451                 return D11UCODE_OVERSIGHT16_MIMO;
452         } else if (strcmp("ucode24_lcn", name) == 0) {
453                 return D11UCODE_OVERSIGHT24_LCN;
454         }
455         return 0;
456 }
457
458 static int brcmsmac_name_to_size_idx(const char *name)
459 {
460         if (strcmp("ucode16_mimo", name) == 0) {
461                 return D11UCODE_OVERSIGHT16_MIMOSZ;
462         } else if (strcmp("ucode24_lcn", name) == 0) {
463                 return D11UCODE_OVERSIGHT24_LCNSZ;
464         }
465         return 0;
466 }
467
468 static void brcmsmac_clear_file(void)
469 {
470         FILE *f;
471         char nbuf[4096];
472         int r;
473
474         r = snprintf(nbuf, sizeof(nbuf),
475                      "%s/brcm", cmdargs.target_dir);
476         if (r >= sizeof(nbuf)) {
477                 fprintf(stderr, "name too long");
478                 exit(2);
479         }
480
481         r = mkdir(nbuf, 0770);
482         if (r && errno != EEXIST) {
483                 perror("failed to create output directory");
484                 exit(2);
485         }
486
487         r = snprintf(nbuf, sizeof(nbuf),
488                      "%s/brcm/bcm43xx-0.fw", cmdargs.target_dir);
489         if (r >= sizeof(nbuf)) {
490                 fprintf(stderr, "name too long");
491                 exit(2);
492         }
493         f = fopen(nbuf, "w");
494         if (!f) {
495                 perror("failed to open data file");
496                 exit(2);
497         }
498         fclose(f);
499
500         r = snprintf(nbuf, sizeof(nbuf),
501                      "%s/brcm/bcm43xx_hdr-0.fw", cmdargs.target_dir);
502         if (r >= sizeof(nbuf)) {
503                 fprintf(stderr, "name too long");
504                 exit(2);
505         }
506         f = fopen(nbuf, "w");
507         fclose(f);
508 }
509
510 static void brcmsmac_write_file(int idx, uint8_t *buf, uint32_t len)
511 {
512         FILE *f;
513         FILE *h;
514         char nbuf[4096];
515         int r;
516         int offset;
517         struct firmware_hdr fw_hdr;
518
519         r = snprintf(nbuf, sizeof(nbuf),
520                      "%s/brcm/bcm43xx-0.fw", cmdargs.target_dir);
521         if (r >= sizeof(nbuf)) {
522                 fprintf(stderr, "name too long");
523                 exit(2);
524         }
525         f = fopen(nbuf, "a");
526         if (!f) {
527                 perror("failed to open data file");
528                 exit(2);
529         }
530         fseek(f, 0L, SEEK_END);
531
532         r = snprintf(nbuf, sizeof(nbuf),
533                      "%s/brcm/bcm43xx_hdr-0.fw", cmdargs.target_dir);
534         if (r >= sizeof(nbuf)) {
535                 fprintf(stderr, "name too long");
536                 exit(2);
537         }
538         h = fopen(nbuf, "a");
539         if (!h) {
540                 perror("failed to open header file");
541                 exit(2);
542         }
543         fseek(h, 0L, SEEK_END);
544
545         offset = ftell(f);
546
547         fw_hdr.offset = to_le32(offset);
548         fw_hdr.len = to_le32(len);
549         fw_hdr.idx = to_le32(idx);
550
551         if (fwrite(&fw_hdr, sizeof(fw_hdr), 1, h) != 1) {
552                 perror("failed to write file");
553                 exit(2);
554         }
555         fclose(h);
556         if (fwrite(buf, 1, len, f) != len) {
557                 perror("failed to write file");
558                 exit(2);
559         }
560         fclose(f);
561 }
562
563 static void brcmsmac_add_dummy_entries(void)
564 {
565         uint8_t buf[4] = {0};
566
567         brcmsmac_write_file(D11N0ABSINITVALS16, buf, 4);
568         brcmsmac_write_file(D11UCODE_OVERSIGHT_BOMMAJOR, buf, 4);
569         brcmsmac_write_file(D11UCODE_OVERSIGHT_BOMMINOR, buf, 4);
570 }
571
572 static void brcmsmac_extract(FILE *f, const struct extract *extract,
573                              uint32_t flags)
574 {
575         uint8_t *buf;
576         size_t data_length;
577         int ucode_rev = 0;
578         int brcmsmac_idx = 0;
579         be32_t size;
580
581
582         brcmsmac_idx = brcmsmac_name_to_idx(extract->name);
583         if (!brcmsmac_idx)
584                 return;
585         printf("%s %s\n",
586                cmdargs.mode == FWCM_IDENTIFY ? "Contains" : "Extracting",
587                extract->name);
588
589         buf = read_object(f, extract);
590
591         switch (extract->type) {
592         case EXT_UCODE_3:
593                 ucode_rev += 1;
594         case EXT_UCODE_2:
595                 ucode_rev += 1;
596         case EXT_UCODE_1:
597                 ucode_rev += 1;
598                 data_length = extract->length;
599                 if (flags & FW_FLAG_LE)
600                         swap_endianness_ucode(buf, data_length);
601                 analyse_ucode(ucode_rev, buf, data_length);
602                 swap_endianness_ucode(buf, data_length);
603                 size = to_le32(data_length);
604                 break;
605         case EXT_PCM:
606                 data_length = extract->length;
607                 if (!(flags & FW_FLAG_LE))
608                         swap_endianness_pcm(buf, data_length);
609                 size = to_le32(data_length);
610                 break;
611         case EXT_IV: {
612                 data_length = extract->length;
613                 if (!(flags & FW_FLAG_LE)) {
614                         struct iv *in = (struct iv *)buf;
615                         int i;
616
617                         for (i = 0; i < data_length / sizeof(struct iv); i++, in++)
618                                 swap_endianness_iv(in);
619                 }
620                 size = to_le32(data_length);
621                 break;
622         }
623         default:
624                 exit(255);
625         }
626
627         brcmsmac_write_file(brcmsmac_idx, buf, data_length);
628         int size_idx = brcmsmac_name_to_size_idx(extract->name);
629         if (size_idx)
630                 brcmsmac_write_file(size_idx, (uint8_t *)&size, 4);
631
632         free(buf);
633 }
634
635 static void print_banner(void)
636 {
637         printf("b43-fwcutter version " FWCUTTER_VERSION "\n");
638 }
639
640 static void print_file(const struct file *file)
641 {
642         char filename[30];
643         char shortname[30];
644
645         if (file->flags & FW_FLAG_V4)
646                 printf(V4_FW_DIRNAME "\t\t");
647         else
648                 printf(V3_FW_DIRNAME "\t");
649
650         if (strlen(file->name) > 20) {
651                 strncpy(shortname, file->name, 20);
652                 shortname[20] = '\0';
653                 snprintf(filename, sizeof(filename), "%s..", shortname);
654         } else
655                 strcpy (filename, file->name);
656
657         printf("%s\t", filename);
658         if (strlen(filename) < 8) printf("\t");
659         if (strlen(filename) < 16) printf("\t");
660
661         printf("%s\t", file->ucode_version);
662         if (strlen(file->ucode_version) < 8) printf("\t");
663
664         printf("%s\n", file->md5);
665 }
666
667 static void print_supported_files(void)
668 {
669         int i;
670
671         print_banner();
672         printf("\nExtracting firmware is possible "
673                "from these binary driver files.\n"
674                "Please read http://linuxwireless.org/en/users/Drivers/b43#devicefirmware\n\n");
675         printf("<driver>\t"
676                "<filename>\t\t"
677                "<microcode>\t"
678                "<MD5 checksum>\n\n");
679         /* print for legacy driver first */
680         for (i = 0; i < ARRAY_SIZE(files); i++)
681                 if (file_ok(&files[i]) && !(files[i].flags & FW_FLAG_V4))
682                         print_file(&files[i]);
683         for (i = 0; i < ARRAY_SIZE(files); i++)
684                 if (file_ok(&files[i]) && files[i].flags & FW_FLAG_V4)
685                         print_file(&files[i]);
686         printf("\n");
687 }
688
689 static const struct file *find_file(FILE *fd)
690 {
691         unsigned char buffer[16384], signature[16];
692         struct MD5Context md5c;
693         char md5sig[33];
694         int i;
695
696         MD5Init(&md5c);
697         while ((i = (int) fread(buffer, 1, sizeof(buffer), fd)) > 0)
698                 MD5Update(&md5c, buffer, (unsigned) i);
699         MD5Final(signature, &md5c);
700
701         snprintf(md5sig, sizeof(md5sig),
702                  "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x"
703                  "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
704                  signature[0], signature[1], signature[2], signature[3],
705                  signature[4], signature[5], signature[6], signature[7],
706                  signature[8], signature[9], signature[10], signature[11],
707                  signature[12], signature[13], signature[14], signature[15]);
708
709         for (i = 0; i < ARRAY_SIZE(files); i++) {
710                 if (file_ok(&files[i]) &&
711                     strcasecmp(md5sig, files[i].md5) == 0) {
712                         printf("This file is recognised as:\n");
713                         printf("  filename   :  %s\n", files[i].name);
714                         printf("  version    :  %s\n", files[i].ucode_version);
715                         printf("  MD5        :  %s\n", files[i].md5);
716                         return &files[i];
717                 }
718         }
719         printf("Sorry, the input file is either wrong or "
720                "not supported by b43-fwcutter.\n");
721         printf("This file has an unknown MD5sum %s.\n", md5sig);
722
723         return NULL;
724 }
725
726 static void print_usage(int argc, char *argv[])
727 {
728         print_banner();
729         printf("\nA tool to extract firmware for a Broadcom 43xx device\n");
730         printf("from a proprietary Broadcom 43xx device driver file.\n");
731         printf("\nUsage: %s [OPTION] [proprietary-driver-file]\n", argv[0]);
732         printf("  --unsupported         "
733                "Allow working on extractable but unsupported drivers\n");
734         printf("  -l|--list             "
735                "List supported driver versions\n");
736         printf("  -b|--brcmsmac         "
737                "create firmware for brcmsmac\n");
738         printf("  -i|--identify         "
739                "Only identify the driver file (don't extract)\n");
740         printf("  -w|--target-dir DIR   "
741                "Extract and write firmware to DIR\n");
742         printf("  -v|--version          "
743                "Print b43-fwcutter version\n");
744         printf("  -h|--help             "
745                "Print this help\n");
746         printf("\nExample: %s -w /lib/firmware wl_apsta.o\n"
747                "         to extract the firmware blobs from wl_apsta.o and store\n"
748                "         the resulting firmware in /lib/firmware\n",
749                argv[0]);
750 }
751
752 static int do_cmp_arg(char **argv, int *pos,
753                       const char *template,
754                       int allow_merged,
755                       char **param)
756 {
757         char *arg;
758         char *next_arg;
759         size_t arg_len, template_len;
760
761         arg = argv[*pos];
762         next_arg = argv[*pos + 1];
763         arg_len = strlen(arg);
764         template_len = strlen(template);
765
766         if (param) {
767                 /* Maybe we have a merged parameter here.
768                  * A merged parameter is "-pfoobar" for example.
769                  */
770                 if (allow_merged && arg_len > template_len) {
771                         if (memcmp(arg, template, template_len) == 0) {
772                                 *param = arg + template_len;
773                                 return ARG_MATCH;
774                         }
775                         return ARG_NOMATCH;
776                 } else if (arg_len != template_len)
777                         return ARG_NOMATCH;
778                 *param = next_arg;
779         }
780         if (strcmp(arg, template) == 0) {
781                 if (param) {
782                         /* Skip the parameter on the next iteration. */
783                         (*pos)++;
784                         if (!*param) {
785                                 printf("%s needs a parameter\n", arg);
786                                 return ARG_ERROR;
787                         }
788                 }
789                 return ARG_MATCH;
790         }
791
792         return ARG_NOMATCH;
793 }
794
795 /* Simple and lean command line argument parsing. */
796 static int cmp_arg(char **argv, int *pos,
797                    const char *long_template,
798                    const char *short_template,
799                    char **param)
800 {
801         int err;
802
803         if (long_template) {
804                 err = do_cmp_arg(argv, pos, long_template, 0, param);
805                 if (err == ARG_MATCH || err == ARG_ERROR)
806                         return err;
807         }
808         err = ARG_NOMATCH;
809         if (short_template)
810                 err = do_cmp_arg(argv, pos, short_template, 1, param);
811         return err;
812 }
813
814 static int parse_args(int argc, char *argv[])
815 {
816         int i, res;
817         char *param;
818
819         if (argc < 2)
820                 goto out_usage;
821         for (i = 1; i < argc; i++) {
822                 res = cmp_arg(argv, &i, "--list", "-l", NULL);
823                 if (res == ARG_MATCH) {
824                         cmdargs.mode = FWCM_LIST;
825                         continue;
826                 } else if (res == ARG_ERROR)
827                         goto out;
828
829                 res = cmp_arg(argv, &i, "--version", "-v", NULL);
830                 if (res == ARG_MATCH) {
831                         print_banner();
832                         return 1;
833                 } else if (res == ARG_ERROR)
834                         goto out;
835
836                 res = cmp_arg(argv, &i, "--help", "-h", NULL);
837                 if (res == ARG_MATCH)
838                         goto out_usage;
839                 else if (res == ARG_ERROR)
840                         goto out;
841
842                 res = cmp_arg(argv, &i, "--identify", "-i", NULL);
843                 if (res == ARG_MATCH) {
844                         cmdargs.mode = FWCM_IDENTIFY;
845                         continue;
846                 } else if (res == ARG_ERROR)
847                         goto out;
848
849                 res = cmp_arg(argv, &i, "--brcmsmac", "-b", NULL);
850                 if (res == ARG_MATCH) {
851                         cmdargs.mode = FWCM_EXTRACT_BRCMSMAC;
852                         continue;
853                 } else if (res == ARG_ERROR)
854                         goto out;
855
856                 res = cmp_arg(argv, &i, "--unsupported", NULL, NULL);
857                 if (res == ARG_MATCH) {
858                         cmdargs.unsupported = 1;
859                         continue;
860                 } else if (res == ARG_ERROR)
861                         goto out;
862
863                 res = cmp_arg(argv, &i, "--target-dir", "-w", &param);
864                 if (res == ARG_MATCH) {
865                         cmdargs.target_dir = param;
866                         continue;
867                 } else if (res == ARG_ERROR)
868                         goto out;
869
870                 cmdargs.infile = argv[i];
871                 break;
872         }
873
874         if (!cmdargs.infile && cmdargs.mode != FWCM_LIST)
875                 goto out_usage;
876         return 0;
877
878 out_usage:
879         print_usage(argc, argv);
880 out:
881         return -1;      
882 }
883
884 int main(int argc, char *argv[])
885 {
886         FILE *fd;
887         const struct file *file;
888         const struct extract *extract;
889         int err;
890         const char *dir;
891
892         cmdargs.target_dir = ".";
893         err = parse_args(argc, argv);
894         if (err == 1)
895                 return 0;
896         else if (err != 0)
897                 return err;
898
899         if (cmdargs.mode == FWCM_LIST) {
900                 print_supported_files();
901                 return 0;
902         }
903
904         fd = fopen(cmdargs.infile, "rb");
905         if (!fd) {
906                 fprintf(stderr, "Cannot open input file %s\n", cmdargs.infile);
907                 return 2;
908         }
909
910         err = -1;
911         file = find_file(fd);
912         if (!file)
913                 goto out_close;
914
915         if (file->flags & FW_FLAG_V4)
916                 dir = V4_FW_DIRNAME;
917         else
918                 dir = V3_FW_DIRNAME;
919
920         if (cmdargs.mode == FWCM_EXTRACT_BRCMSMAC) {
921                 brcmsmac_clear_file();
922                 extract = file->extract;
923                 while (extract->name) {
924                         brcmsmac_extract(fd, extract, file->flags);
925                         extract++;
926                 }
927                 brcmsmac_add_dummy_entries();
928         } else {
929                 extract = file->extract;
930                 while (extract->name) {
931                         extract_or_identify(fd, dir, extract, file->flags);
932                         extract++;
933                 }
934         }
935
936         err = 0;
937 out_close:
938         fclose(fd);
939
940         return err;
941 }