Home | History | Annotate | Download | only in brcm_patchram_plus
      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 **						uart_device_name
     36 **
     37 **                 For example:
     38 **
     39 **                 brcm_patchram_plus -d --patchram  \
     40 **						BCM2045B2_002.002.011.0348.0349.hcd /dev/ttyHS0
     41 **
     42 **                 It will return 0 for success and a number greater than 0
     43 **                 for any errors.
     44 **
     45 **                 For Android, this program invoked using a
     46 **                 "system(2)" call from the beginning of the bt_enable
     47 **                 function inside the file
     48 **                 system/bluetooth/bluedroid/bluetooth.c.
     49 **
     50 **                 If the Android system property "ro.bt.bcm_bdaddr_path" is
     51 **                 set, then the bd_addr will be read from this path.
     52 **                 This is overridden by --bd_addr on the command line.
     53 **
     54 ******************************************************************************/
     55 
     56 // TODO: Integrate BCM support into Bluez hciattach
     57 
     58 #include <stdio.h>
     59 #include <getopt.h>
     60 #include <errno.h>
     61 
     62 #include <sys/types.h>
     63 #include <sys/stat.h>
     64 #include <fcntl.h>
     65 
     66 #include <stdlib.h>
     67 
     68 #ifdef ANDROID
     69 #include <termios.h>
     70 #else
     71 #include <sys/termios.h>
     72 #endif
     73 
     74 #include <string.h>
     75 #include <signal.h>
     76 
     77 #include <cutils/properties.h>
     78 
     79 #ifndef N_HCI
     80 #define N_HCI	15
     81 #endif
     82 
     83 #define HCIUARTSETPROTO		_IOW('U', 200, int)
     84 #define HCIUARTGETPROTO		_IOR('U', 201, int)
     85 #define HCIUARTGETDEVICE	_IOR('U', 202, int)
     86 
     87 #define HCI_UART_H4		0
     88 #define HCI_UART_BCSP	1
     89 #define HCI_UART_3WIRE	2
     90 #define HCI_UART_H4DS	3
     91 #define HCI_UART_LL		4
     92 
     93 
     94 int uart_fd = -1;
     95 int hcdfile_fd = -1;
     96 int termios_baudrate = 0;
     97 int bdaddr_flag = 0;
     98 int enable_lpm = 0;
     99 int enable_hci = 0;
    100 int debug = 0;
    101 
    102 struct termios termios;
    103 unsigned char buffer[1024];
    104 
    105 unsigned char hci_reset[] = { 0x01, 0x03, 0x0c, 0x00 };
    106 
    107 unsigned char hci_download_minidriver[] = { 0x01, 0x2e, 0xfc, 0x00 };
    108 
    109 unsigned char hci_update_baud_rate[] = { 0x01, 0x18, 0xfc, 0x06, 0x00, 0x00,
    110 	0x00, 0x00, 0x00, 0x00 };
    111 
    112 unsigned char hci_write_bd_addr[] = { 0x01, 0x01, 0xfc, 0x06,
    113 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    114 
    115 unsigned char hci_write_sleep_mode[] = { 0x01, 0x27, 0xfc, 0x0c,
    116 	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
    117 	0x00, 0x00 };
    118 
    119 int
    120 parse_patchram(char *optarg)
    121 {
    122 	char *p;
    123 
    124 	if (!(p = strrchr(optarg, '.'))) {
    125 		fprintf(stderr, "file %s not an HCD file\n", optarg);
    126 		exit(3);
    127 	}
    128 
    129 	p++;
    130 
    131 	if (strcasecmp("hcd", p) != 0) {
    132 		fprintf(stderr, "file %s not an HCD file\n", optarg);
    133 		exit(4);
    134 	}
    135 
    136 	if ((hcdfile_fd = open(optarg, O_RDONLY)) == -1) {
    137 		fprintf(stderr, "file %s could not be opened, error %d\n", optarg, errno);
    138 		exit(5);
    139 	}
    140 
    141 	return(0);
    142 }
    143 
    144 void
    145 BRCM_encode_baud_rate(uint baud_rate, unsigned char *encoded_baud)
    146 {
    147 	if(baud_rate == 0 || encoded_baud == NULL) {
    148 		fprintf(stderr, "Baudrate not supported!");
    149 		return;
    150 	}
    151 
    152 	encoded_baud[3] = (unsigned char)(baud_rate >> 24);
    153 	encoded_baud[2] = (unsigned char)(baud_rate >> 16);
    154 	encoded_baud[1] = (unsigned char)(baud_rate >> 8);
    155 	encoded_baud[0] = (unsigned char)(baud_rate & 0xFF);
    156 }
    157 
    158 typedef struct {
    159 	int baud_rate;
    160 	int termios_value;
    161 } tBaudRates;
    162 
    163 tBaudRates baud_rates[] = {
    164 	{ 115200, B115200 },
    165 	{ 230400, B230400 },
    166 	{ 460800, B460800 },
    167 	{ 500000, B500000 },
    168 	{ 576000, B576000 },
    169 	{ 921600, B921600 },
    170 	{ 1000000, B1000000 },
    171 	{ 1152000, B1152000 },
    172 	{ 1500000, B1500000 },
    173 	{ 2000000, B2000000 },
    174 	{ 2500000, B2500000 },
    175 	{ 3000000, B3000000 },
    176 #ifndef __CYGWIN__
    177 	{ 3500000, B3500000 },
    178 	{ 4000000, B4000000 }
    179 #endif
    180 };
    181 
    182 int
    183 validate_baudrate(int baud_rate, int *value)
    184 {
    185 	unsigned int i;
    186 
    187 	for (i = 0; i < (sizeof(baud_rates) / sizeof(tBaudRates)); i++) {
    188 		if (baud_rates[i].baud_rate == baud_rate) {
    189 			*value = baud_rates[i].termios_value;
    190 			return(1);
    191 		}
    192 	}
    193 
    194 	return(0);
    195 }
    196 
    197 int
    198 parse_baudrate(char *optarg)
    199 {
    200 	int baudrate = atoi(optarg);
    201 
    202 	if (validate_baudrate(baudrate, &termios_baudrate)) {
    203 		BRCM_encode_baud_rate(baudrate, &hci_update_baud_rate[6]);
    204 	}
    205 
    206 	return(0);
    207 }
    208 
    209 int
    210 parse_bdaddr(char *optarg)
    211 {
    212 	int bd_addr[6];
    213 	int i;
    214 
    215 	sscanf(optarg, "%02X:%02X:%02X:%02X:%02X:%02X",
    216 		&bd_addr[5], &bd_addr[4], &bd_addr[3],
    217 		&bd_addr[2], &bd_addr[1], &bd_addr[0]);
    218 
    219 	for (i = 0; i < 6; i++) {
    220 		hci_write_bd_addr[4 + i] = bd_addr[i];
    221 	}
    222 
    223 	bdaddr_flag = 1;
    224 
    225 	return(0);
    226 }
    227 
    228 int
    229 parse_enable_lpm(char *optarg)
    230 {
    231 	enable_lpm = 1;
    232 	return(0);
    233 }
    234 
    235 int
    236 parse_enable_hci(char *optarg)
    237 {
    238 	enable_hci = 1;
    239 	return(0);
    240 }
    241 
    242 int
    243 parse_cmd_line(int argc, char **argv)
    244 {
    245 	int c;
    246 	int digit_optind = 0;
    247 
    248 	typedef int (*PFI)();
    249 
    250 	PFI parse_param[] = { parse_patchram, parse_baudrate,
    251 		parse_bdaddr, parse_enable_lpm, parse_enable_hci };
    252 
    253     while (1)
    254     {
    255     	int this_option_optind = optind ? optind : 1;
    256         int option_index = 0;
    257 
    258        	static struct option long_options[] = {
    259          {"patchram", 1, 0, 0},
    260          {"baudrate", 1, 0, 0},
    261          {"bd_addr", 1, 0, 0},
    262          {"enable_lpm", 0, 0, 0},
    263          {"enable_hci", 0, 0, 0},
    264          {0, 0, 0, 0}
    265        	};
    266 
    267        	c = getopt_long_only (argc, argv, "d", long_options, &option_index);
    268 
    269        	if (c == -1) {
    270       		break;
    271 		}
    272 
    273        	switch (c) {
    274         case 0:
    275         	printf ("option %s", long_options[option_index].name);
    276 
    277         	if (optarg) {
    278            		printf (" with arg %s", optarg);
    279 			}
    280 
    281            	printf ("\n");
    282 
    283 			(*parse_param[option_index])(optarg);
    284 		break;
    285 
    286 		case 'd':
    287 			debug = 1;
    288 		break;
    289 
    290         case '?':
    291 			//nobreak
    292         default:
    293 
    294 			printf("Usage %s:\n", argv[0]);
    295 			printf("\t<-d> to print a debug log\n");
    296 			printf("\t<--patchram patchram_file>\n");
    297 			printf("\t<--baudrate baud_rate>\n");
    298 			printf("\t<--bd_addr bd_address>\n");
    299 			printf("\t<--enable_lpm\n");
    300 			printf("\t<--enable_hci\n");
    301 			printf("\tuart_device_name\n");
    302            	break;
    303 
    304         }
    305 	}
    306 
    307    	if (optind < argc) {
    308        	if (optind < argc) {
    309        		printf ("%s ", argv[optind]);
    310 
    311 			if ((uart_fd = open(argv[optind], O_RDWR | O_NOCTTY)) == -1) {
    312 				fprintf(stderr, "port %s could not be opened, error %d\n", argv[2], errno);
    313 			}
    314 		}
    315 
    316        	printf ("\n");
    317     }
    318 
    319 	return(0);
    320 }
    321 
    322 void
    323 init_uart()
    324 {
    325 	tcflush(uart_fd, TCIOFLUSH);
    326 	tcgetattr(uart_fd, &termios);
    327 
    328 #ifndef __CYGWIN__
    329 	cfmakeraw(&termios);
    330 #else
    331 	termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
    332                 | INLCR | IGNCR | ICRNL | IXON);
    333 	termios.c_oflag &= ~OPOST;
    334 	termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    335 	termios.c_cflag &= ~(CSIZE | PARENB);
    336 	termios.c_cflag |= CS8;
    337 #endif
    338 
    339 	termios.c_cflag |= CRTSCTS;
    340 	tcsetattr(uart_fd, TCSANOW, &termios);
    341 	tcflush(uart_fd, TCIOFLUSH);
    342 	tcsetattr(uart_fd, TCSANOW, &termios);
    343 	tcflush(uart_fd, TCIOFLUSH);
    344 	tcflush(uart_fd, TCIOFLUSH);
    345 	cfsetospeed(&termios, B115200);
    346 	cfsetispeed(&termios, B115200);
    347 	tcsetattr(uart_fd, TCSANOW, &termios);
    348 }
    349 
    350 void
    351 dump(unsigned char *out, int len)
    352 {
    353 	int i;
    354 
    355 	for (i = 0; i < len; i++) {
    356 		if (i && !(i % 16)) {
    357 			fprintf(stderr, "\n");
    358 		}
    359 
    360 		fprintf(stderr, "%02x ", out[i]);
    361 	}
    362 
    363 	fprintf(stderr, "\n");
    364 }
    365 
    366 void
    367 read_event(int fd, unsigned char *buffer)
    368 {
    369 	int i = 0;
    370 	int len = 3;
    371 	int count;
    372 
    373 	while ((count = read(fd, &buffer[i], len)) < len) {
    374 		i += count;
    375 		len -= count;
    376 	}
    377 
    378 	i += count;
    379 	len = buffer[2];
    380 
    381 	while ((count = read(fd, &buffer[i], len)) < len) {
    382 		i += count;
    383 		len -= count;
    384 	}
    385 
    386 	if (debug) {
    387 		count += i;
    388 
    389 		fprintf(stderr, "received %d\n", count);
    390 		dump(buffer, count);
    391 	}
    392 }
    393 
    394 void
    395 hci_send_cmd(unsigned char *buf, int len)
    396 {
    397 	if (debug) {
    398 		fprintf(stderr, "writing\n");
    399 		dump(buf, len);
    400 	}
    401 
    402 	write(uart_fd, buf, len);
    403 }
    404 
    405 void
    406 expired(int sig)
    407 {
    408 	hci_send_cmd(hci_reset, sizeof(hci_reset));
    409 	alarm(4);
    410 }
    411 
    412 void
    413 proc_reset()
    414 {
    415 	signal(SIGALRM, expired);
    416 
    417 
    418 	hci_send_cmd(hci_reset, sizeof(hci_reset));
    419 
    420 	alarm(4);
    421 
    422 	read_event(uart_fd, buffer);
    423 
    424 	alarm(0);
    425 }
    426 
    427 void
    428 proc_patchram()
    429 {
    430 	int len;
    431 
    432 	hci_send_cmd(hci_download_minidriver, sizeof(hci_download_minidriver));
    433 
    434 	read_event(uart_fd, buffer);
    435 
    436 	read(uart_fd, &buffer[0], 2);
    437 
    438 	usleep(50000);
    439 
    440 	while (read(hcdfile_fd, &buffer[1], 3)) {
    441 		buffer[0] = 0x01;
    442 
    443 		len = buffer[3];
    444 
    445 		read(hcdfile_fd, &buffer[4], len);
    446 
    447 		hci_send_cmd(buffer, len + 4);
    448 
    449 		read_event(uart_fd, buffer);
    450 	}
    451 
    452 	proc_reset();
    453 }
    454 
    455 void
    456 proc_baudrate()
    457 {
    458 	hci_send_cmd(hci_update_baud_rate, sizeof(hci_update_baud_rate));
    459 
    460 	read_event(uart_fd, buffer);
    461 
    462 	cfsetospeed(&termios, termios_baudrate);
    463 	cfsetispeed(&termios, termios_baudrate);
    464 	tcsetattr(uart_fd, TCSANOW, &termios);
    465 
    466 	if (debug) {
    467 		fprintf(stderr, "Done setting baudrate\n");
    468 	}
    469 }
    470 
    471 void
    472 proc_bdaddr()
    473 {
    474 	hci_send_cmd(hci_write_bd_addr, sizeof(hci_write_bd_addr));
    475 
    476 	read_event(uart_fd, buffer);
    477 }
    478 
    479 void
    480 proc_enable_lpm()
    481 {
    482 	hci_send_cmd(hci_write_sleep_mode, sizeof(hci_write_sleep_mode));
    483 
    484 	read_event(uart_fd, buffer);
    485 }
    486 
    487 void
    488 proc_enable_hci()
    489 {
    490 	int i = N_HCI;
    491 	int proto = HCI_UART_H4;
    492 	if (ioctl(uart_fd, TIOCSETD, &i) < 0) {
    493 		fprintf(stderr, "Can't set line discipline\n");
    494 		return;
    495 	}
    496 
    497 	if (ioctl(uart_fd, HCIUARTSETPROTO, proto) < 0) {
    498 		fprintf(stderr, "Can't set hci protocol\n");
    499 		return;
    500 	}
    501 	fprintf(stderr, "Done setting line discpline\n");
    502 	return;
    503 }
    504 
    505 void
    506 read_default_bdaddr()
    507 {
    508 	int sz;
    509 	int fd;
    510 	char path[PROPERTY_VALUE_MAX];
    511 	char bdaddr[18];
    512 
    513 	property_get("ro.bt.bdaddr_path", path, "");
    514 	if (path[0] == 0)
    515 		return;
    516 
    517 	fd = open(path, O_RDONLY);
    518 	if (fd < 0) {
    519 		fprintf(stderr, "open(%s) failed: %s (%d)", path, strerror(errno),
    520 				errno);
    521 		return;
    522 	}
    523 
    524 	sz = read(fd, bdaddr, sizeof(bdaddr));
    525 	if (sz < 0) {
    526 		fprintf(stderr, "read(%s) failed: %s (%d)", path, strerror(errno),
    527 				errno);
    528 		close(fd);
    529 		return;
    530 	} else if (sz != sizeof(bdaddr)) {
    531 		fprintf(stderr, "read(%s) unexpected size %d", path, sz);
    532 		close(fd);
    533 		return;
    534 	}
    535 
    536 	printf("Read default bdaddr of %s\n", bdaddr);
    537 	parse_bdaddr(bdaddr);
    538 }
    539 
    540 int
    541 main (int argc, char **argv)
    542 {
    543 	read_default_bdaddr();
    544 
    545 	parse_cmd_line(argc, argv);
    546 
    547 	if (uart_fd < 0) {
    548 		exit(1);
    549 	}
    550 
    551 	init_uart();
    552 
    553 	proc_reset();
    554 
    555 	if (hcdfile_fd > 0) {
    556 		proc_patchram();
    557 	}
    558 
    559 	if (termios_baudrate) {
    560 		proc_baudrate();
    561 	}
    562 
    563 	if (bdaddr_flag) {
    564 		proc_bdaddr();
    565 	}
    566 
    567 	if (enable_lpm) {
    568 		proc_enable_lpm();
    569 	}
    570 
    571 	if (enable_hci) {
    572 		proc_enable_hci();
    573 		while (1) {
    574 			sleep(UINT_MAX);
    575 		}
    576 	}
    577 
    578 	exit(0);
    579 }
    580