diff options
author | Luciano Coelho <luciano.coelho@intel.com> | 2014-09-17 04:55:28 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2014-11-19 12:45:45 -0500 |
commit | 8cd4d4563ef0a518002c4a8f47dd950afe386ea8 (patch) | |
tree | 11aac072065c8eeaa882b3386e9368107defa12f /net/wireless/nl80211.c | |
parent | 256da02d1806c740be97576a5e8548d658858319 (diff) |
cfg80211: add wowlan net-detect support
Add a new WoWLAN API to enable net-detect as a wake up trigger.
Net-detect allows the device to scan in the background while the
host is asleep to wake up the host system when a matching network
is found.
Reuse the scheduled scan attributes to specify how the scan is
performed while suspended and the matches that will trigger a
wake event.
Signed-off-by: Luciano Coelho <luciano.coelho@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 111 |
1 files changed, 110 insertions, 1 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 03a302b884fd..3ec7dc557960 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -209,7 +209,7 @@ cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info) | |||
209 | } | 209 | } |
210 | 210 | ||
211 | /* policy for the attributes */ | 211 | /* policy for the attributes */ |
212 | static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | 212 | static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { |
213 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, | 213 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, |
214 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, | 214 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, |
215 | .len = 20-1 }, | 215 | .len = 20-1 }, |
@@ -428,6 +428,7 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { | |||
428 | [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG }, | 428 | [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG }, |
429 | [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG }, | 429 | [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG }, |
430 | [NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED }, | 430 | [NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED }, |
431 | [NL80211_WOWLAN_TRIG_NET_DETECT] = { .type = NLA_NESTED }, | ||
431 | }; | 432 | }; |
432 | 433 | ||
433 | static const struct nla_policy | 434 | static const struct nla_policy |
@@ -1088,6 +1089,8 @@ static int nl80211_send_wowlan(struct sk_buff *msg, | |||
1088 | if (large && nl80211_send_wowlan_tcp_caps(rdev, msg)) | 1089 | if (large && nl80211_send_wowlan_tcp_caps(rdev, msg)) |
1089 | return -ENOBUFS; | 1090 | return -ENOBUFS; |
1090 | 1091 | ||
1092 | /* TODO: send wowlan net detect */ | ||
1093 | |||
1091 | nla_nest_end(msg, nl_wowlan); | 1094 | nla_nest_end(msg, nl_wowlan); |
1092 | 1095 | ||
1093 | return 0; | 1096 | return 0; |
@@ -8695,6 +8698,39 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev, | |||
8695 | return 0; | 8698 | return 0; |
8696 | } | 8699 | } |
8697 | 8700 | ||
8701 | static int nl80211_parse_wowlan_nd(struct cfg80211_registered_device *rdev, | ||
8702 | const struct wiphy_wowlan_support *wowlan, | ||
8703 | struct nlattr *attr, | ||
8704 | struct cfg80211_wowlan *trig) | ||
8705 | { | ||
8706 | struct nlattr **tb; | ||
8707 | int err; | ||
8708 | |||
8709 | tb = kzalloc(NUM_NL80211_ATTR * sizeof(*tb), GFP_KERNEL); | ||
8710 | if (!tb) | ||
8711 | return -ENOMEM; | ||
8712 | |||
8713 | if (!(wowlan->flags & WIPHY_WOWLAN_NET_DETECT)) { | ||
8714 | err = -EOPNOTSUPP; | ||
8715 | goto out; | ||
8716 | } | ||
8717 | |||
8718 | err = nla_parse(tb, NL80211_ATTR_MAX, | ||
8719 | nla_data(attr), nla_len(attr), | ||
8720 | nl80211_policy); | ||
8721 | if (err) | ||
8722 | goto out; | ||
8723 | |||
8724 | trig->nd_config = nl80211_parse_sched_scan(&rdev->wiphy, tb); | ||
8725 | err = PTR_ERR_OR_ZERO(trig->nd_config); | ||
8726 | if (err) | ||
8727 | trig->nd_config = NULL; | ||
8728 | |||
8729 | out: | ||
8730 | kfree(tb); | ||
8731 | return err; | ||
8732 | } | ||
8733 | |||
8698 | static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) | 8734 | static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) |
8699 | { | 8735 | { |
8700 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 8736 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
@@ -8840,6 +8876,14 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) | |||
8840 | goto error; | 8876 | goto error; |
8841 | } | 8877 | } |
8842 | 8878 | ||
8879 | if (tb[NL80211_WOWLAN_TRIG_NET_DETECT]) { | ||
8880 | err = nl80211_parse_wowlan_nd( | ||
8881 | rdev, wowlan, tb[NL80211_WOWLAN_TRIG_NET_DETECT], | ||
8882 | &new_triggers); | ||
8883 | if (err) | ||
8884 | goto error; | ||
8885 | } | ||
8886 | |||
8843 | ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL); | 8887 | ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL); |
8844 | if (!ntrig) { | 8888 | if (!ntrig) { |
8845 | err = -ENOMEM; | 8889 | err = -ENOMEM; |
@@ -12082,6 +12126,67 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy, | |||
12082 | EXPORT_SYMBOL(cfg80211_report_obss_beacon); | 12126 | EXPORT_SYMBOL(cfg80211_report_obss_beacon); |
12083 | 12127 | ||
12084 | #ifdef CONFIG_PM | 12128 | #ifdef CONFIG_PM |
12129 | static int cfg80211_net_detect_results(struct sk_buff *msg, | ||
12130 | struct cfg80211_wowlan_wakeup *wakeup) | ||
12131 | { | ||
12132 | struct cfg80211_wowlan_nd_info *nd = wakeup->net_detect; | ||
12133 | struct nlattr *nl_results, *nl_match, *nl_freqs; | ||
12134 | int i, j; | ||
12135 | |||
12136 | nl_results = nla_nest_start( | ||
12137 | msg, NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS); | ||
12138 | if (!nl_results) | ||
12139 | return -EMSGSIZE; | ||
12140 | |||
12141 | for (i = 0; i < nd->n_matches; i++) { | ||
12142 | struct cfg80211_wowlan_nd_match *match = nd->matches[i]; | ||
12143 | |||
12144 | nl_match = nla_nest_start(msg, i); | ||
12145 | if (!nl_match) | ||
12146 | break; | ||
12147 | |||
12148 | /* The SSID attribute is optional in nl80211, but for | ||
12149 | * simplicity reasons it's always present in the | ||
12150 | * cfg80211 structure. If a driver can't pass the | ||
12151 | * SSID, that needs to be changed. A zero length SSID | ||
12152 | * is still a valid SSID (wildcard), so it cannot be | ||
12153 | * used for this purpose. | ||
12154 | */ | ||
12155 | if (nla_put(msg, NL80211_ATTR_SSID, match->ssid.ssid_len, | ||
12156 | match->ssid.ssid)) { | ||
12157 | nla_nest_cancel(msg, nl_match); | ||
12158 | goto out; | ||
12159 | } | ||
12160 | |||
12161 | if (match->n_channels) { | ||
12162 | nl_freqs = nla_nest_start( | ||
12163 | msg, NL80211_ATTR_SCAN_FREQUENCIES); | ||
12164 | if (!nl_freqs) { | ||
12165 | nla_nest_cancel(msg, nl_match); | ||
12166 | goto out; | ||
12167 | } | ||
12168 | |||
12169 | for (j = 0; j < match->n_channels; j++) { | ||
12170 | if (nla_put_u32(msg, | ||
12171 | NL80211_ATTR_WIPHY_FREQ, | ||
12172 | match->channels[j])) { | ||
12173 | nla_nest_cancel(msg, nl_freqs); | ||
12174 | nla_nest_cancel(msg, nl_match); | ||
12175 | goto out; | ||
12176 | } | ||
12177 | } | ||
12178 | |||
12179 | nla_nest_end(msg, nl_freqs); | ||
12180 | } | ||
12181 | |||
12182 | nla_nest_end(msg, nl_match); | ||
12183 | } | ||
12184 | |||
12185 | out: | ||
12186 | nla_nest_end(msg, nl_results); | ||
12187 | return 0; | ||
12188 | } | ||
12189 | |||
12085 | void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, | 12190 | void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, |
12086 | struct cfg80211_wowlan_wakeup *wakeup, | 12191 | struct cfg80211_wowlan_wakeup *wakeup, |
12087 | gfp_t gfp) | 12192 | gfp_t gfp) |
@@ -12176,6 +12281,10 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, | |||
12176 | goto free_msg; | 12281 | goto free_msg; |
12177 | } | 12282 | } |
12178 | 12283 | ||
12284 | if (wakeup->net_detect && | ||
12285 | cfg80211_net_detect_results(msg, wakeup)) | ||
12286 | goto free_msg; | ||
12287 | |||
12179 | nla_nest_end(msg, reasons); | 12288 | nla_nest_end(msg, reasons); |
12180 | } | 12289 | } |
12181 | 12290 | ||