From 79675fc1c980f3877d87dfd9559b356808d4f5fe Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 3 Apr 2014 09:35:10 -0400 Subject: [PATCH 1/3] wifi: add wifi_utils_get_wowlan() --- src/platform/wifi/wifi-utils-nl80211.c | 44 ++++++++++++++++++++++++++ src/platform/wifi/wifi-utils-private.h | 2 ++ src/platform/wifi/wifi-utils.c | 9 ++++++ src/platform/wifi/wifi-utils.h | 3 ++ 4 files changed, 58 insertions(+) diff --git a/src/platform/wifi/wifi-utils-nl80211.c b/src/platform/wifi/wifi-utils-nl80211.c index 6da4a5737a..cc4e9bcc4a 100644 --- a/src/platform/wifi/wifi-utils-nl80211.c +++ b/src/platform/wifi/wifi-utils-nl80211.c @@ -604,6 +604,43 @@ nla_put_failure: } #endif +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; @@ -612,6 +649,7 @@ struct nl80211_device_info { gboolean can_scan_ssid; gboolean supported; gboolean success; + gboolean can_wowlan; }; #define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00 @@ -770,6 +808,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; @@ -860,6 +901,9 @@ wifi_nl80211_init (const char *iface, int ifindex) nl80211->num_freqs = device_info.num_freqs; 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/platform/wifi/wifi-utils-private.h b/src/platform/wifi/wifi-utils-private.h index 769e25298e..e7601752d0 100644 --- a/src/platform/wifi/wifi-utils-private.h +++ b/src/platform/wifi/wifi-utils-private.h @@ -56,6 +56,8 @@ struct WifiData { void (*deinit) (WifiData *data); + gboolean (*get_wowlan) (WifiData *data); + /* OLPC Mesh-only functions */ guint32 (*get_mesh_channel) (WifiData *data); diff --git a/src/platform/wifi/wifi-utils.c b/src/platform/wifi/wifi-utils.c index 2902d0c8b0..ff48027528 100644 --- a/src/platform/wifi/wifi-utils.c +++ b/src/platform/wifi/wifi-utils.c @@ -143,6 +143,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/platform/wifi/wifi-utils.h b/src/platform/wifi/wifi-utils.h index fa9dc14c61..455e075f5a 100644 --- a/src/platform/wifi/wifi-utils.h +++ b/src/platform/wifi/wifi-utils.h @@ -62,6 +62,9 @@ int wifi_utils_get_qual (WifiData *data); /* Tells the driver DHCP or SLAAC is running */ gboolean wifi_utils_indicate_addressing_running (WifiData *data, gboolean running); +/* 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); From ddb17bef81eace387f628a80e946565ffd56d388 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Wed, 5 Feb 2014 11:56:44 +0100 Subject: [PATCH 2/3] platform: add link_get_wake_on_lan() --- src/platform/nm-fake-platform.c | 10 ++++++++++ src/platform/nm-linux-platform.c | 26 ++++++++++++++++++++++++++ src/platform/nm-platform.c | 17 +++++++++++++++++ src/platform/nm-platform.h | 4 +++- 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 6215a81e77..9dfab56dd3 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -476,6 +476,15 @@ link_get_physical_port_id (NMPlatform *platform, int ifindex) return NULL; } +static gboolean +link_get_wake_on_lan (NMPlatform *platform, int ifindex) +{ + /* We call link_get just to cause an error to be set if @ifindex is bad. */ + link_get (platform, ifindex); + + return FALSE; +} + static gboolean link_supports_carrier_detect (NMPlatform *platform, int ifindex) { @@ -1301,6 +1310,7 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->link_set_mtu = link_set_mtu; platform_class->link_get_physical_port_id = link_get_physical_port_id; + platform_class->link_get_wake_on_lan = link_get_wake_on_lan; platform_class->link_supports_carrier_detect = link_supports_carrier_detect; platform_class->link_supports_vlans = link_supports_vlans; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 439f0e8d02..7ec3da66a3 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -2873,6 +2873,31 @@ mesh_set_ssid (NMPlatform *platform, int ifindex, const GByteArray *ssid) return wifi_utils_set_mesh_ssid (wifi_data, ssid); } +static gboolean +link_get_wake_on_lan (NMPlatform *platform, int ifindex) +{ + NMLinkType type = link_get_type (platform, ifindex); + + if (type == NM_LINK_TYPE_ETHERNET) { + struct ethtool_wolinfo wol; + + memset (&wol, 0, sizeof (wol)); + wol.cmd = ETHTOOL_GWOL; + if (!ethtool_get (link_get_name (platform, ifindex), &wol)) + return FALSE; + + return wol.wolopts != 0; + } else if (type == NM_LINK_TYPE_WIFI) { + WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex); + + if (!wifi_data) + return FALSE; + + return wifi_utils_get_wowlan (wifi_data); + } else + return FALSE; +} + /******************************************************************/ static int @@ -3644,6 +3669,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_set_mtu = link_set_mtu; platform_class->link_get_physical_port_id = link_get_physical_port_id; + platform_class->link_get_wake_on_lan = link_get_wake_on_lan; platform_class->link_supports_carrier_detect = link_supports_carrier_detect; platform_class->link_supports_vlans = link_supports_vlans; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index dc42b50d82..d2e45753fa 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -901,6 +901,23 @@ nm_platform_link_get_physical_port_id (int ifindex) return klass->link_get_physical_port_id (platform, ifindex); } +/** + * nm_platform_link_get_wake_onlan: + * @ifindex: Interface index + * + * Returns: the "Wake-on-LAN" status for @ifindex. + */ +gboolean +nm_platform_link_get_wake_on_lan (int ifindex) +{ + reset_error (); + + g_return_val_if_fail (ifindex >= 0, FALSE); + g_return_val_if_fail (klass->link_get_wake_on_lan, FALSE); + + return klass->link_get_wake_on_lan (platform, ifindex); +} + /** * nm_platform_link_enslave: * @master: Interface index of the master diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 6be78108f1..e817f337c5 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -321,6 +321,7 @@ typedef struct { gboolean (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu); char * (*link_get_physical_port_id) (NMPlatform *, int ifindex); + gboolean (*link_get_wake_on_lan) (NMPlatform *, int ifindex); gboolean (*link_supports_carrier_detect) (NMPlatform *, int ifindex); gboolean (*link_supports_vlans) (NMPlatform *, int ifindex); @@ -466,7 +467,8 @@ gboolean nm_platform_link_set_address (int ifindex, const void *address, size_t guint32 nm_platform_link_get_mtu (int ifindex); gboolean nm_platform_link_set_mtu (int ifindex, guint32 mtu); -char *nm_platform_link_get_physical_port_id (int ifindex); +char *nm_platform_link_get_physical_port_id (int ifindex); +gboolean nm_platform_link_get_wake_on_lan (int ifindex); gboolean nm_platform_link_supports_carrier_detect (int ifindex); gboolean nm_platform_link_supports_vlans (int ifindex); From 1218b7e0c7f7fbef879526f11f9fa817630be25c Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Thu, 3 Apr 2014 15:14:00 -0400 Subject: [PATCH 3/3] core: leave wake-on-LAN devices up over suspend/resume Taking down a wake-on-LAN device before suspending will effectively disable wake-on-LAN. So don't do that. Based on a patch from Stanislaw Gruszka. https://bugzilla.gnome.org/show_bug.cgi?id=712745 https://bugzilla.redhat.com/show_bug.cgi?id=826652 https://bugzilla.redhat.com/show_bug.cgi?id=1025009 --- src/nm-manager.c | 51 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/src/nm-manager.c b/src/nm-manager.c index dc1543b882..fb9716d9bd 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -3589,27 +3589,56 @@ done: g_clear_error (&error); } +static gboolean +device_is_wake_on_lan (NMDevice *device) +{ + return nm_platform_link_get_wake_on_lan (nm_device_get_ip_ifindex (device)); +} + static void -do_sleep_wake (NMManager *self) +do_sleep_wake (NMManager *self, gboolean sleeping_changed) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + gboolean suspending, waking_from_suspend; GSList *iter; - if (manager_sleeping (self)) { - nm_log_info (LOGD_SUSPEND, "sleeping or disabling..."); + suspending = sleeping_changed && priv->sleeping; + waking_from_suspend = sleeping_changed && !priv->sleeping; - /* Just deactivate and down all physical devices from the device list, - * to keep things fast the device list will get resynced when - * the manager wakes up. + if (manager_sleeping (self)) { + nm_log_info (LOGD_SUSPEND, suspending ? "sleeping..." : "disabling..."); + + /* FIXME: are there still hardware devices that need to be disabled around + * suspend/resume? */ for (iter = priv->devices; iter; iter = iter->next) { NMDevice *device = iter->data; - if (!nm_device_is_software (device)) - nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_SLEEPING); + /* FIXME: shouldn't we be unmanaging software devices if !suspending? */ + if (nm_device_is_software (device)) + continue; + /* Wake-on-LAN devices will be taken down post-suspend rather than pre- */ + if (suspending && device_is_wake_on_lan (device)) + continue; + + nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_SLEEPING); } } else { - nm_log_info (LOGD_SUSPEND, "waking up and re-enabling..."); + nm_log_info (LOGD_SUSPEND, waking_from_suspend ? "waking up..." : "re-enabling..."); + + if (waking_from_suspend) { + /* Belatedly take down Wake-on-LAN devices; ideally we wouldn't have to do this + * but for now it's the only way to make sure we re-check their connectivity. + */ + for (iter = priv->devices; iter; iter = iter->next) { + NMDevice *device = iter->data; + + if (nm_device_is_software (device)) + continue; + if (device_is_wake_on_lan (device)) + nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_SLEEPING); + } + } /* Ensure rfkill state is up-to-date since we don't respond to state * changes during sleep. @@ -3665,7 +3694,7 @@ _internal_sleep (NMManager *self, gboolean do_sleep) priv->sleeping = do_sleep; - do_sleep_wake (self); + do_sleep_wake (self, TRUE); g_object_notify (G_OBJECT (self), NM_MANAGER_SLEEPING); } @@ -3805,7 +3834,7 @@ _internal_enable (NMManager *self, gboolean enable) priv->net_enabled = enable; - do_sleep_wake (self); + do_sleep_wake (self, FALSE); g_object_notify (G_OBJECT (self), NM_MANAGER_NETWORKING_ENABLED); }