1/*
2** Copyright 2007, The Android Open Source Project
3**
4** Licensed under the Apache License, Version 2.0 (the "License");
5** you may not use this file except in compliance with the License.
6** You may obtain a copy of the License at
7**
8**     http://www.apache.org/licenses/LICENSE-2.0
9**
10** Unless required by applicable law or agreed to in writing, software
11** distributed under the License is distributed on an "AS IS" BASIS,
12** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13** See the License for the specific language governing permissions and
14** limitations under the License.
15*/
16
17#include <stdio.h>
18#include <unistd.h>
19#include <stdlib.h>
20#include <string.h>
21#include <errno.h>
22#include <ctype.h>
23
24#define FAILIF(x, args...) do {          \
25    if (x) {                             \
26        fprintf(stderr, ##args);         \
27        exit(1);                         \
28    }                                    \
29} while(0)
30
31static void usage() {
32    printf("Usage: brfpatch INPUT OUTPUT\n"
33           "\n"
34           "\tGenerates bluetooth firmware\n"
35           "\n"
36           "INPUT: Bluetooth script in ASCII format.\n"
37           "       For TI BRF chips this can be generated from .bts files using the TI Bluetooth\n"
38           "       script pad to save as .txt. This txt file can be used as input.\n"
39           "       Alternately, run strings on the .bts and manually edit to change decimal\n"
40           "       arguments into hex of the appropriate number of octets.\n"
41           "       FORMAT: Send_HCI_xxxx OPCODE DATA1 DATA2 DATA3 ...\n"
42           "       where OPCODE, DATA1 etc are one of:\n"
43           "       0x12          (1 byte)\n"
44           "       0x1234        (2 byte)\n"
45           "       0x12345678    (4 byte)\n"
46           "       \"0123456789ABCDEF0123\"            (multibyte)\n"
47           "       \"01:23:45:67:89:AB:CD:EF:01:23\"   (multibyte)\n"
48           "\n"
49           "OUTPUT: Binary firmware\n"
50           "        FORMAT: 0x01 OPCODE DATA_LEN DATA\n");
51    exit(1);
52}
53
54static void dump_record(FILE *fpo, unsigned short opcode, unsigned char len,
55                        unsigned char *data) {
56
57    unsigned char prefix = 0x01;  // H4 UART command packet
58    fwrite(&prefix, 1, 1, fpo);
59    fwrite(&opcode, 2, 1, fpo);  // opcode
60    fwrite(&len, 1, 1, fpo);     // data length
61    fwrite(data, len, 1, fpo);   // data
62}
63
64// advance beyond next whitespace. Return -1 if end of string reached
65static int advance(char **buf) {
66    char *b = *buf;
67    while (*b && !isspace(*b))
68        b++;
69    while (*b && isspace(*b))
70        b++;
71    *buf = b;
72    if (!(*b))
73        return -1;
74    return 0;
75}
76
77static void process_line(FILE *file_out, char *buf, char *buffer) {
78    FAILIF(strncmp(buf, "Send_", 5) != 0, "Not expecting: %s\n", buffer);
79
80
81    unsigned int opcode;
82
83    FAILIF(advance(&buf), "Could not find opcode in: %s\n", buffer);
84    FAILIF(sscanf(buf, "0x%04x\n", &opcode) != 1,
85           "Could not find opcode in: %s\n", buffer);
86
87
88    unsigned char data[1024];
89    unsigned char *dp = data;
90
91    while (!advance(&buf)) {
92        switch (*buf) {
93        case '"':
94            buf++;
95            while (*buf != '"' && *buf != 0) {
96                FAILIF(dp > data + sizeof(data),
97                       "Too much data: %s\n", buffer);
98                FAILIF(sscanf(buf, "%02x", (unsigned int *)dp) != 1,
99                       "Error parsing (%d): %s\n", __LINE__, buffer);
100                dp++;
101                buf += 2;
102                if (*buf == ':')
103                    buf++;
104            }
105            break;
106        case '0':
107            buf++;
108            FAILIF(*buf != 'x', "Error parsing: %s\n", buffer);
109            buf++;
110
111            // find length of this piece of data
112            char *end = buf;
113            while (isalnum(*end))
114                end++;
115
116            // switch on length
117            switch ((unsigned int)end - (unsigned int)buf) {
118            case 2:
119                FAILIF(sscanf(buf, "%02x", (unsigned int *)dp) != 1,
120                       "Error parsing (%d): %s\n", __LINE__, buffer);
121                buf += 2;
122                dp += 1;
123                break;
124            case 4:
125                FAILIF(sscanf(buf, "%04x", (unsigned int *)dp) != 1,
126                       "Error parsing (%d): %s\n", __LINE__, buffer);
127                buf += 4;
128                dp += 2;
129                break;
130            case 6:
131                FAILIF(sscanf(buf, "%06x", (unsigned int *)dp) != 1,
132                       "Error parsing (%d): %s\n", __LINE__, buffer);
133                buf += 6;
134                dp += 3;
135                break;
136            case 8:
137                FAILIF(sscanf(buf, "%08x", (unsigned int *)dp) != 1,
138                       "Error parsing (%d): %s\n", __LINE__, buffer);
139                buf += 8;
140                dp += 4;
141                break;
142            case 16:
143                dp += 4;
144                FAILIF(sscanf(buf, "%08x", (unsigned int *)dp) != 1,
145                       "Error parsing (%d): %s\n", __LINE__, buffer);
146                buf += 8;
147                dp -= 4;
148                FAILIF(sscanf(buf, "%08x", (unsigned int *)dp) != 1,
149                       "Error parsing (%d): %s\n", __LINE__, buffer);
150                buf += 8;
151                dp += 8;
152                break;
153            default:
154                FAILIF(1, "Error parsing (%d): %s\n", __LINE__, buffer);
155            }
156            break;
157        default:
158            FAILIF(1, "Error parsing (%d): %s\n", __LINE__, buffer);
159        }
160    }
161
162    dump_record(file_out, opcode, dp - data, data);
163}
164
165
166int main(int argc, char **argv) {
167
168    if (argc != 3)
169        usage();
170
171    FILE *file_in = fopen(argv[1], "r");
172    FAILIF(!file_in, "Could not open %s: %s\n", argv[1], strerror(errno));
173
174    FILE *file_out = fopen(argv[2], "w+");
175    FAILIF(!file_out, "Could not open %s: %s\n", argv[2], strerror(errno));
176
177    char buffer[1024];
178    char *buf;
179
180    while (fgets(buffer, 1024, file_in) != NULL) {
181        buf = buffer;
182        while (*buf && isspace(*buf))
183            buf++;
184        switch (*buf) {
185        case 'S':
186            process_line(file_out, buf, buffer);
187            break;
188        case 'W':  // Wait_HCI_Command... meta-data, not needed
189        case '#':
190        case 0:
191            continue;
192        default:
193            FAILIF(1, "Don't know what to do with: %s\n", buffer);
194        }
195    }
196
197    fclose(file_in);
198    fclose(file_out);
199
200    return 0;
201}
202