diff options
Diffstat (limited to 'target/linux/package/bcm43xx-standalone/fwcutter/fwcutter.c')
-rw-r--r-- | target/linux/package/bcm43xx-standalone/fwcutter/fwcutter.c | 562 |
1 files changed, 562 insertions, 0 deletions
diff --git a/target/linux/package/bcm43xx-standalone/fwcutter/fwcutter.c b/target/linux/package/bcm43xx-standalone/fwcutter/fwcutter.c new file mode 100644 index 000000000..ea98880df --- /dev/null +++ b/target/linux/package/bcm43xx-standalone/fwcutter/fwcutter.c @@ -0,0 +1,562 @@ +/* + * firmware cutter for broadcom 43xx wireless driver files + * + * Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, + * 2005 Michael Buesch <mbuesch@freenet.de> + * 2005 Alex Beregszaszi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + + +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <stdio.h> + +typedef unsigned char byte; + +#define DRIVER_UNSUPPORTED 0x01 /* no support for this driver file */ +#define BYTE_ORDER_BIG_ENDIAN 0x02 /* ppc driver files */ +#define BYTE_ORDER_LITTLE_ENDIAN 0x04 /* x86, mips driver files */ + +#define MISSING_INITVAL_08 0x10 /* initval 8 is missing */ +#define MISSING_INITVAL_80211_A 0x20 /* initvals 3,7,9,10 (802.11a cards) are empty */ + +#define FIRMWARE_UCODE_OFFSET 100 +#define FIRMWARE_UNDEFINED 0 +#define FIRMWARE_PCM_4 4 +#define FIRMWARE_PCM_5 5 +#define FIRMWARE_UCODE_2 (FIRMWARE_UCODE_OFFSET + 2) +#define FIRMWARE_UCODE_4 (FIRMWARE_UCODE_OFFSET + 4) +#define FIRMWARE_UCODE_5 (FIRMWARE_UCODE_OFFSET + 5) +#define FIRMWARE_UCODE_11 (FIRMWARE_UCODE_OFFSET + 11) + + +#define fwcutter_stringify_1(x) #x +#define fwcutter_stringify(x) fwcutter_stringify_1(x) +#define FWCUTTER_VERSION fwcutter_stringify(FWCUTTER_VERSION_) + +#include "md5.h" +#include "fwcutter_list.h" + + +struct cmdline_args { + const char *infile; + const char *postfix; + const char *target_dir; + int identify_only; +}; + +static struct cmdline_args cmdargs; +int big_endian_cpu; + + +static void write_little_endian(FILE *f, byte *buffer, int len) +{ + byte swapbuf[4]; + + while (len > 0) { + swapbuf[0] = buffer[3]; swapbuf[1] = buffer[2]; + swapbuf[2] = buffer[1]; swapbuf[3] = buffer[0]; + fwrite(swapbuf, 4, 1, f); + buffer = buffer + 4; + len = len - 4; + } +} + +static void write_big_endian(FILE *f, byte *buffer, int len) +{ + while (len > 0) { + fwrite(buffer, 4, 1, f); + buffer = buffer + 4; + len = len - 4; + } +} + +static void write_fw(const char *outfilename, uint8_t flags, byte *data, int len) +{ + FILE* fw; + char outfile[2048]; + + snprintf(outfile, sizeof(outfile), + "%s/%s", cmdargs.target_dir, outfilename); + + fw = fopen(outfile, "w"); + if (!fw) { + perror(outfile); + exit(1); + } + + if (flags & BYTE_ORDER_LITTLE_ENDIAN) + write_little_endian(fw, data, len); + else if (flags & BYTE_ORDER_BIG_ENDIAN) + write_big_endian(fw, data, len); + else + printf("unknown byteorder...\n"); + + fflush(fw); + fclose(fw); +} + +static void write_iv(uint8_t flags, byte *data) +{ + FILE* fw; + char ivfilename[2048]; + int i; + + for (i = 1; i <= 10; i++) { + + if ((flags & MISSING_INITVAL_08) && (i==8)) { + printf("*****: Sorry, initval08 is not available in driver file \"%s\".\n", cmdargs.infile); + printf("*****: Extracting firmware from an old driver is bad. Choose a more recent one.\n"); + printf("*****: Luckily bcm43xx driver doesn't include initval08 uploads at the moment.\n"); + printf("*****: But this can be added in the future...\n"); + i++; + } + + snprintf(ivfilename, sizeof(ivfilename), + "%s/bcm43xx_initval%02d%s.fw", + cmdargs.target_dir, i, cmdargs.postfix); + fw = fopen(ivfilename, "w"); + + if (!fw) { + perror(ivfilename); + exit(1); + } + + printf("extracting bcm43xx_initval%02d%s.fw ...\n", i, cmdargs.postfix); + + while (1) { + + if ((data[0]==0xff) && (data[1]==0xff) && (data[2]==0x00) && (data[3]==0x00)) { + data = data + 8; + break; + } + + if (flags & BYTE_ORDER_LITTLE_ENDIAN) + fprintf(fw, "%c%c%c%c%c%c%c%c", + data[1], data[0], /* offset */ + data[3], data[2], /* size */ + data[7], data[6], data[5], data[4]); /* value */ + else if (flags & BYTE_ORDER_BIG_ENDIAN) + fprintf(fw, "%c%c%c%c%c%c%c%c", + data[0], data[1], /* offset */ + data[2], data[3], /* size */ + data[4], data[5], data[6], data[7]); /* value */ + else { + printf("unknown byteorder...\n"); + exit(1); + } + + data = data + 8; + } + fflush(fw); + fclose(fw); + } +} + +static byte* read_file(const char* filename) +{ + FILE* file; + long len; + byte* data; + + file = fopen(filename, "rb"); + if (!file) { + perror(filename); + exit(1); + } + if (fseek(file, 0, SEEK_END)) { + perror("cannot seek"); + exit(1); + } + len = ftell(file); + fseek(file, 0, SEEK_SET); + data = malloc(len); + if (!data) { + fputs("out of memory\n", stderr); + exit(1); + } + if (fread(data, 1, len, file) != len) { + perror("cannot read"); + exit(1); + } + fclose(file); + return data; +} + +static void extract_fw(uint8_t fwtype, uint8_t flags, uint32_t pos, uint32_t length) +{ + byte* filedata; + char outfile[1024]; + + switch (fwtype) { + case FIRMWARE_UCODE_2: + case FIRMWARE_UCODE_4: + case FIRMWARE_UCODE_5: + case FIRMWARE_UCODE_11: + snprintf(outfile, sizeof(outfile), "bcm43xx_microcode%i%s.fw", + fwtype - FIRMWARE_UCODE_OFFSET, cmdargs.postfix); + break; + case FIRMWARE_PCM_4: + case FIRMWARE_PCM_5: + snprintf(outfile, sizeof(outfile), "bcm43xx_pcm%i%s.fw", + fwtype, cmdargs.postfix); + break; + default: + snprintf(outfile, sizeof(outfile), "bcm43xx_unknown.fw"); + } + + if (length > 0) { + printf("extracting %s ...\n", outfile); + filedata = read_file(cmdargs.infile); + write_fw(outfile, flags, filedata + pos, length); + free(filedata); + } else { + printf("*****: Sorry, it's not posible to extract \"%s\".\n", outfile); + printf("*****: Extracting firmware from an old driver is bad. Choose a more recent one.\n"); + + switch (fwtype) { + case FIRMWARE_UCODE_2: + printf("*****: bcm43xx driver will not work with with core revision 2.\n"); + break; + case FIRMWARE_UCODE_4: + printf("*****: bcm43xx driver will not work with with core revision 4.\n"); + break; + case FIRMWARE_UCODE_5: + printf("*****: bcm43xx driver will not work with with core revision 5 or higher.\n"); + break; + case FIRMWARE_UCODE_11: + printf("*****: Luckily bcm43xx driver doesn't include microcode11 uploads at the moment.\n"); + printf("*****: But this can be added in the future...\n"); + break; + case FIRMWARE_PCM_4: + printf("*****: bcm43xx driver will not work with with core revision 4 or smaller.\n"); + break; + case FIRMWARE_PCM_5: + printf("*****: bcm43xx driver will not work with with core revision 5 or higher.\n"); + break; + } + } +} + +static void extract_iv(uint8_t flags, uint32_t pos) +{ + byte* filedata; + + if (pos > 0) { + filedata = read_file(cmdargs.infile); + write_iv(flags, filedata + pos); + free(filedata); + } +} + +static void print_banner(void) +{ + printf("fwcutter " FWCUTTER_VERSION "\n"); +} + +static void print_file(const struct file *file) +{ + printf("%s\t", file->name); + if (strlen(file->name) < 8) + printf("\t"); + + printf("%s\t", file->version); + if (strlen(file->version) < 8) + printf("\t"); + if (strlen(file->version) < 16) + printf("\t"); + + if (!(file->flags & DRIVER_UNSUPPORTED)) { + if (file->flags & MISSING_INITVAL_80211_A) + printf("b/g "); + else + printf("a/b/g"); + } + + printf(" %s", file->md5); + printf("\n"); +} + +static void print_supported_files(void) +{ + int i; + + print_banner(); + printf("\nExtracting firmware is possible from these binary driver files:\n\n"); + printf("<filename>\t<version>\t <802.11><MD5 checksum>\n\n"); + for (i = 0; i < FILES; i++) { + if (files[i].flags & DRIVER_UNSUPPORTED) + continue; + print_file(&files[i]); + } + printf("\n\nExtracting firmware is IMPOSSIBLE from these binary driver files:\n\n"); + printf("<filename>\t<version>\t <MD5 checksum>\n\n"); + for (i = 0; i < FILES; i++) { + if (!(files[i].flags & DRIVER_UNSUPPORTED)) + continue; + print_file(&files[i]); + } +} + +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 < FILES; ++i) { + if (strcasecmp(md5sig, files[i].md5) == 0) { + if (files[i].flags & DRIVER_UNSUPPORTED) { + printf("Extracting firmware from this file is IMPOSSIBLE. (too old)\n"); + return 0; + } + printf("fwcutter can cut the firmware out of %s\n", cmdargs.infile); + printf(" filename : %s\n", files[i].name); + printf(" version : %s\n", files[i].version); + printf(" MD5 : %s\n\n", files[i].md5); + if (files[i].flags & MISSING_INITVAL_80211_A) { + printf("WARNING! This firmware doesn't include support for 802.11a cards.\n"); + printf("WARNING! Use this firmware only for 802.11b/g cards.\n\n"); + } + return &(files[i]); + } + } + printf("Sorry, the input file is either wrong or not supported by fwcutter.\n"); + printf("I can't find the MD5sum %s :(\n", md5sig); + + return 0; +} + +static void get_endianess(void) +{ + const unsigned char x[] = { 0xde, 0xad, 0xbe, 0xef, }; + const uint32_t *p = (uint32_t *)x; + + if (*p == 0xdeadbeef) { + big_endian_cpu = 1; + } else if (*p == 0xefbeadde) { + big_endian_cpu = 0; + } else { + printf("Confused: NUXI endian machine??\n"); + exit(-1); + } +} + +static void print_usage(int argc, char *argv[]) +{ + print_banner(); + printf("\nUsage: %s [OPTION] [driver.sys]\n", argv[0]); + printf(" -l|--list List supported driver versions\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(" -p|--postfix \".FOO\" Postfix for firmware filenames (.FOO.fw)\n"); + printf(" -v|--version Print fwcutter version\n"); + printf(" -h|--help Print this help\n"); + printf("\nExample: %s bcmwl5.sys\n" + " to extract the firmware blobs from bcmwl5.sys\n", argv[0]); +} + +#define ARG_MATCH 0 +#define ARG_NOMATCH 1 +#define ARG_ERROR -1 + +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 == 0) { + 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", 0); + if (res == ARG_MATCH) { + print_supported_files(); + return 1; + } else if (res == ARG_ERROR) + goto out; + + res = cmp_arg(argv, &i, "--version", "-v", 0); + if (res == ARG_MATCH) { + print_banner(); + return 1; + } else if (res == ARG_ERROR) + goto out; + + res = cmp_arg(argv, &i, "--help", "-h", 0); + if (res == ARG_MATCH) + goto out_usage; + else if (res == ARG_ERROR) + goto out; + + res = cmp_arg(argv, &i, "--identify", "-i", 0); + if (res == ARG_MATCH) { + cmdargs.identify_only = 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; + + res = cmp_arg(argv, &i, "--postfix", "-p", ¶m); + if (res == ARG_MATCH) { + cmdargs.postfix = param; + continue; + } else if (res == ARG_ERROR) + goto out; + + cmdargs.infile = argv[i]; + break; + } + + if (!cmdargs.infile) + 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; + int err; + + get_endianess(); + + cmdargs.target_dir = "."; + cmdargs.postfix = ""; + err = parse_args(argc, argv); + if (err == 1) + return 0; + else if (err != 0) + return err; + + 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 (cmdargs.identify_only) { + err = 0; + goto out_close; + } + + extract_fw(FIRMWARE_UCODE_2, file->flags, file->uc2_pos, file->uc2_length); + extract_fw(FIRMWARE_UCODE_4, file->flags, file->uc4_pos, file->uc4_length); + extract_fw(FIRMWARE_UCODE_5, file->flags, file->uc5_pos, file->uc5_length); + extract_fw(FIRMWARE_UCODE_11, file->flags, file->uc11_pos, file->uc11_length); + extract_fw(FIRMWARE_PCM_4, file->flags, file->pcm4_pos, file->pcm4_length); + extract_fw(FIRMWARE_PCM_5, file->flags, file->pcm5_pos, file->pcm5_length); + extract_iv(file->flags, file->iv_pos); + + err = 0; +out_close: + fclose(fd); + + return err; +} |