Commit 39dc8b8e authored by Johannes Berg's avatar Johannes Berg

wifi: mac80211: pass parsed TPE data to drivers

Instead of passing the full TPE elements, in all their glory
and mixed up data formats for HE backward compatibility, parse
them fully into the right values, and pass that to the drivers.

Also introduce proper validation already in mac80211, so that
drivers don't need to do it, and parse the EHT portions.

The code now passes the values in the right order according to
the channel used by an interface, which could also be a subset
of the data advertised by the AP, if we couldn't connect with
the full bandwidth (for whatever reason.)

Also add kunit tests for the more complicated bits of it.
Reviewed-by: default avatarMiriam Rachel Korenblit <miriam.rachel.korenblit@intel.com>
Acked-by: default avatarKalle Valo <kvalo@kernel.org>
Link: https://msgid.link/20240506214536.2aa839969b60.I265b28209e0b29772b2f125f7f83de44a4da877b@changeidSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent caa12b3d
......@@ -7507,32 +7507,6 @@ static int ath11k_mac_stop_vdev_early(struct ieee80211_hw *hw,
return 0;
}
static u8 ath11k_mac_get_tpe_count(u8 txpwr_intrprt, u8 txpwr_cnt)
{
switch (txpwr_intrprt) {
/* Refer "Table 9-276-Meaning of Maximum Transmit Power Count subfield
* if the Maximum Transmit Power Interpretation subfield is 0 or 2" of
* "IEEE Std 802.11ax 2021".
*/
case IEEE80211_TPE_LOCAL_EIRP:
case IEEE80211_TPE_REG_CLIENT_EIRP:
txpwr_cnt = txpwr_cnt <= 3 ? txpwr_cnt : 3;
txpwr_cnt = txpwr_cnt + 1;
break;
/* Refer "Table 9-277-Meaning of Maximum Transmit Power Count subfield
* if Maximum Transmit Power Interpretation subfield is 1 or 3" of
* "IEEE Std 802.11ax 2021".
*/
case IEEE80211_TPE_LOCAL_EIRP_PSD:
case IEEE80211_TPE_REG_CLIENT_EIRP_PSD:
txpwr_cnt = txpwr_cnt <= 4 ? txpwr_cnt : 4;
txpwr_cnt = txpwr_cnt ? (BIT(txpwr_cnt - 1)) : 1;
break;
}
return txpwr_cnt;
}
static u8 ath11k_mac_get_num_pwr_levels(struct cfg80211_chan_def *chan_def)
{
if (chan_def->chan->flags & IEEE80211_CHAN_PSD) {
......@@ -7859,33 +7833,23 @@ static void ath11k_mac_parse_tx_pwr_env(struct ath11k *ar,
struct ath11k_base *ab = ar->ab;
struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
struct ieee80211_tx_pwr_env *single_tpe;
struct ieee80211_parsed_tpe_eirp *non_psd = NULL;
struct ieee80211_parsed_tpe_psd *psd = NULL;
enum wmi_reg_6ghz_client_type client_type;
struct cur_regulatory_info *reg_info;
u8 local_tpe_count, reg_tpe_count;
bool use_local_tpe;
int i;
u8 pwr_count, pwr_interpret, pwr_category;
u8 psd_index = 0, non_psd_index = 0, local_tpe_count = 0, reg_tpe_count = 0;
bool use_local_tpe, non_psd_set = false, psd_set = false;
reg_info = &ab->reg_info_store[ar->pdev_idx];
client_type = reg_info->client_type;
for (i = 0; i < bss_conf->tx_pwr_env_num; i++) {
single_tpe = &bss_conf->tx_pwr_env[i];
pwr_category = u8_get_bits(single_tpe->tx_power_info,
IEEE80211_TX_PWR_ENV_INFO_CATEGORY);
pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
if (pwr_category == client_type) {
if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP ||
pwr_interpret == IEEE80211_TPE_LOCAL_EIRP_PSD)
local_tpe_count++;
else if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP ||
pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP_PSD)
reg_tpe_count++;
}
}
local_tpe_count =
bss_conf->tpe.max_local[client_type].valid +
bss_conf->tpe.psd_local[client_type].valid;
reg_tpe_count =
bss_conf->tpe.max_reg_client[client_type].valid +
bss_conf->tpe.psd_reg_client[client_type].valid;
if (!reg_tpe_count && !local_tpe_count) {
ath11k_warn(ab,
......@@ -7898,83 +7862,44 @@ static void ath11k_mac_parse_tx_pwr_env(struct ath11k *ar,
use_local_tpe = false;
}
for (i = 0; i < bss_conf->tx_pwr_env_num; i++) {
single_tpe = &bss_conf->tx_pwr_env[i];
pwr_category = u8_get_bits(single_tpe->tx_power_info,
IEEE80211_TX_PWR_ENV_INFO_CATEGORY);
pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
if (pwr_category != client_type)
continue;
/* get local transmit power envelope */
if (use_local_tpe) {
if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP) {
non_psd_index = i;
non_psd_set = true;
} else if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP_PSD) {
psd_index = i;
psd_set = true;
}
/* get regulatory transmit power envelope */
} else {
if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP) {
non_psd_index = i;
non_psd_set = true;
} else if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP_PSD) {
psd_index = i;
psd_set = true;
}
}
if (use_local_tpe) {
psd = &bss_conf->tpe.psd_local[client_type];
if (!psd->valid)
psd = NULL;
non_psd = &bss_conf->tpe.max_local[client_type];
if (!non_psd->valid)
non_psd = NULL;
} else {
psd = &bss_conf->tpe.psd_reg_client[client_type];
if (!psd->valid)
psd = NULL;
non_psd = &bss_conf->tpe.max_reg_client[client_type];
if (!non_psd->valid)
non_psd = NULL;
}
if (non_psd_set && !psd_set) {
single_tpe = &bss_conf->tx_pwr_env[non_psd_index];
pwr_count = u8_get_bits(single_tpe->tx_power_info,
IEEE80211_TX_PWR_ENV_INFO_COUNT);
pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
if (non_psd && !psd) {
arvif->reg_tpc_info.is_psd_power = false;
arvif->reg_tpc_info.eirp_power = 0;
arvif->reg_tpc_info.num_pwr_levels =
ath11k_mac_get_tpe_count(pwr_interpret, pwr_count);
arvif->reg_tpc_info.num_pwr_levels = non_psd->count;
for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) {
ath11k_dbg(ab, ATH11K_DBG_MAC,
"non PSD power[%d] : %d\n",
i, single_tpe->tx_power[i]);
arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[i] / 2;
i, non_psd->power[i]);
arvif->reg_tpc_info.tpe[i] = non_psd->power[i] / 2;
}
}
if (psd_set) {
single_tpe = &bss_conf->tx_pwr_env[psd_index];
pwr_count = u8_get_bits(single_tpe->tx_power_info,
IEEE80211_TX_PWR_ENV_INFO_COUNT);
pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
arvif->reg_tpc_info.is_psd_power = true;
if (psd) {
arvif->reg_tpc_info.num_pwr_levels = psd->count;
if (pwr_count == 0) {
for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) {
ath11k_dbg(ab, ATH11K_DBG_MAC,
"TPE PSD power : %d\n", single_tpe->tx_power[0]);
arvif->reg_tpc_info.num_pwr_levels =
ath11k_mac_get_num_pwr_levels(&ctx->def);
for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++)
arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[0] / 2;
} else {
arvif->reg_tpc_info.num_pwr_levels =
ath11k_mac_get_tpe_count(pwr_interpret, pwr_count);
for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) {
ath11k_dbg(ab, ATH11K_DBG_MAC,
"TPE PSD power[%d] : %d\n",
i, single_tpe->tx_power[i]);
arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[i] / 2;
}
"TPE PSD power[%d] : %d\n",
i, psd->power[i]);
arvif->reg_tpc_info.tpe[i] = psd->power[i] / 2;
}
}
}
......
......@@ -2825,17 +2825,6 @@ struct ieee80211_he_6ghz_oper {
u8 minrate;
} __packed;
/*
* In "9.4.2.161 Transmit Power Envelope element" of "IEEE Std 802.11ax-2021",
* it show four types in "Table 9-275a-Maximum Transmit Power Interpretation
* subfield encoding", and two category for each type in "Table E-12-Regulatory
* Info subfield encoding in the United States".
* So it it totally max 8 Transmit Power Envelope element.
*/
#define IEEE80211_TPE_MAX_IE_COUNT 8
#define IEEE80211_TPE_MAX_POWER_COUNT 8
/* transmit power interpretation type of transmit power envelope element */
enum ieee80211_tx_power_intrpt_type {
IEEE80211_TPE_LOCAL_EIRP,
......@@ -2844,24 +2833,107 @@ enum ieee80211_tx_power_intrpt_type {
IEEE80211_TPE_REG_CLIENT_EIRP_PSD,
};
/* category type of transmit power envelope element */
enum ieee80211_tx_power_category_6ghz {
IEEE80211_TPE_CAT_6GHZ_DEFAULT = 0,
IEEE80211_TPE_CAT_6GHZ_SUBORDINATE = 1,
};
/*
* For IEEE80211_TPE_LOCAL_EIRP / IEEE80211_TPE_REG_CLIENT_EIRP,
* setting to 63.5 dBm means no constraint.
*/
#define IEEE80211_TPE_MAX_TX_PWR_NO_CONSTRAINT 127
/*
* For IEEE80211_TPE_LOCAL_EIRP_PSD / IEEE80211_TPE_REG_CLIENT_EIRP_PSD,
* setting to 127 indicates no PSD limit for the 20 MHz channel.
*/
#define IEEE80211_TPE_PSD_NO_LIMIT 127
/**
* struct ieee80211_tx_pwr_env - Transmit Power Envelope
* @tx_power_info: Transmit Power Information field
* @tx_power: Maximum Transmit Power field
* @info: Transmit Power Information field
* @variable: Maximum Transmit Power field
*
* This structure represents the payload of the "Transmit Power
* Envelope element" as described in IEEE Std 802.11ax-2021 section
* 9.4.2.161
*/
struct ieee80211_tx_pwr_env {
u8 tx_power_info;
s8 tx_power[IEEE80211_TPE_MAX_POWER_COUNT];
u8 info;
u8 variable[];
} __packed;
#define IEEE80211_TX_PWR_ENV_INFO_COUNT 0x7
#define IEEE80211_TX_PWR_ENV_INFO_INTERPRET 0x38
#define IEEE80211_TX_PWR_ENV_INFO_CATEGORY 0xC0
#define IEEE80211_TX_PWR_ENV_EXT_COUNT 0xF
static inline bool ieee80211_valid_tpe_element(const u8 *data, u8 len)
{
const struct ieee80211_tx_pwr_env *env = (const void *)data;
u8 count, interpret, category;
u8 needed = sizeof(*env);
u8 N; /* also called N in the spec */
if (len < needed)
return false;
count = u8_get_bits(env->info, IEEE80211_TX_PWR_ENV_INFO_COUNT);
interpret = u8_get_bits(env->info, IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
category = u8_get_bits(env->info, IEEE80211_TX_PWR_ENV_INFO_CATEGORY);
switch (category) {
case IEEE80211_TPE_CAT_6GHZ_DEFAULT:
case IEEE80211_TPE_CAT_6GHZ_SUBORDINATE:
break;
default:
return false;
}
switch (interpret) {
case IEEE80211_TPE_LOCAL_EIRP:
case IEEE80211_TPE_REG_CLIENT_EIRP:
if (count > 3)
return false;
/* count == 0 encodes 1 value for 20 MHz, etc. */
needed += count + 1;
if (len < needed)
return false;
/* there can be extension fields not accounted for in 'count' */
return true;
case IEEE80211_TPE_LOCAL_EIRP_PSD:
case IEEE80211_TPE_REG_CLIENT_EIRP_PSD:
if (count > 4)
return false;
N = count ? 1 << (count - 1) : 1;
needed += N;
if (len < needed)
return false;
if (len > needed) {
u8 K = u8_get_bits(env->variable[N],
IEEE80211_TX_PWR_ENV_EXT_COUNT);
needed += 1 + K;
if (len < needed)
return false;
}
return true;
}
return false;
}
/*
* ieee80211_he_oper_size - calculate 802.11ax HE Operations IE size
* @he_oper_ie: byte data of the He Operations IE, stating from the byte
......
......@@ -550,6 +550,39 @@ struct ieee80211_fils_discovery {
u32 max_interval;
};
#define IEEE80211_TPE_EIRP_ENTRIES_320MHZ 5
struct ieee80211_parsed_tpe_eirp {
bool valid;
s8 power[IEEE80211_TPE_EIRP_ENTRIES_320MHZ];
u8 count;
};
#define IEEE80211_TPE_PSD_ENTRIES_320MHZ 16
struct ieee80211_parsed_tpe_psd {
bool valid;
s8 power[IEEE80211_TPE_PSD_ENTRIES_320MHZ];
u8 count, n;
};
/**
* struct ieee80211_parsed_tpe - parsed transmit power envelope information
* @max_local: maximum local EIRP, one value for 20, 40, 80, 160, 320 MHz each
* (indexed by TX power category)
* @max_reg_client: maximum regulatory client EIRP, one value for 20, 40, 80,
* 160, 320 MHz each
* (indexed by TX power category)
* @psd_local: maximum local power spectral density, one value for each 20 MHz
* subchannel per bss_conf's chanreq.oper
* (indexed by TX power category)
* @psd_reg_client: maximum regulatory power spectral density, one value for
* each 20 MHz subchannel per bss_conf's chanreq.oper
* (indexed by TX power category)
*/
struct ieee80211_parsed_tpe {
struct ieee80211_parsed_tpe_eirp max_local[2], max_reg_client[2];
struct ieee80211_parsed_tpe_psd psd_local[2], psd_reg_client[2];
};
/**
* struct ieee80211_bss_conf - holds the BSS's changing parameters
*
......@@ -662,8 +695,7 @@ struct ieee80211_fils_discovery {
* @beacon_tx_rate: The configured beacon transmit rate that needs to be passed
* to driver when rate control is offloaded to firmware.
* @power_type: power type of BSS for 6 GHz
* @tx_pwr_env: transmit power envelope array of BSS.
* @tx_pwr_env_num: number of @tx_pwr_env.
* @tpe: transmit power envelope information
* @pwr_reduction: power constraint of BSS.
* @eht_support: does this BSS support EHT
* @csa_active: marks whether a channel switch is going on.
......@@ -766,8 +798,9 @@ struct ieee80211_bss_conf {
u32 unsol_bcast_probe_resp_interval;
struct cfg80211_bitrate_mask beacon_tx_rate;
enum ieee80211_ap_reg_power power_type;
struct ieee80211_tx_pwr_env tx_pwr_env[IEEE80211_TPE_MAX_IE_COUNT];
u8 tx_pwr_env_num;
struct ieee80211_parsed_tpe tpe;
u8 pwr_reduction;
bool eht_support;
......
......@@ -26,6 +26,7 @@
#include <linux/idr.h>
#include <linux/rhashtable.h>
#include <linux/rbtree.h>
#include <kunit/visibility.h>
#include <net/ieee80211_radiotap.h>
#include <net/cfg80211.h>
#include <net/mac80211.h>
......@@ -1708,7 +1709,6 @@ struct ieee802_11_elems {
const struct ieee80211_he_spr *he_spr;
const struct ieee80211_mu_edca_param_set *mu_edca_param_set;
const struct ieee80211_he_6ghz_capa *he_6ghz_capa;
const struct ieee80211_tx_pwr_env *tx_pwr_env[IEEE80211_TPE_MAX_IE_COUNT];
const u8 *uora_element;
const u8 *mesh_id;
const u8 *peering;
......@@ -1746,6 +1746,9 @@ struct ieee802_11_elems {
const struct ieee80211_bandwidth_indication *bandwidth_indication;
const struct ieee80211_ttlm_elem *ttlm[IEEE80211_TTLM_MAX_CNT];
/* not the order in the psd values is per element, not per chandef */
struct ieee80211_parsed_tpe tpe;
/* length of them, respectively */
u8 ext_capab_len;
u8 ssid_len;
......@@ -1764,8 +1767,6 @@ struct ieee802_11_elems {
u8 perr_len;
u8 country_elem_len;
u8 bssid_index_len;
u8 tx_pwr_env_len[IEEE80211_TPE_MAX_IE_COUNT];
u8 tx_pwr_env_num;
u8 eht_cap_len;
/* mult-link element can be de-fragmented and thus u8 is not sufficient */
......@@ -2243,6 +2244,7 @@ int ieee80211_frame_duration(enum nl80211_band band, size_t len,
void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,
struct ieee80211_tx_queue_params *qparam,
int ac);
void ieee80211_clear_tpe(struct ieee80211_parsed_tpe *tpe);
void ieee80211_set_wmm_default(struct ieee80211_link_data *link,
bool bss_notify, bool enable_qos);
void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
......@@ -2681,6 +2683,11 @@ void ieee80211_remove_wbrf(struct ieee80211_local *local, struct cfg80211_chan_d
#define VISIBLE_IF_MAC80211_KUNIT
ieee80211_rx_result
ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx);
int ieee80211_calc_chandef_subchan_offset(const struct cfg80211_chan_def *ap,
u8 n_partial_subchans);
void ieee80211_rearrange_tpe_psd(struct ieee80211_parsed_tpe_psd *psd,
const struct cfg80211_chan_def *ap,
const struct cfg80211_chan_def *used);
#else
#define EXPORT_SYMBOL_IF_MAC80211_KUNIT(sym)
#define VISIBLE_IF_MAC80211_KUNIT static
......
This diff is collapsed.
......@@ -187,6 +187,84 @@ ieee80211_parse_extension_element(u32 *crc,
*crc = crc32_be(*crc, (void *)elem, elem->datalen + 2);
}
static void ieee80211_parse_tpe(struct ieee80211_parsed_tpe *tpe,
const u8 *data, u8 len)
{
const struct ieee80211_tx_pwr_env *env = (const void *)data;
u8 count, interpret, category;
u8 *out, N, *cnt_out = NULL, *N_out = NULL;
if (!ieee80211_valid_tpe_element(data, len))
return;
count = u8_get_bits(env->info, IEEE80211_TX_PWR_ENV_INFO_COUNT);
interpret = u8_get_bits(env->info, IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
category = u8_get_bits(env->info, IEEE80211_TX_PWR_ENV_INFO_CATEGORY);
switch (interpret) {
case IEEE80211_TPE_LOCAL_EIRP:
out = tpe->max_local[category].power;
cnt_out = &tpe->max_local[category].count;
tpe->max_local[category].valid = true;
break;
case IEEE80211_TPE_REG_CLIENT_EIRP:
out = tpe->max_reg_client[category].power;
cnt_out = &tpe->max_reg_client[category].count;
tpe->max_reg_client[category].valid = true;
break;
case IEEE80211_TPE_LOCAL_EIRP_PSD:
out = tpe->psd_local[category].power;
cnt_out = &tpe->psd_local[category].count;
N_out = &tpe->psd_local[category].n;
tpe->psd_local[category].valid = true;
break;
case IEEE80211_TPE_REG_CLIENT_EIRP_PSD:
out = tpe->psd_reg_client[category].power;
cnt_out = &tpe->psd_reg_client[category].count;
N_out = &tpe->psd_reg_client[category].n;
tpe->psd_reg_client[category].valid = true;
break;
}
switch (interpret) {
case IEEE80211_TPE_LOCAL_EIRP:
case IEEE80211_TPE_REG_CLIENT_EIRP:
/* count was validated <= 3, plus 320 MHz */
BUILD_BUG_ON(IEEE80211_TPE_EIRP_ENTRIES_320MHZ < 5);
memcpy(out, env->variable, count + 1);
*cnt_out = count + 1;
/* separately take 320 MHz if present */
if (count == 3 && len > sizeof(*env) + count + 1) {
out[4] = env->variable[count + 2];
*cnt_out = 5;
}
break;
case IEEE80211_TPE_LOCAL_EIRP_PSD:
case IEEE80211_TPE_REG_CLIENT_EIRP_PSD:
if (!count) {
memset(out, env->variable[0],
IEEE80211_TPE_PSD_ENTRIES_320MHZ);
*cnt_out = IEEE80211_TPE_PSD_ENTRIES_320MHZ;
break;
}
N = 1 << (count - 1);
memcpy(out, env->variable, N);
*cnt_out = N;
*N_out = N;
if (len > sizeof(*env) + N) {
int K = u8_get_bits(env->variable[N],
IEEE80211_TX_PWR_ENV_EXT_COUNT);
K = min(K, IEEE80211_TPE_PSD_ENTRIES_320MHZ - N);
memcpy(out + N, env->variable + N + 1, K);
(*cnt_out) += K;
}
break;
}
}
static u32
_ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
struct ieee80211_elems_parse *elems_parse,
......@@ -593,16 +671,9 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
elems->rsnx_len = elen;
break;
case WLAN_EID_TX_POWER_ENVELOPE:
if (elen < 1 ||
elen > sizeof(struct ieee80211_tx_pwr_env))
break;
if (elems->tx_pwr_env_num >= ARRAY_SIZE(elems->tx_pwr_env))
if (params->mode < IEEE80211_CONN_MODE_HE)
break;
elems->tx_pwr_env[elems->tx_pwr_env_num] = (void *)pos;
elems->tx_pwr_env_len[elems->tx_pwr_env_num] = elen;
elems->tx_pwr_env_num++;
ieee80211_parse_tpe(&elems->tpe, pos, elen);
break;
case WLAN_EID_EXTENSION:
ieee80211_parse_extension_element(calc_crc ?
......@@ -889,6 +960,9 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
elems->ie_start = params->start;
elems->total_len = params->len;
/* set all TPE entries to unlimited (but invalid) */
ieee80211_clear_tpe(&elems->tpe);
nontransmitted_profile = elems_parse->scratch_pos;
nontransmitted_profile_len =
ieee802_11_find_bssid_profile(params->start, params->len,
......
mac80211-tests-y += module.o elems.o mfp.o
mac80211-tests-y += module.o elems.o mfp.o tpe.o
obj-$(CONFIG_MAC80211_KUNIT_TEST) += mac80211-tests.o
// SPDX-License-Identifier: GPL-2.0-only
/*
* KUnit tests for TPE element handling
*
* Copyright (C) 2024 Intel Corporation
*/
#include <kunit/test.h>
#include "../ieee80211_i.h"
MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING);
static struct ieee80211_channel chan6g_1 = {
.band = NL80211_BAND_6GHZ,
.center_freq = 5955,
};
static struct ieee80211_channel chan6g_33 = {
.band = NL80211_BAND_6GHZ,
.center_freq = 6115,
};
static struct ieee80211_channel chan6g_61 = {
.band = NL80211_BAND_6GHZ,
.center_freq = 6255,
};
static const struct subchan_test_case {
const char *desc;
struct cfg80211_chan_def c;
u8 n;
int expect;
} subchan_offset_cases[] = {
{
.desc = "identical 20 MHz",
.c.width = NL80211_CHAN_WIDTH_20,
.c.chan = &chan6g_1,
.c.center_freq1 = 5955,
.n = 1,
.expect = 0,
},
{
.desc = "identical 40 MHz",
.c.width = NL80211_CHAN_WIDTH_40,
.c.chan = &chan6g_1,
.c.center_freq1 = 5965,
.n = 2,
.expect = 0,
},
{
.desc = "identical 80+80 MHz",
/* not really is valid? doesn't matter for the test */
.c.width = NL80211_CHAN_WIDTH_80P80,
.c.chan = &chan6g_1,
.c.center_freq1 = 5985,
.c.center_freq2 = 6225,
.n = 16,
.expect = 0,
},
{
.desc = "identical 320 MHz",
.c.width = NL80211_CHAN_WIDTH_320,
.c.chan = &chan6g_1,
.c.center_freq1 = 6105,
.n = 16,
.expect = 0,
},
{
.desc = "lower 160 MHz of 320 MHz",
.c.width = NL80211_CHAN_WIDTH_320,
.c.chan = &chan6g_1,
.c.center_freq1 = 6105,
.n = 8,
.expect = 0,
},
{
.desc = "upper 160 MHz of 320 MHz",
.c.width = NL80211_CHAN_WIDTH_320,
.c.chan = &chan6g_61,
.c.center_freq1 = 6105,
.n = 8,
.expect = 8,
},
{
.desc = "upper 160 MHz of 320 MHz, go to 40",
.c.width = NL80211_CHAN_WIDTH_320,
.c.chan = &chan6g_61,
.c.center_freq1 = 6105,
.n = 2,
.expect = 8 + 4 + 2,
},
{
.desc = "secondary 80 above primary in 80+80 MHz",
/* not really is valid? doesn't matter for the test */
.c.width = NL80211_CHAN_WIDTH_80P80,
.c.chan = &chan6g_1,
.c.center_freq1 = 5985,
.c.center_freq2 = 6225,
.n = 4,
.expect = 0,
},
{
.desc = "secondary 80 below primary in 80+80 MHz",
/* not really is valid? doesn't matter for the test */
.c.width = NL80211_CHAN_WIDTH_80P80,
.c.chan = &chan6g_61,
.c.center_freq1 = 6225,
.c.center_freq2 = 5985,
.n = 4,
.expect = 4,
},
{
.desc = "secondary 80 below primary in 80+80 MHz, go to 20",
/* not really is valid? doesn't matter for the test */
.c.width = NL80211_CHAN_WIDTH_80P80,
.c.chan = &chan6g_61,
.c.center_freq1 = 6225,
.c.center_freq2 = 5985,
.n = 1,
.expect = 7,
},
};
KUNIT_ARRAY_PARAM_DESC(subchan_offset, subchan_offset_cases, desc);
static void subchan_offset(struct kunit *test)
{
const struct subchan_test_case *params = test->param_value;
int offset;
KUNIT_ASSERT_EQ(test, cfg80211_chandef_valid(&params->c), true);
offset = ieee80211_calc_chandef_subchan_offset(&params->c, params->n);
KUNIT_EXPECT_EQ(test, params->expect, offset);
}
static const struct psd_reorder_test_case {
const char *desc;
struct cfg80211_chan_def ap, used;
struct ieee80211_parsed_tpe_psd psd, out;
} psd_reorder_cases[] = {
{
.desc = "no changes, 320 MHz",
.ap.width = NL80211_CHAN_WIDTH_320,
.ap.chan = &chan6g_1,
.ap.center_freq1 = 6105,
.used.width = NL80211_CHAN_WIDTH_320,
.used.chan = &chan6g_1,
.used.center_freq1 = 6105,
.psd.valid = true,
.psd.count = 16,
.psd.n = 8,
.psd.power = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
.out.valid = true,
.out.count = 16,
.out.n = 8,
.out.power = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
},
{
.desc = "no changes, 320 MHz, 160 MHz used, n=0",
.ap.width = NL80211_CHAN_WIDTH_320,
.ap.chan = &chan6g_1,
.ap.center_freq1 = 6105,
.used.width = NL80211_CHAN_WIDTH_160,
.used.chan = &chan6g_1,
.used.center_freq1 = 6025,
.psd.valid = true,
.psd.count = 16,
.psd.n = 0,
.psd.power = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, },
.out.valid = true,
.out.count = 8,
.out.n = 0,
.out.power = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, },
},
{
.desc = "320 MHz, HE is 80, used 160, all lower",
.ap.width = NL80211_CHAN_WIDTH_320,
.ap.chan = &chan6g_1,
.ap.center_freq1 = 6105,
.used.width = NL80211_CHAN_WIDTH_160,
.used.chan = &chan6g_1,
.used.center_freq1 = 6025,
.psd.valid = true,
.psd.count = 16,
.psd.n = 4,
.psd.power = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
.out.valid = true,
.out.count = 8,
.out.n = 4,
.out.power = { 0, 1, 2, 3, 4, 5, 6, 7, 127, 127, 127, 127, 127, 127, 127, 127},
},
{
.desc = "320 MHz, HE is 80, used 160, all upper",
/*
* EHT: | | | | | | | | | | | | | | | | |
* HE: | | | | |
* used: | | | | | | | | |
*/
.ap.width = NL80211_CHAN_WIDTH_320,
.ap.chan = &chan6g_61,
.ap.center_freq1 = 6105,
.used.width = NL80211_CHAN_WIDTH_160,
.used.chan = &chan6g_61,
.used.center_freq1 = 6185,
.psd.valid = true,
.psd.count = 16,
.psd.n = 4,
.psd.power = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
.out.valid = true,
.out.count = 8,
.out.n = 4,
.out.power = { 12, 13, 14, 15, 0, 1, 2, 3, 127, 127, 127, 127, 127, 127, 127, 127},
},
{
.desc = "320 MHz, HE is 80, used 160, split",
/*
* EHT: | | | | | | | | | | | | | | | | |
* HE: | | | | |
* used: | | | | | | | | |
*/
.ap.width = NL80211_CHAN_WIDTH_320,
.ap.chan = &chan6g_33,
.ap.center_freq1 = 6105,
.used.width = NL80211_CHAN_WIDTH_160,
.used.chan = &chan6g_33,
.used.center_freq1 = 6185,
.psd.valid = true,
.psd.count = 16,
.psd.n = 4,
.psd.power = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
.out.valid = true,
.out.count = 8,
.out.n = 4,
.out.power = { 0, 1, 2, 3, 12, 13, 14, 15, 127, 127, 127, 127, 127, 127, 127, 127},
},
};
KUNIT_ARRAY_PARAM_DESC(psd_reorder, psd_reorder_cases, desc);
static void psd_reorder(struct kunit *test)
{
const struct psd_reorder_test_case *params = test->param_value;
struct ieee80211_parsed_tpe_psd tmp = params->psd;
KUNIT_ASSERT_EQ(test, cfg80211_chandef_valid(&params->ap), true);
KUNIT_ASSERT_EQ(test, cfg80211_chandef_valid(&params->used), true);
ieee80211_rearrange_tpe_psd(&tmp, &params->ap, &params->used);
KUNIT_EXPECT_MEMEQ(test, &tmp, &params->out, sizeof(tmp));
}
static struct kunit_case tpe_test_cases[] = {
KUNIT_CASE_PARAM(subchan_offset, subchan_offset_gen_params),
KUNIT_CASE_PARAM(psd_reorder, psd_reorder_gen_params),
{}
};
static struct kunit_suite tpe = {
.name = "mac80211-tpe",
.test_cases = tpe_test_cases,
};
kunit_test_suite(tpe);
......@@ -4334,3 +4334,28 @@ ieee80211_min_bw_limit_from_chandef(struct cfg80211_chan_def *chandef)
return IEEE80211_CONN_BW_LIMIT_20;
}
}
void ieee80211_clear_tpe(struct ieee80211_parsed_tpe *tpe)
{
for (int i = 0; i < 2; i++) {
tpe->max_local[i].valid = false;
memset(tpe->max_local[i].power,
IEEE80211_TPE_MAX_TX_PWR_NO_CONSTRAINT,
sizeof(tpe->max_local[i].power));
tpe->max_reg_client[i].valid = false;
memset(tpe->max_reg_client[i].power,
IEEE80211_TPE_MAX_TX_PWR_NO_CONSTRAINT,
sizeof(tpe->max_reg_client[i].power));
tpe->psd_local[i].valid = false;
memset(tpe->psd_local[i].power,
IEEE80211_TPE_PSD_NO_LIMIT,
sizeof(tpe->psd_local[i].power));
tpe->psd_reg_client[i].valid = false;
memset(tpe->psd_reg_client[i].power,
IEEE80211_TPE_PSD_NO_LIMIT,
sizeof(tpe->psd_reg_client[i].power));
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment