brcm_patchram_plus.c revision ac8c432d8eea5f875f672c2a7755c40a76a3c8cd
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**                 mydroid/system/bluetooth/bluedroid/bluetooth.c.
49**
50**
51******************************************************************************/
52
53// TODO: Integrate BCM support into Bluez hciattach
54
55#include <stdio.h>
56#include <getopt.h>
57#include <errno.h>
58
59#include <sys/types.h>
60#include <sys/stat.h>
61#include <fcntl.h>
62
63#include <stdlib.h>
64
65#ifdef ANDROID
66#include <termios.h>
67#else
68#include <sys/termios.h>
69#endif
70
71#include <string.h>
72#include <signal.h>
73
74#ifndef N_HCI
75#define N_HCI	15
76#endif
77
78#define HCIUARTSETPROTO		_IOW('U', 200, int)
79#define HCIUARTGETPROTO		_IOR('U', 201, int)
80#define HCIUARTGETDEVICE	_IOR('U', 202, int)
81
82#define HCI_UART_H4		0
83#define HCI_UART_BCSP	1
84#define HCI_UART_3WIRE	2
85#define HCI_UART_H4DS	3
86#define HCI_UART_LL		4
87
88
89int uart_fd = -1;
90int hcdfile_fd = -1;
91int termios_baudrate = 0;
92int bdaddr_flag = 0;
93int enable_lpm = 0;
94int enable_hci = 0;
95int debug = 0;
96
97struct termios termios;
98unsigned char buffer[1024];
99
100unsigned char hci_reset[] = { 0x01, 0x03, 0x0c, 0x00 };
101
102unsigned char hci_download_minidriver[] = { 0x01, 0x2e, 0xfc, 0x00 };
103
104unsigned char hci_update_baud_rate[] = { 0x01, 0x18, 0xfc, 0x06, 0x00, 0x00,
105	0x00, 0x00, 0x00, 0x00 };
106
107unsigned char hci_write_bd_addr[] = { 0x01, 0x01, 0xfc, 0x06,
108	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109	0x00 };
110
111unsigned char hci_write_sleep_mode[] = { 0x01, 0x27, 0xfc, 0x0c,
112	0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00,
113	0x00, 0x00 };
114
115int
116parse_patchram(char *optarg)
117{
118	char *p;
119
120	if (!(p = strrchr(optarg, '.'))) {
121		fprintf(stderr, "file %s not an HCD file\n", optarg);
122		exit(3);
123	}
124
125	p++;
126
127	if (strcasecmp("hcd", p) != 0) {
128		fprintf(stderr, "file %s not an HCD file\n", optarg);
129		exit(4);
130	}
131
132	if ((hcdfile_fd = open(optarg, O_RDONLY)) == -1) {
133		fprintf(stderr, "file %s could not be opened, error %d\n", optarg, errno);
134		exit(5);
135	}
136
137	return(0);
138}
139
140void
141BRCM_encode_baud_rate(uint baud_rate, unsigned char *encoded_baud)
142{
143	if(baud_rate == 0 || encoded_baud == NULL) {
144		fprintf(stderr, "Baudrate not supported!");
145		return;
146	}
147
148	encoded_baud[3] = (unsigned char)(baud_rate >> 24);
149	encoded_baud[2] = (unsigned char)(baud_rate >> 16);
150	encoded_baud[1] = (unsigned char)(baud_rate >> 8);
151	encoded_baud[0] = (unsigned char)(baud_rate & 0xFF);
152}
153
154typedef struct {
155	int baud_rate;
156	int termios_value;
157} tBaudRates;
158
159tBaudRates baud_rates[] = {
160	{ 115200, B115200 },
161	{ 230400, B230400 },
162	{ 460800, B460800 },
163	{ 500000, B500000 },
164	{ 576000, B576000 },
165	{ 921600, B921600 },
166	{ 1000000, B1000000 },
167	{ 1152000, B1152000 },
168	{ 1500000, B1500000 },
169	{ 2000000, B2000000 },
170	{ 2500000, B2500000 },
171	{ 3000000, B3000000 },
172#ifndef __CYGWIN__
173	{ 3500000, B3500000 },
174	{ 4000000, B4000000 }
175#endif
176};
177
178int
179validate_baudrate(int baud_rate, int *value)
180{
181	unsigned int i;
182
183	for (i = 0; i < (sizeof(baud_rates) / sizeof(tBaudRates)); i++) {
184		if (baud_rates[i].baud_rate == baud_rate) {
185			*value = baud_rates[i].termios_value;
186			return(1);
187		}
188	}
189
190	return(0);
191}
192
193int
194parse_baudrate(char *optarg)
195{
196	int baudrate = atoi(optarg);
197
198	if (validate_baudrate(baudrate, &termios_baudrate)) {
199		BRCM_encode_baud_rate(baudrate, &hci_update_baud_rate[6]);
200	}
201
202	return(0);
203}
204
205int
206parse_bdaddr(char *optarg)
207{
208	int bd_addr[6];
209	int i;
210
211	sscanf(optarg, "%02x%02x%02x%02x%02x%02x",
212		&bd_addr[0], &bd_addr[1], &bd_addr[2],
213		&bd_addr[3], &bd_addr[4], &bd_addr[5]);
214
215	for (i = 0; i < 6; i++) {
216		hci_write_bd_addr[4 + i] = bd_addr[i];
217	}
218
219	bdaddr_flag = 1;
220
221	return(0);
222}
223
224int
225parse_enable_lpm(char *optarg)
226{
227	enable_lpm = 1;
228	return(0);
229}
230
231int
232parse_enable_hci(char *optarg)
233{
234	enable_hci = 1;
235	return(0);
236}
237
238int
239parse_cmd_line(int argc, char **argv)
240{
241	int c;
242	int digit_optind = 0;
243
244	typedef int (*PFI)();
245
246	PFI parse_param[] = { parse_patchram, parse_baudrate,
247		parse_bdaddr, parse_enable_lpm, parse_enable_hci };
248
249    while (1)
250    {
251    	int this_option_optind = optind ? optind : 1;
252        int option_index = 0;
253
254       	static struct option long_options[] = {
255         {"patchram", 1, 0, 0},
256         {"baudrate", 1, 0, 0},
257         {"bd_addr", 1, 0, 0},
258         {"enable_lpm", 0, 0, 0},
259         {"enable_hci", 0, 0, 0},
260         {0, 0, 0, 0}
261       	};
262
263       	c = getopt_long_only (argc, argv, "d", long_options, &option_index);
264
265       	if (c == -1) {
266      		break;
267		}
268
269       	switch (c) {
270        case 0:
271        	printf ("option %s", long_options[option_index].name);
272
273        	if (optarg) {
274           		printf (" with arg %s", optarg);
275			}
276
277           	printf ("\n");
278
279			(*parse_param[option_index])(optarg);
280		break;
281
282		case 'd':
283			debug = 1;
284		break;
285
286        case '?':
287			//nobreak
288        default:
289
290			printf("Usage %s:\n", argv[0]);
291			printf("\t<-d> to print a debug log\n");
292			printf("\t<--patchram patchram_file>\n");
293			printf("\t<--baudrate baud_rate>\n");
294			printf("\t<--bd_addr bd_address>\n");
295			printf("\t<--enable_lpm\n");
296			printf("\t<--enable_hci\n");
297			printf("\tuart_device_name\n");
298           	break;
299
300        }
301	}
302
303   	if (optind < argc) {
304       	if (optind < argc) {
305       		printf ("%s ", argv[optind]);
306
307			if ((uart_fd = open(argv[optind], O_RDWR | O_NOCTTY)) == -1) {
308				fprintf(stderr, "port %s could not be opened, error %d\n", argv[2], errno);
309			}
310		}
311
312       	printf ("\n");
313    }
314
315	return(0);
316}
317
318void
319init_uart()
320{
321	tcflush(uart_fd, TCIOFLUSH);
322	tcgetattr(uart_fd, &termios);
323
324#ifndef __CYGWIN__
325	cfmakeraw(&termios);
326#else
327	termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
328                | INLCR | IGNCR | ICRNL | IXON);
329	termios.c_oflag &= ~OPOST;
330	termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
331	termios.c_cflag &= ~(CSIZE | PARENB);
332	termios.c_cflag |= CS8;
333#endif
334
335	termios.c_cflag |= CRTSCTS;
336	tcsetattr(uart_fd, TCSANOW, &termios);
337	tcflush(uart_fd, TCIOFLUSH);
338	tcsetattr(uart_fd, TCSANOW, &termios);
339	tcflush(uart_fd, TCIOFLUSH);
340	tcflush(uart_fd, TCIOFLUSH);
341	cfsetospeed(&termios, B115200);
342	cfsetispeed(&termios, B115200);
343	tcsetattr(uart_fd, TCSANOW, &termios);
344}
345
346void
347dump(unsigned char *out, int len)
348{
349	int i;
350
351	for (i = 0; i < len; i++) {
352		if (i && !(i % 16)) {
353			fprintf(stderr, "\n");
354		}
355
356		fprintf(stderr, "%02x ", out[i]);
357	}
358
359	fprintf(stderr, "\n");
360}
361
362void
363read_event(int fd, unsigned char *buffer)
364{
365	int i = 0;
366	int len = 3;
367	int count;
368
369	while ((count = read(fd, &buffer[i], len)) < len) {
370		i += count;
371		len -= count;
372	}
373
374	i += count;
375	len = buffer[2];
376
377	while ((count = read(fd, &buffer[i], len)) < len) {
378		i += count;
379		len -= count;
380	}
381
382	if (debug) {
383		count += i;
384
385		fprintf(stderr, "received %d\n", count);
386		dump(buffer, count);
387	}
388}
389
390void
391hci_send_cmd(unsigned char *buf, int len)
392{
393	if (debug) {
394		fprintf(stderr, "writing\n");
395		dump(buf, len);
396	}
397
398	write(uart_fd, buf, len);
399}
400
401void
402expired(int sig)
403{
404	hci_send_cmd(hci_reset, sizeof(hci_reset));
405	alarm(4);
406}
407
408void
409proc_reset()
410{
411	signal(SIGALRM, expired);
412
413
414	hci_send_cmd(hci_reset, sizeof(hci_reset));
415
416	alarm(4);
417
418	read_event(uart_fd, buffer);
419
420	alarm(0);
421}
422
423void
424proc_patchram()
425{
426	int len;
427
428	hci_send_cmd(hci_download_minidriver, sizeof(hci_download_minidriver));
429
430	read_event(uart_fd, buffer);
431
432	read(uart_fd, &buffer[0], 2);
433
434	while (read(hcdfile_fd, &buffer[1], 3)) {
435		buffer[0] = 0x01;
436
437		len = buffer[3];
438
439		read(hcdfile_fd, &buffer[4], len);
440
441		hci_send_cmd(buffer, len + 4);
442
443		read_event(uart_fd, buffer);
444	}
445
446	proc_reset();
447}
448
449void
450proc_baudrate()
451{
452	hci_send_cmd(hci_update_baud_rate, sizeof(hci_update_baud_rate));
453
454	read_event(uart_fd, buffer);
455
456	cfsetospeed(&termios, termios_baudrate);
457	cfsetispeed(&termios, termios_baudrate);
458	tcsetattr(uart_fd, TCSANOW, &termios);
459
460	if (debug) {
461		fprintf(stderr, "Done setting baudrate\n");
462	}
463}
464
465void
466proc_bdaddr()
467{
468	hci_send_cmd(hci_write_bd_addr, sizeof(hci_write_bd_addr));
469
470	read_event(uart_fd, buffer);
471}
472
473void
474proc_enable_lpm()
475{
476	hci_send_cmd(hci_write_sleep_mode, sizeof(hci_write_sleep_mode));
477
478	read_event(uart_fd, buffer);
479}
480
481void
482proc_enable_hci()
483{
484	int i = N_HCI;
485	int proto = HCI_UART_H4;
486	if (enable_lpm) {
487		proto = HCI_UART_LL;
488	}
489	if (ioctl(uart_fd, TIOCSETD, &i) < 0) {
490		fprintf(stderr, "Can't set line discipline\n");
491		return;
492	}
493
494	if (ioctl(uart_fd, HCIUARTSETPROTO, proto) < 0) {
495		fprintf(stderr, "Can't set hci protocol\n");
496		return;
497	}
498	fprintf(stderr, "Done setting line discpline\n");
499	return;
500}
501
502int
503main (int argc, char **argv)
504{
505	parse_cmd_line(argc, argv);
506
507	if (uart_fd < 0) {
508		exit(1);
509	}
510
511	init_uart();
512
513	proc_reset();
514
515	if (hcdfile_fd > 0) {
516		proc_patchram();
517	}
518
519	if (termios_baudrate) {
520		proc_baudrate();
521	}
522
523	if (bdaddr_flag) {
524		proc_bdaddr();
525	}
526
527	if (enable_hci) {
528		proc_enable_hci();
529	}
530
531	if (enable_lpm) {
532		proc_enable_lpm();
533	}
534
535	if (enable_hci) {
536		while (1) {
537			sleep(UINT_MAX);
538		}
539	}
540
541	exit(0);
542}
543