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