diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/rs.c')
| -rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/rs.c | 241 |
1 files changed, 224 insertions, 17 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 9f32f2db95bd..194bd1f939ca 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c | |||
| @@ -39,6 +39,7 @@ | |||
| 39 | #include "sta.h" | 39 | #include "sta.h" |
| 40 | #include "iwl-op-mode.h" | 40 | #include "iwl-op-mode.h" |
| 41 | #include "mvm.h" | 41 | #include "mvm.h" |
| 42 | #include "debugfs.h" | ||
| 42 | 43 | ||
| 43 | #define RS_NAME "iwl-mvm-rs" | 44 | #define RS_NAME "iwl-mvm-rs" |
| 44 | 45 | ||
| @@ -1805,7 +1806,7 @@ static bool rs_stbc_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, | |||
| 1805 | /* Our chip supports Tx STBC and the peer is an HT/VHT STA which | 1806 | /* Our chip supports Tx STBC and the peer is an HT/VHT STA which |
| 1806 | * supports STBC of at least 1*SS | 1807 | * supports STBC of at least 1*SS |
| 1807 | */ | 1808 | */ |
| 1808 | if (!lq_sta->stbc) | 1809 | if (!lq_sta->stbc_capable) |
| 1809 | return false; | 1810 | return false; |
| 1810 | 1811 | ||
| 1811 | if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) | 1812 | if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) |
| @@ -2626,7 +2627,7 @@ static void rs_ht_init(struct iwl_mvm *mvm, | |||
| 2626 | if (mvm->cfg->ht_params->stbc && | 2627 | if (mvm->cfg->ht_params->stbc && |
| 2627 | (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && | 2628 | (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && |
| 2628 | (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)) | 2629 | (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)) |
| 2629 | lq_sta->stbc = true; | 2630 | lq_sta->stbc_capable = true; |
| 2630 | 2631 | ||
| 2631 | lq_sta->is_vht = false; | 2632 | lq_sta->is_vht = false; |
| 2632 | } | 2633 | } |
| @@ -2645,7 +2646,12 @@ static void rs_vht_init(struct iwl_mvm *mvm, | |||
| 2645 | if (mvm->cfg->ht_params->stbc && | 2646 | if (mvm->cfg->ht_params->stbc && |
| 2646 | (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && | 2647 | (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && |
| 2647 | (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)) | 2648 | (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)) |
| 2648 | lq_sta->stbc = true; | 2649 | lq_sta->stbc_capable = true; |
| 2650 | |||
| 2651 | if ((mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_BEAMFORMER) && | ||
| 2652 | (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && | ||
| 2653 | (vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) | ||
| 2654 | lq_sta->bfer_capable = true; | ||
| 2649 | 2655 | ||
| 2650 | lq_sta->is_vht = true; | 2656 | lq_sta->is_vht = true; |
| 2651 | } | 2657 | } |
| @@ -2778,11 +2784,12 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, | |||
| 2778 | rs_get_max_rate_from_mask(lq_sta->active_mimo2_rate); | 2784 | rs_get_max_rate_from_mask(lq_sta->active_mimo2_rate); |
| 2779 | 2785 | ||
| 2780 | IWL_DEBUG_RATE(mvm, | 2786 | IWL_DEBUG_RATE(mvm, |
| 2781 | "RATE MASK: LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d LDPC=%d STBC=%d\n", | 2787 | "LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d LDPC=%d STBC=%d BFER=%d\n", |
| 2782 | lq_sta->active_legacy_rate, | 2788 | lq_sta->active_legacy_rate, |
| 2783 | lq_sta->active_siso_rate, | 2789 | lq_sta->active_siso_rate, |
| 2784 | lq_sta->active_mimo2_rate, | 2790 | lq_sta->active_mimo2_rate, |
| 2785 | lq_sta->is_vht, lq_sta->ldpc, lq_sta->stbc); | 2791 | lq_sta->is_vht, lq_sta->ldpc, lq_sta->stbc_capable, |
| 2792 | lq_sta->bfer_capable); | ||
| 2786 | IWL_DEBUG_RATE(mvm, "MAX RATE: LEGACY=%d SISO=%d MIMO2=%d\n", | 2793 | IWL_DEBUG_RATE(mvm, "MAX RATE: LEGACY=%d SISO=%d MIMO2=%d\n", |
| 2787 | lq_sta->max_legacy_rate_idx, | 2794 | lq_sta->max_legacy_rate_idx, |
| 2788 | lq_sta->max_siso_rate_idx, | 2795 | lq_sta->max_siso_rate_idx, |
| @@ -2916,23 +2923,15 @@ static void rs_build_rates_table(struct iwl_mvm *mvm, | |||
| 2916 | u8 valid_tx_ant = 0; | 2923 | u8 valid_tx_ant = 0; |
| 2917 | struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; | 2924 | struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; |
| 2918 | bool toggle_ant = false; | 2925 | bool toggle_ant = false; |
| 2919 | bool stbc_allowed = false; | ||
| 2920 | 2926 | ||
| 2921 | memcpy(&rate, initial_rate, sizeof(rate)); | 2927 | memcpy(&rate, initial_rate, sizeof(rate)); |
| 2922 | 2928 | ||
| 2923 | valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); | 2929 | valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); |
| 2924 | 2930 | ||
| 2925 | stbc_allowed = rs_stbc_allow(mvm, sta, lq_sta); | 2931 | /* TODO: remove old API when min FW API hits 14 */ |
| 2926 | if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LQ_SS_PARAMS) { | 2932 | if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LQ_SS_PARAMS) && |
| 2927 | u32 ss_params = RS_SS_PARAMS_VALID; | 2933 | rs_stbc_allow(mvm, sta, lq_sta)) |
| 2928 | 2934 | rate.stbc = true; | |
| 2929 | if (stbc_allowed) | ||
| 2930 | ss_params |= RS_SS_STBC_ALLOWED; | ||
| 2931 | lq_cmd->ss_params = cpu_to_le32(ss_params); | ||
| 2932 | } else { | ||
| 2933 | /* TODO: remove old API when min FW API hits 14 */ | ||
| 2934 | rate.stbc = stbc_allowed; | ||
| 2935 | } | ||
| 2936 | 2935 | ||
| 2937 | if (is_siso(&rate)) { | 2936 | if (is_siso(&rate)) { |
| 2938 | num_rates = IWL_MVM_RS_INITIAL_SISO_NUM_RATES; | 2937 | num_rates = IWL_MVM_RS_INITIAL_SISO_NUM_RATES; |
| @@ -2980,6 +2979,142 @@ static void rs_build_rates_table(struct iwl_mvm *mvm, | |||
| 2980 | 2979 | ||
| 2981 | } | 2980 | } |
| 2982 | 2981 | ||
| 2982 | struct rs_bfer_active_iter_data { | ||
| 2983 | struct ieee80211_sta *exclude_sta; | ||
| 2984 | struct iwl_mvm_sta *bfer_mvmsta; | ||
| 2985 | }; | ||
| 2986 | |||
| 2987 | static void rs_bfer_active_iter(void *_data, | ||
| 2988 | struct ieee80211_sta *sta) | ||
| 2989 | { | ||
| 2990 | struct rs_bfer_active_iter_data *data = _data; | ||
| 2991 | struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); | ||
| 2992 | struct iwl_lq_cmd *lq_cmd = &mvmsta->lq_sta.lq; | ||
| 2993 | u32 ss_params = le32_to_cpu(lq_cmd->ss_params); | ||
| 2994 | |||
| 2995 | if (sta == data->exclude_sta) | ||
| 2996 | return; | ||
| 2997 | |||
| 2998 | /* The current sta has BFER allowed */ | ||
| 2999 | if (ss_params & LQ_SS_BFER_ALLOWED) { | ||
| 3000 | WARN_ON_ONCE(data->bfer_mvmsta != NULL); | ||
| 3001 | |||
| 3002 | data->bfer_mvmsta = mvmsta; | ||
| 3003 | } | ||
| 3004 | } | ||
| 3005 | |||
| 3006 | static int rs_bfer_priority(struct iwl_mvm_sta *sta) | ||
| 3007 | { | ||
| 3008 | int prio = -1; | ||
| 3009 | enum nl80211_iftype viftype = ieee80211_vif_type_p2p(sta->vif); | ||
| 3010 | |||
| 3011 | switch (viftype) { | ||
| 3012 | case NL80211_IFTYPE_AP: | ||
| 3013 | case NL80211_IFTYPE_P2P_GO: | ||
| 3014 | prio = 3; | ||
| 3015 | break; | ||
| 3016 | case NL80211_IFTYPE_P2P_CLIENT: | ||
| 3017 | prio = 2; | ||
| 3018 | break; | ||
| 3019 | case NL80211_IFTYPE_STATION: | ||
| 3020 | prio = 1; | ||
| 3021 | break; | ||
| 3022 | default: | ||
| 3023 | WARN_ONCE(true, "viftype %d sta_id %d", viftype, sta->sta_id); | ||
| 3024 | prio = -1; | ||
| 3025 | } | ||
| 3026 | |||
| 3027 | return prio; | ||
| 3028 | } | ||
| 3029 | |||
| 3030 | /* Returns >0 if sta1 has a higher BFER priority compared to sta2 */ | ||
| 3031 | static int rs_bfer_priority_cmp(struct iwl_mvm_sta *sta1, | ||
| 3032 | struct iwl_mvm_sta *sta2) | ||
| 3033 | { | ||
| 3034 | int prio1 = rs_bfer_priority(sta1); | ||
| 3035 | int prio2 = rs_bfer_priority(sta2); | ||
| 3036 | |||
| 3037 | if (prio1 > prio2) | ||
| 3038 | return 1; | ||
| 3039 | if (prio1 < prio2) | ||
| 3040 | return -1; | ||
| 3041 | return 0; | ||
| 3042 | } | ||
| 3043 | |||
| 3044 | static void rs_set_lq_ss_params(struct iwl_mvm *mvm, | ||
| 3045 | struct ieee80211_sta *sta, | ||
| 3046 | struct iwl_lq_sta *lq_sta, | ||
| 3047 | const struct rs_rate *initial_rate) | ||
| 3048 | { | ||
| 3049 | struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; | ||
| 3050 | struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); | ||
| 3051 | struct rs_bfer_active_iter_data data = { | ||
| 3052 | .exclude_sta = sta, | ||
| 3053 | .bfer_mvmsta = NULL, | ||
| 3054 | }; | ||
| 3055 | struct iwl_mvm_sta *bfer_mvmsta = NULL; | ||
| 3056 | u32 ss_params = LQ_SS_PARAMS_VALID; | ||
| 3057 | |||
| 3058 | if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) | ||
| 3059 | goto out; | ||
| 3060 | |||
| 3061 | /* Check if forcing the decision is configured. | ||
| 3062 | * Note that SISO is forced by not allowing STBC or BFER | ||
| 3063 | */ | ||
| 3064 | if (lq_sta->ss_force == RS_SS_FORCE_STBC) | ||
| 3065 | ss_params |= (LQ_SS_STBC_1SS_ALLOWED | LQ_SS_FORCE); | ||
| 3066 | else if (lq_sta->ss_force == RS_SS_FORCE_BFER) | ||
| 3067 | ss_params |= (LQ_SS_BFER_ALLOWED | LQ_SS_FORCE); | ||
| 3068 | |||
| 3069 | if (lq_sta->ss_force != RS_SS_FORCE_NONE) { | ||
| 3070 | IWL_DEBUG_RATE(mvm, "Forcing single stream Tx decision %d\n", | ||
| 3071 | lq_sta->ss_force); | ||
| 3072 | goto out; | ||
| 3073 | } | ||
| 3074 | |||
| 3075 | if (lq_sta->stbc_capable) | ||
| 3076 | ss_params |= LQ_SS_STBC_1SS_ALLOWED; | ||
| 3077 | |||
| 3078 | if (!lq_sta->bfer_capable) | ||
| 3079 | goto out; | ||
| 3080 | |||
| 3081 | ieee80211_iterate_stations_atomic(mvm->hw, | ||
| 3082 | rs_bfer_active_iter, | ||
| 3083 | &data); | ||
| 3084 | bfer_mvmsta = data.bfer_mvmsta; | ||
| 3085 | |||
| 3086 | /* This code is safe as it doesn't run concurrently for different | ||
| 3087 | * stations. This is guaranteed by the fact that calls to | ||
| 3088 | * ieee80211_tx_status wouldn't run concurrently for a single HW. | ||
| 3089 | */ | ||
| 3090 | if (!bfer_mvmsta) { | ||
| 3091 | IWL_DEBUG_RATE(mvm, "No sta with BFER allowed found. Allow\n"); | ||
| 3092 | |||
| 3093 | ss_params |= LQ_SS_BFER_ALLOWED; | ||
| 3094 | goto out; | ||
| 3095 | } | ||
| 3096 | |||
| 3097 | IWL_DEBUG_RATE(mvm, "Found existing sta %d with BFER activated\n", | ||
| 3098 | bfer_mvmsta->sta_id); | ||
| 3099 | |||
| 3100 | /* Disallow BFER on another STA if active and we're a higher priority */ | ||
| 3101 | if (rs_bfer_priority_cmp(mvmsta, bfer_mvmsta) > 0) { | ||
| 3102 | struct iwl_lq_cmd *bfersta_lq_cmd = &bfer_mvmsta->lq_sta.lq; | ||
| 3103 | u32 bfersta_ss_params = le32_to_cpu(bfersta_lq_cmd->ss_params); | ||
| 3104 | |||
| 3105 | bfersta_ss_params &= ~LQ_SS_BFER_ALLOWED; | ||
| 3106 | bfersta_lq_cmd->ss_params = cpu_to_le32(bfersta_ss_params); | ||
| 3107 | iwl_mvm_send_lq_cmd(mvm, bfersta_lq_cmd, false); | ||
| 3108 | |||
| 3109 | ss_params |= LQ_SS_BFER_ALLOWED; | ||
| 3110 | IWL_DEBUG_RATE(mvm, | ||
| 3111 | "Lower priority BFER sta found (%d). Switch BFER\n", | ||
| 3112 | bfer_mvmsta->sta_id); | ||
| 3113 | } | ||
| 3114 | out: | ||
| 3115 | lq_cmd->ss_params = cpu_to_le32(ss_params); | ||
| 3116 | } | ||
| 3117 | |||
| 2983 | static void rs_fill_lq_cmd(struct iwl_mvm *mvm, | 3118 | static void rs_fill_lq_cmd(struct iwl_mvm *mvm, |
| 2984 | struct ieee80211_sta *sta, | 3119 | struct ieee80211_sta *sta, |
| 2985 | struct iwl_lq_sta *lq_sta, | 3120 | struct iwl_lq_sta *lq_sta, |
| @@ -3006,6 +3141,9 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm, | |||
| 3006 | 3141 | ||
| 3007 | rs_build_rates_table(mvm, sta, lq_sta, initial_rate); | 3142 | rs_build_rates_table(mvm, sta, lq_sta, initial_rate); |
| 3008 | 3143 | ||
| 3144 | if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LQ_SS_PARAMS) | ||
| 3145 | rs_set_lq_ss_params(mvm, sta, lq_sta, initial_rate); | ||
| 3146 | |||
| 3009 | if (num_of_ant(initial_rate->ant) == 1) | 3147 | if (num_of_ant(initial_rate->ant) == 1) |
| 3010 | lq_cmd->single_stream_ant_msk = initial_rate->ant; | 3148 | lq_cmd->single_stream_ant_msk = initial_rate->ant; |
| 3011 | 3149 | ||
| @@ -3379,9 +3517,73 @@ static const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = { | |||
| 3379 | .llseek = default_llseek, | 3517 | .llseek = default_llseek, |
| 3380 | }; | 3518 | }; |
| 3381 | 3519 | ||
| 3520 | static ssize_t iwl_dbgfs_ss_force_read(struct file *file, | ||
| 3521 | char __user *user_buf, | ||
| 3522 | size_t count, loff_t *ppos) | ||
| 3523 | { | ||
| 3524 | struct iwl_lq_sta *lq_sta = file->private_data; | ||
| 3525 | char buf[12]; | ||
| 3526 | int bufsz = sizeof(buf); | ||
| 3527 | int pos = 0; | ||
| 3528 | static const char * const ss_force_name[] = { | ||
| 3529 | [RS_SS_FORCE_NONE] = "none", | ||
| 3530 | [RS_SS_FORCE_STBC] = "stbc", | ||
| 3531 | [RS_SS_FORCE_BFER] = "bfer", | ||
| 3532 | [RS_SS_FORCE_SISO] = "siso", | ||
| 3533 | }; | ||
| 3534 | |||
| 3535 | pos += scnprintf(buf+pos, bufsz-pos, "%s\n", | ||
| 3536 | ss_force_name[lq_sta->ss_force]); | ||
| 3537 | return simple_read_from_buffer(user_buf, count, ppos, buf, pos); | ||
| 3538 | } | ||
| 3539 | |||
| 3540 | static ssize_t iwl_dbgfs_ss_force_write(struct iwl_lq_sta *lq_sta, char *buf, | ||
| 3541 | size_t count, loff_t *ppos) | ||
| 3542 | { | ||
| 3543 | struct iwl_mvm *mvm = lq_sta->pers.drv; | ||
| 3544 | int ret = 0; | ||
| 3545 | |||
| 3546 | if (!strncmp("none", buf, 4)) { | ||
| 3547 | lq_sta->ss_force = RS_SS_FORCE_NONE; | ||
| 3548 | } else if (!strncmp("siso", buf, 4)) { | ||
| 3549 | lq_sta->ss_force = RS_SS_FORCE_SISO; | ||
| 3550 | } else if (!strncmp("stbc", buf, 4)) { | ||
| 3551 | if (lq_sta->stbc_capable) { | ||
| 3552 | lq_sta->ss_force = RS_SS_FORCE_STBC; | ||
| 3553 | } else { | ||
| 3554 | IWL_ERR(mvm, | ||
| 3555 | "can't force STBC. peer doesn't support\n"); | ||
| 3556 | ret = -EINVAL; | ||
| 3557 | } | ||
| 3558 | } else if (!strncmp("bfer", buf, 4)) { | ||
| 3559 | if (lq_sta->bfer_capable) { | ||
| 3560 | lq_sta->ss_force = RS_SS_FORCE_BFER; | ||
| 3561 | } else { | ||
| 3562 | IWL_ERR(mvm, | ||
| 3563 | "can't force BFER. peer doesn't support\n"); | ||
| 3564 | ret = -EINVAL; | ||
| 3565 | } | ||
| 3566 | } else { | ||
| 3567 | IWL_ERR(mvm, "valid values none|siso|stbc|bfer\n"); | ||
| 3568 | ret = -EINVAL; | ||
| 3569 | } | ||
| 3570 | return ret ?: count; | ||
| 3571 | } | ||
| 3572 | |||
| 3573 | #define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ | ||
| 3574 | _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_lq_sta) | ||
| 3575 | #define MVM_DEBUGFS_ADD_FILE_RS(name, parent, mode) do { \ | ||
| 3576 | if (!debugfs_create_file(#name, mode, parent, lq_sta, \ | ||
| 3577 | &iwl_dbgfs_##name##_ops)) \ | ||
| 3578 | goto err; \ | ||
| 3579 | } while (0) | ||
| 3580 | |||
| 3581 | MVM_DEBUGFS_READ_WRITE_FILE_OPS(ss_force, 32); | ||
| 3582 | |||
| 3382 | static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir) | 3583 | static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir) |
| 3383 | { | 3584 | { |
| 3384 | struct iwl_lq_sta *lq_sta = mvm_sta; | 3585 | struct iwl_lq_sta *lq_sta = mvm_sta; |
| 3586 | |||
| 3385 | debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, | 3587 | debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, |
| 3386 | lq_sta, &rs_sta_dbgfs_scale_table_ops); | 3588 | lq_sta, &rs_sta_dbgfs_scale_table_ops); |
| 3387 | debugfs_create_file("rate_stats_table", S_IRUSR, dir, | 3589 | debugfs_create_file("rate_stats_table", S_IRUSR, dir, |
| @@ -3392,6 +3594,11 @@ static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir) | |||
| 3392 | &lq_sta->tx_agg_tid_en); | 3594 | &lq_sta->tx_agg_tid_en); |
| 3393 | debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir, | 3595 | debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir, |
| 3394 | &lq_sta->pers.dbg_fixed_txp_reduction); | 3596 | &lq_sta->pers.dbg_fixed_txp_reduction); |
| 3597 | |||
| 3598 | MVM_DEBUGFS_ADD_FILE_RS(ss_force, dir, S_IRUSR | S_IWUSR); | ||
| 3599 | return; | ||
| 3600 | err: | ||
| 3601 | IWL_ERR((struct iwl_mvm *)mvm, "Can't create debugfs entity\n"); | ||
| 3395 | } | 3602 | } |
| 3396 | 3603 | ||
| 3397 | static void rs_remove_debugfs(void *mvm, void *mvm_sta) | 3604 | static void rs_remove_debugfs(void *mvm, void *mvm_sta) |
