From: Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
Subject: Enable the use of powersave mode on supporting wireless devices
Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1384823

---
 src/nm-device-wifi.c          |    5 +++++
 src/wifi/wifi-utils-nl80211.c |   23 +++++++++++++++++++++++
 src/wifi/wifi-utils-private.h |    3 +++
 src/wifi/wifi-utils.c         |    8 ++++++++
 4 files changed, 39 insertions(+)

Index: b/src/nm-device-wifi.c
===================================================================
--- a/src/nm-device-wifi.c
+++ b/src/nm-device-wifi.c
@@ -959,6 +959,8 @@ deactivate (NMDevice *dev)
 	/* Reset MAC address back to initial address */
 	_set_hw_addr (self, priv->initial_hw_addr, "reset");
 
+	wifi_utils_set_powersave (priv->wifi_data, FALSE);
+
 	/* Ensure we're in infrastructure mode after deactivation; some devices
 	 * (usually older ones) don't scan well in adhoc mode.
 	 */
@@ -3489,12 +3491,15 @@ device_state_changed (NMDevice *device,
 			nm_supplicant_interface_disconnect (priv->supplicant.iface);
 		break;
 	case NM_DEVICE_STATE_ACTIVATED:
+		wifi_utils_set_powersave (priv->wifi_data, TRUE);
 		activation_success_handler (device);
 		break;
 	case NM_DEVICE_STATE_FAILED:
+		wifi_utils_set_powersave (priv->wifi_data, FALSE);
 		activation_failure_handler (device);
 		break;
 	case NM_DEVICE_STATE_DISCONNECTED:
+		wifi_utils_set_powersave (priv->wifi_data, FALSE);
 		/* Kick off a scan to get latest results */
 		priv->scan_interval = SCAN_INTERVAL_MIN;
 		cancel_pending_scan (self);
Index: b/src/wifi/wifi-utils-nl80211.c
===================================================================
--- a/src/wifi/wifi-utils-nl80211.c
+++ b/src/wifi/wifi-utils-nl80211.c
@@ -248,6 +248,28 @@ wifi_nl80211_set_mode (WifiData *data, c
 	return FALSE;
 }
 
+static gboolean
+wifi_nl80211_set_powersave (WifiData *data, gboolean powersave)
+{
+	WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data;
+	struct nl_msg *msg;
+	int err;
+
+	msg = nl80211_alloc_msg (nl80211, NL80211_CMD_SET_POWER_SAVE, 0);
+
+	if (powersave)
+		NLA_PUT_U32 (msg, NL80211_ATTR_PS_STATE, NL80211_PS_ENABLED);
+	else
+		NLA_PUT_U32 (msg, NL80211_ATTR_PS_STATE, NL80211_PS_DISABLED);
+
+	err = nl80211_send_and_recv (nl80211, msg, NULL, NULL);
+	return err ? FALSE : TRUE;
+
+nla_put_failure:
+	nlmsg_free (msg);
+	return FALSE;
+}
+
 /* @divisor: pass what value @xbm should be divided by to get dBm */
 static guint32 nl80211_xbm_to_percent (gint32 xbm, guint32 divisor)
 {
@@ -739,6 +761,7 @@ wifi_nl80211_init (const char *iface, in
 	nl80211 = wifi_data_new (iface, ifindex, sizeof (*nl80211));
 	nl80211->parent.get_mode = wifi_nl80211_get_mode;
 	nl80211->parent.set_mode = wifi_nl80211_set_mode;
+	nl80211->parent.set_powersave = wifi_nl80211_set_powersave;
 	nl80211->parent.get_freq = wifi_nl80211_get_freq;
 	nl80211->parent.find_freq = wifi_nl80211_find_freq;
 	nl80211->parent.get_ssid = wifi_nl80211_get_ssid;
Index: b/src/wifi/wifi-utils.c
===================================================================
--- a/src/wifi/wifi-utils.c
+++ b/src/wifi/wifi-utils.c
@@ -104,6 +104,14 @@ wifi_utils_set_mode (WifiData *data, con
 	return data->set_mode ? data->set_mode (data, mode) : TRUE;
 }
 
+gboolean
+wifi_utils_set_powersave (WifiData *data, gboolean powersave)
+{
+	g_return_val_if_fail (data != NULL, FALSE);
+
+	return data->set_powersave ? data->set_powersave (data, powersave) : TRUE;
+}
+
 guint32
 wifi_utils_get_freq (WifiData *data)
 {
Index: b/src/wifi/wifi-utils-private.h
===================================================================
--- a/src/wifi/wifi-utils-private.h
+++ b/src/wifi/wifi-utils-private.h
@@ -36,6 +36,9 @@ struct WifiData {
 
 	gboolean (*set_mode) (WifiData *data, const NM80211Mode mode);
 
+	/* Set power saving mode on an interface (nl80211 only) */
+	gboolean (*set_powersave) (WifiData *data, gboolean powersave);
+
 	/* Return current frequency in MHz (really associated BSS frequency) */
 	guint32 (*get_freq) (WifiData *data);
 
