[PATCH] wpa_supplicant, nl80211: ieee80211 power save support

Janusz.Dziedzic at tieto.com Janusz.Dziedzic
Wed Jul 20 00:54:27 PDT 2011


Support for enabling/disabling/monitoring
current mode of ieee80211 power save.

Change-Id: Ie7bd2679ac5c4b87d6bdbda05cfde88d8ee2be6b
---
 src/drivers/driver.h         |   22 ++++++++++++
 src/drivers/driver_nl80211.c |   78 ++++++++++++++++++++++++++++++++++++++++++
 wpa_supplicant/ctrl_iface.c  |   36 +++++++++++++++++++
 wpa_supplicant/driver_i.h    |   15 ++++++++
 wpa_supplicant/wpa_cli.c     |   24 +++++++++++++
 5 files changed, 175 insertions(+), 0 deletions(-)

diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 92951ae..3e73f47 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -2274,6 +2274,28 @@ struct wpa_driver_ops {
 	 */
 	void (*set_rekey_info)(void *priv, const u8 *kek, const u8 *kck,
 			       const u8 *replay_ctr);
+
+	/**
+	 * set_powersave - set IEEE80211 power save
+	 * @priv: Private driver interface data
+	 * @enable: 1=enable, 0=disable
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This fuction can be used to enable/disable IEEE80211 power
+	 * save for the driver/firmware.
+	 */
+	int (*set_powersave)(void *priv, int enable);
+
+	/**
+	 * get_powersave - get actual IEEE80211 power save settings
+	 * @priv: Private driver interface data
+	 * @enabled: output param, result: 1=enabled, 0=disabled
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This fuction get current IEEE80211 power save mode used
+	 * by driver/firmware.
+	 */
+	int (*get_powersave)(void *priv, int *enabled);
 };
 
 
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 87d474e..663e388 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -6786,6 +6786,82 @@ static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck,
 }
 
 
+static int get_powersave_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	enum nl80211_ps_state ps_state;
+	int *state = (int *) arg;
+	*state = -1;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+	genlmsg_attrlen(gnlh, 0), NULL);
+
+
+	if (tb[NL80211_ATTR_PS_STATE]) {
+		ps_state = nla_get_u32(tb[NL80211_ATTR_PS_STATE]);
+		if (ps_state == NL80211_PS_ENABLED)
+			*state = 1;
+		else
+			*state = 0;
+	}
+
+	return NL_SKIP;
+}
+
+static int nl80211_get_powersave(void *priv, int *enabled)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+		    NL80211_CMD_GET_POWER_SAVE, 0);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+	ret = send_and_recv_msgs(bss->drv, msg, get_powersave_handler, enabled);
+	if (ret == 0) {
+		wpa_printf(MSG_DEBUG, "%s enabled: %d\n", __func__, *enabled);
+		if (*enabled == -1)
+			ret = -1;
+	}
+
+	return ret;
+nla_put_failure:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+static int nl80211_set_powersave(void *priv, int enable)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	enum nl80211_ps_state ps_state = NL80211_PS_DISABLED;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+	if (enable)
+		ps_state = NL80211_PS_ENABLED;
+
+	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+		    NL80211_CMD_SET_POWER_SAVE, 0);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+	NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state);
+
+	return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+nla_put_failure:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.name = "nl80211",
 	.desc = "Linux nl80211/cfg80211",
@@ -6859,4 +6935,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.remove_pmkid = nl80211_remove_pmkid,
 	.flush_pmkid = nl80211_flush_pmkid,
 	.set_rekey_info = nl80211_set_rekey_info,
+	.get_powersave = nl80211_get_powersave,
+	.set_powersave = nl80211_set_powersave,
 };
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 730e607..45afba5 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -2103,6 +2103,39 @@ static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s,
 	return 0;
 }
 
+static int wpa_supplicant_ieee80211_ps(struct wpa_supplicant *wpa_s, char *input,
+				       char *buf, size_t buflen)
+{
+	int ret = 0;
+
+	ret = os_snprintf(buf, buflen, "OK\n");
+	if (ret < 0 || (unsigned int) ret > buflen)
+		ret = -1;
+
+	if (os_strcasecmp(input, "enable") == 0) {
+		/* Enable ieee80211 power save */
+		if (wpa_drv_set_powersave(wpa_s, 1))
+			ret = -1;
+	} else if (os_strcasecmp(input, "disable") == 0) {
+		/* Disable ieee80211 power save */
+		if (wpa_drv_set_powersave(wpa_s, 0))
+			ret = -1;
+	} else if (os_strcasecmp(input, "show") == 0) {
+		/* Get current ieee80211 power save mode */
+		int enabled;
+		if (wpa_drv_get_powersave(wpa_s, &enabled)){
+			ret = -1;
+		} else {
+			ret = os_snprintf(buf, buflen, "%s\nOK",
+					  enabled == 0 ? "disabled":"enabled");
+			if (ret < 0 || (unsigned int) ret > buflen)
+				ret = -1;
+		}
+	} else
+		ret = -1;
+
+	return ret;
+}
 
 #ifdef CONFIG_P2P
 static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
@@ -3294,6 +3327,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 	} else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) {
 		reply_len = wpa_supplicant_signal_poll(wpa_s, reply,
 						       reply_size);
+	} else if (os_strncmp(buf, "IEEE80211_PS", 12) == 0) {
+		reply_len = wpa_supplicant_ieee80211_ps(wpa_s, buf + 13, reply,
+							reply_size);
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 8637754..9dd880d 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -713,4 +713,19 @@ static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s,
 	wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kck, replay_ctr);
 }
 
+static inline int wpa_drv_get_powersave(struct wpa_supplicant *wpa_s,
+					int *enabled)
+{
+	if (!wpa_s->driver->get_powersave)
+		return -1;
+	return wpa_s->driver->get_powersave(wpa_s->drv_priv, enabled);
+}
+
+static inline int wpa_drv_set_powersave(struct wpa_supplicant *wpa_s,
+					int enable)
+{
+	if (!wpa_s->driver->set_powersave)
+		return -1;
+	return wpa_s->driver->set_powersave(wpa_s->drv_priv, enable);
+}
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 9439856..4679c7f 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1678,6 +1678,27 @@ static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int wpa_cli_ieee80211_ps(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[128];
+	int res;
+
+	if (argc != 1) {
+		printf("Invalid IEEE80211_PS command: needs one argument: "
+		       "<enable|disable|show>\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "IEEE80211_PS %s", argv[0]);
+
+	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+		printf("Too long IEEE80211_PS command.\n");
+		return -1;
+	}
+
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
 #ifdef CONFIG_P2P
 
 static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[])
@@ -2540,6 +2561,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
 	{ "roam", wpa_cli_cmd_roam,
 	  cli_cmd_flag_none,
 	  "<addr> = roam to the specified BSS" },
+	{ "ieee80211_ps", wpa_cli_ieee80211_ps,
+	  cli_cmd_flag_none,
+	  "<enable|disable|show>" },
 #ifdef CONFIG_P2P
 	{ "p2p_find", wpa_cli_cmd_p2p_find, cli_cmd_flag_none,
 	  "[timeout] [type=*] = find P2P Devices for up-to timeout seconds" },
-- 
1.7.0.4



More information about the Hostap mailing list