| /* |
| ** Copyright 2007, The Android Open Source Project |
| ** |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| */ |
| |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <ctype.h> |
| |
| #define FAILIF(x, args...) do { \ |
| if (x) { \ |
| fprintf(stderr, ##args); \ |
| exit(1); \ |
| } \ |
| } while(0) |
| |
| static void usage() { |
| printf("Usage: brfpatch INPUT OUTPUT\n" |
| "\n" |
| "\tGenerates bluetooth firmware\n" |
| "\n" |
| "INPUT: Bluetooth script in ASCII format.\n" |
| " For TI BRF chips this can be generated from .bts files using the TI Bluetooth\n" |
| " script pad to save as .txt. This txt file can be used as input.\n" |
| " Alternately, run strings on the .bts and manually edit to change decimal\n" |
| " arguments into hex of the appropriate number of octets.\n" |
| " FORMAT: Send_HCI_xxxx OPCODE DATA1 DATA2 DATA3 ...\n" |
| " where OPCODE, DATA1 etc are one of:\n" |
| " 0x12 (1 byte)\n" |
| " 0x1234 (2 byte)\n" |
| " 0x12345678 (4 byte)\n" |
| " \"0123456789ABCDEF0123\" (multibyte)\n" |
| " \"01:23:45:67:89:AB:CD:EF:01:23\" (multibyte)\n" |
| "\n" |
| "OUTPUT: Binary firmware\n" |
| " FORMAT: 0x01 OPCODE DATA_LEN DATA\n"); |
| exit(1); |
| } |
| |
| static void dump_record(FILE *fpo, unsigned short opcode, unsigned char len, |
| unsigned char *data) { |
| |
| unsigned char prefix = 0x01; // H4 UART command packet |
| fwrite(&prefix, 1, 1, fpo); |
| fwrite(&opcode, 2, 1, fpo); // opcode |
| fwrite(&len, 1, 1, fpo); // data length |
| fwrite(data, len, 1, fpo); // data |
| } |
| |
| // advance beyond next whitespace. Return -1 if end of string reached |
| static int advance(char **buf) { |
| char *b = *buf; |
| while (*b && !isspace(*b)) |
| b++; |
| while (*b && isspace(*b)) |
| b++; |
| *buf = b; |
| if (!(*b)) |
| return -1; |
| return 0; |
| } |
| |
| static void process_line(FILE *file_out, char *buf, char *buffer) { |
| FAILIF(strncmp(buf, "Send_", 5) != 0, "Not expecting: %s\n", buffer); |
| |
| |
| unsigned int opcode; |
| |
| FAILIF(advance(&buf), "Could not find opcode in: %s\n", buffer); |
| FAILIF(sscanf(buf, "0x%04x\n", &opcode) != 1, |
| "Could not find opcode in: %s\n", buffer); |
| |
| |
| unsigned char data[1024]; |
| unsigned char *dp = data; |
| |
| while (!advance(&buf)) { |
| switch (*buf) { |
| case '"': |
| buf++; |
| while (*buf != '"' && *buf != 0) { |
| FAILIF(dp > data + sizeof(data), |
| "Too much data: %s\n", buffer); |
| FAILIF(sscanf(buf, "%02x", (unsigned int *)dp) != 1, |
| "Error parsing (%d): %s\n", __LINE__, buffer); |
| dp++; |
| buf += 2; |
| if (*buf == ':') |
| buf++; |
| } |
| break; |
| case '0': |
| buf++; |
| FAILIF(*buf != 'x', "Error parsing: %s\n", buffer); |
| buf++; |
| |
| // find length of this piece of data |
| char *end = buf; |
| while (isalnum(*end)) |
| end++; |
| |
| // switch on length |
| switch ((unsigned int)end - (unsigned int)buf) { |
| case 2: |
| FAILIF(sscanf(buf, "%02x", (unsigned int *)dp) != 1, |
| "Error parsing (%d): %s\n", __LINE__, buffer); |
| buf += 2; |
| dp += 1; |
| break; |
| case 4: |
| FAILIF(sscanf(buf, "%04x", (unsigned int *)dp) != 1, |
| "Error parsing (%d): %s\n", __LINE__, buffer); |
| buf += 4; |
| dp += 2; |
| break; |
| case 6: |
| FAILIF(sscanf(buf, "%06x", (unsigned int *)dp) != 1, |
| "Error parsing (%d): %s\n", __LINE__, buffer); |
| buf += 6; |
| dp += 3; |
| break; |
| case 8: |
| FAILIF(sscanf(buf, "%08x", (unsigned int *)dp) != 1, |
| "Error parsing (%d): %s\n", __LINE__, buffer); |
| buf += 8; |
| dp += 4; |
| break; |
| case 16: |
| dp += 4; |
| FAILIF(sscanf(buf, "%08x", (unsigned int *)dp) != 1, |
| "Error parsing (%d): %s\n", __LINE__, buffer); |
| buf += 8; |
| dp -= 4; |
| FAILIF(sscanf(buf, "%08x", (unsigned int *)dp) != 1, |
| "Error parsing (%d): %s\n", __LINE__, buffer); |
| buf += 8; |
| dp += 8; |
| break; |
| default: |
| FAILIF(1, "Error parsing (%d): %s\n", __LINE__, buffer); |
| } |
| break; |
| default: |
| FAILIF(1, "Error parsing (%d): %s\n", __LINE__, buffer); |
| } |
| } |
| |
| dump_record(file_out, opcode, dp - data, data); |
| } |
| |
| |
| int main(int argc, char **argv) { |
| |
| if (argc != 3) |
| usage(); |
| |
| FILE *file_in = fopen(argv[1], "r"); |
| FAILIF(!file_in, "Could not open %s: %s\n", argv[1], strerror(errno)); |
| |
| FILE *file_out = fopen(argv[2], "w+"); |
| FAILIF(!file_out, "Could not open %s: %s\n", argv[2], strerror(errno)); |
| |
| char buffer[1024]; |
| char *buf; |
| |
| while (fgets(buffer, 1024, file_in) != NULL) { |
| buf = buffer; |
| while (*buf && isspace(*buf)) |
| buf++; |
| switch (*buf) { |
| case 'S': |
| process_line(file_out, buf, buffer); |
| break; |
| case 'W': // Wait_HCI_Command... meta-data, not needed |
| case '#': |
| case 0: |
| continue; |
| default: |
| FAILIF(1, "Don't know what to do with: %s\n", buffer); |
| } |
| } |
| |
| fclose(file_in); |
| fclose(file_out); |
| |
| return 0; |
| } |