aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladimir Kondratyev <wulf@FreeBSD.org>2024-11-06 23:27:48 +0000
committerVladimir Kondratyev <wulf@FreeBSD.org>2024-11-06 23:27:48 +0000
commitaa0b938434a8af8eebf8f2634914f2d9fe8a5dc4 (patch)
treec54681d419b1628ccc3897f9c39785b4a08fdb6d
parent0b23c50ae834d11842810304e4ddad2754298ada (diff)
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
-rw-r--r--usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c58
-rw-r--r--usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h6
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 */