wpa_supplicant: Add Wake-on-WLAN configuration support
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>
This commit is contained in:
parent
959214b260
commit
e4fa8b120b
7 changed files with 205 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue