1 // SPDX-License-Identifier: BSD-3-Clause-Clear 2 /* 3 * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. 4 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. 5 */ 6 7 #include <linux/export.h> 8 #include "core.h" 9 10 #include "debug.h" 11 12 static int ath11k_fw_request_firmware_api_n(struct ath11k_base *ab, 13 const char *name) 14 { 15 size_t magic_len, len, ie_len; 16 int ie_id, i, index, bit, ret; 17 struct ath11k_fw_ie *hdr; 18 const u8 *data; 19 __le32 *timestamp; 20 21 ab->fw.fw = ath11k_core_firmware_request(ab, name); 22 if (IS_ERR(ab->fw.fw)) { 23 ret = PTR_ERR(ab->fw.fw); 24 ath11k_dbg(ab, ATH11K_DBG_BOOT, "failed to load %s: %d\n", name, ret); 25 ab->fw.fw = NULL; 26 return ret; 27 } 28 29 data = ab->fw.fw->data; 30 len = ab->fw.fw->size; 31 32 /* magic also includes the null byte, check that as well */ 33 magic_len = strlen(ATH11K_FIRMWARE_MAGIC) + 1; 34 35 if (len < magic_len) { 36 ath11k_err(ab, "firmware image too small to contain magic: %zu\n", 37 len); 38 ret = -EINVAL; 39 goto err; 40 } 41 42 if (memcmp(data, ATH11K_FIRMWARE_MAGIC, magic_len) != 0) { 43 ath11k_err(ab, "Invalid firmware magic\n"); 44 ret = -EINVAL; 45 goto err; 46 } 47 48 /* jump over the padding */ 49 magic_len = ALIGN(magic_len, 4); 50 51 /* make sure there's space for padding */ 52 if (magic_len > len) { 53 ath11k_err(ab, "No space for padding after magic\n"); 54 ret = -EINVAL; 55 goto err; 56 } 57 58 len -= magic_len; 59 data += magic_len; 60 61 /* loop elements */ 62 while (len > sizeof(struct ath11k_fw_ie)) { 63 hdr = (struct ath11k_fw_ie *)data; 64 65 ie_id = le32_to_cpu(hdr->id); 66 ie_len = le32_to_cpu(hdr->len); 67 68 len -= sizeof(*hdr); 69 data += sizeof(*hdr); 70 71 if (len < ie_len) { 72 ath11k_err(ab, "Invalid length for FW IE %d (%zu < %zu)\n", 73 ie_id, len, ie_len); 74 ret = -EINVAL; 75 goto err; 76 } 77 78 switch (ie_id) { 79 case ATH11K_FW_IE_TIMESTAMP: 80 if (ie_len != sizeof(u32)) 81 break; 82 83 timestamp = (__le32 *)data; 84 85 ath11k_dbg(ab, ATH11K_DBG_BOOT, "found fw timestamp %d\n", 86 le32_to_cpup(timestamp)); 87 break; 88 case ATH11K_FW_IE_FEATURES: 89 ath11k_dbg(ab, ATH11K_DBG_BOOT, 90 "found firmware features ie (%zd B)\n", 91 ie_len); 92 93 for (i = 0; i < ATH11K_FW_FEATURE_COUNT; i++) { 94 index = i / 8; 95 bit = i % 8; 96 97 if (index == ie_len) 98 break; 99 100 if (data[index] & (1 << bit)) 101 __set_bit(i, ab->fw.fw_features); 102 } 103 104 ath11k_dbg_dump(ab, ATH11K_DBG_BOOT, "features", "", 105 ab->fw.fw_features, 106 sizeof(ab->fw.fw_features)); 107 break; 108 case ATH11K_FW_IE_AMSS_IMAGE: 109 ath11k_dbg(ab, ATH11K_DBG_BOOT, 110 "found fw image ie (%zd B)\n", 111 ie_len); 112 113 ab->fw.amss_data = data; 114 ab->fw.amss_len = ie_len; 115 break; 116 case ATH11K_FW_IE_M3_IMAGE: 117 ath11k_dbg(ab, ATH11K_DBG_BOOT, 118 "found m3 image ie (%zd B)\n", 119 ie_len); 120 121 ab->fw.m3_data = data; 122 ab->fw.m3_len = ie_len; 123 break; 124 default: 125 ath11k_warn(ab, "Unknown FW IE: %u\n", ie_id); 126 break; 127 } 128 129 /* jump over the padding */ 130 ie_len = ALIGN(ie_len, 4); 131 132 /* make sure there's space for padding */ 133 if (ie_len > len) 134 break; 135 136 len -= ie_len; 137 data += ie_len; 138 } 139 140 return 0; 141 142 err: 143 release_firmware(ab->fw.fw); 144 ab->fw.fw = NULL; 145 return ret; 146 } 147 148 int ath11k_fw_pre_init(struct ath11k_base *ab) 149 { 150 int ret; 151 152 ret = ath11k_fw_request_firmware_api_n(ab, ATH11K_FW_API2_FILE); 153 if (ret == 0) { 154 ab->fw.api_version = 2; 155 goto out; 156 } 157 158 ab->fw.api_version = 1; 159 160 out: 161 ath11k_dbg(ab, ATH11K_DBG_BOOT, "using fw api %d\n", 162 ab->fw.api_version); 163 164 return 0; 165 } 166 167 void ath11k_fw_destroy(struct ath11k_base *ab) 168 { 169 release_firmware(ab->fw.fw); 170 } 171 EXPORT_SYMBOL(ath11k_fw_destroy); 172