diff mbox

wpa_supplicant: add wowlan configuration support

Message ID 1398318340-31142-7-git-send-email-ilan.peer@intel.com
State Accepted
Headers show

Commit Message

Peer, Ilan April 24, 2014, 5:45 a.m. UTC
From: Eliad Peller <eliad@wizery.com>

Add a new wowlan_triggers option to wpa_supplicant conf.
The triggers in this key will be used to configure
the kernel wowlan configuration.

For now, support only simple flags. More complex triggers
can be added later on.

Signed-off-by: Eliad Peller <eliadx.peller@intel.com>
---
 src/drivers/driver.h            |   15 ++++++++
 src/drivers/driver_nl80211.c    |   80 +++++++++++++++++++++++++++++++++++++++
 wpa_supplicant/config.c         |    2 +
 wpa_supplicant/config.h         |    7 ++++
 wpa_supplicant/config_file.c    |    4 ++
 wpa_supplicant/driver_i.h       |    8 ++++
 wpa_supplicant/wpa_supplicant.c |   80 +++++++++++++++++++++++++++++++++++++++
 7 files changed, 196 insertions(+)

Comments

Jouni Malinen April 29, 2014, 5:51 p.m. UTC | #1
On Thu, Apr 24, 2014 at 08:45:39AM +0300, Ilan Peer wrote:
> Add a new wowlan_triggers option to wpa_supplicant conf.
> The triggers in this key will be used to configure
> the kernel wowlan configuration.
> 
> For now, support only simple flags. More complex triggers
> can be added later on.

Thanks, applied.
diff mbox

Patch

diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 13bf718..94b5ae6 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -682,6 +682,12 @@  enum hide_ssid {
 	HIDDEN_SSID_ZERO_CONTENTS
 };
 
+struct wowlan_triggers {
+	u8 any, disconnect, magic_pkt, gtk_rekey_failure,
+	   eap_identity_req, four_way_handshake,
+	   rfkill_release;
+};
+
 struct wpa_driver_ap_params {
 	/**
 	 * head - Beacon head from IEEE 802.11 header to IEs before TIM IE
@@ -1026,6 +1032,8 @@  struct wpa_driver_capa {
 	 */
 	const u8 *extended_capa, *extended_capa_mask;
 	unsigned int extended_capa_len;
+
+	struct wowlan_triggers wowlan_triggers;
 };
 
 
@@ -2513,6 +2521,13 @@  struct wpa_driver_ops {
 			   u8 qos_map_set_len);
 
 	/**
+	 * set_wowlan - Set wake-on-wireless triggers
+	 * @priv: Private driver interface data
+	 * @triggers: wowlan triggers
+	 */
+	int (*set_wowlan)(void *priv, const struct wowlan_triggers *triggers);
+
+	/**
 	 * signal_poll - Get current connection information
 	 * @priv: Private driver interface data
 	 * @signal_info: Connection info structure
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 1300703..ad5cc4d 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -3674,6 +3674,33 @@  static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa,
 	capa->probe_resp_offloads = probe_resp_offload_support(protocols);
 }
 
+static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa,
+				       struct nlattr *tb)
+{
+	struct nlattr *triggers[MAX_NL80211_WOWLAN_TRIG + 1];
+
+	if (tb == NULL)
+		return;
+
+	if (nla_parse_nested(triggers, MAX_NL80211_WOWLAN_TRIG,
+			     tb, NULL))
+		return;
+
+	if (triggers[NL80211_WOWLAN_TRIG_ANY])
+		capa->wowlan_triggers.any = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_DISCONNECT])
+		capa->wowlan_triggers.disconnect = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_MAGIC_PKT])
+		capa->wowlan_triggers.magic_pkt = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
+		capa->wowlan_triggers.gtk_rekey_failure = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
+		capa->wowlan_triggers.eap_identity_req = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
+		capa->wowlan_triggers.four_way_handshake = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
+		capa->wowlan_triggers.rfkill_release = 1;
+}
 
 static int wiphy_info_handler(struct nl_msg *msg, void *arg)
 {
@@ -3798,6 +3825,9 @@  static int wiphy_info_handler(struct nl_msg *msg, void *arg)
 		}
 	}
 
+	wiphy_info_wowlan_triggers(capa,
+				   tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]);
+
 	return NL_SKIP;
 }
 
@@ -12082,6 +12112,55 @@  nla_put_failure:
 	return -ENOBUFS;
 }
 
+static int nl80211_set_wowlan(void *priv,
+			      const struct wowlan_triggers *triggers)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *wowlan_triggers;
+	int ret;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan");
+
+	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WOWLAN);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+	wowlan_triggers = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
+	if (!wowlan_triggers)
+		goto nla_put_failure;
+
+	if (triggers->any)
+		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
+	if (triggers->disconnect)
+		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
+	if (triggers->magic_pkt)
+		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
+	if (triggers->gtk_rekey_failure)
+		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
+	if (triggers->eap_identity_req)
+		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
+	if (triggers->four_way_handshake)
+		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
+	if (triggers->rfkill_release)
+		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
+
+	nla_nest_end(msg, wowlan_triggers);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan failed");
+
+	return ret;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
 
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.name = "nl80211",
@@ -12173,4 +12252,5 @@  const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 #endif /* ANDROID */
 	.vendor_cmd = nl80211_vendor_cmd,
 	.set_qos_map = nl80211_set_qos_map,
+	.set_wowlan = nl80211_set_wowlan,
 };
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index b4ee1f5..a5b909d 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2021,6 +2021,7 @@  void wpa_config_free(struct wpa_config *config)
 	os_free(config->sae_groups);
 	wpabuf_free(config->ap_vendor_elements);
 	os_free(config->osu_dir);
+	os_free(config->wowlan_triggers);
 	os_free(config);
 }
 
@@ -3867,6 +3868,7 @@  static const struct global_parse_data global_fields[] = {
 	{ INT(sched_scan_interval), 0 },
 	{ INT(tdls_external_control), 0},
 	{ STR(osu_dir), 0 },
+	{ STR(wowlan_triggers), 0 },
 };
 
 #undef FUNC
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index a398049..3ab5770 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -1014,6 +1014,13 @@  struct wpa_config {
 	 * directory.
 	 */
 	char *osu_dir;
+
+	/**
+	 * wowlan_triggers - Wowlan triggers
+	 *
+	 * If set, these wowlan triggers will be configured
+	 */
+	char *wowlan_triggers;
 };
 
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 8f0561a..2313d75 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1146,6 +1146,10 @@  static void wpa_config_write_global(FILE *f, struct wpa_config *config)
 		fprintf(f, "tdls_external_control=%d\n",
 			config->tdls_external_control);
 
+	if (config->wowlan_triggers)
+		fprintf(f, "wowlan_triggers=\"%s\"\n",
+			config->wowlan_triggers);
+
 	if (config->bgscan)
 		fprintf(f, "bgscan=\"%s\"\n", config->bgscan);
 }
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 938ece6..beeb059 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -614,6 +614,14 @@  static inline int wpa_drv_set_qos_map(struct wpa_supplicant *wpa_s,
 					  qos_map_set_len);
 }
 
+static inline int wpa_drv_wowlan(struct wpa_supplicant *wpa_s,
+				 const struct wowlan_triggers *triggers)
+{
+	if (!wpa_s->driver->set_wowlan)
+		return -1;
+	return wpa_s->driver->set_wowlan(wpa_s->drv_priv, triggers);
+}
+
 static inline int wpa_drv_vendor_cmd(struct wpa_supplicant *wpa_s,
 				     int vendor_id, int subcmd, const u8 *data,
 				     size_t data_len, struct wpabuf *buf)
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 2cf595f..6e67ef7 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -3120,6 +3120,77 @@  int wpas_init_ext_pw(struct wpa_supplicant *wpa_s)
 	return 0;
 }
 
+static int wpas_check_wowlan_trigger(const char *start, const char *trigger,
+				     int capa_trigger, u8 *param_trigger)
+{
+	if (os_strcmp(start, trigger))
+		return 0;
+	if (!capa_trigger)
+		return 0;
+
+	*param_trigger = 1;
+	return 1;
+}
+
+int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s,
+			     struct wpa_driver_capa *capa)
+{
+	struct wowlan_triggers triggers;
+	char *start, *end, *buf;
+	int last, ret;
+
+	if (!wpa_s->conf->wowlan_triggers)
+		return 0;
+
+	buf = os_strdup(wpa_s->conf->wowlan_triggers);
+	if (buf == NULL)
+		return -1;
+
+	os_memset(&triggers, 0, sizeof(triggers));
+
+#define CHECK_TRIGGER(trigger) \
+	wpas_check_wowlan_trigger(start, #trigger,			\
+				  capa->wowlan_triggers.trigger,	\
+				  &triggers.trigger)
+
+	start = buf;
+	while (*start != '\0') {
+		while (isblank(*start))
+			start++;
+		if (*start == '\0')
+			break;
+		end = start;
+		while (!isblank(*end) && *end != '\0')
+			end++;
+		last = *end == '\0';
+		*end = '\0';
+
+		if (!CHECK_TRIGGER(any) &&
+		    !CHECK_TRIGGER(disconnect) &&
+		    !CHECK_TRIGGER(magic_pkt) &&
+		    !CHECK_TRIGGER(gtk_rekey_failure) &&
+		    !CHECK_TRIGGER(eap_identity_req) &&
+		    !CHECK_TRIGGER(four_way_handshake) &&
+		    !CHECK_TRIGGER(rfkill_release)) {
+			wpa_printf(MSG_DEBUG,
+				   "Unknown/unsupported wowlan trigger '%s'",
+				   start);
+			ret = -1;
+			goto out;
+		}
+
+		if (last)
+			break;
+		start = end + 1;
+	}
+
+#undef CHECK_TRIGGER
+
+	ret = wpa_drv_wowlan(wpa_s, &triggers);
+out:
+	os_free(buf);
+	return ret;
+}
 
 static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s,
 					      const char *rn)
@@ -3648,6 +3719,15 @@  static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
 	if (wpa_bss_init(wpa_s) < 0)
 		return -1;
 
+	/*
+	 * set wowlan triggers if configured.
+	 * Note: we don't restore/remove the triggers
+	 * on shutdown (it doesn't have effect anyway
+	 * when the interface is down).
+	 */
+	if (wpas_set_wowlan_triggers(wpa_s, &capa) < 0)
+		return -1;
+
 #ifdef CONFIG_EAP_PROXY
 {
 	size_t len;