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 8822CU Bluetooth devices */
68 { .vendor_id = 0x13d3, .product_id = 0x3549 },
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 #ifdef RTLBTFW_SUPPORTS_FW_V2
80 /* Realtek 8852CE Bluetooth devices */
81 { .vendor_id = 0x04ca, .product_id = 0x4007 },
82 { .vendor_id = 0x04c5, .product_id = 0x1675 },
83 { .vendor_id = 0x0cb8, .product_id = 0xc558 },
84 { .vendor_id = 0x13d3, .product_id = 0x3587 },
85 { .vendor_id = 0x13d3, .product_id = 0x3586 },
86 { .vendor_id = 0x13d3, .product_id = 0x3592 },
87 #endif
88
89 /* Realtek 8852BE Bluetooth devices */
90 { .vendor_id = 0x0cb8, .product_id = 0xc559 },
91 { .vendor_id = 0x0bda, .product_id = 0x887b },
92 { .vendor_id = 0x13d3, .product_id = 0x3571 },
93
94 /* Realtek 8723AE Bluetooth devices */
95 { .vendor_id = 0x0930, .product_id = 0x021d },
96 { .vendor_id = 0x13d3, .product_id = 0x3394 },
97
98 /* Realtek 8723BE Bluetooth devices */
99 { .vendor_id = 0x0489, .product_id = 0xe085 },
100 { .vendor_id = 0x0489, .product_id = 0xe08b },
101 { .vendor_id = 0x04f2, .product_id = 0xb49f },
102 { .vendor_id = 0x13d3, .product_id = 0x3410 },
103 { .vendor_id = 0x13d3, .product_id = 0x3416 },
104 { .vendor_id = 0x13d3, .product_id = 0x3459 },
105 { .vendor_id = 0x13d3, .product_id = 0x3494 },
106
107 /* Realtek 8723BU Bluetooth devices */
108 { .vendor_id = 0x7392, .product_id = 0xa611 },
109
110 /* Realtek 8723DE Bluetooth devices */
111 { .vendor_id = 0x0bda, .product_id = 0xb009 },
112 { .vendor_id = 0x2ff8, .product_id = 0xb011 },
113
114 /* Realtek 8761BUV Bluetooth devices */
115 { .vendor_id = 0x2357, .product_id = 0x0604 },
116 { .vendor_id = 0x0b05, .product_id = 0x190e },
117 { .vendor_id = 0x2550, .product_id = 0x8761 },
118 { .vendor_id = 0x0bda, .product_id = 0x8771 },
119 { .vendor_id = 0x6655, .product_id = 0x8771 },
120 { .vendor_id = 0x7392, .product_id = 0xc611 },
121 { .vendor_id = 0x2b89, .product_id = 0x8761 },
122
123 /* Realtek 8821AE Bluetooth devices */
124 { .vendor_id = 0x0b05, .product_id = 0x17dc },
125 { .vendor_id = 0x13d3, .product_id = 0x3414 },
126 { .vendor_id = 0x13d3, .product_id = 0x3458 },
127 { .vendor_id = 0x13d3, .product_id = 0x3461 },
128 { .vendor_id = 0x13d3, .product_id = 0x3462 },
129
130 /* Realtek 8822BE Bluetooth devices */
131 { .vendor_id = 0x13d3, .product_id = 0x3526 },
132 { .vendor_id = 0x0b05, .product_id = 0x185c },
133
134 /* Realtek 8822CE Bluetooth devices */
135 { .vendor_id = 0x04ca, .product_id = 0x4005 },
136 { .vendor_id = 0x04c5, .product_id = 0x161f },
137 { .vendor_id = 0x0b05, .product_id = 0x18ef },
138 { .vendor_id = 0x13d3, .product_id = 0x3548 },
139 { .vendor_id = 0x13d3, .product_id = 0x3549 },
140 { .vendor_id = 0x13d3, .product_id = 0x3553 },
141 { .vendor_id = 0x13d3, .product_id = 0x3555 },
142 { .vendor_id = 0x2ff8, .product_id = 0x3051 },
143 { .vendor_id = 0x1358, .product_id = 0xc123 },
144 { .vendor_id = 0x0bda, .product_id = 0xc123 },
145 { .vendor_id = 0x0cb5, .product_id = 0xc547 },
146 };
147
148 static int
rtlbt_is_realtek(struct libusb_device_descriptor * d)149 rtlbt_is_realtek(struct libusb_device_descriptor *d)
150 {
151 int i;
152
153 /* Search looking for whether it's a Realtek-based device */
154 for (i = 0; i < (int) nitems(rtlbt_list); i++) {
155 if ((rtlbt_list[i].product_id == d->idProduct) &&
156 (rtlbt_list[i].vendor_id == d->idVendor)) {
157 rtlbt_info("found USB Realtek");
158 return (1);
159 }
160 }
161
162 /* Not found */
163 return (0);
164 }
165
166 static int
rtlbt_is_bluetooth(struct libusb_device * dev)167 rtlbt_is_bluetooth(struct libusb_device *dev)
168 {
169 struct libusb_config_descriptor *cfg;
170 const struct libusb_interface *ifc;
171 const struct libusb_interface_descriptor *d;
172 int r;
173
174 r = libusb_get_active_config_descriptor(dev, &cfg);
175 if (r < 0) {
176 rtlbt_err("Cannot retrieve config descriptor: %s",
177 libusb_error_name(r));
178 return (0);
179 }
180
181 if (cfg->bNumInterfaces != 0) {
182 /* Only 0-th HCI/ACL interface is supported by downloader */
183 ifc = &cfg->interface[0];
184 if (ifc->num_altsetting != 0) {
185 /* BT HCI/ACL interface has no altsettings */
186 d = &ifc->altsetting[0];
187 /* Check if interface is a bluetooth */
188 if (d->bInterfaceClass == LIBUSB_CLASS_WIRELESS &&
189 d->bInterfaceSubClass == 0x01 &&
190 d->bInterfaceProtocol == 0x01) {
191 rtlbt_info("found USB Realtek");
192 libusb_free_config_descriptor(cfg);
193 return (1);
194 }
195 }
196 }
197 libusb_free_config_descriptor(cfg);
198
199 /* Not found */
200 return (0);
201 }
202
203 static libusb_device *
rtlbt_find_device(libusb_context * ctx,int bus_id,int dev_id)204 rtlbt_find_device(libusb_context *ctx, int bus_id, int dev_id)
205 {
206 libusb_device **list, *dev = NULL, *found = NULL;
207 struct libusb_device_descriptor d;
208 ssize_t cnt, i;
209 int r;
210
211 cnt = libusb_get_device_list(ctx, &list);
212 if (cnt < 0) {
213 rtlbt_err("libusb_get_device_list() failed: code %lld",
214 (long long int) cnt);
215 return (NULL);
216 }
217
218 /*
219 * Scan through USB device list.
220 */
221 for (i = 0; i < cnt; i++) {
222 dev = list[i];
223 if (bus_id == libusb_get_bus_number(dev) &&
224 dev_id == libusb_get_device_address(dev)) {
225 /* Get the device descriptor for this device entry */
226 r = libusb_get_device_descriptor(dev, &d);
227 if (r != 0) {
228 rtlbt_err("libusb_get_device_descriptor: %s",
229 libusb_strerror(r));
230 break;
231 }
232
233 /* For non-Realtek match on the vendor/product id */
234 if (rtlbt_is_realtek(&d)) {
235 /*
236 * Take a reference so it's not freed later on.
237 */
238 found = libusb_ref_device(dev);
239 break;
240 }
241 /* For Realtek vendor match on the interface class */
242 if (d.idVendor == 0x0bda && rtlbt_is_bluetooth(dev)) {
243 /*
244 * Take a reference so it's not freed later on.
245 */
246 found = libusb_ref_device(dev);
247 break;
248 }
249 }
250 }
251
252 libusb_free_device_list(list, 1);
253 return (found);
254 }
255
256 static void
rtlbt_dump_version(ng_hci_read_local_ver_rp * ver)257 rtlbt_dump_version(ng_hci_read_local_ver_rp *ver)
258 {
259 rtlbt_info("hci_version 0x%02x", ver->hci_version);
260 rtlbt_info("hci_revision 0x%04x", le16toh(ver->hci_revision));
261 rtlbt_info("lmp_version 0x%02x", ver->lmp_version);
262 rtlbt_info("lmp_subversion 0x%04x", le16toh(ver->lmp_subversion));
263 }
264
265 /*
266 * Parse ugen name and extract device's bus and address
267 */
268
269 static int
parse_ugen_name(char const * ugen,uint8_t * bus,uint8_t * addr)270 parse_ugen_name(char const *ugen, uint8_t *bus, uint8_t *addr)
271 {
272 char *ep;
273
274 if (strncmp(ugen, "ugen", 4) != 0)
275 return (-1);
276
277 *bus = (uint8_t) strtoul(ugen + 4, &ep, 10);
278 if (*ep != '.')
279 return (-1);
280
281 *addr = (uint8_t) strtoul(ep + 1, &ep, 10);
282 if (*ep != '\0')
283 return (-1);
284
285 return (0);
286 }
287
288 static void
usage(void)289 usage(void)
290 {
291 fprintf(stderr,
292 "Usage: rtlbtfw (-D) -d ugenX.Y (-f firmware path) (-I)\n");
293 fprintf(stderr, " -D: enable debugging\n");
294 fprintf(stderr, " -d: device to operate upon\n");
295 fprintf(stderr, " -f: firmware path, if not default\n");
296 fprintf(stderr, " -I: enable informational output\n");
297 exit(127);
298 }
299
300 int
main(int argc,char * argv[])301 main(int argc, char *argv[])
302 {
303 libusb_context *ctx = NULL;
304 libusb_device *dev = NULL;
305 libusb_device_handle *hdl = NULL;
306 ng_hci_read_local_ver_rp ver;
307 int r;
308 uint8_t bus_id = 0, dev_id = 0;
309 int devid_set = 0;
310 int n;
311 char *firmware_dir = NULL;
312 char *firmware_path = NULL;
313 char *config_path = NULL;
314 int retcode = 1;
315 const struct rtlbt_id_table *ic;
316 uint8_t rom_version;
317 struct rtlbt_firmware fw, cfg;
318 enum rtlbt_fw_type fw_type;
319 uint16_t fw_lmp_subversion;
320
321 /* Parse command line arguments */
322 while ((n = getopt(argc, argv, "Dd:f:hIm:p:v:")) != -1) {
323 switch (n) {
324 case 'd': /* ugen device name */
325 devid_set = 1;
326 if (parse_ugen_name(optarg, &bus_id, &dev_id) < 0)
327 usage();
328 break;
329 case 'D':
330 rtlbt_do_debug = 1;
331 break;
332 case 'f': /* firmware dir */
333 if (firmware_dir)
334 free(firmware_dir);
335 firmware_dir = strdup(optarg);
336 break;
337 case 'I':
338 rtlbt_do_info = 1;
339 break;
340 case 'h':
341 default:
342 usage();
343 break;
344 /* NOT REACHED */
345 }
346 }
347
348 /* Ensure the devid was given! */
349 if (devid_set == 0) {
350 usage();
351 /* NOTREACHED */
352 }
353
354 /* libusb setup */
355 r = libusb_init(&ctx);
356 if (r != 0) {
357 rtlbt_err("libusb_init failed: code %d", r);
358 exit(127);
359 }
360
361 rtlbt_debug("opening dev %d.%d", (int) bus_id, (int) dev_id);
362
363 /* Find a device based on the bus/dev id */
364 dev = rtlbt_find_device(ctx, bus_id, dev_id);
365 if (dev == NULL) {
366 rtlbt_err("device not found");
367 goto shutdown;
368 }
369
370 /* XXX enforce that bInterfaceNumber is 0 */
371
372 /* XXX enforce the device/product id if they're non-zero */
373
374 /* Grab device handle */
375 r = libusb_open(dev, &hdl);
376 if (r != 0) {
377 rtlbt_err("libusb_open() failed: code %d", r);
378 goto shutdown;
379 }
380
381 /* Check if ng_ubt is attached */
382 r = libusb_kernel_driver_active(hdl, 0);
383 if (r < 0) {
384 rtlbt_err("libusb_kernel_driver_active() failed: code %d", r);
385 goto shutdown;
386 }
387 if (r > 0) {
388 rtlbt_info("Firmware has already been downloaded");
389 retcode = 0;
390 goto shutdown;
391 }
392
393 /* Get local version */
394 r = rtlbt_read_local_ver(hdl, &ver);
395 if (r < 0) {
396 rtlbt_err("rtlbt_read_local_ver() failed code %d", r);
397 goto shutdown;
398 }
399 rtlbt_dump_version(&ver);
400
401 ic = rtlbt_get_ic(ver.lmp_subversion, ver.hci_revision,
402 ver.hci_version);
403 if (ic == NULL) {
404 rtlbt_err("rtlbt_get_ic() failed: Unknown IC");
405 goto shutdown;
406 }
407
408 /* Default the firmware path */
409 if (firmware_dir == NULL)
410 firmware_dir = strdup(_DEFAULT_RTLBT_FIRMWARE_PATH);
411
412 firmware_path = rtlbt_get_fwname(ic->fw_name, firmware_dir, "_fw.bin");
413 if (firmware_path == NULL)
414 goto shutdown;
415
416 rtlbt_debug("firmware_path = %s", firmware_path);
417
418 rtlbt_info("loading firmware %s", firmware_path);
419
420 /* Read in the firmware */
421 if (rtlbt_fw_read(&fw, firmware_path) <= 0) {
422 rtlbt_debug("rtlbt_fw_read() failed");
423 return (-1);
424 }
425
426 fw_type = rtlbt_get_fw_type(&fw, &fw_lmp_subversion);
427 if (fw_type == RTLBT_FW_TYPE_UNKNOWN &&
428 (ic->flags & RTLBT_IC_FLAG_SIMPLE) == 0) {
429 rtlbt_debug("Unknown firmware type");
430 goto shutdown;
431 }
432
433 if (fw_type != RTLBT_FW_TYPE_UNKNOWN) {
434
435 /* Match hardware and firmware lmp_subversion */
436 if (fw_lmp_subversion != ver.lmp_subversion) {
437 rtlbt_err("firmware is for %x but this is a %x",
438 fw_lmp_subversion, ver.lmp_subversion);
439 goto shutdown;
440 }
441
442 /* Query a ROM version */
443 r = rtlbt_read_rom_ver(hdl, &rom_version);
444 if (r < 0) {
445 rtlbt_err("rtlbt_read_rom_ver() failed code %d", r);
446 goto shutdown;
447 }
448 rtlbt_debug("rom_version = %d", rom_version);
449
450 /* Load in the firmware */
451 r = rtlbt_parse_fwfile_v1(&fw, rom_version);
452 if (r < 0) {
453 rtlbt_err("Parseing firmware file failed");
454 goto shutdown;
455 }
456
457 config_path = rtlbt_get_fwname(ic->fw_name, firmware_dir,
458 "_config.bin");
459 if (config_path == NULL)
460 goto shutdown;
461
462 rtlbt_info("loading config %s", config_path);
463
464 /* Read in the config file */
465 if (rtlbt_fw_read(&cfg, config_path) <= 0) {
466 rtlbt_err("rtlbt_fw_read() failed");
467 if ((ic->flags & RTLBT_IC_FLAG_CONFIG) != 0)
468 goto shutdown;
469 } else {
470 r = rtlbt_append_fwfile(&fw, &cfg);
471 rtlbt_fw_free(&cfg);
472 if (r < 0) {
473 rtlbt_err("Appending config file failed");
474 goto shutdown;
475 }
476 }
477 }
478
479 r = rtlbt_load_fwfile(hdl, &fw);
480 if (r < 0) {
481 rtlbt_debug("Loading firmware file failed");
482 goto shutdown;
483 }
484
485 /* free it */
486 rtlbt_fw_free(&fw);
487
488 rtlbt_info("Firmware download complete");
489
490 /* Execute Read Local Version one more time */
491 r = rtlbt_read_local_ver(hdl, &ver);
492 if (r < 0) {
493 rtlbt_err("rtlbt_read_local_ver() failed code %d", r);
494 goto shutdown;
495 }
496 rtlbt_dump_version(&ver);
497
498 retcode = 0;
499
500 /* Ask kernel driver to probe and attach device again */
501 r = libusb_reset_device(hdl);
502 if (r != 0)
503 rtlbt_err("libusb_reset_device() failed: %s",
504 libusb_strerror(r));
505
506 shutdown:
507
508 /* Shutdown */
509
510 if (hdl != NULL)
511 libusb_close(hdl);
512
513 if (dev != NULL)
514 libusb_unref_device(dev);
515
516 if (ctx != NULL)
517 libusb_exit(ctx);
518
519 if (retcode == 0)
520 rtlbt_info("Firmware download is successful!");
521 else
522 rtlbt_err("Firmware download failed!");
523
524 return (retcode);
525 }
526