add bcm43xx
[openwrt/svn-archive/archive.git] / openwrt / target / linux / package / bcm43xx-dscape / fwcutter / fwcutter.c
diff --git a/openwrt/target/linux/package/bcm43xx-dscape/fwcutter/fwcutter.c b/openwrt/target/linux/package/bcm43xx-dscape/fwcutter/fwcutter.c
new file mode 100644 (file)
index 0000000..ea98880
--- /dev/null
@@ -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", &param);
+               if (res == ARG_MATCH) {
+                       cmdargs.target_dir = param;
+                       continue;
+               } else if (res == ARG_ERROR)
+                       goto out;
+
+               res = cmp_arg(argv, &i, "--postfix", "-p", &param);
+               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;
+}