Merge branch 'freebsd/current/main' into hardened/current/master

This commit is contained in:
HardenedBSD Sync Services 2024-11-06 18:01:24 -07:00
commit d8f6667f0d
No known key found for this signature in database
27 changed files with 2835 additions and 112 deletions

View File

@ -251,12 +251,12 @@ dvd: packagesystem
-DDB_FROM_SRC )
# Copy distfiles
mkdir -p ${.TARGET}/usr/freebsd-dist
for dist in MANIFEST $$(ls *.txz); \
for dist in MANIFEST *.txz; \
do cp $${dist} ${.TARGET}/usr/freebsd-dist; \
done
.if defined(NO_ROOT)
echo "./usr/freebsd-dist type=dir uname=root gname=wheel mode=0755" >> ${.TARGET}/METALOG
for dist in MANIFEST $$(ls *.txz); \
for dist in MANIFEST *.txz; \
do echo "./usr/freebsd-dist/$${dist} type=file uname=root gname=wheel mode=0644" >> ${.TARGET}/METALOG; \
done
.endif

View File

@ -4276,6 +4276,7 @@ net80211/ieee80211_alq.c optional wlan ieee80211_alq
netgraph/bluetooth/common/ng_bluetooth.c optional netgraph_bluetooth
netgraph/bluetooth/drivers/ubt/ng_ubt.c optional netgraph_bluetooth_ubt usb
netgraph/bluetooth/drivers/ubt/ng_ubt_intel.c optional netgraph_bluetooth_ubt usb
netgraph/bluetooth/drivers/ubt/ng_ubt_rtl.c optional netgraph_bluetooth_ubt usb
netgraph/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c optional netgraph_bluetooth_ubtbcmfw usb
netgraph/bluetooth/hci/ng_hci_cmds.c optional netgraph_bluetooth_hci
netgraph/bluetooth/hci/ng_hci_evnt.c optional netgraph_bluetooth_hci

View File

@ -6,7 +6,7 @@ CFLAGS+= -I${SRCTOP}/sys/netgraph/bluetooth/include \
-I${SRCTOP}/sys/netgraph/bluetooth/drivers/ubt
KMOD= ng_ubt
SRCS= ng_ubt.c ng_ubt_intel.c opt_bus.h opt_usb.h device_if.h \
bus_if.h usb_if.h usbdevs.h
SRCS= ng_ubt.c ng_ubt_intel.c ng_ubt_rtl.c opt_bus.h opt_usb.h \
device_if.h bus_if.h usb_if.h usbdevs.h
.include <bsd.kmod.mk>

View File

@ -534,6 +534,7 @@ static const STRUCT_USB_HOST_ID ubt_devs[] =
* Size of both command and response buffers are passed in length field of
* corresponding structures in "Parameter Total Length" format i.e.
* not including HCI packet headers.
* Expected event code must be placed into "Event code" of the response buffer.
*
* Must not be used after USB transfers have been configured in attach routine.
*/
@ -572,6 +573,12 @@ ubt_do_hci_request(struct usb_device *udev, struct ubt_hci_cmd *cmd,
if (evt == NULL)
return (USB_ERR_NORMAL_COMPLETION);
/* Save operation code if we expect completion event in response */
if(((struct ubt_hci_event *)evt)->header.event ==
NG_HCI_EVENT_COMMAND_COMPL)
((struct ubt_hci_event_command_compl *)evt)->opcode =
cmd->opcode;
/* Initialize INTR endpoint xfer and wait for response */
mtx_init(&mtx, "ubt pb", NULL, MTX_DEF | MTX_NEW);
@ -618,6 +625,9 @@ ubt_probe(device_t dev)
if (usbd_lookup_id_by_uaa(ubt_ignore_devs,
sizeof(ubt_ignore_devs), uaa) == 0)
return (ENXIO);
if (usbd_lookup_id_by_uaa(ubt_rtl_devs,
ubt_rtl_devs_sizeof, uaa) == 0)
return (ENXIO);
id = usbd_lookup_id_by_info(ubt_devs,
sizeof(ubt_devs), &uaa->info);
@ -842,6 +852,8 @@ ubt_probe_intr_callback(struct usb_xfer *xfer, usb_error_t error)
struct ubt_hci_event *evt = usbd_xfer_softc(xfer);
struct usb_page_cache *pc;
int actlen;
struct ubt_hci_evhdr evhdr;
uint16_t opcode;
usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
@ -849,7 +861,25 @@ ubt_probe_intr_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_TRANSFERRED:
if (actlen > UBT_HCI_EVENT_SIZE(evt))
actlen = UBT_HCI_EVENT_SIZE(evt);
if (actlen < sizeof(evhdr))
goto submit_next;
pc = usbd_xfer_get_frame(xfer, 0);
usbd_copy_out(pc, 0, &evhdr, sizeof(evhdr));
/* Check for expected event code */
if (evt->header.event != 0 &&
(evt->header.event != evhdr.event))
goto submit_next;
/* For completion events check operation code as well */
if (evt->header.event == NG_HCI_EVENT_COMMAND_COMPL) {
if (actlen < sizeof(struct ubt_hci_event_command_compl))
goto submit_next;
usbd_copy_out(pc,
offsetof(struct ubt_hci_event_command_compl, opcode),
&opcode, sizeof(opcode));
if (opcode !=
((struct ubt_hci_event_command_compl *)evt)->opcode)
goto submit_next;
}
usbd_copy_out(pc, 0, evt, actlen);
/* OneShot mode */
wakeup(evt);

View File

@ -6,6 +6,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2019, 2021 Vladimir Kondratyev <wulf@FreeBSD.org>
* Copyright (c) 2023 Future Crew LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -56,9 +57,13 @@
#include <netgraph/bluetooth/include/ng_ubt.h>
#include <netgraph/bluetooth/drivers/ubt/ng_ubt_var.h>
#define UBT_INTEL_HCICMD_TIMEOUT 2000 /* ms */
#define UBT_INTEL_TLV_IMAGE_TYPE 0x1c
enum {
UBT_INTEL_DEVICE_7260,
UBT_INTEL_DEVICE_8260,
UBT_INTEL_DEVICE_9260,
};
struct ubt_intel_version_rp {
@ -93,6 +98,9 @@ static const STRUCT_USB_HOST_ID ubt_intel_devs[] =
{ USB_VPI(USB_VENDOR_INTEL2, 0x0025, UBT_INTEL_DEVICE_8260) },
{ USB_VPI(USB_VENDOR_INTEL2, 0x0026, UBT_INTEL_DEVICE_8260) },
{ USB_VPI(USB_VENDOR_INTEL2, 0x0029, UBT_INTEL_DEVICE_8260) },
/* Intel Wireless 9260/9560 and successors */
{ USB_VPI(USB_VENDOR_INTEL2, 0x0032, UBT_INTEL_DEVICE_9260) },
{ USB_VPI(USB_VENDOR_INTEL2, 0x0033, UBT_INTEL_DEVICE_9260) },
};
/*
@ -103,7 +111,6 @@ static usb_error_t
ubt_intel_do_hci_request(struct usb_device *udev, uint16_t opcode,
void *resp, uint8_t resp_len)
{
#define UBT_INTEL_HCICMD_TIMEOUT 2000 /* ms */
struct ubt_hci_event_command_compl *evt;
struct ubt_hci_cmd cmd;
usb_error_t error;
@ -112,14 +119,14 @@ ubt_intel_do_hci_request(struct usb_device *udev, uint16_t opcode,
cmd.opcode = htole16(opcode);
evt = malloc(offsetof(struct ubt_hci_event_command_compl, data) +
resp_len, M_TEMP, M_ZERO | M_WAITOK);
evt->header.event = NG_HCI_EVENT_COMMAND_COMPL;
evt->header.length = resp_len + UBT_HCI_EVENT_COMPL_HEAD_SIZE;
error = ubt_do_hci_request(udev, &cmd, evt, UBT_INTEL_HCICMD_TIMEOUT);
if (error != USB_ERR_NORMAL_COMPLETION)
goto exit;
if (evt->header.event == NG_HCI_EVENT_COMMAND_COMPL &&
evt->header.length == resp_len + UBT_HCI_EVENT_COMPL_HEAD_SIZE)
if (evt->header.length == resp_len + UBT_HCI_EVENT_COMPL_HEAD_SIZE)
memcpy(resp, evt->data, resp_len);
else
error = USB_ERR_INVAL;
@ -128,6 +135,54 @@ exit:
return (error);
}
static uint8_t
ubt_intel_get_img_type(struct usb_device *udev)
{
#define UBT_INTEL_MAX_EVT_SIZE 256
static struct ubt_hci_cmd cmd = {
.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_VENDOR, 0x05)),
.length = 1,
.data = { 0xff },
};
struct ubt_hci_event_command_compl *evt;
usb_error_t error;
uint8_t status, datalen, type, len, img_type = 0;
uint8_t *data;
evt = malloc(UBT_INTEL_MAX_EVT_SIZE, M_TEMP, M_ZERO | M_WAITOK);
evt->header.event = NG_HCI_EVENT_COMMAND_COMPL;
evt->header.length =
UBT_INTEL_MAX_EVT_SIZE - sizeof(struct ubt_hci_evhdr);
error = ubt_do_hci_request(udev, &cmd, evt, UBT_INTEL_HCICMD_TIMEOUT);
if (error != USB_ERR_NORMAL_COMPLETION)
goto exit;
datalen = evt->header.length - UBT_HCI_EVENT_COMPL_HEAD_SIZE;
data = evt->data;
status = *data++;
if (status != 0)
goto exit;
datalen--;
while (datalen >= 2) {
type = *data++;
len = *data++;
datalen -= 2;
if (datalen < len)
break;
if (type == UBT_INTEL_TLV_IMAGE_TYPE && len == 1) {
img_type = *data;
break;
}
datalen -= len;
data += len;
}
exit:
free(evt, M_TEMP);
return (img_type);
}
/*
* Probe for a Intel Wireless Bluetooth device.
*/
@ -139,6 +194,7 @@ ubt_intel_probe(device_t dev)
struct ubt_intel_version_rp version;
ng_hci_reset_rp reset;
int error;
uint8_t img_type;
if (uaa->usb_mode != USB_MODE_HOST)
return (ENXIO);
@ -193,6 +249,18 @@ ubt_intel_probe(device_t dev)
return (ENXIO);
break;
case UBT_INTEL_DEVICE_9260:
/*
* Find if the Intel Wireless 9260/9560 device is in bootloader
* mode or is running operational firmware with checking of
* image type byte of "Intel version" HCI command response.
* The value 0x03 identifies the operational firmware.
*/
img_type = ubt_intel_get_img_type(uaa->device);
if (img_type != 0x03)
return (ENXIO);
break;
default:
KASSERT(0 == 1, ("Unknown DRIVER_INFO"));
}

View File

@ -0,0 +1,265 @@
/*
* ng_ubt_rtl.c
*/
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
* Copyright (c) 2023 Future Crew LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Attempt to initialize FreeBSD bluetooth stack while Realtek 87XX/88XX USB
* device is in bootloader mode locks the adapter hardly so it requires power
* on/off cycle to restore. This driver blocks ng_ubt attachment until
* operational firmware is loaded by rtlbtfw utility thus avoiding the lock up.
*/
#include <sys/types.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/taskqueue.h>
#include "usbdevs.h"
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#define USB_DEBUG_VAR usb_debug
#include <dev/usb/usb_debug.h>
#include <netgraph/ng_message.h>
#include <netgraph/netgraph.h>
#include <netgraph/ng_parse.h>
#include <netgraph/bluetooth/include/ng_bluetooth.h>
#include <netgraph/bluetooth/include/ng_hci.h>
#include <netgraph/bluetooth/include/ng_ubt.h>
#include <netgraph/bluetooth/drivers/ubt/ng_ubt_var.h>
static device_probe_t ubt_rtl_probe;
/*
* List of supported bluetooth devices. If you add a new device PID here ensure
* that it is blacklisted in ng_ubt.c and is supported by rtlbtfw utility.
*/
const STRUCT_USB_HOST_ID ubt_rtl_devs[] =
{
/* Generic Realtek Bluetooth class devices */
{ USB_VENDOR(USB_VENDOR_REALTEK),
USB_IFACE_CLASS(UDCLASS_WIRELESS),
USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
/* Realtek 8821CE Bluetooth devices */
{ USB_VPI(0x13d3, 0x3529, 0) },
/* Realtek 8822CE Bluetooth devices */
{ USB_VPI(0x0bda, 0xb00c, 0) },
{ USB_VPI(0x0bda, 0xc822, 0) },
/* Realtek 8822CU Bluetooth devices */
{ USB_VPI(0x13d3, 0x3549, 0) },
/* Realtek 8852AE Bluetooth devices */
{ USB_VPI(0x0bda, 0x2852, 0) },
{ USB_VPI(0x0bda, 0xc852, 0) },
{ USB_VPI(0x0bda, 0x385a, 0) },
{ USB_VPI(0x0bda, 0x4852, 0) },
{ USB_VPI(0x04c5, 0x165c, 0) },
{ USB_VPI(0x04ca, 0x4006, 0) },
{ USB_VPI(0x0cb8, 0xc549, 0) },
/* Realtek 8852CE Bluetooth devices */
{ USB_VPI(0x04ca, 0x4007, 0) },
{ USB_VPI(0x04c5, 0x1675, 0) },
{ USB_VPI(0x0cb8, 0xc558, 0) },
{ USB_VPI(0x13d3, 0x3587, 0) },
{ USB_VPI(0x13d3, 0x3586, 0) },
{ USB_VPI(0x13d3, 0x3592, 0) },
/* Realtek 8852BE Bluetooth devices */
{ USB_VPI(0x0cb8, 0xc559, 0) },
{ USB_VPI(0x0bda, 0x887b, 0) },
{ USB_VPI(0x13d3, 0x3571, 0) },
/* Realtek 8723AE Bluetooth devices */
{ USB_VPI(0x0930, 0x021d, 0) },
{ USB_VPI(0x13d3, 0x3394, 0) },
/* Realtek 8723BE Bluetooth devices */
{ USB_VPI(0x0489, 0xe085, 0) },
{ USB_VPI(0x0489, 0xe08b, 0) },
{ USB_VPI(0x04f2, 0xb49f, 0) },
{ USB_VPI(0x13d3, 0x3410, 0) },
{ USB_VPI(0x13d3, 0x3416, 0) },
{ USB_VPI(0x13d3, 0x3459, 0) },
{ USB_VPI(0x13d3, 0x3494, 0) },
/* Realtek 8723BU Bluetooth devices */
{ USB_VPI(0x7392, 0xa611, 0) },
/* Realtek 8723DE Bluetooth devices */
{ USB_VPI(0x0bda, 0xb009, 0) },
{ USB_VPI(0x2ff8, 0xb011, 0) },
/* Realtek 8761BUV Bluetooth devices */
{ USB_VPI(0x2357, 0x0604, 0) },
{ USB_VPI(0x0b05, 0x190e, 0) },
{ USB_VPI(0x2550, 0x8761, 0) },
{ USB_VPI(0x0bda, 0x8771, 0) },
{ USB_VPI(0x6655, 0x8771, 0) },
{ USB_VPI(0x7392, 0xc611, 0) },
{ USB_VPI(0x2b89, 0x8761, 0) },
/* Realtek 8821AE Bluetooth devices */
{ USB_VPI(0x0b05, 0x17dc, 0) },
{ USB_VPI(0x13d3, 0x3414, 0) },
{ USB_VPI(0x13d3, 0x3458, 0) },
{ USB_VPI(0x13d3, 0x3461, 0) },
{ USB_VPI(0x13d3, 0x3462, 0) },
/* Realtek 8822BE Bluetooth devices */
{ USB_VPI(0x13d3, 0x3526, 0) },
{ USB_VPI(0x0b05, 0x185c, 0) },
/* Realtek 8822CE Bluetooth devices */
{ USB_VPI(0x04ca, 0x4005, 0) },
{ USB_VPI(0x04c5, 0x161f, 0) },
{ USB_VPI(0x0b05, 0x18ef, 0) },
{ USB_VPI(0x13d3, 0x3548, 0) },
{ USB_VPI(0x13d3, 0x3549, 0) },
{ USB_VPI(0x13d3, 0x3553, 0) },
{ USB_VPI(0x13d3, 0x3555, 0) },
{ USB_VPI(0x2ff8, 0x3051, 0) },
{ USB_VPI(0x1358, 0xc123, 0) },
{ USB_VPI(0x0bda, 0xc123, 0) },
{ USB_VPI(0x0cb5, 0xc547, 0) },
};
const size_t ubt_rtl_devs_sizeof = sizeof(ubt_rtl_devs);
/*
* List of lmp_subversion values that correspond to Realtek firmwares
* incompatible with ng_ubt driver. Alternative firmware for these devices
* has to be loaded with rtlbtfw utility. That will trigger lmp_subversion
* change to different value.
*/
static const uint16_t ubt_rtl_lmp_subvers[] = {
0x8703, 0x1200, 0x8723, 0x8821,
0x8761, 0x8822, 0x8852, 0x8851,
};
/*
* Execute generic HCI command and return response in provided buffer.
*/
static usb_error_t
ubt_rtl_do_hci_request(struct usb_device *udev, uint16_t opcode,
void *resp, uint8_t resp_len)
{
#define UBT_RTL_HCICMD_TIMEOUT 2000 /* ms */
struct ubt_hci_event_command_compl *evt;
struct ubt_hci_cmd cmd;
usb_error_t error;
memset(&cmd, 0, sizeof(cmd));
cmd.opcode = htole16(opcode);
evt = malloc(offsetof(struct ubt_hci_event_command_compl, data) +
resp_len, M_TEMP, M_ZERO | M_WAITOK);
evt->header.event = NG_HCI_EVENT_COMMAND_COMPL;
evt->header.length = resp_len + UBT_HCI_EVENT_COMPL_HEAD_SIZE;
error = ubt_do_hci_request(udev, &cmd, evt, UBT_RTL_HCICMD_TIMEOUT);
if (error != USB_ERR_NORMAL_COMPLETION)
goto exit;
if (evt->header.event == NG_HCI_EVENT_COMMAND_COMPL &&
evt->header.length == resp_len + UBT_HCI_EVENT_COMPL_HEAD_SIZE)
memcpy(resp, evt->data, resp_len);
else
error = USB_ERR_INVAL;
exit:
free(evt, M_TEMP);
return (error);
}
/*
* Probe for a Realtek 87XX/88XX USB Bluetooth device.
*/
static int
ubt_rtl_probe(device_t dev)
{
struct usb_attach_arg *uaa = device_get_ivars(dev);
ng_hci_read_local_ver_rp ver;
unsigned int i;
int error;
if (uaa->usb_mode != USB_MODE_HOST)
return (ENXIO);
if (uaa->info.bIfaceIndex != 0)
return (ENXIO);
error = usbd_lookup_id_by_uaa(ubt_rtl_devs, sizeof(ubt_rtl_devs), uaa);
if (error != 0)
return (error);
if (ubt_rtl_do_hci_request(uaa->device,
NG_HCI_OPCODE(NG_HCI_OGF_INFO, NG_HCI_OCF_READ_LOCAL_VER),
&ver, sizeof(ver)) != USB_ERR_NORMAL_COMPLETION)
return (ENXIO);
DPRINTFN(2, "hci_version 0x%02x\n", ver.hci_version);
DPRINTFN(2, "hci_revision 0x%04x\n", le16toh(ver.hci_revision));
DPRINTFN(2, "lmp_version 0x%02x\n", ver.lmp_version);
DPRINTFN(2, "lmp_subversion 0x%04x\n", le16toh(ver.lmp_subversion));
for (i = 0; i < nitems(ubt_rtl_lmp_subvers); i++)
if (le16toh(ver.lmp_subversion) == ubt_rtl_lmp_subvers[i])
return (ENXIO);
return (BUS_PROBE_DEFAULT);
}
/*
* Module interface. Attach and detach methods, netgraph node type
* registration and PNP string are inherited from ng_ubt.c driver.
*/
static device_method_t ubt_rtl_methods[] =
{
DEVMETHOD(device_probe, ubt_rtl_probe),
DEVMETHOD_END
};
DEFINE_CLASS_1(ubt, ubt_rtl_driver, ubt_rtl_methods,
sizeof(struct ubt_softc), ubt_driver);
DRIVER_MODULE(ng_ubt_rtl, uhub, ubt_rtl_driver, 0, 0);
MODULE_VERSION(ng_ubt_rtl, NG_BLUETOOTH_VERSION);
MODULE_DEPEND(ng_ubt_rtl, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
MODULE_DEPEND(ng_ubt_rtl, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
MODULE_DEPEND(ng_ubt_rtl, usb, 1, 1, 1);

View File

@ -164,6 +164,9 @@ typedef struct ubt_softc * ubt_softc_p;
usb_error_t ubt_do_hci_request(struct usb_device *, struct ubt_hci_cmd *,
void *, usb_timeout_t);
extern const STRUCT_USB_HOST_ID ubt_rtl_devs[];
extern const size_t ubt_rtl_devs_sizeof;
extern driver_t ubt_driver;
#endif /* ndef _NG_UBT_VAR_H_ */

View File

@ -430,6 +430,7 @@ DIRDEPS+= \
usr.sbin/bluetooth/l2control \
usr.sbin/bluetooth/l2ping \
usr.sbin/bluetooth/rfcomm_pppd \
usr.sbin/bluetooth/rtlbtfw \
usr.sbin/bluetooth/sdpcontrol \
usr.sbin/bluetooth/sdpd \
usr.sbin/bootparamd/bootparamd \

View File

@ -193,6 +193,7 @@ OLD_FILES+=etc/bluetooth/hosts
OLD_FILES+=etc/bluetooth/protocols
OLD_FILES+=etc/defaults/bluetooth.device.conf
OLD_FILES+=etc/devd/iwmbtfw.conf
OLD_FILES+=etc/devd/rtlbtfw.conf
OLD_DIRS+=etc/bluetooth
OLD_FILES+=etc/rc.d/bluetooth
OLD_FILES+=etc/rc.d/bthidd
@ -240,6 +241,7 @@ OLD_FILES+=usr/sbin/iwmbtfw
OLD_FILES+=usr/sbin/l2control
OLD_FILES+=usr/sbin/l2ping
OLD_FILES+=usr/sbin/rfcomm_pppd
OLD_FILES+=usr/sbin/rtlbtfw
OLD_FILES+=usr/sbin/sdpcontrol
OLD_FILES+=usr/sbin/sdpd
OLD_FILES+=usr/share/examples/etc/defaults/bluetooth.device.conf
@ -318,6 +320,7 @@ OLD_FILES+=usr/share/man/man8/iwmbtfw.8.gz
OLD_FILES+=usr/share/man/man8/l2control.8.gz
OLD_FILES+=usr/share/man/man8/l2ping.8.gz
OLD_FILES+=usr/share/man/man8/rfcomm_pppd.8.gz
OLD_FILES+=usr/share/man/man8/rtlbtfw.8.gz
OLD_FILES+=usr/share/man/man8/sdpcontrol.8.gz
OLD_FILES+=usr/share/man/man8/sdpd.8.gz
.endif

View File

@ -19,6 +19,7 @@ SUBDIR+= bcmfw
SUBDIR+= bthidcontrol
SUBDIR+= bthidd
SUBDIR+= iwmbtfw
SUBDIR+= rtlbtfw
.endif
.include <bsd.subdir.mk>

View File

@ -4,6 +4,8 @@ CONFSDIR= /etc/devd
PROG= iwmbtfw
MAN= iwmbtfw.8
LIBADD+= usb
# Not having NDEBUG defined will enable assertions
CFLAGS+= -DNDEBUG
SRCS= main.c iwmbt_fw.c iwmbt_hw.c
.include <bsd.prog.mk>

View File

@ -3,6 +3,7 @@
*
* Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
* Copyright (c) 2023 Future Crew LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -170,3 +171,23 @@ iwmbt_get_fwname(struct iwmbt_version *ver, struct iwmbt_boot_params *params,
return (fwname);
}
char *
iwmbt_get_fwname_tlv(struct iwmbt_version_tlv *ver, const char *prefix,
const char *suffix)
{
char *fwname;
#define IWMBT_PACK_CNVX_TOP(cnvx_top) ((uint16_t)( \
((cnvx_top) & 0x0f000000) >> 16 | \
((cnvx_top) & 0x0000000f) << 12 | \
((cnvx_top) & 0x00000ff0) >> 4))
asprintf(&fwname, "%s/ibt-%04x-%04x.%s",
prefix,
IWMBT_PACK_CNVX_TOP(ver->cnvi_top),
IWMBT_PACK_CNVX_TOP(ver->cnvr_top),
suffix);
return (fwname);
}

View File

@ -3,6 +3,7 @@
*
* Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
* Copyright (c) 2023 Future Crew LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -29,6 +30,15 @@
#ifndef __IWMBT_FW_H__
#define __IWMBT_FW_H__
#include <sys/types.h>
#define L2CAP_SOCKET_CHECKED
#include <bluetooth.h>
#define RSA_HEADER_LEN 644
#define ECDSA_HEADER_LEN 320
#define ECDSA_OFFSET RSA_HEADER_LEN
#define CSS_HEADER_OFFSET 8
struct iwmbt_version {
uint8_t status;
uint8_t hw_platform;
@ -62,6 +72,65 @@ struct iwmbt_boot_params {
uint8_t unlocked_state;
} __attribute__ ((packed));
enum {
IWMBT_TLV_CNVI_TOP = 0x10,
IWMBT_TLV_CNVR_TOP,
IWMBT_TLV_CNVI_BT,
IWMBT_TLV_CNVR_BT,
IWMBT_TLV_CNVI_OTP,
IWMBT_TLV_CNVR_OTP,
IWMBT_TLV_DEV_REV_ID,
IWMBT_TLV_USB_VENDOR_ID,
IWMBT_TLV_USB_PRODUCT_ID,
IWMBT_TLV_PCIE_VENDOR_ID,
IWMBT_TLV_PCIE_DEVICE_ID,
IWMBT_TLV_PCIE_SUBSYSTEM_ID,
IWMBT_TLV_IMAGE_TYPE,
IWMBT_TLV_TIME_STAMP,
IWMBT_TLV_BUILD_TYPE,
IWMBT_TLV_BUILD_NUM,
IWMBT_TLV_FW_BUILD_PRODUCT,
IWMBT_TLV_FW_BUILD_HW,
IWMBT_TLV_FW_STEP,
IWMBT_TLV_BT_SPEC,
IWMBT_TLV_MFG_NAME,
IWMBT_TLV_HCI_REV,
IWMBT_TLV_LMP_SUBVER,
IWMBT_TLV_OTP_PATCH_VER,
IWMBT_TLV_SECURE_BOOT,
IWMBT_TLV_KEY_FROM_HDR,
IWMBT_TLV_OTP_LOCK,
IWMBT_TLV_API_LOCK,
IWMBT_TLV_DEBUG_LOCK,
IWMBT_TLV_MIN_FW,
IWMBT_TLV_LIMITED_CCE,
IWMBT_TLV_SBE_TYPE,
IWMBT_TLV_OTP_BDADDR,
IWMBT_TLV_UNLOCKED_STATE
};
struct iwmbt_version_tlv {
uint32_t cnvi_top;
uint32_t cnvr_top;
uint32_t cnvi_bt;
uint32_t cnvr_bt;
uint16_t dev_rev_id;
uint8_t img_type;
uint16_t timestamp;
uint8_t build_type;
uint32_t build_num;
uint8_t secure_boot;
uint8_t otp_lock;
uint8_t api_lock;
uint8_t debug_lock;
uint8_t min_fw_build_nn;
uint8_t min_fw_build_cw;
uint8_t min_fw_build_yy;
uint8_t limited_cce;
uint8_t sbe_type;
bdaddr_t otp_bd_addr;
};
struct iwmbt_firmware {
char *fwname;
int len;
@ -73,5 +142,7 @@ extern void iwmbt_fw_free(struct iwmbt_firmware *fw);
extern char *iwmbt_get_fwname(struct iwmbt_version *ver,
struct iwmbt_boot_params *params, const char *prefix,
const char *suffix);
extern char *iwmbt_get_fwname_tlv(struct iwmbt_version_tlv *ver,
const char *prefix, const char *suffix);
#endif

View File

@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
* Copyright (c) 2023 Future Crew LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -29,16 +30,20 @@
#include <sys/endian.h>
#include <sys/stat.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <libusb.h>
#include <netgraph/bluetooth/include/ng_hci.h>
#include "iwmbt_fw.h"
#include "iwmbt_hw.h"
#include "iwmbt_dbg.h"
@ -93,6 +98,7 @@ static int
iwmbt_hci_command(struct libusb_device_handle *hdl, struct iwmbt_hci_cmd *cmd,
void *event, int size, int *transferred, int timeout)
{
struct timespec to, now, remains;
int ret;
ret = libusb_control_transfer(hdl,
@ -110,18 +116,47 @@ iwmbt_hci_command(struct libusb_device_handle *hdl, struct iwmbt_hci_cmd *cmd,
return (ret);
}
ret = libusb_interrupt_transfer(hdl,
IWMBT_INTERRUPT_ENDPOINT_ADDR,
event,
size,
transferred,
timeout);
clock_gettime(CLOCK_MONOTONIC, &now);
to = IWMBT_MSEC2TS(timeout);
timespecadd(&to, &now, &to);
if (ret < 0)
iwmbt_err("libusb_interrupt_transfer() failed: err=%s",
libusb_strerror(ret));
do {
timespecsub(&to, &now, &remains);
ret = libusb_interrupt_transfer(hdl,
IWMBT_INTERRUPT_ENDPOINT_ADDR,
event,
size,
transferred,
IWMBT_TS2MSEC(remains) + 1);
return (ret);
if (ret < 0) {
iwmbt_err("libusb_interrupt_transfer() failed: err=%s",
libusb_strerror(ret));
return (ret);
}
switch (((struct iwmbt_hci_event *)event)->header.event) {
case NG_HCI_EVENT_COMMAND_COMPL:
if (*transferred <
(int)offsetof(struct iwmbt_hci_event_cmd_compl, data))
break;
if (cmd->opcode !=
((struct iwmbt_hci_event_cmd_compl *)event)->opcode)
break;
/* FALLTHROUGH */
case 0xFF:
return (0);
default:
break;
}
iwmbt_debug("Stray HCI event: %x",
((struct iwmbt_hci_event *)event)->header.event);
} while (timespeccmp(&to, &now, >));
iwmbt_err("libusb_interrupt_transfer() failed: err=%s",
libusb_strerror(LIBUSB_ERROR_TIMEOUT));
return (LIBUSB_ERROR_TIMEOUT);
}
int
@ -267,16 +302,6 @@ iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
return (activate_patch);
}
int
iwmbt_load_fwfile(struct libusb_device_handle *hdl,
const struct iwmbt_firmware *fw, uint32_t *boot_param)
{
int ready = 0, sent = 0;
int ret, transferred;
struct iwmbt_hci_cmd *cmd;
struct iwmbt_hci_event *event;
uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
#define IWMBT_SEND_FRAGMENT(fragment_type, size, msg) do { \
iwmbt_debug("transferring %d bytes, offset %d", size, sent); \
\
@ -293,12 +318,11 @@ iwmbt_load_fwfile(struct libusb_device_handle *hdl,
sent += size; \
} while (0)
if (fw->len < 644) {
iwmbt_err("Invalid size of firmware file (%d)", fw->len);
return (-1);
}
iwmbt_debug("file=%s, size=%d", fw->fwname, fw->len);
int
iwmbt_load_rsa_header(struct libusb_device_handle *hdl,
const struct iwmbt_firmware *fw)
{
int ret, sent = 0;
IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment");
IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 1");
@ -310,6 +334,32 @@ iwmbt_load_fwfile(struct libusb_device_handle *hdl,
IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1");
IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2");
return (0);
}
int
iwmbt_load_ecdsa_header(struct libusb_device_handle *hdl,
const struct iwmbt_firmware *fw)
{
int ret, sent = ECDSA_OFFSET;
IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment");
IWMBT_SEND_FRAGMENT(0x03, 0x60, "public key");
IWMBT_SEND_FRAGMENT(0x02, 0x60, "signature");
return (0);
}
int
iwmbt_load_fwfile(struct libusb_device_handle *hdl,
const struct iwmbt_firmware *fw, uint32_t *boot_param, int offset)
{
int ready = 0, sent = offset;
int ret, transferred;
struct iwmbt_hci_cmd *cmd;
struct iwmbt_hci_event *event;
uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
/*
* Send firmware chunks. Chunk len must be 4 byte aligned.
* multiple commands can be combined
@ -460,6 +510,140 @@ iwmbt_get_version(struct libusb_device_handle *hdl,
return (0);
}
int
iwmbt_get_version_tlv(struct libusb_device_handle *hdl,
struct iwmbt_version_tlv *version)
{
int ret, transferred;
struct iwmbt_hci_event_cmd_compl *event;
static struct iwmbt_hci_cmd cmd = {
.opcode = htole16(0xfc05),
.length = 1,
.data = { 0xff },
};
uint8_t status, datalen, type, len;
uint8_t *data;
uint8_t buf[255];
memset(buf, 0, sizeof(buf));
ret = iwmbt_hci_command(hdl,
&cmd,
buf,
sizeof(buf),
&transferred,
IWMBT_HCI_CMD_TIMEOUT);
if (ret < 0 || transferred < (int)IWMBT_HCI_EVT_COMPL_SIZE(uint16_t)) {
iwmbt_debug("Can't get version: code=%d, size=%d",
ret,
transferred);
return (-1);
}
event = (struct iwmbt_hci_event_cmd_compl *)buf;
memcpy(version, event->data, sizeof(struct iwmbt_version));
datalen = event->header.length - IWMBT_HCI_EVENT_COMPL_HEAD_SIZE;
data = event->data;
status = *data++;
if (status != 0)
return (-1);
datalen--;
while (datalen >= 2) {
type = *data++;
len = *data++;
datalen -= 2;
if (datalen < len)
return (-1);
switch (type) {
case IWMBT_TLV_CNVI_TOP:
assert(len == 4);
version->cnvi_top = le32dec(data);
break;
case IWMBT_TLV_CNVR_TOP:
assert(len == 4);
version->cnvr_top = le32dec(data);
break;
case IWMBT_TLV_CNVI_BT:
assert(len == 4);
version->cnvi_bt = le32dec(data);
break;
case IWMBT_TLV_CNVR_BT:
assert(len == 4);
version->cnvr_bt = le32dec(data);
break;
case IWMBT_TLV_DEV_REV_ID:
assert(len == 2);
version->dev_rev_id = le16dec(data);
break;
case IWMBT_TLV_IMAGE_TYPE:
assert(len == 1);
version->img_type = *data;
break;
case IWMBT_TLV_TIME_STAMP:
assert(len == 2);
version->min_fw_build_cw = data[0];
version->min_fw_build_yy = data[1];
version->timestamp = le16dec(data);
break;
case IWMBT_TLV_BUILD_TYPE:
assert(len == 1);
version->build_type = *data;
break;
case IWMBT_TLV_BUILD_NUM:
assert(len == 4);
version->min_fw_build_nn = *data;
version->build_num = le32dec(data);
break;
case IWMBT_TLV_SECURE_BOOT:
assert(len == 1);
version->secure_boot = *data;
break;
case IWMBT_TLV_OTP_LOCK:
assert(len == 1);
version->otp_lock = *data;
break;
case IWMBT_TLV_API_LOCK:
assert(len == 1);
version->api_lock = *data;
break;
case IWMBT_TLV_DEBUG_LOCK:
assert(len == 1);
version->debug_lock = *data;
break;
case IWMBT_TLV_MIN_FW:
assert(len == 3);
version->min_fw_build_nn = data[0];
version->min_fw_build_cw = data[1];
version->min_fw_build_yy = data[2];
break;
case IWMBT_TLV_LIMITED_CCE:
assert(len == 1);
version->limited_cce = *data;
break;
case IWMBT_TLV_SBE_TYPE:
assert(len == 1);
version->sbe_type = *data;
break;
case IWMBT_TLV_OTP_BDADDR:
memcpy(&version->otp_bd_addr, data, sizeof(bdaddr_t));
break;
default:
/* Ignore other types */
break;
}
datalen -= len;
data += len;
}
return (0);
}
int
iwmbt_get_boot_params(struct libusb_device_handle *hdl,
struct iwmbt_boot_params *params)
@ -540,6 +724,7 @@ iwmbt_load_ddc(struct libusb_device_handle *hdl,
int size, sent = 0;
int ret, transferred;
uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
uint8_t evt[IWMBT_HCI_MAX_CMD_SIZE];
struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *)buf;
size = ddc->len;
@ -562,8 +747,8 @@ iwmbt_load_ddc(struct libusb_device_handle *hdl,
ret = iwmbt_hci_command(hdl,
cmd,
buf,
sizeof(buf),
evt,
sizeof(evt),
&transferred,
IWMBT_HCI_CMD_TIMEOUT);

View File

@ -2,6 +2,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
* Copyright (c) 2023 Future Crew LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -59,6 +60,9 @@ struct iwmbt_hci_event_cmd_compl {
#define IWMBT_HCI_EVT_COMPL_SIZE(payload) \
(offsetof(struct iwmbt_hci_event_cmd_compl, data) + sizeof(payload))
#define IWMBT_HCI_EVENT_COMPL_HEAD_SIZE \
(offsetof(struct iwmbt_hci_event_cmd_compl, data) - \
offsetof(struct iwmbt_hci_event_cmd_compl, numpkt))
#define IWMBT_CONTROL_ENDPOINT_ADDR 0x00
#define IWMBT_INTERRUPT_ENDPOINT_ADDR 0x81
@ -68,18 +72,30 @@ struct iwmbt_hci_event_cmd_compl {
#define IWMBT_HCI_MAX_CMD_SIZE 256
#define IWMBT_HCI_MAX_EVENT_SIZE 16
#define IWMBT_MSEC2TS(msec) \
(struct timespec) { \
.tv_sec = (msec) / 1000, \
.tv_nsec = ((msec) % 1000) * 1000000 \
};
#define IWMBT_TS2MSEC(ts) ((ts).tv_sec * 1000 + (ts).tv_nsec / 1000000)
#define IWMBT_HCI_CMD_TIMEOUT 2000 /* ms */
#define IWMBT_LOADCMPL_TIMEOUT 5000 /* ms */
extern int iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
const struct iwmbt_firmware *fw);
extern int iwmbt_load_rsa_header(struct libusb_device_handle *hdl,
const struct iwmbt_firmware *fw);
extern int iwmbt_load_ecdsa_header(struct libusb_device_handle *hdl,
const struct iwmbt_firmware *fw);
extern int iwmbt_load_fwfile(struct libusb_device_handle *hdl,
const struct iwmbt_firmware *fw, uint32_t *boot_param);
const struct iwmbt_firmware *fw, uint32_t *boot_param, int offset);
extern int iwmbt_enter_manufacturer(struct libusb_device_handle *hdl);
extern int iwmbt_exit_manufacturer(struct libusb_device_handle *hdl,
int mode);
extern int iwmbt_get_version(struct libusb_device_handle *hdl,
struct iwmbt_version *version);
extern int iwmbt_get_version_tlv(struct libusb_device_handle *hdl,
struct iwmbt_version_tlv *version);
extern int iwmbt_get_boot_params(struct libusb_device_handle *hdl,
struct iwmbt_boot_params *params);
extern int iwmbt_intel_reset(struct libusb_device_handle *hdl,

View File

@ -26,7 +26,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd May 31, 2024
.Dd September 15, 2024
.Dt IWMBTFW 8
.Os
.Sh NAME
@ -48,7 +48,7 @@ device.
.Pp
This utility will
.Em only
work with Intel Wireless 7260/8260/8265 chip based Bluetooth USB devices
work with Intel Wireless 7260/8260/9260 chip based Bluetooth USB devices
and some of their successors.
The identification is currently based on USB vendor ID/product ID pair.
The vendor ID should be 0x8087

View File

@ -1,11 +1,12 @@
#
# Download Intel Wireless 8260/8265 bluetooth adaptor firmware
# Download Intel Wireless bluetooth adaptor firmware
#
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x8087";
match "product" "(0x07dc|0x0a2a|0x0aa7|0x0a2b|0x0aaa|0x0025|0x0026|0x0029)";
match "product" "(0x07dc|0x0a2a|0x0aa7|0x0a2b|0x0aaa|0x0025|0x0026|0x0029|0x0032|0x0033)";
action "/usr/sbin/iwmbtfw -d $cdev -f /usr/local/share/iwmbt-firmware";
};

View File

@ -3,6 +3,7 @@
*
* Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
* Copyright (c) 2023 Future Crew LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -50,71 +51,63 @@
int iwmbt_do_debug = 0;
int iwmbt_do_info = 0;
enum iwmbt_device {
IWMBT_DEVICE_UNKNOWN,
IWMBT_DEVICE_7260,
IWMBT_DEVICE_8260,
IWMBT_DEVICE_9260,
};
struct iwmbt_devid {
uint16_t product_id;
uint16_t vendor_id;
enum iwmbt_device device;
};
static struct iwmbt_devid iwmbt_list_72xx[] = {
static struct iwmbt_devid iwmbt_list[] = {
/* Intel Wireless 7260/7265 and successors */
{ .vendor_id = 0x8087, .product_id = 0x07dc },
{ .vendor_id = 0x8087, .product_id = 0x0a2a },
{ .vendor_id = 0x8087, .product_id = 0x0aa7 },
/* Intel Wireless 7260/7265 and successors */
{ .vendor_id = 0x8087, .product_id = 0x07dc, .device = IWMBT_DEVICE_7260 },
{ .vendor_id = 0x8087, .product_id = 0x0a2a, .device = IWMBT_DEVICE_7260 },
{ .vendor_id = 0x8087, .product_id = 0x0aa7, .device = IWMBT_DEVICE_7260 },
/* Intel Wireless 8260/8265 and successors */
{ .vendor_id = 0x8087, .product_id = 0x0a2b, .device = IWMBT_DEVICE_8260 },
{ .vendor_id = 0x8087, .product_id = 0x0aaa, .device = IWMBT_DEVICE_8260 },
{ .vendor_id = 0x8087, .product_id = 0x0025, .device = IWMBT_DEVICE_8260 },
{ .vendor_id = 0x8087, .product_id = 0x0026, .device = IWMBT_DEVICE_8260 },
{ .vendor_id = 0x8087, .product_id = 0x0029, .device = IWMBT_DEVICE_8260 },
/* Intel Wireless 9260/9560 and successors */
{ .vendor_id = 0x8087, .product_id = 0x0032, .device = IWMBT_DEVICE_9260 },
{ .vendor_id = 0x8087, .product_id = 0x0033, .device = IWMBT_DEVICE_9260 },
};
static struct iwmbt_devid iwmbt_list_82xx[] = {
/* Intel Wireless 8260/8265 and successors */
{ .vendor_id = 0x8087, .product_id = 0x0a2b },
{ .vendor_id = 0x8087, .product_id = 0x0aaa },
{ .vendor_id = 0x8087, .product_id = 0x0025 },
{ .vendor_id = 0x8087, .product_id = 0x0026 },
{ .vendor_id = 0x8087, .product_id = 0x0029 },
};
static int
iwmbt_is_7260(struct libusb_device_descriptor *d)
static enum iwmbt_device
iwmbt_is_supported(struct libusb_device_descriptor *d)
{
int i;
/* Search looking for whether it's an 7260/7265 */
for (i = 0; i < (int) nitems(iwmbt_list_72xx); i++) {
if ((iwmbt_list_72xx[i].product_id == d->idProduct) &&
(iwmbt_list_72xx[i].vendor_id == d->idVendor)) {
iwmbt_info("found 7260/7265");
return (1);
for (i = 0; i < (int) nitems(iwmbt_list); i++) {
if ((iwmbt_list[i].product_id == d->idProduct) &&
(iwmbt_list[i].vendor_id == d->idVendor)) {
iwmbt_info("found iwmbtfw compatible");
return (iwmbt_list[i].device);
}
}
/* Not found */
return (0);
}
static int
iwmbt_is_8260(struct libusb_device_descriptor *d)
{
int i;
/* Search looking for whether it's an 8260/8265 */
for (i = 0; i < (int) nitems(iwmbt_list_82xx); i++) {
if ((iwmbt_list_82xx[i].product_id == d->idProduct) &&
(iwmbt_list_82xx[i].vendor_id == d->idVendor)) {
iwmbt_info("found 8260/8265");
return (1);
}
}
/* Not found */
return (0);
return (IWMBT_DEVICE_UNKNOWN);
}
static libusb_device *
iwmbt_find_device(libusb_context *ctx, int bus_id, int dev_id,
int *iwmbt_use_old_method)
enum iwmbt_device *iwmbt_device)
{
libusb_device **list, *dev = NULL, *found = NULL;
struct libusb_device_descriptor d;
enum iwmbt_device device;
ssize_t cnt, i;
int r;
@ -141,20 +134,13 @@ iwmbt_find_device(libusb_context *ctx, int bus_id, int dev_id,
}
/* Match on the vendor/product id */
if (iwmbt_is_7260(&d)) {
device = iwmbt_is_supported(&d);
if (device != IWMBT_DEVICE_UNKNOWN) {
/*
* Take a reference so it's not freed later on.
*/
found = libusb_ref_device(dev);
*iwmbt_use_old_method = 1;
break;
} else
if (iwmbt_is_8260(&d)) {
/*
* Take a reference so it's not freed later on.
*/
found = libusb_ref_device(dev);
*iwmbt_use_old_method = 0;
*iwmbt_device = device;
break;
}
}
@ -200,6 +186,44 @@ iwmbt_dump_boot_params(struct iwmbt_boot_params *params)
params->otp_bdaddr[0]);
}
static void
iwmbt_dump_version_tlv(struct iwmbt_version_tlv *ver)
{
iwmbt_info("cnvi_top 0x%08x", ver->cnvi_top);
iwmbt_info("cnvr_top 0x%08x", ver->cnvr_top);
iwmbt_info("cnvi_bt 0x%08x", ver->cnvi_bt);
iwmbt_info("cnvr_bt 0x%08x", ver->cnvr_bt);
iwmbt_info("dev_rev_id 0x%04x", ver->dev_rev_id);
iwmbt_info("img_type 0x%02x", ver->img_type);
iwmbt_info("timestamp 0x%04x", ver->timestamp);
iwmbt_info("build_type 0x%02x", ver->build_type);
iwmbt_info("build_num 0x%08x", ver->build_num);
iwmbt_info("Secure Boot: %s", ver->secure_boot ? "on" : "off");
iwmbt_info("OTP lock: %s", ver->otp_lock ? "on" : "off");
iwmbt_info("API lock: %s", ver->api_lock ? "on" : "off");
iwmbt_info("Debug lock: %s", ver->debug_lock ? "on" : "off");
iwmbt_info("Minimum firmware build %u week %u year %u",
ver->min_fw_build_nn,
ver->min_fw_build_cw,
2000 + ver->min_fw_build_yy);
iwmbt_info("limited_cce 0x%02x", ver->limited_cce);
iwmbt_info("sbe_type 0x%02x", ver->sbe_type);
iwmbt_info("OTC BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x",
ver->otp_bd_addr.b[5],
ver->otp_bd_addr.b[4],
ver->otp_bd_addr.b[3],
ver->otp_bd_addr.b[2],
ver->otp_bd_addr.b[1],
ver->otp_bd_addr.b[0]);
if (ver->img_type == 0x01 || ver->img_type == 0x03)
iwmbt_info("%s timestamp %u.%u buildtype %u build %u",
ver->img_type == 0x01 ? "Bootloader" : "Firmware",
2000 + (ver->timestamp >> 8),
ver->timestamp & 0xff,
ver->build_type,
ver->build_num);
}
static int
iwmbt_patch_firmware(libusb_device_handle *hdl, const char *firmware_path)
{
@ -227,10 +251,10 @@ iwmbt_patch_firmware(libusb_device_handle *hdl, const char *firmware_path)
static int
iwmbt_init_firmware(libusb_device_handle *hdl, const char *firmware_path,
uint32_t *boot_param)
uint32_t *boot_param, uint8_t hw_variant, uint8_t sbe_type)
{
struct iwmbt_firmware fw;
int ret;
int header_len, ret = -1;
iwmbt_debug("loading %s", firmware_path);
@ -240,12 +264,76 @@ iwmbt_init_firmware(libusb_device_handle *hdl, const char *firmware_path,
return (-1);
}
/* Load in the firmware */
ret = iwmbt_load_fwfile(hdl, &fw, boot_param);
if (ret < 0)
iwmbt_debug("Loading firmware file failed");
iwmbt_debug("Firmware file size=%d", fw.len);
/* free it */
if (hw_variant <= 0x14) {
/*
* Hardware variants 0x0b, 0x0c, 0x11 - 0x14 .sfi file have
* a RSA header of 644 bytes followed by Command Buffer.
*/
header_len = RSA_HEADER_LEN;
if (fw.len < header_len) {
iwmbt_err("Invalid size of firmware file (%d)", fw.len);
ret = -1;
goto exit;
}
/* Check if the CSS Header version is RSA(0x00010000) */
if (le32dec(fw.buf + CSS_HEADER_OFFSET) != 0x00010000) {
iwmbt_err("Invalid CSS Header version");
ret = -1;
goto exit;
}
/* Only RSA secure boot engine supported */
if (sbe_type != 0x00) {
iwmbt_err("Invalid SBE type for hardware variant (%d)",
hw_variant);
ret = -1;
goto exit;
}
} else if (hw_variant >= 0x17) {
/*
* Hardware variants 0x17, 0x18 onwards support both RSA and
* ECDSA secure boot engine. As a result, the corresponding sfi
* file will have RSA header of 644, ECDSA header of 320 bytes
* followed by Command Buffer.
*/
header_len = ECDSA_OFFSET + ECDSA_HEADER_LEN;
if (fw.len < header_len) {
iwmbt_err("Invalid size of firmware file (%d)", fw.len);
ret = -1;
goto exit;
}
/* Check if CSS header for ECDSA follows the RSA header */
if (fw.buf[ECDSA_OFFSET] != 0x06) {
ret = -1;
goto exit;
}
/* Check if the CSS Header version is ECDSA(0x00020000) */
if (le32dec(fw.buf + ECDSA_OFFSET + CSS_HEADER_OFFSET) != 0x00020000) {
iwmbt_err("Invalid CSS Header version");
ret = -1;
goto exit;
}
}
/* Load in the CSS header */
if (sbe_type == 0x00)
ret = iwmbt_load_rsa_header(hdl, &fw);
else if (sbe_type == 0x01)
ret = iwmbt_load_ecdsa_header(hdl, &fw);
if (ret < 0)
goto exit;
/* Load in the Command Buffer */
ret = iwmbt_load_fwfile(hdl, &fw, boot_param, header_len);
exit:
/* free firmware */
iwmbt_fw_free(&fw);
return (ret);
@ -318,6 +406,7 @@ main(int argc, char *argv[])
libusb_device *dev = NULL;
libusb_device_handle *hdl = NULL;
static struct iwmbt_version ver;
static struct iwmbt_version_tlv ver_tlv;
static struct iwmbt_boot_params params;
uint32_t boot_param;
int r;
@ -327,7 +416,7 @@ main(int argc, char *argv[])
char *firmware_dir = NULL;
char *firmware_path = NULL;
int retcode = 1;
int iwmbt_use_old_method = 0;
enum iwmbt_device iwmbt_device;
/* Parse command line arguments */
while ((n = getopt(argc, argv, "Dd:f:hIm:p:v:")) != -1) {
@ -372,7 +461,7 @@ main(int argc, char *argv[])
iwmbt_debug("opening dev %d.%d", (int) bus_id, (int) dev_id);
/* Find a device based on the bus/dev id */
dev = iwmbt_find_device(ctx, bus_id, dev_id, &iwmbt_use_old_method);
dev = iwmbt_find_device(ctx, bus_id, dev_id, &iwmbt_device);
if (dev == NULL) {
iwmbt_err("device not found");
goto shutdown;
@ -401,16 +490,16 @@ main(int argc, char *argv[])
goto shutdown;
}
/* Get Intel version */
r = iwmbt_get_version(hdl, &ver);
if (r < 0) {
iwmbt_debug("iwmbt_get_version() failed code %d", r);
goto shutdown;
}
iwmbt_dump_version(&ver);
iwmbt_debug("fw_variant=0x%02x", (int) ver.fw_variant);
if (iwmbt_device == IWMBT_DEVICE_7260) {
if (iwmbt_use_old_method) {
/* Get Intel version */
r = iwmbt_get_version(hdl, &ver);
if (r < 0) {
iwmbt_debug("iwmbt_get_version() failed code %d", r);
goto shutdown;
}
iwmbt_dump_version(&ver);
iwmbt_debug("fw_patch_num=0x%02x", (int) ver.fw_patch_num);
/* fw_patch_num = >0 operational mode */
if (ver.fw_patch_num > 0x00) {
@ -469,7 +558,16 @@ main(int argc, char *argv[])
iwmbt_info("Intel Event Mask is set");
(void)iwmbt_exit_manufacturer(hdl, 0x00);
} else {
} else if (iwmbt_device == IWMBT_DEVICE_8260) {
/* Get Intel version */
r = iwmbt_get_version(hdl, &ver);
if (r < 0) {
iwmbt_debug("iwmbt_get_version() failed code %d", r);
goto shutdown;
}
iwmbt_dump_version(&ver);
iwmbt_debug("fw_variant=0x%02x", (int) ver.fw_variant);
/* fw_variant = 0x06 bootloader mode / 0x23 operational mode */
if (ver.fw_variant == 0x23) {
@ -509,7 +607,7 @@ main(int argc, char *argv[])
iwmbt_debug("firmware_path = %s", firmware_path);
/* Download firmware and parse it for magic Intel Reset parameter */
r = iwmbt_init_firmware(hdl, firmware_path, &boot_param);
r = iwmbt_init_firmware(hdl, firmware_path, &boot_param, 0, 0);
free(firmware_path);
if (r < 0)
goto shutdown;
@ -546,6 +644,93 @@ main(int argc, char *argv[])
r = iwmbt_set_event_mask(hdl);
if (r == 0)
iwmbt_info("Intel Event Mask is set");
} else {
/* Get Intel version */
r = iwmbt_get_version_tlv(hdl, &ver_tlv);
if (r < 0) {
iwmbt_debug("iwmbt_get_version_tlv() failed code %d", r);
goto shutdown;
}
iwmbt_dump_version_tlv(&ver_tlv);
iwmbt_debug("img_type=0x%02x", (int) ver_tlv.img_type);
/* img_type = 0x01 bootloader mode / 0x03 operational mode */
if (ver_tlv.img_type == 0x03) {
iwmbt_info("Firmware has already been downloaded");
retcode = 0;
goto reset;
}
if (ver_tlv.img_type != 0x01){
iwmbt_err("unknown img_type 0x%02x", (int) ver_tlv.img_type);
goto shutdown;
}
/* Check if firmware fragments are ACKed with a cmd complete event */
if (ver_tlv.limited_cce != 0x00) {
iwmbt_err("Unsupported Intel firmware loading method (%u)",
ver_tlv.limited_cce);
goto shutdown;
}
/* Check if secure boot engine is supported: 1 (ECDSA) or 0 (RSA) */
if (ver_tlv.sbe_type > 0x01) {
iwmbt_err("Unsupported secure boot engine (%u)",
ver_tlv.sbe_type);
goto shutdown;
}
/* Default the firmware path */
if (firmware_dir == NULL)
firmware_dir = strdup(_DEFAULT_IWMBT_FIRMWARE_PATH);
firmware_path = iwmbt_get_fwname_tlv(&ver_tlv, firmware_dir, "sfi");
if (firmware_path == NULL)
goto shutdown;
iwmbt_debug("firmware_path = %s", firmware_path);
/* Download firmware and parse it for magic Intel Reset parameter */
r = iwmbt_init_firmware(hdl, firmware_path, &boot_param,
ver_tlv.cnvi_bt >> 16 & 0x3f, ver_tlv.sbe_type);
free(firmware_path);
if (r < 0)
goto shutdown;
r = iwmbt_intel_reset(hdl, boot_param);
if (r < 0) {
iwmbt_debug("iwmbt_intel_reset() failed!");
goto shutdown;
}
iwmbt_info("Firmware operational");
/* Once device is running in operational mode we can ignore failures */
retcode = 0;
/* Execute Read Intel Version one more time */
r = iwmbt_get_version(hdl, &ver);
if (r == 0)
iwmbt_dump_version(&ver);
/* Apply the device configuration (DDC) parameters */
firmware_path = iwmbt_get_fwname_tlv(&ver_tlv, firmware_dir, "ddc");
iwmbt_debug("ddc_path = %s", firmware_path);
if (firmware_path != NULL) {
r = iwmbt_init_ddc(hdl, firmware_path);
if (r == 0)
iwmbt_info("DDC download complete");
free(firmware_path);
}
/* Set Intel Event mask */
r = iwmbt_set_event_mask(hdl);
if (r == 0)
iwmbt_info("Intel Event Mask is set");
iwmbt_info("Firmware download complete");
}
reset:

View File

@ -0,0 +1,9 @@
PACKAGE= bluetooth
CONFS= rtlbtfw.conf
CONFSDIR= /etc/devd
PROG= rtlbtfw
MAN= rtlbtfw.8
LIBADD+= usb
SRCS= main.c rtlbt_fw.c rtlbt_hw.c
.include <bsd.prog.mk>

View File

@ -0,0 +1,525 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
* Copyright (c) 2023 Future Crew LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/endian.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libusb.h>
#include "rtlbt_fw.h"
#include "rtlbt_hw.h"
#include "rtlbt_dbg.h"
#define _DEFAULT_RTLBT_FIRMWARE_PATH "/usr/share/firmware/rtlbt"
int rtlbt_do_debug = 0;
int rtlbt_do_info = 0;
struct rtlbt_devid {
uint16_t product_id;
uint16_t vendor_id;
};
static struct rtlbt_devid rtlbt_list[] = {
/* Realtek 8821CE Bluetooth devices */
{ .vendor_id = 0x13d3, .product_id = 0x3529 },
/* Realtek 8822CE Bluetooth devices */
{ .vendor_id = 0x0bda, .product_id = 0xb00c },
{ .vendor_id = 0x0bda, .product_id = 0xc822 },
/* Realtek 8822CU Bluetooth devices */
{ .vendor_id = 0x13d3, .product_id = 0x3549 },
/* Realtek 8852AE Bluetooth devices */
{ .vendor_id = 0x0bda, .product_id = 0x2852 },
{ .vendor_id = 0x0bda, .product_id = 0xc852 },
{ .vendor_id = 0x0bda, .product_id = 0x385a },
{ .vendor_id = 0x0bda, .product_id = 0x4852 },
{ .vendor_id = 0x04c5, .product_id = 0x165c },
{ .vendor_id = 0x04ca, .product_id = 0x4006 },
{ .vendor_id = 0x0cb8, .product_id = 0xc549 },
#ifdef RTLBTFW_SUPPORTS_FW_V2
/* Realtek 8852CE Bluetooth devices */
{ .vendor_id = 0x04ca, .product_id = 0x4007 },
{ .vendor_id = 0x04c5, .product_id = 0x1675 },
{ .vendor_id = 0x0cb8, .product_id = 0xc558 },
{ .vendor_id = 0x13d3, .product_id = 0x3587 },
{ .vendor_id = 0x13d3, .product_id = 0x3586 },
{ .vendor_id = 0x13d3, .product_id = 0x3592 },
#endif
/* Realtek 8852BE Bluetooth devices */
{ .vendor_id = 0x0cb8, .product_id = 0xc559 },
{ .vendor_id = 0x0bda, .product_id = 0x887b },
{ .vendor_id = 0x13d3, .product_id = 0x3571 },
/* Realtek 8723AE Bluetooth devices */
{ .vendor_id = 0x0930, .product_id = 0x021d },
{ .vendor_id = 0x13d3, .product_id = 0x3394 },
/* Realtek 8723BE Bluetooth devices */
{ .vendor_id = 0x0489, .product_id = 0xe085 },
{ .vendor_id = 0x0489, .product_id = 0xe08b },
{ .vendor_id = 0x04f2, .product_id = 0xb49f },
{ .vendor_id = 0x13d3, .product_id = 0x3410 },
{ .vendor_id = 0x13d3, .product_id = 0x3416 },
{ .vendor_id = 0x13d3, .product_id = 0x3459 },
{ .vendor_id = 0x13d3, .product_id = 0x3494 },
/* Realtek 8723BU Bluetooth devices */
{ .vendor_id = 0x7392, .product_id = 0xa611 },
/* Realtek 8723DE Bluetooth devices */
{ .vendor_id = 0x0bda, .product_id = 0xb009 },
{ .vendor_id = 0x2ff8, .product_id = 0xb011 },
/* Realtek 8761BUV Bluetooth devices */
{ .vendor_id = 0x2357, .product_id = 0x0604 },
{ .vendor_id = 0x0b05, .product_id = 0x190e },
{ .vendor_id = 0x2550, .product_id = 0x8761 },
{ .vendor_id = 0x0bda, .product_id = 0x8771 },
{ .vendor_id = 0x6655, .product_id = 0x8771 },
{ .vendor_id = 0x7392, .product_id = 0xc611 },
{ .vendor_id = 0x2b89, .product_id = 0x8761 },
/* Realtek 8821AE Bluetooth devices */
{ .vendor_id = 0x0b05, .product_id = 0x17dc },
{ .vendor_id = 0x13d3, .product_id = 0x3414 },
{ .vendor_id = 0x13d3, .product_id = 0x3458 },
{ .vendor_id = 0x13d3, .product_id = 0x3461 },
{ .vendor_id = 0x13d3, .product_id = 0x3462 },
/* Realtek 8822BE Bluetooth devices */
{ .vendor_id = 0x13d3, .product_id = 0x3526 },
{ .vendor_id = 0x0b05, .product_id = 0x185c },
/* Realtek 8822CE Bluetooth devices */
{ .vendor_id = 0x04ca, .product_id = 0x4005 },
{ .vendor_id = 0x04c5, .product_id = 0x161f },
{ .vendor_id = 0x0b05, .product_id = 0x18ef },
{ .vendor_id = 0x13d3, .product_id = 0x3548 },
{ .vendor_id = 0x13d3, .product_id = 0x3549 },
{ .vendor_id = 0x13d3, .product_id = 0x3553 },
{ .vendor_id = 0x13d3, .product_id = 0x3555 },
{ .vendor_id = 0x2ff8, .product_id = 0x3051 },
{ .vendor_id = 0x1358, .product_id = 0xc123 },
{ .vendor_id = 0x0bda, .product_id = 0xc123 },
{ .vendor_id = 0x0cb5, .product_id = 0xc547 },
};
static int
rtlbt_is_realtek(struct libusb_device_descriptor *d)
{
int i;
/* Search looking for whether it's a Realtek-based device */
for (i = 0; i < (int) nitems(rtlbt_list); i++) {
if ((rtlbt_list[i].product_id == d->idProduct) &&
(rtlbt_list[i].vendor_id == d->idVendor)) {
rtlbt_info("found USB Realtek");
return (1);
}
}
/* Not found */
return (0);
}
static int
rtlbt_is_bluetooth(struct libusb_device *dev)
{
struct libusb_config_descriptor *cfg;
const struct libusb_interface *ifc;
const struct libusb_interface_descriptor *d;
int r;
r = libusb_get_active_config_descriptor(dev, &cfg);
if (r < 0) {
rtlbt_err("Cannot retrieve config descriptor: %s",
libusb_error_name(r));
return (0);
}
if (cfg->bNumInterfaces != 0) {
/* Only 0-th HCI/ACL interface is supported by downloader */
ifc = &cfg->interface[0];
if (ifc->num_altsetting != 0) {
/* BT HCI/ACL interface has no altsettings */
d = &ifc->altsetting[0];
/* Check if interface is a bluetooth */
if (d->bInterfaceClass == LIBUSB_CLASS_WIRELESS &&
d->bInterfaceSubClass == 0x01 &&
d->bInterfaceProtocol == 0x01) {
rtlbt_info("found USB Realtek");
libusb_free_config_descriptor(cfg);
return (1);
}
}
}
libusb_free_config_descriptor(cfg);
/* Not found */
return (0);
}
static libusb_device *
rtlbt_find_device(libusb_context *ctx, int bus_id, int dev_id)
{
libusb_device **list, *dev = NULL, *found = NULL;
struct libusb_device_descriptor d;
ssize_t cnt, i;
int r;
cnt = libusb_get_device_list(ctx, &list);
if (cnt < 0) {
rtlbt_err("libusb_get_device_list() failed: code %lld",
(long long int) cnt);
return (NULL);
}
/*
* Scan through USB device list.
*/
for (i = 0; i < cnt; i++) {
dev = list[i];
if (bus_id == libusb_get_bus_number(dev) &&
dev_id == libusb_get_device_address(dev)) {
/* Get the device descriptor for this device entry */
r = libusb_get_device_descriptor(dev, &d);
if (r != 0) {
rtlbt_err("libusb_get_device_descriptor: %s",
libusb_strerror(r));
break;
}
/* For non-Realtek match on the vendor/product id */
if (rtlbt_is_realtek(&d)) {
/*
* Take a reference so it's not freed later on.
*/
found = libusb_ref_device(dev);
break;
}
/* For Realtek vendor match on the interface class */
if (d.idVendor == 0x0bda && rtlbt_is_bluetooth(dev)) {
/*
* Take a reference so it's not freed later on.
*/
found = libusb_ref_device(dev);
break;
}
}
}
libusb_free_device_list(list, 1);
return (found);
}
static void
rtlbt_dump_version(ng_hci_read_local_ver_rp *ver)
{
rtlbt_info("hci_version 0x%02x", ver->hci_version);
rtlbt_info("hci_revision 0x%04x", le16toh(ver->hci_revision));
rtlbt_info("lmp_version 0x%02x", ver->lmp_version);
rtlbt_info("lmp_subversion 0x%04x", le16toh(ver->lmp_subversion));
}
/*
* Parse ugen name and extract device's bus and address
*/
static int
parse_ugen_name(char const *ugen, uint8_t *bus, uint8_t *addr)
{
char *ep;
if (strncmp(ugen, "ugen", 4) != 0)
return (-1);
*bus = (uint8_t) strtoul(ugen + 4, &ep, 10);
if (*ep != '.')
return (-1);
*addr = (uint8_t) strtoul(ep + 1, &ep, 10);
if (*ep != '\0')
return (-1);
return (0);
}
static void
usage(void)
{
fprintf(stderr,
"Usage: rtlbtfw (-D) -d ugenX.Y (-f firmware path) (-I)\n");
fprintf(stderr, " -D: enable debugging\n");
fprintf(stderr, " -d: device to operate upon\n");
fprintf(stderr, " -f: firmware path, if not default\n");
fprintf(stderr, " -I: enable informational output\n");
exit(127);
}
int
main(int argc, char *argv[])
{
libusb_context *ctx = NULL;
libusb_device *dev = NULL;
libusb_device_handle *hdl = NULL;
ng_hci_read_local_ver_rp ver;
int r;
uint8_t bus_id = 0, dev_id = 0;
int devid_set = 0;
int n;
char *firmware_dir = NULL;
char *firmware_path = NULL;
char *config_path = NULL;
int retcode = 1;
const struct rtlbt_id_table *ic;
uint8_t rom_version;
struct rtlbt_firmware fw, cfg;
enum rtlbt_fw_type fw_type;
uint16_t fw_lmp_subversion;
/* Parse command line arguments */
while ((n = getopt(argc, argv, "Dd:f:hIm:p:v:")) != -1) {
switch (n) {
case 'd': /* ugen device name */
devid_set = 1;
if (parse_ugen_name(optarg, &bus_id, &dev_id) < 0)
usage();
break;
case 'D':
rtlbt_do_debug = 1;
break;
case 'f': /* firmware dir */
if (firmware_dir)
free(firmware_dir);
firmware_dir = strdup(optarg);
break;
case 'I':
rtlbt_do_info = 1;
break;
case 'h':
default:
usage();
break;
/* NOT REACHED */
}
}
/* Ensure the devid was given! */
if (devid_set == 0) {
usage();
/* NOTREACHED */
}
/* libusb setup */
r = libusb_init(&ctx);
if (r != 0) {
rtlbt_err("libusb_init failed: code %d", r);
exit(127);
}
rtlbt_debug("opening dev %d.%d", (int) bus_id, (int) dev_id);
/* Find a device based on the bus/dev id */
dev = rtlbt_find_device(ctx, bus_id, dev_id);
if (dev == NULL) {
rtlbt_err("device not found");
goto shutdown;
}
/* XXX enforce that bInterfaceNumber is 0 */
/* XXX enforce the device/product id if they're non-zero */
/* Grab device handle */
r = libusb_open(dev, &hdl);
if (r != 0) {
rtlbt_err("libusb_open() failed: code %d", r);
goto shutdown;
}
/* Check if ng_ubt is attached */
r = libusb_kernel_driver_active(hdl, 0);
if (r < 0) {
rtlbt_err("libusb_kernel_driver_active() failed: code %d", r);
goto shutdown;
}
if (r > 0) {
rtlbt_info("Firmware has already been downloaded");
retcode = 0;
goto shutdown;
}
/* Get local version */
r = rtlbt_read_local_ver(hdl, &ver);
if (r < 0) {
rtlbt_err("rtlbt_read_local_ver() failed code %d", r);
goto shutdown;
}
rtlbt_dump_version(&ver);
ic = rtlbt_get_ic(ver.lmp_subversion, ver.hci_revision,
ver.hci_version);
if (ic == NULL) {
rtlbt_err("rtlbt_get_ic() failed: Unknown IC");
goto shutdown;
}
/* Default the firmware path */
if (firmware_dir == NULL)
firmware_dir = strdup(_DEFAULT_RTLBT_FIRMWARE_PATH);
firmware_path = rtlbt_get_fwname(ic->fw_name, firmware_dir, "_fw.bin");
if (firmware_path == NULL)
goto shutdown;
rtlbt_debug("firmware_path = %s", firmware_path);
rtlbt_info("loading firmware %s", firmware_path);
/* Read in the firmware */
if (rtlbt_fw_read(&fw, firmware_path) <= 0) {
rtlbt_debug("rtlbt_fw_read() failed");
return (-1);
}
fw_type = rtlbt_get_fw_type(&fw, &fw_lmp_subversion);
if (fw_type == RTLBT_FW_TYPE_UNKNOWN &&
(ic->flags & RTLBT_IC_FLAG_SIMPLE) == 0) {
rtlbt_debug("Unknown firmware type");
goto shutdown;
}
if (fw_type != RTLBT_FW_TYPE_UNKNOWN) {
/* Match hardware and firmware lmp_subversion */
if (fw_lmp_subversion != ver.lmp_subversion) {
rtlbt_err("firmware is for %x but this is a %x",
fw_lmp_subversion, ver.lmp_subversion);
goto shutdown;
}
/* Query a ROM version */
r = rtlbt_read_rom_ver(hdl, &rom_version);
if (r < 0) {
rtlbt_err("rtlbt_read_rom_ver() failed code %d", r);
goto shutdown;
}
rtlbt_debug("rom_version = %d", rom_version);
/* Load in the firmware */
r = rtlbt_parse_fwfile_v1(&fw, rom_version);
if (r < 0) {
rtlbt_err("Parseing firmware file failed");
goto shutdown;
}
config_path = rtlbt_get_fwname(ic->fw_name, firmware_dir,
"_config.bin");
if (config_path == NULL)
goto shutdown;
rtlbt_info("loading config %s", config_path);
/* Read in the config file */
if (rtlbt_fw_read(&cfg, config_path) <= 0) {
rtlbt_err("rtlbt_fw_read() failed");
if ((ic->flags & RTLBT_IC_FLAG_CONFIG) != 0)
goto shutdown;
} else {
r = rtlbt_append_fwfile(&fw, &cfg);
rtlbt_fw_free(&cfg);
if (r < 0) {
rtlbt_err("Appending config file failed");
goto shutdown;
}
}
}
r = rtlbt_load_fwfile(hdl, &fw);
if (r < 0) {
rtlbt_debug("Loading firmware file failed");
goto shutdown;
}
/* free it */
rtlbt_fw_free(&fw);
rtlbt_info("Firmware download complete");
/* Execute Read Local Version one more time */
r = rtlbt_read_local_ver(hdl, &ver);
if (r < 0) {
rtlbt_err("rtlbt_read_local_ver() failed code %d", r);
goto shutdown;
}
rtlbt_dump_version(&ver);
retcode = 0;
/* Ask kernel driver to probe and attach device again */
r = libusb_reset_device(hdl);
if (r != 0)
rtlbt_err("libusb_reset_device() failed: %s",
libusb_strerror(r));
shutdown:
/* Shutdown */
if (hdl != NULL)
libusb_close(hdl);
if (dev != NULL)
libusb_unref_device(dev);
if (ctx != NULL)
libusb_exit(ctx);
if (retcode == 0)
rtlbt_info("Firmware download is successful!");
else
rtlbt_err("Firmware download failed!");
return (retcode);
}

View File

@ -0,0 +1,46 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef __RTLBT_DEBUG_H__
#define __RTLBT_DEBUG_H__
extern int rtlbt_do_debug;
extern int rtlbt_do_info;
#define rtlbt_err(fmt, ...) \
fprintf(stderr, "rtlbtfw: %s: "fmt"\n", __func__, ##__VA_ARGS__)
#define rtlbt_info(fmt, ...) do { \
if (rtlbt_do_info) \
fprintf(stderr, "%s: "fmt"\n", __func__, ##__VA_ARGS__);\
} while (0)
#define rtlbt_debug(fmt, ...) do { \
if (rtlbt_do_debug) \
fprintf(stderr, "%s: "fmt"\n", __func__, ##__VA_ARGS__);\
} while (0)
#endif

View File

@ -0,0 +1,385 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
* Copyright (c) 2023 Future Crew LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/endian.h>
#include <sys/stat.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "rtlbt_fw.h"
#include "rtlbt_dbg.h"
static const struct rtlbt_id_table rtlbt_ic_id_table[] = {
{ /* 8723A */
.lmp_subversion = RTLBT_ROM_LMP_8723A,
.hci_revision = 0xb,
.hci_version = 0x6,
.flags = RTLBT_IC_FLAG_SIMPLE,
.fw_name = "rtl8723a",
}, { /* 8723B */
.lmp_subversion = RTLBT_ROM_LMP_8723B,
.hci_revision = 0xb,
.hci_version = 0x6,
.fw_name = "rtl8723b",
}, { /* 8723D */
.lmp_subversion = RTLBT_ROM_LMP_8723B,
.hci_revision = 0xd,
.hci_version = 0x8,
.flags = RTLBT_IC_FLAG_CONFIG,
.fw_name = "rtl8723d",
}, { /* 8821A */
.lmp_subversion = RTLBT_ROM_LMP_8821A,
.hci_revision = 0xa,
.hci_version = 0x6,
.fw_name = "rtl8821a",
}, { /* 8821C */
.lmp_subversion = RTLBT_ROM_LMP_8821A,
.hci_revision = 0xc,
.hci_version = 0x8,
.flags = RTLBT_IC_FLAG_MSFT,
.fw_name = "rtl8821c",
}, { /* 8761A */
.lmp_subversion = RTLBT_ROM_LMP_8761A,
.hci_revision = 0xa,
.hci_version = 0x6,
.fw_name = "rtl8761a",
}, { /* 8761BU */
.lmp_subversion = RTLBT_ROM_LMP_8761A,
.hci_revision = 0xb,
.hci_version = 0xa,
.fw_name = "rtl8761bu",
}, { /* 8822C with USB interface */
.lmp_subversion = RTLBT_ROM_LMP_8822B,
.hci_revision = 0xc,
.hci_version = 0xa,
.flags = RTLBT_IC_FLAG_MSFT,
.fw_name = "rtl8822cu",
}, { /* 8822B */
.lmp_subversion = RTLBT_ROM_LMP_8822B,
.hci_revision = 0xb,
.hci_version = 0x7,
.flags = RTLBT_IC_FLAG_CONFIG | RTLBT_IC_FLAG_MSFT,
.fw_name = "rtl8822b",
}, { /* 8852A */
.lmp_subversion = RTLBT_ROM_LMP_8852A,
.hci_revision = 0xa,
.hci_version = 0xb,
.flags = RTLBT_IC_FLAG_MSFT,
.fw_name = "rtl8852au",
}, { /* 8852B */
.lmp_subversion = RTLBT_ROM_LMP_8852A,
.hci_revision = 0xb,
.hci_version = 0xb,
.flags = RTLBT_IC_FLAG_MSFT,
.fw_name = "rtl8852bu",
#ifdef RTLBTFW_SUPPORTS_FW_V2
}, { /* 8852C */
.lmp_subversion = RTLBT_ROM_LMP_8852A,
.hci_revision = 0xc,
.hci_version = 0xc,
.flags = RTLBT_IC_FLAG_MSFT,
.fw_name = "rtl8852cu",
}, { /* 8851B */
.lmp_subversion = RTLBT_ROM_LMP_8851B,
.hci_revision = 0xb,
.hci_version = 0xc,
.flags = RTLBT_IC_FLAG_MSFT,
.fw_name = "rtl8851bu",
#endif
},
};
static const uint16_t project_ids[] = {
[ 0 ] = RTLBT_ROM_LMP_8723A,
[ 1 ] = RTLBT_ROM_LMP_8723B,
[ 2 ] = RTLBT_ROM_LMP_8821A,
[ 3 ] = RTLBT_ROM_LMP_8761A,
[ 7 ] = RTLBT_ROM_LMP_8703B,
[ 8 ] = RTLBT_ROM_LMP_8822B,
[ 9 ] = RTLBT_ROM_LMP_8723B, /* 8723DU */
[ 10 ] = RTLBT_ROM_LMP_8821A, /* 8821CU */
[ 13 ] = RTLBT_ROM_LMP_8822B, /* 8822CU */
[ 14 ] = RTLBT_ROM_LMP_8761A, /* 8761BU */
[ 18 ] = RTLBT_ROM_LMP_8852A, /* 8852AU */
[ 19 ] = RTLBT_ROM_LMP_8723B, /* 8723FU */
[ 20 ] = RTLBT_ROM_LMP_8852A, /* 8852BU */
[ 25 ] = RTLBT_ROM_LMP_8852A, /* 8852CU */
[ 33 ] = RTLBT_ROM_LMP_8822B, /* 8822EU */
[ 36 ] = RTLBT_ROM_LMP_8851B, /* 8851BU */
};
/* Signatures */
static const uint8_t fw_header_sig_v1[8] =
{0x52, 0x65, 0x61, 0x6C, 0x74, 0x65, 0x63, 0x68}; /* Realtech */
#ifdef RTLBTFW_SUPPORTS_FW_V2
static const uint8_t fw_header_sig_v2[8] =
{0x52, 0x54, 0x42, 0x54, 0x43, 0x6F, 0x72, 0x65}; /* RTBTCore */
#endif
static const uint8_t fw_ext_sig[4] = {0x51, 0x04, 0xFD, 0x77};
int
rtlbt_fw_read(struct rtlbt_firmware *fw, const char *fwname)
{
int fd;
struct stat sb;
unsigned char *buf;
ssize_t r;
fd = open(fwname, O_RDONLY);
if (fd < 0) {
warn("%s: open: %s", __func__, fwname);
return (0);
}
if (fstat(fd, &sb) != 0) {
warn("%s: stat: %s", __func__, fwname);
close(fd);
return (0);
}
buf = calloc(1, sb.st_size);
if (buf == NULL) {
warn("%s: calloc", __func__);
close(fd);
return (0);
}
/* XXX handle partial reads */
r = read(fd, buf, sb.st_size);
if (r < 0) {
warn("%s: read", __func__);
free(buf);
close(fd);
return (0);
}
if (r != sb.st_size) {
rtlbt_err("read len %d != file size %d",
(int) r,
(int) sb.st_size);
free(buf);
close(fd);
return (0);
}
/* We have everything, so! */
memset(fw, 0, sizeof(*fw));
fw->fwname = strdup(fwname);
fw->len = sb.st_size;
fw->buf = buf;
close(fd);
return (1);
}
void
rtlbt_fw_free(struct rtlbt_firmware *fw)
{
if (fw->fwname)
free(fw->fwname);
if (fw->buf)
free(fw->buf);
memset(fw, 0, sizeof(*fw));
}
char *
rtlbt_get_fwname(const char *fw_name, const char *prefix, const char *suffix)
{
char *fwname;
asprintf(&fwname, "%s/%s%s", prefix, fw_name, suffix);
return (fwname);
}
const struct rtlbt_id_table *
rtlbt_get_ic(uint16_t lmp_subversion, uint16_t hci_revision,
uint8_t hci_version)
{
unsigned int i;
for (i = 0; i < nitems(rtlbt_ic_id_table); i++) {
if (rtlbt_ic_id_table[i].lmp_subversion == lmp_subversion &&
rtlbt_ic_id_table[i].hci_revision == hci_revision &&
rtlbt_ic_id_table[i].hci_version == hci_version)
return (rtlbt_ic_id_table + i);
}
return (NULL);
}
enum rtlbt_fw_type
rtlbt_get_fw_type(struct rtlbt_firmware *fw, uint16_t *fw_lmp_subversion)
{
enum rtlbt_fw_type fw_type;
size_t fw_header_len;
uint8_t *ptr;
uint8_t opcode, oplen, project_id;
if (fw->len < 8) {
rtlbt_err("firmware file too small");
return (RTLBT_FW_TYPE_UNKNOWN);
}
if (memcmp(fw->buf, fw_header_sig_v1, sizeof(fw_header_sig_v1)) == 0) {
fw_type = RTLBT_FW_TYPE_V1;
fw_header_len = sizeof(struct rtlbt_fw_header_v1);
} else
#ifdef RTLBTFW_SUPPORTS_FW_V2
if (memcmp(fw->buf, fw_header_sig_v2, sizeof(fw_header_sig_v2)) == 0) {
fw_type = RTLBT_FW_TYPE_V2;
fw_header_len = sizeof(struct rtlbt_fw_header_v2);
} else
#endif
return (RTLBT_FW_TYPE_UNKNOWN);
if (fw->len < fw_header_len + sizeof(fw_ext_sig) + 4) {
rtlbt_err("firmware file too small");
return (RTLBT_FW_TYPE_UNKNOWN);
}
ptr = fw->buf + fw->len - sizeof(fw_ext_sig);
if (memcmp(ptr, fw_ext_sig, sizeof(fw_ext_sig)) != 0) {
rtlbt_err("invalid extension section signature");
return (RTLBT_FW_TYPE_UNKNOWN);
}
do {
opcode = *--ptr;
oplen = *--ptr;
ptr -= oplen;
rtlbt_debug("code=%x len=%x", opcode, oplen);
if (opcode == 0x00) {
if (oplen != 1) {
rtlbt_err("invalid instruction length");
return (RTLBT_FW_TYPE_UNKNOWN);
}
project_id = *ptr;
rtlbt_debug("project_id=%x", project_id);
if (project_id >= nitems(project_ids) ||
project_ids[project_id] == 0) {
rtlbt_err("unknown project id %x", project_id);
return (RTLBT_FW_TYPE_UNKNOWN);
}
*fw_lmp_subversion = project_ids[project_id];
return (fw_type);
}
} while (opcode != 0xff && ptr > fw->buf + fw_header_len);
rtlbt_err("can not find project id instruction");
return (RTLBT_FW_TYPE_UNKNOWN);
};
int
rtlbt_parse_fwfile_v1(struct rtlbt_firmware *fw, uint8_t rom_version)
{
struct rtlbt_fw_header_v1 *fw_header;
uint8_t *patch_buf;
unsigned int i;
const uint8_t *chip_id_base;
uint32_t patch_offset;
uint16_t patch_length, num_patches;
fw_header = (struct rtlbt_fw_header_v1 *)fw->buf;
num_patches = le16toh(fw_header->num_patches);
rtlbt_debug("fw_version=%x, num_patches=%d",
le32toh(fw_header->fw_version), num_patches);
/* Find a right patch for the chip. */
if (fw->len < sizeof(struct rtlbt_fw_header_v1) +
sizeof(fw_ext_sig) + 4 + 8 * num_patches) {
errno = EINVAL;
return (-1);
}
chip_id_base = fw->buf + sizeof(struct rtlbt_fw_header_v1);
for (i = 0; i < num_patches; i++) {
if (le16dec(chip_id_base + i * 2) != rom_version + 1)
continue;
patch_length = le16dec(chip_id_base + 2 * (num_patches + i));
patch_offset = le32dec(chip_id_base + 4 * (num_patches + i));
break;
}
if (i >= num_patches) {
rtlbt_err("can not find patch for chip id %d", rom_version);
errno = EINVAL;
return (-1);
}
rtlbt_debug(
"index=%d length=%x offset=%x", i, patch_length, patch_offset);
if (fw->len < patch_offset + patch_length) {
errno = EINVAL;
return (-1);
}
patch_buf = malloc(patch_length);
if (patch_buf == NULL) {
errno = ENOMEM;
return (-1);
}
memcpy(patch_buf, fw->buf + patch_offset, patch_length - 4);
memcpy(patch_buf + patch_length - 4, &fw_header->fw_version, 4);
free(fw->buf);
fw->buf = patch_buf;
fw->len = patch_length;
return (0);
}
int
rtlbt_append_fwfile(struct rtlbt_firmware *fw, struct rtlbt_firmware *opt)
{
uint8_t *buf;
int len;
len = fw->len + opt->len;
buf = realloc(fw->buf, len);
if (buf == NULL)
return (-1);
memcpy(buf + fw->len, opt->buf, opt->len);
fw->buf = buf;
fw->len = len;
return (0);
}

View File

@ -0,0 +1,92 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
* Copyright (c) 2023 Future Crew LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef __RTLBT_FW_H__
#define __RTLBT_FW_H__
#include <stdbool.h>
#define RTLBT_ROM_LMP_8703B 0x8703
#define RTLBT_ROM_LMP_8723A 0x1200
#define RTLBT_ROM_LMP_8723B 0x8723
#define RTLBT_ROM_LMP_8821A 0x8821
#define RTLBT_ROM_LMP_8761A 0x8761
#define RTLBT_ROM_LMP_8822B 0x8822
#define RTLBT_ROM_LMP_8852A 0x8852
#define RTLBT_ROM_LMP_8851B 0x8851
enum rtlbt_fw_type {
RTLBT_FW_TYPE_UNKNOWN,
RTLBT_FW_TYPE_V1,
#ifdef RTLBTFW_SUPPORTS_FW_V2
RTLBT_FW_TYPE_V2,
#endif
};
struct rtlbt_id_table {
uint16_t lmp_subversion;
uint16_t hci_revision;
uint8_t hci_version;
uint8_t flags;
#define RTLBT_IC_FLAG_SIMPLE (0 << 1)
#define RTLBT_IC_FLAG_CONFIG (1 << 1)
#define RTLBT_IC_FLAG_MSFT (2 << 1)
const char *fw_name;
};
struct rtlbt_firmware {
char *fwname;
size_t len;
unsigned char *buf;
};
struct rtlbt_fw_header_v1 {
uint8_t signature[8];
uint32_t fw_version;
uint16_t num_patches;
} __attribute__ ((packed));
struct rtlbt_fw_header_v2 {
uint8_t signature[8];
uint8_t fw_version[8];
uint32_t num_sections;
} __attribute__ ((packed));
int rtlbt_fw_read(struct rtlbt_firmware *fw, const char *fwname);
void rtlbt_fw_free(struct rtlbt_firmware *fw);
char *rtlbt_get_fwname(const char *fw_name, const char *prefix,
const char *suffix);
const struct rtlbt_id_table *rtlbt_get_ic(uint16_t lmp_subversion,
uint16_t hci_revision, uint8_t hci_version);
enum rtlbt_fw_type rtlbt_get_fw_type(struct rtlbt_firmware *fw,
uint16_t *fw_lmp_subversion);
int rtlbt_parse_fwfile_v1(struct rtlbt_firmware *fw, uint8_t rom_version);
int rtlbt_append_fwfile(struct rtlbt_firmware *fw, struct rtlbt_firmware *opt);
#endif

View File

@ -0,0 +1,236 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
* Copyright (c) 2023 Future Crew LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/endian.h>
#include <sys/stat.h>
#include <netgraph/bluetooth/include/ng_hci.h>
#include <err.h>
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <libusb.h>
#include "rtlbt_fw.h"
#include "rtlbt_hw.h"
#include "rtlbt_dbg.h"
static int
rtlbt_hci_command(struct libusb_device_handle *hdl, struct rtlbt_hci_cmd *cmd,
void *event, int size, int *transferred, int timeout)
{
struct timespec to, now, remains;
int ret;
ret = libusb_control_transfer(hdl,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
0,
0,
0,
(uint8_t *)cmd,
RTLBT_HCI_CMD_SIZE(cmd),
timeout);
if (ret < 0) {
rtlbt_err("libusb_control_transfer() failed: err=%s",
libusb_strerror(ret));
return (ret);
}
clock_gettime(CLOCK_MONOTONIC, &now);
to = RTLBT_MSEC2TS(timeout);
timespecadd(&to, &now, &to);
do {
timespecsub(&to, &now, &remains);
ret = libusb_interrupt_transfer(hdl,
RTLBT_INTERRUPT_ENDPOINT_ADDR,
event,
size,
transferred,
RTLBT_TS2MSEC(remains) + 1);
if (ret < 0) {
rtlbt_err("libusb_interrupt_transfer() failed: err=%s",
libusb_strerror(ret));
return (ret);
}
switch (((struct rtlbt_hci_event *)event)->header.event) {
case NG_HCI_EVENT_COMMAND_COMPL:
if (*transferred <
(int)offsetof(struct rtlbt_hci_event_cmd_compl, data))
break;
if (cmd->opcode !=
((struct rtlbt_hci_event_cmd_compl *)event)->opcode)
break;
return (0);
default:
break;
}
rtlbt_debug("Stray HCI event: %x",
((struct rtlbt_hci_event *)event)->header.event);
} while (timespeccmp(&to, &now, >));
rtlbt_err("libusb_interrupt_transfer() failed: err=%s",
libusb_strerror(LIBUSB_ERROR_TIMEOUT));
return (LIBUSB_ERROR_TIMEOUT);
}
int
rtlbt_read_local_ver(struct libusb_device_handle *hdl,
ng_hci_read_local_ver_rp *ver)
{
int ret, transferred;
struct rtlbt_hci_event_cmd_compl *event;
struct rtlbt_hci_cmd cmd = {
.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_INFO,
NG_HCI_OCF_READ_LOCAL_VER)),
.length = 0,
};
uint8_t buf[RTLBT_HCI_EVT_COMPL_SIZE(ng_hci_read_local_ver_rp)];
memset(buf, 0, sizeof(buf));
ret = rtlbt_hci_command(hdl,
&cmd,
buf,
sizeof(buf),
&transferred,
RTLBT_HCI_CMD_TIMEOUT);
if (ret < 0 || transferred != sizeof(buf)) {
rtlbt_debug("Can't read local version: code=%d, size=%d",
ret,
transferred);
return (-1);
}
event = (struct rtlbt_hci_event_cmd_compl *)buf;
memcpy(ver, event->data, sizeof(ng_hci_read_local_ver_rp));
return (0);
}
int
rtlbt_read_rom_ver(struct libusb_device_handle *hdl, uint8_t *ver)
{
int ret, transferred;
struct rtlbt_hci_event_cmd_compl *event;
struct rtlbt_hci_cmd cmd = {
.opcode = htole16(0xfc6d),
.length = 0,
};
uint8_t buf[RTLBT_HCI_EVT_COMPL_SIZE(struct rtlbt_rom_ver_rp)];
memset(buf, 0, sizeof(buf));
ret = rtlbt_hci_command(hdl,
&cmd,
buf,
sizeof(buf),
&transferred,
RTLBT_HCI_CMD_TIMEOUT);
if (ret < 0 || transferred != sizeof(buf)) {
rtlbt_debug("Can't read ROM version: code=%d, size=%d",
ret,
transferred);
return (-1);
}
event = (struct rtlbt_hci_event_cmd_compl *)buf;
*ver = ((struct rtlbt_rom_ver_rp *)event->data)->version;
return (0);
}
int
rtlbt_load_fwfile(struct libusb_device_handle *hdl,
const struct rtlbt_firmware *fw)
{
uint8_t cmd_buf[RTLBT_HCI_MAX_CMD_SIZE];
struct rtlbt_hci_cmd *cmd = (struct rtlbt_hci_cmd *)cmd_buf;
struct rtlbt_hci_dl_cmd *dl_cmd = (struct rtlbt_hci_dl_cmd *)cmd->data;
uint8_t evt_buf[RTLBT_HCI_EVT_COMPL_SIZE(struct rtlbt_hci_dl_rp)];
uint8_t *data = fw->buf;
int frag_num = fw->len / RTLBT_MAX_CMD_DATA_LEN + 1;
int frag_len = RTLBT_MAX_CMD_DATA_LEN;
int i;
int ret, transferred;
for (i = 0; i < frag_num; i++) {
rtlbt_debug("download fw (%d/%d)", i + 1, frag_num);
memset(cmd_buf, 0, sizeof(cmd_buf));
cmd->opcode = htole16(0xfc20);
if (i > 0x7f)
dl_cmd->index = (i & 0x7f) + 1;
else
dl_cmd->index = i;
if (i == (frag_num - 1)) {
dl_cmd->index |= 0x80; /* data end */
frag_len = fw->len % RTLBT_MAX_CMD_DATA_LEN;
}
cmd->length = frag_len + 1;
memcpy(dl_cmd->data, data, frag_len);
/* Send download command */
ret = rtlbt_hci_command(hdl,
cmd,
evt_buf,
sizeof(evt_buf),
&transferred,
RTLBT_HCI_CMD_TIMEOUT);
if (ret < 0) {
rtlbt_err("download fw command failed (%d)", errno);
goto out;
}
if (transferred != sizeof(evt_buf)) {
rtlbt_err("download fw event length mismatch");
errno = EIO;
ret = -1;
goto out;
}
data += RTLBT_MAX_CMD_DATA_LEN;
}
out:
return (ret);
}

View File

@ -0,0 +1,104 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
* Copyright (c) 2023 Future Crew LLC.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef __RTLBT_HW_H__
#define __RTLBT_HW_H__
#include <netgraph/bluetooth/include/ng_hci.h>
/* USB control request (HCI command) structure */
struct rtlbt_hci_cmd {
uint16_t opcode;
uint8_t length;
uint8_t data[];
} __attribute__ ((packed));
#define RTLBT_HCI_CMD_SIZE(cmd) \
((cmd)->length + offsetof(struct rtlbt_hci_cmd, data))
/* USB interrupt transfer HCI event header structure */
struct rtlbt_hci_evhdr {
uint8_t event;
uint8_t length;
} __attribute__ ((packed));
/* USB interrupt transfer (generic HCI event) structure */
struct rtlbt_hci_event {
struct rtlbt_hci_evhdr header;
uint8_t data[];
} __attribute__ ((packed));
/* USB interrupt transfer (HCI command completion event) structure */
struct rtlbt_hci_event_cmd_compl {
struct rtlbt_hci_evhdr header;
uint8_t numpkt;
uint16_t opcode;
uint8_t data[];
} __attribute__ ((packed));
#define RTLBT_HCI_EVT_COMPL_SIZE(payload) \
(offsetof(struct rtlbt_hci_event_cmd_compl, data) + sizeof(payload))
#define RTLBT_CONTROL_ENDPOINT_ADDR 0x00
#define RTLBT_INTERRUPT_ENDPOINT_ADDR 0x81
#define RTLBT_HCI_MAX_CMD_SIZE 256
#define RTLBT_HCI_MAX_EVENT_SIZE 16
#define RTLBT_MSEC2TS(msec) \
(struct timespec) { \
.tv_sec = (msec) / 1000, \
.tv_nsec = ((msec) % 1000) * 1000000 \
};
#define RTLBT_TS2MSEC(ts) ((ts).tv_sec * 1000 + (ts).tv_nsec / 1000000)
#define RTLBT_HCI_CMD_TIMEOUT 2000 /* ms */
#define RTLBT_LOADCMPL_TIMEOUT 5000 /* ms */
#define RTLBT_MAX_CMD_DATA_LEN 252
struct rtlbt_rom_ver_rp {
uint8_t status;
uint8_t version;
} __attribute__ ((packed));
struct rtlbt_hci_dl_cmd {
uint8_t index;
uint8_t data[RTLBT_MAX_CMD_DATA_LEN];
} __attribute__ ((packed));
struct rtlbt_hci_dl_rp {
uint8_t status;
uint8_t index;
} __attribute__ ((packed));
int rtlbt_read_local_ver(struct libusb_device_handle *hdl,
ng_hci_read_local_ver_rp *ver);
int rtlbt_read_rom_ver(struct libusb_device_handle *hdl, uint8_t *ver);
int rtlbt_load_fwfile(struct libusb_device_handle *hdl,
const struct rtlbt_firmware *fw);
#endif

View File

@ -0,0 +1,100 @@
.\" Copyright (c) 2013, 2016 Adrian Chadd <adrian@freebsd.org>
.\" Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
.\" Copyright (c) 2023 Future Crew LLC.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd July 19, 2023
.Dt RTLBTFW 8
.Os
.Sh NAME
.Nm rtlbtfw
.Nd firmware download utility for Realtek 87XX/88XX chip based Bluetooth
USB devices
.Sh SYNOPSIS
.Nm
.Fl d Ar device_name
.Fl f Ar firmware_path
.Nm
.Fl h
.Sh DESCRIPTION
The
.Nm
utility downloads the specified firmware file to the specified
.Xr ugen 4
device.
.Pp
This utility will
.Em only
work with Realtek 87XX/88XX chip based Bluetooth USB devices and some of
their successors.
The identification is currently based on USB vendor ID/product ID pair and
interface class.
For Realtek devices the vendor ID should be 0x0bda
.Pq Dv USB_VENDOR_REALTEK
and the 0-th interface class/subclass/protocol should be a Bluetooth RF
Wireless Controller.
Non-Realtek devices are identified based on USB vendor ID/product ID pair.
.Pp
Firmware files are available in the
.Pa comms/rtlbt-firmware
port.
.Pp
The
.Nm
utility will query the device to determine which firmware image and board
configuration to load in at runtime.
.Pp
The options are as follows:
.Bl -tag -width indent
.It Fl D
Enable verbose debugging.
.It Fl d Ar device_name
Specify
.Xr ugen 4
device name.
.It Fl I
Enable informational debugging.
.It Fl f Ar firmware_path
Specify the directory containing the firmware files to search and upload.
.It Fl h
Display usage message and exit.
.El
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
.Xr libusb 3 ,
.Xr ng_ubt 4 ,
.Xr ugen 4 ,
.Xr devd 8
.Sh AUTHORS
.Nm
is based on
.Xr ath3kfw 8
utility used as firmware downloader template and on Linux btrtl driver
source code.
It is written by
.An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org
under sponsorship from Future Crew LLC.
.Sh BUGS
Most likely.
Please report if found.

View File

@ -0,0 +1,373 @@
#
# Download Realtek 87XX/88XX bluetooth adaptor firmware
#
# Generic Realtek vendor Bluetooth devices
notify 100 {
match "system" "USB";
match "subsystem" "INTERFACE";
match "type" "ATTACH";
match "vendor" "0x0bda";
# only interface 0 is supported by rtlbtfw
match "interface" "0";
match "intclass" "0xe0";
match "intsubclass" "0x01";
match "intprotocol" "0x01";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
# Realtek 8821CE Bluetooth devices
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x13d3";
match "product" "0x3529";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
# Realtek 8822CE Bluetooth devices
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x0bda";
match "product" "(0xb00c|0xc822)";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
# Realtek 8822CU Bluetooth devices
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x13d3";
match "product" "0x3549";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
# Realtek 8852AE Bluetooth devices
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x0bda";
match "product" "(0x2852|0xc852|0x385a|0x4852)";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x04c5";
match "product" "0x165c";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x04ca";
match "product" "0x4006";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x0cb8";
match "product" "0xc549";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
# Realtek 8852CE Bluetooth devices
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x04ca";
match "product" "0x4007";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x04c5";
match "product" "0x1675";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x0cb8";
match "product" "0xc558";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x13d3";
match "product" "(0x3587|0x3586|0x3592)";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
# Realtek 8852BE Bluetooth devices
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x0cb8";
match "product" "0xc559";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x0bda";
match "product" "0x887b";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x13d3";
match "product" "0x3571";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
# Realtek 8723AE Bluetooth devices
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x0930";
match "product" "0x021d";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x13d3";
match "product" "0x3394";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
# Realtek 8723BE Bluetooth devices
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x0489";
match "product" "(0xe085|0xe08b)";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x04f2";
match "product" "0xb49f";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x13d3";
match "product" "(0x3410|0x3416|0x3459|0x3494)";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
# Realtek 8723BU Bluetooth devices
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x7392";
match "product" "0xa611";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
# Realtek 8723DE Bluetooth devices
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x0bda";
match "product" "0xb009";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x2ff8";
match "product" "0xb011";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
# Realtek 8761BUV Bluetooth devices
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x2357";
match "product" "0x0604";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x0b05";
match "product" "0x190e";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x2550";
match "product" "0x8761";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x0bda";
match "product" "0x8771";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x6655";
match "product" "0x8771";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x7392";
match "product" "0xc611";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x2b89";
match "product" "0x8761";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
# Realtek 8821AE Bluetooth devices
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x0b05";
match "product" "0x17dc";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x13d3";
match "product" "(0x3414|0x3458|0x3461|0x3462)";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
# Realtek 8822BE Bluetooth devices
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x13d3";
match "product" "0x3526";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x0b05";
match "product" "0x185c";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
# Realtek 8822CE Bluetooth devices
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x04ca";
match "product" "0x4005";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x04c5";
match "product" "0x161f";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x0b05";
match "product" "0x18ef";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x13d3";
match "product" "(0x3548|0x3549|0x3553|0x3555)";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x2ff8";
match "product" "0x3051";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x1358";
match "product" "0xc123";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x0bda";
match "product" "0xc123";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};
notify 100 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x0cb5";
match "product" "0xc547";
action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware";
};