aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/staging/wfx/data_tx.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2020-06-07 10:45:08 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2020-06-07 10:45:08 -0700
commit80ef846e9909f22ccdc2a4a6d931266cecce8b2c (patch)
tree0dd29cff6e6b4ee38452fb73ad292203c3ddeabf /drivers/staging/wfx/data_tx.c
parentMerge tag 'tty-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty (diff)
parentstaging: rtl8723bs: Use common packet header constants (diff)
downloadwireguard-linux-80ef846e9909f22ccdc2a4a6d931266cecce8b2c.tar.xz
wireguard-linux-80ef846e9909f22ccdc2a4a6d931266cecce8b2c.zip
Merge tag 'staging-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging/IIO driver updates from Greg KH: "Here is the large set of staging and IIO driver changes for 5.8-rc1 Nothing major, but a lot of new IIO drivers are included in here, along with other core iio cleanups and changes. On the staging driver front, again, nothing noticable. No new deletions or additions, just a ton of tiny cleanups all over the tree done by a lot of different people. Most coding style, but many actual real fixes and cleanups that are nice to see. All of these have been in linux-next for a while with no reported issues" * tag 'staging-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (618 commits) staging: rtl8723bs: Use common packet header constants staging: sm750fb: Add names to proc_setBLANK args staging: most: usb: init return value in default path of switch/case expression staging: vchiq: Get rid of VCHIQ_SERVICE_OPENEND callback reason staging: vchiq: move vchiq_release_message() into vchiq staging: vchi: Get rid of C++ guards staging: vchi: Get rid of not implemented function declarations staging: vchi: Get rid of vchiq_status_to_vchi() staging: vchi: Get rid of vchi_service_set_option() staging: vchi: Merge vchi_msg_queue() into vchi_queue_kernel_message() staging: vchiq: Move copy callback handling into vchiq staging: vchi: Get rid of vchi_queue_user_message() staging: vchi: Get rid of vchi_service_destroy() staging: most: usb: use function sysfs_streq staging: most: usb: add missing put_device calls staging: most: usb: use correct error codes staging: most: usb: replace code to calculate array index staging: most: usb: don't use error path to exit function on success staging: most: usb: move allocation of URB out of critical section staging: most: usb: return 0 instead of variable ...
Diffstat (limited to 'drivers/staging/wfx/data_tx.c')
-rw-r--r--drivers/staging/wfx/data_tx.c352
1 files changed, 163 insertions, 189 deletions
diff --git a/drivers/staging/wfx/data_tx.c b/drivers/staging/wfx/data_tx.c
index 42183c70d4df..f042ef36b408 100644
--- a/drivers/staging/wfx/data_tx.c
+++ b/drivers/staging/wfx/data_tx.c
@@ -6,6 +6,7 @@
* Copyright (c) 2010, ST-Ericsson
*/
#include <net/mac80211.h>
+#include <linux/etherdevice.h>
#include "data_tx.h"
#include "wfx.h"
@@ -16,12 +17,11 @@
#include "traces.h"
#include "hif_tx_mib.h"
-#define WFX_INVALID_RATE_ID 15
-#define WFX_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ))
-
static int wfx_get_hw_rate(struct wfx_dev *wdev,
const struct ieee80211_tx_rate *rate)
{
+ struct ieee80211_supported_band *band;
+
if (rate->idx < 0)
return -1;
if (rate->flags & IEEE80211_TX_RC_MCS) {
@@ -33,7 +33,8 @@ static int wfx_get_hw_rate(struct wfx_dev *wdev,
}
// WFx only support 2GHz, else band information should be retrieved
// from ieee80211_tx_info
- return wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]->bitrates[rate->idx].hw_value;
+ band = wdev->hw->wiphy->bands[NL80211_BAND_2GHZ];
+ return band->bitrates[rate->idx].hw_value;
}
/* TX policy cache implementation */
@@ -41,73 +42,13 @@ static int wfx_get_hw_rate(struct wfx_dev *wdev,
static void wfx_tx_policy_build(struct wfx_vif *wvif, struct tx_policy *policy,
struct ieee80211_tx_rate *rates)
{
- int i;
- size_t count;
struct wfx_dev *wdev = wvif->wdev;
+ int i, rateid;
+ u8 count;
WARN(rates[0].idx < 0, "invalid rate policy");
memset(policy, 0, sizeof(*policy));
- for (i = 1; i < IEEE80211_TX_MAX_RATES; i++)
- if (rates[i].idx < 0)
- break;
- count = i;
-
- /* HACK!!! Device has problems (at least) switching from
- * 54Mbps CTS to 1Mbps. This switch takes enormous amount
- * of time (100-200 ms), leading to valuable throughput drop.
- * As a workaround, additional g-rates are injected to the
- * policy.
- */
- if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) &&
- rates[0].idx > 4 && rates[0].count > 2 &&
- rates[1].idx < 2) {
- int mid_rate = (rates[0].idx + 4) >> 1;
-
- /* Decrease number of retries for the initial rate */
- rates[0].count -= 2;
-
- if (mid_rate != 4) {
- /* Keep fallback rate at 1Mbps. */
- rates[3] = rates[1];
-
- /* Inject 1 transmission on lowest g-rate */
- rates[2].idx = 4;
- rates[2].count = 1;
- rates[2].flags = rates[1].flags;
-
- /* Inject 1 transmission on mid-rate */
- rates[1].idx = mid_rate;
- rates[1].count = 1;
-
- /* Fallback to 1 Mbps is a really bad thing,
- * so let's try to increase probability of
- * successful transmission on the lowest g rate
- * even more
- */
- if (rates[0].count >= 3) {
- --rates[0].count;
- ++rates[2].count;
- }
-
- /* Adjust amount of rates defined */
- count += 2;
- } else {
- /* Keep fallback rate at 1Mbps. */
- rates[2] = rates[1];
-
- /* Inject 2 transmissions on lowest g-rate */
- rates[1].idx = 4;
- rates[1].count = 2;
-
- /* Adjust amount of rates defined */
- count += 1;
- }
- }
-
for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) {
- int rateid;
- u8 count;
-
if (rates[i].idx < 0)
break;
WARN_ON(rates[i].count > 15);
@@ -158,8 +99,7 @@ static int wfx_tx_policy_release(struct tx_policy_cache *cache,
}
static int wfx_tx_policy_get(struct wfx_vif *wvif,
- struct ieee80211_tx_rate *rates,
- bool *renew)
+ struct ieee80211_tx_rate *rates, bool *renew)
{
int idx;
struct tx_policy_cache *cache = &wvif->tx_policy_cache;
@@ -171,7 +111,7 @@ static int wfx_tx_policy_get(struct wfx_vif *wvif,
if (list_empty(&cache->free)) {
WARN(1, "unable to get a valid Tx policy");
spin_unlock_bh(&cache->lock);
- return WFX_INVALID_RATE_ID;
+ return HIF_TX_RETRY_POLICY_INVALID;
}
idx = wfx_tx_policy_find(cache, &wanted);
if (idx >= 0) {
@@ -189,10 +129,8 @@ static int wfx_tx_policy_get(struct wfx_vif *wvif,
idx = entry - cache->cache;
}
wfx_tx_policy_use(cache, &cache->cache[idx]);
- if (list_empty(&cache->free)) {
- /* Lock TX queues. */
- wfx_tx_queues_lock(wvif->wdev);
- }
+ if (list_empty(&cache->free))
+ ieee80211_stop_queues(wvif->wdev->hw);
spin_unlock_bh(&cache->lock);
return idx;
}
@@ -202,15 +140,13 @@ static void wfx_tx_policy_put(struct wfx_vif *wvif, int idx)
int usage, locked;
struct tx_policy_cache *cache = &wvif->tx_policy_cache;
- if (idx == WFX_INVALID_RATE_ID)
+ if (idx == HIF_TX_RETRY_POLICY_INVALID)
return;
spin_lock_bh(&cache->lock);
locked = list_empty(&cache->free);
usage = wfx_tx_policy_release(cache, &cache->cache[idx]);
- if (locked && !usage) {
- /* Unlock TX queues. */
- wfx_tx_queues_unlock(wvif->wdev);
- }
+ if (locked && !usage)
+ ieee80211_wake_queues(wvif->wdev->hw);
spin_unlock_bh(&cache->lock);
}
@@ -218,15 +154,17 @@ static int wfx_tx_policy_upload(struct wfx_vif *wvif)
{
struct tx_policy *policies = wvif->tx_policy_cache.cache;
u8 tmp_rates[12];
- int i;
+ int i, is_used;
do {
spin_lock_bh(&wvif->tx_policy_cache.lock);
- for (i = 0; i < HIF_MIB_NUM_TX_RATE_RETRY_POLICIES; ++i)
- if (!policies[i].uploaded &&
- memzcmp(policies[i].rates, sizeof(policies[i].rates)))
+ for (i = 0; i < ARRAY_SIZE(wvif->tx_policy_cache.cache); ++i) {
+ is_used = memzcmp(policies[i].rates,
+ sizeof(policies[i].rates));
+ if (!policies[i].uploaded && is_used)
break;
- if (i < HIF_MIB_NUM_TX_RATE_RETRY_POLICIES) {
+ }
+ if (i < ARRAY_SIZE(wvif->tx_policy_cache.cache)) {
policies[i].uploaded = true;
memcpy(tmp_rates, policies[i].rates, sizeof(tmp_rates));
spin_unlock_bh(&wvif->tx_policy_cache.lock);
@@ -234,7 +172,7 @@ static int wfx_tx_policy_upload(struct wfx_vif *wvif)
} else {
spin_unlock_bh(&wvif->tx_policy_cache.lock);
}
- } while (i < HIF_MIB_NUM_TX_RATE_RETRY_POLICIES);
+ } while (i < ARRAY_SIZE(wvif->tx_policy_cache.cache));
return 0;
}
@@ -244,9 +182,7 @@ void wfx_tx_policy_upload_work(struct work_struct *work)
container_of(work, struct wfx_vif, tx_policy_upload_work);
wfx_tx_policy_upload(wvif);
-
wfx_tx_unlock(wvif->wdev);
- wfx_tx_queues_unlock(wvif->wdev);
}
void wfx_tx_policy_init(struct wfx_vif *wvif)
@@ -260,7 +196,7 @@ void wfx_tx_policy_init(struct wfx_vif *wvif)
INIT_LIST_HEAD(&cache->used);
INIT_LIST_HEAD(&cache->free);
- for (i = 0; i < HIF_MIB_NUM_TX_RATE_RETRY_POLICIES; ++i)
+ for (i = 0; i < ARRAY_SIZE(cache->cache); ++i)
list_add(&cache->cache[i].link, &cache->free);
}
@@ -281,16 +217,11 @@ static void wfx_tx_manage_pm(struct wfx_vif *wvif, struct ieee80211_hdr *hdr,
struct wfx_tx_priv *tx_priv,
struct ieee80211_sta *sta)
{
- u32 mask = ~BIT(tx_priv->raw_link_id);
struct wfx_sta_priv *sta_priv;
int tid = ieee80211_get_tid(hdr);
- spin_lock_bh(&wvif->ps_state_lock);
- if (ieee80211_is_auth(hdr->frame_control))
- wvif->sta_asleep_mask &= mask;
- spin_unlock_bh(&wvif->ps_state_lock);
-
if (sta) {
+ tx_priv->has_sta = true;
sta_priv = (struct wfx_sta_priv *)&sta->drv_priv;
spin_lock_bh(&sta_priv->lock);
sta_priv->buffered[tid]++;
@@ -299,9 +230,8 @@ static void wfx_tx_manage_pm(struct wfx_vif *wvif, struct ieee80211_hdr *hdr,
}
}
-static u8 wfx_tx_get_raw_link_id(struct wfx_vif *wvif,
- struct ieee80211_sta *sta,
- struct ieee80211_hdr *hdr)
+static u8 wfx_tx_get_link_id(struct wfx_vif *wvif, struct ieee80211_sta *sta,
+ struct ieee80211_hdr *hdr)
{
struct wfx_sta_priv *sta_priv =
sta ? (struct wfx_sta_priv *)&sta->drv_priv : NULL;
@@ -313,7 +243,7 @@ static u8 wfx_tx_get_raw_link_id(struct wfx_vif *wvif,
return 0;
if (is_multicast_ether_addr(da))
return 0;
- return WFX_LINK_ID_NO_ASSOC;
+ return HIF_LINK_ID_NOT_ASSOCIATED;
}
static void wfx_tx_fixup_rates(struct ieee80211_tx_rate *rates)
@@ -358,7 +288,8 @@ static void wfx_tx_fixup_rates(struct ieee80211_tx_rate *rates)
if (rates[i].idx == -1) {
rates[i].idx = 0;
rates[i].count = 8; // == hw->max_rate_tries
- rates[i].flags = rates[i - 1].flags & IEEE80211_TX_RC_MCS;
+ rates[i].flags = rates[i - 1].flags &
+ IEEE80211_TX_RC_MCS;
break;
}
}
@@ -375,24 +306,19 @@ static u8 wfx_tx_get_rate_id(struct wfx_vif *wvif,
rate_id = wfx_tx_policy_get(wvif,
tx_info->driver_rates, &tx_policy_renew);
- if (rate_id == WFX_INVALID_RATE_ID)
+ if (rate_id == HIF_TX_RETRY_POLICY_INVALID)
dev_warn(wvif->wdev->dev, "unable to get a valid Tx policy");
if (tx_policy_renew) {
- /* FIXME: It's not so optimal to stop TX queues every now and
- * then. Better to reimplement task scheduling with a counter.
- */
wfx_tx_lock(wvif->wdev);
- wfx_tx_queues_lock(wvif->wdev);
- if (!schedule_work(&wvif->tx_policy_upload_work)) {
- wfx_tx_queues_unlock(wvif->wdev);
+ if (!schedule_work(&wvif->tx_policy_upload_work))
wfx_tx_unlock(wvif->wdev);
- }
}
return rate_id;
}
-static struct hif_ht_tx_parameters wfx_tx_get_tx_parms(struct wfx_dev *wdev, struct ieee80211_tx_info *tx_info)
+static struct hif_ht_tx_parameters wfx_tx_get_tx_parms(struct wfx_dev *wdev,
+ struct ieee80211_tx_info *tx_info)
{
struct ieee80211_tx_rate *rate = &tx_info->driver_rates[0];
struct hif_ht_tx_parameters ret = { };
@@ -429,7 +355,7 @@ static int wfx_tx_inner(struct wfx_vif *wvif, struct ieee80211_sta *sta,
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_key_conf *hw_key = tx_info->control.hw_key;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- int queue_id = tx_info->hw_queue;
+ int queue_id = skb_get_queue_mapping(skb);
size_t offset = (size_t)skb->data & 3;
int wmsg_len = sizeof(struct hif_msg) +
sizeof(struct hif_req_tx) + offset;
@@ -441,14 +367,8 @@ static int wfx_tx_inner(struct wfx_vif *wvif, struct ieee80211_sta *sta,
memset(tx_info->rate_driver_data, 0, sizeof(struct wfx_tx_priv));
// Fill tx_priv
tx_priv = (struct wfx_tx_priv *)tx_info->rate_driver_data;
- tx_priv->raw_link_id = wfx_tx_get_raw_link_id(wvif, sta, hdr);
- tx_priv->link_id = tx_priv->raw_link_id;
if (ieee80211_has_protected(hdr->frame_control))
tx_priv->hw_key = hw_key;
- if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
- tx_priv->link_id = WFX_LINK_ID_AFTER_DTIM;
- if (sta && (sta->uapsd_queues & BIT(queue_id)))
- tx_priv->link_id = WFX_LINK_ID_UAPSD;
// Fill hif_msg
WARN(skb_headroom(skb) < wmsg_len, "not enough space in skb");
@@ -461,7 +381,8 @@ static int wfx_tx_inner(struct wfx_vif *wvif, struct ieee80211_sta *sta,
hif_msg->id = HIF_REQ_ID_TX;
hif_msg->interface = wvif->id;
if (skb->len > wvif->wdev->hw_caps.size_inp_ch_buf) {
- dev_warn(wvif->wdev->dev, "requested frame size (%d) is larger than maximum supported (%d)\n",
+ dev_warn(wvif->wdev->dev,
+ "requested frame size (%d) is larger than maximum supported (%d)\n",
skb->len, wvif->wdev->hw_caps.size_inp_ch_buf);
skb_pull(skb, wmsg_len);
return -EIO;
@@ -472,13 +393,14 @@ static int wfx_tx_inner(struct wfx_vif *wvif, struct ieee80211_sta *sta,
// packet_id just need to be unique on device. 32bits are more than
// necessary for that task, so we tae advantage of it to add some extra
// data for debug.
- req->packet_id = queue_id << 28 |
- IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)) << 16 |
- (atomic_add_return(1, &wvif->wdev->packet_id) & 0xFFFF);
+ req->packet_id = atomic_add_return(1, &wvif->wdev->packet_id) & 0xFFFF;
+ req->packet_id |= IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)) << 16;
+ req->packet_id |= queue_id << 28;
+
req->data_flags.fc_offset = offset;
if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
req->data_flags.after_dtim = 1;
- req->queue_id.peer_sta_id = tx_priv->raw_link_id;
+ req->queue_id.peer_sta_id = wfx_tx_get_link_id(wvif, sta, hdr);
// Queue index are inverted between firmware and Linux
req->queue_id.queue_id = 3 - queue_id;
req->ht_tx_parameters = wfx_tx_get_tx_parms(wvif->wdev, tx_info);
@@ -486,7 +408,7 @@ static int wfx_tx_inner(struct wfx_vif *wvif, struct ieee80211_sta *sta,
// Auxiliary operations
wfx_tx_manage_pm(wvif, hdr, tx_priv, sta);
- wfx_tx_queue_put(wvif->wdev, &wvif->wdev->tx_queue[queue_id], skb);
+ wfx_tx_queues_put(wvif->wdev, skb);
if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
schedule_work(&wvif->update_tim_work);
wfx_bh_request_tx(wvif->wdev);
@@ -528,28 +450,59 @@ drop:
ieee80211_tx_status_irqsafe(wdev->hw, skb);
}
-void wfx_tx_confirm_cb(struct wfx_vif *wvif, const struct hif_cnf_tx *arg)
+static struct ieee80211_hdr *wfx_skb_hdr80211(struct sk_buff *skb)
{
- int i;
- int tx_count;
- struct sk_buff *skb;
- struct ieee80211_tx_rate *rate;
- struct ieee80211_tx_info *tx_info;
- const struct wfx_tx_priv *tx_priv;
+ struct hif_msg *hif = (struct hif_msg *)skb->data;
+ struct hif_req_tx *req = (struct hif_req_tx *)hif->body;
- skb = wfx_pending_get(wvif->wdev, arg->packet_id);
- if (!skb) {
- dev_warn(wvif->wdev->dev,
- "received unknown packet_id (%#.8x) from chip\n",
- arg->packet_id);
- return;
+ return (struct ieee80211_hdr *)(req->frame + req->data_flags.fc_offset);
+}
+
+static void wfx_tx_update_sta(struct wfx_vif *wvif, struct ieee80211_hdr *hdr)
+{
+ int tid = ieee80211_get_tid(hdr);
+ struct wfx_sta_priv *sta_priv;
+ struct ieee80211_sta *sta;
+
+ rcu_read_lock(); // protect sta
+ sta = ieee80211_find_sta(wvif->vif, hdr->addr1);
+ if (sta) {
+ sta_priv = (struct wfx_sta_priv *)&sta->drv_priv;
+ spin_lock_bh(&sta_priv->lock);
+ WARN(!sta_priv->buffered[tid], "inconsistent notification");
+ sta_priv->buffered[tid]--;
+ if (!sta_priv->buffered[tid])
+ ieee80211_sta_set_buffered(sta, tid, false);
+ spin_unlock_bh(&sta_priv->lock);
+ } else {
+ dev_dbg(wvif->wdev->dev, "%s: sta does not exist anymore\n",
+ __func__);
}
- tx_info = IEEE80211_SKB_CB(skb);
- tx_priv = wfx_skb_tx_priv(skb);
- _trace_tx_stats(arg, skb,
- wfx_pending_get_pkt_us_delay(wvif->wdev, skb));
+ rcu_read_unlock();
+}
+
+static void wfx_skb_dtor(struct wfx_vif *wvif, struct sk_buff *skb)
+{
+ struct hif_msg *hif = (struct hif_msg *)skb->data;
+ struct hif_req_tx *req = (struct hif_req_tx *)hif->body;
+ unsigned int offset = sizeof(struct hif_msg) +
+ sizeof(struct hif_req_tx) +
+ req->data_flags.fc_offset;
+
+ WARN_ON(!wvif);
+ wfx_tx_policy_put(wvif, req->tx_flags.retry_policy_index);
+ skb_pull(skb, offset);
+ ieee80211_tx_status_irqsafe(wvif->wdev->hw, skb);
+}
+
+static void wfx_tx_fill_rates(struct wfx_dev *wdev,
+ struct ieee80211_tx_info *tx_info,
+ const struct hif_cnf_tx *arg)
+{
+ struct ieee80211_tx_rate *rate;
+ int tx_count;
+ int i;
- // You can touch to tx_priv, but don't touch to tx_info->status.
tx_count = arg->ack_failures;
if (!arg->status || arg->ack_failures)
tx_count += 1; // Also report success
@@ -558,16 +511,14 @@ void wfx_tx_confirm_cb(struct wfx_vif *wvif, const struct hif_cnf_tx *arg)
if (rate->idx < 0)
break;
if (tx_count < rate->count &&
- arg->status == HIF_STATUS_RETRY_EXCEEDED &&
+ arg->status == HIF_STATUS_TX_FAIL_RETRIES &&
arg->ack_failures)
- dev_dbg(wvif->wdev->dev, "all retries were not consumed: %d != %d\n",
+ dev_dbg(wdev->dev, "all retries were not consumed: %d != %d\n",
rate->count, tx_count);
if (tx_count <= rate->count && tx_count &&
- arg->txed_rate != wfx_get_hw_rate(wvif->wdev, rate))
- dev_dbg(wvif->wdev->dev,
- "inconsistent tx_info rates: %d != %d\n",
- arg->txed_rate,
- wfx_get_hw_rate(wvif->wdev, rate));
+ arg->txed_rate != wfx_get_hw_rate(wdev, rate))
+ dev_dbg(wdev->dev, "inconsistent tx_info rates: %d != %d\n",
+ arg->txed_rate, wfx_get_hw_rate(wdev, rate));
if (tx_count > rate->count) {
tx_count -= rate->count;
} else if (!tx_count) {
@@ -579,8 +530,30 @@ void wfx_tx_confirm_cb(struct wfx_vif *wvif, const struct hif_cnf_tx *arg)
}
}
if (tx_count)
- dev_dbg(wvif->wdev->dev,
- "%d more retries than expected\n", tx_count);
+ dev_dbg(wdev->dev, "%d more retries than expected\n", tx_count);
+}
+
+void wfx_tx_confirm_cb(struct wfx_vif *wvif, const struct hif_cnf_tx *arg)
+{
+ struct ieee80211_tx_info *tx_info;
+ const struct wfx_tx_priv *tx_priv;
+ struct sk_buff *skb;
+
+ skb = wfx_pending_get(wvif->wdev, arg->packet_id);
+ if (!skb) {
+ dev_warn(wvif->wdev->dev, "received unknown packet_id (%#.8x) from chip\n",
+ arg->packet_id);
+ return;
+ }
+ tx_info = IEEE80211_SKB_CB(skb);
+ tx_priv = wfx_skb_tx_priv(skb);
+ _trace_tx_stats(arg, skb,
+ wfx_pending_get_pkt_us_delay(wvif->wdev, skb));
+
+ // You can touch to tx_priv, but don't touch to tx_info->status.
+ wfx_tx_fill_rates(wvif->wdev, tx_info, arg);
+ if (tx_priv->has_sta)
+ wfx_tx_update_sta(wvif, wfx_skb_hdr80211(skb));
skb_trim(skb, skb->len - wfx_tx_get_icv_len(tx_priv->hw_key));
// From now, you can touch to tx_info->status, but do not touch to
@@ -590,63 +563,64 @@ void wfx_tx_confirm_cb(struct wfx_vif *wvif, const struct hif_cnf_tx *arg)
memset(tx_info->pad, 0, sizeof(tx_info->pad));
if (!arg->status) {
- if (wvif->bss_loss_state &&
- arg->packet_id == wvif->bss_loss_confirm_id)
- wfx_cqm_bssloss_sm(wvif, 0, 1, 0);
tx_info->status.tx_time =
- arg->media_delay - arg->tx_queue_delay;
+ le32_to_cpu(arg->media_delay) -
+ le32_to_cpu(arg->tx_queue_delay);
if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
tx_info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
else
tx_info->flags |= IEEE80211_TX_STAT_ACK;
- } else if (arg->status == HIF_REQUEUE) {
- WARN(!arg->tx_result_flags.requeue, "incoherent status and result_flags");
+ } else if (arg->status == HIF_STATUS_TX_FAIL_REQUEUE) {
+ WARN(!arg->tx_result_flags.requeue,
+ "incoherent status and result_flags");
if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
wvif->after_dtim_tx_allowed = false; // DTIM period elapsed
schedule_work(&wvif->update_tim_work);
}
tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
- } else {
- if (wvif->bss_loss_state &&
- arg->packet_id == wvif->bss_loss_confirm_id)
- wfx_cqm_bssloss_sm(wvif, 0, 0, 1);
}
- wfx_pending_remove(wvif->wdev, skb);
+ wfx_skb_dtor(wvif, skb);
}
-static void wfx_notify_buffered_tx(struct wfx_vif *wvif, struct sk_buff *skb)
+void wfx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u32 queues, bool drop)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct ieee80211_sta *sta;
- struct wfx_sta_priv *sta_priv;
- int tid = ieee80211_get_tid(hdr);
+ struct wfx_dev *wdev = hw->priv;
+ struct sk_buff_head dropped;
+ struct wfx_queue *queue;
+ struct wfx_vif *wvif;
+ struct hif_msg *hif;
+ struct sk_buff *skb;
+ int vif_id = -1;
+ int i;
- rcu_read_lock(); // protect sta
- sta = ieee80211_find_sta(wvif->vif, hdr->addr1);
- if (sta) {
- sta_priv = (struct wfx_sta_priv *)&sta->drv_priv;
- spin_lock_bh(&sta_priv->lock);
- WARN(!sta_priv->buffered[tid], "inconsistent notification");
- sta_priv->buffered[tid]--;
- if (!sta_priv->buffered[tid])
- ieee80211_sta_set_buffered(sta, tid, false);
- spin_unlock_bh(&sta_priv->lock);
+ if (vif)
+ vif_id = ((struct wfx_vif *)vif->drv_priv)->id;
+ skb_queue_head_init(&dropped);
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ if (!(BIT(i) & queues))
+ continue;
+ queue = &wdev->tx_queue[i];
+ if (drop)
+ wfx_tx_queue_drop(wdev, queue, vif_id, &dropped);
+ if (wdev->chip_frozen)
+ continue;
+ if (wait_event_timeout(wdev->tx_dequeue,
+ wfx_tx_queue_empty(wdev, queue, vif_id),
+ msecs_to_jiffies(1000)) <= 0)
+ dev_warn(wdev->dev,
+ "frames queued while flushing tx queues?");
+ }
+ wfx_tx_flush(wdev);
+ if (wdev->chip_frozen)
+ wfx_pending_drop(wdev, &dropped);
+ while ((skb = skb_dequeue(&dropped)) != NULL) {
+ hif = (struct hif_msg *)skb->data;
+ wvif = wdev_to_wvif(wdev, hif->interface);
+ if (wfx_skb_tx_priv(skb)->has_sta)
+ wfx_tx_update_sta(wvif, wfx_skb_hdr80211(skb));
+ ieee80211_tx_info_clear_status(IEEE80211_SKB_CB(skb));
+ wfx_skb_dtor(wvif, skb);
}
- rcu_read_unlock();
}
-void wfx_skb_dtor(struct wfx_dev *wdev, struct sk_buff *skb)
-{
- struct hif_msg *hif = (struct hif_msg *)skb->data;
- struct hif_req_tx *req = (struct hif_req_tx *)hif->body;
- struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
- unsigned int offset = sizeof(struct hif_req_tx) +
- sizeof(struct hif_msg) +
- req->data_flags.fc_offset;
-
- WARN_ON(!wvif);
- skb_pull(skb, offset);
- wfx_notify_buffered_tx(wvif, skb);
- wfx_tx_policy_put(wvif, req->tx_flags.retry_policy_index);
- ieee80211_tx_status_irqsafe(wdev->hw, skb);
-}