2 * firmware cutter for broadcom 43xx wireless driver files
4 * Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
5 * 2005-2014 Michael Buesch <m@bues.ch>
6 * 2005 Alex Beregszaszi
7 * 2007 Johannes Berg <johannes@sipsolutions.net>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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.
39 #include <sys/types.h>
41 #if defined(__DragonFly__) || defined(__FreeBSD__)
42 #include <sys/endian.h>
43 #elif defined(__APPLE__)
44 #include <libkern/OSByteOrder.h>
51 #include "fwcutter_list.h"
53 #if defined(__DragonFly__) || defined(__FreeBSD__)
54 #define V3_FW_DIRNAME "v3"
55 #define V4_FW_DIRNAME "v4"
57 #define V3_FW_DIRNAME "b43legacy"
58 #define V4_FW_DIRNAME "b43"
61 static struct cmdline_args cmdargs;
64 /* check whether file will be listed/extracted from */
65 static int file_ok(const struct file *f)
67 return !(f->flags & FW_FLAG_UNSUPPORTED) || cmdargs.unsupported;
70 /* Convert a Big-Endian 16bit integer to CPU-endian */
71 static uint16_t from_be16(be16_t v)
75 ret |= (uint16_t)(((uint8_t *)&v)[0]) << 8;
76 ret |= (uint16_t)(((uint8_t *)&v)[1]);
81 /* Convert a CPU-endian 16bit integer to Big-Endian */
82 static be16_t to_be16(uint16_t v)
84 return (be16_t)from_be16((be16_t)v);
87 /* Convert a Big-Endian 32bit integer to CPU-endian */
88 static uint32_t from_be32(be32_t v)
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]);
100 /* Convert a CPU-endian 32bit integer to Big-Endian */
101 static be32_t to_be32(uint32_t v)
103 return (be32_t)from_be32((be32_t)v);
106 /* Convert a Little-Endian 32bit integer to CPU-endian */
107 static uint32_t from_le32(le32_t v)
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;
119 /* Convert a CPU-endian 32bit integer to Little-Endian */
120 static le32_t to_le32(uint32_t v)
122 return (le32_t)from_le32((le32_t)v);
125 /* tiny disassembler */
126 static void print_ucode_version(struct insn *insn)
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
136 if (insn->opcode != 0x378 && insn->opcode != 0x2de00)
139 val = ((0xFF & insn->op1) << 8) | (0xFF & insn->op2);
142 * Memory offsets are word-offsets, for the meaning
143 * see http://bcm-v4.sipsolutions.net/802.11/ObjectMemory
147 printf(" ucode version: %d\n", val);
150 printf(" ucode revision: %d\n", val);
153 printf(" ucode date: %.4d-%.2d-%.2d\n",
154 2000 + (val >> 12), (val >> 8) & 0xF, val & 0xFF);
157 printf(" ucode time: %.2d:%.2d:%.2d\n",
158 val >> 11, (val >> 5) & 0x3f, val & 0x1f);
163 static void disasm_ucode_1(uint64_t in, struct insn *out)
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
169 in = (in >> 32) | (in << 32);
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 */
178 static void disasm_ucode_2(uint64_t in, struct insn *out)
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
184 in = (in >> 32) | (in << 32);
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 */
193 static void disasm_ucode_3(uint64_t in, struct insn *out)
196 * like 2, but each operand has one bit more; appears
197 * to use the same instruction set slightly extended
199 in = (in >> 32) | (in << 32);
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 */
208 static void analyse_ucode(int ucode_rev, uint8_t *data, uint32_t len)
210 uint64_t *insns = (uint64_t*)data;
214 for (i=0; i<len/sizeof(*insns); i++) {
217 disasm_ucode_1(insns[i], &insn);
218 print_ucode_version(&insn);
221 disasm_ucode_2(insns[i], &insn);
222 print_ucode_version(&insn);
225 disasm_ucode_3(insns[i], &insn);
226 print_ucode_version(&insn);
232 static void swap_endianness_ucode(uint8_t *buf, uint32_t len)
234 uint32_t *buf32 = (uint32_t*)buf;
237 for (i=0; i<len/4; i++)
238 buf32[i] = bswap_32(buf32[i]);
241 #define swap_endianness_pcm swap_endianness_ucode
243 static void swap_endianness_iv(struct iv *iv)
245 iv->reg = bswap_16(iv->reg);
246 iv->size = bswap_16(iv->size);
247 iv->val = bswap_32(iv->val);
250 static uint8_t *read_object(FILE *f, const struct extract *extract)
254 if (fseek(f, extract->offset, SEEK_SET)) {
255 perror("failed to seek on file");
259 buf = malloc(extract->length);
261 perror("failed to allocate buffer");
264 if (fread(buf, 1, extract->length, f) != extract->length) {
265 perror("failed to read complete data");
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,
281 if (sizeof(struct b43_iv) != 6) {
282 printf("sizeof(struct b43_iv) != 6\n");
286 out = malloc(in_size);
288 perror("failed to allocate buffer");
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);
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;
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");
312 out->data.d16 = to_be16(from_be32(in->val));
314 out_size += sizeof(be16_t) + sizeof(be16_t);
315 out = (struct b43_iv *)((uint8_t *)out + sizeof(be16_t) + sizeof(be16_t));
317 printf("Input file IV size != 2|4\n");
321 hdr->size = to_be32(i);
322 *_out_size = out_size;
325 static void write_file(const char *name, uint8_t *buf, uint32_t len,
326 const struct fw_header *hdr, uint32_t flags)
333 if (flags & FW_FLAG_V4)
338 r = snprintf(nbuf, sizeof(nbuf),
339 "%s/%s", cmdargs.target_dir, dir);
340 if (r >= sizeof(nbuf)) {
341 fprintf(stderr, "name too long");
345 r = mkdir(nbuf, 0770);
346 if (r && errno != EEXIST) {
347 perror("failed to create output directory");
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");
357 f = fopen(nbuf, "w");
359 perror("failed to open file");
362 if (fwrite(hdr, sizeof(*hdr), 1, f) != 1) {
363 perror("failed to write file");
366 if (fwrite(buf, 1, len, f) != len) {
367 perror("failed to write file");
373 static void extract_or_identify(FILE *f, const char *dir,
374 const struct extract *extract, uint32_t flags)
379 struct fw_header hdr;
381 memset(&hdr, 0, sizeof(hdr));
382 hdr.ver = FW_HDR_VER;
384 printf("%s %s/%s.fw\n",
385 cmdargs.mode == FWCM_IDENTIFY ? "Contains" : "Extracting",
388 buf = read_object(f, extract);
390 switch (extract->type) {
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);
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);
414 hdr.type = FW_TYPE_IV;
415 build_ivs(&ivs, &data_length,
416 (struct iv *)buf, extract->length,
419 buf = (uint8_t *)ivs;
426 if (cmdargs.mode == FWCM_EXTRACT_B43)
427 write_file(extract->name, buf, data_length, &hdr, flags);
432 static int brcmsmac_name_to_idx(const char *name)
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;
460 static int brcmsmac_name_to_size_idx(const char *name)
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;
470 static void brcmsmac_clear_file(void)
476 r = snprintf(nbuf, sizeof(nbuf),
477 "%s/brcm", cmdargs.target_dir);
478 if (r >= sizeof(nbuf)) {
479 fprintf(stderr, "name too long");
483 r = mkdir(nbuf, 0770);
484 if (r && errno != EEXIST) {
485 perror("failed to create output directory");
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");
495 f = fopen(nbuf, "w");
497 perror("failed to open data file");
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");
508 f = fopen(nbuf, "w");
512 static void brcmsmac_write_file(int idx, uint8_t *buf, uint32_t len)
519 struct firmware_hdr fw_hdr;
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");
527 f = fopen(nbuf, "a");
529 perror("failed to open data file");
532 fseek(f, 0L, SEEK_END);
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");
540 h = fopen(nbuf, "a");
542 perror("failed to open header file");
545 fseek(h, 0L, SEEK_END);
549 fw_hdr.offset = to_le32(offset);
550 fw_hdr.len = to_le32(len);
551 fw_hdr.idx = to_le32(idx);
553 if (fwrite(&fw_hdr, sizeof(fw_hdr), 1, h) != 1) {
554 perror("failed to write file");
558 if (fwrite(buf, 1, len, f) != len) {
559 perror("failed to write file");
565 static void brcmsmac_add_dummy_entries(void)
567 uint8_t buf[4] = {0};
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);
574 static void brcmsmac_extract(FILE *f, const struct extract *extract,
580 int brcmsmac_idx = 0;
584 brcmsmac_idx = brcmsmac_name_to_idx(extract->name);
588 cmdargs.mode == FWCM_IDENTIFY ? "Contains" : "Extracting",
591 buf = read_object(f, extract);
593 switch (extract->type) {
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);
608 data_length = extract->length;
609 if (!(flags & FW_FLAG_LE))
610 swap_endianness_pcm(buf, data_length);
611 size = to_le32(data_length);
614 data_length = extract->length;
615 if (!(flags & FW_FLAG_LE)) {
616 struct iv *in = (struct iv *)buf;
619 for (i = 0; i < data_length / sizeof(struct iv); i++, in++)
620 swap_endianness_iv(in);
622 size = to_le32(data_length);
629 brcmsmac_write_file(brcmsmac_idx, buf, data_length);
630 int size_idx = brcmsmac_name_to_size_idx(extract->name);
632 brcmsmac_write_file(size_idx, (uint8_t *)&size, 4);
637 static void print_banner(void)
639 printf("b43-fwcutter version " FWCUTTER_VERSION "\n");
642 static void print_file(const struct file *file)
647 if (file->flags & FW_FLAG_V4)
648 printf(V4_FW_DIRNAME "\t\t");
650 printf(V3_FW_DIRNAME "\t");
652 if (strlen(file->name) > 20) {
653 strncpy(shortname, file->name, 20);
654 shortname[20] = '\0';
655 snprintf(filename, sizeof(filename), "%s..", shortname);
657 strcpy (filename, file->name);
659 printf("%s\t", filename);
660 if (strlen(filename) < 8) printf("\t");
661 if (strlen(filename) < 16) printf("\t");
663 printf("%s\t", file->ucode_version);
664 if (strlen(file->ucode_version) < 8) printf("\t");
666 printf("%s\n", file->md5);
669 static void print_supported_files(void)
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");
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]);
691 static const struct file *find_file(FILE *fd)
693 unsigned char buffer[16384], signature[16];
694 struct MD5Context md5c;
699 while ((i = (int) fread(buffer, 1, sizeof(buffer), fd)) > 0)
700 MD5Update(&md5c, buffer, (unsigned) i);
701 MD5Final(signature, &md5c);
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]);
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);
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);
728 static void print_usage(int argc, char *argv[])
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");
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");
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",
754 static int do_cmp_arg(char **argv, int *pos,
755 const char *template,
761 size_t arg_len, template_len;
764 next_arg = argv[*pos + 1];
765 arg_len = strlen(arg);
766 template_len = strlen(template);
769 /* Maybe we have a merged parameter here.
770 * A merged parameter is "-pfoobar" for example.
772 if (allow_merged && arg_len > template_len) {
773 if (memcmp(arg, template, template_len) == 0) {
774 *param = arg + template_len;
778 } else if (arg_len != template_len)
782 if (strcmp(arg, template) == 0) {
784 /* Skip the parameter on the next iteration. */
787 printf("%s needs a parameter\n", arg);
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,
806 err = do_cmp_arg(argv, pos, long_template, 0, param);
807 if (err == ARG_MATCH || err == ARG_ERROR)
812 err = do_cmp_arg(argv, pos, short_template, 1, param);
816 static int parse_args(int argc, char *argv[])
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;
828 } else if (res == ARG_ERROR)
831 res = cmp_arg(argv, &i, "--version", "-v", NULL);
832 if (res == ARG_MATCH) {
835 } else if (res == ARG_ERROR)
838 res = cmp_arg(argv, &i, "--help", "-h", NULL);
839 if (res == ARG_MATCH)
841 else if (res == ARG_ERROR)
844 res = cmp_arg(argv, &i, "--identify", "-i", NULL);
845 if (res == ARG_MATCH) {
846 cmdargs.mode = FWCM_IDENTIFY;
848 } else if (res == ARG_ERROR)
851 res = cmp_arg(argv, &i, "--brcmsmac", "-b", NULL);
852 if (res == ARG_MATCH) {
853 cmdargs.mode = FWCM_EXTRACT_BRCMSMAC;
855 } else if (res == ARG_ERROR)
858 res = cmp_arg(argv, &i, "--unsupported", NULL, NULL);
859 if (res == ARG_MATCH) {
860 cmdargs.unsupported = 1;
862 } else if (res == ARG_ERROR)
865 res = cmp_arg(argv, &i, "--target-dir", "-w", ¶m);
866 if (res == ARG_MATCH) {
867 cmdargs.target_dir = param;
869 } else if (res == ARG_ERROR)
872 cmdargs.infile = argv[i];
876 if (!cmdargs.infile && cmdargs.mode != FWCM_LIST)
881 print_usage(argc, argv);
886 int main(int argc, char *argv[])
889 const struct file *file;
890 const struct extract *extract;
894 cmdargs.target_dir = ".";
895 err = parse_args(argc, argv);
901 if (cmdargs.mode == FWCM_LIST) {
902 print_supported_files();
906 fd = fopen(cmdargs.infile, "rb");
908 fprintf(stderr, "Cannot open input file %s\n", cmdargs.infile);
913 file = find_file(fd);
917 if (file->flags & FW_FLAG_V4)
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);
929 brcmsmac_add_dummy_entries();
931 extract = file->extract;
932 while (extract->name) {
933 extract_or_identify(fd, dir, extract, file->flags);