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