iwlwifi: firmware TLV parser — capabilities, version, scan channels
Added rb_iwl_mvm_parse_firmware() — walks Intel firmware TLV sections cross-referenced from Linux 7.1 fw/file.h (struct iwl_ucode_tlv). Extracts: - IWL_UCODE_TLV_ENABLED_CAPABILITIES (type 30) — capability bitmap - IWL_UCODE_TLV_N_SCAN_CHANNELS (type 31) — firmware channel limit - IWL_UCODE_TLV_FW_VERSION (type 36) — human-readable version string Wired into rb_iwlwifi_linux_prepare() — capabilities and version logged at info level during firmware load. TLV entries are 4-byte aligned per Intel firmware specification.
This commit is contained in:
@@ -83,3 +83,91 @@ int rb_iwl_mvm_rate_to_mcs(int signal)
|
||||
if (signal > -80) return 3;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* TLV type IDs — Linux 7.1 fw/file.h enum iwl_ucode_tlv_type */
|
||||
/* ------------------------------------------------------------------ */
|
||||
#define RB_IWL_UCODE_TLV_ENABLED_CAPABILITIES 30
|
||||
#define RB_IWL_UCODE_TLV_N_SCAN_CHANNELS 31
|
||||
#define RB_IWL_UCODE_TLV_FW_VERSION 36
|
||||
|
||||
int rb_iwl_mvm_parse_firmware(const uint8_t *data, size_t len,
|
||||
struct rb_iwl_fw_metadata *out)
|
||||
{
|
||||
const uint8_t *p, *end;
|
||||
uint32_t magic;
|
||||
|
||||
if (!data || !out || len < RB_IWL_TLV_HDR_SIZE)
|
||||
return -1;
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
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)
|
||||
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) {
|
||||
uint32_t tlv_type, tlv_len;
|
||||
const uint8_t *tlv_data;
|
||||
|
||||
tlv_type = (uint32_t)p[0] | ((uint32_t)p[1] << 8) |
|
||||
((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
|
||||
tlv_len = (uint32_t)p[4] | ((uint32_t)p[5] << 8) |
|
||||
((uint32_t)p[6] << 16) | ((uint32_t)p[7] << 24);
|
||||
p += 8;
|
||||
|
||||
if (p + tlv_len > end)
|
||||
break;
|
||||
|
||||
tlv_data = p;
|
||||
p += tlv_len;
|
||||
|
||||
/* TLV entries are 4-byte aligned per Intel firmware spec */
|
||||
if (tlv_len & 3)
|
||||
p += 4 - (tlv_len & 3);
|
||||
|
||||
switch (tlv_type) {
|
||||
case RB_IWL_UCODE_TLV_ENABLED_CAPABILITIES:
|
||||
if (tlv_len >= 4)
|
||||
out->capabilities = (uint32_t)tlv_data[0] |
|
||||
((uint32_t)tlv_data[1] << 8) |
|
||||
((uint32_t)tlv_data[2] << 16) |
|
||||
((uint32_t)tlv_data[3] << 24);
|
||||
break;
|
||||
|
||||
case RB_IWL_UCODE_TLV_N_SCAN_CHANNELS:
|
||||
if (tlv_len >= 4)
|
||||
out->n_scan_channels = (uint32_t)tlv_data[0] |
|
||||
((uint32_t)tlv_data[1] << 8) |
|
||||
((uint32_t)tlv_data[2] << 16) |
|
||||
((uint32_t)tlv_data[3] << 24);
|
||||
break;
|
||||
|
||||
case RB_IWL_UCODE_TLV_FW_VERSION:
|
||||
if (tlv_len > 0) {
|
||||
size_t copy = tlv_len;
|
||||
if (copy >= RB_IWL_FW_VER_HUMAN_READABLE_SZ)
|
||||
copy = RB_IWL_FW_VER_HUMAN_READABLE_SZ - 1;
|
||||
memcpy(out->version_str, tlv_data, copy);
|
||||
out->version_str[copy] = '\0';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out->parsed = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -165,4 +165,42 @@ void rb_iwl_mvm_extract_signal(const uint8_t *desc_data,
|
||||
*/
|
||||
int rb_iwl_mvm_rate_to_mcs(int signal);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Firmware TLV parser — Linux 7.1 fw/file.h. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
#define RB_IWL_TLV_UCODE_MAGIC 0x0A4F5749 /* "IWO\x0a" — Red Bear firmware */
|
||||
#define RB_IWL_TLV_HDR_SIZE 12 /* magic(4) + version(4) + build(4) */
|
||||
#define RB_IWL_FW_VER_HUMAN_READABLE_SZ 64
|
||||
|
||||
/* Linux 7.1 iwl_ucode_tlv — type/length/data format */
|
||||
struct rb_iwl_ucode_tlv {
|
||||
uint32_t type;
|
||||
uint32_t length;
|
||||
uint8_t data[];
|
||||
};
|
||||
|
||||
/* Extracted firmware metadata */
|
||||
struct rb_iwl_fw_metadata {
|
||||
uint32_t version;
|
||||
uint32_t build;
|
||||
uint8_t api;
|
||||
char version_str[RB_IWL_FW_VER_HUMAN_READABLE_SZ];
|
||||
uint32_t capabilities; /* from IWL_UCODE_TLV_ENABLED_CAPABILITIES */
|
||||
uint32_t n_scan_channels; /* from IWL_UCODE_TLV_N_SCAN_CHANNELS */
|
||||
int parsed; /* 1 if TLV parsing succeeded */
|
||||
};
|
||||
|
||||
/*
|
||||
* rb_iwl_mvm_parse_firmware — walk TLV sections in the firmware blob
|
||||
* and extract metadata (capabilities, version string, scan channels).
|
||||
*
|
||||
* Cross-referenced with Linux 7.1 iwl_req_fw_callback() in iwl-drv.c
|
||||
* and iwl_parse_tlv_firmware().
|
||||
*
|
||||
* Returns: 0 on success, negative on error.
|
||||
*/
|
||||
int rb_iwl_mvm_parse_firmware(const uint8_t *data, size_t len,
|
||||
struct rb_iwl_fw_metadata *out);
|
||||
|
||||
#endif /* RB_IWLWIFI_MVM_H */
|
||||
|
||||
@@ -31,10 +31,12 @@
|
||||
|
||||
// Known gaps vs Linux 7.1 iwlwifi (drivers/net/wireless/intel/iwlwifi/):
|
||||
// - Mini-MVM present (linux_mvm.c) but not a full port of iwl-mvm.c (~5200 lines).
|
||||
// Provides RX descriptor parsing, signal extraction, notification dispatch.
|
||||
// Provides RX descriptor parsing, signal extraction, notification dispatch,
|
||||
// and firmware TLV metadata (capabilities, version string).
|
||||
// Missing: Minstrel rate adaptation, thermal management, WoWLAN, debug hooks.
|
||||
// - No firmware TLV/NVM parser — only magic number check.
|
||||
// See Linux 7.1 iwl-nvm-parse.c.
|
||||
// - Firmware TLV parser present — extracts ENABLED_CAPABILITIES, N_SCAN_CHANNELS,
|
||||
// and FW_VERSION from the Intel firmware blob.
|
||||
// Missing: full NVM section parsing (MAC address, calibration, regulatory).
|
||||
// - No power management (PS mode, WoWLAN, thermal throttling).
|
||||
// - PCI device ID table: 37 entries. Linux 7.1 has ~500+ across all families.
|
||||
// - Scan uses active+dwell schedule but no UMAC scan engine integration.
|
||||
@@ -272,6 +274,7 @@ struct iwl_trans_pcie {
|
||||
struct ieee80211_bss_conf bss_conf;
|
||||
struct rb_iwl_fw_blob_info fw_info;
|
||||
struct rb_iwl_fw_blob_info pnvm_info;
|
||||
struct rb_iwl_fw_metadata fw_meta;
|
||||
struct rb_iwl_key keys[4];
|
||||
char fw_name_storage[RB_IWL_MAX_FW_NAME];
|
||||
char pnvm_name_storage[RB_IWL_MAX_FW_NAME];
|
||||
@@ -974,8 +977,17 @@ static int rb_iwlwifi_do_prepare(struct iwl_trans_pcie *trans, const char *ucode
|
||||
return rc;
|
||||
|
||||
rc = rb_iwlwifi_parse_fw_blob(fw, &trans->fw_info);
|
||||
if (!rc)
|
||||
if (!rc) {
|
||||
rb_iwlwifi_update_fw_info(&trans->fw_info, fw);
|
||||
if (rb_iwl_mvm_parse_firmware(fw->data, fw->size, &trans->fw_meta) == 0) {
|
||||
pr_info("prepare: firmware %s v%d.%d (build %u), caps=0x%x\n",
|
||||
trans->fw_meta.version_str[0] ? trans->fw_meta.version_str : "(unknown)",
|
||||
(trans->fw_meta.version >> 16) & 0xFF,
|
||||
(trans->fw_meta.version >> 8) & 0xFF,
|
||||
trans->fw_meta.build,
|
||||
trans->fw_meta.capabilities);
|
||||
}
|
||||
}
|
||||
release_firmware(fw);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
Reference in New Issue
Block a user