3 * BlueZ - Bluetooth protocol stack for Linux
5 * Copyright (C) 2012-2013 Intel Corporation
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
40 static ssize_t process_record(int fd, const char *line, uint16_t *upper_addr)
42 const char *ptr = line + 1;
51 fprintf(stderr, "Invalid record start code (%c)\n", line[0]);
57 fprintf(stderr, "Record information is too short\n");
61 buf = malloc((len / 2) + 3);
63 fprintf(stderr, "Failed to allocate memory for record data\n");
72 buf[3 + n] = strtol(str, NULL, 16);
74 if (*ptr == '\r' || *ptr == '\n')
80 sum = 0x100 - (sum & 0xff);
82 if (n < 4 || buf[3] + 4 != n) {
83 fprintf(stderr, "Record length is not matching data\n");
88 if (buf[3 + n] != sum) {
89 fprintf(stderr, "Checksum mismatch\n");
96 addr = (*upper_addr << 16) + (buf[4] << 8) + buf[5];
102 buf[3] = (addr & 0x000000ff);
103 buf[4] = (addr & 0x0000ff00) >> 8;
104 buf[5] = (addr & 0x00ff0000) >> 16;
105 buf[6] = (addr & 0xff000000) >> 24;
107 if (write(fd, buf, n + 3) < 0) {
108 perror("Failed to write data record");
123 if (write(fd, buf, 7) < 0) {
124 perror("Failed to write end record");
130 *upper_addr = (buf[7] << 8) + buf[8];
133 fprintf(stderr, "Unsupported record type (%02X)\n", buf[3]);
143 static void convert_file(const char *input_path, const char *output_path)
145 uint16_t upper_addr = 0x0000;
146 size_t line_size = 1024;
147 char line_buffer[line_size];
156 path = strdup(output_path);
158 perror("Failed to allocate string");
162 ptr = strrchr(input_path, '.');
164 path = malloc(ptr - input_path + 6);
166 perror("Failed to allocate string");
169 strncpy(path, input_path, ptr - input_path);
170 strcpy(path + (ptr - input_path), ".hcd");
172 if (asprintf(&path, "%s.hcd", input_path) < 0) {
173 perror("Failed to allocate string");
179 printf("Converting %s to %s\n", input_path, path);
181 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
186 perror("Failed to create output file");
190 if (stat(input_path, &st) < 0) {
191 fprintf(stderr, "Failed get file size\n");
196 if (st.st_size == 0) {
197 fprintf(stderr, "Empty file\n");
202 fp = fopen(input_path, "r");
204 fprintf(stderr, "Failed to open input file\n");
213 str = fgets(line_buffer, line_size - 1, fp);
217 len = process_record(fd, str, &upper_addr);
224 if (cur != st.st_size) {
225 fprintf(stderr, "Data length does not match file length\n");
241 struct ver_data *next;
244 static struct ver_data *ver_list = NULL;
246 static void ver_parse_file(const char *pathname)
248 struct ver_data *ver, *tmp, *prev;
249 char dummy1[5], dummy2[5];
251 if (strlen(pathname) < 7)
254 if (strncmp(pathname, "BCM", 3))
257 ver = malloc(sizeof(*ver));
261 memset(ver, 0, sizeof(*ver));
263 if (sscanf(pathname, "%[A-Z0-9]_%3c.%3c.%3c.%4c.%4c.hex",
264 ver->name, ver->major, ver->minor,
265 ver->build, dummy1, dummy2) != 6) {
266 printf("\t/* failed to parse %s */\n", pathname);
271 ver->num = atoi(ver->build) + (atoi(ver->minor) << 8) +
272 (atoi(ver->major) << 13);
279 for (tmp = ver_list, prev = NULL; tmp; prev = tmp, tmp = tmp->next) {
280 if (ver->num == tmp->num) {
285 if (ver->num < tmp->num) {
290 ver->next = ver_list;
300 static void ver_parse_entry(const char *pathname)
305 fd = open(pathname, O_RDONLY);
307 printf("\t/* failed to open %s */\n", pathname);
311 if (fstat(fd, &st) < 0) {
312 printf("\t/* failed to stat %s */\n", pathname);
316 if (S_ISREG(st.st_mode)) {
317 ver_parse_file(basename(pathname));
321 if (S_ISDIR(st.st_mode)) {
335 if (d->d_type == DT_REG)
336 ver_parse_file(d->d_name);
346 static void ver_print_table(int argc, char *argv[])
348 struct ver_data *ver;
350 printf("static const struct {\n");
351 printf("\tuint16_t ver;\n");
352 printf("\tconst char *str\n");
353 printf("} table[] = {\n");
358 for (i = 0; i < argc; i++)
359 ver_parse_entry(argv[i]);
361 ver_parse_entry(".");
363 for (ver = ver_list; ver; ) {
364 struct ver_data *tmp = ver;
366 printf("\t{ 0x%4.4x, \"%s\"\t},\t/* %s.%s.%s */\n",
368 ver->major, ver->minor, ver->build);
378 static void usage(void)
380 printf("Broadcom Bluetooth firmware converter\n"
382 printf("\thex2hcd [options] <file>\n");
384 "\t-o, --output <file> Provide firmware output file\n"
385 "\t-h, --help Show help options\n");
388 static const struct option main_options[] = {
389 { "table", no_argument, NULL, 'T' },
390 { "output", required_argument, NULL, 'o' },
391 { "version", no_argument, NULL, 'v' },
392 { "help", no_argument, NULL, 'h' },
396 int main(int argc, char *argv[])
398 const char *output_path = NULL;
399 bool print_table = false;
405 opt = getopt_long(argc, argv, "To:vh", main_options, NULL);
414 output_path = optarg;
417 printf("%s\n", VERSION);
428 ver_print_table(argc - optind, argv + optind);
432 if (argc - optind < 1) {
433 fprintf(stderr, "No input firmware files provided\n");
437 if (output_path && argc - optind > 1) {
438 fprintf(stderr, "Only single input firmware supported\n");
442 for (i = optind; i < argc; i++)
443 convert_file(argv[i], output_path);