Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/mld/phy.c
48287 views
1
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2
/*
3
* Copyright (C) 2024-2025 Intel Corporation
4
*/
5
#include <net/mac80211.h>
6
7
#include "phy.h"
8
#include "hcmd.h"
9
#include "fw/api/phy-ctxt.h"
10
11
int iwl_mld_allocate_fw_phy_id(struct iwl_mld *mld)
12
{
13
int id;
14
unsigned long used = mld->used_phy_ids;
15
16
for_each_clear_bit(id, &used, NUM_PHY_CTX) {
17
mld->used_phy_ids |= BIT(id);
18
return id;
19
}
20
21
return -ENOSPC;
22
}
23
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_allocate_fw_phy_id);
24
25
struct iwl_mld_chanctx_usage_data {
26
struct iwl_mld *mld;
27
struct ieee80211_chanctx_conf *ctx;
28
bool use_def;
29
};
30
31
static bool iwl_mld_chanctx_fils_enabled(struct ieee80211_vif *vif,
32
struct ieee80211_chanctx_conf *ctx)
33
{
34
if (vif->type != NL80211_IFTYPE_AP)
35
return false;
36
37
return cfg80211_channel_is_psc(ctx->def.chan) ||
38
(ctx->def.chan->band == NL80211_BAND_6GHZ &&
39
ctx->def.width >= NL80211_CHAN_WIDTH_80);
40
}
41
42
static void iwl_mld_chanctx_usage_iter(void *_data, u8 *mac,
43
struct ieee80211_vif *vif)
44
{
45
struct iwl_mld_chanctx_usage_data *data = _data;
46
struct ieee80211_bss_conf *link_conf;
47
int link_id;
48
49
for_each_vif_active_link(vif, link_conf, link_id) {
50
if (rcu_access_pointer(link_conf->chanctx_conf) != data->ctx)
51
continue;
52
53
if (vif->type == NL80211_IFTYPE_AP && link_conf->ftm_responder)
54
data->use_def = true;
55
56
if (iwl_mld_chanctx_fils_enabled(vif, data->ctx))
57
data->use_def = true;
58
}
59
}
60
61
struct cfg80211_chan_def *
62
iwl_mld_get_chandef_from_chanctx(struct iwl_mld *mld,
63
struct ieee80211_chanctx_conf *ctx)
64
{
65
struct iwl_mld_chanctx_usage_data data = {
66
.mld = mld,
67
.ctx = ctx,
68
};
69
70
ieee80211_iterate_active_interfaces_mtx(mld->hw,
71
IEEE80211_IFACE_ITER_NORMAL,
72
iwl_mld_chanctx_usage_iter,
73
&data);
74
75
return data.use_def ? &ctx->def : &ctx->min_def;
76
}
77
78
static u8
79
iwl_mld_nl80211_width_to_fw(enum nl80211_chan_width width)
80
{
81
switch (width) {
82
case NL80211_CHAN_WIDTH_20_NOHT:
83
case NL80211_CHAN_WIDTH_20:
84
return IWL_PHY_CHANNEL_MODE20;
85
case NL80211_CHAN_WIDTH_40:
86
return IWL_PHY_CHANNEL_MODE40;
87
case NL80211_CHAN_WIDTH_80:
88
return IWL_PHY_CHANNEL_MODE80;
89
case NL80211_CHAN_WIDTH_160:
90
return IWL_PHY_CHANNEL_MODE160;
91
case NL80211_CHAN_WIDTH_320:
92
return IWL_PHY_CHANNEL_MODE320;
93
default:
94
WARN(1, "Invalid channel width=%u", width);
95
return IWL_PHY_CHANNEL_MODE20;
96
}
97
}
98
99
/* Maps the driver specific control channel position (relative to the center
100
* freq) definitions to the fw values
101
*/
102
u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef)
103
{
104
int offs = chandef->chan->center_freq - chandef->center_freq1;
105
int abs_offs = abs(offs);
106
u8 ret;
107
108
if (offs == 0) {
109
/* The FW is expected to check the control channel position only
110
* when in HT/VHT and the channel width is not 20MHz. Return
111
* this value as the default one.
112
*/
113
return 0;
114
}
115
116
/* this results in a value 0-7, i.e. fitting into 0b0111 */
117
ret = (abs_offs - 10) / 20;
118
/* But we need the value to be in 0b1011 because 0b0100 is
119
* IWL_PHY_CTRL_POS_ABOVE, so shift bit 2 up to land in
120
* IWL_PHY_CTRL_POS_OFFS_EXT (0b1000)
121
*/
122
ret = (ret & IWL_PHY_CTRL_POS_OFFS_MSK) |
123
((ret & BIT(2)) << 1);
124
/* and add the above bit */
125
ret |= (offs > 0) * IWL_PHY_CTRL_POS_ABOVE;
126
127
return ret;
128
}
129
130
int iwl_mld_phy_fw_action(struct iwl_mld *mld,
131
struct ieee80211_chanctx_conf *ctx, u32 action)
132
{
133
struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx);
134
struct cfg80211_chan_def *chandef = &phy->chandef;
135
struct iwl_phy_context_cmd cmd = {
136
.id_and_color = cpu_to_le32(phy->fw_id),
137
.action = cpu_to_le32(action),
138
.puncture_mask = cpu_to_le16(chandef->punctured),
139
/* Channel info */
140
.ci.channel = cpu_to_le32(chandef->chan->hw_value),
141
.ci.band = iwl_mld_nl80211_band_to_fw(chandef->chan->band),
142
.ci.width = iwl_mld_nl80211_width_to_fw(chandef->width),
143
.ci.ctrl_pos = iwl_mld_get_fw_ctrl_pos(chandef),
144
};
145
int ret;
146
147
if (ctx->ap.chan) {
148
cmd.sbb_bandwidth =
149
iwl_mld_nl80211_width_to_fw(ctx->ap.width);
150
cmd.sbb_ctrl_channel_loc = iwl_mld_get_fw_ctrl_pos(&ctx->ap);
151
}
152
153
ret = iwl_mld_send_cmd_pdu(mld, PHY_CONTEXT_CMD, &cmd);
154
if (ret)
155
IWL_ERR(mld, "Failed to send PHY_CONTEXT_CMD ret = %d\n", ret);
156
157
return ret;
158
}
159
160
static u32 iwl_mld_get_phy_config(struct iwl_mld *mld)
161
{
162
u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN |
163
FW_PHY_CFG_RX_CHAIN);
164
u32 valid_rx_ant = iwl_mld_get_valid_rx_ant(mld);
165
u32 valid_tx_ant = iwl_mld_get_valid_tx_ant(mld);
166
167
phy_config |= valid_tx_ant << FW_PHY_CFG_TX_CHAIN_POS |
168
valid_rx_ant << FW_PHY_CFG_RX_CHAIN_POS;
169
170
return mld->fw->phy_config & phy_config;
171
}
172
173
int iwl_mld_send_phy_cfg_cmd(struct iwl_mld *mld)
174
{
175
const struct iwl_tlv_calib_ctrl *default_calib =
176
&mld->fw->default_calib[IWL_UCODE_REGULAR];
177
struct iwl_phy_cfg_cmd_v3 cmd = {
178
.phy_cfg = cpu_to_le32(iwl_mld_get_phy_config(mld)),
179
.calib_control.event_trigger = default_calib->event_trigger,
180
.calib_control.flow_trigger = default_calib->flow_trigger,
181
.phy_specific_cfg = mld->fwrt.phy_filters,
182
};
183
184
IWL_DEBUG_INFO(mld, "Sending Phy CFG command: 0x%x\n", cmd.phy_cfg);
185
186
return iwl_mld_send_cmd_pdu(mld, PHY_CONFIGURATION_CMD, &cmd);
187
}
188
189
void iwl_mld_update_phy_chandef(struct iwl_mld *mld,
190
struct ieee80211_chanctx_conf *ctx)
191
{
192
struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx);
193
struct cfg80211_chan_def *chandef =
194
iwl_mld_get_chandef_from_chanctx(mld, ctx);
195
196
phy->chandef = *chandef;
197
iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_MODIFY);
198
}
199
200