brcm_patchram_plus.c revision fff5ba85648ed4346d6feb3639aeafa6874bf640
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, &option_index);
325
326       	if (c == -1) {
327      		break;
328		}
329
330       	switch (c) {
331        case 0:
332        	printf ("option %s", long_options[option_index].name);
333
334        	if (optarg) {
335           		printf (" with arg %s", optarg);
336			}
337
338           	printf ("\n");
339
340			(*parse_param[option_index])(optarg);
341		break;
342
343		case 'd':
344			debug = 1;
345		break;
346
347        case '?':
348			//nobreak
349        default:
350
351			printf("Usage %s:\n", argv[0]);
352			printf("\t<-d> to print a debug log\n");
353			printf("\t<--patchram patchram_file>\n");
354			printf("\t<--baudrate baud_rate>\n");
355			printf("\t<--bd_addr bd_address>\n");
356			printf("\t<--enable_lpm>\n");
357			printf("\t<--enable_hci>\n");
358			printf("\t<--pcm_role slave|master>\n");
359			printf("\t<--use_baudrate_for_download> - Uses the\
360					baudrate for downloading the\
361					firmware\n");
362			printf("\tuart_device_name\n");
363           	break;
364
365        }
366	}
367
368   	if (optind < argc) {
369       	if (optind < argc) {
370       		printf ("%s ", argv[optind]);
371
372			if ((uart_fd = open(argv[optind], O_RDWR | O_NOCTTY)) == -1) {
373				fprintf(stderr, "port %s could not be opened, error %d\n", argv[2], errno);
374			}
375		}
376
377       	printf ("\n");
378    }
379
380	return(0);
381}
382
383void
384init_uart()
385{
386	tcflush(uart_fd, TCIOFLUSH);
387	tcgetattr(uart_fd, &termios);
388
389#ifndef __CYGWIN__
390	cfmakeraw(&termios);
391#else
392	termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
393                | INLCR | IGNCR | ICRNL | IXON);
394	termios.c_oflag &= ~OPOST;
395	termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
396	termios.c_cflag &= ~(CSIZE | PARENB);
397	termios.c_cflag |= CS8;
398#endif
399
400	termios.c_cflag |= CRTSCTS;
401	tcsetattr(uart_fd, TCSANOW, &termios);
402	tcflush(uart_fd, TCIOFLUSH);
403	tcsetattr(uart_fd, TCSANOW, &termios);
404	tcflush(uart_fd, TCIOFLUSH);
405	tcflush(uart_fd, TCIOFLUSH);
406	cfsetospeed(&termios, B115200);
407	cfsetispeed(&termios, B115200);
408	tcsetattr(uart_fd, TCSANOW, &termios);
409}
410
411void
412dump(unsigned char *out, int len)
413{
414	int i;
415
416	for (i = 0; i < len; i++) {
417		if (i && !(i % 16)) {
418			fprintf(stderr, "\n");
419		}
420
421		fprintf(stderr, "%02x ", out[i]);
422	}
423
424	fprintf(stderr, "\n");
425}
426
427void
428read_event(int fd, unsigned char *buffer)
429{
430	int i = 0;
431	int len = 3;
432	int count;
433
434	while ((count = read(fd, &buffer[i], len)) < len) {
435		i += count;
436		len -= count;
437	}
438
439	i += count;
440	len = buffer[2];
441
442	while ((count = read(fd, &buffer[i], len)) < len) {
443		i += count;
444		len -= count;
445	}
446
447	if (debug) {
448		count += i;
449
450		fprintf(stderr, "received %d\n", count);
451		dump(buffer, count);
452	}
453}
454
455void
456hci_send_cmd(unsigned char *buf, int len)
457{
458	if (debug) {
459		fprintf(stderr, "writing\n");
460		dump(buf, len);
461	}
462
463	write(uart_fd, buf, len);
464}
465
466void
467expired(int sig)
468{
469	hci_send_cmd(hci_reset, sizeof(hci_reset));
470	alarm(4);
471}
472
473void
474proc_reset()
475{
476	signal(SIGALRM, expired);
477
478
479	hci_send_cmd(hci_reset, sizeof(hci_reset));
480
481	alarm(4);
482
483	read_event(uart_fd, buffer);
484
485	alarm(0);
486}
487
488void
489proc_patchram()
490{
491	int len;
492
493	hci_send_cmd(hci_download_minidriver, sizeof(hci_download_minidriver));
494
495	read_event(uart_fd, buffer);
496
497	read(uart_fd, &buffer[0], 2);
498
499	usleep(50000);
500
501	while (read(hcdfile_fd, &buffer[1], 3)) {
502		buffer[0] = 0x01;
503
504		len = buffer[3];
505
506		read(hcdfile_fd, &buffer[4], len);
507
508		hci_send_cmd(buffer, len + 4);
509
510		read_event(uart_fd, buffer);
511	}
512
513	proc_reset();
514}
515
516void
517proc_baudrate()
518{
519	hci_send_cmd(hci_update_baud_rate, sizeof(hci_update_baud_rate));
520
521	read_event(uart_fd, buffer);
522
523	cfsetospeed(&termios, termios_baudrate);
524	cfsetispeed(&termios, termios_baudrate);
525	tcsetattr(uart_fd, TCSANOW, &termios);
526
527	if (debug) {
528		fprintf(stderr, "Done setting baudrate\n");
529	}
530}
531
532void
533proc_bdaddr()
534{
535	hci_send_cmd(hci_write_bd_addr, sizeof(hci_write_bd_addr));
536
537	read_event(uart_fd, buffer);
538}
539
540void
541proc_enable_lpm()
542{
543	hci_send_cmd(hci_write_sleep_mode, sizeof(hci_write_sleep_mode));
544
545	read_event(uart_fd, buffer);
546}
547
548void
549proc_pcm_slave()
550{
551	printf("Configuring PCM Interface as slave.\n");
552	hci_send_cmd(hci_write_pcm_slave_mode, sizeof(hci_write_pcm_slave_mode));
553}
554
555void
556proc_pcm_master()
557{
558	printf("Configuring PCM Interface as master.\n");
559	hci_send_cmd(hci_write_pcm_master_mode, sizeof(hci_write_pcm_master_mode));
560}
561
562void
563proc_enable_hci()
564{
565	int i = N_HCI;
566	int proto = HCI_UART_H4;
567	if (ioctl(uart_fd, TIOCSETD, &i) < 0) {
568		fprintf(stderr, "Can't set line discipline\n");
569		return;
570	}
571
572	if (ioctl(uart_fd, HCIUARTSETPROTO, proto) < 0) {
573		fprintf(stderr, "Can't set hci protocol\n");
574		return;
575	}
576	fprintf(stderr, "Done setting line discpline\n");
577	return;
578}
579
580void
581read_default_bdaddr()
582{
583	int sz;
584	int fd;
585	char path[PROPERTY_VALUE_MAX];
586	char bdaddr[18];
587
588	property_get("ro.bt.bdaddr_path", path, "");
589	if (path[0] == 0)
590		return;
591
592	fd = open(path, O_RDONLY);
593	if (fd < 0) {
594		fprintf(stderr, "open(%s) failed: %s (%d)", path, strerror(errno),
595				errno);
596		return;
597	}
598
599	sz = read(fd, bdaddr, sizeof(bdaddr));
600	if (sz < 0) {
601		fprintf(stderr, "read(%s) failed: %s (%d)", path, strerror(errno),
602				errno);
603		close(fd);
604		return;
605	} else if (sz != sizeof(bdaddr)) {
606		fprintf(stderr, "read(%s) unexpected size %d", path, sz);
607		close(fd);
608		return;
609	}
610
611	printf("Read default bdaddr of %s\n", bdaddr);
612	parse_bdaddr(bdaddr);
613}
614
615int
616main (int argc, char **argv)
617{
618	read_default_bdaddr();
619
620	parse_cmd_line(argc, argv);
621
622	if (uart_fd < 0) {
623		exit(1);
624	}
625
626	init_uart();
627
628	proc_reset();
629
630	if (use_baudrate_for_download) {
631		if (termios_baudrate) {
632			proc_baudrate();
633		}
634
635		if (hcdfile_fd > 0) {
636			proc_patchram();
637		}
638	} else {
639		if (hcdfile_fd > 0) {
640			proc_patchram();
641		}
642
643		if (termios_baudrate) {
644			proc_baudrate();
645		}
646	}
647
648	if (bdaddr_flag) {
649		proc_bdaddr();
650	}
651
652	if (enable_lpm) {
653		proc_enable_lpm();
654	}
655
656	if (pcm_slave) {
657		proc_pcm_slave();
658	} else if (pcm_master) {
659		proc_pcm_master();
660	}
661
662	if (enable_hci) {
663		proc_enable_hci();
664		while (1) {
665			sleep(UINT_MAX);
666		}
667	}
668
669	exit(0);
670}
671