aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/util.c')
-rw-r--r--net/mac80211/util.c319
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
994u32 ieee80211_sta_get_rates(struct ieee80211_local *local, 1034u32 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
1040int ieee80211_reconfig(struct ieee80211_local *local) 1080int 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
1238static 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 */
1258void 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
1313static 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 */
1348size_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
1359size_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}