wl1251: clear bss_lost on join
[pandora-kernel.git] / drivers / net / wireless / wl1251 / main.c
index e448837..b012867 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/etherdevice.h>
 #include <linux/vmalloc.h>
 #include <linux/slab.h>
+#include <linux/netdevice.h>
 
 #include "wl1251.h"
 #include "wl12xx_80211.h"
 #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);
@@ -278,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) {
@@ -288,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);
@@ -307,6 +319,9 @@ irqreturn_t wl1251_irq(int irq, void *cookie)
                        wl1251_debug(DEBUG_IRQ,
                                     "WL1251_ACX_INTR_INIT_COMPLETE");
 
+               if (skb_queue_len(&wl->tx_queue) > 0)
+                       wl1251_tx_work_unlocked(wl, false);
+
                if (--ctr == 0)
                        break;
 
@@ -359,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;
 }
@@ -491,6 +509,9 @@ static void wl1251_op_stop(struct ieee80211_hw *hw)
        wl->power_level = WL1251_DEFAULT_POWER_LEVEL;
        wl->rssi_thold = 0;
        wl->channel = WL1251_DEFAULT_CHANNEL;
+       wl->monitor_present = false;
+       wl->joined = false;
+       wl->long_doze_mode_set = false;
 
        wl1251_debugfs_reset(wl);
 
@@ -547,9 +568,40 @@ static void wl1251_op_remove_interface(struct ieee80211_hw *hw,
        mutex_lock(&wl->mutex);
        wl1251_debug(DEBUG_MAC80211, "mac80211 remove interface");
        wl->vif = NULL;
+       memset(wl->bssid, 0, ETH_ALEN);
        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;
@@ -601,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);
@@ -613,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) {
@@ -647,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);
@@ -657,6 +711,11 @@ out:
        mutex_unlock(&wl->mutex);
 }
 
+static bool wl1251_can_do_pm(struct ieee80211_conf *conf, struct wl1251 *wl)
+{
+       return (conf->flags & IEEE80211_CONF_PS) && !wl->monitor_present;
+}
+
 static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
 {
        struct wl1251 *wl = hw->priv;
@@ -665,8 +724,10 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
 
        channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
 
-       wl1251_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d",
+       wl1251_debug(DEBUG_MAC80211,
+                    "mac80211 config ch %d monitor %s psm %s power %d",
                     channel,
+                    conf->flags & IEEE80211_CONF_MONITOR ? "on" : "off",
                     conf->flags & IEEE80211_CONF_PS ? "on" : "off",
                     conf->power_level);
 
@@ -676,16 +737,49 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
        if (ret < 0)
                goto out;
 
+       if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+               u32 mode;
+
+               if (conf->flags & IEEE80211_CONF_MONITOR) {
+                       wl->monitor_present = true;
+                       mode = DF_SNIFF_MODE_ENABLE | DF_ENCRYPTION_DISABLE;
+               } else {
+                       wl->monitor_present = false;
+                       mode = 0;
+               }
+
+               ret = wl1251_acx_feature_cfg(wl, mode);
+               if (ret < 0)
+                       goto out_sleep;
+       }
+
        if (channel != wl->channel) {
                wl->channel = channel;
 
-               ret = wl1251_join(wl, wl->bss_type, wl->channel,
-                                 wl->beacon_int, wl->dtim_period);
+               /*
+                * Use ENABLE_RX command for channel switching when no
+                * interface is present (monitor mode only).
+                * 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 {
+                       ret = wl1251_join(wl, wl->bss_type, wl->channel,
+                                         wl->beacon_int, wl->dtim_period);
+               }
                if (ret < 0)
                        goto out_sleep;
        }
 
-       if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) {
+       if (wl1251_can_do_pm(conf, wl) && !wl->psm_requested) {
                wl1251_debug(DEBUG_PSM, "psm enabled");
 
                wl->psm_requested = true;
@@ -693,8 +787,7 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
                wl->dtim_period = conf->ps_dtim_period;
 
                ieee80211_queue_delayed_work(wl->hw, &wl->ps_work, 0);
-       } else if (!(conf->flags & IEEE80211_CONF_PS) &&
-                  wl->psm_requested) {
+       } else if (!wl1251_can_do_pm(conf, wl) && wl->psm_requested) {
                wl1251_debug(DEBUG_PSM, "psm disabled");
 
                wl->psm_requested = false;
@@ -740,6 +833,44 @@ out:
        return ret;
 }
 
+struct wl1251_filter_params {
+       bool enabled;
+       int mc_list_length;
+       u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
+};
+
+static u64 wl1251_op_prepare_multicast(struct ieee80211_hw *hw,
+                                      struct netdev_hw_addr_list *mc_list)
+{
+       struct wl1251_filter_params *fp;
+       struct netdev_hw_addr *ha;
+       struct wl1251 *wl = hw->priv;
+
+       if (unlikely(wl->state == WL1251_STATE_OFF))
+               return 0;
+
+       fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
+       if (!fp) {
+               wl1251_error("Out of memory setting filters.");
+               return 0;
+       }
+
+       /* update multicast filtering parameters */
+       fp->mc_list_length = 0;
+       if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) {
+               fp->enabled = false;
+       } else {
+               fp->enabled = true;
+               netdev_hw_addr_list_for_each(ha, mc_list) {
+                       memcpy(fp->mc_list[fp->mc_list_length],
+                                       ha->addr, ETH_ALEN);
+                       fp->mc_list_length++;
+               }
+       }
+
+       return (u64)(unsigned long)fp;
+}
+
 #define WL1251_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
                                  FIF_ALLMULTI | \
                                  FIF_FCSFAIL | \
@@ -750,8 +881,9 @@ out:
 
 static void wl1251_op_configure_filter(struct ieee80211_hw *hw,
                                       unsigned int changed,
-                                      unsigned int *total,u64 multicast)
+                                      unsigned int *total, u64 multicast)
 {
+       struct wl1251_filter_params *fp = (void *)(unsigned long)multicast;
        struct wl1251 *wl = hw->priv;
        int ret;
 
@@ -760,9 +892,11 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw,
        *total &= WL1251_SUPPORTED_FILTERS;
        changed &= WL1251_SUPPORTED_FILTERS;
 
-       if (changed == 0)
+       if (changed == 0) {
                /* no filters which we support changed */
+               kfree(fp);
                return;
+       }
 
        mutex_lock(&wl->mutex);
 
@@ -799,6 +933,15 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
+       if (*total & FIF_ALLMULTI || *total & FIF_PROMISC_IN_BSS)
+               ret = wl1251_acx_group_address_tbl(wl, false, NULL, 0);
+       else if (fp)
+               ret = wl1251_acx_group_address_tbl(wl, fp->enabled,
+                                                  fp->mc_list,
+                                                  fp->mc_list_length);
+       if (ret < 0)
+               goto out;
+
        /* send filters to firmware */
        wl1251_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
 
@@ -806,6 +949,7 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw,
 
 out:
        mutex_unlock(&wl->mutex);
+       kfree(fp);
 }
 
 /* HW encryption */
@@ -885,12 +1029,12 @@ static int wl1251_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 
        mutex_lock(&wl->mutex);
 
-       ret = wl1251_ps_elp_wakeup(wl);
-       if (ret < 0)
-               goto out_unlock;
-
        switch (cmd) {
        case SET_KEY:
+               if (wl->monitor_present) {
+                       ret = -EOPNOTSUPP;
+                       goto out_unlock;
+               }
                wl_cmd->key_action = KEY_ADD_OR_REPLACE;
                break;
        case DISABLE_KEY:
@@ -901,6 +1045,10 @@ static int wl1251_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                break;
        }
 
+       ret = wl1251_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out_unlock;
+
        ret = wl1251_set_key_type(wl, wl_cmd, cmd, key, addr);
        if (ret < 0) {
                wl1251_error("Set KEY type failed");
@@ -1001,6 +1149,7 @@ static int wl1251_op_hw_scan(struct ieee80211_hw *hw,
        ret = wl1251_cmd_scan(wl, ssid, ssid_len, req->channels,
                              req->n_channels, WL1251_SCAN_NUM_PROBES);
        if (ret < 0) {
+               wl1251_debug(DEBUG_SCAN, "scan failed %d", ret);
                wl->scanning = false;
                goto out_sleep;
        }
@@ -1044,6 +1193,8 @@ 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;
 
        wl1251_debug(DEBUG_MAC80211, "mac80211 bss info changed");
@@ -1064,28 +1215,20 @@ 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;
                }
        }
 
@@ -1142,6 +1285,17 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
                }
        }
 
+       if (changed & BSS_CHANGED_ARP_FILTER) {
+               __be32 addr = bss_conf->arp_addr_list[0];
+               WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
+
+               enable = bss_conf->arp_addr_cnt == 1 && bss_conf->assoc;
+               wl1251_acx_arp_ip_filter(wl, enable, addr);
+
+               if (ret < 0)
+                       goto out_sleep;
+       }
+
        if (changed & BSS_CHANGED_BEACON) {
                beacon = ieee80211_beacon_get(hw, vif);
                if (!beacon)
@@ -1163,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;
        }
@@ -1312,6 +1469,7 @@ static const struct ieee80211_ops wl1251_ops = {
        .add_interface = wl1251_op_add_interface,
        .remove_interface = wl1251_op_remove_interface,
        .config = wl1251_op_config,
+       .prepare_multicast = wl1251_op_prepare_multicast,
        .configure_filter = wl1251_op_configure_filter,
        .tx = wl1251_op_tx,
        .set_key = wl1251_op_set_key,
@@ -1450,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;
@@ -1501,7 +1662,10 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
        INIT_DELAYED_WORK(&wl->ps_work, wl1251_ps_work);
        INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work);
        wl->channel = WL1251_DEFAULT_CHANNEL;
+       wl->monitor_present = false;
+       wl->joined = false;
        wl->scanning = false;
+       wl->bss_type = MAX_BSS_TYPE;
        wl->default_key = 0;
        wl->listen_int = 1;
        wl->rx_counter = 0;
@@ -1519,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;