Page MenuHomeFreeBSD

D21071.id60150.diff
No OneTemporary

D21071.id60150.diff

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, &params);
+ if (r < 0) {
+ iwmbt_debug("iwmbt_get_boot_params() failed!");
+ goto shutdown;
+ }
+ iwmbt_dump_boot_params(&params);
+
+ /* 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, &params, 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, &params, 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

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)

Event Timeline