1bfcc09ddSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2bfcc09ddSBjoern A. Zeeb /* 3*9af1bba4SBjoern A. Zeeb * Copyright (C) 2021-2022 Intel Corporation 4bfcc09ddSBjoern A. Zeeb */ 5bfcc09ddSBjoern A. Zeeb 6bfcc09ddSBjoern A. Zeeb #include <net/mac80211.h> 7bfcc09ddSBjoern A. Zeeb #include "fw/api/rs.h" 8bfcc09ddSBjoern A. Zeeb #include "iwl-drv.h" 9bfcc09ddSBjoern A. Zeeb #include "iwl-config.h" 10bfcc09ddSBjoern A. Zeeb 11bfcc09ddSBjoern A. Zeeb #define IWL_DECLARE_RATE_INFO(r) \ 12bfcc09ddSBjoern A. Zeeb [IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP 13bfcc09ddSBjoern A. Zeeb 14bfcc09ddSBjoern A. Zeeb /* 15bfcc09ddSBjoern A. Zeeb * Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP 16bfcc09ddSBjoern A. Zeeb * */ 17bfcc09ddSBjoern A. Zeeb static const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = { 18bfcc09ddSBjoern A. Zeeb IWL_DECLARE_RATE_INFO(1), 19bfcc09ddSBjoern A. Zeeb IWL_DECLARE_RATE_INFO(2), 20bfcc09ddSBjoern A. Zeeb IWL_DECLARE_RATE_INFO(5), 21bfcc09ddSBjoern A. Zeeb IWL_DECLARE_RATE_INFO(11), 22bfcc09ddSBjoern A. Zeeb IWL_DECLARE_RATE_INFO(6), 23bfcc09ddSBjoern A. Zeeb IWL_DECLARE_RATE_INFO(9), 24bfcc09ddSBjoern A. Zeeb IWL_DECLARE_RATE_INFO(12), 25bfcc09ddSBjoern A. Zeeb IWL_DECLARE_RATE_INFO(18), 26bfcc09ddSBjoern A. Zeeb IWL_DECLARE_RATE_INFO(24), 27bfcc09ddSBjoern A. Zeeb IWL_DECLARE_RATE_INFO(36), 28bfcc09ddSBjoern A. Zeeb IWL_DECLARE_RATE_INFO(48), 29bfcc09ddSBjoern A. Zeeb IWL_DECLARE_RATE_INFO(54), 30bfcc09ddSBjoern A. Zeeb }; 31bfcc09ddSBjoern A. Zeeb 32bfcc09ddSBjoern A. Zeeb /* mbps, mcs */ 33bfcc09ddSBjoern A. Zeeb static const struct iwl_rate_mcs_info rate_mcs[IWL_RATE_COUNT] = { 34bfcc09ddSBjoern A. Zeeb { "1", "BPSK DSSS"}, 35bfcc09ddSBjoern A. Zeeb { "2", "QPSK DSSS"}, 36bfcc09ddSBjoern A. Zeeb {"5.5", "BPSK CCK"}, 37bfcc09ddSBjoern A. Zeeb { "11", "QPSK CCK"}, 38bfcc09ddSBjoern A. Zeeb { "6", "BPSK 1/2"}, 39bfcc09ddSBjoern A. Zeeb { "9", "BPSK 1/2"}, 40bfcc09ddSBjoern A. Zeeb { "12", "QPSK 1/2"}, 41bfcc09ddSBjoern A. Zeeb { "18", "QPSK 3/4"}, 42bfcc09ddSBjoern A. Zeeb { "24", "16QAM 1/2"}, 43bfcc09ddSBjoern A. Zeeb { "36", "16QAM 3/4"}, 44bfcc09ddSBjoern A. Zeeb { "48", "64QAM 2/3"}, 45bfcc09ddSBjoern A. Zeeb { "54", "64QAM 3/4"}, 46bfcc09ddSBjoern A. Zeeb { "60", "64QAM 5/6"}, 47bfcc09ddSBjoern A. Zeeb }; 48bfcc09ddSBjoern A. Zeeb 49bfcc09ddSBjoern A. Zeeb static const char * const ant_name[] = { 50bfcc09ddSBjoern A. Zeeb [ANT_NONE] = "None", 51bfcc09ddSBjoern A. Zeeb [ANT_A] = "A", 52bfcc09ddSBjoern A. Zeeb [ANT_B] = "B", 53bfcc09ddSBjoern A. Zeeb [ANT_AB] = "AB", 54bfcc09ddSBjoern A. Zeeb }; 55bfcc09ddSBjoern A. Zeeb 56bfcc09ddSBjoern A. Zeeb static const char * const pretty_bw[] = { 57bfcc09ddSBjoern A. Zeeb "20Mhz", 58bfcc09ddSBjoern A. Zeeb "40Mhz", 59bfcc09ddSBjoern A. Zeeb "80Mhz", 60bfcc09ddSBjoern A. Zeeb "160 Mhz", 61bfcc09ddSBjoern A. Zeeb "320Mhz", 62bfcc09ddSBjoern A. Zeeb }; 63bfcc09ddSBjoern A. Zeeb 64bfcc09ddSBjoern A. Zeeb u8 iwl_fw_rate_idx_to_plcp(int idx) 65bfcc09ddSBjoern A. Zeeb { 66bfcc09ddSBjoern A. Zeeb return fw_rate_idx_to_plcp[idx]; 67bfcc09ddSBjoern A. Zeeb } 68bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_fw_rate_idx_to_plcp); 69bfcc09ddSBjoern A. Zeeb 70bfcc09ddSBjoern A. Zeeb const struct iwl_rate_mcs_info *iwl_rate_mcs(int idx) 71bfcc09ddSBjoern A. Zeeb { 72bfcc09ddSBjoern A. Zeeb return &rate_mcs[idx]; 73bfcc09ddSBjoern A. Zeeb } 74bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_rate_mcs); 75bfcc09ddSBjoern A. Zeeb 76bfcc09ddSBjoern A. Zeeb const char *iwl_rs_pretty_ant(u8 ant) 77bfcc09ddSBjoern A. Zeeb { 78bfcc09ddSBjoern A. Zeeb if (ant >= ARRAY_SIZE(ant_name)) 79bfcc09ddSBjoern A. Zeeb return "UNKNOWN"; 80bfcc09ddSBjoern A. Zeeb 81bfcc09ddSBjoern A. Zeeb return ant_name[ant]; 82bfcc09ddSBjoern A. Zeeb } 83bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_rs_pretty_ant); 84bfcc09ddSBjoern A. Zeeb 85bfcc09ddSBjoern A. Zeeb const char *iwl_rs_pretty_bw(int bw) 86bfcc09ddSBjoern A. Zeeb { 87bfcc09ddSBjoern A. Zeeb if (bw >= ARRAY_SIZE(pretty_bw)) 88bfcc09ddSBjoern A. Zeeb return "unknown bw"; 89bfcc09ddSBjoern A. Zeeb 90bfcc09ddSBjoern A. Zeeb return pretty_bw[bw]; 91bfcc09ddSBjoern A. Zeeb } 92bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_rs_pretty_bw); 93bfcc09ddSBjoern A. Zeeb 94fac1f593SBjoern A. Zeeb static u32 iwl_legacy_rate_to_fw_idx(u32 rate_n_flags) 95fac1f593SBjoern A. Zeeb { 96fac1f593SBjoern A. Zeeb int rate = rate_n_flags & RATE_LEGACY_RATE_MSK_V1; 97fac1f593SBjoern A. Zeeb int idx; 98fac1f593SBjoern A. Zeeb bool ofdm = !(rate_n_flags & RATE_MCS_CCK_MSK_V1); 99fac1f593SBjoern A. Zeeb int offset = ofdm ? IWL_FIRST_OFDM_RATE : 0; 100fac1f593SBjoern A. Zeeb int last = ofdm ? IWL_RATE_COUNT_LEGACY : IWL_FIRST_OFDM_RATE; 101fac1f593SBjoern A. Zeeb 102fac1f593SBjoern A. Zeeb for (idx = offset; idx < last; idx++) 103fac1f593SBjoern A. Zeeb if (iwl_fw_rate_idx_to_plcp(idx) == rate) 104fac1f593SBjoern A. Zeeb return idx - offset; 105fac1f593SBjoern A. Zeeb return IWL_RATE_INVALID; 106fac1f593SBjoern A. Zeeb } 107fac1f593SBjoern A. Zeeb 108bfcc09ddSBjoern A. Zeeb u32 iwl_new_rate_from_v1(u32 rate_v1) 109bfcc09ddSBjoern A. Zeeb { 110bfcc09ddSBjoern A. Zeeb u32 rate_v2 = 0; 111bfcc09ddSBjoern A. Zeeb u32 dup = 0; 112bfcc09ddSBjoern A. Zeeb 113bfcc09ddSBjoern A. Zeeb if (rate_v1 == 0) 114bfcc09ddSBjoern A. Zeeb return rate_v1; 115bfcc09ddSBjoern A. Zeeb /* convert rate */ 116bfcc09ddSBjoern A. Zeeb if (rate_v1 & RATE_MCS_HT_MSK_V1) { 117bfcc09ddSBjoern A. Zeeb u32 nss = 0; 118bfcc09ddSBjoern A. Zeeb 119bfcc09ddSBjoern A. Zeeb rate_v2 |= RATE_MCS_HT_MSK; 120bfcc09ddSBjoern A. Zeeb rate_v2 |= 121bfcc09ddSBjoern A. Zeeb rate_v1 & RATE_HT_MCS_RATE_CODE_MSK_V1; 122bfcc09ddSBjoern A. Zeeb nss = (rate_v1 & RATE_HT_MCS_MIMO2_MSK) >> 123bfcc09ddSBjoern A. Zeeb RATE_HT_MCS_NSS_POS_V1; 124bfcc09ddSBjoern A. Zeeb rate_v2 |= nss << RATE_MCS_NSS_POS; 125bfcc09ddSBjoern A. Zeeb } else if (rate_v1 & RATE_MCS_VHT_MSK_V1 || 126bfcc09ddSBjoern A. Zeeb rate_v1 & RATE_MCS_HE_MSK_V1) { 127bfcc09ddSBjoern A. Zeeb rate_v2 |= rate_v1 & RATE_VHT_MCS_RATE_CODE_MSK; 128bfcc09ddSBjoern A. Zeeb 129*9af1bba4SBjoern A. Zeeb rate_v2 |= rate_v1 & RATE_MCS_NSS_MSK; 130bfcc09ddSBjoern A. Zeeb 131bfcc09ddSBjoern A. Zeeb if (rate_v1 & RATE_MCS_HE_MSK_V1) { 132bfcc09ddSBjoern A. Zeeb u32 he_type_bits = rate_v1 & RATE_MCS_HE_TYPE_MSK_V1; 133bfcc09ddSBjoern A. Zeeb u32 he_type = he_type_bits >> RATE_MCS_HE_TYPE_POS_V1; 134bfcc09ddSBjoern A. Zeeb u32 he_106t = (rate_v1 & RATE_MCS_HE_106T_MSK_V1) >> 135bfcc09ddSBjoern A. Zeeb RATE_MCS_HE_106T_POS_V1; 136bfcc09ddSBjoern A. Zeeb u32 he_gi_ltf = (rate_v1 & RATE_MCS_HE_GI_LTF_MSK_V1) >> 137bfcc09ddSBjoern A. Zeeb RATE_MCS_HE_GI_LTF_POS; 138bfcc09ddSBjoern A. Zeeb 139bfcc09ddSBjoern A. Zeeb if ((he_type_bits == RATE_MCS_HE_TYPE_SU || 140bfcc09ddSBjoern A. Zeeb he_type_bits == RATE_MCS_HE_TYPE_EXT_SU) && 141bfcc09ddSBjoern A. Zeeb he_gi_ltf == RATE_MCS_HE_SU_4_LTF) 142bfcc09ddSBjoern A. Zeeb /* the new rate have an additional bit to 143bfcc09ddSBjoern A. Zeeb * represent the value 4 rather then using SGI 144bfcc09ddSBjoern A. Zeeb * bit for this purpose - as it was done in the old 145bfcc09ddSBjoern A. Zeeb * rate */ 146bfcc09ddSBjoern A. Zeeb he_gi_ltf += (rate_v1 & RATE_MCS_SGI_MSK_V1) >> 147bfcc09ddSBjoern A. Zeeb RATE_MCS_SGI_POS_V1; 148bfcc09ddSBjoern A. Zeeb 149bfcc09ddSBjoern A. Zeeb rate_v2 |= he_gi_ltf << RATE_MCS_HE_GI_LTF_POS; 150bfcc09ddSBjoern A. Zeeb rate_v2 |= he_type << RATE_MCS_HE_TYPE_POS; 151bfcc09ddSBjoern A. Zeeb rate_v2 |= he_106t << RATE_MCS_HE_106T_POS; 152bfcc09ddSBjoern A. Zeeb rate_v2 |= rate_v1 & RATE_HE_DUAL_CARRIER_MODE_MSK; 153bfcc09ddSBjoern A. Zeeb rate_v2 |= RATE_MCS_HE_MSK; 154bfcc09ddSBjoern A. Zeeb } else { 155bfcc09ddSBjoern A. Zeeb rate_v2 |= RATE_MCS_VHT_MSK; 156bfcc09ddSBjoern A. Zeeb } 157bfcc09ddSBjoern A. Zeeb /* if legacy format */ 158bfcc09ddSBjoern A. Zeeb } else { 159bfcc09ddSBjoern A. Zeeb u32 legacy_rate = iwl_legacy_rate_to_fw_idx(rate_v1); 160bfcc09ddSBjoern A. Zeeb 161fac1f593SBjoern A. Zeeb if (WARN_ON_ONCE(legacy_rate == IWL_RATE_INVALID)) 162fac1f593SBjoern A. Zeeb legacy_rate = (rate_v1 & RATE_MCS_CCK_MSK_V1) ? 163fac1f593SBjoern A. Zeeb IWL_FIRST_CCK_RATE : IWL_FIRST_OFDM_RATE; 164fac1f593SBjoern A. Zeeb 165bfcc09ddSBjoern A. Zeeb rate_v2 |= legacy_rate; 166bfcc09ddSBjoern A. Zeeb if (!(rate_v1 & RATE_MCS_CCK_MSK_V1)) 167bfcc09ddSBjoern A. Zeeb rate_v2 |= RATE_MCS_LEGACY_OFDM_MSK; 168bfcc09ddSBjoern A. Zeeb } 169bfcc09ddSBjoern A. Zeeb 170bfcc09ddSBjoern A. Zeeb /* convert flags */ 171bfcc09ddSBjoern A. Zeeb if (rate_v1 & RATE_MCS_LDPC_MSK_V1) 172bfcc09ddSBjoern A. Zeeb rate_v2 |= RATE_MCS_LDPC_MSK; 173bfcc09ddSBjoern A. Zeeb rate_v2 |= (rate_v1 & RATE_MCS_CHAN_WIDTH_MSK_V1) | 174bfcc09ddSBjoern A. Zeeb (rate_v1 & RATE_MCS_ANT_AB_MSK) | 175bfcc09ddSBjoern A. Zeeb (rate_v1 & RATE_MCS_STBC_MSK) | 176bfcc09ddSBjoern A. Zeeb (rate_v1 & RATE_MCS_BF_MSK); 177bfcc09ddSBjoern A. Zeeb 178bfcc09ddSBjoern A. Zeeb dup = (rate_v1 & RATE_MCS_DUP_MSK_V1) >> RATE_MCS_DUP_POS_V1; 179bfcc09ddSBjoern A. Zeeb if (dup) { 180bfcc09ddSBjoern A. Zeeb rate_v2 |= RATE_MCS_DUP_MSK; 181bfcc09ddSBjoern A. Zeeb rate_v2 |= dup << RATE_MCS_CHAN_WIDTH_POS; 182bfcc09ddSBjoern A. Zeeb } 183bfcc09ddSBjoern A. Zeeb 184bfcc09ddSBjoern A. Zeeb if ((!(rate_v1 & RATE_MCS_HE_MSK_V1)) && 185bfcc09ddSBjoern A. Zeeb (rate_v1 & RATE_MCS_SGI_MSK_V1)) 186bfcc09ddSBjoern A. Zeeb rate_v2 |= RATE_MCS_SGI_MSK; 187bfcc09ddSBjoern A. Zeeb 188bfcc09ddSBjoern A. Zeeb return rate_v2; 189bfcc09ddSBjoern A. Zeeb } 190bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_new_rate_from_v1); 191bfcc09ddSBjoern A. Zeeb 192bfcc09ddSBjoern A. Zeeb int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate) 193bfcc09ddSBjoern A. Zeeb { 194bfcc09ddSBjoern A. Zeeb char *type; 195bfcc09ddSBjoern A. Zeeb u8 mcs = 0, nss = 0; 196bfcc09ddSBjoern A. Zeeb u8 ant = (rate & RATE_MCS_ANT_AB_MSK) >> RATE_MCS_ANT_POS; 197bfcc09ddSBjoern A. Zeeb u32 bw = (rate & RATE_MCS_CHAN_WIDTH_MSK) >> 198bfcc09ddSBjoern A. Zeeb RATE_MCS_CHAN_WIDTH_POS; 199bfcc09ddSBjoern A. Zeeb u32 format = rate & RATE_MCS_MOD_TYPE_MSK; 200bfcc09ddSBjoern A. Zeeb bool sgi; 201bfcc09ddSBjoern A. Zeeb 202bfcc09ddSBjoern A. Zeeb if (format == RATE_MCS_CCK_MSK || 203bfcc09ddSBjoern A. Zeeb format == RATE_MCS_LEGACY_OFDM_MSK) { 204bfcc09ddSBjoern A. Zeeb int legacy_rate = rate & RATE_LEGACY_RATE_MSK; 205bfcc09ddSBjoern A. Zeeb int index = format == RATE_MCS_CCK_MSK ? 206bfcc09ddSBjoern A. Zeeb legacy_rate : 207bfcc09ddSBjoern A. Zeeb legacy_rate + IWL_FIRST_OFDM_RATE; 208bfcc09ddSBjoern A. Zeeb 209bfcc09ddSBjoern A. Zeeb return scnprintf(buf, bufsz, "Legacy | ANT: %s Rate: %s Mbps", 210bfcc09ddSBjoern A. Zeeb iwl_rs_pretty_ant(ant), 211bfcc09ddSBjoern A. Zeeb iwl_rate_mcs(index)->mbps); 212bfcc09ddSBjoern A. Zeeb } 213bfcc09ddSBjoern A. Zeeb 214bfcc09ddSBjoern A. Zeeb if (format == RATE_MCS_VHT_MSK) 215bfcc09ddSBjoern A. Zeeb type = "VHT"; 216bfcc09ddSBjoern A. Zeeb else if (format == RATE_MCS_HT_MSK) 217bfcc09ddSBjoern A. Zeeb type = "HT"; 218bfcc09ddSBjoern A. Zeeb else if (format == RATE_MCS_HE_MSK) 219bfcc09ddSBjoern A. Zeeb type = "HE"; 220*9af1bba4SBjoern A. Zeeb else if (format == RATE_MCS_EHT_MSK) 221*9af1bba4SBjoern A. Zeeb type = "EHT"; 222bfcc09ddSBjoern A. Zeeb else 223bfcc09ddSBjoern A. Zeeb type = "Unknown"; /* shouldn't happen */ 224bfcc09ddSBjoern A. Zeeb 225bfcc09ddSBjoern A. Zeeb mcs = format == RATE_MCS_HT_MSK ? 226bfcc09ddSBjoern A. Zeeb RATE_HT_MCS_INDEX(rate) : 227bfcc09ddSBjoern A. Zeeb rate & RATE_MCS_CODE_MSK; 228bfcc09ddSBjoern A. Zeeb nss = ((rate & RATE_MCS_NSS_MSK) 229bfcc09ddSBjoern A. Zeeb >> RATE_MCS_NSS_POS) + 1; 230bfcc09ddSBjoern A. Zeeb sgi = format == RATE_MCS_HE_MSK ? 231bfcc09ddSBjoern A. Zeeb iwl_he_is_sgi(rate) : 232bfcc09ddSBjoern A. Zeeb rate & RATE_MCS_SGI_MSK; 233bfcc09ddSBjoern A. Zeeb 234bfcc09ddSBjoern A. Zeeb return scnprintf(buf, bufsz, 235bfcc09ddSBjoern A. Zeeb "0x%x: %s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s%s", 236bfcc09ddSBjoern A. Zeeb rate, type, iwl_rs_pretty_ant(ant), iwl_rs_pretty_bw(bw), mcs, nss, 237bfcc09ddSBjoern A. Zeeb (sgi) ? "SGI " : "NGI ", 238bfcc09ddSBjoern A. Zeeb (rate & RATE_MCS_STBC_MSK) ? "STBC " : "", 239bfcc09ddSBjoern A. Zeeb (rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "", 240bfcc09ddSBjoern A. Zeeb (rate & RATE_HE_DUAL_CARRIER_MODE_MSK) ? "DCM " : "", 241bfcc09ddSBjoern A. Zeeb (rate & RATE_MCS_BF_MSK) ? "BF " : ""); 242bfcc09ddSBjoern A. Zeeb } 243bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(rs_pretty_print_rate); 244bfcc09ddSBjoern A. Zeeb 245bfcc09ddSBjoern A. Zeeb bool iwl_he_is_sgi(u32 rate_n_flags) 246bfcc09ddSBjoern A. Zeeb { 247bfcc09ddSBjoern A. Zeeb u32 type = rate_n_flags & RATE_MCS_HE_TYPE_MSK; 248bfcc09ddSBjoern A. Zeeb u32 ltf_gi = rate_n_flags & RATE_MCS_HE_GI_LTF_MSK; 249bfcc09ddSBjoern A. Zeeb 250bfcc09ddSBjoern A. Zeeb if (type == RATE_MCS_HE_TYPE_SU || 251bfcc09ddSBjoern A. Zeeb type == RATE_MCS_HE_TYPE_EXT_SU) 252bfcc09ddSBjoern A. Zeeb return ltf_gi == RATE_MCS_HE_SU_4_LTF_08_GI; 253bfcc09ddSBjoern A. Zeeb return false; 254bfcc09ddSBjoern A. Zeeb } 255bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_he_is_sgi); 256bfcc09ddSBjoern A. Zeeb 257