b43-fwcutter: Add support for generating a fw for brcmsmac
authorHauke Mehrtens <hauke@hauke-m.de>
Sun, 23 Sep 2012 18:39:22 +0000 (20:39 +0200)
committerMichael Buesch <m@bues.ch>
Mon, 24 Sep 2012 20:59:44 +0000 (22:59 +0200)
The normal firmware for brcmsmac is available in the linux-firmware git
repository, but this firmware does not work on a bcm4716 devices with a
ieee80211 core rev 16. When using a firmware extracted from the
proprietary Broadcom driver the device works with brcmsmac.

I tested a firmware version 666.2 extracted from the proprietary driver
with brcmsmac on a BCM4716 and a BCM43224 and haven't seen any
problems. The interface between the firmware and the driver changed
some times, brcmsmac only support firmware version 598 and newer.

For some entries in the firmware used by brcmsmac, fwcutter has to be
more extended, so we are adding dummy entires for them in
brcmsmac_add_dummy_entries(). Currently these entries are not used by
brcmsmac, but it is checked if they are in the archive.

I would still appreciate if Broadcom could release a firmware with
support for the ieee80211 core found in the bcm4716.

Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
Signed-off-by: Michael Buesch <m@bues.ch>
fwcutter/fwcutter.c
fwcutter/fwcutter.h

index 20337589192761ae17b351f9bbd4d488d242f85e..6f5788c535aaf584f34cf20ed20e7d281d9689b8 100644 (file)
@@ -101,6 +101,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 +245,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 +368,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 +379,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 +421,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_mimo", 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_mimo", 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");
@@ -497,6 +733,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   "
@@ -608,6 +846,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;
@@ -672,13 +917,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;
index c6d3c3fc4a5ea3e2a0c336664f99a26f6b61ce92..a441322aded7b302f11bd8c1ebbf8446415acdcb 100644 (file)
@@ -15,6 +15,8 @@
 typedef uint16_t be16_t; /* Big-endian 16bit */
 typedef uint32_t be32_t; /* Big-endian 32bit */
 
+typedef uint32_t le32_t; /* Little-endian 32bit */
+
 #if defined(__DragonFly__) || defined(__FreeBSD__)
 #define bswap_16       bswap16
 #define bswap_32       bswap32
@@ -25,9 +27,10 @@ typedef uint32_t be32_t; /* Big-endian 32bit */
 #define ARG_ERROR      -1
 
 enum fwcutter_mode {
-       FWCM_EXTRACT = 0,       /* default */
+       FWCM_EXTRACT_B43 = 0,   /* default */
        FWCM_LIST,
        FWCM_IDENTIFY,
+       FWCM_EXTRACT_BRCMSMAC,
 };
 
 struct cmdline_args {
@@ -104,5 +107,31 @@ struct b43_iv {
 #define FW_IV_OFFSET_MASK      0x7FFF
 #define FW_IV_32BIT            0x8000
 
+/* header format for brcmsmac firmware */
+struct firmware_hdr {
+       le32_t offset;
+       le32_t len;
+       le32_t idx;
+};
+
+/* numbers of firmware types for brcmsmac firmware */
+enum firmware_brcmsmac {
+       D11UCODE_NAMETAG_START = 0,
+       D11LCN0BSINITVALS24,
+       D11LCN0INITVALS24,
+       D11LCN1BSINITVALS24,
+       D11LCN1INITVALS24,
+       D11LCN2BSINITVALS24,
+       D11LCN2INITVALS24,
+       D11N0ABSINITVALS16,
+       D11N0BSINITVALS16,
+       D11N0INITVALS16,
+       D11UCODE_OVERSIGHT16_MIMO,
+       D11UCODE_OVERSIGHT16_MIMOSZ,
+       D11UCODE_OVERSIGHT24_LCN,
+       D11UCODE_OVERSIGHT24_LCNSZ,
+       D11UCODE_OVERSIGHT_BOMMAJOR,
+       D11UCODE_OVERSIGHT_BOMMINOR
+};
 
 #endif /* _FWCUTTER_H_ */