git: c1643cedbf24 - main - iwmbtfw(4): Add support for 9260/9560 bluetooth adaptors

From: Vladimir Kondratyev <wulf_at_FreeBSD.org>
Date: Wed, 06 Nov 2024 23:31:52 UTC
The branch main has been updated by wulf:

URL: https://cgit.FreeBSD.org/src/commit/?id=c1643cedbf243424370162febf6d9180bdd1df58

commit c1643cedbf243424370162febf6d9180bdd1df58
Author:     Vladimir Kondratyev <wulf@FreeBSD.org>
AuthorDate: 2024-11-06 23:26:51 +0000
Commit:     Vladimir Kondratyev <wulf@FreeBSD.org>
CommitDate: 2024-11-06 23:26:51 +0000

    iwmbtfw(4): Add support for 9260/9560 bluetooth adaptors
    
    Required firmware files are already included in to comms/iwmbt-firmware port
    
    Sponsored by:   Future Crew LLC
    MFC after:      1 month
    Reviewed by:    bz
    Differential Revision:  https://reviews.freebsd.org/D46735
---
 usr.sbin/bluetooth/iwmbtfw/Makefile     |   2 +
 usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c   |  21 +++
 usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h   |  71 +++++++
 usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c   | 183 ++++++++++++++++--
 usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h   |  12 +-
 usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8    |   4 +-
 usr.sbin/bluetooth/iwmbtfw/iwmbtfw.conf |   5 +-
 usr.sbin/bluetooth/iwmbtfw/main.c       | 323 +++++++++++++++++++++++++-------
 8 files changed, 531 insertions(+), 90 deletions(-)

diff --git a/usr.sbin/bluetooth/iwmbtfw/Makefile b/usr.sbin/bluetooth/iwmbtfw/Makefile
index dde586b3aa99..c5cf037eac06 100644
--- a/usr.sbin/bluetooth/iwmbtfw/Makefile
+++ b/usr.sbin/bluetooth/iwmbtfw/Makefile
@@ -4,6 +4,8 @@ CONFSDIR=       /etc/devd
 PROG=		iwmbtfw
 MAN=		iwmbtfw.8
 LIBADD+=	usb
+# Not having NDEBUG defined will enable assertions
+CFLAGS+=	-DNDEBUG
 SRCS=		main.c iwmbt_fw.c iwmbt_hw.c
 
 .include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c
index 6816b152912d..815b40982d5b 100644
--- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c
+++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c
@@ -3,6 +3,7 @@
  *
  * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
  * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
+ * Copyright (c) 2023 Future Crew LLC.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -170,3 +171,23 @@ iwmbt_get_fwname(struct iwmbt_version *ver, struct iwmbt_boot_params *params,
 
 	return (fwname);
 }
+
+char *
+iwmbt_get_fwname_tlv(struct iwmbt_version_tlv *ver, const char *prefix,
+    const char *suffix)
+{
+	char *fwname;
+
+#define	IWMBT_PACK_CNVX_TOP(cnvx_top)	((uint16_t)(	\
+	((cnvx_top) & 0x0f000000) >> 16 |		\
+	((cnvx_top) & 0x0000000f) << 12 |		\
+	((cnvx_top) & 0x00000ff0) >> 4))
+
+	asprintf(&fwname, "%s/ibt-%04x-%04x.%s",
+	    prefix,
+	    IWMBT_PACK_CNVX_TOP(ver->cnvi_top),
+	    IWMBT_PACK_CNVX_TOP(ver->cnvr_top),
+	    suffix);
+
+	return (fwname);
+}
diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h
index f737c1c0c2c8..2666d123c8f0 100644
--- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h
+++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h
@@ -3,6 +3,7 @@
  *
  * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
  * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
+ * Copyright (c) 2023 Future Crew LLC.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -29,6 +30,15 @@
 #ifndef	__IWMBT_FW_H__
 #define	__IWMBT_FW_H__
 
+#include <sys/types.h>
+#define	L2CAP_SOCKET_CHECKED
+#include <bluetooth.h>
+
+#define	RSA_HEADER_LEN		644
+#define	ECDSA_HEADER_LEN	320
+#define	ECDSA_OFFSET		RSA_HEADER_LEN
+#define	CSS_HEADER_OFFSET	8
+
 struct iwmbt_version {
 	uint8_t status;
 	uint8_t hw_platform;
@@ -62,6 +72,65 @@ struct iwmbt_boot_params {
 	uint8_t unlocked_state;
 } __attribute__ ((packed));
 
+enum {
+	IWMBT_TLV_CNVI_TOP = 0x10,
+	IWMBT_TLV_CNVR_TOP,
+	IWMBT_TLV_CNVI_BT,
+	IWMBT_TLV_CNVR_BT,
+	IWMBT_TLV_CNVI_OTP,
+	IWMBT_TLV_CNVR_OTP,
+	IWMBT_TLV_DEV_REV_ID,
+	IWMBT_TLV_USB_VENDOR_ID,
+	IWMBT_TLV_USB_PRODUCT_ID,
+	IWMBT_TLV_PCIE_VENDOR_ID,
+	IWMBT_TLV_PCIE_DEVICE_ID,
+	IWMBT_TLV_PCIE_SUBSYSTEM_ID,
+	IWMBT_TLV_IMAGE_TYPE,
+	IWMBT_TLV_TIME_STAMP,
+	IWMBT_TLV_BUILD_TYPE,
+	IWMBT_TLV_BUILD_NUM,
+	IWMBT_TLV_FW_BUILD_PRODUCT,
+	IWMBT_TLV_FW_BUILD_HW,
+	IWMBT_TLV_FW_STEP,
+	IWMBT_TLV_BT_SPEC,
+	IWMBT_TLV_MFG_NAME,
+	IWMBT_TLV_HCI_REV,
+	IWMBT_TLV_LMP_SUBVER,
+	IWMBT_TLV_OTP_PATCH_VER,
+	IWMBT_TLV_SECURE_BOOT,
+	IWMBT_TLV_KEY_FROM_HDR,
+	IWMBT_TLV_OTP_LOCK,
+	IWMBT_TLV_API_LOCK,
+	IWMBT_TLV_DEBUG_LOCK,
+	IWMBT_TLV_MIN_FW,
+	IWMBT_TLV_LIMITED_CCE,
+	IWMBT_TLV_SBE_TYPE,
+	IWMBT_TLV_OTP_BDADDR,
+	IWMBT_TLV_UNLOCKED_STATE
+};
+
+struct iwmbt_version_tlv {
+	uint32_t cnvi_top;
+	uint32_t cnvr_top;
+	uint32_t cnvi_bt;
+	uint32_t cnvr_bt;
+	uint16_t dev_rev_id;
+	uint8_t img_type;
+	uint16_t timestamp;
+	uint8_t build_type;
+	uint32_t build_num;
+	uint8_t secure_boot;
+	uint8_t otp_lock;
+	uint8_t api_lock;
+	uint8_t debug_lock;
+	uint8_t min_fw_build_nn;
+	uint8_t min_fw_build_cw;
+	uint8_t min_fw_build_yy;
+	uint8_t limited_cce;
+	uint8_t sbe_type;
+	bdaddr_t otp_bd_addr;
+};
+
 struct iwmbt_firmware {
 	char *fwname;
 	int len;
@@ -73,5 +142,7 @@ extern	void iwmbt_fw_free(struct iwmbt_firmware *fw);
 extern	char *iwmbt_get_fwname(struct iwmbt_version *ver,
 	struct iwmbt_boot_params *params, const char *prefix,
 	const char *suffix);
+extern	char *iwmbt_get_fwname_tlv(struct iwmbt_version_tlv *ver,
+	const char *prefix, const char *suffix);
 
 #endif
diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
index ea732c9925ee..05a851f9d85b 100644
--- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
+++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
@@ -2,6 +2,7 @@
  * SPDX-License-Identifier: BSD-2-Clause
  *
  * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
+ * Copyright (c) 2023 Future Crew LLC.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -29,6 +30,7 @@
 #include <sys/endian.h>
 #include <sys/stat.h>
 
+#include <assert.h>
 #include <err.h>
 #include <errno.h>
 #include <stddef.h>
@@ -267,16 +269,6 @@ iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
 	return (activate_patch);
 }
 
-int
-iwmbt_load_fwfile(struct libusb_device_handle *hdl,
-    const struct iwmbt_firmware *fw, uint32_t *boot_param)
-{
-	int ready = 0, sent = 0;
-	int ret, transferred;
-	struct iwmbt_hci_cmd *cmd;
-	struct iwmbt_hci_event *event;
-	uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
-
 #define	IWMBT_SEND_FRAGMENT(fragment_type, size, msg)	do {		\
 	iwmbt_debug("transferring %d bytes, offset %d", size, sent);	\
 									\
@@ -293,12 +285,11 @@ iwmbt_load_fwfile(struct libusb_device_handle *hdl,
 	sent += size;							\
 } while (0)
 
-	if (fw->len < 644) {
-		iwmbt_err("Invalid size of firmware file (%d)", fw->len);
-		return (-1);
-	}
-
-	iwmbt_debug("file=%s, size=%d", fw->fwname, fw->len);
+int
+iwmbt_load_rsa_header(struct libusb_device_handle *hdl,
+    const struct iwmbt_firmware *fw)
+{
+	int ret, sent = 0;
 
 	IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment");
 	IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 1");
@@ -310,6 +301,32 @@ iwmbt_load_fwfile(struct libusb_device_handle *hdl,
 	IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1");
 	IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2");
 
+	return (0);
+}
+
+int
+iwmbt_load_ecdsa_header(struct libusb_device_handle *hdl,
+    const struct iwmbt_firmware *fw)
+{
+	int ret, sent = ECDSA_OFFSET;
+
+	IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment");
+	IWMBT_SEND_FRAGMENT(0x03, 0x60, "public key");
+	IWMBT_SEND_FRAGMENT(0x02, 0x60, "signature");
+
+	return (0);
+}
+
+int
+iwmbt_load_fwfile(struct libusb_device_handle *hdl,
+    const struct iwmbt_firmware *fw, uint32_t *boot_param, int offset)
+{
+	int ready = 0, sent = offset;
+	int ret, transferred;
+	struct iwmbt_hci_cmd *cmd;
+	struct iwmbt_hci_event *event;
+	uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
+
 	/*
 	 * Send firmware chunks. Chunk len must be 4 byte aligned.
 	 * multiple commands can be combined
@@ -460,6 +477,140 @@ iwmbt_get_version(struct libusb_device_handle *hdl,
 	return (0);
 }
 
+int
+iwmbt_get_version_tlv(struct libusb_device_handle *hdl,
+    struct iwmbt_version_tlv *version)
+{
+	int ret, transferred;
+	struct iwmbt_hci_event_cmd_compl *event;
+	static struct iwmbt_hci_cmd cmd = {
+		.opcode = htole16(0xfc05),
+		.length = 1,
+		.data = { 0xff },
+	};
+	uint8_t status, datalen, type, len;
+	uint8_t *data;
+	uint8_t buf[255];
+
+	memset(buf, 0, sizeof(buf));
+
+	ret = iwmbt_hci_command(hdl,
+	    &cmd,
+	    buf,
+	    sizeof(buf),
+	    &transferred,
+	    IWMBT_HCI_CMD_TIMEOUT);
+
+	if (ret < 0 || transferred < (int)IWMBT_HCI_EVT_COMPL_SIZE(uint16_t)) {
+		 iwmbt_debug("Can't get version: code=%d, size=%d",
+		     ret,
+		     transferred);
+		 return (-1);
+	}
+
+	event = (struct iwmbt_hci_event_cmd_compl *)buf;
+	memcpy(version, event->data, sizeof(struct iwmbt_version));
+
+	datalen = event->header.length - IWMBT_HCI_EVENT_COMPL_HEAD_SIZE;
+	data = event->data;
+	status = *data++;
+	if (status != 0)
+		return (-1);
+	datalen--;
+
+	while (datalen >= 2) {
+		type = *data++;
+		len = *data++;
+		datalen -= 2;
+
+		if (datalen < len)
+			return (-1);
+
+		switch (type) {
+		case IWMBT_TLV_CNVI_TOP:
+			assert(len == 4);
+			version->cnvi_top = le32dec(data);
+			break;
+		case IWMBT_TLV_CNVR_TOP:
+			assert(len == 4);
+			version->cnvr_top = le32dec(data);
+			break;
+		case IWMBT_TLV_CNVI_BT:
+			assert(len == 4);
+			version->cnvi_bt = le32dec(data);
+			break;
+		case IWMBT_TLV_CNVR_BT:
+			assert(len == 4);
+			version->cnvr_bt = le32dec(data);
+			break;
+		case IWMBT_TLV_DEV_REV_ID:
+			assert(len == 2);
+			version->dev_rev_id = le16dec(data);
+			break;
+		case IWMBT_TLV_IMAGE_TYPE:
+			assert(len == 1);
+			version->img_type = *data;
+			break;
+		case IWMBT_TLV_TIME_STAMP:
+			assert(len == 2);
+			version->min_fw_build_cw = data[0];
+			version->min_fw_build_yy = data[1];
+			version->timestamp = le16dec(data);
+			break;
+		case IWMBT_TLV_BUILD_TYPE:
+			assert(len == 1);
+			version->build_type = *data;
+			break;
+		case IWMBT_TLV_BUILD_NUM:
+			assert(len == 4);
+			version->min_fw_build_nn = *data;
+			version->build_num = le32dec(data);
+			break;
+		case IWMBT_TLV_SECURE_BOOT:
+			assert(len == 1);
+			version->secure_boot = *data;
+			break;
+		case IWMBT_TLV_OTP_LOCK:
+			assert(len == 1);
+			version->otp_lock = *data;
+			break;
+		case IWMBT_TLV_API_LOCK:
+			assert(len == 1);
+			version->api_lock = *data;
+			break;
+		case IWMBT_TLV_DEBUG_LOCK:
+			assert(len == 1);
+			version->debug_lock = *data;
+			break;
+		case IWMBT_TLV_MIN_FW:
+			assert(len == 3);
+			version->min_fw_build_nn = data[0];
+			version->min_fw_build_cw = data[1];
+			version->min_fw_build_yy = data[2];
+			break;
+		case IWMBT_TLV_LIMITED_CCE:
+			assert(len == 1);
+			version->limited_cce = *data;
+			break;
+		case IWMBT_TLV_SBE_TYPE:
+			assert(len == 1);
+			version->sbe_type = *data;
+			break;
+		case IWMBT_TLV_OTP_BDADDR:
+			memcpy(&version->otp_bd_addr, data, sizeof(bdaddr_t));
+			break;
+		default:
+			/* Ignore other types */
+			break;
+		}
+
+		datalen -= len;
+		data += len;
+	}
+
+	return (0);
+}
+
 int
 iwmbt_get_boot_params(struct libusb_device_handle *hdl,
     struct iwmbt_boot_params *params)
diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h
index eafb2c3f31d8..9467c3807a2a 100644
--- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h
+++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h
@@ -2,6 +2,7 @@
  * SPDX-License-Identifier: BSD-2-Clause
  *
  * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
+ * Copyright (c) 2023 Future Crew LLC.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -59,6 +60,9 @@ struct iwmbt_hci_event_cmd_compl {
 
 #define IWMBT_HCI_EVT_COMPL_SIZE(payload) \
 	(offsetof(struct iwmbt_hci_event_cmd_compl, data) + sizeof(payload))
+#define	IWMBT_HCI_EVENT_COMPL_HEAD_SIZE \
+	(offsetof(struct iwmbt_hci_event_cmd_compl, data) - \
+	 offsetof(struct iwmbt_hci_event_cmd_compl, numpkt))
 
 #define	IWMBT_CONTROL_ENDPOINT_ADDR	0x00
 #define	IWMBT_INTERRUPT_ENDPOINT_ADDR	0x81
@@ -73,13 +77,19 @@ struct iwmbt_hci_event_cmd_compl {
 
 extern	int iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
 	    const struct iwmbt_firmware *fw);
+extern	int iwmbt_load_rsa_header(struct libusb_device_handle *hdl,
+	    const struct iwmbt_firmware *fw);
+extern	int iwmbt_load_ecdsa_header(struct libusb_device_handle *hdl,
+	    const struct iwmbt_firmware *fw);
 extern	int iwmbt_load_fwfile(struct libusb_device_handle *hdl,
-	    const struct iwmbt_firmware *fw, uint32_t *boot_param);
+	    const struct iwmbt_firmware *fw, uint32_t *boot_param, int offset);
 extern	int iwmbt_enter_manufacturer(struct libusb_device_handle *hdl);
 extern	int iwmbt_exit_manufacturer(struct libusb_device_handle *hdl,
 	    int mode);
 extern	int iwmbt_get_version(struct libusb_device_handle *hdl,
 	    struct iwmbt_version *version);
+extern	int iwmbt_get_version_tlv(struct libusb_device_handle *hdl,
+	    struct iwmbt_version_tlv *version);
 extern	int iwmbt_get_boot_params(struct libusb_device_handle *hdl,
 	    struct iwmbt_boot_params *params);
 extern	int iwmbt_intel_reset(struct libusb_device_handle *hdl,
diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8 b/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8
index 1924c5f3ce74..2ce828cb5ebe 100644
--- a/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8
+++ b/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8
@@ -26,7 +26,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd May 31, 2024
+.Dd September 15, 2024
 .Dt IWMBTFW 8
 .Os
 .Sh NAME
@@ -48,7 +48,7 @@ device.
 .Pp
 This utility will
 .Em only
-work with Intel Wireless 7260/8260/8265 chip based Bluetooth USB devices
+work with Intel Wireless 7260/8260/9260 chip based Bluetooth USB devices
 and some of their successors.
 The identification is currently based on USB vendor ID/product ID pair.
 The vendor ID should be 0x8087
diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.conf b/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.conf
index ef8d5263383b..e30a3c15ccaa 100644
--- a/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.conf
+++ b/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.conf
@@ -1,11 +1,12 @@
 #
-# Download Intel Wireless 8260/8265 bluetooth adaptor firmware
+# Download Intel Wireless bluetooth adaptor firmware
+#
 
 notify 100 {
 	match "system"		"USB";
 	match "subsystem"	"DEVICE";
 	match "type"		"ATTACH";
 	match "vendor"		"0x8087";
-	match "product"		"(0x07dc|0x0a2a|0x0aa7|0x0a2b|0x0aaa|0x0025|0x0026|0x0029)";
+	match "product"		"(0x07dc|0x0a2a|0x0aa7|0x0a2b|0x0aaa|0x0025|0x0026|0x0029|0x0032|0x0033)";
 	action "/usr/sbin/iwmbtfw -d $cdev -f /usr/local/share/iwmbt-firmware";
 };
diff --git a/usr.sbin/bluetooth/iwmbtfw/main.c b/usr.sbin/bluetooth/iwmbtfw/main.c
index 9ef31b906b77..c2b67ce01906 100644
--- a/usr.sbin/bluetooth/iwmbtfw/main.c
+++ b/usr.sbin/bluetooth/iwmbtfw/main.c
@@ -3,6 +3,7 @@
  *
  * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
  * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
+ * Copyright (c) 2023 Future Crew LLC.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -50,71 +51,63 @@
 int	iwmbt_do_debug = 0;
 int	iwmbt_do_info = 0;
 
+enum iwmbt_device {
+	IWMBT_DEVICE_UNKNOWN,
+	IWMBT_DEVICE_7260,
+	IWMBT_DEVICE_8260,
+	IWMBT_DEVICE_9260,
+};
+
 struct iwmbt_devid {
 	uint16_t product_id;
 	uint16_t vendor_id;
+	enum iwmbt_device device;
 };
 
-static struct iwmbt_devid iwmbt_list_72xx[] = {
+static struct iwmbt_devid iwmbt_list[] = {
 
-	/* Intel Wireless 7260/7265 and successors */
-	{ .vendor_id = 0x8087, .product_id = 0x07dc },
-	{ .vendor_id = 0x8087, .product_id = 0x0a2a },
-	{ .vendor_id = 0x8087, .product_id = 0x0aa7 },
-};
+    /* Intel Wireless 7260/7265 and successors */
+    { .vendor_id = 0x8087, .product_id = 0x07dc, .device = IWMBT_DEVICE_7260 },
+    { .vendor_id = 0x8087, .product_id = 0x0a2a, .device = IWMBT_DEVICE_7260 },
+    { .vendor_id = 0x8087, .product_id = 0x0aa7, .device = IWMBT_DEVICE_7260 },
 
-static struct iwmbt_devid iwmbt_list_82xx[] = {
+    /* Intel Wireless 8260/8265 and successors */
+    { .vendor_id = 0x8087, .product_id = 0x0a2b, .device = IWMBT_DEVICE_8260 },
+    { .vendor_id = 0x8087, .product_id = 0x0aaa, .device = IWMBT_DEVICE_8260 },
+    { .vendor_id = 0x8087, .product_id = 0x0025, .device = IWMBT_DEVICE_8260 },
+    { .vendor_id = 0x8087, .product_id = 0x0026, .device = IWMBT_DEVICE_8260 },
+    { .vendor_id = 0x8087, .product_id = 0x0029, .device = IWMBT_DEVICE_8260 },
 
-	/* Intel Wireless 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 },
+    /* Intel Wireless 9260/9560 and successors */
+    { .vendor_id = 0x8087, .product_id = 0x0032, .device = IWMBT_DEVICE_9260 },
+    { .vendor_id = 0x8087, .product_id = 0x0033, .device = IWMBT_DEVICE_9260 },
 };
 
-static int
-iwmbt_is_7260(struct libusb_device_descriptor *d)
+static enum iwmbt_device
+iwmbt_is_supported(struct libusb_device_descriptor *d)
 {
 	int i;
 
 	/* Search looking for whether it's an 7260/7265 */
-	for (i = 0; i < (int) nitems(iwmbt_list_72xx); i++) {
-		if ((iwmbt_list_72xx[i].product_id == d->idProduct) &&
-		    (iwmbt_list_72xx[i].vendor_id == d->idVendor)) {
-			iwmbt_info("found 7260/7265");
-			return (1);
-		}
-	}
-
-	/* Not found */
-	return (0);
-}
-
-static int
-iwmbt_is_8260(struct libusb_device_descriptor *d)
-{
-	int i;
-
-	/* Search looking for whether it's an 8260/8265 */
-	for (i = 0; i < (int) nitems(iwmbt_list_82xx); i++) {
-		if ((iwmbt_list_82xx[i].product_id == d->idProduct) &&
-		    (iwmbt_list_82xx[i].vendor_id == d->idVendor)) {
-			iwmbt_info("found 8260/8265");
-			return (1);
+	for (i = 0; i < (int) nitems(iwmbt_list); i++) {
+		if ((iwmbt_list[i].product_id == d->idProduct) &&
+		    (iwmbt_list[i].vendor_id == d->idVendor)) {
+			iwmbt_info("found iwmbtfw compatible");
+			return (iwmbt_list[i].device);
 		}
 	}
 
 	/* Not found */
-	return (0);
+	return (IWMBT_DEVICE_UNKNOWN);
 }
 
 static libusb_device *
 iwmbt_find_device(libusb_context *ctx, int bus_id, int dev_id,
-    int *iwmbt_use_old_method)
+    enum iwmbt_device *iwmbt_device)
 {
 	libusb_device **list, *dev = NULL, *found = NULL;
 	struct libusb_device_descriptor d;
+	enum iwmbt_device device;
 	ssize_t cnt, i;
 	int r;
 
@@ -141,20 +134,13 @@ iwmbt_find_device(libusb_context *ctx, int bus_id, int dev_id,
 			}
 
 			/* Match on the vendor/product id */
-			if (iwmbt_is_7260(&d)) {
+			device = iwmbt_is_supported(&d);
+			if (device != IWMBT_DEVICE_UNKNOWN) {
 				/*
 				 * Take a reference so it's not freed later on.
 				 */
 				found = libusb_ref_device(dev);
-				*iwmbt_use_old_method = 1;
-				break;
-			} else
-			if (iwmbt_is_8260(&d)) {
-				/*
-				 * Take a reference so it's not freed later on.
-				 */
-				found = libusb_ref_device(dev);
-				*iwmbt_use_old_method = 0;
+				*iwmbt_device = device;
 				break;
 			}
 		}
@@ -200,6 +186,44 @@ iwmbt_dump_boot_params(struct iwmbt_boot_params *params)
 	    params->otp_bdaddr[0]);
 }
 
+static void
+iwmbt_dump_version_tlv(struct iwmbt_version_tlv *ver)
+{
+	iwmbt_info("cnvi_top     0x%08x", ver->cnvi_top);
+	iwmbt_info("cnvr_top     0x%08x", ver->cnvr_top);
+	iwmbt_info("cnvi_bt      0x%08x", ver->cnvi_bt);
+	iwmbt_info("cnvr_bt      0x%08x", ver->cnvr_bt);
+	iwmbt_info("dev_rev_id   0x%04x", ver->dev_rev_id);
+	iwmbt_info("img_type     0x%02x", ver->img_type);
+	iwmbt_info("timestamp    0x%04x", ver->timestamp);
+	iwmbt_info("build_type   0x%02x", ver->build_type);
+	iwmbt_info("build_num    0x%08x", ver->build_num);
+	iwmbt_info("Secure Boot:  %s", ver->secure_boot ? "on" : "off");
+	iwmbt_info("OTP lock:     %s", ver->otp_lock    ? "on" : "off");
+	iwmbt_info("API lock:     %s", ver->api_lock    ? "on" : "off");
+	iwmbt_info("Debug lock:   %s", ver->debug_lock  ? "on" : "off");
+	iwmbt_info("Minimum firmware build %u week %u year %u",
+	    ver->min_fw_build_nn,
+	    ver->min_fw_build_cw,
+	    2000 + ver->min_fw_build_yy);
+	iwmbt_info("limited_cce  0x%02x", ver->limited_cce);
+	iwmbt_info("sbe_type     0x%02x", ver->sbe_type);
+	iwmbt_info("OTC BD_ADDR:  %02x:%02x:%02x:%02x:%02x:%02x",
+	    ver->otp_bd_addr.b[5],
+	    ver->otp_bd_addr.b[4],
+	    ver->otp_bd_addr.b[3],
+	    ver->otp_bd_addr.b[2],
+	    ver->otp_bd_addr.b[1],
+	    ver->otp_bd_addr.b[0]);
+	if (ver->img_type == 0x01 || ver->img_type == 0x03)
+		iwmbt_info("%s timestamp %u.%u buildtype %u build %u",
+		    ver->img_type == 0x01 ? "Bootloader" : "Firmware",
+		    2000 + (ver->timestamp >> 8),
+		    ver->timestamp & 0xff,
+		    ver->build_type,
+		    ver->build_num);
+}
+
 static int
 iwmbt_patch_firmware(libusb_device_handle *hdl, const char *firmware_path)
 {
@@ -227,10 +251,10 @@ iwmbt_patch_firmware(libusb_device_handle *hdl, const char *firmware_path)
 
 static int
 iwmbt_init_firmware(libusb_device_handle *hdl, const char *firmware_path,
-    uint32_t *boot_param)
+    uint32_t *boot_param, uint8_t hw_variant, uint8_t sbe_type)
 {
 	struct iwmbt_firmware fw;
-	int ret;
+	int header_len, ret = -1;
 
 	iwmbt_debug("loading %s", firmware_path);
 
@@ -240,12 +264,76 @@ iwmbt_init_firmware(libusb_device_handle *hdl, const char *firmware_path,
 		return (-1);
 	}
 
-	/* Load in the firmware */
-	ret = iwmbt_load_fwfile(hdl, &fw, boot_param);
+	iwmbt_debug("Firmware file size=%d", fw.len);
+
+	if (hw_variant <= 0x14) {
+		/*
+		 * Hardware variants 0x0b, 0x0c, 0x11 - 0x14 .sfi file have
+		 * a RSA header of 644 bytes followed by Command Buffer.
+		 */
+		header_len = RSA_HEADER_LEN;
+		if (fw.len < header_len) {
+			iwmbt_err("Invalid size of firmware file (%d)", fw.len);
+			ret = -1;
+			goto exit;
+		}
+
+		/* Check if the CSS Header version is RSA(0x00010000) */
+		if (le32dec(fw.buf + CSS_HEADER_OFFSET) != 0x00010000) {
+			iwmbt_err("Invalid CSS Header version");
+			ret = -1;
+			goto exit;
+		}
+
+		/* Only RSA secure boot engine supported */
+		if (sbe_type != 0x00) {
+			iwmbt_err("Invalid SBE type for hardware variant (%d)",
+			    hw_variant);
+			ret = -1;
+			goto exit;
+		}
+
+	} else if (hw_variant >= 0x17) {
+		/*
+		 * Hardware variants 0x17, 0x18 onwards support both RSA and
+		 * ECDSA secure boot engine. As a result, the corresponding sfi
+		 * file will have RSA header of 644, ECDSA header of 320 bytes
+		 * followed by Command Buffer.
+		 */
+		header_len = ECDSA_OFFSET + ECDSA_HEADER_LEN;
+		if (fw.len < header_len) {
+			iwmbt_err("Invalid size of firmware file (%d)", fw.len);
+			ret = -1;
+			goto exit;
+		}
+
+		/* Check if CSS header for ECDSA follows the RSA header */
+		if (fw.buf[ECDSA_OFFSET] != 0x06) {
+			ret = -1;
+			goto exit;
+		}
+
+		/* Check if the CSS Header version is ECDSA(0x00020000) */
+		if (le32dec(fw.buf + ECDSA_OFFSET + CSS_HEADER_OFFSET) != 0x00020000) {
+			iwmbt_err("Invalid CSS Header version");
+			ret = -1;
+			goto exit;
+		}
+	}
+
+	/* Load in the CSS header */
+	if (sbe_type == 0x00)
+		ret = iwmbt_load_rsa_header(hdl, &fw);
+	else if (sbe_type == 0x01)
+		ret = iwmbt_load_ecdsa_header(hdl, &fw);
 	if (ret < 0)
-		iwmbt_debug("Loading firmware file failed");
+		goto exit;
 
-	/* free it */
+	/* Load in the Command Buffer */
+	ret = iwmbt_load_fwfile(hdl, &fw, boot_param, header_len);
+
+exit:
+	/* free firmware */
 	iwmbt_fw_free(&fw);
 
 	return (ret);
@@ -318,6 +406,7 @@ main(int argc, char *argv[])
 	libusb_device *dev = NULL;
 	libusb_device_handle *hdl = NULL;
 	static struct iwmbt_version ver;
+	static struct iwmbt_version_tlv ver_tlv;
 	static struct iwmbt_boot_params params;
 	uint32_t boot_param;
 	int r;
@@ -327,7 +416,7 @@ main(int argc, char *argv[])
 	char *firmware_dir = NULL;
 	char *firmware_path = NULL;
 	int retcode = 1;
-	int iwmbt_use_old_method = 0;
+	enum iwmbt_device iwmbt_device;
 
 	/* Parse command line arguments */
 	while ((n = getopt(argc, argv, "Dd:f:hIm:p:v:")) != -1) {
@@ -372,7 +461,7 @@ main(int argc, char *argv[])
 	iwmbt_debug("opening dev %d.%d", (int) bus_id, (int) dev_id);
 
 	/* Find a device based on the bus/dev id */
-	dev = iwmbt_find_device(ctx, bus_id, dev_id, &iwmbt_use_old_method);
+	dev = iwmbt_find_device(ctx, bus_id, dev_id, &iwmbt_device);
 	if (dev == NULL) {
 		iwmbt_err("device not found");
 		goto shutdown;
@@ -401,16 +490,16 @@ main(int argc, char *argv[])
 		goto shutdown;
 	}
 
-	/* Get Intel version */
-	r = iwmbt_get_version(hdl, &ver);
-	if (r < 0) {
-		iwmbt_debug("iwmbt_get_version() failed code %d", r);
-		goto shutdown;
-	}
-	iwmbt_dump_version(&ver);
-	iwmbt_debug("fw_variant=0x%02x", (int) ver.fw_variant);
+	if (iwmbt_device == IWMBT_DEVICE_7260) {
 
-	if (iwmbt_use_old_method) {
+		/* Get Intel version */
+		r = iwmbt_get_version(hdl, &ver);
+		if (r < 0) {
+			iwmbt_debug("iwmbt_get_version() failed code %d", r);
+			goto shutdown;
+		}
+		iwmbt_dump_version(&ver);
+		iwmbt_debug("fw_patch_num=0x%02x", (int) ver.fw_patch_num);
 
 		/* fw_patch_num = >0 operational mode */
 		if (ver.fw_patch_num > 0x00) {
@@ -469,7 +558,16 @@ main(int argc, char *argv[])
 			iwmbt_info("Intel Event Mask is set");
 		(void)iwmbt_exit_manufacturer(hdl, 0x00);
 
-	} else {
+	} else if (iwmbt_device == IWMBT_DEVICE_8260) {
+
+		/* Get Intel version */
+		r = iwmbt_get_version(hdl, &ver);
+		if (r < 0) {
+			iwmbt_debug("iwmbt_get_version() failed code %d", r);
+			goto shutdown;
+		}
+		iwmbt_dump_version(&ver);
+		iwmbt_debug("fw_variant=0x%02x", (int) ver.fw_variant);
 
 		/* fw_variant = 0x06 bootloader mode / 0x23 operational mode */
 		if (ver.fw_variant == 0x23) {
@@ -509,7 +607,7 @@ main(int argc, char *argv[])
 		iwmbt_debug("firmware_path = %s", firmware_path);
 
 		/* Download firmware and parse it for magic Intel Reset parameter */
-		r = iwmbt_init_firmware(hdl, firmware_path, &boot_param);
+		r = iwmbt_init_firmware(hdl, firmware_path, &boot_param, 0, 0);
 		free(firmware_path);
 		if (r < 0)
 			goto shutdown;
@@ -546,6 +644,93 @@ main(int argc, char *argv[])
 		r = iwmbt_set_event_mask(hdl);
 		if (r == 0)
 			iwmbt_info("Intel Event Mask is set");
+
+	} else {
+
+		/* Get Intel version */
+		r = iwmbt_get_version_tlv(hdl, &ver_tlv);
+		if (r < 0) {
+			iwmbt_debug("iwmbt_get_version_tlv() failed code %d", r);
+			goto shutdown;
+		}
+		iwmbt_dump_version_tlv(&ver_tlv);
+		iwmbt_debug("img_type=0x%02x", (int) ver_tlv.img_type);
+
+		/* img_type = 0x01 bootloader mode / 0x03 operational mode */
+		if (ver_tlv.img_type == 0x03) {
+			iwmbt_info("Firmware has already been downloaded");
+			retcode = 0;
+			goto reset;
+		}
+
+		if (ver_tlv.img_type != 0x01){
+			iwmbt_err("unknown img_type 0x%02x", (int) ver_tlv.img_type);
+			goto shutdown;
+		}
+
+		/* Check if firmware fragments are ACKed with a cmd complete event */
+		if (ver_tlv.limited_cce != 0x00) {
+			iwmbt_err("Unsupported Intel firmware loading method (%u)",
+			   ver_tlv.limited_cce);
+			goto shutdown;
+		}
+
+		/* Check if secure boot engine is supported: 1 (ECDSA) or 0 (RSA) */
+		if (ver_tlv.sbe_type > 0x01) {
+			iwmbt_err("Unsupported secure boot engine (%u)",
+			   ver_tlv.sbe_type);
+			goto shutdown;
+		}
+
+		/* Default the firmware path */
+		if (firmware_dir == NULL)
+			firmware_dir = strdup(_DEFAULT_IWMBT_FIRMWARE_PATH);
+
+		firmware_path = iwmbt_get_fwname_tlv(&ver_tlv, firmware_dir, "sfi");
+		if (firmware_path == NULL)
+			goto shutdown;
+
+		iwmbt_debug("firmware_path = %s", firmware_path);
+
+		/* Download firmware and parse it for magic Intel Reset parameter */
+		r = iwmbt_init_firmware(hdl, firmware_path, &boot_param,
+		    ver_tlv.cnvi_bt >> 16 & 0x3f, ver_tlv.sbe_type);
+		free(firmware_path);
+		if (r < 0)
+			goto shutdown;
+
+		r = iwmbt_intel_reset(hdl, boot_param);
+		if (r < 0) {
+			iwmbt_debug("iwmbt_intel_reset() failed!");
+			goto shutdown;
+		}
+
+		iwmbt_info("Firmware operational");
+
+		/* Once device is running in operational mode we can ignore failures */
+		retcode = 0;
+
+		/* Execute Read Intel Version one more time */
+		r = iwmbt_get_version(hdl, &ver);
+		if (r == 0)
+			iwmbt_dump_version(&ver);
+
+		/* Apply the device configuration (DDC) parameters */
+		firmware_path = iwmbt_get_fwname_tlv(&ver_tlv, firmware_dir, "ddc");
+		iwmbt_debug("ddc_path = %s", firmware_path);
+		if (firmware_path != NULL) {
+			r = iwmbt_init_ddc(hdl, firmware_path);
+			if (r == 0)
+				iwmbt_info("DDC download complete");
+			free(firmware_path);
+		}
+
+		/* Set Intel Event mask */
+		r = iwmbt_set_event_mask(hdl);
+		if (r == 0)
+			iwmbt_info("Intel Event Mask is set");
+
+		iwmbt_info("Firmware download complete");
 	}
 
 reset: