mirror of
https://git.hardenedbsd.org/hardenedbsd/HardenedBSD.git
synced 2024-11-13 05:41:26 +01:00
Merge branch 'freebsd/current/main' into hardened/current/master
This commit is contained in:
commit
d8f6667f0d
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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"));
|
||||
}
|
||||
|
265
sys/netgraph/bluetooth/drivers/ubt/ng_ubt_rtl.c
Normal file
265
sys/netgraph/bluetooth/drivers/ubt/ng_ubt_rtl.c
Normal 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);
|
@ -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_ */
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -19,6 +19,7 @@ SUBDIR+= bcmfw
|
||||
SUBDIR+= bthidcontrol
|
||||
SUBDIR+= bthidd
|
||||
SUBDIR+= iwmbtfw
|
||||
SUBDIR+= rtlbtfw
|
||||
.endif
|
||||
|
||||
.include <bsd.subdir.mk>
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
};
|
||||
|
@ -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:
|
||||
|
9
usr.sbin/bluetooth/rtlbtfw/Makefile
Normal file
9
usr.sbin/bluetooth/rtlbtfw/Makefile
Normal 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>
|
525
usr.sbin/bluetooth/rtlbtfw/main.c
Normal file
525
usr.sbin/bluetooth/rtlbtfw/main.c
Normal 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);
|
||||
}
|
46
usr.sbin/bluetooth/rtlbtfw/rtlbt_dbg.h
Normal file
46
usr.sbin/bluetooth/rtlbtfw/rtlbt_dbg.h
Normal 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
|
385
usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.c
Normal file
385
usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.c
Normal 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);
|
||||
}
|
92
usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.h
Normal file
92
usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.h
Normal 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
|
236
usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.c
Normal file
236
usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.c
Normal 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);
|
||||
}
|
104
usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.h
Normal file
104
usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.h
Normal 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
|
100
usr.sbin/bluetooth/rtlbtfw/rtlbtfw.8
Normal file
100
usr.sbin/bluetooth/rtlbtfw/rtlbtfw.8
Normal 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.
|
373
usr.sbin/bluetooth/rtlbtfw/rtlbtfw.conf
Normal file
373
usr.sbin/bluetooth/rtlbtfw/rtlbtfw.conf
Normal 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";
|
||||
};
|
Loading…
Reference in New Issue
Block a user