From 4176434598f8b5f6855d0534aa584e4fd6955d50 Mon Sep 17 00:00:00 2001 From: Yuri Ershov Date: Wed, 27 Oct 2010 17:54:08 +0400 Subject: [PATCH] wl1251: implement PS-Poll poll To reduce latency when full Power Save Mode is enabled (because of BT coexistance) reduce latency by sending PS-Poll frame before every data frame. This is a hack and is not meant for upstream. Signed-off-by: Kalle Valo Reviewed-by: Janne Ylalehto Signed-off-by: Juuso Oikarinen Signed-off-by: Yuri Ershov --- drivers/net/wireless/wl12xx/wl1251.h | 3 ++ drivers/net/wireless/wl12xx/wl1251_tx.c | 71 +++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/drivers/net/wireless/wl12xx/wl1251.h b/drivers/net/wireless/wl12xx/wl1251.h index a851e7d..79fa99e 100644 --- a/drivers/net/wireless/wl12xx/wl1251.h +++ b/drivers/net/wireless/wl12xx/wl1251.h @@ -345,6 +345,9 @@ struct wl1251 { /* Are we currently scanning */ bool scanning; + /* Our association ID */ + u16 aid; + /* Default key (for WEP) */ u32 default_key; diff --git a/drivers/net/wireless/wl12xx/wl1251_tx.c b/drivers/net/wireless/wl12xx/wl1251_tx.c index 74708d5..0bbcf9a 100644 --- a/drivers/net/wireless/wl12xx/wl1251_tx.c +++ b/drivers/net/wireless/wl12xx/wl1251_tx.c @@ -30,6 +30,7 @@ #include "wl1251_tx.h" #include "wl1251_ps.h" #include "wl1251_io.h" +#include "wl12xx_80211.h" static bool wl1251_tx_double_buffer_busy(struct wl1251 *wl, u32 data_out_count) { @@ -300,6 +301,54 @@ static int wl1251_tx_frame(struct wl1251 *wl, struct sk_buff *skb) return ret; } +static int wl1251_tx_pspoll(struct wl1251 *wl) +{ + struct wl12xx_ps_poll_template *pspoll; + struct ieee80211_tx_info *info; + struct sk_buff *skb; + int ret; + u16 fc; + + skb = dev_alloc_skb(wl->hw->extra_tx_headroom + sizeof(*pspoll)); + if (!skb) { + wl1251_warning("failed to allocate buffer for pspoll frame"); + return -ENOMEM; + } + skb_reserve(skb, wl->hw->extra_tx_headroom); + + pspoll = (struct wl12xx_ps_poll_template *) skb_put(skb, + sizeof(*pspoll)); + memset(pspoll, 0, sizeof(*pspoll)); + fc = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL | IEEE80211_FCTL_PM; + pspoll->fc = cpu_to_le16(fc); + pspoll->aid = cpu_to_le16(wl->aid); + + /* aid in PS-Poll has its two MSBs each set to 1 */ + pspoll->aid |= cpu_to_le16(1 << 15 | 1 << 14); + + memcpy(pspoll->bssid, wl->bssid, ETH_ALEN); + memcpy(pspoll->ta, wl->mac_addr, ETH_ALEN); + + /* hack to inform that skb is not from mac80211 */ + info = IEEE80211_SKB_CB(skb); + info->driver_data[0] = (void *) 1; + + ret = wl1251_tx_frame(wl, skb); + + if (ret == -EBUSY) { + /* firmware buffer is full, stop queues */ + wl1251_debug(DEBUG_TX, "tx_pspoll: fw buffer full, " + "stop queues"); + ieee80211_stop_queues(wl->hw); + wl->tx_queue_stopped = true; + } + + if (ret < 0) + dev_kfree_skb(skb); + + return ret; +} + void wl1251_tx_work(struct work_struct *work) { struct wl1251 *wl = container_of(work, struct wl1251, tx_work); @@ -320,6 +369,16 @@ void wl1251_tx_work(struct work_struct *work) woken_up = true; } + if (wl->psm) { + ret = wl1251_tx_pspoll(wl); + + if (ret < 0) { + /* ps poll failed, put skb back to queue */ + skb_queue_head(&wl->tx_queue, skb); + goto out; + } + } + ret = wl1251_tx_frame(wl, skb); if (ret == -EBUSY) { skb_queue_head(&wl->tx_queue, skb); @@ -379,8 +438,19 @@ static void wl1251_tx_packet_cb(struct wl1251 *wl, return; } + wl1251_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x" + " status 0x%x (%s)", + result->id, skb, result->ack_failures, result->rate, + result->status, wl1251_tx_parse_status(result->status)); + info = IEEE80211_SKB_CB(skb); + /* hack: check if skb is not from mac80211 */ + if ((unsigned long) info->driver_data[0] == 1) { + dev_kfree_skb(skb); + goto out; + } + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && (result->status == TX_SUCCESS)) info->flags |= IEEE80211_TX_STAT_ACK; @@ -408,6 +478,7 @@ static void wl1251_tx_packet_cb(struct wl1251 *wl, ieee80211_tx_status(wl->hw, skb); +out: wl->tx_frames[result->id] = NULL; if (wl->tx_queue_stopped) { -- 2.39.5