]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
wpa_supplicant: Add Wake-on-WLAN configuration support
authorEliad Peller <eliad@wizery.com>
Thu, 24 Apr 2014 05:45:39 +0000 (08:45 +0300)
committerJouni Malinen <j@w1.fi>
Tue, 29 Apr 2014 15:59:12 +0000 (18:59 +0300)
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
src/drivers/driver_nl80211.c
wpa_supplicant/config.c
wpa_supplicant/config.h
wpa_supplicant/config_file.c
wpa_supplicant/driver_i.h
wpa_supplicant/wpa_supplicant.c

index 00565a7b99e345f32b1b965510899790d3b6f217..481ddd6553f73a1323307dc2df9b8f9216ec9fff 100644 (file)
@@ -682,6 +682,16 @@ enum hide_ssid {
        HIDDEN_SSID_ZERO_CONTENTS
 };
 
+struct wowlan_triggers {
+       u8 any;
+       u8 disconnect;
+       u8 magic_pkt;
+       u8 gtk_rekey_failure;
+       u8 eap_identity_req;
+       u8 four_way_handshake;
+       u8 rfkill_release;
+};
+
 struct wpa_driver_ap_params {
        /**
         * head - Beacon head from IEEE 802.11 header to IEs before TIM IE
@@ -1032,6 +1042,8 @@ struct wpa_driver_capa {
         */
        const u8 *extended_capa, *extended_capa_mask;
        unsigned int extended_capa_len;
+
+       struct wowlan_triggers wowlan_triggers;
 };
 
 
@@ -2518,6 +2530,13 @@ struct wpa_driver_ops {
        int (*set_qos_map)(void *priv, const u8 *qos_map_set,
                           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
index fe224cedce8c942a4d68a625b2e614fcaccaa6b1..9cbe6f35b4d2f6d53835dbbbca5530a192ef6f02 100644 (file)
@@ -3697,6 +3697,35 @@ static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa,
 }
 
 
+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)
 {
        struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -3820,6 +3849,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;
 }
 
@@ -12137,6 +12169,57 @@ nla_put_failure:
 }
 
 
+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",
        .desc = "Linux nl80211/cfg80211",
@@ -12227,4 +12310,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,
 };
index 698b4592e1a427711c0390476bb369658bc1d798..b5a5d7885fe115b48f72299aa42a5f450b4fb397 100644 (file)
@@ -2022,6 +2022,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);
 }
 
@@ -3875,6 +3876,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
index 26b1233a653adc63de22d257f12a152ee63b24a6..bf3f3f79a7983b2ec1f4e86ebfc01f4c9f047506 100644 (file)
@@ -1022,6 +1022,13 @@ struct wpa_config {
         * directory.
         */
        char *osu_dir;
+
+       /**
+        * wowlan_triggers - Wake-on-WLAN triggers
+        *
+        * If set, these wowlan triggers will be configured.
+        */
+       char *wowlan_triggers;
 };
 
 
index 3a7adc2b6c0913d88041c76eea96e796ff0a1fb0..3cfe5ba93c0331be3a8a16bf952d115fefd8c255 100644 (file)
@@ -1155,6 +1155,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);
 }
index 938ece69902c987aef377be23da838631d9d092c..beeb05909da7c4bd506442dcdc41b981eba4db0b 100644 (file)
@@ -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)
index af7b847b83a269a50f8fa86e63cce3d8d0334f04..f56b198bde096b645b49de752638d65350bc2729 100644 (file)
@@ -3123,6 +3123,79 @@ int wpas_init_ext_pw(struct wpa_supplicant *wpa_s)
 }
 
 
+static int wpas_check_wowlan_trigger(const char *start, const char *trigger,
+                                    int capa_trigger, u8 *param_trigger)
+{
+       if (os_strcmp(start, trigger) != 0)
+               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)
 {
@@ -3650,6 +3723,14 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
        if (wpa_bss_init(wpa_s) < 0)
                return -1;
 
+       /*
+        * Set Wake-on-WLAN 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;