wl1251: avoid redundant join commands
[pandora-kernel.git] / drivers / net / wireless / wl1251 / main.c
index 3a3f19c..243d8d8 100644 (file)
@@ -509,6 +509,7 @@ static void wl1251_op_stop(struct ieee80211_hw *hw)
        wl->rssi_thold = 0;
        wl->channel = WL1251_DEFAULT_CHANNEL;
        wl->monitor_present = false;
+       wl->joined = false;
 
        wl1251_debugfs_reset(wl);
 
@@ -565,9 +566,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;
@@ -675,6 +707,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;
@@ -715,13 +752,25 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
        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.
+                */
+               if (wl->vif == NULL) {
+                       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;
@@ -729,8 +778,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;
@@ -767,6 +815,16 @@ 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);
 
@@ -1092,6 +1150,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;
        }
@@ -1135,6 +1194,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;
 
@@ -1156,32 +1216,27 @@ 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;
 
@@ -1266,9 +1321,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;
        }
@@ -1606,7 +1664,9 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
        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;