1// SPDX-License-Identifier: BSD-3-Clause-Clear
2/*
3 * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
4 */
5
6#include "core.h"
7
8#include "debug.h"
9
10static 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, filename: name);
20 if (IS_ERR(ptr: ab->fw.fw)) {
21 ret = PTR_ERR(ptr: 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, fmt: "firmware image too small to contain magic: %zu\n",
35 len);
36 ret = -EINVAL;
37 goto err;
38 }
39
40 if (memcmp(p: data, ATH12K_FIRMWARE_MAGIC, size: magic_len) != 0) {
41 ath12k_err(ab, fmt: "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, fmt: "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, fmt: "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 ath12k_dbg_dump(ab, mask: ATH12K_DBG_BOOT, msg: "features", prefix: "",
103 buf: ab->fw.fw_features,
104 len: sizeof(ab->fw.fw_features));
105 break;
106 case ATH12K_FW_IE_AMSS_IMAGE:
107 ath12k_dbg(ab, ATH12K_DBG_BOOT,
108 "found fw image ie (%zd B)\n",
109 ie_len);
110
111 ab->fw.amss_data = data;
112 ab->fw.amss_len = ie_len;
113 break;
114 case ATH12K_FW_IE_M3_IMAGE:
115 ath12k_dbg(ab, ATH12K_DBG_BOOT,
116 "found m3 image ie (%zd B)\n",
117 ie_len);
118
119 ab->fw.m3_data = data;
120 ab->fw.m3_len = ie_len;
121 break;
122 case ATH12K_FW_IE_AMSS_DUALMAC_IMAGE:
123 ath12k_dbg(ab, ATH12K_DBG_BOOT,
124 "found dualmac fw image ie (%zd B)\n",
125 ie_len);
126 ab->fw.amss_dualmac_data = data;
127 ab->fw.amss_dualmac_len = ie_len;
128 break;
129 default:
130 ath12k_warn(ab, fmt: "Unknown FW IE: %u\n", ie_id);
131 break;
132 }
133
134 /* jump over the padding */
135 ie_len = ALIGN(ie_len, 4);
136
137 /* make sure there's space for padding */
138 if (ie_len > len)
139 break;
140
141 len -= ie_len;
142 data += ie_len;
143 }
144
145 return 0;
146
147err:
148 release_firmware(fw: ab->fw.fw);
149 ab->fw.fw = NULL;
150 return ret;
151}
152
153void ath12k_fw_map(struct ath12k_base *ab)
154{
155 int ret;
156
157 ret = ath12k_fw_request_firmware_api_n(ab, ATH12K_FW_API2_FILE);
158 if (ret == 0)
159 ab->fw.api_version = 2;
160 else
161 ab->fw.api_version = 1;
162
163 ath12k_dbg(ab, ATH12K_DBG_BOOT, "using fw api %d\n",
164 ab->fw.api_version);
165}
166
167void ath12k_fw_unmap(struct ath12k_base *ab)
168{
169 release_firmware(fw: ab->fw.fw);
170 memset(&ab->fw, 0, sizeof(ab->fw));
171}
172

source code of linux/drivers/net/wireless/ath/ath12k/fw.c