diff options
Diffstat (limited to 'net/mac80211/util.c')
| -rw-r--r-- | net/mac80211/util.c | 319 |
1 files changed, 245 insertions, 74 deletions
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 3848140313f5..c453226f06b2 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> |
| @@ -480,8 +479,8 @@ void ieee80211_iterate_active_interfaces( | |||
| 480 | case NL80211_IFTYPE_MESH_POINT: | 479 | case NL80211_IFTYPE_MESH_POINT: |
| 481 | break; | 480 | break; |
| 482 | } | 481 | } |
| 483 | if (netif_running(sdata->dev)) | 482 | if (ieee80211_sdata_running(sdata)) |
| 484 | iterator(data, sdata->dev->dev_addr, | 483 | iterator(data, sdata->vif.addr, |
| 485 | &sdata->vif); | 484 | &sdata->vif); |
| 486 | } | 485 | } |
| 487 | 486 | ||
| @@ -514,8 +513,8 @@ void ieee80211_iterate_active_interfaces_atomic( | |||
| 514 | case NL80211_IFTYPE_MESH_POINT: | 513 | case NL80211_IFTYPE_MESH_POINT: |
| 515 | break; | 514 | break; |
| 516 | } | 515 | } |
| 517 | if (netif_running(sdata->dev)) | 516 | if (ieee80211_sdata_running(sdata)) |
| 518 | iterator(data, sdata->dev->dev_addr, | 517 | iterator(data, sdata->vif.addr, |
| 519 | &sdata->vif); | 518 | &sdata->vif); |
| 520 | } | 519 | } |
| 521 | 520 | ||
| @@ -793,6 +792,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata) | |||
| 793 | break; | 792 | break; |
| 794 | } | 793 | } |
| 795 | 794 | ||
| 795 | qparam.uapsd = false; | ||
| 796 | |||
| 796 | drv_conf_tx(local, queue, &qparam); | 797 | drv_conf_tx(local, queue, &qparam); |
| 797 | } | 798 | } |
| 798 | } | 799 | } |
| @@ -860,7 +861,7 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, | |||
| 860 | sizeof(*mgmt) + 6 + extra_len); | 861 | sizeof(*mgmt) + 6 + extra_len); |
| 861 | if (!skb) { | 862 | if (!skb) { |
| 862 | printk(KERN_DEBUG "%s: failed to allocate buffer for auth " | 863 | printk(KERN_DEBUG "%s: failed to allocate buffer for auth " |
| 863 | "frame\n", sdata->dev->name); | 864 | "frame\n", sdata->name); |
| 864 | return; | 865 | return; |
| 865 | } | 866 | } |
| 866 | skb_reserve(skb, local->hw.extra_tx_headroom); | 867 | skb_reserve(skb, local->hw.extra_tx_headroom); |
| @@ -870,7 +871,7 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, | |||
| 870 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | 871 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | |
| 871 | IEEE80211_STYPE_AUTH); | 872 | IEEE80211_STYPE_AUTH); |
| 872 | memcpy(mgmt->da, bssid, ETH_ALEN); | 873 | memcpy(mgmt->da, bssid, ETH_ALEN); |
| 873 | memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); | 874 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); |
| 874 | memcpy(mgmt->bssid, bssid, ETH_ALEN); | 875 | memcpy(mgmt->bssid, bssid, ETH_ALEN); |
| 875 | mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg); | 876 | mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg); |
| 876 | mgmt->u.auth.auth_transaction = cpu_to_le16(transaction); | 877 | mgmt->u.auth.auth_transaction = cpu_to_le16(transaction); |
| @@ -893,43 +894,87 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | |||
| 893 | enum ieee80211_band band) | 894 | enum ieee80211_band band) |
| 894 | { | 895 | { |
| 895 | struct ieee80211_supported_band *sband; | 896 | struct ieee80211_supported_band *sband; |
| 896 | u8 *pos, *supp_rates_len, *esupp_rates_len = NULL; | 897 | u8 *pos; |
| 897 | int i; | 898 | size_t offset = 0, noffset; |
| 899 | int supp_rates_len, i; | ||
| 898 | 900 | ||
| 899 | sband = local->hw.wiphy->bands[band]; | 901 | sband = local->hw.wiphy->bands[band]; |
| 900 | 902 | ||
| 901 | pos = buffer; | 903 | pos = buffer; |
| 902 | 904 | ||
| 905 | supp_rates_len = min_t(int, sband->n_bitrates, 8); | ||
| 906 | |||
| 903 | *pos++ = WLAN_EID_SUPP_RATES; | 907 | *pos++ = WLAN_EID_SUPP_RATES; |
| 904 | supp_rates_len = pos; | 908 | *pos++ = supp_rates_len; |
| 905 | *pos++ = 0; | 909 | |
| 906 | 910 | for (i = 0; i < supp_rates_len; i++) { | |
| 907 | for (i = 0; i < sband->n_bitrates; i++) { | 911 | int rate = sband->bitrates[i].bitrate; |
| 908 | struct ieee80211_rate *rate = &sband->bitrates[i]; | 912 | *pos++ = (u8) (rate / 5); |
| 909 | 913 | } | |
| 910 | if (esupp_rates_len) { | 914 | |
| 911 | *esupp_rates_len += 1; | 915 | /* insert "request information" if in custom IEs */ |
| 912 | } else if (*supp_rates_len == 8) { | 916 | if (ie && ie_len) { |
| 913 | *pos++ = WLAN_EID_EXT_SUPP_RATES; | 917 | static const u8 before_extrates[] = { |
| 914 | esupp_rates_len = pos; | 918 | WLAN_EID_SSID, |
| 915 | *pos++ = 1; | 919 | WLAN_EID_SUPP_RATES, |
| 916 | } else | 920 | WLAN_EID_REQUEST, |
| 917 | *supp_rates_len += 1; | 921 | }; |
| 922 | noffset = ieee80211_ie_split(ie, ie_len, | ||
| 923 | before_extrates, | ||
| 924 | ARRAY_SIZE(before_extrates), | ||
| 925 | offset); | ||
| 926 | memcpy(pos, ie + offset, noffset - offset); | ||
| 927 | pos += noffset - offset; | ||
| 928 | offset = noffset; | ||
| 929 | } | ||
| 930 | |||
| 931 | if (sband->n_bitrates > i) { | ||
| 932 | *pos++ = WLAN_EID_EXT_SUPP_RATES; | ||
| 933 | *pos++ = sband->n_bitrates - i; | ||
| 934 | |||
| 935 | for (; i < sband->n_bitrates; i++) { | ||
| 936 | int rate = sband->bitrates[i].bitrate; | ||
| 937 | *pos++ = (u8) (rate / 5); | ||
| 938 | } | ||
| 939 | } | ||
| 918 | 940 | ||
| 919 | *pos++ = rate->bitrate / 5; | 941 | /* insert custom IEs that go before HT */ |
| 942 | if (ie && ie_len) { | ||
| 943 | static const u8 before_ht[] = { | ||
| 944 | WLAN_EID_SSID, | ||
| 945 | WLAN_EID_SUPP_RATES, | ||
| 946 | WLAN_EID_REQUEST, | ||
| 947 | WLAN_EID_EXT_SUPP_RATES, | ||
| 948 | WLAN_EID_DS_PARAMS, | ||
| 949 | WLAN_EID_SUPPORTED_REGULATORY_CLASSES, | ||
| 950 | }; | ||
| 951 | noffset = ieee80211_ie_split(ie, ie_len, | ||
| 952 | before_ht, ARRAY_SIZE(before_ht), | ||
| 953 | offset); | ||
| 954 | memcpy(pos, ie + offset, noffset - offset); | ||
| 955 | pos += noffset - offset; | ||
| 956 | offset = noffset; | ||
| 920 | } | 957 | } |
| 921 | 958 | ||
| 922 | if (sband->ht_cap.ht_supported) { | 959 | if (sband->ht_cap.ht_supported) { |
| 923 | __le16 tmp = cpu_to_le16(sband->ht_cap.cap); | 960 | u16 cap = sband->ht_cap.cap; |
| 961 | __le16 tmp; | ||
| 962 | |||
| 963 | if (ieee80211_disable_40mhz_24ghz && | ||
| 964 | sband->band == IEEE80211_BAND_2GHZ) { | ||
| 965 | cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; | ||
| 966 | cap &= ~IEEE80211_HT_CAP_SGI_40; | ||
| 967 | } | ||
| 924 | 968 | ||
| 925 | *pos++ = WLAN_EID_HT_CAPABILITY; | 969 | *pos++ = WLAN_EID_HT_CAPABILITY; |
| 926 | *pos++ = sizeof(struct ieee80211_ht_cap); | 970 | *pos++ = sizeof(struct ieee80211_ht_cap); |
| 927 | memset(pos, 0, sizeof(struct ieee80211_ht_cap)); | 971 | memset(pos, 0, sizeof(struct ieee80211_ht_cap)); |
| 972 | tmp = cpu_to_le16(cap); | ||
| 928 | memcpy(pos, &tmp, sizeof(u16)); | 973 | memcpy(pos, &tmp, sizeof(u16)); |
| 929 | pos += sizeof(u16); | 974 | pos += sizeof(u16); |
| 930 | /* TODO: needs a define here for << 2 */ | ||
| 931 | *pos++ = sband->ht_cap.ampdu_factor | | 975 | *pos++ = sband->ht_cap.ampdu_factor | |
| 932 | (sband->ht_cap.ampdu_density << 2); | 976 | (sband->ht_cap.ampdu_density << |
| 977 | IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); | ||
| 933 | memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); | 978 | memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); |
| 934 | pos += sizeof(sband->ht_cap.mcs); | 979 | pos += sizeof(sband->ht_cap.mcs); |
| 935 | pos += 2 + 4 + 1; /* ext info, BF cap, antsel */ | 980 | pos += 2 + 4 + 1; /* ext info, BF cap, antsel */ |
| @@ -940,9 +985,11 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | |||
| 940 | * that calculates local->scan_ies_len. | 985 | * that calculates local->scan_ies_len. |
| 941 | */ | 986 | */ |
| 942 | 987 | ||
| 943 | if (ie) { | 988 | /* add any remaining custom IEs */ |
| 944 | memcpy(pos, ie, ie_len); | 989 | if (ie && ie_len) { |
| 945 | pos += ie_len; | 990 | noffset = ie_len; |
| 991 | memcpy(pos, ie + offset, noffset - offset); | ||
| 992 | pos += noffset - offset; | ||
| 946 | } | 993 | } |
| 947 | 994 | ||
| 948 | return pos - buffer; | 995 | return pos - buffer; |
| @@ -955,40 +1002,33 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, | |||
| 955 | struct ieee80211_local *local = sdata->local; | 1002 | struct ieee80211_local *local = sdata->local; |
| 956 | struct sk_buff *skb; | 1003 | struct sk_buff *skb; |
| 957 | struct ieee80211_mgmt *mgmt; | 1004 | struct ieee80211_mgmt *mgmt; |
| 958 | u8 *pos; | 1005 | size_t buf_len; |
| 959 | 1006 | u8 *buf; | |
| 960 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + | 1007 | |
| 961 | ie_len); | 1008 | /* FIXME: come up with a proper value */ |
| 962 | if (!skb) { | 1009 | buf = kmalloc(200 + ie_len, GFP_KERNEL); |
| 963 | printk(KERN_DEBUG "%s: failed to allocate buffer for probe " | 1010 | if (!buf) { |
| 964 | "request\n", sdata->dev->name); | 1011 | printk(KERN_DEBUG "%s: failed to allocate temporary IE " |
| 1012 | "buffer\n", sdata->name); | ||
| 965 | return; | 1013 | return; |
| 966 | } | 1014 | } |
| 967 | skb_reserve(skb, local->hw.extra_tx_headroom); | ||
| 968 | 1015 | ||
| 969 | mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); | 1016 | buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, |
| 970 | memset(mgmt, 0, 24); | 1017 | local->hw.conf.channel->band); |
| 971 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | 1018 | |
| 972 | IEEE80211_STYPE_PROBE_REQ); | 1019 | skb = ieee80211_probereq_get(&local->hw, &sdata->vif, |
| 973 | memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); | 1020 | ssid, ssid_len, |
| 1021 | buf, buf_len); | ||
| 1022 | |||
| 974 | if (dst) { | 1023 | if (dst) { |
| 1024 | mgmt = (struct ieee80211_mgmt *) skb->data; | ||
| 975 | memcpy(mgmt->da, dst, ETH_ALEN); | 1025 | memcpy(mgmt->da, dst, ETH_ALEN); |
| 976 | memcpy(mgmt->bssid, dst, ETH_ALEN); | 1026 | memcpy(mgmt->bssid, dst, ETH_ALEN); |
| 977 | } else { | ||
| 978 | memset(mgmt->da, 0xff, ETH_ALEN); | ||
| 979 | memset(mgmt->bssid, 0xff, ETH_ALEN); | ||
| 980 | } | 1027 | } |
| 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 | 1028 | ||
| 990 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; | 1029 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; |
| 991 | ieee80211_tx_skb(sdata, skb); | 1030 | ieee80211_tx_skb(sdata, skb); |
| 1031 | kfree(buf); | ||
| 992 | } | 1032 | } |
| 993 | 1033 | ||
| 994 | u32 ieee80211_sta_get_rates(struct ieee80211_local *local, | 1034 | u32 ieee80211_sta_get_rates(struct ieee80211_local *local, |
| @@ -1032,18 +1072,16 @@ void ieee80211_stop_device(struct ieee80211_local *local) | |||
| 1032 | ieee80211_led_radio(local, false); | 1072 | ieee80211_led_radio(local, false); |
| 1033 | 1073 | ||
| 1034 | cancel_work_sync(&local->reconfig_filter); | 1074 | cancel_work_sync(&local->reconfig_filter); |
| 1035 | drv_stop(local); | ||
| 1036 | 1075 | ||
| 1037 | flush_workqueue(local->workqueue); | 1076 | flush_workqueue(local->workqueue); |
| 1077 | drv_stop(local); | ||
| 1038 | } | 1078 | } |
| 1039 | 1079 | ||
| 1040 | int ieee80211_reconfig(struct ieee80211_local *local) | 1080 | int ieee80211_reconfig(struct ieee80211_local *local) |
| 1041 | { | 1081 | { |
| 1042 | struct ieee80211_hw *hw = &local->hw; | 1082 | struct ieee80211_hw *hw = &local->hw; |
| 1043 | struct ieee80211_sub_if_data *sdata; | 1083 | struct ieee80211_sub_if_data *sdata; |
| 1044 | struct ieee80211_if_init_conf conf; | ||
| 1045 | struct sta_info *sta; | 1084 | struct sta_info *sta; |
| 1046 | unsigned long flags; | ||
| 1047 | int res; | 1085 | int res; |
| 1048 | 1086 | ||
| 1049 | if (local->suspended) | 1087 | if (local->suspended) |
| @@ -1061,7 +1099,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
| 1061 | if (res) { | 1099 | if (res) { |
| 1062 | WARN(local->suspended, "Harware became unavailable " | 1100 | WARN(local->suspended, "Harware became unavailable " |
| 1063 | "upon resume. This is could be a software issue" | 1101 | "upon resume. This is could be a software issue" |
| 1064 | "prior to suspend or a harware issue\n"); | 1102 | "prior to suspend or a hardware issue\n"); |
| 1065 | return res; | 1103 | return res; |
| 1066 | } | 1104 | } |
| 1067 | 1105 | ||
| @@ -1072,29 +1110,24 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
| 1072 | list_for_each_entry(sdata, &local->interfaces, list) { | 1110 | list_for_each_entry(sdata, &local->interfaces, list) { |
| 1073 | if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && | 1111 | if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && |
| 1074 | sdata->vif.type != NL80211_IFTYPE_MONITOR && | 1112 | sdata->vif.type != NL80211_IFTYPE_MONITOR && |
| 1075 | netif_running(sdata->dev)) { | 1113 | ieee80211_sdata_running(sdata)) |
| 1076 | conf.vif = &sdata->vif; | 1114 | 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 | } | 1115 | } |
| 1082 | 1116 | ||
| 1083 | /* add STAs back */ | 1117 | /* add STAs back */ |
| 1084 | if (local->ops->sta_notify) { | 1118 | mutex_lock(&local->sta_mtx); |
| 1085 | spin_lock_irqsave(&local->sta_lock, flags); | 1119 | list_for_each_entry(sta, &local->sta_list, list) { |
| 1086 | list_for_each_entry(sta, &local->sta_list, list) { | 1120 | if (sta->uploaded) { |
| 1087 | sdata = sta->sdata; | 1121 | sdata = sta->sdata; |
| 1088 | if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | 1122 | if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) |
| 1089 | sdata = container_of(sdata->bss, | 1123 | sdata = container_of(sdata->bss, |
| 1090 | struct ieee80211_sub_if_data, | 1124 | struct ieee80211_sub_if_data, |
| 1091 | u.ap); | 1125 | u.ap); |
| 1092 | 1126 | ||
| 1093 | drv_sta_notify(local, &sdata->vif, STA_NOTIFY_ADD, | 1127 | WARN_ON(drv_sta_add(local, sdata, &sta->sta)); |
| 1094 | &sta->sta); | ||
| 1095 | } | 1128 | } |
| 1096 | spin_unlock_irqrestore(&local->sta_lock, flags); | ||
| 1097 | } | 1129 | } |
| 1130 | mutex_unlock(&local->sta_mtx); | ||
| 1098 | 1131 | ||
| 1099 | /* Clear Suspend state so that ADDBA requests can be processed */ | 1132 | /* Clear Suspend state so that ADDBA requests can be processed */ |
| 1100 | 1133 | ||
| @@ -1119,7 +1152,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
| 1119 | /* Finally also reconfigure all the BSS information */ | 1152 | /* Finally also reconfigure all the BSS information */ |
| 1120 | list_for_each_entry(sdata, &local->interfaces, list) { | 1153 | list_for_each_entry(sdata, &local->interfaces, list) { |
| 1121 | u32 changed = ~0; | 1154 | u32 changed = ~0; |
| 1122 | if (!netif_running(sdata->dev)) | 1155 | if (!ieee80211_sdata_running(sdata)) |
| 1123 | continue; | 1156 | continue; |
| 1124 | switch (sdata->vif.type) { | 1157 | switch (sdata->vif.type) { |
| 1125 | case NL80211_IFTYPE_STATION: | 1158 | case NL80211_IFTYPE_STATION: |
| @@ -1145,9 +1178,17 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
| 1145 | } | 1178 | } |
| 1146 | } | 1179 | } |
| 1147 | 1180 | ||
| 1181 | rcu_read_lock(); | ||
| 1182 | if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { | ||
| 1183 | list_for_each_entry_rcu(sta, &local->sta_list, list) { | ||
| 1184 | ieee80211_sta_tear_down_BA_sessions(sta); | ||
| 1185 | } | ||
| 1186 | } | ||
| 1187 | rcu_read_unlock(); | ||
| 1188 | |||
| 1148 | /* add back keys */ | 1189 | /* add back keys */ |
| 1149 | list_for_each_entry(sdata, &local->interfaces, list) | 1190 | list_for_each_entry(sdata, &local->interfaces, list) |
| 1150 | if (netif_running(sdata->dev)) | 1191 | if (ieee80211_sdata_running(sdata)) |
| 1151 | ieee80211_enable_keys(sdata); | 1192 | ieee80211_enable_keys(sdata); |
| 1152 | 1193 | ||
| 1153 | ieee80211_wake_queues_by_reason(hw, | 1194 | ieee80211_wake_queues_by_reason(hw, |
| @@ -1184,13 +1225,143 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
| 1184 | 1225 | ||
| 1185 | add_timer(&local->sta_cleanup); | 1226 | add_timer(&local->sta_cleanup); |
| 1186 | 1227 | ||
| 1187 | spin_lock_irqsave(&local->sta_lock, flags); | 1228 | mutex_lock(&local->sta_mtx); |
| 1188 | list_for_each_entry(sta, &local->sta_list, list) | 1229 | list_for_each_entry(sta, &local->sta_list, list) |
| 1189 | mesh_plink_restart(sta); | 1230 | mesh_plink_restart(sta); |
| 1190 | spin_unlock_irqrestore(&local->sta_lock, flags); | 1231 | mutex_unlock(&local->sta_mtx); |
| 1191 | #else | 1232 | #else |
| 1192 | WARN_ON(1); | 1233 | WARN_ON(1); |
| 1193 | #endif | 1234 | #endif |
| 1194 | return 0; | 1235 | return 0; |
| 1195 | } | 1236 | } |
| 1196 | 1237 | ||
| 1238 | static int check_mgd_smps(struct ieee80211_if_managed *ifmgd, | ||
| 1239 | enum ieee80211_smps_mode *smps_mode) | ||
| 1240 | { | ||
| 1241 | if (ifmgd->associated) { | ||
| 1242 | *smps_mode = ifmgd->ap_smps; | ||
| 1243 | |||
| 1244 | if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) { | ||
| 1245 | if (ifmgd->powersave) | ||
| 1246 | *smps_mode = IEEE80211_SMPS_DYNAMIC; | ||
| 1247 | else | ||
| 1248 | *smps_mode = IEEE80211_SMPS_OFF; | ||
| 1249 | } | ||
| 1250 | |||
| 1251 | return 1; | ||
| 1252 | } | ||
| 1253 | |||
| 1254 | return 0; | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | /* must hold iflist_mtx */ | ||
| 1258 | void ieee80211_recalc_smps(struct ieee80211_local *local, | ||
| 1259 | struct ieee80211_sub_if_data *forsdata) | ||
| 1260 | { | ||
| 1261 | struct ieee80211_sub_if_data *sdata; | ||
| 1262 | enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF; | ||
| 1263 | int count = 0; | ||
| 1264 | |||
| 1265 | if (forsdata) | ||
| 1266 | WARN_ON(!mutex_is_locked(&forsdata->u.mgd.mtx)); | ||
| 1267 | |||
| 1268 | WARN_ON(!mutex_is_locked(&local->iflist_mtx)); | ||
| 1269 | |||
| 1270 | /* | ||
| 1271 | * This function could be improved to handle multiple | ||
| 1272 | * interfaces better, but right now it makes any | ||
| 1273 | * non-station interfaces force SM PS to be turned | ||
| 1274 | * off. If there are multiple station interfaces it | ||
| 1275 | * could also use the best possible mode, e.g. if | ||
| 1276 | * one is in static and the other in dynamic then | ||
| 1277 | * dynamic is ok. | ||
| 1278 | */ | ||
| 1279 | |||
| 1280 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
| 1281 | if (!netif_running(sdata->dev)) | ||
| 1282 | continue; | ||
| 1283 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
| 1284 | goto set; | ||
| 1285 | if (sdata != forsdata) { | ||
| 1286 | /* | ||
| 1287 | * This nested is ok -- we are holding the iflist_mtx | ||
| 1288 | * so can't get here twice or so. But it's required | ||
| 1289 | * since normally we acquire it first and then the | ||
| 1290 | * iflist_mtx. | ||
| 1291 | */ | ||
| 1292 | mutex_lock_nested(&sdata->u.mgd.mtx, SINGLE_DEPTH_NESTING); | ||
| 1293 | count += check_mgd_smps(&sdata->u.mgd, &smps_mode); | ||
| 1294 | mutex_unlock(&sdata->u.mgd.mtx); | ||
| 1295 | } else | ||
| 1296 | count += check_mgd_smps(&sdata->u.mgd, &smps_mode); | ||
| 1297 | |||
| 1298 | if (count > 1) { | ||
| 1299 | smps_mode = IEEE80211_SMPS_OFF; | ||
| 1300 | break; | ||
| 1301 | } | ||
| 1302 | } | ||
| 1303 | |||
| 1304 | if (smps_mode == local->smps_mode) | ||
| 1305 | return; | ||
| 1306 | |||
| 1307 | set: | ||
| 1308 | local->smps_mode = smps_mode; | ||
| 1309 | /* changed flag is auto-detected for this */ | ||
| 1310 | ieee80211_hw_config(local, 0); | ||
| 1311 | } | ||
| 1312 | |||
| 1313 | static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) | ||
| 1314 | { | ||
| 1315 | int i; | ||
| 1316 | |||
| 1317 | for (i = 0; i < n_ids; i++) | ||
| 1318 | if (ids[i] == id) | ||
| 1319 | return true; | ||
| 1320 | return false; | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | /** | ||
| 1324 | * ieee80211_ie_split - split an IE buffer according to ordering | ||
| 1325 | * | ||
| 1326 | * @ies: the IE buffer | ||
| 1327 | * @ielen: the length of the IE buffer | ||
| 1328 | * @ids: an array with element IDs that are allowed before | ||
| 1329 | * the split | ||
| 1330 | * @n_ids: the size of the element ID array | ||
| 1331 | * @offset: offset where to start splitting in the buffer | ||
| 1332 | * | ||
| 1333 | * This function splits an IE buffer by updating the @offset | ||
| 1334 | * variable to point to the location where the buffer should be | ||
| 1335 | * split. | ||
| 1336 | * | ||
| 1337 | * It assumes that the given IE buffer is well-formed, this | ||
| 1338 | * has to be guaranteed by the caller! | ||
| 1339 | * | ||
| 1340 | * It also assumes that the IEs in the buffer are ordered | ||
| 1341 | * correctly, if not the result of using this function will not | ||
| 1342 | * be ordered correctly either, i.e. it does no reordering. | ||
| 1343 | * | ||
| 1344 | * The function returns the offset where the next part of the | ||
| 1345 | * buffer starts, which may be @ielen if the entire (remainder) | ||
| 1346 | * of the buffer should be used. | ||
| 1347 | */ | ||
| 1348 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, | ||
| 1349 | const u8 *ids, int n_ids, size_t offset) | ||
| 1350 | { | ||
| 1351 | size_t pos = offset; | ||
| 1352 | |||
| 1353 | while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) | ||
| 1354 | pos += 2 + ies[pos + 1]; | ||
| 1355 | |||
| 1356 | return pos; | ||
| 1357 | } | ||
| 1358 | |||
| 1359 | size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) | ||
| 1360 | { | ||
| 1361 | size_t pos = offset; | ||
| 1362 | |||
| 1363 | while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC) | ||
| 1364 | pos += 2 + ies[pos + 1]; | ||
| 1365 | |||
| 1366 | return pos; | ||
| 1367 | } | ||
