@@ -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
@@ -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,
};
@@ -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
@@ -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;
};
@@ -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);
}
@@ -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)
@@ -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;