wl1251: clear bss_lost on join
[pandora-kernel.git] / drivers / net / wireless / wl1251 / main.c
index 1f36861..b012867 100644 (file)
 #include "debugfs.h"
 #include "boot.h"
 
+static bool use_fw_ps = true;
+module_param(use_fw_ps, bool, 0644);
+MODULE_PARM_DESC(use_fw_ps, "Enable powersave once and leave it for chip's "
+                           "firmware to manage. When disabled, mac80211 "
+                           "will toggle powersave on tx activity instead. "
+                           "Default: y/Y/1");
+
 void wl1251_enable_interrupts(struct wl1251 *wl)
 {
        wl->if_ops->enable_irq(wl);
@@ -279,9 +286,18 @@ irqreturn_t wl1251_irq(int irq, void *cookie)
                        goto out_sleep;
                }
 
+               if (intr & WL1251_ACX_INTR_TX_RESULT) {
+                       wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT");
+                       wl1251_tx_complete(wl);
+               }
+
                if (intr & WL1251_ACX_INTR_RX0_DATA) {
                        wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA");
                        wl1251_rx(wl);
+
+                       if ((intr & WL1251_ACX_INTR_RX1_DATA)
+                           && skb_queue_len(&wl->tx_queue) > 0)
+                               wl1251_tx_work_unlocked(wl, false);
                }
 
                if (intr & WL1251_ACX_INTR_RX1_DATA) {
@@ -289,11 +305,6 @@ irqreturn_t wl1251_irq(int irq, void *cookie)
                        wl1251_rx(wl);
                }
 
-               if (intr & WL1251_ACX_INTR_TX_RESULT) {
-                       wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT");
-                       wl1251_tx_complete(wl);
-               }
-
                if (intr & WL1251_ACX_INTR_EVENT_A) {
                        wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT_A");
                        wl1251_event_handle(wl, 0);
@@ -308,21 +319,8 @@ irqreturn_t wl1251_irq(int irq, void *cookie)
                        wl1251_debug(DEBUG_IRQ,
                                     "WL1251_ACX_INTR_INIT_COMPLETE");
 
-               while (skb_queue_len(&wl->tx_queue) > 0
-                      && wl1251_tx_path_status(wl) == 0) {
-
-                       struct sk_buff *skb = skb_dequeue(&wl->tx_queue);
-                       if (skb == NULL)
-                               goto out_sleep;
-
-                       ret = wl1251_tx_frame(wl, skb);
-                       if (ret == -EBUSY) {
-                               skb_queue_head(&wl->tx_queue, skb);
-                               break;
-                       } else if (ret < 0) {
-                               dev_kfree_skb(skb);
-                       }
-               }
+               if (skb_queue_len(&wl->tx_queue) > 0)
+                       wl1251_tx_work_unlocked(wl, false);
 
                if (--ctr == 0)
                        break;
@@ -376,6 +374,9 @@ static int wl1251_join(struct wl1251 *wl, u8 bss_type, u8 channel,
        if (ret < 0)
                wl1251_warning("join timeout");
 
+       wl1251_no_ps_event(wl);
+       wl->bss_lost = 0;
+
 out:
        return ret;
 }
@@ -510,6 +511,7 @@ static void wl1251_op_stop(struct ieee80211_hw *hw)
        wl->channel = WL1251_DEFAULT_CHANNEL;
        wl->monitor_present = false;
        wl->joined = false;
+       wl->long_doze_mode_set = false;
 
        wl1251_debugfs_reset(wl);
 
@@ -570,6 +572,36 @@ static void wl1251_op_remove_interface(struct ieee80211_hw *hw,
        mutex_unlock(&wl->mutex);
 }
 
+static int wl1251_build_null_data(struct wl1251 *wl)
+{
+       struct sk_buff *skb = NULL;
+       int size;
+       void *ptr;
+       int ret = -ENOMEM;
+
+
+       if (wl->bss_type == BSS_TYPE_IBSS) {
+               size = sizeof(struct wl12xx_null_data_template);
+               ptr = NULL;
+       } else {
+               skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
+               if (!skb)
+                       goto out;
+               size = skb->len;
+               ptr = skb->data;
+       }
+
+       ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, ptr, size);
+
+out:
+       dev_kfree_skb(skb);
+       if (ret)
+               wl1251_warning("cmd buld null data failed %d", ret);
+
+       return ret;
+
+}
+
 static int wl1251_build_qos_null_data(struct wl1251 *wl)
 {
        struct ieee80211_qos_hdr template;
@@ -621,8 +653,9 @@ static void wl1251_ps_work(struct work_struct *work)
                }
        }
 
-       need_ps = wl->psm_requested && !wl->bss_lost;
        have_ps = wl->station_mode == STATION_POWER_SAVE_MODE;
+       need_ps = wl->psm_requested && !wl->bss_lost
+               && wl->rate < wl->ps_rate_threshold;
 
        if (need_ps == have_ps) {
                //wl1251_info("ps: already in mode %d", have_ps);
@@ -633,11 +666,13 @@ static void wl1251_ps_work(struct work_struct *work)
        if (need_ps) {
                wait = 0;
 
-               diff = jiffies - wl->last_io_jiffies;
-               if (diff < msecs_to_jiffies(150)) {
-                       //wl1251_info("ps: postponed psm, j %ld", diff);
-                       wait = msecs_to_jiffies(150) - diff + 1;
-               }
+               diff = jiffies - wl->last_no_ps_jiffies[1];
+               if (diff < msecs_to_jiffies(1000))
+                       wait = msecs_to_jiffies(1000) - diff + 1;
+
+               diff = jiffies - wl->last_no_ps_jiffies[0];
+               if (diff < msecs_to_jiffies(3000))
+                       wait += msecs_to_jiffies(1000);
 
                for (i = 0; i < ARRAY_SIZE(wl->tx_frames); i++) {
                        if (wl->tx_frames[i] != NULL) {
@@ -667,8 +702,7 @@ static void wl1251_ps_work(struct work_struct *work)
        if (ret < 0)
                goto out_sleep;
 
-       //wl1251_info("psm %d, j %ld, d %ld", need_ps,
-       //      jiffies - wl->last_io_jiffies);
+       // wl1251_info("psm %d, r %u", need_ps, wl->rate);
 
 out_sleep:
        wl1251_ps_elp_sleep(wl);
@@ -728,8 +762,13 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
                 * This leaves the tx path disabled in firmware, whereas
                 * the usual JOIN command seems to transmit some frames
                 * at firmware level.
+                *
+                * Note that bss_type must be BSS_TYPE_STA_BSS, also at least
+                * one join has to be performed before CMD_ENABLE_RX can
+                * properly switch channels (join will be done by CONF_IDLE).
                 */
                if (wl->vif == NULL) {
+                       wl->bss_type = BSS_TYPE_STA_BSS;
                        wl->joined = false;
                        ret = wl1251_cmd_data_path_rx(wl, wl->channel, 1);
                } else {
@@ -785,16 +824,6 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
                wl->power_level = conf->power_level;
        }
 
-       /*
-        * Tell stack that connection is lost because hw encryption isn't
-        * supported in monitor mode.
-        * This requires temporary enabling of the hw connection monitor flag
-        */
-       if ((changed & IEEE80211_CONF_CHANGE_MONITOR) && wl->vif) {
-               wl->hw->flags |= IEEE80211_HW_CONNECTION_MONITOR;
-               ieee80211_connection_loss(wl->vif);
-       }
-
 out_sleep:
        wl1251_ps_elp_sleep(wl);
 
@@ -1164,6 +1193,7 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
 {
        struct wl1251 *wl = hw->priv;
        struct sk_buff *beacon, *skb;
+       bool do_join = false;
        bool enable;
        int ret;
 
@@ -1185,35 +1215,24 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
                wl->rssi_thold = bss_conf->cqm_rssi_thold;
        }
 
-       if (changed & BSS_CHANGED_BSSID) {
+       if ((changed & BSS_CHANGED_BSSID) &&
+           memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
                memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
 
-               skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
-               if (!skb)
-                       goto out_sleep;
-
-               ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA,
-                                             skb->data, skb->len);
-               dev_kfree_skb(skb);
-               if (ret < 0)
-                       goto out_sleep;
-
-               ret = wl1251_build_qos_null_data(wl);
-               if (ret < 0)
-                       goto out;
+               if (!is_zero_ether_addr(wl->bssid)) {
+                       ret = wl1251_build_null_data(wl);
+                       if (ret < 0)
+                               goto out_sleep;
 
-               if (wl->bss_type != BSS_TYPE_IBSS) {
-                       ret = wl1251_join(wl, wl->bss_type, wl->channel,
-                                         wl->beacon_int, wl->dtim_period);
+                       ret = wl1251_build_qos_null_data(wl);
                        if (ret < 0)
                                goto out_sleep;
+
+                       do_join = true;
                }
        }
 
        if (changed & BSS_CHANGED_ASSOC) {
-               /* Disable temporary enabled hw connection monitor flag */
-               wl->hw->flags &= ~IEEE80211_HW_CONNECTION_MONITOR;
-
                if (bss_conf->assoc) {
                        wl->beacon_int = bss_conf->beacon_int;
 
@@ -1298,9 +1317,12 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
                if (ret < 0)
                        goto out_sleep;
 
-               ret = wl1251_join(wl, wl->bss_type, wl->beacon_int,
-                                 wl->channel, wl->dtim_period);
+               do_join = true;
+       }
 
+       if (do_join) {
+               ret = wl1251_join(wl, wl->bss_type, wl->channel,
+                                 wl->beacon_int, wl->dtim_period);
                if (ret < 0)
                        goto out_sleep;
        }
@@ -1586,6 +1608,9 @@ int wl1251_init_ieee80211(struct wl1251 *wl)
                IEEE80211_HW_SUPPORTS_UAPSD |
                IEEE80211_HW_SUPPORTS_CQM_RSSI;
 
+       if (use_fw_ps)
+               wl->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
+
        wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
                                         BIT(NL80211_IFTYPE_ADHOC);
        wl->hw->wiphy->max_scan_ssids = 1;
@@ -1658,6 +1683,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
        wl->beacon_int = WL1251_DEFAULT_BEACON_INT;
        wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD;
        wl->vif = NULL;
+       wl->ps_rate_threshold = 100000;
 
        for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
                wl->tx_frames[i] = NULL;