brcm_patchram_plus.c revision db5ccf71519a49fbb36b915dba551aa0b3fa0db8
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
94int uart_fd = -1;
95int hcdfile_fd = -1;
96int termios_baudrate = 0;
97int bdaddr_flag = 0;
98int enable_lpm = 0;
99int enable_hci = 0;
100int debug = 0;
101
102struct termios termios;
103unsigned char buffer[1024];
104
105unsigned char hci_reset[] = { 0x01, 0x03, 0x0c, 0x00 };
106
107unsigned char hci_download_minidriver[] = { 0x01, 0x2e, 0xfc, 0x00 };
108
109unsigned char hci_update_baud_rate[] = { 0x01, 0x18, 0xfc, 0x06, 0x00, 0x00,
110	0x00, 0x00, 0x00, 0x00 };
111
112unsigned char hci_write_bd_addr[] = { 0x01, 0x01, 0xfc, 0x06,
113	0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
114
115unsigned char hci_write_sleep_mode[] = { 0x01, 0x27, 0xfc, 0x0c,
116	0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00,
117	0x00, 0x00 };
118
119int
120parse_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
144void
145BRCM_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
158typedef struct {
159	int baud_rate;
160	int termios_value;
161} tBaudRates;
162
163tBaudRates 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
182int
183validate_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
197int
198parse_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
209int
210parse_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
228int
229parse_enable_lpm(char *optarg)
230{
231	enable_lpm = 1;
232	return(0);
233}
234
235int
236parse_enable_hci(char *optarg)
237{
238	enable_hci = 1;
239	return(0);
240}
241
242int
243parse_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
322void
323init_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
350void
351dump(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
366void
367read_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
394void
395hci_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
405void
406expired(int sig)
407{
408	hci_send_cmd(hci_reset, sizeof(hci_reset));
409	alarm(4);
410}
411
412void
413proc_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
427void
428proc_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
453void
454proc_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
469void
470proc_bdaddr()
471{
472	hci_send_cmd(hci_write_bd_addr, sizeof(hci_write_bd_addr));
473
474	read_event(uart_fd, buffer);
475}
476
477void
478proc_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
485void
486proc_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
506void
507read_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
541int
542main (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_hci) {
569		proc_enable_hci();
570	}
571
572	if (enable_lpm) {
573		proc_enable_lpm();
574	}
575
576	if (enable_hci) {
577		while (1) {
578			sleep(UINT_MAX);
579		}
580	}
581
582	exit(0);
583}
584