162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2021-2022 Intel Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <net/mac80211.h> 762306a36Sopenharmony_ci#include "fw/api/rs.h" 862306a36Sopenharmony_ci#include "iwl-drv.h" 962306a36Sopenharmony_ci#include "iwl-config.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define IWL_DECLARE_RATE_INFO(r) \ 1262306a36Sopenharmony_ci [IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP 1662306a36Sopenharmony_ci * */ 1762306a36Sopenharmony_cistatic const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = { 1862306a36Sopenharmony_ci IWL_DECLARE_RATE_INFO(1), 1962306a36Sopenharmony_ci IWL_DECLARE_RATE_INFO(2), 2062306a36Sopenharmony_ci IWL_DECLARE_RATE_INFO(5), 2162306a36Sopenharmony_ci IWL_DECLARE_RATE_INFO(11), 2262306a36Sopenharmony_ci IWL_DECLARE_RATE_INFO(6), 2362306a36Sopenharmony_ci IWL_DECLARE_RATE_INFO(9), 2462306a36Sopenharmony_ci IWL_DECLARE_RATE_INFO(12), 2562306a36Sopenharmony_ci IWL_DECLARE_RATE_INFO(18), 2662306a36Sopenharmony_ci IWL_DECLARE_RATE_INFO(24), 2762306a36Sopenharmony_ci IWL_DECLARE_RATE_INFO(36), 2862306a36Sopenharmony_ci IWL_DECLARE_RATE_INFO(48), 2962306a36Sopenharmony_ci IWL_DECLARE_RATE_INFO(54), 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* mbps, mcs */ 3362306a36Sopenharmony_cistatic const struct iwl_rate_mcs_info rate_mcs[IWL_RATE_COUNT] = { 3462306a36Sopenharmony_ci { "1", "BPSK DSSS"}, 3562306a36Sopenharmony_ci { "2", "QPSK DSSS"}, 3662306a36Sopenharmony_ci {"5.5", "BPSK CCK"}, 3762306a36Sopenharmony_ci { "11", "QPSK CCK"}, 3862306a36Sopenharmony_ci { "6", "BPSK 1/2"}, 3962306a36Sopenharmony_ci { "9", "BPSK 1/2"}, 4062306a36Sopenharmony_ci { "12", "QPSK 1/2"}, 4162306a36Sopenharmony_ci { "18", "QPSK 3/4"}, 4262306a36Sopenharmony_ci { "24", "16QAM 1/2"}, 4362306a36Sopenharmony_ci { "36", "16QAM 3/4"}, 4462306a36Sopenharmony_ci { "48", "64QAM 2/3"}, 4562306a36Sopenharmony_ci { "54", "64QAM 3/4"}, 4662306a36Sopenharmony_ci { "60", "64QAM 5/6"}, 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic const char * const ant_name[] = { 5062306a36Sopenharmony_ci [ANT_NONE] = "None", 5162306a36Sopenharmony_ci [ANT_A] = "A", 5262306a36Sopenharmony_ci [ANT_B] = "B", 5362306a36Sopenharmony_ci [ANT_AB] = "AB", 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic const char * const pretty_bw[] = { 5762306a36Sopenharmony_ci "20Mhz", 5862306a36Sopenharmony_ci "40Mhz", 5962306a36Sopenharmony_ci "80Mhz", 6062306a36Sopenharmony_ci "160 Mhz", 6162306a36Sopenharmony_ci "320Mhz", 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ciu8 iwl_fw_rate_idx_to_plcp(int idx) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci return fw_rate_idx_to_plcp[idx]; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_fw_rate_idx_to_plcp); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ciconst struct iwl_rate_mcs_info *iwl_rate_mcs(int idx) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci return &rate_mcs[idx]; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_rate_mcs); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ciconst char *iwl_rs_pretty_ant(u8 ant) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci if (ant >= ARRAY_SIZE(ant_name)) 7962306a36Sopenharmony_ci return "UNKNOWN"; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return ant_name[ant]; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_rs_pretty_ant); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciconst char *iwl_rs_pretty_bw(int bw) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci if (bw >= ARRAY_SIZE(pretty_bw)) 8862306a36Sopenharmony_ci return "unknown bw"; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return pretty_bw[bw]; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_rs_pretty_bw); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic u32 iwl_legacy_rate_to_fw_idx(u32 rate_n_flags) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci int rate = rate_n_flags & RATE_LEGACY_RATE_MSK_V1; 9762306a36Sopenharmony_ci int idx; 9862306a36Sopenharmony_ci bool ofdm = !(rate_n_flags & RATE_MCS_CCK_MSK_V1); 9962306a36Sopenharmony_ci int offset = ofdm ? IWL_FIRST_OFDM_RATE : 0; 10062306a36Sopenharmony_ci int last = ofdm ? IWL_RATE_COUNT_LEGACY : IWL_FIRST_OFDM_RATE; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci for (idx = offset; idx < last; idx++) 10362306a36Sopenharmony_ci if (iwl_fw_rate_idx_to_plcp(idx) == rate) 10462306a36Sopenharmony_ci return idx - offset; 10562306a36Sopenharmony_ci return IWL_RATE_INVALID; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ciu32 iwl_new_rate_from_v1(u32 rate_v1) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci u32 rate_v2 = 0; 11162306a36Sopenharmony_ci u32 dup = 0; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (rate_v1 == 0) 11462306a36Sopenharmony_ci return rate_v1; 11562306a36Sopenharmony_ci /* convert rate */ 11662306a36Sopenharmony_ci if (rate_v1 & RATE_MCS_HT_MSK_V1) { 11762306a36Sopenharmony_ci u32 nss = 0; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci rate_v2 |= RATE_MCS_HT_MSK; 12062306a36Sopenharmony_ci rate_v2 |= 12162306a36Sopenharmony_ci rate_v1 & RATE_HT_MCS_RATE_CODE_MSK_V1; 12262306a36Sopenharmony_ci nss = (rate_v1 & RATE_HT_MCS_MIMO2_MSK) >> 12362306a36Sopenharmony_ci RATE_HT_MCS_NSS_POS_V1; 12462306a36Sopenharmony_ci rate_v2 |= nss << RATE_MCS_NSS_POS; 12562306a36Sopenharmony_ci } else if (rate_v1 & RATE_MCS_VHT_MSK_V1 || 12662306a36Sopenharmony_ci rate_v1 & RATE_MCS_HE_MSK_V1) { 12762306a36Sopenharmony_ci rate_v2 |= rate_v1 & RATE_VHT_MCS_RATE_CODE_MSK; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci rate_v2 |= rate_v1 & RATE_MCS_NSS_MSK; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (rate_v1 & RATE_MCS_HE_MSK_V1) { 13262306a36Sopenharmony_ci u32 he_type_bits = rate_v1 & RATE_MCS_HE_TYPE_MSK_V1; 13362306a36Sopenharmony_ci u32 he_type = he_type_bits >> RATE_MCS_HE_TYPE_POS_V1; 13462306a36Sopenharmony_ci u32 he_106t = (rate_v1 & RATE_MCS_HE_106T_MSK_V1) >> 13562306a36Sopenharmony_ci RATE_MCS_HE_106T_POS_V1; 13662306a36Sopenharmony_ci u32 he_gi_ltf = (rate_v1 & RATE_MCS_HE_GI_LTF_MSK_V1) >> 13762306a36Sopenharmony_ci RATE_MCS_HE_GI_LTF_POS; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if ((he_type_bits == RATE_MCS_HE_TYPE_SU || 14062306a36Sopenharmony_ci he_type_bits == RATE_MCS_HE_TYPE_EXT_SU) && 14162306a36Sopenharmony_ci he_gi_ltf == RATE_MCS_HE_SU_4_LTF) 14262306a36Sopenharmony_ci /* the new rate have an additional bit to 14362306a36Sopenharmony_ci * represent the value 4 rather then using SGI 14462306a36Sopenharmony_ci * bit for this purpose - as it was done in the old 14562306a36Sopenharmony_ci * rate */ 14662306a36Sopenharmony_ci he_gi_ltf += (rate_v1 & RATE_MCS_SGI_MSK_V1) >> 14762306a36Sopenharmony_ci RATE_MCS_SGI_POS_V1; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci rate_v2 |= he_gi_ltf << RATE_MCS_HE_GI_LTF_POS; 15062306a36Sopenharmony_ci rate_v2 |= he_type << RATE_MCS_HE_TYPE_POS; 15162306a36Sopenharmony_ci rate_v2 |= he_106t << RATE_MCS_HE_106T_POS; 15262306a36Sopenharmony_ci rate_v2 |= rate_v1 & RATE_HE_DUAL_CARRIER_MODE_MSK; 15362306a36Sopenharmony_ci rate_v2 |= RATE_MCS_HE_MSK; 15462306a36Sopenharmony_ci } else { 15562306a36Sopenharmony_ci rate_v2 |= RATE_MCS_VHT_MSK; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci /* if legacy format */ 15862306a36Sopenharmony_ci } else { 15962306a36Sopenharmony_ci u32 legacy_rate = iwl_legacy_rate_to_fw_idx(rate_v1); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (WARN_ON_ONCE(legacy_rate == IWL_RATE_INVALID)) 16262306a36Sopenharmony_ci legacy_rate = (rate_v1 & RATE_MCS_CCK_MSK_V1) ? 16362306a36Sopenharmony_ci IWL_FIRST_CCK_RATE : IWL_FIRST_OFDM_RATE; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci rate_v2 |= legacy_rate; 16662306a36Sopenharmony_ci if (!(rate_v1 & RATE_MCS_CCK_MSK_V1)) 16762306a36Sopenharmony_ci rate_v2 |= RATE_MCS_LEGACY_OFDM_MSK; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* convert flags */ 17162306a36Sopenharmony_ci if (rate_v1 & RATE_MCS_LDPC_MSK_V1) 17262306a36Sopenharmony_ci rate_v2 |= RATE_MCS_LDPC_MSK; 17362306a36Sopenharmony_ci rate_v2 |= (rate_v1 & RATE_MCS_CHAN_WIDTH_MSK_V1) | 17462306a36Sopenharmony_ci (rate_v1 & RATE_MCS_ANT_AB_MSK) | 17562306a36Sopenharmony_ci (rate_v1 & RATE_MCS_STBC_MSK) | 17662306a36Sopenharmony_ci (rate_v1 & RATE_MCS_BF_MSK); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci dup = (rate_v1 & RATE_MCS_DUP_MSK_V1) >> RATE_MCS_DUP_POS_V1; 17962306a36Sopenharmony_ci if (dup) { 18062306a36Sopenharmony_ci rate_v2 |= RATE_MCS_DUP_MSK; 18162306a36Sopenharmony_ci rate_v2 |= dup << RATE_MCS_CHAN_WIDTH_POS; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if ((!(rate_v1 & RATE_MCS_HE_MSK_V1)) && 18562306a36Sopenharmony_ci (rate_v1 & RATE_MCS_SGI_MSK_V1)) 18662306a36Sopenharmony_ci rate_v2 |= RATE_MCS_SGI_MSK; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return rate_v2; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_new_rate_from_v1); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ciint rs_pretty_print_rate(char *buf, int bufsz, const u32 rate) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci char *type; 19562306a36Sopenharmony_ci u8 mcs = 0, nss = 0; 19662306a36Sopenharmony_ci u8 ant = (rate & RATE_MCS_ANT_AB_MSK) >> RATE_MCS_ANT_POS; 19762306a36Sopenharmony_ci u32 bw = (rate & RATE_MCS_CHAN_WIDTH_MSK) >> 19862306a36Sopenharmony_ci RATE_MCS_CHAN_WIDTH_POS; 19962306a36Sopenharmony_ci u32 format = rate & RATE_MCS_MOD_TYPE_MSK; 20062306a36Sopenharmony_ci bool sgi; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (format == RATE_MCS_CCK_MSK || 20362306a36Sopenharmony_ci format == RATE_MCS_LEGACY_OFDM_MSK) { 20462306a36Sopenharmony_ci int legacy_rate = rate & RATE_LEGACY_RATE_MSK; 20562306a36Sopenharmony_ci int index = format == RATE_MCS_CCK_MSK ? 20662306a36Sopenharmony_ci legacy_rate : 20762306a36Sopenharmony_ci legacy_rate + IWL_FIRST_OFDM_RATE; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return scnprintf(buf, bufsz, "Legacy | ANT: %s Rate: %s Mbps", 21062306a36Sopenharmony_ci iwl_rs_pretty_ant(ant), 21162306a36Sopenharmony_ci index == IWL_RATE_INVALID ? "BAD" : 21262306a36Sopenharmony_ci iwl_rate_mcs(index)->mbps); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (format == RATE_MCS_VHT_MSK) 21662306a36Sopenharmony_ci type = "VHT"; 21762306a36Sopenharmony_ci else if (format == RATE_MCS_HT_MSK) 21862306a36Sopenharmony_ci type = "HT"; 21962306a36Sopenharmony_ci else if (format == RATE_MCS_HE_MSK) 22062306a36Sopenharmony_ci type = "HE"; 22162306a36Sopenharmony_ci else if (format == RATE_MCS_EHT_MSK) 22262306a36Sopenharmony_ci type = "EHT"; 22362306a36Sopenharmony_ci else 22462306a36Sopenharmony_ci type = "Unknown"; /* shouldn't happen */ 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci mcs = format == RATE_MCS_HT_MSK ? 22762306a36Sopenharmony_ci RATE_HT_MCS_INDEX(rate) : 22862306a36Sopenharmony_ci rate & RATE_MCS_CODE_MSK; 22962306a36Sopenharmony_ci nss = ((rate & RATE_MCS_NSS_MSK) 23062306a36Sopenharmony_ci >> RATE_MCS_NSS_POS) + 1; 23162306a36Sopenharmony_ci sgi = format == RATE_MCS_HE_MSK ? 23262306a36Sopenharmony_ci iwl_he_is_sgi(rate) : 23362306a36Sopenharmony_ci rate & RATE_MCS_SGI_MSK; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return scnprintf(buf, bufsz, 23662306a36Sopenharmony_ci "0x%x: %s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s%s", 23762306a36Sopenharmony_ci rate, type, iwl_rs_pretty_ant(ant), iwl_rs_pretty_bw(bw), mcs, nss, 23862306a36Sopenharmony_ci (sgi) ? "SGI " : "NGI ", 23962306a36Sopenharmony_ci (rate & RATE_MCS_STBC_MSK) ? "STBC " : "", 24062306a36Sopenharmony_ci (rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "", 24162306a36Sopenharmony_ci (rate & RATE_HE_DUAL_CARRIER_MODE_MSK) ? "DCM " : "", 24262306a36Sopenharmony_ci (rate & RATE_MCS_BF_MSK) ? "BF " : ""); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(rs_pretty_print_rate); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cibool iwl_he_is_sgi(u32 rate_n_flags) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci u32 type = rate_n_flags & RATE_MCS_HE_TYPE_MSK; 24962306a36Sopenharmony_ci u32 ltf_gi = rate_n_flags & RATE_MCS_HE_GI_LTF_MSK; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (type == RATE_MCS_HE_TYPE_SU || 25262306a36Sopenharmony_ci type == RATE_MCS_HE_TYPE_EXT_SU) 25362306a36Sopenharmony_ci return ltf_gi == RATE_MCS_HE_SU_4_LTF_08_GI; 25462306a36Sopenharmony_ci return false; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_he_is_sgi); 25762306a36Sopenharmony_ci 258