258 lines
6.6 KiB
C
258 lines
6.6 KiB
C
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
|
/*
|
|
* Copyright (C) 2021 Intel Corporation
|
|
*/
|
|
|
|
#include <net/mac80211.h>
|
|
#include "fw/api/rs.h"
|
|
#include "iwl-drv.h"
|
|
#include "iwl-config.h"
|
|
|
|
#define IWL_DECLARE_RATE_INFO(r) \
|
|
[IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP
|
|
|
|
/*
|
|
* Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP
|
|
* */
|
|
static const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = {
|
|
IWL_DECLARE_RATE_INFO(1),
|
|
IWL_DECLARE_RATE_INFO(2),
|
|
IWL_DECLARE_RATE_INFO(5),
|
|
IWL_DECLARE_RATE_INFO(11),
|
|
IWL_DECLARE_RATE_INFO(6),
|
|
IWL_DECLARE_RATE_INFO(9),
|
|
IWL_DECLARE_RATE_INFO(12),
|
|
IWL_DECLARE_RATE_INFO(18),
|
|
IWL_DECLARE_RATE_INFO(24),
|
|
IWL_DECLARE_RATE_INFO(36),
|
|
IWL_DECLARE_RATE_INFO(48),
|
|
IWL_DECLARE_RATE_INFO(54),
|
|
};
|
|
|
|
/* mbps, mcs */
|
|
static const struct iwl_rate_mcs_info rate_mcs[IWL_RATE_COUNT] = {
|
|
{ "1", "BPSK DSSS"},
|
|
{ "2", "QPSK DSSS"},
|
|
{"5.5", "BPSK CCK"},
|
|
{ "11", "QPSK CCK"},
|
|
{ "6", "BPSK 1/2"},
|
|
{ "9", "BPSK 1/2"},
|
|
{ "12", "QPSK 1/2"},
|
|
{ "18", "QPSK 3/4"},
|
|
{ "24", "16QAM 1/2"},
|
|
{ "36", "16QAM 3/4"},
|
|
{ "48", "64QAM 2/3"},
|
|
{ "54", "64QAM 3/4"},
|
|
{ "60", "64QAM 5/6"},
|
|
};
|
|
|
|
static const char * const ant_name[] = {
|
|
[ANT_NONE] = "None",
|
|
[ANT_A] = "A",
|
|
[ANT_B] = "B",
|
|
[ANT_AB] = "AB",
|
|
};
|
|
|
|
static const char * const pretty_bw[] = {
|
|
"20Mhz",
|
|
"40Mhz",
|
|
"80Mhz",
|
|
"160 Mhz",
|
|
"320Mhz",
|
|
};
|
|
|
|
u8 iwl_fw_rate_idx_to_plcp(int idx)
|
|
{
|
|
return fw_rate_idx_to_plcp[idx];
|
|
}
|
|
IWL_EXPORT_SYMBOL(iwl_fw_rate_idx_to_plcp);
|
|
|
|
const struct iwl_rate_mcs_info *iwl_rate_mcs(int idx)
|
|
{
|
|
return &rate_mcs[idx];
|
|
}
|
|
IWL_EXPORT_SYMBOL(iwl_rate_mcs);
|
|
|
|
const char *iwl_rs_pretty_ant(u8 ant)
|
|
{
|
|
if (ant >= ARRAY_SIZE(ant_name))
|
|
return "UNKNOWN";
|
|
|
|
return ant_name[ant];
|
|
}
|
|
IWL_EXPORT_SYMBOL(iwl_rs_pretty_ant);
|
|
|
|
const char *iwl_rs_pretty_bw(int bw)
|
|
{
|
|
if (bw >= ARRAY_SIZE(pretty_bw))
|
|
return "unknown bw";
|
|
|
|
return pretty_bw[bw];
|
|
}
|
|
IWL_EXPORT_SYMBOL(iwl_rs_pretty_bw);
|
|
|
|
static u32 iwl_legacy_rate_to_fw_idx(u32 rate_n_flags)
|
|
{
|
|
int rate = rate_n_flags & RATE_LEGACY_RATE_MSK_V1;
|
|
int idx;
|
|
bool ofdm = !(rate_n_flags & RATE_MCS_CCK_MSK_V1);
|
|
int offset = ofdm ? IWL_FIRST_OFDM_RATE : 0;
|
|
int last = ofdm ? IWL_RATE_COUNT_LEGACY : IWL_FIRST_OFDM_RATE;
|
|
|
|
for (idx = offset; idx < last; idx++)
|
|
if (iwl_fw_rate_idx_to_plcp(idx) == rate)
|
|
return idx - offset;
|
|
return IWL_RATE_INVALID;
|
|
}
|
|
|
|
u32 iwl_new_rate_from_v1(u32 rate_v1)
|
|
{
|
|
u32 rate_v2 = 0;
|
|
u32 dup = 0;
|
|
|
|
if (rate_v1 == 0)
|
|
return rate_v1;
|
|
/* convert rate */
|
|
if (rate_v1 & RATE_MCS_HT_MSK_V1) {
|
|
u32 nss = 0;
|
|
|
|
rate_v2 |= RATE_MCS_HT_MSK;
|
|
rate_v2 |=
|
|
rate_v1 & RATE_HT_MCS_RATE_CODE_MSK_V1;
|
|
nss = (rate_v1 & RATE_HT_MCS_MIMO2_MSK) >>
|
|
RATE_HT_MCS_NSS_POS_V1;
|
|
rate_v2 |= nss << RATE_MCS_NSS_POS;
|
|
} else if (rate_v1 & RATE_MCS_VHT_MSK_V1 ||
|
|
rate_v1 & RATE_MCS_HE_MSK_V1) {
|
|
rate_v2 |= rate_v1 & RATE_VHT_MCS_RATE_CODE_MSK;
|
|
|
|
rate_v2 |= rate_v1 & RATE_VHT_MCS_MIMO2_MSK;
|
|
|
|
if (rate_v1 & RATE_MCS_HE_MSK_V1) {
|
|
u32 he_type_bits = rate_v1 & RATE_MCS_HE_TYPE_MSK_V1;
|
|
u32 he_type = he_type_bits >> RATE_MCS_HE_TYPE_POS_V1;
|
|
u32 he_106t = (rate_v1 & RATE_MCS_HE_106T_MSK_V1) >>
|
|
RATE_MCS_HE_106T_POS_V1;
|
|
u32 he_gi_ltf = (rate_v1 & RATE_MCS_HE_GI_LTF_MSK_V1) >>
|
|
RATE_MCS_HE_GI_LTF_POS;
|
|
|
|
if ((he_type_bits == RATE_MCS_HE_TYPE_SU ||
|
|
he_type_bits == RATE_MCS_HE_TYPE_EXT_SU) &&
|
|
he_gi_ltf == RATE_MCS_HE_SU_4_LTF)
|
|
/* the new rate have an additional bit to
|
|
* represent the value 4 rather then using SGI
|
|
* bit for this purpose - as it was done in the old
|
|
* rate */
|
|
he_gi_ltf += (rate_v1 & RATE_MCS_SGI_MSK_V1) >>
|
|
RATE_MCS_SGI_POS_V1;
|
|
|
|
rate_v2 |= he_gi_ltf << RATE_MCS_HE_GI_LTF_POS;
|
|
rate_v2 |= he_type << RATE_MCS_HE_TYPE_POS;
|
|
rate_v2 |= he_106t << RATE_MCS_HE_106T_POS;
|
|
rate_v2 |= rate_v1 & RATE_HE_DUAL_CARRIER_MODE_MSK;
|
|
rate_v2 |= RATE_MCS_HE_MSK;
|
|
} else {
|
|
rate_v2 |= RATE_MCS_VHT_MSK;
|
|
}
|
|
/* if legacy format */
|
|
} else {
|
|
u32 legacy_rate = iwl_legacy_rate_to_fw_idx(rate_v1);
|
|
|
|
if (WARN_ON_ONCE(legacy_rate == IWL_RATE_INVALID))
|
|
legacy_rate = (rate_v1 & RATE_MCS_CCK_MSK_V1) ?
|
|
IWL_FIRST_CCK_RATE : IWL_FIRST_OFDM_RATE;
|
|
|
|
rate_v2 |= legacy_rate;
|
|
if (!(rate_v1 & RATE_MCS_CCK_MSK_V1))
|
|
rate_v2 |= RATE_MCS_LEGACY_OFDM_MSK;
|
|
}
|
|
|
|
/* convert flags */
|
|
if (rate_v1 & RATE_MCS_LDPC_MSK_V1)
|
|
rate_v2 |= RATE_MCS_LDPC_MSK;
|
|
rate_v2 |= (rate_v1 & RATE_MCS_CHAN_WIDTH_MSK_V1) |
|
|
(rate_v1 & RATE_MCS_ANT_AB_MSK) |
|
|
(rate_v1 & RATE_MCS_STBC_MSK) |
|
|
(rate_v1 & RATE_MCS_BF_MSK);
|
|
|
|
dup = (rate_v1 & RATE_MCS_DUP_MSK_V1) >> RATE_MCS_DUP_POS_V1;
|
|
if (dup) {
|
|
rate_v2 |= RATE_MCS_DUP_MSK;
|
|
rate_v2 |= dup << RATE_MCS_CHAN_WIDTH_POS;
|
|
}
|
|
|
|
if ((!(rate_v1 & RATE_MCS_HE_MSK_V1)) &&
|
|
(rate_v1 & RATE_MCS_SGI_MSK_V1))
|
|
rate_v2 |= RATE_MCS_SGI_MSK;
|
|
|
|
return rate_v2;
|
|
}
|
|
IWL_EXPORT_SYMBOL(iwl_new_rate_from_v1);
|
|
|
|
int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate)
|
|
{
|
|
char *type;
|
|
u8 mcs = 0, nss = 0;
|
|
u8 ant = (rate & RATE_MCS_ANT_AB_MSK) >> RATE_MCS_ANT_POS;
|
|
u32 bw = (rate & RATE_MCS_CHAN_WIDTH_MSK) >>
|
|
RATE_MCS_CHAN_WIDTH_POS;
|
|
u32 format = rate & RATE_MCS_MOD_TYPE_MSK;
|
|
bool sgi;
|
|
|
|
if (format == RATE_MCS_CCK_MSK ||
|
|
format == RATE_MCS_LEGACY_OFDM_MSK) {
|
|
int legacy_rate = rate & RATE_LEGACY_RATE_MSK;
|
|
int index = format == RATE_MCS_CCK_MSK ?
|
|
legacy_rate :
|
|
legacy_rate + IWL_FIRST_OFDM_RATE;
|
|
|
|
return scnprintf(buf, bufsz, "Legacy | ANT: %s Rate: %s Mbps",
|
|
iwl_rs_pretty_ant(ant),
|
|
index == IWL_RATE_INVALID ? "BAD" :
|
|
iwl_rate_mcs(index)->mbps);
|
|
}
|
|
|
|
if (format == RATE_MCS_VHT_MSK)
|
|
type = "VHT";
|
|
else if (format == RATE_MCS_HT_MSK)
|
|
type = "HT";
|
|
else if (format == RATE_MCS_HE_MSK)
|
|
type = "HE";
|
|
else if (format == RATE_MCS_EHT_MSK)
|
|
type = "EHT";
|
|
else
|
|
type = "Unknown"; /* shouldn't happen */
|
|
|
|
mcs = format == RATE_MCS_HT_MSK ?
|
|
RATE_HT_MCS_INDEX(rate) :
|
|
rate & RATE_MCS_CODE_MSK;
|
|
nss = ((rate & RATE_MCS_NSS_MSK)
|
|
>> RATE_MCS_NSS_POS) + 1;
|
|
sgi = format == RATE_MCS_HE_MSK ?
|
|
iwl_he_is_sgi(rate) :
|
|
rate & RATE_MCS_SGI_MSK;
|
|
|
|
return scnprintf(buf, bufsz,
|
|
"0x%x: %s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s%s",
|
|
rate, type, iwl_rs_pretty_ant(ant), iwl_rs_pretty_bw(bw), mcs, nss,
|
|
(sgi) ? "SGI " : "NGI ",
|
|
(rate & RATE_MCS_STBC_MSK) ? "STBC " : "",
|
|
(rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "",
|
|
(rate & RATE_HE_DUAL_CARRIER_MODE_MSK) ? "DCM " : "",
|
|
(rate & RATE_MCS_BF_MSK) ? "BF " : "");
|
|
}
|
|
IWL_EXPORT_SYMBOL(rs_pretty_print_rate);
|
|
|
|
bool iwl_he_is_sgi(u32 rate_n_flags)
|
|
{
|
|
u32 type = rate_n_flags & RATE_MCS_HE_TYPE_MSK;
|
|
u32 ltf_gi = rate_n_flags & RATE_MCS_HE_GI_LTF_MSK;
|
|
|
|
if (type == RATE_MCS_HE_TYPE_SU ||
|
|
type == RATE_MCS_HE_TYPE_EXT_SU)
|
|
return ltf_gi == RATE_MCS_HE_SU_4_LTF_08_GI;
|
|
return false;
|
|
}
|
|
IWL_EXPORT_SYMBOL(iwl_he_is_sgi);
|
|
|