fwcutter/make: Avoid _DEFAULT_SOURCE warning
[b43-tools.git] / fwcutter / fwcutter.c
index 79e351f6dd4c3ca5ff297bd301f7c497b614117b..294502b8394ba9f06cbab3ef2e284f53fd782750 100644 (file)
@@ -2,7 +2,7 @@
  * firmware cutter for broadcom 43xx wireless driver files
  * 
  * Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
- *               2005-2007 Michael Buesch <mb@bu3sch.de>
+ *               2005-2014 Michael Buesch <m@bues.ch>
  *              2005 Alex Beregszaszi
  *              2007 Johannes Berg <johannes@sipsolutions.net>
  *
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#ifdef __DragonFly__
+#if defined(__DragonFly__) || defined(__FreeBSD__)
 #include <sys/endian.h>
+#elif defined(__APPLE__)
+#include <libkern/OSByteOrder.h>
 #else
 #include <byteswap.h>
 #endif
@@ -48,7 +50,7 @@
 #include "fwcutter.h"
 #include "fwcutter_list.h"
 
-#ifdef __DragonFly__
+#if defined(__DragonFly__) || defined(__FreeBSD__)
 #define V3_FW_DIRNAME  "v3"
 #define V4_FW_DIRNAME  "v4"
 #else
@@ -101,6 +103,25 @@ 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)
 {
@@ -226,6 +247,27 @@ static void swap_endianness_iv(struct iv *iv)
        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,
@@ -328,8 +370,8 @@ static void write_file(const char *name, uint8_t *buf, uint32_t len,
        fclose(f);
 }
 
-static void extract_or_identify(FILE *f, const struct extract *extract,
-                               uint32_t flags)
+static void extract_or_identify(FILE *f, const char *dir,
+                               const struct extract *extract, uint32_t flags)
 {
        uint8_t *buf;
        size_t data_length;
@@ -339,20 +381,11 @@ static void extract_or_identify(FILE *f, const struct extract *extract,
        memset(&hdr, 0, sizeof(hdr));
        hdr.ver = FW_HDR_VER;
 
-       if (fseek(f, extract->offset, SEEK_SET)) {
-               perror("failed to seek on file");
-               exit(2);
-       }
+       printf("%s %s/%s.fw\n",
+              cmdargs.mode == FWCM_IDENTIFY ? "Contains" : "Extracting",
+              dir, extract->name);
 
-       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);
-       }
+       buf = read_object(f, extract);
 
        switch (extract->type) {
        case EXT_UCODE_3:
@@ -390,12 +423,217 @@ static void extract_or_identify(FILE *f, const struct extract *extract,
                exit(255);
        }
 
-       if (cmdargs.mode == FWCM_EXTRACT)
+       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");
@@ -425,8 +663,6 @@ static void print_file(const struct file *file)
        printf("%s\t", file->ucode_version);
        if (strlen(file->ucode_version) < 8) printf("\t");
 
-       printf("%s\t", file->id);
-
        printf("%s\n", file->md5);
 }
 
@@ -437,15 +673,10 @@ static void print_supported_files(void)
        print_banner();
        printf("\nExtracting firmware is possible "
               "from these binary driver files.\n"
-              "The <ID> column shows the unique identifier string "
-              "for your firmware.\nYou must select the firmware with the "
-              "same ID as printed by the kernel driver on modprobe.\n"
-              "Note that only recent drivers print such a message on modprobe.\n"
               "Please read http://linuxwireless.org/en/users/Drivers/b43#devicefirmware\n\n");
        printf("<driver>\t"
               "<filename>\t\t"
               "<microcode>\t"
-              "<ID>\t"
               "<MD5 checksum>\n\n");
        /* print for legacy driver first */
        for (i = 0; i < ARRAY_SIZE(files); i++)
@@ -481,7 +712,6 @@ static const struct file *find_file(FILE *fd)
                if (file_ok(&files[i]) &&
                    strcasecmp(md5sig, files[i].md5) == 0) {
                        printf("This file is recognised as:\n");
-                       printf("  ID         :  %s\n", files[i].id);
                        printf("  filename   :  %s\n", files[i].name);
                        printf("  version    :  %s\n", files[i].ucode_version);
                        printf("  MD5        :  %s\n", files[i].md5);
@@ -505,6 +735,8 @@ static void print_usage(int argc, char *argv[])
               "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   "
@@ -616,6 +848,13 @@ static int parse_args(int argc, char *argv[])
                } 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;
@@ -680,13 +919,20 @@ int main(int argc, char *argv[])
        else
                dir = V3_FW_DIRNAME;
 
-       extract = file->extract;
-       while (extract->name) {
-               printf("%s %s/%s.fw\n",
-                      cmdargs.mode == FWCM_IDENTIFY ? "Contains" : "Extracting",
-                      dir, extract->name);
-               extract_or_identify(fd, extract, file->flags);
-               extract++;
+       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;