|
@@ -734,7 +734,6 @@ static int iwl_mvm_sta_alloc_queue_tvqm(struct iwl_mvm *mvm,
|
|
|
spin_lock_bh(&mvmsta->lock);
|
|
|
mvmsta->tid_data[tid].txq_id = queue;
|
|
|
mvmsta->tid_data[tid].is_tid_active = true;
|
|
|
- mvmsta->tfd_queue_msk |= BIT(queue);
|
|
|
spin_unlock_bh(&mvmsta->lock);
|
|
|
|
|
|
return 0;
|
|
@@ -758,7 +757,7 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
|
|
|
bool using_inactive_queue = false, same_sta = false;
|
|
|
unsigned long disable_agg_tids = 0;
|
|
|
enum iwl_mvm_agg_state queue_state;
|
|
|
- bool shared_queue = false;
|
|
|
+ bool shared_queue = false, inc_ssn;
|
|
|
int ssn;
|
|
|
unsigned long tfd_queue_mask;
|
|
|
int ret;
|
|
@@ -885,8 +884,12 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
|
|
|
}
|
|
|
|
|
|
ssn = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
|
|
|
- iwl_mvm_enable_txq(mvm, queue, mac_queue, ssn, &cfg,
|
|
|
- wdg_timeout);
|
|
|
+ inc_ssn = iwl_mvm_enable_txq(mvm, queue, mac_queue,
|
|
|
+ ssn, &cfg, wdg_timeout);
|
|
|
+ if (inc_ssn) {
|
|
|
+ ssn = (ssn + 1) & IEEE80211_SCTL_SEQ;
|
|
|
+ le16_add_cpu(&hdr->seq_ctrl, 0x10);
|
|
|
+ }
|
|
|
|
|
|
/*
|
|
|
* Mark queue as shared in transport if shared
|
|
@@ -898,6 +901,13 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
|
|
|
iwl_trans_txq_set_shared_mode(mvm->trans, queue, true);
|
|
|
|
|
|
spin_lock_bh(&mvmsta->lock);
|
|
|
+ /*
|
|
|
+ * This looks racy, but it is not. We have only one packet for
|
|
|
+ * this ra/tid in our Tx path since we stop the Qdisc when we
|
|
|
+ * need to allocate a new TFD queue.
|
|
|
+ */
|
|
|
+ if (inc_ssn)
|
|
|
+ mvmsta->tid_data[tid].seq_number += 0x10;
|
|
|
mvmsta->tid_data[tid].txq_id = queue;
|
|
|
mvmsta->tid_data[tid].is_tid_active = true;
|
|
|
mvmsta->tfd_queue_msk |= BIT(queue);
|
|
@@ -1993,8 +2003,6 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
|
|
|
mvm->probe_queue = queue;
|
|
|
else if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
|
|
|
mvm->p2p_dev_queue = queue;
|
|
|
-
|
|
|
- bsta->tfd_queue_msk |= BIT(queue);
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -2004,29 +2012,32 @@ static void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm,
|
|
|
struct ieee80211_vif *vif)
|
|
|
{
|
|
|
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
|
|
+ int queue;
|
|
|
|
|
|
lockdep_assert_held(&mvm->mutex);
|
|
|
|
|
|
iwl_mvm_flush_sta(mvm, &mvmvif->bcast_sta, true, 0);
|
|
|
|
|
|
- if (vif->type == NL80211_IFTYPE_AP ||
|
|
|
- vif->type == NL80211_IFTYPE_ADHOC)
|
|
|
- iwl_mvm_disable_txq(mvm, vif->cab_queue, vif->cab_queue,
|
|
|
- IWL_MAX_TID_COUNT, 0);
|
|
|
-
|
|
|
- if (mvmvif->bcast_sta.tfd_queue_msk & BIT(mvm->probe_queue)) {
|
|
|
- iwl_mvm_disable_txq(mvm, mvm->probe_queue,
|
|
|
- vif->hw_queue[0], IWL_MAX_TID_COUNT,
|
|
|
- 0);
|
|
|
- mvmvif->bcast_sta.tfd_queue_msk &= ~BIT(mvm->probe_queue);
|
|
|
+ switch (vif->type) {
|
|
|
+ case NL80211_IFTYPE_AP:
|
|
|
+ case NL80211_IFTYPE_ADHOC:
|
|
|
+ queue = mvm->probe_queue;
|
|
|
+ break;
|
|
|
+ case NL80211_IFTYPE_P2P_DEVICE:
|
|
|
+ queue = mvm->p2p_dev_queue;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ WARN(1, "Can't free bcast queue on vif type %d\n",
|
|
|
+ vif->type);
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- if (mvmvif->bcast_sta.tfd_queue_msk & BIT(mvm->p2p_dev_queue)) {
|
|
|
- iwl_mvm_disable_txq(mvm, mvm->p2p_dev_queue,
|
|
|
- vif->hw_queue[0], IWL_MAX_TID_COUNT,
|
|
|
- 0);
|
|
|
- mvmvif->bcast_sta.tfd_queue_msk &= ~BIT(mvm->p2p_dev_queue);
|
|
|
- }
|
|
|
+ iwl_mvm_disable_txq(mvm, queue, vif->hw_queue[0], IWL_MAX_TID_COUNT, 0);
|
|
|
+ if (iwl_mvm_has_new_tx_api(mvm))
|
|
|
+ return;
|
|
|
+
|
|
|
+ WARN_ON(!(mvmvif->bcast_sta.tfd_queue_msk & BIT(queue)));
|
|
|
+ mvmvif->bcast_sta.tfd_queue_msk &= ~BIT(queue);
|
|
|
}
|
|
|
|
|
|
/* Send the FW a request to remove the station from it's internal data
|
|
@@ -2529,6 +2540,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
|
|
{
|
|
|
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
|
|
|
struct iwl_mvm_tid_data *tid_data;
|
|
|
+ u16 normalized_ssn;
|
|
|
int txq_id;
|
|
|
int ret;
|
|
|
|
|
@@ -2616,7 +2628,15 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
|
|
mvmsta->sta_id, tid, txq_id, tid_data->ssn,
|
|
|
tid_data->next_reclaimed);
|
|
|
|
|
|
- if (tid_data->ssn == tid_data->next_reclaimed) {
|
|
|
+ /*
|
|
|
+ * In A000 HW, the next_reclaimed index is only 8 bit, so we'll need
|
|
|
+ * to align the wrap around of ssn so we compare relevant values.
|
|
|
+ */
|
|
|
+ normalized_ssn = tid_data->ssn;
|
|
|
+ if (mvm->trans->cfg->gen2)
|
|
|
+ normalized_ssn &= 0xff;
|
|
|
+
|
|
|
+ if (normalized_ssn == tid_data->next_reclaimed) {
|
|
|
tid_data->state = IWL_AGG_STARTING;
|
|
|
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
|
|
|
} else {
|
|
@@ -2814,8 +2834,13 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
|
|
"ssn = %d, next_recl = %d\n",
|
|
|
tid_data->ssn, tid_data->next_reclaimed);
|
|
|
|
|
|
- /* There are still packets for this RA / TID in the HW */
|
|
|
- if (tid_data->ssn != tid_data->next_reclaimed) {
|
|
|
+ /*
|
|
|
+ * There are still packets for this RA / TID in the HW.
|
|
|
+ * Not relevant for DQA mode, since there is no need to disable
|
|
|
+ * the queue.
|
|
|
+ */
|
|
|
+ if (!iwl_mvm_is_dqa_supported(mvm) &&
|
|
|
+ tid_data->ssn != tid_data->next_reclaimed) {
|
|
|
tid_data->state = IWL_EMPTYING_HW_QUEUE_DELBA;
|
|
|
err = 0;
|
|
|
break;
|
|
@@ -2888,14 +2913,17 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
|
|
|
|
|
if (old_state >= IWL_AGG_ON) {
|
|
|
iwl_mvm_drain_sta(mvm, mvmsta, true);
|
|
|
- if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), 0))
|
|
|
- IWL_ERR(mvm, "Couldn't flush the AGG queue\n");
|
|
|
|
|
|
- if (iwl_mvm_has_new_tx_api(mvm))
|
|
|
+ if (iwl_mvm_has_new_tx_api(mvm)) {
|
|
|
+ if (iwl_mvm_flush_sta_tids(mvm, mvmsta->sta_id,
|
|
|
+ BIT(tid), 0))
|
|
|
+ IWL_ERR(mvm, "Couldn't flush the AGG queue\n");
|
|
|
iwl_trans_wait_txq_empty(mvm->trans, txq_id);
|
|
|
-
|
|
|
- else
|
|
|
+ } else {
|
|
|
+ if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), 0))
|
|
|
+ IWL_ERR(mvm, "Couldn't flush the AGG queue\n");
|
|
|
iwl_trans_wait_tx_queues_empty(mvm->trans, BIT(txq_id));
|
|
|
+ }
|
|
|
|
|
|
iwl_mvm_drain_sta(mvm, mvmsta, false);
|
|
|
|
|
@@ -2975,7 +3003,7 @@ static struct iwl_mvm_sta *iwl_mvm_get_key_sta(struct iwl_mvm *mvm,
|
|
|
}
|
|
|
|
|
|
static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
|
|
|
- struct iwl_mvm_sta *mvm_sta,
|
|
|
+ u32 sta_id,
|
|
|
struct ieee80211_key_conf *key, bool mcast,
|
|
|
u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags,
|
|
|
u8 key_offset)
|
|
@@ -2993,6 +3021,9 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
|
|
|
bool new_api = fw_has_api(&mvm->fw->ucode_capa,
|
|
|
IWL_UCODE_TLV_API_TKIP_MIC_KEYS);
|
|
|
|
|
|
+ if (sta_id == IWL_MVM_INVALID_STA)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
keyidx = (key->keyidx << STA_KEY_FLG_KEYID_POS) &
|
|
|
STA_KEY_FLG_KEYID_MSK;
|
|
|
key_flags = cpu_to_le16(keyidx);
|
|
@@ -3051,7 +3082,7 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
|
|
|
|
|
|
u.cmd.common.key_offset = key_offset;
|
|
|
u.cmd.common.key_flags = key_flags;
|
|
|
- u.cmd.common.sta_id = mvm_sta->sta_id;
|
|
|
+ u.cmd.common.sta_id = sta_id;
|
|
|
|
|
|
if (new_api) {
|
|
|
u.cmd.transmit_seq_cnt = cpu_to_le64(pn);
|
|
@@ -3184,19 +3215,37 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
|
|
|
u8 key_offset,
|
|
|
bool mcast)
|
|
|
{
|
|
|
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
|
|
|
int ret;
|
|
|
const u8 *addr;
|
|
|
struct ieee80211_key_seq seq;
|
|
|
u16 p1k[5];
|
|
|
+ u32 sta_id;
|
|
|
+
|
|
|
+ if (sta) {
|
|
|
+ struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
|
|
|
+
|
|
|
+ sta_id = mvm_sta->sta_id;
|
|
|
+ } else if (vif->type == NL80211_IFTYPE_AP &&
|
|
|
+ !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
|
|
|
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
|
|
+
|
|
|
+ sta_id = mvmvif->mcast_sta.sta_id;
|
|
|
+ } else {
|
|
|
+ IWL_ERR(mvm, "Failed to find station id\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
|
|
|
switch (keyconf->cipher) {
|
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
|
|
+ if (vif->type == NL80211_IFTYPE_AP) {
|
|
|
+ ret = -EINVAL;
|
|
|
+ break;
|
|
|
+ }
|
|
|
addr = iwl_mvm_get_mac_addr(mvm, vif, sta);
|
|
|
/* get phase 1 key from mac80211 */
|
|
|
ieee80211_get_key_rx_seq(keyconf, 0, &seq);
|
|
|
ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
|
|
|
- ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
|
|
|
+ ret = iwl_mvm_send_sta_key(mvm, sta_id, keyconf, mcast,
|
|
|
seq.tkip.iv32, p1k, 0, key_offset);
|
|
|
break;
|
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
@@ -3204,11 +3253,11 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
|
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
|
|
case WLAN_CIPHER_SUITE_GCMP:
|
|
|
case WLAN_CIPHER_SUITE_GCMP_256:
|
|
|
- ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
|
|
|
+ ret = iwl_mvm_send_sta_key(mvm, sta_id, keyconf, mcast,
|
|
|
0, NULL, 0, key_offset);
|
|
|
break;
|
|
|
default:
|
|
|
- ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
|
|
|
+ ret = iwl_mvm_send_sta_key(mvm, sta_id, keyconf, mcast,
|
|
|
0, NULL, 0, key_offset);
|
|
|
}
|
|
|
|
|
@@ -3229,6 +3278,9 @@ static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id,
|
|
|
int ret, size;
|
|
|
u32 status;
|
|
|
|
|
|
+ if (sta_id == IWL_MVM_INVALID_STA)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
|
|
|
STA_KEY_FLG_KEYID_MSK);
|
|
|
key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP);
|
|
@@ -3272,42 +3324,48 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
|
|
|
{
|
|
|
bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
|
|
|
struct iwl_mvm_sta *mvm_sta;
|
|
|
- u8 sta_id;
|
|
|
+ u8 sta_id = IWL_MVM_INVALID_STA;
|
|
|
int ret;
|
|
|
static const u8 __maybe_unused zero_addr[ETH_ALEN] = {0};
|
|
|
|
|
|
lockdep_assert_held(&mvm->mutex);
|
|
|
|
|
|
- /* Get the station id from the mvm local station table */
|
|
|
- mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta);
|
|
|
- if (!mvm_sta) {
|
|
|
- IWL_ERR(mvm, "Failed to find station\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- sta_id = mvm_sta->sta_id;
|
|
|
+ if (vif->type != NL80211_IFTYPE_AP ||
|
|
|
+ keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
|
|
|
+ /* Get the station id from the mvm local station table */
|
|
|
+ mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta);
|
|
|
+ if (!mvm_sta) {
|
|
|
+ IWL_ERR(mvm, "Failed to find station\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ sta_id = mvm_sta->sta_id;
|
|
|
|
|
|
- if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
|
|
|
- keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
|
|
|
- keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256) {
|
|
|
- ret = iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, false);
|
|
|
- goto end;
|
|
|
- }
|
|
|
+ if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
|
|
|
+ keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
|
|
|
+ keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256) {
|
|
|
+ ret = iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id,
|
|
|
+ false);
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
|
|
|
- /*
|
|
|
- * It is possible that the 'sta' parameter is NULL, and thus
|
|
|
- * there is a need to retrieve the sta from the local station table.
|
|
|
- */
|
|
|
- if (!sta) {
|
|
|
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
|
|
|
- lockdep_is_held(&mvm->mutex));
|
|
|
- if (IS_ERR_OR_NULL(sta)) {
|
|
|
- IWL_ERR(mvm, "Invalid station id\n");
|
|
|
- return -EINVAL;
|
|
|
+ /*
|
|
|
+ * It is possible that the 'sta' parameter is NULL, and thus
|
|
|
+ * there is a need to retrieve the sta from the local station
|
|
|
+ * table.
|
|
|
+ */
|
|
|
+ if (!sta) {
|
|
|
+ sta = rcu_dereference_protected(
|
|
|
+ mvm->fw_id_to_mac_id[sta_id],
|
|
|
+ lockdep_is_held(&mvm->mutex));
|
|
|
+ if (IS_ERR_OR_NULL(sta)) {
|
|
|
+ IWL_ERR(mvm, "Invalid station id\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif))
|
|
|
- return -EINVAL;
|
|
|
+ if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif))
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
|
|
|
/* If the key_offset is not pre-assigned, we need to find a
|
|
|
* new offset to use. In normal cases, the offset is not
|
|
@@ -3337,8 +3395,9 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
|
|
|
* to the same key slot (offset).
|
|
|
* If this fails, remove the original as well.
|
|
|
*/
|
|
|
- if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
|
|
|
- keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) {
|
|
|
+ if ((keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
|
|
|
+ keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) &&
|
|
|
+ sta) {
|
|
|
ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf,
|
|
|
key_offset, !mcast);
|
|
|
if (ret) {
|
|
@@ -3372,6 +3431,9 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
|
|
|
mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta);
|
|
|
if (mvm_sta)
|
|
|
sta_id = mvm_sta->sta_id;
|
|
|
+ else if (!sta && vif->type == NL80211_IFTYPE_AP && mcast)
|
|
|
+ sta_id = iwl_mvm_vif_from_mac80211(vif)->mcast_sta.sta_id;
|
|
|
+
|
|
|
|
|
|
IWL_DEBUG_WEP(mvm, "mvm remove dynamic key: idx=%d sta=%d\n",
|
|
|
keyconf->keyidx, sta_id);
|
|
@@ -3394,7 +3456,7 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
|
|
|
}
|
|
|
mvm->fw_key_deleted[keyconf->hw_key_idx] = 0;
|
|
|
|
|
|
- if (!mvm_sta) {
|
|
|
+ if (sta && !mvm_sta) {
|
|
|
IWL_DEBUG_WEP(mvm, "station non-existent, early return.\n");
|
|
|
return 0;
|
|
|
}
|
|
@@ -3425,7 +3487,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
|
|
|
mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta);
|
|
|
if (WARN_ON_ONCE(!mvm_sta))
|
|
|
goto unlock;
|
|
|
- iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
|
|
|
+ iwl_mvm_send_sta_key(mvm, mvm_sta->sta_id, keyconf, mcast,
|
|
|
iv32, phase1key, CMD_ASYNC, keyconf->hw_key_idx);
|
|
|
|
|
|
unlock:
|
|
@@ -3501,7 +3563,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- n_queued = iwl_mvm_tid_queued(tid_data);
|
|
|
+ n_queued = iwl_mvm_tid_queued(mvm, tid_data);
|
|
|
if (n_queued > remaining) {
|
|
|
more_data = true;
|
|
|
remaining = 0;
|
|
@@ -3683,3 +3745,17 @@ void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
|
|
|
|
|
|
rcu_read_unlock();
|
|
|
}
|
|
|
+
|
|
|
+u16 iwl_mvm_tid_queued(struct iwl_mvm *mvm, struct iwl_mvm_tid_data *tid_data)
|
|
|
+{
|
|
|
+ u16 sn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * In A000 HW, the next_reclaimed index is only 8 bit, so we'll need
|
|
|
+ * to align the wrap around of ssn so we compare relevant values.
|
|
|
+ */
|
|
|
+ if (mvm->trans->cfg->gen2)
|
|
|
+ sn &= 0xff;
|
|
|
+
|
|
|
+ return ieee80211_sn_sub(sn, tid_data->next_reclaimed);
|
|
|
+}
|