]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/mac80211/offchannel.c
Merge branch 'master' into tk71
[mv-sheeva.git] / net / mac80211 / offchannel.c
index c36b1911987afec08262139ccaaf5e7887a17735..b4e52676f3fb9d1de2f835d5f0d41d03586d9946 100644 (file)
@@ -14,6 +14,7 @@
  */
 #include <net/mac80211.h>
 #include "ieee80211_i.h"
+#include "driver-trace.h"
 
 /*
  * inform AP that we will go to sleep so that it will buffer the frames
 static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
        local->offchannel_ps_enabled = false;
 
        /* FIXME: what to do when local->pspolling is true? */
 
        del_timer_sync(&local->dynamic_ps_timer);
+       del_timer_sync(&ifmgd->bcn_mon_timer);
+       del_timer_sync(&ifmgd->conn_mon_timer);
+
        cancel_work_sync(&local->dynamic_ps_enable_work);
 
        if (local->hw.conf.flags & IEEE80211_CONF_PS) {
@@ -85,6 +90,9 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
                mod_timer(&local->dynamic_ps_timer, jiffies +
                          msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
        }
+
+       ieee80211_sta_reset_beacon_monitor(sdata);
+       ieee80211_sta_reset_conn_monitor(sdata);
 }
 
 void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local)
@@ -112,8 +120,10 @@ void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local)
                 * used from user space controlled off-channel operations.
                 */
                if (sdata->vif.type != NL80211_IFTYPE_STATION &&
-                   sdata->vif.type != NL80211_IFTYPE_MONITOR)
+                   sdata->vif.type != NL80211_IFTYPE_MONITOR) {
+                       set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
                        netif_tx_stop_all_queues(sdata->dev);
+               }
        }
        mutex_unlock(&local->iflist_mtx);
 }
@@ -131,6 +141,7 @@ void ieee80211_offchannel_stop_station(struct ieee80211_local *local)
                        continue;
 
                if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+                       set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
                        netif_tx_stop_all_queues(sdata->dev);
                        if (sdata->u.mgd.associated)
                                ieee80211_offchannel_ps_enable(sdata);
@@ -155,8 +166,20 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
                                ieee80211_offchannel_ps_disable(sdata);
                }
 
-               if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
+               if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
+                       clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
+                       /*
+                        * This may wake up queues even though the driver
+                        * currently has them stopped. This is not very
+                        * likely, since the driver won't have gotten any
+                        * (or hardly any) new packets while we weren't
+                        * on the right channel, and even if it happens
+                        * it will at most lead to queueing up one more
+                        * packet per queue in mac80211 rather than on
+                        * the interface qdisc.
+                        */
                        netif_tx_wake_all_queues(sdata->dev);
+               }
 
                /* re-enable beaconing */
                if (enable_beaconing &&
@@ -168,3 +191,87 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
        }
        mutex_unlock(&local->iflist_mtx);
 }
+
+static void ieee80211_hw_roc_start(struct work_struct *work)
+{
+       struct ieee80211_local *local =
+               container_of(work, struct ieee80211_local, hw_roc_start);
+       struct ieee80211_sub_if_data *sdata;
+
+       mutex_lock(&local->mtx);
+
+       if (!local->hw_roc_channel) {
+               mutex_unlock(&local->mtx);
+               return;
+       }
+
+       ieee80211_recalc_idle(local);
+
+       if (local->hw_roc_skb) {
+               sdata = IEEE80211_DEV_TO_SUB_IF(local->hw_roc_dev);
+               ieee80211_tx_skb(sdata, local->hw_roc_skb);
+               local->hw_roc_skb = NULL;
+       } else {
+               cfg80211_ready_on_channel(local->hw_roc_dev,
+                                         local->hw_roc_cookie,
+                                         local->hw_roc_channel,
+                                         local->hw_roc_channel_type,
+                                         local->hw_roc_duration,
+                                         GFP_KERNEL);
+       }
+
+       mutex_unlock(&local->mtx);
+}
+
+void ieee80211_ready_on_channel(struct ieee80211_hw *hw)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+
+       trace_api_ready_on_channel(local);
+
+       ieee80211_queue_work(hw, &local->hw_roc_start);
+}
+EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel);
+
+static void ieee80211_hw_roc_done(struct work_struct *work)
+{
+       struct ieee80211_local *local =
+               container_of(work, struct ieee80211_local, hw_roc_done);
+
+       mutex_lock(&local->mtx);
+
+       if (!local->hw_roc_channel) {
+               mutex_unlock(&local->mtx);
+               return;
+       }
+
+       if (!local->hw_roc_for_tx)
+               cfg80211_remain_on_channel_expired(local->hw_roc_dev,
+                                                  local->hw_roc_cookie,
+                                                  local->hw_roc_channel,
+                                                  local->hw_roc_channel_type,
+                                                  GFP_KERNEL);
+
+       local->hw_roc_channel = NULL;
+       local->hw_roc_cookie = 0;
+
+       ieee80211_recalc_idle(local);
+
+       mutex_unlock(&local->mtx);
+}
+
+void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+
+       trace_api_remain_on_channel_expired(local);
+
+       ieee80211_queue_work(hw, &local->hw_roc_done);
+}
+EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired);
+
+void ieee80211_hw_roc_setup(struct ieee80211_local *local)
+{
+       INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start);
+       INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done);
+}