123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944 |
- /*
- * firmware cutter for broadcom 43xx wireless driver files
- *
- * Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
- * 2005-2014 Michael Buesch <m@bues.ch>
- * 2005 Alex Beregszaszi
- * 2007 Johannes Berg <johannes@sipsolutions.net>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
- * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #include <stdlib.h>
- #include <ctype.h>
- #include <string.h>
- #include <stdio.h>
- #include <errno.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #if defined(__DragonFly__) || defined(__FreeBSD__)
- #include <sys/endian.h>
- #elif defined(__APPLE__)
- #include <libkern/OSByteOrder.h>
- #else
- #include <byteswap.h>
- #endif
- #include "md5.h"
- #include "fwcutter.h"
- #include "fwcutter_list.h"
- #if defined(__DragonFly__) || defined(__FreeBSD__)
- #define V3_FW_DIRNAME "v3"
- #define V4_FW_DIRNAME "v4"
- #else
- #define V3_FW_DIRNAME "b43legacy"
- #define V4_FW_DIRNAME "b43"
- #endif
- static struct cmdline_args cmdargs;
- /* check whether file will be listed/extracted from */
- static int file_ok(const struct file *f)
- {
- return !(f->flags & FW_FLAG_UNSUPPORTED) || cmdargs.unsupported;
- }
- /* Convert a Big-Endian 16bit integer to CPU-endian */
- static uint16_t from_be16(be16_t v)
- {
- uint16_t ret = 0;
- ret |= (uint16_t)(((uint8_t *)&v)[0]) << 8;
- ret |= (uint16_t)(((uint8_t *)&v)[1]);
- return ret;
- }
- /* Convert a CPU-endian 16bit integer to Big-Endian */
- static be16_t to_be16(uint16_t v)
- {
- return (be16_t)from_be16((be16_t)v);
- }
- /* Convert a Big-Endian 32bit integer to CPU-endian */
- static uint32_t from_be32(be32_t v)
- {
- uint32_t ret = 0;
- ret |= (uint32_t)(((uint8_t *)&v)[0]) << 24;
- ret |= (uint32_t)(((uint8_t *)&v)[1]) << 16;
- ret |= (uint32_t)(((uint8_t *)&v)[2]) << 8;
- ret |= (uint32_t)(((uint8_t *)&v)[3]);
- return ret;
- }
- /* Convert a CPU-endian 32bit integer to Big-Endian */
- static be32_t to_be32(uint32_t v)
- {
- return (be32_t)from_be32((be32_t)v);
- }
- /* Convert a Little-Endian 32bit integer to CPU-endian */
- static uint32_t from_le32(le32_t v)
- {
- uint32_t ret = 0;
- ret |= (uint32_t)(((uint8_t *)&v)[0]);
- ret |= (uint32_t)(((uint8_t *)&v)[1]) << 8;
- ret |= (uint32_t)(((uint8_t *)&v)[2]) << 16;
- ret |= (uint32_t)(((uint8_t *)&v)[3]) << 24;
- return ret;
- }
- /* Convert a CPU-endian 32bit integer to Little-Endian */
- static le32_t to_le32(uint32_t v)
- {
- return (le32_t)from_le32((le32_t)v);
- }
- /* tiny disassembler */
- static void print_ucode_version(struct insn *insn)
- {
- int val;
- /*
- * The instruction we're looking for is a store to memory
- * offset insn->op3 of the constant formed like `val' below.
- * 0x2de00 is the opcode for type 1, 0x378 is the opcode
- * for type 2 and 3.
- */
- if (insn->opcode != 0x378 && insn->opcode != 0x2de00)
- return;
- val = ((0xFF & insn->op1) << 8) | (0xFF & insn->op2);
- /*
- * Memory offsets are word-offsets, for the meaning
- * see http://bcm-v4.sipsolutions.net/802.11/ObjectMemory
- */
- switch (insn->op3) {
- case 0:
- printf(" ucode version: %d\n", val);
- break;
- case 1:
- printf(" ucode revision: %d\n", val);
- break;
- case 2:
- printf(" ucode date: %.4d-%.2d-%.2d\n",
- 2000 + (val >> 12), (val >> 8) & 0xF, val & 0xFF);
- break;
- case 3:
- printf(" ucode time: %.2d:%.2d:%.2d\n",
- val >> 11, (val >> 5) & 0x3f, val & 0x1f);
- break;
- }
- }
- static void disasm_ucode_1(uint64_t in, struct insn *out)
- {
- /* xxyyyzzz00oooooX -> ooooo Xxx yyy zzz
- * if we swap the upper and lower 32-bits first it becomes easier:
- * 00oooooxxxyyyzzz -> ooooo xxx yyy zzz
- */
- in = (in >> 32) | (in << 32);
- out->op3 = in & 0xFFF;
- out->op2 = (in >> 12) & 0xFFF;
- out->op1 = (in >> 24) & 0xFFF;
- out->opcode = (in >> 36) & 0xFFFFF;
- /* the rest of the in word should be zero */
- }
- static void disasm_ucode_2(uint64_t in, struct insn *out)
- {
- /* xxyyyzzz0000oooX -> ooo Xxx yyy zzz
- * if we swap the upper and lower 32-bits first it becomes easier:
- * 0000oooxxxyyyzzz -> ooo xxx yyy zzz
- */
- in = (in >> 32) | (in << 32);
- out->op3 = in & 0xFFF;
- out->op2 = (in >> 12) & 0xFFF;
- out->op1 = (in >> 24) & 0xFFF;
- out->opcode = (in >> 36) & 0xFFF;
- /* the rest of the in word should be zero */
- }
- static void disasm_ucode_3(uint64_t in, struct insn *out)
- {
- /*
- * like 2, but each operand has one bit more; appears
- * to use the same instruction set slightly extended
- */
- in = (in >> 32) | (in << 32);
- out->op3 = in & 0x1FFF;
- out->op2 = (in >> 13) & 0x1FFF;
- out->op1 = (in >> 26) & 0x1FFF;
- out->opcode = (in >> 39) & 0xFFF;
- /* the rest of the in word should be zero */
- }
- static void analyse_ucode(int ucode_rev, uint8_t *data, uint32_t len)
- {
- uint64_t *insns = (uint64_t*)data;
- struct insn insn;
- uint32_t i;
- for (i=0; i<len/sizeof(*insns); i++) {
- switch (ucode_rev) {
- case 1:
- disasm_ucode_1(insns[i], &insn);
- print_ucode_version(&insn);
- break;
- case 2:
- disasm_ucode_2(insns[i], &insn);
- print_ucode_version(&insn);
- break;
- case 3:
- disasm_ucode_3(insns[i], &insn);
- print_ucode_version(&insn);
- break;
- }
- }
- }
- static void swap_endianness_ucode(uint8_t *buf, uint32_t len)
- {
- uint32_t *buf32 = (uint32_t*)buf;
- uint32_t i;
- for (i=0; i<len/4; i++)
- buf32[i] = bswap_32(buf32[i]);
- }
- #define swap_endianness_pcm swap_endianness_ucode
- static void swap_endianness_iv(struct iv *iv)
- {
- iv->reg = bswap_16(iv->reg);
- iv->size = bswap_16(iv->size);
- iv->val = bswap_32(iv->val);
- }
- static uint8_t *read_object(FILE *f, const struct extract *extract)
- {
- uint8_t *buf;
- if (fseek(f, extract->offset, SEEK_SET)) {
- perror("failed to seek on file");
- exit(2);
- }
- buf = malloc(extract->length);
- if (!buf) {
- perror("failed to allocate buffer");
- exit(3);
- }
- if (fread(buf, 1, extract->length, f) != extract->length) {
- perror("failed to read complete data");
- exit(3);
- }
- return buf;
- }
- static void build_ivs(struct b43_iv **_out, size_t *_out_size,
- struct iv *in, size_t in_size,
- struct fw_header *hdr,
- uint32_t flags)
- {
- struct iv *iv;
- struct b43_iv *out;
- uint32_t i;
- size_t out_size = 0;
- if (sizeof(struct b43_iv) != 6) {
- printf("sizeof(struct b43_iv) != 6\n");
- exit(255);
- }
- out = malloc(in_size);
- if (!out) {
- perror("failed to allocate buffer");
- exit(1);
- }
- *_out = out;
- for (i = 0; i < in_size / sizeof(*iv); i++, in++) {
- if (flags & FW_FLAG_LE)
- swap_endianness_iv(in);
- /* input-IV is BigEndian */
- if (in->reg & to_be16(~FW_IV_OFFSET_MASK)) {
- printf("Input file IV offset > 0x%X\n", FW_IV_OFFSET_MASK);
- exit(1);
- }
- out->offset_size = in->reg;
- if (in->size == to_be16(4)) {
- out->offset_size |= to_be16(FW_IV_32BIT);
- out->data.d32 = in->val;
- out_size += sizeof(be16_t) + sizeof(be32_t);
- out = (struct b43_iv *)((uint8_t *)out + sizeof(be16_t) + sizeof(be32_t));
- } else if (in->size == to_be16(2)) {
- if (in->val & to_be32(~0xFFFF)) {
- printf("Input file 16bit IV value overflow\n");
- exit(1);
- }
- out->data.d16 = to_be16(from_be32(in->val));
- out_size += sizeof(be16_t) + sizeof(be16_t);
- out = (struct b43_iv *)((uint8_t *)out + sizeof(be16_t) + sizeof(be16_t));
- } else {
- printf("Input file IV size != 2|4\n");
- exit(1);
- }
- }
- hdr->size = to_be32(i);
- *_out_size = out_size;
- }
- static void write_file(const char *name, uint8_t *buf, uint32_t len,
- const struct fw_header *hdr, uint32_t flags)
- {
- FILE *f;
- char nbuf[4096];
- const char *dir;
- int r;
- if (flags & FW_FLAG_V4)
- dir = V4_FW_DIRNAME;
- else
- dir = V3_FW_DIRNAME;
- r = snprintf(nbuf, sizeof(nbuf),
- "%s/%s", cmdargs.target_dir, dir);
- if (r >= sizeof(nbuf)) {
- fprintf(stderr, "name too long");
- exit(2);
- }
- r = mkdir(nbuf, 0770);
- if (r && errno != EEXIST) {
- perror("failed to create output directory");
- exit(2);
- }
- r = snprintf(nbuf, sizeof(nbuf),
- "%s/%s/%s.fw", cmdargs.target_dir, dir, name);
- if (r >= sizeof(nbuf)) {
- fprintf(stderr, "name too long");
- exit(2);
- }
- f = fopen(nbuf, "w");
- if (!f) {
- perror("failed to open file");
- exit(2);
- }
- if (fwrite(hdr, sizeof(*hdr), 1, f) != 1) {
- perror("failed to write file");
- exit(2);
- }
- if (fwrite(buf, 1, len, f) != len) {
- perror("failed to write file");
- exit(2);
- }
- fclose(f);
- }
- static void extract_or_identify(FILE *f, const char *dir,
- const struct extract *extract, uint32_t flags)
- {
- uint8_t *buf;
- size_t data_length;
- int ucode_rev = 0;
- struct fw_header hdr;
- memset(&hdr, 0, sizeof(hdr));
- hdr.ver = FW_HDR_VER;
- printf("%s %s/%s.fw\n",
- cmdargs.mode == FWCM_IDENTIFY ? "Contains" : "Extracting",
- dir, extract->name);
- buf = read_object(f, extract);
- switch (extract->type) {
- case EXT_UCODE_3:
- ucode_rev += 1;
- case EXT_UCODE_2:
- ucode_rev += 1;
- case EXT_UCODE_1:
- ucode_rev += 1;
- data_length = extract->length;
- if (flags & FW_FLAG_LE)
- swap_endianness_ucode(buf, data_length);
- analyse_ucode(ucode_rev, buf, data_length);
- hdr.type = FW_TYPE_UCODE;
- hdr.size = to_be32(data_length);
- break;
- case EXT_PCM:
- data_length = extract->length;
- if (flags & FW_FLAG_LE)
- swap_endianness_pcm(buf, data_length);
- hdr.type = FW_TYPE_PCM;
- hdr.size = to_be32(data_length);
- break;
- case EXT_IV: {
- struct b43_iv *ivs;
- hdr.type = FW_TYPE_IV;
- build_ivs(&ivs, &data_length,
- (struct iv *)buf, extract->length,
- &hdr, flags);
- free(buf);
- buf = (uint8_t *)ivs;
- break;
- }
- default:
- exit(255);
- }
- if (cmdargs.mode == FWCM_EXTRACT_B43)
- write_file(extract->name, buf, data_length, &hdr, flags);
- free(buf);
- }
- static int brcmsmac_name_to_idx(const char *name)
- {
- if (strcmp("lcn0bsinitvals24", name) == 0) {
- return D11LCN0BSINITVALS24;
- } else if (strcmp("lcn0initvals24", name) == 0) {
- return D11LCN0INITVALS24;
- } else if (strcmp("lcn1bsinitvals24", name) == 0) {
- return D11LCN1BSINITVALS24;
- } else if (strcmp("lcn1initvals24", name) == 0) {
- return D11LCN1INITVALS24;
- } else if (strcmp("lcn2bsinitvals24", name) == 0) {
- return D11LCN2BSINITVALS24;
- } else if (strcmp("lcn2initvals24", name) == 0) {
- return D11LCN2INITVALS24;
- } else if (strcmp("n0absinitvals16", name) == 0) {
- return D11N0ABSINITVALS16;
- } else if (strcmp("n0bsinitvals16", name) == 0) {
- return D11N0BSINITVALS16;
- } else if (strcmp("n0initvals16", name) == 0) {
- return D11N0INITVALS16;
- } else if (strcmp("ucode16_mimo", name) == 0) {
- return D11UCODE_OVERSIGHT16_MIMO;
- } else if (strcmp("ucode24_lcn", name) == 0) {
- return D11UCODE_OVERSIGHT24_LCN;
- }
- return 0;
- }
- static int brcmsmac_name_to_size_idx(const char *name)
- {
- if (strcmp("ucode16_mimo", name) == 0) {
- return D11UCODE_OVERSIGHT16_MIMOSZ;
- } else if (strcmp("ucode24_lcn", name) == 0) {
- return D11UCODE_OVERSIGHT24_LCNSZ;
- }
- return 0;
- }
- static void brcmsmac_clear_file(void)
- {
- FILE *f;
- char nbuf[4096];
- int r;
- r = snprintf(nbuf, sizeof(nbuf),
- "%s/brcm", cmdargs.target_dir);
- if (r >= sizeof(nbuf)) {
- fprintf(stderr, "name too long");
- exit(2);
- }
- r = mkdir(nbuf, 0770);
- if (r && errno != EEXIST) {
- perror("failed to create output directory");
- exit(2);
- }
- r = snprintf(nbuf, sizeof(nbuf),
- "%s/brcm/bcm43xx-0.fw", cmdargs.target_dir);
- if (r >= sizeof(nbuf)) {
- fprintf(stderr, "name too long");
- exit(2);
- }
- f = fopen(nbuf, "w");
- if (!f) {
- perror("failed to open data file");
- exit(2);
- }
- fclose(f);
- r = snprintf(nbuf, sizeof(nbuf),
- "%s/brcm/bcm43xx_hdr-0.fw", cmdargs.target_dir);
- if (r >= sizeof(nbuf)) {
- fprintf(stderr, "name too long");
- exit(2);
- }
- f = fopen(nbuf, "w");
- fclose(f);
- }
- static void brcmsmac_write_file(int idx, uint8_t *buf, uint32_t len)
- {
- FILE *f;
- FILE *h;
- char nbuf[4096];
- int r;
- int offset;
- struct firmware_hdr fw_hdr;
- r = snprintf(nbuf, sizeof(nbuf),
- "%s/brcm/bcm43xx-0.fw", cmdargs.target_dir);
- if (r >= sizeof(nbuf)) {
- fprintf(stderr, "name too long");
- exit(2);
- }
- f = fopen(nbuf, "a");
- if (!f) {
- perror("failed to open data file");
- exit(2);
- }
- fseek(f, 0L, SEEK_END);
- r = snprintf(nbuf, sizeof(nbuf),
- "%s/brcm/bcm43xx_hdr-0.fw", cmdargs.target_dir);
- if (r >= sizeof(nbuf)) {
- fprintf(stderr, "name too long");
- exit(2);
- }
- h = fopen(nbuf, "a");
- if (!h) {
- perror("failed to open header file");
- exit(2);
- }
- fseek(h, 0L, SEEK_END);
- offset = ftell(f);
- fw_hdr.offset = to_le32(offset);
- fw_hdr.len = to_le32(len);
- fw_hdr.idx = to_le32(idx);
- if (fwrite(&fw_hdr, sizeof(fw_hdr), 1, h) != 1) {
- perror("failed to write file");
- exit(2);
- }
- fclose(h);
- if (fwrite(buf, 1, len, f) != len) {
- perror("failed to write file");
- exit(2);
- }
- fclose(f);
- }
- static void brcmsmac_add_dummy_entries(void)
- {
- uint8_t buf[4] = {0};
- brcmsmac_write_file(D11N0ABSINITVALS16, buf, 4);
- brcmsmac_write_file(D11UCODE_OVERSIGHT_BOMMAJOR, buf, 4);
- brcmsmac_write_file(D11UCODE_OVERSIGHT_BOMMINOR, buf, 4);
- }
- static void brcmsmac_extract(FILE *f, const struct extract *extract,
- uint32_t flags)
- {
- uint8_t *buf;
- size_t data_length;
- int ucode_rev = 0;
- int brcmsmac_idx = 0;
- be32_t size;
- brcmsmac_idx = brcmsmac_name_to_idx(extract->name);
- if (!brcmsmac_idx)
- return;
- printf("%s %s\n",
- cmdargs.mode == FWCM_IDENTIFY ? "Contains" : "Extracting",
- extract->name);
- buf = read_object(f, extract);
- switch (extract->type) {
- case EXT_UCODE_3:
- ucode_rev += 1;
- case EXT_UCODE_2:
- ucode_rev += 1;
- case EXT_UCODE_1:
- ucode_rev += 1;
- data_length = extract->length;
- if (flags & FW_FLAG_LE)
- swap_endianness_ucode(buf, data_length);
- analyse_ucode(ucode_rev, buf, data_length);
- swap_endianness_ucode(buf, data_length);
- size = to_le32(data_length);
- break;
- case EXT_PCM:
- data_length = extract->length;
- if (!(flags & FW_FLAG_LE))
- swap_endianness_pcm(buf, data_length);
- size = to_le32(data_length);
- break;
- case EXT_IV: {
- data_length = extract->length;
- if (!(flags & FW_FLAG_LE)) {
- struct iv *in = (struct iv *)buf;
- int i;
- for (i = 0; i < data_length / sizeof(struct iv); i++, in++)
- swap_endianness_iv(in);
- }
- size = to_le32(data_length);
- break;
- }
- default:
- exit(255);
- }
- brcmsmac_write_file(brcmsmac_idx, buf, data_length);
- int size_idx = brcmsmac_name_to_size_idx(extract->name);
- if (size_idx)
- brcmsmac_write_file(size_idx, (uint8_t *)&size, 4);
- free(buf);
- }
- static void print_banner(void)
- {
- printf("b43-fwcutter version " FWCUTTER_VERSION "\n");
- }
- static void print_file(const struct file *file)
- {
- char filename[30];
- char shortname[30];
- if (file->flags & FW_FLAG_V4)
- printf(V4_FW_DIRNAME "\t\t");
- else
- printf(V3_FW_DIRNAME "\t");
- if (strlen(file->name) > 20) {
- strncpy(shortname, file->name, 20);
- shortname[20] = '\0';
- snprintf(filename, sizeof(filename), "%s..", shortname);
- } else
- strcpy (filename, file->name);
- printf("%s\t", filename);
- if (strlen(filename) < 8) printf("\t");
- if (strlen(filename) < 16) printf("\t");
- printf("%s\t", file->ucode_version);
- if (strlen(file->ucode_version) < 8) printf("\t");
- printf("%s\n", file->md5);
- }
- static void print_supported_files(void)
- {
- int i;
- print_banner();
- printf("\nExtracting firmware is possible "
- "from these binary driver files.\n"
- "Please read http://linuxwireless.org/en/users/Drivers/b43#devicefirmware\n\n");
- printf("<driver>\t"
- "<filename>\t\t"
- "<microcode>\t"
- "<MD5 checksum>\n\n");
- /* print for legacy driver first */
- for (i = 0; i < ARRAY_SIZE(files); i++)
- if (file_ok(&files[i]) && !(files[i].flags & FW_FLAG_V4))
- print_file(&files[i]);
- for (i = 0; i < ARRAY_SIZE(files); i++)
- if (file_ok(&files[i]) && files[i].flags & FW_FLAG_V4)
- print_file(&files[i]);
- printf("\n");
- }
- static const struct file *find_file(FILE *fd)
- {
- unsigned char buffer[16384], signature[16];
- struct MD5Context md5c;
- char md5sig[33];
- int i;
- MD5Init(&md5c);
- while ((i = (int) fread(buffer, 1, sizeof(buffer), fd)) > 0)
- MD5Update(&md5c, buffer, (unsigned) i);
- MD5Final(signature, &md5c);
- snprintf(md5sig, sizeof(md5sig),
- "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x"
- "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
- signature[0], signature[1], signature[2], signature[3],
- signature[4], signature[5], signature[6], signature[7],
- signature[8], signature[9], signature[10], signature[11],
- signature[12], signature[13], signature[14], signature[15]);
- for (i = 0; i < ARRAY_SIZE(files); i++) {
- if (file_ok(&files[i]) &&
- strcasecmp(md5sig, files[i].md5) == 0) {
- printf("This file is recognised as:\n");
- printf(" filename : %s\n", files[i].name);
- printf(" version : %s\n", files[i].ucode_version);
- printf(" MD5 : %s\n", files[i].md5);
- return &files[i];
- }
- }
- printf("Sorry, the input file is either wrong or "
- "not supported by b43-fwcutter.\n");
- printf("This file has an unknown MD5sum %s.\n", md5sig);
- return NULL;
- }
- static void print_usage(int argc, char *argv[])
- {
- print_banner();
- printf("\nA tool to extract firmware for a Broadcom 43xx device\n");
- printf("from a proprietary Broadcom 43xx device driver file.\n");
- printf("\nUsage: %s [OPTION] [proprietary-driver-file]\n", argv[0]);
- printf(" --unsupported "
- "Allow working on extractable but unsupported drivers\n");
- printf(" -l|--list "
- "List supported driver versions\n");
- printf(" -b|--brcmsmac "
- "create firmware for brcmsmac\n");
- printf(" -i|--identify "
- "Only identify the driver file (don't extract)\n");
- printf(" -w|--target-dir DIR "
- "Extract and write firmware to DIR\n");
- printf(" -v|--version "
- "Print b43-fwcutter version\n");
- printf(" -h|--help "
- "Print this help\n");
- printf("\nExample: %s -w /lib/firmware wl_apsta.o\n"
- " to extract the firmware blobs from wl_apsta.o and store\n"
- " the resulting firmware in /lib/firmware\n",
- argv[0]);
- }
- static int do_cmp_arg(char **argv, int *pos,
- const char *template,
- int allow_merged,
- char **param)
- {
- char *arg;
- char *next_arg;
- size_t arg_len, template_len;
- arg = argv[*pos];
- next_arg = argv[*pos + 1];
- arg_len = strlen(arg);
- template_len = strlen(template);
- if (param) {
- /* Maybe we have a merged parameter here.
- * A merged parameter is "-pfoobar" for example.
- */
- if (allow_merged && arg_len > template_len) {
- if (memcmp(arg, template, template_len) == 0) {
- *param = arg + template_len;
- return ARG_MATCH;
- }
- return ARG_NOMATCH;
- } else if (arg_len != template_len)
- return ARG_NOMATCH;
- *param = next_arg;
- }
- if (strcmp(arg, template) == 0) {
- if (param) {
- /* Skip the parameter on the next iteration. */
- (*pos)++;
- if (!*param) {
- printf("%s needs a parameter\n", arg);
- return ARG_ERROR;
- }
- }
- return ARG_MATCH;
- }
- return ARG_NOMATCH;
- }
- /* Simple and lean command line argument parsing. */
- static int cmp_arg(char **argv, int *pos,
- const char *long_template,
- const char *short_template,
- char **param)
- {
- int err;
- if (long_template) {
- err = do_cmp_arg(argv, pos, long_template, 0, param);
- if (err == ARG_MATCH || err == ARG_ERROR)
- return err;
- }
- err = ARG_NOMATCH;
- if (short_template)
- err = do_cmp_arg(argv, pos, short_template, 1, param);
- return err;
- }
- static int parse_args(int argc, char *argv[])
- {
- int i, res;
- char *param;
- if (argc < 2)
- goto out_usage;
- for (i = 1; i < argc; i++) {
- res = cmp_arg(argv, &i, "--list", "-l", NULL);
- if (res == ARG_MATCH) {
- cmdargs.mode = FWCM_LIST;
- continue;
- } else if (res == ARG_ERROR)
- goto out;
- res = cmp_arg(argv, &i, "--version", "-v", NULL);
- if (res == ARG_MATCH) {
- print_banner();
- return 1;
- } else if (res == ARG_ERROR)
- goto out;
- res = cmp_arg(argv, &i, "--help", "-h", NULL);
- if (res == ARG_MATCH)
- goto out_usage;
- else if (res == ARG_ERROR)
- goto out;
- res = cmp_arg(argv, &i, "--identify", "-i", NULL);
- if (res == ARG_MATCH) {
- cmdargs.mode = FWCM_IDENTIFY;
- continue;
- } else if (res == ARG_ERROR)
- goto out;
- res = cmp_arg(argv, &i, "--brcmsmac", "-b", NULL);
- if (res == ARG_MATCH) {
- cmdargs.mode = FWCM_EXTRACT_BRCMSMAC;
- continue;
- } else if (res == ARG_ERROR)
- goto out;
- res = cmp_arg(argv, &i, "--unsupported", NULL, NULL);
- if (res == ARG_MATCH) {
- cmdargs.unsupported = 1;
- continue;
- } else if (res == ARG_ERROR)
- goto out;
- res = cmp_arg(argv, &i, "--target-dir", "-w", ¶m);
- if (res == ARG_MATCH) {
- cmdargs.target_dir = param;
- continue;
- } else if (res == ARG_ERROR)
- goto out;
- cmdargs.infile = argv[i];
- break;
- }
- if (!cmdargs.infile && cmdargs.mode != FWCM_LIST)
- goto out_usage;
- return 0;
- out_usage:
- print_usage(argc, argv);
- out:
- return -1;
- }
- int main(int argc, char *argv[])
- {
- FILE *fd;
- const struct file *file;
- const struct extract *extract;
- int err;
- const char *dir;
- cmdargs.target_dir = ".";
- err = parse_args(argc, argv);
- if (err == 1)
- return 0;
- else if (err != 0)
- return err;
- if (cmdargs.mode == FWCM_LIST) {
- print_supported_files();
- return 0;
- }
- fd = fopen(cmdargs.infile, "rb");
- if (!fd) {
- fprintf(stderr, "Cannot open input file %s\n", cmdargs.infile);
- return 2;
- }
- err = -1;
- file = find_file(fd);
- if (!file)
- goto out_close;
- if (file->flags & FW_FLAG_V4)
- dir = V4_FW_DIRNAME;
- else
- dir = V3_FW_DIRNAME;
- if (cmdargs.mode == FWCM_EXTRACT_BRCMSMAC) {
- brcmsmac_clear_file();
- extract = file->extract;
- while (extract->name) {
- brcmsmac_extract(fd, extract, file->flags);
- extract++;
- }
- brcmsmac_add_dummy_entries();
- } else {
- extract = file->extract;
- while (extract->name) {
- extract_or_identify(fd, dir, extract, file->flags);
- extract++;
- }
- }
- err = 0;
- out_close:
- fclose(fd);
- return err;
- }
|