2 * firmware cutter for broadcom 43xx wireless driver files
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>
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>
49 #include "fwcutter_list.h"
51 #if defined(__DragonFly__) || defined(__FreeBSD__)
52 #define V3_FW_DIRNAME "v3"
53 #define V4_FW_DIRNAME "v4"
55 #define V3_FW_DIRNAME "b43legacy"
56 #define V4_FW_DIRNAME "b43"
59 static struct cmdline_args cmdargs;
62 /* check whether file will be listed/extracted from */
63 static int file_ok(const struct file *f)
65 return !(f->flags & FW_FLAG_UNSUPPORTED) || cmdargs.unsupported;
68 /* Convert a Big-Endian 16bit integer to CPU-endian */
69 static uint16_t from_be16(be16_t v)
73 ret |= (uint16_t)(((uint8_t *)&v)[0]) << 8;
74 ret |= (uint16_t)(((uint8_t *)&v)[1]);
79 /* Convert a CPU-endian 16bit integer to Big-Endian */
80 static be16_t to_be16(uint16_t v)
82 return (be16_t)from_be16((be16_t)v);
85 /* Convert a Big-Endian 32bit integer to CPU-endian */
86 static uint32_t from_be32(be32_t v)
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]);
98 /* Convert a CPU-endian 32bit integer to Big-Endian */
99 static be32_t to_be32(uint32_t v)
101 return (be32_t)from_be32((be32_t)v);
104 /* Convert a Little-Endian 32bit integer to CPU-endian */
105 static uint32_t from_le32(le32_t v)
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;
117 /* Convert a CPU-endian 32bit integer to Little-Endian */
118 static le32_t to_le32(uint32_t v)
120 return (le32_t)from_le32((le32_t)v);
123 /* tiny disassembler */
124 static void print_ucode_version(struct insn *insn)
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
134 if (insn->opcode != 0x378 && insn->opcode != 0x2de00)
137 val = ((0xFF & insn->op1) << 8) | (0xFF & insn->op2);
140 * Memory offsets are word-offsets, for the meaning
141 * see http://bcm-v4.sipsolutions.net/802.11/ObjectMemory
145 printf(" ucode version: %d\n", val);
148 printf(" ucode revision: %d\n", val);
151 printf(" ucode date: %.4d-%.2d-%.2d\n",
152 2000 + (val >> 12), (val >> 8) & 0xF, val & 0xFF);
155 printf(" ucode time: %.2d:%.2d:%.2d\n",
156 val >> 11, (val >> 5) & 0x3f, val & 0x1f);
161 static void disasm_ucode_1(uint64_t in, struct insn *out)
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
167 in = (in >> 32) | (in << 32);
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 */
176 static void disasm_ucode_2(uint64_t in, struct insn *out)
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
182 in = (in >> 32) | (in << 32);
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 */
191 static void disasm_ucode_3(uint64_t in, struct insn *out)
194 * like 2, but each operand has one bit more; appears
195 * to use the same instruction set slightly extended
197 in = (in >> 32) | (in << 32);
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 */
206 static void analyse_ucode(int ucode_rev, uint8_t *data, uint32_t len)
208 uint64_t *insns = (uint64_t*)data;
212 for (i=0; i<len/sizeof(*insns); i++) {
215 disasm_ucode_1(insns[i], &insn);
216 print_ucode_version(&insn);
219 disasm_ucode_2(insns[i], &insn);
220 print_ucode_version(&insn);
223 disasm_ucode_3(insns[i], &insn);
224 print_ucode_version(&insn);
230 static void swap_endianness_ucode(uint8_t *buf, uint32_t len)
232 uint32_t *buf32 = (uint32_t*)buf;
235 for (i=0; i<len/4; i++)
236 buf32[i] = bswap_32(buf32[i]);
239 #define swap_endianness_pcm swap_endianness_ucode
241 static void swap_endianness_iv(struct iv *iv)
243 iv->reg = bswap_16(iv->reg);
244 iv->size = bswap_16(iv->size);
245 iv->val = bswap_32(iv->val);
248 static uint8_t *read_object(FILE *f, const struct extract *extract)
252 if (fseek(f, extract->offset, SEEK_SET)) {
253 perror("failed to seek on file");
257 buf = malloc(extract->length);
259 perror("failed to allocate buffer");
262 if (fread(buf, 1, extract->length, f) != extract->length) {
263 perror("failed to read complete data");
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,
279 if (sizeof(struct b43_iv) != 6) {
280 printf("sizeof(struct b43_iv) != 6\n");
284 out = malloc(in_size);
286 perror("failed to allocate buffer");
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);
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;
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");
310 out->data.d16 = to_be16(from_be32(in->val));
312 out_size += sizeof(be16_t) + sizeof(be16_t);
313 out = (struct b43_iv *)((uint8_t *)out + sizeof(be16_t) + sizeof(be16_t));
315 printf("Input file IV size != 2|4\n");
319 hdr->size = to_be32(i);
320 *_out_size = out_size;
323 static void write_file(const char *name, uint8_t *buf, uint32_t len,
324 const struct fw_header *hdr, uint32_t flags)
331 if (flags & FW_FLAG_V4)
336 r = snprintf(nbuf, sizeof(nbuf),
337 "%s/%s", cmdargs.target_dir, dir);
338 if (r >= sizeof(nbuf)) {
339 fprintf(stderr, "name too long");
343 r = mkdir(nbuf, 0770);
344 if (r && errno != EEXIST) {
345 perror("failed to create output directory");
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");
355 f = fopen(nbuf, "w");
357 perror("failed to open file");
360 if (fwrite(hdr, sizeof(*hdr), 1, f) != 1) {
361 perror("failed to write file");
364 if (fwrite(buf, 1, len, f) != len) {
365 perror("failed to write file");
371 static void extract_or_identify(FILE *f, const char *dir,
372 const struct extract *extract, uint32_t flags)
377 struct fw_header hdr;
379 memset(&hdr, 0, sizeof(hdr));
380 hdr.ver = FW_HDR_VER;
382 printf("%s %s/%s.fw\n",
383 cmdargs.mode == FWCM_IDENTIFY ? "Contains" : "Extracting",
386 buf = read_object(f, extract);
388 switch (extract->type) {
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);
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);
412 hdr.type = FW_TYPE_IV;
413 build_ivs(&ivs, &data_length,
414 (struct iv *)buf, extract->length,
417 buf = (uint8_t *)ivs;
424 if (cmdargs.mode == FWCM_EXTRACT_B43)
425 write_file(extract->name, buf, data_length, &hdr, flags);
430 static int brcmsmac_name_to_idx(const char *name)
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;
458 static int brcmsmac_name_to_size_idx(const char *name)
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;
468 static void brcmsmac_clear_file(void)
474 r = snprintf(nbuf, sizeof(nbuf),
475 "%s/brcm", cmdargs.target_dir);
476 if (r >= sizeof(nbuf)) {
477 fprintf(stderr, "name too long");
481 r = mkdir(nbuf, 0770);
482 if (r && errno != EEXIST) {
483 perror("failed to create output directory");
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");
493 f = fopen(nbuf, "w");
495 perror("failed to open data file");
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");
506 f = fopen(nbuf, "w");
510 static void brcmsmac_write_file(int idx, uint8_t *buf, uint32_t len)
517 struct firmware_hdr fw_hdr;
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");
525 f = fopen(nbuf, "a");
527 perror("failed to open data file");
530 fseek(f, 0L, SEEK_END);
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");
538 h = fopen(nbuf, "a");
540 perror("failed to open header file");
543 fseek(h, 0L, SEEK_END);
547 fw_hdr.offset = to_le32(offset);
548 fw_hdr.len = to_le32(len);
549 fw_hdr.idx = to_le32(idx);
551 if (fwrite(&fw_hdr, sizeof(fw_hdr), 1, h) != 1) {
552 perror("failed to write file");
556 if (fwrite(buf, 1, len, f) != len) {
557 perror("failed to write file");
563 static void brcmsmac_add_dummy_entries(void)
565 uint8_t buf[4] = {0};
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);
572 static void brcmsmac_extract(FILE *f, const struct extract *extract,
578 int brcmsmac_idx = 0;
582 brcmsmac_idx = brcmsmac_name_to_idx(extract->name);
586 cmdargs.mode == FWCM_IDENTIFY ? "Contains" : "Extracting",
589 buf = read_object(f, extract);
591 switch (extract->type) {
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);
606 data_length = extract->length;
607 if (!(flags & FW_FLAG_LE))
608 swap_endianness_pcm(buf, data_length);
609 size = to_le32(data_length);
612 data_length = extract->length;
613 if (!(flags & FW_FLAG_LE)) {
614 struct iv *in = (struct iv *)buf;
617 for (i = 0; i < data_length / sizeof(struct iv); i++, in++)
618 swap_endianness_iv(in);
620 size = to_le32(data_length);
627 brcmsmac_write_file(brcmsmac_idx, buf, data_length);
628 int size_idx = brcmsmac_name_to_size_idx(extract->name);
630 brcmsmac_write_file(size_idx, (uint8_t *)&size, 4);
635 static void print_banner(void)
637 printf("b43-fwcutter version " FWCUTTER_VERSION "\n");
640 static void print_file(const struct file *file)
645 if (file->flags & FW_FLAG_V4)
646 printf(V4_FW_DIRNAME "\t\t");
648 printf(V3_FW_DIRNAME "\t");
650 if (strlen(file->name) > 20) {
651 strncpy(shortname, file->name, 20);
652 shortname[20] = '\0';
653 snprintf(filename, sizeof(filename), "%s..", shortname);
655 strcpy (filename, file->name);
657 printf("%s\t", filename);
658 if (strlen(filename) < 8) printf("\t");
659 if (strlen(filename) < 16) printf("\t");
661 printf("%s\t", file->ucode_version);
662 if (strlen(file->ucode_version) < 8) printf("\t");
664 printf("%s\n", file->md5);
667 static void print_supported_files(void)
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");
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]);
689 static const struct file *find_file(FILE *fd)
691 unsigned char buffer[16384], signature[16];
692 struct MD5Context md5c;
697 while ((i = (int) fread(buffer, 1, sizeof(buffer), fd)) > 0)
698 MD5Update(&md5c, buffer, (unsigned) i);
699 MD5Final(signature, &md5c);
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]);
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);
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);
726 static void print_usage(int argc, char *argv[])
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");
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");
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",
752 static int do_cmp_arg(char **argv, int *pos,
753 const char *template,
759 size_t arg_len, template_len;
762 next_arg = argv[*pos + 1];
763 arg_len = strlen(arg);
764 template_len = strlen(template);
767 /* Maybe we have a merged parameter here.
768 * A merged parameter is "-pfoobar" for example.
770 if (allow_merged && arg_len > template_len) {
771 if (memcmp(arg, template, template_len) == 0) {
772 *param = arg + template_len;
776 } else if (arg_len != template_len)
780 if (strcmp(arg, template) == 0) {
782 /* Skip the parameter on the next iteration. */
785 printf("%s needs a parameter\n", arg);
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,
804 err = do_cmp_arg(argv, pos, long_template, 0, param);
805 if (err == ARG_MATCH || err == ARG_ERROR)
810 err = do_cmp_arg(argv, pos, short_template, 1, param);
814 static int parse_args(int argc, char *argv[])
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;
826 } else if (res == ARG_ERROR)
829 res = cmp_arg(argv, &i, "--version", "-v", NULL);
830 if (res == ARG_MATCH) {
833 } else if (res == ARG_ERROR)
836 res = cmp_arg(argv, &i, "--help", "-h", NULL);
837 if (res == ARG_MATCH)
839 else if (res == ARG_ERROR)
842 res = cmp_arg(argv, &i, "--identify", "-i", NULL);
843 if (res == ARG_MATCH) {
844 cmdargs.mode = FWCM_IDENTIFY;
846 } else if (res == ARG_ERROR)
849 res = cmp_arg(argv, &i, "--brcmsmac", "-b", NULL);
850 if (res == ARG_MATCH) {
851 cmdargs.mode = FWCM_EXTRACT_BRCMSMAC;
853 } else if (res == ARG_ERROR)
856 res = cmp_arg(argv, &i, "--unsupported", NULL, NULL);
857 if (res == ARG_MATCH) {
858 cmdargs.unsupported = 1;
860 } else if (res == ARG_ERROR)
863 res = cmp_arg(argv, &i, "--target-dir", "-w", ¶m);
864 if (res == ARG_MATCH) {
865 cmdargs.target_dir = param;
867 } else if (res == ARG_ERROR)
870 cmdargs.infile = argv[i];
874 if (!cmdargs.infile && cmdargs.mode != FWCM_LIST)
879 print_usage(argc, argv);
884 int main(int argc, char *argv[])
887 const struct file *file;
888 const struct extract *extract;
892 cmdargs.target_dir = ".";
893 err = parse_args(argc, argv);
899 if (cmdargs.mode == FWCM_LIST) {
900 print_supported_files();
904 fd = fopen(cmdargs.infile, "rb");
906 fprintf(stderr, "Cannot open input file %s\n", cmdargs.infile);
911 file = find_file(fd);
915 if (file->flags & FW_FLAG_V4)
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);
927 brcmsmac_add_dummy_entries();
929 extract = file->extract;
930 while (extract->name) {
931 extract_or_identify(fd, dir, extract, file->flags);