From 434228aca40dd9be2a0a2ae73df66c7841602fa7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 23 Jan 2015 21:27:38 -0800 Subject: [PATCH] tools: Complete rewrite of hex2hcd with better command handling --- tools/hex2hcd.c | 337 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 243 insertions(+), 94 deletions(-) diff --git a/tools/hex2hcd.c b/tools/hex2hcd.c index d9b5d3b2c..e8224b543 100644 --- a/tools/hex2hcd.c +++ b/tools/hex2hcd.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2012 Canonical + * Copyright (C) 2012-2013 Intel Corporation * * * This program is free software; you can redistribute it and/or modify @@ -25,119 +25,268 @@ #include #endif -#include #include #include +#include +#include +#include +#include +#include +#include +#include -#define RBUF_SIZE 640 - -static unsigned int asc_to_int(char a) +static ssize_t process_record(int fd, const char *line, uint16_t *upper_addr) { - if (a >= 'A') - return (a - 'A') + 10; - else - return a - '0'; -} + const char *ptr = line + 1; + char str[3]; + size_t len; + uint8_t *buf; + uint32_t addr; + uint8_t sum = 0; + int n = 0; -static unsigned int hex_to_int(const char *h) -{ - return asc_to_int(*h) * 0x10 + asc_to_int(*(h + 1)); -} + if (line[0] != ':') { + fprintf(stderr, "Invalid record start code (%c)\n", line[0]); + return -EINVAL; + } -static unsigned int lhex_to_int(const char *h) -{ - return hex_to_int(h) * 0x100 + hex_to_int(h + 2); + 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]; + printf("address 0x%08x: %d\n", addr, n); + + 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 int check_sum(const char *str, int len) +static void convert_file(const char *input_path, const char *output_path) { - unsigned int sum, cal; - int i; - sum = hex_to_int(str + len - 2); - for (cal = 0, i = 1; i < len - 2; i += 2) - cal += hex_to_int(str + i); - cal = 0x100 - (cal & 0xFF); - return sum == cal; + 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); } -static int check_hex_line(const char *str, unsigned int len) +static void usage(void) { - if ((str[0] != ':') || (len < 11) || !check_sum(str, len) || - (hex_to_int(str + 1) * 2 + 11 != len)) - return 0; - return 1; + printf("Broadcom Bluetooth firmware converter\n" + "Usage:\n"); + printf("\thex2hcd [options] \n"); + printf("Options:\n" + "\t-o, --output Provide firmware output file\n" + "\t-h, --help Show help options\n"); } +static const struct option main_options[] = { + { "output", required_argument, NULL, 'o' }, + { "version", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { } +}; + int main(int argc, char *argv[]) { - unsigned int i, addr = 0; - FILE *ifp, *ofp; - char *rbuf; - ssize_t len; - size_t buflen; - - if (argc != 3) { - printf("Usage: %s \n", argv[0]); - return 0; - } - - ifp = fopen(argv[1], "r"); - ofp = fopen(argv[2], "w"); - if ((ifp == NULL) || (ofp == NULL)) { - puts("failed to open file."); - return -EIO; - } - - rbuf = NULL; - while ((len = getline(&rbuf, &buflen, ifp)) > 0) { - int type; - char obuf[7]; - unsigned int dest_addr; - while ((rbuf[len - 1] == '\r') || (rbuf[len - 1] == '\n')) - len--; - printf("%d, %s\n", (int)len, rbuf); - if (!check_hex_line(rbuf, len)) + const char *output_path = NULL; + int i; + + for (;;) { + int opt; + + opt = getopt_long(argc, argv, "o:vh", main_options, NULL); + if (opt < 0) + break; + + switch (opt) { + case 'o': + output_path = optarg; break; - type = hex_to_int(rbuf + 7); - switch (type) { - case 4: - addr = lhex_to_int(rbuf + 9) * 0x10000; - printf("bump addr to 0x%08X\n", addr); - break; - case 0: - dest_addr = addr + lhex_to_int(rbuf + 3); - obuf[0] = 0x4c; - obuf[1] = 0xfc; - obuf[2] = hex_to_int(rbuf + 1) + 4; - obuf[3] = dest_addr; - obuf[4] = dest_addr >> 8; - obuf[5] = dest_addr >> 16; - obuf[6] = dest_addr >> 24; - if (fwrite(obuf, 7, 1, ofp) != 1) - goto output_err; - for (i = 0; i < hex_to_int(rbuf + 1); i++) { - obuf[0] = hex_to_int(rbuf + 9 + i * 2); - if (fwrite(obuf, 1, 1, ofp) != 1) - goto output_err; - } - break; - case 1: - if (fwrite("\x4e\xfc\x04\xff\xff\xff\xff", 7, 1, ofp) != 1) - goto output_err; - goto end; - default: - return -EINVAL; + case 'v': + printf("%s\n", VERSION); + return EXIT_SUCCESS; + case 'h': + usage(); + return EXIT_SUCCESS; + default: + return EXIT_FAILURE; } } - puts("hex file formatting error"); - return -EINVAL; + 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; + } -output_err: - puts("error on writing output file"); - return -EIO; + for (i = optind; i < argc; i++) + convert_file(argv[i], output_path); -end: - return 0; + return EXIT_SUCCESS; } - -- 2.47.3