Message ID | 20130925210848.2596.56332.stgit@localhost6.localdomain6 (mailing list archive) |
---|---|
State | Not Applicable, archived |
Headers | show |
On 25 September 2013 23:08, Kalle Valo <kvalo@qca.qualcomm.com> wrote: > +static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) > +{ > + size_t magic_len, len, ie_len; > + struct ath10k_fw_ie *hdr; > + const u8 *data; > + int ie_id, i, index, bit; > + __le32 *timestamp; > + > + ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, name); > + if (IS_ERR(ar->firmware)) { > + ath10k_err("Could not fetch firmware file '%s': %ld\n", > + name, PTR_ERR(ar->firmware)); > + return PTR_ERR(ar->firmware); > + } > + > + data = ar->firmware->data; > + len = ar->firmware->size; > + > + /* magic also includes the null byte, check that as well */ > + magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1; > + > + if (len < magic_len) { > + ath10k_err("firmware image too small to contain magic: %d\n", > + len); > + return -EINVAL; > + } > + > + if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) { > + ath10k_err("Invalid firmware magic\n"); > + return -EINVAL; > + } > + > + /* jump over the padding */ > + magic_len = ALIGN(magic_len, 4); > + > + len -= magic_len; > + data += magic_len; > + > + /* loop elements */ > + while (len > sizeof(struct ath10k_fw_ie)) { > + hdr = (struct ath10k_fw_ie *)data; > + > + ie_id = le32_to_cpu(hdr->id); > + ie_len = le32_to_cpu(hdr->len); > + > + len -= sizeof(*hdr); > + data += sizeof(*hdr); > + > + if (len < ie_len) { > + ath10k_err("Invalid length for FW IE %d (%d < %d)\n", > + ie_id, len, ie_len); > + return -EINVAL; > + } > + > + switch (ie_id) { > + case ATH10K_FW_IE_FW_VERSION: > + if (ie_len > sizeof(ar->hw->wiphy->fw_version) - 1) > + break; > + > + memcpy(ar->hw->wiphy->fw_version, data, ie_len); > + ar->hw->wiphy->fw_version[ie_len] = '\0'; > + > + ath10k_dbg(ATH10K_DBG_BOOT, > + "found fw version %s\n", > + ar->hw->wiphy->fw_version); > + break; > + case ATH10K_FW_IE_TIMESTAMP: > + if (ie_len != sizeof(u32)) > + break; > + > + timestamp = (__le32 *)data; > + > + ath10k_dbg(ATH10K_DBG_BOOT, "found fw timestamp %d\n", > + le32_to_cpup(timestamp)); > + break; > + case ATH10K_FW_IE_FEATURES: > + ath10k_dbg(ATH10K_DBG_BOOT, > + "found firmware features ie (%zd B)\n", > + ie_len); > + > + for (i = 0; i < ATH10K_FW_FEATURE_COUNT; i++) { > + index = i / 8; > + bit = i % 8; > + > + if (index == ie_len) > + break; > + > + if (data[index] & (1 << bit)) > + __set_bit(i, ar->fw_features); > + } > + > + ath10k_dbg_dump(ATH10K_DBG_BOOT, "features", "", > + ar->fw_features, > + sizeof(ar->fw_features)); > + break; > + case ATH10K_FW_IE_FW_IMAGE: > + ath10k_dbg(ATH10K_DBG_BOOT, > + "found fw image ie (%zd B)\n", > + ie_len); > + > + ar->firmware_data = data; > + ar->firmware_len = ie_len; > + > + break; > + case ATH10K_FW_IE_OTP_IMAGE: > + ath10k_dbg(ATH10K_DBG_BOOT, > + "found otp image ie (%zd B)\n", > + ie_len); > + > + ar->otp_data = data; > + ar->otp_len = ie_len; > + > + break; > + default: > + ath10k_warn("Unknown FW IE: %u\n", > + le32_to_cpu(hdr->id)); > + break; > + } > + > + /* jump over the padding */ > + ie_len = ALIGN(ie_len, 4); > + > + len -= ie_len; > + data += ie_len; > + }; > + > + return 0; > +} > + > +static int ath10k_core_fetch_firmware_files(struct ath10k *ar) > +{ > + int ret; > + > + ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API2_FILE); > + if (ret == 0) { > + ar->fw_api = 2; > + goto out; > + } Did you skip board file intentionally? Micha?. -- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Michal Kazior <michal.kazior@tieto.com> writes:
> Did you skip board file intentionally?
Yes, because my understanding is that the board file is not tied to any
particular firmware version. The board file should be specific to the
hardware configuration in use, or at least that's how it is with ath6kl.
On 26 September 2013 07:55, Kalle Valo <kvalo@qca.qualcomm.com> wrote: > Michal Kazior <michal.kazior@tieto.com> writes: > >> Did you skip board file intentionally? > > Yes, because my understanding is that the board file is not tied to any > particular firmware version. The board file should be specific to the > hardware configuration in use, or at least that's how it is with ath6kl. But the board file is not loaded at all for API 2. The api_1 variant requests 3 files and can result with up to 3 binaries loaded. api_n requests 1 file and can result with up to 2 binaries loaded. Micha?. -- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Michal Kazior <michal.kazior@tieto.com> writes: > On 26 September 2013 07:55, Kalle Valo <kvalo@qca.qualcomm.com> wrote: >> Michal Kazior <michal.kazior@tieto.com> writes: >> >>> Did you skip board file intentionally? >> >> Yes, because my understanding is that the board file is not tied to any >> particular firmware version. The board file should be specific to the >> hardware configuration in use, or at least that's how it is with ath6kl. > > But the board file is not loaded at all for API 2. The api_1 variant > requests 3 files and can result with up to 3 binaries loaded. api_n > requests 1 file and can result with up to 2 binaries loaded. Oops, that's clearly a bug. How did it even pass my tests without the board file? I'll send v2 to fix that. So my idea is that with API 2 we load only firmware-2.bin and board.bin from user space. With API 1 we continue loading firmware.bin, otp.bin (optional) and board.bin, just as before. And firmware-2.bin can contain the old firmware.bin (mandatory) and otp.bin (optional) images. I just realised that I'm missing a check that there really is a firmware image inside firmware-2.bin, need to add that as well.
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 6a354f7..a1a3dcc 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -339,7 +339,7 @@ static void ath10k_core_free_firmware_files(struct ath10k *ar) ar->firmware_len = 0; } -static int ath10k_core_fetch_firmware_files(struct ath10k *ar) +static int ath10k_core_fetch_firmware_api_1(struct ath10k *ar) { int ret = 0; @@ -400,6 +400,157 @@ err: return ret; } +static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) +{ + size_t magic_len, len, ie_len; + struct ath10k_fw_ie *hdr; + const u8 *data; + int ie_id, i, index, bit; + __le32 *timestamp; + + ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, name); + if (IS_ERR(ar->firmware)) { + ath10k_err("Could not fetch firmware file '%s': %ld\n", + name, PTR_ERR(ar->firmware)); + return PTR_ERR(ar->firmware); + } + + data = ar->firmware->data; + len = ar->firmware->size; + + /* magic also includes the null byte, check that as well */ + magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1; + + if (len < magic_len) { + ath10k_err("firmware image too small to contain magic: %d\n", + len); + return -EINVAL; + } + + if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) { + ath10k_err("Invalid firmware magic\n"); + return -EINVAL; + } + + /* jump over the padding */ + magic_len = ALIGN(magic_len, 4); + + len -= magic_len; + data += magic_len; + + /* loop elements */ + while (len > sizeof(struct ath10k_fw_ie)) { + hdr = (struct ath10k_fw_ie *)data; + + ie_id = le32_to_cpu(hdr->id); + ie_len = le32_to_cpu(hdr->len); + + len -= sizeof(*hdr); + data += sizeof(*hdr); + + if (len < ie_len) { + ath10k_err("Invalid length for FW IE %d (%d < %d)\n", + ie_id, len, ie_len); + return -EINVAL; + } + + switch (ie_id) { + case ATH10K_FW_IE_FW_VERSION: + if (ie_len > sizeof(ar->hw->wiphy->fw_version) - 1) + break; + + memcpy(ar->hw->wiphy->fw_version, data, ie_len); + ar->hw->wiphy->fw_version[ie_len] = '\0'; + + ath10k_dbg(ATH10K_DBG_BOOT, + "found fw version %s\n", + ar->hw->wiphy->fw_version); + break; + case ATH10K_FW_IE_TIMESTAMP: + if (ie_len != sizeof(u32)) + break; + + timestamp = (__le32 *)data; + + ath10k_dbg(ATH10K_DBG_BOOT, "found fw timestamp %d\n", + le32_to_cpup(timestamp)); + break; + case ATH10K_FW_IE_FEATURES: + ath10k_dbg(ATH10K_DBG_BOOT, + "found firmware features ie (%zd B)\n", + ie_len); + + for (i = 0; i < ATH10K_FW_FEATURE_COUNT; i++) { + index = i / 8; + bit = i % 8; + + if (index == ie_len) + break; + + if (data[index] & (1 << bit)) + __set_bit(i, ar->fw_features); + } + + ath10k_dbg_dump(ATH10K_DBG_BOOT, "features", "", + ar->fw_features, + sizeof(ar->fw_features)); + break; + case ATH10K_FW_IE_FW_IMAGE: + ath10k_dbg(ATH10K_DBG_BOOT, + "found fw image ie (%zd B)\n", + ie_len); + + ar->firmware_data = data; + ar->firmware_len = ie_len; + + break; + case ATH10K_FW_IE_OTP_IMAGE: + ath10k_dbg(ATH10K_DBG_BOOT, + "found otp image ie (%zd B)\n", + ie_len); + + ar->otp_data = data; + ar->otp_len = ie_len; + + break; + default: + ath10k_warn("Unknown FW IE: %u\n", + le32_to_cpu(hdr->id)); + break; + } + + /* jump over the padding */ + ie_len = ALIGN(ie_len, 4); + + len -= ie_len; + data += ie_len; + }; + + return 0; +} + +static int ath10k_core_fetch_firmware_files(struct ath10k *ar) +{ + int ret; + + ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API2_FILE); + if (ret == 0) { + ar->fw_api = 2; + goto out; + } + + ret = ath10k_core_fetch_firmware_api_1(ar); + if (ret) + return ret; + + ar->fw_api = 1; + +out: + ath10k_dbg(ATH10K_DBG_BOOT, "using fw api %d\n", ar->fw_api); + + return 0; +} + static int ath10k_init_download_firmware(struct ath10k *ar) { int ret; diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 511b20d..3b5e78d 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -336,6 +336,8 @@ struct ath10k { const void *firmware_data; size_t firmware_len; + int fw_api; + struct { struct completion started; struct completion completed; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 643f0c9..a9cac43 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -38,6 +38,25 @@ #define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin" #define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234 +#define ATH10K_FW_API2_FILE "firmware-2.bin" + +/* includes also the null byte */ +#define ATH10K_FIRMWARE_MAGIC "QCA-ATH10K" + +struct ath10k_fw_ie { + __le32 id; + __le32 len; + u8 data[0]; +}; + +enum ath10k_fw_ie_type { + ATH10K_FW_IE_FW_VERSION = 0, + ATH10K_FW_IE_TIMESTAMP = 1, + ATH10K_FW_IE_FEATURES = 2, + ATH10K_FW_IE_FW_IMAGE = 3, + ATH10K_FW_IE_OTP_IMAGE = 4, +}; + /* Known pecularities: * - current FW doesn't support raw rx mode (last tested v599) * - current FW dumps upon raw tx mode (last tested v599) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 6803ead..771d77c 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -988,7 +988,8 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, ar->phy_capability = __le32_to_cpu(ev->phy_capability); ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains); - if (ar->fw_version_build > 636) + /* only manually set fw features when not using FW IE format */ + if (ar->fw_api == 1 && ar->fw_version_build > 636) set_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features); if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) {
Firmware IE containers can dynamically provide various information what firmware supports. Also it can embed more than one image so updating firmware is easy, user just needs to update one file in /lib/firmware/. The firmware API 2 or higher will use the IE container format, the current API 1 will not use the new format but it still is supported for some time. FW API 2 files are named as firmware-2.bin (which contains both firmware and otp images) and API 1 files are firmware.bin and otp.bin. Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com> --- drivers/net/wireless/ath/ath10k/core.c | 153 ++++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/core.h | 2 drivers/net/wireless/ath/ath10k/hw.h | 19 ++++ drivers/net/wireless/ath/ath10k/wmi.c | 3 - 4 files changed, 175 insertions(+), 2 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html