diff options
Diffstat (limited to 'net/mac80211/util.c')
-rw-r--r-- | net/mac80211/util.c | 297 |
1 files changed, 231 insertions, 66 deletions
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 3848140313f5..ca170b417da6 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; | ||
918 | 934 | ||
919 | *pos++ = rate->bitrate / 5; | 935 | for (; i < sband->n_bitrates; i++) { |
936 | int rate = sband->bitrates[i].bitrate; | ||
937 | *pos++ = (u8) (rate / 5); | ||
938 | } | ||
939 | } | ||
940 | |||
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,16 +1072,15 @@ 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; | 1085 | unsigned long flags; |
1047 | int res; | 1086 | int res; |
@@ -1061,7 +1100,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1061 | if (res) { | 1100 | if (res) { |
1062 | WARN(local->suspended, "Harware became unavailable " | 1101 | WARN(local->suspended, "Harware became unavailable " |
1063 | "upon resume. This is could be a software issue" | 1102 | "upon resume. This is could be a software issue" |
1064 | "prior to suspend or a harware issue\n"); | 1103 | "prior to suspend or a hardware issue\n"); |
1065 | return res; | 1104 | return res; |
1066 | } | 1105 | } |
1067 | 1106 | ||
@@ -1072,12 +1111,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1072 | list_for_each_entry(sdata, &local->interfaces, list) { | 1111 | list_for_each_entry(sdata, &local->interfaces, list) { |
1073 | if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && | 1112 | if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && |
1074 | sdata->vif.type != NL80211_IFTYPE_MONITOR && | 1113 | sdata->vif.type != NL80211_IFTYPE_MONITOR && |
1075 | netif_running(sdata->dev)) { | 1114 | ieee80211_sdata_running(sdata)) |
1076 | conf.vif = &sdata->vif; | 1115 | 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 | } | 1116 | } |
1082 | 1117 | ||
1083 | /* add STAs back */ | 1118 | /* add STAs back */ |
@@ -1090,7 +1125,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1090 | struct ieee80211_sub_if_data, | 1125 | struct ieee80211_sub_if_data, |
1091 | u.ap); | 1126 | u.ap); |
1092 | 1127 | ||
1093 | drv_sta_notify(local, &sdata->vif, STA_NOTIFY_ADD, | 1128 | drv_sta_notify(local, sdata, STA_NOTIFY_ADD, |
1094 | &sta->sta); | 1129 | &sta->sta); |
1095 | } | 1130 | } |
1096 | spin_unlock_irqrestore(&local->sta_lock, flags); | 1131 | spin_unlock_irqrestore(&local->sta_lock, flags); |
@@ -1119,7 +1154,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1119 | /* Finally also reconfigure all the BSS information */ | 1154 | /* Finally also reconfigure all the BSS information */ |
1120 | list_for_each_entry(sdata, &local->interfaces, list) { | 1155 | list_for_each_entry(sdata, &local->interfaces, list) { |
1121 | u32 changed = ~0; | 1156 | u32 changed = ~0; |
1122 | if (!netif_running(sdata->dev)) | 1157 | if (!ieee80211_sdata_running(sdata)) |
1123 | continue; | 1158 | continue; |
1124 | switch (sdata->vif.type) { | 1159 | switch (sdata->vif.type) { |
1125 | case NL80211_IFTYPE_STATION: | 1160 | case NL80211_IFTYPE_STATION: |
@@ -1147,7 +1182,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1147 | 1182 | ||
1148 | /* add back keys */ | 1183 | /* add back keys */ |
1149 | list_for_each_entry(sdata, &local->interfaces, list) | 1184 | list_for_each_entry(sdata, &local->interfaces, list) |
1150 | if (netif_running(sdata->dev)) | 1185 | if (ieee80211_sdata_running(sdata)) |
1151 | ieee80211_enable_keys(sdata); | 1186 | ieee80211_enable_keys(sdata); |
1152 | 1187 | ||
1153 | ieee80211_wake_queues_by_reason(hw, | 1188 | ieee80211_wake_queues_by_reason(hw, |
@@ -1194,3 +1229,133 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1194 | return 0; | 1229 | return 0; |
1195 | } | 1230 | } |
1196 | 1231 | ||
1232 | static int check_mgd_smps(struct ieee80211_if_managed *ifmgd, | ||
1233 | enum ieee80211_smps_mode *smps_mode) | ||
1234 | { | ||
1235 | if (ifmgd->associated) { | ||
1236 | *smps_mode = ifmgd->ap_smps; | ||
1237 | |||
1238 | if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) { | ||
1239 | if (ifmgd->powersave) | ||
1240 | *smps_mode = IEEE80211_SMPS_DYNAMIC; | ||
1241 | else | ||
1242 | *smps_mode = IEEE80211_SMPS_OFF; | ||
1243 | } | ||
1244 | |||
1245 | return 1; | ||
1246 | } | ||
1247 | |||
1248 | return 0; | ||
1249 | } | ||
1250 | |||
1251 | /* must hold iflist_mtx */ | ||
1252 | void ieee80211_recalc_smps(struct ieee80211_local *local, | ||
1253 | struct ieee80211_sub_if_data *forsdata) | ||
1254 | { | ||
1255 | struct ieee80211_sub_if_data *sdata; | ||
1256 | enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF; | ||
1257 | int count = 0; | ||
1258 | |||
1259 | if (forsdata) | ||
1260 | WARN_ON(!mutex_is_locked(&forsdata->u.mgd.mtx)); | ||
1261 | |||
1262 | WARN_ON(!mutex_is_locked(&local->iflist_mtx)); | ||
1263 | |||
1264 | /* | ||
1265 | * This function could be improved to handle multiple | ||
1266 | * interfaces better, but right now it makes any | ||
1267 | * non-station interfaces force SM PS to be turned | ||
1268 | * off. If there are multiple station interfaces it | ||
1269 | * could also use the best possible mode, e.g. if | ||
1270 | * one is in static and the other in dynamic then | ||
1271 | * dynamic is ok. | ||
1272 | */ | ||
1273 | |||
1274 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
1275 | if (!netif_running(sdata->dev)) | ||
1276 | continue; | ||
1277 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
1278 | goto set; | ||
1279 | if (sdata != forsdata) { | ||
1280 | /* | ||
1281 | * This nested is ok -- we are holding the iflist_mtx | ||
1282 | * so can't get here twice or so. But it's required | ||
1283 | * since normally we acquire it first and then the | ||
1284 | * iflist_mtx. | ||
1285 | */ | ||
1286 | mutex_lock_nested(&sdata->u.mgd.mtx, SINGLE_DEPTH_NESTING); | ||
1287 | count += check_mgd_smps(&sdata->u.mgd, &smps_mode); | ||
1288 | mutex_unlock(&sdata->u.mgd.mtx); | ||
1289 | } else | ||
1290 | count += check_mgd_smps(&sdata->u.mgd, &smps_mode); | ||
1291 | |||
1292 | if (count > 1) { | ||
1293 | smps_mode = IEEE80211_SMPS_OFF; | ||
1294 | break; | ||
1295 | } | ||
1296 | } | ||
1297 | |||
1298 | if (smps_mode == local->smps_mode) | ||
1299 | return; | ||
1300 | |||
1301 | set: | ||
1302 | local->smps_mode = smps_mode; | ||
1303 | /* changed flag is auto-detected for this */ | ||
1304 | ieee80211_hw_config(local, 0); | ||
1305 | } | ||
1306 | |||
1307 | static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) | ||
1308 | { | ||
1309 | int i; | ||
1310 | |||
1311 | for (i = 0; i < n_ids; i++) | ||
1312 | if (ids[i] == id) | ||
1313 | return true; | ||
1314 | return false; | ||
1315 | } | ||
1316 | |||
1317 | /** | ||
1318 | * ieee80211_ie_split - split an IE buffer according to ordering | ||
1319 | * | ||
1320 | * @ies: the IE buffer | ||
1321 | * @ielen: the length of the IE buffer | ||
1322 | * @ids: an array with element IDs that are allowed before | ||
1323 | * the split | ||
1324 | * @n_ids: the size of the element ID array | ||
1325 | * @offset: offset where to start splitting in the buffer | ||
1326 | * | ||
1327 | * This function splits an IE buffer by updating the @offset | ||
1328 | * variable to point to the location where the buffer should be | ||
1329 | * split. | ||
1330 | * | ||
1331 | * It assumes that the given IE buffer is well-formed, this | ||
1332 | * has to be guaranteed by the caller! | ||
1333 | * | ||
1334 | * It also assumes that the IEs in the buffer are ordered | ||
1335 | * correctly, if not the result of using this function will not | ||
1336 | * be ordered correctly either, i.e. it does no reordering. | ||
1337 | * | ||
1338 | * The function returns the offset where the next part of the | ||
1339 | * buffer starts, which may be @ielen if the entire (remainder) | ||
1340 | * of the buffer should be used. | ||
1341 | */ | ||
1342 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, | ||
1343 | const u8 *ids, int n_ids, size_t offset) | ||
1344 | { | ||
1345 | size_t pos = offset; | ||
1346 | |||
1347 | while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) | ||
1348 | pos += 2 + ies[pos + 1]; | ||
1349 | |||
1350 | return pos; | ||
1351 | } | ||
1352 | |||
1353 | size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) | ||
1354 | { | ||
1355 | size_t pos = offset; | ||
1356 | |||
1357 | while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC) | ||
1358 | pos += 2 + ies[pos + 1]; | ||
1359 | |||
1360 | return pos; | ||
1361 | } | ||