aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2011-05-04 09:37:28 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-05-05 14:59:19 -0400
commitff1b6e69ad4f31fb3c9c6da2665655f2e798dd70 (patch)
tree6fc049fd0389ffb382ea401096d7bd665642af5c /net
parent8f7f3b2fcc4ccbba0be776049df41a2f96c986ac (diff)
nl80211/cfg80211: WoWLAN support
This is based on (but now quite far from) the original work from Luis and Eliad. Add support for configuring WoWLAN triggers, and getting the configuration out again. Changes from the original patchset are too numerous to list, but one important change needs highlighting: the suspend() callback is passed NULL for the trigger configuration if userspace has not configured WoWLAN at all. Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com> Signed-off-by: Eliad Peller <eliad@wizery.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/cfg.c3
-rw-r--r--net/wireless/core.c8
-rw-r--r--net/wireless/core.h14
-rw-r--r--net/wireless/nl80211.c243
-rw-r--r--net/wireless/sysfs.c2
5 files changed, 268 insertions, 2 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 12d52cec9515..321d598eb8cb 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1297,7 +1297,8 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
1297} 1297}
1298 1298
1299#ifdef CONFIG_PM 1299#ifdef CONFIG_PM
1300static int ieee80211_suspend(struct wiphy *wiphy) 1300static int ieee80211_suspend(struct wiphy *wiphy,
1301 struct cfg80211_wowlan *wowlan)
1301{ 1302{
1302 return __ieee80211_suspend(wiphy_priv(wiphy)); 1303 return __ieee80211_suspend(wiphy_priv(wiphy));
1303} 1304}
diff --git a/net/wireless/core.c b/net/wireless/core.c
index bbf1fa11107a..bea0d80710c8 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -493,6 +493,13 @@ int wiphy_register(struct wiphy *wiphy)
493 return -EINVAL; 493 return -EINVAL;
494 } 494 }
495 495
496 if (rdev->wiphy.wowlan.n_patterns) {
497 if (WARN_ON(!rdev->wiphy.wowlan.pattern_min_len ||
498 rdev->wiphy.wowlan.pattern_min_len >
499 rdev->wiphy.wowlan.pattern_max_len))
500 return -EINVAL;
501 }
502
496 /* check and set up bitrates */ 503 /* check and set up bitrates */
497 ieee80211_set_bitrate_flags(wiphy); 504 ieee80211_set_bitrate_flags(wiphy);
498 505
@@ -631,6 +638,7 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
631 mutex_destroy(&rdev->devlist_mtx); 638 mutex_destroy(&rdev->devlist_mtx);
632 list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) 639 list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list)
633 cfg80211_put_bss(&scan->pub); 640 cfg80211_put_bss(&scan->pub);
641 cfg80211_rdev_free_wowlan(rdev);
634 kfree(rdev); 642 kfree(rdev);
635} 643}
636 644
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 26a0a084e16b..7a18c10a7fb6 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -70,6 +70,8 @@ struct cfg80211_registered_device {
70 struct work_struct conn_work; 70 struct work_struct conn_work;
71 struct work_struct event_work; 71 struct work_struct event_work;
72 72
73 struct cfg80211_wowlan *wowlan;
74
73 /* must be last because of the way we do wiphy_priv(), 75 /* must be last because of the way we do wiphy_priv(),
74 * and it should at least be aligned to NETDEV_ALIGN */ 76 * and it should at least be aligned to NETDEV_ALIGN */
75 struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); 77 struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN)));
@@ -89,6 +91,18 @@ bool wiphy_idx_valid(int wiphy_idx)
89 return wiphy_idx >= 0; 91 return wiphy_idx >= 0;
90} 92}
91 93
94static inline void
95cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev)
96{
97 int i;
98
99 if (!rdev->wowlan)
100 return;
101 for (i = 0; i < rdev->wowlan->n_patterns; i++)
102 kfree(rdev->wowlan->patterns[i].mask);
103 kfree(rdev->wowlan->patterns);
104 kfree(rdev->wowlan);
105}
92 106
93extern struct workqueue_struct *cfg80211_wq; 107extern struct workqueue_struct *cfg80211_wq;
94extern struct mutex cfg80211_mutex; 108extern struct mutex cfg80211_mutex;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index ab77f943dc04..0a199a1ca9b6 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -173,6 +173,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
173 [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 }, 173 [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 },
174 [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG }, 174 [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG },
175 [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, 175 [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
176 [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
176}; 177};
177 178
178/* policy for the key attributes */ 179/* policy for the key attributes */
@@ -194,6 +195,15 @@ nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = {
194 [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG }, 195 [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG },
195}; 196};
196 197
198/* policy for WoWLAN attributes */
199static const struct nla_policy
200nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
201 [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
202 [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
203 [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
204 [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
205};
206
197/* ifidx get helper */ 207/* ifidx get helper */
198static int nl80211_get_ifidx(struct netlink_callback *cb) 208static int nl80211_get_ifidx(struct netlink_callback *cb)
199{ 209{
@@ -821,6 +831,35 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
821 nla_nest_end(msg, nl_ifs); 831 nla_nest_end(msg, nl_ifs);
822 } 832 }
823 833
834 if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) {
835 struct nlattr *nl_wowlan;
836
837 nl_wowlan = nla_nest_start(msg,
838 NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
839 if (!nl_wowlan)
840 goto nla_put_failure;
841
842 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY)
843 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
844 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT)
845 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
846 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT)
847 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
848 if (dev->wiphy.wowlan.n_patterns) {
849 struct nl80211_wowlan_pattern_support pat = {
850 .max_patterns = dev->wiphy.wowlan.n_patterns,
851 .min_pattern_len =
852 dev->wiphy.wowlan.pattern_min_len,
853 .max_pattern_len =
854 dev->wiphy.wowlan.pattern_max_len,
855 };
856 NLA_PUT(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
857 sizeof(pat), &pat);
858 }
859
860 nla_nest_end(msg, nl_wowlan);
861 }
862
824 return genlmsg_end(msg, hdr); 863 return genlmsg_end(msg, hdr);
825 864
826 nla_put_failure: 865 nla_put_failure:
@@ -4808,6 +4847,194 @@ static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
4808 return cfg80211_leave_mesh(rdev, dev); 4847 return cfg80211_leave_mesh(rdev, dev);
4809} 4848}
4810 4849
4850static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
4851{
4852 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4853 struct sk_buff *msg;
4854 void *hdr;
4855
4856 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
4857 return -EOPNOTSUPP;
4858
4859 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
4860 if (!msg)
4861 return -ENOMEM;
4862
4863 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
4864 NL80211_CMD_GET_WOWLAN);
4865 if (!hdr)
4866 goto nla_put_failure;
4867
4868 if (rdev->wowlan) {
4869 struct nlattr *nl_wowlan;
4870
4871 nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
4872 if (!nl_wowlan)
4873 goto nla_put_failure;
4874
4875 if (rdev->wowlan->any)
4876 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
4877 if (rdev->wowlan->disconnect)
4878 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
4879 if (rdev->wowlan->magic_pkt)
4880 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
4881 if (rdev->wowlan->n_patterns) {
4882 struct nlattr *nl_pats, *nl_pat;
4883 int i, pat_len;
4884
4885 nl_pats = nla_nest_start(msg,
4886 NL80211_WOWLAN_TRIG_PKT_PATTERN);
4887 if (!nl_pats)
4888 goto nla_put_failure;
4889
4890 for (i = 0; i < rdev->wowlan->n_patterns; i++) {
4891 nl_pat = nla_nest_start(msg, i + 1);
4892 if (!nl_pat)
4893 goto nla_put_failure;
4894 pat_len = rdev->wowlan->patterns[i].pattern_len;
4895 NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_MASK,
4896 DIV_ROUND_UP(pat_len, 8),
4897 rdev->wowlan->patterns[i].mask);
4898 NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
4899 pat_len,
4900 rdev->wowlan->patterns[i].pattern);
4901 nla_nest_end(msg, nl_pat);
4902 }
4903 nla_nest_end(msg, nl_pats);
4904 }
4905
4906 nla_nest_end(msg, nl_wowlan);
4907 }
4908
4909 genlmsg_end(msg, hdr);
4910 return genlmsg_reply(msg, info);
4911
4912nla_put_failure:
4913 nlmsg_free(msg);
4914 return -ENOBUFS;
4915}
4916
4917static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
4918{
4919 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4920 struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
4921 struct cfg80211_wowlan no_triggers = {};
4922 struct cfg80211_wowlan new_triggers = {};
4923 struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
4924 int err, i;
4925
4926 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
4927 return -EOPNOTSUPP;
4928
4929 if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS])
4930 goto no_triggers;
4931
4932 err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
4933 nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
4934 nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
4935 nl80211_wowlan_policy);
4936 if (err)
4937 return err;
4938
4939 if (tb[NL80211_WOWLAN_TRIG_ANY]) {
4940 if (!(wowlan->flags & WIPHY_WOWLAN_ANY))
4941 return -EINVAL;
4942 new_triggers.any = true;
4943 }
4944
4945 if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) {
4946 if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
4947 return -EINVAL;
4948 new_triggers.disconnect = true;
4949 }
4950
4951 if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
4952 if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
4953 return -EINVAL;
4954 new_triggers.magic_pkt = true;
4955 }
4956
4957 if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
4958 struct nlattr *pat;
4959 int n_patterns = 0;
4960 int rem, pat_len, mask_len;
4961 struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
4962
4963 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
4964 rem)
4965 n_patterns++;
4966 if (n_patterns > wowlan->n_patterns)
4967 return -EINVAL;
4968
4969 new_triggers.patterns = kcalloc(n_patterns,
4970 sizeof(new_triggers.patterns[0]),
4971 GFP_KERNEL);
4972 if (!new_triggers.patterns)
4973 return -ENOMEM;
4974
4975 new_triggers.n_patterns = n_patterns;
4976 i = 0;
4977
4978 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
4979 rem) {
4980 nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
4981 nla_data(pat), nla_len(pat), NULL);
4982 err = -EINVAL;
4983 if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
4984 !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
4985 goto error;
4986 pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
4987 mask_len = DIV_ROUND_UP(pat_len, 8);
4988 if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
4989 mask_len)
4990 goto error;
4991 if (pat_len > wowlan->pattern_max_len ||
4992 pat_len < wowlan->pattern_min_len)
4993 goto error;
4994
4995 new_triggers.patterns[i].mask =
4996 kmalloc(mask_len + pat_len, GFP_KERNEL);
4997 if (!new_triggers.patterns[i].mask) {
4998 err = -ENOMEM;
4999 goto error;
5000 }
5001 new_triggers.patterns[i].pattern =
5002 new_triggers.patterns[i].mask + mask_len;
5003 memcpy(new_triggers.patterns[i].mask,
5004 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
5005 mask_len);
5006 new_triggers.patterns[i].pattern_len = pat_len;
5007 memcpy(new_triggers.patterns[i].pattern,
5008 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
5009 pat_len);
5010 i++;
5011 }
5012 }
5013
5014 if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) {
5015 struct cfg80211_wowlan *ntrig;
5016 ntrig = kmemdup(&new_triggers, sizeof(new_triggers),
5017 GFP_KERNEL);
5018 if (!ntrig) {
5019 err = -ENOMEM;
5020 goto error;
5021 }
5022 cfg80211_rdev_free_wowlan(rdev);
5023 rdev->wowlan = ntrig;
5024 } else {
5025 no_triggers:
5026 cfg80211_rdev_free_wowlan(rdev);
5027 rdev->wowlan = NULL;
5028 }
5029
5030 return 0;
5031 error:
5032 for (i = 0; i < new_triggers.n_patterns; i++)
5033 kfree(new_triggers.patterns[i].mask);
5034 kfree(new_triggers.patterns);
5035 return err;
5036}
5037
4811#define NL80211_FLAG_NEED_WIPHY 0x01 5038#define NL80211_FLAG_NEED_WIPHY 0x01
4812#define NL80211_FLAG_NEED_NETDEV 0x02 5039#define NL80211_FLAG_NEED_NETDEV 0x02
4813#define NL80211_FLAG_NEED_RTNL 0x04 5040#define NL80211_FLAG_NEED_RTNL 0x04
@@ -5306,6 +5533,22 @@ static struct genl_ops nl80211_ops[] = {
5306 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | 5533 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5307 NL80211_FLAG_NEED_RTNL, 5534 NL80211_FLAG_NEED_RTNL,
5308 }, 5535 },
5536 {
5537 .cmd = NL80211_CMD_GET_WOWLAN,
5538 .doit = nl80211_get_wowlan,
5539 .policy = nl80211_policy,
5540 /* can be retrieved by unprivileged users */
5541 .internal_flags = NL80211_FLAG_NEED_WIPHY |
5542 NL80211_FLAG_NEED_RTNL,
5543 },
5544 {
5545 .cmd = NL80211_CMD_SET_WOWLAN,
5546 .doit = nl80211_set_wowlan,
5547 .policy = nl80211_policy,
5548 .flags = GENL_ADMIN_PERM,
5549 .internal_flags = NL80211_FLAG_NEED_WIPHY |
5550 NL80211_FLAG_NEED_RTNL,
5551 },
5309}; 5552};
5310 5553
5311static struct genl_multicast_group nl80211_mlme_mcgrp = { 5554static struct genl_multicast_group nl80211_mlme_mcgrp = {
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
index 4294fa22bb2d..c6e4ca6a7d2e 100644
--- a/net/wireless/sysfs.c
+++ b/net/wireless/sysfs.c
@@ -93,7 +93,7 @@ static int wiphy_suspend(struct device *dev, pm_message_t state)
93 93
94 if (rdev->ops->suspend) { 94 if (rdev->ops->suspend) {
95 rtnl_lock(); 95 rtnl_lock();
96 ret = rdev->ops->suspend(&rdev->wiphy); 96 ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan);
97 rtnl_unlock(); 97 rtnl_unlock();
98 } 98 }
99 99