|
@@ -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 |
|
|
@@ -3717,8 +4052,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;
|
|
|
|