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
52int	iwmbt_do_debug = 0;
53int	iwmbt_do_info = 0;
54
55struct iwmbt_devid {
56	uint16_t product_id;
57	uint16_t vendor_id;
58};
59
60static 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
70static int
71iwmbt_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
88static libusb_device *
89iwmbt_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
133static void
134iwmbt_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
148static void
149iwmbt_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
169static int
170iwmbt_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
195static int
196iwmbt_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
224static int
225parse_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
243static void
244usage(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
255int
256main(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
429reset:
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
437shutdown:
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