diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h @@ -72,6 +72,12 @@ #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 */ diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c @@ -37,10 +37,13 @@ #include #include #include +#include #include #include +#include + #include "iwmbt_fw.h" #include "iwmbt_hw.h" #include "iwmbt_dbg.h" @@ -95,6 +98,7 @@ 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 @@ 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 @@ 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 @@ ret = iwmbt_hci_command(hdl, cmd, - buf, - sizeof(buf), + evt, + sizeof(evt), &transferred, IWMBT_HCI_CMD_TIMEOUT);