]> Pileus Git - ~andy/linux/blobdiff - drivers/net/wireless/ath/ath10k/mac.c
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac802...
[~andy/linux] / drivers / net / wireless / ath / ath10k / mac.c
index 747c8bd749802336d46f70f884c71f1bd4b46685..776e364eadcd76c82ed37a64e20601f5bb1926ab 100644 (file)
@@ -332,6 +332,9 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
                ath10k_warn("Failed to wait for created wmi peer: %i\n", ret);
                return ret;
        }
+       spin_lock_bh(&ar->data_lock);
+       ar->num_peers++;
+       spin_unlock_bh(&ar->data_lock);
 
        return 0;
 }
@@ -377,6 +380,10 @@ static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
        if (ret)
                return ret;
 
+       spin_lock_bh(&ar->data_lock);
+       ar->num_peers--;
+       spin_unlock_bh(&ar->data_lock);
+
        return 0;
 }
 
@@ -396,6 +403,7 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
 
                list_del(&peer->list);
                kfree(peer);
+               ar->num_peers--;
        }
        spin_unlock_bh(&ar->data_lock);
 }
@@ -411,6 +419,7 @@ static void ath10k_peer_cleanup_all(struct ath10k *ar)
                list_del(&peer->list);
                kfree(peer);
        }
+       ar->num_peers = 0;
        spin_unlock_bh(&ar->data_lock);
 }
 
@@ -2205,7 +2214,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        enum wmi_sta_powersave_param param;
        int ret = 0;
-       u32 value;
+       u32 value, param_id;
        int bit;
        u32 vdev_param;
 
@@ -2297,6 +2306,13 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                        ath10k_warn("Failed to create peer for AP: %d\n", ret);
                        goto err_vdev_delete;
                }
+
+               param_id = ar->wmi.pdev_param->sta_kickout_th;
+
+               /* Disable STA KICKOUT functionality in FW */
+               ret = ath10k_wmi_pdev_set_param(ar, param_id, 0);
+               if (ret)
+                       ath10k_warn("Failed to disable STA KICKOUT\n");
        }
 
        if (arvif->vdev_type == WMI_VDEV_TYPE_STA) {
@@ -2842,6 +2858,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
 {
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       int max_num_peers;
        int ret = 0;
 
        mutex_lock(&ar->conf_mutex);
@@ -2852,9 +2869,21 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                /*
                 * New station addition.
                 */
+               if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+                       max_num_peers = TARGET_10X_NUM_PEERS_MAX - 1;
+               else
+                       max_num_peers = TARGET_NUM_PEERS;
+
+               if (ar->num_peers >= max_num_peers) {
+                       ath10k_warn("Number of peers exceeded: peers number %d (max peers %d)\n",
+                                   ar->num_peers, max_num_peers);
+                       ret = -ENOBUFS;
+                       goto exit;
+               }
+
                ath10k_dbg(ATH10K_DBG_MAC,
-                          "mac vdev %d peer create %pM (new sta)\n",
-                          arvif->vdev_id, sta->addr);
+                          "mac vdev %d peer create %pM (new sta) num_peers %d\n",
+                          arvif->vdev_id, sta->addr, ar->num_peers);
 
                ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
                if (ret)
@@ -2904,7 +2933,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                        ath10k_warn("Failed to disassociate station: %pM\n",
                                    sta->addr);
        }
-
+exit:
        mutex_unlock(&ar->conf_mutex);
        return ret;
 }
@@ -3310,6 +3339,307 @@ exit:
        return ret;
 }
 
+/* Helper table for legacy fixed_rate/bitrate_mask */
+static const u8 cck_ofdm_rate[] = {
+       /* CCK */
+       3, /* 1Mbps */
+       2, /* 2Mbps */
+       1, /* 5.5Mbps */
+       0, /* 11Mbps */
+       /* OFDM */
+       3, /* 6Mbps */
+       7, /* 9Mbps */
+       2, /* 12Mbps */
+       6, /* 18Mbps */
+       1, /* 24Mbps */
+       5, /* 36Mbps */
+       0, /* 48Mbps */
+       4, /* 54Mbps */
+};
+
+/* Check if only one bit set */
+static int ath10k_check_single_mask(u32 mask)
+{
+       int bit;
+
+       bit = ffs(mask);
+       if (!bit)
+               return 0;
+
+       mask &= ~BIT(bit - 1);
+       if (mask)
+               return 2;
+
+       return 1;
+}
+
+static bool
+ath10k_default_bitrate_mask(struct ath10k *ar,
+                           enum ieee80211_band band,
+                           const struct cfg80211_bitrate_mask *mask)
+{
+       u32 legacy = 0x00ff;
+       u8 ht = 0xff, i;
+       u16 vht = 0x3ff;
+
+       switch (band) {
+       case IEEE80211_BAND_2GHZ:
+               legacy = 0x00fff;
+               vht = 0;
+               break;
+       case IEEE80211_BAND_5GHZ:
+               break;
+       default:
+               return false;
+       }
+
+       if (mask->control[band].legacy != legacy)
+               return false;
+
+       for (i = 0; i < ar->num_rf_chains; i++)
+               if (mask->control[band].ht_mcs[i] != ht)
+                       return false;
+
+       for (i = 0; i < ar->num_rf_chains; i++)
+               if (mask->control[band].vht_mcs[i] != vht)
+                       return false;
+
+       return true;
+}
+
+static bool
+ath10k_bitrate_mask_nss(const struct cfg80211_bitrate_mask *mask,
+                       enum ieee80211_band band,
+                       u8 *fixed_nss)
+{
+       int ht_nss = 0, vht_nss = 0, i;
+
+       /* check legacy */
+       if (ath10k_check_single_mask(mask->control[band].legacy))
+               return false;
+
+       /* check HT */
+       for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+               if (mask->control[band].ht_mcs[i] == 0xff)
+                       continue;
+               else if (mask->control[band].ht_mcs[i] == 0x00)
+                       break;
+               else
+                       return false;
+       }
+
+       ht_nss = i;
+
+       /* check VHT */
+       for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
+               if (mask->control[band].vht_mcs[i] == 0x03ff)
+                       continue;
+               else if (mask->control[band].vht_mcs[i] == 0x0000)
+                       break;
+               else
+                       return false;
+       }
+
+       vht_nss = i;
+
+       if (ht_nss > 0 && vht_nss > 0)
+               return false;
+
+       if (ht_nss)
+               *fixed_nss = ht_nss;
+       else if (vht_nss)
+               *fixed_nss = vht_nss;
+       else
+               return false;
+
+       return true;
+}
+
+static bool
+ath10k_bitrate_mask_correct(const struct cfg80211_bitrate_mask *mask,
+                           enum ieee80211_band band,
+                           enum wmi_rate_preamble *preamble)
+{
+       int legacy = 0, ht = 0, vht = 0, i;
+
+       *preamble = WMI_RATE_PREAMBLE_OFDM;
+
+       /* check legacy */
+       legacy = ath10k_check_single_mask(mask->control[band].legacy);
+       if (legacy > 1)
+               return false;
+
+       /* check HT */
+       for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
+               ht += ath10k_check_single_mask(mask->control[band].ht_mcs[i]);
+       if (ht > 1)
+               return false;
+
+       /* check VHT */
+       for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
+               vht += ath10k_check_single_mask(mask->control[band].vht_mcs[i]);
+       if (vht > 1)
+               return false;
+
+       /* Currently we support only one fixed_rate */
+       if ((legacy + ht + vht) != 1)
+               return false;
+
+       if (ht)
+               *preamble = WMI_RATE_PREAMBLE_HT;
+       else if (vht)
+               *preamble = WMI_RATE_PREAMBLE_VHT;
+
+       return true;
+}
+
+static bool
+ath10k_bitrate_mask_rate(const struct cfg80211_bitrate_mask *mask,
+                        enum ieee80211_band band,
+                        u8 *fixed_rate,
+                        u8 *fixed_nss)
+{
+       u8 rate = 0, pream = 0, nss = 0, i;
+       enum wmi_rate_preamble preamble;
+
+       /* Check if single rate correct */
+       if (!ath10k_bitrate_mask_correct(mask, band, &preamble))
+               return false;
+
+       pream = preamble;
+
+       switch (preamble) {
+       case WMI_RATE_PREAMBLE_CCK:
+       case WMI_RATE_PREAMBLE_OFDM:
+               i = ffs(mask->control[band].legacy) - 1;
+
+               if (band == IEEE80211_BAND_2GHZ && i < 4)
+                       pream = WMI_RATE_PREAMBLE_CCK;
+
+               if (band == IEEE80211_BAND_5GHZ)
+                       i += 4;
+
+               if (i >= ARRAY_SIZE(cck_ofdm_rate))
+                       return false;
+
+               rate = cck_ofdm_rate[i];
+               break;
+       case WMI_RATE_PREAMBLE_HT:
+               for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
+                       if (mask->control[band].ht_mcs[i])
+                               break;
+
+               if (i == IEEE80211_HT_MCS_MASK_LEN)
+                       return false;
+
+               rate = ffs(mask->control[band].ht_mcs[i]) - 1;
+               nss = i;
+               break;
+       case WMI_RATE_PREAMBLE_VHT:
+               for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
+                       if (mask->control[band].vht_mcs[i])
+                               break;
+
+               if (i == NL80211_VHT_NSS_MAX)
+                       return false;
+
+               rate = ffs(mask->control[band].vht_mcs[i]) - 1;
+               nss = i;
+               break;
+       }
+
+       *fixed_nss = nss + 1;
+       nss <<= 4;
+       pream <<= 6;
+
+       ath10k_dbg(ATH10K_DBG_MAC, "mac fixed rate pream 0x%02x nss 0x%02x rate 0x%02x\n",
+                  pream, nss, rate);
+
+       *fixed_rate = pream | nss | rate;
+
+       return true;
+}
+
+static bool ath10k_get_fixed_rate_nss(const struct cfg80211_bitrate_mask *mask,
+                                     enum ieee80211_band band,
+                                     u8 *fixed_rate,
+                                     u8 *fixed_nss)
+{
+       /* First check full NSS mask, if we can simply limit NSS */
+       if (ath10k_bitrate_mask_nss(mask, band, fixed_nss))
+               return true;
+
+       /* Next Check single rate is set */
+       return ath10k_bitrate_mask_rate(mask, band, fixed_rate, fixed_nss);
+}
+
+static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif,
+                                      u8 fixed_rate,
+                                      u8 fixed_nss)
+{
+       struct ath10k *ar = arvif->ar;
+       u32 vdev_param;
+       int ret = 0;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (arvif->fixed_rate == fixed_rate &&
+           arvif->fixed_nss == fixed_nss)
+               goto exit;
+
+       if (fixed_rate == WMI_FIXED_RATE_NONE)
+               ath10k_dbg(ATH10K_DBG_MAC, "mac disable fixed bitrate mask\n");
+
+       vdev_param = ar->wmi.vdev_param->fixed_rate;
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                       vdev_param, fixed_rate);
+       if (ret) {
+               ath10k_warn("Could not set fixed_rate param 0x%02x: %d\n",
+                           fixed_rate, ret);
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       arvif->fixed_rate = fixed_rate;
+
+       vdev_param = ar->wmi.vdev_param->nss;
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                       vdev_param, fixed_nss);
+
+       if (ret) {
+               ath10k_warn("Could not set fixed_nss param %d: %d\n",
+                           fixed_nss, ret);
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       arvif->fixed_nss = fixed_nss;
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif,
+                                  const struct cfg80211_bitrate_mask *mask)
+{
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ath10k *ar = arvif->ar;
+       enum ieee80211_band band = ar->hw->conf.chandef.chan->band;
+       u8 fixed_rate = WMI_FIXED_RATE_NONE;
+       u8 fixed_nss = ar->num_rf_chains;
+
+       if (!ath10k_default_bitrate_mask(ar, band, mask)) {
+               if (!ath10k_get_fixed_rate_nss(mask, band,
+                                              &fixed_rate,
+                                              &fixed_nss))
+                       return -EINVAL;
+       }
+
+       return ath10k_set_fixed_rate_param(arvif, fixed_rate, fixed_nss);
+}
+
 static const struct ieee80211_ops ath10k_ops = {
        .tx                             = ath10k_tx,
        .start                          = ath10k_start,
@@ -3332,6 +3662,7 @@ static const struct ieee80211_ops ath10k_ops = {
        .tx_last_beacon                 = ath10k_tx_last_beacon,
        .restart_complete               = ath10k_restart_complete,
        .get_survey                     = ath10k_get_survey,
+       .set_bitrate_mask               = ath10k_set_bitrate_mask,
 #ifdef CONFIG_PM
        .suspend                        = ath10k_suspend,
        .resume                         = ath10k_resume,
@@ -3464,14 +3795,12 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = {
        },
 };
 
-#ifdef CONFIG_ATH10K_DFS_CERTIFIED
-static const struct ieee80211_iface_limit ath10k_if_dfs_limits[] = {
+static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = {
        {
        .max    = 8,
        .types  = BIT(NL80211_IFTYPE_AP)
        },
 };
-#endif
 
 static const struct ieee80211_iface_combination ath10k_if_comb[] = {
        {
@@ -3481,19 +3810,22 @@ static const struct ieee80211_iface_combination ath10k_if_comb[] = {
                .num_different_channels = 1,
                .beacon_int_infra_match = true,
        },
-#ifdef CONFIG_ATH10K_DFS_CERTIFIED
+};
+
+static const struct ieee80211_iface_combination ath10k_10x_if_comb[] = {
        {
-               .limits = ath10k_if_dfs_limits,
-               .n_limits = ARRAY_SIZE(ath10k_if_dfs_limits),
+               .limits = ath10k_10x_if_limits,
+               .n_limits = ARRAY_SIZE(ath10k_10x_if_limits),
                .max_interfaces = 8,
                .num_different_channels = 1,
                .beacon_int_infra_match = true,
+#ifdef CONFIG_ATH10K_DFS_CERTIFIED
                .radar_detect_widths =  BIT(NL80211_CHAN_WIDTH_20_NOHT) |
                                        BIT(NL80211_CHAN_WIDTH_20) |
                                        BIT(NL80211_CHAN_WIDTH_40) |
                                        BIT(NL80211_CHAN_WIDTH_80),
-       }
 #endif
+       },
 };
 
 static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar)
@@ -3672,9 +4004,12 @@ int ath10k_mac_register(struct ath10k *ar)
        ar->hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_STATION) |
                BIT(NL80211_IFTYPE_ADHOC) |
-               BIT(NL80211_IFTYPE_AP) |
-               BIT(NL80211_IFTYPE_P2P_CLIENT) |
-               BIT(NL80211_IFTYPE_P2P_GO);
+               BIT(NL80211_IFTYPE_AP);
+
+       if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features))
+               ar->hw->wiphy->interface_modes |=
+                       BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                       BIT(NL80211_IFTYPE_P2P_GO);
 
        ar->hw->flags = IEEE80211_HW_SIGNAL_DBM |
                        IEEE80211_HW_SUPPORTS_PS |
@@ -3716,8 +4051,15 @@ int ath10k_mac_register(struct ath10k *ar)
         */
        ar->hw->queues = 4;
 
-       ar->hw->wiphy->iface_combinations = ath10k_if_comb;
-       ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ath10k_if_comb);
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+               ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb;
+               ar->hw->wiphy->n_iface_combinations =
+                       ARRAY_SIZE(ath10k_10x_if_comb);
+       } else {
+               ar->hw->wiphy->iface_combinations = ath10k_if_comb;
+               ar->hw->wiphy->n_iface_combinations =
+                       ARRAY_SIZE(ath10k_if_comb);
+       }
 
        ar->hw->netdev_features = NETIF_F_HW_CSUM;