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.
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user