aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/nl80211.c
diff options
context:
space:
mode:
authorLuciano Coelho <luciano.coelho@intel.com>2014-09-17 04:55:28 -0400
committerJohannes Berg <johannes.berg@intel.com>2014-11-19 12:45:45 -0500
commit8cd4d4563ef0a518002c4a8f47dd950afe386ea8 (patch)
tree11aac072065c8eeaa882b3386e9368107defa12f /net/wireless/nl80211.c
parent256da02d1806c740be97576a5e8548d658858319 (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.c111
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 */
212static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { 212static 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
433static const struct nla_policy 434static 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
8701static 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
8729out:
8730 kfree(tb);
8731 return err;
8732}
8733
8698static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) 8734static 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,
12082EXPORT_SYMBOL(cfg80211_report_obss_beacon); 12126EXPORT_SYMBOL(cfg80211_report_obss_beacon);
12083 12127
12084#ifdef CONFIG_PM 12128#ifdef CONFIG_PM
12129static 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
12185out:
12186 nla_nest_end(msg, nl_results);
12187 return 0;
12188}
12189
12085void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, 12190void 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