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$
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>
38188945Sthompsa#include <dev/usb/usb_ioctl.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
54131120Simp#define USB_VENDOR_BROADCOM 0x0a5c
55131120Simp#define USB_PRODUCT_BROADCOM_BCM2033 0x2033
56131120Simp
57114881Sjulianstatic int bcmfw_check_device
58114881Sjulian	(char const *name);
59114881Sjulianstatic int bcmfw_load_firmware
60114881Sjulian	(char const *name, char const *md, char const *fw);
61114881Sjulianstatic void bcmfw_usage
62114881Sjulian	(void);
63114881Sjulian
64114881Sjulian/*
65114881Sjulian * Main
66114881Sjulian */
67114881Sjulian
68114881Sjulianint
69114881Sjulianmain(int argc, char *argv[])
70114881Sjulian{
71114881Sjulian	char	*name = NULL, *md = NULL, *fw = NULL;
72114881Sjulian	int	 x;
73114881Sjulian
74114881Sjulian	while ((x = getopt(argc, argv, "f:hn:m:")) != -1) {
75114881Sjulian		switch (x) {
76114881Sjulian		case 'f': /* firmware file */
77114881Sjulian			fw = optarg;
78114881Sjulian			break;
79114881Sjulian
80114881Sjulian		case 'n': /* name */
81114881Sjulian			name = optarg;
82114881Sjulian			break;
83114881Sjulian
84114881Sjulian		case 'm': /* Mini-driver */
85114881Sjulian			md = optarg;
86114881Sjulian			break;
87114881Sjulian
88114881Sjulian		case 'h':
89114881Sjulian		default:
90114881Sjulian			bcmfw_usage();
91114881Sjulian			/* NOT REACHED */
92114881Sjulian		}
93114881Sjulian	}
94114881Sjulian
95114881Sjulian	if (name == NULL || md == NULL || fw == NULL)
96114881Sjulian		bcmfw_usage();
97114881Sjulian		/* NOT REACHED */
98114881Sjulian
99114881Sjulian	openlog(BCMFW, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_USER);
100114881Sjulian
101114881Sjulian	if (bcmfw_check_device(name) < 0)
102114881Sjulian		exit(1);
103114881Sjulian
104114881Sjulian	if (bcmfw_load_firmware(name, md, fw) < 0)
105114881Sjulian		exit(1);
106114881Sjulian
107114881Sjulian	closelog();
108114881Sjulian
109114881Sjulian	return (0);
110114881Sjulian} /* main */
111114881Sjulian
112114881Sjulian/*
113114881Sjulian * Check device VendorID/ProductID
114114881Sjulian */
115114881Sjulian
116114881Sjulianstatic int
117114881Sjulianbcmfw_check_device(char const *name)
118114881Sjulian{
119114881Sjulian	usb_device_descriptor_t	desc;
120114881Sjulian	char			path[BCMFW_BSIZE];
121114881Sjulian	int			fd = -1, error = -1;
122114881Sjulian
123114881Sjulian	snprintf(path, sizeof(path), "/dev/%s", name);
124114881Sjulian
125114881Sjulian	if ((fd = open(path, O_WRONLY)) < 0) {
126114881Sjulian		syslog(LOG_ERR, "Could not open(%s). %s (%d)",
127114881Sjulian				path, strerror(errno), errno);
128114881Sjulian		goto out;
129114881Sjulian	}
130114881Sjulian
131114881Sjulian	if (ioctl(fd, USB_GET_DEVICE_DESC, &desc) < 0) {
132114881Sjulian		syslog(LOG_ERR, "Could not ioctl(%d, %ld, %p). %s (%d)",
133114881Sjulian				fd, USB_GET_DEVICE_DESC, &desc,
134114881Sjulian				strerror(errno), errno);
135114881Sjulian		goto out;
136114881Sjulian	}
137114881Sjulian
138114881Sjulian	if (UGETW(desc.idVendor) != USB_VENDOR_BROADCOM ||
139131120Simp	    UGETW(desc.idProduct) != USB_PRODUCT_BROADCOM_BCM2033) {
140114881Sjulian		syslog(LOG_ERR, "Unsupported device, VendorID=%#x, " \
141114881Sjulian				"ProductID=%#x", UGETW(desc.idVendor),
142114881Sjulian				UGETW(desc.idProduct));
143114881Sjulian		error = -1;
144114881Sjulian	} else
145114881Sjulian		error = 0;
146114881Sjulianout:
147114881Sjulian	if (fd != -1)
148114881Sjulian		close(fd);
149114881Sjulian
150114881Sjulian	return (error);
151114881Sjulian} /* bcmfw_check_device */
152114881Sjulian
153114881Sjulian/*
154114881Sjulian * Download minidriver and firmware
155114881Sjulian */
156114881Sjulian
157114881Sjulianstatic int
158114881Sjulianbcmfw_load_firmware(char const *name, char const *md, char const *fw)
159114881Sjulian{
160114881Sjulian	char	buf[BCMFW_BSIZE];
161114881Sjulian	int	intr = -1, bulk = -1, fd = -1, error = -1, len;
162114881Sjulian
163114881Sjulian	/* Open interrupt endpoint device */
164114881Sjulian	snprintf(buf, sizeof(buf), "/dev/%s.%d", name, BCMFW_INTR_EP);
165114881Sjulian	if ((intr = open(buf, O_RDONLY)) < 0) {
166114881Sjulian		syslog(LOG_ERR, "Could not open(%s). %s (%d)",
167114881Sjulian				buf, strerror(errno), errno);
168114881Sjulian		goto out;
169114881Sjulian	}
170114881Sjulian
171114881Sjulian	/* Open bulk endpoint device */
172114881Sjulian	snprintf(buf, sizeof(buf), "/dev/%s.%d", name, BCMFW_BULK_EP);
173114881Sjulian	if ((bulk = open(buf, O_WRONLY)) < 0) {
174114881Sjulian		syslog(LOG_ERR, "Could not open(%s). %s (%d)",
175114881Sjulian				buf, strerror(errno), errno);
176114881Sjulian		goto out;
177114881Sjulian	}
178114881Sjulian
179114881Sjulian	/*
180114881Sjulian	 * Load mini-driver
181114881Sjulian	 */
182114881Sjulian
183114881Sjulian	if ((fd = open(md, O_RDONLY)) < 0) {
184114881Sjulian		syslog(LOG_ERR, "Could not open(%s). %s (%d)",
185114881Sjulian				md, strerror(errno), errno);
186114881Sjulian		goto out;
187114881Sjulian	}
188114881Sjulian
189114881Sjulian	for (;;) {
190114881Sjulian		len = read(fd, buf, sizeof(buf));
191114881Sjulian		if (len < 0) {
192114881Sjulian			syslog(LOG_ERR, "Could not read(%s). %s (%d)",
193114881Sjulian					md, strerror(errno), errno);
194114881Sjulian			goto out;
195114881Sjulian		}
196114881Sjulian		if (len == 0)
197114881Sjulian			break;
198114881Sjulian
199114881Sjulian		len = write(bulk, buf, len);
200114881Sjulian		if (len < 0) {
201114881Sjulian			syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)",
202114881Sjulian					name, BCMFW_BULK_EP, strerror(errno),
203114881Sjulian					errno);
204114881Sjulian			goto out;
205114881Sjulian		}
206114881Sjulian	}
207114881Sjulian
208114881Sjulian	close(fd);
209114881Sjulian	fd = -1;
210114881Sjulian
211114881Sjulian	usleep(10);
212114881Sjulian
213114881Sjulian	/*
214114881Sjulian	 * Memory select
215114881Sjulian	 */
216114881Sjulian
217114881Sjulian	if (write(bulk, "#", 1) < 0) {
218114881Sjulian		syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)",
219114881Sjulian				name, BCMFW_BULK_EP, strerror(errno), errno);
220114881Sjulian		goto out;
221114881Sjulian	}
222114881Sjulian
223114881Sjulian	if (read(intr, buf, sizeof(buf)) < 0) {
224114881Sjulian		syslog(LOG_ERR, "Could not read(/dev/%s.%d). %s (%d)",
225114881Sjulian				name, BCMFW_INTR_EP, strerror(errno), errno);
226114881Sjulian		goto out;
227114881Sjulian	}
228114881Sjulian
229114881Sjulian	if (buf[0] != '#') {
230114881Sjulian		syslog(LOG_ERR, "%s: Memory select failed (%c)", name, buf[0]);
231114881Sjulian		goto out;
232114881Sjulian	}
233114881Sjulian
234114881Sjulian	/*
235114881Sjulian	 * Load firmware
236114881Sjulian	 */
237114881Sjulian
238114881Sjulian	if ((fd = open(fw, O_RDONLY)) < 0) {
239114881Sjulian		syslog(LOG_ERR, "Could not open(%s). %s (%d)",
240114881Sjulian				fw, strerror(errno), errno);
241114881Sjulian		goto out;
242114881Sjulian	}
243114881Sjulian
244114881Sjulian	for (;;) {
245114881Sjulian		len = read(fd, buf, sizeof(buf));
246114881Sjulian		if (len < 0) {
247114881Sjulian			syslog(LOG_ERR, "Could not read(%s). %s (%d)",
248114881Sjulian					fw, strerror(errno), errno);
249114881Sjulian			goto out;
250114881Sjulian		}
251114881Sjulian		if (len == 0)
252114881Sjulian			break;
253114881Sjulian
254114881Sjulian		len = write(bulk, buf, len);
255114881Sjulian		if (len < 0) {
256114881Sjulian			syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)",
257114881Sjulian					name, BCMFW_BULK_EP, strerror(errno),
258114881Sjulian					errno);
259114881Sjulian			goto out;
260114881Sjulian		}
261114881Sjulian	}
262114881Sjulian
263114881Sjulian	close(fd);
264114881Sjulian	fd = -1;
265114881Sjulian
266114881Sjulian	if (read(intr, buf, sizeof(buf)) < 0) {
267114881Sjulian		syslog(LOG_ERR, "Could not read(/dev/%s.%d). %s (%d)",
268114881Sjulian				name, BCMFW_INTR_EP, strerror(errno), errno);
269114881Sjulian		goto out;
270114881Sjulian	}
271114881Sjulian
272114881Sjulian	if (buf[0] != '.') {
273114881Sjulian		syslog(LOG_ERR, "%s: Could not load firmware (%c)",
274114881Sjulian				name, buf[0]);
275114881Sjulian		goto out;
276114881Sjulian	}
277114881Sjulian
278114881Sjulian	usleep(500000);
279114881Sjulian	error = 0;
280114881Sjulianout:
281114881Sjulian	if (fd != -1)
282114881Sjulian		close(fd);
283114881Sjulian	if (bulk != -1)
284114881Sjulian		close(bulk);
285114881Sjulian	if (intr != -1)
286114881Sjulian		close(intr);
287114881Sjulian
288114881Sjulian	return (error);
289114881Sjulian} /* bcmfw_load_firmware */
290114881Sjulian
291114881Sjulian/*
292114881Sjulian * Display usage message and quit
293114881Sjulian */
294114881Sjulian
295114881Sjulianstatic void
296114881Sjulianbcmfw_usage(void)
297114881Sjulian{
298114881Sjulian	fprintf(stdout,
299114881Sjulian"Usage: %s -n name -m md_file -f fw_file\n"
300114881Sjulian"Where:\n" \
301114881Sjulian"\t-n name              device name\n" \
302114881Sjulian"\t-m mini-driver       image mini-driver image file name for download\n" \
303114881Sjulian"\t-f firmware image    firmware image file name for download\n" \
304114881Sjulian"\t-h                   display this message\n", BCMFW);
305114881Sjulian
306114881Sjulian	exit(255);
307114881Sjulian} /* bcmfw_usage */
308114881Sjulian
309