diff options
Diffstat (limited to 'net/mac80211/util.c')
| -rw-r--r-- | net/mac80211/util.c | 399 |
1 files changed, 300 insertions, 99 deletions
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 3848140313f5..748387d45bc0 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
| @@ -18,7 +18,6 @@ | |||
| 18 | #include <linux/skbuff.h> | 18 | #include <linux/skbuff.h> |
| 19 | #include <linux/etherdevice.h> | 19 | #include <linux/etherdevice.h> |
| 20 | #include <linux/if_arp.h> | 20 | #include <linux/if_arp.h> |
| 21 | #include <linux/wireless.h> | ||
| 22 | #include <linux/bitmap.h> | 21 | #include <linux/bitmap.h> |
| 23 | #include <linux/crc32.h> | 22 | #include <linux/crc32.h> |
| 24 | #include <net/net_namespace.h> | 23 | #include <net/net_namespace.h> |
| @@ -271,6 +270,8 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, | |||
| 271 | struct ieee80211_local *local = hw_to_local(hw); | 270 | struct ieee80211_local *local = hw_to_local(hw); |
| 272 | struct ieee80211_sub_if_data *sdata; | 271 | struct ieee80211_sub_if_data *sdata; |
| 273 | 272 | ||
| 273 | trace_wake_queue(local, queue, reason); | ||
| 274 | |||
| 274 | if (WARN_ON(queue >= hw->queues)) | 275 | if (WARN_ON(queue >= hw->queues)) |
| 275 | return; | 276 | return; |
| 276 | 277 | ||
| @@ -280,13 +281,13 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, | |||
| 280 | /* someone still has this queue stopped */ | 281 | /* someone still has this queue stopped */ |
| 281 | return; | 282 | return; |
| 282 | 283 | ||
| 283 | if (!skb_queue_empty(&local->pending[queue])) | 284 | if (skb_queue_empty(&local->pending[queue])) { |
| 285 | rcu_read_lock(); | ||
| 286 | list_for_each_entry_rcu(sdata, &local->interfaces, list) | ||
| 287 | netif_tx_wake_queue(netdev_get_tx_queue(sdata->dev, queue)); | ||
| 288 | rcu_read_unlock(); | ||
| 289 | } else | ||
| 284 | tasklet_schedule(&local->tx_pending_tasklet); | 290 | tasklet_schedule(&local->tx_pending_tasklet); |
| 285 | |||
| 286 | rcu_read_lock(); | ||
| 287 | list_for_each_entry_rcu(sdata, &local->interfaces, list) | ||
| 288 | netif_tx_wake_queue(netdev_get_tx_queue(sdata->dev, queue)); | ||
| 289 | rcu_read_unlock(); | ||
| 290 | } | 291 | } |
| 291 | 292 | ||
| 292 | void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, | 293 | void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, |
| @@ -313,6 +314,8 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, | |||
| 313 | struct ieee80211_local *local = hw_to_local(hw); | 314 | struct ieee80211_local *local = hw_to_local(hw); |
| 314 | struct ieee80211_sub_if_data *sdata; | 315 | struct ieee80211_sub_if_data *sdata; |
| 315 | 316 | ||
| 317 | trace_stop_queue(local, queue, reason); | ||
| 318 | |||
| 316 | if (WARN_ON(queue >= hw->queues)) | 319 | if (WARN_ON(queue >= hw->queues)) |
| 317 | return; | 320 | return; |
| 318 | 321 | ||
| @@ -480,8 +483,8 @@ void ieee80211_iterate_active_interfaces( | |||
| 480 | case NL80211_IFTYPE_MESH_POINT: | 483 | case NL80211_IFTYPE_MESH_POINT: |
| 481 | break; | 484 | break; |
| 482 | } | 485 | } |
| 483 | if (netif_running(sdata->dev)) | 486 | if (ieee80211_sdata_running(sdata)) |
| 484 | iterator(data, sdata->dev->dev_addr, | 487 | iterator(data, sdata->vif.addr, |
| 485 | &sdata->vif); | 488 | &sdata->vif); |
| 486 | } | 489 | } |
| 487 | 490 | ||
| @@ -514,8 +517,8 @@ void ieee80211_iterate_active_interfaces_atomic( | |||
| 514 | case NL80211_IFTYPE_MESH_POINT: | 517 | case NL80211_IFTYPE_MESH_POINT: |
| 515 | break; | 518 | break; |
| 516 | } | 519 | } |
| 517 | if (netif_running(sdata->dev)) | 520 | if (ieee80211_sdata_running(sdata)) |
| 518 | iterator(data, sdata->dev->dev_addr, | 521 | iterator(data, sdata->vif.addr, |
| 519 | &sdata->vif); | 522 | &sdata->vif); |
| 520 | } | 523 | } |
| 521 | 524 | ||
| @@ -793,8 +796,19 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata) | |||
| 793 | break; | 796 | break; |
| 794 | } | 797 | } |
| 795 | 798 | ||
| 799 | qparam.uapsd = false; | ||
| 800 | |||
| 796 | drv_conf_tx(local, queue, &qparam); | 801 | drv_conf_tx(local, queue, &qparam); |
| 797 | } | 802 | } |
| 803 | |||
| 804 | /* after reinitialize QoS TX queues setting to default, | ||
| 805 | * disable QoS at all */ | ||
| 806 | |||
| 807 | if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { | ||
| 808 | sdata->vif.bss_conf.qos = | ||
| 809 | sdata->vif.type != NL80211_IFTYPE_STATION; | ||
| 810 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS); | ||
| 811 | } | ||
| 798 | } | 812 | } |
| 799 | 813 | ||
| 800 | void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, | 814 | void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, |
| @@ -860,7 +874,7 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, | |||
| 860 | sizeof(*mgmt) + 6 + extra_len); | 874 | sizeof(*mgmt) + 6 + extra_len); |
| 861 | if (!skb) { | 875 | if (!skb) { |
| 862 | printk(KERN_DEBUG "%s: failed to allocate buffer for auth " | 876 | printk(KERN_DEBUG "%s: failed to allocate buffer for auth " |
| 863 | "frame\n", sdata->dev->name); | 877 | "frame\n", sdata->name); |
| 864 | return; | 878 | return; |
| 865 | } | 879 | } |
| 866 | skb_reserve(skb, local->hw.extra_tx_headroom); | 880 | skb_reserve(skb, local->hw.extra_tx_headroom); |
| @@ -870,7 +884,7 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, | |||
| 870 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | 884 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | |
| 871 | IEEE80211_STYPE_AUTH); | 885 | IEEE80211_STYPE_AUTH); |
| 872 | memcpy(mgmt->da, bssid, ETH_ALEN); | 886 | memcpy(mgmt->da, bssid, ETH_ALEN); |
| 873 | memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); | 887 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); |
| 874 | memcpy(mgmt->bssid, bssid, ETH_ALEN); | 888 | memcpy(mgmt->bssid, bssid, ETH_ALEN); |
| 875 | mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg); | 889 | mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg); |
| 876 | mgmt->u.auth.auth_transaction = cpu_to_le16(transaction); | 890 | mgmt->u.auth.auth_transaction = cpu_to_le16(transaction); |
| @@ -893,43 +907,87 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | |||
| 893 | enum ieee80211_band band) | 907 | enum ieee80211_band band) |
| 894 | { | 908 | { |
| 895 | struct ieee80211_supported_band *sband; | 909 | struct ieee80211_supported_band *sband; |
| 896 | u8 *pos, *supp_rates_len, *esupp_rates_len = NULL; | 910 | u8 *pos; |
| 897 | int i; | 911 | size_t offset = 0, noffset; |
| 912 | int supp_rates_len, i; | ||
| 898 | 913 | ||
| 899 | sband = local->hw.wiphy->bands[band]; | 914 | sband = local->hw.wiphy->bands[band]; |
| 900 | 915 | ||
| 901 | pos = buffer; | 916 | pos = buffer; |
| 902 | 917 | ||
| 918 | supp_rates_len = min_t(int, sband->n_bitrates, 8); | ||
| 919 | |||
| 903 | *pos++ = WLAN_EID_SUPP_RATES; | 920 | *pos++ = WLAN_EID_SUPP_RATES; |
| 904 | supp_rates_len = pos; | 921 | *pos++ = supp_rates_len; |
| 905 | *pos++ = 0; | ||
| 906 | |||
| 907 | for (i = 0; i < sband->n_bitrates; i++) { | ||
| 908 | struct ieee80211_rate *rate = &sband->bitrates[i]; | ||
| 909 | |||
| 910 | if (esupp_rates_len) { | ||
| 911 | *esupp_rates_len += 1; | ||
| 912 | } else if (*supp_rates_len == 8) { | ||
| 913 | *pos++ = WLAN_EID_EXT_SUPP_RATES; | ||
| 914 | esupp_rates_len = pos; | ||
| 915 | *pos++ = 1; | ||
| 916 | } else | ||
| 917 | *supp_rates_len += 1; | ||
| 918 | 922 | ||
| 919 | *pos++ = rate->bitrate / 5; | 923 | for (i = 0; i < supp_rates_len; i++) { |
| 924 | int rate = sband->bitrates[i].bitrate; | ||
| 925 | *pos++ = (u8) (rate / 5); | ||
| 926 | } | ||
| 927 | |||
| 928 | /* insert "request information" if in custom IEs */ | ||
| 929 | if (ie && ie_len) { | ||
| 930 | static const u8 before_extrates[] = { | ||
| 931 | WLAN_EID_SSID, | ||
| 932 | WLAN_EID_SUPP_RATES, | ||
| 933 | WLAN_EID_REQUEST, | ||
| 934 | }; | ||
| 935 | noffset = ieee80211_ie_split(ie, ie_len, | ||
| 936 | before_extrates, | ||
| 937 | ARRAY_SIZE(before_extrates), | ||
| 938 | offset); | ||
| 939 | memcpy(pos, ie + offset, noffset - offset); | ||
| 940 | pos += noffset - offset; | ||
| 941 | offset = noffset; | ||
| 942 | } | ||
| 943 | |||
| 944 | if (sband->n_bitrates > i) { | ||
| 945 | *pos++ = WLAN_EID_EXT_SUPP_RATES; | ||
| 946 | *pos++ = sband->n_bitrates - i; | ||
| 947 | |||
| 948 | for (; i < sband->n_bitrates; i++) { | ||
| 949 | int rate = sband->bitrates[i].bitrate; | ||
| 950 | *pos++ = (u8) (rate / 5); | ||
| 951 | } | ||
| 952 | } | ||
| 953 | |||
| 954 | /* insert custom IEs that go before HT */ | ||
| 955 | if (ie && ie_len) { | ||
| 956 | static const u8 before_ht[] = { | ||
| 957 | WLAN_EID_SSID, | ||
| 958 | WLAN_EID_SUPP_RATES, | ||
| 959 | WLAN_EID_REQUEST, | ||
| 960 | WLAN_EID_EXT_SUPP_RATES, | ||
| 961 | WLAN_EID_DS_PARAMS, | ||
| 962 | WLAN_EID_SUPPORTED_REGULATORY_CLASSES, | ||
| 963 | }; | ||
| 964 | noffset = ieee80211_ie_split(ie, ie_len, | ||
| 965 | before_ht, ARRAY_SIZE(before_ht), | ||
| 966 | offset); | ||
| 967 | memcpy(pos, ie + offset, noffset - offset); | ||
| 968 | pos += noffset - offset; | ||
| 969 | offset = noffset; | ||
| 920 | } | 970 | } |
| 921 | 971 | ||
| 922 | if (sband->ht_cap.ht_supported) { | 972 | if (sband->ht_cap.ht_supported) { |
| 923 | __le16 tmp = cpu_to_le16(sband->ht_cap.cap); | 973 | u16 cap = sband->ht_cap.cap; |
| 974 | __le16 tmp; | ||
| 975 | |||
| 976 | if (ieee80211_disable_40mhz_24ghz && | ||
| 977 | sband->band == IEEE80211_BAND_2GHZ) { | ||
| 978 | cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; | ||
| 979 | cap &= ~IEEE80211_HT_CAP_SGI_40; | ||
| 980 | } | ||
| 924 | 981 | ||
| 925 | *pos++ = WLAN_EID_HT_CAPABILITY; | 982 | *pos++ = WLAN_EID_HT_CAPABILITY; |
| 926 | *pos++ = sizeof(struct ieee80211_ht_cap); | 983 | *pos++ = sizeof(struct ieee80211_ht_cap); |
| 927 | memset(pos, 0, sizeof(struct ieee80211_ht_cap)); | 984 | memset(pos, 0, sizeof(struct ieee80211_ht_cap)); |
| 985 | tmp = cpu_to_le16(cap); | ||
| 928 | memcpy(pos, &tmp, sizeof(u16)); | 986 | memcpy(pos, &tmp, sizeof(u16)); |
| 929 | pos += sizeof(u16); | 987 | pos += sizeof(u16); |
| 930 | /* TODO: needs a define here for << 2 */ | ||
| 931 | *pos++ = sband->ht_cap.ampdu_factor | | 988 | *pos++ = sband->ht_cap.ampdu_factor | |
| 932 | (sband->ht_cap.ampdu_density << 2); | 989 | (sband->ht_cap.ampdu_density << |
| 990 | IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); | ||
| 933 | memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); | 991 | memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); |
| 934 | pos += sizeof(sband->ht_cap.mcs); | 992 | pos += sizeof(sband->ht_cap.mcs); |
| 935 | pos += 2 + 4 + 1; /* ext info, BF cap, antsel */ | 993 | pos += 2 + 4 + 1; /* ext info, BF cap, antsel */ |
| @@ -940,9 +998,11 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | |||
| 940 | * that calculates local->scan_ies_len. | 998 | * that calculates local->scan_ies_len. |
| 941 | */ | 999 | */ |
| 942 | 1000 | ||
| 943 | if (ie) { | 1001 | /* add any remaining custom IEs */ |
| 944 | memcpy(pos, ie, ie_len); | 1002 | if (ie && ie_len) { |
| 945 | pos += ie_len; | 1003 | noffset = ie_len; |
| 1004 | memcpy(pos, ie + offset, noffset - offset); | ||
| 1005 | pos += noffset - offset; | ||
| 946 | } | 1006 | } |
| 947 | 1007 | ||
| 948 | return pos - buffer; | 1008 | return pos - buffer; |
| @@ -955,40 +1015,33 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, | |||
| 955 | struct ieee80211_local *local = sdata->local; | 1015 | struct ieee80211_local *local = sdata->local; |
| 956 | struct sk_buff *skb; | 1016 | struct sk_buff *skb; |
| 957 | struct ieee80211_mgmt *mgmt; | 1017 | struct ieee80211_mgmt *mgmt; |
| 958 | u8 *pos; | 1018 | size_t buf_len; |
| 959 | 1019 | u8 *buf; | |
| 960 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + | 1020 | |
| 961 | ie_len); | 1021 | /* FIXME: come up with a proper value */ |
| 962 | if (!skb) { | 1022 | buf = kmalloc(200 + ie_len, GFP_KERNEL); |
| 963 | printk(KERN_DEBUG "%s: failed to allocate buffer for probe " | 1023 | if (!buf) { |
| 964 | "request\n", sdata->dev->name); | 1024 | printk(KERN_DEBUG "%s: failed to allocate temporary IE " |
| 1025 | "buffer\n", sdata->name); | ||
| 965 | return; | 1026 | return; |
| 966 | } | 1027 | } |
| 967 | skb_reserve(skb, local->hw.extra_tx_headroom); | ||
| 968 | 1028 | ||
| 969 | mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); | 1029 | buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, |
| 970 | memset(mgmt, 0, 24); | 1030 | local->hw.conf.channel->band); |
| 971 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | 1031 | |
| 972 | IEEE80211_STYPE_PROBE_REQ); | 1032 | skb = ieee80211_probereq_get(&local->hw, &sdata->vif, |
| 973 | memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); | 1033 | ssid, ssid_len, |
| 1034 | buf, buf_len); | ||
| 1035 | |||
| 974 | if (dst) { | 1036 | if (dst) { |
| 1037 | mgmt = (struct ieee80211_mgmt *) skb->data; | ||
| 975 | memcpy(mgmt->da, dst, ETH_ALEN); | 1038 | memcpy(mgmt->da, dst, ETH_ALEN); |
| 976 | memcpy(mgmt->bssid, dst, ETH_ALEN); | 1039 | memcpy(mgmt->bssid, dst, ETH_ALEN); |
| 977 | } else { | ||
| 978 | memset(mgmt->da, 0xff, ETH_ALEN); | ||
| 979 | memset(mgmt->bssid, 0xff, ETH_ALEN); | ||
| 980 | } | 1040 | } |
| 981 | pos = skb_put(skb, 2 + ssid_len); | ||
| 982 | *pos++ = WLAN_EID_SSID; | ||
| 983 | *pos++ = ssid_len; | ||
| 984 | memcpy(pos, ssid, ssid_len); | ||
| 985 | pos += ssid_len; | ||
| 986 | |||
| 987 | skb_put(skb, ieee80211_build_preq_ies(local, pos, ie, ie_len, | ||
| 988 | local->hw.conf.channel->band)); | ||
| 989 | 1041 | ||
| 990 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; | 1042 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; |
| 991 | ieee80211_tx_skb(sdata, skb); | 1043 | ieee80211_tx_skb(sdata, skb); |
| 1044 | kfree(buf); | ||
| 992 | } | 1045 | } |
| 993 | 1046 | ||
| 994 | u32 ieee80211_sta_get_rates(struct ieee80211_local *local, | 1047 | u32 ieee80211_sta_get_rates(struct ieee80211_local *local, |
| @@ -1032,18 +1085,16 @@ void ieee80211_stop_device(struct ieee80211_local *local) | |||
| 1032 | ieee80211_led_radio(local, false); | 1085 | ieee80211_led_radio(local, false); |
| 1033 | 1086 | ||
| 1034 | cancel_work_sync(&local->reconfig_filter); | 1087 | cancel_work_sync(&local->reconfig_filter); |
| 1035 | drv_stop(local); | ||
| 1036 | 1088 | ||
| 1037 | flush_workqueue(local->workqueue); | 1089 | flush_workqueue(local->workqueue); |
| 1090 | drv_stop(local); | ||
| 1038 | } | 1091 | } |
| 1039 | 1092 | ||
| 1040 | int ieee80211_reconfig(struct ieee80211_local *local) | 1093 | int ieee80211_reconfig(struct ieee80211_local *local) |
| 1041 | { | 1094 | { |
| 1042 | struct ieee80211_hw *hw = &local->hw; | 1095 | struct ieee80211_hw *hw = &local->hw; |
| 1043 | struct ieee80211_sub_if_data *sdata; | 1096 | struct ieee80211_sub_if_data *sdata; |
| 1044 | struct ieee80211_if_init_conf conf; | ||
| 1045 | struct sta_info *sta; | 1097 | struct sta_info *sta; |
| 1046 | unsigned long flags; | ||
| 1047 | int res; | 1098 | int res; |
| 1048 | 1099 | ||
| 1049 | if (local->suspended) | 1100 | if (local->suspended) |
| @@ -1059,9 +1110,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
| 1059 | */ | 1110 | */ |
| 1060 | res = drv_start(local); | 1111 | res = drv_start(local); |
| 1061 | if (res) { | 1112 | if (res) { |
| 1062 | WARN(local->suspended, "Harware became unavailable " | 1113 | WARN(local->suspended, "Hardware became unavailable " |
| 1063 | "upon resume. This is could be a software issue" | 1114 | "upon resume. This could be a software issue " |
| 1064 | "prior to suspend or a harware issue\n"); | 1115 | "prior to suspend or a hardware issue.\n"); |
| 1065 | return res; | 1116 | return res; |
| 1066 | } | 1117 | } |
| 1067 | 1118 | ||
| @@ -1072,41 +1123,24 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
| 1072 | list_for_each_entry(sdata, &local->interfaces, list) { | 1123 | list_for_each_entry(sdata, &local->interfaces, list) { |
| 1073 | if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && | 1124 | if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && |
| 1074 | sdata->vif.type != NL80211_IFTYPE_MONITOR && | 1125 | sdata->vif.type != NL80211_IFTYPE_MONITOR && |
| 1075 | netif_running(sdata->dev)) { | 1126 | ieee80211_sdata_running(sdata)) |
| 1076 | conf.vif = &sdata->vif; | 1127 | res = drv_add_interface(local, &sdata->vif); |
| 1077 | conf.type = sdata->vif.type; | ||
| 1078 | conf.mac_addr = sdata->dev->dev_addr; | ||
| 1079 | res = drv_add_interface(local, &conf); | ||
| 1080 | } | ||
| 1081 | } | 1128 | } |
| 1082 | 1129 | ||
| 1083 | /* add STAs back */ | 1130 | /* add STAs back */ |
| 1084 | if (local->ops->sta_notify) { | 1131 | mutex_lock(&local->sta_mtx); |
| 1085 | spin_lock_irqsave(&local->sta_lock, flags); | 1132 | list_for_each_entry(sta, &local->sta_list, list) { |
| 1086 | list_for_each_entry(sta, &local->sta_list, list) { | 1133 | if (sta->uploaded) { |
| 1087 | sdata = sta->sdata; | 1134 | sdata = sta->sdata; |
| 1088 | if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | 1135 | if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) |
| 1089 | sdata = container_of(sdata->bss, | 1136 | sdata = container_of(sdata->bss, |
| 1090 | struct ieee80211_sub_if_data, | 1137 | struct ieee80211_sub_if_data, |
| 1091 | u.ap); | 1138 | u.ap); |
| 1092 | 1139 | ||
| 1093 | drv_sta_notify(local, &sdata->vif, STA_NOTIFY_ADD, | 1140 | WARN_ON(drv_sta_add(local, sdata, &sta->sta)); |
| 1094 | &sta->sta); | ||
| 1095 | } | 1141 | } |
| 1096 | spin_unlock_irqrestore(&local->sta_lock, flags); | ||
| 1097 | } | 1142 | } |
| 1098 | 1143 | mutex_unlock(&local->sta_mtx); | |
| 1099 | /* Clear Suspend state so that ADDBA requests can be processed */ | ||
| 1100 | |||
| 1101 | rcu_read_lock(); | ||
| 1102 | |||
| 1103 | if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { | ||
| 1104 | list_for_each_entry_rcu(sta, &local->sta_list, list) { | ||
| 1105 | clear_sta_flags(sta, WLAN_STA_SUSPEND); | ||
| 1106 | } | ||
| 1107 | } | ||
| 1108 | |||
| 1109 | rcu_read_unlock(); | ||
| 1110 | 1144 | ||
| 1111 | /* setup RTS threshold */ | 1145 | /* setup RTS threshold */ |
| 1112 | drv_set_rts_threshold(local, hw->wiphy->rts_threshold); | 1146 | drv_set_rts_threshold(local, hw->wiphy->rts_threshold); |
| @@ -1118,18 +1152,34 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
| 1118 | 1152 | ||
| 1119 | /* Finally also reconfigure all the BSS information */ | 1153 | /* Finally also reconfigure all the BSS information */ |
| 1120 | list_for_each_entry(sdata, &local->interfaces, list) { | 1154 | list_for_each_entry(sdata, &local->interfaces, list) { |
| 1121 | u32 changed = ~0; | 1155 | u32 changed; |
| 1122 | if (!netif_running(sdata->dev)) | 1156 | |
| 1157 | if (!ieee80211_sdata_running(sdata)) | ||
| 1123 | continue; | 1158 | continue; |
| 1159 | |||
| 1160 | /* common change flags for all interface types */ | ||
| 1161 | changed = BSS_CHANGED_ERP_CTS_PROT | | ||
| 1162 | BSS_CHANGED_ERP_PREAMBLE | | ||
| 1163 | BSS_CHANGED_ERP_SLOT | | ||
| 1164 | BSS_CHANGED_HT | | ||
| 1165 | BSS_CHANGED_BASIC_RATES | | ||
| 1166 | BSS_CHANGED_BEACON_INT | | ||
| 1167 | BSS_CHANGED_BSSID | | ||
| 1168 | BSS_CHANGED_CQM | | ||
| 1169 | BSS_CHANGED_QOS; | ||
| 1170 | |||
| 1124 | switch (sdata->vif.type) { | 1171 | switch (sdata->vif.type) { |
| 1125 | case NL80211_IFTYPE_STATION: | 1172 | case NL80211_IFTYPE_STATION: |
| 1126 | /* disable beacon change bits */ | 1173 | changed |= BSS_CHANGED_ASSOC; |
| 1127 | changed &= ~(BSS_CHANGED_BEACON | | 1174 | ieee80211_bss_info_change_notify(sdata, changed); |
| 1128 | BSS_CHANGED_BEACON_ENABLED); | 1175 | break; |
| 1129 | /* fall through */ | ||
| 1130 | case NL80211_IFTYPE_ADHOC: | 1176 | case NL80211_IFTYPE_ADHOC: |
| 1177 | changed |= BSS_CHANGED_IBSS; | ||
| 1178 | /* fall through */ | ||
| 1131 | case NL80211_IFTYPE_AP: | 1179 | case NL80211_IFTYPE_AP: |
| 1132 | case NL80211_IFTYPE_MESH_POINT: | 1180 | case NL80211_IFTYPE_MESH_POINT: |
| 1181 | changed |= BSS_CHANGED_BEACON | | ||
| 1182 | BSS_CHANGED_BEACON_ENABLED; | ||
| 1133 | ieee80211_bss_info_change_notify(sdata, changed); | 1183 | ieee80211_bss_info_change_notify(sdata, changed); |
| 1134 | break; | 1184 | break; |
| 1135 | case NL80211_IFTYPE_WDS: | 1185 | case NL80211_IFTYPE_WDS: |
| @@ -1145,9 +1195,30 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
| 1145 | } | 1195 | } |
| 1146 | } | 1196 | } |
| 1147 | 1197 | ||
| 1198 | /* | ||
| 1199 | * Clear the WLAN_STA_BLOCK_BA flag so new aggregation | ||
| 1200 | * sessions can be established after a resume. | ||
| 1201 | * | ||
| 1202 | * Also tear down aggregation sessions since reconfiguring | ||
| 1203 | * them in a hardware restart scenario is not easily done | ||
| 1204 | * right now, and the hardware will have lost information | ||
| 1205 | * about the sessions, but we and the AP still think they | ||
| 1206 | * are active. This is really a workaround though. | ||
| 1207 | */ | ||
| 1208 | if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { | ||
| 1209 | mutex_lock(&local->sta_mtx); | ||
| 1210 | |||
| 1211 | list_for_each_entry(sta, &local->sta_list, list) { | ||
| 1212 | ieee80211_sta_tear_down_BA_sessions(sta); | ||
| 1213 | clear_sta_flags(sta, WLAN_STA_BLOCK_BA); | ||
| 1214 | } | ||
| 1215 | |||
| 1216 | mutex_unlock(&local->sta_mtx); | ||
| 1217 | } | ||
| 1218 | |||
| 1148 | /* add back keys */ | 1219 | /* add back keys */ |
| 1149 | list_for_each_entry(sdata, &local->interfaces, list) | 1220 | list_for_each_entry(sdata, &local->interfaces, list) |
| 1150 | if (netif_running(sdata->dev)) | 1221 | if (ieee80211_sdata_running(sdata)) |
| 1151 | ieee80211_enable_keys(sdata); | 1222 | ieee80211_enable_keys(sdata); |
| 1152 | 1223 | ||
| 1153 | ieee80211_wake_queues_by_reason(hw, | 1224 | ieee80211_wake_queues_by_reason(hw, |
| @@ -1184,13 +1255,143 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
| 1184 | 1255 | ||
| 1185 | add_timer(&local->sta_cleanup); | 1256 | add_timer(&local->sta_cleanup); |
| 1186 | 1257 | ||
| 1187 | spin_lock_irqsave(&local->sta_lock, flags); | 1258 | mutex_lock(&local->sta_mtx); |
| 1188 | list_for_each_entry(sta, &local->sta_list, list) | 1259 | list_for_each_entry(sta, &local->sta_list, list) |
| 1189 | mesh_plink_restart(sta); | 1260 | mesh_plink_restart(sta); |
| 1190 | spin_unlock_irqrestore(&local->sta_lock, flags); | 1261 | mutex_unlock(&local->sta_mtx); |
| 1191 | #else | 1262 | #else |
| 1192 | WARN_ON(1); | 1263 | WARN_ON(1); |
| 1193 | #endif | 1264 | #endif |
| 1194 | return 0; | 1265 | return 0; |
| 1195 | } | 1266 | } |
| 1196 | 1267 | ||
| 1268 | static int check_mgd_smps(struct ieee80211_if_managed *ifmgd, | ||
| 1269 | enum ieee80211_smps_mode *smps_mode) | ||
| 1270 | { | ||
| 1271 | if (ifmgd->associated) { | ||
| 1272 | *smps_mode = ifmgd->ap_smps; | ||
| 1273 | |||
| 1274 | if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) { | ||
| 1275 | if (ifmgd->powersave) | ||
| 1276 | *smps_mode = IEEE80211_SMPS_DYNAMIC; | ||
| 1277 | else | ||
| 1278 | *smps_mode = IEEE80211_SMPS_OFF; | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | return 1; | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | return 0; | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | /* must hold iflist_mtx */ | ||
| 1288 | void ieee80211_recalc_smps(struct ieee80211_local *local, | ||
| 1289 | struct ieee80211_sub_if_data *forsdata) | ||
| 1290 | { | ||
| 1291 | struct ieee80211_sub_if_data *sdata; | ||
| 1292 | enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF; | ||
| 1293 | int count = 0; | ||
| 1294 | |||
| 1295 | if (forsdata) | ||
| 1296 | WARN_ON(!mutex_is_locked(&forsdata->u.mgd.mtx)); | ||
| 1297 | |||
| 1298 | WARN_ON(!mutex_is_locked(&local->iflist_mtx)); | ||
| 1299 | |||
| 1300 | /* | ||
| 1301 | * This function could be improved to handle multiple | ||
| 1302 | * interfaces better, but right now it makes any | ||
| 1303 | * non-station interfaces force SM PS to be turned | ||
| 1304 | * off. If there are multiple station interfaces it | ||
| 1305 | * could also use the best possible mode, e.g. if | ||
| 1306 | * one is in static and the other in dynamic then | ||
| 1307 | * dynamic is ok. | ||
| 1308 | */ | ||
| 1309 | |||
| 1310 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
| 1311 | if (!netif_running(sdata->dev)) | ||
| 1312 | continue; | ||
| 1313 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
| 1314 | goto set; | ||
| 1315 | if (sdata != forsdata) { | ||
| 1316 | /* | ||
| 1317 | * This nested is ok -- we are holding the iflist_mtx | ||
| 1318 | * so can't get here twice or so. But it's required | ||
| 1319 | * since normally we acquire it first and then the | ||
| 1320 | * iflist_mtx. | ||
| 1321 | */ | ||
| 1322 | mutex_lock_nested(&sdata->u.mgd.mtx, SINGLE_DEPTH_NESTING); | ||
| 1323 | count += check_mgd_smps(&sdata->u.mgd, &smps_mode); | ||
| 1324 | mutex_unlock(&sdata->u.mgd.mtx); | ||
| 1325 | } else | ||
| 1326 | count += check_mgd_smps(&sdata->u.mgd, &smps_mode); | ||
| 1327 | |||
| 1328 | if (count > 1) { | ||
| 1329 | smps_mode = IEEE80211_SMPS_OFF; | ||
| 1330 | break; | ||
| 1331 | } | ||
| 1332 | } | ||
| 1333 | |||
| 1334 | if (smps_mode == local->smps_mode) | ||
| 1335 | return; | ||
| 1336 | |||
| 1337 | set: | ||
| 1338 | local->smps_mode = smps_mode; | ||
| 1339 | /* changed flag is auto-detected for this */ | ||
| 1340 | ieee80211_hw_config(local, 0); | ||
| 1341 | } | ||
| 1342 | |||
| 1343 | static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) | ||
| 1344 | { | ||
| 1345 | int i; | ||
| 1346 | |||
| 1347 | for (i = 0; i < n_ids; i++) | ||
| 1348 | if (ids[i] == id) | ||
| 1349 | return true; | ||
| 1350 | return false; | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | /** | ||
| 1354 | * ieee80211_ie_split - split an IE buffer according to ordering | ||
| 1355 | * | ||
| 1356 | * @ies: the IE buffer | ||
| 1357 | * @ielen: the length of the IE buffer | ||
| 1358 | * @ids: an array with element IDs that are allowed before | ||
| 1359 | * the split | ||
| 1360 | * @n_ids: the size of the element ID array | ||
| 1361 | * @offset: offset where to start splitting in the buffer | ||
| 1362 | * | ||
| 1363 | * This function splits an IE buffer by updating the @offset | ||
| 1364 | * variable to point to the location where the buffer should be | ||
| 1365 | * split. | ||
| 1366 | * | ||
| 1367 | * It assumes that the given IE buffer is well-formed, this | ||
| 1368 | * has to be guaranteed by the caller! | ||
| 1369 | * | ||
| 1370 | * It also assumes that the IEs in the buffer are ordered | ||
| 1371 | * correctly, if not the result of using this function will not | ||
| 1372 | * be ordered correctly either, i.e. it does no reordering. | ||
| 1373 | * | ||
| 1374 | * The function returns the offset where the next part of the | ||
| 1375 | * buffer starts, which may be @ielen if the entire (remainder) | ||
| 1376 | * of the buffer should be used. | ||
| 1377 | */ | ||
| 1378 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, | ||
| 1379 | const u8 *ids, int n_ids, size_t offset) | ||
| 1380 | { | ||
| 1381 | size_t pos = offset; | ||
| 1382 | |||
| 1383 | while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) | ||
| 1384 | pos += 2 + ies[pos + 1]; | ||
| 1385 | |||
| 1386 | return pos; | ||
| 1387 | } | ||
| 1388 | |||
| 1389 | size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) | ||
| 1390 | { | ||
| 1391 | size_t pos = offset; | ||
| 1392 | |||
| 1393 | while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC) | ||
| 1394 | pos += 2 + ies[pos + 1]; | ||
| 1395 | |||
| 1396 | return pos; | ||
| 1397 | } | ||
