bcmfw.c revision 114881
1114881Sjulian/*
2114881Sjulian * bcmfw.c
3114881Sjulian *
4114881Sjulian * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
5114881Sjulian * All rights reserved.
6114881Sjulian *
7114881Sjulian * Redistribution and use in source and binary forms, with or without
8114881Sjulian * modification, are permitted provided that the following conditions
9114881Sjulian * are met:
10114881Sjulian * 1. Redistributions of source code must retain the above copyright
11114881Sjulian *    notice, this list of conditions and the following disclaimer.
12114881Sjulian * 2. Redistributions in binary form must reproduce the above copyright
13114881Sjulian *    notice, this list of conditions and the following disclaimer in the
14114881Sjulian *    documentation and/or other materials provided with the distribution.
15114881Sjulian *
16114881Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17114881Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18114881Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19114881Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20114881Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21114881Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22114881Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23114881Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24114881Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25114881Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26114881Sjulian * SUCH DAMAGE.
27114881Sjulian *
28114881Sjulian * $Id: bcmfw.c,v 1.4 2003/04/27 19:28:09 max Exp $
29114881Sjulian * $FreeBSD: head/usr.sbin/bluetooth/bcmfw/bcmfw.c 114881 2003-05-10 22:03:45Z julian $
30114881Sjulian *
31114881Sjulian * Based on Linux BlueZ BlueFW-0.9 package
32114881Sjulian *
33114881Sjulian */
34114881Sjulian
35114881Sjulian#include <sys/types.h>
36114881Sjulian#include <sys/ioctl.h>
37114881Sjulian#include <dev/usb/usb.h>
38114881Sjulian#include <dev/usb/usbdevs.h>
39114881Sjulian#include <errno.h>
40114881Sjulian#include <fcntl.h>
41114881Sjulian#include <netgraph.h>
42114881Sjulian#include <stdarg.h>
43114881Sjulian#include <stdio.h>
44114881Sjulian#include <stdlib.h>
45114881Sjulian#include <string.h>
46114881Sjulian#include <syslog.h>
47114881Sjulian#include <unistd.h>
48114881Sjulian
49114881Sjulian#define BCMFW		"bcmfw"
50114881Sjulian#define BCMFW_INTR_EP	1
51114881Sjulian#define BCMFW_BULK_EP	2
52114881Sjulian#define BCMFW_BSIZE	4096
53114881Sjulian
54114881Sjulianstatic int bcmfw_check_device
55114881Sjulian	(char const *name);
56114881Sjulianstatic int bcmfw_load_firmware
57114881Sjulian	(char const *name, char const *md, char const *fw);
58114881Sjulianstatic void bcmfw_usage
59114881Sjulian	(void);
60114881Sjulian
61114881Sjulian/*
62114881Sjulian * Main
63114881Sjulian */
64114881Sjulian
65114881Sjulianint
66114881Sjulianmain(int argc, char *argv[])
67114881Sjulian{
68114881Sjulian	char	*name = NULL, *md = NULL, *fw = NULL;
69114881Sjulian	int	 x;
70114881Sjulian
71114881Sjulian	while ((x = getopt(argc, argv, "f:hn:m:")) != -1) {
72114881Sjulian		switch (x) {
73114881Sjulian		case 'f': /* firmware file */
74114881Sjulian			fw = optarg;
75114881Sjulian			break;
76114881Sjulian
77114881Sjulian		case 'n': /* name */
78114881Sjulian			name = optarg;
79114881Sjulian			break;
80114881Sjulian
81114881Sjulian		case 'm': /* Mini-driver */
82114881Sjulian			md = optarg;
83114881Sjulian			break;
84114881Sjulian
85114881Sjulian		case 'h':
86114881Sjulian		default:
87114881Sjulian			bcmfw_usage();
88114881Sjulian			/* NOT REACHED */
89114881Sjulian		}
90114881Sjulian	}
91114881Sjulian
92114881Sjulian	if (name == NULL || md == NULL || fw == NULL)
93114881Sjulian		bcmfw_usage();
94114881Sjulian		/* NOT REACHED */
95114881Sjulian
96114881Sjulian	openlog(BCMFW, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_USER);
97114881Sjulian
98114881Sjulian	if (bcmfw_check_device(name) < 0)
99114881Sjulian		exit(1);
100114881Sjulian
101114881Sjulian	if (bcmfw_load_firmware(name, md, fw) < 0)
102114881Sjulian		exit(1);
103114881Sjulian
104114881Sjulian	closelog();
105114881Sjulian
106114881Sjulian	return (0);
107114881Sjulian} /* main */
108114881Sjulian
109114881Sjulian/*
110114881Sjulian * Check device VendorID/ProductID
111114881Sjulian */
112114881Sjulian
113114881Sjulianstatic int
114114881Sjulianbcmfw_check_device(char const *name)
115114881Sjulian{
116114881Sjulian	usb_device_descriptor_t	desc;
117114881Sjulian	char			path[BCMFW_BSIZE];
118114881Sjulian	int			fd = -1, error = -1;
119114881Sjulian
120114881Sjulian	snprintf(path, sizeof(path), "/dev/%s", name);
121114881Sjulian
122114881Sjulian	if ((fd = open(path, O_WRONLY)) < 0) {
123114881Sjulian		syslog(LOG_ERR, "Could not open(%s). %s (%d)",
124114881Sjulian				path, strerror(errno), errno);
125114881Sjulian		goto out;
126114881Sjulian	}
127114881Sjulian
128114881Sjulian	if (ioctl(fd, USB_GET_DEVICE_DESC, &desc) < 0) {
129114881Sjulian		syslog(LOG_ERR, "Could not ioctl(%d, %ld, %p). %s (%d)",
130114881Sjulian				fd, USB_GET_DEVICE_DESC, &desc,
131114881Sjulian				strerror(errno), errno);
132114881Sjulian		goto out;
133114881Sjulian	}
134114881Sjulian
135114881Sjulian	if (UGETW(desc.idVendor) != USB_VENDOR_BROADCOM ||
136114881Sjulian	    UGETW(desc.idProduct) != 0x2033) {
137114881Sjulian		syslog(LOG_ERR, "Unsupported device, VendorID=%#x, " \
138114881Sjulian				"ProductID=%#x", UGETW(desc.idVendor),
139114881Sjulian				UGETW(desc.idProduct));
140114881Sjulian		error = -1;
141114881Sjulian	} else
142114881Sjulian		error = 0;
143114881Sjulianout:
144114881Sjulian	if (fd != -1)
145114881Sjulian		close(fd);
146114881Sjulian
147114881Sjulian	return (error);
148114881Sjulian} /* bcmfw_check_device */
149114881Sjulian
150114881Sjulian/*
151114881Sjulian * Download minidriver and firmware
152114881Sjulian */
153114881Sjulian
154114881Sjulianstatic int
155114881Sjulianbcmfw_load_firmware(char const *name, char const *md, char const *fw)
156114881Sjulian{
157114881Sjulian	char	buf[BCMFW_BSIZE];
158114881Sjulian	int	intr = -1, bulk = -1, fd = -1, error = -1, len;
159114881Sjulian
160114881Sjulian	/* Open interrupt endpoint device */
161114881Sjulian	snprintf(buf, sizeof(buf), "/dev/%s.%d", name, BCMFW_INTR_EP);
162114881Sjulian	if ((intr = open(buf, O_RDONLY)) < 0) {
163114881Sjulian		syslog(LOG_ERR, "Could not open(%s). %s (%d)",
164114881Sjulian				buf, strerror(errno), errno);
165114881Sjulian		goto out;
166114881Sjulian	}
167114881Sjulian
168114881Sjulian	/* Open bulk endpoint device */
169114881Sjulian	snprintf(buf, sizeof(buf), "/dev/%s.%d", name, BCMFW_BULK_EP);
170114881Sjulian	if ((bulk = open(buf, O_WRONLY)) < 0) {
171114881Sjulian		syslog(LOG_ERR, "Could not open(%s). %s (%d)",
172114881Sjulian				buf, strerror(errno), errno);
173114881Sjulian		goto out;
174114881Sjulian	}
175114881Sjulian
176114881Sjulian	/*
177114881Sjulian	 * Load mini-driver
178114881Sjulian	 */
179114881Sjulian
180114881Sjulian	if ((fd = open(md, O_RDONLY)) < 0) {
181114881Sjulian		syslog(LOG_ERR, "Could not open(%s). %s (%d)",
182114881Sjulian				md, strerror(errno), errno);
183114881Sjulian		goto out;
184114881Sjulian	}
185114881Sjulian
186114881Sjulian	for (;;) {
187114881Sjulian		len = read(fd, buf, sizeof(buf));
188114881Sjulian		if (len < 0) {
189114881Sjulian			syslog(LOG_ERR, "Could not read(%s). %s (%d)",
190114881Sjulian					md, strerror(errno), errno);
191114881Sjulian			goto out;
192114881Sjulian		}
193114881Sjulian		if (len == 0)
194114881Sjulian			break;
195114881Sjulian
196114881Sjulian		len = write(bulk, buf, len);
197114881Sjulian		if (len < 0) {
198114881Sjulian			syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)",
199114881Sjulian					name, BCMFW_BULK_EP, strerror(errno),
200114881Sjulian					errno);
201114881Sjulian			goto out;
202114881Sjulian		}
203114881Sjulian	}
204114881Sjulian
205114881Sjulian	close(fd);
206114881Sjulian	fd = -1;
207114881Sjulian
208114881Sjulian	usleep(10);
209114881Sjulian
210114881Sjulian	/*
211114881Sjulian	 * Memory select
212114881Sjulian	 */
213114881Sjulian
214114881Sjulian	if (write(bulk, "#", 1) < 0) {
215114881Sjulian		syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)",
216114881Sjulian				name, BCMFW_BULK_EP, strerror(errno), errno);
217114881Sjulian		goto out;
218114881Sjulian	}
219114881Sjulian
220114881Sjulian	if (read(intr, buf, sizeof(buf)) < 0) {
221114881Sjulian		syslog(LOG_ERR, "Could not read(/dev/%s.%d). %s (%d)",
222114881Sjulian				name, BCMFW_INTR_EP, strerror(errno), errno);
223114881Sjulian		goto out;
224114881Sjulian	}
225114881Sjulian
226114881Sjulian	if (buf[0] != '#') {
227114881Sjulian		syslog(LOG_ERR, "%s: Memory select failed (%c)", name, buf[0]);
228114881Sjulian		goto out;
229114881Sjulian	}
230114881Sjulian
231114881Sjulian	/*
232114881Sjulian	 * Load firmware
233114881Sjulian	 */
234114881Sjulian
235114881Sjulian	if ((fd = open(fw, O_RDONLY)) < 0) {
236114881Sjulian		syslog(LOG_ERR, "Could not open(%s). %s (%d)",
237114881Sjulian				fw, strerror(errno), errno);
238114881Sjulian		goto out;
239114881Sjulian	}
240114881Sjulian
241114881Sjulian	for (;;) {
242114881Sjulian		len = read(fd, buf, sizeof(buf));
243114881Sjulian		if (len < 0) {
244114881Sjulian			syslog(LOG_ERR, "Could not read(%s). %s (%d)",
245114881Sjulian					fw, strerror(errno), errno);
246114881Sjulian			goto out;
247114881Sjulian		}
248114881Sjulian		if (len == 0)
249114881Sjulian			break;
250114881Sjulian
251114881Sjulian		len = write(bulk, buf, len);
252114881Sjulian		if (len < 0) {
253114881Sjulian			syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)",
254114881Sjulian					name, BCMFW_BULK_EP, strerror(errno),
255114881Sjulian					errno);
256114881Sjulian			goto out;
257114881Sjulian		}
258114881Sjulian	}
259114881Sjulian
260114881Sjulian	close(fd);
261114881Sjulian	fd = -1;
262114881Sjulian
263114881Sjulian	if (read(intr, buf, sizeof(buf)) < 0) {
264114881Sjulian		syslog(LOG_ERR, "Could not read(/dev/%s.%d). %s (%d)",
265114881Sjulian				name, BCMFW_INTR_EP, strerror(errno), errno);
266114881Sjulian		goto out;
267114881Sjulian	}
268114881Sjulian
269114881Sjulian	if (buf[0] != '.') {
270114881Sjulian		syslog(LOG_ERR, "%s: Could not load firmware (%c)",
271114881Sjulian				name, buf[0]);
272114881Sjulian		goto out;
273114881Sjulian	}
274114881Sjulian
275114881Sjulian	usleep(500000);
276114881Sjulian	error = 0;
277114881Sjulianout:
278114881Sjulian	if (fd != -1)
279114881Sjulian		close(fd);
280114881Sjulian	if (bulk != -1)
281114881Sjulian		close(bulk);
282114881Sjulian	if (intr != -1)
283114881Sjulian		close(intr);
284114881Sjulian
285114881Sjulian	return (error);
286114881Sjulian} /* bcmfw_load_firmware */
287114881Sjulian
288114881Sjulian/*
289114881Sjulian * Display usage message and quit
290114881Sjulian */
291114881Sjulian
292114881Sjulianstatic void
293114881Sjulianbcmfw_usage(void)
294114881Sjulian{
295114881Sjulian	fprintf(stdout,
296114881Sjulian"Usage: %s -n name -m md_file -f fw_file\n"
297114881Sjulian"Where:\n" \
298114881Sjulian"\t-n name              device name\n" \
299114881Sjulian"\t-m mini-driver       image mini-driver image file name for download\n" \
300114881Sjulian"\t-f firmware image    firmware image file name for download\n" \
301114881Sjulian"\t-h                   display this message\n", BCMFW);
302114881Sjulian
303114881Sjulian	exit(255);
304114881Sjulian} /* bcmfw_usage */
305114881Sjulian
306