diff options
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/Kconfig | 11 | ||||
-rw-r--r-- | net/wireless/core.c | 14 | ||||
-rw-r--r-- | net/wireless/core.h | 4 | ||||
-rw-r--r-- | net/wireless/mlme.c | 15 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 358 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 4 | ||||
-rw-r--r-- | net/wireless/reg.c | 61 | ||||
-rw-r--r-- | net/wireless/scan.c | 13 | ||||
-rw-r--r-- | net/wireless/sme.c | 8 | ||||
-rw-r--r-- | net/wireless/sysfs.c | 6 | ||||
-rw-r--r-- | net/wireless/util.c | 38 |
11 files changed, 471 insertions, 61 deletions
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 1f1ef70f34f..8e2a668c923 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig | |||
@@ -159,3 +159,14 @@ config LIB80211_DEBUG | |||
159 | from lib80211. | 159 | from lib80211. |
160 | 160 | ||
161 | If unsure, say N. | 161 | If unsure, say N. |
162 | |||
163 | config CFG80211_ALLOW_RECONNECT | ||
164 | bool "Allow reconnect while already connected" | ||
165 | depends on CFG80211 | ||
166 | default n | ||
167 | help | ||
168 | cfg80211 stack doesn't allow to connect if you are already | ||
169 | connected. This option allows to make a connection in this case. | ||
170 | |||
171 | Select this option ONLY for wlan drivers that are specifically | ||
172 | built for such purposes. | ||
diff --git a/net/wireless/core.c b/net/wireless/core.c index 880dbe2e6f9..c14865172da 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -488,6 +488,10 @@ int wiphy_register(struct wiphy *wiphy) | |||
488 | int i; | 488 | int i; |
489 | u16 ifmodes = wiphy->interface_modes; | 489 | u16 ifmodes = wiphy->interface_modes; |
490 | 490 | ||
491 | if (WARN_ON((wiphy->wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && | ||
492 | !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY))) | ||
493 | return -EINVAL; | ||
494 | |||
491 | if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) | 495 | if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) |
492 | return -EINVAL; | 496 | return -EINVAL; |
493 | 497 | ||
@@ -612,6 +616,9 @@ int wiphy_register(struct wiphy *wiphy) | |||
612 | if (res) | 616 | if (res) |
613 | goto out_rm_dev; | 617 | goto out_rm_dev; |
614 | 618 | ||
619 | rtnl_lock(); | ||
620 | rdev->wiphy.registered = true; | ||
621 | rtnl_unlock(); | ||
615 | return 0; | 622 | return 0; |
616 | 623 | ||
617 | out_rm_dev: | 624 | out_rm_dev: |
@@ -643,6 +650,10 @@ void wiphy_unregister(struct wiphy *wiphy) | |||
643 | { | 650 | { |
644 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 651 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
645 | 652 | ||
653 | rtnl_lock(); | ||
654 | rdev->wiphy.registered = false; | ||
655 | rtnl_unlock(); | ||
656 | |||
646 | rfkill_unregister(rdev->rfkill); | 657 | rfkill_unregister(rdev->rfkill); |
647 | 658 | ||
648 | /* protect the device list */ | 659 | /* protect the device list */ |
@@ -918,7 +929,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
918 | * Configure power management to the driver here so that its | 929 | * Configure power management to the driver here so that its |
919 | * correctly set also after interface type changes etc. | 930 | * correctly set also after interface type changes etc. |
920 | */ | 931 | */ |
921 | if (wdev->iftype == NL80211_IFTYPE_STATION && | 932 | if ((wdev->iftype == NL80211_IFTYPE_STATION || |
933 | wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) && | ||
922 | rdev->ops->set_power_mgmt) | 934 | rdev->ops->set_power_mgmt) |
923 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, | 935 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, |
924 | wdev->ps, | 936 | wdev->ps, |
diff --git a/net/wireless/core.h b/net/wireless/core.h index a570ff9214e..8672e028022 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -447,6 +447,10 @@ int cfg80211_set_freq(struct cfg80211_registered_device *rdev, | |||
447 | 447 | ||
448 | u16 cfg80211_calculate_bitrate(struct rate_info *rate); | 448 | u16 cfg80211_calculate_bitrate(struct rate_info *rate); |
449 | 449 | ||
450 | int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, | ||
451 | const u8 *rates, unsigned int n_rates, | ||
452 | u32 *mask); | ||
453 | |||
450 | int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, | 454 | int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, |
451 | u32 beacon_int); | 455 | u32 beacon_int); |
452 | 456 | ||
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 493b939970c..832f6574e4e 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c | |||
@@ -170,7 +170,9 @@ void __cfg80211_send_deauth(struct net_device *dev, | |||
170 | break; | 170 | break; |
171 | } | 171 | } |
172 | if (wdev->authtry_bsses[i] && | 172 | if (wdev->authtry_bsses[i] && |
173 | memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { | 173 | memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, |
174 | ETH_ALEN) == 0 && | ||
175 | memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) == 0) { | ||
174 | cfg80211_unhold_bss(wdev->authtry_bsses[i]); | 176 | cfg80211_unhold_bss(wdev->authtry_bsses[i]); |
175 | cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); | 177 | cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); |
176 | wdev->authtry_bsses[i] = NULL; | 178 | wdev->authtry_bsses[i] = NULL; |
@@ -1082,3 +1084,14 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev, | |||
1082 | nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp); | 1084 | nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp); |
1083 | } | 1085 | } |
1084 | EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify); | 1086 | EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify); |
1087 | |||
1088 | void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, | ||
1089 | const u8 *replay_ctr, gfp_t gfp) | ||
1090 | { | ||
1091 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
1092 | struct wiphy *wiphy = wdev->wiphy; | ||
1093 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
1094 | |||
1095 | nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp); | ||
1096 | } | ||
1097 | EXPORT_SYMBOL(cfg80211_gtk_rekey_notify); | ||
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index cea338150d0..fb18bb4dea7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -83,8 +83,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
83 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, | 83 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, |
84 | [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, | 84 | [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, |
85 | 85 | ||
86 | [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN }, | 86 | [NL80211_ATTR_MAC] = { .len = ETH_ALEN }, |
87 | [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN }, | 87 | [NL80211_ATTR_PREV_BSSID] = { .len = ETH_ALEN }, |
88 | 88 | ||
89 | [NL80211_ATTR_KEY] = { .type = NLA_NESTED, }, | 89 | [NL80211_ATTR_KEY] = { .type = NLA_NESTED, }, |
90 | [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, | 90 | [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, |
@@ -126,8 +126,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
126 | [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED }, | 126 | [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED }, |
127 | [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG }, | 127 | [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG }, |
128 | 128 | ||
129 | [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, | 129 | [NL80211_ATTR_HT_CAPABILITY] = { .len = NL80211_HT_CAPABILITY_LEN }, |
130 | .len = NL80211_HT_CAPABILITY_LEN }, | ||
131 | 130 | ||
132 | [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, | 131 | [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, |
133 | [NL80211_ATTR_IE] = { .type = NLA_BINARY, | 132 | [NL80211_ATTR_IE] = { .type = NLA_BINARY, |
@@ -176,6 +175,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
176 | [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED }, | 175 | [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED }, |
177 | [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 }, | 176 | [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 }, |
178 | [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 }, | 177 | [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 }, |
178 | [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED }, | ||
179 | [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED }, | ||
179 | }; | 180 | }; |
180 | 181 | ||
181 | /* policy for the key attributes */ | 182 | /* policy for the key attributes */ |
@@ -204,6 +205,18 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { | |||
204 | [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG }, | 205 | [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG }, |
205 | [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG }, | 206 | [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG }, |
206 | [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED }, | 207 | [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED }, |
208 | [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG }, | ||
209 | [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG }, | ||
210 | [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG }, | ||
211 | [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG }, | ||
212 | }; | ||
213 | |||
214 | /* policy for GTK rekey offload attributes */ | ||
215 | static const struct nla_policy | ||
216 | nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { | ||
217 | [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN }, | ||
218 | [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN }, | ||
219 | [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN }, | ||
207 | }; | 220 | }; |
208 | 221 | ||
209 | /* ifidx get helper */ | 222 | /* ifidx get helper */ |
@@ -683,8 +696,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
683 | dev->wiphy.coverage_class); | 696 | dev->wiphy.coverage_class); |
684 | NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, | 697 | NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, |
685 | dev->wiphy.max_scan_ssids); | 698 | dev->wiphy.max_scan_ssids); |
699 | NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, | ||
700 | dev->wiphy.max_sched_scan_ssids); | ||
686 | NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN, | 701 | NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN, |
687 | dev->wiphy.max_scan_ie_len); | 702 | dev->wiphy.max_scan_ie_len); |
703 | NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, | ||
704 | dev->wiphy.max_sched_scan_ie_len); | ||
688 | 705 | ||
689 | if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) | 706 | if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) |
690 | NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN); | 707 | NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN); |
@@ -920,6 +937,16 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
920 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); | 937 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); |
921 | if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) | 938 | if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) |
922 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); | 939 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); |
940 | if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) | ||
941 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED); | ||
942 | if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) | ||
943 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); | ||
944 | if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) | ||
945 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); | ||
946 | if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) | ||
947 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); | ||
948 | if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) | ||
949 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); | ||
923 | if (dev->wiphy.wowlan.n_patterns) { | 950 | if (dev->wiphy.wowlan.n_patterns) { |
924 | struct nl80211_wowlan_pattern_support pat = { | 951 | struct nl80211_wowlan_pattern_support pat = { |
925 | .max_patterns = dev->wiphy.wowlan.n_patterns, | 952 | .max_patterns = dev->wiphy.wowlan.n_patterns, |
@@ -2209,6 +2236,10 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | |||
2209 | } | 2236 | } |
2210 | nla_nest_end(msg, sinfoattr); | 2237 | nla_nest_end(msg, sinfoattr); |
2211 | 2238 | ||
2239 | if (sinfo->assoc_req_ies) | ||
2240 | NLA_PUT(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len, | ||
2241 | sinfo->assoc_req_ies); | ||
2242 | |||
2212 | return genlmsg_end(msg, hdr); | 2243 | return genlmsg_end(msg, hdr); |
2213 | 2244 | ||
2214 | nla_put_failure: | 2245 | nla_put_failure: |
@@ -2236,6 +2267,7 @@ static int nl80211_dump_station(struct sk_buff *skb, | |||
2236 | } | 2267 | } |
2237 | 2268 | ||
2238 | while (1) { | 2269 | while (1) { |
2270 | memset(&sinfo, 0, sizeof(sinfo)); | ||
2239 | err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx, | 2271 | err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx, |
2240 | mac_addr, &sinfo); | 2272 | mac_addr, &sinfo); |
2241 | if (err == -ENOENT) | 2273 | if (err == -ENOENT) |
@@ -3297,7 +3329,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | |||
3297 | struct nlattr *attr; | 3329 | struct nlattr *attr; |
3298 | struct wiphy *wiphy; | 3330 | struct wiphy *wiphy; |
3299 | int err, tmp, n_ssids = 0, n_channels, i; | 3331 | int err, tmp, n_ssids = 0, n_channels, i; |
3300 | enum ieee80211_band band; | ||
3301 | size_t ie_len; | 3332 | size_t ie_len; |
3302 | 3333 | ||
3303 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) | 3334 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) |
@@ -3317,6 +3348,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | |||
3317 | if (!n_channels) | 3348 | if (!n_channels) |
3318 | return -EINVAL; | 3349 | return -EINVAL; |
3319 | } else { | 3350 | } else { |
3351 | enum ieee80211_band band; | ||
3320 | n_channels = 0; | 3352 | n_channels = 0; |
3321 | 3353 | ||
3322 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) | 3354 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) |
@@ -3377,6 +3409,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | |||
3377 | i++; | 3409 | i++; |
3378 | } | 3410 | } |
3379 | } else { | 3411 | } else { |
3412 | enum ieee80211_band band; | ||
3413 | |||
3380 | /* all channels */ | 3414 | /* all channels */ |
3381 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 3415 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
3382 | int j; | 3416 | int j; |
@@ -3423,6 +3457,30 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | |||
3423 | request->ie_len); | 3457 | request->ie_len); |
3424 | } | 3458 | } |
3425 | 3459 | ||
3460 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) | ||
3461 | if (wiphy->bands[i]) | ||
3462 | request->rates[i] = | ||
3463 | (1 << wiphy->bands[i]->n_bitrates) - 1; | ||
3464 | |||
3465 | if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) { | ||
3466 | nla_for_each_nested(attr, | ||
3467 | info->attrs[NL80211_ATTR_SCAN_SUPP_RATES], | ||
3468 | tmp) { | ||
3469 | enum ieee80211_band band = nla_type(attr); | ||
3470 | |||
3471 | if (band < 0 || band >= IEEE80211_NUM_BANDS) { | ||
3472 | err = -EINVAL; | ||
3473 | goto out_free; | ||
3474 | } | ||
3475 | err = ieee80211_get_ratemask(wiphy->bands[band], | ||
3476 | nla_data(attr), | ||
3477 | nla_len(attr), | ||
3478 | &request->rates[band]); | ||
3479 | if (err) | ||
3480 | goto out_free; | ||
3481 | } | ||
3482 | } | ||
3483 | |||
3426 | request->dev = dev; | 3484 | request->dev = dev; |
3427 | request->wiphy = &rdev->wiphy; | 3485 | request->wiphy = &rdev->wiphy; |
3428 | 3486 | ||
@@ -3488,7 +3546,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, | |||
3488 | tmp) | 3546 | tmp) |
3489 | n_ssids++; | 3547 | n_ssids++; |
3490 | 3548 | ||
3491 | if (n_ssids > wiphy->max_scan_ssids) | 3549 | if (n_ssids > wiphy->max_sched_scan_ssids) |
3492 | return -EINVAL; | 3550 | return -EINVAL; |
3493 | 3551 | ||
3494 | if (info->attrs[NL80211_ATTR_IE]) | 3552 | if (info->attrs[NL80211_ATTR_IE]) |
@@ -3496,7 +3554,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, | |||
3496 | else | 3554 | else |
3497 | ie_len = 0; | 3555 | ie_len = 0; |
3498 | 3556 | ||
3499 | if (ie_len > wiphy->max_scan_ie_len) | 3557 | if (ie_len > wiphy->max_sched_scan_ie_len) |
3500 | return -EINVAL; | 3558 | return -EINVAL; |
3501 | 3559 | ||
3502 | mutex_lock(&rdev->sched_scan_mtx); | 3560 | mutex_lock(&rdev->sched_scan_mtx); |
@@ -3632,7 +3690,8 @@ static int nl80211_stop_sched_scan(struct sk_buff *skb, | |||
3632 | return err; | 3690 | return err; |
3633 | } | 3691 | } |
3634 | 3692 | ||
3635 | static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, | 3693 | static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, |
3694 | u32 seq, int flags, | ||
3636 | struct cfg80211_registered_device *rdev, | 3695 | struct cfg80211_registered_device *rdev, |
3637 | struct wireless_dev *wdev, | 3696 | struct wireless_dev *wdev, |
3638 | struct cfg80211_internal_bss *intbss) | 3697 | struct cfg80211_internal_bss *intbss) |
@@ -3644,11 +3703,13 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
3644 | 3703 | ||
3645 | ASSERT_WDEV_LOCK(wdev); | 3704 | ASSERT_WDEV_LOCK(wdev); |
3646 | 3705 | ||
3647 | hdr = nl80211hdr_put(msg, pid, seq, flags, | 3706 | hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).pid, seq, flags, |
3648 | NL80211_CMD_NEW_SCAN_RESULTS); | 3707 | NL80211_CMD_NEW_SCAN_RESULTS); |
3649 | if (!hdr) | 3708 | if (!hdr) |
3650 | return -1; | 3709 | return -1; |
3651 | 3710 | ||
3711 | genl_dump_check_consistent(cb, hdr, &nl80211_fam); | ||
3712 | |||
3652 | NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation); | 3713 | NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation); |
3653 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex); | 3714 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex); |
3654 | 3715 | ||
@@ -3737,11 +3798,12 @@ static int nl80211_dump_scan(struct sk_buff *skb, | |||
3737 | spin_lock_bh(&rdev->bss_lock); | 3798 | spin_lock_bh(&rdev->bss_lock); |
3738 | cfg80211_bss_expire(rdev); | 3799 | cfg80211_bss_expire(rdev); |
3739 | 3800 | ||
3801 | cb->seq = rdev->bss_generation; | ||
3802 | |||
3740 | list_for_each_entry(scan, &rdev->bss_list, list) { | 3803 | list_for_each_entry(scan, &rdev->bss_list, list) { |
3741 | if (++idx <= start) | 3804 | if (++idx <= start) |
3742 | continue; | 3805 | continue; |
3743 | if (nl80211_send_bss(skb, | 3806 | if (nl80211_send_bss(skb, cb, |
3744 | NETLINK_CB(cb->skb).pid, | ||
3745 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | 3807 | cb->nlh->nlmsg_seq, NLM_F_MULTI, |
3746 | rdev, wdev, scan) < 0) { | 3808 | rdev, wdev, scan) < 0) { |
3747 | idx--; | 3809 | idx--; |
@@ -3765,10 +3827,6 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq, | |||
3765 | void *hdr; | 3827 | void *hdr; |
3766 | struct nlattr *infoattr; | 3828 | struct nlattr *infoattr; |
3767 | 3829 | ||
3768 | /* Survey without a channel doesn't make sense */ | ||
3769 | if (!survey->channel) | ||
3770 | return -EINVAL; | ||
3771 | |||
3772 | hdr = nl80211hdr_put(msg, pid, seq, flags, | 3830 | hdr = nl80211hdr_put(msg, pid, seq, flags, |
3773 | NL80211_CMD_NEW_SURVEY_RESULTS); | 3831 | NL80211_CMD_NEW_SURVEY_RESULTS); |
3774 | if (!hdr) | 3832 | if (!hdr) |
@@ -3831,6 +3889,8 @@ static int nl80211_dump_survey(struct sk_buff *skb, | |||
3831 | } | 3889 | } |
3832 | 3890 | ||
3833 | while (1) { | 3891 | while (1) { |
3892 | struct ieee80211_channel *chan; | ||
3893 | |||
3834 | res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx, | 3894 | res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx, |
3835 | &survey); | 3895 | &survey); |
3836 | if (res == -ENOENT) | 3896 | if (res == -ENOENT) |
@@ -3838,6 +3898,19 @@ static int nl80211_dump_survey(struct sk_buff *skb, | |||
3838 | if (res) | 3898 | if (res) |
3839 | goto out_err; | 3899 | goto out_err; |
3840 | 3900 | ||
3901 | /* Survey without a channel doesn't make sense */ | ||
3902 | if (!survey.channel) { | ||
3903 | res = -EINVAL; | ||
3904 | goto out; | ||
3905 | } | ||
3906 | |||
3907 | chan = ieee80211_get_channel(&dev->wiphy, | ||
3908 | survey.channel->center_freq); | ||
3909 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) { | ||
3910 | survey_idx++; | ||
3911 | continue; | ||
3912 | } | ||
3913 | |||
3841 | if (nl80211_send_survey(skb, | 3914 | if (nl80211_send_survey(skb, |
3842 | NETLINK_CB(cb->skb).pid, | 3915 | NETLINK_CB(cb->skb).pid, |
3843 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | 3916 | cb->nlh->nlmsg_seq, NLM_F_MULTI, |
@@ -4044,9 +4117,12 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, | |||
4044 | if (len % sizeof(u32)) | 4117 | if (len % sizeof(u32)) |
4045 | return -EINVAL; | 4118 | return -EINVAL; |
4046 | 4119 | ||
4120 | if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES) | ||
4121 | return -EINVAL; | ||
4122 | |||
4047 | memcpy(settings->akm_suites, data, len); | 4123 | memcpy(settings->akm_suites, data, len); |
4048 | 4124 | ||
4049 | for (i = 0; i < settings->n_ciphers_pairwise; i++) | 4125 | for (i = 0; i < settings->n_akm_suites; i++) |
4050 | if (!nl80211_valid_akm_suite(settings->akm_suites[i])) | 4126 | if (!nl80211_valid_akm_suite(settings->akm_suites[i])) |
4051 | return -EINVAL; | 4127 | return -EINVAL; |
4052 | } | 4128 | } |
@@ -4294,25 +4370,12 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) | |||
4294 | nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); | 4370 | nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); |
4295 | struct ieee80211_supported_band *sband = | 4371 | struct ieee80211_supported_band *sband = |
4296 | wiphy->bands[ibss.channel->band]; | 4372 | wiphy->bands[ibss.channel->band]; |
4297 | int i, j; | 4373 | int err; |
4298 | |||
4299 | if (n_rates == 0) | ||
4300 | return -EINVAL; | ||
4301 | 4374 | ||
4302 | for (i = 0; i < n_rates; i++) { | 4375 | err = ieee80211_get_ratemask(sband, rates, n_rates, |
4303 | int rate = (rates[i] & 0x7f) * 5; | 4376 | &ibss.basic_rates); |
4304 | bool found = false; | 4377 | if (err) |
4305 | 4378 | return err; | |
4306 | for (j = 0; j < sband->n_bitrates; j++) { | ||
4307 | if (sband->bitrates[j].bitrate == rate) { | ||
4308 | found = true; | ||
4309 | ibss.basic_rates |= BIT(j); | ||
4310 | break; | ||
4311 | } | ||
4312 | } | ||
4313 | if (!found) | ||
4314 | return -EINVAL; | ||
4315 | } | ||
4316 | } | 4379 | } |
4317 | 4380 | ||
4318 | if (info->attrs[NL80211_ATTR_MCAST_RATE] && | 4381 | if (info->attrs[NL80211_ATTR_MCAST_RATE] && |
@@ -4372,6 +4435,93 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) | |||
4372 | return err; | 4435 | return err; |
4373 | } | 4436 | } |
4374 | 4437 | ||
4438 | static int nl80211_testmode_dump(struct sk_buff *skb, | ||
4439 | struct netlink_callback *cb) | ||
4440 | { | ||
4441 | struct cfg80211_registered_device *dev; | ||
4442 | int err; | ||
4443 | long phy_idx; | ||
4444 | void *data = NULL; | ||
4445 | int data_len = 0; | ||
4446 | |||
4447 | if (cb->args[0]) { | ||
4448 | /* | ||
4449 | * 0 is a valid index, but not valid for args[0], | ||
4450 | * so we need to offset by 1. | ||
4451 | */ | ||
4452 | phy_idx = cb->args[0] - 1; | ||
4453 | } else { | ||
4454 | err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, | ||
4455 | nl80211_fam.attrbuf, nl80211_fam.maxattr, | ||
4456 | nl80211_policy); | ||
4457 | if (err) | ||
4458 | return err; | ||
4459 | if (!nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]) | ||
4460 | return -EINVAL; | ||
4461 | phy_idx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]); | ||
4462 | if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]) | ||
4463 | cb->args[1] = | ||
4464 | (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]; | ||
4465 | } | ||
4466 | |||
4467 | if (cb->args[1]) { | ||
4468 | data = nla_data((void *)cb->args[1]); | ||
4469 | data_len = nla_len((void *)cb->args[1]); | ||
4470 | } | ||
4471 | |||
4472 | mutex_lock(&cfg80211_mutex); | ||
4473 | dev = cfg80211_rdev_by_wiphy_idx(phy_idx); | ||
4474 | if (!dev) { | ||
4475 | mutex_unlock(&cfg80211_mutex); | ||
4476 | return -ENOENT; | ||
4477 | } | ||
4478 | cfg80211_lock_rdev(dev); | ||
4479 | mutex_unlock(&cfg80211_mutex); | ||
4480 | |||
4481 | if (!dev->ops->testmode_dump) { | ||
4482 | err = -EOPNOTSUPP; | ||
4483 | goto out_err; | ||
4484 | } | ||
4485 | |||
4486 | while (1) { | ||
4487 | void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid, | ||
4488 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | ||
4489 | NL80211_CMD_TESTMODE); | ||
4490 | struct nlattr *tmdata; | ||
4491 | |||
4492 | if (nla_put_u32(skb, NL80211_ATTR_WIPHY, dev->wiphy_idx) < 0) { | ||
4493 | genlmsg_cancel(skb, hdr); | ||
4494 | break; | ||
4495 | } | ||
4496 | |||
4497 | tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA); | ||
4498 | if (!tmdata) { | ||
4499 | genlmsg_cancel(skb, hdr); | ||
4500 | break; | ||
4501 | } | ||
4502 | err = dev->ops->testmode_dump(&dev->wiphy, skb, cb, | ||
4503 | data, data_len); | ||
4504 | nla_nest_end(skb, tmdata); | ||
4505 | |||
4506 | if (err == -ENOBUFS || err == -ENOENT) { | ||
4507 | genlmsg_cancel(skb, hdr); | ||
4508 | break; | ||
4509 | } else if (err) { | ||
4510 | genlmsg_cancel(skb, hdr); | ||
4511 | goto out_err; | ||
4512 | } | ||
4513 | |||
4514 | genlmsg_end(skb, hdr); | ||
4515 | } | ||
4516 | |||
4517 | err = skb->len; | ||
4518 | /* see above */ | ||
4519 | cb->args[0] = phy_idx + 1; | ||
4520 | out_err: | ||
4521 | cfg80211_unlock_rdev(dev); | ||
4522 | return err; | ||
4523 | } | ||
4524 | |||
4375 | static struct sk_buff * | 4525 | static struct sk_buff * |
4376 | __cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, | 4526 | __cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, |
4377 | int approxlen, u32 pid, u32 seq, gfp_t gfp) | 4527 | int approxlen, u32 pid, u32 seq, gfp_t gfp) |
@@ -5161,6 +5311,14 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) | |||
5161 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); | 5311 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); |
5162 | if (rdev->wowlan->magic_pkt) | 5312 | if (rdev->wowlan->magic_pkt) |
5163 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); | 5313 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); |
5314 | if (rdev->wowlan->gtk_rekey_failure) | ||
5315 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); | ||
5316 | if (rdev->wowlan->eap_identity_req) | ||
5317 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); | ||
5318 | if (rdev->wowlan->four_way_handshake) | ||
5319 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); | ||
5320 | if (rdev->wowlan->rfkill_release) | ||
5321 | NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); | ||
5164 | if (rdev->wowlan->n_patterns) { | 5322 | if (rdev->wowlan->n_patterns) { |
5165 | struct nlattr *nl_pats, *nl_pat; | 5323 | struct nlattr *nl_pats, *nl_pat; |
5166 | int i, pat_len; | 5324 | int i, pat_len; |
@@ -5237,6 +5395,33 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) | |||
5237 | new_triggers.magic_pkt = true; | 5395 | new_triggers.magic_pkt = true; |
5238 | } | 5396 | } |
5239 | 5397 | ||
5398 | if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED]) | ||
5399 | return -EINVAL; | ||
5400 | |||
5401 | if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) { | ||
5402 | if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)) | ||
5403 | return -EINVAL; | ||
5404 | new_triggers.gtk_rekey_failure = true; | ||
5405 | } | ||
5406 | |||
5407 | if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) { | ||
5408 | if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)) | ||
5409 | return -EINVAL; | ||
5410 | new_triggers.eap_identity_req = true; | ||
5411 | } | ||
5412 | |||
5413 | if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) { | ||
5414 | if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)) | ||
5415 | return -EINVAL; | ||
5416 | new_triggers.four_way_handshake = true; | ||
5417 | } | ||
5418 | |||
5419 | if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) { | ||
5420 | if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE)) | ||
5421 | return -EINVAL; | ||
5422 | new_triggers.rfkill_release = true; | ||
5423 | } | ||
5424 | |||
5240 | if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { | 5425 | if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { |
5241 | struct nlattr *pat; | 5426 | struct nlattr *pat; |
5242 | int n_patterns = 0; | 5427 | int n_patterns = 0; |
@@ -5318,6 +5503,57 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) | |||
5318 | return err; | 5503 | return err; |
5319 | } | 5504 | } |
5320 | 5505 | ||
5506 | static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) | ||
5507 | { | ||
5508 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
5509 | struct net_device *dev = info->user_ptr[1]; | ||
5510 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
5511 | struct nlattr *tb[NUM_NL80211_REKEY_DATA]; | ||
5512 | struct cfg80211_gtk_rekey_data rekey_data; | ||
5513 | int err; | ||
5514 | |||
5515 | if (!info->attrs[NL80211_ATTR_REKEY_DATA]) | ||
5516 | return -EINVAL; | ||
5517 | |||
5518 | err = nla_parse(tb, MAX_NL80211_REKEY_DATA, | ||
5519 | nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]), | ||
5520 | nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]), | ||
5521 | nl80211_rekey_policy); | ||
5522 | if (err) | ||
5523 | return err; | ||
5524 | |||
5525 | if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN) | ||
5526 | return -ERANGE; | ||
5527 | if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN) | ||
5528 | return -ERANGE; | ||
5529 | if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN) | ||
5530 | return -ERANGE; | ||
5531 | |||
5532 | memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]), | ||
5533 | NL80211_KEK_LEN); | ||
5534 | memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]), | ||
5535 | NL80211_KCK_LEN); | ||
5536 | memcpy(rekey_data.replay_ctr, | ||
5537 | nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]), | ||
5538 | NL80211_REPLAY_CTR_LEN); | ||
5539 | |||
5540 | wdev_lock(wdev); | ||
5541 | if (!wdev->current_bss) { | ||
5542 | err = -ENOTCONN; | ||
5543 | goto out; | ||
5544 | } | ||
5545 | |||
5546 | if (!rdev->ops->set_rekey_data) { | ||
5547 | err = -EOPNOTSUPP; | ||
5548 | goto out; | ||
5549 | } | ||
5550 | |||
5551 | err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data); | ||
5552 | out: | ||
5553 | wdev_unlock(wdev); | ||
5554 | return err; | ||
5555 | } | ||
5556 | |||
5321 | #define NL80211_FLAG_NEED_WIPHY 0x01 | 5557 | #define NL80211_FLAG_NEED_WIPHY 0x01 |
5322 | #define NL80211_FLAG_NEED_NETDEV 0x02 | 5558 | #define NL80211_FLAG_NEED_NETDEV 0x02 |
5323 | #define NL80211_FLAG_NEED_RTNL 0x04 | 5559 | #define NL80211_FLAG_NEED_RTNL 0x04 |
@@ -5669,6 +5905,7 @@ static struct genl_ops nl80211_ops[] = { | |||
5669 | { | 5905 | { |
5670 | .cmd = NL80211_CMD_TESTMODE, | 5906 | .cmd = NL80211_CMD_TESTMODE, |
5671 | .doit = nl80211_testmode_do, | 5907 | .doit = nl80211_testmode_do, |
5908 | .dumpit = nl80211_testmode_dump, | ||
5672 | .policy = nl80211_policy, | 5909 | .policy = nl80211_policy, |
5673 | .flags = GENL_ADMIN_PERM, | 5910 | .flags = GENL_ADMIN_PERM, |
5674 | .internal_flags = NL80211_FLAG_NEED_WIPHY | | 5911 | .internal_flags = NL80211_FLAG_NEED_WIPHY | |
@@ -5848,6 +6085,14 @@ static struct genl_ops nl80211_ops[] = { | |||
5848 | .internal_flags = NL80211_FLAG_NEED_WIPHY | | 6085 | .internal_flags = NL80211_FLAG_NEED_WIPHY | |
5849 | NL80211_FLAG_NEED_RTNL, | 6086 | NL80211_FLAG_NEED_RTNL, |
5850 | }, | 6087 | }, |
6088 | { | ||
6089 | .cmd = NL80211_CMD_SET_REKEY_OFFLOAD, | ||
6090 | .doit = nl80211_set_rekey_data, | ||
6091 | .policy = nl80211_policy, | ||
6092 | .flags = GENL_ADMIN_PERM, | ||
6093 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | ||
6094 | NL80211_FLAG_NEED_RTNL, | ||
6095 | }, | ||
5851 | }; | 6096 | }; |
5852 | 6097 | ||
5853 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 6098 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
@@ -6792,6 +7037,51 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, | |||
6792 | nlmsg_free(msg); | 7037 | nlmsg_free(msg); |
6793 | } | 7038 | } |
6794 | 7039 | ||
7040 | void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, | ||
7041 | struct net_device *netdev, const u8 *bssid, | ||
7042 | const u8 *replay_ctr, gfp_t gfp) | ||
7043 | { | ||
7044 | struct sk_buff *msg; | ||
7045 | struct nlattr *rekey_attr; | ||
7046 | void *hdr; | ||
7047 | |||
7048 | msg = nlmsg_new(NLMSG_GOODSIZE, gfp); | ||
7049 | if (!msg) | ||
7050 | return; | ||
7051 | |||
7052 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD); | ||
7053 | if (!hdr) { | ||
7054 | nlmsg_free(msg); | ||
7055 | return; | ||
7056 | } | ||
7057 | |||
7058 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
7059 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
7060 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); | ||
7061 | |||
7062 | rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA); | ||
7063 | if (!rekey_attr) | ||
7064 | goto nla_put_failure; | ||
7065 | |||
7066 | NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, | ||
7067 | NL80211_REPLAY_CTR_LEN, replay_ctr); | ||
7068 | |||
7069 | nla_nest_end(msg, rekey_attr); | ||
7070 | |||
7071 | if (genlmsg_end(msg, hdr) < 0) { | ||
7072 | nlmsg_free(msg); | ||
7073 | return; | ||
7074 | } | ||
7075 | |||
7076 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
7077 | nl80211_mlme_mcgrp.id, gfp); | ||
7078 | return; | ||
7079 | |||
7080 | nla_put_failure: | ||
7081 | genlmsg_cancel(msg, hdr); | ||
7082 | nlmsg_free(msg); | ||
7083 | } | ||
7084 | |||
6795 | void | 7085 | void |
6796 | nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, | 7086 | nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, |
6797 | struct net_device *netdev, const u8 *peer, | 7087 | struct net_device *netdev, const u8 *peer, |
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 2f1bfb87a65..5d69c56400a 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h | |||
@@ -109,4 +109,8 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, | |||
109 | struct net_device *netdev, const u8 *peer, | 109 | struct net_device *netdev, const u8 *peer, |
110 | u32 num_packets, gfp_t gfp); | 110 | u32 num_packets, gfp_t gfp); |
111 | 111 | ||
112 | void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, | ||
113 | struct net_device *netdev, const u8 *bssid, | ||
114 | const u8 *replay_ctr, gfp_t gfp); | ||
115 | |||
112 | #endif /* __NET_WIRELESS_NL80211_H */ | 116 | #endif /* __NET_WIRELESS_NL80211_H */ |
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 1ad0f39fe09..ca754451e76 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -57,8 +57,17 @@ | |||
57 | #define REG_DBG_PRINT(args...) | 57 | #define REG_DBG_PRINT(args...) |
58 | #endif | 58 | #endif |
59 | 59 | ||
60 | static struct regulatory_request core_request_world = { | ||
61 | .initiator = NL80211_REGDOM_SET_BY_CORE, | ||
62 | .alpha2[0] = '0', | ||
63 | .alpha2[1] = '0', | ||
64 | .intersect = false, | ||
65 | .processed = true, | ||
66 | .country_ie_env = ENVIRON_ANY, | ||
67 | }; | ||
68 | |||
60 | /* Receipt of information from last regulatory request */ | 69 | /* Receipt of information from last regulatory request */ |
61 | static struct regulatory_request *last_request; | 70 | static struct regulatory_request *last_request = &core_request_world; |
62 | 71 | ||
63 | /* To trigger userspace events */ | 72 | /* To trigger userspace events */ |
64 | static struct platform_device *reg_pdev; | 73 | static struct platform_device *reg_pdev; |
@@ -150,7 +159,7 @@ static char user_alpha2[2]; | |||
150 | module_param(ieee80211_regdom, charp, 0444); | 159 | module_param(ieee80211_regdom, charp, 0444); |
151 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); | 160 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); |
152 | 161 | ||
153 | static void reset_regdomains(void) | 162 | static void reset_regdomains(bool full_reset) |
154 | { | 163 | { |
155 | /* avoid freeing static information or freeing something twice */ | 164 | /* avoid freeing static information or freeing something twice */ |
156 | if (cfg80211_regdomain == cfg80211_world_regdom) | 165 | if (cfg80211_regdomain == cfg80211_world_regdom) |
@@ -165,6 +174,13 @@ static void reset_regdomains(void) | |||
165 | 174 | ||
166 | cfg80211_world_regdom = &world_regdom; | 175 | cfg80211_world_regdom = &world_regdom; |
167 | cfg80211_regdomain = NULL; | 176 | cfg80211_regdomain = NULL; |
177 | |||
178 | if (!full_reset) | ||
179 | return; | ||
180 | |||
181 | if (last_request != &core_request_world) | ||
182 | kfree(last_request); | ||
183 | last_request = &core_request_world; | ||
168 | } | 184 | } |
169 | 185 | ||
170 | /* | 186 | /* |
@@ -175,7 +191,7 @@ static void update_world_regdomain(const struct ieee80211_regdomain *rd) | |||
175 | { | 191 | { |
176 | BUG_ON(!last_request); | 192 | BUG_ON(!last_request); |
177 | 193 | ||
178 | reset_regdomains(); | 194 | reset_regdomains(false); |
179 | 195 | ||
180 | cfg80211_world_regdom = rd; | 196 | cfg80211_world_regdom = rd; |
181 | cfg80211_regdomain = rd; | 197 | cfg80211_regdomain = rd; |
@@ -852,6 +868,7 @@ static void handle_channel(struct wiphy *wiphy, | |||
852 | return; | 868 | return; |
853 | } | 869 | } |
854 | 870 | ||
871 | chan->beacon_found = false; | ||
855 | chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); | 872 | chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); |
856 | chan->max_antenna_gain = min(chan->orig_mag, | 873 | chan->max_antenna_gain = min(chan->orig_mag, |
857 | (int) MBI_TO_DBI(power_rule->max_antenna_gain)); | 874 | (int) MBI_TO_DBI(power_rule->max_antenna_gain)); |
@@ -903,7 +920,7 @@ static bool ignore_reg_update(struct wiphy *wiphy, | |||
903 | initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | 920 | initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && |
904 | !is_world_regdom(last_request->alpha2)) { | 921 | !is_world_regdom(last_request->alpha2)) { |
905 | REG_DBG_PRINT("Ignoring regulatory request %s " | 922 | REG_DBG_PRINT("Ignoring regulatory request %s " |
906 | "since the driver requires its own regulaotry " | 923 | "since the driver requires its own regulatory " |
907 | "domain to be set first", | 924 | "domain to be set first", |
908 | reg_initiator_name(initiator)); | 925 | reg_initiator_name(initiator)); |
909 | return true; | 926 | return true; |
@@ -1125,12 +1142,13 @@ void wiphy_update_regulatory(struct wiphy *wiphy, | |||
1125 | enum ieee80211_band band; | 1142 | enum ieee80211_band band; |
1126 | 1143 | ||
1127 | if (ignore_reg_update(wiphy, initiator)) | 1144 | if (ignore_reg_update(wiphy, initiator)) |
1128 | goto out; | 1145 | return; |
1146 | |||
1129 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 1147 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
1130 | if (wiphy->bands[band]) | 1148 | if (wiphy->bands[band]) |
1131 | handle_band(wiphy, band, initiator); | 1149 | handle_band(wiphy, band, initiator); |
1132 | } | 1150 | } |
1133 | out: | 1151 | |
1134 | reg_process_beacons(wiphy); | 1152 | reg_process_beacons(wiphy); |
1135 | reg_process_ht_flags(wiphy); | 1153 | reg_process_ht_flags(wiphy); |
1136 | if (wiphy->reg_notifier) | 1154 | if (wiphy->reg_notifier) |
@@ -1394,7 +1412,8 @@ static int __regulatory_hint(struct wiphy *wiphy, | |||
1394 | } | 1412 | } |
1395 | 1413 | ||
1396 | new_request: | 1414 | new_request: |
1397 | kfree(last_request); | 1415 | if (last_request != &core_request_world) |
1416 | kfree(last_request); | ||
1398 | 1417 | ||
1399 | last_request = pending_request; | 1418 | last_request = pending_request; |
1400 | last_request->intersect = intersect; | 1419 | last_request->intersect = intersect; |
@@ -1564,9 +1583,6 @@ static int regulatory_hint_core(const char *alpha2) | |||
1564 | { | 1583 | { |
1565 | struct regulatory_request *request; | 1584 | struct regulatory_request *request; |
1566 | 1585 | ||
1567 | kfree(last_request); | ||
1568 | last_request = NULL; | ||
1569 | |||
1570 | request = kzalloc(sizeof(struct regulatory_request), | 1586 | request = kzalloc(sizeof(struct regulatory_request), |
1571 | GFP_KERNEL); | 1587 | GFP_KERNEL); |
1572 | if (!request) | 1588 | if (!request) |
@@ -1757,6 +1773,7 @@ static void restore_alpha2(char *alpha2, bool reset_user) | |||
1757 | static void restore_regulatory_settings(bool reset_user) | 1773 | static void restore_regulatory_settings(bool reset_user) |
1758 | { | 1774 | { |
1759 | char alpha2[2]; | 1775 | char alpha2[2]; |
1776 | char world_alpha2[2]; | ||
1760 | struct reg_beacon *reg_beacon, *btmp; | 1777 | struct reg_beacon *reg_beacon, *btmp; |
1761 | struct regulatory_request *reg_request, *tmp; | 1778 | struct regulatory_request *reg_request, *tmp; |
1762 | LIST_HEAD(tmp_reg_req_list); | 1779 | LIST_HEAD(tmp_reg_req_list); |
@@ -1764,7 +1781,7 @@ static void restore_regulatory_settings(bool reset_user) | |||
1764 | mutex_lock(&cfg80211_mutex); | 1781 | mutex_lock(&cfg80211_mutex); |
1765 | mutex_lock(®_mutex); | 1782 | mutex_lock(®_mutex); |
1766 | 1783 | ||
1767 | reset_regdomains(); | 1784 | reset_regdomains(true); |
1768 | restore_alpha2(alpha2, reset_user); | 1785 | restore_alpha2(alpha2, reset_user); |
1769 | 1786 | ||
1770 | /* | 1787 | /* |
@@ -1807,11 +1824,13 @@ static void restore_regulatory_settings(bool reset_user) | |||
1807 | 1824 | ||
1808 | /* First restore to the basic regulatory settings */ | 1825 | /* First restore to the basic regulatory settings */ |
1809 | cfg80211_regdomain = cfg80211_world_regdom; | 1826 | cfg80211_regdomain = cfg80211_world_regdom; |
1827 | world_alpha2[0] = cfg80211_regdomain->alpha2[0]; | ||
1828 | world_alpha2[1] = cfg80211_regdomain->alpha2[1]; | ||
1810 | 1829 | ||
1811 | mutex_unlock(®_mutex); | 1830 | mutex_unlock(®_mutex); |
1812 | mutex_unlock(&cfg80211_mutex); | 1831 | mutex_unlock(&cfg80211_mutex); |
1813 | 1832 | ||
1814 | regulatory_hint_core(cfg80211_regdomain->alpha2); | 1833 | regulatory_hint_core(world_alpha2); |
1815 | 1834 | ||
1816 | /* | 1835 | /* |
1817 | * This restores the ieee80211_regdom module parameter | 1836 | * This restores the ieee80211_regdom module parameter |
@@ -2024,12 +2043,18 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2024 | } | 2043 | } |
2025 | 2044 | ||
2026 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 2045 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); |
2046 | if (!request_wiphy && | ||
2047 | (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || | ||
2048 | last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { | ||
2049 | schedule_delayed_work(®_timeout, 0); | ||
2050 | return -ENODEV; | ||
2051 | } | ||
2027 | 2052 | ||
2028 | if (!last_request->intersect) { | 2053 | if (!last_request->intersect) { |
2029 | int r; | 2054 | int r; |
2030 | 2055 | ||
2031 | if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { | 2056 | if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { |
2032 | reset_regdomains(); | 2057 | reset_regdomains(false); |
2033 | cfg80211_regdomain = rd; | 2058 | cfg80211_regdomain = rd; |
2034 | return 0; | 2059 | return 0; |
2035 | } | 2060 | } |
@@ -2050,7 +2075,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2050 | if (r) | 2075 | if (r) |
2051 | return r; | 2076 | return r; |
2052 | 2077 | ||
2053 | reset_regdomains(); | 2078 | reset_regdomains(false); |
2054 | cfg80211_regdomain = rd; | 2079 | cfg80211_regdomain = rd; |
2055 | return 0; | 2080 | return 0; |
2056 | } | 2081 | } |
@@ -2075,7 +2100,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2075 | 2100 | ||
2076 | rd = NULL; | 2101 | rd = NULL; |
2077 | 2102 | ||
2078 | reset_regdomains(); | 2103 | reset_regdomains(false); |
2079 | cfg80211_regdomain = intersected_rd; | 2104 | cfg80211_regdomain = intersected_rd; |
2080 | 2105 | ||
2081 | return 0; | 2106 | return 0; |
@@ -2095,7 +2120,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2095 | kfree(rd); | 2120 | kfree(rd); |
2096 | rd = NULL; | 2121 | rd = NULL; |
2097 | 2122 | ||
2098 | reset_regdomains(); | 2123 | reset_regdomains(false); |
2099 | cfg80211_regdomain = intersected_rd; | 2124 | cfg80211_regdomain = intersected_rd; |
2100 | 2125 | ||
2101 | return 0; | 2126 | return 0; |
@@ -2248,9 +2273,9 @@ void /* __init_or_exit */ regulatory_exit(void) | |||
2248 | mutex_lock(&cfg80211_mutex); | 2273 | mutex_lock(&cfg80211_mutex); |
2249 | mutex_lock(®_mutex); | 2274 | mutex_lock(®_mutex); |
2250 | 2275 | ||
2251 | reset_regdomains(); | 2276 | reset_regdomains(true); |
2252 | 2277 | ||
2253 | kfree(last_request); | 2278 | dev_set_uevent_suppress(®_pdev->dev, true); |
2254 | 2279 | ||
2255 | platform_device_unregister(reg_pdev); | 2280 | platform_device_unregister(reg_pdev); |
2256 | 2281 | ||
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index ae0c2256ba3..1c100d331e1 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c | |||
@@ -17,7 +17,7 @@ | |||
17 | #include "nl80211.h" | 17 | #include "nl80211.h" |
18 | #include "wext-compat.h" | 18 | #include "wext-compat.h" |
19 | 19 | ||
20 | #define IEEE80211_SCAN_RESULT_EXPIRE (15 * HZ) | 20 | #define IEEE80211_SCAN_RESULT_EXPIRE (3 * HZ) |
21 | 21 | ||
22 | void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) | 22 | void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) |
23 | { | 23 | { |
@@ -132,18 +132,17 @@ EXPORT_SYMBOL(cfg80211_sched_scan_stopped); | |||
132 | int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, | 132 | int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, |
133 | bool driver_initiated) | 133 | bool driver_initiated) |
134 | { | 134 | { |
135 | int err; | ||
136 | struct net_device *dev; | 135 | struct net_device *dev; |
137 | 136 | ||
138 | lockdep_assert_held(&rdev->sched_scan_mtx); | 137 | lockdep_assert_held(&rdev->sched_scan_mtx); |
139 | 138 | ||
140 | if (!rdev->sched_scan_req) | 139 | if (!rdev->sched_scan_req) |
141 | return 0; | 140 | return -ENOENT; |
142 | 141 | ||
143 | dev = rdev->sched_scan_req->dev; | 142 | dev = rdev->sched_scan_req->dev; |
144 | 143 | ||
145 | if (!driver_initiated) { | 144 | if (!driver_initiated) { |
146 | err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); | 145 | int err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); |
147 | if (err) | 146 | if (err) |
148 | return err; | 147 | return err; |
149 | } | 148 | } |
@@ -153,7 +152,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, | |||
153 | kfree(rdev->sched_scan_req); | 152 | kfree(rdev->sched_scan_req); |
154 | rdev->sched_scan_req = NULL; | 153 | rdev->sched_scan_req = NULL; |
155 | 154 | ||
156 | return err; | 155 | return 0; |
157 | } | 156 | } |
158 | 157 | ||
159 | static void bss_release(struct kref *ref) | 158 | static void bss_release(struct kref *ref) |
@@ -863,6 +862,10 @@ int cfg80211_wext_siwscan(struct net_device *dev, | |||
863 | creq->n_ssids = 0; | 862 | creq->n_ssids = 0; |
864 | } | 863 | } |
865 | 864 | ||
865 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) | ||
866 | if (wiphy->bands[i]) | ||
867 | creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; | ||
868 | |||
866 | rdev->scan_req = creq; | 869 | rdev->scan_req = creq; |
867 | err = rdev->ops->scan(wiphy, dev, creq); | 870 | err = rdev->ops->scan(wiphy, dev, creq); |
868 | if (err) { | 871 | if (err) { |
diff --git a/net/wireless/sme.c b/net/wireless/sme.c index b7b6ff8be55..c0fe1a8a6ee 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c | |||
@@ -118,6 +118,8 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) | |||
118 | i++, j++) | 118 | i++, j++) |
119 | request->channels[i] = | 119 | request->channels[i] = |
120 | &wdev->wiphy->bands[band]->channels[j]; | 120 | &wdev->wiphy->bands[band]->channels[j]; |
121 | request->rates[band] = | ||
122 | (1 << wdev->wiphy->bands[band]->n_bitrates) - 1; | ||
121 | } | 123 | } |
122 | } | 124 | } |
123 | request->n_channels = n_channels; | 125 | request->n_channels = n_channels; |
@@ -659,8 +661,10 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, | |||
659 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) | 661 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) |
660 | return; | 662 | return; |
661 | 663 | ||
664 | #ifndef CONFIG_CFG80211_ALLOW_RECONNECT | ||
662 | if (wdev->sme_state != CFG80211_SME_CONNECTED) | 665 | if (wdev->sme_state != CFG80211_SME_CONNECTED) |
663 | return; | 666 | return; |
667 | #endif | ||
664 | 668 | ||
665 | if (wdev->current_bss) { | 669 | if (wdev->current_bss) { |
666 | cfg80211_unhold_bss(wdev->current_bss); | 670 | cfg80211_unhold_bss(wdev->current_bss); |
@@ -758,10 +762,14 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
758 | 762 | ||
759 | ASSERT_WDEV_LOCK(wdev); | 763 | ASSERT_WDEV_LOCK(wdev); |
760 | 764 | ||
765 | #ifndef CONFIG_CFG80211_ALLOW_RECONNECT | ||
761 | if (wdev->sme_state != CFG80211_SME_IDLE) | 766 | if (wdev->sme_state != CFG80211_SME_IDLE) |
762 | return -EALREADY; | 767 | return -EALREADY; |
763 | 768 | ||
764 | if (WARN_ON(wdev->connect_keys)) { | 769 | if (WARN_ON(wdev->connect_keys)) { |
770 | #else | ||
771 | if (wdev->connect_keys) { | ||
772 | #endif | ||
765 | kfree(wdev->connect_keys); | 773 | kfree(wdev->connect_keys); |
766 | wdev->connect_keys = NULL; | 774 | wdev->connect_keys = NULL; |
767 | } | 775 | } |
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index c6e4ca6a7d2..ff574597a85 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c | |||
@@ -93,7 +93,8 @@ 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, rdev->wowlan); | 96 | if (rdev->wiphy.registered) |
97 | ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan); | ||
97 | rtnl_unlock(); | 98 | rtnl_unlock(); |
98 | } | 99 | } |
99 | 100 | ||
@@ -112,7 +113,8 @@ static int wiphy_resume(struct device *dev) | |||
112 | 113 | ||
113 | if (rdev->ops->resume) { | 114 | if (rdev->ops->resume) { |
114 | rtnl_lock(); | 115 | rtnl_lock(); |
115 | ret = rdev->ops->resume(&rdev->wiphy); | 116 | if (rdev->wiphy.registered) |
117 | ret = rdev->ops->resume(&rdev->wiphy); | ||
116 | rtnl_unlock(); | 118 | rtnl_unlock(); |
117 | } | 119 | } |
118 | 120 | ||
diff --git a/net/wireless/util.c b/net/wireless/util.c index 4d7b83fbc32..be75a3a0424 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -1006,3 +1006,41 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, | |||
1006 | 1006 | ||
1007 | return -EBUSY; | 1007 | return -EBUSY; |
1008 | } | 1008 | } |
1009 | |||
1010 | int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, | ||
1011 | const u8 *rates, unsigned int n_rates, | ||
1012 | u32 *mask) | ||
1013 | { | ||
1014 | int i, j; | ||
1015 | |||
1016 | if (!sband) | ||
1017 | return -EINVAL; | ||
1018 | |||
1019 | if (n_rates == 0 || n_rates > NL80211_MAX_SUPP_RATES) | ||
1020 | return -EINVAL; | ||
1021 | |||
1022 | *mask = 0; | ||
1023 | |||
1024 | for (i = 0; i < n_rates; i++) { | ||
1025 | int rate = (rates[i] & 0x7f) * 5; | ||
1026 | bool found = false; | ||
1027 | |||
1028 | for (j = 0; j < sband->n_bitrates; j++) { | ||
1029 | if (sband->bitrates[j].bitrate == rate) { | ||
1030 | found = true; | ||
1031 | *mask |= BIT(j); | ||
1032 | break; | ||
1033 | } | ||
1034 | } | ||
1035 | if (!found) | ||
1036 | return -EINVAL; | ||
1037 | } | ||
1038 | |||
1039 | /* | ||
1040 | * mask must have at least one bit set here since we | ||
1041 | * didn't accept a 0-length rates array nor allowed | ||
1042 | * entries in the array that didn't exist | ||
1043 | */ | ||
1044 | |||
1045 | return 0; | ||
1046 | } | ||