Date: Sun, 12 Apr 2026 18:10:10 +0000 From: Vladimir Kondratyev <wulf@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: 84488787f42b - main - iwmbtfw(8): Identify device type based on HCI quieries result Message-ID: <69dbe002.38304.4d803701@gitrepo.freebsd.org>
index | next in thread | raw e-mail
The branch main has been updated by wulf: URL: https://cgit.FreeBSD.org/src/commit/?id=84488787f42bc62b428da37793ac45d1411f2b74 commit 84488787f42bc62b428da37793ac45d1411f2b74 Author: Vladimir Kondratyev <wulf@FreeBSD.org> AuthorDate: 2026-04-12 18:09:22 +0000 Commit: Vladimir Kondratyev <wulf@FreeBSD.org> CommitDate: 2026-04-12 18:09:22 +0000 iwmbtfw(8): Identify device type based on HCI quieries result rather than on VID/PID. Later is not reliable for some types. VID/PID identification can be restored by specifying of -p option. Tested by: arrowd, wulf PR: 290639 MFC after: 1 week --- usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c | 105 +++++++++++++++++++++++++ usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h | 2 + usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c | 140 ++++++++++------------------------ usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h | 3 + usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8 | 7 +- usr.sbin/bluetooth/iwmbtfw/main.c | 80 +++++++++++++++++-- 6 files changed, 232 insertions(+), 105 deletions(-) diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c index 3a5cd9d42658..f71aacd6f2e9 100644 --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c @@ -31,6 +31,7 @@ #include <sys/endian.h> #include <sys/stat.h> +#include <assert.h> #include <err.h> #include <errno.h> #include <fcntl.h> @@ -192,3 +193,107 @@ iwmbt_get_fwname_tlv(struct iwmbt_version_tlv *ver, const char *prefix, return (fwname); } + +int +iwmbt_parse_tlv(uint8_t *data, uint8_t datalen, + struct iwmbt_version_tlv *version) +{ + uint8_t status, type, len; + + 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); +} diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h index eb6909a1f91d..1763f8688ed0 100644 --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h @@ -152,5 +152,7 @@ extern char *iwmbt_get_fwname(struct iwmbt_version *ver, const char *suffix); extern char *iwmbt_get_fwname_tlv(struct iwmbt_version_tlv *ver, const char *prefix, const char *suffix); +extern int iwmbt_parse_tlv(uint8_t *data, uint8_t datalen, + struct iwmbt_version_tlv *ver); #endif diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c index 255181b8f4bc..81f1fbe7c5ce 100644 --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c @@ -30,7 +30,6 @@ #include <sys/endian.h> #include <sys/stat.h> -#include <assert.h> #include <err.h> #include <errno.h> #include <stddef.h> @@ -408,6 +407,29 @@ iwmbt_load_fwfile(struct libusb_device_handle *hdl, return (0); } +int +iwmbt_bt_reset(struct libusb_device_handle *hdl) +{ + int ret, transferred; + static struct iwmbt_hci_cmd cmd = { + .opcode = htole16(0x0c03), + .length = 0, + }; + uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE]; + + ret = iwmbt_hci_command(hdl, + &cmd, + buf, + sizeof(buf), + &transferred, + IWMBT_HCI_CMD_TIMEOUT); + + if (ret < 0) + iwmbt_debug("HCI reset command failed: code=%d", ret); + + return (ret); +} + int iwmbt_enter_manufacturer(struct libusb_device_handle *hdl) { @@ -502,8 +524,8 @@ iwmbt_get_version(struct libusb_device_handle *hdl, } int -iwmbt_get_version_tlv(struct libusb_device_handle *hdl, - struct iwmbt_version_tlv *version) +iwmbt_read_version_tlv(struct libusb_device_handle *hdl, + uint8_t *data, uint8_t *datalen) { int ret, transferred; struct iwmbt_hci_event_cmd_compl *event; @@ -512,8 +534,6 @@ iwmbt_get_version_tlv(struct libusb_device_handle *hdl, .length = 1, .data = { 0xff }, }; - uint8_t status, datalen, type, len; - uint8_t *data; uint8_t buf[255]; memset(buf, 0, sizeof(buf)); @@ -533,106 +553,30 @@ iwmbt_get_version_tlv(struct libusb_device_handle *hdl, } 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; + memcpy(data, event->data, *datalen); - datalen = event->header.length - IWMBT_HCI_EVENT_COMPL_HEAD_SIZE; - data = event->data; - status = *data++; - if (status != 0) - return (-1); - datalen--; + return (0); +} - while (datalen >= 2) { - type = *data++; - len = *data++; - datalen -= 2; +int +iwmbt_get_version_tlv(struct libusb_device_handle *hdl, + struct iwmbt_version_tlv *version) +{ - if (datalen < len) - return (-1); + uint8_t data[255]; + uint8_t datalen; + int ret; - 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; - } + memset(data, 0, sizeof(data)); - datalen -= len; - data += len; + ret = iwmbt_read_version_tlv(hdl, data, &datalen); + if (ret < 0) { + iwmbt_debug("Can't get version tlv"); + return (-1); } - return (0); + return (iwmbt_parse_tlv(data, datalen, version)); } int diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h index aac885dfd153..f1f1f56e0ca2 100644 --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h @@ -101,11 +101,14 @@ 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, int offset); +extern int iwmbt_bt_reset(struct libusb_device_handle *hdl); extern int iwmbt_enter_manufacturer(struct libusb_device_handle *hdl); extern int iwmbt_exit_manufacturer(struct libusb_device_handle *hdl, enum iwmbt_mm_exit mode); extern int iwmbt_get_version(struct libusb_device_handle *hdl, struct iwmbt_version *version); +extern int iwmbt_read_version_tlv(struct libusb_device_handle *hdl, + uint8_t *data, uint8_t *datalen); 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, diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8 b/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8 index fd0118655a67..342656613421 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 July 15, 2025 +.Dd April 2, 2026 .Dt IWMBTFW 8 .Os .Sh NAME @@ -50,7 +50,8 @@ This utility will .Em only work with Intel Wireless 7260/8260/9260 and newer chip based Bluetooth USB devices, including AX and BE series wireless adapters. -The identification is currently based on USB vendor ID/product ID pair. +The identification is currently based on USB vendor ID/product ID pair +and result of HCI queries. The vendor ID should be 0x8087 .Pq Dv USB_VENDOR_INTEL2 and the product ID should be one of the supported devices. @@ -78,6 +79,8 @@ device name. Specify the directory containing the firmware files to search and upload. .It Fl h Display usage message and exit. +.It Fl p +Use only USB vendor ID/product ID pair for device model identification. .El .Sh EXIT STATUS .Ex -std diff --git a/usr.sbin/bluetooth/iwmbtfw/main.c b/usr.sbin/bluetooth/iwmbtfw/main.c index 1e11cc468015..560735fabd67 100644 --- a/usr.sbin/bluetooth/iwmbtfw/main.c +++ b/usr.sbin/bluetooth/iwmbtfw/main.c @@ -35,6 +35,7 @@ #include <errno.h> #include <fcntl.h> #include <libgen.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -50,6 +51,7 @@ int iwmbt_do_debug = 0; int iwmbt_do_info = 0; +static bool iwmbt_do_pidvid = false; enum iwmbt_device { IWMBT_DEVICE_UNKNOWN, @@ -228,6 +230,65 @@ iwmbt_dump_version_tlv(struct iwmbt_version_tlv *ver) ver->build_num); } +static enum iwmbt_device +iwmbt_identify(libusb_device_handle *hdl, enum iwmbt_device device) +{ + uint8_t data[255]; + uint8_t datalen, hw_platform, hw_variant; + struct iwmbt_version *ver = (struct iwmbt_version *)data; + struct iwmbt_version_tlv ver_tlv; + int r; + + if (device == IWMBT_DEVICE_7260) { + r = iwmbt_bt_reset(hdl); + if (r < 0) { + iwmbt_debug("iwmbt_bt_reset() failed!"); + return (IWMBT_DEVICE_UNKNOWN); + } + } + + memset(data, 0, sizeof(data)); + r = iwmbt_read_version_tlv(hdl, data, &datalen); + if (r < 0) { + iwmbt_debug("iwmbt_read_version_tlv() failed"); + return (IWMBT_DEVICE_UNKNOWN); + } + + if (datalen == sizeof(*ver) && ver->hw_platform == 0x37) { + switch (ver->hw_variant) { + case 0x07: + case 0x08: + return (IWMBT_DEVICE_7260); + case 0x0b: + case 0x0c: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + return (IWMBT_DEVICE_8260); + default: + iwmbt_debug("Unsupported hw_variant (0x%2x)", + ver->hw_variant); + return (IWMBT_DEVICE_UNKNOWN); + } + } + + r = iwmbt_parse_tlv(data, datalen, &ver_tlv); + if (r < 0) { + iwmbt_debug("iwmbt_parse_tlv() failed"); + return (IWMBT_DEVICE_UNKNOWN); + } + + hw_platform = (ver_tlv.cnvi_bt >> 8) & 0xff; + hw_variant = (ver_tlv.cnvi_bt >> 16) & 0x3f; + + if (hw_platform != 0x37) { + iwmbt_debug("Unsupported hw_platform (0x%2x)", hw_platform); + return (IWMBT_DEVICE_UNKNOWN); + } + + return (hw_variant < 0x17 ? IWMBT_DEVICE_8260 : IWMBT_DEVICE_9260); +} static int iwmbt_init_firmware(libusb_device_handle *hdl, const char *firmware_path, @@ -377,11 +438,10 @@ usage(void) fprintf(stderr, " -f: firmware path (defaults to %s)\n", _DEFAULT_IWMBT_FIRMWARE_PATH); fprintf(stderr, " -I: enable informational output\n"); + fprintf(stderr, " -p: use PID/VID for model identification\n"); exit(127); } - - /* * Returns 0 on success. */ @@ -558,7 +618,6 @@ handle_9260(libusb_device_handle *hdl, char *firmware_dir) { int r; uint32_t boot_param; - struct iwmbt_version vl; struct iwmbt_version_tlv vt; char *firmware_path = NULL; @@ -618,9 +677,9 @@ handle_9260(libusb_device_handle *hdl, char *firmware_dir) /* Once device is running in operational mode we can ignore failures */ - r = iwmbt_get_version(hdl, &vl); + r = iwmbt_get_version_tlv(hdl, &vt); if (r == 0) - iwmbt_dump_version(&vl); + iwmbt_dump_version_tlv(&vt); /* Apply the device configuration (DDC) parameters */ firmware_path = iwmbt_get_fwname_tlv(&vt, firmware_dir, "ddc"); @@ -673,6 +732,9 @@ main(int argc, char *argv[]) case 'I': iwmbt_do_info = 1; break; + case 'p': + iwmbt_do_pidvid = true; + break; case 'h': default: usage(); @@ -730,6 +792,14 @@ main(int argc, char *argv[]) goto shutdown; } + if (!iwmbt_do_pidvid) { + iwmbt_device = iwmbt_identify(hdl, iwmbt_device); + if (iwmbt_device == IWMBT_DEVICE_UNKNOWN) { + iwmbt_err("Failed to identify device"); + goto shutdown; + } + } + switch(iwmbt_device) { case IWMBT_DEVICE_7260: retcode = handle_7260(hdl, firmware_dir);home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69dbe002.38304.4d803701>
