* 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
#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
static struct cmdline_args cmdargs;
-/* Convert a CPU-endian 16bit integer to Big-Endian */
-static be16_t to_be16(uint16_t v)
+/* check whether file will be listed/extracted from */
+static int file_ok(const struct file *f)
{
- uint8_t ret[2];
-
- ret[0] = (v & 0xFF00) >> 8;
- ret[1] = (v & 0x00FF);
-
- return *((be16_t *)ret);
+ return !(f->flags & FW_FLAG_UNSUPPORTED) || cmdargs.unsupported;
}
-#if 0
/* Convert a Big-Endian 16bit integer to CPU-endian */
static uint16_t from_be16(be16_t v)
{
return ret;
}
-#endif
-/* Convert a CPU-endian 32bit integer to Big-Endian */
-static be32_t to_be32(uint32_t v)
+/* Convert a CPU-endian 16bit integer to Big-Endian */
+static be16_t to_be16(uint16_t v)
{
- uint8_t ret[4];
-
- ret[0] = (v & 0xFF000000) >> 24;
- ret[1] = (v & 0x00FF0000) >> 16;
- ret[2] = (v & 0x0000FF00) >> 8;
- ret[3] = (v & 0x000000FF);
-
- return *((be32_t *)ret);
+ return (be16_t)from_be16((be16_t)v);
}
/* Convert a Big-Endian 32bit integer to CPU-endian */
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)
{
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,
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;
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:
exit(255);
}
- if (!cmdargs.identify_only)
+ 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");
print_banner();
printf("\nExtracting firmware is possible "
- "from these binary driver files:\n\n");
+ "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 < FILES; i++)
- if (!(files[i].flags & FW_FLAG_V4))
+ 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 < FILES; i++)
- if (files[i].flags & FW_FLAG_V4)
+ 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");
}
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) {
+ 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);
static void print_usage(int argc, char *argv[])
{
print_banner();
- printf("\nUsage: %s [OPTION] [driver.sys]\n", argv[0]);
+ 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 "
"Print b43-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",
+ 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]);
}
if (param) {
/* Skip the parameter on the next iteration. */
(*pos)++;
- if (*param == 0) {
+ if (!*param) {
printf("%s needs a parameter\n", arg);
return ARG_ERROR;
}
if (argc < 2)
goto out_usage;
for (i = 1; i < argc; i++) {
- res = cmp_arg(argv, &i, "--list", "-l", 0);
+ res = cmp_arg(argv, &i, "--list", "-l", NULL);
if (res == ARG_MATCH) {
- print_supported_files();
- return 1;
+ cmdargs.mode = FWCM_LIST;
+ continue;
} else if (res == ARG_ERROR)
goto out;
- res = cmp_arg(argv, &i, "--version", "-v", 0);
+ 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", 0);
+ 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", 0);
+ 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.identify_only = 1;
+ cmdargs.unsupported = 1;
continue;
} else if (res == ARG_ERROR)
goto out;
break;
}
- if (!cmdargs.infile)
+ if (!cmdargs.infile && cmdargs.mode != FWCM_LIST)
goto out_usage;
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);
else
dir = V3_FW_DIRNAME;
- extract = file->extract;
- while (extract->name) {
- printf("%s %s/%s.fw\n",
- cmdargs.identify_only ? "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;