/* ** 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 8: FAILIF(sscanf(buf, "%08x", (unsigned int *)dp) != 1, "Error parsing (%d): %s\n", __LINE__, buffer); buf += 8; dp += 4; 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; }