Diff between b1004a59e949201ff6acc5d43938b7f68e144df3 and 434228aca40dd9be2a0a2ae73df66c7841602fa7

Changed Files

File Additions Deletions Status
tools/hex2hcd.c +243 -94 modified

Full Patch

diff --git a/tools/hex2hcd.c b/tools/hex2hcd.c
index d9b5d3b..e8224b5 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 <config.h>
 #endif
 
-#include <stdlib.h>
 #include <stdio.h>
 #include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/stat.h>
 
-#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] <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[] = {
+	{ "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 <input hex file> <output file>\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;
 }
-