xref: /freebsd/usr.sbin/bluetooth/bcmfw/bcmfw.c (revision edf8578117e8844e02c0121147f45e4609b30680)
1 /*-
2  * bcmfw.c
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  *
6  * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $Id: bcmfw.c,v 1.4 2003/04/27 19:28:09 max Exp $
31  *
32  * Based on Linux BlueZ BlueFW-0.9 package
33  *
34  */
35 
36 #include <sys/types.h>
37 #include <sys/ioctl.h>
38 #include <dev/usb/usb.h>
39 #include <dev/usb/usb_ioctl.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <netgraph.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <syslog.h>
48 #include <unistd.h>
49 
50 #define BCMFW		"bcmfw"
51 #define BCMFW_INTR_EP	1
52 #define BCMFW_BULK_EP	2
53 #define BCMFW_BSIZE	4096
54 
55 #define USB_VENDOR_BROADCOM 0x0a5c
56 #define USB_PRODUCT_BROADCOM_BCM2033 0x2033
57 
58 static int bcmfw_check_device
59 	(char const *name);
60 static int bcmfw_load_firmware
61 	(char const *name, char const *md, char const *fw);
62 static void bcmfw_usage
63 	(void);
64 
65 /*
66  * Main
67  */
68 
69 int
70 main(int argc, char *argv[])
71 {
72 	char	*name = NULL, *md = NULL, *fw = NULL;
73 	int	 x;
74 
75 	while ((x = getopt(argc, argv, "f:hn:m:")) != -1) {
76 		switch (x) {
77 		case 'f': /* firmware file */
78 			fw = optarg;
79 			break;
80 
81 		case 'n': /* name */
82 			name = optarg;
83 			break;
84 
85 		case 'm': /* Mini-driver */
86 			md = optarg;
87 			break;
88 
89 		case 'h':
90 		default:
91 			bcmfw_usage();
92 			/* NOT REACHED */
93 		}
94 	}
95 
96 	if (name == NULL || md == NULL || fw == NULL)
97 		bcmfw_usage();
98 		/* NOT REACHED */
99 
100 	openlog(BCMFW, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_USER);
101 
102 	if (bcmfw_check_device(name) < 0)
103 		exit(1);
104 
105 	if (bcmfw_load_firmware(name, md, fw) < 0)
106 		exit(1);
107 
108 	closelog();
109 
110 	return (0);
111 } /* main */
112 
113 /*
114  * Check device VendorID/ProductID
115  */
116 
117 static int
118 bcmfw_check_device(char const *name)
119 {
120 	usb_device_descriptor_t	desc;
121 	char			path[BCMFW_BSIZE];
122 	int			fd = -1, error = -1;
123 
124 	snprintf(path, sizeof(path), "/dev/%s", name);
125 
126 	if ((fd = open(path, O_WRONLY)) < 0) {
127 		syslog(LOG_ERR, "Could not open(%s). %s (%d)",
128 				path, strerror(errno), errno);
129 		goto out;
130 	}
131 
132 	if (ioctl(fd, USB_GET_DEVICE_DESC, &desc) < 0) {
133 		syslog(LOG_ERR, "Could not ioctl(%d, %ld, %p). %s (%d)",
134 				fd, USB_GET_DEVICE_DESC, &desc,
135 				strerror(errno), errno);
136 		goto out;
137 	}
138 
139 	if (UGETW(desc.idVendor) != USB_VENDOR_BROADCOM ||
140 	    UGETW(desc.idProduct) != USB_PRODUCT_BROADCOM_BCM2033) {
141 		syslog(LOG_ERR, "Unsupported device, VendorID=%#x, " \
142 				"ProductID=%#x", UGETW(desc.idVendor),
143 				UGETW(desc.idProduct));
144 		error = -1;
145 	} else
146 		error = 0;
147 out:
148 	if (fd != -1)
149 		close(fd);
150 
151 	return (error);
152 } /* bcmfw_check_device */
153 
154 /*
155  * Download minidriver and firmware
156  */
157 
158 static int
159 bcmfw_load_firmware(char const *name, char const *md, char const *fw)
160 {
161 	char	buf[BCMFW_BSIZE];
162 	int	intr = -1, bulk = -1, fd = -1, error = -1, len;
163 
164 	/* Open interrupt endpoint device */
165 	snprintf(buf, sizeof(buf), "/dev/%s.%d", name, BCMFW_INTR_EP);
166 	if ((intr = open(buf, O_RDONLY)) < 0) {
167 		syslog(LOG_ERR, "Could not open(%s). %s (%d)",
168 				buf, strerror(errno), errno);
169 		goto out;
170 	}
171 
172 	/* Open bulk endpoint device */
173 	snprintf(buf, sizeof(buf), "/dev/%s.%d", name, BCMFW_BULK_EP);
174 	if ((bulk = open(buf, O_WRONLY)) < 0) {
175 		syslog(LOG_ERR, "Could not open(%s). %s (%d)",
176 				buf, strerror(errno), errno);
177 		goto out;
178 	}
179 
180 	/*
181 	 * Load mini-driver
182 	 */
183 
184 	if ((fd = open(md, O_RDONLY)) < 0) {
185 		syslog(LOG_ERR, "Could not open(%s). %s (%d)",
186 				md, strerror(errno), errno);
187 		goto out;
188 	}
189 
190 	for (;;) {
191 		len = read(fd, buf, sizeof(buf));
192 		if (len < 0) {
193 			syslog(LOG_ERR, "Could not read(%s). %s (%d)",
194 					md, strerror(errno), errno);
195 			goto out;
196 		}
197 		if (len == 0)
198 			break;
199 
200 		len = write(bulk, buf, len);
201 		if (len < 0) {
202 			syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)",
203 					name, BCMFW_BULK_EP, strerror(errno),
204 					errno);
205 			goto out;
206 		}
207 	}
208 
209 	close(fd);
210 	fd = -1;
211 
212 	usleep(10);
213 
214 	/*
215 	 * Memory select
216 	 */
217 
218 	if (write(bulk, "#", 1) < 0) {
219 		syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)",
220 				name, BCMFW_BULK_EP, strerror(errno), errno);
221 		goto out;
222 	}
223 
224 	if (read(intr, buf, sizeof(buf)) < 0) {
225 		syslog(LOG_ERR, "Could not read(/dev/%s.%d). %s (%d)",
226 				name, BCMFW_INTR_EP, strerror(errno), errno);
227 		goto out;
228 	}
229 
230 	if (buf[0] != '#') {
231 		syslog(LOG_ERR, "%s: Memory select failed (%c)", name, buf[0]);
232 		goto out;
233 	}
234 
235 	/*
236 	 * Load firmware
237 	 */
238 
239 	if ((fd = open(fw, O_RDONLY)) < 0) {
240 		syslog(LOG_ERR, "Could not open(%s). %s (%d)",
241 				fw, strerror(errno), errno);
242 		goto out;
243 	}
244 
245 	for (;;) {
246 		len = read(fd, buf, sizeof(buf));
247 		if (len < 0) {
248 			syslog(LOG_ERR, "Could not read(%s). %s (%d)",
249 					fw, strerror(errno), errno);
250 			goto out;
251 		}
252 		if (len == 0)
253 			break;
254 
255 		len = write(bulk, buf, len);
256 		if (len < 0) {
257 			syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)",
258 					name, BCMFW_BULK_EP, strerror(errno),
259 					errno);
260 			goto out;
261 		}
262 	}
263 
264 	close(fd);
265 	fd = -1;
266 
267 	if (read(intr, buf, sizeof(buf)) < 0) {
268 		syslog(LOG_ERR, "Could not read(/dev/%s.%d). %s (%d)",
269 				name, BCMFW_INTR_EP, strerror(errno), errno);
270 		goto out;
271 	}
272 
273 	if (buf[0] != '.') {
274 		syslog(LOG_ERR, "%s: Could not load firmware (%c)",
275 				name, buf[0]);
276 		goto out;
277 	}
278 
279 	usleep(500000);
280 	error = 0;
281 out:
282 	if (fd != -1)
283 		close(fd);
284 	if (bulk != -1)
285 		close(bulk);
286 	if (intr != -1)
287 		close(intr);
288 
289 	return (error);
290 } /* bcmfw_load_firmware */
291 
292 /*
293  * Display usage message and quit
294  */
295 
296 static void
297 bcmfw_usage(void)
298 {
299 	fprintf(stdout,
300 "Usage: %s -n name -m md_file -f fw_file\n"
301 "Where:\n" \
302 "\t-n name              device name\n" \
303 "\t-m mini-driver       image mini-driver image file name for download\n" \
304 "\t-f firmware image    firmware image file name for download\n" \
305 "\t-h                   display this message\n", BCMFW);
306 
307 	exit(255);
308 } /* bcmfw_usage */
309 
310