diff options
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/core.c | 8 | ||||
-rw-r--r-- | net/wireless/core.h | 14 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 243 | ||||
-rw-r--r-- | net/wireless/sysfs.c | 2 |
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 | ||
94 | static inline void | ||
95 | cfg80211_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 | ||
93 | extern struct workqueue_struct *cfg80211_wq; | 107 | extern struct workqueue_struct *cfg80211_wq; |
94 | extern struct mutex cfg80211_mutex; | 108 | extern 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 */ | ||
199 | static const struct nla_policy | ||
200 | nl80211_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 */ |
198 | static int nl80211_get_ifidx(struct netlink_callback *cb) | 208 | static 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 | ||
4850 | static 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 | |||
4912 | nla_put_failure: | ||
4913 | nlmsg_free(msg); | ||
4914 | return -ENOBUFS; | ||
4915 | } | ||
4916 | |||
4917 | static 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 | ||
5311 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 5554 | static 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 | ||