|
|
|
@ -403,6 +403,14 @@ nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = { |
|
|
|
|
[NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 }, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/* policy for coalesce rule attributes */ |
|
|
|
|
static const struct nla_policy |
|
|
|
|
nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = { |
|
|
|
|
[NL80211_ATTR_COALESCE_RULE_DELAY] = { .type = NLA_U32 }, |
|
|
|
|
[NL80211_ATTR_COALESCE_RULE_CONDITION] = { .type = NLA_U32 }, |
|
|
|
|
[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN] = { .type = NLA_NESTED }, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/* policy for GTK rekey offload attributes */ |
|
|
|
|
static const struct nla_policy |
|
|
|
|
nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { |
|
|
|
@ -974,7 +982,7 @@ static int nl80211_send_wowlan(struct sk_buff *msg, |
|
|
|
|
return -ENOBUFS; |
|
|
|
|
|
|
|
|
|
if (dev->wiphy.wowlan->n_patterns) { |
|
|
|
|
struct nl80211_wowlan_pattern_support pat = { |
|
|
|
|
struct nl80211_pattern_support pat = { |
|
|
|
|
.max_patterns = dev->wiphy.wowlan->n_patterns, |
|
|
|
|
.min_pattern_len = dev->wiphy.wowlan->pattern_min_len, |
|
|
|
|
.max_pattern_len = dev->wiphy.wowlan->pattern_max_len, |
|
|
|
@ -995,6 +1003,27 @@ static int nl80211_send_wowlan(struct sk_buff *msg, |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
static int nl80211_send_coalesce(struct sk_buff *msg, |
|
|
|
|
struct cfg80211_registered_device *dev) |
|
|
|
|
{ |
|
|
|
|
struct nl80211_coalesce_rule_support rule; |
|
|
|
|
|
|
|
|
|
if (!dev->wiphy.coalesce) |
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|
|
rule.max_rules = dev->wiphy.coalesce->n_rules; |
|
|
|
|
rule.max_delay = dev->wiphy.coalesce->max_delay; |
|
|
|
|
rule.pat.max_patterns = dev->wiphy.coalesce->n_patterns; |
|
|
|
|
rule.pat.min_pattern_len = dev->wiphy.coalesce->pattern_min_len; |
|
|
|
|
rule.pat.max_pattern_len = dev->wiphy.coalesce->pattern_max_len; |
|
|
|
|
rule.pat.max_pkt_offset = dev->wiphy.coalesce->max_pkt_offset; |
|
|
|
|
|
|
|
|
|
if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule)) |
|
|
|
|
return -ENOBUFS; |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int nl80211_send_band_rateinfo(struct sk_buff *msg, |
|
|
|
|
struct ieee80211_supported_band *sband) |
|
|
|
|
{ |
|
|
|
@ -1513,6 +1542,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, |
|
|
|
|
dev->wiphy.vht_capa_mod_mask)) |
|
|
|
|
goto nla_put_failure; |
|
|
|
|
|
|
|
|
|
state->split_start++; |
|
|
|
|
break; |
|
|
|
|
case 10: |
|
|
|
|
if (nl80211_send_coalesce(msg, dev)) |
|
|
|
|
goto nla_put_failure; |
|
|
|
|
|
|
|
|
|
/* done */ |
|
|
|
|
state->split_start = 0; |
|
|
|
|
break; |
|
|
|
@ -5639,6 +5674,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, |
|
|
|
|
goto nla_put_failure; |
|
|
|
|
if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) || |
|
|
|
|
nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) || |
|
|
|
|
nla_put_u32(msg, NL80211_BSS_CHAN_WIDTH, res->scan_width) || |
|
|
|
|
nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO, |
|
|
|
|
jiffies_to_msecs(jiffies - intbss->ts))) |
|
|
|
|
goto nla_put_failure; |
|
|
|
@ -6319,6 +6355,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) |
|
|
|
|
return -EINVAL; |
|
|
|
|
|
|
|
|
|
switch (ibss.chandef.width) { |
|
|
|
|
case NL80211_CHAN_WIDTH_5: |
|
|
|
|
case NL80211_CHAN_WIDTH_10: |
|
|
|
|
case NL80211_CHAN_WIDTH_20_NOHT: |
|
|
|
|
break; |
|
|
|
|
case NL80211_CHAN_WIDTH_20: |
|
|
|
@ -6346,6 +6384,19 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) |
|
|
|
|
return err; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) |
|
|
|
|
memcpy(&ibss.ht_capa_mask, |
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]), |
|
|
|
|
sizeof(ibss.ht_capa_mask)); |
|
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) { |
|
|
|
|
if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) |
|
|
|
|
return -EINVAL; |
|
|
|
|
memcpy(&ibss.ht_capa, |
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]), |
|
|
|
|
sizeof(ibss.ht_capa)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_MCAST_RATE] && |
|
|
|
|
!nl80211_parse_mcast_rate(rdev, ibss.mcast_rate, |
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]))) |
|
|
|
@ -7593,12 +7644,11 @@ static int nl80211_send_wowlan_patterns(struct sk_buff *msg, |
|
|
|
|
if (!nl_pat) |
|
|
|
|
return -ENOBUFS; |
|
|
|
|
pat_len = wowlan->patterns[i].pattern_len; |
|
|
|
|
if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK, |
|
|
|
|
DIV_ROUND_UP(pat_len, 8), |
|
|
|
|
if (nla_put(msg, NL80211_PKTPAT_MASK, DIV_ROUND_UP(pat_len, 8), |
|
|
|
|
wowlan->patterns[i].mask) || |
|
|
|
|
nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN, |
|
|
|
|
pat_len, wowlan->patterns[i].pattern) || |
|
|
|
|
nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET, |
|
|
|
|
nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len, |
|
|
|
|
wowlan->patterns[i].pattern) || |
|
|
|
|
nla_put_u32(msg, NL80211_PKTPAT_OFFSET, |
|
|
|
|
wowlan->patterns[i].pkt_offset)) |
|
|
|
|
return -ENOBUFS; |
|
|
|
|
nla_nest_end(msg, nl_pat); |
|
|
|
@ -7939,7 +7989,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) |
|
|
|
|
struct nlattr *pat; |
|
|
|
|
int n_patterns = 0; |
|
|
|
|
int rem, pat_len, mask_len, pkt_offset; |
|
|
|
|
struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT]; |
|
|
|
|
struct nlattr *pat_tb[NUM_NL80211_PKTPAT]; |
|
|
|
|
|
|
|
|
|
nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], |
|
|
|
|
rem) |
|
|
|
@ -7958,26 +8008,25 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) |
|
|
|
|
|
|
|
|
|
nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], |
|
|
|
|
rem) { |
|
|
|
|
nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT, |
|
|
|
|
nla_data(pat), nla_len(pat), NULL); |
|
|
|
|
nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat), |
|
|
|
|
nla_len(pat), NULL); |
|
|
|
|
err = -EINVAL; |
|
|
|
|
if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] || |
|
|
|
|
!pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]) |
|
|
|
|
if (!pat_tb[NL80211_PKTPAT_MASK] || |
|
|
|
|
!pat_tb[NL80211_PKTPAT_PATTERN]) |
|
|
|
|
goto error; |
|
|
|
|
pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]); |
|
|
|
|
pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]); |
|
|
|
|
mask_len = DIV_ROUND_UP(pat_len, 8); |
|
|
|
|
if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) != |
|
|
|
|
mask_len) |
|
|
|
|
if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len) |
|
|
|
|
goto error; |
|
|
|
|
if (pat_len > wowlan->pattern_max_len || |
|
|
|
|
pat_len < wowlan->pattern_min_len) |
|
|
|
|
goto error; |
|
|
|
|
|
|
|
|
|
if (!pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]) |
|
|
|
|
if (!pat_tb[NL80211_PKTPAT_OFFSET]) |
|
|
|
|
pkt_offset = 0; |
|
|
|
|
else |
|
|
|
|
pkt_offset = nla_get_u32( |
|
|
|
|
pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]); |
|
|
|
|
pat_tb[NL80211_PKTPAT_OFFSET]); |
|
|
|
|
if (pkt_offset > wowlan->max_pkt_offset) |
|
|
|
|
goto error; |
|
|
|
|
new_triggers.patterns[i].pkt_offset = pkt_offset; |
|
|
|
@ -7991,11 +8040,11 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) |
|
|
|
|
new_triggers.patterns[i].pattern = |
|
|
|
|
new_triggers.patterns[i].mask + mask_len; |
|
|
|
|
memcpy(new_triggers.patterns[i].mask, |
|
|
|
|
nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]), |
|
|
|
|
nla_data(pat_tb[NL80211_PKTPAT_MASK]), |
|
|
|
|
mask_len); |
|
|
|
|
new_triggers.patterns[i].pattern_len = pat_len; |
|
|
|
|
memcpy(new_triggers.patterns[i].pattern, |
|
|
|
|
nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]), |
|
|
|
|
nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), |
|
|
|
|
pat_len); |
|
|
|
|
i++; |
|
|
|
|
} |
|
|
|
@ -8034,6 +8083,264 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
static int nl80211_send_coalesce_rules(struct sk_buff *msg, |
|
|
|
|
struct cfg80211_registered_device *rdev) |
|
|
|
|
{ |
|
|
|
|
struct nlattr *nl_pats, *nl_pat, *nl_rule, *nl_rules; |
|
|
|
|
int i, j, pat_len; |
|
|
|
|
struct cfg80211_coalesce_rules *rule; |
|
|
|
|
|
|
|
|
|
if (!rdev->coalesce->n_rules) |
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|
|
nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE); |
|
|
|
|
if (!nl_rules) |
|
|
|
|
return -ENOBUFS; |
|
|
|
|
|
|
|
|
|
for (i = 0; i < rdev->coalesce->n_rules; i++) { |
|
|
|
|
nl_rule = nla_nest_start(msg, i + 1); |
|
|
|
|
if (!nl_rule) |
|
|
|
|
return -ENOBUFS; |
|
|
|
|
|
|
|
|
|
rule = &rdev->coalesce->rules[i]; |
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_DELAY, |
|
|
|
|
rule->delay)) |
|
|
|
|
return -ENOBUFS; |
|
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION, |
|
|
|
|
rule->condition)) |
|
|
|
|
return -ENOBUFS; |
|
|
|
|
|
|
|
|
|
nl_pats = nla_nest_start(msg, |
|
|
|
|
NL80211_ATTR_COALESCE_RULE_PKT_PATTERN); |
|
|
|
|
if (!nl_pats) |
|
|
|
|
return -ENOBUFS; |
|
|
|
|
|
|
|
|
|
for (j = 0; j < rule->n_patterns; j++) { |
|
|
|
|
nl_pat = nla_nest_start(msg, j + 1); |
|
|
|
|
if (!nl_pat) |
|
|
|
|
return -ENOBUFS; |
|
|
|
|
pat_len = rule->patterns[j].pattern_len; |
|
|
|
|
if (nla_put(msg, NL80211_PKTPAT_MASK, |
|
|
|
|
DIV_ROUND_UP(pat_len, 8), |
|
|
|
|
rule->patterns[j].mask) || |
|
|
|
|
nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len, |
|
|
|
|
rule->patterns[j].pattern) || |
|
|
|
|
nla_put_u32(msg, NL80211_PKTPAT_OFFSET, |
|
|
|
|
rule->patterns[j].pkt_offset)) |
|
|
|
|
return -ENOBUFS; |
|
|
|
|
nla_nest_end(msg, nl_pat); |
|
|
|
|
} |
|
|
|
|
nla_nest_end(msg, nl_pats); |
|
|
|
|
nla_nest_end(msg, nl_rule); |
|
|
|
|
} |
|
|
|
|
nla_nest_end(msg, nl_rules); |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int nl80211_get_coalesce(struct sk_buff *skb, struct genl_info *info) |
|
|
|
|
{ |
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
|
|
|
|
struct sk_buff *msg; |
|
|
|
|
void *hdr; |
|
|
|
|
|
|
|
|
|
if (!rdev->wiphy.coalesce) |
|
|
|
|
return -EOPNOTSUPP; |
|
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
|
|
|
|
if (!msg) |
|
|
|
|
return -ENOMEM; |
|
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, |
|
|
|
|
NL80211_CMD_GET_COALESCE); |
|
|
|
|
if (!hdr) |
|
|
|
|
goto nla_put_failure; |
|
|
|
|
|
|
|
|
|
if (rdev->coalesce && nl80211_send_coalesce_rules(msg, rdev)) |
|
|
|
|
goto nla_put_failure; |
|
|
|
|
|
|
|
|
|
genlmsg_end(msg, hdr); |
|
|
|
|
return genlmsg_reply(msg, info); |
|
|
|
|
|
|
|
|
|
nla_put_failure: |
|
|
|
|
nlmsg_free(msg); |
|
|
|
|
return -ENOBUFS; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev) |
|
|
|
|
{ |
|
|
|
|
struct cfg80211_coalesce *coalesce = rdev->coalesce; |
|
|
|
|
int i, j; |
|
|
|
|
struct cfg80211_coalesce_rules *rule; |
|
|
|
|
|
|
|
|
|
if (!coalesce) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
for (i = 0; i < coalesce->n_rules; i++) { |
|
|
|
|
rule = &coalesce->rules[i]; |
|
|
|
|
for (j = 0; j < rule->n_patterns; j++) |
|
|
|
|
kfree(rule->patterns[j].mask); |
|
|
|
|
kfree(rule->patterns); |
|
|
|
|
} |
|
|
|
|
kfree(coalesce->rules); |
|
|
|
|
kfree(coalesce); |
|
|
|
|
rdev->coalesce = NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev, |
|
|
|
|
struct nlattr *rule, |
|
|
|
|
struct cfg80211_coalesce_rules *new_rule) |
|
|
|
|
{ |
|
|
|
|
int err, i; |
|
|
|
|
const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce; |
|
|
|
|
struct nlattr *tb[NUM_NL80211_ATTR_COALESCE_RULE], *pat; |
|
|
|
|
int rem, pat_len, mask_len, pkt_offset, n_patterns = 0; |
|
|
|
|
struct nlattr *pat_tb[NUM_NL80211_PKTPAT]; |
|
|
|
|
|
|
|
|
|
err = nla_parse(tb, NL80211_ATTR_COALESCE_RULE_MAX, nla_data(rule), |
|
|
|
|
nla_len(rule), nl80211_coalesce_policy); |
|
|
|
|
if (err) |
|
|
|
|
return err; |
|
|
|
|
|
|
|
|
|
if (tb[NL80211_ATTR_COALESCE_RULE_DELAY]) |
|
|
|
|
new_rule->delay = |
|
|
|
|
nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_DELAY]); |
|
|
|
|
if (new_rule->delay > coalesce->max_delay) |
|
|
|
|
return -EINVAL; |
|
|
|
|
|
|
|
|
|
if (tb[NL80211_ATTR_COALESCE_RULE_CONDITION]) |
|
|
|
|
new_rule->condition = |
|
|
|
|
nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_CONDITION]); |
|
|
|
|
if (new_rule->condition != NL80211_COALESCE_CONDITION_MATCH && |
|
|
|
|
new_rule->condition != NL80211_COALESCE_CONDITION_NO_MATCH) |
|
|
|
|
return -EINVAL; |
|
|
|
|
|
|
|
|
|
if (!tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN]) |
|
|
|
|
return -EINVAL; |
|
|
|
|
|
|
|
|
|
nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN], |
|
|
|
|
rem) |
|
|
|
|
n_patterns++; |
|
|
|
|
if (n_patterns > coalesce->n_patterns) |
|
|
|
|
return -EINVAL; |
|
|
|
|
|
|
|
|
|
new_rule->patterns = kcalloc(n_patterns, sizeof(new_rule->patterns[0]), |
|
|
|
|
GFP_KERNEL); |
|
|
|
|
if (!new_rule->patterns) |
|
|
|
|
return -ENOMEM; |
|
|
|
|
|
|
|
|
|
new_rule->n_patterns = n_patterns; |
|
|
|
|
i = 0; |
|
|
|
|
|
|
|
|
|
nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN], |
|
|
|
|
rem) { |
|
|
|
|
nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat), |
|
|
|
|
nla_len(pat), NULL); |
|
|
|
|
if (!pat_tb[NL80211_PKTPAT_MASK] || |
|
|
|
|
!pat_tb[NL80211_PKTPAT_PATTERN]) |
|
|
|
|
return -EINVAL; |
|
|
|
|
pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]); |
|
|
|
|
mask_len = DIV_ROUND_UP(pat_len, 8); |
|
|
|
|
if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len) |
|
|
|
|
return -EINVAL; |
|
|
|
|
if (pat_len > coalesce->pattern_max_len || |
|
|
|
|
pat_len < coalesce->pattern_min_len) |
|
|
|
|
return -EINVAL; |
|
|
|
|
|
|
|
|
|
if (!pat_tb[NL80211_PKTPAT_OFFSET]) |
|
|
|
|
pkt_offset = 0; |
|
|
|
|
else |
|
|
|
|
pkt_offset = nla_get_u32(pat_tb[NL80211_PKTPAT_OFFSET]); |
|
|
|
|
if (pkt_offset > coalesce->max_pkt_offset) |
|
|
|
|
return -EINVAL; |
|
|
|
|
new_rule->patterns[i].pkt_offset = pkt_offset; |
|
|
|
|
|
|
|
|
|
new_rule->patterns[i].mask = |
|
|
|
|
kmalloc(mask_len + pat_len, GFP_KERNEL); |
|
|
|
|
if (!new_rule->patterns[i].mask) |
|
|
|
|
return -ENOMEM; |
|
|
|
|
new_rule->patterns[i].pattern = |
|
|
|
|
new_rule->patterns[i].mask + mask_len; |
|
|
|
|
memcpy(new_rule->patterns[i].mask, |
|
|
|
|
nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len); |
|
|
|
|
new_rule->patterns[i].pattern_len = pat_len; |
|
|
|
|
memcpy(new_rule->patterns[i].pattern, |
|
|
|
|
nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len); |
|
|
|
|
i++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info) |
|
|
|
|
{ |
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
|
|
|
|
const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce; |
|
|
|
|
struct cfg80211_coalesce new_coalesce = {}; |
|
|
|
|
struct cfg80211_coalesce *n_coalesce; |
|
|
|
|
int err, rem_rule, n_rules = 0, i, j; |
|
|
|
|
struct nlattr *rule; |
|
|
|
|
struct cfg80211_coalesce_rules *tmp_rule; |
|
|
|
|
|
|
|
|
|
if (!rdev->wiphy.coalesce || !rdev->ops->set_coalesce) |
|
|
|
|
return -EOPNOTSUPP; |
|
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) { |
|
|
|
|
cfg80211_rdev_free_coalesce(rdev); |
|
|
|
|
rdev->ops->set_coalesce(&rdev->wiphy, NULL); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE], |
|
|
|
|
rem_rule) |
|
|
|
|
n_rules++; |
|
|
|
|
if (n_rules > coalesce->n_rules) |
|
|
|
|
return -EINVAL; |
|
|
|
|
|
|
|
|
|
new_coalesce.rules = kcalloc(n_rules, sizeof(new_coalesce.rules[0]), |
|
|
|
|
GFP_KERNEL); |
|
|
|
|
if (!new_coalesce.rules) |
|
|
|
|
return -ENOMEM; |
|
|
|
|
|
|
|
|
|
new_coalesce.n_rules = n_rules; |
|
|
|
|
i = 0; |
|
|
|
|
|
|
|
|
|
nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE], |
|
|
|
|
rem_rule) { |
|
|
|
|
err = nl80211_parse_coalesce_rule(rdev, rule, |
|
|
|
|
&new_coalesce.rules[i]); |
|
|
|
|
if (err) |
|
|
|
|
goto error; |
|
|
|
|
|
|
|
|
|
i++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
err = rdev->ops->set_coalesce(&rdev->wiphy, &new_coalesce); |
|
|
|
|
if (err) |
|
|
|
|
goto error; |
|
|
|
|
|
|
|
|
|
n_coalesce = kmemdup(&new_coalesce, sizeof(new_coalesce), GFP_KERNEL); |
|
|
|
|
if (!n_coalesce) { |
|
|
|
|
err = -ENOMEM; |
|
|
|
|
goto error; |
|
|
|
|
} |
|
|
|
|
cfg80211_rdev_free_coalesce(rdev); |
|
|
|
|
rdev->coalesce = n_coalesce; |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
error: |
|
|
|
|
for (i = 0; i < new_coalesce.n_rules; i++) { |
|
|
|
|
tmp_rule = &new_coalesce.rules[i]; |
|
|
|
|
for (j = 0; j < tmp_rule->n_patterns; j++) |
|
|
|
|
kfree(tmp_rule->patterns[j].mask); |
|
|
|
|
kfree(tmp_rule->patterns); |
|
|
|
|
} |
|
|
|
|
kfree(new_coalesce.rules); |
|
|
|
|
|
|
|
|
|
return err; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) |
|
|
|
|
{ |
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
|
|
|
@ -9041,6 +9348,21 @@ static struct genl_ops nl80211_ops[] = { |
|
|
|
|
.flags = GENL_ADMIN_PERM, |
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_WDEV_UP | |
|
|
|
|
NL80211_FLAG_NEED_RTNL, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
.cmd = NL80211_CMD_GET_COALESCE, |
|
|
|
|
.doit = nl80211_get_coalesce, |
|
|
|
|
.policy = nl80211_policy, |
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_WIPHY | |
|
|
|
|
NL80211_FLAG_NEED_RTNL, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
.cmd = NL80211_CMD_SET_COALESCE, |
|
|
|
|
.doit = nl80211_set_coalesce, |
|
|
|
|
.policy = nl80211_policy, |
|
|
|
|
.flags = GENL_ADMIN_PERM, |
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_WIPHY | |
|
|
|
|
NL80211_FLAG_NEED_RTNL, |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|