// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2021 Intel Corporation */ #include #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); 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); WARN_ON(legacy_rate < 0); 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); 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 -1; } 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 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);