Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F132381024
D21071.id60150.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
47 KB
Referenced Files
None
Subscribers
None
D21071.id60150.diff
View Options
Index: sbin/devd/Makefile
===================================================================
--- sbin/devd/Makefile
+++ sbin/devd/Makefile
@@ -12,6 +12,10 @@
DEVD+= asus.conf
.endif
+.if ${MK_BLUETOOTH} != "no"
+DEVD+= iwmbtfw.conf
+.endif
+
.if ${MK_HYPERV} != "no"
DEVD+= hyperv.conf
.endif
Index: sbin/devd/iwmbtfw.conf
===================================================================
--- /dev/null
+++ sbin/devd/iwmbtfw.conf
@@ -0,0 +1,9 @@
+# Download Intel Wireless 8260/8265 bluetooth adaptor firmware
+notify 100 {
+ match "system" "USB";
+ match "subsystem" "DEVICE";
+ match "type" "ATTACH";
+ match "vendor" "0x8087";
+ match "product" "(0x0a2b|0x0aaa|0x0025|0x0026|0x0029)";
+ action "/usr/sbin/iwmbtfw -d $cdev -f /usr/local/share/iwmbt-firmware";
+};
Index: sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c
===================================================================
--- sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c
+++ sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c
@@ -252,6 +252,7 @@
****************************************************************************/
/* USB methods */
+static usb_callback_t ubt_probe_intr_callback;
static usb_callback_t ubt_ctrl_write_callback;
static usb_callback_t ubt_intr_read_callback;
static usb_callback_t ubt_bulk_read_callback;
@@ -421,6 +422,13 @@
/* Atheros AR5BBU12 with sflash firmware */
{ USB_VPI(0x0489, 0xe03c, 0), USB_DEV_BCD_LTEQ(1) },
{ USB_VPI(0x0489, 0xe036, 0), USB_DEV_BCD_LTEQ(1) },
+
+ /* Intel Wireless 8260/8265 and successors */
+ { USB_VPI(USB_VENDOR_INTEL2, 0x0a2b, UBT_INTEL8265) },
+ { USB_VPI(USB_VENDOR_INTEL2, 0x0aaa, UBT_INTEL8265) },
+ { USB_VPI(USB_VENDOR_INTEL2, 0x0025, UBT_INTEL8265) },
+ { USB_VPI(USB_VENDOR_INTEL2, 0x0026, UBT_INTEL8265) },
+ { USB_VPI(USB_VENDOR_INTEL2, 0x0029, UBT_INTEL8265) },
};
/* List of supported bluetooth devices */
@@ -502,6 +510,114 @@
{ USB_VPI(USB_VENDOR_DELL, 0x8197, 0) },
};
+/*
+ * Does a synchronous (waits for completion event) execution of HCI command.
+ * 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.
+ *
+ * Must not be used after USB transfers have been configured in attach routine.
+ */
+
+static usb_error_t
+ubt_do_hci_request(struct usb_device *udev, struct ubt_hci_cmd *cmd,
+ void *evt, usb_timeout_t timeout)
+{
+ static const struct usb_config ubt_probe_config = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .flags = { .pipe_bof = 1, .short_xfer_ok = 1 },
+ .bufsize = UBT_INTR_BUFFER_SIZE,
+ .callback = &ubt_probe_intr_callback,
+ };
+ struct usb_device_request req;
+ struct usb_xfer *xfer[1];
+ struct mtx mtx;
+ usb_error_t error = USB_ERR_NORMAL_COMPLETION;
+ uint8_t iface_index = 0;
+
+ /* Initialize a USB control request and then do it */
+ bzero(&req, sizeof(req));
+ req.bmRequestType = UBT_HCI_REQUEST;
+ req.wIndex[0] = iface_index;
+ USETW(req.wLength, UBT_HCI_CMD_SIZE(cmd));
+
+ error = usbd_do_request(udev, NULL, &req, cmd);
+ if (error != USB_ERR_NORMAL_COMPLETION) {
+ printf("ng_ubt: usbd_do_request error=%s\n",
+ usbd_errstr(error));
+ return (error);
+ }
+
+ if (evt == NULL)
+ return (USB_ERR_NORMAL_COMPLETION);
+
+ /* Initialize INTR endpoint xfer and wait for response */
+ mtx_init(&mtx, "ubt pb", NULL, MTX_DEF);
+
+ error = usbd_transfer_setup(udev, &iface_index, xfer,
+ &ubt_probe_config, 1, evt, &mtx);
+ if (error == USB_ERR_NORMAL_COMPLETION) {
+
+ mtx_lock(&mtx);
+ usbd_transfer_start(*xfer);
+
+ if (msleep_sbt(evt, &mtx, 0, "ubt pb", SBT_1MS * timeout,
+ 0, C_HARDCLOCK) == EWOULDBLOCK) {
+ printf("ng_ubt: HCI command 0x%04x timed out\n",
+ le16toh(cmd->opcode));
+ error = USB_ERR_TIMEOUT;
+ }
+
+ usbd_transfer_stop(*xfer);
+ mtx_unlock(&mtx);
+
+ usbd_transfer_unsetup(xfer, 1);
+ } else
+ printf("ng_ubt: usbd_transfer_setup error=%s\n",
+ usbd_errstr(error));
+
+ mtx_destroy(&mtx);
+
+ return (error);
+}
+
+/*
+ * Find if the Intel Wireless 8260/8265 device is in bootloader mode or is
+ * running operational firmware with checking of 4-th byte "Intel version"
+ * HCI command response. The value 0x23 identifies the operational firmware.
+ * Attempt to initialize bluetooth stack while device is in bootloader mode
+ * (4-th byte is 0x06) locks the adapter hardly and requires power on/off
+ * cycle to restore.
+ */
+
+static bool
+ubt_check_intel8265_firmware_state(struct usb_device *udev)
+{
+#define UBT_INTEL_VER_LEN 13
+#define UBT_INTEL_HCICMD_TIMEOUT 2000 /* ms */
+ struct ubt_hci_event_command_compl *evt;
+ uint8_t buf[offsetof(struct ubt_hci_event, data) + UBT_INTEL_VER_LEN];
+ static struct ubt_hci_cmd cmd = {
+ .opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_VENDOR, 0x05)),
+ .length = 0,
+ };
+ usb_error_t error;
+
+ bzero(buf, sizeof(buf));
+ evt = (struct ubt_hci_event_command_compl *)buf;
+ evt->header.length = UBT_INTEL_VER_LEN;
+
+ error = ubt_do_hci_request(udev, &cmd, evt, UBT_INTEL_HCICMD_TIMEOUT);
+ if (error != USB_ERR_NORMAL_COMPLETION)
+ return false;
+
+ return (evt->header.event == NG_HCI_EVENT_COMMAND_COMPL &&
+ evt->header.length == UBT_INTEL_VER_LEN &&
+ evt->data[4] == 0x23);
+}
+
/*
* Probe for a USB Bluetooth device.
* USB context.
@@ -520,8 +636,13 @@
return (ENXIO);
if (usbd_lookup_id_by_uaa(ubt_ignore_devs,
- sizeof(ubt_ignore_devs), uaa) == 0)
- return (ENXIO);
+ sizeof(ubt_ignore_devs), uaa) == 0) {
+ if ((USB_GET_DRIVER_INFO(uaa) & UBT_INTEL8265) != 0) {
+ if (!ubt_check_intel8265_firmware_state(uaa->device))
+ return (ENXIO);
+ } else
+ return (ENXIO);
+ }
error = usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa);
if (error == 0)
@@ -717,6 +838,49 @@
return (0);
} /* ubt_detach */
+/*
+ * Called when incoming interrupt transfer (HCI event) has completed, i.e.
+ * HCI event was received from the device during device probe stage.
+ * USB context.
+ */
+
+static void
+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;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ if (actlen > UBT_HCI_EVENT_SIZE(evt))
+ actlen = UBT_HCI_EVENT_SIZE(evt);
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, 0, evt, actlen);
+ /* OneShot mode */
+ wakeup(evt);
+ break;
+
+ case USB_ST_SETUP:
+submit_next:
+ /* Try clear stall first */
+ usbd_xfer_set_stall(xfer);
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ break;
+
+ default:
+ if (error != USB_ERR_CANCELLED) {
+ printf("ng_ubt: interrupt transfer failed: %s\n",
+ usbd_errstr(error));
+ goto submit_next;
+ }
+ break;
+ }
+} /* ubt_probe_intr_callback */
+
/*
* Called when outgoing control request (HCI command) has completed, i.e.
* HCI command was sent to the device.
Index: sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h
===================================================================
--- sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h
+++ sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h
@@ -57,6 +57,9 @@
#define UBT_DEFAULT_QLEN 64
#define UBT_ISOC_NFRAMES 32 /* should be factor of 8 */
+/* USB driver info flags */
+#define UBT_INTEL8265 (1 << 0)
+
/* Bluetooth USB defines */
enum {
/* Interface #0 transfers */
@@ -74,6 +77,35 @@
UBT_N_TRANSFER, /* total number of transfers */
};
+/* USB control request (HCI command) structure */
+struct ubt_hci_cmd {
+ uint16_t opcode;
+ uint8_t length;
+ uint8_t data[];
+} __attribute__ ((packed));
+#define UBT_HCI_CMD_SIZE(cmd) \
+ ((cmd)->length + offsetof(struct ubt_hci_cmd, data))
+
+/* USB interrupt transfer HCI event header structure */
+struct ubt_hci_evhdr {
+ uint8_t event;
+ uint8_t length;
+} __attribute__ ((packed));
+/* USB interrupt transfer (generic HCI event) structure */
+struct ubt_hci_event {
+ struct ubt_hci_evhdr header;
+ uint8_t data[];
+} __attribute__ ((packed));
+/* USB interrupt transfer (HCI command completion event) structure */
+struct ubt_hci_event_command_compl {
+ struct ubt_hci_evhdr header;
+ uint8_t numpkt;
+ uint16_t opcode;
+ uint8_t data[];
+} __attribute__ ((packed));
+#define UBT_HCI_EVENT_SIZE(evt) \
+ ((evt)->header.length + offsetof(struct ubt_hci_event, data))
+
/* USB device softc structure */
struct ubt_softc {
device_t sc_dev; /* for debug printf */
Index: targets/pseudo/userland/Makefile.depend
===================================================================
--- targets/pseudo/userland/Makefile.depend
+++ targets/pseudo/userland/Makefile.depend
@@ -443,6 +443,7 @@
usr.sbin/bluetooth/hccontrol \
usr.sbin/bluetooth/hcsecd \
usr.sbin/bluetooth/hcseriald \
+ usr.sbin/bluetooth/iwmbtfw \
usr.sbin/bluetooth/l2control \
usr.sbin/bluetooth/l2ping \
usr.sbin/bluetooth/rfcomm_pppd \
Index: tools/build/mk/OptionalObsoleteFiles.inc
===================================================================
--- tools/build/mk/OptionalObsoleteFiles.inc
+++ tools/build/mk/OptionalObsoleteFiles.inc
@@ -470,6 +470,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_DIRS+=etc/bluetooth
OLD_FILES+=etc/rc.d/bluetooth
OLD_FILES+=etc/rc.d/bthidd
@@ -523,6 +524,7 @@
OLD_FILES+=usr/sbin/hccontrol
OLD_FILES+=usr/sbin/hcsecd
OLD_FILES+=usr/sbin/hcseriald
+OLD_FILES+=usr/sbin/iwmbtfw
OLD_FILES+=usr/sbin/l2control
OLD_FILES+=usr/sbin/l2ping
OLD_FILES+=usr/sbin/rfcomm_pppd
@@ -600,6 +602,7 @@
OLD_FILES+=usr/share/man/man8/hccontrol.8.gz
OLD_FILES+=usr/share/man/man8/hcsecd.8.gz
OLD_FILES+=usr/share/man/man8/hcseriald.8.gz
+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
Index: usr.sbin/bluetooth/Makefile
===================================================================
--- usr.sbin/bluetooth/Makefile
+++ usr.sbin/bluetooth/Makefile
@@ -21,6 +21,7 @@
SUBDIR+= bcmfw
SUBDIR+= bthidcontrol
SUBDIR+= bthidd
+SUBDIR+= iwmbtfw
.endif
.include <bsd.subdir.mk>
Index: usr.sbin/bluetooth/iwmbtfw/Makefile
===================================================================
--- /dev/null
+++ usr.sbin/bluetooth/iwmbtfw/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PACKAGE= bluetooth
+PROG= iwmbtfw
+MAN= iwmbtfw.8
+LIBADD+= usb
+SRCS= main.c iwmbt_fw.c iwmbt_hw.c
+
+.include <bsd.prog.mk>
Index: usr.sbin/bluetooth/iwmbtfw/iwmbt_dbg.h
===================================================================
--- /dev/null
+++ usr.sbin/bluetooth/iwmbtfw/iwmbt_dbg.h
@@ -0,0 +1,47 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+#ifndef __IWMBT_DEBUG_H__
+#define __IWMBT_DEBUG_H__
+
+extern int iwmbt_do_debug;
+extern int iwmbt_do_info;
+
+#define iwmbt_err(fmt, ...) \
+ fprintf(stderr, "iwmbtfw: %s: "fmt"\n", __func__, ##__VA_ARGS__)
+#define iwmbt_info(fmt, ...) do { \
+ if (iwmbt_do_info) \
+ fprintf(stderr, "%s: "fmt"\n", __func__, ##__VA_ARGS__);\
+} while (0)
+#define iwmbt_debug(fmt, ...) do { \
+ if (iwmbt_do_debug) \
+ fprintf(stderr, "%s: "fmt"\n", __func__, ##__VA_ARGS__);\
+} while (0)
+
+#endif
Index: usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h
===================================================================
--- /dev/null
+++ usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h
@@ -0,0 +1,79 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __IWMBT_FW_H__
+#define __IWMBT_FW_H__
+
+struct iwmbt_version {
+ uint8_t status;
+ uint8_t hw_platform;
+ uint8_t hw_variant;
+ uint8_t hw_revision;
+ uint8_t fw_variant;
+ uint8_t fw_revision;
+ uint8_t fw_build_num;
+ uint8_t fw_build_ww;
+ uint8_t fw_build_yy;
+ uint8_t fw_patch_num;
+} __attribute__ ((packed));
+
+struct iwmbt_boot_params {
+ uint8_t status;
+ uint8_t otp_format;
+ uint8_t otp_content;
+ uint8_t otp_patch;
+ uint16_t dev_revid;
+ uint8_t secure_boot;
+ uint8_t key_from_hdr;
+ uint8_t key_type;
+ uint8_t otp_lock;
+ uint8_t api_lock;
+ uint8_t debug_lock;
+ uint8_t otp_bdaddr[6];
+ uint8_t min_fw_build_nn;
+ uint8_t min_fw_build_cw;
+ uint8_t min_fw_build_yy;
+ uint8_t limited_cce;
+ uint8_t unlocked_state;
+} __attribute__ ((packed));
+
+struct iwmbt_firmware {
+ char *fwname;
+ int len;
+ unsigned char *buf;
+};
+
+extern int iwmbt_fw_read(struct iwmbt_firmware *fw, const char *fwname);
+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);
+
+#endif
Index: usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c
===================================================================
--- /dev/null
+++ usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c
@@ -0,0 +1,148 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.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 "iwmbt_fw.h"
+#include "iwmbt_dbg.h"
+
+int
+iwmbt_fw_read(struct iwmbt_firmware *fw, const char *fwname)
+{
+ int fd;
+ struct stat sb;
+ unsigned char *buf;
+ ssize_t r;
+ int i;
+
+ 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);
+ }
+
+ i = 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) {
+ iwmbt_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
+iwmbt_fw_free(struct iwmbt_firmware *fw)
+{
+ if (fw->fwname)
+ free(fw->fwname);
+ if (fw->buf)
+ free(fw->buf);
+ memset(fw, 0, sizeof(*fw));
+}
+
+char *
+iwmbt_get_fwname(struct iwmbt_version *ver, struct iwmbt_boot_params *params,
+ const char *prefix, const char *suffix)
+{
+ char *fwname;
+
+ switch (ver->hw_variant) {
+ case 0x0b: /* 8260 */
+ case 0x0c: /* 8265 */
+ asprintf(&fwname, "%s/ibt-%u-%u.%s",
+ prefix,
+ le16toh(ver->hw_variant),
+ le16toh(params->dev_revid),
+ suffix);
+ break;
+
+ case 0x11: /* 9560 */
+ case 0x12: /* 9260 */
+ case 0x13:
+ case 0x14: /* 22161 */
+ asprintf(&fwname, "%s/ibt-%u-%u-%u.%s",
+ prefix,
+ le16toh(ver->hw_variant),
+ le16toh(ver->hw_revision),
+ le16toh(ver->fw_revision),
+ suffix);
+ break;
+
+ default:
+ fwname = NULL;
+ }
+
+ return (fwname);
+}
Index: usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h
===================================================================
--- /dev/null
+++ usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h
@@ -0,0 +1,88 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+#ifndef __IWMBT_HW_H__
+#define __IWMBT_HW_H__
+
+/* USB control request (HCI command) structure */
+struct iwmbt_hci_cmd {
+ uint16_t opcode;
+ uint8_t length;
+ uint8_t data[];
+} __attribute__ ((packed));
+
+#define IWMBT_HCI_CMD_SIZE(cmd) \
+ ((cmd)->length + offsetof(struct iwmbt_hci_cmd, data))
+
+/* USB interrupt transfer HCI event header structure */
+struct iwmbt_hci_evhdr {
+ uint8_t event;
+ uint8_t length;
+} __attribute__ ((packed));
+
+/* USB interrupt transfer (generic HCI event) structure */
+struct iwmbt_hci_event {
+ struct iwmbt_hci_evhdr header;
+ uint8_t data[];
+} __attribute__ ((packed));
+
+/* USB interrupt transfer (HCI command completion event) structure */
+struct iwmbt_hci_event_cmd_compl {
+ struct iwmbt_hci_evhdr header;
+ uint8_t numpkt;
+ uint16_t opcode;
+ uint8_t data[];
+} __attribute__ ((packed));
+
+#define IWMBT_HCI_EVT_COMPL_SIZE(payload) \
+ (offsetof(struct iwmbt_hci_event_cmd_compl, data) + sizeof(payload))
+
+#define IWMBT_CONTROL_ENDPOINT_ADDR 0x00
+#define IWMBT_INTERRUPT_ENDPOINT_ADDR 0x81
+#define IWMBT_BULK_IN_ENDPOINT_ADDR 0x82
+#define IWMBT_BULK_OUT_ENDPOINT_ADDR 0x02
+
+#define IWMBT_HCI_MAX_CMD_SIZE 256
+#define IWMBT_HCI_MAX_EVENT_SIZE 16
+
+#define IWMBT_HCI_CMD_TIMEOUT 2000 /* ms */
+#define IWMBT_LOADCMPL_TIMEOUT 5000 /* ms */
+
+extern int iwmbt_load_fwfile(struct libusb_device_handle *hdl,
+ const struct iwmbt_firmware *fw, uint32_t *boot_param);
+extern int iwmbt_get_version(struct libusb_device_handle *hdl,
+ struct iwmbt_version *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,
+ uint32_t boot_param);
+extern int iwmbt_load_ddc(struct libusb_device_handle *hdl,
+ const struct iwmbt_firmware *ddc);
+extern int iwmbt_set_event_mask(struct libusb_device_handle *hdl);
+
+#endif
Index: usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
===================================================================
--- /dev/null
+++ usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
@@ -0,0 +1,392 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libusb.h>
+
+#include "iwmbt_fw.h"
+#include "iwmbt_hw.h"
+#include "iwmbt_dbg.h"
+
+#define XMIN(x, y) ((x) < (y) ? (x) : (y))
+
+static int
+iwmbt_send_fragment(struct libusb_device_handle *hdl,
+ uint8_t fragment_type, const void *data, uint8_t len, int timeout)
+{
+ int ret, transferred;
+ uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
+ struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *) buf;
+
+ memset(buf, 0, sizeof(buf));
+ cmd->opcode = htole16(0xfc09),
+ cmd->length = len + 1,
+ cmd->data[0] = fragment_type;
+ memcpy(cmd->data + 1, data, len);
+
+ ret = libusb_bulk_transfer(hdl,
+ IWMBT_BULK_OUT_ENDPOINT_ADDR,
+ (uint8_t *)cmd,
+ IWMBT_HCI_CMD_SIZE(cmd),
+ &transferred,
+ timeout);
+
+ if (ret < 0 || transferred != IWMBT_HCI_CMD_SIZE(cmd)) {
+ iwmbt_err("libusb_bulk_transfer() failed: err=%s, size=%zu",
+ libusb_strerror(ret),
+ IWMBT_HCI_CMD_SIZE(cmd));
+ return (-1);
+ }
+
+ ret = libusb_bulk_transfer(hdl,
+ IWMBT_BULK_IN_ENDPOINT_ADDR,
+ buf,
+ sizeof(buf),
+ &transferred,
+ timeout);
+
+ if (ret < 0) {
+ iwmbt_err("libusb_bulk_transfer() failed: err=%s",
+ libusb_strerror(ret));
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+iwmbt_hci_command(struct libusb_device_handle *hdl, struct iwmbt_hci_cmd *cmd,
+ void *event, int size, int *transferred, int timeout)
+{
+ int ret;
+
+ ret = libusb_control_transfer(hdl,
+ LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
+ 0,
+ 0,
+ 0,
+ (uint8_t *)cmd,
+ IWMBT_HCI_CMD_SIZE(cmd),
+ timeout);
+
+ if (ret < 0) {
+ iwmbt_err("libusb_control_transfer() failed: err=%s",
+ libusb_strerror(ret));
+ return (ret);
+ }
+
+ ret = libusb_interrupt_transfer(hdl,
+ IWMBT_INTERRUPT_ENDPOINT_ADDR,
+ event,
+ size,
+ transferred,
+ timeout);
+
+ if (ret < 0)
+ iwmbt_err("libusb_interrupt_transfer() failed: err=%s",
+ libusb_strerror(ret));
+
+ return (ret);
+}
+
+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); \
+ \
+ ret = iwmbt_send_fragment(hdl, \
+ fragment_type, \
+ fw->buf + sent, \
+ XMIN(size, fw->len - sent), \
+ IWMBT_HCI_CMD_TIMEOUT); \
+ \
+ if (ret < 0) { \
+ iwmbt_debug("Failed to send "msg": code=%d", ret); \
+ return (-1); \
+ } \
+ 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);
+
+ IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment");
+ IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 1");
+ IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 2");
+
+ /* skip 4 bytes */
+ sent += 4;
+
+ IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1");
+ IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2");
+
+ /*
+ * Send firmware chunks. Chunk len must be 4 byte aligned.
+ * multiple commands can be combined
+ */
+ while (fw->len - sent - ready >= (int) sizeof(struct iwmbt_hci_cmd)) {
+ cmd = (struct iwmbt_hci_cmd *)(fw->buf + sent + ready);
+ /* Parse firmware for Intel Reset HCI command parameter */
+ if (cmd->opcode == htole16(0xfc0e)) {
+ *boot_param = le32dec(cmd->data);
+ iwmbt_debug("boot_param=0x%08x", *boot_param);
+ }
+ ready += IWMBT_HCI_CMD_SIZE(cmd);
+ while (ready >= 0xFC) {
+ IWMBT_SEND_FRAGMENT(0x01, 0xFC, "firmware chunk");
+ ready -= 0xFC;
+ }
+ if (ready > 0 && ready % 4 == 0) {
+ IWMBT_SEND_FRAGMENT(0x01, ready, "firmware chunk");
+ ready = 0;
+ }
+ }
+
+ /* Wait for firmware download completion event */
+ ret = libusb_interrupt_transfer(hdl,
+ IWMBT_INTERRUPT_ENDPOINT_ADDR,
+ buf,
+ sizeof(buf),
+ &transferred,
+ IWMBT_LOADCMPL_TIMEOUT);
+
+ if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
+ iwmbt_err("libusb_interrupt_transfer() failed: "
+ "err=%s, size=%d",
+ libusb_strerror(ret),
+ transferred);
+ return (-1);
+ }
+
+ /* Expect Vendor Specific Event 0x06 */
+ event = (struct iwmbt_hci_event *)buf;
+ if (event->header.event != 0xFF || event->data[0] != 0x06) {
+ iwmbt_err("firmware download completion event missed");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+iwmbt_get_version(struct libusb_device_handle *hdl,
+ struct iwmbt_version *version)
+{
+ int ret, transferred;
+ struct iwmbt_hci_event_cmd_compl*event;
+ struct iwmbt_hci_cmd cmd = {
+ .opcode = htole16(0xfc05),
+ .length = 0,
+ };
+ uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_version)];
+
+ memset(buf, 0, sizeof(buf));
+
+ ret = iwmbt_hci_command(hdl,
+ &cmd,
+ buf,
+ sizeof(buf),
+ &transferred,
+ IWMBT_HCI_CMD_TIMEOUT);
+
+ if (ret < 0 || transferred != sizeof(buf)) {
+ 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));
+
+ return (0);
+}
+
+int
+iwmbt_get_boot_params(struct libusb_device_handle *hdl,
+ struct iwmbt_boot_params *params)
+{
+ int ret, transferred = 0;
+ struct iwmbt_hci_event_cmd_compl *event;
+ struct iwmbt_hci_cmd cmd = {
+ .opcode = htole16(0xfc0d),
+ .length = 0,
+ };
+ uint8_t buf[IWMBT_HCI_EVT_COMPL_SIZE(struct iwmbt_boot_params)];
+
+ memset(buf, 0, sizeof(buf));
+
+ ret = iwmbt_hci_command(hdl,
+ &cmd,
+ buf,
+ sizeof(buf),
+ &transferred,
+ IWMBT_HCI_CMD_TIMEOUT);
+
+ if (ret < 0 || transferred != sizeof(buf)) {
+ iwmbt_debug("Can't get boot params: code=%d, size=%d",
+ ret,
+ transferred);
+ return (-1);
+ }
+
+ event = (struct iwmbt_hci_event_cmd_compl *)buf;
+ memcpy(params, event->data, sizeof(struct iwmbt_boot_params));
+
+ return (0);
+}
+
+int
+iwmbt_intel_reset(struct libusb_device_handle *hdl, uint32_t boot_param)
+{
+ int ret, transferred = 0;
+ struct iwmbt_hci_event *event;
+ static struct iwmbt_hci_cmd cmd = {
+ .opcode = htole16(0xfc01),
+ .length = 8,
+ .data = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 },
+ };
+ uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
+
+ le32enc(cmd.data + 4, boot_param);
+ memset(buf, 0, sizeof(buf));
+
+ ret = iwmbt_hci_command(hdl,
+ &cmd,
+ buf,
+ sizeof(buf),
+ &transferred,
+ IWMBT_HCI_CMD_TIMEOUT);
+
+ if (ret < 0 || transferred < (int)sizeof(struct iwmbt_hci_event) + 1) {
+ iwmbt_debug("Intel Reset command failed: code=%d, size=%d",
+ ret,
+ transferred);
+ return (ret);
+ }
+
+ /* expect Vendor Specific Event 0x02 */
+ event = (struct iwmbt_hci_event *)buf;
+ if (event->header.event != 0xFF || event->data[0] != 0x02) {
+ iwmbt_err("Intel Reset completion event missed");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+iwmbt_load_ddc(struct libusb_device_handle *hdl,
+ const struct iwmbt_firmware *ddc)
+{
+ int size, sent = 0;
+ int ret, transferred;
+ uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
+ struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *)buf;
+
+ size = ddc->len;
+
+ iwmbt_debug("file=%s, size=%d", ddc->fwname, size);
+
+ while (size > 0) {
+
+ memset(buf, 0, sizeof(buf));
+ cmd->opcode = htole16(0xfc8b);
+ cmd->length = ddc->buf[sent] + 1;
+ memcpy(cmd->data, ddc->buf + sent, XMIN(ddc->buf[sent], size));
+
+ iwmbt_debug("transferring %d bytes, offset %d",
+ cmd->length,
+ sent);
+
+ size -= cmd->length;
+ sent += cmd->length;
+
+ ret = iwmbt_hci_command(hdl,
+ cmd,
+ buf,
+ sizeof(buf),
+ &transferred,
+ IWMBT_HCI_CMD_TIMEOUT);
+
+ if (ret < 0) {
+ iwmbt_debug("Intel Write DDC failed: code=%d", ret);
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+int
+iwmbt_set_event_mask(struct libusb_device_handle *hdl)
+{
+ int ret, transferred = 0;
+ static struct iwmbt_hci_cmd cmd = {
+ .opcode = htole16(0xfc52),
+ .length = 8,
+ .data = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ };
+ uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
+
+ ret = iwmbt_hci_command(hdl,
+ &cmd,
+ buf,
+ sizeof(buf),
+ &transferred,
+ IWMBT_HCI_CMD_TIMEOUT);
+
+ if (ret < 0)
+ iwmbt_debug("Intel Set Event Mask failed: code=%d", ret);
+
+ return (ret);
+}
Index: usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8
===================================================================
--- /dev/null
+++ usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8
@@ -0,0 +1,96 @@
+.\" Copyright (c) 2013, 2016 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 4, 2019
+.Dt IWMBTFW 8
+.Os
+.Sh NAME
+.Nm iwmbtfw
+.Nd firmware download utility for Intel Wireless 8260/8265 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 Intel Wireless 8260/8265 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
+.Pq Dv USB_VENDOR_INTEL2
+and the product ID should be one of the supported devices.
+.Pp
+Firmware files are available in the
+.Pa comms/iwmbt-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 ugen 4 ,
+.Xr devd 8
+.Sh AUTHORS
+.Nm
+is based on
+.Xr ath3kfw 8
+utility used as firmware downloader template and on Linux btintel driver
+source code.
+It is written by
+.An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org .
+.Sh BUGS
+Most likely.
+Please report if found.
Index: usr.sbin/bluetooth/iwmbtfw/main.c
===================================================================
--- /dev/null
+++ usr.sbin/bluetooth/iwmbtfw/main.c
@@ -0,0 +1,456 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#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 "iwmbt_fw.h"
+#include "iwmbt_hw.h"
+#include "iwmbt_dbg.h"
+
+#define _DEFAULT_IWMBT_FIRMWARE_PATH "/usr/share/firmware/intel"
+
+int iwmbt_do_debug = 0;
+int iwmbt_do_info = 0;
+
+struct iwmbt_devid {
+ uint16_t product_id;
+ uint16_t vendor_id;
+};
+
+static struct iwmbt_devid iwmbt_list[] = {
+
+ /* 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_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); i++) {
+ if ((iwmbt_list[i].product_id == d->idProduct) &&
+ (iwmbt_list[i].vendor_id == d->idVendor)) {
+ iwmbt_info("found 8260/8265");
+ return (1);
+ }
+ }
+
+ /* Not found */
+ return (0);
+}
+
+static libusb_device *
+iwmbt_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) {
+ iwmbt_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) {
+ iwmbt_err("libusb_get_device_descriptor: %s",
+ libusb_strerror(r));
+ break;
+ }
+
+ /* Match on the vendor/product id */
+ if (iwmbt_is_8260(&d)) {
+ /*
+ * 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
+iwmbt_dump_version(struct iwmbt_version *ver)
+{
+ iwmbt_info("status 0x%02x", ver->status);
+ iwmbt_info("hw_platform 0x%02x", ver->hw_platform);
+ iwmbt_info("hw_variant 0x%02x", ver->hw_variant);
+ iwmbt_info("hw_revision 0x%02x", ver->hw_revision);
+ iwmbt_info("fw_variant 0x%02x", ver->fw_variant);
+ iwmbt_info("fw_revision 0x%02x", ver->fw_revision);
+ iwmbt_info("fw_build_num 0x%02x", ver->fw_build_num);
+ iwmbt_info("fw_build_ww 0x%02x", ver->fw_build_ww);
+ iwmbt_info("fw_build_yy 0x%02x", ver->fw_build_yy);
+ iwmbt_info("fw_patch_num 0x%02x", ver->fw_patch_num);
+}
+
+static void
+iwmbt_dump_boot_params(struct iwmbt_boot_params *params)
+{
+ iwmbt_info("Device revision: %u", le16toh(params->dev_revid));
+ iwmbt_info("Secure Boot: %s", params->secure_boot ? "on" : "off");
+ iwmbt_info("OTP lock: %s", params->otp_lock ? "on" : "off");
+ iwmbt_info("API lock: %s", params->api_lock ? "on" : "off");
+ iwmbt_info("Debug lock: %s", params->debug_lock ? "on" : "off");
+ iwmbt_info("Minimum firmware build %u week %u year %u",
+ params->min_fw_build_nn,
+ params->min_fw_build_cw,
+ 2000 + params->min_fw_build_yy);
+ iwmbt_info("OTC BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x",
+ params->otp_bdaddr[5],
+ params->otp_bdaddr[4],
+ params->otp_bdaddr[3],
+ params->otp_bdaddr[2],
+ params->otp_bdaddr[1],
+ params->otp_bdaddr[0]);
+}
+
+static int
+iwmbt_init_firmware(libusb_device_handle *hdl, const char *firmware_path,
+ uint32_t *boot_param)
+{
+ struct iwmbt_firmware fw;
+ int ret;
+
+ iwmbt_debug("loading %s", firmware_path);
+
+ /* Read in the firmware */
+ if (iwmbt_fw_read(&fw, firmware_path) <= 0) {
+ iwmbt_debug("iwmbt_fw_read() failed");
+ return (-1);
+ }
+
+ /* Load in the firmware */
+ ret = iwmbt_load_fwfile(hdl, &fw, boot_param);
+ if (ret < 0)
+ iwmbt_debug("Loading firmware file failed");
+
+ /* free it */
+ iwmbt_fw_free(&fw);
+
+ return (ret);
+}
+
+static int
+iwmbt_init_ddc(libusb_device_handle *hdl, const char *ddc_path)
+{
+ struct iwmbt_firmware ddc;
+ int ret;
+
+ iwmbt_debug("loading %s", ddc_path);
+
+ /* Read in the DDC file */
+ if (iwmbt_fw_read(&ddc, ddc_path) <= 0) {
+ iwmbt_debug("iwmbt_fw_read() failed");
+ return (-1);
+ }
+
+ /* Load in the DDC file */
+ ret = iwmbt_load_ddc(hdl, &ddc);
+ if (ret < 0)
+ iwmbt_debug("Loading DDC file failed");
+
+ /* free it */
+ iwmbt_fw_free(&ddc);
+
+ return (ret);
+}
+
+/*
+ * 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: iwmbtfw (-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;
+ static struct iwmbt_version ver;
+ static struct iwmbt_boot_params params;
+ uint32_t boot_param;
+ int r;
+ uint8_t bus_id = 0, dev_id = 0;
+ int devid_set = 0;
+ int n;
+ char *firmware_dir = NULL;
+ char *firmware_path = NULL;
+ int retcode = 1;
+
+ /* 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':
+ iwmbt_do_debug = 1;
+ break;
+ case 'f': /* firmware dir */
+ if (firmware_dir)
+ free(firmware_dir);
+ firmware_dir = strdup(optarg);
+ break;
+ case 'I':
+ iwmbt_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) {
+ iwmbt_err("libusb_init failed: code %d", r);
+ exit(127);
+ }
+
+ 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);
+ if (dev == NULL) {
+ iwmbt_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) {
+ iwmbt_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) {
+ iwmbt_err("libusb_kernel_driver_active() failed: code %d", r);
+ goto shutdown;
+ }
+ if (r > 0) {
+ iwmbt_info("Firmware has already been downloaded");
+ retcode = 0;
+ goto shutdown;
+ }
+
+ /* Get Intel version */
+ r = iwmbt_get_version(hdl, &ver);
+ if (r < 0) {
+ iwmbt_debug("iwmbt_get_version() failedL 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) {
+ iwmbt_info("Firmware has already been downloaded");
+ retcode = 0;
+ goto reset;
+ }
+
+ if (ver.fw_variant != 0x06){
+ iwmbt_err("unknown fw_variant 0x%02x", (int) ver.fw_variant);
+ goto shutdown;
+ }
+
+ /* Read Intel Secure Boot Params */
+ r = iwmbt_get_boot_params(hdl, ¶ms);
+ if (r < 0) {
+ iwmbt_debug("iwmbt_get_boot_params() failed!");
+ goto shutdown;
+ }
+ iwmbt_dump_boot_params(¶ms);
+
+ /* Check if firmware fragments are ACKed with a cmd complete event */
+ if (params.limited_cce != 0x00) {
+ iwmbt_err("Unsupported Intel firmware loading method (%u)",
+ params.limited_cce);
+ goto shutdown;
+ }
+
+ /* Default the firmware path */
+ if (firmware_dir == NULL)
+ firmware_dir = strdup(_DEFAULT_IWMBT_FIRMWARE_PATH);
+
+ firmware_path = iwmbt_get_fwname(&ver, ¶ms, 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);
+ free(firmware_path);
+ if (r < 0)
+ goto shutdown;
+
+ iwmbt_info("Firmware download complete");
+
+ 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(&ver, ¶ms, 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");
+
+reset:
+
+ /* Ask kernel driver to probe and attach device again */
+ r = libusb_reset_device(hdl);
+ if (r != 0)
+ iwmbt_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)
+ iwmbt_info("Firmware download is succesful!");
+ else
+ iwmbt_err("Firmware download failed!");
+
+ return (retcode);
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Oct 17, 10:02 AM (4 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
23806395
Default Alt Text
D21071.id60150.diff (47 KB)
Attached To
Mode
D21071: Firmware loader for Intel Wireless 8260 based Bluetooth USB devices
Attached
Detach File
Event Timeline
Log In to Comment