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