diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/wireless/core.c | 9 | ||||
-rw-r--r-- | net/wireless/core.h | 2 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 308 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 2 |
4 files changed, 321 insertions, 0 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c index 4f9f216665e9..389a3f2ee464 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -462,6 +462,14 @@ int wiphy_register(struct wiphy *wiphy) | |||
462 | return -EINVAL; | 462 | return -EINVAL; |
463 | #endif | 463 | #endif |
464 | 464 | ||
465 | if (WARN_ON(wiphy->coalesce && | ||
466 | (!wiphy->coalesce->n_rules || | ||
467 | !wiphy->coalesce->n_patterns) && | ||
468 | (!wiphy->coalesce->pattern_min_len || | ||
469 | wiphy->coalesce->pattern_min_len > | ||
470 | wiphy->coalesce->pattern_max_len))) | ||
471 | return -EINVAL; | ||
472 | |||
465 | if (WARN_ON(wiphy->ap_sme_capa && | 473 | if (WARN_ON(wiphy->ap_sme_capa && |
466 | !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) | 474 | !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) |
467 | return -EINVAL; | 475 | return -EINVAL; |
@@ -668,6 +676,7 @@ void wiphy_unregister(struct wiphy *wiphy) | |||
668 | rdev_set_wakeup(rdev, false); | 676 | rdev_set_wakeup(rdev, false); |
669 | #endif | 677 | #endif |
670 | cfg80211_rdev_free_wowlan(rdev); | 678 | cfg80211_rdev_free_wowlan(rdev); |
679 | cfg80211_rdev_free_coalesce(rdev); | ||
671 | } | 680 | } |
672 | EXPORT_SYMBOL(wiphy_unregister); | 681 | EXPORT_SYMBOL(wiphy_unregister); |
673 | 682 | ||
diff --git a/net/wireless/core.h b/net/wireless/core.h index a6b45bf00f33..9ad43c619c54 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -79,6 +79,8 @@ struct cfg80211_registered_device { | |||
79 | /* netlink port which started critical protocol (0 means not started) */ | 79 | /* netlink port which started critical protocol (0 means not started) */ |
80 | u32 crit_proto_nlportid; | 80 | u32 crit_proto_nlportid; |
81 | 81 | ||
82 | struct cfg80211_coalesce *coalesce; | ||
83 | |||
82 | /* must be last because of the way we do wiphy_priv(), | 84 | /* must be last because of the way we do wiphy_priv(), |
83 | * and it should at least be aligned to NETDEV_ALIGN */ | 85 | * and it should at least be aligned to NETDEV_ALIGN */ |
84 | struct wiphy wiphy __aligned(NETDEV_ALIGN); | 86 | struct wiphy wiphy __aligned(NETDEV_ALIGN); |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0492478ab74e..6dca5a700174 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -403,6 +403,14 @@ nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = { | |||
403 | [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 }, | 403 | [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 }, |
404 | }; | 404 | }; |
405 | 405 | ||
406 | /* policy for coalesce rule attributes */ | ||
407 | static const struct nla_policy | ||
408 | nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = { | ||
409 | [NL80211_ATTR_COALESCE_RULE_DELAY] = { .type = NLA_U32 }, | ||
410 | [NL80211_ATTR_COALESCE_RULE_CONDITION] = { .type = NLA_U32 }, | ||
411 | [NL80211_ATTR_COALESCE_RULE_PKT_PATTERN] = { .type = NLA_NESTED }, | ||
412 | }; | ||
413 | |||
406 | /* policy for GTK rekey offload attributes */ | 414 | /* policy for GTK rekey offload attributes */ |
407 | static const struct nla_policy | 415 | static const struct nla_policy |
408 | nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { | 416 | nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { |
@@ -995,6 +1003,27 @@ static int nl80211_send_wowlan(struct sk_buff *msg, | |||
995 | } | 1003 | } |
996 | #endif | 1004 | #endif |
997 | 1005 | ||
1006 | static int nl80211_send_coalesce(struct sk_buff *msg, | ||
1007 | struct cfg80211_registered_device *dev) | ||
1008 | { | ||
1009 | struct nl80211_coalesce_rule_support rule; | ||
1010 | |||
1011 | if (!dev->wiphy.coalesce) | ||
1012 | return 0; | ||
1013 | |||
1014 | rule.max_rules = dev->wiphy.coalesce->n_rules; | ||
1015 | rule.max_delay = dev->wiphy.coalesce->max_delay; | ||
1016 | rule.pat.max_patterns = dev->wiphy.coalesce->n_patterns; | ||
1017 | rule.pat.min_pattern_len = dev->wiphy.coalesce->pattern_min_len; | ||
1018 | rule.pat.max_pattern_len = dev->wiphy.coalesce->pattern_max_len; | ||
1019 | rule.pat.max_pkt_offset = dev->wiphy.coalesce->max_pkt_offset; | ||
1020 | |||
1021 | if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule)) | ||
1022 | return -ENOBUFS; | ||
1023 | |||
1024 | return 0; | ||
1025 | } | ||
1026 | |||
998 | static int nl80211_send_band_rateinfo(struct sk_buff *msg, | 1027 | static int nl80211_send_band_rateinfo(struct sk_buff *msg, |
999 | struct ieee80211_supported_band *sband) | 1028 | struct ieee80211_supported_band *sband) |
1000 | { | 1029 | { |
@@ -1513,6 +1542,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, | |||
1513 | dev->wiphy.vht_capa_mod_mask)) | 1542 | dev->wiphy.vht_capa_mod_mask)) |
1514 | goto nla_put_failure; | 1543 | goto nla_put_failure; |
1515 | 1544 | ||
1545 | state->split_start++; | ||
1546 | break; | ||
1547 | case 10: | ||
1548 | if (nl80211_send_coalesce(msg, dev)) | ||
1549 | goto nla_put_failure; | ||
1550 | |||
1516 | /* done */ | 1551 | /* done */ |
1517 | state->split_start = 0; | 1552 | state->split_start = 0; |
1518 | break; | 1553 | break; |
@@ -8043,6 +8078,264 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) | |||
8043 | } | 8078 | } |
8044 | #endif | 8079 | #endif |
8045 | 8080 | ||
8081 | static int nl80211_send_coalesce_rules(struct sk_buff *msg, | ||
8082 | struct cfg80211_registered_device *rdev) | ||
8083 | { | ||
8084 | struct nlattr *nl_pats, *nl_pat, *nl_rule, *nl_rules; | ||
8085 | int i, j, pat_len; | ||
8086 | struct cfg80211_coalesce_rules *rule; | ||
8087 | |||
8088 | if (!rdev->coalesce->n_rules) | ||
8089 | return 0; | ||
8090 | |||
8091 | nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE); | ||
8092 | if (!nl_rules) | ||
8093 | return -ENOBUFS; | ||
8094 | |||
8095 | for (i = 0; i < rdev->coalesce->n_rules; i++) { | ||
8096 | nl_rule = nla_nest_start(msg, i + 1); | ||
8097 | if (!nl_rule) | ||
8098 | return -ENOBUFS; | ||
8099 | |||
8100 | rule = &rdev->coalesce->rules[i]; | ||
8101 | if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_DELAY, | ||
8102 | rule->delay)) | ||
8103 | return -ENOBUFS; | ||
8104 | |||
8105 | if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION, | ||
8106 | rule->condition)) | ||
8107 | return -ENOBUFS; | ||
8108 | |||
8109 | nl_pats = nla_nest_start(msg, | ||
8110 | NL80211_ATTR_COALESCE_RULE_PKT_PATTERN); | ||
8111 | if (!nl_pats) | ||
8112 | return -ENOBUFS; | ||
8113 | |||
8114 | for (j = 0; j < rule->n_patterns; j++) { | ||
8115 | nl_pat = nla_nest_start(msg, j + 1); | ||
8116 | if (!nl_pat) | ||
8117 | return -ENOBUFS; | ||
8118 | pat_len = rule->patterns[j].pattern_len; | ||
8119 | if (nla_put(msg, NL80211_PKTPAT_MASK, | ||
8120 | DIV_ROUND_UP(pat_len, 8), | ||
8121 | rule->patterns[j].mask) || | ||
8122 | nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len, | ||
8123 | rule->patterns[j].pattern) || | ||
8124 | nla_put_u32(msg, NL80211_PKTPAT_OFFSET, | ||
8125 | rule->patterns[j].pkt_offset)) | ||
8126 | return -ENOBUFS; | ||
8127 | nla_nest_end(msg, nl_pat); | ||
8128 | } | ||
8129 | nla_nest_end(msg, nl_pats); | ||
8130 | nla_nest_end(msg, nl_rule); | ||
8131 | } | ||
8132 | nla_nest_end(msg, nl_rules); | ||
8133 | |||
8134 | return 0; | ||
8135 | } | ||
8136 | |||
8137 | static int nl80211_get_coalesce(struct sk_buff *skb, struct genl_info *info) | ||
8138 | { | ||
8139 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
8140 | struct sk_buff *msg; | ||
8141 | void *hdr; | ||
8142 | |||
8143 | if (!rdev->wiphy.coalesce) | ||
8144 | return -EOPNOTSUPP; | ||
8145 | |||
8146 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
8147 | if (!msg) | ||
8148 | return -ENOMEM; | ||
8149 | |||
8150 | hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, | ||
8151 | NL80211_CMD_GET_COALESCE); | ||
8152 | if (!hdr) | ||
8153 | goto nla_put_failure; | ||
8154 | |||
8155 | if (rdev->coalesce && nl80211_send_coalesce_rules(msg, rdev)) | ||
8156 | goto nla_put_failure; | ||
8157 | |||
8158 | genlmsg_end(msg, hdr); | ||
8159 | return genlmsg_reply(msg, info); | ||
8160 | |||
8161 | nla_put_failure: | ||
8162 | nlmsg_free(msg); | ||
8163 | return -ENOBUFS; | ||
8164 | } | ||
8165 | |||
8166 | void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev) | ||
8167 | { | ||
8168 | struct cfg80211_coalesce *coalesce = rdev->coalesce; | ||
8169 | int i, j; | ||
8170 | struct cfg80211_coalesce_rules *rule; | ||
8171 | |||
8172 | if (!coalesce) | ||
8173 | return; | ||
8174 | |||
8175 | for (i = 0; i < coalesce->n_rules; i++) { | ||
8176 | rule = &coalesce->rules[i]; | ||
8177 | for (j = 0; j < rule->n_patterns; j++) | ||
8178 | kfree(rule->patterns[j].mask); | ||
8179 | kfree(rule->patterns); | ||
8180 | } | ||
8181 | kfree(coalesce->rules); | ||
8182 | kfree(coalesce); | ||
8183 | rdev->coalesce = NULL; | ||
8184 | } | ||
8185 | |||
8186 | static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev, | ||
8187 | struct nlattr *rule, | ||
8188 | struct cfg80211_coalesce_rules *new_rule) | ||
8189 | { | ||
8190 | int err, i; | ||
8191 | const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce; | ||
8192 | struct nlattr *tb[NUM_NL80211_ATTR_COALESCE_RULE], *pat; | ||
8193 | int rem, pat_len, mask_len, pkt_offset, n_patterns = 0; | ||
8194 | struct nlattr *pat_tb[NUM_NL80211_PKTPAT]; | ||
8195 | |||
8196 | err = nla_parse(tb, NL80211_ATTR_COALESCE_RULE_MAX, nla_data(rule), | ||
8197 | nla_len(rule), nl80211_coalesce_policy); | ||
8198 | if (err) | ||
8199 | return err; | ||
8200 | |||
8201 | if (tb[NL80211_ATTR_COALESCE_RULE_DELAY]) | ||
8202 | new_rule->delay = | ||
8203 | nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_DELAY]); | ||
8204 | if (new_rule->delay > coalesce->max_delay) | ||
8205 | return -EINVAL; | ||
8206 | |||
8207 | if (tb[NL80211_ATTR_COALESCE_RULE_CONDITION]) | ||
8208 | new_rule->condition = | ||
8209 | nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_CONDITION]); | ||
8210 | if (new_rule->condition != NL80211_COALESCE_CONDITION_MATCH && | ||
8211 | new_rule->condition != NL80211_COALESCE_CONDITION_NO_MATCH) | ||
8212 | return -EINVAL; | ||
8213 | |||
8214 | if (!tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN]) | ||
8215 | return -EINVAL; | ||
8216 | |||
8217 | nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN], | ||
8218 | rem) | ||
8219 | n_patterns++; | ||
8220 | if (n_patterns > coalesce->n_patterns) | ||
8221 | return -EINVAL; | ||
8222 | |||
8223 | new_rule->patterns = kcalloc(n_patterns, sizeof(new_rule->patterns[0]), | ||
8224 | GFP_KERNEL); | ||
8225 | if (!new_rule->patterns) | ||
8226 | return -ENOMEM; | ||
8227 | |||
8228 | new_rule->n_patterns = n_patterns; | ||
8229 | i = 0; | ||
8230 | |||
8231 | nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN], | ||
8232 | rem) { | ||
8233 | nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat), | ||
8234 | nla_len(pat), NULL); | ||
8235 | if (!pat_tb[NL80211_PKTPAT_MASK] || | ||
8236 | !pat_tb[NL80211_PKTPAT_PATTERN]) | ||
8237 | return -EINVAL; | ||
8238 | pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]); | ||
8239 | mask_len = DIV_ROUND_UP(pat_len, 8); | ||
8240 | if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len) | ||
8241 | return -EINVAL; | ||
8242 | if (pat_len > coalesce->pattern_max_len || | ||
8243 | pat_len < coalesce->pattern_min_len) | ||
8244 | return -EINVAL; | ||
8245 | |||
8246 | if (!pat_tb[NL80211_PKTPAT_OFFSET]) | ||
8247 | pkt_offset = 0; | ||
8248 | else | ||
8249 | pkt_offset = nla_get_u32(pat_tb[NL80211_PKTPAT_OFFSET]); | ||
8250 | if (pkt_offset > coalesce->max_pkt_offset) | ||
8251 | return -EINVAL; | ||
8252 | new_rule->patterns[i].pkt_offset = pkt_offset; | ||
8253 | |||
8254 | new_rule->patterns[i].mask = | ||
8255 | kmalloc(mask_len + pat_len, GFP_KERNEL); | ||
8256 | if (!new_rule->patterns[i].mask) | ||
8257 | return -ENOMEM; | ||
8258 | new_rule->patterns[i].pattern = | ||
8259 | new_rule->patterns[i].mask + mask_len; | ||
8260 | memcpy(new_rule->patterns[i].mask, | ||
8261 | nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len); | ||
8262 | new_rule->patterns[i].pattern_len = pat_len; | ||
8263 | memcpy(new_rule->patterns[i].pattern, | ||
8264 | nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len); | ||
8265 | i++; | ||
8266 | } | ||
8267 | |||
8268 | return 0; | ||
8269 | } | ||
8270 | |||
8271 | static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info) | ||
8272 | { | ||
8273 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
8274 | const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce; | ||
8275 | struct cfg80211_coalesce new_coalesce = {}; | ||
8276 | struct cfg80211_coalesce *n_coalesce; | ||
8277 | int err, rem_rule, n_rules = 0, i, j; | ||
8278 | struct nlattr *rule; | ||
8279 | struct cfg80211_coalesce_rules *tmp_rule; | ||
8280 | |||
8281 | if (!rdev->wiphy.coalesce || !rdev->ops->set_coalesce) | ||
8282 | return -EOPNOTSUPP; | ||
8283 | |||
8284 | if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) { | ||
8285 | cfg80211_rdev_free_coalesce(rdev); | ||
8286 | rdev->ops->set_coalesce(&rdev->wiphy, NULL); | ||
8287 | return 0; | ||
8288 | } | ||
8289 | |||
8290 | nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE], | ||
8291 | rem_rule) | ||
8292 | n_rules++; | ||
8293 | if (n_rules > coalesce->n_rules) | ||
8294 | return -EINVAL; | ||
8295 | |||
8296 | new_coalesce.rules = kcalloc(n_rules, sizeof(new_coalesce.rules[0]), | ||
8297 | GFP_KERNEL); | ||
8298 | if (!new_coalesce.rules) | ||
8299 | return -ENOMEM; | ||
8300 | |||
8301 | new_coalesce.n_rules = n_rules; | ||
8302 | i = 0; | ||
8303 | |||
8304 | nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE], | ||
8305 | rem_rule) { | ||
8306 | err = nl80211_parse_coalesce_rule(rdev, rule, | ||
8307 | &new_coalesce.rules[i]); | ||
8308 | if (err) | ||
8309 | goto error; | ||
8310 | |||
8311 | i++; | ||
8312 | } | ||
8313 | |||
8314 | err = rdev->ops->set_coalesce(&rdev->wiphy, &new_coalesce); | ||
8315 | if (err) | ||
8316 | goto error; | ||
8317 | |||
8318 | n_coalesce = kmemdup(&new_coalesce, sizeof(new_coalesce), GFP_KERNEL); | ||
8319 | if (!n_coalesce) { | ||
8320 | err = -ENOMEM; | ||
8321 | goto error; | ||
8322 | } | ||
8323 | cfg80211_rdev_free_coalesce(rdev); | ||
8324 | rdev->coalesce = n_coalesce; | ||
8325 | |||
8326 | return 0; | ||
8327 | error: | ||
8328 | for (i = 0; i < new_coalesce.n_rules; i++) { | ||
8329 | tmp_rule = &new_coalesce.rules[i]; | ||
8330 | for (j = 0; j < tmp_rule->n_patterns; j++) | ||
8331 | kfree(tmp_rule->patterns[j].mask); | ||
8332 | kfree(tmp_rule->patterns); | ||
8333 | } | ||
8334 | kfree(new_coalesce.rules); | ||
8335 | |||
8336 | return err; | ||
8337 | } | ||
8338 | |||
8046 | static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) | 8339 | static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) |
8047 | { | 8340 | { |
8048 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 8341 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
@@ -9050,6 +9343,21 @@ static struct genl_ops nl80211_ops[] = { | |||
9050 | .flags = GENL_ADMIN_PERM, | 9343 | .flags = GENL_ADMIN_PERM, |
9051 | .internal_flags = NL80211_FLAG_NEED_WDEV_UP | | 9344 | .internal_flags = NL80211_FLAG_NEED_WDEV_UP | |
9052 | NL80211_FLAG_NEED_RTNL, | 9345 | NL80211_FLAG_NEED_RTNL, |
9346 | }, | ||
9347 | { | ||
9348 | .cmd = NL80211_CMD_GET_COALESCE, | ||
9349 | .doit = nl80211_get_coalesce, | ||
9350 | .policy = nl80211_policy, | ||
9351 | .internal_flags = NL80211_FLAG_NEED_WIPHY | | ||
9352 | NL80211_FLAG_NEED_RTNL, | ||
9353 | }, | ||
9354 | { | ||
9355 | .cmd = NL80211_CMD_SET_COALESCE, | ||
9356 | .doit = nl80211_set_coalesce, | ||
9357 | .policy = nl80211_policy, | ||
9358 | .flags = GENL_ADMIN_PERM, | ||
9359 | .internal_flags = NL80211_FLAG_NEED_WIPHY | | ||
9360 | NL80211_FLAG_NEED_RTNL, | ||
9053 | } | 9361 | } |
9054 | }; | 9362 | }; |
9055 | 9363 | ||
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index a4073e808c13..44341bf53cfc 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h | |||
@@ -74,4 +74,6 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev, | |||
74 | enum nl80211_radar_event event, | 74 | enum nl80211_radar_event event, |
75 | struct net_device *netdev, gfp_t gfp); | 75 | struct net_device *netdev, gfp_t gfp); |
76 | 76 | ||
77 | void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev); | ||
78 | |||
77 | #endif /* __NET_WIRELESS_NL80211_H */ | 79 | #endif /* __NET_WIRELESS_NL80211_H */ |