Added
Link Here
|
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, ¶ms); |
367 |
if (r < 0) { |
368 |
iwmbt_debug("iwmbt_get_boot_params() failed!"); |
369 |
goto shutdown; |
370 |
} |
371 |
iwmbt_dump_boot_params(¶ms); |
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, ¶ms, 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, ¶ms, 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 |
} |