1 // SPDX-License-Identifier: BSD-3-Clause-Clear 2 /* 3 * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. 4 */ 5 6 #include "core.h" 7 8 #include "debug.h" 9 10 static int ath12k_fw_request_firmware_api_n(struct ath12k_base *ab, 11 const char *name) 12 { 13 size_t magic_len, len, ie_len; 14 int ie_id, i, index, bit, ret; 15 struct ath12k_fw_ie *hdr; 16 const u8 *data; 17 __le32 *timestamp; 18 19 ab->fw.fw = ath12k_core_firmware_request(ab, name); 20 if (IS_ERR(ab->fw.fw)) { 21 ret = PTR_ERR(ab->fw.fw); 22 ath12k_dbg(ab, ATH12K_DBG_BOOT, "failed to load %s: %d\n", name, ret); 23 ab->fw.fw = NULL; 24 return ret; 25 } 26 27 data = ab->fw.fw->data; 28 len = ab->fw.fw->size; 29 30 /* magic also includes the null byte, check that as well */ 31 magic_len = strlen(ATH12K_FIRMWARE_MAGIC) + 1; 32 33 if (len < magic_len) { 34 ath12k_err(ab, "firmware image too small to contain magic: %zu\n", 35 len); 36 ret = -EINVAL; 37 goto err; 38 } 39 40 if (memcmp(data, ATH12K_FIRMWARE_MAGIC, magic_len) != 0) { 41 ath12k_err(ab, "Invalid firmware magic\n"); 42 ret = -EINVAL; 43 goto err; 44 } 45 46 /* jump over the padding */ 47 magic_len = ALIGN(magic_len, 4); 48 49 /* make sure there's space for padding */ 50 if (magic_len > len) { 51 ath12k_err(ab, "No space for padding after magic\n"); 52 ret = -EINVAL; 53 goto err; 54 } 55 56 len -= magic_len; 57 data += magic_len; 58 59 /* loop elements */ 60 while (len > sizeof(struct ath12k_fw_ie)) { 61 hdr = (struct ath12k_fw_ie *)data; 62 63 ie_id = le32_to_cpu(hdr->id); 64 ie_len = le32_to_cpu(hdr->len); 65 66 len -= sizeof(*hdr); 67 data += sizeof(*hdr); 68 69 if (len < ie_len) { 70 ath12k_err(ab, "Invalid length for FW IE %d (%zu < %zu)\n", 71 ie_id, len, ie_len); 72 ret = -EINVAL; 73 goto err; 74 } 75 76 switch (ie_id) { 77 case ATH12K_FW_IE_TIMESTAMP: 78 if (ie_len != sizeof(u32)) 79 break; 80 81 timestamp = (__le32 *)data; 82 83 ath12k_dbg(ab, ATH12K_DBG_BOOT, "found fw timestamp %d\n", 84 le32_to_cpup(timestamp)); 85 break; 86 case ATH12K_FW_IE_FEATURES: 87 ath12k_dbg(ab, ATH12K_DBG_BOOT, 88 "found firmware features ie (%zd B)\n", 89 ie_len); 90 91 for (i = 0; i < ATH12K_FW_FEATURE_COUNT; i++) { 92 index = i / 8; 93 bit = i % 8; 94 95 if (index == ie_len) 96 break; 97 98 if (data[index] & (1 << bit)) 99 __set_bit(i, ab->fw.fw_features); 100 } 101 102 ab->fw.fw_features_valid = true; 103 104 ath12k_dbg_dump(ab, ATH12K_DBG_BOOT, "features", "", 105 ab->fw.fw_features, 106 sizeof(ab->fw.fw_features)); 107 break; 108 case ATH12K_FW_IE_AMSS_IMAGE: 109 ath12k_dbg(ab, ATH12K_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 ATH12K_FW_IE_M3_IMAGE: 117 ath12k_dbg(ab, ATH12K_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 case ATH12K_FW_IE_AMSS_DUALMAC_IMAGE: 125 ath12k_dbg(ab, ATH12K_DBG_BOOT, 126 "found dualmac fw image ie (%zd B)\n", 127 ie_len); 128 ab->fw.amss_dualmac_data = data; 129 ab->fw.amss_dualmac_len = ie_len; 130 break; 131 default: 132 ath12k_warn(ab, "Unknown FW IE: %u\n", ie_id); 133 break; 134 } 135 136 /* jump over the padding */ 137 ie_len = ALIGN(ie_len, 4); 138 139 /* make sure there's space for padding */ 140 if (ie_len > len) 141 break; 142 143 len -= ie_len; 144 data += ie_len; 145 } 146 147 return 0; 148 149 err: 150 release_firmware(ab->fw.fw); 151 ab->fw.fw = NULL; 152 return ret; 153 } 154 155 void ath12k_fw_map(struct ath12k_base *ab) 156 { 157 int ret; 158 159 ret = ath12k_fw_request_firmware_api_n(ab, ATH12K_FW_API2_FILE); 160 if (ret == 0) 161 ab->fw.api_version = 2; 162 else 163 ab->fw.api_version = 1; 164 165 ath12k_dbg(ab, ATH12K_DBG_BOOT, "using fw api %d\n", 166 ab->fw.api_version); 167 } 168 169 void ath12k_fw_unmap(struct ath12k_base *ab) 170 { 171 release_firmware(ab->fw.fw); 172 memset(&ab->fw, 0, sizeof(ab->fw)); 173 } 174 175 bool ath12k_fw_feature_supported(struct ath12k_base *ab, enum ath12k_fw_features feat) 176 { 177 return ab->fw.fw_features_valid && test_bit(feat, ab->fw.fw_features); 178 } 179