xref: /freebsd/usr.sbin/bluetooth/iwmbtfw/main.c (revision cfd6422a5217410fbd66f7a7a8a64d9d85e61229)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
5  * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30 
31 #include <sys/param.h>
32 #include <sys/stat.h>
33 #include <sys/endian.h>
34 
35 #include <err.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <libgen.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 
44 #include <libusb.h>
45 
46 #include "iwmbt_fw.h"
47 #include "iwmbt_hw.h"
48 #include "iwmbt_dbg.h"
49 
50 #define	_DEFAULT_IWMBT_FIRMWARE_PATH	"/usr/share/firmware/intel"
51 
52 int	iwmbt_do_debug = 0;
53 int	iwmbt_do_info = 0;
54 
55 struct iwmbt_devid {
56 	uint16_t product_id;
57 	uint16_t vendor_id;
58 };
59 
60 static struct iwmbt_devid iwmbt_list[] = {
61 
62 	/* Intel Wireless 8260/8265 and successors */
63 	{ .vendor_id = 0x8087, .product_id = 0x0a2b },
64 	{ .vendor_id = 0x8087, .product_id = 0x0aaa },
65 	{ .vendor_id = 0x8087, .product_id = 0x0025 },
66 	{ .vendor_id = 0x8087, .product_id = 0x0026 },
67 	{ .vendor_id = 0x8087, .product_id = 0x0029 },
68 };
69 
70 static int
71 iwmbt_is_8260(struct libusb_device_descriptor *d)
72 {
73 	int i;
74 
75 	/* Search looking for whether it's an 8260/8265 */
76 	for (i = 0; i < (int) nitems(iwmbt_list); i++) {
77 		if ((iwmbt_list[i].product_id == d->idProduct) &&
78 		    (iwmbt_list[i].vendor_id == d->idVendor)) {
79 			iwmbt_info("found 8260/8265");
80 			return (1);
81 		}
82 	}
83 
84 	/* Not found */
85 	return (0);
86 }
87 
88 static libusb_device *
89 iwmbt_find_device(libusb_context *ctx, int bus_id, int dev_id)
90 {
91 	libusb_device **list, *dev = NULL, *found = NULL;
92 	struct libusb_device_descriptor d;
93 	ssize_t cnt, i;
94 	int r;
95 
96 	cnt = libusb_get_device_list(ctx, &list);
97 	if (cnt < 0) {
98 		iwmbt_err("libusb_get_device_list() failed: code %lld",
99 		    (long long int) cnt);
100 		return (NULL);
101 	}
102 
103 	/*
104 	 * Scan through USB device list.
105 	 */
106 	for (i = 0; i < cnt; i++) {
107 		dev = list[i];
108 		if (bus_id == libusb_get_bus_number(dev) &&
109 		    dev_id == libusb_get_device_address(dev)) {
110 			/* Get the device descriptor for this device entry */
111 			r = libusb_get_device_descriptor(dev, &d);
112 			if (r != 0) {
113 				iwmbt_err("libusb_get_device_descriptor: %s",
114 				    libusb_strerror(r));
115 				break;
116 			}
117 
118 			/* Match on the vendor/product id */
119 			if (iwmbt_is_8260(&d)) {
120 				/*
121 				 * Take a reference so it's not freed later on.
122 				 */
123 				found = libusb_ref_device(dev);
124 				break;
125 			}
126 		}
127 	}
128 
129 	libusb_free_device_list(list, 1);
130 	return (found);
131 }
132 
133 static void
134 iwmbt_dump_version(struct iwmbt_version *ver)
135 {
136 	iwmbt_info("status       0x%02x", ver->status);
137 	iwmbt_info("hw_platform  0x%02x", ver->hw_platform);
138 	iwmbt_info("hw_variant   0x%02x", ver->hw_variant);
139 	iwmbt_info("hw_revision  0x%02x", ver->hw_revision);
140 	iwmbt_info("fw_variant   0x%02x", ver->fw_variant);
141 	iwmbt_info("fw_revision  0x%02x", ver->fw_revision);
142 	iwmbt_info("fw_build_num 0x%02x", ver->fw_build_num);
143 	iwmbt_info("fw_build_ww  0x%02x", ver->fw_build_ww);
144 	iwmbt_info("fw_build_yy  0x%02x", ver->fw_build_yy);
145 	iwmbt_info("fw_patch_num 0x%02x", ver->fw_patch_num);
146 }
147 
148 static void
149 iwmbt_dump_boot_params(struct iwmbt_boot_params *params)
150 {
151 	iwmbt_info("Device revision: %u", le16toh(params->dev_revid));
152 	iwmbt_info("Secure Boot:  %s", params->secure_boot ? "on" : "off");
153 	iwmbt_info("OTP lock:     %s", params->otp_lock    ? "on" : "off");
154 	iwmbt_info("API lock:     %s", params->api_lock    ? "on" : "off");
155 	iwmbt_info("Debug lock:   %s", params->debug_lock  ? "on" : "off");
156 	iwmbt_info("Minimum firmware build %u week %u year %u",
157 	    params->min_fw_build_nn,
158 	    params->min_fw_build_cw,
159 	    2000 + params->min_fw_build_yy);
160 	iwmbt_info("OTC BD_ADDR:  %02x:%02x:%02x:%02x:%02x:%02x",
161 	    params->otp_bdaddr[5],
162 	    params->otp_bdaddr[4],
163 	    params->otp_bdaddr[3],
164 	    params->otp_bdaddr[2],
165 	    params->otp_bdaddr[1],
166 	    params->otp_bdaddr[0]);
167 }
168 
169 static int
170 iwmbt_init_firmware(libusb_device_handle *hdl, const char *firmware_path,
171     uint32_t *boot_param)
172 {
173 	struct iwmbt_firmware fw;
174 	int ret;
175 
176 	iwmbt_debug("loading %s", firmware_path);
177 
178 	/* Read in the firmware */
179 	if (iwmbt_fw_read(&fw, firmware_path) <= 0) {
180 		iwmbt_debug("iwmbt_fw_read() failed");
181 		return (-1);
182 	}
183 
184 	/* Load in the firmware */
185 	ret = iwmbt_load_fwfile(hdl, &fw, boot_param);
186 	if (ret < 0)
187 		iwmbt_debug("Loading firmware file failed");
188 
189 	/* free it */
190 	iwmbt_fw_free(&fw);
191 
192 	return (ret);
193 }
194 
195 static int
196 iwmbt_init_ddc(libusb_device_handle *hdl, const char *ddc_path)
197 {
198 	struct iwmbt_firmware ddc;
199 	int ret;
200 
201 	iwmbt_debug("loading %s", ddc_path);
202 
203 	/* Read in the DDC file */
204 	if (iwmbt_fw_read(&ddc, ddc_path) <= 0) {
205 		iwmbt_debug("iwmbt_fw_read() failed");
206 		return (-1);
207 	}
208 
209 	/* Load in the DDC file */
210 	ret = iwmbt_load_ddc(hdl, &ddc);
211 	if (ret < 0)
212 		iwmbt_debug("Loading DDC file failed");
213 
214 	/* free it */
215 	iwmbt_fw_free(&ddc);
216 
217 	return (ret);
218 }
219 
220 /*
221  * Parse ugen name and extract device's bus and address
222  */
223 
224 static int
225 parse_ugen_name(char const *ugen, uint8_t *bus, uint8_t *addr)
226 {
227 	char *ep;
228 
229 	if (strncmp(ugen, "ugen", 4) != 0)
230 		return (-1);
231 
232 	*bus = (uint8_t) strtoul(ugen + 4, &ep, 10);
233 	if (*ep != '.')
234 		return (-1);
235 
236 	*addr = (uint8_t) strtoul(ep + 1, &ep, 10);
237 	if (*ep != '\0')
238 		return (-1);
239 
240 	return (0);
241 }
242 
243 static void
244 usage(void)
245 {
246 	fprintf(stderr,
247 	    "Usage: iwmbtfw (-D) -d ugenX.Y (-f firmware path) (-I)\n");
248 	fprintf(stderr, "    -D: enable debugging\n");
249 	fprintf(stderr, "    -d: device to operate upon\n");
250 	fprintf(stderr, "    -f: firmware path, if not default\n");
251 	fprintf(stderr, "    -I: enable informational output\n");
252 	exit(127);
253 }
254 
255 int
256 main(int argc, char *argv[])
257 {
258 	libusb_context *ctx = NULL;
259 	libusb_device *dev = NULL;
260 	libusb_device_handle *hdl = NULL;
261 	static struct iwmbt_version ver;
262 	static struct iwmbt_boot_params params;
263 	uint32_t boot_param;
264 	int r;
265 	uint8_t bus_id = 0, dev_id = 0;
266 	int devid_set = 0;
267 	int n;
268 	char *firmware_dir = NULL;
269 	char *firmware_path = NULL;
270 	int retcode = 1;
271 
272 	/* Parse command line arguments */
273 	while ((n = getopt(argc, argv, "Dd:f:hIm:p:v:")) != -1) {
274 		switch (n) {
275 		case 'd': /* ugen device name */
276 			devid_set = 1;
277 			if (parse_ugen_name(optarg, &bus_id, &dev_id) < 0)
278 				usage();
279 			break;
280 		case 'D':
281 			iwmbt_do_debug = 1;
282 			break;
283 		case 'f': /* firmware dir */
284 			if (firmware_dir)
285 				free(firmware_dir);
286 			firmware_dir = strdup(optarg);
287 			break;
288 		case 'I':
289 			iwmbt_do_info = 1;
290 			break;
291 		case 'h':
292 		default:
293 			usage();
294 			break;
295 			/* NOT REACHED */
296 		}
297 	}
298 
299 	/* Ensure the devid was given! */
300 	if (devid_set == 0) {
301 		usage();
302 		/* NOTREACHED */
303 	}
304 
305 	/* libusb setup */
306 	r = libusb_init(&ctx);
307 	if (r != 0) {
308 		iwmbt_err("libusb_init failed: code %d", r);
309 		exit(127);
310 	}
311 
312 	iwmbt_debug("opening dev %d.%d", (int) bus_id, (int) dev_id);
313 
314 	/* Find a device based on the bus/dev id */
315 	dev = iwmbt_find_device(ctx, bus_id, dev_id);
316 	if (dev == NULL) {
317 		iwmbt_err("device not found");
318 		goto shutdown;
319 	}
320 
321 	/* XXX enforce that bInterfaceNumber is 0 */
322 
323 	/* XXX enforce the device/product id if they're non-zero */
324 
325 	/* Grab device handle */
326 	r = libusb_open(dev, &hdl);
327 	if (r != 0) {
328 		iwmbt_err("libusb_open() failed: code %d", r);
329 		goto shutdown;
330 	}
331 
332 	/* Check if ng_ubt is attached */
333 	r = libusb_kernel_driver_active(hdl, 0);
334 	if (r < 0) {
335 		iwmbt_err("libusb_kernel_driver_active() failed: code %d", r);
336 		goto shutdown;
337 	}
338 	if (r > 0) {
339 		iwmbt_info("Firmware has already been downloaded");
340 		retcode = 0;
341 		goto shutdown;
342 	}
343 
344 	/* Get Intel version */
345 	r = iwmbt_get_version(hdl, &ver);
346 	if (r < 0) {
347 		iwmbt_debug("iwmbt_get_version() failedL code %d", r);
348 		goto shutdown;
349 	}
350 	iwmbt_dump_version(&ver);
351 	iwmbt_debug("fw_variant=0x%02x", (int) ver.fw_variant);
352 
353 	/* fw_variant = 0x06 bootloader mode / 0x23 operational mode */
354 	if (ver.fw_variant == 0x23) {
355 		iwmbt_info("Firmware has already been downloaded");
356 		retcode = 0;
357 		goto reset;
358 	}
359 
360 	if (ver.fw_variant != 0x06){
361 		iwmbt_err("unknown fw_variant 0x%02x", (int) ver.fw_variant);
362 		goto shutdown;
363 	}
364 
365 	/* Read Intel Secure Boot Params */
366 	r = iwmbt_get_boot_params(hdl, &params);
367 	if (r < 0) {
368 		iwmbt_debug("iwmbt_get_boot_params() failed!");
369 		goto shutdown;
370 	}
371 	iwmbt_dump_boot_params(&params);
372 
373 	/* Check if firmware fragments are ACKed with a cmd complete event */
374 	if (params.limited_cce != 0x00) {
375 		iwmbt_err("Unsupported Intel firmware loading method (%u)",
376 		   params.limited_cce);
377 		goto shutdown;
378 	}
379 
380 	/* Default the firmware path */
381 	if (firmware_dir == NULL)
382 		firmware_dir = strdup(_DEFAULT_IWMBT_FIRMWARE_PATH);
383 
384 	firmware_path = iwmbt_get_fwname(&ver, &params, firmware_dir, "sfi");
385 	if (firmware_path == NULL)
386 		goto shutdown;
387 
388 	iwmbt_debug("firmware_path = %s", firmware_path);
389 
390 	/* Download firmware and parse it for magic Intel Reset parameter */
391 	r = iwmbt_init_firmware(hdl, firmware_path, &boot_param);
392 	free(firmware_path);
393 	if (r < 0)
394 		goto shutdown;
395 
396 	iwmbt_info("Firmware download complete");
397 
398 	r = iwmbt_intel_reset(hdl, boot_param);
399 	if (r < 0) {
400 		iwmbt_debug("iwmbt_intel_reset() failed!");
401 		goto shutdown;
402 	}
403 
404 	iwmbt_info("Firmware operational");
405 
406 	/* Once device is running in operational mode we can ignore failures */
407 	retcode = 0;
408 
409 	/* Execute Read Intel Version one more time */
410 	r = iwmbt_get_version(hdl, &ver);
411 	if (r == 0)
412 		iwmbt_dump_version(&ver);
413 
414 	/* Apply the device configuration (DDC) parameters */
415 	firmware_path = iwmbt_get_fwname(&ver, &params, firmware_dir, "ddc");
416 	iwmbt_debug("ddc_path = %s", firmware_path);
417 	if (firmware_path != NULL) {
418 		r = iwmbt_init_ddc(hdl, firmware_path);
419 		if (r == 0)
420 			iwmbt_info("DDC download complete");
421 		free(firmware_path);
422 	}
423 
424 	/* Set Intel Event mask */
425 	r = iwmbt_set_event_mask(hdl);
426 	if (r == 0)
427 		iwmbt_info("Intel Event Mask is set");
428 
429 reset:
430 
431 	/* Ask kernel driver to probe and attach device again */
432 	r = libusb_reset_device(hdl);
433 	if (r != 0)
434 		iwmbt_err("libusb_reset_device() failed: %s",
435 		    libusb_strerror(r));
436 
437 shutdown:
438 
439 	/* Shutdown */
440 
441 	if (hdl != NULL)
442 		libusb_close(hdl);
443 
444 	if (dev != NULL)
445 		libusb_unref_device(dev);
446 
447 	if (ctx != NULL)
448 		libusb_exit(ctx);
449 
450 	if (retcode == 0)
451 		iwmbt_info("Firmware download is succesful!");
452 	else
453 		iwmbt_err("Firmware download failed!");
454 
455 	return (retcode);
456 }
457