From nobody Wed Apr 22 20:08:12 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4g19JX4lbNz6bDs2 for ; Wed, 22 Apr 2026 20:08:12 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R13" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4g19JX3MLTz3Lwd for ; Wed, 22 Apr 2026 20:08:12 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1776888492; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=NFQUQ4trkCbgai+gV0a8hUjNRK66Ey83zWJ0D9piRTw=; b=p7iuVLXd5+fjtSdOhJfdDBagWDrnDjnVlIjm0Y1F2zGKCNRsZlCL3PazS2PnzZHxjxi21U 1zk23p89DTRBOMY8uIJXlew9qLT9pqZaGsUX/OKbDT7ytqwPy1V31kdKI9yiXAQhN5JQft MpKT7hjlXQFTpIwLt59MMboLxwdcrmPtXWO/PYWdVzRBvc5g6ze/hJm/0Qtqa4J4TzQ6fy d8nsS9uzU7dus1SDGYRfRGpU7M8Q+7HdPwtmA2LnUByOApS31ntHvZYabk/V1dvpJKadSI ic8lg2Lp0Z8H+HBb12JrYSc7FlZFjzCwNEvlYm3W7GTpA2BHayJnVrSSCbC/tw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1776888492; a=rsa-sha256; cv=none; b=EO20NAzltTEMeoHBiZ5ZBfZ4OQYnM3ktCGUdx7IebeV3iaq0/0ilmt/ctJZqW+i+jN5zsK bDNC58coItiQ1okm/KH8tCkJvMznZRKE5C3tY7NRwqLw2aaCMSOS2jywmTFWANILbEpJoH aEGHByPb5SWbPFIUA04LXNIlw+vj7+pQMCkb4OCF07QXx5fEItXVfqaGxIs2tTlGQUEqS5 odMlNvDZ3kTm/FBcpC5MVLJsXaQK9J/zoWrk1rM/N/6fDUHIayVZvJpWP3mnj3YptuxO8/ ylu7xAg4CgdDe2pKul8JtmQyz+ROooLrfBawxUScx7Y4VE41qg9qGW1KI7zRIw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1776888492; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=NFQUQ4trkCbgai+gV0a8hUjNRK66Ey83zWJ0D9piRTw=; b=D01DcpZjs2OBTmMPKFIP1ZGyleFUSN1W0JIREIgka9O8Vl0gwqLF2p1km0D/mX3MqnZqav 2xhyPggUq4nxD5/meFQb906EZTnuwCgYycOXwS/pdRi3XWOw5POHq9f30m31LP+4vhwSPN RprhXgVd9FbZZk/MjNa5+9GuoeIaPT+SvHMUE7QKpNMSTezvKQmX+Z8RT9Xday9ARwnwdq 8G04sOH8XEFS/0NQW64xxB+8W1NNS7KhpbGO2qGZiUg9of+LSbJqxAhTZMxkpdzzjU+FtR Hl1Q3i265uB10a4zeq3dfxF+4BsP6ibrFElS/03a/gzpWXTthvSbdQAtIoDQ1Q== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4g19JX2vpNzl12 for ; Wed, 22 Apr 2026 20:08:12 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 220e1 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Wed, 22 Apr 2026 20:08:12 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Vladimir Kondratyev Subject: git: 665ea58a3134 - stable/15 - iwmbtfw(8): Identify device type based on HCI quieries result List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: wulf X-Git-Repository: src X-Git-Refname: refs/heads/stable/15 X-Git-Reftype: branch X-Git-Commit: 665ea58a31342f6cbe4f597d63616728e850a545 Auto-Submitted: auto-generated Date: Wed, 22 Apr 2026 20:08:12 +0000 Message-Id: <69e92aac.220e1.10b3c376@gitrepo.freebsd.org> The branch stable/15 has been updated by wulf: URL: https://cgit.FreeBSD.org/src/commit/?id=665ea58a31342f6cbe4f597d63616728e850a545 commit 665ea58a31342f6cbe4f597d63616728e850a545 Author: Vladimir Kondratyev AuthorDate: 2026-04-12 18:09:22 +0000 Commit: Vladimir Kondratyev CommitDate: 2026-04-22 19:59:03 +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 (cherry picked from commit 84488787f42bc62b428da37793ac45d1411f2b74) --- 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 #include +#include #include #include #include @@ -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 #include -#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -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);