Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/athk/ath11k/fw.c
96339 views
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
173