summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/tx.c')
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tx.c178
1 files changed, 121 insertions, 57 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
index f21901cd4a4f..60360ed73f26 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
@@ -105,9 +105,9 @@ iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr,
static u16 iwl_mvm_tx_csum(struct iwl_mvm *mvm, struct sk_buff *skb,
struct ieee80211_hdr *hdr,
- struct ieee80211_tx_info *info)
+ struct ieee80211_tx_info *info,
+ u16 offload_assist)
{
- u16 offload_assist = 0;
#if IS_ENABLED(CONFIG_INET)
u16 mh_len = ieee80211_hdrlen(hdr->frame_control);
u8 protocol = 0;
@@ -207,6 +207,7 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
__le16 fc = hdr->frame_control;
u32 tx_flags = le32_to_cpu(tx_cmd->tx_flags);
u32 len = skb->len + FCS_LEN;
+ u16 offload_assist = 0;
u8 ac;
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
@@ -225,8 +226,7 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
tx_cmd->tid_tspec = qc[0] & 0xf;
tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT)
- tx_cmd->offload_assist |=
- cpu_to_le16(BIT(TX_CMD_OFFLD_AMSDU));
+ offload_assist |= BIT(TX_CMD_OFFLD_AMSDU);
} else if (ieee80211_is_back_req(fc)) {
struct ieee80211_bar *bar = (void *)skb->data;
u16 control = le16_to_cpu(bar->control);
@@ -291,11 +291,12 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
/* padding is inserted later in transport */
if (ieee80211_hdrlen(fc) % 4 &&
- !(tx_cmd->offload_assist & cpu_to_le16(BIT(TX_CMD_OFFLD_AMSDU))))
- tx_cmd->offload_assist |= cpu_to_le16(BIT(TX_CMD_OFFLD_PAD));
+ !(offload_assist & BIT(TX_CMD_OFFLD_AMSDU)))
+ offload_assist |= BIT(TX_CMD_OFFLD_PAD);
tx_cmd->offload_assist |=
- cpu_to_le16(iwl_mvm_tx_csum(mvm, skb, hdr, info));
+ cpu_to_le16(iwl_mvm_tx_csum(mvm, skb, hdr, info,
+ offload_assist));
}
static u32 iwl_mvm_get_tx_rate(struct iwl_mvm *mvm,
@@ -473,15 +474,27 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
if (unlikely(!dev_cmd))
return NULL;
- memset(dev_cmd, 0, sizeof(*dev_cmd));
+ /* Make sure we zero enough of dev_cmd */
+ BUILD_BUG_ON(sizeof(struct iwl_tx_cmd_gen2) > sizeof(*tx_cmd));
+
+ memset(dev_cmd, 0, sizeof(dev_cmd->hdr) + sizeof(*tx_cmd));
dev_cmd->hdr.cmd = TX_CMD;
if (iwl_mvm_has_new_tx_api(mvm)) {
struct iwl_tx_cmd_gen2 *cmd = (void *)dev_cmd->payload;
- u16 offload_assist = iwl_mvm_tx_csum(mvm, skb, hdr, info);
+ u16 offload_assist = 0;
+
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ u8 *qc = ieee80211_get_qos_ctl(hdr);
+
+ if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT)
+ offload_assist |= BIT(TX_CMD_OFFLD_AMSDU);
+ }
+
+ offload_assist = iwl_mvm_tx_csum(mvm, skb, hdr, info,
+ offload_assist);
/* padding is inserted later in transport */
- /* FIXME - check for AMSDU may need to be removed */
if (ieee80211_hdrlen(hdr->frame_control) % 4 &&
!(offload_assist & BIT(TX_CMD_OFFLD_AMSDU)))
offload_assist |= BIT(TX_CMD_OFFLD_PAD);
@@ -538,22 +551,32 @@ static void iwl_mvm_skb_prepare_status(struct sk_buff *skb,
static int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm,
struct ieee80211_tx_info *info, __le16 fc)
{
+ struct iwl_mvm_vif *mvmvif;
+
if (!iwl_mvm_is_dqa_supported(mvm))
return info->hw_queue;
+ mvmvif = iwl_mvm_vif_from_mac80211(info->control.vif);
+
switch (info->control.vif->type) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_ADHOC:
/*
- * Handle legacy hostapd as well, where station may be added
- * only after assoc. Take care of the case where we send a
- * deauth to a station that we don't have.
+ * Handle legacy hostapd as well, where station will be added
+ * only just before sending the association response.
+ * Also take care of the case where we send a deauth to a
+ * station that we don't have, or similarly an association
+ * response (with non-success status) for a station we can't
+ * accept.
+ * Also, disassociate frames might happen, particular with
+ * reason 7 ("Class 3 frame received from nonassociated STA").
*/
if (ieee80211_is_probe_resp(fc) || ieee80211_is_auth(fc) ||
- ieee80211_is_deauth(fc))
+ ieee80211_is_deauth(fc) || ieee80211_is_assoc_resp(fc) ||
+ ieee80211_is_disassoc(fc))
return mvm->probe_queue;
if (info->hw_queue == info->control.vif->cab_queue)
- return info->hw_queue;
+ return mvmvif->cab_queue;
WARN_ONCE(info->control.vif->type != NL80211_IFTYPE_ADHOC,
"fc=0x%02x", le16_to_cpu(fc));
@@ -562,7 +585,7 @@ static int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm,
if (ieee80211_is_mgmt(fc))
return mvm->p2p_dev_queue;
if (info->hw_queue == info->control.vif->cab_queue)
- return info->hw_queue;
+ return mvmvif->cab_queue;
WARN_ON_ONCE(1);
return mvm->p2p_dev_queue;
@@ -611,10 +634,6 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
* (this is not possible for unicast packets as a TLDS discovery
* response are sent without a station entry); otherwise use the
* AUX station.
- * In DQA mode, if vif is of type STATION and frames are not multicast
- * or offchannel, they should be sent from the BSS queue.
- * For example, TDLS setup frames should be sent on this queue,
- * as they go through the AP.
*/
sta_id = mvm->aux_sta.sta_id;
if (info.control.vif) {
@@ -629,9 +648,6 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
hdr->frame_control);
if (queue < 0)
return -1;
-
- if (queue == info.control.vif->cab_queue)
- queue = mvmvif->cab_queue;
} else if (info.control.vif->type == NL80211_IFTYPE_STATION &&
is_multicast_ether_addr(hdr->addr1)) {
u8 ap_sta_id = ACCESS_ONCE(mvmvif->ap_sta_id);
@@ -639,9 +655,8 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
if (ap_sta_id != IWL_MVM_INVALID_STA)
sta_id = ap_sta_id;
} else if (iwl_mvm_is_dqa_supported(mvm) &&
- info.control.vif->type == NL80211_IFTYPE_STATION &&
- queue != mvm->aux_queue) {
- queue = IWL_MVM_DQA_BSS_CLIENT_QUEUE;
+ info.control.vif->type == NL80211_IFTYPE_MONITOR) {
+ queue = mvm->aux_queue;
}
}
@@ -695,11 +710,6 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
snap_ip_tcp = 8 + skb_transport_header(skb) - skb_network_header(skb) +
tcp_hdrlen(skb);
- qc = ieee80211_get_qos_ctl(hdr);
- tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
- if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
- return -EINVAL;
-
dbg_max_amsdu_len = ACCESS_ONCE(mvm->max_amsdu_len);
if (!sta->max_amsdu_len ||
@@ -710,6 +720,11 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
goto segment;
}
+ qc = ieee80211_get_qos_ctl(hdr);
+ tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
+ if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
+ return -EINVAL;
+
/*
* Do not build AMSDU for IPv6 with extension headers.
* ask stack to segment and checkum the generated MPDUs for us.
@@ -829,11 +844,13 @@ segment:
if (tcp_payload_len > mss) {
skb_shinfo(tmp)->gso_size = mss;
} else {
- qc = ieee80211_get_qos_ctl((void *)tmp->data);
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ qc = ieee80211_get_qos_ctl((void *)tmp->data);
- if (ipv4)
- ip_send_check(ip_hdr(tmp));
- *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+ if (ipv4)
+ ip_send_check(ip_hdr(tmp));
+ *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+ }
skb_shinfo(tmp)->gso_size = 0;
}
@@ -1120,13 +1137,14 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
struct ieee80211_vif *vif = mvmsta->vif;
+ u16 normalized_ssn;
lockdep_assert_held(&mvmsta->lock);
if ((tid_data->state == IWL_AGG_ON ||
tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA ||
iwl_mvm_is_dqa_supported(mvm)) &&
- iwl_mvm_tid_queued(tid_data) == 0) {
+ iwl_mvm_tid_queued(mvm, tid_data) == 0) {
/*
* Now that this aggregation or DQA queue is empty tell
* mac80211 so it knows we no longer have frames buffered for
@@ -1135,7 +1153,15 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
ieee80211_sta_set_buffered(sta, tid, false);
}
- 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)
return;
switch (tid_data->state) {
@@ -1313,6 +1339,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
struct ieee80211_sta *sta;
u16 sequence = le16_to_cpu(pkt->hdr.sequence);
int txq_id = SEQ_TO_QUEUE(sequence);
+ /* struct iwl_mvm_tx_resp_v3 is almost the same */
struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid);
int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid);
@@ -1330,7 +1357,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
__skb_queue_head_init(&skbs);
if (iwl_mvm_has_new_tx_api(mvm))
- txq_id = le16_to_cpu(tx_resp->v6.tx_queue);
+ txq_id = le16_to_cpu(tx_resp->tx_queue);
seq_ctl = le16_to_cpu(tx_resp->seq_ctl);
@@ -1479,7 +1506,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
if (mvmsta->sleep_tx_count) {
mvmsta->sleep_tx_count--;
if (mvmsta->sleep_tx_count &&
- !iwl_mvm_tid_queued(tid_data)) {
+ !iwl_mvm_tid_queued(mvm, tid_data)) {
/*
* The number of frames in the queue
* dropped to 0 even if we sent less
@@ -1791,6 +1818,7 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
if (iwl_mvm_has_new_tx_api(mvm)) {
struct iwl_mvm_compressed_ba_notif *ba_res =
(void *)pkt->data;
+ int i;
sta_id = ba_res->sta_id;
ba_info.status.ampdu_ack_len = (u8)le16_to_cpu(ba_res->done);
@@ -1803,22 +1831,17 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
if (!le16_to_cpu(ba_res->tfd_cnt))
goto out;
- /*
- * TODO:
- * When supporting multi TID aggregations - we need to move
- * next_reclaimed to be per TXQ and not per TID or handle it
- * in a different way.
- * This will go together with SN and AddBA offload and cannot
- * be handled properly for now.
- */
- WARN_ON(le16_to_cpu(ba_res->ra_tid_cnt) != 1);
- tid = ba_res->ra_tid[0].tid;
- if (tid == IWL_MGMT_TID)
- tid = IWL_MAX_TID_COUNT;
- iwl_mvm_tx_reclaim(mvm, sta_id, tid,
- (int)(le16_to_cpu(ba_res->tfd[0].q_num)),
- le16_to_cpu(ba_res->tfd[0].tfd_index),
- &ba_info, le32_to_cpu(ba_res->tx_rate));
+ /* Free per TID */
+ for (i = 0; i < le16_to_cpu(ba_res->tfd_cnt); i++) {
+ struct iwl_mvm_compressed_ba_tfd *ba_tfd =
+ &ba_res->tfd[i];
+
+ iwl_mvm_tx_reclaim(mvm, sta_id, ba_tfd->tid,
+ (int)(le16_to_cpu(ba_tfd->q_num)),
+ le16_to_cpu(ba_tfd->tfd_index),
+ &ba_info,
+ le32_to_cpu(ba_res->tx_rate));
+ }
out:
IWL_DEBUG_TX_REPLY(mvm,
@@ -1860,7 +1883,7 @@ out:
IWL_DEBUG_TX_REPLY(mvm,
"BA_NOTIFICATION Received from %pM, sta_id = %d\n",
- (u8 *)&ba_notif->sta_addr_lo32, ba_notif->sta_id);
+ ba_notif->sta_addr, ba_notif->sta_id);
IWL_DEBUG_TX_REPLY(mvm,
"TID = %d, SeqCtl = %d, bitmap = 0x%llx, scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n",
@@ -1883,14 +1906,55 @@ out:
int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags)
{
int ret;
- struct iwl_tx_path_flush_cmd flush_cmd = {
+ struct iwl_tx_path_flush_cmd_v1 flush_cmd = {
.queues_ctl = cpu_to_le32(tfd_msk),
.flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH),
};
+ WARN_ON(iwl_mvm_has_new_tx_api(mvm));
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags,
+ sizeof(flush_cmd), &flush_cmd);
+ if (ret)
+ IWL_ERR(mvm, "Failed to send flush command (%d)\n", ret);
+ return ret;
+}
+
+int iwl_mvm_flush_sta_tids(struct iwl_mvm *mvm, u32 sta_id,
+ u16 tids, u32 flags)
+{
+ int ret;
+ struct iwl_tx_path_flush_cmd flush_cmd = {
+ .sta_id = cpu_to_le32(sta_id),
+ .tid_mask = cpu_to_le16(tids),
+ };
+
+ WARN_ON(!iwl_mvm_has_new_tx_api(mvm));
+
ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags,
sizeof(flush_cmd), &flush_cmd);
if (ret)
IWL_ERR(mvm, "Failed to send flush command (%d)\n", ret);
return ret;
}
+
+int iwl_mvm_flush_sta(struct iwl_mvm *mvm, void *sta, bool internal, u32 flags)
+{
+ struct iwl_mvm_int_sta *int_sta = sta;
+ struct iwl_mvm_sta *mvm_sta = sta;
+
+ if (iwl_mvm_has_new_tx_api(mvm)) {
+ if (internal)
+ return iwl_mvm_flush_sta_tids(mvm, int_sta->sta_id,
+ BIT(IWL_MGMT_TID), flags);
+
+ return iwl_mvm_flush_sta_tids(mvm, mvm_sta->sta_id,
+ 0xFF, flags);
+ }
+
+ if (internal)
+ return iwl_mvm_flush_tx_path(mvm, int_sta->tfd_queue_msk,
+ flags);
+
+ return iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, flags);
+}
OpenPOWER on IntegriCloud