diff options
-rw-r--r-- | include/net/cfg80211.h | 54 | ||||
-rw-r--r-- | include/uapi/linux/nl80211.h | 90 | ||||
-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 |
6 files changed, 463 insertions, 2 deletions
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 49409602fe3d..071ed2395c9a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h | |||
@@ -1781,6 +1781,35 @@ struct cfg80211_wowlan { | |||
1781 | }; | 1781 | }; |
1782 | 1782 | ||
1783 | /** | 1783 | /** |
1784 | * struct cfg80211_coalesce_rules - Coalesce rule parameters | ||
1785 | * | ||
1786 | * This structure defines coalesce rule for the device. | ||
1787 | * @delay: maximum coalescing delay in msecs. | ||
1788 | * @condition: condition for packet coalescence. | ||
1789 | * see &enum nl80211_coalesce_condition. | ||
1790 | * @patterns: array of packet patterns | ||
1791 | * @n_patterns: number of patterns | ||
1792 | */ | ||
1793 | struct cfg80211_coalesce_rules { | ||
1794 | int delay; | ||
1795 | enum nl80211_coalesce_condition condition; | ||
1796 | struct cfg80211_pkt_pattern *patterns; | ||
1797 | int n_patterns; | ||
1798 | }; | ||
1799 | |||
1800 | /** | ||
1801 | * struct cfg80211_coalesce - Packet coalescing settings | ||
1802 | * | ||
1803 | * This structure defines coalescing settings. | ||
1804 | * @rules: array of coalesce rules | ||
1805 | * @n_rules: number of rules | ||
1806 | */ | ||
1807 | struct cfg80211_coalesce { | ||
1808 | struct cfg80211_coalesce_rules *rules; | ||
1809 | int n_rules; | ||
1810 | }; | ||
1811 | |||
1812 | /** | ||
1784 | * struct cfg80211_wowlan_wakeup - wakeup report | 1813 | * struct cfg80211_wowlan_wakeup - wakeup report |
1785 | * @disconnect: woke up by getting disconnected | 1814 | * @disconnect: woke up by getting disconnected |
1786 | * @magic_pkt: woke up by receiving magic packet | 1815 | * @magic_pkt: woke up by receiving magic packet |
@@ -2076,6 +2105,7 @@ struct cfg80211_update_ft_ies_params { | |||
2076 | * driver can take the most appropriate actions. | 2105 | * driver can take the most appropriate actions. |
2077 | * @crit_proto_stop: Indicates critical protocol no longer needs increased link | 2106 | * @crit_proto_stop: Indicates critical protocol no longer needs increased link |
2078 | * reliability. This operation can not fail. | 2107 | * reliability. This operation can not fail. |
2108 | * @set_coalesce: Set coalesce parameters. | ||
2079 | */ | 2109 | */ |
2080 | struct cfg80211_ops { | 2110 | struct cfg80211_ops { |
2081 | int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); | 2111 | int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); |
@@ -2311,6 +2341,8 @@ struct cfg80211_ops { | |||
2311 | u16 duration); | 2341 | u16 duration); |
2312 | void (*crit_proto_stop)(struct wiphy *wiphy, | 2342 | void (*crit_proto_stop)(struct wiphy *wiphy, |
2313 | struct wireless_dev *wdev); | 2343 | struct wireless_dev *wdev); |
2344 | int (*set_coalesce)(struct wiphy *wiphy, | ||
2345 | struct cfg80211_coalesce *coalesce); | ||
2314 | }; | 2346 | }; |
2315 | 2347 | ||
2316 | /* | 2348 | /* |
@@ -2537,6 +2569,25 @@ struct wiphy_wowlan_support { | |||
2537 | }; | 2569 | }; |
2538 | 2570 | ||
2539 | /** | 2571 | /** |
2572 | * struct wiphy_coalesce_support - coalesce support data | ||
2573 | * @n_rules: maximum number of coalesce rules | ||
2574 | * @max_delay: maximum supported coalescing delay in msecs | ||
2575 | * @n_patterns: number of supported patterns in a rule | ||
2576 | * (see nl80211.h for the pattern definition) | ||
2577 | * @pattern_max_len: maximum length of each pattern | ||
2578 | * @pattern_min_len: minimum length of each pattern | ||
2579 | * @max_pkt_offset: maximum Rx packet offset | ||
2580 | */ | ||
2581 | struct wiphy_coalesce_support { | ||
2582 | int n_rules; | ||
2583 | int max_delay; | ||
2584 | int n_patterns; | ||
2585 | int pattern_max_len; | ||
2586 | int pattern_min_len; | ||
2587 | int max_pkt_offset; | ||
2588 | }; | ||
2589 | |||
2590 | /** | ||
2540 | * struct wiphy - wireless hardware description | 2591 | * struct wiphy - wireless hardware description |
2541 | * @reg_notifier: the driver's regulatory notification callback, | 2592 | * @reg_notifier: the driver's regulatory notification callback, |
2542 | * note that if your driver uses wiphy_apply_custom_regulatory() | 2593 | * note that if your driver uses wiphy_apply_custom_regulatory() |
@@ -2646,6 +2697,7 @@ struct wiphy_wowlan_support { | |||
2646 | * 802.11-2012 8.4.2.29 for the defined fields. | 2697 | * 802.11-2012 8.4.2.29 for the defined fields. |
2647 | * @extended_capabilities_mask: mask of the valid values | 2698 | * @extended_capabilities_mask: mask of the valid values |
2648 | * @extended_capabilities_len: length of the extended capabilities | 2699 | * @extended_capabilities_len: length of the extended capabilities |
2700 | * @coalesce: packet coalescing support information | ||
2649 | */ | 2701 | */ |
2650 | struct wiphy { | 2702 | struct wiphy { |
2651 | /* assign these fields before you register the wiphy */ | 2703 | /* assign these fields before you register the wiphy */ |
@@ -2755,6 +2807,8 @@ struct wiphy { | |||
2755 | const struct iw_handler_def *wext; | 2807 | const struct iw_handler_def *wext; |
2756 | #endif | 2808 | #endif |
2757 | 2809 | ||
2810 | const struct wiphy_coalesce_support *coalesce; | ||
2811 | |||
2758 | char priv[0] __aligned(NETDEV_ALIGN); | 2812 | char priv[0] __aligned(NETDEV_ALIGN); |
2759 | }; | 2813 | }; |
2760 | 2814 | ||
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index de0ce809068a..5abc54d14d4d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h | |||
@@ -126,6 +126,31 @@ | |||
126 | */ | 126 | */ |
127 | 127 | ||
128 | /** | 128 | /** |
129 | * DOC: packet coalesce support | ||
130 | * | ||
131 | * In most cases, host that receives IPv4 and IPv6 multicast/broadcast | ||
132 | * packets does not do anything with these packets. Therefore the | ||
133 | * reception of these unwanted packets causes unnecessary processing | ||
134 | * and power consumption. | ||
135 | * | ||
136 | * Packet coalesce feature helps to reduce number of received interrupts | ||
137 | * to host by buffering these packets in firmware/hardware for some | ||
138 | * predefined time. Received interrupt will be generated when one of the | ||
139 | * following events occur. | ||
140 | * a) Expiration of hardware timer whose expiration time is set to maximum | ||
141 | * coalescing delay of matching coalesce rule. | ||
142 | * b) Coalescing buffer in hardware reaches it's limit. | ||
143 | * c) Packet doesn't match any of the configured coalesce rules. | ||
144 | * | ||
145 | * User needs to configure following parameters for creating a coalesce | ||
146 | * rule. | ||
147 | * a) Maximum coalescing delay | ||
148 | * b) List of packet patterns which needs to be matched | ||
149 | * c) Condition for coalescence. pattern 'match' or 'no match' | ||
150 | * Multiple such rules can be created. | ||
151 | */ | ||
152 | |||
153 | /** | ||
129 | * enum nl80211_commands - supported nl80211 commands | 154 | * enum nl80211_commands - supported nl80211 commands |
130 | * | 155 | * |
131 | * @NL80211_CMD_UNSPEC: unspecified command to catch errors | 156 | * @NL80211_CMD_UNSPEC: unspecified command to catch errors |
@@ -648,6 +673,9 @@ | |||
648 | * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can | 673 | * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can |
649 | * return back to normal. | 674 | * return back to normal. |
650 | * | 675 | * |
676 | * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules. | ||
677 | * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules. | ||
678 | * | ||
651 | * @NL80211_CMD_MAX: highest used command number | 679 | * @NL80211_CMD_MAX: highest used command number |
652 | * @__NL80211_CMD_AFTER_LAST: internal use | 680 | * @__NL80211_CMD_AFTER_LAST: internal use |
653 | */ | 681 | */ |
@@ -810,6 +838,9 @@ enum nl80211_commands { | |||
810 | NL80211_CMD_CRIT_PROTOCOL_START, | 838 | NL80211_CMD_CRIT_PROTOCOL_START, |
811 | NL80211_CMD_CRIT_PROTOCOL_STOP, | 839 | NL80211_CMD_CRIT_PROTOCOL_STOP, |
812 | 840 | ||
841 | NL80211_CMD_GET_COALESCE, | ||
842 | NL80211_CMD_SET_COALESCE, | ||
843 | |||
813 | /* add new commands above here */ | 844 | /* add new commands above here */ |
814 | 845 | ||
815 | /* used to define NL80211_CMD_MAX below */ | 846 | /* used to define NL80211_CMD_MAX below */ |
@@ -1436,6 +1467,8 @@ enum nl80211_commands { | |||
1436 | * allowed to be used with the first @NL80211_CMD_SET_STATION command to | 1467 | * allowed to be used with the first @NL80211_CMD_SET_STATION command to |
1437 | * update a TDLS peer STA entry. | 1468 | * update a TDLS peer STA entry. |
1438 | * | 1469 | * |
1470 | * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information. | ||
1471 | * | ||
1439 | * @NL80211_ATTR_MAX: highest attribute number currently defined | 1472 | * @NL80211_ATTR_MAX: highest attribute number currently defined |
1440 | * @__NL80211_ATTR_AFTER_LAST: internal use | 1473 | * @__NL80211_ATTR_AFTER_LAST: internal use |
1441 | */ | 1474 | */ |
@@ -1736,6 +1769,8 @@ enum nl80211_attrs { | |||
1736 | 1769 | ||
1737 | NL80211_ATTR_PEER_AID, | 1770 | NL80211_ATTR_PEER_AID, |
1738 | 1771 | ||
1772 | NL80211_ATTR_COALESCE_RULE, | ||
1773 | |||
1739 | /* add attributes here, update the policy in nl80211.c */ | 1774 | /* add attributes here, update the policy in nl80211.c */ |
1740 | 1775 | ||
1741 | __NL80211_ATTR_AFTER_LAST, | 1776 | __NL80211_ATTR_AFTER_LAST, |
@@ -3098,8 +3133,10 @@ enum nl80211_packet_pattern_attr { | |||
3098 | * @max_pkt_offset: maximum Rx packet offset | 3133 | * @max_pkt_offset: maximum Rx packet offset |
3099 | * | 3134 | * |
3100 | * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when | 3135 | * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when |
3101 | * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the | 3136 | * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in |
3102 | * capability information given by the kernel to userspace. | 3137 | * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of |
3138 | * %NL80211_ATTR_COALESCE_RULE in the capability information given | ||
3139 | * by the kernel to userspace. | ||
3103 | */ | 3140 | */ |
3104 | struct nl80211_pattern_support { | 3141 | struct nl80211_pattern_support { |
3105 | __u32 max_patterns; | 3142 | __u32 max_patterns; |
@@ -3318,6 +3355,55 @@ enum nl80211_wowlan_tcp_attrs { | |||
3318 | }; | 3355 | }; |
3319 | 3356 | ||
3320 | /** | 3357 | /** |
3358 | * struct nl80211_coalesce_rule_support - coalesce rule support information | ||
3359 | * @max_rules: maximum number of rules supported | ||
3360 | * @pat: packet pattern support information | ||
3361 | * @max_delay: maximum supported coalescing delay in msecs | ||
3362 | * | ||
3363 | * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the | ||
3364 | * capability information given by the kernel to userspace. | ||
3365 | */ | ||
3366 | struct nl80211_coalesce_rule_support { | ||
3367 | __u32 max_rules; | ||
3368 | struct nl80211_pattern_support pat; | ||
3369 | __u32 max_delay; | ||
3370 | } __attribute__((packed)); | ||
3371 | |||
3372 | /** | ||
3373 | * enum nl80211_attr_coalesce_rule - coalesce rule attribute | ||
3374 | * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute | ||
3375 | * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing | ||
3376 | * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence, | ||
3377 | * see &enum nl80211_coalesce_condition. | ||
3378 | * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched | ||
3379 | * after these fixed number of bytes of received packet | ||
3380 | * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes | ||
3381 | * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number | ||
3382 | */ | ||
3383 | enum nl80211_attr_coalesce_rule { | ||
3384 | __NL80211_COALESCE_RULE_INVALID, | ||
3385 | NL80211_ATTR_COALESCE_RULE_DELAY, | ||
3386 | NL80211_ATTR_COALESCE_RULE_CONDITION, | ||
3387 | NL80211_ATTR_COALESCE_RULE_PKT_PATTERN, | ||
3388 | |||
3389 | /* keep last */ | ||
3390 | NUM_NL80211_ATTR_COALESCE_RULE, | ||
3391 | NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1 | ||
3392 | }; | ||
3393 | |||
3394 | /** | ||
3395 | * enum nl80211_coalesce_condition - coalesce rule conditions | ||
3396 | * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns | ||
3397 | * in a rule are matched. | ||
3398 | * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns | ||
3399 | * in a rule are not matched. | ||
3400 | */ | ||
3401 | enum nl80211_coalesce_condition { | ||
3402 | NL80211_COALESCE_CONDITION_MATCH, | ||
3403 | NL80211_COALESCE_CONDITION_NO_MATCH | ||
3404 | }; | ||
3405 | |||
3406 | /** | ||
3321 | * enum nl80211_iface_limit_attrs - limit attributes | 3407 | * enum nl80211_iface_limit_attrs - limit attributes |
3322 | * @NL80211_IFACE_LIMIT_UNSPEC: (reserved) | 3408 | * @NL80211_IFACE_LIMIT_UNSPEC: (reserved) |
3323 | * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that | 3409 | * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that |
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 */ |