aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless')
-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
4 files changed, 266 insertions, 1 deletions
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