diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/tx.c')
| -rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/tx.c | 218 |
1 files changed, 124 insertions, 94 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index f0e96a92740..3846a6c41eb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -79,6 +79,7 @@ static 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; + u8 ac; if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) tx_flags |= TX_CMD_FLG_ACK; @@ -90,14 +91,6 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, else if (ieee80211_is_back_req(fc)) tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR; - /* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */ - if (info->band == IEEE80211_BAND_2GHZ && - (skb->protocol == cpu_to_be16(ETH_P_PAE) || - is_multicast_ether_addr(hdr->addr1) || - ieee80211_is_back_req(fc) || - ieee80211_is_mgmt(fc))) - tx_flags |= TX_CMD_FLG_BT_DIS; - if (ieee80211_has_morefrags(fc)) tx_flags |= TX_CMD_FLG_MORE_FRAG; @@ -113,6 +106,11 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, tx_flags &= ~TX_CMD_FLG_SEQ_CTL; } + /* tid_tspec will default to 0 = BE when QOS isn't enabled */ + ac = tid_to_mac80211_ac[tx_cmd->tid_tspec]; + tx_flags |= iwl_mvm_bt_coex_tx_prio(mvm, hdr, info, ac) << + TX_CMD_FLG_BT_PRIO_POS; + if (ieee80211_is_mgmt(fc)) { if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) tx_cmd->pm_frame_timeout = cpu_to_le16(3); @@ -123,13 +121,12 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, * it */ WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU); + } else if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) { + tx_cmd->pm_frame_timeout = cpu_to_le16(2); } else { tx_cmd->pm_frame_timeout = 0; } - if (info->flags & IEEE80211_TX_CTL_AMPDU) - tx_flags |= TX_CMD_FLG_PROT_REQUIRE; - if (ieee80211_is_data(fc) && len > mvm->rts_threshold && !is_multicast_ether_addr(ieee80211_get_DA(hdr))) tx_flags |= TX_CMD_FLG_PROT_REQUIRE; @@ -171,7 +168,7 @@ static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, } /* - * for data packets, rate info comes from the table inside he fw. This + * for data packets, rate info comes from the table inside the fw. This * table is controlled by LINK_QUALITY commands */ @@ -206,7 +203,7 @@ static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx); mvm->mgmt_last_antenna_idx = - iwl_mvm_next_antenna(mvm, iwl_fw_valid_tx_ant(mvm->fw), + iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant, mvm->mgmt_last_antenna_idx); rate_flags = BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS; @@ -252,8 +249,7 @@ static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm, memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); break; default: - IWL_ERR(mvm, "Unknown encode cipher %x\n", keyconf->cipher); - break; + tx_cmd->sec_ctl |= TX_CMD_SEC_EXT; } } @@ -275,6 +271,7 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, return NULL; memset(dev_cmd, 0, sizeof(*dev_cmd)); + dev_cmd->hdr.cmd = TX_CMD; tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; if (info->control.hw_key) @@ -360,7 +357,7 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, u8 txq_id = info->hw_queue; bool is_data_qos = false, is_ampdu = false; - mvmsta = (void *)sta->drv_priv; + mvmsta = iwl_mvm_sta_from_mac80211(sta); fc = hdr->frame_control; if (WARN_ON_ONCE(!mvmsta)) @@ -376,6 +373,13 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; /* From now on, we cannot access info->control */ + /* + * we handle that entirely ourselves -- for uAPSD the firmware + * will always send a notification, and for PS-Poll responses + * we'll notify mac80211 when getting frame status + */ + info->flags &= ~IEEE80211_TX_STATUS_EOSP; + spin_lock(&mvmsta->lock); if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { @@ -389,7 +393,6 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, seq_number &= IEEE80211_SCTL_SEQ; hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); hdr->seq_ctrl |= cpu_to_le16(seq_number); - seq_number += 0x10; is_data_qos = true; is_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU; } @@ -406,17 +409,17 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, } IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id, - tid, txq_id, seq_number); + tid, txq_id, IEEE80211_SEQ_TO_SN(seq_number)); if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id)) goto drop_unlock_sta; if (is_data_qos && !ieee80211_has_morefrags(fc)) - mvmsta->tid_data[tid].seq_number = seq_number; + mvmsta->tid_data[tid].seq_number = seq_number + 0x10; spin_unlock(&mvmsta->lock); - if (txq_id < IWL_MVM_FIRST_AGG_QUEUE) + if (txq_id < mvm->first_agg_queue) atomic_inc(&mvm->pending_frames[mvmsta->sta_id]); return 0; @@ -431,12 +434,23 @@ drop: static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, struct ieee80211_sta *sta, u8 tid) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + 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; lockdep_assert_held(&mvmsta->lock); + if ((tid_data->state == IWL_AGG_ON || + tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) && + iwl_mvm_tid_queued(tid_data) == 0) { + /* + * Now that this aggregation queue is empty tell mac80211 so it + * knows we no longer have frames buffered for the station on + * this TID (for the TIM bitmap calculation.) + */ + ieee80211_sta_set_buffered(sta, tid, false); + } + if (tid_data->ssn != tid_data->next_reclaimed) return; @@ -510,16 +524,10 @@ const char *iwl_mvm_get_tx_fail_reason(u32 status) } #endif /* CONFIG_IWLWIFI_DEBUG */ -/** - * translate ucode response to mac80211 tx status control values - */ -static void iwl_mvm_hwrate_to_tx_control(u32 rate_n_flags, - struct ieee80211_tx_info *info) +void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, + enum ieee80211_band band, + struct ieee80211_tx_rate *r) { - struct ieee80211_tx_rate *r = &info->status.rates[0]; - - info->status.antenna = - ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); if (rate_n_flags & RATE_HT_MCS_GF_MSK) r->flags |= IEEE80211_TX_RC_GREEN_FIELD; switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { @@ -548,10 +556,23 @@ static void iwl_mvm_hwrate_to_tx_control(u32 rate_n_flags, r->flags |= IEEE80211_TX_RC_VHT_MCS; } else { r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, - info->band); + band); } } +/** + * translate ucode response to mac80211 tx status control values + */ +static void iwl_mvm_hwrate_to_tx_status(u32 rate_n_flags, + struct ieee80211_tx_info *info) +{ + struct ieee80211_tx_rate *r = &info->status.rates[0]; + + info->status.antenna = + ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); + iwl_mvm_hwrate_to_tx_rate(rate_n_flags, info->band, r); +} + static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { @@ -601,11 +622,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, } info->status.rates[0].count = tx_resp->failure_frame + 1; - iwl_mvm_hwrate_to_tx_control(le32_to_cpu(tx_resp->initial_rate), - info); + iwl_mvm_hwrate_to_tx_status(le32_to_cpu(tx_resp->initial_rate), + info); /* Single frame failure in an AMPDU queue => send BAR */ - if (txq_id >= IWL_MVM_FIRST_AGG_QUEUE && + if (txq_id >= mvm->first_agg_queue && !(info->flags & IEEE80211_TX_STAT_ACK)) info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; @@ -615,10 +636,14 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, seq_ctl = le16_to_cpu(hdr->seq_ctrl); } - ieee80211_tx_status_ni(mvm->hw, skb); + BUILD_BUG_ON(ARRAY_SIZE(info->status.status_driver_data) < 1); + info->status.status_driver_data[0] = + (void *)(uintptr_t)tx_resp->reduced_tpc; + + ieee80211_tx_status(mvm->hw, skb); } - if (txq_id >= IWL_MVM_FIRST_AGG_QUEUE) { + if (txq_id >= mvm->first_agg_queue) { /* If this is an aggregation queue, we use the ssn since: * ssn = wifi seq_num % 256. * The seq_ctl is the sequence control of the packet to which @@ -652,9 +677,15 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, rcu_read_lock(); sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + /* + * sta can't be NULL otherwise it'd mean that the sta has been freed in + * the firmware while we still have packets for it in the Tx queues. + */ + if (WARN_ON_ONCE(!sta)) + goto out; - if (!IS_ERR_OR_NULL(sta)) { - mvmsta = (void *)sta->drv_priv; + if (!IS_ERR(sta)) { + mvmsta = iwl_mvm_sta_from_mac80211(sta); if (tid != IWL_TID_NON_QOS) { struct iwl_mvm_tid_data *tid_data = @@ -668,11 +699,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, spin_unlock_bh(&mvmsta->lock); } -#ifdef CONFIG_PM_SLEEP - mvmsta->last_seq_ctl = seq_ctl; -#endif + if (mvmsta->next_status_eosp) { + mvmsta->next_status_eosp = false; + ieee80211_sta_eosp(sta); + } } else { - sta = NULL; mvmsta = NULL; } @@ -680,42 +711,38 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, * If the txq is not an AMPDU queue, there is no chance we freed * several skbs. Check that out... */ - if (txq_id < IWL_MVM_FIRST_AGG_QUEUE && !WARN_ON(skb_freed > 1) && - atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) { - if (mvmsta) { - /* - * If there are no pending frames for this STA, notify - * mac80211 that this station can go to sleep in its - * STA table. - */ - if (mvmsta->vif->type == NL80211_IFTYPE_AP) - ieee80211_sta_block_awake(mvm->hw, sta, false); - /* - * We might very well have taken mvmsta pointer while - * the station was being removed. The remove flow might - * have seen a pending_frame (because we didn't take - * the lock) even if now the queues are drained. So make - * really sure now that this the station is not being - * removed. If it is, run the drain worker to remove it. - */ - spin_lock_bh(&mvmsta->lock); - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - if (IS_ERR_OR_NULL(sta)) { - /* - * Station disappeared in the meantime: - * so we are draining. - */ - set_bit(sta_id, mvm->sta_drained); - schedule_work(&mvm->sta_drained_wk); - } - spin_unlock_bh(&mvmsta->lock); - } else if (!mvmsta) { - /* Tx response without STA, so we are draining */ - set_bit(sta_id, mvm->sta_drained); - schedule_work(&mvm->sta_drained_wk); - } + if (txq_id >= mvm->first_agg_queue) + goto out; + + /* We can't free more than one frame at once on a shared queue */ + WARN_ON(skb_freed > 1); + + /* If we have still frames from this STA nothing to do here */ + if (!atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) + goto out; + + if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) { + /* + * If there are no pending frames for this STA, notify + * mac80211 that this station can go to sleep in its + * STA table. + * If mvmsta is not NULL, sta is valid. + */ + ieee80211_sta_block_awake(mvm->hw, sta, false); } + if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) { + /* + * We are draining and this was the last packet - pre_rcu_remove + * has been called already. We might be after the + * synchronize_net already. + * Don't rely on iwl_mvm_rm_sta to see the empty Tx queues. + */ + set_bit(sta_id, mvm->sta_drained); + schedule_work(&mvm->sta_drained_wk); + } + +out: rcu_read_unlock(); } @@ -776,7 +803,7 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, u16 sequence = le16_to_cpu(pkt->hdr.sequence); struct ieee80211_sta *sta; - if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < IWL_MVM_FIRST_AGG_QUEUE)) + if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < mvm->first_agg_queue)) return; if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS)) @@ -789,9 +816,10 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); if (!WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); mvmsta->tid_data[tid].rate_n_flags = le32_to_cpu(tx_resp->initial_rate); + mvmsta->tid_data[tid].reduced_tpc = tx_resp->reduced_tpc; } rcu_read_unlock(); @@ -818,16 +846,12 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_mvm_ba_notif *ba_notif = (void *)pkt->data; struct sk_buff_head reclaimed_skbs; struct iwl_mvm_tid_data *tid_data; - struct ieee80211_tx_info *info; struct ieee80211_sta *sta; struct iwl_mvm_sta *mvmsta; - struct ieee80211_hdr *hdr; struct sk_buff *skb; int sta_id, tid, freed; - /* "flow" corresponds to Tx queue */ u16 scd_flow = le16_to_cpu(ba_notif->scd_flow); - /* "ssn" is start of block-ack Tx window, corresponds to index * (in Tx queue's circular buffer) of first TFD/frame in window */ u16 ba_resp_scd_ssn = le16_to_cpu(ba_notif->scd_ssn); @@ -845,7 +869,7 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, return 0; } - mvmsta = (void *)sta->drv_priv; + mvmsta = iwl_mvm_sta_from_mac80211(sta); tid_data = &mvmsta->tid_data[tid]; if (WARN_ONCE(tid_data->txq_id != scd_flow, "Q %d, tid %d, flow %d", @@ -884,27 +908,33 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, freed = 0; skb_queue_walk(&reclaimed_skbs, skb) { - hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (ieee80211_is_data_qos(hdr->frame_control)) freed++; else WARN_ON_ONCE(1); - info = IEEE80211_SKB_CB(skb); iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]); + memset(&info->status, 0, sizeof(info->status)); + /* Packet was transmitted successfully, failures come as single + * frames because before failing a frame the firmware transmits + * it without aggregation at least once. + */ + info->flags |= IEEE80211_TX_STAT_ACK; + if (freed == 1) { /* this is the first skb we deliver in this batch */ /* put the rate scaling data there */ - info = IEEE80211_SKB_CB(skb); - memset(&info->status, 0, sizeof(info->status)); - info->flags |= IEEE80211_TX_STAT_ACK; info->flags |= IEEE80211_TX_STAT_AMPDU; info->status.ampdu_ack_len = ba_notif->txed_2_done; info->status.ampdu_len = ba_notif->txed; - iwl_mvm_hwrate_to_tx_control(tid_data->rate_n_flags, - info); + iwl_mvm_hwrate_to_tx_status(tid_data->rate_n_flags, + info); + info->status.status_driver_data[0] = + (void *)(uintptr_t)tid_data->reduced_tpc; } } @@ -914,7 +944,7 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, while (!skb_queue_empty(&reclaimed_skbs)) { skb = __skb_dequeue(&reclaimed_skbs); - ieee80211_tx_status_ni(mvm->hw, skb); + ieee80211_tx_status(mvm->hw, skb); } return 0; @@ -928,7 +958,7 @@ int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync) .flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH), }; - u32 flags = sync ? CMD_SYNC : CMD_ASYNC; + u32 flags = sync ? 0 : CMD_ASYNC; ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags, sizeof(flush_cmd), &flush_cmd); |
