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