xref: /linux/drivers/net/wireless/ath/ath11k/fw.c (revision 8be4d31cb8aaeea27bde4b7ddb26e28a89062ebf)
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 
ath11k_fw_request_firmware_api_n(struct ath11k_base * ab,const char * name)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 
ath11k_fw_pre_init(struct ath11k_base * ab)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 
ath11k_fw_destroy(struct ath11k_base * ab)167 void ath11k_fw_destroy(struct ath11k_base *ab)
168 {
169 	release_firmware(ab->fw.fw);
170 }
171 EXPORT_SYMBOL(ath11k_fw_destroy);
172