cfg80211/mac80211: enable proper device_set_wakeup_enable handling
Johannes Berg [Wed, 4 Apr 2012 13:05:25 +0000 (15:05 +0200)]
In WoWLAN, we only get the triggers when we actually get
to suspend. As a consequence, drivers currently don't
know that the device should enable wakeup. However, the
device_set_wakeup_enable() API is intended to be called
when the wakeup is enabled, not later when needed.

Add a new set_wakeup() call to cfg80211 and mac80211 to
allow drivers to properly call device_set_wakeup_enable.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

include/net/cfg80211.h
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/driver-ops.h
net/mac80211/driver-trace.h
net/wireless/core.c
net/wireless/nl80211.c

index ae3a3bb..1fd1c4a 100644 (file)
@@ -1344,6 +1344,9 @@ struct cfg80211_gtk_rekey_data {
  *     be %NULL or contain the enabled Wake-on-Wireless triggers that are
  *     configured for the device.
  * @resume: wiphy device needs to be resumed
+ * @set_wakeup: Called when WoWLAN is enabled/disabled, use this callback
+ *     to call device_set_wakeup_enable() to enable/disable wakeup from
+ *     the device.
  *
  * @add_virtual_intf: create a new virtual interface with the given name,
  *     must set the struct wireless_dev's iftype. Beware: You must create
@@ -1515,6 +1518,7 @@ struct cfg80211_gtk_rekey_data {
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
        int     (*resume)(struct wiphy *wiphy);
+       void    (*set_wakeup)(struct wiphy *wiphy, bool enabled);
 
        struct net_device * (*add_virtual_intf)(struct wiphy *wiphy,
                                                char *name,
index ec4995d..838a4db 100644 (file)
@@ -2233,6 +2233,7 @@ struct ieee80211_ops {
 #ifdef CONFIG_PM
        int (*suspend)(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
        int (*resume)(struct ieee80211_hw *hw);
+       void (*set_wakeup)(struct ieee80211_hw *hw, bool enabled);
 #endif
        int (*add_interface)(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif);
index d6163b9..3557354 100644 (file)
@@ -2695,6 +2695,13 @@ ieee80211_wiphy_get_channel(struct wiphy *wiphy)
        return local->oper_channel;
 }
 
+#ifdef CONFIG_PM
+static void ieee80211_set_wakeup(struct wiphy *wiphy, bool enabled)
+{
+       drv_set_wakeup(wiphy_priv(wiphy), enabled);
+}
+#endif
+
 struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
@@ -2763,4 +2770,7 @@ struct cfg80211_ops mac80211_config_ops = {
        .probe_client = ieee80211_probe_client,
        .get_channel = ieee80211_wiphy_get_channel,
        .set_noack_map = ieee80211_set_noack_map,
+#ifdef CONFIG_PM
+       .set_wakeup = ieee80211_set_wakeup,
+#endif
 };
index 492c08c..4a0e559 100644 (file)
@@ -91,6 +91,19 @@ static inline int drv_resume(struct ieee80211_local *local)
        trace_drv_return_int(local, ret);
        return ret;
 }
+
+static inline void drv_set_wakeup(struct ieee80211_local *local,
+                                 bool enabled)
+{
+       might_sleep();
+
+       if (!local->ops->set_wakeup)
+               return;
+
+       trace_drv_set_wakeup(local, enabled);
+       local->ops->set_wakeup(&local->hw, enabled);
+       trace_drv_return_void(local);
+}
 #endif
 
 static inline int drv_add_interface(struct ieee80211_local *local,
index d1f017a..7c0754b 100644 (file)
@@ -171,6 +171,20 @@ DEFINE_EVENT(local_only_evt, drv_resume,
        TP_ARGS(local)
 );
 
+TRACE_EVENT(drv_set_wakeup,
+       TP_PROTO(struct ieee80211_local *local, bool enabled),
+       TP_ARGS(local, enabled),
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               __field(bool, enabled)
+       ),
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               __entry->enabled = enabled;
+       ),
+       TP_printk(LOCAL_PR_FMT " enabled:%d", LOCAL_PR_ARG, __entry->enabled)
+);
+
 DEFINE_EVENT(local_only_evt, drv_stop,
        TP_PROTO(struct ieee80211_local *local),
        TP_ARGS(local)
index ccdfed8..59f4a7e 100644 (file)
@@ -708,6 +708,10 @@ void wiphy_unregister(struct wiphy *wiphy)
        flush_work(&rdev->scan_done_wk);
        cancel_work_sync(&rdev->conn_work);
        flush_work(&rdev->event_work);
+
+       if (rdev->wowlan && rdev->ops->set_wakeup)
+               rdev->ops->set_wakeup(&rdev->wiphy, false);
+       cfg80211_rdev_free_wowlan(rdev);
 }
 EXPORT_SYMBOL(wiphy_unregister);
 
@@ -720,7 +724,6 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
        mutex_destroy(&rdev->sched_scan_mtx);
        list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list)
                cfg80211_put_bss(&scan->pub);
-       cfg80211_rdev_free_wowlan(rdev);
        kfree(rdev);
 }
 
index b12a052..40e5620 100644 (file)
@@ -6014,6 +6014,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_wowlan new_triggers = {};
        struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
        int err, i;
+       bool prev_enabled = rdev->wowlan;
 
        if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
                return -EOPNOTSUPP;
@@ -6146,6 +6147,9 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                rdev->wowlan = NULL;
        }
 
+       if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan)
+               rdev->ops->set_wakeup(&rdev->wiphy, rdev->wowlan);
+
        return 0;
  error:
        for (i = 0; i < new_triggers.n_patterns; i++)