git: aa0b938434a8 - main - iwmbtfw(8): Ignore unexpected HCI events

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

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

commit aa0b938434a8af8eebf8f2634914f2d9fe8a5dc4
Author:     Vladimir Kondratyev <wulf@FreeBSD.org>
AuthorDate: 2024-11-06 23:27:48 +0000
Commit:     Vladimir Kondratyev <wulf@FreeBSD.org>
CommitDate: 2024-11-06 23:27:48 +0000

    iwmbtfw(8): Ignore unexpected HCI events
    
    If Intel firmware is already in operational mode at boot that takes
    place at warm boot, BT adaptor can generate extra HCI events which
    interferes with firmware mode detection logic. Ignore them.
    
    Sponsored by:   Future Crew LLC
    MFC after:      1 month
    Differential Revision:  https://reviews.freebsd.org/D46737
---
 usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c | 58 +++++++++++++++++++++++++++--------
 usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h |  6 ++++
 2 files changed, 52 insertions(+), 12 deletions(-)

diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
index 05a851f9d85b..1efd24ecf9f6 100644
--- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
+++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
@@ -37,10 +37,13 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 
 #include <libusb.h>
 
+#include <netgraph/bluetooth/include/ng_hci.h>
+
 #include "iwmbt_fw.h"
 #include "iwmbt_hw.h"
 #include "iwmbt_dbg.h"
@@ -95,6 +98,7 @@ static int
 iwmbt_hci_command(struct libusb_device_handle *hdl, struct iwmbt_hci_cmd *cmd,
     void *event, int size, int *transferred, int timeout)
 {
+	struct timespec to, now, remains;
 	int ret;
 
 	ret = libusb_control_transfer(hdl,
@@ -112,18 +116,47 @@ iwmbt_hci_command(struct libusb_device_handle *hdl, struct iwmbt_hci_cmd *cmd,
 		return (ret);
 	}
 
-	ret = libusb_interrupt_transfer(hdl,
-	    IWMBT_INTERRUPT_ENDPOINT_ADDR,
-	    event,
-	    size,
-	    transferred,
-	    timeout);
+	clock_gettime(CLOCK_MONOTONIC, &now);
+	to = IWMBT_MSEC2TS(timeout);
+	timespecadd(&to, &now, &to);
 
-	if (ret < 0)
-		iwmbt_err("libusb_interrupt_transfer() failed: err=%s",
-		    libusb_strerror(ret));
+	do {
+		timespecsub(&to, &now, &remains);
+		ret = libusb_interrupt_transfer(hdl,
+		    IWMBT_INTERRUPT_ENDPOINT_ADDR,
+		    event,
+		    size,
+		    transferred,
+		    IWMBT_TS2MSEC(remains) + 1);
 
-	return (ret);
+		if (ret < 0) {
+			iwmbt_err("libusb_interrupt_transfer() failed: err=%s",
+			    libusb_strerror(ret));
+			return (ret);
+		}
+
+		switch (((struct iwmbt_hci_event *)event)->header.event) {
+		case NG_HCI_EVENT_COMMAND_COMPL:
+			if (*transferred <
+			    (int)offsetof(struct iwmbt_hci_event_cmd_compl, data))
+				break;
+			if (cmd->opcode !=
+			    ((struct iwmbt_hci_event_cmd_compl *)event)->opcode)
+				break;
+			/* FALLTHROUGH */
+		case 0xFF:
+			return (0);
+		default:
+			break;
+		}
+		iwmbt_debug("Stray HCI event: %x",
+		    ((struct iwmbt_hci_event *)event)->header.event);
+	} while (timespeccmp(&to, &now, >));
+
+	iwmbt_err("libusb_interrupt_transfer() failed: err=%s",
+	    libusb_strerror(LIBUSB_ERROR_TIMEOUT));
+
+	return (LIBUSB_ERROR_TIMEOUT);
 }
 
 int
@@ -691,6 +724,7 @@ iwmbt_load_ddc(struct libusb_device_handle *hdl,
 	int size, sent = 0;
 	int ret, transferred;
 	uint8_t buf[IWMBT_HCI_MAX_CMD_SIZE];
+	uint8_t evt[IWMBT_HCI_MAX_CMD_SIZE];
 	struct iwmbt_hci_cmd *cmd = (struct iwmbt_hci_cmd *)buf;
 
 	size = ddc->len;
@@ -713,8 +747,8 @@ iwmbt_load_ddc(struct libusb_device_handle *hdl,
 
 		ret = iwmbt_hci_command(hdl,
 		    cmd,
-		    buf,
-		    sizeof(buf),
+		    evt,
+		    sizeof(evt),
 		    &transferred,
 		    IWMBT_HCI_CMD_TIMEOUT);
 
diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h
index 9467c3807a2a..89ee344fe587 100644
--- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h
+++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h
@@ -72,6 +72,12 @@ struct iwmbt_hci_event_cmd_compl {
 #define	IWMBT_HCI_MAX_CMD_SIZE		256
 #define	IWMBT_HCI_MAX_EVENT_SIZE	16
 
+#define	IWMBT_MSEC2TS(msec)				\
+	(struct timespec) {				\
+	    .tv_sec = (msec) / 1000,			\
+	    .tv_nsec = ((msec) % 1000) * 1000000	\
+	};
+#define	IWMBT_TS2MSEC(ts)	((ts).tv_sec * 1000 + (ts).tv_nsec / 1000000)
 #define	IWMBT_HCI_CMD_TIMEOUT		2000	/* ms */
 #define	IWMBT_LOADCMPL_TIMEOUT		5000	/* ms */