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