[PATCH] wifi: do not disconnect on suspend if WoWLAN is enabled



If user configure device to enable WoWLAN (Wake over Wireless LAN) we
should not disconnect before suspend as device will not be able to
receive wake up magic-packet (or other triggers of WoWLAN event).

Is still better disconnect before suspend without WoWLAN configured,
because mac80211 layer have several problems when suspending with
active connections, which manifest usually by various kernel WARNINGS.
 
diff --git a/src/devices/nm-device-wifi.c b/src/devices/nm-device-wifi.c
index 1434485..3099e13 100644
--- a/src/devices/nm-device-wifi.c
+++ b/src/devices/nm-device-wifi.c
@@ -271,6 +271,16 @@ ipw_rfkill_state_work (gpointer user_data)
 
 /*****************************************************************/
 
+gboolean
+nm_device_wifi_get_wowlan_state (NMDeviceWifi *self)
+{
+       NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+       return wifi_utils_get_wowlan (priv->wifi_data);
+}
+
+/*****************************************************************/
+
 static GObject*
 constructor (GType type,
              guint n_construct_params,
diff --git a/src/devices/nm-device-wifi.h b/src/devices/nm-device-wifi.h
index 6ea303e..094de0e 100644
--- a/src/devices/nm-device-wifi.h
+++ b/src/devices/nm-device-wifi.h
@@ -95,6 +95,8 @@ NMAccessPoint * nm_device_wifi_get_activation_ap (NMDeviceWifi *self);
 
 RfKillState nm_device_wifi_get_ipw_rfkill_state (NMDeviceWifi *self);
 
+gboolean nm_device_wifi_get_wowlan_state (NMDeviceWifi *self);
+
 G_END_DECLS
 
 #endif /* NM_DEVICE_WIFI_H */
diff --git a/src/nm-manager.c b/src/nm-manager.c
index bf03084..ef7047e 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -3543,8 +3543,21 @@ nm_manager_can_device_auto_connect (NMManager *manager, const char *ifname)
        return !g_hash_table_contains (priv->noauto_sw_devices, ifname);
 }
 
+static gboolean
+nm_device_disconnect_on_suspend (NMDevice *device)
+{
+       if (nm_device_get_device_type (device) == NM_DEVICE_TYPE_WIFI &&
+           nm_device_wifi_get_wowlan_state (NM_DEVICE_WIFI (device))) {
+               nm_log_info (LOGD_SUSPEND, "(%s) WoWLAN enabled - do not disconnect on suspend.",
+                            nm_device_get_ip_iface(device));
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
 static void
-do_sleep_wake (NMManager *self)
+do_sleep_wake (NMManager *self, gboolean disabling_enabling)
 {
        NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
        const GSList *unmanaged_specs;
@@ -3558,11 +3571,18 @@ do_sleep_wake (NMManager *self)
                 * the manager wakes up.
                 */
                for (iter = priv->devices; iter; iter = iter->next)
-                       nm_device_set_manager_managed (NM_DEVICE (iter->data), FALSE, 
NM_DEVICE_STATE_REASON_SLEEPING);
+                       if (disabling_enabling || nm_device_disconnect_on_suspend (NM_DEVICE (iter->data)))
+                               nm_device_set_manager_managed (NM_DEVICE (iter->data), FALSE, 
NM_DEVICE_STATE_REASON_SLEEPING);
 
        } else {
                nm_log_info (LOGD_SUSPEND, "waking up and re-enabling...");
 
+               for (iter = priv->devices; iter; iter = iter->next)
+                       if (!disabling_enabling) {
+                               /* Disconnect devices that were not disconnected on suspend */
+                               /* FIXME reason ? */
+                               nm_device_set_manager_managed (NM_DEVICE (iter->data), FALSE, 
NM_DEVICE_STATE_REASON_SLEEPING);
+               }
                unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings);
 
                /* Ensure rfkill state is up-to-date since we don't respond to state
@@ -3619,7 +3639,7 @@ _internal_sleep (NMManager *self, gboolean do_sleep)
 
        priv->sleeping = do_sleep;
 
-       do_sleep_wake (self);
+       do_sleep_wake (self, FALSE);
 
        g_object_notify (G_OBJECT (self), NM_MANAGER_SLEEPING);
 }
@@ -3759,7 +3779,7 @@ _internal_enable (NMManager *self, gboolean enable)
 
        priv->net_enabled = enable;
 
-       do_sleep_wake (self);
+       do_sleep_wake (self, TRUE);
 
        g_object_notify (G_OBJECT (self), NM_MANAGER_NETWORKING_ENABLED);
 }
diff --git a/src/wifi/wifi-utils-nl80211.c b/src/wifi/wifi-utils-nl80211.c
index 5f65aef..3b0017e 100644
--- a/src/wifi/wifi-utils-nl80211.c
+++ b/src/wifi/wifi-utils-nl80211.c
@@ -555,6 +555,42 @@ wifi_nl80211_get_qual (WifiData *data)
        return sta_info.signal;
 }
 
+struct nl80211_wowlan_info {
+       gboolean enabled;
+};
+
+static int nl80211_wowlan_handler (struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
+       struct nl80211_wowlan_info *info = arg;
+
+       info->enabled = FALSE;
+
+       if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                      genlmsg_attrlen (gnlh, 0), NULL) < 0)
+               return NL_SKIP;
+
+       if (tb[NL80211_ATTR_WOWLAN_TRIGGERS])
+               info->enabled = TRUE;
+
+       return NL_SKIP;
+}
+
+static gboolean
+wifi_nl80211_get_wowlan (WifiData *data)
+{
+       WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
+       struct nl_msg *msg;
+       struct nl80211_wowlan_info info;
+
+       msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_WOWLAN, 0);
+
+       nl80211_send_and_recv (nl80211, msg, nl80211_wowlan_handler, &info);
+
+       return info.enabled;
+}
+
 struct nl80211_device_info {
        guint32 *freqs;
        int num_freqs;
@@ -563,6 +599,7 @@ struct nl80211_device_info {
        gboolean can_scan_ssid;
        gboolean supported;
        gboolean success;
+       gboolean can_wowlan;
 };
 
 #define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00
@@ -721,6 +758,9 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg)
                }
        }
 
+       if (tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED])
+               info->can_wowlan = TRUE;
+
        info->success = TRUE;
 
        return NL_SKIP;
@@ -809,6 +849,9 @@ wifi_nl80211_init (const char *iface, int ifindex)
        nl80211->parent.can_scan_ssid = device_info.can_scan_ssid;
        nl80211->parent.caps = device_info.caps;
 
+       if (device_info.can_wowlan)
+               nl80211->parent.get_wowlan = wifi_nl80211_get_wowlan;
+
        nm_log_info (LOGD_HW | LOGD_WIFI,
                     "(%s): using nl80211 for WiFi device control",
                     nl80211->parent.iface);
diff --git a/src/wifi/wifi-utils-private.h b/src/wifi/wifi-utils-private.h
index 8307509..bd2c9d4 100644
--- a/src/wifi/wifi-utils-private.h
+++ b/src/wifi/wifi-utils-private.h
@@ -66,6 +66,8 @@ struct WifiData {
 
        /* ssid == NULL means "auto SSID" */
        gboolean (*set_mesh_ssid) (WifiData *data, const GByteArray *ssid);
+
+       gboolean (*get_wowlan) (WifiData *data);
 };
 
 gpointer wifi_data_new (const char *iface, int ifindex, gsize len);
diff --git a/src/wifi/wifi-utils.c b/src/wifi/wifi-utils.c
index aa07a66..e2d663b 100644
--- a/src/wifi/wifi-utils.c
+++ b/src/wifi/wifi-utils.c
@@ -150,6 +150,15 @@ wifi_utils_get_qual (WifiData *data)
        return data->get_qual (data);
 }
 
+gboolean
+wifi_utils_get_wowlan (WifiData *data)
+{
+       g_return_val_if_fail (data != NULL, 0);
+       if (!data->get_wowlan)
+               return FALSE;
+       return data->get_wowlan (data);
+}
+
 void
 wifi_utils_deinit (WifiData *data)
 {
diff --git a/src/wifi/wifi-utils.h b/src/wifi/wifi-utils.h
index 09583e7..28f5402 100644
--- a/src/wifi/wifi-utils.h
+++ b/src/wifi/wifi-utils.h
@@ -61,6 +61,8 @@ guint32 wifi_utils_get_rate (WifiData *data);
 /* Returns quality 0 - 100% on succes, or -1 on error */
 int wifi_utils_get_qual (WifiData *data);
 
+/* Returns true if WoWLAN is enabled on device */
+gboolean wifi_utils_get_wowlan (WifiData *data);
 
 /* OLPC Mesh-only functions */
 guint32 wifi_utils_get_mesh_channel (WifiData *data);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]