xref: /freebsd/usr.sbin/bluetooth/rtlbtfw/main.c (revision 187d8a3ce55a4e2d41fbe61465d5ff4ac0fc6bd5)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
5  * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
6  * Copyright (c) 2023 Future Crew LLC.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/stat.h>
32 #include <sys/endian.h>
33 
34 #include <err.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <libgen.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #include <libusb.h>
44 
45 #include "rtlbt_fw.h"
46 #include "rtlbt_hw.h"
47 #include "rtlbt_dbg.h"
48 
49 #define	_DEFAULT_RTLBT_FIRMWARE_PATH	"/usr/share/firmware/rtlbt"
50 
51 int	rtlbt_do_debug = 0;
52 int	rtlbt_do_info = 0;
53 
54 struct rtlbt_devid {
55 	uint16_t product_id;
56 	uint16_t vendor_id;
57 };
58 
59 static struct rtlbt_devid rtlbt_list[] = {
60 	/* Realtek 8821CE Bluetooth devices */
61 	{ .vendor_id = 0x13d3, .product_id = 0x3529 },
62 
63 	/* Realtek 8822CE Bluetooth devices */
64 	{ .vendor_id = 0x0bda, .product_id = 0xb00c },
65 	{ .vendor_id = 0x0bda, .product_id = 0xc822 },
66 
67 	/* Realtek 8851BE Bluetooth devices */
68 	{ .vendor_id = 0x13d3, .product_id = 0x3600 },
69 
70 	/* Realtek 8852AE Bluetooth devices */
71 	{ .vendor_id = 0x0bda, .product_id = 0x2852 },
72 	{ .vendor_id = 0x0bda, .product_id = 0xc852 },
73 	{ .vendor_id = 0x0bda, .product_id = 0x385a },
74 	{ .vendor_id = 0x0bda, .product_id = 0x4852 },
75 	{ .vendor_id = 0x04c5, .product_id = 0x165c },
76 	{ .vendor_id = 0x04ca, .product_id = 0x4006 },
77 	{ .vendor_id = 0x0cb8, .product_id = 0xc549 },
78 
79 	/* Realtek 8852CE Bluetooth devices */
80 	{ .vendor_id = 0x04ca, .product_id = 0x4007 },
81 	{ .vendor_id = 0x04c5, .product_id = 0x1675 },
82 	{ .vendor_id = 0x0cb8, .product_id = 0xc558 },
83 	{ .vendor_id = 0x13d3, .product_id = 0x3587 },
84 	{ .vendor_id = 0x13d3, .product_id = 0x3586 },
85 	{ .vendor_id = 0x13d3, .product_id = 0x3592 },
86 	{ .vendor_id = 0x13d3, .product_id = 0x3612 },
87 	{ .vendor_id = 0x0489, .product_id = 0xe122 },
88 
89 	/* Realtek 8852BE Bluetooth devices */
90 	{ .vendor_id = 0x0cb8, .product_id = 0xc559 },
91 	{ .vendor_id = 0x0bda, .product_id = 0x4853 },
92 	{ .vendor_id = 0x0bda, .product_id = 0x887b },
93 	{ .vendor_id = 0x0bda, .product_id = 0xb85b },
94 	{ .vendor_id = 0x13d3, .product_id = 0x3570 },
95 	{ .vendor_id = 0x13d3, .product_id = 0x3571 },
96 	{ .vendor_id = 0x13d3, .product_id = 0x3572 },
97 	{ .vendor_id = 0x13d3, .product_id = 0x3591 },
98 	{ .vendor_id = 0x0489, .product_id = 0xe123 },
99 	{ .vendor_id = 0x0489, .product_id = 0xe125 },
100 
101 	/* Realtek 8852BT/8852BE-VT Bluetooth devices */
102 	{ .vendor_id = 0x0bda, .product_id = 0x8520 },
103 
104 	/* Realtek 8922AE Bluetooth devices */
105 	{ .vendor_id = 0x0bda, .product_id = 0x8922 },
106 	{ .vendor_id = 0x13d3, .product_id = 0x3617 },
107 	{ .vendor_id = 0x13d3, .product_id = 0x3616 },
108 	{ .vendor_id = 0x0489, .product_id = 0xe130 },
109 
110 	/* Realtek 8723AE Bluetooth devices */
111 	{ .vendor_id = 0x0930, .product_id = 0x021d },
112 	{ .vendor_id = 0x13d3, .product_id = 0x3394 },
113 
114 	/* Realtek 8723BE Bluetooth devices */
115 	{ .vendor_id = 0x0489, .product_id = 0xe085 },
116 	{ .vendor_id = 0x0489, .product_id = 0xe08b },
117 	{ .vendor_id = 0x04f2, .product_id = 0xb49f },
118 	{ .vendor_id = 0x13d3, .product_id = 0x3410 },
119 	{ .vendor_id = 0x13d3, .product_id = 0x3416 },
120 	{ .vendor_id = 0x13d3, .product_id = 0x3459 },
121 	{ .vendor_id = 0x13d3, .product_id = 0x3494 },
122 
123 	/* Realtek 8723BU Bluetooth devices */
124 	{ .vendor_id = 0x7392, .product_id = 0xa611 },
125 
126 	/* Realtek 8723DE Bluetooth devices */
127 	{ .vendor_id = 0x0bda, .product_id = 0xb009 },
128 	{ .vendor_id = 0x2ff8, .product_id = 0xb011 },
129 
130 	/* Realtek 8761BUV Bluetooth devices */
131 	{ .vendor_id = 0x2c4e, .product_id = 0x0115 },
132 	{ .vendor_id = 0x2357, .product_id = 0x0604 },
133 	{ .vendor_id = 0x0b05, .product_id = 0x190e },
134 	{ .vendor_id = 0x2550, .product_id = 0x8761 },
135 	{ .vendor_id = 0x0bda, .product_id = 0x8771 },
136 	{ .vendor_id = 0x6655, .product_id = 0x8771 },
137 	{ .vendor_id = 0x7392, .product_id = 0xc611 },
138 	{ .vendor_id = 0x2b89, .product_id = 0x8761 },
139 
140 	/* Realtek 8821AE Bluetooth devices */
141 	{ .vendor_id = 0x0b05, .product_id = 0x17dc },
142 	{ .vendor_id = 0x13d3, .product_id = 0x3414 },
143 	{ .vendor_id = 0x13d3, .product_id = 0x3458 },
144 	{ .vendor_id = 0x13d3, .product_id = 0x3461 },
145 	{ .vendor_id = 0x13d3, .product_id = 0x3462 },
146 
147 	/* Realtek 8822BE Bluetooth devices */
148 	{ .vendor_id = 0x13d3, .product_id = 0x3526 },
149 	{ .vendor_id = 0x0b05, .product_id = 0x185c },
150 
151 	/* Realtek 8822CE Bluetooth devices */
152 	{ .vendor_id = 0x04ca, .product_id = 0x4005 },
153 	{ .vendor_id = 0x04c5, .product_id = 0x161f },
154 	{ .vendor_id = 0x0b05, .product_id = 0x18ef },
155 	{ .vendor_id = 0x13d3, .product_id = 0x3548 },
156 	{ .vendor_id = 0x13d3, .product_id = 0x3549 },
157 	{ .vendor_id = 0x13d3, .product_id = 0x3553 },
158 	{ .vendor_id = 0x13d3, .product_id = 0x3555 },
159 	{ .vendor_id = 0x2ff8, .product_id = 0x3051 },
160 	{ .vendor_id = 0x1358, .product_id = 0xc123 },
161 	{ .vendor_id = 0x0bda, .product_id = 0xc123 },
162 	{ .vendor_id = 0x0cb5, .product_id = 0xc547 },
163 };
164 
165 static int
166 rtlbt_is_realtek(struct libusb_device_descriptor *d)
167 {
168 	int i;
169 
170 	/* Search looking for whether it's a Realtek-based device */
171 	for (i = 0; i < (int) nitems(rtlbt_list); i++) {
172 		if ((rtlbt_list[i].product_id == d->idProduct) &&
173 		    (rtlbt_list[i].vendor_id == d->idVendor)) {
174 			rtlbt_info("found USB Realtek");
175 			return (1);
176 		}
177 	}
178 
179 	/* Not found */
180 	return (0);
181 }
182 
183 static int
184 rtlbt_is_bluetooth(struct libusb_device *dev)
185 {
186 	struct libusb_config_descriptor *cfg;
187 	const struct libusb_interface *ifc;
188 	const struct libusb_interface_descriptor *d;
189 	int r;
190 
191 	r = libusb_get_active_config_descriptor(dev, &cfg);
192 	if (r < 0) {
193 		rtlbt_err("Cannot retrieve config descriptor: %s",
194 		    libusb_error_name(r));
195 		return (0);
196 	}
197 
198 	if (cfg->bNumInterfaces != 0) {
199 		/* Only 0-th HCI/ACL interface is supported by downloader */
200 		ifc = &cfg->interface[0];
201 		if (ifc->num_altsetting != 0) {
202 			/* BT HCI/ACL interface has no altsettings */
203 			d = &ifc->altsetting[0];
204 			/* Check if interface is a bluetooth */
205 			if (d->bInterfaceClass == LIBUSB_CLASS_WIRELESS &&
206 			    d->bInterfaceSubClass == 0x01 &&
207 			    d->bInterfaceProtocol == 0x01) {
208 				rtlbt_info("found USB Realtek");
209 				libusb_free_config_descriptor(cfg);
210 				return (1);
211 			}
212 		}
213 	}
214 	libusb_free_config_descriptor(cfg);
215 
216 	/* Not found */
217 	return (0);
218 }
219 
220 static libusb_device *
221 rtlbt_find_device(libusb_context *ctx, int bus_id, int dev_id)
222 {
223 	libusb_device **list, *dev = NULL, *found = NULL;
224 	struct libusb_device_descriptor d;
225 	ssize_t cnt, i;
226 	int r;
227 
228 	cnt = libusb_get_device_list(ctx, &list);
229 	if (cnt < 0) {
230 		rtlbt_err("libusb_get_device_list() failed: code %lld",
231 		    (long long int) cnt);
232 		return (NULL);
233 	}
234 
235 	/*
236 	 * Scan through USB device list.
237 	 */
238 	for (i = 0; i < cnt; i++) {
239 		dev = list[i];
240 		if (bus_id == libusb_get_bus_number(dev) &&
241 		    dev_id == libusb_get_device_address(dev)) {
242 			/* Get the device descriptor for this device entry */
243 			r = libusb_get_device_descriptor(dev, &d);
244 			if (r != 0) {
245 				rtlbt_err("libusb_get_device_descriptor: %s",
246 				    libusb_strerror(r));
247 				break;
248 			}
249 
250 			/* For non-Realtek match on the vendor/product id */
251 			if (rtlbt_is_realtek(&d)) {
252 				/*
253 				 * Take a reference so it's not freed later on.
254 				 */
255 				found = libusb_ref_device(dev);
256 				break;
257 			}
258 			/* For Realtek vendor match on the interface class */
259 			if (d.idVendor == 0x0bda && rtlbt_is_bluetooth(dev)) {
260 				/*
261 				 * Take a reference so it's not freed later on.
262 				 */
263 				found = libusb_ref_device(dev);
264 				break;
265 			}
266 		}
267 	}
268 
269 	libusb_free_device_list(list, 1);
270 	return (found);
271 }
272 
273 static void
274 rtlbt_dump_version(ng_hci_read_local_ver_rp *ver)
275 {
276 	rtlbt_info("hci_version    0x%02x", ver->hci_version);
277 	rtlbt_info("hci_revision   0x%04x", le16toh(ver->hci_revision));
278 	rtlbt_info("lmp_version    0x%02x", ver->lmp_version);
279 	rtlbt_info("lmp_subversion 0x%04x", le16toh(ver->lmp_subversion));
280 }
281 
282 /*
283  * Parse ugen name and extract device's bus and address
284  */
285 
286 static int
287 parse_ugen_name(char const *ugen, uint8_t *bus, uint8_t *addr)
288 {
289 	char *ep;
290 
291 	if (strncmp(ugen, "ugen", 4) != 0)
292 		return (-1);
293 
294 	*bus = (uint8_t) strtoul(ugen + 4, &ep, 10);
295 	if (*ep != '.')
296 		return (-1);
297 
298 	*addr = (uint8_t) strtoul(ep + 1, &ep, 10);
299 	if (*ep != '\0')
300 		return (-1);
301 
302 	return (0);
303 }
304 
305 static void
306 usage(void)
307 {
308 	fprintf(stderr,
309 	    "Usage: rtlbtfw (-D) -d ugenX.Y (-f firmware path) (-I)\n");
310 	fprintf(stderr, "    -D: enable debugging\n");
311 	fprintf(stderr, "    -d: device to operate upon\n");
312 	fprintf(stderr, "    -f: firmware path, if not default\n");
313 	fprintf(stderr, "    -I: enable informational output\n");
314 	exit(127);
315 }
316 
317 int
318 main(int argc, char *argv[])
319 {
320 	libusb_context *ctx = NULL;
321 	libusb_device *dev = NULL;
322 	libusb_device_handle *hdl = NULL;
323 	ng_hci_read_local_ver_rp ver;
324 	int r;
325 	uint8_t bus_id = 0, dev_id = 0;
326 	int devid_set = 0;
327 	int n;
328 	char *firmware_dir = NULL;
329 	char *firmware_path = NULL;
330 	char *config_path = NULL;
331 	const char *fw_suffix;
332 	int retcode = 1;
333 	const struct rtlbt_id_table *ic;
334 	uint8_t rom_version;
335 	struct rtlbt_firmware fw, cfg;
336 	enum rtlbt_fw_type fw_type;
337 	uint16_t fw_lmp_subversion;
338 
339 	/* Parse command line arguments */
340 	while ((n = getopt(argc, argv, "Dd:f:hIm:p:v:")) != -1) {
341 		switch (n) {
342 		case 'd': /* ugen device name */
343 			devid_set = 1;
344 			if (parse_ugen_name(optarg, &bus_id, &dev_id) < 0)
345 				usage();
346 			break;
347 		case 'D':
348 			rtlbt_do_debug = 1;
349 			break;
350 		case 'f': /* firmware dir */
351 			if (firmware_dir)
352 				free(firmware_dir);
353 			firmware_dir = strdup(optarg);
354 			break;
355 		case 'I':
356 			rtlbt_do_info = 1;
357 			break;
358 		case 'h':
359 		default:
360 			usage();
361 			break;
362 			/* NOT REACHED */
363 		}
364 	}
365 
366 	/* Ensure the devid was given! */
367 	if (devid_set == 0) {
368 		usage();
369 		/* NOTREACHED */
370 	}
371 
372 	/* libusb setup */
373 	r = libusb_init(&ctx);
374 	if (r != 0) {
375 		rtlbt_err("libusb_init failed: code %d", r);
376 		exit(127);
377 	}
378 
379 	rtlbt_debug("opening dev %d.%d", (int) bus_id, (int) dev_id);
380 
381 	/* Find a device based on the bus/dev id */
382 	dev = rtlbt_find_device(ctx, bus_id, dev_id);
383 	if (dev == NULL) {
384 		rtlbt_err("device not found");
385 		goto shutdown;
386 	}
387 
388 	/* XXX enforce that bInterfaceNumber is 0 */
389 
390 	/* XXX enforce the device/product id if they're non-zero */
391 
392 	/* Grab device handle */
393 	r = libusb_open(dev, &hdl);
394 	if (r != 0) {
395 		rtlbt_err("libusb_open() failed: code %d", r);
396 		goto shutdown;
397 	}
398 
399 	/* Check if ng_ubt is attached */
400 	r = libusb_kernel_driver_active(hdl, 0);
401 	if (r < 0) {
402 		rtlbt_err("libusb_kernel_driver_active() failed: code %d", r);
403 		goto shutdown;
404 	}
405 	if (r > 0) {
406 		rtlbt_info("Firmware has already been downloaded");
407 		retcode = 0;
408 		goto shutdown;
409 	}
410 
411 	/* Get local version */
412 	r = rtlbt_read_local_ver(hdl, &ver);
413 	if (r < 0) {
414 		rtlbt_err("rtlbt_read_local_ver() failed code %d", r);
415 		goto shutdown;
416 	}
417 	rtlbt_dump_version(&ver);
418 
419 	ic = rtlbt_get_ic(ver.lmp_subversion, ver.hci_revision,
420 	    ver.hci_version);
421 	if (ic == NULL) {
422 		rtlbt_err("rtlbt_get_ic() failed: Unknown IC");
423 		goto shutdown;
424 	}
425 
426 	/* Default the firmware path */
427 	if (firmware_dir == NULL)
428 		firmware_dir = strdup(_DEFAULT_RTLBT_FIRMWARE_PATH);
429 
430 	fw_suffix = ic->fw_suffix == NULL ? "_fw.bin" : ic->fw_suffix;
431 	firmware_path = rtlbt_get_fwname(ic->fw_name, firmware_dir, fw_suffix);
432 	if (firmware_path == NULL)
433 		goto shutdown;
434 
435 	rtlbt_debug("firmware_path = %s", firmware_path);
436 
437 	rtlbt_info("loading firmware %s", firmware_path);
438 
439 	/* Read in the firmware */
440 	if (rtlbt_fw_read(&fw, firmware_path) <= 0) {
441 		rtlbt_debug("rtlbt_fw_read() failed");
442 		return (-1);
443 	}
444 
445 	fw_type = rtlbt_get_fw_type(&fw, &fw_lmp_subversion);
446 	if (fw_type == RTLBT_FW_TYPE_UNKNOWN &&
447 	    (ic->flags & RTLBT_IC_FLAG_SIMPLE) == 0) {
448 		rtlbt_debug("Unknown firmware type");
449 		goto shutdown;
450 	}
451 
452 	if (fw_type != RTLBT_FW_TYPE_UNKNOWN) {
453 
454 		/* Match hardware and firmware lmp_subversion */
455 		if (fw_lmp_subversion != ver.lmp_subversion) {
456 			rtlbt_err("firmware is for %x but this is a %x",
457 			    fw_lmp_subversion, ver.lmp_subversion);
458 			goto shutdown;
459 		}
460 
461 		/* Query a ROM version */
462 		r = rtlbt_read_rom_ver(hdl, &rom_version);
463 		if (r < 0) {
464 			rtlbt_err("rtlbt_read_rom_ver() failed code %d", r);
465 			goto shutdown;
466 		}
467 		rtlbt_debug("rom_version = %d", rom_version);
468 
469 		/* Load in the firmware */
470 		if (fw_type == RTLBT_FW_TYPE_V2) {
471 			uint8_t key_id, reg_val[2];
472 			r = rtlbt_read_reg16(hdl, RTLBT_SEC_PROJ, reg_val);
473 			if (r < 0) {
474 				rtlbt_err("rtlbt_read_reg16() failed code %d", r);
475 				goto shutdown;
476 			}
477 			key_id = reg_val[0];
478 			rtlbt_debug("key_id = %d", key_id);
479 			r = rtlbt_parse_fwfile_v2(&fw, rom_version, key_id);
480 		} else
481 			r = rtlbt_parse_fwfile_v1(&fw, rom_version);
482 		if (r < 0) {
483 			rtlbt_err("Parsing firmware file failed");
484 			goto shutdown;
485 		}
486 
487 		config_path = rtlbt_get_fwname(ic->fw_name, firmware_dir,
488 		    "_config.bin");
489 		if (config_path == NULL)
490 			goto shutdown;
491 
492 		rtlbt_info("loading config %s", config_path);
493 
494 		/* Read in the config file */
495 		if (rtlbt_fw_read(&cfg, config_path) <= 0) {
496 			rtlbt_err("rtlbt_fw_read() failed");
497 			if ((ic->flags & RTLBT_IC_FLAG_CONFIG) != 0)
498 				goto shutdown;
499 		} else {
500 			r = rtlbt_append_fwfile(&fw, &cfg);
501 			rtlbt_fw_free(&cfg);
502 			if (r < 0) {
503 				rtlbt_err("Appending config file failed");
504 				goto shutdown;
505 			}
506 		}
507 	}
508 
509 	r = rtlbt_load_fwfile(hdl, &fw);
510 	if (r < 0) {
511 		rtlbt_debug("Loading firmware file failed");
512 		goto shutdown;
513 	}
514 
515 	/* free it */
516 	rtlbt_fw_free(&fw);
517 
518 	rtlbt_info("Firmware download complete");
519 
520 	/* Execute Read Local Version one more time */
521 	r = rtlbt_read_local_ver(hdl, &ver);
522 	if (r < 0) {
523 		rtlbt_err("rtlbt_read_local_ver() failed code %d", r);
524 		goto shutdown;
525 	}
526 	rtlbt_dump_version(&ver);
527 
528 	retcode = 0;
529 
530 	/* Ask kernel driver to probe and attach device again */
531 	r = libusb_reset_device(hdl);
532 	if (r != 0)
533 		rtlbt_err("libusb_reset_device() failed: %s",
534 		    libusb_strerror(r));
535 
536 shutdown:
537 
538 	/* Shutdown */
539 
540 	if (hdl != NULL)
541 		libusb_close(hdl);
542 
543 	if (dev != NULL)
544 		libusb_unref_device(dev);
545 
546 	if (ctx != NULL)
547 		libusb_exit(ctx);
548 
549 	if (retcode == 0)
550 		rtlbt_info("Firmware download is successful!");
551 	else
552 		rtlbt_err("Firmware download failed!");
553 
554 	return (retcode);
555 }
556