wl1251: implement PS-Poll poll
authorYuri Ershov <ext-yuri.ershov@nokia.com>
Wed, 27 Oct 2010 13:54:08 +0000 (17:54 +0400)
committerGrazvydas Ignotas <notasas@gmail.com>
Sun, 31 Oct 2010 20:10:34 +0000 (22:10 +0200)
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 <kalle.valo@nokia.com>
Reviewed-by: Janne Ylalehto <janne.ylalehto@nokia.com>
Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Signed-off-by: Yuri Ershov <ext-yuri.ershov@nokia.com>
drivers/net/wireless/wl12xx/wl1251.h
drivers/net/wireless/wl12xx/wl1251_tx.c

index a851e7d..79fa99e 100644 (file)
@@ -345,6 +345,9 @@ struct wl1251 {
        /* Are we currently scanning */
        bool scanning;
 
+       /* Our association ID */
+       u16 aid;
+
        /* Default key (for WEP) */
        u32 default_key;
 
index 74708d5..0bbcf9a 100644 (file)
@@ -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) {