From a849fa91f0cf31a43672e052edff6798c8d72568 Mon Sep 17 00:00:00 2001 From: vasilito Date: Wed, 8 Jul 2026 14:30:26 +0300 Subject: [PATCH] iwlwifi: dual-format firmware TLV parser (Red Bear + Linux Intel) Support both firmware formats: - Red Bear simple format: magic 0x0A4F5749 (IWO\n) at offset 0, version/build at 4-11 - Linux Intel TLV format: zero at offset 0, magic 0x0a4c5749 (IWL\n) at offset 4, version string (64 bytes) at offset 8, TLVs from offset 88. TLV type 30 (ENABLED_CAPABILITIES) now correctly parses {api_index, bitmap} pairs per Linux 7.1 iwl-drv.c. Multiple type-30 TLVs are merged via bitmap << (api_index*16). Verified against real firmware: iwlwifi-bz-b0-fm-c0-c101.ucode (BE201 Wi-Fi 7) extracts api_index 0..4, 67 scan channels, version string + major version. --- .../redbear-iwlwifi/source/src/linux_mvm.c | 56 +++++++++++++++---- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/local/recipes/drivers/redbear-iwlwifi/source/src/linux_mvm.c b/local/recipes/drivers/redbear-iwlwifi/source/src/linux_mvm.c index e9a04e6073..365a551327 100644 --- a/local/recipes/drivers/redbear-iwlwifi/source/src/linux_mvm.c +++ b/local/recipes/drivers/redbear-iwlwifi/source/src/linux_mvm.c @@ -104,16 +104,43 @@ int rb_iwl_mvm_parse_firmware(const uint8_t *data, size_t len, magic = (uint32_t)data[0] | ((uint32_t)data[1] << 8) | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24); - if (magic != RB_IWL_TLV_UCODE_MAGIC) + + if (magic == RB_IWL_TLV_UCODE_MAGIC) { + /* Simple format: "IWO\n" at offset 0, version at 4, build at 8 */ + out->version = (uint32_t)data[4] | ((uint32_t)data[5] << 8) | + ((uint32_t)data[6] << 16) | ((uint32_t)data[7] << 24); + out->build = (uint32_t)data[8] | ((uint32_t)data[9] << 8) | + ((uint32_t)data[10] << 16) | ((uint32_t)data[11] << 24); + out->api = (uint8_t)((out->version >> 8) & 0xFFU); + p = data + RB_IWL_TLV_HDR_SIZE; + } else if (magic == 0 && len >= 8) { + /* Linux TLV format: zero at offset 0, "IWL\n" at offset 4 */ + uint32_t tlv_magic; + tlv_magic = (uint32_t)data[4] | ((uint32_t)data[5] << 8) | + ((uint32_t)data[6] << 16) | ((uint32_t)data[7] << 24); + if (tlv_magic != 0x0a4c5749U) /* "IWL\n" */ + return -1; + /* Extract version string from offset 8 (64 bytes) */ + if (len >= 72) { + size_t copy = RB_IWL_FW_VER_HUMAN_READABLE_SZ - 1; + if (len - 8 < copy) + copy = len - 8; + if (copy > 0) { + memcpy(out->version_str, data + 8, copy); + out->version_str[copy] = '\0'; + } + /* Major version at offset 72 */ + if (len >= 76) { + out->version = (uint32_t)data[72] | + ((uint32_t)data[73] << 8) | + ((uint32_t)data[74] << 16) | + ((uint32_t)data[75] << 24); + } + } + p = data + 88; /* skip TLV header */ + } else { return -1; - - out->version = (uint32_t)data[4] | ((uint32_t)data[5] << 8) | - ((uint32_t)data[6] << 16) | ((uint32_t)data[7] << 24); - out->build = (uint32_t)data[8] | ((uint32_t)data[9] << 8) | - ((uint32_t)data[10] << 16) | ((uint32_t)data[11] << 24); - out->api = (uint8_t)((out->version >> 8) & 0xFFU); - - p = data + RB_IWL_TLV_HDR_SIZE; + } end = data + len; while (p + 8 <= end) { @@ -138,11 +165,18 @@ int rb_iwl_mvm_parse_firmware(const uint8_t *data, size_t len, switch (tlv_type) { case RB_IWL_UCODE_TLV_ENABLED_CAPABILITIES: - if (tlv_len >= 4) - out->capabilities = (uint32_t)tlv_data[0] | + if (tlv_len >= 8) { + uint32_t api_index, bitmap; + api_index = (uint32_t)tlv_data[0] | ((uint32_t)tlv_data[1] << 8) | ((uint32_t)tlv_data[2] << 16) | ((uint32_t)tlv_data[3] << 24); + bitmap = (uint32_t)tlv_data[4] | + ((uint32_t)tlv_data[5] << 8) | + ((uint32_t)tlv_data[6] << 16) | + ((uint32_t)tlv_data[7] << 24); + out->capabilities |= (bitmap << (api_index * 16)); + } break; case RB_IWL_UCODE_TLV_N_SCAN_CHANNELS: