• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
parse_patchram(char * optarg)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
BRCM_encode_baud_rate(uint baud_rate,unsigned char * encoded_baud)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
validate_baudrate(int baud_rate,int * value)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
parse_baudrate(char * optarg)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
parse_bdaddr(char * optarg)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
parse_enable_lpm(char * optarg)229 parse_enable_lpm(char *optarg)
230 {
231 	enable_lpm = 1;
232 	return(0);
233 }
234 
235 int
parse_enable_hci(char * optarg)236 parse_enable_hci(char *optarg)
237 {
238 	enable_hci = 1;
239 	return(0);
240 }
241 
242 int
parse_cmd_line(int argc,char ** argv)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
init_uart()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
dump(unsigned char * out,int len)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
read_event(int fd,unsigned char * buffer)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
hci_send_cmd(unsigned char * buf,int len)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
expired(int sig)406 expired(int sig)
407 {
408 	hci_send_cmd(hci_reset, sizeof(hci_reset));
409 	alarm(4);
410 }
411 
412 void
proc_reset()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
proc_patchram()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 	while (read(hcdfile_fd, &buffer[1], 3)) {
439 		buffer[0] = 0x01;
440 
441 		len = buffer[3];
442 
443 		read(hcdfile_fd, &buffer[4], len);
444 
445 		hci_send_cmd(buffer, len + 4);
446 
447 		read_event(uart_fd, buffer);
448 	}
449 
450 	proc_reset();
451 }
452 
453 void
proc_baudrate()454 proc_baudrate()
455 {
456 	hci_send_cmd(hci_update_baud_rate, sizeof(hci_update_baud_rate));
457 
458 	read_event(uart_fd, buffer);
459 
460 	cfsetospeed(&termios, termios_baudrate);
461 	cfsetispeed(&termios, termios_baudrate);
462 	tcsetattr(uart_fd, TCSANOW, &termios);
463 
464 	if (debug) {
465 		fprintf(stderr, "Done setting baudrate\n");
466 	}
467 }
468 
469 void
proc_bdaddr()470 proc_bdaddr()
471 {
472 	hci_send_cmd(hci_write_bd_addr, sizeof(hci_write_bd_addr));
473 
474 	read_event(uart_fd, buffer);
475 }
476 
477 void
proc_enable_lpm()478 proc_enable_lpm()
479 {
480 	hci_send_cmd(hci_write_sleep_mode, sizeof(hci_write_sleep_mode));
481 
482 	read_event(uart_fd, buffer);
483 }
484 
485 void
proc_enable_hci()486 proc_enable_hci()
487 {
488 	int i = N_HCI;
489 	int proto = HCI_UART_H4;
490 	if (enable_lpm) {
491 		proto = HCI_UART_LL;
492 	}
493 	if (ioctl(uart_fd, TIOCSETD, &i) < 0) {
494 		fprintf(stderr, "Can't set line discipline\n");
495 		return;
496 	}
497 
498 	if (ioctl(uart_fd, HCIUARTSETPROTO, proto) < 0) {
499 		fprintf(stderr, "Can't set hci protocol\n");
500 		return;
501 	}
502 	fprintf(stderr, "Done setting line discpline\n");
503 	return;
504 }
505 
506 void
read_default_bdaddr()507 read_default_bdaddr()
508 {
509 	int sz;
510 	int fd;
511 	char path[PROPERTY_VALUE_MAX];
512 	char bdaddr[18];
513 
514 	property_get("ro.bt.bdaddr_path", path, "");
515 	if (path[0] == 0)
516 		return;
517 
518 	fd = open(path, O_RDONLY);
519 	if (fd < 0) {
520 		fprintf(stderr, "open(%s) failed: %s (%d)", path, strerror(errno),
521 				errno);
522 		return;
523 	}
524 
525 	sz = read(fd, bdaddr, sizeof(bdaddr));
526 	if (sz < 0) {
527 		fprintf(stderr, "read(%s) failed: %s (%d)", path, strerror(errno),
528 				errno);
529 		close(fd);
530 		return;
531 	} else if (sz != sizeof(bdaddr)) {
532 		fprintf(stderr, "read(%s) unexpected size %d", path, sz);
533 		close(fd);
534 		return;
535 	}
536 
537 	printf("Read default bdaddr of %s\n", bdaddr);
538 	parse_bdaddr(bdaddr);
539 }
540 
541 int
main(int argc,char ** argv)542 main (int argc, char **argv)
543 {
544 	read_default_bdaddr();
545 
546 	parse_cmd_line(argc, argv);
547 
548 	if (uart_fd < 0) {
549 		exit(1);
550 	}
551 
552 	init_uart();
553 
554 	proc_reset();
555 
556 	if (hcdfile_fd > 0) {
557 		proc_patchram();
558 	}
559 
560 	if (termios_baudrate) {
561 		proc_baudrate();
562 	}
563 
564 	if (bdaddr_flag) {
565 		proc_bdaddr();
566 	}
567 
568 	if (enable_lpm) {
569 		proc_enable_lpm();
570 	}
571 
572 	if (enable_hci) {
573 		proc_enable_hci();
574 		while (1) {
575 			sleep(UINT_MAX);
576 		}
577 	}
578 
579 	exit(0);
580 }
581