linux/drivers/net/wireless/intel/iwlwifi/mvm/sf.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/*
   3 * Copyright (C) 2013-2014, 2018-2019 Intel Corporation
   4 * Copyright (C) 2013-2014 Intel Mobile Communications GmbH
   5 */
   6#include "mvm.h"
   7
   8/* For counting bound interfaces */
   9struct iwl_mvm_active_iface_iterator_data {
  10        struct ieee80211_vif *ignore_vif;
  11        u8 sta_vif_ap_sta_id;
  12        enum iwl_sf_state sta_vif_state;
  13        u32 num_active_macs;
  14};
  15
  16/*
  17 * Count bound interfaces which are not p2p, besides data->ignore_vif.
  18 * data->station_vif will point to one bound vif of type station, if exists.
  19 */
  20static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac,
  21                                         struct ieee80211_vif *vif)
  22{
  23        struct iwl_mvm_active_iface_iterator_data *data = _data;
  24        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
  25
  26        if (vif == data->ignore_vif || !mvmvif->phy_ctxt ||
  27            vif->type == NL80211_IFTYPE_P2P_DEVICE)
  28                return;
  29
  30        data->num_active_macs++;
  31
  32        if (vif->type == NL80211_IFTYPE_STATION) {
  33                data->sta_vif_ap_sta_id = mvmvif->ap_sta_id;
  34                if (vif->bss_conf.assoc)
  35                        data->sta_vif_state = SF_FULL_ON;
  36                else
  37                        data->sta_vif_state = SF_INIT_OFF;
  38        }
  39}
  40
  41/*
  42 * Aging and idle timeouts for the different possible scenarios
  43 * in default configuration
  44 */
  45static const
  46__le32 sf_full_timeout_def[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
  47        {
  48                cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER_DEF),
  49                cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER_DEF)
  50        },
  51        {
  52                cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER_DEF),
  53                cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER_DEF)
  54        },
  55        {
  56                cpu_to_le32(SF_MCAST_AGING_TIMER_DEF),
  57                cpu_to_le32(SF_MCAST_IDLE_TIMER_DEF)
  58        },
  59        {
  60                cpu_to_le32(SF_BA_AGING_TIMER_DEF),
  61                cpu_to_le32(SF_BA_IDLE_TIMER_DEF)
  62        },
  63        {
  64                cpu_to_le32(SF_TX_RE_AGING_TIMER_DEF),
  65                cpu_to_le32(SF_TX_RE_IDLE_TIMER_DEF)
  66        },
  67};
  68
  69/*
  70 * Aging and idle timeouts for the different possible scenarios
  71 * in single BSS MAC configuration.
  72 */
  73static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
  74        {
  75                cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER),
  76                cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER)
  77        },
  78        {
  79                cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER),
  80                cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER)
  81        },
  82        {
  83                cpu_to_le32(SF_MCAST_AGING_TIMER),
  84                cpu_to_le32(SF_MCAST_IDLE_TIMER)
  85        },
  86        {
  87                cpu_to_le32(SF_BA_AGING_TIMER),
  88                cpu_to_le32(SF_BA_IDLE_TIMER)
  89        },
  90        {
  91                cpu_to_le32(SF_TX_RE_AGING_TIMER),
  92                cpu_to_le32(SF_TX_RE_IDLE_TIMER)
  93        },
  94};
  95
  96static void iwl_mvm_fill_sf_command(struct iwl_mvm *mvm,
  97                                    struct iwl_sf_cfg_cmd *sf_cmd,
  98                                    struct ieee80211_sta *sta)
  99{
 100        int i, j, watermark;
 101
 102        sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN);
 103
 104        /*
 105         * If we are in association flow - check antenna configuration
 106         * capabilities of the AP station, and choose the watermark accordingly.
 107         */
 108        if (sta) {
 109                if (sta->ht_cap.ht_supported ||
 110                    sta->vht_cap.vht_supported ||
 111                    sta->he_cap.has_he) {
 112                        switch (sta->rx_nss) {
 113                        case 1:
 114                                watermark = SF_W_MARK_SISO;
 115                                break;
 116                        case 2:
 117                                watermark = SF_W_MARK_MIMO2;
 118                                break;
 119                        default:
 120                                watermark = SF_W_MARK_MIMO3;
 121                                break;
 122                        }
 123                } else {
 124                        watermark = SF_W_MARK_LEGACY;
 125                }
 126        /* default watermark value for unassociated mode. */
 127        } else {
 128                watermark = SF_W_MARK_MIMO2;
 129        }
 130        sf_cmd->watermark[SF_FULL_ON] = cpu_to_le32(watermark);
 131
 132        for (i = 0; i < SF_NUM_SCENARIO; i++) {
 133                for (j = 0; j < SF_NUM_TIMEOUT_TYPES; j++) {
 134                        sf_cmd->long_delay_timeouts[i][j] =
 135                                        cpu_to_le32(SF_LONG_DELAY_AGING_TIMER);
 136                }
 137        }
 138
 139        if (sta) {
 140                BUILD_BUG_ON(sizeof(sf_full_timeout) !=
 141                             sizeof(__le32) * SF_NUM_SCENARIO *
 142                             SF_NUM_TIMEOUT_TYPES);
 143
 144                memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,
 145                       sizeof(sf_full_timeout));
 146        } else {
 147                BUILD_BUG_ON(sizeof(sf_full_timeout_def) !=
 148                             sizeof(__le32) * SF_NUM_SCENARIO *
 149                             SF_NUM_TIMEOUT_TYPES);
 150
 151                memcpy(sf_cmd->full_on_timeouts, sf_full_timeout_def,
 152                       sizeof(sf_full_timeout_def));
 153        }
 154
 155}
 156
 157static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id,
 158                             enum iwl_sf_state new_state)
 159{
 160        struct iwl_sf_cfg_cmd sf_cmd = {
 161                .state = cpu_to_le32(new_state),
 162        };
 163        struct ieee80211_sta *sta;
 164        int ret = 0;
 165
 166        if (mvm->cfg->disable_dummy_notification)
 167                sf_cmd.state |= cpu_to_le32(SF_CFG_DUMMY_NOTIF_OFF);
 168
 169        /*
 170         * If an associated AP sta changed its antenna configuration, the state
 171         * will remain FULL_ON but SF parameters need to be reconsidered.
 172         */
 173        if (new_state != SF_FULL_ON && mvm->sf_state == new_state)
 174                return 0;
 175
 176        switch (new_state) {
 177        case SF_UNINIT:
 178                iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL);
 179                break;
 180        case SF_FULL_ON:
 181                if (sta_id == IWL_MVM_INVALID_STA) {
 182                        IWL_ERR(mvm,
 183                                "No station: Cannot switch SF to FULL_ON\n");
 184                        return -EINVAL;
 185                }
 186                rcu_read_lock();
 187                sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
 188                if (IS_ERR_OR_NULL(sta)) {
 189                        IWL_ERR(mvm, "Invalid station id\n");
 190                        rcu_read_unlock();
 191                        return -EINVAL;
 192                }
 193                iwl_mvm_fill_sf_command(mvm, &sf_cmd, sta);
 194                rcu_read_unlock();
 195                break;
 196        case SF_INIT_OFF:
 197                iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL);
 198                break;
 199        default:
 200                WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n",
 201                          new_state);
 202                return -EINVAL;
 203        }
 204
 205        ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_SF_CFG_CMD, CMD_ASYNC,
 206                                   sizeof(sf_cmd), &sf_cmd);
 207        if (!ret)
 208                mvm->sf_state = new_state;
 209
 210        return ret;
 211}
 212
 213/*
 214 * Update Smart fifo:
 215 * Count bound interfaces that are not to be removed, ignoring p2p devices,
 216 * and set new state accordingly.
 217 */
 218int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,
 219                      bool remove_vif)
 220{
 221        enum iwl_sf_state new_state;
 222        u8 sta_id = IWL_MVM_INVALID_STA;
 223        struct iwl_mvm_vif *mvmvif = NULL;
 224        struct iwl_mvm_active_iface_iterator_data data = {
 225                .ignore_vif = changed_vif,
 226                .sta_vif_state = SF_UNINIT,
 227                .sta_vif_ap_sta_id = IWL_MVM_INVALID_STA,
 228        };
 229
 230        /*
 231         * Ignore the call if we are in HW Restart flow, or if the handled
 232         * vif is a p2p device.
 233         */
 234        if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) ||
 235            (changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE))
 236                return 0;
 237
 238        ieee80211_iterate_active_interfaces_atomic(mvm->hw,
 239                                                   IEEE80211_IFACE_ITER_NORMAL,
 240                                                   iwl_mvm_bound_iface_iterator,
 241                                                   &data);
 242
 243        /* If changed_vif exists and is not to be removed, add to the count */
 244        if (changed_vif && !remove_vif)
 245                data.num_active_macs++;
 246
 247        switch (data.num_active_macs) {
 248        case 0:
 249                /* If there are no active macs - change state to SF_INIT_OFF */
 250                new_state = SF_INIT_OFF;
 251                break;
 252        case 1:
 253                if (remove_vif) {
 254                        /* The one active mac left is of type station
 255                         * and we filled the relevant data during iteration
 256                         */
 257                        new_state = data.sta_vif_state;
 258                        sta_id = data.sta_vif_ap_sta_id;
 259                } else {
 260                        if (WARN_ON(!changed_vif))
 261                                return -EINVAL;
 262                        if (changed_vif->type != NL80211_IFTYPE_STATION) {
 263                                new_state = SF_UNINIT;
 264                        } else if (changed_vif->bss_conf.assoc &&
 265                                   changed_vif->bss_conf.dtim_period) {
 266                                mvmvif = iwl_mvm_vif_from_mac80211(changed_vif);
 267                                sta_id = mvmvif->ap_sta_id;
 268                                new_state = SF_FULL_ON;
 269                        } else {
 270                                new_state = SF_INIT_OFF;
 271                        }
 272                }
 273                break;
 274        default:
 275                /* If there are multiple active macs - change to SF_UNINIT */
 276                new_state = SF_UNINIT;
 277        }
 278        return iwl_mvm_sf_config(mvm, sta_id, new_state);
 279}
 280