tizen 2.3.1 release
[framework/connectivity/bluez.git] / tools / hex2hcd.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2012-2013  Intel Corporation
6  *
7  *
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.
12  *
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.
17  *
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
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <getopt.h>
34 #include <dirent.h>
35 #include <stdint.h>
36 #include <stdlib.h>
37 #include <stdbool.h>
38 #include <sys/stat.h>
39
40 static ssize_t process_record(int fd, const char *line, uint16_t *upper_addr)
41 {
42         const char *ptr = line + 1;
43         char str[3];
44         size_t len;
45         uint8_t *buf;
46         uint32_t addr;
47         uint8_t sum = 0;
48         int n = 0;
49
50         if (line[0] != ':') {
51                 fprintf(stderr, "Invalid record start code (%c)\n", line[0]);
52                 return -EINVAL;
53         }
54
55         len = strlen(line);
56         if (len < 11) {
57                 fprintf(stderr, "Record information is too short\n");
58                 return -EILSEQ;
59         }
60
61         buf = malloc((len / 2) + 3);
62         if (!buf) {
63                 fprintf(stderr, "Failed to allocate memory for record data\n");
64                 return -ENOMEM;
65         }
66
67         while (1) {
68                 str[0] = *ptr++;
69                 str[1] = *ptr++;
70                 str[2] = '\0';
71
72                 buf[3 + n] = strtol(str, NULL, 16);
73
74                 if (*ptr == '\r' || *ptr == '\n')
75                         break;
76
77                 sum += buf[3 + n++];
78         }
79
80         sum = 0x100 - (sum & 0xff);
81
82         if (n < 4 || buf[3] + 4 != n) {
83                 fprintf(stderr, "Record length is not matching data\n");
84                 free(buf);
85                 return -EILSEQ;
86         }
87
88         if (buf[3 + n] != sum) {
89                 fprintf(stderr, "Checksum mismatch\n");
90                 free(buf);
91                 return -EILSEQ;
92         }
93
94         switch (buf[6]) {
95         case 0x00:
96                 addr = (*upper_addr << 16) + (buf[4] << 8) + buf[5];
97
98                 buf[0] = 0x4c;
99                 buf[1] = 0xfc;
100                 buf[2] = n;
101
102                 buf[3] = (addr & 0x000000ff);
103                 buf[4] = (addr & 0x0000ff00) >> 8;
104                 buf[5] = (addr & 0x00ff0000) >> 16;
105                 buf[6] = (addr & 0xff000000) >> 24;
106
107                 if (write(fd, buf, n + 3) < 0) {
108                         perror("Failed to write data record");
109                         free(buf);
110                         return -errno;
111                 }
112                 break;
113         case 0x01:
114                 buf[0] = 0x4e;
115                 buf[1] = 0xfc;
116                 buf[2] = 0x04;
117
118                 buf[3] = 0xff;
119                 buf[4] = 0xff;
120                 buf[5] = 0xff;
121                 buf[6] = 0xff;
122
123                 if (write(fd, buf, 7) < 0) {
124                         perror("Failed to write end record");
125                         free(buf);
126                         return -errno;
127                 }
128                 break;
129         case 0x04:
130                 *upper_addr = (buf[7] << 8) + buf[8];
131                 break;
132         default:
133                 fprintf(stderr, "Unsupported record type (%02X)\n", buf[3]);
134                 free(buf);
135                 return -EILSEQ;
136         }
137
138         free(buf);
139
140         return len;
141 }
142
143 static void convert_file(const char *input_path, const char *output_path)
144 {
145         uint16_t upper_addr = 0x0000;
146         size_t line_size = 1024;
147         char line_buffer[line_size];
148         char *path;
149         const char *ptr;
150         FILE *fp;
151         struct stat st;
152         off_t cur = 0;
153         int fd;
154
155         if (output_path) {
156                 path = strdup(output_path);
157                 if (!path) {
158                         perror("Failed to allocate string");
159                         return;
160                 }
161         } else {
162                 ptr = strrchr(input_path, '.');
163                 if (ptr) {
164                         path = malloc(ptr - input_path + 6);
165                         if (!path) {
166                                 perror("Failed to allocate string");
167                                 return;
168                         }
169                         strncpy(path, input_path, ptr - input_path);
170                         strcpy(path + (ptr - input_path), ".hcd");
171                 } else {
172                         if (asprintf(&path, "%s.hcd", input_path) < 0) {
173                                 perror("Failed to allocate string");
174                                 return;
175                         }
176                 }
177         }
178
179         printf("Converting %s to %s\n", input_path, path);
180
181         fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
182
183         free(path);
184
185         if (fd < 0) {
186                 perror("Failed to create output file");
187                 return;
188         }
189
190         if (stat(input_path, &st) < 0) {
191                 fprintf(stderr, "Failed get file size\n");
192                 close(fd);
193                 return;
194         }
195
196         if (st.st_size == 0) {
197                 fprintf(stderr, "Empty file\n");
198                 close(fd);
199                 return;
200         }
201
202         fp = fopen(input_path, "r");
203         if (!fp) {
204                 fprintf(stderr, "Failed to open input file\n");
205                 close(fd);
206                 return;
207         }
208
209         while (1) {
210                 char *str;
211                 ssize_t len;
212
213                 str = fgets(line_buffer, line_size - 1, fp);
214                 if (!str)
215                         break;
216
217                 len = process_record(fd, str, &upper_addr);
218                 if (len < 0)
219                         goto done;
220
221                 cur += len;
222         }
223
224         if (cur != st.st_size) {
225                 fprintf(stderr, "Data length does not match file length\n");
226                 goto done;
227         }
228
229 done:
230         fclose(fp);
231
232         close(fd);
233 }
234
235 struct ver_data {
236         uint16_t num;
237         char name[20];
238         char major[4];
239         char minor[4];
240         char build[4];
241         struct ver_data *next;
242 };
243
244 static struct ver_data *ver_list = NULL;
245
246 static void ver_parse_file(const char *pathname)
247 {
248         struct ver_data *ver, *tmp, *prev;
249         char dummy1[5], dummy2[5];
250
251         if (strlen(pathname) < 7)
252                 return;
253
254         if (strncmp(pathname, "BCM", 3))
255                 return;
256
257         ver = malloc(sizeof(*ver));
258         if (!ver)
259                 return;
260
261         memset(ver, 0, sizeof(*ver));
262
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);
267                 free(ver);
268                 return;
269         }
270
271         ver->num = atoi(ver->build) + (atoi(ver->minor) << 8) +
272                                                 (atoi(ver->major) << 13);
273
274         if (!ver_list) {
275                 ver_list = ver;
276                 return;
277         }
278
279         for (tmp = ver_list, prev = NULL; tmp; prev = tmp, tmp = tmp->next) {
280                 if (ver->num == tmp->num) {
281                         free(ver);
282                         return;
283                 }
284
285                 if (ver->num < tmp->num) {
286                         if (prev) {
287                                 prev->next = ver;
288                                 ver->next = tmp;
289                         } else {
290                                 ver->next = ver_list;
291                                 ver_list = ver;
292                         }
293                         return;
294                 }
295         }
296
297         prev->next = ver;
298 }
299
300 static void ver_parse_entry(const char *pathname)
301 {
302         struct stat st;
303         int fd;
304
305         fd = open(pathname, O_RDONLY);
306         if (fd < 0) {
307                 printf("\t/* failed to open %s */\n", pathname);
308                 return;
309         }
310
311         if (fstat(fd, &st) < 0) {
312                 printf("\t/* failed to stat %s */\n", pathname);
313                 goto done;
314         }
315
316         if (S_ISREG(st.st_mode)) {
317                 ver_parse_file(basename(pathname));
318                 goto done;
319         }
320
321         if (S_ISDIR(st.st_mode)) {
322                 DIR *dir;
323
324                 dir = fdopendir(fd);
325                 if (!dir)
326                         goto done;
327
328                 while (1) {
329                         struct dirent *d;
330
331                         d = readdir(dir);
332                         if (!d)
333                                 break;
334
335                         if (d->d_type == DT_REG)
336                                 ver_parse_file(d->d_name);
337                 }
338
339                 closedir(dir);
340         }
341
342 done:
343         close(fd);
344 }
345
346 static void ver_print_table(int argc, char *argv[])
347 {
348         struct ver_data *ver;
349
350         printf("static const struct {\n");
351         printf("\tuint16_t ver;\n");
352         printf("\tconst char *str\n");
353         printf("} table[] = {\n");
354
355         if (argc > 0) {
356                 int i;
357
358                 for (i = 0; i < argc; i++)
359                         ver_parse_entry(argv[i]);
360         } else
361                 ver_parse_entry(".");
362
363         for (ver = ver_list; ver; ) {
364                 struct ver_data *tmp = ver;
365
366                 printf("\t{ 0x%4.4x, \"%s\"\t},\t/* %s.%s.%s */\n",
367                                         ver->num, ver->name,
368                                         ver->major, ver->minor, ver->build);
369
370                 ver = ver->next;
371                 free(tmp);
372         }
373
374         printf("        { }\n");
375         printf("};\n");
376 }
377
378 static void usage(void)
379 {
380         printf("Broadcom Bluetooth firmware converter\n"
381                 "Usage:\n");
382         printf("\thex2hcd [options] <file>\n");
383         printf("Options:\n"
384                 "\t-o, --output <file>    Provide firmware output file\n"
385                 "\t-h, --help             Show help options\n");
386 }
387
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' },
393         { }
394 };
395
396 int main(int argc, char *argv[])
397 {
398         const char *output_path = NULL;
399         bool print_table = false;
400         int i;
401
402         for (;;) {
403                 int opt;
404
405                 opt = getopt_long(argc, argv, "To:vh", main_options, NULL);
406                 if (opt < 0)
407                         break;
408
409                 switch (opt) {
410                 case 'T':
411                         print_table = true;
412                         break;
413                 case 'o':
414                         output_path = optarg;
415                         break;
416                 case 'v':
417                         printf("%s\n", VERSION);
418                         return EXIT_SUCCESS;
419                 case 'h':
420                         usage();
421                         return EXIT_SUCCESS;
422                 default:
423                         return EXIT_FAILURE;
424                 }
425         }
426
427         if (print_table) {
428                 ver_print_table(argc - optind, argv + optind);
429                 return EXIT_SUCCESS;
430         }
431
432         if (argc - optind < 1) {
433                 fprintf(stderr, "No input firmware files provided\n");
434                 return EXIT_FAILURE;
435         }
436
437         if (output_path && argc - optind > 1) {
438                 fprintf(stderr, "Only single input firmware supported\n");
439                 return EXIT_FAILURE;
440         }
441
442         for (i = optind; i < argc; i++)
443                 convert_file(argv[i], output_path);
444
445         return EXIT_SUCCESS;
446 }