| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2012-2013 Intel Corporation
- *
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #define _GNU_SOURCE
- #include <stdio.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <string.h>
- #include <getopt.h>
- #include <dirent.h>
- #include <stdint.h>
- #include <stdlib.h>
- #include <stdbool.h>
- #include <sys/stat.h>
- static ssize_t process_record(int fd, const char *line, uint16_t *upper_addr)
- {
- const char *ptr = line + 1;
- char str[3];
- size_t len;
- uint8_t *buf;
- uint32_t addr;
- uint8_t sum = 0;
- int n = 0;
- if (line[0] != ':') {
- fprintf(stderr, "Invalid record start code (%c)\n", line[0]);
- return -EINVAL;
- }
- len = strlen(line);
- if (len < 11) {
- fprintf(stderr, "Record information is too short\n");
- return -EILSEQ;
- }
- buf = malloc((len / 2) + 3);
- if (!buf) {
- fprintf(stderr, "Failed to allocate memory for record data\n");
- return -ENOMEM;
- }
- while (1) {
- str[0] = *ptr++;
- str[1] = *ptr++;
- str[2] = '\0';
- buf[3 + n] = strtol(str, NULL, 16);
- if (*ptr == '\r' || *ptr == '\n')
- break;
- sum += buf[3 + n++];
- }
- sum = 0x100 - (sum & 0xff);
- if (n < 4 || buf[3] + 4 != n) {
- fprintf(stderr, "Record length is not matching data\n");
- free(buf);
- return -EILSEQ;
- }
- if (buf[3 + n] != sum) {
- fprintf(stderr, "Checksum mismatch\n");
- free(buf);
- return -EILSEQ;
- }
- switch (buf[6]) {
- case 0x00:
- addr = (*upper_addr << 16) + (buf[4] << 8) + buf[5];
- buf[0] = 0x4c;
- buf[1] = 0xfc;
- buf[2] = n;
- buf[3] = (addr & 0x000000ff);
- buf[4] = (addr & 0x0000ff00) >> 8;
- buf[5] = (addr & 0x00ff0000) >> 16;
- buf[6] = (addr & 0xff000000) >> 24;
- if (write(fd, buf, n + 3) < 0) {
- perror("Failed to write data record");
- free(buf);
- return -errno;
- }
- break;
- case 0x01:
- buf[0] = 0x4e;
- buf[1] = 0xfc;
- buf[2] = 0x04;
- buf[3] = 0xff;
- buf[4] = 0xff;
- buf[5] = 0xff;
- buf[6] = 0xff;
- if (write(fd, buf, 7) < 0) {
- perror("Failed to write end record");
- free(buf);
- return -errno;
- }
- break;
- case 0x04:
- *upper_addr = (buf[7] << 8) + buf[8];
- break;
- default:
- fprintf(stderr, "Unsupported record type (%02X)\n", buf[3]);
- free(buf);
- return -EILSEQ;
- }
- free(buf);
- return len;
- }
- static void convert_file(const char *input_path, const char *output_path)
- {
- uint16_t upper_addr = 0x0000;
- size_t line_size = 1024;
- char line_buffer[line_size];
- char *path;
- const char *ptr;
- FILE *fp;
- struct stat st;
- off_t cur = 0;
- int fd;
- if (output_path) {
- path = strdup(output_path);
- if (!path) {
- perror("Failed to allocate string");
- return;
- }
- } else {
- ptr = strrchr(input_path, '.');
- if (ptr) {
- path = malloc(ptr - input_path + 6);
- if (!path) {
- perror("Failed to allocate string");
- return;
- }
- strncpy(path, input_path, ptr - input_path);
- strcpy(path + (ptr - input_path), ".hcd");
- } else {
- if (asprintf(&path, "%s.hcd", input_path) < 0) {
- perror("Failed to allocate string");
- return;
- }
- }
- }
- printf("Converting %s to %s\n", input_path, path);
- fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
- free(path);
- if (fd < 0) {
- perror("Failed to create output file");
- return;
- }
- if (stat(input_path, &st) < 0) {
- fprintf(stderr, "Failed get file size\n");
- close(fd);
- return;
- }
- if (st.st_size == 0) {
- fprintf(stderr, "Empty file\n");
- close(fd);
- return;
- }
- fp = fopen(input_path, "r");
- if (!fp) {
- fprintf(stderr, "Failed to open input file\n");
- close(fd);
- return;
- }
- while (1) {
- char *str;
- ssize_t len;
- str = fgets(line_buffer, line_size - 1, fp);
- if (!str)
- break;
- len = process_record(fd, str, &upper_addr);
- if (len < 0)
- goto done;
- cur += len;
- }
- if (cur != st.st_size) {
- fprintf(stderr, "Data length does not match file length\n");
- goto done;
- }
- done:
- fclose(fp);
- close(fd);
- }
- struct ver_data {
- uint16_t num;
- char name[20];
- char major[4];
- char minor[4];
- char build[4];
- struct ver_data *next;
- };
- static struct ver_data *ver_list = NULL;
- static void ver_parse_file(const char *pathname)
- {
- struct ver_data *ver, *tmp, *prev;
- char dummy1[5], dummy2[5];
- if (strlen(pathname) < 7)
- return;
- if (strncmp(pathname, "BCM", 3))
- return;
- ver = malloc(sizeof(*ver));
- if (!ver)
- return;
- memset(ver, 0, sizeof(*ver));
- if (sscanf(pathname, "%[A-Z0-9]_%3c.%3c.%3c.%4c.%4c.hex",
- ver->name, ver->major, ver->minor,
- ver->build, dummy1, dummy2) != 6) {
- printf("\t/* failed to parse %s */\n", pathname);
- free(ver);
- return;
- }
- ver->num = atoi(ver->build) + (atoi(ver->minor) << 8) +
- (atoi(ver->major) << 13);
- if (!ver_list) {
- ver_list = ver;
- return;
- }
- for (tmp = ver_list, prev = NULL; tmp; prev = tmp, tmp = tmp->next) {
- if (ver->num == tmp->num) {
- free(ver);
- return;
- }
- if (ver->num < tmp->num) {
- if (prev) {
- prev->next = ver;
- ver->next = tmp;
- } else {
- ver->next = ver_list;
- ver_list = ver;
- }
- return;
- }
- }
- prev->next = ver;
- }
- static void ver_parse_entry(const char *pathname)
- {
- struct stat st;
- int fd;
- fd = open(pathname, O_RDONLY);
- if (fd < 0) {
- printf("\t/* failed to open %s */\n", pathname);
- return;
- }
- if (fstat(fd, &st) < 0) {
- printf("\t/* failed to stat %s */\n", pathname);
- goto done;
- }
- if (S_ISREG(st.st_mode)) {
- ver_parse_file(basename(pathname));
- goto done;
- }
- if (S_ISDIR(st.st_mode)) {
- DIR *dir;
- dir = fdopendir(fd);
- if (!dir)
- goto done;
- while (1) {
- struct dirent *d;
- d = readdir(dir);
- if (!d)
- break;
- if (d->d_type == DT_REG)
- ver_parse_file(d->d_name);
- }
- closedir(dir);
- }
- done:
- close(fd);
- }
- static void ver_print_table(int argc, char *argv[])
- {
- struct ver_data *ver;
- printf("static const struct {\n");
- printf("\tuint16_t ver;\n");
- printf("\tconst char *str\n");
- printf("} table[] = {\n");
- if (argc > 0) {
- int i;
- for (i = 0; i < argc; i++)
- ver_parse_entry(argv[i]);
- } else
- ver_parse_entry(".");
- for (ver = ver_list; ver; ) {
- struct ver_data *tmp = ver;
- printf("\t{ 0x%4.4x, \"%s\"\t},\t/* %s.%s.%s */\n",
- ver->num, ver->name,
- ver->major, ver->minor, ver->build);
- ver = ver->next;
- free(tmp);
- }
- printf(" { }\n");
- printf("};\n");
- }
- static void usage(void)
- {
- printf("Broadcom Bluetooth firmware converter\n"
- "Usage:\n");
- printf("\thex2hcd [options] <file>\n");
- printf("Options:\n"
- "\t-o, --output <file> Provide firmware output file\n"
- "\t-h, --help Show help options\n");
- }
- static const struct option main_options[] = {
- { "table", no_argument, NULL, 'T' },
- { "output", required_argument, NULL, 'o' },
- { "version", no_argument, NULL, 'v' },
- { "help", no_argument, NULL, 'h' },
- { }
- };
- int main(int argc, char *argv[])
- {
- const char *output_path = NULL;
- bool print_table = false;
- int i;
- for (;;) {
- int opt;
- opt = getopt_long(argc, argv, "To:vh", main_options, NULL);
- if (opt < 0)
- break;
- switch (opt) {
- case 'T':
- print_table = true;
- break;
- case 'o':
- output_path = optarg;
- break;
- case 'v':
- printf("%s\n", VERSION);
- return EXIT_SUCCESS;
- case 'h':
- usage();
- return EXIT_SUCCESS;
- default:
- return EXIT_FAILURE;
- }
- }
- if (print_table) {
- ver_print_table(argc - optind, argv + optind);
- return EXIT_SUCCESS;
- }
- if (argc - optind < 1) {
- fprintf(stderr, "No input firmware files provided\n");
- return EXIT_FAILURE;
- }
- if (output_path && argc - optind > 1) {
- fprintf(stderr, "Only single input firmware supported\n");
- return EXIT_FAILURE;
- }
- for (i = optind; i < argc; i++)
- convert_file(argv[i], output_path);
- return EXIT_SUCCESS;
- }
|