git: d95de59e943e - stable/12 - iwmbtfw(8): Improve Intel 7260/7265 adaptors handling

From: Vladimir Kondratyev <wulf_at_FreeBSD.org>
Date: Thu, 24 Nov 2022 23:17:42 UTC
The branch stable/12 has been updated by wulf:

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

commit d95de59e943e2c07e35880d04467e822c234c3db
Author:     Vladimir Kondratyev <wulf@FreeBSD.org>
AuthorDate: 2021-05-31 19:32:08 +0000
Commit:     Vladimir Kondratyev <wulf@FreeBSD.org>
CommitDate: 2022-11-24 23:16:08 +0000

    iwmbtfw(8): Improve Intel 7260/7265 adaptors handling
    
    - Allow firmware downloading for hw_variant #8;
    - Enter manufacturer mode for setting of event mask;
    - Handle multi-event response on HCI commands for 7260;
      This allows to remove kludge with skipping of 0xfc2f opcode.
    - Disable patch and exit manufacturer mode on downloading failure;
    - Use default firmware if correct firmware file is not found;
    
    Reviewed by:    Philippe Michaud-Boudreault <pitwuu_AT_gmail_DOT_com>
    MFC after:      1 week
    Tested by:      arrowd
    Differential revision:  https://reviews.freebsd.org/D30543
    
    (cherry picked from commit da93a73f834612b659b37b513c8296e1178d249b)
---
 usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c |  14 ++++
 usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c | 116 ++++++++++++++++++++--------------
 usr.sbin/bluetooth/iwmbtfw/main.c     |   9 ++-
 3 files changed, 91 insertions(+), 48 deletions(-)

diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c
index fc93ce094adc..963d5d5d9008 100644
--- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c
+++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c
@@ -116,10 +116,12 @@ char *
 iwmbt_get_fwname(struct iwmbt_version *ver, struct iwmbt_boot_params *params,
     const char *prefix, const char *suffix)
 {
+	struct stat sb;
 	char *fwname;
 
 	switch (ver->hw_variant) {
 	case 0x07:	/* 7260 */
+	case 0x08:	/* 7265 */
 		asprintf(&fwname, "%s/ibt-hw-%x.%x.%x-fw-%x.%x.%x.%x.%x.%s",
 		    prefix,
 		    le16toh(ver->hw_platform),
@@ -131,6 +133,18 @@ iwmbt_get_fwname(struct iwmbt_version *ver, struct iwmbt_boot_params *params,
 		    le16toh(ver->fw_build_ww),
 		    le16toh(ver->fw_build_yy),
 		    suffix);
+		/*
+		 * Fallback to the default firmware patch if
+		 * the correct firmware patch file is not found.
+		 */
+		if (stat(fwname, &sb) != 0 && errno == ENOENT) {
+			free(fwname);
+			asprintf(&fwname, "%s/ibt-hw-%x.%x.%s",
+			    prefix,
+			    le16toh(ver->hw_platform),
+			    le16toh(ver->hw_variant),
+			    suffix);
+		}
 		break;
 
 	case 0x0b:	/* 8260 */
diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
index f4272548d560..218fd28b74a2 100644
--- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
+++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
@@ -134,17 +134,18 @@ iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
 	struct iwmbt_firmware fw_job = *fw;
 	uint16_t cmd_opcode;
 	uint8_t cmd_length;
-	uint8_t cmd_buf[IWMBT_HCI_MAX_CMD_SIZE];
+	struct iwmbt_hci_cmd *cmd_buf;
 	uint8_t evt_code;
 	uint8_t evt_length;
 	uint8_t evt_buf[IWMBT_HCI_MAX_EVENT_SIZE];
-	int skip_patch = 0;
+	int activate_patch = 0;
 
-	for (;;) {
-		skip_patch = 0;
-
-		if (fw_job.len < 4)
-			break;
+	while (fw_job.len > 0) {
+		if (fw_job.len < 4) {
+			iwmbt_err("Invalid firmware, unexpected EOF in HCI "
+			    "command header. Remains=%d", fw_job.len);
+			return (-1);
+		}
 
 		if (fw_job.buf[0] != 0x01) {
 			iwmbt_err("Invalid firmware, expected HCI command (%d)",
@@ -159,47 +160,61 @@ iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
 		/* Load in the HCI command to perform. */
 		cmd_opcode = le16dec(fw_job.buf);
 		cmd_length = fw_job.buf[2];
-		memcpy(cmd_buf, fw_job.buf, 3);
+		cmd_buf = (struct iwmbt_hci_cmd *)fw_job.buf;
 
 		iwmbt_debug("opcode=%04x, len=%02x", cmd_opcode, cmd_length);
 
-		/* For some reason the command 0xfc2f hangs up my card. */
-		if (cmd_opcode == 0xfc2f)
-			skip_patch = 1;
+		/*
+		 * If there is a command that loads a patch in the
+		 * firmware file, then activate the patch upon success,
+		 * otherwise just disable the manufacturer mode.
+		 */
+		if (cmd_opcode == 0xfc8e)
+			activate_patch = 1;
 
 		/* Advance by three. */
 		fw_job.buf += 3;
 		fw_job.len -= 3;
 
-		if (fw_job.len < cmd_length)
-			cmd_length = fw_job.len;
-
-		/* Copy data to HCI command buffer. */
-		memcpy(cmd_buf + 3, fw_job.buf,
-		    MIN(cmd_length, IWMBT_HCI_MAX_CMD_SIZE - 3));
+		if (fw_job.len < cmd_length) {
+			iwmbt_err("Invalid firmware, unexpected EOF in HCI "
+			    "command data. len=%d, remains=%d",
+			    cmd_length, fw_job.len);
+			return (-1);
+		}
 
 		/* Advance by data length. */
 		fw_job.buf += cmd_length;
 		fw_job.len -= cmd_length;
 
+		ret = libusb_control_transfer(hdl,
+		    LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE,
+		    0,
+		    0,
+		    0,
+		    (uint8_t *)cmd_buf,
+		    IWMBT_HCI_CMD_SIZE(cmd_buf),
+		    IWMBT_HCI_CMD_TIMEOUT);
+
+		if (ret < 0) {
+			iwmbt_err("libusb_control_transfer() failed: err=%s",
+			    libusb_strerror(ret));
+			return (-1);
+		}
+
 		/*
 		 * Every command has its associated event: data must match
 		 * what is recorded in the firmware file. Perform that check
 		 * now.
-		 *
-		 * Some commands are mapped to more than one event sequence,
-		 * in that case we can drop the non-patch commands, as we
-		 * probably don't need them for operation of the card.
-		 *
 		 */
 
-		for (;;) {
+		while (fw_job.len > 0 && fw_job.buf[0] == 0x02) {
 			/* Is this the end of the file? */
-			if (fw_job.len < 3)
-				break;
-
-			if (fw_job.buf[0] != 0x02)
-				break;
+			if (fw_job.len < 3) {
+				iwmbt_err("Invalid firmware, unexpected EOF in"
+				    "event header. remains=%d", fw_job.len);
+				return (-1);
+			}
 
 			/* Advance by one. */
 			fw_job.buf++;
@@ -219,30 +234,39 @@ iwmbt_patch_fwfile(struct libusb_device_handle *hdl,
 			iwmbt_debug("event=%04x, len=%02x",
 					evt_code, evt_length);
 
+			if (fw_job.len < evt_length) {
+				iwmbt_err("Invalid firmware, unexpected EOF in"
+				    " event data. len=%d, remains=%d",
+				    evt_length, fw_job.len);
+				return (-1);
+			}
+
+			ret = libusb_interrupt_transfer(hdl,
+			    IWMBT_INTERRUPT_ENDPOINT_ADDR,
+			    evt_buf,
+			    IWMBT_HCI_MAX_EVENT_SIZE,
+			    &transferred,
+			    IWMBT_HCI_CMD_TIMEOUT);
+
+			if (ret < 0) {
+				iwmbt_err("libusb_interrupt_transfer() failed:"
+				    " err=%s", libusb_strerror(ret));
+				return (-1);
+			}
+
+			if ((int)evt_length + 2 != transferred ||
+			    memcmp(evt_buf + 2, fw_job.buf, evt_length) != 0) {
+				iwmbt_err("event does not match firmware");
+				return (-1);
+			}
+
 			/* Advance by data length. */
 			fw_job.buf += evt_length;
 			fw_job.len -= evt_length;
-
-			if (skip_patch == 0) {
-				ret = iwmbt_hci_command(hdl,
-				    (struct iwmbt_hci_cmd *)cmd_buf,
-				    evt_buf,
-				    IWMBT_HCI_MAX_EVENT_SIZE,
-				    &transferred,
-				    IWMBT_HCI_CMD_TIMEOUT);
-
-				if (ret < 0) {
-					iwmbt_debug("Can't send patch: "
-					    "code=%d, size=%d",
-					    ret,
-					    transferred);
-					 return (-1);
-				}
-			}
 		}
 	}
 
-	return (0);
+	return (activate_patch);
 }
 
 int
diff --git a/usr.sbin/bluetooth/iwmbtfw/main.c b/usr.sbin/bluetooth/iwmbtfw/main.c
index 3476e3fcd613..202894740805 100644
--- a/usr.sbin/bluetooth/iwmbtfw/main.c
+++ b/usr.sbin/bluetooth/iwmbtfw/main.c
@@ -441,13 +441,15 @@ main(int argc, char *argv[])
 		/* Download firmware and parse it for magic Intel Reset parameter */
 		r = iwmbt_patch_firmware(hdl, firmware_path);
 		free(firmware_path);
-		if (r < 0)
+		if (r < 0) {
+			(void)iwmbt_exit_manufacturer(hdl, 0x01);
 			goto shutdown;
+		}
 
 		iwmbt_info("Firmware download complete");
 
 		/* Exit manufacturer mode */
-		r = iwmbt_exit_manufacturer(hdl, 0x02);
+		r = iwmbt_exit_manufacturer(hdl, r == 0 ? 0x00 : 0x02);
 		if (r < 0) {
 			iwmbt_debug("iwmbt_exit_manufacturer() failed code %d", r);
 			goto shutdown;
@@ -462,9 +464,12 @@ main(int argc, char *argv[])
 			iwmbt_dump_version(&ver);
 
 		/* Set Intel Event mask */
+		if (iwmbt_enter_manufacturer(hdl) < 0)
+			goto reset;
 		r = iwmbt_set_event_mask(hdl);
 		if (r == 0)
 			iwmbt_info("Intel Event Mask is set");
+		(void)iwmbt_exit_manufacturer(hdl, 0x00);
 
 	} else {