Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/mld/mcc.c
48285 views
1
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2
/*
3
* Copyright (C) 2024-2025 Intel Corporation
4
*/
5
6
#include <net/cfg80211.h>
7
#include <net/mac80211.h>
8
9
#include <fw/dbg.h>
10
#include <iwl-nvm-parse.h>
11
12
#include "mld.h"
13
#include "hcmd.h"
14
#include "mcc.h"
15
16
/* It is the caller's responsibility to free the pointer returned here */
17
static struct iwl_mcc_update_resp_v8 *
18
iwl_mld_copy_mcc_resp(const struct iwl_rx_packet *pkt)
19
{
20
const struct iwl_mcc_update_resp_v8 *mcc_resp_v8 = (const void *)pkt->data;
21
int n_channels = __le32_to_cpu(mcc_resp_v8->n_channels);
22
struct iwl_mcc_update_resp_v8 *resp_cp;
23
int notif_len = struct_size(resp_cp, channels, n_channels);
24
25
if (iwl_rx_packet_payload_len(pkt) != notif_len)
26
return ERR_PTR(-EINVAL);
27
28
resp_cp = kmemdup(mcc_resp_v8, notif_len, GFP_KERNEL);
29
if (!resp_cp)
30
return ERR_PTR(-ENOMEM);
31
32
return resp_cp;
33
}
34
35
/* It is the caller's responsibility to free the pointer returned here */
36
static struct iwl_mcc_update_resp_v8 *
37
iwl_mld_update_mcc(struct iwl_mld *mld, const char *alpha2,
38
enum iwl_mcc_source src_id)
39
{
40
struct iwl_mcc_update_cmd mcc_update_cmd = {
41
.mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]),
42
.source_id = (u8)src_id,
43
};
44
struct iwl_mcc_update_resp_v8 *resp_cp;
45
struct iwl_rx_packet *pkt;
46
struct iwl_host_cmd cmd = {
47
.id = MCC_UPDATE_CMD,
48
.flags = CMD_WANT_SKB,
49
.data = { &mcc_update_cmd },
50
.len[0] = sizeof(mcc_update_cmd),
51
};
52
int ret;
53
u16 mcc;
54
55
IWL_DEBUG_LAR(mld, "send MCC update to FW with '%c%c' src = %d\n",
56
alpha2[0], alpha2[1], src_id);
57
58
ret = iwl_mld_send_cmd(mld, &cmd);
59
if (ret)
60
return ERR_PTR(ret);
61
62
pkt = cmd.resp_pkt;
63
64
resp_cp = iwl_mld_copy_mcc_resp(pkt);
65
if (IS_ERR(resp_cp))
66
goto exit;
67
68
mcc = le16_to_cpu(resp_cp->mcc);
69
70
IWL_FW_CHECK(mld, !mcc, "mcc can't be 0: %d\n", mcc);
71
72
IWL_DEBUG_LAR(mld,
73
"MCC response status: 0x%x. new MCC: 0x%x ('%c%c')\n",
74
le32_to_cpu(resp_cp->status), mcc, mcc >> 8, mcc & 0xff);
75
76
exit:
77
iwl_free_resp(&cmd);
78
return resp_cp;
79
}
80
81
/* It is the caller's responsibility to free the pointer returned here */
82
struct ieee80211_regdomain *
83
iwl_mld_get_regdomain(struct iwl_mld *mld,
84
const char *alpha2,
85
enum iwl_mcc_source src_id,
86
bool *changed)
87
{
88
struct ieee80211_regdomain *regd = NULL;
89
struct iwl_mcc_update_resp_v8 *resp;
90
u8 resp_ver = iwl_fw_lookup_notif_ver(mld->fw, IWL_ALWAYS_LONG_GROUP,
91
MCC_UPDATE_CMD, 0);
92
93
IWL_DEBUG_LAR(mld, "Getting regdomain data for %s from FW\n", alpha2);
94
95
lockdep_assert_wiphy(mld->wiphy);
96
97
resp = iwl_mld_update_mcc(mld, alpha2, src_id);
98
if (IS_ERR(resp)) {
99
IWL_DEBUG_LAR(mld, "Could not get update from FW %ld\n",
100
PTR_ERR(resp));
101
resp = NULL;
102
goto out;
103
}
104
105
if (changed) {
106
u32 status = le32_to_cpu(resp->status);
107
108
*changed = (status == MCC_RESP_NEW_CHAN_PROFILE ||
109
status == MCC_RESP_ILLEGAL);
110
}
111
IWL_DEBUG_LAR(mld, "MCC update response version: %d\n", resp_ver);
112
113
regd = iwl_parse_nvm_mcc_info(mld->trans,
114
__le32_to_cpu(resp->n_channels),
115
resp->channels,
116
__le16_to_cpu(resp->mcc),
117
__le16_to_cpu(resp->geo_info),
118
le32_to_cpu(resp->cap), resp_ver);
119
120
if (IS_ERR(regd)) {
121
IWL_DEBUG_LAR(mld, "Could not get parse update from FW %ld\n",
122
PTR_ERR(regd));
123
goto out;
124
}
125
126
IWL_DEBUG_LAR(mld, "setting alpha2 from FW to %s (0x%x, 0x%x) src=%d\n",
127
regd->alpha2, regd->alpha2[0],
128
regd->alpha2[1], resp->source_id);
129
130
mld->mcc_src = resp->source_id;
131
132
/* FM is the earliest supported and later always do puncturing */
133
if (CSR_HW_RFID_TYPE(mld->trans->info.hw_rf_id) == IWL_CFG_RF_TYPE_FM) {
134
if (!iwl_puncturing_is_allowed_in_bios(mld->bios_enable_puncturing,
135
le16_to_cpu(resp->mcc)))
136
ieee80211_hw_set(mld->hw, DISALLOW_PUNCTURING);
137
else
138
__clear_bit(IEEE80211_HW_DISALLOW_PUNCTURING,
139
mld->hw->flags);
140
}
141
142
out:
143
kfree(resp);
144
return regd;
145
}
146
147
/* It is the caller's responsibility to free the pointer returned here */
148
static struct ieee80211_regdomain *
149
iwl_mld_get_current_regdomain(struct iwl_mld *mld,
150
bool *changed)
151
{
152
return iwl_mld_get_regdomain(mld, "ZZ",
153
MCC_SOURCE_GET_CURRENT, changed);
154
}
155
156
void iwl_mld_update_changed_regdomain(struct iwl_mld *mld)
157
{
158
struct ieee80211_regdomain *regd;
159
bool changed;
160
161
regd = iwl_mld_get_current_regdomain(mld, &changed);
162
163
if (IS_ERR_OR_NULL(regd))
164
return;
165
166
if (changed)
167
regulatory_set_wiphy_regd(mld->wiphy, regd);
168
kfree(regd);
169
}
170
171
static int iwl_mld_apply_last_mcc(struct iwl_mld *mld,
172
const char *alpha2)
173
{
174
struct ieee80211_regdomain *regd;
175
u32 used_src;
176
bool changed;
177
int ret;
178
179
/* save the last source in case we overwrite it below */
180
used_src = mld->mcc_src;
181
182
/* Notify the firmware we support wifi location updates */
183
regd = iwl_mld_get_current_regdomain(mld, NULL);
184
if (!IS_ERR_OR_NULL(regd))
185
kfree(regd);
186
187
/* Now set our last stored MCC and source */
188
regd = iwl_mld_get_regdomain(mld, alpha2, used_src,
189
&changed);
190
if (IS_ERR_OR_NULL(regd))
191
return -EIO;
192
193
/* update cfg80211 if the regdomain was changed */
194
if (changed)
195
ret = regulatory_set_wiphy_regd_sync(mld->wiphy, regd);
196
else
197
ret = 0;
198
199
kfree(regd);
200
return ret;
201
}
202
203
int iwl_mld_init_mcc(struct iwl_mld *mld)
204
{
205
const struct ieee80211_regdomain *r;
206
struct ieee80211_regdomain *regd;
207
char mcc[3];
208
int retval;
209
210
/* try to replay the last set MCC to FW */
211
r = wiphy_dereference(mld->wiphy, mld->wiphy->regd);
212
213
if (r)
214
return iwl_mld_apply_last_mcc(mld, r->alpha2);
215
216
regd = iwl_mld_get_current_regdomain(mld, NULL);
217
if (IS_ERR_OR_NULL(regd))
218
return -EIO;
219
220
if (!iwl_bios_get_mcc(&mld->fwrt, mcc)) {
221
kfree(regd);
222
regd = iwl_mld_get_regdomain(mld, mcc, MCC_SOURCE_BIOS, NULL);
223
if (IS_ERR_OR_NULL(regd))
224
return -EIO;
225
}
226
227
retval = regulatory_set_wiphy_regd_sync(mld->wiphy, regd);
228
229
kfree(regd);
230
return retval;
231
}
232
233
static void iwl_mld_find_assoc_vif_iterator(void *data, u8 *mac,
234
struct ieee80211_vif *vif)
235
{
236
bool *assoc = data;
237
238
if (vif->type == NL80211_IFTYPE_STATION &&
239
vif->cfg.assoc)
240
*assoc = true;
241
}
242
243
static bool iwl_mld_is_a_vif_assoc(struct iwl_mld *mld)
244
{
245
bool assoc = false;
246
247
ieee80211_iterate_active_interfaces_atomic(mld->hw,
248
IEEE80211_IFACE_ITER_NORMAL,
249
iwl_mld_find_assoc_vif_iterator,
250
&assoc);
251
return assoc;
252
}
253
254
void iwl_mld_handle_update_mcc(struct iwl_mld *mld, struct iwl_rx_packet *pkt)
255
{
256
struct iwl_mcc_chub_notif *notif = (void *)pkt->data;
257
enum iwl_mcc_source src;
258
char mcc[3];
259
struct ieee80211_regdomain *regd;
260
bool changed;
261
262
lockdep_assert_wiphy(mld->wiphy);
263
264
if (iwl_mld_is_a_vif_assoc(mld) &&
265
notif->source_id == MCC_SOURCE_WIFI) {
266
IWL_DEBUG_LAR(mld, "Ignore mcc update while associated\n");
267
return;
268
}
269
270
mcc[0] = le16_to_cpu(notif->mcc) >> 8;
271
mcc[1] = le16_to_cpu(notif->mcc) & 0xff;
272
mcc[2] = '\0';
273
src = notif->source_id;
274
275
IWL_DEBUG_LAR(mld,
276
"RX: received chub update mcc cmd (mcc '%s' src %d)\n",
277
mcc, src);
278
regd = iwl_mld_get_regdomain(mld, mcc, src, &changed);
279
if (IS_ERR_OR_NULL(regd))
280
return;
281
282
if (changed)
283
regulatory_set_wiphy_regd(mld->hw->wiphy, regd);
284
kfree(regd);
285
}
286
287