brcm_patchram_plus.c revision 1725ef80c89ba54e0b246d5d4d31e217496d01b5
1/** 2 * brcm_patchram_plus.c 3 * 4 * Copyright (C) 2009 Broadcom Corporation. 5 * 6 * This software is licensed under the terms of the GNU General Public License, 7 * version 2, as published by the Free Software Foundation (the "GPL"), and may 8 * be copied, distributed, and modified under those terms. 9 * 10 * This program is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. 13 * 14 * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php 15 * or by writing to the Free Software Foundation, Inc., 16 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 17 */ 18 19 20/***************************************************************************** 21** 22** Name: brcm_patchram_plus.c 23** 24** Description: This program downloads a patchram files in the HCD format 25** to Broadcom Bluetooth based silicon and combo chips and 26** and other utility functions. 27** 28** It can be invoked from the command line in the form 29** <-d> to print a debug log 30** <--patchram patchram_file> 31** <--baudrate baud_rate> 32** <--bd_addr bd_address> 33** <--enable_lpm> 34** <--enable_hci> 35** <--pcm_role master|slave> 36** uart_device_name 37** 38** For example: 39** 40** brcm_patchram_plus -d --patchram \ 41** BCM2045B2_002.002.011.0348.0349.hcd /dev/ttyHS0 42** 43** It will return 0 for success and a number greater than 0 44** for any errors. 45** 46** For Android, this program invoked using a 47** "system(2)" call from the beginning of the bt_enable 48** function inside the file 49** system/bluetooth/bluedroid/bluetooth.c. 50** 51** If the Android system property "ro.bt.bcm_bdaddr_path" is 52** set, then the bd_addr will be read from this path. 53** This is overridden by --bd_addr on the command line. 54** 55******************************************************************************/ 56 57// TODO: Integrate BCM support into Bluez hciattach 58 59#include <stdio.h> 60#include <getopt.h> 61#include <errno.h> 62 63#include <sys/types.h> 64#include <sys/stat.h> 65#include <fcntl.h> 66 67#include <stdlib.h> 68 69#ifdef ANDROID 70#include <termios.h> 71#else 72#include <sys/termios.h> 73#endif 74 75#include <string.h> 76#include <signal.h> 77 78#include <cutils/properties.h> 79 80#ifdef ANDROID 81#define LOG_TAG "brcm_patchram_plus" 82#include <cutils/log.h> 83#undef printf 84#define printf LOGD 85#undef fprintf 86#define fprintf(x, ...) \ 87 { if(x==stderr) LOGE(__VA_ARGS__); else fprintf(x, __VA_ARGS__); } 88 89#endif //ANDROID 90 91#ifndef N_HCI 92#define N_HCI 15 93#endif 94 95#define HCIUARTSETPROTO _IOW('U', 200, int) 96#define HCIUARTGETPROTO _IOR('U', 201, int) 97#define HCIUARTGETDEVICE _IOR('U', 202, int) 98 99#define HCI_UART_H4 0 100#define HCI_UART_BCSP 1 101#define HCI_UART_3WIRE 2 102#define HCI_UART_H4DS 3 103#define HCI_UART_LL 4 104 105 106int uart_fd = -1; 107int hcdfile_fd = -1; 108int termios_baudrate = 0; 109int bdaddr_flag = 0; 110int enable_lpm = 0; 111int enable_hci = 0; 112int pcm_slave = 0; 113int pcm_master = 0; 114int debug = 0; 115 116struct termios termios; 117unsigned char buffer[1024]; 118 119unsigned char hci_reset[] = { 0x01, 0x03, 0x0c, 0x00 }; 120 121unsigned char hci_download_minidriver[] = { 0x01, 0x2e, 0xfc, 0x00 }; 122 123unsigned char hci_update_baud_rate[] = { 0x01, 0x18, 0xfc, 0x06, 0x00, 0x00, 124 0x00, 0x00, 0x00, 0x00 }; 125 126unsigned char hci_write_bd_addr[] = { 0x01, 0x01, 0xfc, 0x06, 127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 128 129unsigned char hci_write_sleep_mode[] = { 0x01, 0x27, 0xfc, 0x0c, 130 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 131 0x00, 0x00 }; 132 133// HCI_COMMAND_PKT = 0x01 134// unpacked ogf/ocf: 0x3F 0x1C 135// ... ((ocf & 0x03ff)|(ogf << 10)) => 0xFC1C (0x1C 0xFC) 136// packet data len: 0x5 137// Data for the Write_SCO_PCM_Int_Param Message: 138// 00 (Use PCM) 139// 0x (02== 512 KHz bit clock, 03==1024 KHz bit clock, 04==2048 KHz) 140// 01 (Long FS) 141// 00 (Slave frame sync) 142// 00 (Slave bit clock) 143unsigned char hci_write_pcm_slave_mode[] = 144 { 0x01, 0x1C, 0xFC, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00 }; 145unsigned char hci_write_pcm_master_mode[] = 146 { 0x01, 0x1C, 0xFC, 0x05, 0x00, 0x02, 0x00, 0x01, 0x01 }; 147 148int 149parse_patchram(char *optarg) 150{ 151 char *p; 152 153 if (!(p = strrchr(optarg, '.'))) { 154 fprintf(stderr, "file %s not an HCD file\n", optarg); 155 exit(3); 156 } 157 158 p++; 159 160 if (strcasecmp("hcd", p) != 0) { 161 fprintf(stderr, "file %s not an HCD file\n", optarg); 162 exit(4); 163 } 164 165 if ((hcdfile_fd = open(optarg, O_RDONLY)) == -1) { 166 fprintf(stderr, "file %s could not be opened, error %d\n", optarg, errno); 167 exit(5); 168 } 169 170 return(0); 171} 172 173void 174BRCM_encode_baud_rate(uint baud_rate, unsigned char *encoded_baud) 175{ 176 if(baud_rate == 0 || encoded_baud == NULL) { 177 fprintf(stderr, "Baudrate not supported!"); 178 return; 179 } 180 181 encoded_baud[3] = (unsigned char)(baud_rate >> 24); 182 encoded_baud[2] = (unsigned char)(baud_rate >> 16); 183 encoded_baud[1] = (unsigned char)(baud_rate >> 8); 184 encoded_baud[0] = (unsigned char)(baud_rate & 0xFF); 185} 186 187typedef struct { 188 int baud_rate; 189 int termios_value; 190} tBaudRates; 191 192tBaudRates baud_rates[] = { 193 { 115200, B115200 }, 194 { 230400, B230400 }, 195 { 460800, B460800 }, 196 { 500000, B500000 }, 197 { 576000, B576000 }, 198 { 921600, B921600 }, 199 { 1000000, B1000000 }, 200 { 1152000, B1152000 }, 201 { 1500000, B1500000 }, 202 { 2000000, B2000000 }, 203 { 2500000, B2500000 }, 204 { 3000000, B3000000 }, 205#ifndef __CYGWIN__ 206 { 3500000, B3500000 }, 207 { 4000000, B4000000 } 208#endif 209}; 210 211int 212validate_baudrate(int baud_rate, int *value) 213{ 214 unsigned int i; 215 216 for (i = 0; i < (sizeof(baud_rates) / sizeof(tBaudRates)); i++) { 217 if (baud_rates[i].baud_rate == baud_rate) { 218 *value = baud_rates[i].termios_value; 219 return(1); 220 } 221 } 222 223 return(0); 224} 225 226int 227parse_baudrate(char *optarg) 228{ 229 int baudrate = atoi(optarg); 230 231 if (validate_baudrate(baudrate, &termios_baudrate)) { 232 BRCM_encode_baud_rate(baudrate, &hci_update_baud_rate[6]); 233 } 234 235 return(0); 236} 237 238int 239parse_bdaddr(char *optarg) 240{ 241 int bd_addr[6]; 242 int i; 243 244 sscanf(optarg, "%02X:%02X:%02X:%02X:%02X:%02X", 245 &bd_addr[5], &bd_addr[4], &bd_addr[3], 246 &bd_addr[2], &bd_addr[1], &bd_addr[0]); 247 248 for (i = 0; i < 6; i++) { 249 hci_write_bd_addr[4 + i] = bd_addr[i]; 250 } 251 252 bdaddr_flag = 1; 253 254 return(0); 255} 256 257int 258parse_enable_lpm(char *optarg) 259{ 260 enable_lpm = 1; 261 return(0); 262} 263 264int 265parse_enable_hci(char *optarg) 266{ 267 enable_hci = 1; 268 return(0); 269} 270 271int 272parse_pcm_role(char *optarg) { 273 if(!strcmp(optarg, "master")) { 274 pcm_master = 1; 275 } else if (!strcmp(optarg, "slave")) { 276 pcm_slave = 1; 277 } else { 278 printf("Unknown PCM Role received: %s\n", optarg); 279 } 280 if (pcm_master && pcm_slave) { 281 fprintf(stderr, "Illegal command line- pcm role master && slave\n"); 282 exit(6); 283 } 284 return(0); 285} 286 287int 288parse_cmd_line(int argc, char **argv) 289{ 290 int c; 291 int digit_optind = 0; 292 293 typedef int (*PFI)(); 294 295 PFI parse_param[] = { parse_patchram, parse_baudrate, 296 parse_bdaddr, parse_enable_lpm, parse_enable_hci, 297 parse_pcm_role}; 298 299 while (1) 300 { 301 int this_option_optind = optind ? optind : 1; 302 int option_index = 0; 303 304 static struct option long_options[] = { 305 {"patchram", 1, 0, 0}, 306 {"baudrate", 1, 0, 0}, 307 {"bd_addr", 1, 0, 0}, 308 {"enable_lpm", 0, 0, 0}, 309 {"enable_hci", 0, 0, 0}, 310 {"pcm_role", 1, 0, 0}, 311 {0, 0, 0, 0} 312 }; 313 314 c = getopt_long_only (argc, argv, "d", long_options, &option_index); 315 316 if (c == -1) { 317 break; 318 } 319 320 switch (c) { 321 case 0: 322 printf ("option %s", long_options[option_index].name); 323 324 if (optarg) { 325 printf (" with arg %s", optarg); 326 } 327 328 printf ("\n"); 329 330 (*parse_param[option_index])(optarg); 331 break; 332 333 case 'd': 334 debug = 1; 335 break; 336 337 case '?': 338 //nobreak 339 default: 340 341 printf("Usage %s:\n", argv[0]); 342 printf("\t<-d> to print a debug log\n"); 343 printf("\t<--patchram patchram_file>\n"); 344 printf("\t<--baudrate baud_rate>\n"); 345 printf("\t<--bd_addr bd_address>\n"); 346 printf("\t<--enable_lpm>\n"); 347 printf("\t<--enable_hci>\n"); 348 printf("\t<--pcm_role slave|master>\n"); 349 printf("\tuart_device_name\n"); 350 break; 351 352 } 353 } 354 355 if (optind < argc) { 356 if (optind < argc) { 357 printf ("%s ", argv[optind]); 358 359 if ((uart_fd = open(argv[optind], O_RDWR | O_NOCTTY)) == -1) { 360 fprintf(stderr, "port %s could not be opened, error %d\n", argv[2], errno); 361 } 362 } 363 364 printf ("\n"); 365 } 366 367 return(0); 368} 369 370void 371init_uart() 372{ 373 tcflush(uart_fd, TCIOFLUSH); 374 tcgetattr(uart_fd, &termios); 375 376#ifndef __CYGWIN__ 377 cfmakeraw(&termios); 378#else 379 termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP 380 | INLCR | IGNCR | ICRNL | IXON); 381 termios.c_oflag &= ~OPOST; 382 termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); 383 termios.c_cflag &= ~(CSIZE | PARENB); 384 termios.c_cflag |= CS8; 385#endif 386 387 termios.c_cflag |= CRTSCTS; 388 tcsetattr(uart_fd, TCSANOW, &termios); 389 tcflush(uart_fd, TCIOFLUSH); 390 tcsetattr(uart_fd, TCSANOW, &termios); 391 tcflush(uart_fd, TCIOFLUSH); 392 tcflush(uart_fd, TCIOFLUSH); 393 cfsetospeed(&termios, B115200); 394 cfsetispeed(&termios, B115200); 395 tcsetattr(uart_fd, TCSANOW, &termios); 396} 397 398void 399dump(unsigned char *out, int len) 400{ 401 int i; 402 403 for (i = 0; i < len; i++) { 404 if (i && !(i % 16)) { 405 fprintf(stderr, "\n"); 406 } 407 408 fprintf(stderr, "%02x ", out[i]); 409 } 410 411 fprintf(stderr, "\n"); 412} 413 414void 415read_event(int fd, unsigned char *buffer) 416{ 417 int i = 0; 418 int len = 3; 419 int count; 420 421 while ((count = read(fd, &buffer[i], len)) < len) { 422 i += count; 423 len -= count; 424 } 425 426 i += count; 427 len = buffer[2]; 428 429 while ((count = read(fd, &buffer[i], len)) < len) { 430 i += count; 431 len -= count; 432 } 433 434 if (debug) { 435 count += i; 436 437 fprintf(stderr, "received %d\n", count); 438 dump(buffer, count); 439 } 440} 441 442void 443hci_send_cmd(unsigned char *buf, int len) 444{ 445 if (debug) { 446 fprintf(stderr, "writing\n"); 447 dump(buf, len); 448 } 449 450 write(uart_fd, buf, len); 451} 452 453void 454expired(int sig) 455{ 456 hci_send_cmd(hci_reset, sizeof(hci_reset)); 457 alarm(4); 458} 459 460void 461proc_reset() 462{ 463 signal(SIGALRM, expired); 464 465 466 hci_send_cmd(hci_reset, sizeof(hci_reset)); 467 468 alarm(4); 469 470 read_event(uart_fd, buffer); 471 472 alarm(0); 473} 474 475void 476proc_patchram() 477{ 478 int len; 479 480 hci_send_cmd(hci_download_minidriver, sizeof(hci_download_minidriver)); 481 482 read_event(uart_fd, buffer); 483 484 read(uart_fd, &buffer[0], 2); 485 486 usleep(50000); 487 488 while (read(hcdfile_fd, &buffer[1], 3)) { 489 buffer[0] = 0x01; 490 491 len = buffer[3]; 492 493 read(hcdfile_fd, &buffer[4], len); 494 495 hci_send_cmd(buffer, len + 4); 496 497 read_event(uart_fd, buffer); 498 } 499 500 proc_reset(); 501} 502 503void 504proc_baudrate() 505{ 506 hci_send_cmd(hci_update_baud_rate, sizeof(hci_update_baud_rate)); 507 508 read_event(uart_fd, buffer); 509 510 cfsetospeed(&termios, termios_baudrate); 511 cfsetispeed(&termios, termios_baudrate); 512 tcsetattr(uart_fd, TCSANOW, &termios); 513 514 if (debug) { 515 fprintf(stderr, "Done setting baudrate\n"); 516 } 517} 518 519void 520proc_bdaddr() 521{ 522 hci_send_cmd(hci_write_bd_addr, sizeof(hci_write_bd_addr)); 523 524 read_event(uart_fd, buffer); 525} 526 527void 528proc_enable_lpm() 529{ 530 hci_send_cmd(hci_write_sleep_mode, sizeof(hci_write_sleep_mode)); 531 532 read_event(uart_fd, buffer); 533} 534 535void 536proc_pcm_slave() 537{ 538 printf("Configuring PCM Interface as slave.\n"); 539 hci_send_cmd(hci_write_pcm_slave_mode, sizeof(hci_write_pcm_slave_mode)); 540} 541 542void 543proc_pcm_master() 544{ 545 printf("Configuring PCM Interface as master.\n"); 546 hci_send_cmd(hci_write_pcm_master_mode, sizeof(hci_write_pcm_master_mode)); 547} 548 549void 550proc_enable_hci() 551{ 552 int i = N_HCI; 553 int proto = HCI_UART_H4; 554 if (ioctl(uart_fd, TIOCSETD, &i) < 0) { 555 fprintf(stderr, "Can't set line discipline\n"); 556 return; 557 } 558 559 if (ioctl(uart_fd, HCIUARTSETPROTO, proto) < 0) { 560 fprintf(stderr, "Can't set hci protocol\n"); 561 return; 562 } 563 fprintf(stderr, "Done setting line discpline\n"); 564 return; 565} 566 567void 568read_default_bdaddr() 569{ 570 int sz; 571 int fd; 572 char path[PROPERTY_VALUE_MAX]; 573 char bdaddr[18]; 574 575 property_get("ro.bt.bdaddr_path", path, ""); 576 if (path[0] == 0) 577 return; 578 579 fd = open(path, O_RDONLY); 580 if (fd < 0) { 581 fprintf(stderr, "open(%s) failed: %s (%d)", path, strerror(errno), 582 errno); 583 return; 584 } 585 586 sz = read(fd, bdaddr, sizeof(bdaddr)); 587 if (sz < 0) { 588 fprintf(stderr, "read(%s) failed: %s (%d)", path, strerror(errno), 589 errno); 590 close(fd); 591 return; 592 } else if (sz != sizeof(bdaddr)) { 593 fprintf(stderr, "read(%s) unexpected size %d", path, sz); 594 close(fd); 595 return; 596 } 597 598 printf("Read default bdaddr of %s\n", bdaddr); 599 parse_bdaddr(bdaddr); 600} 601 602int 603main (int argc, char **argv) 604{ 605 read_default_bdaddr(); 606 607 parse_cmd_line(argc, argv); 608 609 if (uart_fd < 0) { 610 exit(1); 611 } 612 613 init_uart(); 614 615 proc_reset(); 616 617 if (hcdfile_fd > 0) { 618 proc_patchram(); 619 } 620 621 if (termios_baudrate) { 622 proc_baudrate(); 623 } 624 625 if (bdaddr_flag) { 626 proc_bdaddr(); 627 } 628 629 if (enable_lpm) { 630 proc_enable_lpm(); 631 } 632 633 if (pcm_slave) { 634 proc_pcm_slave(); 635 } else if (pcm_master) { 636 proc_pcm_master(); 637 } 638 639 if (enable_hci) { 640 proc_enable_hci(); 641 while (1) { 642 sleep(UINT_MAX); 643 } 644 } 645 646 exit(0); 647} 648