summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorsam <sam@FreeBSD.org>2008-04-20 20:35:46 +0000
committersam <sam@FreeBSD.org>2008-04-20 20:35:46 +0000
commit3569e353ca63336d80ab0143dd9669b0b9e6b123 (patch)
treebc7985c57e7ecfa1ac03e48c406a25430dba634b /sys
parent682b4ae9be70192e298129ada878af3486683aaf (diff)
downloadFreeBSD-src-3569e353ca63336d80ab0143dd9669b0b9e6b123.zip
FreeBSD-src-3569e353ca63336d80ab0143dd9669b0b9e6b123.tar.gz
Multi-bss (aka vap) support for 802.11 devices.
Note this includes changes to all drivers and moves some device firmware loading to use firmware(9) and a separate module (e.g. ral). Also there no longer are separate wlan_scan* modules; this functionality is now bundled into the wlan module. Supported by: Hobnob and Marvell Reviewed by: many Obtained from: Atheros (some bits)
Diffstat (limited to 'sys')
-rw-r--r--sys/amd64/conf/GENERIC2
-rw-r--r--sys/arm/conf/AVILA2
-rw-r--r--sys/arm/conf/HL2002
-rw-r--r--sys/arm/conf/KB920X2
-rw-r--r--sys/conf/NOTES2
-rw-r--r--sys/conf/files16
-rw-r--r--sys/conf/options10
-rw-r--r--sys/contrib/dev/ral/LICENSE16
-rw-r--r--sys/contrib/dev/ral/Makefile36
-rw-r--r--sys/contrib/dev/ral/rt2561.fw.uu202
-rw-r--r--sys/contrib/dev/ral/rt2561s.fw.uu202
-rw-r--r--sys/contrib/dev/ral/rt2661.fw.uu202
-rw-r--r--sys/contrib/dev/ral/rt2661_ucode.h (renamed from sys/dev/ral/rt2661_ucode.h)758
-rw-r--r--sys/contrib/dev/ral/rt2860.fw.uu202
-rw-r--r--sys/dev/ath/ah_osdep.c2
-rw-r--r--sys/dev/ath/ah_osdep.h2
-rw-r--r--sys/dev/ath/ath_rate/amrr/amrr.c146
-rw-r--r--sys/dev/ath/ath_rate/amrr/amrr.h4
-rw-r--r--sys/dev/ath/ath_rate/onoe/onoe.c117
-rw-r--r--sys/dev/ath/ath_rate/onoe/onoe.h5
-rw-r--r--sys/dev/ath/ath_rate/sample/sample.c132
-rw-r--r--sys/dev/ath/ath_rate/sample/sample.h3
-rw-r--r--sys/dev/ath/if_ath.c2175
-rw-r--r--sys/dev/ath/if_ath_pci.c2
-rw-r--r--sys/dev/ath/if_athioctl.h2
-rw-r--r--sys/dev/ath/if_athrate.h4
-rw-r--r--sys/dev/ath/if_athvar.h118
-rw-r--r--sys/dev/if_ndis/if_ndis.c549
-rw-r--r--sys/dev/if_ndis/if_ndisvar.h9
-rw-r--r--sys/dev/ipw/if_ipw.c1029
-rw-r--r--sys/dev/ipw/if_ipwvar.h52
-rw-r--r--sys/dev/iwi/if_iwi.c1072
-rw-r--r--sys/dev/iwi/if_iwivar.h65
-rw-r--r--sys/dev/malo/if_malo.c597
-rw-r--r--sys/dev/malo/if_malo.h14
-rw-r--r--sys/dev/ral/if_ral_pci.c4
-rw-r--r--sys/dev/ral/if_ralrate.c192
-rw-r--r--sys/dev/ral/rt2560.c1221
-rw-r--r--sys/dev/ral/rt2560reg.h2
-rw-r--r--sys/dev/ral/rt2560var.h46
-rw-r--r--sys/dev/ral/rt2661.c1249
-rw-r--r--sys/dev/ral/rt2661var.h48
-rw-r--r--sys/dev/usb/if_rum.c978
-rw-r--r--sys/dev/usb/if_rumvar.h45
-rw-r--r--sys/dev/usb/if_ural.c949
-rw-r--r--sys/dev/usb/if_uralvar.h45
-rw-r--r--sys/dev/usb/if_zyd.c559
-rw-r--r--sys/dev/usb/if_zydreg.h21
-rw-r--r--sys/dev/wi/if_wavelan_ieee.h3
-rw-r--r--sys/dev/wi/if_wi.c2815
-rw-r--r--sys/dev/wi/if_wi_pccard.c63
-rw-r--r--sys/dev/wi/if_wi_pci.c7
-rw-r--r--sys/dev/wi/if_wivar.h102
-rw-r--r--sys/dev/wi/spectrum24t_cf.h4327
-rw-r--r--sys/dev/wpi/if_wpi.c747
-rw-r--r--sys/dev/wpi/if_wpivar.h46
-rw-r--r--sys/i386/conf/GENERIC2
-rw-r--r--sys/mips/conf/IDT2
-rw-r--r--sys/modules/Makefile3
-rw-r--r--sys/modules/ath_rate_amrr/Makefile8
-rw-r--r--sys/modules/ath_rate_onoe/Makefile8
-rw-r--r--sys/modules/ath_rate_sample/Makefile8
-rw-r--r--sys/modules/malo/Makefile6
-rw-r--r--sys/modules/ral/Makefile6
-rw-r--r--sys/modules/ralfw/Makefile5
-rw-r--r--sys/modules/ralfw/Makefile.inc15
-rw-r--r--sys/modules/ralfw/rt2561/Makefile5
-rw-r--r--sys/modules/ralfw/rt2561s/Makefile5
-rw-r--r--sys/modules/ralfw/rt2661/Makefile6
-rw-r--r--sys/modules/wlan/Makefile16
-rw-r--r--sys/modules/wlan_acl/Makefile6
-rw-r--r--sys/modules/wlan_amrr/Makefile6
-rw-r--r--sys/modules/wlan_ccmp/Makefile6
-rw-r--r--sys/modules/wlan_rssadapt/Makefile14
-rw-r--r--sys/modules/wlan_scan_ap/Makefile8
-rw-r--r--sys/modules/wlan_scan_sta/Makefile8
-rw-r--r--sys/modules/wlan_tkip/Makefile6
-rw-r--r--sys/modules/wlan_wep/Makefile6
-rw-r--r--sys/modules/wlan_xauth/Makefile6
-rw-r--r--sys/net80211/_ieee80211.h119
-rw-r--r--sys/net80211/ieee80211.c1002
-rw-r--r--sys/net80211/ieee80211.h19
-rw-r--r--sys/net80211/ieee80211_acl.c116
-rw-r--r--sys/net80211/ieee80211_adhoc.c877
-rw-r--r--sys/net80211/ieee80211_adhoc.h35
-rw-r--r--sys/net80211/ieee80211_amrr.c178
-rw-r--r--sys/net80211/ieee80211_amrr.h53
-rw-r--r--sys/net80211/ieee80211_crypto.c256
-rw-r--r--sys/net80211/ieee80211_crypto.h93
-rw-r--r--sys/net80211/ieee80211_crypto_ccmp.c53
-rw-r--r--sys/net80211/ieee80211_crypto_none.c37
-rw-r--r--sys/net80211/ieee80211_crypto_tkip.c98
-rw-r--r--sys/net80211/ieee80211_crypto_wep.c48
-rw-r--r--sys/net80211/ieee80211_ddb.c789
-rw-r--r--sys/net80211/ieee80211_dfs.c372
-rw-r--r--sys/net80211/ieee80211_dfs.h57
-rw-r--r--sys/net80211/ieee80211_freebsd.c397
-rw-r--r--sys/net80211/ieee80211_freebsd.h318
-rw-r--r--sys/net80211/ieee80211_hostap.c2236
-rw-r--r--sys/net80211/ieee80211_hostap.h35
-rw-r--r--sys/net80211/ieee80211_ht.c591
-rw-r--r--sys/net80211/ieee80211_ht.h84
-rw-r--r--sys/net80211/ieee80211_input.c3250
-rw-r--r--sys/net80211/ieee80211_input.h156
-rw-r--r--sys/net80211/ieee80211_ioctl.c2511
-rw-r--r--sys/net80211/ieee80211_ioctl.h253
-rw-r--r--sys/net80211/ieee80211_monitor.c136
-rw-r--r--sys/net80211/ieee80211_monitor.h35
-rw-r--r--sys/net80211/ieee80211_node.c1605
-rw-r--r--sys/net80211/ieee80211_node.h257
-rw-r--r--sys/net80211/ieee80211_output.c2026
-rw-r--r--sys/net80211/ieee80211_phy.c472
-rw-r--r--sys/net80211/ieee80211_phy.h149
-rw-r--r--sys/net80211/ieee80211_power.c239
-rw-r--r--sys/net80211/ieee80211_power.h8
-rw-r--r--sys/net80211/ieee80211_proto.c1251
-rw-r--r--sys/net80211/ieee80211_proto.h190
-rw-r--r--sys/net80211/ieee80211_regdomain.c429
-rw-r--r--sys/net80211/ieee80211_regdomain.h96
-rw-r--r--sys/net80211/ieee80211_rssadapt.c273
-rw-r--r--sys/net80211/ieee80211_rssadapt.h (renamed from sys/dev/ral/if_ralrate.h)101
-rw-r--r--sys/net80211/ieee80211_scan.c703
-rw-r--r--sys/net80211/ieee80211_scan.h147
-rw-r--r--sys/net80211/ieee80211_scan_ap.c408
-rw-r--r--sys/net80211/ieee80211_scan_sta.c837
-rw-r--r--sys/net80211/ieee80211_sta.c1647
-rw-r--r--sys/net80211/ieee80211_sta.h36
-rw-r--r--sys/net80211/ieee80211_var.h511
-rw-r--r--sys/net80211/ieee80211_wds.c865
-rw-r--r--sys/net80211/ieee80211_wds.h39
-rw-r--r--sys/net80211/ieee80211_xauth.c37
-rw-r--r--sys/pc98/conf/GENERIC2
-rw-r--r--sys/sparc64/conf/GENERIC2
133 files changed, 26321 insertions, 21596 deletions
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC
index 8a3566a..4d0a1d7 100644
--- a/sys/amd64/conf/GENERIC
+++ b/sys/amd64/conf/GENERIC
@@ -239,8 +239,6 @@ device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
device wlan_amrr # AMRR transmit rate control algorithm
-device wlan_scan_ap # 802.11 AP mode scanning
-device wlan_scan_sta # 802.11 STA mode scanning
device an # Aironet 4500/4800 802.11 wireless NICs.
device ath # Atheros pci/cardbus NIC's
device ath_hal # Atheros HAL (Hardware Access Layer)
diff --git a/sys/arm/conf/AVILA b/sys/arm/conf/AVILA
index 53b2681..c436eab 100644
--- a/sys/arm/conf/AVILA
+++ b/sys/arm/conf/AVILA
@@ -130,8 +130,6 @@ device wlan # 802.11 support
device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
-device wlan_scan_sta
-device wlan_scan_ap
device wlan_xauth
device ath # Atheros pci/cardbus NIC's
device ath_hal # Atheros HAL (Hardware Access Layer)
diff --git a/sys/arm/conf/HL200 b/sys/arm/conf/HL200
index fc0529c..225a12b 100644
--- a/sys/arm/conf/HL200
+++ b/sys/arm/conf/HL200
@@ -147,5 +147,3 @@ device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
device wlan_amrr # AMRR transmit rate control algorithm
-device wlan_scan_ap # 802.11 AP mode scanning
-device wlan_scan_sta # 802.11 STA mode scanning
diff --git a/sys/arm/conf/KB920X b/sys/arm/conf/KB920X
index 60e3da1..54f631d 100644
--- a/sys/arm/conf/KB920X
+++ b/sys/arm/conf/KB920X
@@ -135,5 +135,3 @@ device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
device wlan_amrr # AMRR transmit rate control algorithm
-device wlan_scan_ap # 802.11 AP mode scanning
-device wlan_scan_sta # 802.11 STA mode scanning
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index 34564a5..5aef1fd 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -768,8 +768,6 @@ device wlan_tkip #802.11 TKIP support
device wlan_xauth #802.11 external authenticator support
device wlan_acl #802.11 MAC ACL support
device wlan_amrr #AMRR transmit rate control algorithm
-device wlan_scan_ap #802.11 AP mode scanning
-device wlan_scan_sta #802.11 STA mode scanning
device token #Generic TokenRing
device fddi #Generic FDDI
device arcnet #Generic Arcnet
diff --git a/sys/conf/files b/sys/conf/files
index 125d2ba..fd41e73 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -770,6 +770,9 @@ dev/le/lance.c optional le
dev/led/led.c standard
dev/lge/if_lge.c optional lge
dev/lmc/if_lmc.c optional lmc
+dev/malo/if_malo.c optional malo
+dev/malo/if_malohal.c optional malo
+dev/malo/if_malo_pci.c optional malo pci
dev/mc146818/mc146818.c optional mc146818
dev/mca/mca_bus.c optional mca
dev/mcd/mcd.c optional mcd isa nowerror
@@ -918,7 +921,6 @@ dev/puc/pucdata.c optional puc pci
dev/quicc/quicc_core.c optional quicc
dev/ral/rt2560.c optional ral
dev/ral/rt2661.c optional ral
-dev/ral/if_ralrate.c optional ral
dev/ral/if_ral_pci.c optional ral pci
dev/random/harvest.c standard
dev/random/hash.c optional random
@@ -1677,24 +1679,32 @@ net/zlib.c optional crypto | geom_uzip | ipsec | \
mxge | ppp_deflate | netgraph_deflate
net80211/ieee80211.c optional wlan
net80211/ieee80211_acl.c optional wlan_acl
+net80211/ieee80211_adhoc.c optional wlan
net80211/ieee80211_amrr.c optional wlan_amrr
net80211/ieee80211_crypto.c optional wlan
net80211/ieee80211_crypto_ccmp.c optional wlan_ccmp
net80211/ieee80211_crypto_none.c optional wlan
net80211/ieee80211_crypto_tkip.c optional wlan_tkip
net80211/ieee80211_crypto_wep.c optional wlan_wep
+net80211/ieee80211_ddb.c optional wlan ddb
+net80211/ieee80211_dfs.c optional wlan
net80211/ieee80211_freebsd.c optional wlan
+net80211/ieee80211_hostap.c optional wlan
net80211/ieee80211_ht.c optional wlan
net80211/ieee80211_input.c optional wlan
net80211/ieee80211_ioctl.c optional wlan
+net80211/ieee80211_monitor.c optional wlan
net80211/ieee80211_node.c optional wlan
net80211/ieee80211_output.c optional wlan
+net80211/ieee80211_phy.c optional wlan
net80211/ieee80211_power.c optional wlan
net80211/ieee80211_proto.c optional wlan
net80211/ieee80211_regdomain.c optional wlan
+net80211/ieee80211_rssadapt.c optional wlan_rssadapt
net80211/ieee80211_scan.c optional wlan
-net80211/ieee80211_scan_ap.c optional wlan_scan_ap
-net80211/ieee80211_scan_sta.c optional wlan_scan_sta
+net80211/ieee80211_scan_sta.c optional wlan
+net80211/ieee80211_sta.c optional wlan
+net80211/ieee80211_wds.c optional wlan
net80211/ieee80211_xauth.c optional wlan_xauth
netatalk/aarp.c optional netatalk
netatalk/at_control.c optional netatalk
diff --git a/sys/conf/options b/sys/conf/options
index 09acf41..eec0e9d 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -734,6 +734,11 @@ ATH_RXBUF opt_ath.h
ATH_DIAGAPI opt_ath.h
ATH_TX99_DIAG opt_ath.h
+# options for the Marvell 8335 wireless driver
+MALO_DEBUG opt_malo.h
+MALO_TXBUF opt_malo.h
+MALO_RXBUF opt_malo.h
+
# dcons options
DCONS_BUF_SIZE opt_dcons.h
DCONS_POLL_HZ opt_dcons.h
@@ -763,5 +768,10 @@ XFS
# Interrupt filtering
INTR_FILTER
+# 802.11 support layer
+IEEE80211_DEBUG opt_wlan.h
+IEEE80211_DEBUG_REFCNT opt_wlan.h
+IEEE80211_AMPDU_AGE opt_wlan.h
+
#Disable code to dispatch tcp offloading
TCP_OFFLOAD_DISABLE opt_inet.h
diff --git a/sys/contrib/dev/ral/LICENSE b/sys/contrib/dev/ral/LICENSE
new file mode 100644
index 0000000..5acf6db
--- /dev/null
+++ b/sys/contrib/dev/ral/LICENSE
@@ -0,0 +1,16 @@
+$FreeBSD$
+
+Copyright (c) 2005-2008, Ralink Technology Corp.
+ Paul Lin <paul_lin@ralinktech.com.tw>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/sys/contrib/dev/ral/Makefile b/sys/contrib/dev/ral/Makefile
new file mode 100644
index 0000000..8560f42
--- /dev/null
+++ b/sys/contrib/dev/ral/Makefile
@@ -0,0 +1,36 @@
+# $FreeBSD$
+
+FILES= rt2561s.fw.uu rt2561.fw.uu rt2661.fw.uu rt2860.fw.uu
+
+rt2561s.fw.uu: rt2661_ucode.h LICENSE
+ (cat rt2661_ucode.h; \
+ echo 'int main(void) { \
+ write(1, rt2561s_ucode, sizeof(rt2561s_ucode)); return 0; \
+ }') | ${CC} -o build -x c -
+ (sed 's/^/# /' LICENSE; ./build | uuencode rt2561s.fw) > ${.TARGET}
+
+rt2561.fw.uu: rt2661_ucode.h LICENSE
+ (cat rt2661_ucode.h; \
+ echo 'int main(void) { \
+ write(1, rt2561_ucode, sizeof(rt2561_ucode)); return 0; \
+ }') | ${CC} -o build -x c -
+ (sed 's/^/# /' LICENSE; ./build | uuencode rt2561.fw) > ${.TARGET}
+
+rt2661.fw.uu: rt2661_ucode.h LICENSE
+ (cat rt2661_ucode.h; \
+ echo 'int main(void) { \
+ write(1, rt2661_ucode, sizeof(rt2661_ucode)); return 0; \
+ }') | ${CC} -o build -x c -
+ (sed 's/^/# /' LICENSE; ./build | uuencode rt2661.fw) > ${.TARGET}
+
+rt2860.fw.uu: rt2661_ucode.h LICENSE
+ (cat rt2661_ucode.h; \
+ echo 'int main(void) { \
+ write(1, rt2860_ucode, sizeof(rt2860_ucode)); return 0; \
+ }') | ${CC} -o build -x c -
+ (sed 's/^/# /' LICENSE; ./build | uuencode rt2860.fw) > ${.TARGET}
+
+clean:
+ rm -f build build.c ${FILES}
+
+.include <bsd.prog.mk>
diff --git a/sys/contrib/dev/ral/rt2561.fw.uu b/sys/contrib/dev/ral/rt2561.fw.uu
new file mode 100644
index 0000000..9e511d4
--- /dev/null
+++ b/sys/contrib/dev/ral/rt2561.fw.uu
@@ -0,0 +1,202 @@
+# $FreeBSD$
+#
+# Copyright (c) 2005-2008, Ralink Technology Corp.
+# Paul Lin <paul_lin@ralinktech.com.tw>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+begin 644 rt2561.fw
+M`AP2`A/+PHPB(@`"%@_"K\*-=8R4=8J3TJ\B`AC:$AOH0`,"`AZ0(0+@]2V0
+M``/@$@@E`+```,X!`%X0`&\1`/(@`4TA`7`B`80P`8\Q`=50`9]1`?)2`@9@
+M```"%)``"N`@Y0,P!P/2""(2%Z4BD"$`X/41Y1'$,U3@)"'U@N0T(?6#X$2`
+M\.41Q#-4X"0L]8+D-"'U@^41\,0S5.`D+?6"Y#0A]8/E+?#DD"$#\"(2$3&0
+M(0#@]3%@!1(;BH`#$AL]Y)`A`_"O+1(<8B)U,?^0`0#@5/?PD`$!X%3^\%0^
+M\.20``OP\)`A`_"O+1(<8B)^*W^`?0,2!`Z0-,W@(./YD"$4$@@!D#3`$@@-
+MD"$8$@@!D#3($@@-D"$<$@@!D#3$$@@-D#3,=`'PH^!$!/"0`0'@1`'P1$#P
+MD``+X$00\.20(0/PKRT2'&(BD`$`X%3W\)`!`>!4_O!4O_"0``O@5._PY)`A
+M`_"O+1(<8B)^*W^`?0,2!`[DD"$#\*\M$AQB(M(%A2TCY)`A`_`B$AITP@#D
+MD"$#\*\M$AQB(H4M)9``"^!4^__PY)``!_"0``IT!/#DD``(\)`A`."0``GP
+MD``'=''P[T0$D``+\.20(0/P(I`A`.#_5!_U,*/@]2>/)A((D.20(0/PKRT2
+M'&(BD"$`X/4L$A@3Y)`A`_"O+1(<8B(2&5/DD"$#\*\M$AQB(N20(0/PKRT2
+M'&(BCA6/%LKMRLGKR3`*!']*@`)_0LOOR^K#E`10`H`!PT`$RT0@RX46@H45
+M@^OPH^3PA1:"A16#HZ/E&O#E&846@H45@Z.CH_#E%B0$]8+D-17U@W0/\.46
+M)`7U@N0U%?6#Y/#E%B0&]8+D-17U@^3PY18D!_6"Y#45]8-T$/#JD!J<D_OJ
+M9`%@".ID`F`#N@,$RT0(R^46)`CU@N0U%?6#Z_#E%B05]8+D-17U@W3_\.46
+M)!;U@N0U%?6#Z?#E%B0)]8+D-17U@W0$\"4:]1KD-1GU&>K#E`1``P(#UNI@
+M`[H!'^HD`?WD,_SE&JX9>`/#,\XSSMCY_Q('EHX9CQH"`[;J)/_]Y#3__'X`
+M?PL2!X3,[LS-[\WE&L3X5`_(:/_E&<14\$C^$@>6C!N-'.HD__WD-/_\?@!_
+M"Q('A,SNS,WOS>4:Q/A4#\AH_^49Q%3P2/X2!Y:.&8\:Y1Q%&V`(!1KE&G`"
+M!1GJ)/_]Y#3__'X`?P,2!X33Y1R?Y1N>4!CE'$4;8!*Z`P_E%B0)]8+D-17U
+M@^!$@/#E%B0*]8+D-17U@^4:\.49_^46)`OU@N0U%?6#[_"`+>4:5#__Y18D
+M"O6"Y#45]8/O\.4:KAEX!L[#$\X3V/G_Y18D"_6"Y#45]8/O\(46@H45@^!$
+M`?`BCA*/$XT4Y12BX9()Y30D&?6"Y#4S]8/@_>4T)!KU@N0U,_6#X/NB"9(*
+M=1D`=1H:$@(?,`D$?\B``G_HY1,D&/6"Y#42]8/O\.4Q8`1_`H`"?P'E$R09
+M]8+D-1+U@^_PY30D&?6"Y#4S]8/@_WT:?``2#F3E$R0:]8+D-1+U@^_PY1,D
+M&_6"Y#42]8/N\.4Q8&#E$R0<_^0U$O[E-"02_>0U,_QU&Q%[!A(4J^43)"+_
+MY#42_GPP?1!U&Q%[!A(4J^43)"C_Y#42_GPP?0AU&Q%[!A(4J^4T)!CU@N0U
+M,_6#X/_E$R0M]8+D-1+U@^_P@#_E$R0<_^0U$OY\,'T0=1L1>P82%*OE$R0B
+M_^0U$OY\,'T(=1L1>P82%*OE$R0H_^0U$OY\,'T0=1L1>P82%*OE$R0N]8+D
+M-1+U@^3PY1,D+_6"Y#42]8/D\.4T)!'U@N0U,_6#X/_#$__E$R0P]8+D-1+U
+M@^_P,`E!Y1,D,/6"Y#42]8/@_^4Q8`1^`(`"?A#O3O#E,6`&?@!_`(`/Y10P
+MX`9^`'__@`1^`'\`Y1,D,?6"Y#42]8/O\"+E$R0P]8+D-1+U@^!$0/#E%##@
+M#^4T)!#U@N0U,_6#X/^``G\`Y1,D,?6"Y#42]8/O\"+E-"01]8+D-3/U@^`P
+MYSOE-"0<]8+D-3/U@^!E*W`#=2O_Y30D'?6"Y#4S]8/@_Q(<8GXB?Q`2&'R.
+M,X\TD"(NX/ZCX(XS]33#(M(*Y30D&_6"Y#4S]8/@<#J%-(*%,X/`@\""X/ZC
+MX/^%-(*%,X.CH^#\H^#]P^^=_^Z<_M""T(/PH^_PTY0`[F2`E(!0`P(')X#&
+MA32"A3.#X/ZCX,/N9("4@%`#`@<G$AQ!A32"A3.#X/RCX/W#G^YD@/CL9("8
+M0""%-(*%,X/`@\""HZ/@_J/@_^V?_^R>T(+0@_"C[_#""H4T@H4S@^#^H^#_
+MY30D$/6"Y#4S]8/@_</OG?WNE`#\$A9:4"R%-(*%,X/`@\""X/ZCX/^%-(*%
+M,X.CH^#\H^#]P^^=_^Z<T(+0@_"C[_#""B`*`P(&-WXB?Q`2&'R.,X\TCX*.
+M@^#^H^#3E`#N9("4@$`-?B)_$*TTK#,2%0Z`&A(;JX4T@H4S@^Z/\!('ZWXB
+M?S"M-*PS$A4.D"(NX/ZCX/]E-'`#[F4S<`+3(HXSCS3#(N^-\*2H\,^,\*0H
+MSHWPI"[^(KP`"[X`*>^-\(3_K?`BY,SX=?`([R__[C/^[#/\[IWLF$`%_.Z=
+M_@_5\.GDSOTB[?CU\.Z$(-(<_JWP=?`([R__[3/]0`>84`;5\/(BPYC]#]7P
+MZB+%\/BCX"CPQ?#XY8(5@G`"%8/@./`BX/RCX/VCX/ZCX/\B[/"C[?"C[O"C
+M[_`BI"6"]8+E\#6#]8,BT(/0@OCDDW`2=`&3<`VCHY/X=`&3]8*(@^1S=`*3
+M:&#OHZ.C@-^*@XF"Y'/D_Y`PC.3P[Y`;49-$@)`PC?"C=`'PH^3PD#",X/YT
+M-B_XQN[&H^#^[Y`;49-$@&Y@`1\/[\.4"4#((@````#E,!(()0BQ``D?`0F'
+M`@H;`PIO!`JV!0LI!@N8!P``"]#"`1(`!I`P.N#U$N4F(.4(D#28X%3^\"*0
+M-)C@1`'PY28PY@_E)S#F!5,2_8`20Q("@`WE)S#F!4,2`H`#4Q+]Y28PYP_E
+M)S#G!5,2]X`20Q((@`WE)S#G!4,2"(`#4Q+W0Q(!0Q($D#`ZY1+P(L(!$@`&
+MD#`ZX/42Y28@Y0B0-)C@5/[P(I`TF.!$`?#E)E3`8!SE)S#F!5,2_8`#0Q("
+MY2<PYP53$O>`'T,2"(`:Y2<PY@5#$@*``U,2_>4G,.<%0Q((@`-3$O=#$@%#
+M$@20,#KE$O`BP@$2``:0,#K@]1)#$@%#$@3E)C#E7)`TF.!$`?#E)E3`8!SE
+M)S#F!5,2_8`#0Q("Y2<PYP53$O>`,$,2"(`KY2<PY@5#$@*``U,2_>4G,.<%
+M0Q((@`-3$O?E)_14'_^0,#3@5.!/\.3U+)`P.N42\(`5D#28X%3^\.4G]%0?
+M_Y`P-.!4X$_PD#`UX/424Q+@Y1+P(L(!$@`&D#`ZX/42Y28PY3R0-)C@1`'P
+MY2<PY@53$OV``T,2`N4G,.<%4Q+W@`-#$@CE)E3`8`A#$@%#$@2`!E,2_D,2
+M!)`P.N42\"*0-)C@5/[P(L(!$@`&D#`ZX/42Y2<PY@5#$@*``U,2_>4G,.<%
+M0Q((@`-3$O?E)E3`8`A3$OY3$ON`!D,2`4,2!)`TF.!$`?"0,#KE$O`B(`(3
+M$AP>KRE^`!(<=*\U?@`2''O2`I`P.N#U$N4F(.4-P@$2``:0-)C@5/[P(I`T
+MF.!$`?#E)E3`8"S"`1(`!N4G,.8%4Q+]@`-#$@+E)S#G!5,2]X`#0Q((0Q(!
+M0Q($D#`ZY1+P(C`!`P(+T!(5P-(!(L(!$@`&Y28@Y0F0-)C@5/[P@%60-)C@
+M1`'PY28PY@_E)S#F!5,2_8`20Q("@`WE)S#F!4,2`H`#4Q+]Y28PYP_E)S#G
+M!5,2]X`20Q((@`WE)S#G!4,2"(`#4Q+W0Q(!4Q+[D#`ZY1+PD#`ZX/42(N4F
+M,.4L(`,AT@,2'!YU-09U*0FO*7X`$AQTD#`ZX/424Q+^0Q($Y1+PD#28X$0!
+M\"*0-)C@5/[P(N4Q9`%P01(:U$`#`@U/$AME4"!^*W^`?0,2!`Y_`1(9>$`)
+MT@D2#^[D]2\B$@U0=2\!(G\!$AEX4`1U+P(BT@D2#^[D]2\B$AH=4%$2&\N0
+M,/3@]2I^,'_LH^#]Y/L2&2OD__X2'#:0``IT`O"0``O@1`+_\/V0`05T(/"0
+M`0;@1"#P[52_D``+\)`TS.!$`?"CX$0!\*/@1`'PT@02&CI00Q(:5WXP?^!\
+M,'WL=1L1>P82%*N0,/7@=?`@I/^N\!(<-I``"^!4_?_P_>20``3PD`$&X%3?
+M\)``"G1`\$V0``OPP@02&OY0.!(:5WXP?^!\''V"=1L2>P82%*N0``1T`O"0
+M``KPY/_^$APVD``+X%3]\.20``3PD`$&X%3?\,($$ALH4"42&E=_`A(9>)`!
+M!.!4?_"0``O@5/W_\.20``3P[U2_D``+\,($$AK44"T2&E=^,'_@?!Q]@G4;
+M$GL&$A2KD``$=`+PD``*\)`!!N!4W_"0``O@5+_PP@0BD#3-X/D@X_CE*_1@
+M9I`TP!((`84T@H4S@W7P(.4K$@@9Y8(D!/6"Y#6#]8,2"`V0-,@2"`&%-(*%
+M,X-U\"#E*Q((&>6")`CU@N0U@_6#$@@-D#30$@@!A32"A3.#=?`@Y2L2"!GE
+M@B0,]8+D-8/U@Q((#>4T)/#_Y3,TWO[O>`7.PQ/.$]CY]2N%-(*%,X-U\"`2
+M"!GE@B0$]8+D-8/U@Q((`9`TP!((#84T@H4S@W7P(.4K$@@9Y8(D"/6"Y#6#
+M]8,2"`&0-,@2"`V%-(*%,X-U\"#E*Q((&>6")`SU@N0U@_6#$@@!D#3$$@@-
+MD`$!X$1`\)`!`.!$"/#I1`20-,WPD#3,X$0!\*/@1`'PH^!$`?`BCQ6,%HT7
+MY17#E`105N45E`!`!GH`>V"`!'H`>\#E%\3X5`_(:/_E%L14\$C^Y160&HZ3
+M_7P`$@>6[RO[[CKZY1?$^%0/R&C_Y1;$5/!(_N45D_U\`!('ENU,8&,+NP`!
+M"H!<>@![&N47KA9X`L,SSC/.V/DD"__D/O[E%9`:CI/]?``2!Y;O>`+#,\XS
+MSMCY*_ON.OKE%ZX6>`+#,\XSSMCY)`O_Y#[^Y160&HZ3_7P`$@>6[4Q@!W0$
+M*_OD.OK/Z\_.ZLXBY2X48!T48#T48%T4<`,"#]<D!&`#`@_M(`T#`@_M=2X!
+M(I``"N#_,.4#1"#PY4!%/V`#`@_M=2X"$AF;$AMXKR@2&J@BD`$#X/\PYW;O
+M1("0`0/P$@A1$AG>$AN[=2X#KR)^`!(<*B+E0$4_<"$2%$$2&W@2&;X2&[L2
+M'`0P#0MU+@&O,GX`$APJ(N3U+B*0``K@_S#E+$0@\!(401(;>!(9OA(;NQ(<
+M!'4N!"+E0$4_<!`P#0IU+@&O,OX2'"HBY/4N(I``!'0"\)``"O`P"3+E-$4S
+M<`+#(H4T@H4S@\"#P(+@_J/@_X4T@H4S@Z.CX/RCX/W#[YW_[IS0@M"#\*/O
+M\.4T13-P`L,B$@7M4/.0``K@(.4#,`=!Y31%,W`"PR*%-(*%,X/`@\""X/ZC
+MX/^%-(*%,X.CH^#\H^#]P^^=_^Z<T(+0@_"C[_#E-$4S<`+#(A(%[5#S@+6%
+M-(*%,X/@_J/@_Q(6ZM,B$AK^0`42&M101'XP?^!\''V"=1L2>P82%*N0``1T
+M`O"0``KPY/_^$APVD``+X%2_\%1___#DD##I\.]4_9``"_#DD``$\-()$@_N
+MY/4O$AL34$A^,'_@?!Q]@G4;$GL&$A2KD``$=`+PD``*\.3__A(<-I``"^!4
+MO_!4_?#DD``$\/\2&7A0!'4O!R*0`03@5'_PT@D2#^[D]2\BPJ_D]2_UB'6H
+M#W6)$?6X]>AUD`]U,?]U*_^0(B[PH_"0(D[PH_#"!<((P@#"!\($D``*=/_P
+MD``+=`'PD`$#=/_PY)`!!/"0`05T__#DD`$&\)``!/"0,.AT$/"0`0?PD`$(
+M!/"0`0ET2/"0`0IT?_"0`0)T'_"0`0!T%/"0`0%T(/"0``#@1(#P=4D`=4H!
+MP@'2KR(2&M10+1(82)`!!N!4W_!^,'_@?!Q]@G4;$GL&$A2KD``$=`+PD``*
+M\-()$@_NY/4O(A(;*%!0$AA(D``+X%3]\.20``3PD`$#=(#PD`$$X$2`\'\"
+M$AEX4`1U+P4B?C!_X'P<?8)U&Q)[!A(4JY``!'0"\)``"O#2"1(/[I`!!.!4
+M?_#D]2\BD#`P=`+P=1$'=1+0D#`PX##@#N42%1)P`A41Y1)%$7#KY1)%$7`2
+M$AITD"$`X&`'D#28X$0$\,,BY)`T6/"0-#)T'_!U$0=U$M"0-('@9`-@#N42
+M%1)P`A41Y1)%$7#JY1)%$7`2$AITD"$`X&`'D#28X$0$\,,BD#28X$0$\.20
+M``'PTR*0,#K@]1`2'%=0)N4G,.8%4Q#]@`-#$`+E)S#G!5,0]X`#0Q`(4Q#^
+M0Q`$D#`ZY1#P$AQ,4$B0`0/@]1!4'&`^Y1!4X_"CX/40\.4G,.8%0Q`"@`-3
+M$/WE)S#G!4,0"(`#4Q#W4Q#^0Q`$D#`ZY1#PKRE^`!(<=*\U?@`2''LB$AJ_
+M4'(2'$&%-(*%,X/@_*/@PY_U$NR>]1'3Y1*4`.419("4@$`&KA&O$H`$?@!_
+M`(X1CQ+E-"00]8+D-3/U@^##E1+U$N25$?41PV2`E(!0!>3U$?42Y30D$O_D
+M-3/^K1)[`1(9*Y`!!70@\)`!!N!$(/!U+P,BP.#`\,"#P(+`T'70",*OD"(N
+MX/ZCX(XS]33E+R7@))OU@N0T&_6#Y)/^=`&3RN[*^1((2Q(;]E`"T@<2%RP2
+M`!X2&NE0!:\E$AQB,`4;Y2]P%R`$%!(23I(`P@72"Z(`Y#/U%*\C$A:BTJ_0
+MT-""T(/0\-#@,I`IH.!P8Y`PC.3PHW3"\*-T`?"CY/"0,(S@_Y`IH/"0,(SD
+M\*-TQ?"C=`'PH^3PD#",X/^0*:'PD#",Y/"C=,3PHW0!\*/D\)`PC.#_D"FB
+M\)`PC.3PHW3#\*-T`?"CY/"0,(S@D"FC\"*.%H\7C!B-&>3_[\.;4%/E&S#@
+M$N]\`"49_>PU&(V"]8/@]1R`'^4;,.$3[WP`)1G][#48C8+U@^23]1R`!^49
+M+_CF]1SE&S#D#^47+_6"Y#46]8/E'/"`!N47+_BF'`^`J"*,$XT4[R0>]8+D
+M/O6#X/RCX$Q@0>\D'O6"Y#[U@^#\H^#U@HR#X/RCX/V%%(*%$X/@^J/@^]/M
+MF^ID@/CL9("80!/O)![U@N0^]8/@_*/@SNS._X"OK12L$Q(8KR(2&K]03^4T
+M)!+_Y#4S_N4T)!#U@N0U,_6#X/WD^Q(9*^4T)!#U@N0U,_6#X/]^`!(<-I``
+M"G1`\)``"^!$0/_PD``*=(#P3Y``"_"0,.ET`?!U+P8BD#`ZX/_E)S#F$C`,
+M!N]4]?Z`!.]$"O[/[L^`$#`,!N]$"OZ`!.]4]?[/[L_/5/[/ST0$SY`P.N_P
+M,`P)?PA^`!(<=(`'?R)^`1(<=+(,(L#@P/#`@\""P-!UT`C"K\*,PHW3Y4J4
+M`.5)E`!`".5*%4IP`A5)T^5,E`#E2Y0`0`CE3!5,<`(52Q(`#M*,TJ_0T-""
+MT(/0\-#@,L/OE`3N9("4@$`,T^V4!.QD@)2`4`$BP^^4_.YD@)1_0`S3[93\
+M[&2`E']0`2+3[Y0$[F2`E(!0#</ME/SL9("4?T`"TR+#(N3^[_1@070$+O6"
+MY#0A]8/@M/\C=`0N]8+D-"'U@^_P,`L-=`@N]8+D-"'U@^44\)```G0!\"*^
+M`PJ0``)T`?#D_H#"#H"_(HX3CQ02'$'#[Y44_^Z5$\WOS?S3[90`[&2`E(!`
+M!<[LSH`$?@!_`<SNS.R0``7PD``&[_"0``1T4?"0``O@1`+P(C`'/.4O<#C"
+M!Y`B+N#^H^".$?42D").X/ZCX/^0(B[N\*/O\)`B3N41\*/E$O".,X\T,`@%
+M$A>EP@C""1(/[B)_@'XIY/W\CX*.@^#[=$4M^,;KQG0$+_6"Y#[U@^#[=$$M
+M^,;KQG0(+__D/OX-O0`!#.UD!$QPSR*0(0#@Q#-4X"00]8+D-"&K@OH2&ZN+
+M@HJ#[H_P$@?K?B)_,,WKS<SJS!(5#N20(0/PKRT2'&(BD```=`[P````Y/`2
+M$3$2'(B0-)C@1`'PY3"T!0H2'$Q0#1(5P(`(Y3"T!P,2$M02#RJ`Y(#^(N3_
+MY3`D_G`LY/[NPY4L4!)T`<CNR`B``L,SV/S/3\\.@.B0,#3@5.#^Y2=4'V_T
+MSD[.[O`BD#3.X$0"\)`TS>!4_O"0-,W@(./YD`$1X%0B_[\B`].``<-0\)`!
+M`.!4]_"0`0'@5+_P(N\D'O6"Y#[U@^#\H^#[RNS*)![U@N0\]8/@_*/@_>\D
+M'O6"Y#[U@^SPH^WPSNK.S^O/(N\D'O6"Y#[U@^#ZH^#[[20>]8+D//6#ZO"C
+MZ_#O)![U@N0^]8/L\*/M\"+`X,#0PJ_"CL*/T^5`E`#E/Y0`0`WE0!5`<`(5
+M/Q(<:]*.TJ_0T-#@,A(:OU`B?C!_X'P<?8)U&Q)[!A(4JY``!'0"\)``"O#2
+M"1(/[N3U+R*.$X\4C17K8`D4<!NO%1(9_B)^,'_@K!.M%'4;$7L&$A2KKQ42
+M&?XB$A=LD"$!X/4H=$$E*/CF]3)T124H^.;U(I`A`.!@`](-(L(-(LWOS9`!
+M`N`PYP+#(GXJ?P`2!`Z0`03@1(#PD`$"X$2`\-,BD#0PY/"C\*-T'_"CY/"0
+M`1#@(.$#`(#VD`$2X"#A`P"`]B+D_W0V+_CFD#",\.^0&U&3D#"-\*-T`?"C
+MY/`/OPGC(N3_[Y`;6Y.0,(SP[Y`;49.0,(WPHW0!\*/D\`^_">,BY)``!?#O
+M8`*``G\!D``&[_"0``1T4?"0``O@1`+P(I`P\.#U*I``"N`PY`Z0,/+@8`B0
+M``IT$/#3(L,BD##PX/4JD``*X##D#I`P\N!P")``"G00\-,BPR*0-,[@1`+P
+MD#3-X%3^\)`!`.!4]_"0`0'@5+_P(I```70.\)`TF.!4^_"0-%AT`?"0,#!T
+M!/`B`@0+%@P2&"0P2&!LD,@``0(#"P\*#@D-"`SOQ#,S5,#_D`$`X%0_3_"0
+M`0+@1(#P(I`!`^`PYPQT@/"0`03@5'_PTR+#(I``"N`PX0QT`O"0``O@5/WP
+MTR+#(I``"N`PX@QT!/"0``O@5/OPTR+#(I``"N`PY@QT0/"0``O@5+_PTR+#
+M(I``"N`PYPQT@/"0``O@5'_PTR+#(I`!!>`PY0QT(/"0`0;@5-_PTR+#(N3U
+M,9``"G3_\)`B+G0A\*-T$/`B4E-4$A46$!%``"``0/\_/[TH(0#E-"01]8+D
+M-3/U@^`PY@+3(L,BD#!`=#+PHW2P\*-T`?"CY/`B=3$!D``*=/_PD``+X$0@
+M\"(+T1-3%6L1Q@`)&0,0E1O:D#!DX/VCX/[M)>#_[C/^(I`P0'0R\*-TL/"C
+MY/"C\"*0`0#@1`CPD`$!X$1`\"(2&K]0"-()$@_NY/4O(I``"N`PX`5T`?#3
+M(L,BD``*X##E!70@\-,BPR*0-#!T'_"CY/"C\*/P(GA_Y/;8_76!3`(7W,*O
+MPHS"C1(`#M*O(L*.CC^/0!(<:]*.(I`P/._P[D2`H_`BD#!XX/VCX/[M_R+E
+M2D5)<`/3@`'#(N5,14MP`].``<,BP@OD]102%J(BPH]UC?5UBT$BCDF/2M*,
+M(HY+CTS2C"(```````#"#>3U+B(`````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````(
+"K#``
+`
+end
diff --git a/sys/contrib/dev/ral/rt2561s.fw.uu b/sys/contrib/dev/ral/rt2561s.fw.uu
new file mode 100644
index 0000000..43d4353
--- /dev/null
+++ b/sys/contrib/dev/ral/rt2561s.fw.uu
@@ -0,0 +1,202 @@
+# $FreeBSD$
+#
+# Copyright (c) 2005-2008, Ralink Technology Corp.
+# Paul Lin <paul_lin@ralinktech.com.tw>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+begin 644 rt2561s.fw
+M`APM`@??PHPB(@`"&4/"K\*-=8R4=8J3TJ\B`AJ<$@C?0`,"`AZ0(0+@]2V0
+M``/@$@0_`+```,X!`%X0`&\1`/(@`4TA`7`B`80P`8\Q`=50`9]1`?)2`@9@
+M```"%)``"N`@Y0,P!P/2""(2%"LBD"$`X/41Y1'$,U3@)"'U@N0T(?6#X$2`
+M\.41Q#-4X"0L]8+D-"'U@^41\,0S5.`D+?6"Y#0A]8/E+?#DD"$#\"(2!<N0
+M(0#@]3%@!1(;X(`#$ANFY)`A`_"O+1(0Z")U,?^0`0#@5/?PD`$!X%3^\%0^
+M\.20``OP\)`A`_"O+1(0Z")^*W^`?0,2"ON0-,W@(./YD"$4$@0;D#3`$@0G
+MD"$8$@0;D#3($@0GD"$<$@0;D#3$$@0GD#3,=`'PH^!$!/"0`0'@1`'P1$#P
+MD``+X$00\.20(0/PKRT2$.@BD`$`X%3W\)`!`>!4_O!4O_"0``O@5._PY)`A
+M`_"O+1(0Z")^*W^`?0,2"OODD"$#\*\M$A#H(M(%A2TCY)`A`_`B$A.NP@#D
+MD"$#\*\M$A#H(H4M)9``"^!4^__PY)``!_"0``IT!/#DD``(\)`A`."0``GP
+MD``'=''P[T0$D``+\.20(0/P(I`A`.#_5!_U,*/@]2>/)A(48N20(0/PKRT2
+M$.@BD"$`X/4L$A>CY)`A`_"O+1(0Z"(2&NWDD"$#\*\M$A#H(N20(0/PKRT2
+M$.@BY3%D`7!!$@CM0`,"`YT2#C10('XK?X!]`Q(*^W\!$@K80`G2"1(.1^3U
+M+R(2"61U+P$B?P$2"MA0!'4O`B+2"1(.1^3U+R(2")!041(*K)`P].#U*GXP
+M?^RCX/WD^Q(-H.3__A(.*9``"G0"\)``"^!$`O_P_9`!!70@\)`!!N!$(/#M
+M5+^0``OPD#3,X$0!\*/@1`'PH^!$`?#2!!((K5!#$@J[?C!_X'PP?>QU&Q%[
+M!A(3R)`P]>!U\""D_Z[P$@XID``+X%3]__#]Y)``!/"0`0;@5-_PD``*=$#P
+M39``"_#"!!())5`X$@J[?C!_X'P<?7YU&Q)[!A(3R)``!'0"\)``"O#D__X2
+M#BF0``O@5/WPY)``!/"0`0;@5-_PP@02"4]0)1(*NW\"$@K8D`$$X%1_\)``
+M"^!4_?_PY)``!/#O5+^0``OPP@02".U0+1(*NWXP?^!\''U^=1L2>P82$\B0
+M``1T`O"0``KPD`$&X%3?\)``"^!4O_#"!"+OC?"DJ/#/C/"D*,Z-\*0N_B*\
+M``N^`"GOC?"$_ZWP(N3,^'7P".\O_^XS_NPS_.Z=[)A`!?SNG?X/U?#IY,[]
+M(NWX]?#NA"#2'/ZM\'7P".\O_^TS_4`'F%`&U?#R(L.8_0_5\.HBQ?#XH^`H
+M\,7P^.6"%8)P`A6#X#CP(N#\H^#]H^#^H^#_(NSPH^WPH^[PH^_P(J0E@O6"
+MY?`U@_6#(M"#T(+XY)-P$G0!DW`-HZ.3^'0!D_6"B(/D<W0"DVA@[Z.CHX#?
+MBH.)@N1SY2X48!T48#T48%T4<`,"!1@D!&`#`@4N(`T#`@4N=2X!(I``"N#_
+M,.4#1"#PY4!%/V`#`@4N=2X"$AL2$AO.KR@2&X\BD`$#X/\PYW;O1("0`0/P
+M$@A5$AM5$AP!=2X#KR)^`!(<12+E0$4_<"$2%]@2&\X2&S42'`$2'!\P#0MU
+M+@&O,GX`$AQ%(N3U+B*0``K@_S#E+$0@\!(7V!(;SA(;-1(<`1(<'W4N!"+E
+M0$4_<!`P#0IU+@&O,OX2'$4BY/4N(A())4`%$@CM4$1^,'_@?!Q]?G4;$GL&
+M$A/(D``$=`+PD``*\.3__A(.*9``"^!4O_!4?__PY)`PZ?#O5/V0``OPY)``
+M!/#2"1(.1^3U+Q().E!(?C!_X'P<?7YU&Q)[!A(3R)``!'0"\)``"O#D__X2
+M#BF0``O@5+_P5/WPY)``!/#_$@K84`1U+P<BD`$$X%1_\-()$@Y'Y/4O(L*O
+MY/4O]8AUJ`]UB1'UN/7H=9`/=3'_=2O_D"(N\*/PD").\*/PP@7"",(`P@?"
+M!)``"G3_\)``"W0!\)`!`W3_\.20`03PD`$%=/_PY)`!!O"0``3PD##H=!#P
+MD`$'\)`!"`3PD`$)=$CPD`$*='_PD`$"=!_PD`$`=!3PD`$!="#PD```X$2`
+M\'5)`'5*`<(!TJ\B$@CM4"T2"GB0`0;@5-_P?C!_X'P<?7YU&Q)[!A(3R)``
+M!'0"\)``"O#2"1(.1^3U+R(2"4]04!(*>)``"^!4_?#DD``$\)`!`W2`\)`!
+M!.!$@/!_`A(*V%`$=2\%(GXP?^!\''U^=1L2>P82$\B0``1T`O"0``KPT@D2
+M#D>0`03@5'_PY/4O(I`P.N#U$!(<7%`FY2<PY@53$/V``T,0`N4G,.<%4Q#W
+M@`-#$`A3$/Y#$`20,#KE$/`2'%%02)`!`^#U$%0<8#[E$%3C\*/@]1#PY2<P
+MY@5#$`*``U,0_>4G,.<%0Q`(@`-3$/=3$/Y#$`20,#KE$/"O*7X`$AQPKS5^
+M`!(<=R(2",I0<A(0S84T@H4S@^#\H^##G_42[)[U$=/E$I0`Y1%D@)2`0`:N
+M$:\2@`1^`'\`CA&/$N4T)!#U@N0U,_6#X,.5$O42Y)41]1'#9("4@%`%Y/41
+M]1+E-"02_^0U,_ZM$GL!$@V@D`$%="#PD`$&X$0@\'4O`R+`X,#PP(/`@L#0
+M==`(PJ^0(B[@_J/@CC/U-.4O)>`D\?6"Y#0;]8/DD_YT`9/*[LKY$@1E$@D7
+M4`+2!Q(9CA(`'A()`E`%KR42$.@P!1OE+W`7(`04$A,HD@#"!=(+H@#D,_44
+MKR,2$/'2K]#0T(+0@]#PT.`RY/^0,(SD\.^0&[J31("0,(WPHW0!\*/D\)`P
+MC.#^=#8O^,;NQJ/@_N^0&[J31(!N8`$?#^_#E`E`R"*0,/#@]2J0``K@,.0.
+MD##RX&`(D``*=!#PTR+#(I`P\.#U*I``"N`PY`Z0,/+@<`B0``IT$/#3(L,B
+MD`$#X##G#'2`\)`!!.!4?_#3(L,BD``*X##@!70!\-,BPR*0``K@,.$,=`+P
+MD``+X%3]\-,BPR*0``K@,.(,=`3PD``+X%3[\-,BPR*0``K@,.4%="#PTR+#
+M(I``"N`PY@QT0/"0``O@5+_PTR+#(I``"N`PYPQT@/"0``O@5'_PTR+#(I`!
+M!>`PY0QT(/"0`0;@5-_PTR+#(I`TS>#Y(./XY2OT8&:0-,`2!!N%-(*%,X-U
+M\"#E*Q($,^6")`3U@N0U@_6#$@0GD#3($@0;A32"A3.#=?`@Y2L2!#/E@B0(
+M]8+D-8/U@Q($)Y`TT!($&X4T@H4S@W7P(.4K$@0SY8(D#/6"Y#6#]8,2!"?E
+M-"3P_^4S--[^[W@%SL,3SA/8^?4KA32"A3.#=?`@$@0SY8(D!/6"Y#6#]8,2
+M!!N0-,`2!">%-(*%,X-U\"#E*Q($,^6")`CU@N0U@_6#$@0;D#3($@0GA32"
+MA3.#=?`@Y2L2!#/E@B0,]8+D-8/U@Q($&Y`TQ!($)Y`!`>!$0/"0`0#@1`CP
+MZ40$D#3-\)`TS.!$`?"CX$0!\*/@1`'P(I`TSN!$`O"0-,W@5/[PD#3-X"#C
+M^9`!$>!4(O^_(@/3@`'#4/"0`0#@5/?PD`$!X%2_\"*0`0#@1`CPD`$!X$1`
+M\"*0-,[@1`+PD#3-X%3^\)`!`.!4]_"0`0'@5+_P(LWOS9`!`N`PYP+#(GXJ
+M?P`2"ON0`03@1(#PD`$"X$2`\-,BCA*/$XT4Y12BX9()Y30D&?6"Y#4S]8/@
+M_>4T)!KU@N0U,_6#X/NB"9(*=1D`=1H:$A$Y,`D$?\B``G_HY1,D&/6"Y#42
+M]8/O\.4Q8`1_`H`"?P'E$R09]8+D-1+U@^_PY30D&?6"Y#4S]8/@_WT:?``2
+M#-KE$R0:]8+D-1+U@^_PY1,D&_6"Y#42]8/N\.4Q8&#E$R0<_^0U$O[E-"02
+M_>0U,_QU&Q%[!A(3R.43)"+_Y#42_GPP?1!U&Q%[!A(3R.43)"C_Y#42_GPP
+M?0AU&Q%[!A(3R.4T)!CU@N0U,_6#X/_E$R0M]8+D-1+U@^_P@#_E$R0<_^0U
+M$OY\,'T0=1L1>P82$\CE$R0B_^0U$OY\,'T(=1L1>P82$\CE$R0H_^0U$OY\
+M,'T0=1L1>P82$\CE$R0N]8+D-1+U@^3PY1,D+_6"Y#42]8/D\.4T)!'U@N0U
+M,_6#X/_#$__E$R0P]8+D-1+U@^_P,`E!Y1,D,/6"Y#42]8/@_^4Q8`1^`(`"
+M?A#O3O#E,6`&?@!_`(`/Y10PX`9^`'__@`1^`'\`Y1,D,?6"Y#42]8/O\"+E
+M$R0P]8+D-1+U@^!$0/#E%##@#^4T)!#U@N0U,_6#X/^``G\`Y1,D,?6"Y#42
+M]8/O\"*/%8P6C1?E%<.4!%!6Y164`$`&>@![8(`$>@![P.47Q/A4#\AH_^46
+MQ%3P2/[E%9`;=9/]?``2`[#O*_ON.OKE%\3X5`_(:/_E%L14\$C^Y163_7P`
+M$@.P[4Q@8PN[``$*@%QZ`'L:Y1>N%G@"PS/.,\[8^20+_^0^_N45D!MUD_U\
+M`!(#L.]X`L,SSC/.V/DK^^XZ^N47KA9X`L,SSC/.V/DD"__D/O[E%9`;=9/]
+M?``2`[#M3&`'=`0K^^0Z^L_KS\[JSB*.$X\4C17K8`D4<!NO%1(."B)^,'_@
+MK!.M%'4;$7L&$A/(KQ42#@HBCA./%!(0S</OE13_[I43S>_-_-/ME`#L9("4
+M@$`%SNS.@`1^`'\!S.[,[)``!?"0``;O\)``!'11\)``"^!$`O`BY)``!?#O
+M8`*``G\!D``&[_"0``1T4?"0``O@1`+P(I`P/._P[D2`H_`BY30D$?6"Y#4S
+M]8/@,.8"TR+#(I``!'0"\)``"O`P"3+E-$4S<`+#(H4T@H4S@\"#P(+@_J/@
+M_X4T@H4S@Z.CX/RCX/W#[YW_[IS0@M"#\*/O\.4T13-P`L,B$@[N4/.0``K@
+M(.4#,`=!Y31%,W`"PR*%-(*%,X/`@\""X/ZCX/^%-(*%,X.CH^#\H^#]P^^=
+M_^Z<T(+0@_"C[_#E-$4S<`+#(A(.[E#S@+6%-(*%,X/@_J/@_Q(-R-,BY30D
+M$?6"Y#4S]8/@,.<[Y30D'/6"Y#4S]8/@92MP`W4K_^4T)!WU@N0U,_6#X/\2
+M$.A^(G\0$AH^CC./-)`B+N#^H^".,_4TPR+2"N4T)!OU@N0U,_6#X'`ZA32"
+MA3.#P(/`@N#^H^#_A32"A3.#HZ/@_*/@_</OG?_NG/[0@M"#\*/O\-.4`.YD
+M@)2`4`,"$"B`QH4T@H4S@^#^H^##[F2`E(!0`P(0*!(0S84T@H4S@^#\H^#]
+MPY_N9(#X[&2`F$`@A32"A3.#P(/`@J.CX/ZCX/_MG__LGM""T(/PH^_PP@J%
+M-(*%,X/@_J/@_^4T)!#U@N0U,_6#X/W#[YW][I0`_!(0A5`LA32"A3.#P(/`
+M@N#^H^#_A32"A3.#HZ/@_*/@_</OG?_NG-""T(/PH^_PP@H@"@,"#SA^(G\0
+M$AH^CC./-(^"CH/@_J/@TY0`[F2`E(!`#7XB?Q"M-*PS$AA"@!H2$-B%-(*%
+M,X/NC_`2!`5^(G\PK32L,Q(80I`B+N#^H^#_931P`^YE,W`"TR*.,X\TPR+#
+M[Y0$[F2`E(!`#-/ME`3L9("4@%`!(L/OE/SN9("4?T`,T^V4_.QD@)1_4`$B
+MT^^4!.YD@)2`4`W#[93\[&2`E']``M,BPR*0,'C@_:/@_NW_(I`P9.#]H^#^
+M[27@_^XS_B+""^3U%!(0\2+D_N_T8$%T!"[U@N0T(?6#X+3_(W0$+O6"Y#0A
+M]8/O\#`+#70(+O6"Y#0A]8/E%/"0``)T`?`BO@,*D``"=`'PY/Z`P@Z`OR*.
+M%8\6RNW*R>O),`H$?TJ``G]"R^_+ZL.4!%`"@`'#0`3+1"#+A1:"A16#Z_"C
+MY/"%%H*%%8.CH^4:\.49A1:"A16#HZ.C\.46)`3U@N0U%?6#=`_PY18D!?6"
+MY#45]8/D\.46)`;U@N0U%?6#Y/#E%B0']8+D-17U@W00\.J0&X.3^^ID`6`(
+MZF0"8`.Z`P3+1`C+Y18D"/6"Y#45]8/K\.46)!7U@N0U%?6#=/_PY18D%O6"
+MY#45]8/I\.46)`GU@N0U%?6#=`3P)1KU&N0U&?49ZL.4!$`#`A+PZF`#N@$?
+MZB0!_>0S_.4:KAEX`\,SSC/.V/G_$@.PCAF/&@(2T.HD__WD-/_\?@!_"Q(#
+MGLSNS,WOS>4:Q/A4#\AH_^49Q%3P2/X2`[",&XT<ZB3__>0T__Q^`'\+$@.>
+MS.[,S>_-Y1K$^%0/R&C_Y1G$5/!(_A(#L(X9CQKE'$4;8`@%&N4:<`(%&>HD
+M__WD-/_\?@!_`Q(#GM/E')_E&YY0&.4<11M@$KH##^46)`GU@N0U%?6#X$2`
+M\.46)`KU@N0U%?6#Y1KPY1G_Y18D"_6"Y#45]8/O\(`MY1I4/__E%B0*]8+D
+M-17U@^_PY1JN&7@&SL,3SA/8^?_E%B0+]8+D-17U@^_PA1:"A16#X$0!\"*0
+M,#!T`O!U$0=U$M"0,##@,.`.Y1(5$G`"%1'E$D41<.OE$D41<!(2$ZZ0(0#@
+M8`>0-)C@1`3PPR+DD#18\)`T,G0?\'41!W42T)`T@>!D`V`.Y1(5$G`"%1'E
+M$D41<.KE$D41<!(2$ZZ0(0#@8`>0-)C@1`3PPR*0-)C@1`3PY)```?#3(I``
+M`70.\)`TF.!4^_"0-%AT`?"0,#!T!/`BCA:/%XP8C1GD_^_#FU!3Y1LPX!+O
+M?``E&?WL-1B-@O6#X/4<@!_E&S#A$^]\`"49_>PU&(V"]8/DD_4<@`?E&2_X
+MYO4<Y1LPY`_E%R_U@N0U%O6#Y1SP@`;E%R_XIAP/@*@BD"$`X,0S5.`D$/6"
+MY#0AJX+Z$A#8BX**@^Z/\!($!7XB?S#-Z\W,ZLP2&$+DD"$#\*\M$A#H(N4P
+M$@0_%(,`%/$!%5D"%>T#%D$$%H@%%OL&%VH'```7HL(!$@`&D#`ZX/42Y28@
+MY0B0-)C@5/[P(I`TF.!$`?#E)C#F#^4G,.8%4Q+]@!)#$@*`#>4G,.8%0Q("
+M@`-3$OWE)C#G#^4G,.<%4Q+W@!)#$@B`#>4G,.<%0Q((@`-3$O=#$@%#$@20
+M,#KE$O`BP@$2``:0,#K@]1+E)B#E")`TF.!4_O`BD#28X$0!\.4F5,!@'.4G
+M,.8%4Q+]@`-#$@+E)S#G!5,2]X`?0Q((@!KE)S#F!4,2`H`#4Q+]Y2<PYP5#
+M$@B``U,2]T,2`4,2!)`P.N42\"+"`1(`!I`P.N#U$D,2`4,2!.4F,.5<D#28
+MX$0!\.4F5,!@'.4G,.8%4Q+]@`-#$@+E)S#G!5,2]X`P0Q((@"OE)S#F!4,2
+M`H`#4Q+]Y2<PYP5#$@B``U,2]^4G]%0?_Y`P-.!4X$_PY/4LD#`ZY1+P@!60
+M-)C@5/[PY2?T5!__D#`TX%3@3_"0,#7@]1)3$N#E$O`BP@$2``:0,#K@]1+E
+M)C#E/)`TF.!$`?#E)S#F!5,2_8`#0Q("Y2<PYP53$O>``T,2".4F5,!@"$,2
+M`4,2!(`&4Q+^0Q($D#`ZY1+P(I`TF.!4_O`BP@$2``:0,#K@]1+E)S#F!4,2
+M`H`#4Q+]Y2<PYP5#$@B``U,2]^4F5,!@"%,2_E,2^X`&0Q(!0Q($D#28X$0!
+M\)`P.N42\"(@`A,2'#FO*7X`$AQPKS5^`!(<=]("D#`ZX/42Y28@Y0W"`1(`
+M!I`TF.!4_O`BD#28X$0!\.4F5,!@+,(!$@`&Y2<PY@53$OV``T,2`N4G,.<%
+M4Q+W@`-#$@A#$@%#$@20,#KE$O`B,`$#`A>B$ACTT@$BP@$2``;E)B#E"9`T
+MF.!4_O"`59`TF.!$`?#E)C#F#^4G,.8%4Q+]@!)#$@*`#>4G,.8%0Q("@`-3
+M$OWE)C#G#^4G,.<%4Q+W@!)#$@B`#>4G,.<%0Q((@`-3$O=#$@%3$ON0,#KE
+M$O"0,#K@]1(BY28PY2P@`R'2`Q(<.74U!G4I":\I?@`2''"0,#K@]1)3$OY#
+M$@3E$O"0-)C@1`'P(I`TF.!4_O`BY/_E,"3^<"SD_N[#E2Q0$G0!R.[("(`"
+MPS/8_,]/SPZ`Z)`P-.!4X/[E)U0?;_3.3L[N\"*0*:#@<&.0,(SD\*-TPO"C
+M=`'PH^3PD#",X/^0*:#PD#",Y/"C=,7PHW0!\*/D\)`PC.#_D"FA\)`PC.3P
+MHW3$\*-T`?"CY/"0,(S@_Y`IHO"0,(SD\*-TP_"C=`'PH^3PD#",X)`IH_`B
+MC!.-%.\D'O6"Y#[U@^#\H^!,8$'O)![U@N0^]8/@_*/@]8*,@^#\H^#]A12"
+MA1.#X/JCX/O3[9OJ9(#X[&2`F$`3[R0>]8+D/O6#X/RCX,[LSO^`KZT4K!,2
+M&G$B$@C*4$_E-"02_^0U,_[E-"00]8+D-3/U@^#]Y/L2#:#E-"00]8+D-3/U
+M@^#_?@`2#BF0``IT0/"0``O@1$#_\)``"G2`\$^0``OPD##I=`'P=2\&(I`P
+M.N#_Y2<PYA(P#`;O5/7^@`3O1`K^S^[/@!`P#`;O1`K^@`3O5/7^S^[/SU3^
+MS\]$!,^0,#KO\#`,"7\(?@`2''"`!W\B?@$2''"R#"+`X,#PP(/`@L#0==`(
+MPJ_"C,*-T^5*E`#E290`0`CE2A5*<`(52=/E3)0`Y4N4`$`(Y4P53'`"%4L2
+M``[2C-*OT-#0@M"#T/#0X#(P!SSE+W`XP@>0(B[@_J/@CA'U$I`B3N#^H^#_
+MD"(N[O"C[_"0(D[E$?"CY1+PCC./-#`(!1(4*\((P@D2#D<B?X!^*>3]_(^"
+MCH/@^W1%+?C&Z\9T!"_U@N0^]8/@^W1!+?C&Z\9T""__Y#[^#;T``0SM9`1,
+M<,\BD```=`[P````Y/`2!<L2'(20-)C@1`'PY3"T!0H2'%%0#1(8](`(Y3"T
+M!P,2!N@2!&N`Y(#^(N\D'O6"Y#[U@^#\H^#[RNS*)![U@N0\]8/@_*/@_>\D
+M'O6"Y#[U@^SPH^WPSNK.S^O/(N\D'O6"Y#[U@^#ZH^#[[20>]8+D//6#ZO"C
+MZ_#O)![U@N0^]8/L\*/M\"+`X,#0PJ_"CL*/T^5`E`#E/Y0`0`WE0!5`<`(5
+M/Q(<9]*.TJ_0T-#@,A((RE`B?C!_X'P<?7YU&Q)[!A(3R)``!'0"\)``"O#2
+M"1(.1^3U+R(2&<Z0(0'@]2AT024H^.;U,G1%)2CXYO4BD"$`X&`#T@TBP@TB
+MD#0PY/"C\*-T'_"CY/"0`1#@(.$#`(#VD`$2X"#A`P"`]B+D_W0V+_CFD#",
+M\.^0&[J3D#"-\*-T`?"CY/`/OPGC(N3_[Y`;Q).0,(SP[Y`;NI.0,(WPHW0!
+M\*/D\`^_">,B`@0+%@P2&"0P2&!LD,@``0(#"P\*#@D-"`SOQ#,S5,#_D`$`
+MX%0_3_"0`0+@1(#P(N3U,9``"G3_\)`B+G0A\*-T$/`B4E-4$A46$!%``"``
+M0/\_/[TH(0"0,$!T,O"C=+#PHW0!\*/D\")U,0&0``IT__"0``O@1"#P(@(?
+M!V<8GP9@``D:Q04O'!&0,$!T,O"C=+#PH^3PH_`B$@C*4`C2"1(.1^3U+R*0
+M-#!T'_"CY/"C\*/P(GA_Y/;8_76!3`(:!\*OPHS"C1(`#M*O(L*.CC^/0!(<
+M9]*.(N5*14EP`].``<,BY4Q%2W`#TX`!PR+"CW6-]76+02*.28]*THPBCDN/
+M3-*,(@```````,(-Y/4N(@``````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````(
+"NV``
+`
+end
diff --git a/sys/contrib/dev/ral/rt2661.fw.uu b/sys/contrib/dev/ral/rt2661.fw.uu
new file mode 100644
index 0000000..ab02267
--- /dev/null
+++ b/sys/contrib/dev/ral/rt2661.fw.uu
@@ -0,0 +1,202 @@
+# $FreeBSD$
+#
+# Copyright (c) 2005-2008, Ralink Technology Corp.
+# Paul Lin <paul_lin@ralinktech.com.tw>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+begin 644 rt2661.fw
+M`A+>`A3PPHPB(@`"%N7"K\*-=8R4=8J3TJ\B`AF)Y3`2#^4`/P``K0$!%0(!
+MJ0,!_00"1`4"MP8#)@<```->P@$2``:0,#K@]1+E)B#E")`TF.!4_O`BD#28
+MX$0!\.4F,.8/Y2<PY@53$OV`$D,2`H`-Y2<PY@5#$@*``U,2_>4F,.</Y2<P
+MYP53$O>`$D,2"(`-Y2<PYP5#$@B``U,2]T,2`4,2!)`P.N42\"+"`1(`!I`P
+M.N#U$N4F(.4(D#28X%3^\"*0-)C@1`'PY294P&`<Y2<PY@53$OV``T,2`N4G
+M,.<%4Q+W@!]#$@B`&N4G,.8%0Q("@`-3$OWE)S#G!4,2"(`#4Q+W0Q(!0Q($
+MD#`ZY1+P(L(!$@`&D#`ZX/420Q(!0Q($Y28PY5R0-)C@1`'PY294P&`<Y2<P
+MY@53$OV``T,2`N4G,.<%4Q+W@#!#$@B`*^4G,.8%0Q("@`-3$OWE)S#G!4,2
+M"(`#4Q+WY2?T5!__D#`TX%3@3_#D]2R0,#KE$O"`%9`TF.!4_O#E)_14'_^0
+M,#3@5.!/\)`P->#U$E,2X.42\"+"`1(`!I`P.N#U$N4F,.4\D#28X$0!\.4G
+M,.8%4Q+]@`-#$@+E)S#G!5,2]X`#0Q((Y294P&`(0Q(!0Q($@`93$OY#$@20
+M,#KE$O`BD#28X%3^\"+"`1(`!I`P.N#U$N4G,.8%0Q("@`-3$OWE)S#G!4,2
+M"(`#4Q+WY294P&`(4Q+^4Q+[@`9#$@%#$@20-)C@1`'PD#`ZY1+P(B`"$Q(<
+MG*\I?@`2'/*O-7X`$ASYT@*0,#K@]1+E)B#E#<(!$@`&D#28X%3^\"*0-)C@
+M1`'PY294P&`LP@$2``;E)S#F!5,2_8`#0Q("Y2<PYP53$O>``T,2"$,2`4,2
+M!)`P.N42\"(P`0,"`UX2"8'2`2+"`1(`!N4F(.4)D#28X%3^\(!5D#28X$0!
+M\.4F,.8/Y2<PY@53$OV`$D,2`H`-Y2<PY@5#$@*``U,2_>4F,.</Y2<PYP53
+M$O>`$D,2"(`-Y2<PYP5#$@B``U,2]T,2`5,2^Y`P.N42\)`P.N#U$B+E)C#E
+M+"`#(=(#$AR<=34&=2D)KRE^`!(<\I`P.N#U$E,2_D,2!.42\)`TF.!$`?`B
+MD#28X%3^\"(2'')``P(%7Y`A`N#U+9```^`2#^4#\0`$#P$#GQ`#L!$$,R`$
+MCB$$L2($Q3`$T#$%%E`$X%$%,U(%1V````55D``*X"#E`S`'`](((A(8MB*0
+M(0#@]1'E$<0S5.`D(?6"Y#0A]8/@1(#PY1'$,U3@)"SU@N0T(?6#Y1'PQ#-4
+MX"0M]8+D-"'U@^4M\.20(0/P(A((QY`A`.#U,6`%$@E<@`,2"6WDD"$#\*\M
+M$AS@(G4Q_Y`!`.!4]_"0`0'@5/[P5#[PY)``"_#PD"$#\*\M$AS@(GXK?X!]
+M`Q(*3Y`TS>`@X_F0(102#\&0-,`2#\V0(1@2#\&0-,@2#\V0(1P2#\&0-,02
+M#\V0-,QT`?"CX$0$\)`!`>!$`?!$0/"0``O@1!#PY)`A`_"O+1(<X"*0`0#@
+M5/?PD`$!X%3^\%2_\)``"^!4[_#DD"$#\*\M$AS@(GXK?X!]`Q(*3^20(0/P
+MKRT2'.`BT@6%+2/DD"$#\"(2&R/"`.20(0/PKRT2'.`BA2TED``+X%3[__#D
+MD``'\)``"G0$\.20``CPD"$`X)``"?"0``=T<?#O1`20``OPY)`A`_`BD"$`
+MX/]4'_4PH^#U)X\F$@`>Y)`A`_"O+1(<X"*0(0#@]2P2&.WDD"$#\*\M$AS@
+M(A(:`N20(0/PKRT2'.`BY)`A`_"O+1(<X"*.%8\6RNW*R>O),`H$?TJ``G]"
+MR^_+ZL.4!%`"@`'#0`3+1"#+A1:"A16#Z_"CY/"%%H*%%8.CH^4:\.49A1:"
+MA16#HZ.C\.46)`3U@N0U%?6#=`_PY18D!?6"Y#45]8/D\.46)`;U@N0U%?6#
+MY/#E%B0']8+D-17U@W00\.J0&TN3^^ID`6`(ZF0"8`.Z`P3+1`C+Y18D"/6"
+MY#45]8/K\.46)!7U@N0U%?6#=/_PY18D%O6"Y#45]8/I\.46)`GU@N0U%?6#
+M=`3P)1KU&N0U&?49ZL.4!$`#`@<7ZF`#N@$?ZB0!_>0S_.4:KAEX`\,SSC/.
+MV/G_$@]6CAF/&@(&]^HD__WD-/_\?@!_"Q(/1,SNS,WOS>4:Q/A4#\AH_^49
+MQ%3P2/X2#U:,&XT<ZB3__>0T__Q^`'\+$@]$S.[,S>_-Y1K$^%0/R&C_Y1G$
+M5/!(_A(/5HX9CQKE'$4;8`@%&N4:<`(%&>HD__WD-/_\?@!_`Q(/1-/E')_E
+M&YY0&.4<11M@$KH##^46)`GU@N0U%?6#X$2`\.46)`KU@N0U%?6#Y1KPY1G_
+MY18D"_6"Y#45]8/O\(`MY1I4/__E%B0*]8+D-17U@^_PY1JN&7@&SL,3SA/8
+M^?_E%B0+]8+D-17U@^_PA1:"A16#X$0!\"*0-,W@^2#C^.4K]&!FD#3`$@_!
+MA32"A3.#=?`@Y2L2#]GE@B0$]8+D-8/U@Q(/S9`TR!(/P84T@H4S@W7P(.4K
+M$@_9Y8(D"/6"Y#6#]8,2#\V0--`2#\&%-(*%,X-U\"#E*Q(/V>6")`SU@N0U
+M@_6#$@_-Y30D\/_E,S3>_N]X!<[#$\X3V/GU*X4T@H4S@W7P(!(/V>6")`3U
+M@N0U@_6#$@_!D#3`$@_-A32"A3.#=?`@Y2L2#]GE@B0(]8+D-8/U@Q(/P9`T
+MR!(/S84T@H4S@W7P(.4K$@_9Y8(D#/6"Y#6#]8,2#\&0-,02#\V0`0'@1$#P
+MD`$`X$0(\.E$!)`TS?"0-,S@1`'PH^!$`?"CX$0!\"+O)![U@N0^]8/@^J/@
+M^^TD'O6"Y#SU@^KPH^OP[R0>]8+D/O6#[/"C[?`B``"0``!T#O````#D\!((
+MQQ(=!I`TF.!$`?#E,+0%"A(<RE`-$@F!@`CE,+0'`Q()T!(0UX#D@/XBPJ_D
+M]2_UB'6H#W6)$?6X]>AUD`]U,?]U*_^0(B[PH_"0(D[PH_#"!<((P@#"!\($
+MD``*=/_PD``+=`'PD`$#=/_PY)`!!/"0`05T__#DD`$&\)``!/"0,.AT$/"0
+M`0?PD`$(!/"0`0ET2/"0`0IT?_"0`0)T'_"0`0!T%/"0`0%T(/"0``#@1(#P
+M=4D`=4H!P@'2KR)U,0&0``IT__"0``O@1"#P(N3U,9``"G3_\)`B+G0A\*-T
+M$/`BD#`ZX/_E)S#F$C`,!N]4]?Z`!.]$"O[/[L^`$#`,!N]$"OZ`!.]4]?[/
+M[L_/5/[/ST0$SY`P.N_P,`P)?PA^`!(<\H`'?R)^`1(<\K(,(I`P.N#U$!(<
+MU5`FY2<PY@53$/V``T,0`N4G,.<%4Q#W@`-#$`A3$/Y#$`20,#KE$/`2',I0
+M2)`!`^#U$%0<8#[E$%3C\*/@]1#PY2<PY@5#$`*``U,0_>4G,.<%0Q`(@`-3
+M$/=3$/Y#$`20,#KE$/"O*7X`$ASRKS5^`!(<^2*.$H\3C13E%*+AD@GE-"09
+M]8+D-3/U@^#]Y30D&O6"Y#4S]8/@^Z()D@IU&0!U&AH2!6`P"01_R(`"?^CE
+M$R08]8+D-1+U@^_PY3%@!'\"@`)_`>43)!GU@N0U$O6#[_#E-"09]8+D-3/U
+M@^#_?1I\`!(0$>43)!KU@N0U$O6#[_#E$R0;]8+D-1+U@^[PY3%@8.43)!S_
+MY#42_N4T)!+]Y#4S_'4;$7L&$A70Y1,D(O_D-1+^?#!]$'4;$7L&$A70Y1,D
+M*/_D-1+^?#!]"'4;$7L&$A70Y30D&/6"Y#4S]8/@_^43)"WU@N0U$O6#[_"`
+M/^43)!S_Y#42_GPP?1!U&Q%[!A(5T.43)"+_Y#42_GPP?0AU&Q%[!A(5T.43
+M)"C_Y#42_GPP?1!U&Q%[!A(5T.43)"[U@N0U$O6#Y/#E$R0O]8+D-1+U@^3P
+MY30D$?6"Y#4S]8/@_\,3_^43)##U@N0U$O6#[_`P"4'E$R0P]8+D-1+U@^#_
+MY3%@!'X`@`)^$.].\.4Q8`9^`'\`@`_E%##@!GX`?_^`!'X`?P#E$R0Q]8+D
+M-1+U@^_P(N43)##U@N0U$O6#X$1`\.44,.`/Y30D$/6"Y#4S]8/@_X`"?P#E
+M$R0Q]8+D-1+U@^_P(N4T)!'U@N0U,_6#X##G.^4T)!SU@N0U,_6#X&4K<`-U
+M*__E-"0=]8+D-3/U@^#_$AS@?B)_$!(95HXSCS20(B[@_J/@CC/U-,,BT@KE
+M-"0;]8+D-3/U@^!P.H4T@H4S@\"#P(+@_J/@_X4T@H4S@Z.CX/RCX/W#[YW_
+M[IS^T(+0@_"C[_#3E`#N9("4@%`#`@UH@,:%-(*%,X/@_J/@P^YD@)2`4`,"
+M#6@2'+^%-(*%,X/@_*/@_<.?[F2`^.QD@)A`((4T@H4S@\"#P(*CH^#^H^#_
+M[9__[)[0@M"#\*/O\,(*A32"A3.#X/ZCX/_E-"00]8+D-3/U@^#]P^^=_>Z4
+M`/P2%S!0+(4T@H4S@\"#P(+@_J/@_X4T@H4S@Z.CX/RCX/W#[YW_[IS0@M"#
+M\*/O\,(*(`H#`@QX?B)_$!(95HXSCS2/@HZ#X/ZCX-.4`.YD@)2`0`U^(G\0
+MK32L,Q(6,X`:$APUA32"A3.#[H_P$@^K?B)_,*TTK#,2%C.0(B[@_J/@_V4T
+M<`/N93-P`M,BCC./-,,BY3%D`7!!$AN#0`,"#T,2'`!0('XK?X!]`Q(*3W\!
+M$AHG0`G2"1(1F^3U+R(2!T]U+P$B?P$2&B=0!'4O`B+2"1(1F^3U+R(2&LQ0
+M41(<59`P].#U*GXP?^RCX/WD^Q(9VN3__A(<M)``"G0"\)``"^!$`O_P_9`!
+M!70@\)`!!N!$(/#M5+^0``OPD#3,X$0!\*/@1`'PH^!$`?#2!!(:Z5!#$AL&
+M?C!_X'PP?>QU&Q%[!A(5T)`P]>!U\""D_Z[P$ARTD``+X%3]__#]Y)``!/"0
+M`0;@5-_PD``*=$#P39``"_#"!!(;K5`X$AL&?C!_X'P=?0!U&Q)[!A(5T)``
+M!'0"\)``"O#D__X2'+20``O@5/WPY)``!/"0`0;@5-_PP@02&]=0)1(;!G\"
+M$AHGD`$$X%1_\)``"^!4_?_PY)``!/#O5+^0``OPP@02&X-0+1(;!GXP?^!\
+M'7T`=1L2>P82%="0``1T`O"0``KPD`$&X%3?\)``"^!4O_#"!"+OC?"DJ/#/
+MC/"D*,Z-\*0N_B*\``N^`"GOC?"$_ZWP(N3,^'7P".\O_^XS_NPS_.Z=[)A`
+M!?SNG?X/U?#IY,[](NWX]?#NA"#2'/ZM\'7P".\O_^TS_4`'F%`&U?#R(L.8
+M_0_5\.HBQ?#XH^`H\,7P^.6"%8)P`A6#X#CP(N#\H^#]H^#^H^#_(NSPH^WP
+MH^[PH^_P(J0E@O6"Y?`U@_6#(M"#T(+XY)-P$G0!DW`-HZ.3^'0!D_6"B(/D
+M<W0"DVA@[Z.CHX#?BH.)@N1SCQ6,%HT7Y17#E`105N45E`!`!GH`>V"`!'H`
+M>\#E%\3X5`_(:/_E%L14\$C^Y160&SV3_7P`$@]6[RO[[CKZY1?$^%0/R&C_
+MY1;$5/!(_N45D_U\`!(/5NU,8&,+NP`!"H!<>@![&N47KA9X`L,SSC/.V/DD
+M"__D/O[E%9`;/9/]?``2#U;O>`+#,\XSSMCY*_ON.OKE%ZX6>`+#,\XSSMCY
+M)`O_Y#[^Y160&SV3_7P`$@]6[4Q@!W0$*_OD.OK/Z\_.ZLXBY2X48!T48#T4
+M8%T4<`,"$80D!&`#`A&:(`T#`A&:=2X!(I``"N#_,.4#1"#PY4!%/V`#`A&:
+M=2X"$AI*$AP3KR@2&U<BD`$#X/\PYW;O1("0`0/P$AA"$AJ-$AQ%=2X#KR)^
+M`!(<J"+E0$4_<"$2%682'!,2&FT2'$42'(XP#0MU+@&O,GX`$ARH(N3U+B*0
+M``K@_S#E+$0@\!(59A(<$Q(:;1(<11(<CG4N!"+E0$4_<!`P#0IU+@&O,OX2
+M'*@BY/4N(I``!'0"\)``"O`P"3+E-$4S<`+#(H4T@H4S@\"#P(+@_J/@_X4T
+M@H4S@Z.CX/RCX/W#[YW_[IS0@M"#\*/O\.4T13-P`L,B$@PN4/.0``K@(.4#
+M,`=!Y31%,W`"PR*%-(*%,X/`@\""X/ZCX/^%-(*%,X.CH^#\H^#]P^^=_^Z<
+MT(+0@_"C[_#E-$4S<`+#(A(,+E#S@+6%-(*%,X/@_J/@_Q(7P-,B$ANM0`42
+M&X-01'XP?^!\'7T`=1L2>P82%="0``1T`O"0``KPY/_^$ARTD``+X%2_\%1_
+M__#DD##I\.]4_9``"_#DD``$\-()$A&;Y/4O$AO"4$A^,'_@?!U]`'4;$GL&
+M$A70D``$=`+PD``*\.3__A(<M)``"^!4O_!4_?#DD``$\/\2&B=0!'4O!R*0
+M`03@5'_PT@D2$9OD]2\B>'_D]MC]=8%,`A,E`@B0Y).C^.23HT`#]H`!\@C?
+M](`IY).C^%0')`S(PS/$5`]$(,B#0`3T5H`!1O;?Y(`+`0($"!`@0("0``KD
+M?@&38+RC_U0_,.4)5!_^Y).C8`$.SU3`)>!@J$"XY).C^N23H_CDDZ/(Q8+(
+MRL6#RO"CR,6"R,K%@\K?Z=[G@+X2&X-0+1(9(I`!!N!4W_!^,'_@?!U]`'4;
+M$GL&$A70D``$=`+PD``*\-()$A&;Y/4O(A(;UU!0$ADBD``+X%3]\.20``3P
+MD`$#=(#PD`$$X$2`\'\"$AHG4`1U+P4B?C!_X'P=?0!U&Q)[!A(5T)``!'0"
+M\)``"O#2"1(1FY`!!.!4?_#D]2\BD#`P=`+P=1$'=1+0D#`PX##@#N42%1)P
+M`A41Y1)%$7#KY1)%$7`2$ALCD"$`X&`'D#28X$0$\,,BY)`T6/"0-#)T'_!U
+M$0=U$M"0-('@9`-@#N42%1)P`A41Y1)%$7#JY1)%$7`2$ALCD"$`X&`'D#28
+MX$0$\,,BD#28X$0$\.20``'PTR(2&VY0<A(<OX4T@H4S@^#\H^##G_42[)[U
+M$=/E$I0`Y1%D@)2`0`:N$:\2@`1^`'\`CA&/$N4T)!#U@N0U,_6#X,.5$O42
+MY)41]1'#9("4@%`%Y/41]1+E-"02_^0U,_ZM$GL!$AG:D`$%="#PD`$&X$0@
+M\'4O`R+`X,#PP(/`@L#0==`(PJ^0(B[@_J/@CC/U-.4O)>`D)?6"Y#0<]8/D
+MD_YT`9/*[LKY$A`+$AR`4`+2!Q(8`A(#7Q(;F%`%KR42'.`P!1OE+W`7(`04
+M$A/RD@#"!=(+H@#D,_44KR,2%WC2K]#0T(+0@]#PT.`RD"F@X'!CD#",Y/"C
+M=,+PHW0!\*/D\)`PC.#_D"F@\)`PC.3PHW3%\*-T`?"CY/"0,(S@_Y`IH?"0
+M,(SD\*-TQ/"C=`'PH^3PD#",X/^0*:+PD#",Y/"C=,/PHW0!\*/D\)`PC."0
+M*:/P(HX6CQ>,&(T9Y/_OPYM04^4;,.`2[WP`)1G][#48C8+U@^#U'(`?Y1LP
+MX1/O?``E&?WL-1B-@O6#Y)/U'(`'Y1DO^.;U'.4;,.0/Y1<O]8+D-1;U@^4<
+M\(`&Y1<O^*8<#X"H(HP3C13O)![U@N0^]8/@_*/@3&!![R0>]8+D/O6#X/RC
+MX/6"C(/@_*/@_844@H43@^#ZH^#[T^V;ZF2`^.QD@)A`$^\D'O6"Y#[U@^#\
+MH^#.[,[_@*^M%*P3$@AC(A(;;E!/Y30D$O_D-3/^Y30D$/6"Y#4S]8/@_>3[
+M$AG:Y30D$/6"Y#4S]8/@_WX`$ARTD``*=$#PD``+X$1`__"0``IT@/!/D``+
+M\)`PZ70!\'4O!B+`X,#PP(/`@L#0==`(PJ_"C,*-T^5*E`#E290`0`CE2A5*
+M<`(52=/E3)0`Y4N4`$`(Y4P53'`"%4L2``[2C-*OT-#0@M"#T/#0X#+#[Y0$
+M[F2`E(!`#-/ME`3L9("4@%`!(L/OE/SN9("4?T`,T^V4_.QD@)1_4`$BT^^4
+M!.YD@)2`4`W#[93\[&2`E']``M,BPR+D_N_T8$%T!"[U@N0T(?6#X+3_(W0$
+M+O6"Y#0A]8/O\#`+#70(+O6"Y#0A]8/E%/"0``)T`?`BO@,*D``"=`'PY/Z`
+MP@Z`OR*.$X\4$AR_P^^5%/_NE1/-[\W\T^V4`.QD@)2`0`7.[,Z`!'X`?P',
+M[LSLD``%\)``!N_PD``$=%'PD``+X$0"\"(P!SSE+W`XP@>0(B[@_J/@CA'U
+M$I`B3N#^H^#_D"(N[O"C[_"0(D[E$?"CY1+PCC./-#`(!1(8ML((P@D2$9LB
+MY/^0,(SD\.^0&^R31("0,(WPHW0!\*/D\)`PC.#^=#8O^,;NQJ/@_N^0&^R3
+M1(!N8`$?#^_#E`E`R")_@'XIY/W\CX*.@^#[=$4M^,;KQG0$+_6"Y#[U@^#[
+M=$$M^,;KQG0(+__D/OX-O0`!#.UD!$QPSR*0(0#@Q#-4X"00]8+D-"&K@OH2
+M'#6+@HJ#[H_P$@^K?B)_,,WKS<SJS!(6,^20(0/PKRT2'.`BY/_E,"3^<"SD
+M_N[#E2Q0$G0!R.[("(`"PS/8_,]/SPZ`Z)`P-.!4X/[E)U0?;_3.3L[N\"*0
+M-,[@1`+PD#3-X%3^\)`TS>`@X_F0`1'@5"+_OR(#TX`!PU#PD`$`X%3W\)`!
+M`>!4O_`B[R0>]8+D/O6#X/RCX/O*[,HD'O6"Y#SU@^#\H^#][R0>]8+D/O6#
+M[/"C[?#.ZL[/Z\\BP.#`T,*OPH["C]/E0)0`Y3^4`$`-Y4`50'`"%3\2'.G2
+MCM*OT-#0X#(2&VY0(GXP?^!\'7T`=1L2>P82%="0``1T`O"0``KPT@D2$9OD
+M]2\BCA./%(T5ZV`)%'`;KQ42&JTB?C!_X*P3K11U&Q%[!A(5T*\5$AJM(A(8
+M?9`A`>#U*'1!)2CXYO4R=$4E*/CF]2*0(0#@8`/2#2+"#2+-[\V0`0+@,.<"
+MPR)^*G\`$@I/D`$$X$2`\)`!`N!$@/#3(I`T,.3PH_"C=!_PH^3PD`$0X"#A
+M`P"`]I`!$N`@X0,`@/8BY/]T-B_XYI`PC/#OD!OLDY`PC?"C=`'PH^3P#[\)
+MXR+D_^^0&_:3D#",\.^0&^R3D#"-\*-T`?"CY/`/OPGC(N20``7P[V`"@`)_
+M`9``!N_PD``$=%'PD``+X$0"\"*0,/#@]2J0``K@,.0.D##RX&`(D``*=!#P
+MTR+#(I`P\.#U*I``"N`PY`Z0,/+@<`B0``IT$/#3(L,BD#3.X$0"\)`TS>!4
+M_O"0`0#@5/?PD`$!X%2_\"*0``%T#O"0-)C@5/OPD#18=`'PD#`P=`3P(@($
+M"Q8,$A@D,$A@;)#(``$"`PL/"@X)#0@,[\0S,U3`_Y`!`.!4/T_PD`$"X$2`
+M\"*0`0/@,.<,=(#PD`$$X%1_\-,BPR*0``K@,.$,=`+PD``+X%3]\-,BPR*0
+M``K@,.(,=`3PD``+X%3[\-,BPR*0``K@,.8,=$#PD``+X%2_\-,BPR*0``K@
+M,.<,=(#PD``+X%1_\-,BPR*0`07@,.4,="#PD`$&X%3?\-,BPR)24U02%180
+M$4``(`!`_S\_O2@A`.4T)!'U@N0U,_6#X##F`M,BPR*0,$!T,O"C=+#PHW0!
+M\*/D\"(-Q11X%I`3:@`)&;(20AQDD#!DX/VCX/[M)>#_[C/^(I`P0'0R\*-T
+ML/"CY/"C\"*0`0#@1`CPD`$!X$1`\"(2&VY0"-()$A&;Y/4O(I``"N`PX`5T
+M`?#3(L,BD``*X##E!70@\-,BPR*0-#!T'_"CY/"C\*/P(L*OPHS"C1(`#M*O
+M(L*.CC^/0!(<Z=*.(I`P/._P[D2`H_`BD#!XX/VCX/[M_R+E2D5)<`/3@`'#
+M(N5,14MP`].``<,BP@OD]102%W@BPH]UC?5UBT$BCDF/2M*,(HY+CTS2C"(`
+M``````#"#>3U+B(`````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````(
+"0\\`
+`
+end
diff --git a/sys/dev/ral/rt2661_ucode.h b/sys/contrib/dev/ral/rt2661_ucode.h
index 556ac5f..db09b02 100644
--- a/sys/dev/ral/rt2661_ucode.h
+++ b/sys/contrib/dev/ral/rt2661_ucode.h
@@ -1,8 +1,8 @@
/* $FreeBSD$ */
-/* OpenBSD: microcode.h,v 1.1 2006/01/09 20:03:40 damien Exp */
+/* $OpenBSD: microcode.h,v 1.5 2008/03/06 09:18:04 deraadt Exp $ */
/*-
- * Copyright (c) 2005-2006, Ralink Technology, Corp.
+ * Copyright (c) 2005-2008, Ralink Technology, Corp.
* Paul Lin <paul_lin@ralinktech.com.tw>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -23,7 +23,7 @@
* RT2561S and RT2661 chipsets.
*/
-static const uint8_t rt2561_ucode[] = {
+static const unsigned char rt2561_ucode[] = {
0x02, 0x1c, 0x12, 0x02, 0x13, 0xcb, 0xc2, 0x8c, 0x22, 0x22, 0x00,
0x02, 0x16, 0x0f, 0xc2, 0xaf, 0xc2, 0x8d, 0x75, 0x8c, 0x94, 0x75,
0x8a, 0x93, 0xd2, 0xaf, 0x22, 0x02, 0x18, 0xda, 0x12, 0x1b, 0xe8,
@@ -771,7 +771,7 @@ static const uint8_t rt2561_ucode[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xac, 0x30
};
-static const uint8_t rt2561s_ucode[] = {
+static const unsigned char rt2561s_ucode[] = {
0x02, 0x1c, 0x2d, 0x02, 0x07, 0xdf, 0xc2, 0x8c, 0x22, 0x22, 0x00,
0x02, 0x19, 0x43, 0xc2, 0xaf, 0xc2, 0x8d, 0x75, 0x8c, 0x94, 0x75,
0x8a, 0x93, 0xd2, 0xaf, 0x22, 0x02, 0x1a, 0x9c, 0x12, 0x08, 0xdf,
@@ -1519,7 +1519,7 @@ static const uint8_t rt2561s_ucode[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xbb, 0x60
};
-static const uint8_t rt2661_ucode[] = {
+static const unsigned char rt2661_ucode[] = {
0x02, 0x12, 0xde, 0x02, 0x14, 0xf0, 0xc2, 0x8c, 0x22, 0x22, 0x00,
0x02, 0x16, 0xe5, 0xc2, 0xaf, 0xc2, 0x8d, 0x75, 0x8c, 0x94, 0x75,
0x8a, 0x93, 0xd2, 0xaf, 0x22, 0x02, 0x19, 0x89, 0xe5, 0x30, 0x12,
@@ -2266,3 +2266,751 @@ static const uint8_t rt2661_ucode[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43, 0xcf
};
+
+static const unsigned char rt2860_ucode[] = {
+ 0x02, 0x00, 0xe2, 0x02, 0x02, 0xec, 0x22, 0x22, 0xff, 0xff, 0xff,
+ 0x02, 0x01, 0xbd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x1e,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x02, 0x01, 0x6e, 0xc0, 0xe0, 0xc0,
+ 0xf0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x18, 0xc2,
+ 0xaf, 0x30, 0x45, 0x03, 0x12, 0x10, 0x09, 0x90, 0x04, 0x16, 0xe0,
+ 0x30, 0xe3, 0x0d, 0x74, 0x08, 0xf0, 0xe5, 0x55, 0x60, 0x06, 0x64,
+ 0x03, 0x60, 0x02, 0xd2, 0x03, 0x90, 0x04, 0x14, 0xe0, 0x20, 0xe7,
+ 0x03, 0x02, 0x00, 0xd5, 0x74, 0x80, 0xf0, 0x90, 0x70, 0x12, 0xe0,
+ 0xf5, 0x36, 0x90, 0x04, 0x04, 0xe0, 0x24, 0xcf, 0x60, 0x30, 0x14,
+ 0x60, 0x42, 0x24, 0xe2, 0x60, 0x47, 0x14, 0x60, 0x55, 0x24, 0x21,
+ 0x70, 0x60, 0xe5, 0x55, 0x24, 0xfe, 0x60, 0x07, 0x14, 0x60, 0x08,
+ 0x24, 0x02, 0x70, 0x08, 0x7d, 0x01, 0x80, 0x28, 0x7d, 0x02, 0x80,
+ 0x24, 0x90, 0x70, 0x10, 0xe0, 0xf5, 0x50, 0x85, 0x36, 0x40, 0xd2,
+ 0x01, 0x80, 0x3e, 0xe5, 0x55, 0x64, 0x03, 0x60, 0x04, 0xe5, 0x55,
+ 0x70, 0x04, 0x7d, 0x02, 0x80, 0x09, 0x85, 0x36, 0x41, 0xd2, 0x02,
+ 0x80, 0x29, 0xad, 0x55, 0xaf, 0x36, 0x12, 0x02, 0xc8, 0x80, 0x20,
+ 0x90, 0x70, 0x10, 0xe0, 0xf5, 0x47, 0x90, 0x70, 0x11, 0xe0, 0xf5,
+ 0x44, 0x12, 0x10, 0x25, 0x80, 0x06, 0x90, 0x70, 0x10, 0xe0, 0xf5,
+ 0x45, 0xe4, 0xfd, 0xaf, 0x36, 0x12, 0x02, 0xc8, 0xd2, 0x04, 0x90,
+ 0x70, 0x13, 0xe4, 0xf0, 0xd2, 0xaf, 0xd0, 0xd0, 0xd0, 0x82, 0xd0,
+ 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0x78, 0x7f, 0xe4, 0xf6, 0xd8,
+ 0xfd, 0x75, 0x81, 0x7d, 0x02, 0x01, 0x29, 0x02, 0x02, 0x0b, 0xe4,
+ 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01,
+ 0xf2, 0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54,
+ 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33, 0xc4, 0x54, 0x0f, 0x44, 0x20,
+ 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf,
+ 0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
+ 0x90, 0x03, 0x9a, 0xe4, 0x7e, 0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff,
+ 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93, 0xa3,
+ 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40,
+ 0xb8, 0xe4, 0x93, 0xa3, 0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93,
+ 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xf0, 0xa3,
+ 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde,
+ 0xe7, 0x80, 0xbe, 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, 0xc0, 0x82,
+ 0xc0, 0xd0, 0xe8, 0xc0, 0xe0, 0xe9, 0xc0, 0xe0, 0xea, 0xc0, 0xe0,
+ 0xeb, 0xc0, 0xe0, 0xec, 0xc0, 0xe0, 0xed, 0xc0, 0xe0, 0xee, 0xc0,
+ 0xe0, 0xef, 0xc0, 0xe0, 0xc2, 0xaf, 0x30, 0x45, 0x03, 0x12, 0x10,
+ 0x12, 0xd2, 0xaf, 0xd0, 0xe0, 0xff, 0xd0, 0xe0, 0xfe, 0xd0, 0xe0,
+ 0xfd, 0xd0, 0xe0, 0xfc, 0xd0, 0xe0, 0xfb, 0xd0, 0xe0, 0xfa, 0xd0,
+ 0xe0, 0xf9, 0xd0, 0xe0, 0xf8, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83,
+ 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83,
+ 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x10, 0xc2, 0xaf, 0x30, 0x45,
+ 0x03, 0x12, 0x10, 0x0c, 0x30, 0x58, 0x0a, 0xe5, 0x54, 0x60, 0x04,
+ 0x15, 0x54, 0x80, 0x02, 0xc2, 0x58, 0x30, 0x59, 0x0a, 0xe5, 0x50,
+ 0x60, 0x04, 0x15, 0x50, 0x80, 0x02, 0xc2, 0x59, 0xd5, 0x53, 0x07,
+ 0x30, 0x60, 0x04, 0x15, 0x46, 0xd2, 0x04, 0x30, 0x45, 0x03, 0x12,
+ 0x10, 0x0f, 0xc2, 0x8d, 0xd2, 0xaf, 0xd0, 0xd0, 0xd0, 0x82, 0xd0,
+ 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0x12, 0x03, 0x0e, 0x30, 0x45,
+ 0x03, 0x12, 0x10, 0x03, 0x30, 0x01, 0x06, 0x20, 0x09, 0x03, 0x12,
+ 0x10, 0x1c, 0x30, 0x02, 0x06, 0x20, 0x0a, 0x03, 0x12, 0x10, 0x1f,
+ 0x30, 0x03, 0x06, 0x20, 0x0b, 0x03, 0x12, 0x10, 0x1f, 0x30, 0x04,
+ 0x06, 0x20, 0x0c, 0x03, 0x12, 0x10, 0x22, 0x20, 0x13, 0x09, 0x20,
+ 0x11, 0x06, 0xe5, 0x2b, 0x45, 0x2c, 0x60, 0x03, 0xd3, 0x80, 0x01,
+ 0xc3, 0x92, 0xa9, 0x12, 0x03, 0x3e, 0x80, 0xbf, 0xd0, 0x83, 0xd0,
+ 0x82, 0xf8, 0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d,
+ 0xa3, 0xa3, 0x93, 0xf8, 0x74, 0x01, 0x93, 0xf5, 0x82, 0x88, 0x83,
+ 0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60, 0xef, 0xa3, 0xa3, 0xa3,
+ 0x80, 0xdf, 0x8a, 0x83, 0x89, 0x82, 0xe4, 0x73, 0xc8, 0xef, 0xc8,
+ 0xe6, 0xfa, 0x08, 0xe6, 0x4a, 0x60, 0x0c, 0xc8, 0xef, 0xc8, 0x08,
+ 0xe6, 0x16, 0x18, 0x70, 0x01, 0x16, 0xc3, 0x22, 0xed, 0x24, 0xff,
+ 0xfd, 0xec, 0x34, 0xff, 0xc8, 0xef, 0xc8, 0xf6, 0x08, 0xc6, 0xed,
+ 0xc6, 0xd3, 0x22, 0xc2, 0x43, 0xd2, 0x45, 0xe4, 0xf5, 0x20, 0xf5,
+ 0x21, 0xf5, 0x53, 0xf5, 0x46, 0xf5, 0x2b, 0xf5, 0x2c, 0xc2, 0x42,
+ 0xf5, 0x51, 0xf5, 0x52, 0xf5, 0x55, 0x90, 0x04, 0x18, 0x74, 0x80,
+ 0xf0, 0x90, 0x04, 0x1a, 0x74, 0x08, 0xf0, 0x22, 0xef, 0xf4, 0x60,
+ 0x1f, 0xe4, 0xfe, 0x12, 0x03, 0x7d, 0xe0, 0xb4, 0xff, 0x12, 0x12,
+ 0x03, 0x7d, 0xef, 0xf0, 0x74, 0x1c, 0x2e, 0xf5, 0x82, 0xe4, 0x34,
+ 0x70, 0xf5, 0x83, 0xed, 0xf0, 0x22, 0x0e, 0xbe, 0x04, 0xe3, 0x22,
+ 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75,
+ 0xd0, 0x08, 0xc2, 0xaf, 0x30, 0x45, 0x03, 0x12, 0x10, 0x06, 0xd2,
+ 0xaf, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0,
+ 0x32, 0xc2, 0xaf, 0x12, 0x00, 0x06, 0x12, 0x02, 0xa2, 0x12, 0x03,
+ 0x27, 0xe4, 0xf5, 0x22, 0xf5, 0x47, 0x90, 0x04, 0x00, 0x74, 0x80,
+ 0xf0, 0xd2, 0xaf, 0x22, 0x75, 0x89, 0x02, 0xe4, 0xf5, 0x8c, 0xf5,
+ 0x8a, 0xf5, 0x88, 0xf5, 0xb8, 0xf5, 0xe8, 0x75, 0x90, 0x18, 0xd2,
+ 0x8c, 0x75, 0xa8, 0x05, 0x22, 0x30, 0x45, 0x03, 0x12, 0x10, 0x15,
+ 0xe5, 0x20, 0x70, 0x03, 0x20, 0x10, 0x03, 0x30, 0x11, 0x03, 0x43,
+ 0x87, 0x01, 0x22, 0xce, 0xef, 0xce, 0xee, 0x60, 0x08, 0x7f, 0xff,
+ 0x12, 0x03, 0x93, 0x1e, 0x80, 0xf5, 0x22, 0xc8, 0xef, 0xc8, 0xe6,
+ 0x60, 0x03, 0x16, 0xc3, 0x22, 0xed, 0x14, 0xf6, 0xd3, 0x22, 0xc8,
+ 0xef, 0xc8, 0xe6, 0x60, 0x06, 0x16, 0xe6, 0x24, 0xff, 0xb3, 0x22,
+ 0xc3, 0x22, 0x74, 0x14, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x70, 0xf5,
+ 0x83, 0x22, 0xef, 0x90, 0x03, 0x91, 0x93, 0x90, 0x04, 0x00, 0x73,
+ 0x0a, 0x18, 0xef, 0x60, 0x03, 0x1f, 0x80, 0xfa, 0x22, 0x01, 0x3b,
+ 0x00, 0xc1, 0xae, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xc0, 0x26, 0x74, 0x04, 0xc0, 0xe0, 0xc0, 0x82, 0xc0, 0x83,
+ 0x75, 0x26, 0x0a, 0x22, 0xc0, 0x26, 0x74, 0x04, 0xc0, 0xe0, 0xc0,
+ 0x82, 0xc0, 0x83, 0x75, 0x26, 0x18, 0x22, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02, 0x10, 0x28, 0x02,
+ 0x10, 0x44, 0x02, 0x10, 0x45, 0x02, 0x12, 0xa9, 0x02, 0x12, 0xaa,
+ 0x02, 0x13, 0x45, 0x02, 0x13, 0x46, 0xc3, 0x22, 0xff, 0xff, 0x02,
+ 0x15, 0x48, 0x02, 0x16, 0x1e, 0x02, 0x13, 0xfd, 0x02, 0x13, 0x47,
+ 0x30, 0x05, 0x06, 0x20, 0x0d, 0x03, 0x12, 0x16, 0x9b, 0x30, 0x06,
+ 0x06, 0x20, 0x0e, 0x03, 0x12, 0x18, 0xbd, 0x30, 0x07, 0x06, 0x20,
+ 0x0f, 0x03, 0x12, 0x18, 0xfe, 0x22, 0x22, 0x90, 0x04, 0x14, 0xe0,
+ 0x20, 0xe7, 0x03, 0x02, 0x12, 0x9c, 0x90, 0x70, 0x12, 0xe0, 0xf5,
+ 0x56, 0x90, 0x04, 0x04, 0xe0, 0x12, 0x02, 0x4f, 0x10, 0x87, 0x31,
+ 0x10, 0xb2, 0x51, 0x10, 0xbd, 0x52, 0x10, 0xbd, 0x53, 0x10, 0xbd,
+ 0x54, 0x11, 0x71, 0x60, 0x10, 0xfe, 0x61, 0x11, 0xbf, 0x62, 0x10,
+ 0xfe, 0x63, 0x11, 0xe8, 0x70, 0x12, 0x12, 0x71, 0x12, 0x3c, 0x72,
+ 0x12, 0x6e, 0x80, 0x00, 0x00, 0x12, 0x9c, 0x20, 0x02, 0x03, 0x30,
+ 0x03, 0x1d, 0x7d, 0x02, 0xaf, 0x56, 0x12, 0x02, 0xc8, 0x90, 0x04,
+ 0x14, 0x74, 0x80, 0xf0, 0xe4, 0x90, 0x70, 0x13, 0xf0, 0xe5, 0x56,
+ 0xf4, 0x70, 0x03, 0x02, 0x12, 0x9c, 0x02, 0x12, 0x8f, 0x85, 0x56,
+ 0x41, 0xd2, 0x02, 0x02, 0x12, 0x9c, 0x90, 0x70, 0x11, 0xe0, 0x24,
+ 0xff, 0x92, 0x47, 0x02, 0x12, 0x9c, 0x90, 0x04, 0x04, 0xe0, 0x25,
+ 0xe0, 0x24, 0x5d, 0xf5, 0x57, 0x90, 0x70, 0x10, 0xe0, 0xff, 0x74,
+ 0x47, 0x25, 0x57, 0xf8, 0xc6, 0xef, 0xc6, 0x90, 0x70, 0x11, 0xe0,
+ 0xff, 0x74, 0x48, 0x25, 0x57, 0xf8, 0xc6, 0xef, 0xc6, 0xe4, 0xfd,
+ 0xaf, 0x56, 0x12, 0x02, 0xc8, 0x90, 0x04, 0x14, 0x74, 0x80, 0xf0,
+ 0xe4, 0x90, 0x70, 0x13, 0xf0, 0xe5, 0x56, 0xf4, 0x70, 0x03, 0x02,
+ 0x12, 0x9c, 0x02, 0x12, 0x8f, 0x90, 0x70, 0x11, 0xe0, 0x54, 0x1f,
+ 0xf5, 0x62, 0xe0, 0x54, 0x80, 0xf5, 0x64, 0x90, 0x70, 0x10, 0xe0,
+ 0xff, 0x7e, 0x00, 0x90, 0x04, 0x04, 0xe0, 0xb4, 0x61, 0x04, 0x7d,
+ 0x00, 0x80, 0x02, 0x7d, 0x07, 0xef, 0xc8, 0xed, 0xc8, 0x08, 0x80,
+ 0x05, 0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9, 0xf5, 0x61, 0x8e,
+ 0x60, 0x90, 0x70, 0x11, 0xe0, 0x54, 0x60, 0x24, 0xff, 0x92, 0x2d,
+ 0xe0, 0x54, 0x60, 0xc4, 0x13, 0x54, 0x07, 0x14, 0xf5, 0x63, 0x75,
+ 0x65, 0x80, 0x75, 0x66, 0x23, 0x75, 0x67, 0x06, 0x75, 0x68, 0x18,
+ 0x75, 0x69, 0x15, 0xad, 0x57, 0xaf, 0x56, 0x12, 0x02, 0xc8, 0x90,
+ 0x04, 0x14, 0x74, 0x80, 0xf0, 0xe4, 0x90, 0x70, 0x13, 0xf0, 0xe5,
+ 0x56, 0xf4, 0x70, 0x03, 0x02, 0x12, 0x9c, 0x02, 0x12, 0x8f, 0x90,
+ 0x70, 0x11, 0xe0, 0x54, 0x1f, 0xf5, 0x6e, 0x90, 0x70, 0x10, 0xe0,
+ 0xf5, 0x6b, 0x90, 0x70, 0x11, 0xe0, 0x54, 0x60, 0x24, 0xff, 0x92,
+ 0x2c, 0xe0, 0x54, 0x60, 0xc4, 0x13, 0x54, 0x07, 0x14, 0xf5, 0x6f,
+ 0x75, 0x71, 0x40, 0x75, 0x72, 0x24, 0x75, 0x73, 0x05, 0x75, 0x74,
+ 0x17, 0x75, 0x75, 0xe7, 0xad, 0x57, 0xaf, 0x56, 0x12, 0x02, 0xc8,
+ 0x90, 0x04, 0x14, 0x74, 0x80, 0xf0, 0xe4, 0x90, 0x70, 0x13, 0xf0,
+ 0xe5, 0x56, 0xf4, 0x70, 0x03, 0x02, 0x12, 0x9c, 0x02, 0x12, 0x8f,
+ 0x90, 0x70, 0x10, 0xe0, 0x60, 0x04, 0xd2, 0x1a, 0x80, 0x02, 0xd2,
+ 0x22, 0xad, 0x57, 0xaf, 0x56, 0x12, 0x02, 0xc8, 0x90, 0x04, 0x14,
+ 0x74, 0x80, 0xf0, 0xe4, 0x90, 0x70, 0x13, 0xf0, 0xe5, 0x56, 0xf4,
+ 0x70, 0x03, 0x02, 0x12, 0x9c, 0x02, 0x12, 0x8f, 0x90, 0x70, 0x10,
+ 0xe0, 0xfe, 0x90, 0x70, 0x11, 0xe0, 0xfd, 0xed, 0xf8, 0xe6, 0xf5,
+ 0x57, 0xfd, 0xaf, 0x56, 0x12, 0x02, 0xc8, 0x90, 0x04, 0x14, 0x74,
+ 0x80, 0xf0, 0xe4, 0x90, 0x70, 0x13, 0xf0, 0xe5, 0x56, 0xf4, 0x70,
+ 0x03, 0x02, 0x12, 0x9c, 0x80, 0x7d, 0x90, 0x70, 0x10, 0xe0, 0xfe,
+ 0x90, 0x70, 0x11, 0xe0, 0xfd, 0xed, 0xf5, 0x82, 0x8e, 0x83, 0xe0,
+ 0xf5, 0x57, 0xfd, 0xaf, 0x56, 0x12, 0x02, 0xc8, 0x90, 0x04, 0x14,
+ 0x74, 0x80, 0xf0, 0xe4, 0x90, 0x70, 0x13, 0xf0, 0xe5, 0x56, 0xf4,
+ 0x60, 0x62, 0x80, 0x53, 0xe4, 0xf5, 0x7a, 0x75, 0x7b, 0x01, 0xf5,
+ 0x6a, 0xc2, 0x2d, 0xf5, 0x23, 0xf5, 0x3b, 0xd2, 0x2e, 0xf5, 0x77,
+ 0xc2, 0x2c, 0xf5, 0x24, 0x75, 0x76, 0x18, 0xad, 0x57, 0xaf, 0x56,
+ 0x12, 0x02, 0xc8, 0x90, 0x04, 0x14, 0x74, 0x80, 0xf0, 0xe4, 0x90,
+ 0x70, 0x13, 0xf0, 0xe5, 0x56, 0xf4, 0x60, 0x30, 0x80, 0x21, 0x90,
+ 0x70, 0x10, 0xe0, 0x24, 0xff, 0x92, 0x4a, 0xd2, 0x05, 0xad, 0x57,
+ 0xaf, 0x56, 0x12, 0x02, 0xc8, 0x90, 0x04, 0x14, 0x74, 0x80, 0xf0,
+ 0xe4, 0x90, 0x70, 0x13, 0xf0, 0xe5, 0x56, 0xf4, 0x60, 0x0d, 0x90,
+ 0x70, 0x24, 0xe0, 0x44, 0x10, 0xf0, 0x90, 0x02, 0x2c, 0x74, 0xff,
+ 0xf0, 0xe5, 0x23, 0x24, 0xff, 0x92, 0x06, 0xe5, 0x24, 0x24, 0xff,
+ 0x92, 0x07, 0x22, 0x22, 0xe5, 0x76, 0x60, 0x04, 0x15, 0x76, 0x80,
+ 0x03, 0x75, 0x76, 0x18, 0xe5, 0x76, 0x70, 0x43, 0x30, 0x2d, 0x0d,
+ 0x7f, 0x6c, 0xad, 0x61, 0xac, 0x60, 0x12, 0x02, 0x7b, 0x50, 0x02,
+ 0xd2, 0x18, 0x30, 0x2c, 0x0b, 0x7f, 0x7c, 0xad, 0x6b, 0x12, 0x03,
+ 0x61, 0x50, 0x02, 0xd2, 0x20, 0xe5, 0x77, 0x60, 0x09, 0x7f, 0x7a,
+ 0x12, 0x03, 0x6f, 0x50, 0x02, 0xd2, 0x21, 0xe5, 0x6a, 0x60, 0x09,
+ 0x7f, 0x7a, 0x12, 0x03, 0x6f, 0x50, 0x02, 0xd2, 0x19, 0xe5, 0x77,
+ 0x60, 0x07, 0xe5, 0x7b, 0xb4, 0x03, 0x02, 0xd2, 0x23, 0xe5, 0x23,
+ 0x24, 0xff, 0x92, 0x06, 0xe5, 0x24, 0x24, 0xff, 0x92, 0x07, 0x90,
+ 0x70, 0x60, 0xe5, 0x7b, 0xf0, 0xa3, 0xe5, 0x6a, 0xf0, 0xa3, 0xe5,
+ 0x20, 0xf0, 0xa3, 0xe5, 0x23, 0xf0, 0xe5, 0x6c, 0xa3, 0xf0, 0xa3,
+ 0xe5, 0x6d, 0xf0, 0xa2, 0x2d, 0xe4, 0x33, 0xa3, 0xf0, 0xa3, 0xe5,
+ 0x62, 0xf0, 0x90, 0x70, 0x6b, 0xe5, 0x76, 0xf0, 0xe5, 0x53, 0x70,
+ 0x0e, 0xe5, 0x4f, 0x45, 0x4e, 0x60, 0x08, 0xe5, 0x4f, 0x15, 0x4f,
+ 0x70, 0x02, 0x15, 0x4e, 0x22, 0x22, 0x22, 0xc2, 0x4b, 0xc2, 0x4c,
+ 0xe5, 0x44, 0x12, 0x02, 0x4f, 0x13, 0x69, 0x00, 0x13, 0xc4, 0x04,
+ 0x13, 0xc0, 0x08, 0x13, 0xa8, 0x10, 0x13, 0x74, 0x20, 0x13, 0x86,
+ 0x60, 0x13, 0x91, 0xa0, 0x00, 0x00, 0x13, 0xc6, 0x85, 0x48, 0x43,
+ 0x85, 0x4a, 0x42, 0x85, 0x4c, 0x5e, 0x80, 0x52, 0xe5, 0x48, 0xc4,
+ 0x54, 0x0f, 0xf5, 0x43, 0xe5, 0x4a, 0xc4, 0x54, 0x0f, 0xf5, 0x42,
+ 0xe5, 0x4c, 0x80, 0x1b, 0x85, 0x49, 0x43, 0x85, 0x4b, 0x42, 0x85,
+ 0x4d, 0x5e, 0x80, 0x35, 0xe5, 0x49, 0xc4, 0x54, 0x0f, 0xf5, 0x43,
+ 0xe5, 0x4b, 0xc4, 0x54, 0x0f, 0xf5, 0x42, 0xe5, 0x4d, 0xc4, 0x54,
+ 0x0f, 0xf5, 0x5e, 0x80, 0x1e, 0xe5, 0x47, 0xb4, 0x04, 0x06, 0x53,
+ 0x5e, 0xfb, 0x75, 0x42, 0x09, 0xe5, 0x47, 0xb4, 0x05, 0x0e, 0x43,
+ 0x5e, 0x04, 0x75, 0x42, 0x09, 0x80, 0x06, 0xd2, 0x4b, 0x80, 0x02,
+ 0xd2, 0x4c, 0xe4, 0xf5, 0x4e, 0xf5, 0x4f, 0xf5, 0x27, 0xe5, 0x42,
+ 0xc4, 0x54, 0xf0, 0xff, 0xe5, 0x43, 0x54, 0x0f, 0x4f, 0xf5, 0x5f,
+ 0x90, 0x70, 0x44, 0xf0, 0xa3, 0xe5, 0x5e, 0xf0, 0xa3, 0xe5, 0x4a,
+ 0xf0, 0xa3, 0xe5, 0x48, 0xf0, 0xa3, 0xe5, 0x4c, 0xf0, 0xa3, 0xe5,
+ 0x44, 0xf0, 0xa3, 0xe5, 0x42, 0xf0, 0xa3, 0xe5, 0x43, 0xf0, 0xd2,
+ 0x60, 0x22, 0x90, 0x70, 0x40, 0xe0, 0x04, 0xf0, 0x90, 0x70, 0x42,
+ 0xe5, 0x47, 0xf0, 0xe5, 0x47, 0x60, 0x10, 0x24, 0xc0, 0x70, 0x03,
+ 0x12, 0x15, 0x28, 0x12, 0x14, 0x1e, 0xc2, 0xaf, 0xc2, 0x04, 0xd2,
+ 0xaf, 0x22, 0xc2, 0xaf, 0x90, 0x04, 0x14, 0xe0, 0x54, 0x0e, 0x60,
+ 0x14, 0xe5, 0x47, 0xb4, 0x02, 0x0b, 0xe5, 0x44, 0xb4, 0x20, 0x06,
+ 0x75, 0x4e, 0x08, 0x75, 0x4f, 0x00, 0xd2, 0x28, 0x80, 0x08, 0xe5,
+ 0x4e, 0x45, 0x4f, 0x24, 0xff, 0x92, 0x28, 0xd2, 0xaf, 0x90, 0x04,
+ 0x14, 0xe0, 0xa2, 0xe4, 0x92, 0x29, 0x74, 0x1e, 0xf0, 0xe5, 0x5f,
+ 0x54, 0x0f, 0xf5, 0x2d, 0xe5, 0x27, 0x70, 0x13, 0x30, 0x28, 0x05,
+ 0xe5, 0x5f, 0x20, 0xe5, 0x0b, 0x30, 0x29, 0x19, 0xe5, 0x5f, 0x54,
+ 0x30, 0xff, 0xbf, 0x30, 0x11, 0xe5, 0x27, 0x70, 0x05, 0x75, 0x27,
+ 0x0c, 0x80, 0x02, 0x15, 0x27, 0xd2, 0x6c, 0xd2, 0x6d, 0x80, 0x0f,
+ 0xe5, 0x5f, 0x30, 0xe6, 0x06, 0xc2, 0x6c, 0xd2, 0x6d, 0x80, 0x04,
+ 0xd2, 0x6c, 0xc2, 0x6d, 0xe5, 0x47, 0x64, 0x03, 0x70, 0x21, 0x30,
+ 0x4b, 0x06, 0xc2, 0x6c, 0xd2, 0x6d, 0x80, 0x18, 0xe5, 0x27, 0x70,
+ 0x03, 0x30, 0x4c, 0x11, 0xc2, 0x4c, 0xe5, 0x27, 0x70, 0x05, 0x75,
+ 0x27, 0x07, 0x80, 0x02, 0x15, 0x27, 0xd2, 0x6c, 0xd2, 0x6d, 0x90,
+ 0x70, 0x46, 0xe5, 0x2d, 0xf0, 0x20, 0x69, 0x07, 0xe5, 0x5e, 0x20,
+ 0xe0, 0x02, 0xb2, 0x68, 0x20, 0x6b, 0x07, 0xe5, 0x5e, 0x20, 0xe1,
+ 0x02, 0xb2, 0x6a, 0x20, 0x6d, 0x07, 0xe5, 0x5e, 0x20, 0xe2, 0x02,
+ 0xb2, 0x6c, 0x90, 0x70, 0x47, 0xe5, 0x2d, 0xf0, 0x75, 0x2e, 0x40,
+ 0x20, 0x69, 0x04, 0xa2, 0x68, 0x80, 0x0a, 0xe5, 0x46, 0x30, 0x68,
+ 0x04, 0xa2, 0xe3, 0x80, 0x01, 0x33, 0x92, 0x73, 0x92, 0x72, 0x20,
+ 0x6b, 0x04, 0xa2, 0x6a, 0x80, 0x0a, 0xe5, 0x46, 0x30, 0x6a, 0x04,
+ 0xa2, 0xe3, 0x80, 0x01, 0x33, 0x92, 0x75, 0x92, 0x74, 0x20, 0x6d,
+ 0x04, 0xa2, 0x6c, 0x80, 0x0a, 0xe5, 0x46, 0x30, 0x6c, 0x04, 0xa2,
+ 0xe3, 0x80, 0x01, 0x33, 0x92, 0x71, 0x92, 0x70, 0x90, 0x10, 0x2f,
+ 0xe5, 0x2e, 0xf0, 0x22, 0xe4, 0x90, 0x02, 0x29, 0xf0, 0x30, 0x47,
+ 0x04, 0xaf, 0x45, 0x80, 0x04, 0xe5, 0x45, 0xf4, 0xff, 0x90, 0x02,
+ 0x28, 0xef, 0xf0, 0x22, 0x8f, 0x50, 0xd2, 0x59, 0x22, 0x8f, 0x54,
+ 0xd2, 0x58, 0x22, 0xe4, 0xf5, 0x38, 0xc2, 0xaf, 0xe5, 0x51, 0x14,
+ 0x60, 0x45, 0x14, 0x60, 0x61, 0x24, 0x02, 0x60, 0x03, 0x02, 0x16,
+ 0x02, 0xd2, 0x59, 0x75, 0x55, 0x01, 0x90, 0x02, 0x08, 0xe0, 0x54,
+ 0xfe, 0xf0, 0xe0, 0x20, 0xe1, 0x22, 0x90, 0x04, 0x34, 0xe0, 0xb4,
+ 0x02, 0x1b, 0xa3, 0xe0, 0xb4, 0x02, 0x16, 0xa3, 0xe0, 0xb4, 0x02,
+ 0x11, 0x7f, 0x20, 0x12, 0x15, 0x3e, 0x90, 0x10, 0x04, 0xe0, 0x54,
+ 0xf3, 0xf0, 0x75, 0x51, 0x01, 0x80, 0x74, 0xe5, 0x50, 0x70, 0x05,
+ 0x75, 0x38, 0x03, 0x80, 0x6b, 0x90, 0x12, 0x00, 0xe0, 0x54, 0x03,
+ 0x70, 0x11, 0x7f, 0x20, 0x12, 0x15, 0x3e, 0x90, 0x02, 0x08, 0xe0,
+ 0x54, 0xfb, 0xf0, 0x75, 0x51, 0x02, 0x80, 0x52, 0xe5, 0x50, 0x70,
+ 0x02, 0x80, 0x47, 0x90, 0x02, 0x08, 0xe0, 0x20, 0xe3, 0x3c, 0x90,
+ 0x04, 0x37, 0xe0, 0x64, 0x22, 0x70, 0x34, 0x90, 0x12, 0x04, 0x74,
+ 0x0a, 0xf0, 0x90, 0x13, 0x28, 0xe0, 0x54, 0xf0, 0xf0, 0xa3, 0xe0,
+ 0x54, 0xf0, 0xf0, 0xa3, 0xe0, 0x54, 0xfa, 0xf0, 0x90, 0x04, 0x01,
+ 0xe0, 0x44, 0x10, 0xf0, 0xe0, 0x54, 0xf9, 0xf0, 0x90, 0x12, 0x04,
+ 0xe0, 0x44, 0x04, 0xf0, 0x75, 0x38, 0x01, 0x75, 0x55, 0x02, 0xe4,
+ 0xf5, 0x51, 0x80, 0x09, 0xe5, 0x50, 0x70, 0x05, 0x75, 0x38, 0x03,
+ 0xf5, 0x51, 0xe5, 0x38, 0x60, 0x15, 0xc2, 0x01, 0xe4, 0xf5, 0x51,
+ 0xc2, 0x59, 0xad, 0x38, 0xaf, 0x40, 0x12, 0x02, 0xc8, 0xe5, 0x38,
+ 0xb4, 0x03, 0x02, 0xd2, 0x03, 0xd2, 0xaf, 0x22, 0xc2, 0xaf, 0x30,
+ 0x01, 0x0e, 0xe4, 0xf5, 0x51, 0xc2, 0x59, 0xc2, 0x01, 0x7d, 0x02,
+ 0xaf, 0x40, 0x12, 0x02, 0xc8, 0xe5, 0x52, 0x14, 0x60, 0x17, 0x04,
+ 0x70, 0x5d, 0x90, 0x12, 0x04, 0xe0, 0x54, 0xfb, 0xf0, 0x7f, 0x20,
+ 0x12, 0x15, 0x43, 0x75, 0x52, 0x01, 0x75, 0x55, 0x03, 0x80, 0x49,
+ 0xe5, 0x54, 0x70, 0x45, 0x90, 0x04, 0x01, 0xe0, 0x44, 0x0e, 0xf0,
+ 0xe0, 0x54, 0xef, 0xf0, 0x90, 0x13, 0x28, 0xe0, 0x44, 0x0f, 0xf0,
+ 0xa3, 0xe0, 0x44, 0x0f, 0xf0, 0xa3, 0xe0, 0x44, 0x05, 0xf0, 0x90,
+ 0x12, 0x04, 0x74, 0x03, 0xf0, 0x90, 0x02, 0x08, 0xe0, 0x44, 0x05,
+ 0xf0, 0x90, 0x10, 0x04, 0xe0, 0x44, 0x0c, 0xf0, 0xe4, 0xf5, 0x52,
+ 0xf5, 0x55, 0x30, 0x02, 0x0b, 0xc2, 0x02, 0x7d, 0x01, 0xaf, 0x41,
+ 0x12, 0x02, 0xc8, 0x80, 0x02, 0xc2, 0x03, 0xd2, 0xaf, 0x22, 0x22,
+ 0x22, 0xc2, 0xaf, 0xe5, 0x6a, 0x70, 0x04, 0xe5, 0x77, 0x60, 0x03,
+ 0xd3, 0x80, 0x01, 0xc3, 0x92, 0x28, 0x90, 0x70, 0x2a, 0xe0, 0x30,
+ 0xe1, 0x2c, 0xe0, 0x13, 0x92, 0x29, 0x90, 0x70, 0x29, 0xe0, 0xff,
+ 0x90, 0x70, 0x28, 0xe0, 0xfd, 0xa2, 0x28, 0x92, 0x2a, 0xa2, 0x29,
+ 0x92, 0x2b, 0x12, 0x16, 0xe1, 0x50, 0x03, 0x20, 0x28, 0x05, 0x12,
+ 0x17, 0x61, 0x80, 0x07, 0x90, 0x70, 0x2a, 0xe0, 0x54, 0xfd, 0xf0,
+ 0xc2, 0x05, 0xd2, 0xaf, 0x22, 0x30, 0x2b, 0x49, 0x30, 0x2a, 0x46,
+ 0xbf, 0x41, 0x08, 0x90, 0x70, 0x28, 0xe5, 0x32, 0xf0, 0x80, 0x39,
+ 0xbf, 0x42, 0x08, 0x90, 0x70, 0x28, 0xe5, 0x33, 0xf0, 0x80, 0x2e,
+ 0xbf, 0x45, 0x08, 0x90, 0x70, 0x28, 0xe5, 0x35, 0xf0, 0x80, 0x23,
+ 0xbf, 0x46, 0x08, 0x90, 0x70, 0x28, 0xe5, 0x30, 0xf0, 0x80, 0x18,
+ 0xbf, 0x49, 0x08, 0x90, 0x70, 0x28, 0xe5, 0x31, 0xf0, 0x80, 0x0d,
+ 0xbf, 0x71, 0x08, 0x90, 0x70, 0x28, 0xe5, 0x2f, 0xf0, 0x80, 0x02,
+ 0xc3, 0x22, 0xd3, 0x22, 0x20, 0x2b, 0x2f, 0xbf, 0x41, 0x04, 0x8d,
+ 0x32, 0x80, 0x25, 0xbf, 0x42, 0x04, 0x8d, 0x33, 0x80, 0x1e, 0xbf,
+ 0x45, 0x04, 0x8d, 0x35, 0x80, 0x17, 0xbf, 0x46, 0x04, 0x8d, 0x30,
+ 0x80, 0x10, 0xbf, 0x49, 0x04, 0x8d, 0x31, 0x80, 0x09, 0xbf, 0x71,
+ 0x04, 0x8d, 0x2f, 0x80, 0x02, 0xc3, 0x22, 0xa2, 0x2a, 0x22, 0xc3,
+ 0x22, 0x90, 0x70, 0x28, 0xe0, 0x90, 0x10, 0x1c, 0xf0, 0x90, 0x70,
+ 0x29, 0xe0, 0x90, 0x10, 0x1d, 0xf0, 0x90, 0x70, 0x2a, 0xe0, 0x90,
+ 0x10, 0x1e, 0xf0, 0x90, 0x10, 0x1c, 0xe0, 0xf5, 0x38, 0x90, 0x10,
+ 0x1e, 0xe0, 0x20, 0xe1, 0xf3, 0x90, 0x10, 0x1c, 0xe0, 0x90, 0x70,
+ 0x28, 0xf0, 0x90, 0x10, 0x1d, 0xe0, 0x90, 0x70, 0x29, 0xf0, 0x90,
+ 0x10, 0x1e, 0xe0, 0x90, 0x70, 0x2a, 0xf0, 0x30, 0x4a, 0x0d, 0x90,
+ 0x70, 0x24, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x02, 0x2c, 0x74, 0xff,
+ 0xf0, 0x22, 0xc2, 0xaf, 0x90, 0x10, 0x1c, 0xed, 0xf0, 0xa3, 0xef,
+ 0xf0, 0xa3, 0x74, 0x0a, 0xf0, 0x90, 0x10, 0x1c, 0xe0, 0xf5, 0x3a,
+ 0x90, 0x10, 0x1e, 0xe0, 0x20, 0xe1, 0xf3, 0xd2, 0xaf, 0x22, 0xc2,
+ 0xaf, 0x90, 0x10, 0x1d, 0xef, 0xf0, 0xa3, 0x74, 0x0b, 0xf0, 0x90,
+ 0x10, 0x1c, 0xe0, 0xff, 0x90, 0x10, 0x1e, 0xe0, 0x20, 0xe1, 0xf4,
+ 0xd2, 0xaf, 0x22, 0x90, 0x13, 0x44, 0xe0, 0xf5, 0x34, 0xe4, 0xf0,
+ 0x90, 0x02, 0x29, 0xf0, 0x90, 0x02, 0x28, 0xe0, 0x44, 0x02, 0xf0,
+ 0x7d, 0x1d, 0x7f, 0x41, 0x12, 0x17, 0xaf, 0x7d, 0x60, 0x0f, 0x12,
+ 0x17, 0xaf, 0x7f, 0x71, 0x12, 0x17, 0xcd, 0xef, 0x44, 0x20, 0xfd,
+ 0x7f, 0x71, 0x02, 0x17, 0xaf, 0xe4, 0x90, 0x02, 0x29, 0xf0, 0x90,
+ 0x02, 0x28, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0x13, 0x44, 0xe0, 0xf5,
+ 0x34, 0xe4, 0xf0, 0x7f, 0x04, 0x12, 0x17, 0xcd, 0xef, 0x54, 0x18,
+ 0x60, 0x1d, 0xe5, 0x78, 0x24, 0x02, 0xf8, 0xe6, 0x60, 0x07, 0x90,
+ 0x70, 0x2c, 0xe0, 0xfd, 0x80, 0x05, 0x90, 0x70, 0x2d, 0xe0, 0xfd,
+ 0x7f, 0x42, 0x12, 0x17, 0xaf, 0x7d, 0x30, 0x80, 0x1b, 0xe5, 0x78,
+ 0x24, 0x02, 0xf8, 0xe6, 0x60, 0x07, 0x90, 0x70, 0x2e, 0xe0, 0xfd,
+ 0x80, 0x05, 0x90, 0x70, 0x2f, 0xe0, 0xfd, 0x7f, 0x42, 0x12, 0x17,
+ 0xaf, 0x7d, 0x28, 0x7f, 0x45, 0x12, 0x17, 0xaf, 0x0f, 0x12, 0x17,
+ 0xaf, 0x7d, 0x08, 0x7f, 0x49, 0x12, 0x17, 0xaf, 0x22, 0xe4, 0x90,
+ 0x02, 0x29, 0xf0, 0x90, 0x02, 0x28, 0xe0, 0x54, 0xfd, 0xf0, 0x90,
+ 0x13, 0x44, 0xe5, 0x34, 0xf0, 0x7f, 0x71, 0x12, 0x17, 0xcd, 0xef,
+ 0x54, 0xdf, 0xfd, 0x7f, 0x71, 0x12, 0x17, 0xaf, 0xad, 0x32, 0x7f,
+ 0x41, 0x12, 0x17, 0xaf, 0xad, 0x33, 0x0f, 0x12, 0x17, 0xaf, 0xad,
+ 0x35, 0x7f, 0x45, 0x12, 0x17, 0xaf, 0xad, 0x30, 0x0f, 0x12, 0x17,
+ 0xaf, 0xad, 0x31, 0x7f, 0x49, 0x02, 0x17, 0xaf, 0xe5, 0x6a, 0x14,
+ 0x60, 0x16, 0x04, 0x70, 0x31, 0x90, 0x70, 0x6c, 0xe0, 0x04, 0xf0,
+ 0x7f, 0x06, 0x12, 0x1b, 0x95, 0x50, 0x24, 0x75, 0x6a, 0x01, 0x75,
+ 0x78, 0x62, 0x90, 0x70, 0x6d, 0xe0, 0x04, 0xf0, 0x12, 0x19, 0x60,
+ 0x40, 0x13, 0x90, 0x70, 0x24, 0xe0, 0x44, 0x04, 0xf0, 0x90, 0x02,
+ 0x2c, 0x74, 0xff, 0xf0, 0xe4, 0xf5, 0x6a, 0x12, 0x1b, 0xae, 0xc2,
+ 0xaf, 0x53, 0x20, 0xbf, 0xd2, 0xaf, 0x22, 0xe5, 0x77, 0x14, 0x60,
+ 0x10, 0x04, 0x70, 0x52, 0x7f, 0x07, 0x12, 0x1b, 0x95, 0x50, 0x4b,
+ 0x75, 0x77, 0x01, 0x75, 0x78, 0x6e, 0x12, 0x19, 0x60, 0x40, 0x40,
+ 0xe5, 0x79, 0x24, 0x05, 0xff, 0x13, 0x13, 0x13, 0x54, 0x1f, 0xfe,
+ 0x24, 0x24, 0xf5, 0x82, 0xe4, 0x34, 0x70, 0xf5, 0x83, 0xe0, 0xfd,
+ 0xef, 0x54, 0x07, 0xff, 0x74, 0x01, 0xc8, 0xef, 0xc8, 0x08, 0x80,
+ 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x4d, 0xff, 0x74, 0x24, 0x2e, 0xf5,
+ 0x82, 0xe4, 0x34, 0x70, 0xf5, 0x83, 0xef, 0xf0, 0x90, 0x02, 0x2c,
+ 0x74, 0xff, 0xf0, 0xe4, 0xf5, 0x77, 0x12, 0x1b, 0xae, 0xc2, 0xaf,
+ 0x53, 0x20, 0x7f, 0xd2, 0xaf, 0x22, 0xe5, 0x78, 0x24, 0x04, 0xf8,
+ 0xe6, 0xf5, 0x38, 0x90, 0x70, 0x68, 0xf0, 0xf8, 0xe6, 0xff, 0xa3,
+ 0xf0, 0x90, 0x70, 0x6f, 0xe0, 0x04, 0xf0, 0xe5, 0x7b, 0x24, 0xfe,
+ 0x70, 0x03, 0x02, 0x1a, 0x26, 0x14, 0x70, 0x03, 0x02, 0x1a, 0x78,
+ 0x24, 0x02, 0x60, 0x03, 0x02, 0x1b, 0x94, 0xe4, 0xf5, 0x79, 0xef,
+ 0x20, 0xe0, 0x03, 0x02, 0x1a, 0x24, 0xa8, 0x38, 0xe6, 0x54, 0xfe,
+ 0xf6, 0xe5, 0x78, 0x04, 0xf8, 0xe6, 0x60, 0x4d, 0xc2, 0xaf, 0x75,
+ 0x7a, 0x05, 0xa8, 0x38, 0xe6, 0x54, 0xfb, 0xf6, 0xd2, 0xaf, 0xe5,
+ 0x78, 0x04, 0xf8, 0xe6, 0x64, 0x02, 0x70, 0x1c, 0x90, 0x77, 0x85,
+ 0xf0, 0xe5, 0x78, 0x24, 0x03, 0xf8, 0xe6, 0x90, 0x04, 0x10, 0xf0,
+ 0xe5, 0x78, 0x24, 0x03, 0xf8, 0xe6, 0xff, 0x90, 0x04, 0x10, 0xe0,
+ 0x5f, 0x70, 0xf2, 0x90, 0x77, 0x85, 0x74, 0xfe, 0xf0, 0xa8, 0x38,
+ 0xe6, 0x54, 0xfb, 0xf6, 0xe5, 0x78, 0x24, 0x03, 0xf8, 0xe6, 0x90,
+ 0x04, 0x10, 0xf0, 0x75, 0x7b, 0x02, 0x80, 0x33, 0xc2, 0xaf, 0xa8,
+ 0x78, 0xe6, 0xf5, 0x7a, 0xa8, 0x38, 0xe6, 0x54, 0xfb, 0xf6, 0xd2,
+ 0xaf, 0x90, 0x10, 0x04, 0xe0, 0x54, 0xfb, 0xf0, 0xe5, 0x6a, 0x60,
+ 0x06, 0x90, 0x17, 0x04, 0xe0, 0xf5, 0x39, 0xe5, 0x78, 0x24, 0x06,
+ 0xf8, 0xe6, 0xfe, 0x08, 0xe6, 0xca, 0xee, 0xca, 0xf9, 0x12, 0x02,
+ 0x75, 0x75, 0x7b, 0x03, 0xd3, 0x22, 0xa8, 0x38, 0xe6, 0x30, 0xe2,
+ 0x3b, 0x54, 0xfb, 0xf6, 0xc2, 0xaf, 0xa8, 0x78, 0xe6, 0xf5, 0x7a,
+ 0xa8, 0x38, 0xe6, 0x54, 0xfb, 0xf6, 0xd2, 0xaf, 0x90, 0x10, 0x04,
+ 0xe0, 0x54, 0xfb, 0xf0, 0xe5, 0x6a, 0x60, 0x06, 0x90, 0x17, 0x04,
+ 0xe0, 0xf5, 0x39, 0xe5, 0x78, 0x24, 0x06, 0xf8, 0xe6, 0xfe, 0x08,
+ 0xe6, 0xca, 0xee, 0xca, 0xf9, 0x12, 0x02, 0x75, 0xe4, 0xf5, 0x7d,
+ 0x75, 0x7b, 0x03, 0xd3, 0x22, 0xa8, 0x38, 0xe6, 0x30, 0xe1, 0x09,
+ 0x54, 0xfd, 0xf6, 0xe4, 0xf5, 0x79, 0x02, 0x1b, 0x8e, 0xd3, 0x22,
+ 0xe5, 0x77, 0x70, 0x03, 0x02, 0x1b, 0x6e, 0xa8, 0x38, 0xe6, 0x20,
+ 0xe3, 0x03, 0x02, 0x1b, 0x6e, 0x54, 0xf7, 0xf6, 0x7f, 0x73, 0x12,
+ 0x17, 0xcd, 0xef, 0x64, 0x02, 0x60, 0x03, 0x02, 0x1b, 0x28, 0x90,
+ 0x02, 0x29, 0xf0, 0x90, 0x02, 0x28, 0xe0, 0x44, 0x08, 0xf0, 0x7f,
+ 0x76, 0x12, 0x17, 0xcd, 0xe5, 0x7d, 0x25, 0xe0, 0x25, 0xe0, 0x24,
+ 0x00, 0xf5, 0x82, 0xe4, 0x34, 0x71, 0xf5, 0x83, 0xef, 0xf0, 0x7f,
+ 0x75, 0x12, 0x17, 0xcd, 0xe5, 0x7d, 0x25, 0xe0, 0x25, 0xe0, 0x24,
+ 0x01, 0xf5, 0x82, 0xe4, 0x34, 0x71, 0xf5, 0x83, 0xef, 0xf0, 0x7f,
+ 0x74, 0x12, 0x17, 0xcd, 0xe5, 0x7d, 0x25, 0xe0, 0x25, 0xe0, 0x24,
+ 0x02, 0xf5, 0x82, 0xe4, 0x34, 0x71, 0xf5, 0x83, 0xef, 0xf0, 0x7f,
+ 0x73, 0x12, 0x17, 0xcd, 0xe5, 0x7d, 0x25, 0xe0, 0x25, 0xe0, 0x24,
+ 0x03, 0xf5, 0x82, 0xe4, 0x34, 0x71, 0xf5, 0x83, 0xef, 0xf0, 0x7f,
+ 0x71, 0x12, 0x17, 0xcd, 0xef, 0x54, 0xdf, 0xfd, 0x7f, 0x71, 0x12,
+ 0x17, 0xaf, 0x7f, 0x71, 0x12, 0x17, 0xcd, 0xef, 0x44, 0x20, 0xfd,
+ 0x7f, 0x71, 0x12, 0x17, 0xaf, 0x75, 0x79, 0xff, 0xe4, 0x90, 0x02,
+ 0x29, 0xf0, 0x90, 0x02, 0x28, 0xe0, 0x54, 0xf7, 0xf0, 0x80, 0x44,
+ 0xe5, 0x7d, 0x25, 0xe0, 0x25, 0xe0, 0x24, 0x00, 0xf5, 0x82, 0xe4,
+ 0x34, 0x71, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x7d, 0x25, 0xe0, 0x25,
+ 0xe0, 0x24, 0x01, 0xf5, 0x82, 0xe4, 0x34, 0x71, 0xf5, 0x83, 0xe4,
+ 0xf0, 0xe5, 0x7d, 0x25, 0xe0, 0x25, 0xe0, 0x24, 0x02, 0xf5, 0x82,
+ 0xe4, 0x34, 0x71, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x7d, 0x25, 0xe0,
+ 0x25, 0xe0, 0x24, 0x03, 0xf5, 0x82, 0xe4, 0x34, 0x71, 0xf5, 0x83,
+ 0xe4, 0xf0, 0x05, 0x7d, 0xa8, 0x38, 0xe6, 0xff, 0x30, 0xe1, 0x1e,
+ 0x54, 0xfd, 0xf6, 0xe5, 0x79, 0xf4, 0x60, 0x07, 0x7f, 0x73, 0x12,
+ 0x17, 0xcd, 0x8f, 0x79, 0x12, 0x18, 0x7c, 0x90, 0x10, 0x04, 0xe0,
+ 0x44, 0x04, 0xf0, 0x75, 0x7b, 0x01, 0xc3, 0x22, 0xd3, 0x22, 0xa2,
+ 0x2e, 0xe4, 0x33, 0x90, 0x70, 0x6a, 0xf0, 0xc2, 0xaf, 0x30, 0x2e,
+ 0x06, 0xc2, 0x2e, 0xd2, 0xaf, 0xd3, 0x22, 0x8f, 0x3b, 0xd2, 0xaf,
+ 0xc3, 0x22, 0xc2, 0xaf, 0xe5, 0x3b, 0x60, 0x12, 0xff, 0x74, 0x01,
+ 0xc8, 0xef, 0xc8, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x42,
+ 0x20, 0xe4, 0xf5, 0x3b, 0xd2, 0x2e, 0xd2, 0xaf, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x65, 0x30
+};
diff --git a/sys/contrib/dev/ral/rt2860.fw.uu b/sys/contrib/dev/ral/rt2860.fw.uu
new file mode 100644
index 0000000..14b3f7b
--- /dev/null
+++ b/sys/contrib/dev/ral/rt2860.fw.uu
@@ -0,0 +1,202 @@
+# $FreeBSD$
+#
+# Copyright (c) 2005-2008, Ralink Technology Corp.
+# Paul Lin <paul_lin@ralinktech.com.tw>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+begin 644 rt2860.fw
+M`@#B`@+L(B+___\"`;W______P(`'O______`@%NP.#`\,"#P(+`T'70&,*O
+M,$4#$A`)D`06X##C#70(\.558`9D`V`"T@.0!!3@(.<#`@#5=(#PD'`2X/4V
+MD`0$X"3/8#`48$(DXF!'%&!5)"%P8.55)/Y@!Q1@""0"<`A]`8`H?0*`))!P
+M$.#U4(4V0-(!@#[E560#8`3E57`$?0*`"84V0=("@"FM5:\V$@+(@""0<!#@
+M]4>0<!'@]402$"6`!I!P$.#U1>3]KS82`LC2!)!P$^3PTJ_0T-""T(/0\-#@
+M,GA_Y/;8_76!?0(!*0(""^23H_CDDZ-``_:``?((W_2`*>23H_A4!R0,R,,S
+MQ%0/1"#(@T`$]%:``4;VW^2`"P$"!`@0($"`D`.:Y'X!DV"\H_]4/S#E"50?
+M_N23HV`!#L]4P"7@8*A`N.23H_KDDZ/XY).CR,6"R,K%@\KPH\C%@LC*Q8/*
+MW^G>YX"^P.#`\,"#P(+`T.C`X.G`X.K`X.O`X.S`X.W`X.[`X._`X,*O,$4#
+M$A`2TJ_0X/_0X/[0X/W0X/S0X/O0X/K0X/G0X/C0T-""T(/0\-#@,L#@P/#`
+M@\""P-!UT!#"KS!%`Q(0##!8"N548`055(`"PE@P60KE4&`$%5"``L)9U5,'
+M,&`$%4;2!#!%`Q(0#\*-TJ_0T-""T(/0\-#@,A(##C!%`Q(0`S`!!B`)`Q(0
+M'#`"!B`*`Q(0'S`#!B`+`Q(0'S`$!B`,`Q(0(B`3"2`1!N4K12Q@`].``<.2
+MJ1(#/H"_T(/0@OCDDW`2=`&3<`VCHY/X=`&3]8*(@^1S=`*3:&#OHZ.C@-^*
+M@XF"Y'/([\CF^@CF2F`,R._(".86&'`!%L,B[23__>PT_\COR/8(QNW&TR+"
+M0])%Y/4@]2'U4_5&]2OU+,)"]5'U4O55D`08=(#PD`0:=`CP(N_T8!_D_A(#
+M?>"T_Q(2`WWO\'0<+O6"Y#1P]8/M\"(.O@3C(L#@P/#`@\""P-!UT`C"KS!%
+M`Q(0!M*OT-#0@M"#T/#0X#+"KQ(`!A("HA(#)^3U(O5'D`0`=(#PTJ\B=8D"
+MY/6,]8KUB/6X]>AUD!C2C'6H!2(P10,2$!7E('`#(!`#,!$#0X<!(L[OSNY@
+M"'__$@.3'H#U(LCOR.9@`Q;#(NT4]M,BR._(YF`&%N8D_[,BPR)T%"[U@N0T
+M</6#(N^0`Y&3D`0`<PH8[V`#'X#Z(@$[`,&N`/______________________
+M____________________________________________________________
+M_____________________________________________\`F=`3`X,""P(-U
+M)@HBP"9T!,#@P(+`@W4F&"+_____________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________________
+M_____P(0*`(01`(010(2J0(2J@(310(31L,B__\"%4@"%AX"$_T"$T<P!08@
+M#0,2%ILP!@8@#@,2&+TP!P8@#P,2&/XB(I`$%.`@YP,"$IR0<!+@]5:0!`3@
+M$@)/$(<Q$+)1$+U2$+U3$+U4$7%@$/YA$;]B$/YC$>AP$A)Q$CQR$FZ````2
+MG"`"`S`#'7T"KU82`LB0!!1T@/#DD'`3\.56]'`#`A*<`A*/A59!T@("$IR0
+M<!'@)/^21P(2G)`$!.`EX"1=]5>0<!#@_W1')5?XQN_&D'`1X/]T2"57^,;O
+MQN3]KU82`LB0!!1T@/#DD'`3\.56]'`#`A*<`A*/D'`1X%0?]6+@5(#U9)!P
+M$.#_?@"0!`3@M&$$?0"``GT'[\CMR`B`!<,SSC/.V/GU88Y@D'`1X%1@)/^2
+M+>!48,035`<4]6-U98!U9B-U9P9U:!AU:16M5Z]6$@+(D`04=(#PY)!P$_#E
+M5O1P`P(2G`(2CY!P$>!4'_5ND'`0X/5KD'`1X%1@)/^2+.!48,035`<4]6]U
+M<4!U<B1U<P5U=!=U=>>M5Z]6$@+(D`04=(#PY)!P$_#E5O1P`P(2G`(2CY!P
+M$.!@!-(:@`+2(JU7KU82`LB0!!1T@/#DD'`3\.56]'`#`A*<`A*/D'`0X/Z0
+M<!'@_>WXYO57_:]6$@+(D`04=(#PY)!P$_#E5O1P`P(2G(!]D'`0X/Z0<!'@
+M_>WU@HZ#X/57_:]6$@+(D`04=(#PY)!P$_#E5O1@8H!3Y/5Z=7L!]6K"+?4C
+M]3O2+O5WPBSU)'5V&*U7KU82`LB0!!1T@/#DD'`3\.56]&`P@"&0<!#@)/^2
+M2M(%K5>O5A("R)`$%'2`\.20<!/PY5;T8`V0<"3@1!#PD`(L=/_PY2,D_Y(&
+MY20D_Y('(B+E=F`$%7:``W5V&.5V<$,P+0U_;*UAK&`2`GM0`M(8,"P+?WRM
+M:Q(#85`"TB#E=V`)?WH2`V]0`M(AY6I@"7]Z$@-O4`+2&>5W8`?E>[0#`M(C
+MY2,D_Y(&Y20D_Y('D'!@Y7OPH^5J\*/E(/"CY2/PY6RC\*/E;?"B+>0SH_"C
+MY6+PD'!KY7;PY5-P#N5/14Y@".5/%4]P`A5.(B(BPDO"3.5$$@)/$VD`$\0$
+M$\`($Z@0$W0@$X9@$Y&@```3QH5(0X5*0H5,7H!2Y4C$5`_U0^5*Q%0/]4+E
+M3(`;A4E#A4M"A4U>@#7E2<14#_5#Y4O$5`_U0N5-Q%0/]5Z`'N5'M`0&4U[[
+M=4()Y4>T!0Y#7@1U0@F`!M)+@`+23.3U3O5/]2?E0L14\/_E0U0/3_5?D'!$
+M\*/E7O"CY4KPH^5(\*/E3/"CY43PH^5"\*/E0_#28"*0<$#@!/"0<$+E1_#E
+M1V`0),!P`Q(5*!(4'L*OP@32KR+"KY`$%.!4#F`4Y4>T`@OE1+0@!G5."'5/
+M`-(H@`CE3D5/)/^2*-*OD`04X*+DDBET'O#E7U0/]2WE)W`3,"@%Y5\@Y0LP
+M*1GE7U0P_[\P$>4G<`5U)PR``A4GTFS2;8`/Y5\PY@;";-)M@`32;,)MY4=D
+M`W`A,$L&PFS2;8`8Y2=P`S!,$<),Y2=P!74G!X`"%2?2;-)MD'!&Y2WP(&D'
+MY5X@X`*R:"!K!^5>(.$"LFH@;0?E7B#B`K)LD'!'Y2WP=2Y`(&D$HFB`"N5&
+M,&@$HN.``3.2<Y)R(&L$HFJ`"N5&,&H$HN.``3.2=9)T(&T$HFR`"N5&,&P$
+MHN.``3.2<9)PD!`OY2[P(N20`BGP,$<$KT6`!.5%]/^0`BCO\"*/4-)9(H]4
+MTE@BY/4XPJ_E411@111@820"8`,"%@+26755`9`"".!4_O#@(.$BD`0TX+0"
+M&Z/@M`(6H^"T`A%_(!(5/I`0!.!4\_!U40&`=.50<`5U.`.`:Y`2`.!4`W`1
+M?R`2%3Z0`@C@5/OP=5$"@%+E4'`"@$>0`@C@(.,\D`0WX&0B<#20$@1T"O"0
+M$RC@5/#PH^!4\/"CX%3Z\)`$`>!$$/#@5/GPD!($X$0$\'4X`755`N3U48`)
+MY5!P!74X`_51Y3A@%<(!Y/51PEFM.*]`$@+(Y3BT`P+2`]*O(L*O,`$.Y/51
+MPEG"`7T"KT`2`LCE4A1@%P1P79`2!.!4^_!_(!(50W52`755`X!)Y51P19`$
+M`>!$#O#@5._PD!,HX$0/\*/@1`_PH^!$!?"0$@1T`_"0`@C@1`7PD!`$X$0,
+M\.3U4O55,`(+P@)]`:]!$@+(@`+"`]*O(B(BPJ_E:G`$Y7=@`].``<.2*)!P
+M*N`PX2S@$Y(ID'`IX/^0<"C@_:(HDBJB*9(K$A;A4`,@*`42%V&`!Y!P*N!4
+M_?#"!=*O(C`K23`J1K]!")!P*.4R\(`YOT((D'`HY3/P@"Z_10B0<"CE-?"`
+M([]&")!P*.4P\(`8OTD(D'`HY3'P@`V_<0B0<"CE+_"``L,BTR(@*R^_002-
+M,H`EOT($C3.`'K]%!(TU@!>_1@2-,(`0OTD$C3&`";]Q!(TO@`+#(J(J(L,B
+MD'`HX)`0'/"0<"G@D!`=\)!P*N"0$![PD!`<X/4XD!`>X"#A\Y`0'."0<"CP
+MD!`=X)!P*?"0$![@D'`J\#!*#9!P).!$`?"0`BQT__`BPJ^0$!SM\*/O\*-T
+M"O"0$!S@]3J0$![@(.'STJ\BPJ^0$!WO\*-T"_"0$!S@_Y`0'N`@X?32KR*0
+M$T3@]33D\)`"*?"0`BC@1`+P?1U_01(7KWU@#Q(7KW]Q$A?-[T0@_7]Q`A>O
+MY)`"*?"0`BC@1`+PD!-$X/4TY/!_!!(7S>]4&&`=Y7@D`OCF8`>0<"S@_8`%
+MD'`MX/U_0A(7KWTP@!OE>"0"^.9@!Y!P+N#]@`60<"_@_7]"$A>O?2A_11(7
+MKP\2%Z]]"'])$A>O(N20`BGPD`(HX%3]\)`31.4T\']Q$A?-[U3?_7]Q$A>O
+MK3)_01(7KZTS#Q(7KZTU?T42%Z^M,`\2%Z^M,7])`A>OY6H48!8$<#&0<&S@
+M!/!_!A(;E5`D=6H!=7ABD'!MX`3P$AE@0!.0<"3@1`3PD`(L=/_PY/5J$ANN
+MPJ]3(+_2KR+E=Q1@$`1P4G\'$AN54$MU=P%U>&X2&6!`0.5Y)`7_$Q,35!_^
+M)"3U@N0T</6#X/WO5`?_=`'([\@(@`+#,]C\3?]T)"[U@N0T</6#[_"0`BQT
+M__#D]7<2&Z["KU,@?]*O(N5X)`3XYO4XD'!H\/CF_Z/PD'!OX`3PY7LD_G`#
+M`AHF%'`#`AIX)`)@`P(;E.3U>>\@X`,"&B2H..94_O;E>`3XYF!-PJ]U>@6H
+M..94^_;2K^5X!/CF9`)P')!WA?#E>"0#^.:0!!#PY7@D`_CF_Y`$$.!?</*0
+M=X5T_O"H..94^_;E>"0#^.:0!!#P=7L"@#/"KZAXYO5ZJ#CF5/OVTJ^0$`3@
+M5/OPY6I@!I`7!.#U.>5X)`;XYOX(YLKNROD2`G5U>P/3(J@XYC#B.U3[]L*O
+MJ'CF]7JH..94^_;2KY`0!.!4^_#E:F`&D!<$X/4YY7@D!OCF_@CFRN[*^1("
+M=>3U?75[`],BJ#CF,.$)5/WVY/5Y`AN.TR+E=W`#`AMNJ#CF(.,#`AMN5/?V
+M?W,2%\WO9`)@`P(;*)`"*?"0`BC@1`CP?W82%\WE?27@)>`D`/6"Y#1Q]8/O
+M\']U$A?-Y7TEX"7@)`'U@N0T<?6#[_!_=!(7S>5])>`EX"0"]8+D-''U@^_P
+M?W,2%\WE?27@)>`D`_6"Y#1Q]8/O\']Q$A?-[U3?_7]Q$A>O?W$2%\WO1"#]
+M?W$2%Z]U>?_DD`(I\)`"*.!4]_"`1.5])>`EX"0`]8+D-''U@^3PY7TEX"7@
+M)`'U@N0T<?6#Y/#E?27@)>`D`O6"Y#1Q]8/D\.5])>`EX"0#]8+D-''U@^3P
+M!7VH..;_,.$>5/WVY7GT8`=_<Q(7S8]Y$AA\D!`$X$0$\'5[`<,BTR*B+N0S
+MD'!J\,*O,"X&PB[2K],BCSO2K\,BPJ_E.V`2_W0!R._("(`"PS/8_$(@Y/4[
+MTB[2KR(`````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````````````#]
+"93``
+`
+end
diff --git a/sys/dev/ath/ah_osdep.c b/sys/dev/ath/ah_osdep.c
index dcc5765..249c260 100644
--- a/sys/dev/ath/ah_osdep.c
+++ b/sys/dev/ath/ah_osdep.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
diff --git a/sys/dev/ath/ah_osdep.h b/sys/dev/ath/ah_osdep.h
index a82bd02..2e2a561 100644
--- a/sys/dev/ath/ah_osdep.h
+++ b/sys/dev/ath/ah_osdep.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
diff --git a/sys/dev/ath/ath_rate/amrr/amrr.c b/sys/dev/ath/ath_rate/amrr/amrr.c
index 00ae568..c230bdf 100644
--- a/sys/dev/ath/ath_rate/amrr/amrr.c
+++ b/sys/dev/ath/ath_rate/amrr/amrr.c
@@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
* Mathieu Lacage, Hossein Manshaei, Thierry Turletti
*/
#include "opt_inet.h"
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -65,7 +66,6 @@ __FBSDID("$FreeBSD$");
#include <net/if.h>
#include <net/if_media.h>
#include <net/if_arp.h>
-#include <net/ethernet.h> /* XXX for ether_sprintf */
#include <net80211/ieee80211_var.h>
@@ -80,21 +80,10 @@ __FBSDID("$FreeBSD$");
#include <dev/ath/ath_rate/amrr/amrr.h>
#include <contrib/dev/ath/ah_desc.h>
-#define AMRR_DEBUG
-#ifdef AMRR_DEBUG
-#define DPRINTF(sc, _fmt, ...) do { \
- if (sc->sc_debug & 0x10) \
- printf(_fmt, __VA_ARGS__); \
-} while (0)
-#else
-#define DPRINTF(sc, _fmt, ...)
-#endif
-
static int ath_rateinterval = 1000; /* rate ctl interval (ms) */
static int ath_rate_max_success_threshold = 10;
static int ath_rate_min_success_threshold = 1;
-static void ath_ratectl(void *);
static void ath_rate_update(struct ath_softc *, struct ieee80211_node *,
int rate);
static void ath_rate_ctl_start(struct ath_softc *, struct ieee80211_node *);
@@ -104,7 +93,6 @@ void
ath_rate_node_init(struct ath_softc *sc, struct ath_node *an)
{
/* NB: assumed to be zero'd by caller */
- ath_rate_update(sc, &an->an_node, 0);
}
void
@@ -166,6 +154,11 @@ ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
amn->amn_tx_try3_cnt++;
amn->amn_tx_failure_cnt++;
}
+ if (amn->amn_interval != 0 &&
+ ticks - amn->amn_ticks > amn->amn_interval) {
+ ath_rate_ctl(sc, &an->an_node);
+ amn->amn_ticks = ticks;
+ }
}
void
@@ -176,7 +169,7 @@ ath_rate_newassoc(struct ath_softc *sc, struct ath_node *an, int isnew)
}
static void
-node_reset (struct amrr_node *amn)
+node_reset(struct amrr_node *amn)
{
amn->amn_tx_try0_cnt = 0;
amn->amn_tx_try1_cnt = 0;
@@ -200,17 +193,18 @@ ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)
{
struct ath_node *an = ATH_NODE(ni);
struct amrr_node *amn = ATH_NODE_AMRR(an);
+ struct ieee80211vap *vap = ni->ni_vap;
const HAL_RATE_TABLE *rt = sc->sc_currates;
u_int8_t rix;
KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
- DPRINTF(sc, "%s: set xmit rate for %s to %dM\n",
- __func__, ether_sprintf(ni->ni_macaddr),
+ IEEE80211_NOTE(vap, IEEE80211_MSG_RATECTL, ni,
+ "%s: set xmit rate to %dM", __func__,
ni->ni_rates.rs_nrates > 0 ?
(ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL) / 2 : 0);
- ni->ni_txrate = rate;
+ amn->amn_rix = rate;
/*
* Before associating a node has no rate set setup
* so we can't calculate any transmit codes to use.
@@ -219,8 +213,8 @@ ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)
* lowest hardware rate.
*/
if (ni->ni_rates.rs_nrates > 0) {
- amn->amn_tx_rix0 = sc->sc_rixmap[
- ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL];
+ ni->ni_txrate = ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL;
+ amn->amn_tx_rix0 = sc->sc_rixmap[ni->ni_txrate];
amn->amn_tx_rate0 = rt->info[amn->amn_tx_rix0].rateCode;
amn->amn_tx_rate0sp = amn->amn_tx_rate0 |
rt->info[amn->amn_tx_rix0].shortPreamble;
@@ -268,7 +262,12 @@ ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)
amn->amn_tx_rate3 = amn->amn_tx_rate3sp = 0;
}
}
- node_reset (amn);
+ node_reset(amn);
+
+ amn->amn_interval = ath_rateinterval;
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ amn->amn_interval /= 2;
+ amn->amn_interval = (amn->amn_interval * hz) / 1000;
}
/*
@@ -278,11 +277,12 @@ static void
ath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni)
{
#define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ath_node *an = ATH_NODE(ni);
+ const struct ieee80211_txparam *tp = an->an_tp;
int srate;
KASSERT(ni->ni_rates.rs_nrates > 0, ("no rates"));
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
+ if (tp == NULL || tp->ucastrate == IEEE80211_FIXED_RATE_NONE) {
/*
* No fixed rate is requested. For 11b start with
* the highest negotiated rate; otherwise, for 11g
@@ -308,7 +308,7 @@ ath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni)
*/
/* NB: the rate set is assumed sorted */
srate = ni->ni_rates.rs_nrates - 1;
- for (; srate >= 0 && RATE(srate) != ic->ic_fixed_rate; srate--)
+ for (; srate >= 0 && RATE(srate) != tp->ucastrate; srate--)
;
}
/*
@@ -333,22 +333,20 @@ ath_rate_cb(void *arg, struct ieee80211_node *ni)
* Reset the rate control state for each 802.11 state transition.
*/
void
-ath_rate_newstate(struct ath_softc *sc, enum ieee80211_state state)
+ath_rate_newstate(struct ieee80211vap *vap, enum ieee80211_state state)
{
- struct amrr_softc *asc = (struct amrr_softc *) sc->sc_rc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ath_softc *sc = ic->ic_ifp->if_softc;
struct ieee80211_node *ni;
- if (state == IEEE80211_S_INIT) {
- callout_stop(&asc->timer);
+ if (state == IEEE80211_S_INIT)
return;
- }
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
/*
* Reset local xmit state; this is really only
* meaningful when operating in station mode.
*/
- ni = ic->ic_bss;
+ ni = vap->iv_bss;
if (state == IEEE80211_S_RUN) {
ath_rate_ctl_start(sc, ni);
} else {
@@ -362,20 +360,7 @@ ath_rate_newstate(struct ath_softc *sc, enum ieee80211_state state)
* tx rate state of each node.
*/
ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_cb, sc);
- ath_rate_update(sc, ic->ic_bss, 0);
- }
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE &&
- state == IEEE80211_S_RUN) {
- int interval;
- /*
- * Start the background rate control thread if we
- * are not configured to use a fixed xmit rate.
- */
- interval = ath_rateinterval;
- if (ic->ic_opmode == IEEE80211_M_STA)
- interval /= 2;
- callout_reset(&asc->timer, (interval * hz) / 1000,
- ath_ratectl, sc->sc_ifp);
+ ath_rate_update(sc, vap->iv_bss, 0);
}
}
@@ -387,7 +372,7 @@ ath_rate_ctl(void *arg, struct ieee80211_node *ni)
{
struct ath_softc *sc = arg;
struct amrr_node *amn = ATH_NODE_AMRR(ATH_NODE (ni));
- int old_rate;
+ int rix;
#define is_success(amn) \
(amn->amn_tx_try1_cnt < (amn->amn_tx_try0_cnt/10))
@@ -395,52 +380,53 @@ ath_rate_ctl(void *arg, struct ieee80211_node *ni)
(amn->amn_tx_try0_cnt > 10)
#define is_failure(amn) \
(amn->amn_tx_try1_cnt > (amn->amn_tx_try0_cnt/3))
-#define is_max_rate(ni) \
-((ni->ni_txrate + 1) >= ni->ni_rates.rs_nrates)
-#define is_min_rate(ni) \
-(ni->ni_txrate == 0)
- old_rate = ni->ni_txrate;
+ rix = amn->amn_rix;
- DPRINTF (sc, "cnt0: %d cnt1: %d cnt2: %d cnt3: %d -- threshold: %d\n",
- amn->amn_tx_try0_cnt,
- amn->amn_tx_try1_cnt,
- amn->amn_tx_try2_cnt,
- amn->amn_tx_try3_cnt,
- amn->amn_success_threshold);
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "cnt0: %d cnt1: %d cnt2: %d cnt3: %d -- threshold: %d",
+ amn->amn_tx_try0_cnt, amn->amn_tx_try1_cnt, amn->amn_tx_try2_cnt,
+ amn->amn_tx_try3_cnt, amn->amn_success_threshold);
if (is_success (amn) && is_enough (amn)) {
amn->amn_success++;
if (amn->amn_success == amn->amn_success_threshold &&
- !is_max_rate (ni)) {
+ rix + 1 < ni->ni_rates.rs_nrates) {
amn->amn_recovery = 1;
amn->amn_success = 0;
- ni->ni_txrate++;
- DPRINTF (sc, "increase rate to %d\n", ni->ni_txrate);
+ rix++;
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "increase rate to %d", rix);
} else {
amn->amn_recovery = 0;
}
} else if (is_failure (amn)) {
amn->amn_success = 0;
- if (!is_min_rate (ni)) {
+ if (rix > 0) {
if (amn->amn_recovery) {
/* recovery failure. */
amn->amn_success_threshold *= 2;
amn->amn_success_threshold = min (amn->amn_success_threshold,
(u_int)ath_rate_max_success_threshold);
- DPRINTF (sc, "decrease rate recovery thr: %d\n", amn->amn_success_threshold);
+ IEEE80211_NOTE(ni->ni_vap,
+ IEEE80211_MSG_RATECTL, ni,
+ "decrease rate recovery thr: %d",
+ amn->amn_success_threshold);
} else {
/* simple failure. */
amn->amn_success_threshold = ath_rate_min_success_threshold;
- DPRINTF (sc, "decrease rate normal thr: %d\n", amn->amn_success_threshold);
+ IEEE80211_NOTE(ni->ni_vap,
+ IEEE80211_MSG_RATECTL, ni,
+ "decrease rate normal thr: %d",
+ amn->amn_success_threshold);
}
amn->amn_recovery = 0;
- ni->ni_txrate--;
+ rix--;
} else {
amn->amn_recovery = 0;
}
}
- if (is_enough (amn) || old_rate != ni->ni_txrate) {
+ if (is_enough (amn) || rix != amn->amn_rix) {
/* reset counters. */
amn->amn_tx_try0_cnt = 0;
amn->amn_tx_try1_cnt = 0;
@@ -448,33 +434,9 @@ ath_rate_ctl(void *arg, struct ieee80211_node *ni)
amn->amn_tx_try3_cnt = 0;
amn->amn_tx_failure_cnt = 0;
}
- if (old_rate != ni->ni_txrate) {
- ath_rate_update(sc, ni, ni->ni_txrate);
- }
-}
-
-static void
-ath_ratectl(void *arg)
-{
- struct ifnet *ifp = arg;
- struct ath_softc *sc = ifp->if_softc;
- struct amrr_softc *asc = (struct amrr_softc *) sc->sc_rc;
- struct ieee80211com *ic = &sc->sc_ic;
- int interval;
-
- if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- sc->sc_stats.ast_rate_calls++;
-
- if (ic->ic_opmode == IEEE80211_M_STA)
- ath_rate_ctl(sc, ic->ic_bss); /* NB: no reference */
- else
- ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_ctl, sc);
+ if (rix != amn->amn_rix) {
+ ath_rate_update(sc, ni, rix);
}
- interval = ath_rateinterval;
- if (ic->ic_opmode == IEEE80211_M_STA)
- interval /= 2;
- callout_reset(&asc->timer, (interval * hz) / 1000,
- ath_ratectl, sc->sc_ifp);
}
static void
@@ -504,7 +466,6 @@ ath_rate_attach(struct ath_softc *sc)
if (asc == NULL)
return NULL;
asc->arc.arc_space = sizeof(struct amrr_node);
- callout_init(&asc->timer, CALLOUT_MPSAFE);
ath_rate_sysctlattach(sc);
return &asc->arc;
@@ -515,7 +476,6 @@ ath_rate_detach(struct ath_ratectrl *arc)
{
struct amrr_softc *asc = (struct amrr_softc *) arc;
- callout_drain(&asc->timer);
free(asc, M_DEVBUF);
}
diff --git a/sys/dev/ath/ath_rate/amrr/amrr.h b/sys/dev/ath/ath_rate/amrr/amrr.h
index cb5d135..c97a007 100644
--- a/sys/dev/ath/ath_rate/amrr/amrr.h
+++ b/sys/dev/ath/ath_rate/amrr/amrr.h
@@ -43,11 +43,13 @@
/* per-device state */
struct amrr_softc {
struct ath_ratectrl arc; /* base state */
- struct callout timer; /* periodic timer */
};
/* per-node state */
struct amrr_node {
+ int amn_rix; /* current rate index */
+ int amn_ticks; /* time of last update */
+ int amn_interval; /* update interval (ticks) */
/* AMRR statistics for this node */
u_int amn_tx_try0_cnt;
u_int amn_tx_try1_cnt;
diff --git a/sys/dev/ath/ath_rate/onoe/onoe.c b/sys/dev/ath/ath_rate/onoe/onoe.c
index eb5759e..25bac09 100644
--- a/sys/dev/ath/ath_rate/onoe/onoe.c
+++ b/sys/dev/ath/ath_rate/onoe/onoe.c
@@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$");
* Atsushi Onoe's rate control algorithm.
*/
#include "opt_inet.h"
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -68,19 +69,6 @@ __FBSDID("$FreeBSD$");
#include <dev/ath/ath_rate/onoe/onoe.h>
#include <contrib/dev/ath/ah_desc.h>
-#define ONOE_DEBUG
-#ifdef ONOE_DEBUG
-enum {
- ATH_DEBUG_RATE = 0x00000010, /* rate control */
-};
-#define DPRINTF(sc, _fmt, ...) do { \
- if (sc->sc_debug & ATH_DEBUG_RATE) \
- printf(_fmt, __VA_ARGS__); \
-} while (0)
-#else
-#define DPRINTF(sc, _fmt, ...)
-#endif
-
/*
* Default parameters for the rate control algorithm. These are
* all tunable with sysctls. The rate controller runs periodically
@@ -104,7 +92,6 @@ static int ath_rateinterval = 1000; /* rate ctl interval (ms) */
static int ath_rate_raise = 10; /* add credit threshold */
static int ath_rate_raise_threshold = 10; /* rate ctl raise threshold */
-static void ath_ratectl(void *);
static void ath_rate_update(struct ath_softc *, struct ieee80211_node *,
int rate);
static void ath_rate_ctl_start(struct ath_softc *, struct ieee80211_node *);
@@ -114,7 +101,6 @@ void
ath_rate_node_init(struct ath_softc *sc, struct ath_node *an)
{
/* NB: assumed to be zero'd by caller */
- ath_rate_update(sc, &an->an_node, 0);
}
void
@@ -163,6 +149,10 @@ ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
on->on_tx_err++;
on->on_tx_retr += ts->ts_shortretry
+ ts->ts_longretry;
+ if (on->on_interval != 0 && ticks - on->on_ticks > on->on_interval) {
+ ath_rate_ctl(sc, &an->an_node);
+ on->on_ticks = ticks;
+ }
}
void
@@ -177,17 +167,17 @@ ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)
{
struct ath_node *an = ATH_NODE(ni);
struct onoe_node *on = ATH_NODE_ONOE(an);
+ struct ieee80211vap *vap = ni->ni_vap;
const HAL_RATE_TABLE *rt = sc->sc_currates;
u_int8_t rix;
KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
- DPRINTF(sc, "%s: set xmit rate for %s to %dM\n",
- __func__, ether_sprintf(ni->ni_macaddr),
- ni->ni_rates.rs_nrates > 0 ?
+ IEEE80211_NOTE(vap, IEEE80211_MSG_RATECTL, ni,
+ "%s: set xmit rate to %dM", __func__,
+ ni->ni_rates.rs_nrates > 0 ?
(ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL) / 2 : 0);
- ni->ni_txrate = rate;
/*
* Before associating a node has no rate set setup
* so we can't calculate any transmit codes to use.
@@ -197,8 +187,9 @@ ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)
*/
if (ni->ni_rates.rs_nrates == 0)
goto done;
- on->on_tx_rix0 = sc->sc_rixmap[
- ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL];
+ on->on_rix = rate;
+ ni->ni_txrate = ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL;
+ on->on_tx_rix0 = sc->sc_rixmap[ni->ni_txrate];
on->on_tx_rate0 = rt->info[on->on_tx_rix0].rateCode;
on->on_tx_rate0sp = on->on_tx_rate0 |
@@ -246,6 +237,11 @@ ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)
}
done:
on->on_tx_ok = on->on_tx_err = on->on_tx_retr = on->on_tx_upper = 0;
+
+ on->on_interval = ath_rateinterval;
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ on->on_interval /= 2;
+ on->on_interval = (on->on_interval * hz) / 1000;
}
/*
@@ -255,11 +251,12 @@ static void
ath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni)
{
#define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ath_node *an = ATH_NODE(ni);
+ const struct ieee80211_txparam *tp = an->an_tp;
int srate;
KASSERT(ni->ni_rates.rs_nrates > 0, ("no rates"));
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
+ if (tp == NULL || tp->ucastrate == IEEE80211_FIXED_RATE_NONE) {
/*
* No fixed rate is requested. For 11b start with
* the highest negotiated rate; otherwise, for 11g
@@ -285,7 +282,7 @@ ath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni)
*/
/* NB: the rate set is assumed sorted */
srate = ni->ni_rates.rs_nrates - 1;
- for (; srate >= 0 && RATE(srate) != ic->ic_fixed_rate; srate--)
+ for (; srate >= 0 && RATE(srate) != tp->ucastrate; srate--)
;
}
/*
@@ -310,22 +307,20 @@ ath_rate_cb(void *arg, struct ieee80211_node *ni)
* Reset the rate control state for each 802.11 state transition.
*/
void
-ath_rate_newstate(struct ath_softc *sc, enum ieee80211_state state)
+ath_rate_newstate(struct ieee80211vap *vap, enum ieee80211_state state)
{
- struct onoe_softc *osc = (struct onoe_softc *) sc->sc_rc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ath_softc *sc = ic->ic_ifp->if_softc;
struct ieee80211_node *ni;
- if (state == IEEE80211_S_INIT) {
- callout_stop(&osc->timer);
+ if (state == IEEE80211_S_INIT)
return;
- }
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
/*
* Reset local xmit state; this is really only
* meaningful when operating in station mode.
*/
- ni = ic->ic_bss;
+ ni = vap->iv_bss;
if (state == IEEE80211_S_RUN) {
ath_rate_ctl_start(sc, ni);
} else {
@@ -339,20 +334,7 @@ ath_rate_newstate(struct ath_softc *sc, enum ieee80211_state state)
* tx rate state of each node.
*/
ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_cb, sc);
- ath_rate_update(sc, ic->ic_bss, 0);
- }
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE &&
- state == IEEE80211_S_RUN) {
- int interval;
- /*
- * Start the background rate control thread if we
- * are not configured to use a fixed xmit rate.
- */
- interval = ath_rateinterval;
- if (ic->ic_opmode == IEEE80211_M_STA)
- interval /= 2;
- callout_reset(&osc->timer, (interval * hz) / 1000,
- ath_ratectl, sc->sc_ifp);
+ ath_rate_update(sc, vap->iv_bss, 0);
}
}
@@ -386,12 +368,11 @@ ath_rate_ctl(void *arg, struct ieee80211_node *ni)
on->on_tx_retr < (on->on_tx_ok * ath_rate_raise) / 100)
dir = 1;
- DPRINTF(sc, "%s: ok %d err %d retr %d upper %d dir %d\n",
- ether_sprintf(ni->ni_macaddr),
- on->on_tx_ok, on->on_tx_err, on->on_tx_retr,
- on->on_tx_upper, dir);
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "ok %d err %d retr %d upper %d dir %d",
+ on->on_tx_ok, on->on_tx_err, on->on_tx_retr, on->on_tx_upper, dir);
- nrate = ni->ni_txrate;
+ nrate = on->on_rix;
switch (dir) {
case 0:
if (enough && on->on_tx_upper > 0)
@@ -416,10 +397,10 @@ ath_rate_ctl(void *arg, struct ieee80211_node *ni)
break;
}
- if (nrate != ni->ni_txrate) {
- DPRINTF(sc, "%s: %dM -> %dM (%d ok, %d err, %d retr)\n",
- __func__,
- (rs->rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL) / 2,
+ if (nrate != on->on_rix) {
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "%s: %dM -> %dM (%d ok, %d err, %d retr)", __func__,
+ ni->ni_txrate / 2,
(rs->rs_rates[nrate] & IEEE80211_RATE_VAL) / 2,
on->on_tx_ok, on->on_tx_err, on->on_tx_retr);
ath_rate_update(sc, ni, nrate);
@@ -428,30 +409,6 @@ ath_rate_ctl(void *arg, struct ieee80211_node *ni)
}
static void
-ath_ratectl(void *arg)
-{
- struct ifnet *ifp = arg;
- struct ath_softc *sc = ifp->if_softc;
- struct onoe_softc *osc = (struct onoe_softc *) sc->sc_rc;
- struct ieee80211com *ic = &sc->sc_ic;
- int interval;
-
- if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- sc->sc_stats.ast_rate_calls++;
-
- if (ic->ic_opmode == IEEE80211_M_STA)
- ath_rate_ctl(sc, ic->ic_bss); /* NB: no reference */
- else
- ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_ctl, sc);
- }
- interval = ath_rateinterval;
- if (ic->ic_opmode == IEEE80211_M_STA)
- interval /= 2;
- callout_reset(&osc->timer, (interval * hz) / 1000,
- ath_ratectl, sc->sc_ifp);
-}
-
-static void
ath_rate_sysctlattach(struct ath_softc *sc)
{
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
@@ -478,7 +435,6 @@ ath_rate_attach(struct ath_softc *sc)
if (osc == NULL)
return NULL;
osc->arc.arc_space = sizeof(struct onoe_node);
- callout_init(&osc->timer, CALLOUT_MPSAFE);
ath_rate_sysctlattach(sc);
return &osc->arc;
@@ -489,7 +445,6 @@ ath_rate_detach(struct ath_ratectrl *arc)
{
struct onoe_softc *osc = (struct onoe_softc *) arc;
- callout_drain(&osc->timer);
free(osc, M_DEVBUF);
}
diff --git a/sys/dev/ath/ath_rate/onoe/onoe.h b/sys/dev/ath/ath_rate/onoe/onoe.h
index 4eef862..27bbe94 100644
--- a/sys/dev/ath/ath_rate/onoe/onoe.h
+++ b/sys/dev/ath/ath_rate/onoe/onoe.h
@@ -38,11 +38,14 @@
/* per-device state */
struct onoe_softc {
struct ath_ratectrl arc; /* base state */
- struct callout timer; /* periodic timer */
};
/* per-node state */
struct onoe_node {
+ int on_rix; /* current rate index */
+ int on_ticks; /* time of last update */
+ int on_interval; /* update interval (ticks) */
+
u_int on_tx_ok; /* tx ok pkt */
u_int on_tx_err; /* tx !ok pkt */
u_int on_tx_retr; /* tx retry count */
diff --git a/sys/dev/ath/ath_rate/sample/sample.c b/sys/dev/ath/ath_rate/sample/sample.c
index 180ef82..60a81ed 100644
--- a/sys/dev/ath/ath_rate/sample/sample.c
+++ b/sys/dev/ath/ath_rate/sample/sample.c
@@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
* John Bicket's SampleRate control algorithm.
*/
#include "opt_inet.h"
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -61,7 +62,6 @@ __FBSDID("$FreeBSD$");
#include <net/if.h>
#include <net/if_media.h>
#include <net/if_arp.h>
-#include <net/ethernet.h> /* XXX for ether_sprintf */
#include <net80211/ieee80211_var.h>
@@ -76,23 +76,6 @@ __FBSDID("$FreeBSD$");
#include <dev/ath/ath_rate/sample/sample.h>
#include <contrib/dev/ath/ah_desc.h>
-#define SAMPLE_DEBUG
-#ifdef SAMPLE_DEBUG
-enum {
- ATH_DEBUG_NODE = 0x00080000, /* node management */
- ATH_DEBUG_RATE = 0x00000010, /* rate control */
- ATH_DEBUG_ANY = 0xffffffff
-};
-#define DPRINTF(sc, m, fmt, ...) do { \
- if (sc->sc_debug & (m)) \
- printf(fmt, __VA_ARGS__); \
-} while (0)
-#else
-#define DPRINTF(sc, m, fmt, ...) do { \
- (void) sc; \
-} while (0)
-#endif
-
/*
* This file is an implementation of the SampleRate algorithm
* in "Bit-rate Selection in Wireless Networks"
@@ -152,14 +135,12 @@ rate_to_ndx(struct sample_node *sn, int rate) {
void
ath_rate_node_init(struct ath_softc *sc, struct ath_node *an)
{
- DPRINTF(sc, ATH_DEBUG_NODE, "%s:\n", __func__);
/* NB: assumed to be zero'd by caller */
}
void
ath_rate_node_cleanup(struct ath_softc *sc, struct ath_node *an)
{
- DPRINTF(sc, ATH_DEBUG_NODE, "%s:\n", __func__);
}
@@ -258,7 +239,8 @@ ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,
{
struct sample_node *sn = ATH_NODE_SAMPLE(an);
struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc);
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
int ndx, size_bin, mrr, best_ndx, change_rates;
unsigned average_tx_time;
@@ -323,10 +305,11 @@ ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,
if (change_rates) {
if (best_ndx != sn->current_rate[size_bin]) {
- DPRINTF(sc, ATH_DEBUG_RATE,
-"%s: %s size %d switch rate %d (%d/%d) -> %d (%d/%d) after %d packets mrr %d\n",
+ IEEE80211_NOTE(an->an_node.ni_vap,
+ IEEE80211_MSG_RATECTL,
+ &an->an_node,
+"%s: size %d switch rate %d (%d/%d) -> %d (%d/%d) after %d packets mrr %d",
__func__,
- ether_sprintf(an->an_node.ni_macaddr),
packet_size_bins[size_bin],
sn->rates[sn->current_rate[size_bin]].rate,
sn->stats[size_bin][sn->current_rate[size_bin]].average_tx_time,
@@ -340,16 +323,13 @@ ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,
sn->packets_since_switch[size_bin] = 0;
sn->current_rate[size_bin] = best_ndx;
sn->ticks_since_switch[size_bin] = ticks;
- }
- ndx = sn->current_rate[size_bin];
- sn->packets_since_switch[size_bin]++;
- if (size_bin == 0) {
/*
- * set the visible txrate for this node
- * to the rate of small packets
+ * Set the visible txrate for this node.
*/
- an->an_node.ni_txrate = ndx;
+ an->an_node.ni_txrate = sn->rates[best_ndx].rate;
}
+ ndx = sn->current_rate[size_bin];
+ sn->packets_since_switch[size_bin]++;
}
}
@@ -494,9 +474,10 @@ update_stats(struct ath_softc *sc, struct ath_node *an,
if (ndx0 == sn->current_sample_ndx[size_bin]) {
- DPRINTF(sc, ATH_DEBUG_RATE,
-"%s: %s size %d %s sample rate %d tries (%d/%d) tt %d avg_tt (%d/%d)\n",
- __func__, ether_sprintf(an->an_node.ni_macaddr),
+ IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
+ &an->an_node,
+"%s: size %d %s sample rate %d tries (%d/%d) tt %d avg_tt (%d/%d)",
+ __func__,
size,
status ? "FAIL" : "OK",
rate, short_tries, tries, tt,
@@ -511,7 +492,8 @@ void
ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
const struct ath_buf *bf)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct sample_node *sn = ATH_NODE_SAMPLE(an);
const struct ath_tx_status *ts = &bf->bf_status.ds_txstat;
const struct ath_desc *ds0 = &bf->bf_desc[0];
@@ -526,9 +508,10 @@ ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
frame_size = 1500;
if (sn->num_rates <= 0) {
- DPRINTF(sc, ATH_DEBUG_RATE,
- "%s: %s size %d %s rate/try %d/%d no rates yet\n",
- __func__, ether_sprintf(an->an_node.ni_macaddr),
+ IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
+ &an->an_node,
+ "%s: size %d %s rate/try %d/%d no rates yet",
+ __func__,
bin_to_size(size_to_bin(frame_size)),
ts->ts_status ? "FAIL" : "OK",
short_tries, long_tries);
@@ -541,9 +524,9 @@ ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
/*
* Only one rate was used; optimize work.
*/
- DPRINTF(sc, ATH_DEBUG_RATE,
- "%s: %s size %d %s rate/try %d/%d/%d\n",
- __func__, ether_sprintf(an->an_node.ni_macaddr),
+ IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
+ &an->an_node, "%s: size %d %s rate/try %d/%d/%d",
+ __func__,
bin_to_size(size_to_bin(frame_size)),
ts->ts_status ? "FAIL" : "OK",
final_rate, short_tries, long_tries);
@@ -591,9 +574,10 @@ ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
tries3 = MS(ds0->ds_ctl2, AR_XmitDataTries3);
ndx3 = rate_to_ndx(sn, rate3);
- DPRINTF(sc, ATH_DEBUG_RATE,
-"%s: %s size %d finaltsidx %d tries %d %s rate/try [%d/%d %d/%d %d/%d %d/%d]\n",
- __func__, ether_sprintf(an->an_node.ni_macaddr),
+ IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
+ &an->an_node,
+"%s: size %d finaltsidx %d tries %d %s rate/try [%d/%d %d/%d %d/%d %d/%d]",
+ __func__,
bin_to_size(size_to_bin(frame_size)),
finalTSIdx,
long_tries,
@@ -658,8 +642,6 @@ ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
void
ath_rate_newassoc(struct ath_softc *sc, struct ath_node *an, int isnew)
{
- DPRINTF(sc, ATH_DEBUG_NODE, "%s: %s isnew %d\n", __func__,
- ether_sprintf(an->an_node.ni_macaddr), isnew);
if (isnew)
ath_rate_ctl_reset(sc, &an->an_node);
}
@@ -671,15 +653,15 @@ static void
ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni)
{
#define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
- struct ieee80211com *ic = &sc->sc_ic;
struct ath_node *an = ATH_NODE(ni);
+ const struct ieee80211_txparam *tp = an->an_tp;
struct sample_node *sn = ATH_NODE_SAMPLE(an);
const HAL_RATE_TABLE *rt = sc->sc_currates;
int x, y, srate;
KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
sn->static_rate_ndx = -1;
- if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
+ if (tp != NULL && tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
/*
* A fixed rate is to be used; ic_fixed_rate is the
* IEEE code for this rate (sans basic bit). Convert this
@@ -688,7 +670,7 @@ ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni)
*/
/* NB: the rate set is assumed sorted */
srate = ni->ni_rates.rs_nrates - 1;
- for (; srate >= 0 && RATE(srate) != ic->ic_fixed_rate; srate--)
+ for (; srate >= 0 && RATE(srate) != tp->ucastrate; srate--)
;
/*
* The fixed rate may not be available due to races
@@ -700,32 +682,34 @@ ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni)
sn->static_rate_ndx = srate;
}
- DPRINTF(sc, ATH_DEBUG_RATE, "%s: %s size 1600 rate/tt",
- __func__, ether_sprintf(ni->ni_macaddr));
-
sn->num_rates = ni->ni_rates.rs_nrates;
for (x = 0; x < ni->ni_rates.rs_nrates; x++) {
sn->rates[x].rate = ni->ni_rates.rs_rates[x] & IEEE80211_RATE_VAL;
sn->rates[x].rix = sc->sc_rixmap[sn->rates[x].rate];
if (sn->rates[x].rix == 0xff) {
- DPRINTF(sc, ATH_DEBUG_RATE,
- "%s: ignore bogus rix at %d\n", __func__, x);
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "%s: ignore bogus rix at %d", __func__, x);
continue;
}
sn->rates[x].rateCode = rt->info[sn->rates[x].rix].rateCode;
sn->rates[x].shortPreambleRateCode =
rt->info[sn->rates[x].rix].rateCode |
rt->info[sn->rates[x].rix].shortPreamble;
-
- DPRINTF(sc, ATH_DEBUG_RATE, " %d/%d", sn->rates[x].rate,
- calc_usecs_unicast_packet(sc, 1600, sn->rates[x].rix, 0,0));
}
- DPRINTF(sc, ATH_DEBUG_RATE, "%s\n", "");
-
- /* set the visible bit-rate to the lowest one available */
- ni->ni_txrate = 0;
- sn->num_rates = ni->ni_rates.rs_nrates;
-
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg(ni->ni_vap, IEEE80211_MSG_RATECTL)) {
+ ieee80211_note(ni->ni_vap, "[%6D] %s: size 1600 rate/tt",
+ __func__, ni->ni_macaddr, ":");
+ for (x = 0; x < sn->num_rates; x++) {
+ if (sn->rates[x].rix == 0xff)
+ continue;
+ printf(" %d/%d", sn->rates[x].rate,
+ calc_usecs_unicast_packet(sc, 1600,
+ sn->rates[x].rix, 0,0));
+ }
+ printf("\n");
+ }
+#endif
for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
int size = bin_to_size(y);
int ndx = 0;
@@ -756,9 +740,8 @@ ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni)
sn->current_rate[y] = ndx;
}
- DPRINTF(sc, ATH_DEBUG_RATE,
- "%s: %s %d rates %d%sMbps (%dus)- %d%sMbps (%dus)\n",
- __func__, ether_sprintf(ni->ni_macaddr),
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "%s: %d rates %d%sMbps (%dus)- %d%sMbps (%dus)", __func__,
sn->num_rates,
sn->rates[0].rate/2, sn->rates[0].rate % 0x1 ? ".5" : "",
sn->stats[1][0].perfect_tx_time,
@@ -767,10 +750,11 @@ ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni)
sn->stats[1][sn->num_rates-1].perfect_tx_time
);
- if (sn->static_rate_ndx != -1)
- ni->ni_txrate = sn->static_rate_ndx;
+ /* set the visible bit-rate */
+ if (sn->static_rate_ndx != -1)
+ ni->ni_txrate = sn->rates[sn->static_rate_ndx].rate;
else
- ni->ni_txrate = sn->current_rate[0];
+ ni->ni_txrate = sn->rates[0].rate;
#undef RATE
}
@@ -786,18 +770,19 @@ rate_cb(void *arg, struct ieee80211_node *ni)
* Reset the rate control state for each 802.11 state transition.
*/
void
-ath_rate_newstate(struct ath_softc *sc, enum ieee80211_state state)
+ath_rate_newstate(struct ieee80211vap *vap, enum ieee80211_state state)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ath_softc *sc = ic->ic_ifp->if_softc;
if (state == IEEE80211_S_RUN) {
- if (ic->ic_opmode != IEEE80211_M_STA) {
+ if (vap->iv_opmode != IEEE80211_M_STA) {
/*
* Sync rates for associated stations and neighbors.
*/
ieee80211_iterate_nodes(&ic->ic_sta, rate_cb, sc);
}
- ath_rate_newassoc(sc, ATH_NODE(ic->ic_bss), 1);
+ ath_rate_newassoc(sc, ATH_NODE(vap->iv_bss), 1);
}
}
@@ -822,7 +807,6 @@ ath_rate_attach(struct ath_softc *sc)
{
struct sample_softc *osc;
- DPRINTF(sc, ATH_DEBUG_ANY, "%s:\n", __func__);
osc = malloc(sizeof(struct sample_softc), M_DEVBUF, M_NOWAIT|M_ZERO);
if (osc == NULL)
return NULL;
diff --git a/sys/dev/ath/ath_rate/sample/sample.h b/sys/dev/ath/ath_rate/sample/sample.h
index 1e9377e..1ea96c4 100644
--- a/sys/dev/ath/ath_rate/sample/sample.h
+++ b/sys/dev/ath/ath_rate/sample/sample.h
@@ -155,12 +155,13 @@ static unsigned calc_usecs_unicast_packet(struct ath_softc *sc,
int length,
int rix, int short_retries, int long_retries) {
const HAL_RATE_TABLE *rt = sc->sc_currates;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
int rts, cts;
unsigned t_slot = 20;
unsigned t_difs = 50;
unsigned t_sifs = 10;
- struct ieee80211com *ic = &sc->sc_ic;
int tt = 0;
int x = 0;
int cw = WIFI_CW_MIN;
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c
index fd02770..0d5ef91 100644
--- a/sys/dev/ath/if_ath.c
+++ b/sys/dev/ath/if_ath.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -84,6 +84,22 @@ __FBSDID("$FreeBSD$");
#include <dev/ath/ath_tx99/ath_tx99.h>
#endif
+/*
+ * ATH_BCBUF determines the number of vap's that can transmit
+ * beacons and also (currently) the number of vap's that can
+ * have unique mac addresses/bssid. When staggering beacons
+ * 4 is probably a good max as otherwise the beacons become
+ * very closely spaced and there is limited time for cab q traffic
+ * to go out. You can burst beacons instead but that is not good
+ * for stations in power save and at some point you really want
+ * another radio (and channel).
+ *
+ * The limit on the number of mac addresses is tied to our use of
+ * the U/L bit and tracking addresses in a byte; it would be
+ * worthwhile to allow more for applications like proxy sta.
+ */
+CTASSERT(ATH_BCBUF <= 8);
+
/* unaligned little endian access */
#define LE_READ_2(p) \
((u_int16_t) \
@@ -99,49 +115,59 @@ enum {
ATH_LED_POLL,
};
+static struct ieee80211vap *ath_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void ath_vap_delete(struct ieee80211vap *);
static void ath_init(void *);
static void ath_stop_locked(struct ifnet *);
static void ath_stop(struct ifnet *);
static void ath_start(struct ifnet *);
static int ath_reset(struct ifnet *);
+static int ath_reset_vap(struct ieee80211vap *, u_long);
static int ath_media_change(struct ifnet *);
static void ath_watchdog(struct ifnet *);
static int ath_ioctl(struct ifnet *, u_long, caddr_t);
static void ath_fatal_proc(void *, int);
static void ath_rxorn_proc(void *, int);
+static void ath_bmiss_vap(struct ieee80211vap *);
static void ath_bmiss_proc(void *, int);
-static int ath_key_alloc(struct ieee80211com *,
+static int ath_key_alloc(struct ieee80211vap *,
const struct ieee80211_key *,
ieee80211_keyix *, ieee80211_keyix *);
-static int ath_key_delete(struct ieee80211com *,
+static int ath_key_delete(struct ieee80211vap *,
const struct ieee80211_key *);
-static int ath_key_set(struct ieee80211com *, const struct ieee80211_key *,
+static int ath_key_set(struct ieee80211vap *, const struct ieee80211_key *,
const u_int8_t mac[IEEE80211_ADDR_LEN]);
-static void ath_key_update_begin(struct ieee80211com *);
-static void ath_key_update_end(struct ieee80211com *);
+static void ath_key_update_begin(struct ieee80211vap *);
+static void ath_key_update_end(struct ieee80211vap *);
+static void ath_update_mcast(struct ifnet *);
+static void ath_update_promisc(struct ifnet *);
static void ath_mode_init(struct ath_softc *);
static void ath_setslottime(struct ath_softc *);
static void ath_updateslot(struct ifnet *);
static int ath_beaconq_setup(struct ath_hal *);
static int ath_beacon_alloc(struct ath_softc *, struct ieee80211_node *);
-static void ath_beacon_update(struct ieee80211com *, int item);
+static void ath_beacon_update(struct ieee80211vap *, int item);
static void ath_beacon_setup(struct ath_softc *, struct ath_buf *);
static void ath_beacon_proc(void *, int);
+static struct ath_buf *ath_beacon_generate(struct ath_softc *,
+ struct ieee80211vap *);
static void ath_bstuck_proc(void *, int);
+static void ath_beacon_return(struct ath_softc *, struct ath_buf *);
static void ath_beacon_free(struct ath_softc *);
-static void ath_beacon_config(struct ath_softc *);
+static void ath_beacon_config(struct ath_softc *, struct ieee80211vap *);
static void ath_descdma_cleanup(struct ath_softc *sc,
struct ath_descdma *, ath_bufhead *);
static int ath_desc_alloc(struct ath_softc *);
static void ath_desc_free(struct ath_softc *);
static struct ieee80211_node *ath_node_alloc(struct ieee80211_node_table *);
static void ath_node_free(struct ieee80211_node *);
-static int8_t ath_node_getrssi(const struct ieee80211_node *);
static void ath_node_getsignal(const struct ieee80211_node *,
int8_t *, int8_t *);
static int ath_rxbuf_init(struct ath_softc *, struct ath_buf *);
-static void ath_recv_mgmt(struct ieee80211com *ic, struct mbuf *m,
- struct ieee80211_node *ni,
+static void ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
int subtype, int rssi, int noise, u_int32_t rstamp);
static void ath_setdefantenna(struct ath_softc *, u_int);
static void ath_rx_proc(void *, int);
@@ -157,6 +183,7 @@ static int ath_tx_start(struct ath_softc *, struct ieee80211_node *,
static void ath_tx_proc_q0(void *, int);
static void ath_tx_proc_q0123(void *, int);
static void ath_tx_proc(void *, int);
+static void ath_tx_draintxq(struct ath_softc *, struct ath_txq *);
static int ath_chan_set(struct ath_softc *, struct ieee80211_channel *);
static void ath_draintxq(struct ath_softc *);
static void ath_stoprecv(struct ath_softc *);
@@ -166,13 +193,16 @@ static void ath_scan_start(struct ieee80211com *);
static void ath_scan_end(struct ieee80211com *);
static void ath_set_channel(struct ieee80211com *);
static void ath_calibrate(void *);
-static int ath_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static int ath_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static void ath_setup_stationkey(struct ieee80211_node *);
static void ath_newassoc(struct ieee80211_node *, int);
-static int ath_getchannels(struct ath_softc *,
- HAL_REG_DOMAIN, HAL_CTRY_CODE, HAL_BOOL, HAL_BOOL);
+static int ath_setregdomain(struct ieee80211com *,
+ struct ieee80211_regdomain *, int,
+ struct ieee80211_channel []);
+static void ath_getradiocaps(struct ieee80211com *, int *,
+ struct ieee80211_channel []);
+static int ath_getchannels(struct ath_softc *);
static void ath_led_event(struct ath_softc *, int);
-static void ath_update_txpow(struct ath_softc *);
static int ath_rate_setup(struct ath_softc *, u_int mode);
static void ath_setcurmode(struct ath_softc *, enum ieee80211_phymode);
@@ -189,21 +219,6 @@ SYSCTL_DECL(_hw_ath);
static int ath_calinterval = 30; /* calibrate every 30 secs */
SYSCTL_INT(_hw_ath, OID_AUTO, calibrate, CTLFLAG_RW, &ath_calinterval,
0, "chip calibration interval (secs)");
-static int ath_outdoor = AH_TRUE; /* outdoor operation */
-SYSCTL_INT(_hw_ath, OID_AUTO, outdoor, CTLFLAG_RW, &ath_outdoor,
- 0, "outdoor operation");
-TUNABLE_INT("hw.ath.outdoor", &ath_outdoor);
-static int ath_xchanmode = AH_TRUE; /* extended channel use */
-SYSCTL_INT(_hw_ath, OID_AUTO, xchanmode, CTLFLAG_RW, &ath_xchanmode,
- 0, "extended channel mode");
-TUNABLE_INT("hw.ath.xchanmode", &ath_xchanmode);
-static int ath_countrycode = CTRY_DEFAULT; /* country code */
-SYSCTL_INT(_hw_ath, OID_AUTO, countrycode, CTLFLAG_RW, &ath_countrycode,
- 0, "country code");
-TUNABLE_INT("hw.ath.countrycode", &ath_countrycode);
-static int ath_regdomain = 0; /* regulatory domain */
-SYSCTL_INT(_hw_ath, OID_AUTO, regdomain, CTLFLAG_RD, &ath_regdomain,
- 0, "regulatory domain");
static int ath_rxbuf = ATH_RXBUF; /* # rx buffers to allocate */
SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RW, &ath_rxbuf,
@@ -273,19 +288,20 @@ int
ath_attach(u_int16_t devid, struct ath_softc *sc)
{
struct ifnet *ifp;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
struct ath_hal *ah = NULL;
HAL_STATUS status;
int error = 0, i;
DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid);
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(sc->sc_dev, "can not if_alloc()\n");
error = ENOSPC;
goto bad;
}
+ ic = ifp->if_l2com;
/* set these up early for if_printf use */
if_initname(ifp, device_get_name(sc->sc_dev),
@@ -342,13 +358,9 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
ath_hal_keyreset(ah, i);
/*
- * Collect the channel list using the default country
- * code and including outdoor channels. The 802.11 layer
- * is resposible for filtering this list based on settings
- * like the phy mode.
+ * Collect the default channel list.
*/
- error = ath_getchannels(sc, ath_regdomain, ath_countrycode,
- ath_outdoor != 0, ath_xchanmode != 0);
+ error = ath_getchannels(sc);
if (error != 0)
goto bad;
@@ -378,7 +390,6 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
goto bad;
}
callout_init(&sc->sc_cal_ch, CALLOUT_MPSAFE);
- callout_init(&sc->sc_dfs_ch, CALLOUT_MPSAFE);
ATH_TXBUF_LOCK_INIT(sc);
@@ -412,8 +423,6 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
error = EIO;
goto bad2;
}
- /* NB: s/w q, qnum used only by WITNESS */
- ath_txq_init(sc, &sc->sc_mcastq, HAL_NUM_TX_QUEUES+1);
/* NB: insure BK queue is the lowest priority h/w queue */
if (!ath_tx_setup(sc, WME_AC_BK, HAL_WME_AC_BK)) {
if_printf(ifp, "unable to setup xmit queue for %s traffic!\n",
@@ -497,10 +506,6 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
IFQ_SET_READY(&ifp->if_snd);
ic->ic_ifp = ifp;
- ic->ic_reset = ath_reset;
- ic->ic_newassoc = ath_newassoc;
- ic->ic_updateslot = ath_updateslot;
- ic->ic_wme.wme_update = ath_wme_update;
/* XXX not right but it's not used anywhere important */
ic->ic_phytype = IEEE80211_T_OFDM;
ic->ic_opmode = IEEE80211_M_STA;
@@ -509,6 +514,7 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
| IEEE80211_C_HOSTAP /* hostap mode */
| IEEE80211_C_MONITOR /* monitor mode */
| IEEE80211_C_AHDEMO /* adhoc demo mode */
+ | IEEE80211_C_WDS /* 4-address traffic works */
| IEEE80211_C_SHPREAMBLE /* short preamble supported */
| IEEE80211_C_SHSLOT /* short slot time supported */
| IEEE80211_C_WPA /* capable of WPA1+WPA2 */
@@ -519,22 +525,22 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
* Query the hal to figure out h/w crypto support.
*/
if (ath_hal_ciphersupported(ah, HAL_CIPHER_WEP))
- ic->ic_caps |= IEEE80211_C_WEP;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP;
if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_OCB))
- ic->ic_caps |= IEEE80211_C_AES;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_OCB;
if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_CCM))
- ic->ic_caps |= IEEE80211_C_AES_CCM;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_CCM;
if (ath_hal_ciphersupported(ah, HAL_CIPHER_CKIP))
- ic->ic_caps |= IEEE80211_C_CKIP;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_CKIP;
if (ath_hal_ciphersupported(ah, HAL_CIPHER_TKIP)) {
- ic->ic_caps |= IEEE80211_C_TKIP;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIP;
/*
* Check if h/w does the MIC and/or whether the
* separate key cache entries are required to
* handle both tx+rx MIC keys.
*/
if (ath_hal_ciphersupported(ah, HAL_CIPHER_MIC))
- ic->ic_caps |= IEEE80211_C_TKIPMIC;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC;
/*
* If the h/w supports storing tx+rx MIC keys
* in one cache slot automatically enable use.
@@ -542,6 +548,13 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
if (ath_hal_hastkipsplit(ah) ||
!ath_hal_settkipsplit(ah, AH_FALSE))
sc->sc_splitmic = 1;
+ /*
+ * If the h/w can do TKIP MIC together with WME then
+ * we use it; otherwise we force the MIC to be done
+ * in software by the net80211 layer.
+ */
+ if (ath_hal_haswmetkipmic(ah))
+ sc->sc_wmetkipmic = 1;
}
sc->sc_hasclrkey = ath_hal_ciphersupported(ah, HAL_CIPHER_CLR);
sc->sc_mcastkey = ath_hal_getmcastkeysearch(ah);
@@ -578,9 +591,11 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
*/
if (ath_hal_hasbursting(ah))
ic->ic_caps |= IEEE80211_C_BURST;
+ sc->sc_hasbmask = ath_hal_hasbssidmask(ah);
+ sc->sc_hastsfadd = ath_hal_hastsfadjust(ah);
if (ath_hal_hasfastframes(ah))
ic->ic_caps |= IEEE80211_C_FF;
- if (ath_hal_getwirelessmodes(ah, ath_countrycode) & (HAL_MODE_108G|HAL_MODE_TURBO))
+ if (ath_hal_getwirelessmodes(ah, ic->ic_regdomain.country) & (HAL_MODE_108G|HAL_MODE_TURBO))
ic->ic_caps |= IEEE80211_C_TURBOP;
/*
@@ -602,33 +617,33 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
/* get mac address from hardware */
ath_hal_getmac(ah, ic->ic_myaddr);
+ if (sc->sc_hasbmask)
+ ath_hal_getbssidmask(ah, sc->sc_hwbssidmask);
+ /* NB: used to size node table key mapping array */
+ ic->ic_max_keyix = sc->sc_keymax;
/* call MI attach routine. */
ieee80211_ifattach(ic);
- sc->sc_opmode = ic->ic_opmode;
+ ic->ic_setregdomain = ath_setregdomain;
+ ic->ic_getradiocaps = ath_getradiocaps;
+ sc->sc_opmode = HAL_M_STA;
+
/* override default methods */
+ ic->ic_newassoc = ath_newassoc;
+ ic->ic_updateslot = ath_updateslot;
+ ic->ic_wme.wme_update = ath_wme_update;
+ ic->ic_vap_create = ath_vap_create;
+ ic->ic_vap_delete = ath_vap_delete;
+ ic->ic_raw_xmit = ath_raw_xmit;
+ ic->ic_update_mcast = ath_update_mcast;
+ ic->ic_update_promisc = ath_update_promisc;
ic->ic_node_alloc = ath_node_alloc;
sc->sc_node_free = ic->ic_node_free;
ic->ic_node_free = ath_node_free;
- ic->ic_node_getrssi = ath_node_getrssi;
ic->ic_node_getsignal = ath_node_getsignal;
- sc->sc_recv_mgmt = ic->ic_recv_mgmt;
- ic->ic_recv_mgmt = ath_recv_mgmt;
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = ath_newstate;
ic->ic_scan_start = ath_scan_start;
ic->ic_scan_end = ath_scan_end;
ic->ic_set_channel = ath_set_channel;
- ic->ic_crypto.cs_max_keyix = sc->sc_keymax;
- ic->ic_crypto.cs_key_alloc = ath_key_alloc;
- ic->ic_crypto.cs_key_delete = ath_key_delete;
- ic->ic_crypto.cs_key_set = ath_key_set;
- ic->ic_crypto.cs_key_update_begin = ath_key_update_begin;
- ic->ic_crypto.cs_key_update_end = ath_key_update_end;
- ic->ic_raw_xmit = ath_raw_xmit;
- ic->ic_update_beacon = ath_beacon_update;
- /* complete initialization */
- ieee80211_media_init(ic, ath_media_change, ieee80211_media_status);
ath_bpfattach(sc);
/*
@@ -675,7 +690,7 @@ ath_detach(struct ath_softc *sc)
* it last
* Other than that, it's straightforward...
*/
- ieee80211_ifdetach(&sc->sc_ic);
+ ieee80211_ifdetach(ifp->if_l2com);
#ifdef ATH_TX99_DIAG
if (sc->sc_tx99 != NULL)
sc->sc_tx99->detach(sc->sc_tx99);
@@ -690,6 +705,343 @@ ath_detach(struct ath_softc *sc)
return 0;
}
+/*
+ * MAC address handling for multiple BSS on the same radio.
+ * The first vap uses the MAC address from the EEPROM. For
+ * subsequent vap's we set the U/L bit (bit 1) in the MAC
+ * address and use the next six bits as an index.
+ */
+static void
+assign_address(struct ath_softc *sc, uint8_t mac[IEEE80211_ADDR_LEN], int clone)
+{
+ int i;
+
+ if (clone && sc->sc_hasbmask) {
+ /* NB: we only do this if h/w supports multiple bssid */
+ for (i = 0; i < 8; i++)
+ if ((sc->sc_bssidmask & (1<<i)) == 0)
+ break;
+ if (i != 0)
+ mac[0] |= (i << 2)|0x2;
+ } else
+ i = 0;
+ sc->sc_bssidmask |= 1<<i;
+ sc->sc_hwbssidmask[0] &= ~mac[0];
+ if (i == 0)
+ sc->sc_nbssid0++;
+}
+
+static void
+reclaim_address(struct ath_softc *sc, const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ int i = mac[0] >> 2;
+ uint8_t mask;
+
+ if (i != 0 || --sc->sc_nbssid0 == 0) {
+ sc->sc_bssidmask &= ~(1<<i);
+ /* recalculate bssid mask from remaining addresses */
+ mask = 0xff;
+ for (i = 1; i < 8; i++)
+ if (sc->sc_bssidmask & (1<<i))
+ mask &= ~((i<<2)|0x2);
+ sc->sc_hwbssidmask[0] |= mask;
+ }
+}
+
+/*
+ * Assign a beacon xmit slot. We try to space out
+ * assignments so when beacons are staggered the
+ * traffic coming out of the cab q has maximal time
+ * to go out before the next beacon is scheduled.
+ */
+static int
+assign_bslot(struct ath_softc *sc)
+{
+ u_int slot, free;
+
+ free = 0;
+ for (slot = 0; slot < ATH_BCBUF; slot++)
+ if (sc->sc_bslot[slot] == NULL) {
+ if (sc->sc_bslot[(slot+1)%ATH_BCBUF] == NULL &&
+ sc->sc_bslot[(slot-1)%ATH_BCBUF] == NULL)
+ return slot;
+ free = slot;
+ /* NB: keep looking for a double slot */
+ }
+ return free;
+}
+
+static struct ieee80211vap *
+ath_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac0[IEEE80211_ADDR_LEN])
+{
+ struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ath_vap *avp;
+ struct ieee80211vap *vap;
+ uint8_t mac[IEEE80211_ADDR_LEN];
+ int ic_opmode, needbeacon, error;
+
+ avp = (struct ath_vap *) malloc(sizeof(struct ath_vap),
+ M_80211_VAP, M_WAITOK | M_ZERO);
+ needbeacon = 0;
+ IEEE80211_ADDR_COPY(mac, mac0);
+
+ ATH_LOCK(sc);
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ if (sc->sc_nstavaps != 0) { /* XXX only 1 sta for now */
+ device_printf(sc->sc_dev, "only 1 sta vap supported\n");
+ goto bad;
+ }
+ if (sc->sc_nvaps) {
+ /*
+ * When there are multiple vaps we must fall
+ * back to s/w beacon miss handling.
+ */
+ flags |= IEEE80211_CLONE_NOBEACONS;
+ }
+ if (flags & IEEE80211_CLONE_NOBEACONS) {
+ sc->sc_swbmiss = 1;
+ ic_opmode = IEEE80211_M_HOSTAP;
+ } else
+ ic_opmode = opmode;
+ break;
+ case IEEE80211_M_IBSS:
+ if (sc->sc_nvaps != 0) { /* XXX only 1 for now */
+ device_printf(sc->sc_dev,
+ "only 1 ibss vap supported\n");
+ goto bad;
+ }
+ ic_opmode = opmode;
+ needbeacon = 1;
+ break;
+ case IEEE80211_M_AHDEMO:
+ /* fall thru... */
+ case IEEE80211_M_MONITOR:
+ if (sc->sc_nvaps != 0 && ic->ic_opmode != opmode) {
+ /* XXX not right for monitor mode */
+ ic_opmode = ic->ic_opmode;
+ } else
+ ic_opmode = opmode;
+ break;
+ case IEEE80211_M_HOSTAP:
+ needbeacon = 1;
+ /* fall thru... */
+ case IEEE80211_M_WDS:
+ if (sc->sc_nvaps && ic->ic_opmode == IEEE80211_M_STA) {
+ device_printf(sc->sc_dev,
+ "wds not supported in sta mode\n");
+ goto bad;
+ }
+ if (opmode == IEEE80211_M_WDS) {
+ /*
+ * Silently remove any request for a unique
+ * bssid; WDS vap's always share the local
+ * mac address.
+ */
+ flags &= ~IEEE80211_CLONE_BSSID;
+ }
+ ic_opmode = IEEE80211_M_HOSTAP;
+ break;
+ default:
+ device_printf(sc->sc_dev, "unknown opmode %d\n", opmode);
+ goto bad;
+ }
+ /*
+ * Check that a beacon buffer is available; the code below assumes it.
+ */
+ if (needbeacon & STAILQ_EMPTY(&sc->sc_bbuf)) {
+ device_printf(sc->sc_dev, "no beacon buffer available\n");
+ goto bad;
+ }
+
+ /* STA, AHDEMO? */
+ if (opmode == IEEE80211_M_HOSTAP) {
+ assign_address(sc, mac, flags & IEEE80211_CLONE_BSSID);
+ ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask);
+ }
+
+ vap = &avp->av_vap;
+ /* XXX can't hold mutex across if_alloc */
+ ATH_UNLOCK(sc);
+ error = ieee80211_vap_setup(ic, vap, name, unit, opmode, flags,
+ bssid, mac);
+ ATH_LOCK(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "%s: error %d creating vap\n",
+ __func__, error);
+ goto bad2;
+ }
+
+ /* h/w crypto support */
+ vap->iv_key_alloc = ath_key_alloc;
+ vap->iv_key_delete = ath_key_delete;
+ vap->iv_key_set = ath_key_set;
+ vap->iv_key_update_begin = ath_key_update_begin;
+ vap->iv_key_update_end = ath_key_update_end;
+
+ /* override various methods */
+ avp->av_recv_mgmt = vap->iv_recv_mgmt;
+ vap->iv_recv_mgmt = ath_recv_mgmt;
+ vap->iv_reset = ath_reset_vap;
+ vap->iv_update_beacon = ath_beacon_update;
+ avp->av_newstate = vap->iv_newstate;
+ vap->iv_newstate = ath_newstate;
+ avp->av_bmiss = vap->iv_bmiss;
+ vap->iv_bmiss = ath_bmiss_vap;
+
+ avp->av_bslot = -1;
+ if (needbeacon) {
+ /*
+ * Allocate beacon state and setup the q for buffered
+ * multicast frames. We know a beacon buffer is
+ * available because we checked above.
+ */
+ avp->av_bcbuf = STAILQ_FIRST(&sc->sc_bbuf);
+ STAILQ_REMOVE_HEAD(&sc->sc_bbuf, bf_list);
+ if (opmode != IEEE80211_M_IBSS || !sc->sc_hasveol) {
+ /*
+ * Assign the vap to a beacon xmit slot. As above
+ * this cannot fail to find a free one.
+ */
+ avp->av_bslot = assign_bslot(sc);
+ KASSERT(sc->sc_bslot[avp->av_bslot] == NULL,
+ ("beacon slot %u not empty", avp->av_bslot));
+ sc->sc_bslot[avp->av_bslot] = vap;
+ sc->sc_nbcnvaps++;
+ }
+ if (sc->sc_hastsfadd && sc->sc_nbcnvaps > 0) {
+ /*
+ * Multple vaps are to transmit beacons and we
+ * have h/w support for TSF adjusting; enable
+ * use of staggered beacons.
+ */
+ sc->sc_stagbeacons = 1;
+ }
+ ath_txq_init(sc, &avp->av_mcastq, ATH_TXQ_SWQ);
+ }
+
+ ic->ic_opmode = ic_opmode;
+ if (opmode != IEEE80211_M_WDS) {
+ sc->sc_nvaps++;
+ if (opmode == IEEE80211_M_STA)
+ sc->sc_nstavaps++;
+ }
+ switch (ic_opmode) {
+ case IEEE80211_M_IBSS:
+ sc->sc_opmode = HAL_M_IBSS;
+ break;
+ case IEEE80211_M_STA:
+ sc->sc_opmode = HAL_M_STA;
+ break;
+ case IEEE80211_M_AHDEMO:
+ case IEEE80211_M_HOSTAP:
+ sc->sc_opmode = HAL_M_HOSTAP;
+ break;
+ case IEEE80211_M_MONITOR:
+ sc->sc_opmode = HAL_M_MONITOR;
+ break;
+ default:
+ /* XXX should not happen */
+ break;
+ }
+ if (sc->sc_hastsfadd) {
+ /*
+ * Configure whether or not TSF adjust should be done.
+ */
+ ath_hal_settsfadjust(sc->sc_ah, sc->sc_stagbeacons);
+ }
+ ATH_UNLOCK(sc);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ath_media_change, ieee80211_media_status);
+ return vap;
+bad2:
+ reclaim_address(sc, mac);
+ ath_hal_setbssidmask(sc->sc_ah, sc->sc_hwbssidmask);
+bad:
+ free(avp, M_80211_VAP);
+ ATH_UNLOCK(sc);
+ return NULL;
+}
+
+static void
+ath_vap_delete(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ath_softc *sc = ifp->if_softc;
+ struct ath_hal *ah = sc->sc_ah;
+ struct ath_vap *avp = ATH_VAP(vap);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ /*
+ * Quiesce the hardware while we remove the vap. In
+ * particular we need to reclaim all references to
+ * the vap state by any frames pending on the tx queues.
+ */
+ ath_hal_intrset(ah, 0); /* disable interrupts */
+ ath_draintxq(sc); /* stop xmit side */
+ ath_stoprecv(sc); /* stop recv side */
+ }
+
+ ieee80211_vap_detach(vap);
+ ATH_LOCK(sc);
+ /*
+ * Reclaim beacon state. Note this must be done before
+ * the vap instance is reclaimed as we may have a reference
+ * to it in the buffer for the beacon frame.
+ */
+ if (avp->av_bcbuf != NULL) {
+ if (avp->av_bslot != -1) {
+ sc->sc_bslot[avp->av_bslot] = NULL;
+ sc->sc_nbcnvaps--;
+ }
+ ath_beacon_return(sc, avp->av_bcbuf);
+ avp->av_bcbuf = NULL;
+ if (sc->sc_nbcnvaps == 0) {
+ sc->sc_stagbeacons = 0;
+ if (sc->sc_hastsfadd)
+ ath_hal_settsfadjust(sc->sc_ah, 0);
+ }
+ /*
+ * Reclaim any pending mcast frames for the vap.
+ */
+ ath_tx_draintxq(sc, &avp->av_mcastq);
+ ATH_TXQ_LOCK_DESTROY(&avp->av_mcastq);
+ }
+ /*
+ * Update bookkeeping.
+ */
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+ sc->sc_nstavaps--;
+ if (sc->sc_nstavaps == 0 && sc->sc_swbmiss)
+ sc->sc_swbmiss = 0;
+ } else if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ reclaim_address(sc, vap->iv_myaddr);
+ ath_hal_setbssidmask(ah, sc->sc_hwbssidmask);
+ }
+ if (vap->iv_opmode != IEEE80211_M_WDS)
+ sc->sc_nvaps--;
+ ATH_UNLOCK(sc);
+ free(avp, M_80211_VAP);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ /*
+ * Restart rx+tx machines if still running (RUNNING will
+ * be reset if we just destroyed the last vap).
+ */
+ if (ath_startrecv(sc) != 0)
+ if_printf(ifp, "%s: unable to restart recv logic\n",
+ __func__);
+ if (sc->sc_beacons)
+ ath_beacon_config(sc, NULL);
+ ath_hal_intrset(ah, sc->sc_imask);
+ }
+}
+
void
ath_suspend(struct ath_softc *sc)
{
@@ -864,37 +1216,40 @@ ath_rxorn_proc(void *arg, int pending)
}
static void
+ath_bmiss_vap(struct ieee80211vap *vap)
+{
+ struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
+ u_int64_t lastrx = sc->sc_lastrx;
+ u_int64_t tsf = ath_hal_gettsf64(sc->sc_ah);
+ u_int bmisstimeout =
+ vap->iv_bmissthreshold * vap->iv_bss->ni_intval * 1024;
+
+ DPRINTF(sc, ATH_DEBUG_BEACON,
+ "%s: tsf %llu lastrx %lld (%llu) bmiss %u\n",
+ __func__, (unsigned long long) tsf,
+ (unsigned long long)(tsf - lastrx),
+ (unsigned long long) lastrx, bmisstimeout);
+ /*
+ * Workaround phantom bmiss interrupts by sanity-checking
+ * the time of our last rx'd frame. If it is within the
+ * beacon miss interval then ignore the interrupt. If it's
+ * truly a bmiss we'll get another interrupt soon and that'll
+ * be dispatched up for processing.
+ */
+ if (tsf - lastrx > bmisstimeout)
+ ATH_VAP(vap)->av_bmiss(vap);
+ else
+ sc->sc_stats.ast_bmiss_phantom++;
+}
+
+static void
ath_bmiss_proc(void *arg, int pending)
{
struct ath_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
DPRINTF(sc, ATH_DEBUG_ANY, "%s: pending %u\n", __func__, pending);
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("unexpect operating mode %u", ic->ic_opmode));
- if (ic->ic_state == IEEE80211_S_RUN) {
- u_int64_t lastrx = sc->sc_lastrx;
- u_int64_t tsf = ath_hal_gettsf64(sc->sc_ah);
- u_int bmisstimeout =
- ic->ic_bmissthreshold * ic->ic_bss->ni_intval * 1024;
-
- DPRINTF(sc, ATH_DEBUG_BEACON,
- "%s: tsf %llu lastrx %lld (%llu) bmiss %u\n",
- __func__, (unsigned long long) tsf,
- (unsigned long long)(tsf - lastrx),
- (unsigned long long) lastrx, bmisstimeout);
- /*
- * Workaround phantom bmiss interrupts by sanity-checking
- * the time of our last rx'd frame. If it is within the
- * beacon miss interval then ignore the interrupt. If it's
- * truly a bmiss we'll get another interrupt soon and that'll
- * be dispatched up for processing.
- */
- if (tsf - lastrx > bmisstimeout)
- ieee80211_beacon_miss(ic);
- else
- sc->sc_stats.ast_bmiss_phantom++;
- }
+ ieee80211_beacon_miss(ifp->if_l2com);
}
/*
@@ -939,12 +1294,35 @@ ath_mapchan(HAL_CHANNEL *hc, const struct ieee80211_channel *chan)
#undef N
}
+/*
+ * Handle TKIP MIC setup to deal hardware that doesn't do MIC
+ * calcs together with WME. If necessary disable the crypto
+ * hardware and mark the 802.11 state so keys will be setup
+ * with the MIC work done in software.
+ */
+static void
+ath_settkipmic(struct ath_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ if ((ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP) && !sc->sc_wmetkipmic) {
+ if (ic->ic_flags & IEEE80211_F_WME) {
+ ath_hal_settkipmic(sc->sc_ah, AH_FALSE);
+ ic->ic_cryptocaps &= ~IEEE80211_CRYPTO_TKIPMIC;
+ } else {
+ ath_hal_settkipmic(sc->sc_ah, AH_TRUE);
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_TKIPMIC;
+ }
+ }
+}
+
static void
ath_init(void *arg)
{
struct ath_softc *sc = (struct ath_softc *) arg;
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ath_hal *ah = sc->sc_ah;
HAL_STATUS status;
@@ -966,18 +1344,16 @@ ath_init(void *arg)
* and then setup of the interrupt mask.
*/
ath_mapchan(&sc->sc_curchan, ic->ic_curchan);
+ ath_settkipmic(sc);
if (!ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, AH_FALSE, &status)) {
if_printf(ifp, "unable to reset hardware; hal status %u\n",
status);
- goto done;
+ ATH_UNLOCK(sc);
+ return;
}
+ ath_chan_change(sc, ic->ic_curchan);
/*
- * This is needed only to setup initial state
- * but it's best done after a reset.
- */
- ath_update_txpow(sc);
- /*
* Likewise this is set during reset so update
* state cached in the driver.
*/
@@ -994,7 +1370,8 @@ ath_init(void *arg)
*/
if (ath_startrecv(sc) != 0) {
if_printf(ifp, "unable to start recv logic\n");
- goto done;
+ ATH_UNLOCK(sc);
+ return;
}
/*
@@ -1009,36 +1386,24 @@ ath_init(void *arg)
*/
if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA)
sc->sc_imask |= HAL_INT_MIB;
- ath_hal_intrset(ah, sc->sc_imask);
ifp->if_drv_flags |= IFF_DRV_RUNNING;
- ic->ic_state = IEEE80211_S_INIT;
+ ath_hal_intrset(ah, sc->sc_imask);
+
+ ATH_UNLOCK(sc);
- /*
- * The hardware should be ready to go now so it's safe
- * to kick the 802.11 state machine as it's likely to
- * immediately call back to us to send mgmt frames.
- */
- ath_chan_change(sc, ic->ic_curchan);
#ifdef ATH_TX99_DIAG
if (sc->sc_tx99 != NULL)
sc->sc_tx99->start(sc->sc_tx99);
else
#endif
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-done:
- ATH_UNLOCK(sc);
+ ieee80211_start_all(ic); /* start all vap's */
}
static void
ath_stop_locked(struct ifnet *ifp)
{
struct ath_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
struct ath_hal *ah = sc->sc_ah;
DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid %u if_flags 0x%x\n",
@@ -1065,7 +1430,6 @@ ath_stop_locked(struct ifnet *ifp)
if (sc->sc_tx99 != NULL)
sc->sc_tx99->stop(sc->sc_tx99);
#endif
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
ifp->if_timer = 0;
if (!sc->sc_invalid) {
@@ -1083,8 +1447,7 @@ ath_stop_locked(struct ifnet *ifp)
ath_hal_phydisable(ah);
} else
sc->sc_rxlink = NULL;
- IFQ_DRV_PURGE(&ifp->if_snd);
- ath_beacon_free(sc);
+ ath_beacon_free(sc); /* XXX not needed */
}
}
@@ -1121,7 +1484,7 @@ static int
ath_reset(struct ifnet *ifp)
{
struct ath_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ath_hal *ah = sc->sc_ah;
HAL_STATUS status;
@@ -1134,11 +1497,11 @@ ath_reset(struct ifnet *ifp)
ath_hal_intrset(ah, 0); /* disable interrupts */
ath_draintxq(sc); /* stop xmit side */
ath_stoprecv(sc); /* stop recv side */
+ ath_settkipmic(sc); /* configure TKIP MIC handling */
/* NB: indicate channel change so we do a full reset */
if (!ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, AH_TRUE, &status))
if_printf(ifp, "%s: unable to reset hardware; hal status %u\n",
__func__, status);
- ath_update_txpow(sc); /* update tx power state */
sc->sc_diversity = ath_hal_getdiversity(ah);
sc->sc_calinterval = 1;
sc->sc_caltries = 0;
@@ -1150,14 +1513,20 @@ ath_reset(struct ifnet *ifp)
* might change as a result.
*/
ath_chan_change(sc, ic->ic_curchan);
- if (ic->ic_state == IEEE80211_S_RUN)
- ath_beacon_config(sc); /* restart beacons */
+ if (sc->sc_beacons)
+ ath_beacon_config(sc, NULL); /* restart beacons */
ath_hal_intrset(ah, sc->sc_imask);
ath_start(ifp); /* restart xmit */
return 0;
}
+static int
+ath_reset_vap(struct ieee80211vap *vap, u_long cmd)
+{
+ return ath_reset(vap->iv_ic->ic_ifp);
+}
+
static int
ath_ff_always(struct ath_txq *txq, struct ath_buf *bf)
{
@@ -1210,7 +1579,7 @@ ath_ff_stageq_flush(struct ath_softc *sc, struct ath_txq *txq,
sc->sc_stats.ast_ff_flush++;
/* encap and xmit */
- bf->bf_m = ieee80211_encap(&sc->sc_ic, bf->bf_m, ni);
+ bf->bf_m = ieee80211_encap(ni, bf->bf_m);
if (bf->bf_m == NULL) {
DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF,
"%s: discard, encapsulation failure\n",
@@ -1243,6 +1612,7 @@ ath_ff_stageq_flush(struct ath_softc *sc, struct ath_txq *txq,
static __inline u_int32_t
ath_ff_approx_txtime(struct ath_softc *sc, struct ath_node *an, struct mbuf *m)
{
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
u_int32_t framelen;
struct ath_buf *bf;
@@ -1256,7 +1626,7 @@ ath_ff_approx_txtime(struct ath_softc *sc, struct ath_node *an, struct mbuf *m)
* - 14: 1 802.3 FF tunnel header (skb already accounts for 2nd)
*/
framelen = m->m_pkthdr.len + 32 + 4 + 6 + 16 + 14;
- if (sc->sc_ic.ic_flags & IEEE80211_F_PRIVACY)
+ if (ic->ic_flags & IEEE80211_F_PRIVACY)
framelen += 24;
bf = an->an_ff_buf[M_WME_GETAC(m)];
if (bf != NULL)
@@ -1280,7 +1650,7 @@ static __inline int
ath_ff_can_aggregate(struct ath_softc *sc,
struct ath_node *an, struct mbuf *m, int *flushq)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
struct ath_txq *txq;
u_int32_t txoplimit;
u_int pri;
@@ -1330,7 +1700,6 @@ static struct mbuf *
ath_ff_check(struct ath_softc *sc, struct ath_txq *txq,
struct ath_buf *bf, struct mbuf *m, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
struct ath_node *an = ATH_NODE(ni);
struct ath_buf *bfstaged;
int ff_flush, pri;
@@ -1423,7 +1792,7 @@ ath_ff_check(struct ath_softc *sc, struct ath_txq *txq,
ether_sprintf(an->an_node.ni_macaddr));
/* encap and xmit */
- bfstaged->bf_m = ieee80211_encap(ic, bfstaged->bf_m, ni);
+ bfstaged->bf_m = ieee80211_encap(ni, bfstaged->bf_m);
if (bfstaged->bf_m == NULL) {
DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF,
"%s: discard, encap failure\n", __func__);
@@ -1525,13 +1894,10 @@ static void
ath_start(struct ifnet *ifp)
{
struct ath_softc *sc = ifp->if_softc;
- struct ath_hal *ah = sc->sc_ah;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211_node *ni;
struct ath_buf *bf;
struct mbuf *m, *next;
- struct ieee80211_frame *wh;
- struct ether_header *eh;
struct ath_txq *txq;
ath_bufhead frags;
int pri;
@@ -1554,154 +1920,63 @@ ath_start(struct ifnet *ifp)
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
break;
}
- /*
- * Poll the management queue for frames; they
- * have priority over normal data frames.
- */
- IF_DEQUEUE(&ic->ic_mgtq, m);
- if (m == NULL) {
- /*
- * No data frames go out unless we're associated.
- */
- if (ic->ic_state != IEEE80211_S_RUN) {
- DPRINTF(sc, ATH_DEBUG_XMIT,
- "%s: discard data packet, state %s\n",
- __func__,
- ieee80211_state_name[ic->ic_state]);
- sc->sc_stats.ast_tx_discard++;
- ATH_TXBUF_LOCK(sc);
- STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
- ATH_TXBUF_UNLOCK(sc);
- break;
- }
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m); /* XXX: LOCK */
- if (m == NULL) {
- ATH_TXBUF_LOCK(sc);
- STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
- ATH_TXBUF_UNLOCK(sc);
- break;
- }
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
- STAILQ_INIT(&frags);
+ IFQ_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL) {
+ ATH_TXBUF_LOCK(sc);
+ STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+ ATH_TXBUF_UNLOCK(sc);
+ break;
+ }
+ STAILQ_INIT(&frags);
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ pri = M_WME_GETAC(m);
+ txq = sc->sc_ac2q[pri];
+ if (ni->ni_ath_flags & IEEE80211_NODE_FF) {
/*
- * Find the node for the destination so we can do
- * things like power save and fast frames aggregation.
+ * Check queue length; if too deep drop this
+ * frame (tail drop considered good).
*/
- if (m->m_len < sizeof(struct ether_header) &&
- (m = m_pullup(m, sizeof(struct ether_header))) == NULL) {
- ic->ic_stats.is_tx_nobuf++; /* XXX */
- ni = NULL;
- goto bad;
- }
- eh = mtod(m, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- /* NB: ieee80211_find_txnode does stat+msg */
+ if (txq->axq_depth >= sc->sc_fftxqmax) {
+ DPRINTF(sc, ATH_DEBUG_FF,
+ "[%s] tail drop on q %u depth %u\n",
+ ether_sprintf(ni->ni_macaddr),
+ txq->axq_qnum, txq->axq_depth);
+ sc->sc_stats.ast_tx_qfull++;
m_freem(m);
- goto bad;
- }
- if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
- (m->m_flags & M_PWR_SAV) == 0) {
- /*
- * Station in power save mode; pass the frame
- * to the 802.11 layer and continue. We'll get
- * the frame back when the time is right.
- */
- ieee80211_pwrsave(ni, m);
goto reclaim;
}
- /* calculate priority so we can find the tx queue */
- if (ieee80211_classify(ic, m, ni)) {
- DPRINTF(sc, ATH_DEBUG_XMIT,
- "%s: discard, classification failure\n",
- __func__);
- m_freem(m);
- goto bad;
- }
- pri = M_WME_GETAC(m);
- txq = sc->sc_ac2q[pri];
- if (ni->ni_ath_flags & IEEE80211_NODE_FF) {
- /*
- * Check queue length; if too deep drop this
- * frame (tail drop considered good).
- */
- if (txq->axq_depth >= sc->sc_fftxqmax) {
- DPRINTF(sc, ATH_DEBUG_FF,
- "[%s] tail drop on q %u depth %u\n",
- ether_sprintf(ni->ni_macaddr),
- txq->axq_qnum, txq->axq_depth);
- sc->sc_stats.ast_tx_qfull++;
- m_freem(m);
- goto reclaim;
- }
- m = ath_ff_check(sc, txq, bf, m, ni);
- if (m == NULL) {
- /* NB: ni ref & bf held on stageq */
- continue;
- }
- }
- ifp->if_opackets++;
- BPF_MTAP(ifp, m);
- /*
- * Encapsulate the packet in prep for transmission.
- */
- m = ieee80211_encap(ic, m, ni);
+ m = ath_ff_check(sc, txq, bf, m, ni);
if (m == NULL) {
- DPRINTF(sc, ATH_DEBUG_XMIT,
- "%s: encapsulation failure\n",
- __func__);
- sc->sc_stats.ast_tx_encap++;
- goto bad;
+ /* NB: ni ref & bf held on stageq */
+ continue;
}
- /*
- * Check for fragmentation. If this frame
- * has been broken up verify we have enough
- * buffers to send all the fragments so all
- * go out or none...
- */
- if ((m->m_flags & M_FRAG) &&
- !ath_txfrag_setup(sc, &frags, m, ni)) {
- DPRINTF(sc, ATH_DEBUG_XMIT,
- "%s: out of txfrag buffers\n", __func__);
- ic->ic_stats.is_tx_nobuf++; /* XXX */
- ath_freetx(m);
- goto bad;
- }
- } else {
- /*
- * Hack! The referenced node pointer is in the
- * rcvif field of the packet header. This is
- * placed there by ieee80211_mgmt_output because
- * we need to hold the reference with the frame
- * and there's no other way (other than packet
- * tags which we consider too expensive to use)
- * to pass it along.
- */
- ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
- m->m_pkthdr.rcvif = NULL;
-
- wh = mtod(m, struct ieee80211_frame *);
- if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
- IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
- /* fill time stamp */
- u_int64_t tsf;
- u_int32_t *tstamp;
-
- tsf = ath_hal_gettsf64(ah);
- /* XXX: adjust 100us delay to xmit */
- tsf += 100;
- tstamp = (u_int32_t *)&wh[1];
- tstamp[0] = htole32(tsf & 0xffffffff);
- tstamp[1] = htole32(tsf >> 32);
- }
- sc->sc_stats.ast_tx_mgmt++;
}
-
+ ifp->if_opackets++;
+ /*
+ * Encapsulate the packet in prep for transmission.
+ */
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ DPRINTF(sc, ATH_DEBUG_XMIT,
+ "%s: encapsulation failure\n", __func__);
+ sc->sc_stats.ast_tx_encap++;
+ goto bad;
+ }
+ /*
+ * Check for fragmentation. If this frame
+ * has been broken up verify we have enough
+ * buffers to send all the fragments so all
+ * go out or none...
+ */
+ if ((m->m_flags & M_FRAG) &&
+ !ath_txfrag_setup(sc, &frags, m, ni)) {
+ DPRINTF(sc, ATH_DEBUG_XMIT,
+ "%s: out of txfrag buffers\n", __func__);
+ ic->ic_stats.is_tx_nobuf++; /* XXX */
+ ath_freetx(m);
+ goto bad;
+ }
nextfrag:
/*
* Pass the frame to the h/w for transmission.
@@ -1735,11 +2010,11 @@ ath_start(struct ifnet *ifp)
* Beware of state changing between frags.
* XXX check sta power-save state?
*/
- if (ic->ic_state != IEEE80211_S_RUN) {
+ if (ni->ni_vap->iv_state != IEEE80211_S_RUN) {
DPRINTF(sc, ATH_DEBUG_XMIT,
"%s: flush fragmented packet, state %s\n",
__func__,
- ieee80211_state_name[ic->ic_state]);
+ ieee80211_state_name[ni->ni_vap->iv_state]);
ath_freetx(next);
goto reclaim;
}
@@ -1751,7 +2026,6 @@ ath_start(struct ifnet *ifp)
}
ifp->if_timer = 5;
- ic->ic_lastdata = ticks;
#if 0
/*
* Flush stale frames from the fast-frame staging queue.
@@ -1765,30 +2039,9 @@ ath_start(struct ifnet *ifp)
static int
ath_media_change(struct ifnet *ifp)
{
-#define IS_UP(ifp) \
- ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
- int error;
-
- error = ieee80211_media_change(ifp);
- if (error == ENETRESET) {
- struct ath_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
-
- if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
- /*
- * Adhoc demo mode is just ibss mode w/o beacons
- * (mostly). The hal knows nothing about it;
- * tell it we're operating in ibss mode.
- */
- sc->sc_opmode = HAL_M_IBSS;
- } else
- sc->sc_opmode = ic->ic_opmode;
- if (IS_UP(ifp))
- ath_init(sc); /* XXX lose error */
- error = 0;
- }
- return error;
-#undef IS_UP
+ int error = ieee80211_media_change(ifp);
+ /* NB: only the fixed rate can change and that doesn't need a reset */
+ return (error == ENETRESET ? 0 : error);
}
#ifdef ATH_DEBUG
@@ -1860,7 +2113,7 @@ ath_keyset_tkip(struct ath_softc *sc, const struct ieee80211_key *k,
/*
* Room for both TX+RX MIC keys in one key cache
* slot, just set key at the first index; the hal
- * will handle the reset.
+ * will handle the rest.
*/
memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic));
#if HAL_ABI_VERSION > 0x06052200
@@ -1869,13 +2122,16 @@ ath_keyset_tkip(struct ath_softc *sc, const struct ieee80211_key *k,
KEYPRINTF(sc, k->wk_keyix, hk, mac);
return ath_hal_keyset(ah, k->wk_keyix, hk, mac);
}
- } else if (k->wk_flags & IEEE80211_KEY_XR) {
- /*
- * TX/RX key goes at first index.
- * The hal handles the MIC keys are index+64.
- */
- memcpy(hk->kv_mic, k->wk_flags & IEEE80211_KEY_XMIT ?
- k->wk_txmic : k->wk_rxmic, sizeof(hk->kv_mic));
+ } else if (k->wk_flags & IEEE80211_KEY_XMIT) {
+#if HAL_ABI_VERSION > 0x06052200
+ memcpy(hk->kv_txmic, k->wk_txmic, sizeof(hk->kv_txmic));
+#else
+ memcpy(hk->kv_mic, k->wk_mic, sizeof(hk->kv_mic));
+#endif
+ KEYPRINTF(sc, k->wk_keyix, hk, mac);
+ return ath_hal_keyset(ah, k->wk_keyix, hk, mac);
+ } else if (k->wk_flags & IEEE80211_KEY_RECV) {
+ memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic));
KEYPRINTF(sc, k->wk_keyix, hk, mac);
return ath_hal_keyset(ah, k->wk_keyix, hk, mac);
}
@@ -2091,10 +2347,10 @@ key_alloc_single(struct ath_softc *sc,
* 64 entries.
*/
static int
-ath_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
+ath_key_alloc(struct ieee80211vap *vap, const struct ieee80211_key *k,
ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
{
- struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
/*
* Group key allocation must be handled specially for
@@ -2108,8 +2364,8 @@ ath_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
* multi-station operation.
*/
if ((k->wk_flags & IEEE80211_KEY_GROUP) && !sc->sc_mcastkey) {
- if (!(&ic->ic_nw_keys[0] <= k &&
- k < &ic->ic_nw_keys[IEEE80211_WEP_NKID])) {
+ if (!(&vap->iv_nw_keys[0] <= k &&
+ k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) {
/* should not happen */
DPRINTF(sc, ATH_DEBUG_KEYCACHE,
"%s: bogus group key\n", __func__);
@@ -2119,7 +2375,7 @@ ath_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
* XXX we pre-allocate the global keys so
* have no way to check if they've already been allocated.
*/
- *keyix = *rxkeyix = k - ic->ic_nw_keys;
+ *keyix = *rxkeyix = k - vap->iv_nw_keys;
return 1;
}
@@ -2148,9 +2404,9 @@ ath_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
* Delete an entry in the key cache allocated by ath_key_alloc.
*/
static int
-ath_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
+ath_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
{
- struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
struct ath_hal *ah = sc->sc_ah;
const struct ieee80211_cipher *cip = k->wk_cipher;
u_int keyix = k->wk_keyix;
@@ -2188,12 +2444,12 @@ ath_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
* slot(s) must already have been allocated by ath_key_alloc.
*/
static int
-ath_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
+ath_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k,
const u_int8_t mac[IEEE80211_ADDR_LEN])
{
- struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
- return ath_keyset(sc, k, mac, ic->ic_bss);
+ return ath_keyset(sc, k, mac, vap->iv_bss);
}
/*
@@ -2203,29 +2459,25 @@ ath_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
* uses that originate in the driver.
*/
static void
-ath_key_update_begin(struct ieee80211com *ic)
+ath_key_update_begin(struct ieee80211vap *vap)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
struct ath_softc *sc = ifp->if_softc;
DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__);
-#if 0
- tasklet_disable(&sc->sc_rxtq);
-#endif
+ taskqueue_block(sc->sc_tq);
IF_LOCK(&ifp->if_snd); /* NB: doesn't block mgmt frames */
}
static void
-ath_key_update_end(struct ieee80211com *ic)
+ath_key_update_end(struct ieee80211vap *vap)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
struct ath_softc *sc = ifp->if_softc;
DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__);
IF_UNLOCK(&ifp->if_snd);
-#if 0
- tasklet_enable(&sc->sc_rxtq);
-#endif
+ taskqueue_unblock(sc->sc_tq);
}
/*
@@ -2233,80 +2485,101 @@ ath_key_update_end(struct ieee80211com *ic)
* operating mode and state:
*
* o always accept unicast, broadcast, and multicast traffic
- * o maintain current state of phy error reception (the hal
- * may enable phy error frames for noise immunity work)
+ * o accept PHY error frames when hardware doesn't have MIB support
+ * to count and we need them for ANI (sta mode only at the moment)
+ * and we are not scanning (ANI is disabled)
+ * NB: only with recent hal's; older hal's add rx filter bits out
+ * of sight and we need to blindly preserve them
* o probe request frames are accepted only when operating in
* hostap, adhoc, or monitor modes
- * o enable promiscuous mode according to the interface state
+ * o enable promiscuous mode
+ * - when in monitor mode
+ * - if interface marked PROMISC (assumes bridge setting is filtered)
* o accept beacons:
- * - when operating in adhoc mode so the 802.11 layer creates
- * node table entries for peers,
* - when operating in station mode for collecting rssi data when
* the station is otherwise quiet, or
+ * - when operating in adhoc mode so the 802.11 layer creates
+ * node table entries for peers,
* - when scanning
+ * - when doing s/w beacon miss (e.g. for ap+sta)
+ * - when operating in ap mode in 11g to detect overlapping bss that
+ * require protection
* o accept control frames:
* - when in monitor mode
+ * XXX BAR frames for 11n
+ * XXX HT protection for 11n
*/
static u_int32_t
ath_calcrxfilter(struct ath_softc *sc)
{
-#define RX_FILTER_PRESERVE (HAL_RX_FILTER_PHYERR | HAL_RX_FILTER_PHYRADAR)
- struct ieee80211com *ic = &sc->sc_ic;
- struct ath_hal *ah = sc->sc_ah;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
u_int32_t rfilt;
- rfilt = (ath_hal_getrxfilter(ah) & RX_FILTER_PRESERVE)
+#if HAL_ABI_VERSION < 0x08011600
+ rfilt = (ath_hal_getrxfilter(sc->sc_ah) &
+ (HAL_RX_FILTER_PHYRADAR | HAL_RX_FILTER_PHYERR))
| HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST;
+#else
+ rfilt = HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST;
+ if (ic->ic_opmode == IEEE80211_M_STA &&
+ !sc->sc_needmib && !sc->sc_scanning)
+ rfilt |= HAL_RX_FILTER_PHYERR;
+#endif
if (ic->ic_opmode != IEEE80211_M_STA)
rfilt |= HAL_RX_FILTER_PROBEREQ;
- if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
- (ifp->if_flags & IFF_PROMISC))
+ if (ic->ic_opmode == IEEE80211_M_MONITOR || (ifp->if_flags & IFF_PROMISC))
rfilt |= HAL_RX_FILTER_PROM;
if (ic->ic_opmode == IEEE80211_M_STA ||
- ic->ic_opmode == IEEE80211_M_IBSS ||
- sc->sc_scanning)
+ sc->sc_opmode == HAL_M_IBSS ||
+ sc->sc_swbmiss || sc->sc_scanning)
+ rfilt |= HAL_RX_FILTER_BEACON;
+ /*
+ * NB: We don't recalculate the rx filter when
+ * ic_protmode changes; otherwise we could do
+ * this only when ic_protmode != NONE.
+ */
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
rfilt |= HAL_RX_FILTER_BEACON;
if (ic->ic_opmode == IEEE80211_M_MONITOR)
rfilt |= HAL_RX_FILTER_CONTROL;
+ DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, %s if_flags 0x%x\n",
+ __func__, rfilt, ieee80211_opmode_name[ic->ic_opmode], ifp->if_flags);
return rfilt;
-#undef RX_FILTER_PRESERVE
}
static void
-ath_mode_init(struct ath_softc *sc)
+ath_update_promisc(struct ifnet *ifp)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ath_hal *ah = sc->sc_ah;
- struct ifnet *ifp = sc->sc_ifp;
- u_int32_t rfilt, mfilt[2], val;
- u_int8_t pos;
- struct ifmultiaddr *ifma;
+ struct ath_softc *sc = ifp->if_softc;
+ u_int32_t rfilt;
/* configure rx filter */
rfilt = ath_calcrxfilter(sc);
- ath_hal_setrxfilter(ah, rfilt);
+ ath_hal_setrxfilter(sc->sc_ah, rfilt);
- /* configure operational mode */
- ath_hal_setopmode(ah);
+ DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt);
+}
- /*
- * Handle any link-level address change. Note that we only
- * need to force ic_myaddr; any other addresses are handled
- * as a byproduct of the ifnet code marking the interface
- * down then up.
- *
- * XXX should get from lladdr instead of arpcom but that's more work
- */
- IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
- ath_hal_setmac(ah, ic->ic_myaddr);
+static void
+ath_update_mcast(struct ifnet *ifp)
+{
+ struct ath_softc *sc = ifp->if_softc;
+ u_int32_t mfilt[2];
/* calculate and install multicast filter */
if ((ifp->if_flags & IFF_ALLMULTI) == 0) {
+ struct ifmultiaddr *ifma;
+ /*
+ * Merge multicast addresses to form the hardware filter.
+ */
mfilt[0] = mfilt[1] = 0;
- IF_ADDR_LOCK(ifp);
+ IF_ADDR_LOCK(ifp); /* XXX need some fiddling to remove? */
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
caddr_t dl;
+ u_int32_t val;
+ u_int8_t pos;
/* calculate XOR of eight 6bit values */
dl = LLADDR((struct sockaddr_dl *) ifma->ifma_addr);
@@ -2318,12 +2591,41 @@ ath_mode_init(struct ath_softc *sc)
mfilt[pos / 32] |= (1 << (pos % 32));
}
IF_ADDR_UNLOCK(ifp);
- } else {
+ } else
mfilt[0] = mfilt[1] = ~0;
- }
- ath_hal_setmcastfilter(ah, mfilt[0], mfilt[1]);
- DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, MC filter %08x:%08x\n",
- __func__, rfilt, mfilt[0], mfilt[1]);
+ ath_hal_setmcastfilter(sc->sc_ah, mfilt[0], mfilt[1]);
+ DPRINTF(sc, ATH_DEBUG_MODE, "%s: MC filter %08x:%08x\n",
+ __func__, mfilt[0], mfilt[1]);
+}
+
+static void
+ath_mode_init(struct ath_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ath_hal *ah = sc->sc_ah;
+ u_int32_t rfilt;
+
+ /* configure rx filter */
+ rfilt = ath_calcrxfilter(sc);
+ ath_hal_setrxfilter(ah, rfilt);
+
+ /* configure operational mode */
+ ath_hal_setopmode(ah);
+
+ /*
+ * Handle any link-level address change. Note that we only
+ * need to force ic_myaddr; any other addresses are handled
+ * as a byproduct of the ifnet code marking the interface
+ * down then up.
+ *
+ * XXX should get from lladdr instead of arpcom but that's more work
+ */
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
+ ath_hal_setmac(ah, ic->ic_myaddr);
+
+ /* calculate and install multicast filter */
+ ath_update_mcast(ifp);
}
/*
@@ -2332,7 +2634,7 @@ ath_mode_init(struct ath_softc *sc)
static void
ath_setslottime(struct ath_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
struct ath_hal *ah = sc->sc_ah;
u_int usec;
@@ -2367,7 +2669,7 @@ static void
ath_updateslot(struct ifnet *ifp)
{
struct ath_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
/*
* When not coordinating the BSS, change the hardware
@@ -2404,7 +2706,7 @@ static int
ath_beaconq_config(struct ath_softc *sc)
{
#define ATH_EXPONENT_TO_VALUE(v) ((1<<(v))-1)
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
struct ath_hal *ah = sc->sc_ah;
HAL_TXQ_INFO qi;
@@ -2444,38 +2746,81 @@ ath_beaconq_config(struct ath_softc *sc)
static int
ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni)
{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ath_vap *avp = ATH_VAP(vap);
struct ath_buf *bf;
struct mbuf *m;
int error;
- bf = STAILQ_FIRST(&sc->sc_bbuf);
- if (bf == NULL) {
- DPRINTF(sc, ATH_DEBUG_BEACON, "%s: no dma buffers\n", __func__);
- sc->sc_stats.ast_be_nombuf++; /* XXX */
- return ENOMEM; /* XXX */
+ bf = avp->av_bcbuf;
+ if (bf->bf_m != NULL) {
+ bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+ m_freem(bf->bf_m);
+ bf->bf_m = NULL;
+ }
+ if (bf->bf_node != NULL) {
+ ieee80211_free_node(bf->bf_node);
+ bf->bf_node = NULL;
}
+
/*
* NB: the beacon data buffer must be 32-bit aligned;
* we assume the mbuf routines will return us something
* with this alignment (perhaps should assert).
*/
- m = ieee80211_beacon_alloc(ni, &sc->sc_boff);
+ m = ieee80211_beacon_alloc(ni, &avp->av_boff);
if (m == NULL) {
- DPRINTF(sc, ATH_DEBUG_BEACON, "%s: cannot get mbuf\n",
- __func__);
+ device_printf(sc->sc_dev, "%s: cannot get mbuf\n", __func__);
sc->sc_stats.ast_be_nombuf++;
return ENOMEM;
}
error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m,
bf->bf_segs, &bf->bf_nseg,
BUS_DMA_NOWAIT);
- if (error == 0) {
- bf->bf_m = m;
- bf->bf_node = ieee80211_ref_node(ni);
- } else {
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "%s: cannot map mbuf, bus_dmamap_load_mbuf_sg returns %d\n",
+ __func__, error);
m_freem(m);
+ return error;
}
- return error;
+
+ /*
+ * Calculate a TSF adjustment factor required for staggered
+ * beacons. Note that we assume the format of the beacon
+ * frame leaves the tstamp field immediately following the
+ * header.
+ */
+ if (sc->sc_stagbeacons && avp->av_bslot > 0) {
+ uint64_t tsfadjust;
+ struct ieee80211_frame *wh;
+
+ /*
+ * The beacon interval is in TU's; the TSF is in usecs.
+ * We figure out how many TU's to add to align the timestamp
+ * then convert to TSF units and handle byte swapping before
+ * inserting it in the frame. The hardware will then add this
+ * each time a beacon frame is sent. Note that we align vap's
+ * 1..N and leave vap 0 untouched. This means vap 0 has a
+ * timestamp in one beacon interval while the others get a
+ * timstamp aligned to the next interval.
+ */
+ tsfadjust = ni->ni_intval *
+ (ATH_BCBUF - avp->av_bslot) / ATH_BCBUF;
+ tsfadjust = htole64(tsfadjust << 10); /* TU -> TSF */
+
+ DPRINTF(sc, ATH_DEBUG_BEACON,
+ "%s: %s beacons bslot %d intval %u tsfadjust %llu\n",
+ __func__, sc->sc_stagbeacons ? "stagger" : "burst",
+ avp->av_bslot, ni->ni_intval, le64toh(tsfadjust));
+
+ wh = mtod(m, struct ieee80211_frame *);
+ memcpy(&wh[1], &tsfadjust, sizeof(tsfadjust));
+ }
+ bf->bf_m = m;
+ bf->bf_node = ieee80211_ref_node(ni);
+
+ return 0;
}
/*
@@ -2516,8 +2861,12 @@ ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf)
* Switch antenna every 4 beacons.
* XXX assumes two antenna
*/
- antenna = sc->sc_txantenna != 0 ? sc->sc_txantenna
- : (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1);
+ if (sc->sc_txantenna != 0)
+ antenna = sc->sc_txantenna;
+ else if (sc->sc_stagbeacons && sc->sc_nbcnvaps != 0)
+ antenna = ((sc->sc_stats.ast_be_xmit / sc->sc_nbcnvaps) & 4 ? 2 : 1);
+ else
+ antenna = (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1);
}
KASSERT(bf->bf_nseg == 1,
@@ -2527,7 +2876,7 @@ ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf)
* Calculate rate code.
* XXX everything at min xmit rate
*/
- rix = sc->sc_minrateix;
+ rix = 0;
rt = sc->sc_currates;
rate = rt->info[rix].rateCode;
if (USE_SHPREAMBLE(ic))
@@ -2551,14 +2900,16 @@ ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf)
, AH_TRUE /* last segment */
, ds /* first descriptor */
);
+#if 0
+ ath_desc_swap(ds);
+#endif
#undef USE_SHPREAMBLE
}
static void
-ath_beacon_update(struct ieee80211com *ic, int item)
+ath_beacon_update(struct ieee80211vap *vap, int item)
{
- struct ath_softc *sc = ic->ic_ifp->if_softc;
- struct ieee80211_beacon_offsets *bo = &sc->sc_boff;
+ struct ieee80211_beacon_offsets *bo = &ATH_VAP(vap)->av_boff;
setbit(bo->bo_flags, item);
}
@@ -2586,24 +2937,14 @@ static void
ath_beacon_proc(void *arg, int pending)
{
struct ath_softc *sc = arg;
- struct ath_buf *bf = STAILQ_FIRST(&sc->sc_bbuf);
- struct ieee80211_node *ni = bf->bf_node;
- struct ieee80211com *ic = ni->ni_ic;
struct ath_hal *ah = sc->sc_ah;
- struct ath_txq *cabq = sc->sc_cabq;
- struct mbuf *m;
- int ncabq, nmcastq, error, otherant;
+ struct ieee80211vap *vap;
+ struct ath_buf *bf;
+ int slot, otherant;
+ uint32_t bfaddr;
DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: pending %u\n",
__func__, pending);
-
- if (ic->ic_opmode == IEEE80211_M_STA ||
- ic->ic_opmode == IEEE80211_M_MONITOR ||
- bf == NULL || bf->bf_m == NULL) {
- DPRINTF(sc, ATH_DEBUG_ANY, "%s: ic_flags=%x bf=%p bf_m=%p\n",
- __func__, ic->ic_flags, bf, bf ? bf->bf_m : NULL);
- return;
- }
/*
* Check if the previous beacon has gone out. If
* not don't try to post another, skip this period
@@ -2627,39 +2968,34 @@ ath_beacon_proc(void *arg, int pending)
sc->sc_bmisscount = 0;
}
- /*
- * Update dynamic beacon contents. If this returns
- * non-zero then we need to remap the memory because
- * the beacon frame changed size (probably because
- * of the TIM bitmap).
- */
- m = bf->bf_m;
- nmcastq = sc->sc_mcastq.axq_depth;
- ncabq = ath_hal_numtxpending(ah, cabq->axq_qnum);
- if (ieee80211_beacon_update(bf->bf_node, &sc->sc_boff, m, ncabq+nmcastq)) {
- /* XXX too conservative? */
- bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
- error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m,
- bf->bf_segs, &bf->bf_nseg,
- BUS_DMA_NOWAIT);
- if (error != 0) {
- if_printf(ic->ic_ifp,
- "%s: bus_dmamap_load_mbuf_sg failed, error %u\n",
- __func__, error);
- return;
+ if (sc->sc_stagbeacons) { /* staggered beacons */
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+ uint32_t tsftu;
+
+ tsftu = ath_hal_gettsf32(ah) >> 10;
+ /* XXX lintval */
+ slot = ((tsftu % ic->ic_lintval) * ATH_BCBUF) / ic->ic_lintval;
+ vap = sc->sc_bslot[(slot+1) % ATH_BCBUF];
+ bfaddr = 0;
+ if (vap != NULL && vap->iv_state == IEEE80211_S_RUN) {
+ bf = ath_beacon_generate(sc, vap);
+ if (bf != NULL)
+ bfaddr = bf->bf_daddr;
}
- }
- if (ncabq && (sc->sc_boff.bo_tim[4] & 1)) {
- /*
- * CABQ traffic from the previous DTIM is still pending.
- * This is ok for now but when there are multiple vap's
- * and we are using staggered beacons we'll want to drain
- * the cabq before loading frames for the different vap.
- */
- DPRINTF(sc, ATH_DEBUG_BEACON,
- "%s: cabq did not drain, mcastq %u cabq %u/%u\n",
- __func__, nmcastq, ncabq, cabq->axq_depth);
- sc->sc_stats.ast_cabq_busy++;
+ } else { /* burst'd beacons */
+ uint32_t *bflink = &bfaddr;
+
+ for (slot = 0; slot < ATH_BCBUF; slot++) {
+ vap = sc->sc_bslot[slot];
+ if (vap != NULL && vap->iv_state == IEEE80211_S_RUN) {
+ bf = ath_beacon_generate(sc, vap);
+ if (bf != NULL) {
+ *bflink = bf->bf_daddr;
+ bflink = &bf->bf_desc->ds_link;
+ }
+ }
+ }
+ *bflink = 0; /* terminate list */
}
/*
@@ -2670,9 +3006,10 @@ ath_beacon_proc(void *arg, int pending)
* beacon interval to note the state change.
*/
/* XXX locking */
- if (sc->sc_updateslot == UPDATE)
+ if (sc->sc_updateslot == UPDATE) {
sc->sc_updateslot = COMMIT; /* commit next beacon */
- else if (sc->sc_updateslot == COMMIT)
+ sc->sc_slotupdate = slot;
+ } else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot)
ath_setslottime(sc); /* commit change to h/w */
/*
@@ -2681,64 +3018,159 @@ ath_beacon_proc(void *arg, int pending)
* on the non-default antenna.
* XXX assumes 2 anntenae
*/
- otherant = sc->sc_defant & 1 ? 2 : 1;
- if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2)
- ath_setdefantenna(sc, otherant);
- sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0;
+ if (!sc->sc_diversity && (!sc->sc_stagbeacons || slot == 0)) {
+ otherant = sc->sc_defant & 1 ? 2 : 1;
+ if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2)
+ ath_setdefantenna(sc, otherant);
+ sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0;
+ }
- /*
- * Construct tx descriptor.
- */
- ath_beacon_setup(sc, bf);
+ if (bfaddr != 0) {
+ /*
+ * Stop any current dma and put the new frame on the queue.
+ * This should never fail since we check above that no frames
+ * are still pending on the queue.
+ */
+ if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) {
+ DPRINTF(sc, ATH_DEBUG_ANY,
+ "%s: beacon queue %u did not stop?\n",
+ __func__, sc->sc_bhalq);
+ }
+ /* NB: cabq traffic should already be queued and primed */
+ ath_hal_puttxbuf(ah, sc->sc_bhalq, bfaddr);
+ ath_hal_txstart(ah, sc->sc_bhalq);
+
+ sc->sc_stats.ast_be_xmit++;
+ }
+}
+
+static struct ath_buf *
+ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap)
+{
+ struct ath_vap *avp = ATH_VAP(vap);
+ struct ath_txq *cabq = sc->sc_cabq;
+ struct ath_buf *bf;
+ struct mbuf *m;
+ int nmcastq, error;
+
+ KASSERT(vap->iv_state == IEEE80211_S_RUN,
+ ("not running, state %d", vap->iv_state));
+ KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer"));
/*
- * Stop any current dma and put the new frame on the queue.
- * This should never fail since we check above that no frames
- * are still pending on the queue.
+ * Update dynamic beacon contents. If this returns
+ * non-zero then we need to remap the memory because
+ * the beacon frame changed size (probably because
+ * of the TIM bitmap).
*/
- if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) {
- DPRINTF(sc, ATH_DEBUG_ANY,
- "%s: beacon queue %u did not stop?\n",
- __func__, sc->sc_bhalq);
+ bf = avp->av_bcbuf;
+ m = bf->bf_m;
+ nmcastq = avp->av_mcastq.axq_depth;
+ if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, nmcastq)) {
+ /* XXX too conservative? */
+ bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+ error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m,
+ bf->bf_segs, &bf->bf_nseg,
+ BUS_DMA_NOWAIT);
+ if (error != 0) {
+ if_printf(vap->iv_ifp,
+ "%s: bus_dmamap_load_mbuf_sg failed, error %u\n",
+ __func__, error);
+ return NULL;
+ }
}
+ if ((avp->av_boff.bo_tim[4] & 1) && cabq->axq_depth) {
+ DPRINTF(sc, ATH_DEBUG_BEACON,
+ "%s: cabq did not drain, mcastq %u cabq %u\n",
+ __func__, nmcastq, cabq->axq_depth);
+ sc->sc_stats.ast_cabq_busy++;
+ if (sc->sc_nvaps > 1 && sc->sc_stagbeacons) {
+ /*
+ * CABQ traffic from a previous vap is still pending.
+ * We must drain the q before this beacon frame goes
+ * out as otherwise this vap's stations will get cab
+ * frames from a different vap.
+ * XXX could be slow causing us to miss DBA
+ */
+ ath_tx_draintxq(sc, cabq);
+ }
+ }
+ ath_beacon_setup(sc, bf);
bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
/*
* Enable the CAB queue before the beacon queue to
* insure cab frames are triggered by this beacon.
*/
- if (sc->sc_boff.bo_tim_len && (sc->sc_boff.bo_tim[4] & 1)) {
+ if (avp->av_boff.bo_tim[4] & 1) {
+ struct ath_hal *ah = sc->sc_ah;
+
/* NB: only at DTIM */
ATH_TXQ_LOCK(cabq);
- ATH_TXQ_LOCK(&sc->sc_mcastq);
+ ATH_TXQ_LOCK(&avp->av_mcastq);
if (nmcastq) {
struct ath_buf *bfm;
/*
* Move frames from the s/w mcast q to the h/w cab q.
+ * XXX MORE_DATA bit
*/
- bfm = STAILQ_FIRST(&sc->sc_mcastq.axq_q);
+ bfm = STAILQ_FIRST(&avp->av_mcastq.axq_q);
if (cabq->axq_link != NULL) {
*cabq->axq_link = bfm->bf_daddr;
} else
ath_hal_puttxbuf(ah, cabq->axq_qnum,
bfm->bf_daddr);
- ath_txqmove(cabq, &sc->sc_mcastq);
+ ath_txqmove(cabq, &avp->av_mcastq);
sc->sc_stats.ast_cabq_xmit += nmcastq;
}
/* NB: gated by beacon so safe to start here */
ath_hal_txstart(ah, cabq->axq_qnum);
ATH_TXQ_UNLOCK(cabq);
- ATH_TXQ_UNLOCK(&sc->sc_mcastq);
+ ATH_TXQ_UNLOCK(&avp->av_mcastq);
}
+ return bf;
+}
+
+static void
+ath_beacon_start_adhoc(struct ath_softc *sc, struct ieee80211vap *vap)
+{
+ struct ath_vap *avp = ATH_VAP(vap);
+ struct ath_hal *ah = sc->sc_ah;
+ struct ath_buf *bf;
+ struct mbuf *m;
+ int error;
+
+ KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer"));
+
+ /*
+ * Update dynamic beacon contents. If this returns
+ * non-zero then we need to remap the memory because
+ * the beacon frame changed size (probably because
+ * of the TIM bitmap).
+ */
+ bf = avp->av_bcbuf;
+ m = bf->bf_m;
+ if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, 0)) {
+ /* XXX too conservative? */
+ bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+ error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m,
+ bf->bf_segs, &bf->bf_nseg,
+ BUS_DMA_NOWAIT);
+ if (error != 0) {
+ if_printf(vap->iv_ifp,
+ "%s: bus_dmamap_load_mbuf_sg failed, error %u\n",
+ __func__, error);
+ return;
+ }
+ }
+ ath_beacon_setup(sc, bf);
+ bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
+
+ /* NB: caller is known to have already stopped tx dma */
ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr);
ath_hal_txstart(ah, sc->sc_bhalq);
- DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
- "%s: TXDP[%u] = %p (%p)\n", __func__,
- sc->sc_bhalq, (caddr_t)bf->bf_daddr, bf->bf_desc);
-
- sc->sc_stats.ast_be_xmit++;
}
/*
@@ -2756,6 +3188,25 @@ ath_bstuck_proc(void *arg, int pending)
}
/*
+ * Reclaim beacon resources and return buffer to the pool.
+ */
+static void
+ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf)
+{
+
+ if (bf->bf_m != NULL) {
+ bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+ m_freem(bf->bf_m);
+ bf->bf_m = NULL;
+ }
+ if (bf->bf_node != NULL) {
+ ieee80211_free_node(bf->bf_node);
+ bf->bf_node = NULL;
+ }
+ STAILQ_INSERT_TAIL(&sc->sc_bbuf, bf, bf_list);
+}
+
+/*
* Reclaim beacon resources.
*/
static void
@@ -2792,29 +3243,46 @@ ath_beacon_free(struct ath_softc *sc)
* we've associated with.
*/
static void
-ath_beacon_config(struct ath_softc *sc)
+ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap)
{
#define TSF_TO_TU(_h,_l) \
((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10))
#define FUDGE 2
struct ath_hal *ah = sc->sc_ah;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+ struct ieee80211_node *ni;
u_int32_t nexttbtt, intval, tsftu;
u_int64_t tsf;
+ if (vap == NULL)
+ vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */
+ ni = vap->iv_bss;
+
/* extract tstamp from last beacon and convert to TU */
nexttbtt = TSF_TO_TU(LE_READ_4(ni->ni_tstamp.data + 4),
LE_READ_4(ni->ni_tstamp.data));
- /* NB: the beacon interval is kept internally in TU's */
- intval = ni->ni_intval & HAL_BEACON_PERIOD;
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ /*
+ * For multi-bss ap support beacons are either staggered
+ * evenly over N slots or burst together. For the former
+ * arrange for the SWBA to be delivered for each slot.
+ * Slots that are not occupied will generate nothing.
+ */
+ /* NB: the beacon interval is kept internally in TU's */
+ intval = ni->ni_intval & HAL_BEACON_PERIOD;
+ if (sc->sc_stagbeacons)
+ intval /= ATH_BCBUF;
+ } else {
+ /* NB: the beacon interval is kept internally in TU's */
+ intval = ni->ni_intval & HAL_BEACON_PERIOD;
+ }
if (nexttbtt == 0) /* e.g. for ap mode */
nexttbtt = intval;
else if (intval) /* NB: can be 0 for monitor mode */
nexttbtt = roundup(nexttbtt, intval);
DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u intval %u (%u)\n",
__func__, nexttbtt, intval, ni->ni_intval);
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (ic->ic_opmode == IEEE80211_M_STA && !sc->sc_swbmiss) {
HAL_BEACON_STATE bs;
int dtimperiod, dtimcount;
int cfpperiod, cfpcount;
@@ -2870,7 +3338,7 @@ ath_beacon_config(struct ath_softc *sc)
* before taking a BMISS interrupt.
* Note that we clamp the result to at most 10 beacons.
*/
- bs.bs_bmissthreshold = ic->ic_bmissthreshold;
+ bs.bs_bmissthreshold = vap->iv_bmissthreshold;
if (bs.bs_bmissthreshold > 10)
bs.bs_bmissthreshold = 10;
else if (bs.bs_bmissthreshold <= 0)
@@ -2953,7 +3421,7 @@ ath_beacon_config(struct ath_softc *sc)
* ibss mode load it once here.
*/
if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol)
- ath_beacon_proc(sc, 0);
+ ath_beacon_start_adhoc(sc, vap);
}
sc->sc_syncbeacon = 0;
#undef FUDGE
@@ -3130,7 +3598,7 @@ ath_desc_alloc(struct ath_softc *sc)
}
error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf,
- "beacon", 1, 1);
+ "beacon", ATH_BCBUF, 1);
if (error != 0) {
ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf);
ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
@@ -3164,7 +3632,6 @@ ath_node_alloc(struct ieee80211_node_table *nt)
/* XXX stat+msg */
return NULL;
}
- an->an_avgrssi = ATH_RSSI_DUMMY_MARKER;
ath_rate_node_init(sc, an);
DPRINTF(sc, ATH_DEBUG_NODE, "%s: an %p\n", __func__, an);
@@ -3183,26 +3650,6 @@ ath_node_free(struct ieee80211_node *ni)
sc->sc_node_free(ni);
}
-static int8_t
-ath_node_getrssi(const struct ieee80211_node *ni)
-{
-#define HAL_EP_RND(x, mul) \
- ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
- u_int32_t avgrssi = ATH_NODE_CONST(ni)->an_avgrssi;
- int32_t rssi;
-
- /*
- * When only one frame is received there will be no state in
- * avgrssi so fallback on the value recorded by the 802.11 layer.
- */
- if (avgrssi != ATH_RSSI_DUMMY_MARKER)
- rssi = HAL_EP_RND(avgrssi, HAL_RSSI_EP_MULTIPLIER);
- else
- rssi = ni->ni_rssi;
- return rssi < 0 ? 0 : rssi > 127 ? 127 : rssi;
-#undef HAL_EP_RND
-}
-
static void
ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise)
{
@@ -3211,7 +3658,7 @@ ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise)
struct ath_hal *ah = sc->sc_ah;
HAL_CHANNEL hchan;
- *rssi = ath_node_getrssi(ni);
+ *rssi = ic->ic_node_getrssi(ni);
if (ni->ni_chan != IEEE80211_CHAN_ANYC) {
ath_mapchan(&hchan, ni->ni_chan);
*noise = ath_hal_getchannoise(ah, &hchan);
@@ -3309,33 +3756,33 @@ ath_extend_tsf(u_int32_t rstamp, u_int64_t tsf)
* and to do ibss merges.
*/
static void
-ath_recv_mgmt(struct ieee80211com *ic, struct mbuf *m,
- struct ieee80211_node *ni,
+ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
int subtype, int rssi, int noise, u_int32_t rstamp)
{
- struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
/*
* Call up first so subsequent work can use information
* potentially stored in the node (e.g. for ibss merge).
*/
- sc->sc_recv_mgmt(ic, m, ni, subtype, rssi, noise, rstamp);
+ ATH_VAP(vap)->av_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
switch (subtype) {
case IEEE80211_FC0_SUBTYPE_BEACON:
/* update rssi statistics for use by the hal */
ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi);
if (sc->sc_syncbeacon &&
- ni == ic->ic_bss && ic->ic_state == IEEE80211_S_RUN) {
+ ni == vap->iv_bss && vap->iv_state == IEEE80211_S_RUN) {
/*
* Resync beacon timers using the tsf of the beacon
* frame we just received.
*/
- ath_beacon_config(sc);
+ ath_beacon_config(sc, vap);
}
/* fall thru... */
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
- if (ic->ic_opmode == IEEE80211_M_IBSS &&
- ic->ic_state == IEEE80211_S_RUN) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS &&
+ vap->iv_state == IEEE80211_S_RUN) {
u_int64_t tsf = ath_extend_tsf(rstamp,
ath_hal_gettsf64(sc->sc_ah));
/*
@@ -3377,14 +3824,13 @@ ath_setdefantenna(struct ath_softc *sc, u_int antenna)
}
static int
-ath_rx_tap(struct ath_softc *sc, struct mbuf *m,
+ath_rx_tap(struct ifnet *ifp, struct mbuf *m,
const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf)
{
#define CHANNEL_HT (CHANNEL_HT20|CHANNEL_HT40PLUS|CHANNEL_HT40MINUS)
+ struct ath_softc *sc = ifp->if_softc;
u_int8_t rix;
- KASSERT(sc->sc_drvbpf != NULL, ("no tap"));
-
/*
* Discard anything shorter than an ack or cts.
*/
@@ -3425,13 +3871,28 @@ ath_rx_tap(struct ath_softc *sc, struct mbuf *m,
sc->sc_rx_th.wr_antnoise = nf;
sc->sc_rx_th.wr_antenna = rs->rs_antenna;
- bpf_mtap2(sc->sc_drvbpf, &sc->sc_rx_th, sc->sc_rx_th_len, m);
+ bpf_mtap2(ifp->if_bpf, &sc->sc_rx_th, sc->sc_rx_th_len, m);
return 1;
#undef CHANNEL_HT
}
static void
+ath_handle_micerror(struct ieee80211com *ic,
+ struct ieee80211_frame *wh, int keyix)
+{
+ struct ieee80211_node *ni;
+
+ /* XXX recheck MIC to deal w/ chips that lie */
+ /* XXX discard MIC errors on !data frames */
+ ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) wh);
+ if (ni != NULL) {
+ ieee80211_notify_michael_failure(ni->ni_vap, wh, keyix);
+ ieee80211_free_node(ni);
+ }
+}
+
+static void
ath_rx_proc(void *arg, int npending)
{
#define PA2DESC(_sc, _pa) \
@@ -3439,21 +3900,19 @@ ath_rx_proc(void *arg, int npending)
((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
struct ath_softc *sc = arg;
struct ath_buf *bf;
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ath_hal *ah = sc->sc_ah;
struct ath_desc *ds;
struct ath_rx_status *rs;
struct mbuf *m;
struct ieee80211_node *ni;
- struct ath_node *an;
int len, type, ngood;
u_int phyerr;
HAL_STATUS status;
int16_t nf;
u_int64_t tsf;
-
DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending);
ngood = 0;
nf = ath_hal_getchannoise(ah, &sc->sc_curchan);
@@ -3540,11 +3999,10 @@ ath_rx_proc(void *arg, int npending)
bus_dmamap_sync(sc->sc_dmat,
bf->bf_dmamap,
BUS_DMASYNC_POSTREAD);
- ieee80211_notify_michael_failure(ic,
+ ath_handle_micerror(ic,
mtod(m, struct ieee80211_frame *),
sc->sc_splitmic ?
- rs->rs_keyix-32 : rs->rs_keyix
- );
+ rs->rs_keyix-32 : rs->rs_keyix);
}
}
ifp->if_ierrors++;
@@ -3562,14 +4020,14 @@ rx_error:
* pass decrypt+mic errors but others may be
* interesting (e.g. crc).
*/
- if (bpf_peers_present(sc->sc_drvbpf) &&
+ if (bpf_peers_present(ifp->if_bpf) &&
(rs->rs_status & sc->sc_monpass)) {
bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
BUS_DMASYNC_POSTREAD);
/* NB: bpf needs the mbuf length setup */
len = rs->rs_datalen;
m->m_pkthdr.len = m->m_len = len;
- (void) ath_rx_tap(sc, m, rs, tsf, nf);
+ (void) ath_rx_tap(ifp, m, rs, tsf, nf);
}
/* XXX pass MIC errors up for s/w reclaculation */
goto rx_next;
@@ -3624,10 +4082,11 @@ rx_accept:
m->m_pkthdr.len = len;
}
+ ifp->if_ipackets++;
sc->sc_stats.ast_ant_rx[rs->rs_antenna]++;
- if (bpf_peers_present(sc->sc_drvbpf) &&
- !ath_rx_tap(sc, m, rs, tsf, nf)) {
+ if (bpf_peers_present(ifp->if_bpf) &&
+ !ath_rx_tap(ifp, m, rs, tsf, nf)) {
m_freem(m); /* XXX reclaim */
goto rx_next;
}
@@ -3661,18 +4120,30 @@ rx_accept:
mtod(m, const struct ieee80211_frame_min *),
rs->rs_keyix == HAL_RXKEYIX_INVALID ?
IEEE80211_KEYIX_NONE : rs->rs_keyix);
+ if (ni != NULL) {
+ /*
+ * Sending station is known, dispatch directly.
+ */
+ type = ieee80211_input(ni, m,
+ rs->rs_rssi, nf, rs->rs_tstamp);
+ ieee80211_free_node(ni);
+ /*
+ * Arrange to update the last rx timestamp only for
+ * frames from our ap when operating in station mode.
+ * This assumes the rx key is always setup when
+ * associated.
+ */
+ if (ic->ic_opmode == IEEE80211_M_STA &&
+ rs->rs_keyix != HAL_RXKEYIX_INVALID)
+ ngood++;
+ } else {
+ type = ieee80211_input_all(ic, m,
+ rs->rs_rssi, nf, rs->rs_tstamp);
+ }
/*
* Track rx rssi and do any rx antenna management.
*/
- an = ATH_NODE(ni);
- ATH_RSSI_LPF(an->an_avgrssi, rs->rs_rssi);
ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, rs->rs_rssi);
- /*
- * Send frame up for processing.
- */
- type = ieee80211_input(ic, m, ni,
- rs->rs_rssi, nf, rs->rs_tstamp);
- ieee80211_free_node(ni);
if (sc->sc_diversity) {
/*
* When using fast diversity, change the default rx
@@ -3698,14 +4169,6 @@ rx_accept:
} else if (ticks - sc->sc_ledevent >= sc->sc_ledidle)
ath_led_event(sc, ATH_LED_POLL);
}
- /*
- * Arrange to update the last rx timestamp only for
- * frames from our ap when operating in station mode.
- * This assumes the rx key is always setup when associated.
- */
- if (ic->ic_opmode == IEEE80211_M_STA &&
- rs->rs_keyix != HAL_RXKEYIX_INVALID)
- ngood++;
rx_next:
STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
} while (ath_rxbuf_init(sc, bf) == 0);
@@ -3715,7 +4178,6 @@ rx_next:
if (ngood)
sc->sc_lastrx = tsf;
- /* NB: may want to check mgtq too */
if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0 &&
!IFQ_IS_EMPTY(&ifp->if_snd))
ath_start(ifp);
@@ -3825,7 +4287,8 @@ ath_txq_update(struct ath_softc *sc, int ac)
{
#define ATH_EXPONENT_TO_VALUE(v) ((1<<v)-1)
#define ATH_TXOP_TO_US(v) (v<<5)
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ath_txq *txq = sc->sc_ac2q[ac];
struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac];
struct ath_hal *ah = sc->sc_ah;
@@ -3838,7 +4301,7 @@ ath_txq_update(struct ath_softc *sc, int ac)
qi.tqi_burstTime = ATH_TXOP_TO_US(wmep->wmep_txopLimit);
if (!ath_hal_settxqueueprops(ah, txq->axq_qnum, &qi)) {
- device_printf(sc->sc_dev, "unable to update hardware queue "
+ if_printf(ifp, "unable to update hardware queue "
"parameters for %s traffic!\n",
ieee80211_wme_acnames[ac]);
return 0;
@@ -3888,7 +4351,6 @@ ath_tx_cleanup(struct ath_softc *sc)
for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
if (ATH_TXQ_SETUP(sc, i))
ath_tx_cleanupq(sc, &sc->sc_txq[i]);
- ATH_TXQ_LOCK_DESTROY(&sc->sc_mcastq);
}
/*
@@ -4016,8 +4478,8 @@ ath_tx_handoff(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf)
* to avoid possible races.
*/
ATH_TXQ_LOCK(txq);
- ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
- if (txq != &sc->sc_mcastq) {
+ if (txq->axq_qnum != ATH_TXQ_SWQ) {
+ ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
if (txq->axq_link == NULL) {
ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
DPRINTF(sc, ATH_DEBUG_XMIT,
@@ -4034,8 +4496,20 @@ ath_tx_handoff(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf)
txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
ath_hal_txstart(ah, txq->axq_qnum);
} else {
- if (txq->axq_link != NULL)
+ if (txq->axq_link != NULL) {
+ struct ath_buf *last = ATH_TXQ_LAST(txq);
+ struct ieee80211_frame *wh;
+
+ /* mark previous frame */
+ wh = mtod(last->bf_m, struct ieee80211_frame *);
+ wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+ bus_dmamap_sync(sc->sc_dmat, last->bf_dmamap,
+ BUS_DMASYNC_PREWRITE);
+
+ /* link descriptor */
*txq->axq_link = bf->bf_daddr;
+ }
+ ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
}
ATH_TXQ_UNLOCK(txq);
@@ -4045,9 +4519,11 @@ static int
ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf,
struct mbuf *m0)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ath_vap *avp = ATH_VAP(vap);
struct ath_hal *ah = sc->sc_ah;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams;
int error, iswep, ismcast, isfrag, ismrr;
int keyix, hdrlen, pktlen, try0;
@@ -4083,7 +4559,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
* frame. The only reason this can fail is because of an
* unknown or unsupported cipher/key type.
*/
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
/*
* This can happen when the key is yanked after the
@@ -4156,6 +4632,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
an = ATH_NODE(ni);
flags = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */
ismrr = 0; /* default no multi-rate retry*/
+ pri = M_WME_GETAC(m0); /* honor classification */
/*
* Calculate Atheros packet type from IEEE80211 packet header,
* setup for rate calculations, and select h/w transmit queue.
@@ -4171,32 +4648,20 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
atype = HAL_PKT_TYPE_ATIM;
else
atype = HAL_PKT_TYPE_NORMAL; /* XXX */
- rix = sc->sc_minrateix;
+ rix = an->an_mgmtrix;
txrate = rt->info[rix].rateCode;
if (shortPreamble)
txrate |= rt->info[rix].shortPreamble;
try0 = ATH_TXMGTTRY;
- /* NB: force all management frames to highest queue */
- if (ni->ni_flags & IEEE80211_NODE_QOS) {
- /* NB: force all management frames to highest queue */
- pri = WME_AC_VO;
- } else
- pri = WME_AC_BE;
flags |= HAL_TXDESC_INTREQ; /* force interrupt */
break;
case IEEE80211_FC0_TYPE_CTL:
atype = HAL_PKT_TYPE_PSPOLL; /* stop setting of duration */
- rix = sc->sc_minrateix;
+ rix = an->an_mgmtrix;
txrate = rt->info[rix].rateCode;
if (shortPreamble)
txrate |= rt->info[rix].shortPreamble;
try0 = ATH_TXMGTTRY;
- /* NB: force all ctl frames to highest queue */
- if (ni->ni_flags & IEEE80211_NODE_QOS) {
- /* NB: force all ctl frames to highest queue */
- pri = WME_AC_VO;
- } else
- pri = WME_AC_BE;
flags |= HAL_TXDESC_INTREQ; /* force interrupt */
break;
case IEEE80211_FC0_TYPE_DATA:
@@ -4207,16 +4672,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
* rate to use.
*/
if (ismcast) {
- /*
- * Check mcast rate setting in case it's changed.
- * XXX move out of fastpath
- */
- if (ic->ic_mcast_rate != sc->sc_mcastrate) {
- sc->sc_mcastrix =
- ath_tx_findrix(rt, ic->ic_mcast_rate);
- sc->sc_mcastrate = ic->ic_mcast_rate;
- }
- rix = sc->sc_mcastrix;
+ rix = an->an_mcastrix;
txrate = rt->info[rix].rateCode;
if (shortPreamble)
txrate |= rt->info[rix].shortPreamble;
@@ -4229,7 +4685,6 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
if (try0 != ATH_TXMAXTRY)
ismrr = 1;
}
- pri = M_WME_GETAC(m0);
if (cap->cap_wmeParams[pri].wmep_noackPolicy)
flags |= HAL_TXDESC_NOACK;
break;
@@ -4248,17 +4703,15 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
* queue (to prevent out of order delivery) multicast
* frames must be buffered until after the beacon.
*/
- if (ismcast && (ic->ic_ps_sta || sc->sc_mcastq.axq_depth)) {
- txq = &sc->sc_mcastq;
- /* XXX? more bit in 802.11 frame header */
- }
+ if (ismcast && (vap->iv_ps_sta || avp->av_mcastq.axq_depth))
+ txq = &avp->av_mcastq;
/*
* Calculate miscellaneous flags.
*/
if (ismcast) {
flags |= HAL_TXDESC_NOACK; /* no ack on broad/multicast */
- } else if (pktlen > ic->ic_rtsthreshold &&
+ } else if (pktlen > vap->iv_rtsthreshold &&
(ni->ni_ath_flags & IEEE80211_NODE_FF) == 0) {
flags |= HAL_TXDESC_RTSENA; /* RTS based on frame length */
cix = rt->info[rix].controlRate;
@@ -4386,9 +4839,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
ieee80211_dump_pkt(ic, mtod(m0, caddr_t), m0->m_len,
sc->sc_hwmap[txrate].ieeerate, -1);
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
u_int64_t tsf = ath_hal_gettsf64(ah);
sc->sc_tx_th.wt_tsf = htole64(tsf);
@@ -4401,8 +4852,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
sc->sc_tx_th.wt_txpower = ni->ni_txpower;
sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
- bpf_mtap2(sc->sc_drvbpf,
- &sc->sc_tx_th, sc->sc_tx_th_len, m0);
+ bpf_mtap2(ifp->if_bpf, &sc->sc_tx_th, sc->sc_tx_th_len, m0);
}
/*
@@ -4465,7 +4915,8 @@ static int
ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
{
struct ath_hal *ah = sc->sc_ah;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ath_buf *bf;
struct ath_desc *ds, *ds0;
struct ath_tx_status *ts;
@@ -4706,10 +5157,12 @@ ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq)
ATH_TXQ_UNLOCK(txq);
#ifdef ATH_DEBUG
if (sc->sc_debug & ATH_DEBUG_RESET) {
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+
ath_printtxbuf(bf, txq->axq_qnum, ix,
ath_hal_txprocdesc(ah, bf->bf_desc,
&bf->bf_status.ds_txstat) == HAL_OK);
- ieee80211_dump_pkt(&sc->sc_ic, mtod(bf->bf_m, caddr_t),
+ ieee80211_dump_pkt(ic, mtod(bf->bf_m, caddr_t),
bf->bf_m->m_len, 0, -1);
}
#endif /* ATH_DEBUG */
@@ -4770,7 +5223,6 @@ ath_draintxq(struct ath_softc *sc)
for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
if (ATH_TXQ_SETUP(sc, i))
ath_tx_draintxq(sc, &sc->sc_txq[i]);
- ath_tx_draintxq(sc, &sc->sc_mcastq);
#ifdef ATH_DEBUG
if (sc->sc_debug & ATH_DEBUG_RESET) {
struct ath_buf *bf = STAILQ_FIRST(&sc->sc_bbuf);
@@ -4778,7 +5230,7 @@ ath_draintxq(struct ath_softc *sc)
ath_printtxbuf(bf, sc->sc_bhalq, 0,
ath_hal_txprocdesc(ah, bf->bf_desc,
&bf->bf_status.ds_txstat) == HAL_OK);
- ieee80211_dump_pkt(&sc->sc_ic, mtod(bf->bf_m, caddr_t),
+ ieee80211_dump_pkt(ifp->if_l2com, mtod(bf->bf_m, caddr_t),
bf->bf_m->m_len, 0, -1);
}
}
@@ -4890,42 +5342,6 @@ ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan)
}
/*
- * Poll for a channel clear indication; this is required
- * for channels requiring DFS and not previously visited
- * and/or with a recent radar detection.
- */
-static void
-ath_dfswait(void *arg)
-{
- struct ath_softc *sc = arg;
- struct ath_hal *ah = sc->sc_ah;
- HAL_CHANNEL hchan;
-
- ath_hal_radar_wait(ah, &hchan);
- DPRINTF(sc, ATH_DEBUG_DFS, "%s: radar_wait %u/%x/%x\n",
- __func__, hchan.channel, hchan.channelFlags, hchan.privFlags);
-
- if (hchan.privFlags & CHANNEL_INTERFERENCE) {
- if_printf(sc->sc_ifp,
- "channel %u/0x%x/0x%x has interference\n",
- hchan.channel, hchan.channelFlags, hchan.privFlags);
- return;
- }
- if ((hchan.privFlags & CHANNEL_DFS) == 0) {
- /* XXX should not happen */
- return;
- }
- if (hchan.privFlags & CHANNEL_DFS_CLEAR) {
- sc->sc_curchan.privFlags |= CHANNEL_DFS_CLEAR;
- sc->sc_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- if_printf(sc->sc_ifp,
- "channel %u/0x%x/0x%x marked clear\n",
- hchan.channel, hchan.channelFlags, hchan.privFlags);
- } else
- callout_reset(&sc->sc_dfs_ch, 2 * hz, ath_dfswait, sc);
-}
-
-/*
* Set/change channels. If the channel is really being changed,
* it's done by reseting the chip. To accomplish this we must
* first cleanup any pending DMA, then restart stuff after a la
@@ -4934,8 +5350,9 @@ ath_dfswait(void *arg)
static int
ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ath_hal *ah = sc->sc_ah;
- struct ieee80211com *ic = &sc->sc_ic;
HAL_CHANNEL hchan;
/*
@@ -4967,7 +5384,7 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
ath_draintxq(sc); /* clear pending tx frames */
ath_stoprecv(sc); /* turn off frame recv */
if (!ath_hal_reset(ah, sc->sc_opmode, &hchan, AH_TRUE, &status)) {
- if_printf(ic->ic_ifp, "%s: unable to reset "
+ if_printf(ifp, "%s: unable to reset "
"channel %u (%u Mhz, flags 0x%x hal flags 0x%x), "
"hal status %u\n", __func__,
ieee80211_chan2ieee(ic, chan), chan->ic_freq,
@@ -4975,7 +5392,6 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
return EIO;
}
sc->sc_curchan = hchan;
- ath_update_txpow(sc); /* update tx power state */
sc->sc_diversity = ath_hal_getdiversity(ah);
sc->sc_calinterval = 1;
sc->sc_caltries = 0;
@@ -4984,8 +5400,8 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
* Re-enable rx framework.
*/
if (ath_startrecv(sc) != 0) {
- if_printf(ic->ic_ifp,
- "%s: unable to restart recv logic\n", __func__);
+ if_printf(ifp, "%s: unable to restart recv logic\n",
+ __func__);
return EIO;
}
@@ -4996,25 +5412,6 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
ath_chan_change(sc, chan);
/*
- * Handle DFS required waiting period to determine
- * if channel is clear of radar traffic.
- */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
-#define DFS_AND_NOT_CLEAR(_c) \
- (((_c)->privFlags & (CHANNEL_DFS | CHANNEL_DFS_CLEAR)) == CHANNEL_DFS)
- if (DFS_AND_NOT_CLEAR(&sc->sc_curchan)) {
- if_printf(sc->sc_ifp,
- "wait for DFS clear channel signal\n");
- /* XXX stop sndq */
- sc->sc_ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- callout_reset(&sc->sc_dfs_ch,
- 2 * hz, ath_dfswait, sc);
- } else
- callout_stop(&sc->sc_dfs_ch);
-#undef DFS_NOT_CLEAR
- }
-
- /*
* Re-enable interrupts.
*/
ath_hal_intrset(ah, sc->sc_imask);
@@ -5138,13 +5535,32 @@ ath_set_channel(struct ieee80211com *ic)
sc->sc_syncbeacon = 1;
}
+/*
+ * Walk the vap list and check if there any vap's in RUN state.
+ */
static int
-ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+ath_isanyrunningvaps(struct ieee80211vap *this)
{
- struct ifnet *ifp = ic->ic_ifp;
- struct ath_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = this->iv_ic;
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap != this && vap->iv_state == IEEE80211_S_RUN)
+ return 1;
+ }
+ return 0;
+}
+
+static int
+ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ath_vap *avp = ATH_VAP(vap);
struct ath_hal *ah = sc->sc_ah;
- struct ieee80211_node *ni;
+ struct ieee80211_node *ni = NULL;
int i, error, stamode;
u_int32_t rfilt;
static const HAL_LED_STATE leds[] = {
@@ -5159,75 +5575,70 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
};
DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__,
- ieee80211_state_name[ic->ic_state],
+ ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate]);
callout_stop(&sc->sc_cal_ch);
- callout_stop(&sc->sc_dfs_ch);
ath_hal_setledstate(ah, leds[nstate]); /* set LED */
- if (nstate == IEEE80211_S_INIT) {
+ if (nstate == IEEE80211_S_SCAN) {
/*
- * Shutdown host/driver operation:
- * o disable interrupts so we don't rx frames
- * o clean any pending items on the task q
- * o notify the rate control algorithm
+ * Scanning: turn off beacon miss and don't beacon.
+ * Mark beacon state so when we reach RUN state we'll
+ * [re]setup beacons. Unblock the task q thread so
+ * deferred interrupt processing is done.
*/
+ ath_hal_intrset(ah,
+ sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS));
sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
- ath_hal_intrset(ah, sc->sc_imask &~ HAL_INT_GLOBAL);
-#if 0
- /* XXX can't use taskqueue_drain 'cuz we're holding sc_mtx */
- taskqueue_drain(sc->sc_tq, &sc->sc_rxtask);
- taskqueue_drain(sc->sc_tq, &sc->sc_rxorntask);
- taskqueue_drain(sc->sc_tq, &sc->sc_bmisstask);
- taskqueue_drain(sc->sc_tq, &sc->sc_bstucktask);
-#endif
- ath_rate_newstate(sc, nstate);
- goto done;
+ sc->sc_beacons = 0;
+ taskqueue_unblock(sc->sc_tq);
}
- ni = ic->ic_bss;
+ ni = vap->iv_bss;
rfilt = ath_calcrxfilter(sc);
- stamode = (sc->sc_opmode == HAL_M_STA || sc->sc_opmode == HAL_M_IBSS);
+ stamode = (vap->iv_opmode == IEEE80211_M_STA ||
+ vap->iv_opmode == IEEE80211_M_IBSS);
if (stamode && nstate == IEEE80211_S_RUN) {
sc->sc_curaid = ni->ni_associd;
IEEE80211_ADDR_COPY(sc->sc_curbssid, ni->ni_bssid);
- } else
- sc->sc_curaid = 0;
-
+ ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid);
+ }
DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n",
- __func__, rfilt, ether_sprintf(sc->sc_curbssid),
- sc->sc_curaid);
-
+ __func__, rfilt, ether_sprintf(sc->sc_curbssid), sc->sc_curaid);
ath_hal_setrxfilter(ah, rfilt);
- if (stamode)
- ath_hal_setassocid(ah, sc->sc_curbssid, ni->ni_associd);
- if (ic->ic_opmode != IEEE80211_M_STA &&
- (ic->ic_flags & IEEE80211_F_PRIVACY)) {
+ /* XXX is this to restore keycache on resume? */
+ if (vap->iv_opmode != IEEE80211_M_STA &&
+ (vap->iv_flags & IEEE80211_F_PRIVACY)) {
for (i = 0; i < IEEE80211_WEP_NKID; i++)
if (ath_hal_keyisvalid(ah, i))
ath_hal_keysetmac(ah, i, ni->ni_bssid);
}
-
/*
* Notify the rate control algorithm so rates
* are setup should ath_beacon_alloc be called.
*/
- ath_rate_newstate(sc, nstate);
+ ath_rate_newstate(vap, nstate);
+
+ /*
+ * Invoke the parent method to do net80211 work.
+ */
+ error = avp->av_newstate(vap, nstate, arg);
+ if (error != 0)
+ goto bad;
if (nstate == IEEE80211_S_RUN) {
+ /* NB: collect bss node again, it may have changed */
+ ni = vap->iv_bss;
+
DPRINTF(sc, ATH_DEBUG_STATE,
- "%s(RUN): ic_flags=0x%08x iv=%d bssid=%s "
- "capinfo=0x%04x chan=%d\n"
- , __func__
- , ic->ic_flags
- , ni->ni_intval
- , ether_sprintf(ni->ni_bssid)
- , ni->ni_capinfo
- , ieee80211_chan2ieee(ic, ic->ic_curchan));
-
- switch (ic->ic_opmode) {
+ "%s(RUN): iv_flags 0x%08x bintvl %d bssid %s "
+ "capinfo 0x%04x chan %d\n", __func__,
+ vap->iv_flags, ni->ni_intval, ether_sprintf(ni->ni_bssid),
+ ni->ni_capinfo, ieee80211_chan2ieee(ic, ic->ic_curchan));
+
+ switch (vap->iv_opmode) {
case IEEE80211_M_HOSTAP:
case IEEE80211_M_IBSS:
/*
@@ -5240,7 +5651,7 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
* be called with beacon transmission active.
*/
ath_hal_stoptxdma(ah, sc->sc_bhalq);
- ath_beacon_free(sc);
+
error = ath_beacon_alloc(sc, ni);
if (error != 0)
goto bad;
@@ -5248,22 +5659,23 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
* If joining an adhoc network defer beacon timer
* configuration to the next beacon frame so we
* have a current TSF to use. Otherwise we're
- * starting an ibss/bss so there's no need to delay.
+ * starting an ibss/bss so there's no need to delay;
+ * if this is the first vap moving to RUN state, then
+ * beacon state needs to be [re]configured.
*/
- if (ic->ic_opmode == IEEE80211_M_IBSS &&
- ic->ic_bss->ni_tstamp.tsf != 0)
+ if (vap->iv_opmode == IEEE80211_M_IBSS &&
+ ni->ni_tstamp.tsf != 0) {
sc->sc_syncbeacon = 1;
- else
- ath_beacon_config(sc);
+ } else if (!sc->sc_beacons) {
+ ath_beacon_config(sc, vap);
+ sc->sc_beacons = 1;
+ }
break;
case IEEE80211_M_STA:
/*
- * Allocate a key cache slot to the station.
+ * Fakeup since we're not called by net80211.
*/
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0 &&
- sc->sc_hasclrkey &&
- ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE)
- ath_setup_stationkey(ni);
+ ath_newassoc(ni, 1);
/*
* Defer beacon timer configuration to the next
* beacon frame so we have a current TSF to use
@@ -5271,6 +5683,16 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
*/
sc->sc_syncbeacon = 1;
break;
+ case IEEE80211_M_MONITOR:
+ /*
+ * Monitor mode vaps have only INIT->RUN and RUN->RUN
+ * transitions so we must re-enable interrupts here to
+ * handle the case of a single monitor mode vap.
+ */
+ ath_hal_intrset(ah, sc->sc_imask);
+ break;
+ case IEEE80211_M_WDS:
+ break;
default:
break;
}
@@ -5285,23 +5707,31 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
- } else {
- ath_hal_intrset(ah,
- sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS));
- sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
- }
-done:
- /*
- * Invoke the parent method to complete the work.
- */
- error = sc->sc_newstate(ic, nstate, arg);
- /*
- * Finally, start any timers.
- */
- if (nstate == IEEE80211_S_RUN) {
- /* start periodic recalibration timer */
- callout_reset(&sc->sc_cal_ch, sc->sc_calinterval * hz,
- ath_calibrate, sc);
+ /*
+ * Finally, start any timers and the task q thread
+ * (in case we didn't go through SCAN state).
+ */
+ if (sc->sc_calinterval != 0) {
+ /* start periodic recalibration timer */
+ callout_reset(&sc->sc_cal_ch, sc->sc_calinterval * hz,
+ ath_calibrate, sc);
+ }
+ taskqueue_unblock(sc->sc_tq);
+ } else if (nstate == IEEE80211_S_INIT) {
+ /*
+ * If there are no vaps left in RUN state then
+ * shutdown host/driver operation:
+ * o disable interrupts
+ * o disable the task queue thread
+ * o mark beacon processing as stopped
+ */
+ if (!ath_isanyrunningvaps(vap)) {
+ sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
+ /* disable interrupts */
+ ath_hal_intrset(ah, sc->sc_imask &~ HAL_INT_GLOBAL);
+ taskqueue_block(sc->sc_tq);
+ sc->sc_beacons = 0;
+ }
}
bad:
return error;
@@ -5318,11 +5748,11 @@ bad:
static void
ath_setup_stationkey(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
- struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
ieee80211_keyix keyix, rxkeyix;
- if (!ath_key_alloc(ic, &ni->ni_ucastkey, &keyix, &rxkeyix)) {
+ if (!ath_key_alloc(vap, &ni->ni_ucastkey, &keyix, &rxkeyix)) {
/*
* Key cache is full; we'll fall back to doing
* the more expensive lookup in software. Note
@@ -5334,7 +5764,7 @@ ath_setup_stationkey(struct ieee80211_node *ni)
ni->ni_ucastkey.wk_keyix = keyix;
ni->ni_ucastkey.wk_rxkeyix = rxkeyix;
/* NB: this will create a pass-thru key entry */
- ath_keyset(sc, &ni->ni_ucastkey, ni->ni_macaddr, ic->ic_bss);
+ ath_keyset(sc, &ni->ni_ucastkey, ni->ni_macaddr, vap->iv_bss);
}
}
@@ -5346,58 +5776,78 @@ ath_setup_stationkey(struct ieee80211_node *ni)
static void
ath_newassoc(struct ieee80211_node *ni, int isnew)
{
- struct ieee80211com *ic = ni->ni_ic;
- struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ath_node *an = ATH_NODE(ni);
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
+ const struct ieee80211_txparam *tp;
+ enum ieee80211_phymode mode;
- ath_rate_newassoc(sc, ATH_NODE(ni), isnew);
- if (isnew &&
- (ic->ic_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey) {
- KASSERT(ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE,
- ("new assoc with a unicast key already setup (keyix %u)",
- ni->ni_ucastkey.wk_keyix));
+ /*
+ * Deduce netband of station to simplify setting up xmit
+ * parameters. Note this allows us to assign different
+ * parameters to each station in a mixed bss (b/g, n/[abg]).
+ */
+ if (ni->ni_flags & IEEE80211_NODE_HT) {
+ if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan))
+ mode = IEEE80211_MODE_11NA;
+ else
+ mode = IEEE80211_MODE_11NG;
+ } else if (IEEE80211_IS_CHAN_A(ni->ni_chan))
+ mode = IEEE80211_MODE_11A;
+ else if (ni->ni_flags & IEEE80211_NODE_ERP)
+ mode = IEEE80211_MODE_11G;
+ else
+ mode = IEEE80211_MODE_11B;
+ tp = &vap->iv_txparms[mode];
+ an->an_tp = tp;
+ an->an_mcastrix = ath_tx_findrix(sc->sc_rates[mode], tp->mcastrate);
+ an->an_mgmtrix = ath_tx_findrix(sc->sc_rates[mode], tp->mgmtrate);
+
+ ath_rate_newassoc(sc, an, isnew);
+ if (isnew &&
+ (vap->iv_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey &&
+ ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE)
ath_setup_stationkey(ni);
- }
}
static int
-ath_getchannels(struct ath_softc *sc,
- HAL_REG_DOMAIN rd, HAL_CTRY_CODE cc, HAL_BOOL outdoor, HAL_BOOL xchanmode)
+getchannels(struct ath_softc *sc, int *nchans, struct ieee80211_channel chans[],
+ int cc, int ecm, int outdoor)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = sc->sc_ifp;
struct ath_hal *ah = sc->sc_ah;
- HAL_CHANNEL *chans;
- int i, nchan;
- u_int32_t regdomain;
-
- chans = malloc(IEEE80211_CHAN_MAX * sizeof(HAL_CHANNEL),
- M_TEMP, M_NOWAIT);
- if (chans == NULL) {
- if_printf(ifp, "unable to allocate channel table\n");
+ HAL_CHANNEL *halchans;
+ int i, nhalchans, error;
+
+ halchans = malloc(IEEE80211_CHAN_MAX * sizeof(HAL_CHANNEL),
+ M_TEMP, M_NOWAIT | M_ZERO);
+ if (halchans == NULL) {
+ device_printf(sc->sc_dev,
+ "%s: unable to allocate channel table\n", __func__);
return ENOMEM;
}
- if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan,
- NULL, 0, NULL, cc, HAL_MODE_ALL, outdoor, xchanmode)) {
- (void) ath_hal_getregdomain(ah, &regdomain);
- if_printf(ifp, "unable to collect channel list from hal; "
- "regdomain likely %u country code %u\n", regdomain, cc);
- free(chans, M_TEMP);
- return EINVAL;
+ error = 0;
+ if (!ath_hal_init_channels(ah, halchans, IEEE80211_CHAN_MAX, &nhalchans,
+ NULL, 0, NULL, CTRY_DEFAULT, HAL_MODE_ALL, AH_FALSE, AH_TRUE)) {
+ error = EINVAL;
+ goto done;
}
+ if (nchans == NULL) /* no table requested */
+ goto done;
/*
* Convert HAL channels to ieee80211 ones.
*/
- memset(ic->ic_channels, 0, sizeof(ic->ic_channels));
- for (i = 0; i < nchan; i++) {
- HAL_CHANNEL *c = &chans[i];
- struct ieee80211_channel *ichan = &ic->ic_channels[i];
+ for (i = 0; i < nhalchans; i++) {
+ HAL_CHANNEL *c = &halchans[i];
+ struct ieee80211_channel *ichan = &chans[i];
ichan->ic_ieee = ath_hal_mhz2ieee(ah, c->channel,
c->channelFlags);
if (bootverbose)
- if_printf(ifp, "hal channel %u/%x -> %u\n",
- c->channel, c->channelFlags, ichan->ic_ieee);
+ device_printf(sc->sc_dev, "hal channel %u/%x -> %u "
+ "maxpow %d minpow %d maxreg %d\n",
+ c->channel, c->channelFlags, ichan->ic_ieee,
+ c->maxTxPower, c->minTxPower, c->maxRegTxPower);
ichan->ic_freq = c->channel;
if ((c->channelFlags & CHANNEL_PUREG) == CHANNEL_PUREG) {
@@ -5419,15 +5869,98 @@ ath_getchannels(struct ath_softc *sc,
ichan->ic_flags);
}
ichan->ic_maxregpower = c->maxRegTxPower; /* dBm */
- ichan->ic_maxpower = c->maxTxPower; /* 1/2 dBm */
+ /* XXX: old hal's don't provide maxTxPower for some parts */
+ ichan->ic_maxpower = (c->maxTxPower != 0) ?
+ c->maxTxPower : 2*c->maxRegTxPower; /* 1/2 dBm */
ichan->ic_minpower = c->minTxPower; /* 1/2 dBm */
}
- ic->ic_nchans = nchan;
- free(chans, M_TEMP);
- (void) ath_hal_getregdomain(ah, &sc->sc_regdomain);
- ath_hal_getcountrycode(ah, &sc->sc_countrycode);
- sc->sc_xchanmode = xchanmode;
- sc->sc_outdoor = outdoor;
+ *nchans = nhalchans;
+done:
+ free(halchans, M_TEMP);
+ return error;
+}
+
+static int
+ath_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd,
+ int nchans, struct ieee80211_channel chans[])
+{
+ struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ath_hal *ah = sc->sc_ah;
+ u_int32_t ord;
+ int error;
+
+ (void) ath_hal_getregdomain(ah, &ord);
+ /* XXX map sku->rd */
+ ath_hal_setregdomain(ah, rd->regdomain);
+ error = getchannels(sc, &nchans, chans, rd->country,
+ rd->ecm ? AH_TRUE : AH_FALSE,
+ rd->location == 'O' ? AH_TRUE : AH_FALSE);
+ if (error != 0) {
+ /*
+ * Restore previous state.
+ */
+ ath_hal_setregdomain(ah, ord);
+ (void) getchannels(sc, NULL, NULL, ic->ic_regdomain.country,
+ ic->ic_regdomain.ecm ? AH_TRUE : AH_FALSE,
+ ic->ic_regdomain.location == 'O' ? AH_TRUE : AH_FALSE);
+ return error;
+ }
+ return 0;
+}
+
+static void
+ath_getradiocaps(struct ieee80211com *ic,
+ int *nchans, struct ieee80211_channel chans[])
+{
+ struct ath_softc *sc = ic->ic_ifp->if_softc;
+ struct ath_hal *ah = sc->sc_ah;
+ u_int32_t ord;
+
+ (void) ath_hal_getregdomain(ah, &ord);
+ ath_hal_setregdomain(ah, 0);
+ /* XXX not quite right but close enough for now */
+ getchannels(sc, nchans, chans, CTRY_DEBUG, AH_TRUE, AH_FALSE);
+ ath_hal_setregdomain(ah, ord);
+}
+
+static int
+ath_mapregdomain(struct ath_softc *sc, u_int32_t rd)
+{
+ /* map Atheros rd's to SKU's */
+ return rd;
+}
+
+static int
+ath_getchannels(struct ath_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ath_hal *ah = sc->sc_ah;
+ u_int32_t rd, cc;
+ int error;
+
+ /*
+ * Convert HAL channels to ieee80211 ones.
+ */
+ error = getchannels(sc, &ic->ic_nchans, ic->ic_channels,
+ CTRY_DEFAULT, AH_TRUE, AH_FALSE);
+ (void) ath_hal_getregdomain(ah, &rd);
+ ath_hal_getcountrycode(ah, &cc); /* NB: cannot fail */
+ if (error) {
+ if_printf(ifp, "%s: unable to collect channel list from hal, "
+ "error %d\n", __func__, error);
+ if (error == EINVAL) {
+ if_printf(ifp, "%s: regdomain likely %u country code %u\n",
+ __func__, rd, cc);
+ }
+ return error;
+ }
+ ic->ic_regdomain.regdomain = ath_mapregdomain(sc, rd);
+ ic->ic_regdomain.country = cc;
+ ic->ic_regdomain.ecm = 1;
+ ic->ic_regdomain.location = 'I';
+ ic->ic_regdomain.isocc[0] = ' '; /* XXX don't know */
+ ic->ic_regdomain.isocc[1] = ' ';
return 0;
}
@@ -5488,26 +6021,6 @@ ath_led_event(struct ath_softc *sc, int event)
}
}
-static void
-ath_update_txpow(struct ath_softc *sc)
-{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ath_hal *ah = sc->sc_ah;
- u_int32_t txpow;
-
- if (sc->sc_curtxpow != ic->ic_txpowlimit) {
- ath_hal_settxpowlimit(ah, ic->ic_txpowlimit);
- /* read back in case value is clamped */
- if (ath_hal_gettxpowlimit(ah, &txpow))
- ic->ic_txpowlimit = sc->sc_curtxpow = txpow;
- }
- /*
- * Fetch max tx power level for status requests.
- */
- if (ath_hal_getmaxtxpow(sc->sc_ah, &txpow))
- ic->ic_bss->ni_txpower = txpow;
-}
-
static int
ath_rate_setup(struct ath_softc *sc, u_int mode)
{
@@ -5630,14 +6143,6 @@ ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode)
sc->sc_protrix = ath_tx_findrix(rt, 2*2);
else
sc->sc_protrix = ath_tx_findrix(rt, 2*1);
- /* rate index used to send management frames */
- sc->sc_minrateix = 0;
- /*
- * Setup multicast rate state.
- */
- /* XXX layering violation */
- sc->sc_mcastrix = ath_tx_findrix(rt, sc->sc_ic.ic_mcast_rate);
- sc->sc_mcastrate = sc->sc_ic.ic_mcast_rate;
/* NB: caller is responsible for reseting rate control state */
#undef N
}
@@ -5763,7 +6268,7 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
#define IS_RUNNING(ifp) \
((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
struct ath_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ifreq *ifr = (struct ifreq *)data;
int error = 0;
@@ -5787,7 +6292,7 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
* torn down much of our state. There's
* probably a better way to deal with this.
*/
- if (!sc->sc_invalid && ic->ic_bss != NULL)
+ if (!sc->sc_invalid)
ath_init(sc); /* XXX lose error */
} else
ath_stop_locked(ifp);
@@ -5802,12 +6307,18 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
ath_mode_init(sc);
break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
case SIOCGATHSTATS:
/* NB: embed these numbers to get a consistent view */
sc->sc_stats.ast_tx_packets = ifp->if_opackets;
sc->sc_stats.ast_rx_packets = ifp->if_ipackets;
+#if 0
ieee80211_getsignal(ic, &sc->sc_stats.ast_rx_rssi,
&sc->sc_stats.ast_rx_noise);
+#endif
sc->sc_stats.ast_tx_rate = sc->sc_hwmap[sc->sc_txrate].ieeerate;
ATH_UNLOCK(sc);
/*
@@ -5826,15 +6337,7 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
break;
#endif
default:
- error = ieee80211_ioctl(ic, cmd, data);
- if (error == ENETRESET) {
- if (IS_RUNNING(ifp) &&
- ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ath_init(sc); /* XXX lose error */
- error = 0;
- }
- if (error == ERESTART)
- error = IS_RUNNING(ifp) ? ath_reset(ifp) : 0;
+ error = ether_ioctl(ifp, cmd, data);
break;
}
ATH_UNLOCK(sc);
@@ -6059,48 +6562,6 @@ ath_sysctl_rfsilent(SYSCTL_HANDLER_ARGS)
}
static int
-ath_sysctl_countrycode(SYSCTL_HANDLER_ARGS)
-{
- struct ath_softc *sc = arg1;
- u_int32_t cc = sc->sc_countrycode;
- struct ieee80211com *ic = &sc->sc_ic;
- int error;
-
- error = sysctl_handle_int(oidp, &cc, 0, req);
- if (error || !req->newptr)
- return error;
- error = ath_getchannels(sc, sc->sc_regdomain, cc,
- sc->sc_outdoor != 0, sc->sc_xchanmode != 0);
- if (error != 0)
- return error;
- ieee80211_media_init(ic, ath_media_change, ieee80211_media_status);
- /* setcurmode? */
- return 0;
-}
-
-static int
-ath_sysctl_regdomain(SYSCTL_HANDLER_ARGS)
-{
- struct ath_softc *sc = arg1;
- u_int32_t rd = sc->sc_regdomain;
- struct ieee80211com *ic = &sc->sc_ic;
- int error;
-
- error = sysctl_handle_int(oidp, &rd, 0, req);
- if (error || !req->newptr)
- return error;
- if (!ath_hal_setregdomain(sc->sc_ah, rd))
- return EINVAL;
- error = ath_getchannels(sc, rd, sc->sc_countrycode,
- sc->sc_outdoor != 0, sc->sc_xchanmode != 0);
- if (error != 0)
- return error;
- ieee80211_media_init(ic, ath_media_change, ieee80211_media_status);
- /* setcurmode? */
- return 0;
-}
-
-static int
ath_sysctl_tpack(SYSCTL_HANDLER_ARGS)
{
struct ath_softc *sc = arg1;
@@ -6135,12 +6596,6 @@ ath_sysctlattach(struct ath_softc *sc)
struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
struct ath_hal *ah = sc->sc_ah;
- SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
- "countrycode", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
- ath_sysctl_countrycode, "I", "country code");
- SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
- "regdomain", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
- ath_sysctl_regdomain, "I", "EEPROM regdomain code");
#ifdef ATH_DEBUG
sc->sc_debug = ath_debug;
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
@@ -6228,9 +6683,8 @@ ath_bpfattach(struct ath_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof(struct ieee80211_frame) + sizeof(sc->sc_tx_th),
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof(struct ieee80211_frame) + sizeof(sc->sc_tx_th));
/*
* Initialize constant fields.
* XXX make header lengths a multiple of 32-bits so subsequent
@@ -6254,12 +6708,12 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni,
struct ath_buf *bf, struct mbuf *m0,
const struct ieee80211_bpf_params *params)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ath_hal *ah = sc->sc_ah;
int error, ismcast, ismrr;
int hdrlen, pktlen, try0, txantenna;
u_int8_t rix, cix, txrate, ctsrate, rate1, rate2, rate3;
- struct ath_txq *txq;
struct ieee80211_frame *wh;
u_int flags, ctsduration;
HAL_PKT_TYPE atype;
@@ -6340,9 +6794,7 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni,
ieee80211_dump_pkt(ic, mtod(m0, caddr_t), m0->m_len,
sc->sc_hwmap[txrate].ieeerate, -1);
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
u_int64_t tsf = ath_hal_gettsf64(ah);
sc->sc_tx_th.wt_tsf = htole64(tsf);
@@ -6353,8 +6805,7 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni,
sc->sc_tx_th.wt_txpower = ni->ni_txpower;
sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
- bpf_mtap2(sc->sc_drvbpf,
- &sc->sc_tx_th, sc->sc_tx_th_len, m0);
+ bpf_mtap2(ifp->if_bpf, &sc->sc_tx_th, sc->sc_tx_th_len, m0);
}
/*
@@ -6402,16 +6853,8 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni,
);
}
- /*
- * When servicing one or more stations in power-save mode
- * (or) if there is some mcast data waiting on the mcast
- * queue (to prevent out of order delivery) multicast
- * frames must be buffered until after the beacon.
- */
- txq = sc->sc_ac2q[pri];
- if (ismcast && (ic->ic_ps_sta || sc->sc_mcastq.axq_depth))
- txq = &sc->sc_mcastq;
- ath_tx_handoff(sc, txq, bf);
+ /* NB: no buffered multicast in power save support */
+ ath_tx_handoff(sc, sc->sc_ac2q[pri], bf);
return 0;
}
diff --git a/sys/dev/ath/if_ath_pci.c b/sys/dev/ath/if_ath_pci.c
index d775966..ed35447 100644
--- a/sys/dev/ath/if_ath_pci.c
+++ b/sys/dev/ath/if_ath_pci.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
diff --git a/sys/dev/ath/if_athioctl.h b/sys/dev/ath/if_athioctl.h
index ccd7220..1c737a4 100644
--- a/sys/dev/ath/if_athioctl.h
+++ b/sys/dev/ath/if_athioctl.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
diff --git a/sys/dev/ath/if_athrate.h b/sys/dev/ath/if_athrate.h
index a6aaf6f..3ad287a 100644
--- a/sys/dev/ath/if_athrate.h
+++ b/sys/dev/ath/if_athrate.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2008 Sam Leffler, Errno Consulting
* Copyright (c) 2004 Video54 Technologies, Inc.
* All rights reserved.
*
@@ -102,7 +102,7 @@ void ath_rate_newassoc(struct ath_softc *, struct ath_node *,
* Important mostly as the analog to ath_rate_newassoc when operating
* in station mode.
*/
-void ath_rate_newstate(struct ath_softc *, enum ieee80211_state);
+void ath_rate_newstate(struct ieee80211vap *, enum ieee80211_state);
/*
* Transmit handling.
diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h
index af1045d..257c6ee 100644
--- a/sys/dev/ath/if_athvar.h
+++ b/sys/dev/ath/if_athvar.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -49,6 +49,8 @@
#ifndef ATH_TXBUF
#define ATH_TXBUF 200 /* number of TX buffers */
#endif
+#define ATH_BCBUF 4 /* number of beacon buffers */
+
#define ATH_TXDESC 10 /* number of descriptors per buffer */
#define ATH_TXMAXTRY 11 /* max number of transmit attempts */
#define ATH_TXMGTTRY 4 /* xmit attempts for mgt/ctl frames */
@@ -80,7 +82,9 @@ struct ath_buf;
/* driver-specific node state */
struct ath_node {
struct ieee80211_node an_node; /* base class */
- u_int32_t an_avgrssi; /* average rssi over all rx frames */
+ const struct ieee80211_txparam *an_tp;
+ u_int8_t an_mgmtrix; /* min h/w rate index */
+ u_int8_t an_mcastrix; /* mcast h/w rate index */
struct ath_buf *an_ff_buf[WME_NUM_AC]; /* ff staging area */
/* variable-length rate control state follows */
};
@@ -141,6 +145,7 @@ struct ath_descdma {
*/
struct ath_txq {
u_int axq_qnum; /* hardware q number */
+#define ATH_TXQ_SWQ (HAL_NUM_TX_QUEUES+1) /* qnum for s/w only queue */
u_int axq_depth; /* queue depth (stat only) */
u_int axq_intrcnt; /* interrupt count */
u_int32_t *axq_link; /* link ptr in last TX desc */
@@ -175,6 +180,25 @@ struct ath_txq {
STAILQ_REMOVE_HEAD(&(_tq)->axq_q, _field); \
(_tq)->axq_depth--; \
} while (0)
+/* NB: this does not do the "head empty check" that STAILQ_LAST does */
+#define ATH_TXQ_LAST(_tq) \
+ ((struct ath_buf *)(void *) \
+ ((char *)((_tq)->axq_q.stqh_last) - __offsetof(struct ath_buf, bf_list)))
+
+struct ath_vap {
+ struct ieee80211vap av_vap; /* base class */
+ int av_bslot; /* beacon slot index */
+ struct ath_buf *av_bcbuf; /* beacon buffer */
+ struct ieee80211_beacon_offsets av_boff;/* dynamic update state */
+ struct ath_txq av_mcastq; /* buffered mcast s/w queue */
+
+ void (*av_recv_mgmt)(struct ieee80211_node *,
+ struct mbuf *, int, int, int, u_int32_t);
+ int (*av_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ void (*av_bmiss)(struct ieee80211vap *);
+};
+#define ATH_VAP(vap) ((struct ath_vap *)(vap))
struct taskqueue;
struct ath_tx99;
@@ -182,16 +206,13 @@ struct ath_tx99;
struct ath_softc {
struct ifnet *sc_ifp; /* interface common */
struct ath_stats sc_stats; /* interface statistics */
- struct ieee80211com sc_ic; /* IEEE 802.11 common */
int sc_debug;
- u_int32_t sc_countrycode;
- u_int32_t sc_regdomain;
- void (*sc_recv_mgmt)(struct ieee80211com *,
- struct mbuf *,
- struct ieee80211_node *,
- int, int, int, u_int32_t);
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
+ int sc_nvaps; /* # vaps */
+ int sc_nstavaps; /* # station vaps */
+ u_int8_t sc_hwbssidmask[IEEE80211_ADDR_LEN];
+ u_int8_t sc_nbssid0; /* # vap's using base mac */
+ uint32_t sc_bssidmask; /* bssid mask */
+
void (*sc_node_free)(struct ieee80211_node *);
device_t sc_dev;
HAL_BUS_TAG sc_st; /* bus space tag */
@@ -203,22 +224,28 @@ struct ath_softc {
struct ath_ratectrl *sc_rc; /* tx rate control support */
struct ath_tx99 *sc_tx99; /* tx99 adjunct state */
void (*sc_setdefantenna)(struct ath_softc *, u_int);
- unsigned int sc_invalid : 1, /* disable hardware accesses */
- sc_mrretry : 1, /* multi-rate retry support */
- sc_softled : 1, /* enable LED gpio status */
- sc_splitmic: 1, /* split TKIP MIC keys */
- sc_needmib : 1, /* enable MIB stats intr */
- sc_diversity : 1,/* enable rx diversity */
- sc_hasveol : 1, /* tx VEOL support */
- sc_ledstate: 1, /* LED on/off state */
- sc_blinking: 1, /* LED blink operation active */
- sc_mcastkey: 1, /* mcast key cache search */
- sc_scanning: 1, /* scanning active */
+ unsigned int sc_invalid : 1,/* disable hardware accesses */
+ sc_mrretry : 1,/* multi-rate retry support */
+ sc_softled : 1,/* enable LED gpio status */
+ sc_splitmic : 1,/* split TKIP MIC keys */
+ sc_needmib : 1,/* enable MIB stats intr */
+ sc_diversity: 1,/* enable rx diversity */
+ sc_hasveol : 1,/* tx VEOL support */
+ sc_ledstate : 1,/* LED on/off state */
+ sc_blinking : 1,/* LED blink operation active */
+ sc_mcastkey : 1,/* mcast key cache search */
+ sc_scanning : 1,/* scanning active */
sc_syncbeacon:1,/* sync/resync beacon timers */
- sc_hasclrkey:1, /* CLR key supported */
+ sc_hasclrkey: 1,/* CLR key supported */
sc_xchanmode: 1,/* extended channel mode */
sc_outdoor : 1,/* outdoor operation */
- sc_dturbo : 1; /* dynamic turbo in use */
+ sc_dturbo : 1,/* dynamic turbo in use */
+ sc_hasbmask : 1,/* bssid mask support */
+ sc_hastsfadd: 1,/* tsf adjust support */
+ sc_beacons : 1,/* beacons running */
+ sc_swbmiss : 1,/* sta mode using sw bmiss */
+ sc_stagbeacons:1,/* use staggered beacons */
+ sc_wmetkipmic:1;/* can do WME+TKIP MIC */
/* rate tables */
#define IEEE80211_MODE_HALF (IEEE80211_MODE_MAX+0)
#define IEEE80211_MODE_QUARTER (IEEE80211_MODE_MAX+1)
@@ -238,8 +265,6 @@ struct ath_softc {
u_int16_t ledon; /* softled on time */
u_int16_t ledoff; /* softled off time */
} sc_hwmap[32]; /* h/w rate ix mappings */
- u_int8_t sc_minrateix; /* min h/w rate index */
- u_int8_t sc_mcastrix; /* mcast h/w rate index */
u_int8_t sc_protrix; /* protection rate index */
u_int8_t sc_lastdatarix; /* last data frame rate index */
u_int sc_mcastrate; /* ieee rate for mcastrateix */
@@ -262,20 +287,13 @@ struct ath_softc {
u_int sc_rfsilentpin; /* GPIO pin for rfkill int */
u_int sc_rfsilentpol; /* pin setting for rfkill on */
- struct bpf_if *sc_drvbpf;
- union {
- struct ath_tx_radiotap_header th;
- u_int8_t pad[64];
- } u_tx_rt;
+ struct ath_tx_radiotap_header sc_tx_th;
int sc_tx_th_len;
- union {
- struct ath_rx_radiotap_header th;
- u_int8_t pad[64];
- } u_rx_rt;
+ struct ath_rx_radiotap_header sc_rx_th;
int sc_rx_th_len;
u_int sc_monpass; /* frames to pass in mon.mode */
- struct ath_descdma sc_rxdma; /* RX descriptos */
+ struct ath_descdma sc_rxdma; /* RX descriptors */
ath_bufhead sc_rxbuf; /* receive buffer */
struct mbuf *sc_rxpending; /* pending receive data */
u_int32_t *sc_rxlink; /* link ptr in last RX desc */
@@ -301,7 +319,6 @@ struct ath_softc {
u_int sc_bmisscount; /* missed beacon transmits */
u_int32_t sc_ant_tx[8]; /* recent tx frames/antenna */
struct ath_txq *sc_cabq; /* tx q for cab frames */
- struct ieee80211_beacon_offsets sc_boff;/* dynamic update state */
struct task sc_bmisstask; /* bmiss int processing */
struct task sc_bstucktask; /* stuck beacon processing */
enum {
@@ -309,16 +326,15 @@ struct ath_softc {
UPDATE, /* update pending */
COMMIT /* beacon sent, commit change */
} sc_updateslot; /* slot time update fsm */
- struct ath_txq sc_mcastq; /* mcast xmits w/ ps sta's */
+ int sc_slotupdate; /* slot to advance fsm */
+ struct ieee80211vap *sc_bslot[ATH_BCBUF];
+ int sc_nbcnvaps; /* # vaps with beacons */
struct callout sc_cal_ch; /* callout handle for cals */
int sc_calinterval; /* current polling interval */
int sc_caltries; /* cals at current interval */
HAL_NODE_STATS sc_halstats; /* station-mode rssi stats */
- struct callout sc_dfs_ch; /* callout handle for dfs */
};
-#define sc_tx_th u_tx_rt.th
-#define sc_rx_th u_rx_rt.th
#define ATH_LOCK_INIT(_sc) \
mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
@@ -361,6 +377,10 @@ void ath_intr(void *);
((*(_ah)->ah_getMacAddress)((_ah), (_mac)))
#define ath_hal_setmac(_ah, _mac) \
((*(_ah)->ah_setMacAddress)((_ah), (_mac)))
+#define ath_hal_getbssidmask(_ah, _mask) \
+ ((*(_ah)->ah_getBssIdMask)((_ah), (_mask)))
+#define ath_hal_setbssidmask(_ah, _mask) \
+ ((*(_ah)->ah_setBssIdMask)((_ah), (_mask)))
#define ath_hal_intrset(_ah, _mask) \
((*(_ah)->ah_setInterrupts)((_ah), (_mask)))
#define ath_hal_intrget(_ah) \
@@ -483,15 +503,21 @@ void ath_intr(void *);
#define ath_hal_getregdomain(_ah, _prd) \
(ath_hal_getcapability(_ah, HAL_CAP_REG_DMN, 0, (_prd)) == HAL_OK)
#define ath_hal_setregdomain(_ah, _rd) \
- ((*(_ah)->ah_setRegulatoryDomain)((_ah), (_rd), NULL))
+ (*(uint16_t *)(((uint8_t *)(_ah)) + 520) = (_rd))
#define ath_hal_getcountrycode(_ah, _pcc) \
(*(_pcc) = (_ah)->ah_countryCode)
+#define ath_hal_gettkipmic(_ah) \
+ (ath_hal_getcapability(_ah, HAL_CAP_TKIP_MIC, 1, NULL) == HAL_OK)
+#define ath_hal_settkipmic(_ah, _v) \
+ ath_hal_setcapability(_ah, HAL_CAP_TKIP_MIC, 1, _v, NULL)
#define ath_hal_hastkipsplit(_ah) \
(ath_hal_getcapability(_ah, HAL_CAP_TKIP_SPLIT, 0, NULL) == HAL_OK)
#define ath_hal_gettkipsplit(_ah) \
(ath_hal_getcapability(_ah, HAL_CAP_TKIP_SPLIT, 1, NULL) == HAL_OK)
#define ath_hal_settkipsplit(_ah, _v) \
ath_hal_setcapability(_ah, HAL_CAP_TKIP_SPLIT, 1, _v, NULL)
+#define ath_hal_haswmetkipmic(_ah) \
+ (ath_hal_getcapability(_ah, HAL_CAP_WME_TKIPMIC, 0, NULL) == HAL_OK)
#define ath_hal_hwphycounters(_ah) \
(ath_hal_getcapability(_ah, HAL_CAP_PHYCOUNTERS, 0, NULL) == HAL_OK)
#define ath_hal_hasdiversity(_ah) \
@@ -542,6 +568,14 @@ void ath_intr(void *);
#endif
#define ath_hal_hasfastframes(_ah) \
(ath_hal_getcapability(_ah, HAL_CAP_FASTFRAME, 0, NULL) == HAL_OK)
+#define ath_hal_hasbssidmask(_ah) \
+ (ath_hal_getcapability(_ah, HAL_CAP_BSSIDMASK, 0, NULL) == HAL_OK)
+#define ath_hal_hastsfadjust(_ah) \
+ (ath_hal_getcapability(_ah, HAL_CAP_TSF_ADJUST, 0, NULL) == HAL_OK)
+#define ath_hal_gettsfadjust(_ah) \
+ (ath_hal_getcapability(_ah, HAL_CAP_TSF_ADJUST, 1, NULL) == HAL_OK)
+#define ath_hal_settsfadjust(_ah, _onoff) \
+ ath_hal_setcapability(_ah, HAL_CAP_TSF_ADJUST, 1, _onoff, NULL)
#define ath_hal_hasrfsilent(_ah) \
(ath_hal_getcapability(_ah, HAL_CAP_RFSILENT, 0, NULL) == HAL_OK)
#define ath_hal_getrfkill(_ah) \
diff --git a/sys/dev/if_ndis/if_ndis.c b/sys/dev/if_ndis/if_ndis.c
index c44ed53..896ddb3 100644
--- a/sys/dev/if_ndis/if_ndis.c
+++ b/sys/dev/if_ndis/if_ndis.c
@@ -133,16 +133,21 @@ static funcptr ndis_starttask_wrap;
static funcptr ndis_resettask_wrap;
static funcptr ndis_inputtask_wrap;
+static struct ieee80211vap *ndis_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void ndis_vap_delete (struct ieee80211vap *);
static void ndis_tick (void *);
static void ndis_ticktask (device_object *, void *);
+static int ndis_raw_xmit (struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
static void ndis_start (struct ifnet *);
static void ndis_starttask (device_object *, void *);
static void ndis_resettask (device_object *, void *);
static void ndis_inputtask (device_object *, void *);
static int ndis_ioctl (struct ifnet *, u_long, caddr_t);
-static int ndis_80211_ioctl_get (struct ifnet *, u_long, caddr_t);
-static int ndis_80211_ioctl_set (struct ifnet *, u_long, caddr_t);
-static int ndis_newstate (struct ieee80211com *, enum ieee80211_state,
+static int ndis_newstate (struct ieee80211vap *, enum ieee80211_state,
int);
static int ndis_nettype_chan (uint32_t);
static int ndis_nettype_mode (uint32_t);
@@ -151,8 +156,8 @@ static void ndis_scan_results (struct ndis_softc *);
static void ndis_scan_start (struct ieee80211com *);
static void ndis_scan_end (struct ieee80211com *);
static void ndis_set_channel (struct ieee80211com *);
-static void ndis_scan_curchan (struct ieee80211com *, unsigned long);
-static void ndis_scan_mindwell (struct ieee80211com *);
+static void ndis_scan_curchan (struct ieee80211_scan_state *, unsigned long);
+static void ndis_scan_mindwell (struct ieee80211_scan_state *);
static void ndis_init (void *);
static void ndis_stop (struct ndis_softc *);
static void ndis_watchdog (struct ifnet *);
@@ -164,12 +169,11 @@ static int ndis_set_offload (struct ndis_softc *);
static void ndis_getstate_80211 (struct ndis_softc *);
static void ndis_setstate_80211 (struct ndis_softc *);
static int ndis_set_cipher (struct ndis_softc *, int);
-static int ndis_set_wpa (struct ndis_softc *);
-static int ndis_add_key (struct ieee80211com *,
+static int ndis_set_wpa (struct ndis_softc *, void *, int);
+static int ndis_add_key (struct ieee80211vap *,
const struct ieee80211_key *, const u_int8_t []);
-static int ndis_del_key (struct ieee80211com *,
+static int ndis_del_key (struct ieee80211vap *,
const struct ieee80211_key *);
-static void ndis_media_status (struct ifnet *, struct ifmediareq *);
static void ndis_setmulti (struct ndis_softc *);
static void ndis_map_sclist (void *, bus_dma_segment_t *,
@@ -520,16 +524,11 @@ ndis_attach(dev)
driver_object *pdrv;
device_object *pdo;
struct ifnet *ifp = NULL;
- int error = 0, len, mode, bands = 0;
+ int error = 0, len, mode;
+ uint8_t bands = 0;
int i;
sc = device_get_softc(dev);
- ifp = sc->ifp = if_alloc(IFT_ETHER);
- if (ifp == NULL) {
- error = ENOSPC;
- goto fail;
- }
- ifp->if_softc = sc;
KeInitializeSpinLock(&sc->ndis_spinlock);
KeInitializeSpinLock(&sc->ndis_rxlock);
@@ -598,10 +597,6 @@ ndis_attach(dev)
sc->ndis_inputitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj);
KeInitializeDpc(&sc->ndis_rxdpc, ndis_rxeof_xfr_wrap, sc->ndis_block);
- /* make sure drv flags are all cleared before initing the NIC. */
-
- ifp->if_drv_flags = 0;
-
/* Call driver's init routine. */
if (ndis_init_nic(sc)) {
device_printf (dev, "init handler failed\n");
@@ -677,6 +672,17 @@ ndis_attach(dev)
}
}
+ if (sc->ndis_80211)
+ ifp = if_alloc(IFT_IEEE80211);
+ else
+ ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ error = ENOSPC;
+ goto fail;
+ }
+ sc->ifp = ifp;
+ ifp->if_softc = sc;
+
/* Check for task offload support. */
ndis_probe_offload(sc);
@@ -696,7 +702,7 @@ ndis_attach(dev)
/* Do media setup */
if (sc->ndis_80211) {
- struct ieee80211com *ic = (void *)&sc->ic;
+ struct ieee80211com *ic = ifp->if_l2com;
ndis_80211_rates_ex rates;
struct ndis_80211_nettype_list *ntl;
uint32_t arg;
@@ -709,8 +715,8 @@ ndis_attach(dev)
TASK_INIT(&sc->ndis_scantask, 0, ndis_scan, sc);
ic->ic_ifp = ifp;
- ic->ic_phytype = IEEE80211_T_DS;
ic->ic_opmode = IEEE80211_M_STA;
+ ic->ic_phytype = IEEE80211_T_DS;
ic->ic_caps = IEEE80211_C_IBSS;
setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO);
len = 0;
@@ -840,7 +846,7 @@ nonettypes:
}
#undef SETRATE
#undef INCRATE
- ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
+ ieee80211_init_channels(ic, NULL, &bands);
/*
* To test for WPA support, we need to see if we can
@@ -869,20 +875,22 @@ nonettypes:
arg = NDIS_80211_WEPSTAT_ENC3ENABLED;
r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i);
if (r == 0) {
- ic->ic_caps |= IEEE80211_C_WEP|IEEE80211_C_TKIP|
- IEEE80211_C_AES_CCM;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP
+ | IEEE80211_CRYPTO_TKIP
+ | IEEE80211_CRYPTO_AES_CCM;
goto got_crypto;
}
arg = NDIS_80211_WEPSTAT_ENC2ENABLED;
r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i);
if (r == 0) {
- ic->ic_caps |= IEEE80211_C_WEP|IEEE80211_C_TKIP;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP
+ | IEEE80211_CRYPTO_TKIP;
goto got_crypto;
}
arg = NDIS_80211_WEPSTAT_ENC1ENABLED;
r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i);
if (r == 0)
- ic->ic_caps |= IEEE80211_C_WEP;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP;
got_crypto:
i = sizeof(arg);
r = ndis_get_info(sc, OID_802_11_POWER_MODE, &arg, &i);
@@ -890,21 +898,17 @@ got_crypto:
ic->ic_caps |= IEEE80211_C_PMGT;
bcopy(eaddr, &ic->ic_myaddr, sizeof(eaddr));
ieee80211_ifattach(ic);
- ieee80211_media_init(ic, ieee80211_media_change,
- ndis_media_status);
+ ic->ic_raw_xmit = ndis_raw_xmit;
ic->ic_scan_start = ndis_scan_start;
ic->ic_scan_end = ndis_scan_end;
ic->ic_set_channel = ndis_set_channel;
ic->ic_scan_curchan = ndis_scan_curchan;
ic->ic_scan_mindwell = ndis_scan_mindwell;
ic->ic_bsschan = IEEE80211_CHAN_ANYC;
- ic->ic_bss->ni_chan = ic->ic_bsschan;
- /* override state transition machine */
- sc->ndis_newstate = ic->ic_newstate;
- ic->ic_newstate = ndis_newstate;
- /* install key handing routines */
- ic->ic_crypto.cs_key_set = ndis_add_key;
- ic->ic_crypto.cs_key_delete = ndis_del_key;
+ //ic->ic_bss->ni_chan = ic->ic_bsschan;
+ ic->ic_vap_create = ndis_vap_create;
+ ic->ic_vap_delete = ndis_vap_delete;
+
} else {
ifmedia_init(&sc->ifmedia, IFM_IMASK, ndis_ifmedia_upd,
ndis_ifmedia_sts);
@@ -928,6 +932,45 @@ fail:
return(error);
}
+static struct ieee80211vap *
+ndis_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ndis_vap *nvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ nvp = (struct ndis_vap *) malloc(sizeof(struct ndis_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (nvp == NULL)
+ return NULL;
+ vap = &nvp->vap;
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
+ /* override with driver methods */
+ nvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = ndis_newstate;
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ /* install key handing routines */
+ vap->iv_key_set = ndis_add_key;
+ vap->iv_key_delete = ndis_del_key;
+ return vap;
+}
+
+static void
+ndis_vap_delete(struct ieee80211vap *vap)
+{
+ struct ndis_vap *nvp = NDIS_VAP(vap);
+
+ ieee80211_vap_detach(vap);
+ free(nvp, M_80211_VAP);
+}
+
/*
* Shutdown hardware and free up resources. This can be called any
* time after the mutex has been initialized. It is called in both
@@ -952,7 +995,7 @@ ndis_detach(dev)
NDIS_UNLOCK(sc);
ndis_stop(sc);
if (sc->ndis_80211)
- ieee80211_ifdetach(&sc->ic);
+ ieee80211_ifdetach(ifp->if_l2com);
else
ether_ifdetach(ifp);
} else
@@ -1422,10 +1465,14 @@ ndis_inputtask(dobj, arg)
struct ifnet *ifp;
struct ndis_softc *sc;
struct mbuf *m;
+ struct ieee80211com *ic;
+ struct ieee80211vap *vap;
uint8_t irql;
ifp = arg;
sc = ifp->if_softc;
+ ic = ifp->if_l2com;
+ vap = TAILQ_FIRST(&ic->ic_vaps);
block = dobj->do_devext;
KeAcquireSpinLock(&sc->ndis_rxlock, &irql);
@@ -1434,8 +1481,10 @@ ndis_inputtask(dobj, arg)
if (m == NULL)
break;
KeReleaseSpinLock(&sc->ndis_rxlock, irql);
- ifp->if_ipackets++;
- (*ifp->if_input)(ifp, m);
+ if (sc->ndis_80211)
+ vap->iv_deliver_data(vap, vap->iv_bss, m);
+ else
+ (*ifp->if_input)(ifp, m);
KeAcquireSpinLock(&sc->ndis_rxlock, &irql);
}
KeReleaseSpinLock(&sc->ndis_rxlock, irql);
@@ -1600,11 +1649,13 @@ ndis_ticktask(d, xsc)
{
struct ndis_softc *sc;
struct ieee80211com *ic;
+ struct ieee80211vap *vap;
ndis_checkforhang_handler hangfunc;
uint8_t rval;
sc = xsc;
- ic = &sc->ic;
+ ic = sc->ifp->if_l2com;
+ vap = TAILQ_FIRST(&ic->ic_vaps);
NDIS_LOCK(sc);
if (!NDIS_INITIALIZED(sc)) {
@@ -1631,7 +1682,7 @@ ndis_ticktask(d, xsc)
NDIS_UNLOCK(sc);
if (sc->ndis_80211) {
ndis_getstate_80211(sc);
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ ieee80211_new_state(vap, IEEE80211_S_RUN, -1);
}
NDIS_LOCK(sc);
if_link_state_change(sc->ifp, LINK_STATE_UP);
@@ -1641,7 +1692,7 @@ ndis_ticktask(d, xsc)
sc->ndis_sts == NDIS_STATUS_MEDIA_DISCONNECT) {
sc->ndis_link = 0;
if (sc->ndis_80211)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
if_link_state_change(sc->ifp, LINK_STATE_DOWN);
}
@@ -1677,6 +1728,16 @@ ndis_map_sclist(arg, segs, nseg, mapsize, error)
return;
}
+static int
+ndis_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ /* no support; just discard */
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return 0;
+}
+
static void
ndis_starttask(d, arg)
device_object *d;
@@ -1791,7 +1852,8 @@ ndis_start(ifp)
* to him.
*/
- BPF_MTAP(ifp, m);
+ if (!sc->ndis_80211) /* XXX handle 80211 */
+ BPF_MTAP(ifp, m);
/*
* The array that p0 points to must appear contiguous,
@@ -1840,8 +1902,8 @@ ndis_init(xsc)
void *xsc;
{
struct ndis_softc *sc = xsc;
- struct ieee80211com *ic = (void *)&sc->ic;
struct ifnet *ifp = sc->ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
int i, len, error;
/*
@@ -1850,7 +1912,7 @@ ndis_init(xsc)
* fixing the upper layer modules so they don't
* call ifp->if_init() quite as often.
*/
- if (sc->ndis_link && sc->ndis_skip)
+ if (sc->ndis_link)
return;
/*
@@ -1908,23 +1970,14 @@ ndis_init(xsc)
if_link_state_change(sc->ifp, LINK_STATE_UNKNOWN);
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- /*
- * NB: When restarting the adapter clock the state
- * machine regardless of the roaming mode; otherwise
- * we need to notify user apps so they can manually
- * get us going again.
- */
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-
ifp->if_drv_flags |= IFF_DRV_RUNNING;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
NDIS_UNLOCK(sc);
+ /* XXX force handling */
+ ieee80211_start_all(ic); /* start all vap's */
+
/*
* Some drivers don't set this value. The NDIS spec says
* the default checkforhang timeout is "approximately 2
@@ -2017,24 +2070,24 @@ ndis_set_cipher(sc, cipher)
int rval = 0, len;
uint32_t arg, save;
- ic = &sc->ic;
+ ic = sc->ifp->if_l2com;
len = sizeof(arg);
if (cipher == WPA_CSE_WEP40 || WPA_CSE_WEP104) {
- if (!(ic->ic_caps & IEEE80211_C_WEP))
+ if (!(ic->ic_cryptocaps & IEEE80211_CRYPTO_WEP))
return(ENOTSUP);
arg = NDIS_80211_WEPSTAT_ENC1ENABLED;
}
if (cipher == WPA_CSE_TKIP) {
- if (!(ic->ic_caps & IEEE80211_C_TKIP))
+ if (!(ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP))
return(ENOTSUP);
arg = NDIS_80211_WEPSTAT_ENC2ENABLED;
}
if (cipher == WPA_CSE_CCMP) {
- if (!(ic->ic_caps & IEEE80211_C_AES_CCM))
+ if (!(ic->ic_cryptocaps & IEEE80211_CRYPTO_AES_CCM))
return(ENOTSUP);
arg = NDIS_80211_WEPSTAT_ENC3ENABLED;
}
@@ -2067,18 +2120,17 @@ ndis_set_cipher(sc, cipher)
*/
static int
-ndis_set_wpa(sc)
+ndis_set_wpa(sc, ie, ielen)
struct ndis_softc *sc;
+ void *ie;
+ int ielen;
{
- struct ieee80211com *ic;
struct ieee80211_ie_wpa *w;
struct ndis_ie *n;
char *pos;
uint32_t arg;
int i;
- ic = &sc->ic;
-
/*
* Apparently, the only way for us to know what ciphers
* and key management/authentication mode to use is for
@@ -2087,7 +2139,7 @@ ndis_set_wpa(sc)
* supplied by the WPA supplicant.
*/
- w = (struct ieee80211_ie_wpa *)ic->ic_opt_ie;
+ w = (struct ieee80211_ie_wpa *)ie;
/* Check for the right kind of IE. */
if (w->wpa_id != IEEE80211_ELEMID_VENDOR) {
@@ -2150,18 +2202,20 @@ ndis_setstate_80211(sc)
struct ndis_softc *sc;
{
struct ieee80211com *ic;
+ struct ieee80211vap *vap;
struct ieee80211_node *ni;
ndis_80211_ssid ssid;
ndis_80211_macaddr bssid;
ndis_80211_config config;
ndis_80211_wep wep;
- int i, rval = 0, len;
+ int i, rval = 0, len, error;
uint32_t arg;
struct ifnet *ifp;
- ic = &sc->ic;
ifp = sc->ifp;
- ni = ic->ic_bss;
+ ic = ifp->if_l2com;
+ vap = TAILQ_FIRST(&ic->ic_vaps);
+ ni = vap->iv_bss;
if (!NDIS_INITIALIZED(sc)) {
DPRINTF(("%s: NDIS not initialized\n", __func__));
@@ -2177,7 +2231,7 @@ ndis_setstate_80211(sc)
/* Set network infrastructure mode. */
len = sizeof(arg);
- if (ic->ic_opmode == IEEE80211_M_IBSS)
+ if (vap->iv_opmode == IEEE80211_M_IBSS)
arg = NDIS_80211_NET_INFRA_IBSS;
else
arg = NDIS_80211_NET_INFRA_BSS;
@@ -2190,13 +2244,13 @@ ndis_setstate_80211(sc)
/* Set RTS threshold */
len = sizeof(arg);
- arg = ic->ic_rtsthreshold;
+ arg = vap->iv_rtsthreshold;
ndis_set_info(sc, OID_802_11_RTS_THRESHOLD, &arg, &len);
/* Set fragmentation threshold */
len = sizeof(arg);
- arg = ic->ic_fragthreshold;
+ arg = vap->iv_fragthreshold;
ndis_set_info(sc, OID_802_11_FRAGMENTATION_THRESHOLD, &arg, &len);
/* Set power management */
@@ -2231,11 +2285,11 @@ ndis_setstate_80211(sc)
/* Set WEP */
- if (ic->ic_flags & IEEE80211_F_PRIVACY &&
- !(ic->ic_flags & IEEE80211_F_WPA)) {
+ if (vap->iv_flags & IEEE80211_F_PRIVACY &&
+ !(vap->iv_flags & IEEE80211_F_WPA)) {
int keys_set = 0;
- if (ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED) {
+ if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
len = sizeof(arg);
arg = NDIS_80211_AUTHMODE_SHARED;
DPRINTF(("Setting shared auth\n"));
@@ -2243,12 +2297,12 @@ ndis_setstate_80211(sc)
&arg, &len);
}
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- if (ic->ic_nw_keys[i].wk_keylen) {
- if (ic->ic_nw_keys[i].wk_cipher->ic_cipher !=
+ if (vap->iv_nw_keys[i].wk_keylen) {
+ if (vap->iv_nw_keys[i].wk_cipher->ic_cipher !=
IEEE80211_CIPHER_WEP)
continue;
bzero((char *)&wep, sizeof(wep));
- wep.nw_keylen = ic->ic_nw_keys[i].wk_keylen;
+ wep.nw_keylen = vap->iv_nw_keys[i].wk_keylen;
/*
* 5, 13 and 16 are the only valid
@@ -2256,21 +2310,21 @@ ndis_setstate_80211(sc)
* in between will be zero padded out to
* the next highest boundary.
*/
- if (ic->ic_nw_keys[i].wk_keylen < 5)
+ if (vap->iv_nw_keys[i].wk_keylen < 5)
wep.nw_keylen = 5;
- else if (ic->ic_nw_keys[i].wk_keylen > 5 &&
- ic->ic_nw_keys[i].wk_keylen < 13)
+ else if (vap->iv_nw_keys[i].wk_keylen > 5 &&
+ vap->iv_nw_keys[i].wk_keylen < 13)
wep.nw_keylen = 13;
- else if (ic->ic_nw_keys[i].wk_keylen > 13 &&
- ic->ic_nw_keys[i].wk_keylen < 16)
+ else if (vap->iv_nw_keys[i].wk_keylen > 13 &&
+ vap->iv_nw_keys[i].wk_keylen < 16)
wep.nw_keylen = 16;
wep.nw_keyidx = i;
wep.nw_length = (sizeof(uint32_t) * 3)
+ wep.nw_keylen;
- if (i == ic->ic_def_txkey)
+ if (i == vap->iv_def_txkey)
wep.nw_keyidx |= NDIS_80211_WEPKEY_TX;
- bcopy(ic->ic_nw_keys[i].wk_key,
+ bcopy(vap->iv_nw_keys[i].wk_key,
wep.nw_keydata, wep.nw_length);
len = sizeof(wep);
DPRINTF(("Setting WEP key %d\n", i));
@@ -2291,7 +2345,7 @@ ndis_setstate_80211(sc)
if (rval)
device_printf(sc->ndis_dev,
"enable WEP failed: %d\n", rval);
- if (ic->ic_flags & IEEE80211_F_DROPUNENC)
+ if (vap->iv_flags & IEEE80211_F_DROPUNENC)
arg = NDIS_80211_PRIVFILT_8021XWEP;
else
arg = NDIS_80211_PRIVFILT_ACCEPTALL;
@@ -2300,20 +2354,23 @@ ndis_setstate_80211(sc)
ndis_set_info(sc,
OID_802_11_PRIVACY_FILTER, &arg, &len);
}
- }
+ }
/* Set up WPA. */
- if (ic->ic_flags & IEEE80211_F_WPA1 && ic->ic_opt_ie_len &&
- ic->ic_caps & IEEE80211_C_WPA)
- if (ndis_set_wpa(sc))
+ if ((vap->iv_flags & IEEE80211_F_WPA) &&
+ vap->iv_appie_assocreq != NULL) {
+ struct ieee80211_appie *ie = vap->iv_appie_assocreq;
+ error = ndis_set_wpa(sc, ie->ie_data, ie->ie_len);
+ if (error != 0)
device_printf(sc->ndis_dev, "WPA setup failed\n");
+ }
#ifdef notyet
/* Set network type. */
arg = 0;
- switch (ic->ic_curmode) {
+ switch (vap->iv_curmode) {
case IEEE80211_MODE_11A:
arg = NDIS_80211_NETTYPE_11OFDM5;
break;
@@ -2325,7 +2382,7 @@ ndis_setstate_80211(sc)
break;
default:
device_printf(sc->ndis_dev, "unknown mode: %d\n",
- ic->ic_curmode);
+ vap->iv_curmode);
}
if (arg) {
@@ -2365,7 +2422,7 @@ ndis_setstate_80211(sc)
if (chan != ieee80211_mhz2ieee(config.nc_dsconfig / 1000, 0)) {
config.nc_dsconfig =
ic->ic_bsschan->ic_freq * 1000;
- ic->ic_bss->ni_chan = ic->ic_bsschan;
+ ni->ni_chan = ic->ic_bsschan;
len = sizeof(config);
config.nc_length = len;
config.nc_fhconfig.ncf_length =
@@ -2397,8 +2454,8 @@ ndis_setstate_80211(sc)
*/
len = IEEE80211_ADDR_LEN;
- if (ic->ic_flags & IEEE80211_F_DESBSSID &&
- ic->ic_opmode != IEEE80211_M_IBSS)
+ if (vap->iv_flags & IEEE80211_F_DESBSSID &&
+ vap->iv_opmode != IEEE80211_M_IBSS)
bcopy(ni->ni_bssid, bssid, len);
else
bcopy(ifp->if_broadcastaddr, bssid, len);
@@ -2432,67 +2489,12 @@ ndis_setstate_80211(sc)
if (rval)
device_printf (sc->ndis_dev, "set ssid failed: %d\n", rval);
- if (ic->ic_state == IEEE80211_S_AUTH)
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
+ if (vap->iv_state == IEEE80211_S_AUTH)
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
return;
}
-static void
-ndis_media_status(struct ifnet *ifp, struct ifmediareq *imr)
-{
- struct ieee80211com *ic = &((struct ndis_softc *)ifp->if_softc)->ic;
- struct ieee80211_node *ni = NULL;
-
- imr->ifm_status = IFM_AVALID;
- imr->ifm_active = IFM_IEEE80211;
- if (ic->ic_state == IEEE80211_S_RUN)
- imr->ifm_status |= IFM_ACTIVE;
- imr->ifm_active |= IFM_AUTO;
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- ni = ic->ic_bss;
- /* calculate rate subtype */
- imr->ifm_active |= ieee80211_rate2media(ic,
- ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode);
- break;
- case IEEE80211_M_IBSS:
- ni = ic->ic_bss;
- /* calculate rate subtype */
- imr->ifm_active |= ieee80211_rate2media(ic,
- ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode);
- imr->ifm_active |= IFM_IEEE80211_ADHOC;
- break;
- case IEEE80211_M_AHDEMO:
- /* should not come here */
- break;
- case IEEE80211_M_HOSTAP:
- imr->ifm_active |= IFM_IEEE80211_HOSTAP;
- break;
- case IEEE80211_M_MONITOR:
- imr->ifm_active |= IFM_IEEE80211_MONITOR;
- break;
- case IEEE80211_M_WDS:
- printf("WARNING: WDS operation mode not supported by NDIS\n");
- break;
- }
- switch (ic->ic_curmode) {
- case IEEE80211_MODE_11A:
- imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A);
- break;
- case IEEE80211_MODE_11B:
- imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11B);
- break;
- case IEEE80211_MODE_11G:
- imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11G);
- break;
- case IEEE80211_MODE_TURBO_A:
- imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A)
- | IFM_IEEE80211_TURBO;
- break;
- }
-}
-
static int
ndis_get_assoc(sc, assoc)
struct ndis_softc *sc;
@@ -2557,14 +2559,18 @@ ndis_getstate_80211(sc)
struct ndis_softc *sc;
{
struct ieee80211com *ic;
+ struct ieee80211vap *vap;
+ struct ieee80211_node *ni;
ndis_wlan_bssid_ex *bs;
int rval, len, i = 0;
int chanflag;
uint32_t arg;
struct ifnet *ifp;
- ic = &sc->ic;
ifp = sc->ifp;
+ ic = ifp->if_l2com;
+ vap = TAILQ_FIRST(&ic->ic_vaps);
+ ni = vap->iv_bss;
if (!NDIS_INITIALIZED(sc))
return;
@@ -2575,12 +2581,12 @@ ndis_getstate_80211(sc)
/* We're associated, retrieve info on the current bssid. */
ic->ic_curmode = ndis_nettype_mode(bs->nwbx_nettype);
chanflag = ndis_nettype_chan(bs->nwbx_nettype);
- IEEE80211_ADDR_COPY(ic->ic_bss->ni_bssid, bs->nwbx_macaddr);
+ IEEE80211_ADDR_COPY(ni->ni_bssid, bs->nwbx_macaddr);
/* Get SSID from current association info. */
- bcopy(bs->nwbx_ssid.ns_ssid, ic->ic_bss->ni_essid,
+ bcopy(bs->nwbx_ssid.ns_ssid, ni->ni_essid,
bs->nwbx_ssid.ns_ssidlen);
- ic->ic_bss->ni_esslen = bs->nwbx_ssid.ns_ssidlen;
+ ni->ni_esslen = bs->nwbx_ssid.ns_ssidlen;
len = sizeof(arg);
rval = ndis_get_info(sc, OID_GEN_LINK_SPEED, &arg, &len);
@@ -2589,29 +2595,29 @@ ndis_getstate_80211(sc)
rval);
if (isset(ic->ic_modecaps, IEEE80211_MODE_11B)) {
- ic->ic_bss->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11B];
- for (i = 0; i < ic->ic_bss->ni_rates.rs_nrates; i++) {
- if ((ic->ic_bss->ni_rates.rs_rates[i] &
+ ni->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11B];
+ for (i = 0; i < ni->ni_rates.rs_nrates; i++) {
+ if ((ni->ni_rates.rs_rates[i] &
IEEE80211_RATE_VAL) == arg / 5000)
break;
}
}
- if (i == ic->ic_bss->ni_rates.rs_nrates &&
+ if (i == ni->ni_rates.rs_nrates &&
isset(ic->ic_modecaps, IEEE80211_MODE_11G)) {
- ic->ic_bss->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11G];
- for (i = 0; i < ic->ic_bss->ni_rates.rs_nrates; i++) {
- if ((ic->ic_bss->ni_rates.rs_rates[i] &
+ ni->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11G];
+ for (i = 0; i < ni->ni_rates.rs_nrates; i++) {
+ if ((ni->ni_rates.rs_rates[i] &
IEEE80211_RATE_VAL) == arg / 5000)
break;
}
}
- if (i == ic->ic_bss->ni_rates.rs_nrates)
+ if (i == ni->ni_rates.rs_nrates)
device_printf(sc->ndis_dev, "no matching rate for: %d\n",
arg / 5000);
else
- ic->ic_bss->ni_txrate = i;
+ ni->ni_txrate = i;
if (ic->ic_caps & IEEE80211_C_PMGT) {
len = sizeof(arg);
@@ -2634,7 +2640,7 @@ ndis_getstate_80211(sc)
bs->nwbx_config.nc_dsconfig / 1000, chanflag);
if (ic->ic_curchan == NULL)
ic->ic_curchan = &ic->ic_channels[0];
- ic->ic_bss->ni_chan = ic->ic_curchan;
+ ni->ni_chan = ic->ic_curchan;
ic->ic_bsschan = ic->ic_curchan;
free(bs, M_TEMP);
@@ -2653,27 +2659,27 @@ ndis_getstate_80211(sc)
ic->ic_flags &= ~IEEE80211_F_WPA;
switch(arg) {
case NDIS_80211_AUTHMODE_OPEN:
- ic->ic_bss->ni_authmode = IEEE80211_AUTH_OPEN;
+ ni->ni_authmode = IEEE80211_AUTH_OPEN;
break;
case NDIS_80211_AUTHMODE_SHARED:
- ic->ic_bss->ni_authmode = IEEE80211_AUTH_SHARED;
+ ni->ni_authmode = IEEE80211_AUTH_SHARED;
break;
case NDIS_80211_AUTHMODE_AUTO:
- ic->ic_bss->ni_authmode = IEEE80211_AUTH_AUTO;
+ ni->ni_authmode = IEEE80211_AUTH_AUTO;
break;
case NDIS_80211_AUTHMODE_WPA:
case NDIS_80211_AUTHMODE_WPAPSK:
case NDIS_80211_AUTHMODE_WPANONE:
- ic->ic_bss->ni_authmode = IEEE80211_AUTH_WPA;
+ ni->ni_authmode = IEEE80211_AUTH_WPA;
ic->ic_flags |= IEEE80211_F_WPA1;
break;
case NDIS_80211_AUTHMODE_WPA2:
case NDIS_80211_AUTHMODE_WPA2PSK:
- ic->ic_bss->ni_authmode = IEEE80211_AUTH_WPA;
+ ni->ni_authmode = IEEE80211_AUTH_WPA;
ic->ic_flags |= IEEE80211_F_WPA2;
break;
default:
- ic->ic_bss->ni_authmode = IEEE80211_AUTH_NONE;
+ ni->ni_authmode = IEEE80211_AUTH_NONE;
break;
}
}
@@ -2699,6 +2705,7 @@ ndis_ioctl(ifp, command, data)
caddr_t data;
{
struct ndis_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ifreq *ifr = (struct ifreq *) data;
struct ndis_oid_data oid;
struct ndis_evt evt;
@@ -2744,14 +2751,9 @@ ndis_ioctl(ifp, command, data)
break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
- if (sc->ndis_80211) {
- error = ieee80211_ioctl(&sc->ic, command, data);
- if (error == ENETRESET) {
- ndis_setstate_80211(sc);
- /*ndis_init(sc);*/
- error = 0;
- }
- } else
+ if (sc->ndis_80211)
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, command);
+ else
error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command);
break;
case SIOCSIFCAP:
@@ -2762,22 +2764,6 @@ ndis_ioctl(ifp, command, data)
ifp->if_hwassist = 0;
ndis_set_offload(sc);
break;
- case SIOCG80211:
- if (!NDIS_INITIALIZED(sc))
- goto do_80211;
- if (sc->ndis_80211)
- error = ndis_80211_ioctl_get(ifp, command, data);
- else
- error = ENOTTY;
- break;
- case SIOCS80211:
- if (!NDIS_INITIALIZED(sc))
- goto do_80211;
- if (sc->ndis_80211)
- error = ndis_80211_ioctl_set(ifp, command, data);
- else
- error = ENOTTY;
- break;
case SIOCGDRVSPEC:
if ((error = priv_check(curthread, PRIV_DRIVER)))
break;
@@ -2878,17 +2864,7 @@ ndis_ioctl(ifp, command, data)
NDIS_UNLOCK(sc);
break;
default:
-do_80211:
- sc->ndis_skip = 1;
- if (sc->ndis_80211) {
- error = ieee80211_ioctl(&sc->ic, command, data);
- if (error == ENETRESET) {
- ndis_setstate_80211(sc);
- error = 0;
- }
- } else
- error = ether_ioctl(ifp, command, data);
- sc->ndis_skip = 0;
+ error = ether_ioctl(ifp, command, data);
break;
}
@@ -2897,55 +2873,16 @@ do_80211:
return(error);
}
-static int
-ndis_80211_ioctl_get(struct ifnet *ifp, u_long command, caddr_t data)
-{
- struct ndis_softc *sc;
- struct ieee80211req *ireq;
- int error, len;
- uint16_t nodename_u[IEEE80211_NWID_LEN + 1];
- unicode_string us;
- ansi_string as;
-
- sc = ifp->if_softc;
- ireq = (struct ieee80211req *) data;
-
- switch (ireq->i_type) {
- case IEEE80211_IOC_MLME:
- error = 0;
- break;
- case IEEE80211_IOC_STATIONNAME:
- error = ndis_get_info(sc, OID_GEN_MACHINE_NAME,
- &nodename_u, &len);
- if (error)
- break;
- us.us_len = us.us_maxlen = len;
- us.us_buf = nodename_u;
- if (RtlUnicodeStringToAnsiString(&as, &us, TRUE)) {
- error = ENOMEM;
- break;
- }
- ireq->i_len = as.as_len;
- error = copyout(as.as_buf, ireq->i_data, ireq->i_len);
- RtlFreeAnsiString(&as);
- break;
- default:
- error = ieee80211_ioctl(&sc->ic, command, data);
- break;
- }
- return(error);
-}
-
int
-ndis_del_key(ic, key)
- struct ieee80211com *ic;
+ndis_del_key(vap, key)
+ struct ieee80211vap *vap;
const struct ieee80211_key *key;
{
struct ndis_softc *sc;
ndis_80211_key rkey;
int len, error = 0;
- sc = ic->ic_ifp->if_softc;
+ sc = vap->iv_ic->ic_ifp->if_softc;
bzero((char *)&rkey, sizeof(rkey));
len = sizeof(rkey);
@@ -2953,7 +2890,7 @@ ndis_del_key(ic, key)
rkey.nk_len = len;
rkey.nk_keyidx = key->wk_keyix;
- bcopy(ic->ic_ifp->if_broadcastaddr,
+ bcopy(vap->iv_ifp->if_broadcastaddr,
rkey.nk_bssid, IEEE80211_ADDR_LEN);
error = ndis_set_info(sc, OID_802_11_REMOVE_KEY, &rkey, &len);
@@ -2971,16 +2908,18 @@ ndis_del_key(ic, key)
*/
static int
-ndis_add_key(ic, key, mac)
- struct ieee80211com *ic;
+ndis_add_key(vap, key, mac)
+ struct ieee80211vap *vap;
const struct ieee80211_key *key;
const uint8_t mac[IEEE80211_ADDR_LEN];
{
struct ndis_softc *sc;
+ struct ifnet *ifp;
ndis_80211_key rkey;
int len, error = 0;
- sc = ic->ic_ifp->if_softc;
+ ifp = vap->iv_ic->ic_ifp;
+ sc = ifp->if_softc;
switch (key->wk_cipher->ic_cipher) {
case IEEE80211_CIPHER_TKIP:
@@ -3005,17 +2944,17 @@ ndis_add_key(ic, key, mac)
rkey.nk_keyidx |= 1 << 31;
if (key->wk_flags & IEEE80211_KEY_GROUP) {
- bcopy(ic->ic_ifp->if_broadcastaddr,
+ bcopy(ifp->if_broadcastaddr,
rkey.nk_bssid, IEEE80211_ADDR_LEN);
} else {
- bcopy(ic->ic_bss->ni_bssid,
+ bcopy(vap->iv_bss->ni_bssid,
rkey.nk_bssid, IEEE80211_ADDR_LEN);
/* pairwise key */
rkey.nk_keyidx |= 1 << 30;
}
/* need to set bit 29 based on keyrsc */
- rkey.nk_keyrsc = key->wk_keyrsc;
+ rkey.nk_keyrsc = key->wk_keyrsc[0]; /* XXX need tid */
if (rkey.nk_keyrsc)
rkey.nk_keyidx |= 1 << 29;
@@ -3050,54 +2989,6 @@ ndis_add_key(ic, key, mac)
return (1);
}
-static int
-ndis_80211_ioctl_set(struct ifnet *ifp, u_long command, caddr_t data)
-{
- struct ndis_softc *sc;
- struct ieee80211req *ireq;
- int error = EINVAL, len;
- ansi_string as;
- unicode_string us;
-
- sc = ifp->if_softc;
- ireq = (struct ieee80211req *) data;
-
- switch (ireq->i_type) {
- case IEEE80211_IOC_COUNTERMEASURES:
- case IEEE80211_IOC_DROPUNENCRYPTED:
- error = 0;
- break;
- case IEEE80211_IOC_STATIONNAME:
- error = priv_check(curthread, PRIV_NET80211_MANAGE);
- if (error)
- break;
- if (ireq->i_val != 0 ||
- ireq->i_len > IEEE80211_NWID_LEN) {
- error = EINVAL;
- break;
- }
- as.as_len = as.as_maxlen = ireq->i_len;
- as.as_buf = ireq->i_data;
- if (RtlAnsiStringToUnicodeString(&us, &as, TRUE)) {
- error = ENOMEM;
- break;
- }
- len = us.us_len;
- error = ndis_set_info(sc, OID_GEN_MACHINE_NAME,
- us.us_buf, &len);
- RtlFreeUnicodeString(&us);
- break;
- default:
- error = ieee80211_ioctl(&sc->ic, command, data);
- if (error == ENETRESET) {
- ndis_setstate_80211(sc);
- error = 0;
- }
- }
-
- return(error);
-}
-
static void
ndis_resettask(d, arg)
device_object *d;
@@ -3142,13 +3033,8 @@ ndis_stop(sc)
struct ndis_softc *sc;
{
struct ifnet *ifp;
- struct ieee80211com *ic;
int i;
- ic = &sc->ic;
- if (sc->ndis_80211)
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
-
ifp = sc->ifp;
callout_drain(&sc->ndis_stat_callout);
@@ -3191,24 +3077,26 @@ ndis_shutdown(dev)
}
static int
-ndis_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+ndis_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct ndis_vap *nvp = NDIS_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
struct ifnet *ifp = ic->ic_ifp;
struct ndis_softc *sc = ifp->if_softc;
- enum ieee80211_state ostate;
+ enum ieee80211_state ostate;
DPRINTF(("%s: %s -> %s\n", __func__,
- ieee80211_state_name[ic->ic_state],
+ ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate]));
- ostate = ic->ic_state;
- ic->ic_state = nstate;
+ ostate = vap->iv_state;
+ vap->iv_state = nstate;
switch (nstate) {
/* pass on to net80211 */
case IEEE80211_S_INIT:
case IEEE80211_S_SCAN:
- return (sc->ndis_newstate(ic, nstate, arg));
+ return nvp->newstate(vap, nstate, arg);
case IEEE80211_S_ASSOC:
if (ostate != IEEE80211_S_AUTH)
@@ -3229,14 +3117,19 @@ static void
ndis_scan(void *arg, int npending)
{
struct ndis_softc *sc = arg;
- struct ieee80211com *ic = (void *)&sc->ic;
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211com *ic;
+ struct ieee80211vap *vap;
+ struct ieee80211_scan_state *ss;
ndis_80211_ssid ssid;
int error, len;
+ ic = sc->ifp->if_l2com;
+ ss = ic->ic_scan;
+ vap = TAILQ_FIRST(&ic->ic_vaps);
+
if (!NDIS_INITIALIZED(sc)) {
DPRINTF(("%s: scan aborted\n", __func__));
- ieee80211_cancel_scan(ic);
+ ieee80211_cancel_scan(vap);
return;
}
@@ -3257,7 +3150,7 @@ ndis_scan(void *arg, int npending)
NULL, &len);
if (error) {
DPRINTF(("%s: scan command failed\n", __func__));
- ieee80211_cancel_scan(ic);
+ ieee80211_cancel_scan(vap);
return;
}
@@ -3267,13 +3160,14 @@ ndis_scan(void *arg, int npending)
return;
ndis_scan_results(sc);
- ieee80211_scan_done(ic);
+ ieee80211_scan_done(vap);
}
static void
ndis_scan_results(struct ndis_softc *sc)
{
- struct ieee80211com *ic = (void *)&sc->ic;
+ struct ieee80211com *ic;
+ struct ieee80211vap *vap;
ndis_80211_bssid_list_ex *bl;
ndis_wlan_bssid_ex *wb;
struct ieee80211_scanparams sp;
@@ -3285,6 +3179,8 @@ ndis_scan_results(struct ndis_softc *sc)
uint8_t rates[2+IEEE80211_RATE_MAXSIZE];
uint8_t *frm, *efrm;
+ ic = sc->ifp->if_l2com;
+ vap = TAILQ_FIRST(&ic->ic_vaps);
noise = -96;
len = sizeof(uint32_t) + (sizeof(ndis_wlan_bssid_ex) * 16);
@@ -3344,10 +3240,7 @@ ndis_scan_results(struct ndis_softc *sc)
chanflag = ndis_nettype_chan(wb->nwbx_nettype);
freq = wb->nwbx_config.nc_dsconfig / 1000;
- sp.bchan = ieee80211_mhz2ieee(freq, chanflag);
- sp.curchan = ieee80211_find_channel(ic, freq, chanflag);
- if (sp.curchan == NULL)
- sp.curchan = &ic->ic_channels[0];
+ sp.chan = sp.bchan = ieee80211_mhz2ieee(freq, chanflag);
/* Process extended info from AP */
if (wb->nwbx_len > sizeof(ndis_wlan_bssid)) {
@@ -3378,7 +3271,7 @@ done:
DPRINTF(("scan: bssid %s chan %dMHz (%d/%d) rssi %d\n",
ether_sprintf(wb->nwbx_macaddr), freq, sp.bchan, chanflag,
rssi));
- ieee80211_add_scan(ic, &sp, &wh, 0, rssi, noise, rstamp);
+ ieee80211_add_scan(vap, &sp, &wh, 0, rssi, noise, rstamp);
wb = (ndis_wlan_bssid_ex *)((char *)wb + wb->nwbx_len);
}
free(bl, M_DEVBUF);
@@ -3400,13 +3293,13 @@ ndis_set_channel(struct ieee80211com *ic)
}
static void
-ndis_scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
+ndis_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
/* ignore */
}
static void
-ndis_scan_mindwell(struct ieee80211com *ic)
+ndis_scan_mindwell(struct ieee80211_scan_state *ss)
{
/* NB: don't try to abort scan; wait for firmware to finish */
}
diff --git a/sys/dev/if_ndis/if_ndisvar.h b/sys/dev/if_ndis/if_ndisvar.h
index cdbf543..6814709 100644
--- a/sys/dev/if_ndis/if_ndisvar.h
+++ b/sys/dev/if_ndis/if_ndisvar.h
@@ -99,8 +99,15 @@ struct ndis_evt {
char *ne_buf;
};
+struct ndis_vap {
+ struct ieee80211vap vap;
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define NDIS_VAP(vap) ((struct ndis_vap *)(vap))
+
struct ndis_softc {
- struct ieee80211com ic; /* interface info */
struct ifnet *ifp;
struct ifmedia ifmedia; /* media info */
u_long ndis_hwassist;
diff --git a/sys/dev/ipw/if_ipw.c b/sys/dev/ipw/if_ipw.c
index 8a69a40..f21bc5b 100644
--- a/sys/dev/ipw/if_ipw.c
+++ b/sys/dev/ipw/if_ipw.c
@@ -107,13 +107,21 @@ static const struct ipw_ident ipw_ident_table[] = {
{ 0, 0, NULL }
};
+static struct ieee80211vap *ipw_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void ipw_vap_delete(struct ieee80211vap *);
static int ipw_dma_alloc(struct ipw_softc *);
static void ipw_release(struct ipw_softc *);
-static int ipw_media_change(struct ifnet *);
static void ipw_media_status(struct ifnet *, struct ifmediareq *);
-static int ipw_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static int ipw_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static uint16_t ipw_read_prom_word(struct ipw_softc *, uint8_t);
static void ipw_rx_cmd_intr(struct ipw_softc *, struct ipw_soft_buf *);
+static void ipw_assocsuccess(void *, int);
+static void ipw_assocfailed(void *, int);
+static void ipw_scandone(void *, int);
+static void ipw_bmiss(void *, int);
static void ipw_rx_newstate_intr(struct ipw_softc *, struct ipw_soft_buf *);
static void ipw_rx_data_intr(struct ipw_softc *, struct ipw_status *,
struct ipw_soft_bd *, struct ipw_soft_buf *);
@@ -126,6 +134,8 @@ static const char * ipw_cmdname(int);
static int ipw_cmd(struct ipw_softc *, uint32_t, void *, uint32_t);
static int ipw_tx_start(struct ifnet *, struct mbuf *,
struct ieee80211_node *);
+static int ipw_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
static void ipw_start(struct ifnet *);
static void ipw_start_locked(struct ifnet *);
static void ipw_watchdog(void *);
@@ -138,12 +148,10 @@ static int ipw_load_ucode(struct ipw_softc *, const char *, int);
static int ipw_load_firmware(struct ipw_softc *, const char *, int);
static int ipw_config(struct ipw_softc *);
static void ipw_assoc_task(void *, int);
-static int ipw_auth_and_assoc(struct ipw_softc *);
static void ipw_disassoc_task(void *, int);
-static int ipw_disassociate(struct ipw_softc *);
static void ipw_init_task(void *, int);
static void ipw_init(void *);
-static void ipw_init_locked(struct ipw_softc *, int);
+static void ipw_init_locked(struct ipw_softc *);
static void ipw_stop(void *);
static void ipw_stop_locked(struct ipw_softc *);
static int ipw_sysctl_stats(SYSCTL_HANDLER_ARGS);
@@ -163,8 +171,9 @@ static int ipw_scan(struct ipw_softc *);
static void ipw_scan_start(struct ieee80211com *);
static void ipw_scan_end(struct ieee80211com *);
static void ipw_set_channel(struct ieee80211com *);
-static void ipw_scan_curchan(struct ieee80211com *, unsigned long maxdwell);
-static void ipw_scan_mindwell(struct ieee80211com *);
+static void ipw_scan_curchan(struct ieee80211_scan_state *,
+ unsigned long maxdwell);
+static void ipw_scan_mindwell(struct ieee80211_scan_state *);
static int ipw_probe(device_t);
static int ipw_attach(device_t);
@@ -219,7 +228,7 @@ ipw_attach(device_t dev)
{
struct ipw_softc *sc = device_get_softc(dev);
struct ifnet *ifp;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
struct ieee80211_channel *c;
uint16_t val;
int error, i;
@@ -231,8 +240,7 @@ ipw_attach(device_t dev)
TASK_INIT(&sc->sc_init_task, 0, ipw_init_task, sc);
TASK_INIT(&sc->sc_scan_task, 0, ipw_scan_task, sc);
- TASK_INIT(&sc->sc_assoc_task, 0, ipw_assoc_task, sc);
- TASK_INIT(&sc->sc_disassoc_task, 0, ipw_disassoc_task, sc);
+ TASK_INIT(&sc->sc_bmiss_task, 0, ipw_bmiss, sc);
callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0);
if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
@@ -262,24 +270,25 @@ ipw_attach(device_t dev)
RF_ACTIVE | RF_SHAREABLE);
if (sc->irq == NULL) {
device_printf(dev, "could not allocate interrupt resource\n");
- goto fail;
+ goto fail1;
}
if (ipw_reset(sc) != 0) {
device_printf(dev, "could not reset adapter\n");
- goto fail;
+ goto fail2;
}
if (ipw_dma_alloc(sc) != 0) {
device_printf(dev, "could not allocate DMA resources\n");
- goto fail;
+ goto fail2;
}
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(dev, "can not if_alloc()\n");
- goto fail;
+ goto fail3;
}
+ ic = ifp->if_l2com;
ifp->if_softc = sc;
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
@@ -292,9 +301,8 @@ ipw_attach(device_t dev)
IFQ_SET_READY(&ifp->if_snd);
ic->ic_ifp = ifp;
- ic->ic_phytype = IEEE80211_T_DS;
ic->ic_opmode = IEEE80211_M_STA;
- ic->ic_state = IEEE80211_S_INIT;
+ ic->ic_phytype = IEEE80211_T_DS;
/* set device capabilities */
ic->ic_caps = IEEE80211_C_IBSS /* IBSS mode supported */
@@ -333,20 +341,18 @@ ipw_attach(device_t dev)
sc->flags |= IPW_FLAG_HAS_RADIO_SWITCH;
ieee80211_ifattach(ic);
- /* override state transition machine */
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = ipw_newstate;
- ieee80211_media_init(ic, ipw_media_change, ipw_media_status);
-
ic->ic_scan_start = ipw_scan_start;
ic->ic_scan_end = ipw_scan_end;
ic->ic_set_channel = ipw_set_channel;
ic->ic_scan_curchan = ipw_scan_curchan;
ic->ic_scan_mindwell = ipw_scan_mindwell;
+ ic->ic_raw_xmit = ipw_raw_xmit;
+
+ ic->ic_vap_create = ipw_vap_create;
+ ic->ic_vap_delete = ipw_vap_delete;
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap),
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap));
sc->sc_rxtap_len = sizeof sc->sc_rxtap;
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
@@ -359,8 +365,6 @@ ipw_attach(device_t dev)
/*
* Add a few sysctl knobs.
*/
- sc->dwelltime = 100;
-
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "radio",
CTLTYPE_INT | CTLFLAG_RD, sc, 0, ipw_sysctl_radio, "I",
@@ -371,11 +375,6 @@ ipw_attach(device_t dev)
CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, ipw_sysctl_stats, "S",
"statistics");
- SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "dwell",
- CTLFLAG_RW, &sc->dwelltime, 0,
- "channel dwell time (ms) for AP/station scanning");
-
/*
* Hook our interrupt after all initialization is complete.
*/
@@ -383,15 +382,23 @@ ipw_attach(device_t dev)
NULL, ipw_intr, sc, &sc->sc_ih);
if (error != 0) {
device_printf(dev, "could not set up interrupt\n");
- goto fail;
+ goto fail4;
}
if (bootverbose)
ieee80211_announce(ic);
return 0;
-
-fail: ipw_detach(dev);
+fail4:
+ if_free(ifp);
+fail3:
+ ipw_release(sc);
+fail2:
+ bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
+fail1:
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
+fail:
+ mtx_destroy(&sc->sc_mtx);
return ENXIO;
}
@@ -399,33 +406,27 @@ static int
ipw_detach(device_t dev)
{
struct ipw_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
ipw_stop(sc);
+
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+
callout_drain(&sc->sc_wdtimer);
taskqueue_drain(taskqueue_fast, &sc->sc_init_task);
taskqueue_drain(taskqueue_fast, &sc->sc_scan_task);
- taskqueue_drain(taskqueue_fast, &sc->sc_assoc_task);
- taskqueue_drain(taskqueue_fast, &sc->sc_disassoc_task);
-
- if (ifp != NULL) {
- bpfdetach(ifp);
- ieee80211_ifdetach(ic);
- }
+ taskqueue_drain(taskqueue_fast, &sc->sc_bmiss_task);
ipw_release(sc);
- if (sc->irq != NULL) {
- bus_teardown_intr(dev, sc->irq, sc->sc_ih);
- bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
- }
+ bus_teardown_intr(dev, sc->irq, sc->sc_ih);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
- if (sc->mem != NULL)
- bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
- if (ifp != NULL)
- if_free(ifp);
+ if_free(ifp);
if (sc->sc_firmware != NULL) {
firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD);
@@ -437,6 +438,103 @@ ipw_detach(device_t dev)
return 0;
}
+static struct ieee80211vap *
+ipw_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ipw_softc *sc = ifp->if_softc;
+ struct ipw_vap *ivp;
+ struct ieee80211vap *vap;
+ const struct firmware *fp;
+ const struct ipw_firmware_hdr *hdr;
+ const char *imagename;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ imagename = "ipw_bss";
+ break;
+ case IEEE80211_M_IBSS:
+ imagename = "ipw_ibss";
+ break;
+ case IEEE80211_M_MONITOR:
+ imagename = "ipw_monitor";
+ break;
+ default:
+ return NULL;
+ }
+
+ /*
+ * Load firmware image using the firmware(9) subsystem. Doing
+ * this unlocked is ok since we're single-threaded by the
+ * 802.11 layer.
+ */
+ if (sc->sc_firmware == NULL ||
+ strcmp(sc->sc_firmware->name, imagename) != 0) {
+ if (sc->sc_firmware != NULL)
+ firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD);
+ sc->sc_firmware = firmware_get(imagename);
+ }
+ if (sc->sc_firmware == NULL) {
+ device_printf(sc->sc_dev,
+ "could not load firmware image '%s'\n", imagename);
+ return NULL;
+ }
+ fp = sc->sc_firmware;
+ if (fp->datasize < sizeof *hdr) {
+ device_printf(sc->sc_dev,
+ "firmware image too short %zu\n", fp->datasize);
+ firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD);
+ sc->sc_firmware = NULL;
+ return NULL;
+ }
+ hdr = (const struct ipw_firmware_hdr *)fp->data;
+ if (fp->datasize < sizeof *hdr + le32toh(hdr->mainsz) +
+ le32toh(hdr->ucodesz)) {
+ device_printf(sc->sc_dev,
+ "firmware image too short %zu\n", fp->datasize);
+ firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD);
+ sc->sc_firmware = NULL;
+ return NULL;
+ }
+
+ ivp = (struct ipw_vap *) malloc(sizeof(struct ipw_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (ivp == NULL)
+ return NULL;
+ vap = &ivp->vap;
+
+ TASK_INIT(&ivp->assoc_task, 0, ipw_assoc_task, vap);
+ TASK_INIT(&ivp->disassoc_task, 0, ipw_disassoc_task, vap);
+ TASK_INIT(&ivp->assoc_success_task, 0, ipw_assocsuccess, vap);
+ TASK_INIT(&ivp->assoc_failed_task, 0, ipw_assocfailed, vap);
+ TASK_INIT(&ivp->scandone_task, 0, ipw_scandone, vap);
+
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
+ /* override with driver methods */
+ ivp->newstate = vap->iv_newstate;
+ vap->iv_newstate = ipw_newstate;
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ipw_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+ipw_vap_delete(struct ieee80211vap *vap)
+{
+ struct ipw_vap *ivp = IPW_VAP(vap);
+
+ ieee80211_vap_detach(vap);
+ free(ivp, M_80211_VAP);
+}
+
static int
ipw_dma_alloc(struct ipw_softc *sc)
{
@@ -748,45 +846,17 @@ static int
ipw_resume(device_t dev)
{
struct ipw_softc *sc = device_get_softc(dev);
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
- IPW_LOCK_DECL;
-
- IPW_LOCK(sc);
+ struct ifnet *ifp = sc->sc_ifp;
pci_write_config(dev, 0x41, 0, 1);
- if (ifp->if_flags & IFF_UP) {
- ipw_init_locked(sc, 0);
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- ipw_start_locked(ifp);
- }
-
- IPW_UNLOCK(sc);
+ if (ifp->if_flags & IFF_UP)
+ ipw_init(sc);
return 0;
}
static int
-ipw_media_change(struct ifnet *ifp)
-{
- struct ipw_softc *sc = ifp->if_softc;
- int error;
- IPW_LOCK_DECL;
-
- IPW_LOCK(sc);
- error = ieee80211_media_change(ifp);
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING))
- ipw_init_locked(sc, 0);
- error = 0;
- }
- IPW_UNLOCK(sc);
-
- return (error);
-}
-
-static int
ipw_cvtrate(int ipwrate)
{
switch (ipwrate) {
@@ -805,47 +875,26 @@ ipw_cvtrate(int ipwrate)
static void
ipw_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
- struct ipw_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int rate;
-
- imr->ifm_status = IFM_AVALID;
- imr->ifm_active = IFM_IEEE80211;
- if (ic->ic_state == IEEE80211_S_RUN)
- imr->ifm_status |= IFM_ACTIVE;
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ipw_softc *sc = ic->ic_ifp->if_softc;
/* read current transmission rate from adapter */
- rate = ipw_cvtrate(ipw_read_table1(sc, IPW_INFO_CURRENT_TX_RATE) & 0xf);
- imr->ifm_active |= ieee80211_rate2media(ic, rate, IEEE80211_MODE_11B);
-
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- break;
-
- case IEEE80211_M_IBSS:
- imr->ifm_active |= IFM_IEEE80211_IBSS;
- break;
-
- case IEEE80211_M_MONITOR:
- imr->ifm_active |= IFM_IEEE80211_MONITOR;
- break;
-
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_HOSTAP:
- case IEEE80211_M_WDS:
- /* should not get there */
- break;
- }
+ vap->iv_bss->ni_txrate = ipw_cvtrate(
+ ipw_read_table1(sc, IPW_INFO_CURRENT_TX_RATE) & 0xf);
+ ieee80211_media_status(ifp, imr);
}
static int
-ipw_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+ipw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct ipw_vap *ivp = IPW_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
struct ifnet *ifp = ic->ic_ifp;
struct ipw_softc *sc = ifp->if_softc;
DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__,
- ieee80211_state_name[ic->ic_state],
+ ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate], sc->flags));
switch (nstate) {
@@ -859,36 +908,40 @@ ipw_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
* AUTH -> RUN transition and we want to do nothing.
* This is all totally bogus and needs to be redone.
*/
- if (ic->ic_state == IEEE80211_S_SCAN)
- taskqueue_enqueue_fast(taskqueue_fast,
- &sc->sc_assoc_task);
+ if (vap->iv_state == IEEE80211_S_SCAN) {
+ taskqueue_enqueue(taskqueue_swi,
+ &IPW_VAP(vap)->assoc_task);
+ return EINPROGRESS;
+ }
}
break;
case IEEE80211_S_INIT:
if (sc->flags & IPW_FLAG_ASSOCIATED)
- taskqueue_enqueue_fast(taskqueue_fast,
- &sc->sc_disassoc_task);
+ taskqueue_enqueue(taskqueue_swi,
+ &IPW_VAP(vap)->disassoc_task);
break;
case IEEE80211_S_AUTH:
- taskqueue_enqueue_fast(taskqueue_fast, &sc->sc_assoc_task);
- break;
+ taskqueue_enqueue(taskqueue_swi, &IPW_VAP(vap)->assoc_task);
+ return EINPROGRESS;
case IEEE80211_S_ASSOC:
/*
* If we are not transitioning from AUTH the resend the
* association request.
*/
- if (ic->ic_state != IEEE80211_S_AUTH)
- taskqueue_enqueue_fast(taskqueue_fast,
- &sc->sc_assoc_task);
+ if (vap->iv_state != IEEE80211_S_AUTH) {
+ taskqueue_enqueue(taskqueue_swi,
+ &IPW_VAP(vap)->assoc_task);
+ return EINPROGRESS;
+ }
break;
default:
break;
}
- return (*sc->sc_newstate)(ic, nstate, arg);
+ return ivp->newstate(vap, nstate, arg);
}
/*
@@ -965,10 +1018,44 @@ ipw_rx_cmd_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
}
static void
+ipw_assocsuccess(void *arg, int npending)
+{
+ struct ieee80211vap *vap = arg;
+
+ ieee80211_new_state(vap, IEEE80211_S_RUN, -1);
+}
+
+static void
+ipw_assocfailed(void *arg, int npending)
+{
+ struct ieee80211vap *vap = arg;
+
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
+}
+
+static void
+ipw_scandone(void *arg, int npending)
+{
+ struct ieee80211vap *vap = arg;
+
+ ieee80211_scan_done(vap);
+}
+
+static void
+ipw_bmiss(void *arg, int npending)
+{
+ struct ieee80211com *ic = arg;
+
+ ieee80211_beacon_miss(ic);
+}
+
+static void
ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
{
-#define IEEESTATE(ic) ieee80211_state_name[ic->ic_state]
- struct ieee80211com *ic = &sc->sc_ic;
+#define IEEESTATE(vap) ieee80211_state_name[vap->iv_state]
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
uint32_t state;
bus_dmamap_sync(sc->rxbuf_dmat, sbuf->map, BUS_DMASYNC_POSTREAD);
@@ -978,27 +1065,32 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
switch (state) {
case IPW_STATE_ASSOCIATED:
DPRINTFN(2, ("Association succeeded (%s flags 0x%x)\n",
- IEEESTATE(ic), sc->flags));
- sc->flags |= IPW_FLAG_ASSOCIATED;
+ IEEESTATE(vap), sc->flags));
/* XXX suppress state change in case the fw auto-associates */
- if (ic->ic_state != IEEE80211_S_ASSOC) {
- DPRINTF(("Unexpected association (state %u)\n",
- ic->ic_state));
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ if ((sc->flags & IPW_FLAG_ASSOCIATING) == 0) {
+ DPRINTF(("Unexpected association (%s, flags 0x%x)\n",
+ IEEESTATE(vap), sc->flags));
+ break;
+ }
+ sc->flags &= ~IPW_FLAG_ASSOCIATING;
+ sc->flags |= IPW_FLAG_ASSOCIATED;
+ taskqueue_enqueue(taskqueue_swi,
+ &IPW_VAP(vap)->assoc_success_task);
break;
case IPW_STATE_SCANNING:
DPRINTFN(3, ("Scanning (%s flags 0x%x)\n",
- IEEESTATE(ic), sc->flags));
+ IEEESTATE(vap), sc->flags));
/*
* NB: Check driver state for association on assoc
* loss as the firmware will immediately start to
* scan and we would treat it as a beacon miss if
* we checked the 802.11 layer state.
*/
- if (sc->flags & IPW_FLAG_ASSOCIATED)
- ieee80211_beacon_miss(ic);
+ if (sc->flags & IPW_FLAG_ASSOCIATED) {
+ /* XXX probably need to issue disassoc to fw */
+ taskqueue_enqueue(taskqueue_swi, &sc->sc_bmiss_task);
+ }
break;
case IPW_STATE_SCAN_COMPLETE:
@@ -1009,14 +1101,15 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
* around this by marking the HACK flag and skipping
* the first scan complete event.
*/
+ DPRINTFN(3, ("Scan complete (%s flags 0x%x)\n",
+ IEEESTATE(vap), sc->flags));
if (sc->flags & IPW_FLAG_HACK) {
sc->flags &= ~IPW_FLAG_HACK;
break;
}
- DPRINTFN(3, ("Scan complete (%s flags 0x%x)\n",
- IEEESTATE(ic), sc->flags));
if (sc->flags & IPW_FLAG_SCANNING) {
- ieee80211_scan_done(ic);
+ taskqueue_enqueue(taskqueue_swi,
+ &IPW_VAP(vap)->scandone_task);
sc->flags &= ~IPW_FLAG_SCANNING;
sc->sc_scan_timer = 0;
}
@@ -1024,27 +1117,31 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
case IPW_STATE_ASSOCIATION_LOST:
DPRINTFN(2, ("Association lost (%s flags 0x%x)\n",
- IEEESTATE(ic), sc->flags));
- sc->flags &= ~IPW_FLAG_ASSOCIATED;
- if (ic->ic_state == IEEE80211_S_RUN)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+ IEEESTATE(vap), sc->flags));
+ sc->flags &= ~(IPW_FLAG_ASSOCIATING | IPW_FLAG_ASSOCIATED);
+ if (vap->iv_state == IEEE80211_S_RUN)
+ taskqueue_enqueue(taskqueue_swi,
+ &IPW_VAP(vap)->assoc_failed_task);
break;
case IPW_STATE_DISABLED:
+ /* XXX? is this right? */
+ sc->flags &= ~(IPW_FLAG_HACK | IPW_FLAG_SCANNING |
+ IPW_FLAG_ASSOCIATING | IPW_FLAG_ASSOCIATED);
DPRINTFN(2, ("Firmware disabled (%s flags 0x%x)\n",
- IEEESTATE(ic), sc->flags));
+ IEEESTATE(vap), sc->flags));
break;
case IPW_STATE_RADIO_DISABLED:
- DPRINTFN(2, ("Radio off (%s flags 0x%x)\n",
- IEEESTATE(ic), sc->flags));
- ic->ic_ifp->if_flags &= ~IFF_UP;
+ device_printf(sc->sc_dev, "radio turned off\n");
+ ieee80211_notify_radio(ic, 0);
ipw_stop_locked(sc);
+ /* XXX start polling thread to detect radio on */
break;
default:
DPRINTFN(2, ("%s: unhandled state %u %s flags 0x%x\n",
- __func__, state, IEEESTATE(ic), sc->flags));
+ __func__, state, IEEESTATE(vap), sc->flags));
break;
}
#undef IEEESTATE
@@ -1056,7 +1153,8 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf)
static void
ipw_setcurchan(struct ipw_softc *sc, struct ieee80211_channel *chan)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
ic->ic_curchan = chan;
sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq =
@@ -1072,7 +1170,8 @@ ipw_setcurchan(struct ipw_softc *sc, struct ieee80211_channel *chan)
static void
ipw_fix_channel(struct ipw_softc *sc, struct mbuf *m)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211_channel *c;
struct ieee80211_frame *wh;
uint8_t subtype;
@@ -1089,6 +1188,7 @@ ipw_fix_channel(struct ipw_softc *sc, struct mbuf *m)
subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP)
return;
+ /* XXX use ieee80211_parse_beacon */
frm = (uint8_t *)(wh + 1);
efrm = mtod(m, uint8_t *) + m->m_len;
@@ -1116,10 +1216,9 @@ static void
ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status,
struct ipw_soft_bd *sbd, struct ipw_soft_buf *sbuf)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct mbuf *mnew, *m;
- struct ieee80211_frame *wh;
struct ieee80211_node *ni;
bus_addr_t physaddr;
int error;
@@ -1177,7 +1276,7 @@ ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status,
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = m->m_len = le32toh(status->len);
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct ipw_rx_radiotap_header *tap = &sc->sc_rxtap;
tap->wr_flags = 0;
@@ -1185,21 +1284,19 @@ ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status,
tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
}
if (sc->flags & IPW_FLAG_SCANNING)
ipw_fix_channel(sc, m);
- wh = mtod(m, struct ieee80211_frame *);
IPW_UNLOCK(sc);
- ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
-
- /* send the frame to the 802.11 layer */
- ieee80211_input(ic, m, ni, status->rssi, -95/*XXX*/, 0);
-
- /* node is no longer needed */
- ieee80211_free_node(ni);
+ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, status->rssi, -95, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, status->rssi, -95, 0);
IPW_LOCK(sc);
bus_dmamap_sync(sc->rbd_dmat, sc->rbd_map, BUS_DMASYNC_PREWRITE);
@@ -1208,7 +1305,6 @@ ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status,
static void
ipw_rx_intr(struct ipw_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
struct ipw_status *status;
struct ipw_soft_bd *sbd;
struct ipw_soft_buf *sbuf;
@@ -1243,11 +1339,7 @@ ipw_rx_intr(struct ipw_softc *sc)
case IPW_STATUS_CODE_NOTIFICATION:
DPRINTFN(2, ("notification status, len %u flags 0x%x\n",
le32toh(status->len), status->flags));
- if (ic->ic_state == IEEE80211_S_AUTH) {
- /* XXX assume auth notification */
- ieee80211_node_authorize(ic->ic_bss);
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
- }
+ /* XXX maybe drive state machine AUTH->ASSOC? */
break;
default:
@@ -1311,7 +1403,7 @@ ipw_release_sbd(struct ipw_softc *sc, struct ipw_soft_bd *sbd)
static void
ipw_tx_intr(struct ipw_softc *sc)
{
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
struct ipw_soft_bd *sbd;
uint32_t r, i;
@@ -1359,7 +1451,7 @@ ipw_intr(void *arg)
if (r & (IPW_INTR_FATAL_ERROR | IPW_INTR_PARITY_ERROR)) {
device_printf(sc->sc_dev, "firmware error\n");
- taskqueue_enqueue_fast(taskqueue_fast, &sc->sc_init_task);
+ taskqueue_enqueue(taskqueue_swi, &sc->sc_init_task);
r = 0; /* don't process more interrupts */
}
@@ -1447,6 +1539,8 @@ ipw_cmd(struct ipw_softc *sc, uint32_t type, void *data, uint32_t len)
bus_addr_t physaddr;
int error;
+ IPW_LOCK_ASSERT(sc);
+
if (sc->flags & IPW_FLAG_BUSY) {
device_printf(sc->sc_dev, "%s: %s not sent, busy\n",
__func__, ipw_cmdname(type));
@@ -1514,7 +1608,7 @@ static int
ipw_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni)
{
struct ipw_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211_frame *wh;
struct ipw_soft_bd *sbd;
struct ipw_soft_hdr *shdr;
@@ -1528,24 +1622,23 @@ ipw_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni)
wh = mtod(m0, struct ieee80211_frame *);
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
}
-
/* packet header may have moved, reset our local pointer */
wh = mtod(m0, struct ieee80211_frame *);
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct ipw_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
shdr = SLIST_FIRST(&sc->free_shdr);
@@ -1660,6 +1753,16 @@ ipw_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni)
return 0;
}
+static int
+ipw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ /* no support; just discard */
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return 0;
+}
+
static void
ipw_start(struct ifnet *ifp)
{
@@ -1675,54 +1778,31 @@ static void
ipw_start_locked(struct ifnet *ifp)
{
struct ipw_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct mbuf *m0;
- struct ether_header *eh;
struct ieee80211_node *ni;
+ struct mbuf *m;
IPW_LOCK_ASSERT(sc);
- if (ic->ic_state != IEEE80211_S_RUN)
- return;
-
for (;;) {
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
- if (m0 == NULL)
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
break;
-
if (sc->txfree < 1 + IPW_MAX_NSEG) {
- IFQ_DRV_PREPEND(&ifp->if_snd, m0);
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
break;
}
-
- if (m0->m_len < sizeof (struct ether_header) &&
- (m0 = m_pullup(m0, sizeof (struct ether_header))) == NULL)
- continue;
-
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- continue;
- }
- BPF_MTAP(ifp, m0);
-
- m0 = ieee80211_encap(ic, m0, ni);
- if (m0 == NULL) {
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
ieee80211_free_node(ni);
continue;
}
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (ipw_tx_start(ifp, m0, ni) != 0) {
+ if (ipw_tx_start(ifp, m, ni) != 0) {
ieee80211_free_node(ni);
ifp->if_oerrors++;
break;
}
-
/* start watchdog timer */
sc->sc_tx_timer = 5;
}
@@ -1732,8 +1812,8 @@ static void
ipw_watchdog(void *arg)
{
struct ipw_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
IPW_LOCK_ASSERT(sc);
@@ -1741,8 +1821,7 @@ ipw_watchdog(void *arg)
if (--sc->sc_tx_timer == 0) {
if_printf(ifp, "device timeout\n");
ifp->if_oerrors++;
- taskqueue_enqueue_fast(taskqueue_fast,
- &sc->sc_init_task);
+ taskqueue_enqueue(taskqueue_swi, &sc->sc_init_task);
}
}
if (sc->sc_scan_timer > 0) {
@@ -1750,7 +1829,7 @@ ipw_watchdog(void *arg)
DPRINTFN(3, ("Scan timeout\n"));
/* End the scan */
if (sc->flags & IPW_FLAG_SCANNING) {
- ieee80211_scan_done(ic);
+ ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps));
sc->flags &= ~IPW_FLAG_SCANNING;
}
}
@@ -1763,37 +1842,35 @@ static int
ipw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct ipw_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
IPW_LOCK_DECL;
IPW_LOCK(sc);
-
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
- if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
- ipw_init_locked(sc, 0);
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ ipw_init_locked(sc);
+ startall = 1;
+ }
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
ipw_stop_locked(sc);
}
break;
-
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
default:
- error = ieee80211_ioctl(ic, cmd, data);
- }
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
- (ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
- ipw_init_locked(sc, 0);
- error = 0;
+ error = ether_ioctl(ifp, cmd, data);
}
-
IPW_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
@@ -2010,13 +2087,15 @@ ipw_load_firmware(struct ipw_softc *sc, const char *fw, int size)
static int
ipw_setwepkeys(struct ipw_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
struct ipw_wep_key wepkey;
struct ieee80211_key *wk;
int error, i;
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- wk = &ic->ic_crypto.cs_nw_keys[i];
+ wk = &vap->iv_nw_keys[i];
if (wk->wk_cipher == NULL ||
wk->wk_cipher->ic_cipher != IEEE80211_CIPHER_WEP)
@@ -2166,7 +2245,8 @@ done:
static int
ipw_setchannel(struct ipw_softc *sc, struct ieee80211_channel *chan)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t data;
int error;
@@ -2178,308 +2258,126 @@ ipw_setchannel(struct ipw_softc *sc, struct ieee80211_channel *chan)
return error;
}
-static int
-ipw_config(struct ipw_softc *sc)
+static void
+ipw_assoc_task(void *context, int pending)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap = context;
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ipw_softc *sc = ifp->if_softc;
+ struct ieee80211_node *ni = vap->iv_bss;
struct ipw_security security;
- struct ipw_configuration config;
uint32_t data;
int error;
+ IPW_LOCK_DECL;
+ IPW_LOCK(sc);
error = ipw_disable(sc);
if (error != 0)
- return error;
-
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- case IEEE80211_M_HOSTAP:
- case IEEE80211_M_WDS: /* XXX */
- data = htole32(IPW_MODE_BSS);
- break;
- case IEEE80211_M_IBSS:
- case IEEE80211_M_AHDEMO:
- data = htole32(IPW_MODE_IBSS);
- break;
- case IEEE80211_M_MONITOR:
- data = htole32(IPW_MODE_MONITOR);
- break;
- }
- DPRINTF(("Setting mode to %u\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_MODE, &data, sizeof data);
- if (error != 0)
- return error;
-
- if (ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_MONITOR) {
- error = ipw_setchannel(sc, ic->ic_curchan);
- if (error != 0)
- return error;
- }
-
- if (ic->ic_opmode == IEEE80211_M_MONITOR)
- return ipw_enable(sc);
-
- IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
- DPRINTF(("Setting MAC address to %6D\n", ic->ic_myaddr, ":"));
- error = ipw_cmd(sc, IPW_CMD_SET_MAC_ADDRESS, ic->ic_myaddr,
- IEEE80211_ADDR_LEN);
- if (error != 0)
- return error;
-
- config.flags = htole32(IPW_CFG_BSS_MASK | IPW_CFG_IBSS_MASK |
- IPW_CFG_PREAMBLE_AUTO | IPW_CFG_802_1x_ENABLE);
- if (ic->ic_opmode == IEEE80211_M_IBSS)
- config.flags |= htole32(IPW_CFG_IBSS_AUTO_START);
- if (ifp->if_flags & IFF_PROMISC)
- config.flags |= htole32(IPW_CFG_PROMISCUOUS);
- config.bss_chan = htole32(0x3fff); /* channels 1-14 */
- config.ibss_chan = htole32(0x7ff); /* channels 1-11 */
- DPRINTF(("Setting configuration to 0x%x\n", le32toh(config.flags)));
- error = ipw_cmd(sc, IPW_CMD_SET_CONFIGURATION, &config, sizeof config);
- if (error != 0)
- return error;
-
- data = htole32(0x3); /* 1, 2 */
- DPRINTF(("Setting basic tx rates to 0x%x\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_BASIC_TX_RATES, &data, sizeof data);
- if (error != 0)
- return error;
-
- /* NB: use the same rate set */
- DPRINTF(("Setting msdu tx rates to 0x%x\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_MSDU_TX_RATES, &data, sizeof data);
- if (error != 0)
- return error;
-
- data = htole32(0xf); /* 1, 2, 5.5, 11 */
- DPRINTF(("Setting tx rates to 0x%x\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_TX_RATES, &data, sizeof data);
- if (error != 0)
- return error;
-
- data = htole32(IPW_POWER_MODE_CAM);
- DPRINTF(("Setting power mode to %u\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_POWER_MODE, &data, sizeof data);
- if (error != 0)
- return error;
-
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- data = htole32(32); /* default value */
- DPRINTF(("Setting tx power index to %u\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_TX_POWER_INDEX, &data,
- sizeof data);
- if (error != 0)
- return error;
- }
-
- data = htole32(ic->ic_rtsthreshold);
- DPRINTF(("Setting RTS threshold to %u\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_RTS_THRESHOLD, &data, sizeof data);
- if (error != 0)
- return error;
-
- data = htole32(ic->ic_fragthreshold);
- DPRINTF(("Setting frag threshold to %u\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_FRAG_THRESHOLD, &data, sizeof data);
- if (error != 0)
- return error;
-
- error = ipw_setssid(sc, ic->ic_des_ssid[0].ssid, ic->ic_des_ssid[0].len);
- if (error != 0)
- return error;
-
- error = ipw_setbssid(sc, NULL);
- if (error != 0)
- return error;
-
- if (ic->ic_flags & IEEE80211_F_DESBSSID) {
- DPRINTF(("Setting desired BSSID to %6D\n", ic->ic_des_bssid,
- ":"));
- error = ipw_cmd(sc, IPW_CMD_SET_DESIRED_BSSID,
- ic->ic_des_bssid, IEEE80211_ADDR_LEN);
- if (error != 0)
- return error;
- }
+ goto done;
memset(&security, 0, sizeof security);
- security.authmode = (ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED) ?
+ security.authmode = (ni->ni_authmode == IEEE80211_AUTH_SHARED) ?
IPW_AUTH_SHARED : IPW_AUTH_OPEN;
security.ciphers = htole32(IPW_CIPHER_NONE);
DPRINTF(("Setting authmode to %u\n", security.authmode));
error = ipw_cmd(sc, IPW_CMD_SET_SECURITY_INFO, &security,
sizeof security);
if (error != 0)
- return error;
-
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
- error = ipw_setwepkeys(sc);
- if (error != 0)
- return error;
-
- if (ic->ic_crypto.cs_def_txkey != IEEE80211_KEYIX_NONE) {
- data = htole32(ic->ic_crypto.cs_def_txkey);
- DPRINTF(("Setting wep tx key index to %u\n",
- le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_WEP_KEY_INDEX, &data,
- sizeof data);
- if (error != 0)
- return error;
- }
- }
-
- data = htole32((ic->ic_flags & IEEE80211_F_PRIVACY) ? IPW_WEPON : 0);
- DPRINTF(("Setting wep flags to 0x%x\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_WEP_FLAGS, &data, sizeof data);
- if (error != 0)
- return error;
-
- if (ic->ic_opt_ie != NULL) {
- error = ipw_setwpaie(sc, ic->ic_opt_ie, ic->ic_opt_ie_len);
- if (error != 0)
- return error;
- }
-
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- data = htole32(ic->ic_bintval);
- DPRINTF(("Setting beacon interval to %u\n", le32toh(data)));
- error = ipw_cmd(sc, IPW_CMD_SET_BEACON_INTERVAL, &data,
- sizeof data);
- if (error != 0)
- return error;
- }
-
- error = ipw_setscanopts(sc, 0x3fff, 0);
- if (error != 0)
- return error;
-
- return (ipw_enable(sc));
-}
-
-/*
- * Handler for sc_assoc_task. This is a simple wrapper around
- * ipw_auth_and_assoc().
- */
-static void
-ipw_assoc_task(void *context, int pending)
-{
- struct ipw_softc *sc = context;
- IPW_LOCK_DECL;
-
- IPW_LOCK(sc);
- ipw_auth_and_assoc(sc);
- IPW_UNLOCK(sc);
-}
-
-static int
-ipw_auth_and_assoc(struct ipw_softc *sc)
-{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ieee80211_node *ni = ic->ic_bss;
- struct ipw_security security;
- uint32_t data;
- int error;
+ goto done;
- error = ipw_disable(sc);
+ data = htole32(vap->iv_rtsthreshold);
+ DPRINTF(("Setting RTS threshold to %u\n", le32toh(data)));
+ error = ipw_cmd(sc, IPW_CMD_SET_RTS_THRESHOLD, &data, sizeof data);
if (error != 0)
- return (error);
+ goto done;
- memset(&security, 0, sizeof security);
- security.authmode = (ni->ni_authmode == IEEE80211_AUTH_SHARED) ?
- IPW_AUTH_SHARED : IPW_AUTH_OPEN;
- security.ciphers = htole32(IPW_CIPHER_NONE);
- DPRINTF(("Setting authmode to %u\n", security.authmode));
- error = ipw_cmd(sc, IPW_CMD_SET_SECURITY_INFO, &security,
- sizeof security);
+ data = htole32(vap->iv_fragthreshold);
+ DPRINTF(("Setting frag threshold to %u\n", le32toh(data)));
+ error = ipw_cmd(sc, IPW_CMD_SET_FRAG_THRESHOLD, &data, sizeof data);
if (error != 0)
- return (error);
+ goto done;
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ if (vap->iv_flags & IEEE80211_F_PRIVACY) {
error = ipw_setwepkeys(sc);
if (error != 0)
- return error;
+ goto done;
- if (ic->ic_crypto.cs_def_txkey != IEEE80211_KEYIX_NONE) {
- data = htole32(ic->ic_crypto.cs_def_txkey);
+ if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE) {
+ data = htole32(vap->iv_def_txkey);
DPRINTF(("Setting wep tx key index to %u\n",
le32toh(data)));
error = ipw_cmd(sc, IPW_CMD_SET_WEP_KEY_INDEX, &data,
sizeof data);
if (error != 0)
- return error;
+ goto done;
}
}
- data = htole32((ic->ic_flags & IEEE80211_F_PRIVACY) ? IPW_WEPON : 0);
+ data = htole32((vap->iv_flags & IEEE80211_F_PRIVACY) ? IPW_WEPON : 0);
DPRINTF(("Setting wep flags to 0x%x\n", le32toh(data)));
error = ipw_cmd(sc, IPW_CMD_SET_WEP_FLAGS, &data, sizeof data);
if (error != 0)
- return error;
+ goto done;
error = ipw_setssid(sc, ni->ni_essid, ni->ni_esslen);
if (error != 0)
- return (error);
+ goto done;
error = ipw_setbssid(sc, ni->ni_bssid);
if (error != 0)
- return (error);
+ goto done;
- if (ic->ic_opt_ie != NULL) {
- error = ipw_setwpaie(sc, ic->ic_opt_ie, ic->ic_opt_ie_len);
+ if (vap->iv_appie_assocreq != NULL) {
+ struct ieee80211_appie *ie = vap->iv_appie_assocreq;
+ error = ipw_setwpaie(sc, ie->ie_data, ie->ie_len);
if (error != 0)
- return error;
+ goto done;
}
if (ic->ic_opmode == IEEE80211_M_IBSS) {
error = ipw_setchannel(sc, ni->ni_chan);
if (error != 0)
- return (error);
+ goto done;
}
/* lock scan to ap's channel and enable associate */
error = ipw_setscanopts(sc,
- 1<<(ieee80211_chan2ieee(ic, ni->ni_chan)-1), 0);
+ 1<<(ieee80211_chan2ieee(ic, ni->ni_chan)-1), 0);
+ if (error != 0)
+ goto done;
- return ipw_enable(sc); /* finally, enable adapter */
+ error = ipw_enable(sc); /* finally, enable adapter */
+ if (error == 0)
+ sc->flags |= IPW_FLAG_ASSOCIATING;
+done:
+ IPW_UNLOCK(sc);
}
-/*
- * Handler for sc_disassoc_task. This is a simple wrapper around
- * ipw_disassociate().
- */
static void
ipw_disassoc_task(void *context, int pending)
{
- struct ipw_softc *sc = context;
+ struct ieee80211vap *vap = context;
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct ipw_softc *sc = ifp->if_softc;
IPW_LOCK_DECL;
IPW_LOCK(sc);
- ipw_disassociate(sc);
- IPW_UNLOCK(sc);
-}
-
-static int
-ipw_disassociate(struct ipw_softc *sc)
-{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ieee80211_node *ni = ic->ic_bss;
-
DPRINTF(("Disassociate from %6D\n", ni->ni_bssid, ":"));
-
/*
* NB: don't try to do this if ipw_stop_master has
* shutdown the firmware and disabled interrupts.
*/
- if (!(sc->flags & IPW_FLAG_FW_INITED))
- return (0);
-
- sc->flags &= ~IPW_FLAG_ASSOCIATED;
- /*
- * NB: firmware currently ignores bssid parameter, but
- * supply it in case this changes (follow linux driver).
- */
- return ipw_cmd(sc, IPW_CMD_DISASSOCIATE,
- ni->ni_bssid, IEEE80211_ADDR_LEN);
+ if (sc->flags & IPW_FLAG_FW_INITED) {
+ sc->flags &= ~IPW_FLAG_ASSOCIATED;
+ /*
+ * NB: firmware currently ignores bssid parameter, but
+ * supply it in case this changes (follow linux driver).
+ */
+ (void) ipw_cmd(sc, IPW_CMD_DISASSOCIATE,
+ ni->ni_bssid, IEEE80211_ADDR_LEN);
+ }
+ IPW_UNLOCK(sc);
}
/*
@@ -2496,27 +2394,31 @@ static void
ipw_init(void *priv)
{
struct ipw_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
IPW_LOCK_DECL;
IPW_LOCK(sc);
- ipw_init_locked(sc, 0);
+ ipw_init_locked(sc);
IPW_UNLOCK(sc);
+
+ ieee80211_start_all(ic);
}
static void
-ipw_init_locked(struct ipw_softc *sc, int force)
+ipw_init_locked(struct ipw_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
const struct firmware *fp;
const struct ipw_firmware_hdr *hdr;
- const char *imagename, *fw;
- IPW_LOCK_DECL;
+ const char *fw;
IPW_LOCK_ASSERT(sc);
DPRINTF(("%s: state %s flags 0x%x\n", __func__,
- ieee80211_state_name[ic->ic_state], sc->flags));
+ ieee80211_state_name[vap->iv_state], sc->flags));
/*
* Avoid re-entrant calls. We need to release the mutex in ipw_init()
@@ -2531,63 +2433,22 @@ ipw_init_locked(struct ipw_softc *sc, int force)
if (ipw_reset(sc) != 0) {
device_printf(sc->sc_dev, "could not reset adapter\n");
- goto fail1;
- }
-
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- imagename = "ipw_bss";
- break;
- case IEEE80211_M_IBSS:
- imagename = "ipw_ibss";
- break;
- case IEEE80211_M_MONITOR:
- imagename = "ipw_monitor";
- break;
- default:
- imagename = NULL; /* should not get there */
- }
-
- /*
- * Load firmware image using the firmware(9) subsystem. We need to
- * release the driver's lock first.
- */
- if (sc->sc_firmware == NULL || strcmp(sc->sc_firmware->name,
- imagename) != 0) {
- IPW_UNLOCK(sc);
- if (sc->sc_firmware != NULL)
- firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD);
- sc->sc_firmware = firmware_get(imagename);
- IPW_LOCK(sc);
+ goto fail;
}
if (sc->sc_firmware == NULL) {
- device_printf(sc->sc_dev,
- "could not load firmware image '%s'\n", imagename);
- goto fail1;
+ device_printf(sc->sc_dev, "no firmware\n");
+ goto fail;
}
-
+ /* NB: consistency already checked on load */
fp = sc->sc_firmware;
- if (fp->datasize < sizeof *hdr) {
- device_printf(sc->sc_dev,
- "firmware image too short %zu\n", fp->datasize);
- goto fail2;
- }
-
hdr = (const struct ipw_firmware_hdr *)fp->data;
- if (fp->datasize < sizeof *hdr + le32toh(hdr->mainsz) +
- le32toh(hdr->ucodesz)) {
- device_printf(sc->sc_dev,
- "firmware image too short %zu\n", fp->datasize);
- goto fail2;
- }
-
- DPRINTF(("Loading firmware image '%s'\n", imagename));
+ DPRINTF(("Loading firmware image '%s'\n", fp->name));
fw = (const char *)fp->data + sizeof *hdr + le32toh(hdr->mainsz);
if (ipw_load_ucode(sc, fw, le32toh(hdr->ucodesz)) != 0) {
device_printf(sc->sc_dev, "could not load microcode\n");
- goto fail2;
+ goto fail;
}
ipw_stop_master(sc);
@@ -2615,7 +2476,7 @@ ipw_init_locked(struct ipw_softc *sc, int force)
fw = (const char *)fp->data + sizeof *hdr;
if (ipw_load_firmware(sc, fw, le32toh(hdr->mainsz)) != 0) {
device_printf(sc->sc_dev, "could not load firmware\n");
- goto fail2;
+ goto fail;
}
sc->flags |= IPW_FLAG_FW_INITED;
@@ -2628,21 +2489,9 @@ ipw_init_locked(struct ipw_softc *sc, int force)
if (ipw_config(sc) != 0) {
device_printf(sc->sc_dev, "device configuration failed\n");
- goto fail1;
+ goto fail;
}
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- /*
- * NB: When restarting the adapter clock the state
- * machine regardless of the roaming mode; otherwise
- * we need to notify user apps so they can manually
- * get us going again.
- */
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL || force)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-
callout_reset(&sc->sc_wdtimer, hz, ipw_watchdog, sc);
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
@@ -2650,13 +2499,102 @@ ipw_init_locked(struct ipw_softc *sc, int force)
sc->flags &=~ IPW_FLAG_INIT_LOCKED;
return;
-fail2: firmware_put(fp, FIRMWARE_UNLOAD);
- sc->sc_firmware = NULL;
-fail1: ifp->if_flags &= ~IFF_UP;
+fail:
ipw_stop_locked(sc);
sc->flags &=~ IPW_FLAG_INIT_LOCKED;
}
+static int
+ipw_config(struct ipw_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ipw_configuration config;
+ uint32_t data;
+ int error;
+
+ error = ipw_disable(sc);
+ if (error != 0)
+ return error;
+
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_WDS: /* XXX */
+ data = htole32(IPW_MODE_BSS);
+ break;
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
+ data = htole32(IPW_MODE_IBSS);
+ break;
+ case IEEE80211_M_MONITOR:
+ data = htole32(IPW_MODE_MONITOR);
+ break;
+ }
+ DPRINTF(("Setting mode to %u\n", le32toh(data)));
+ error = ipw_cmd(sc, IPW_CMD_SET_MODE, &data, sizeof data);
+ if (error != 0)
+ return error;
+
+ if (ic->ic_opmode == IEEE80211_M_IBSS ||
+ ic->ic_opmode == IEEE80211_M_MONITOR) {
+ error = ipw_setchannel(sc, ic->ic_curchan);
+ if (error != 0)
+ return error;
+ }
+
+ if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ return ipw_enable(sc);
+
+ config.flags = htole32(IPW_CFG_BSS_MASK | IPW_CFG_IBSS_MASK |
+ IPW_CFG_PREAMBLE_AUTO | IPW_CFG_802_1x_ENABLE);
+ if (ic->ic_opmode == IEEE80211_M_IBSS)
+ config.flags |= htole32(IPW_CFG_IBSS_AUTO_START);
+ if (ifp->if_flags & IFF_PROMISC)
+ config.flags |= htole32(IPW_CFG_PROMISCUOUS);
+ config.bss_chan = htole32(0x3fff); /* channels 1-14 */
+ config.ibss_chan = htole32(0x7ff); /* channels 1-11 */
+ DPRINTF(("Setting configuration to 0x%x\n", le32toh(config.flags)));
+ error = ipw_cmd(sc, IPW_CMD_SET_CONFIGURATION, &config, sizeof config);
+ if (error != 0)
+ return error;
+
+ data = htole32(0x3); /* 1, 2 */
+ DPRINTF(("Setting basic tx rates to 0x%x\n", le32toh(data)));
+ error = ipw_cmd(sc, IPW_CMD_SET_BASIC_TX_RATES, &data, sizeof data);
+ if (error != 0)
+ return error;
+
+ /* NB: use the same rate set */
+ DPRINTF(("Setting msdu tx rates to 0x%x\n", le32toh(data)));
+ error = ipw_cmd(sc, IPW_CMD_SET_MSDU_TX_RATES, &data, sizeof data);
+ if (error != 0)
+ return error;
+
+ data = htole32(0xf); /* 1, 2, 5.5, 11 */
+ DPRINTF(("Setting tx rates to 0x%x\n", le32toh(data)));
+ error = ipw_cmd(sc, IPW_CMD_SET_TX_RATES, &data, sizeof data);
+ if (error != 0)
+ return error;
+
+ data = htole32(IPW_POWER_MODE_CAM);
+ DPRINTF(("Setting power mode to %u\n", le32toh(data)));
+ error = ipw_cmd(sc, IPW_CMD_SET_POWER_MODE, &data, sizeof data);
+ if (error != 0)
+ return error;
+
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ data = htole32(32); /* default value */
+ DPRINTF(("Setting tx power index to %u\n", le32toh(data)));
+ error = ipw_cmd(sc, IPW_CMD_SET_TX_POWER_INDEX, &data,
+ sizeof data);
+ if (error != 0)
+ return error;
+ }
+
+ return 0;
+}
+
static void
ipw_stop(void *priv)
{
@@ -2671,14 +2609,11 @@ ipw_stop(void *priv)
static void
ipw_stop_locked(struct ipw_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
int i;
IPW_LOCK_ASSERT(sc);
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
-
callout_stop(&sc->sc_wdtimer);
ipw_stop_master(sc);
@@ -2795,7 +2730,7 @@ ipw_scan_start(struct ieee80211com *ic)
IPW_LOCK(sc);
if (!(sc->flags & IPW_FLAG_SCANNING))
- taskqueue_enqueue_fast(taskqueue_fast, &sc->sc_scan_task);
+ taskqueue_enqueue(taskqueue_swi, &sc->sc_scan_task);
IPW_UNLOCK(sc);
}
@@ -2816,13 +2751,13 @@ ipw_set_channel(struct ieee80211com *ic)
}
static void
-ipw_scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
+ipw_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
/* NB: all channels are scanned at once */
}
static void
-ipw_scan_mindwell(struct ieee80211com *ic)
+ipw_scan_mindwell(struct ieee80211_scan_state *ss)
{
/* NB: don't try to abort scan; wait for firmware to finish */
}
diff --git a/sys/dev/ipw/if_ipwvar.h b/sys/dev/ipw/if_ipwvar.h
index f6ec999..de35019 100644
--- a/sys/dev/ipw/if_ipwvar.h
+++ b/sys/dev/ipw/if_ipwvar.h
@@ -76,30 +76,40 @@ struct ipw_tx_radiotap_header {
((1 << IEEE80211_RADIOTAP_FLAGS) | \
(1 << IEEE80211_RADIOTAP_CHANNEL))
+struct ipw_vap {
+ struct ieee80211vap vap;
+ struct task assoc_task;
+ struct task disassoc_task;
+ struct task assoc_success_task;
+ struct task assoc_failed_task;
+ struct task scandone_task;
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define IPW_VAP(vap) ((struct ipw_vap *)(vap))
+
struct ipw_softc {
struct ifnet *sc_ifp;
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
device_t sc_dev;
struct mtx sc_mtx;
struct task sc_init_task;
struct task sc_scan_task;
struct task sc_chan_task;
- struct task sc_assoc_task;
- struct task sc_disassoc_task;
+ struct task sc_bmiss_task;
struct callout sc_wdtimer; /* watchdog timer */
uint32_t flags;
-#define IPW_FLAG_FW_INITED (1 << 0)
-#define IPW_FLAG_INIT_LOCKED (1 << 1)
-#define IPW_FLAG_HAS_RADIO_SWITCH (1 << 2)
-#define IPW_FLAG_HACK (1 << 3)
-#define IPW_FLAG_SCANNING (1 << 4)
-#define IPW_FLAG_ENABLED (1 << 5)
-#define IPW_FLAG_BUSY (1 << 6)
-#define IPW_FLAG_ASSOCIATED (1 << 7)
+#define IPW_FLAG_FW_INITED 0x0001
+#define IPW_FLAG_INIT_LOCKED 0x0002
+#define IPW_FLAG_HAS_RADIO_SWITCH 0x0004
+#define IPW_FLAG_HACK 0x0008
+#define IPW_FLAG_SCANNING 0x0010
+#define IPW_FLAG_ENABLED 0x0020
+#define IPW_FLAG_BUSY 0x0040
+#define IPW_FLAG_ASSOCIATING 0x0080
+#define IPW_FLAG_ASSOCIATED 0x0100
int irq_rid;
int mem_rid;
@@ -152,22 +162,10 @@ struct ipw_softc {
uint32_t rxcur;
int txfree;
- int dwelltime;
-
- struct bpf_if *sc_drvbpf;
-
- union {
- struct ipw_rx_radiotap_header th;
- uint8_t pad[64];
- } sc_rxtapu;
-#define sc_rxtap sc_rxtapu.th
+ struct ipw_rx_radiotap_header sc_rxtap;
int sc_rxtap_len;
- union {
- struct ipw_tx_radiotap_header th;
- uint8_t pad[64];
- } sc_txtapu;
-#define sc_txtap sc_txtapu.th
+ struct ipw_tx_radiotap_header sc_txtap;
int sc_txtap_len;
};
diff --git a/sys/dev/iwi/if_iwi.c b/sys/dev/iwi/if_iwi.c
index 97dad68..1ddb59b 100644
--- a/sys/dev/iwi/if_iwi.c
+++ b/sys/dev/iwi/if_iwi.c
@@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_input.h>
#include <net80211/ieee80211_regdomain.h>
#include <netinet/in.h>
@@ -90,6 +91,14 @@ __FBSDID("$FreeBSD$");
#define DPRINTFN(n, x) do { if (iwi_debug >= (n)) printf x; } while (0)
int iwi_debug = 0;
SYSCTL_INT(_debug, OID_AUTO, iwi, CTLFLAG_RW, &iwi_debug, 0, "iwi debug level");
+
+static const char *iwi_fw_states[] = {
+ "IDLE", /* IWI_FW_IDLE */
+ "LOADING", /* IWI_FW_LOADING */
+ "ASSOCIATING", /* IWI_FW_ASSOCIATING */
+ "DISASSOCIATING", /* IWI_FW_DISASSOCIATING */
+ "SCANNING", /* IWI_FW_SCANNING */
+};
#else
#define DPRINTF(x)
#define DPRINTFN(n, x)
@@ -120,6 +129,11 @@ static const struct iwi_ident iwi_ident_table[] = {
{ 0, 0, NULL }
};
+static struct ieee80211vap *iwi_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void iwi_vap_delete(struct ieee80211vap *);
static void iwi_dma_map_addr(void *, bus_dma_segment_t *, int, int);
static int iwi_alloc_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *,
int);
@@ -135,15 +149,17 @@ static void iwi_reset_rx_ring(struct iwi_softc *, struct iwi_rx_ring *);
static void iwi_free_rx_ring(struct iwi_softc *, struct iwi_rx_ring *);
static struct ieee80211_node *iwi_node_alloc(struct ieee80211_node_table *);
static void iwi_node_free(struct ieee80211_node *);
-static int iwi_media_change(struct ifnet *);
static void iwi_media_status(struct ifnet *, struct ifmediareq *);
-static int iwi_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static int iwi_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static void iwi_wme_init(struct iwi_softc *);
-static int iwi_wme_setparams(struct iwi_softc *);
+static int iwi_wme_setparams(struct iwi_softc *, struct ieee80211com *);
static int iwi_wme_update(struct ieee80211com *);
static uint16_t iwi_read_prom_word(struct iwi_softc *, uint8_t);
static void iwi_frame_intr(struct iwi_softc *, struct iwi_rx_data *, int,
struct iwi_frame *);
+static void iwi_authsuccess(void *, int);
+static void iwi_assocsuccess(void *, int);
+static void iwi_assocfailed(void *, int);
static void iwi_notification_intr(struct iwi_softc *, struct iwi_notif *);
static void iwi_rx_intr(struct iwi_softc *);
static void iwi_tx_intr(struct iwi_softc *, struct iwi_tx_ring *);
@@ -152,6 +168,9 @@ static int iwi_cmd(struct iwi_softc *, uint8_t, void *, uint8_t);
static void iwi_write_ibssnode(struct iwi_softc *, const u_int8_t [], int);
static int iwi_tx_start(struct ifnet *, struct mbuf *,
struct ieee80211_node *, int);
+static int iwi_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static void iwi_start_locked(struct ifnet *);
static void iwi_start(struct ifnet *);
static void iwi_watchdog(void *);
static int iwi_ioctl(struct ifnet *, u_long, caddr_t);
@@ -161,27 +180,27 @@ static int iwi_load_ucode(struct iwi_softc *, const struct iwi_fw *);
static int iwi_load_firmware(struct iwi_softc *, const struct iwi_fw *);
static void iwi_release_fw_dma(struct iwi_softc *sc);
static int iwi_config(struct iwi_softc *);
-static int iwi_get_firmware(struct iwi_softc *);
+static int iwi_get_firmware(struct iwi_softc *, enum ieee80211_opmode);
static void iwi_put_firmware(struct iwi_softc *);
static int iwi_scanchan(struct iwi_softc *, unsigned long, int);
static void iwi_scan_start(struct ieee80211com *);
static void iwi_scan_end(struct ieee80211com *);
static void iwi_scanabort(void *, int);
static void iwi_set_channel(struct ieee80211com *);
-static void iwi_scan_curchan(struct ieee80211com *, unsigned long maxdwell);
+static void iwi_scan_curchan(struct ieee80211_scan_state *, unsigned long maxdwell);
#if 0
static void iwi_scan_allchan(struct ieee80211com *, unsigned long maxdwell);
#endif
-static void iwi_scan_mindwell(struct ieee80211com *);
-static void iwi_assoc(struct ieee80211com *ic);
-static void iwi_disassoc(struct ieee80211com *);
+static void iwi_scan_mindwell(struct ieee80211_scan_state *);
static void iwi_ops(void *, int);
-static int iwi_queue_cmd(struct iwi_softc *, int);
-static int iwi_auth_and_assoc(struct iwi_softc *);
+static int iwi_queue_cmd(struct iwi_softc *, int, unsigned long);
+static int iwi_auth_and_assoc(struct iwi_softc *, struct ieee80211vap *);
static int iwi_disassociate(struct iwi_softc *, int quiet);
+static void iwi_init_locked(struct iwi_softc *);
static void iwi_init(void *);
-static void iwi_init_locked(void *, int);
-static void iwi_stop(void *);
+static int iwi_init_fw_dma(struct iwi_softc *, int);
+static void iwi_stop_locked(void *);
+static void iwi_stop(struct iwi_softc *);
static void iwi_restart(void *, int);
static int iwi_getrfkill(struct iwi_softc *);
static void iwi_radio_on(void *, int);
@@ -256,18 +275,25 @@ iwi_attach(device_t dev)
{
struct iwi_softc *sc = device_get_softc(dev);
struct ifnet *ifp;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
uint16_t val;
- int i, error, bands;
+ int i, error;
+ uint8_t bands;
sc->sc_dev = dev;
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(dev, "can not if_alloc()\n");
+ return ENXIO;
+ }
+ ic = ifp->if_l2com;
+
IWI_LOCK_INIT(sc);
IWI_CMD_LOCK_INIT(sc);
sc->sc_unr = new_unrhdr(1, IWI_MAX_IBSSNODE-1, &sc->sc_mtx);
-#if __FreeBSD_version >= 700000
sc->sc_tq = taskqueue_create("iwi_taskq", M_NOWAIT | M_ZERO,
taskqueue_thread_enqueue, &sc->sc_tq);
taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq",
@@ -276,22 +302,14 @@ iwi_attach(device_t dev)
taskqueue_thread_enqueue, &sc->sc_tq2);
taskqueue_start_threads(&sc->sc_tq2, 1, PI_NET, "%s taskq2",
device_get_nameunit(dev));
-#else
- sc->sc_tq = taskqueue_create("iwi_taskq", M_NOWAIT | M_ZERO,
- taskqueue_thread_enqueue, &sc->sc_tq, &sc->sc_tqproc);
- kproc_create(taskqueue_thread_loop, &sc->sc_tq, &sc->sc_tqproc,
- 0, 0, "%s taskq", device_get_nameunit(dev));
- sc->sc_tq2 = taskqueue_create("iwi_taskq2", M_NOWAIT | M_ZERO,
- taskqueue_thread_enqueue, &sc->sc_tq2, &sc->sc_tqproc);
- kproc_create(taskqueue_thread_loop, &sc->sc_tq2, &sc->sc_tqproc,
- 0, 0, "%s taskq2", device_get_nameunit(dev));
-#endif
+
TASK_INIT(&sc->sc_radiontask, 0, iwi_radio_on, sc);
TASK_INIT(&sc->sc_radiofftask, 0, iwi_radio_off, sc);
TASK_INIT(&sc->sc_restarttask, 0, iwi_restart, sc);
TASK_INIT(&sc->sc_opstask, 0, iwi_ops, sc);
TASK_INIT(&sc->sc_scanaborttask, 0, iwi_scanabort, sc);
callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0);
+ callout_init_mtx(&sc->sc_rftimer, &sc->sc_mtx, 0);
if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
device_printf(dev, "chip is in D%d power mode "
@@ -354,12 +372,6 @@ iwi_attach(device_t dev)
iwi_wme_init(sc);
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
- if (ifp == NULL) {
- device_printf(dev, "can not if_alloc()\n");
- goto fail;
- }
- ic->ic_ifp = ifp;
ifp->if_softc = sc;
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
@@ -370,10 +382,9 @@ iwi_attach(device_t dev)
ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
IFQ_SET_READY(&ifp->if_snd);
- ic->ic_wme.wme_update = iwi_wme_update;
+ ic->ic_ifp = ifp;
+ ic->ic_opmode = IEEE80211_M_STA;
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
- ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
- ic->ic_state = IEEE80211_S_INIT;
/* set device capabilities */
ic->ic_caps =
@@ -383,7 +394,9 @@ iwi_attach(device_t dev)
| IEEE80211_C_SHPREAMBLE /* short preamble supported */
| IEEE80211_C_WPA /* 802.11i */
| IEEE80211_C_WME /* 802.11e */
+#if 0
| IEEE80211_C_BGSCAN /* capable of bg scanning */
+#endif
;
/* read MAC address from EEPROM */
@@ -402,28 +415,26 @@ iwi_attach(device_t dev)
setbit(&bands, IEEE80211_MODE_11G);
if (pci_get_device(dev) >= 0x4223)
setbit(&bands, IEEE80211_MODE_11A);
- ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
+ ieee80211_init_channels(ic, NULL, &bands);
ieee80211_ifattach(ic);
- ic->ic_bmissthreshold = 10; /* override default */
/* override default methods */
ic->ic_node_alloc = iwi_node_alloc;
sc->sc_node_free = ic->ic_node_free;
ic->ic_node_free = iwi_node_free;
+ ic->ic_raw_xmit = iwi_raw_xmit;
ic->ic_scan_start = iwi_scan_start;
ic->ic_scan_end = iwi_scan_end;
ic->ic_set_channel = iwi_set_channel;
ic->ic_scan_curchan = iwi_scan_curchan;
ic->ic_scan_mindwell = iwi_scan_mindwell;
+ ic->ic_wme.wme_update = iwi_wme_update;
- /* override state transition machine */
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = iwi_newstate;
- ieee80211_media_init(ic, iwi_media_change, iwi_media_status);
+ ic->ic_vap_create = iwi_vap_create;
+ ic->ic_vap_delete = iwi_vap_delete;
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap),
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap));
sc->sc_rxtap_len = sizeof sc->sc_rxtap;
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
@@ -450,8 +461,9 @@ iwi_attach(device_t dev)
ieee80211_announce(ic);
return 0;
-
-fail: iwi_detach(dev);
+fail:
+ /* XXX fix */
+ iwi_detach(dev);
return ENXIO;
}
@@ -459,19 +471,18 @@ static int
iwi_detach(device_t dev)
{
struct iwi_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
- IWI_LOCK_DECL;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
- if (ifp != NULL) {
- IWI_LOCK(sc);
- iwi_stop(sc);
- IWI_UNLOCK(sc);
- bpfdetach(ifp);
- ieee80211_ifdetach(ic);
- }
+ iwi_stop(sc);
+
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+
+ /* NB: do early to drain any pending tasks */
+ taskqueue_free(sc->sc_tq);
+ taskqueue_free(sc->sc_tq2);
- callout_drain(&sc->sc_wdtimer);
iwi_put_firmware(sc);
iwi_release_fw_dma(sc);
@@ -482,29 +493,81 @@ iwi_detach(device_t dev)
iwi_free_tx_ring(sc, &sc->txq[3]);
iwi_free_rx_ring(sc, &sc->rxq);
- if (sc->irq != NULL) {
- bus_teardown_intr(dev, sc->irq, sc->sc_ih);
- bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
- }
-
- if (sc->mem != NULL)
- bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
-
- if (ifp != NULL)
- if_free(ifp);
+ bus_teardown_intr(dev, sc->irq, sc->sc_ih);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
- taskqueue_free(sc->sc_tq);
- taskqueue_free(sc->sc_tq2);
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
- if (sc->sc_unr != NULL)
- delete_unrhdr(sc->sc_unr);
+ delete_unrhdr(sc->sc_unr);
IWI_LOCK_DESTROY(sc);
IWI_CMD_LOCK_DESTROY(sc);
+ if_free(ifp);
+
return 0;
}
+static struct ieee80211vap *
+iwi_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct iwi_softc *sc = ifp->if_softc;
+ struct iwi_vap *ivp;
+ struct ieee80211vap *vap;
+ int i;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ /*
+ * Get firmware image (and possibly dma memory) on mode change.
+ */
+ if (iwi_get_firmware(sc, opmode))
+ return NULL;
+ /* allocate DMA memory for mapping firmware image */
+ i = sc->fw_fw.size;
+ if (sc->fw_boot.size > i)
+ i = sc->fw_boot.size;
+ /* XXX do we dma the ucode as well ? */
+ if (sc->fw_uc.size > i)
+ i = sc->fw_uc.size;
+ if (iwi_init_fw_dma(sc, i))
+ return NULL;
+
+ ivp = (struct iwi_vap *) malloc(sizeof(struct iwi_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (ivp == NULL)
+ return NULL;
+ vap = &ivp->iwi_vap;
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
+ /* override the default, the setting comes from the linux driver */
+ vap->iv_bmissthreshold = 24;
+ /* override with driver methods */
+ ivp->iwi_newstate = vap->iv_newstate;
+ vap->iv_newstate = iwi_newstate;
+
+ TASK_INIT(&ivp->iwi_authsuccess_task, 0, iwi_authsuccess, vap);
+ TASK_INIT(&ivp->iwi_assocsuccess_task, 0, iwi_assocsuccess, vap);
+ TASK_INIT(&ivp->iwi_assocfailed_task, 0, iwi_assocfailed, vap);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, iwi_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+iwi_vap_delete(struct ieee80211vap *vap)
+{
+ struct iwi_vap *ivp = IWI_VAP(vap);
+
+ ieee80211_vap_detach(vap);
+ free(ivp, M_80211_VAP);
+}
+
static void
iwi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
@@ -807,11 +870,8 @@ static int
iwi_shutdown(device_t dev)
{
struct iwi_softc *sc = device_get_softc(dev);
- IWI_LOCK_DECL;
- IWI_LOCK(sc);
iwi_stop(sc);
- IWI_UNLOCK(sc);
iwi_put_firmware(sc); /* ??? XXX */
return 0;
@@ -821,11 +881,8 @@ static int
iwi_suspend(device_t dev)
{
struct iwi_softc *sc = device_get_softc(dev);
- IWI_LOCK_DECL;
- IWI_LOCK(sc);
iwi_stop(sc);
- IWI_UNLOCK(sc);
return 0;
}
@@ -834,20 +891,12 @@ static int
iwi_resume(device_t dev)
{
struct iwi_softc *sc = device_get_softc(dev);
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
- IWI_LOCK_DECL;
-
- IWI_LOCK(sc);
+ struct ifnet *ifp = sc->sc_ifp;
pci_write_config(dev, 0x41, 0, 1);
- if (ifp->if_flags & IFF_UP) {
- ifp->if_init(ifp->if_softc);
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- ifp->if_start(ifp);
- }
-
- IWI_UNLOCK(sc);
+ if (ifp->if_flags & IFF_UP)
+ iwi_init(sc);
return 0;
}
@@ -882,25 +931,6 @@ iwi_node_free(struct ieee80211_node *ni)
sc->sc_node_free(ni);
}
-static int
-iwi_media_change(struct ifnet *ifp)
-{
- struct iwi_softc *sc = ifp->if_softc;
- int error;
- IWI_LOCK_DECL;
-
- IWI_LOCK(sc);
-
- error = ieee80211_media_change(ifp);
- if (error == ENETRESET &&
- (ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
- iwi_init_locked(sc, 0);
-
- IWI_UNLOCK(sc);
-
- return error;
-}
-
/*
* Convert h/w rate code to IEEE rate code.
*/
@@ -931,43 +961,47 @@ iwi_cvtrate(int iwirate)
static void
iwi_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
- struct iwi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int rate;
-
- imr->ifm_status = IFM_AVALID;
- imr->ifm_active = IFM_IEEE80211;
- if (ic->ic_state == IEEE80211_S_RUN)
- imr->ifm_status |= IFM_ACTIVE;
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct iwi_softc *sc = ic->ic_ifp->if_softc;
/* read current transmission rate from adapter */
- rate = iwi_cvtrate(CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE));
- imr->ifm_active |= ieee80211_rate2media(ic, rate, ic->ic_curmode);
-
- if (ic->ic_opmode == IEEE80211_M_IBSS)
- imr->ifm_active |= IFM_IEEE80211_ADHOC;
- else if (ic->ic_opmode == IEEE80211_M_MONITOR)
- imr->ifm_active |= IFM_IEEE80211_MONITOR;
+ vap->iv_bss->ni_txrate =
+ iwi_cvtrate(CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE));
+ ieee80211_media_status(ifp, imr);
}
static int
-iwi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+iwi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct iwi_vap *ivp = IWI_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
struct ifnet *ifp = ic->ic_ifp;
struct iwi_softc *sc = ifp->if_softc;
- int error = 0;
+ IWI_LOCK_DECL;
DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__,
- ieee80211_state_name[ic->ic_state],
+ ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate], sc->flags));
- /* XXX state change race with taskqueue */
switch (nstate) {
- case IEEE80211_S_AUTH:
- iwi_assoc(ic);
+ case IEEE80211_S_INIT:
+ IWI_LOCK(sc);
+ /*
+ * NB: don't try to do this if iwi_stop_master has
+ * shutdown the firmware and disabled interrupts.
+ */
+ if (vap->iv_state == IEEE80211_S_RUN &&
+ (sc->flags & IWI_FLAG_FW_INITED))
+ iwi_queue_cmd(sc, IWI_DISASSOC, 1);
+ IWI_UNLOCK(sc);
break;
+ case IEEE80211_S_AUTH:
+ iwi_queue_cmd(sc, IWI_AUTH, arg);
+ return EINPROGRESS;
case IEEE80211_S_RUN:
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS &&
+ vap->iv_state == IEEE80211_S_SCAN) {
/*
* XXX when joining an ibss network we are called
* with a SCAN -> RUN transition on scan complete.
@@ -976,35 +1010,24 @@ iwi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
* AUTH -> RUN transition and we want to do nothing.
* This is all totally bogus and needs to be redone.
*/
- if (ic->ic_state == IEEE80211_S_SCAN)
- iwi_assoc(ic);
- }
- break;
- case IEEE80211_S_INIT:
- /*
- * NB: don't try to do this if iwi_stop_master has
- * shutdown the firmware and disabled interrupts.
- */
- if (ic->ic_state == IEEE80211_S_RUN &&
- (sc->flags & IWI_FLAG_FW_INITED))
- iwi_disassoc(ic);
- if (ic->ic_state == IEEE80211_S_SCAN &&
- (sc->fw_state == IWI_FW_SCANNING))
- ieee80211_cancel_scan(ic);
+ iwi_queue_cmd(sc, IWI_ASSOC, 0);
+ return EINPROGRESS;
+ }
break;
case IEEE80211_S_ASSOC:
/*
- * If we are not transitioning from AUTH the resend the
- * association request.
+ * If we are transitioning from AUTH then just wait
+ * for the ASSOC status to come back from the firmware.
+ * Otherwise we need to issue the association request.
*/
- if (ic->ic_state != IEEE80211_S_AUTH)
- iwi_assoc(ic);
- break;
+ if (vap->iv_state == IEEE80211_S_AUTH)
+ break;
+ iwi_queue_cmd(sc, IWI_ASSOC, arg);
+ return EINPROGRESS;
default:
break;
}
- return (error != 0) ? error : sc->sc_newstate(ic, nstate, arg);
-
+ return ivp->iwi_newstate(vap, nstate, arg);
}
/*
@@ -1055,9 +1078,8 @@ iwi_wme_init(struct iwi_softc *sc)
}
static int
-iwi_wme_setparams(struct iwi_softc *sc)
+iwi_wme_setparams(struct iwi_softc *sc, struct ieee80211com *ic)
{
- struct ieee80211com *ic = &sc->sc_ic;
const struct wmeParams *wmep;
int ac;
@@ -1090,7 +1112,7 @@ iwi_wme_update(struct ieee80211com *ic)
* will get sent down to the adapter as part of the
* work iwi_auth_and_assoc does.
*/
- return (iwi_queue_cmd(sc, IWI_SET_WME));
+ return iwi_queue_cmd(sc, IWI_SET_WME, 0);
}
static int
@@ -1171,7 +1193,8 @@ iwi_read_prom_word(struct iwi_softc *sc, uint8_t addr)
static void
iwi_setcurchan(struct iwi_softc *sc, int chan)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
sc->curchan = chan;
@@ -1185,8 +1208,8 @@ static void
iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i,
struct iwi_frame *frame)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct mbuf *mnew, *m;
struct ieee80211_node *ni;
int type, error, framelen;
@@ -1261,7 +1284,7 @@ iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i,
m_adj(m, sizeof (struct iwi_hdr) + sizeof (struct iwi_frame));
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct iwi_rx_radiotap_header *tap = &sc->sc_rxtap;
tap->wr_flags = 0;
@@ -1269,17 +1292,16 @@ iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i,
tap->wr_antsignal = frame->signal;
tap->wr_antenna = frame->antenna;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
}
IWI_UNLOCK(sc);
ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
-
- /* send the frame to the 802.11 layer */
- type = ieee80211_input(ic, m, ni, frame->rssi_dbm, 0, 0);
-
- /* node is no longer needed */
- ieee80211_free_node(ni);
+ if (ni != NULL) {
+ type = ieee80211_input(ni, m, frame->rssi_dbm, 0, 0);
+ ieee80211_free_node(ni);
+ } else
+ type = ieee80211_input_all(ic, m, frame->rssi_dbm, 0, 0);
IWI_LOCK(sc);
if (sc->sc_softled) {
@@ -1297,30 +1319,6 @@ iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i,
}
}
-/* unaligned little endian access */
-#define LE_READ_2(p) \
- ((u_int16_t) \
- ((((const u_int8_t *)(p))[0] ) | \
- (((const u_int8_t *)(p))[1] << 8)))
-#define LE_READ_4(p) \
- ((u_int32_t) \
- ((((const u_int8_t *)(p))[0] ) | \
- (((const u_int8_t *)(p))[1] << 8) | \
- (((const u_int8_t *)(p))[2] << 16) | \
- (((const u_int8_t *)(p))[3] << 24)))
-
-#define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \
- if ((_len) < (_minlen)) { \
- return; \
- } \
-} while (0)
-
-static int __inline
-iswmeoui(const u_int8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
-}
-
/*
* Check for an association response frame to see if QoS
* has been negotiated. We parse just enough to figure
@@ -1330,7 +1328,8 @@ iswmeoui(const u_int8_t *frm)
* done in the driver.
*/
static void
-iwi_checkforqos(struct iwi_softc *sc, const struct ieee80211_frame *wh, int len)
+iwi_checkforqos(struct ieee80211vap *vap,
+ const struct ieee80211_frame *wh, int len)
{
#define SUBTYPE(wh) ((wh)->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
const uint8_t *frm, *efrm, *wme;
@@ -1362,7 +1361,7 @@ iwi_checkforqos(struct iwi_softc *sc, const struct ieee80211_frame *wh, int len)
wme = NULL;
while (frm < efrm) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1]);
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1], return);
switch (*frm) {
case IEEE80211_ELEMID_VENDOR:
if (iswmeoui(frm))
@@ -1372,7 +1371,7 @@ iwi_checkforqos(struct iwi_softc *sc, const struct ieee80211_frame *wh, int len)
frm += frm[1] + 2;
}
- ni = sc->sc_ic.ic_bss;
+ ni = vap->iv_bss;
ni->ni_capinfo = capinfo;
ni->ni_associd = associd;
if (wme != NULL)
@@ -1382,10 +1381,40 @@ iwi_checkforqos(struct iwi_softc *sc, const struct ieee80211_frame *wh, int len)
#undef SUBTYPE
}
+/*
+ * Task queue callbacks for iwi_notification_intr used to avoid LOR's.
+ */
+
+static void
+iwi_authsuccess(void *arg, int npending)
+{
+ struct ieee80211vap *vap = arg;
+
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, -1);
+}
+
+static void
+iwi_assocsuccess(void *arg, int npending)
+{
+ struct ieee80211vap *vap = arg;
+
+ ieee80211_new_state(vap, IEEE80211_S_RUN, -1);
+}
+
+static void
+iwi_assocfailed(void *arg, int npending)
+{
+ struct ieee80211vap *vap = arg;
+
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
+}
+
static void
iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
struct iwi_notif_scan_channel *chan;
struct iwi_notif_scan_complete *scan;
struct iwi_notif_authentication *auth;
@@ -1411,85 +1440,95 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
IWI_STATE_END(sc, IWI_FW_SCANNING);
- if (scan->status == IWI_SCAN_COMPLETED)
- ieee80211_scan_next(ic);
-
+ if (scan->status == IWI_SCAN_COMPLETED) {
+ /* NB: don't need to defer, net80211 does it for us */
+ ieee80211_scan_next(vap);
+ }
break;
case IWI_NOTIF_TYPE_AUTHENTICATION:
auth = (struct iwi_notif_authentication *)(notif + 1);
-
switch (auth->state) {
case IWI_AUTH_SUCCESS:
DPRINTFN(2, ("Authentication succeeeded\n"));
- ieee80211_node_authorize(ic->ic_bss);
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
+ taskqueue_enqueue(taskqueue_swi,
+ &IWI_VAP(vap)->iwi_authsuccess_task);
break;
-
case IWI_AUTH_FAIL:
- DPRINTFN(2, ("Authentication failed\n"));
+ /*
+ * These are delivered as an unsolicited deauth
+ * (e.g. due to inactivity) or in response to an
+ * associate request.
+ */
sc->flags &= ~IWI_FLAG_ASSOCIATED;
- IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
- /* XXX */
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ DPRINTFN(2, ("Authentication failed\n"));
+ vap->iv_stats.is_rx_auth_fail++;
+ IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
+ } else {
+ DPRINTFN(2, ("Deauthenticated\n"));
+ vap->iv_stats.is_rx_deauth++;
+ }
+ taskqueue_enqueue(taskqueue_swi,
+ &IWI_VAP(vap)->iwi_assocfailed_task);
break;
-
case IWI_AUTH_SENT_1:
case IWI_AUTH_RECV_2:
case IWI_AUTH_SEQ1_PASS:
break;
-
case IWI_AUTH_SEQ1_FAIL:
DPRINTFN(2, ("Initial authentication handshake failed; "
"you probably need shared key\n"));
+ vap->iv_stats.is_rx_auth_fail++;
IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
/* XXX retry shared key when in auto */
break;
-
default:
device_printf(sc->sc_dev,
"unknown authentication state %u\n", auth->state);
+ break;
}
break;
case IWI_NOTIF_TYPE_ASSOCIATION:
assoc = (struct iwi_notif_association *)(notif + 1);
-
switch (assoc->state) {
case IWI_AUTH_SUCCESS:
/* re-association, do nothing */
break;
-
case IWI_ASSOC_SUCCESS:
DPRINTFN(2, ("Association succeeded\n"));
sc->flags |= IWI_FLAG_ASSOCIATED;
IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
- iwi_checkforqos(sc,
+ iwi_checkforqos(vap,
(const struct ieee80211_frame *)(assoc+1),
le16toh(notif->len) - sizeof(*assoc));
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ taskqueue_enqueue(taskqueue_swi,
+ &IWI_VAP(vap)->iwi_assocsuccess_task);
break;
-
case IWI_ASSOC_INIT:
+ sc->flags &= ~IWI_FLAG_ASSOCIATED;
switch (sc->fw_state) {
- case IWI_FW_ASSOCIATING:
- DPRINTFN(2, ("Association failed\n"));
- IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
- ieee80211_new_state(ic,
- IEEE80211_S_SCAN, -1);
- break;
+ case IWI_FW_ASSOCIATING:
+ DPRINTFN(2, ("Association failed\n"));
+ IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
+ taskqueue_enqueue(taskqueue_swi,
+ &IWI_VAP(vap)->iwi_assocfailed_task);
+ break;
- case IWI_FW_DISASSOCIATING:
- DPRINTFN(2, ("Dissassociated\n"));
- IWI_STATE_END(sc,
- IWI_FW_DISASSOCIATING);
- break;
+ case IWI_FW_DISASSOCIATING:
+ DPRINTFN(2, ("Dissassociated\n"));
+ IWI_STATE_END(sc, IWI_FW_DISASSOCIATING);
+ vap->iv_stats.is_rx_disassoc++;
+ taskqueue_enqueue(taskqueue_swi,
+ &IWI_VAP(vap)->iwi_assocfailed_task);
+ break;
}
- sc->flags &= ~IWI_FLAG_ASSOCIATED;
break;
-
default:
device_printf(sc->sc_dev,
"unknown association state %u\n", assoc->state);
+ break;
}
break;
@@ -1508,11 +1547,20 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
* 802.11 layer.
* XXX try to roam, drop assoc only on much higher count
*/
- if (le32toh(beacon->number) >= ic->ic_bmissthreshold) {
+ if (le32toh(beacon->number) >= vap->iv_bmissthreshold) {
DPRINTF(("Beacon miss: %u >= %u\n",
le32toh(beacon->number),
- ic->ic_bmissthreshold));
- ieee80211_beacon_miss(ic);
+ vap->iv_bmissthreshold));
+ vap->iv_stats.is_beacon_miss++;
+ /*
+ * It's pointless to notify the 802.11 layer
+ * as it'll try to send a probe request (which
+ * we'll discard) and then timeout and drop us
+ * into scan state. Instead tell the firmware
+ * to disassociate and then on completion we'll
+ * kick the state machine to scan.
+ */
+ iwi_queue_cmd(sc, IWI_DISASSOC, 1);
}
}
break;
@@ -1526,6 +1574,7 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
default:
DPRINTF(("unknown notification type %u flags 0x%x len %u\n",
notif->type, notif->flags, le16toh(notif->len)));
+ break;
}
}
@@ -1575,8 +1624,7 @@ iwi_rx_intr(struct iwi_softc *sc)
static void
iwi_tx_intr(struct iwi_softc *sc, struct iwi_tx_ring *txq)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
struct iwi_tx_data *data;
uint32_t hw;
@@ -1609,7 +1657,7 @@ iwi_tx_intr(struct iwi_softc *sc, struct iwi_tx_ring *txq)
if (sc->sc_softled)
iwi_led_event(sc, IWI_LED_TX);
- iwi_start(ifp);
+ iwi_start_locked(ifp);
}
static void
@@ -1631,9 +1679,7 @@ iwi_intr(void *arg)
if (r & IWI_INTR_FATAL_ERROR) {
device_printf(sc->sc_dev, "firmware error\n");
- /* don't restart if the interface isn't up */
- if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING)
- taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask);
+ taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask);
sc->flags &= ~IWI_FLAG_BUSY;
sc->sc_busy_timer = 0;
@@ -1734,7 +1780,8 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni,
int ac)
{
struct iwi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
struct iwi_node *in = (struct iwi_node *)ni;
const struct ieee80211_frame *wh;
struct ieee80211_key *k;
@@ -1756,7 +1803,7 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni,
if (!ismcast)
flags |= IWI_DATA_FLAG_NEED_ACK;
- if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+ if (vap->iv_flags & IEEE80211_F_SHPREAMBLE)
flags |= IWI_DATA_FLAG_SHPREAMBLE;
if (IEEE80211_QOS_HAS_SEQ(wh)) {
xflags |= IWI_DATA_XFLAG_QOS;
@@ -1769,7 +1816,7 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni,
* This is only used in IBSS mode where the firmware expect an index
* in a h/w table instead of a destination address.
*/
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
if (!ismcast) {
if (in->in_station == -1) {
in->in_station = alloc_unr(sc->sc_unr);
@@ -1803,7 +1850,7 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni,
staid = 0;
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -1813,12 +1860,12 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni,
wh = mtod(m0, struct ieee80211_frame *);
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct iwi_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data = &txq->data[txq->cur];
@@ -1868,8 +1915,8 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni,
desc->xflags = xflags;
#if 0
- if (ic->ic_flags & IEEE80211_F_PRIVACY)
- desc->wep_txkey = ic->ic_crypto.cs_def_txkey;
+ if (vap->iv_flags & IEEE80211_F_PRIVACY)
+ desc->wep_txkey = vap->iv_def_txkey;
else
#endif
desc->flags |= IWI_DATA_FLAG_NO_WEP;
@@ -1893,99 +1940,70 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni,
return 0;
}
+static int
+iwi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ /* no support; just discard */
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return 0;
+}
+
static void
-iwi_start(struct ifnet *ifp)
+iwi_start_locked(struct ifnet *ifp)
{
struct iwi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct mbuf *m0;
- struct ether_header *eh;
+ struct mbuf *m;
struct ieee80211_node *ni;
int ac;
- IWI_LOCK_DECL;
- IWI_LOCK(sc);
+ IWI_LOCK_ASSERT(sc);
- if (ic->ic_state != IEEE80211_S_RUN) {
- IWI_UNLOCK(sc);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
return;
- }
for (;;) {
- IF_DEQUEUE(&ic->ic_mgtq, m0);
- if (m0 == NULL) {
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
-
- if (m0->m_len < sizeof (struct ether_header) &&
- (m0 = m_pullup(m0, sizeof (struct ether_header))) == NULL) {
- ifp->if_oerrors++;
- continue;
- }
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- ifp->if_oerrors++;
- continue;
- }
-
- /* classify mbuf so we can find which tx ring to use */
- if (ieee80211_classify(ic, m0, ni) != 0) {
- m_freem(m0);
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- continue;
- }
-
- /* XXX does not belong here */
- /* no QoS encapsulation for EAPOL frames */
- ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ?
- M_WME_GETAC(m0) : WME_AC_BE;
-
- if (sc->txq[ac].queued > IWI_TX_RING_COUNT - 8) {
- /* there is no place left in this ring */
- IFQ_DRV_PREPEND(&ifp->if_snd, m0);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ ac = M_WME_GETAC(m);
+ if (sc->txq[ac].queued > IWI_TX_RING_COUNT - 8) {
+ /* there is no place left in this ring; tail drop */
+ /* XXX tail drop */
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
- BPF_MTAP(ifp, m0);
+ BPF_MTAP(ifp, m);
- m0 = ieee80211_encap(ic, m0, ni);
- if (m0 == NULL) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- continue;
- }
- } else {
- ni = (struct ieee80211_node *) m0->m_pkthdr.rcvif;
- m0->m_pkthdr.rcvif = NULL;
- /* XXX no way to send mgt frames (yet), discard */
- m_freem(m0);
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
ieee80211_free_node(ni);
+ ifp->if_oerrors++;
continue;
}
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (iwi_tx_start(ifp, m0, ni, ac) != 0) {
+ if (iwi_tx_start(ifp, m, ni, ac) != 0) {
ieee80211_free_node(ni);
ifp->if_oerrors++;
break;
}
sc->sc_tx_timer = 5;
- ic->ic_lastdata = ticks;
}
+}
+
+static void
+iwi_start(struct ifnet *ifp)
+{
+ struct iwi_softc *sc = ifp->if_softc;
+ IWI_LOCK_DECL;
+ IWI_LOCK(sc);
+ iwi_start_locked(ifp);
IWI_UNLOCK(sc);
}
@@ -2004,26 +2022,15 @@ iwi_watchdog(void *arg)
taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask);
}
}
- if (sc->sc_rfkill_timer > 0) {
- if (--sc->sc_rfkill_timer == 0) {
- /*
- * Check for a change in rfkill state. We get an
- * interrupt when a radio is disabled but not when
- * it is enabled so we must poll for the latter.
- */
- if (!iwi_getrfkill(sc))
- taskqueue_enqueue(sc->sc_tq, &sc->sc_radiontask);
- else
- sc->sc_rfkill_timer = 2;
- }
- }
if (sc->sc_state_timer > 0) {
if (--sc->sc_state_timer == 0) {
if_printf(ifp, "firmware stuck in state %d, resetting\n",
sc->fw_state);
taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask);
- if (sc->fw_state == IWI_FW_SCANNING)
- ieee80211_cancel_scan(&sc->sc_ic);
+ if (sc->fw_state == IWI_FW_SCANNING) {
+ struct ieee80211com *ic = ifp->if_l2com;
+ ieee80211_cancel_scan(TAILQ_FIRST(&ic->ic_vaps));
+ }
sc->sc_state_timer = 3;
}
}
@@ -2033,61 +2040,43 @@ iwi_watchdog(void *arg)
taskqueue_enqueue(sc->sc_tq2, &sc->sc_restarttask);
}
}
-
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc);
+ callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc);
}
static int
iwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct iwi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
IWI_LOCK_DECL;
IWI_LOCK(sc);
-
- /*
- * wait until pending iwi_cmd() are completed, to avoid races
- * that could cause problems.
- */
- while (sc->flags & IWI_FLAG_BUSY)
- msleep(sc, &sc->sc_mtx, 0, "iwiioctl", hz);
-
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
- if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
- iwi_init_locked(sc, 0);
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ iwi_init_locked(sc);
+ startall = 1;
+ }
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- iwi_stop(sc);
- else {
- /*
- * If device was stopped due to rfkill then
- * marked down we'll have the polling thread
- * running; stop it explicitly.
- */
- sc->sc_rfkill_timer = 0;
- }
+ iwi_stop_locked(sc);
}
break;
-
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
default:
- error = ieee80211_ioctl(ic, cmd, data);
- }
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
- (ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
- iwi_init_locked(sc, 0);
- error = 0;
+ error = ether_ioctl(ifp, cmd, data);
+ break;
}
-
IWI_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
@@ -2221,30 +2210,26 @@ iwi_getfw(struct iwi_fw *fw, const char *fwname,
* the boot firmware as "master".
*/
static int
-iwi_get_firmware(struct iwi_softc *sc)
+iwi_get_firmware(struct iwi_softc *sc, enum ieee80211_opmode opmode)
{
- struct ieee80211com *ic = &sc->sc_ic;
const struct iwi_firmware_hdr *hdr;
const struct firmware *fp;
/* invalidate cached firmware on mode change */
- if (sc->fw_mode != ic->ic_opmode)
+ if (sc->fw_mode != opmode)
iwi_put_firmware(sc);
- switch (ic->ic_opmode) {
+ switch (opmode) {
case IEEE80211_M_STA:
iwi_getfw(&sc->fw_fw, "iwi_bss", &sc->fw_uc, "iwi_ucode_bss");
break;
-
case IEEE80211_M_IBSS:
iwi_getfw(&sc->fw_fw, "iwi_ibss", &sc->fw_uc, "iwi_ucode_ibss");
break;
-
case IEEE80211_M_MONITOR:
iwi_getfw(&sc->fw_fw, "iwi_monitor",
&sc->fw_uc, "iwi_ucode_monitor");
break;
-
default:
break;
}
@@ -2324,7 +2309,7 @@ iwi_get_firmware(struct iwi_softc *sc)
sc->fw_boot.size, sc->fw_uc.size, sc->fw_fw.size);
#endif
- sc->fw_mode = ic->ic_opmode;
+ sc->fw_mode = opmode;
return 0;
bad:
iwi_put_firmware(sc);
@@ -2437,6 +2422,7 @@ iwi_load_firmware(struct iwi_softc *sc, const struct iwi_fw *fw)
int ntries, error;
IWI_LOCK_ASSERT(sc);
+
/* copy firmware image to DMA memory */
memcpy(sc->fw_virtaddr, fw->data, fw->size);
@@ -2527,12 +2513,11 @@ iwi_load_firmware(struct iwi_softc *sc, const struct iwi_fw *fw)
}
static int
-iwi_setpowermode(struct iwi_softc *sc)
+iwi_setpowermode(struct iwi_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
uint32_t data;
- if (ic->ic_flags & IEEE80211_F_PMGTON) {
+ if (vap->iv_flags & IEEE80211_F_PMGTON) {
/* XXX set more fine-grained operation */
data = htole32(IWI_POWER_MODE_MAX);
} else
@@ -2543,15 +2528,14 @@ iwi_setpowermode(struct iwi_softc *sc)
}
static int
-iwi_setwepkeys(struct iwi_softc *sc)
+iwi_setwepkeys(struct iwi_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
struct iwi_wep_key wepkey;
struct ieee80211_key *wk;
int error, i;
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- wk = &ic->ic_crypto.cs_nw_keys[i];
+ wk = &vap->iv_nw_keys[i];
wepkey.cmd = IWI_WEP_KEY_CMD_SETKEY;
wepkey.idx = i;
@@ -2571,13 +2555,14 @@ iwi_setwepkeys(struct iwi_softc *sc)
static int
iwi_config(struct iwi_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct iwi_configuration config;
struct iwi_rateset rs;
struct iwi_txpower power;
uint32_t data;
int error, i;
+
IWI_LOCK_ASSERT(sc);
IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
@@ -2599,23 +2584,6 @@ iwi_config(struct iwi_softc *sc)
error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config);
if (error != 0)
return error;
-
- error = iwi_setpowermode(sc);
- if (error != 0)
- return error;
-
- data = htole32(ic->ic_rtsthreshold);
- DPRINTF(("Setting RTS threshold to %u\n", le32toh(data)));
- error = iwi_cmd(sc, IWI_CMD_SET_RTS_THRESHOLD, &data, sizeof data);
- if (error != 0)
- return error;
-
- data = htole32(ic->ic_fragthreshold);
- DPRINTF(("Setting fragmentation threshold to %u\n", le32toh(data)));
- error = iwi_cmd(sc, IWI_CMD_SET_FRAG_THRESHOLD, &data, sizeof data);
- if (error != 0)
- return error;
-
if (ic->ic_opmode == IEEE80211_M_IBSS) {
power.mode = IWI_MODE_11B;
power.nchan = 11;
@@ -2657,32 +2625,12 @@ iwi_config(struct iwi_softc *sc)
if (error != 0)
return error;
- /* if we have a desired ESSID, set it now */
- if (ic->ic_des_ssid[0].len != 0) {
-#ifdef IWI_DEBUG
- if (iwi_debug > 0) {
- printf("Setting desired ESSID to ");
- ieee80211_print_essid(ic->ic_des_ssid[0].ssid,
- ic->ic_des_ssid[0].len);
- printf("\n");
- }
-#endif
- error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ic->ic_des_ssid[0].ssid,
- ic->ic_des_ssid[0].len);
- if (error != 0)
- return error;
- }
-
data = htole32(arc4random());
DPRINTF(("Setting initialization vector to %u\n", le32toh(data)));
error = iwi_cmd(sc, IWI_CMD_SET_IV, &data, sizeof data);
if (error != 0)
return error;
- error = iwi_setwepkeys(sc);
- if (error != 0)
- return error;
-
/* enable adapter */
DPRINTF(("Enabling adapter\n"));
return iwi_cmd(sc, IWI_CMD_ENABLE, NULL, 0);
@@ -2740,7 +2688,7 @@ iwi_scanchan(struct iwi_softc *sc, unsigned long maxdwell, int mode)
}
IWI_STATE_BEGIN(sc, IWI_FW_SCANNING);
- ic = &sc->sc_ic;
+ ic = sc->sc_ifp->if_l2com;
ss = ic->ic_scan;
memset(&scan, 0, sizeof scan);
@@ -2864,15 +2812,16 @@ iwi_set_sensitivity(struct iwi_softc *sc, int8_t rssi_dbm)
}
static int
-iwi_auth_and_assoc(struct iwi_softc *sc)
+iwi_auth_and_assoc(struct iwi_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
- struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_node *ni = vap->iv_bss;
struct iwi_configuration config;
struct iwi_associate *assoc = &sc->assoc;
struct iwi_rateset rs;
uint16_t capinfo;
+ uint32_t data;
int error, mode;
IWI_LOCK_ASSERT(sc);
@@ -2901,7 +2850,7 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
if (mode == IWI_MODE_11G)
config.use_protection = 1;
config.answer_pbreq =
- (ic->ic_opmode == IEEE80211_M_IBSS) ? 1 : 0;
+ (vap->iv_opmode == IEEE80211_M_IBSS) ? 1 : 0;
config.disable_unicast_decryption = 1;
config.disable_multicast_decryption = 1;
DPRINTF(("Configuring adapter\n"));
@@ -2921,6 +2870,22 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
if (error != 0)
goto done;
+ error = iwi_setpowermode(sc, vap);
+ if (error != 0)
+ goto done;
+
+ data = htole32(vap->iv_rtsthreshold);
+ DPRINTF(("Setting RTS threshold to %u\n", le32toh(data)));
+ error = iwi_cmd(sc, IWI_CMD_SET_RTS_THRESHOLD, &data, sizeof data);
+ if (error != 0)
+ goto done;
+
+ data = htole32(vap->iv_fragthreshold);
+ DPRINTF(("Setting fragmentation threshold to %u\n", le32toh(data)));
+ error = iwi_cmd(sc, IWI_CMD_SET_FRAG_THRESHOLD, &data, sizeof data);
+ if (error != 0)
+ goto done;
+
/* the rate set has already been "negotiated" */
memset(&rs, 0, sizeof rs);
rs.mode = mode;
@@ -2939,22 +2904,23 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
memset(assoc, 0, sizeof *assoc);
- if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) {
+ if ((vap->iv_flags & IEEE80211_F_WME) && ni->ni_ies.wme_ie != NULL) {
/* NB: don't treat WME setup as failure */
- if (iwi_wme_setparams(sc) == 0 && iwi_wme_setie(sc) == 0)
+ if (iwi_wme_setparams(sc, ic) == 0 && iwi_wme_setie(sc) == 0)
assoc->policy |= htole16(IWI_POLICY_WME);
/* XXX complain on failure? */
}
- if (ic->ic_opt_ie != NULL) {
- DPRINTF(("Setting optional IE (len=%u)\n", ic->ic_opt_ie_len));
- error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, ic->ic_opt_ie,
- ic->ic_opt_ie_len);
+ if (vap->iv_appie_wpa != NULL) {
+ struct ieee80211_appie *ie = vap->iv_appie_wpa;
+
+ DPRINTF(("Setting optional IE (len=%u)\n", ie->ie_len));
+ error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, ie->ie_data, ie->ie_len);
if (error != 0)
goto done;
}
- error = iwi_set_sensitivity(sc, ni->ni_rssi);
+ error = iwi_set_sensitivity(sc, ic->ic_node_getrssi(ni));
if (error != 0)
goto done;
@@ -2964,7 +2930,7 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
* NB: do not arrange for shared key auth w/o privacy
* (i.e. a wep key); it causes a firmware error.
*/
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) &&
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) &&
ni->ni_authmode == IEEE80211_AUTH_SHARED) {
assoc->auth = IWI_AUTH_SHARED;
/*
@@ -2973,26 +2939,26 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
* but if we blindly grab the key the firmware will
* barf so avoid it for now.
*/
- if (ic->ic_crypto.cs_def_txkey != IEEE80211_KEYIX_NONE)
- assoc->auth |= ic->ic_crypto.cs_def_txkey << 4;
+ if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE)
+ assoc->auth |= vap->iv_def_txkey << 4;
- error = iwi_setwepkeys(sc);
+ error = iwi_setwepkeys(sc, vap);
if (error != 0)
goto done;
}
- if (ic->ic_flags & IEEE80211_F_WPA)
+ if (vap->iv_flags & IEEE80211_F_WPA)
assoc->policy |= htole16(IWI_POLICY_WPA);
- if (ic->ic_opmode == IEEE80211_M_IBSS && ni->ni_tstamp.tsf == 0)
+ if (vap->iv_opmode == IEEE80211_M_IBSS && ni->ni_tstamp.tsf == 0)
assoc->type = IWI_HC_IBSS_START;
else
assoc->type = IWI_HC_ASSOC;
memcpy(assoc->tstamp, ni->ni_tstamp.data, 8);
- if (ic->ic_opmode == IEEE80211_M_IBSS)
+ if (vap->iv_opmode == IEEE80211_M_IBSS)
capinfo = IEEE80211_CAPINFO_IBSS;
else
capinfo = IEEE80211_CAPINFO_ESS;
- if (ic->ic_flags & IEEE80211_F_PRIVACY)
+ if (vap->iv_flags & IEEE80211_F_PRIVACY)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
@@ -3004,7 +2970,7 @@ iwi_auth_and_assoc(struct iwi_softc *sc)
assoc->lintval = htole16(ic->ic_lintval);
assoc->intval = htole16(ni->ni_intval);
IEEE80211_ADDR_COPY(assoc->bssid, ni->ni_bssid);
- if (ic->ic_opmode == IEEE80211_M_IBSS)
+ if (vap->iv_opmode == IEEE80211_M_IBSS)
IEEE80211_ADDR_COPY(assoc->dst, ifp->if_broadcastaddr);
else
IEEE80211_ADDR_COPY(assoc->dst, ni->ni_bssid);
@@ -3046,17 +3012,6 @@ iwi_disassociate(struct iwi_softc *sc, int quiet)
return iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc);
}
-static void
-iwi_init(void *priv)
-{
- struct iwi_softc *sc = priv;
- IWI_LOCK_DECL;
-
- IWI_LOCK(sc);
- iwi_init_locked(sc, 0);
- IWI_UNLOCK(sc);
-}
-
/*
* release dma resources for the firmware
*/
@@ -3118,54 +3073,35 @@ error:
}
static void
-iwi_init_locked(void *priv, int force)
+iwi_init_locked(struct iwi_softc *sc)
{
- struct iwi_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
struct iwi_rx_data *data;
int i;
- IWI_LOCK_DECL;
IWI_LOCK_ASSERT(sc);
+
if (sc->fw_state == IWI_FW_LOADING) {
device_printf(sc->sc_dev, "%s: already loading\n", __func__);
return; /* XXX: condvar? */
}
- iwi_stop(sc);
+ iwi_stop_locked(sc);
+
IWI_STATE_BEGIN(sc, IWI_FW_LOADING);
+ taskqueue_unblock(sc->sc_tq);
+ taskqueue_unblock(sc->sc_tq2);
+
if (iwi_reset(sc) != 0) {
device_printf(sc->sc_dev, "could not reset adapter\n");
goto fail;
}
-
- IWI_UNLOCK(sc);
- if (iwi_get_firmware(sc)) {
- IWI_LOCK(sc);
- goto fail;
- }
-
- /* allocate DMA memory for mapping firmware image */
- i = sc->fw_fw.size;
- if (sc->fw_boot.size > i)
- i = sc->fw_boot.size;
- /* XXX do we dma the ucode as well ? */
- if (sc->fw_uc.size > i)
- i = sc->fw_uc.size;
- if (iwi_init_fw_dma(sc, i)) {
- IWI_LOCK(sc);
- goto fail;
- }
- IWI_LOCK(sc);
-
if (iwi_load_firmware(sc, &sc->fw_boot) != 0) {
device_printf(sc->sc_dev,
"could not load boot firmware %s\n", sc->fw_boot.name);
goto fail;
}
-
if (iwi_load_ucode(sc, &sc->fw_uc) != 0) {
device_printf(sc->sc_dev,
"could not load microcode %s\n", sc->fw_uc.name);
@@ -3208,50 +3144,58 @@ iwi_init_locked(void *priv, int force)
}
sc->flags |= IWI_FLAG_FW_INITED;
+ IWI_STATE_END(sc, IWI_FW_LOADING);
+
if (iwi_config(sc) != 0) {
- device_printf(sc->sc_dev, "device configuration failed\n");
- goto fail;
+ device_printf(sc->sc_dev, "unable to enable adapter\n");
+ goto fail2;
}
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- /*
- * NB: When restarting the adapter clock the state
- * machine regardless of the roaming mode; otherwise
- * we need to notify user apps so they can manually
- * get us going again.
- */
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL || force)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-
callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc);
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
-
- IWI_STATE_END(sc, IWI_FW_LOADING);
return;
-
-fail: ifp->if_flags &= ~IFF_UP;
+fail:
IWI_STATE_END(sc, IWI_FW_LOADING);
- iwi_stop(sc);
- iwi_put_firmware(sc);
+fail2:
+ iwi_stop_locked(sc);
}
static void
-iwi_stop(void *priv)
+iwi_init(void *priv)
{
struct iwi_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ IWI_LOCK_DECL;
+
+ IWI_LOCK(sc);
+ iwi_init_locked(sc);
+ IWI_UNLOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic);
+}
+
+static void
+iwi_stop_locked(void *priv)
+{
+ struct iwi_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
IWI_LOCK_ASSERT(sc);
+
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+ taskqueue_block(sc->sc_tq);
+ taskqueue_block(sc->sc_tq2);
if (sc->sc_softled) {
callout_stop(&sc->sc_ledtimer);
sc->sc_blinking = 0;
}
-
callout_stop(&sc->sc_wdtimer);
+ callout_stop(&sc->sc_rftimer);
+
iwi_stop_master(sc);
CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_SOFT_RESET);
@@ -3264,31 +3208,33 @@ iwi_stop(void *priv)
iwi_reset_tx_ring(sc, &sc->txq[3]);
iwi_reset_rx_ring(sc, &sc->rxq);
- ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
-
memset(sc->sc_cmd, 0, sizeof(sc->sc_cmd));
sc->sc_tx_timer = 0;
- sc->sc_rfkill_timer = 0;
sc->sc_state_timer = 0;
sc->sc_busy_timer = 0;
sc->flags &= ~(IWI_FLAG_BUSY | IWI_FLAG_ASSOCIATED);
sc->fw_state = IWI_FW_IDLE;
wakeup(sc);
-
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
}
static void
-iwi_restart(void *arg, int npending)
+iwi_stop(struct iwi_softc *sc)
{
- struct iwi_softc *sc = arg;
IWI_LOCK_DECL;
IWI_LOCK(sc);
- iwi_init_locked(sc, 1); /* NB: force state machine */
+ iwi_stop_locked(sc);
IWI_UNLOCK(sc);
}
+static void
+iwi_restart(void *arg, int npending)
+{
+ struct iwi_softc *sc = arg;
+
+ iwi_init(sc);
+}
+
/*
* Return whether or not the radio is enabled in hardware
* (i.e. the rfkill switch is "off").
@@ -3303,21 +3249,48 @@ static void
iwi_radio_on(void *arg, int pending)
{
struct iwi_softc *sc = arg;
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
device_printf(sc->sc_dev, "radio turned on\n");
+
iwi_init(sc);
+ ieee80211_notify_radio(ic, 1);
+}
+
+static void
+iwi_rfkill_poll(void *arg)
+{
+ struct iwi_softc *sc = arg;
+
+ IWI_LOCK_ASSERT(sc);
+
+ /*
+ * Check for a change in rfkill state. We get an
+ * interrupt when a radio is disabled but not when
+ * it is enabled so we must poll for the latter.
+ */
+ if (!iwi_getrfkill(sc)) {
+ taskqueue_unblock(sc->sc_tq);
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_radiontask);
+ return;
+ }
+ callout_reset(&sc->sc_rftimer, 2*hz, iwi_rfkill_poll, sc);
}
static void
iwi_radio_off(void *arg, int pending)
{
struct iwi_softc *sc = arg;
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
IWI_LOCK_DECL;
device_printf(sc->sc_dev, "radio turned off\n");
+
+ ieee80211_notify_radio(ic, 0);
+
IWI_LOCK(sc);
- iwi_stop(sc);
- sc->sc_rfkill_timer = 2;
+ iwi_stop_locked(sc);
+ iwi_rfkill_poll(sc);
IWI_UNLOCK(sc);
}
@@ -3564,22 +3537,37 @@ iwi_ledattach(struct iwi_softc *sc)
}
static void
-iwi_ops(void *arg, int npending)
-{
- struct iwi_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
+iwi_ops(void *arg0, int npending)
+{
+ static const char *opnames[] = {
+ [IWI_CMD_FREE] = "FREE",
+ [IWI_SCAN_START] = "SCAN_START",
+ [IWI_SET_CHANNEL] = "SET_CHANNEL",
+ [IWI_AUTH] = "AUTH",
+ [IWI_ASSOC] = "ASSOC",
+ [IWI_DISASSOC] = "DISASSOC",
+ [IWI_SCAN_CURCHAN] = "SCAN_CURCHAN",
+ [IWI_SCAN_ALLCHAN] = "SCAN_ALLCHAN",
+ [IWI_SET_WME] = "SET_WME",
+ };
+ struct iwi_softc *sc = arg0;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
IWI_LOCK_DECL;
int cmd;
+ unsigned long arg;
again:
IWI_CMD_LOCK(sc);
cmd = sc->sc_cmd[sc->sc_cmd_cur];
- if (cmd == 0) {
+ if (cmd == IWI_CMD_FREE) {
/* No more commands to process */
IWI_CMD_UNLOCK(sc);
return;
}
- sc->sc_cmd[sc->sc_cmd_cur] = 0; /* free the slot */
+ arg = sc->sc_arg[sc->sc_cmd_cur];
+ sc->sc_cmd[sc->sc_cmd_cur] = IWI_CMD_FREE; /* free the slot */
sc->sc_cmd_cur = (sc->sc_cmd_cur + 1) % IWI_CMD_MAXOPS;
IWI_CMD_UNLOCK(sc);
@@ -3588,21 +3576,28 @@ again:
msleep(sc, &sc->sc_mtx, 0, "iwicmd", hz/10);
}
- if (!(sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
IWI_UNLOCK(sc);
return;
}
+ DPRINTF(("%s: %s arg %lu\n", __func__, opnames[cmd], arg));
switch (cmd) {
+ case IWI_AUTH:
case IWI_ASSOC:
- iwi_auth_and_assoc(sc);
+ if (cmd == IWI_AUTH)
+ vap->iv_state = IEEE80211_S_AUTH;
+ else
+ vap->iv_state = IEEE80211_S_ASSOC;
+ iwi_auth_and_assoc(sc, vap);
+ /* NB: completion done in iwi_notification_intr */
break;
case IWI_DISASSOC:
iwi_disassociate(sc, 0);
break;
case IWI_SET_WME:
- if (ic->ic_state == IEEE80211_S_RUN)
- (void) iwi_wme_setparams(sc);
+ if (vap->iv_state == IEEE80211_S_RUN)
+ (void) iwi_wme_setparams(sc, ic);
break;
case IWI_SCAN_START:
sc->flags |= IWI_FLAG_CHANNEL_SCAN;
@@ -3614,9 +3609,8 @@ again:
__func__));
goto done;
}
- if (iwi_scanchan(sc, sc->sc_maxdwell, cmd))
- ieee80211_cancel_scan(ic);
-
+ if (iwi_scanchan(sc, arg, cmd))
+ ieee80211_cancel_scan(vap);
break;
}
done:
@@ -3627,7 +3621,7 @@ done:
}
static int
-iwi_queue_cmd(struct iwi_softc *sc, int cmd)
+iwi_queue_cmd(struct iwi_softc *sc, int cmd, unsigned long arg)
{
IWI_CMD_LOCK(sc);
if (sc->sc_cmd[sc->sc_cmd_next] != 0) {
@@ -3637,6 +3631,7 @@ iwi_queue_cmd(struct iwi_softc *sc, int cmd)
}
sc->sc_cmd[sc->sc_cmd_next] = cmd;
+ sc->sc_arg[sc->sc_cmd_next] = arg;
sc->sc_cmd_next = (sc->sc_cmd_next + 1) % IWI_CMD_MAXOPS;
taskqueue_enqueue(sc->sc_tq, &sc->sc_opstask);
IWI_CMD_UNLOCK(sc);
@@ -3649,7 +3644,7 @@ iwi_scan_start(struct ieee80211com *ic)
struct ifnet *ifp = ic->ic_ifp;
struct iwi_softc *sc = ifp->if_softc;
- iwi_queue_cmd(sc, IWI_SCAN_START);
+ iwi_queue_cmd(sc, IWI_SCAN_START, 0);
}
static void
@@ -3662,13 +3657,13 @@ iwi_set_channel(struct ieee80211com *ic)
}
static void
-iwi_scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
+iwi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap = ss->ss_vap;
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
struct iwi_softc *sc = ifp->if_softc;
- sc->sc_maxdwell = maxdwell;
- iwi_queue_cmd(sc, IWI_SCAN_CURCHAN);
+ iwi_queue_cmd(sc, IWI_SCAN_CURCHAN, maxdwell);
}
#if 0
@@ -3678,13 +3673,12 @@ iwi_scan_allchan(struct ieee80211com *ic, unsigned long maxdwell)
struct ifnet *ifp = ic->ic_ifp;
struct iwi_softc *sc = ifp->if_softc;
- sc->sc_maxdwell = maxdwell;
- iwi_queue_cmd(sc, IWI_SCAN_ALLCHAN);
+ iwi_queue_cmd(sc, IWI_SCAN_ALLCHAN, maxdwell);
}
#endif
static void
-iwi_scan_mindwell(struct ieee80211com *ic)
+iwi_scan_mindwell(struct ieee80211_scan_state *ss)
{
/* NB: don't try to abort scan; wait for firmware to finish */
}
@@ -3697,25 +3691,3 @@ iwi_scan_end(struct ieee80211com *ic)
taskqueue_enqueue(sc->sc_tq2, &sc->sc_scanaborttask);
}
-
-static void
-iwi_assoc(struct ieee80211com *ic)
-{
- struct ifnet *ifp = ic->ic_ifp;
- struct iwi_softc *sc = ifp->if_softc;
-
- /* The firmware will fail if we are already associated */
- if (sc->flags & IWI_FLAG_ASSOCIATED)
- iwi_disassoc(ic);
-
- iwi_queue_cmd(sc, IWI_ASSOC);
-}
-
-static void
-iwi_disassoc(struct ieee80211com *ic)
-{
- struct ifnet *ifp = ic->ic_ifp;
- struct iwi_softc *sc = ifp->if_softc;
-
- iwi_queue_cmd(sc, IWI_DISASSOC);
-}
diff --git a/sys/dev/iwi/if_iwivar.h b/sys/dev/iwi/if_iwivar.h
index fca0b7a..abc6f9c 100644
--- a/sys/dev/iwi/if_iwivar.h
+++ b/sys/dev/iwi/if_iwivar.h
@@ -114,11 +114,19 @@ struct iwi_fw {
const char *name; /* associated image name */
};
+struct iwi_vap {
+ struct ieee80211vap iwi_vap;
+ struct task iwi_authsuccess_task;
+ struct task iwi_assocsuccess_task;
+ struct task iwi_assocfailed_task;
+
+ int (*iwi_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define IWI_VAP(vap) ((struct iwi_vap *)(vap))
+
struct iwi_softc {
struct ifnet *sc_ifp;
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
void (*sc_node_free)(struct ieee80211_node *);
device_t sc_dev;
@@ -129,9 +137,6 @@ struct iwi_softc {
struct unrhdr *sc_unr;
struct taskqueue *sc_tq; /* private task queue */
struct taskqueue *sc_tq2; /* reset task queue */
-#if __FreeBSD_version < 700000
- struct proc *sc_tqproc;
-#endif
uint32_t flags;
#define IWI_FLAG_FW_INITED (1 << 0)
@@ -208,39 +213,31 @@ struct iwi_softc {
u_int16_t sc_ledoff; /* off time for current blink */
struct callout sc_ledtimer; /* led off timer */
struct callout sc_wdtimer; /* watchdog timer */
+ struct callout sc_rftimer; /* rfkill timer */
int sc_tx_timer;
- int sc_rfkill_timer;/* poll for rfkill change */
int sc_state_timer; /* firmware state timer */
int sc_busy_timer; /* firmware cmd timer */
-#define IWI_SCAN_START (1 << 0)
-#define IWI_SET_CHANNEL (1 << 1)
-#define IWI_SCAN_END (1 << 2)
-#define IWI_ASSOC (1 << 3)
-#define IWI_DISASSOC (1 << 4)
-#define IWI_SCAN_CURCHAN (1 << 5)
-#define IWI_SCAN_ALLCHAN (1 << 6)
-#define IWI_SET_WME (1 << 7)
#define IWI_CMD_MAXOPS 10
- int sc_cmd[IWI_CMD_MAXOPS];
- int sc_cmd_cur; /* current queued scan task */
- int sc_cmd_next; /* last queued scan task */
- unsigned long sc_maxdwell; /* max dwell time for curchan */
- struct bpf_if *sc_drvbpf;
+ int sc_cmd[IWI_CMD_MAXOPS];
+ unsigned long sc_arg[IWI_CMD_MAXOPS];
+ int sc_cmd_cur; /* current queued scan task */
+ int sc_cmd_next; /* last queued scan task */
+#define IWI_CMD_FREE 0 /* for marking slots unused */
+#define IWI_SCAN_START 1
+#define IWI_SET_CHANNEL 2
+#define IWI_AUTH 3
+#define IWI_ASSOC 4
+#define IWI_DISASSOC 5
+#define IWI_SCAN_CURCHAN 6
+#define IWI_SCAN_ALLCHAN 7
+#define IWI_SET_WME 8
- union {
- struct iwi_rx_radiotap_header th;
- uint8_t pad[64];
- } sc_rxtapu;
-#define sc_rxtap sc_rxtapu.th
+ struct iwi_rx_radiotap_header sc_rxtap;
int sc_rxtap_len;
- union {
- struct iwi_tx_radiotap_header th;
- uint8_t pad[64];
- } sc_txtapu;
-#define sc_txtap sc_txtapu.th
+ struct iwi_tx_radiotap_header sc_txtap;
int sc_txtap_len;
};
@@ -249,15 +246,15 @@ struct iwi_softc {
("iwi firmware not idle")); \
_sc->fw_state = _state; \
_sc->sc_state_timer = 5; \
- DPRINTF(("enter FW state %d\n", _state)); \
+ DPRINTF(("enter %s state\n", iwi_fw_states[_state])); \
} while (0)
#define IWI_STATE_END(_sc, _state) do { \
if (_sc->fw_state == _state) \
- DPRINTF(("exit FW state %d\n", _state)); \
+ DPRINTF(("exit %s state\n", iwi_fw_states[_state])); \
else \
- DPRINTF(("expected FW state %d, got %d\n", \
- _state, _sc->fw_state)); \
+ DPRINTF(("expected %s state, got %s\n", \
+ iwi_fw_states[_state], iwi_fw_states[_sc->fw_state])); \
_sc->fw_state = IWI_FW_IDLE; \
wakeup(_sc); \
_sc->sc_state_timer = 0; \
diff --git a/sys/dev/malo/if_malo.c b/sys/dev/malo/if_malo.c
index a1a6285..8e70f40 100644
--- a/sys/dev/malo/if_malo.c
+++ b/sys/dev/malo/if_malo.c
@@ -34,6 +34,8 @@
__FBSDID("$FreeBSD$");
#endif
+#include "opt_malo.h"
+
#include <sys/param.h>
#include <sys/endian.h>
#include <sys/kernel.h>
@@ -123,6 +125,11 @@ enum {
MALLOC_DEFINE(M_MALODEV, "malodev", "malo driver dma buffers");
+static struct ieee80211vap *malo_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void malo_vap_delete(struct ieee80211vap *);
static int malo_dma_setup(struct malo_softc *);
static int malo_setup_hwdma(struct malo_softc *);
static void malo_txq_init(struct malo_softc *, struct malo_txq *, int);
@@ -131,13 +138,12 @@ static void malo_start(struct ifnet *);
static void malo_watchdog(struct ifnet *);
static int malo_ioctl(struct ifnet *, u_long, caddr_t);
static void malo_updateslot(struct ifnet *);
-static int malo_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static int malo_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static void malo_scan_start(struct ieee80211com *);
static void malo_scan_end(struct ieee80211com *);
static void malo_set_channel(struct ieee80211com *);
static int malo_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
-static int malo_media_change(struct ifnet *);
static void malo_bpfattach(struct malo_softc *);
static void malo_sysctlattach(struct malo_softc *);
static void malo_announce(struct malo_softc *);
@@ -163,7 +169,7 @@ malo_bar0_read4(struct malo_softc *sc, bus_size_t off)
static void
malo_bar0_write4(struct malo_softc *sc, bus_size_t off, uint32_t val)
{
- DPRINTF(sc, MALO_DEBUG_FW, "%s: off 0x%x val 0x%x\n",
+ DPRINTF(sc, MALO_DEBUG_FW, "%s: off 0x%zx val 0x%x\n",
__func__, off, val);
bus_space_write_4(sc->malo_io0t, sc->malo_io0h, off, val);
@@ -178,17 +184,18 @@ malo_bar1_read1(struct malo_softc *sc, bus_size_t off)
int
malo_attach(uint16_t devid, struct malo_softc *sc)
{
- int error, i;
- struct ieee80211com *ic = &sc->malo_ic;
+ int error;
+ struct ieee80211com *ic;
struct ifnet *ifp;
struct malo_hal *mh;
uint8_t bands;
- ifp = sc->malo_ifp = if_alloc(IFT_ETHER);
+ ifp = sc->malo_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(sc->malo_dev, "can not if_alloc()\n");
return ENOSPC;
}
+ ic = ifp->if_l2com;
MALO_LOCK_INIT(sc);
@@ -215,6 +222,45 @@ malo_attach(uint16_t devid, struct malo_softc *sc)
}
sc->malo_mh = mh;
+ /*
+ * Load firmware so we can get setup. We arbitrarily pick station
+ * firmware; we'll re-load firmware as needed so setting up
+ * the wrong mode isn't a big deal.
+ */
+ error = malo_hal_fwload(mh, "malo8335-h", "malo8335-m");
+ if (error != 0) {
+ if_printf(ifp, "unable to setup firmware\n");
+ goto bad1;
+ }
+ /* XXX gethwspecs() extracts correct informations? not maybe! */
+ error = malo_hal_gethwspecs(mh, &sc->malo_hwspecs);
+ if (error != 0) {
+ if_printf(ifp, "unable to fetch h/w specs\n");
+ goto bad1;
+ }
+
+ DPRINTF(sc, MALO_DEBUG_FW,
+ "malo_hal_gethwspecs: hwversion 0x%x hostif 0x%x"
+ "maxnum_wcb 0x%x maxnum_mcaddr 0x%x maxnum_tx_wcb 0x%x"
+ "regioncode 0x%x num_antenna 0x%x fw_releasenum 0x%x"
+ "wcbbase0 0x%x rxdesc_read 0x%x rxdesc_write 0x%x"
+ "ul_fw_awakecookie 0x%x w[4] = %x %x %x %x",
+ sc->malo_hwspecs.hwversion,
+ sc->malo_hwspecs.hostinterface, sc->malo_hwspecs.maxnum_wcb,
+ sc->malo_hwspecs.maxnum_mcaddr, sc->malo_hwspecs.maxnum_tx_wcb,
+ sc->malo_hwspecs.regioncode, sc->malo_hwspecs.num_antenna,
+ sc->malo_hwspecs.fw_releasenum, sc->malo_hwspecs.wcbbase0,
+ sc->malo_hwspecs.rxdesc_read, sc->malo_hwspecs.rxdesc_write,
+ sc->malo_hwspecs.ul_fw_awakecookie,
+ sc->malo_hwspecs.wcbbase[0], sc->malo_hwspecs.wcbbase[1],
+ sc->malo_hwspecs.wcbbase[2], sc->malo_hwspecs.wcbbase[3]);
+
+ /* NB: firmware looks that it does not export regdomain info API. */
+ bands = 0;
+ setbit(&bands, IEEE80211_MODE_11B);
+ setbit(&bands, IEEE80211_MODE_11G);
+ ieee80211_init_channels(ic, NULL, &bands);
+
sc->malo_txantenna = 0x2; /* h/w default */
sc->malo_rxantenna = 0xffff; /* h/w default */
@@ -228,6 +274,9 @@ malo_attach(uint16_t devid, struct malo_softc *sc)
if_printf(ifp, "failed to setup descriptors: %d\n", error);
goto bad1;
}
+ error = malo_setup_hwdma(sc); /* push to firmware */
+ if (error != 0) /* NB: malo_setupdma prints msg */
+ goto bad1;
sc->malo_tq = taskqueue_create_fast("malo_taskq", M_NOWAIT,
taskqueue_thread_enqueue, &sc->malo_tq);
@@ -247,12 +296,6 @@ malo_attach(uint16_t devid, struct malo_softc *sc)
ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
IFQ_SET_READY(&ifp->if_snd);
- /* NB: firmware looks that it does not export regdomain info API. */
- bands = 0;
- setbit(&bands, IEEE80211_MODE_11B);
- setbit(&bands, IEEE80211_MODE_11G);
- ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
-
ic->ic_ifp = ifp;
/* XXX not right but it's not used anywhere important */
ic->ic_phytype = IEEE80211_T_OFDM;
@@ -273,24 +316,23 @@ malo_attach(uint16_t devid, struct malo_softc *sc)
* packets so we can add it efficiently.
*/
ic->ic_headroom = sizeof(struct malo_txrec) -
- sizeof(struct ieee80211_frame);
+ sizeof(struct ieee80211_frame);
+
+ /* get mac address from hardware */
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->malo_hwspecs.macaddr);
/* call MI attach routine. */
ieee80211_ifattach(ic);
/* override default methods */
- ic->ic_updateslot = malo_updateslot;
+ ic->ic_vap_create = malo_vap_create;
+ ic->ic_vap_delete = malo_vap_delete;
ic->ic_raw_xmit = malo_raw_xmit;
-
- sc->malo_newstate = ic->ic_newstate;
- ic->ic_newstate = malo_newstate;
+ ic->ic_updateslot = malo_updateslot;
ic->ic_scan_start = malo_scan_start;
ic->ic_scan_end = malo_scan_end;
ic->ic_set_channel = malo_set_channel;
- /* complete initialization */
- ieee80211_media_init(ic, malo_media_change, ieee80211_media_status);
-
sc->malo_invalid = 0; /* ready to go, enable int handling */
malo_bpfattach(sc);
@@ -302,6 +344,7 @@ malo_attach(uint16_t devid, struct malo_softc *sc)
if (bootverbose)
ieee80211_announce(ic);
+ malo_announce(sc);
return 0;
bad1:
@@ -313,6 +356,61 @@ bad:
return error;
}
+static struct ieee80211vap *
+malo_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct malo_vap *mvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) {
+ if_printf(ifp, "multiple vaps not supported\n");
+ return NULL;
+ }
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ if (opmode == IEEE80211_M_STA)
+ flags |= IEEE80211_CLONE_NOBEACONS;
+ /* fall thru... */
+ case IEEE80211_M_MONITOR:
+ break;
+ default:
+ if_printf(ifp, "%s mode not supported\n",
+ ieee80211_opmode_name[opmode]);
+ return NULL; /* unsupported */
+ }
+ mvp = (struct malo_vap *) malloc(sizeof(struct malo_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (mvp == NULL) {
+ if_printf(ifp, "cannot allocate vap state block\n");
+ return NULL;
+ }
+ vap = &mvp->malo_vap;
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
+
+ /* override state transition machine */
+ mvp->malo_newstate = vap->iv_newstate;
+ vap->iv_newstate = malo_newstate;
+
+ /* complete setup */
+ ieee80211_vap_attach(vap,
+ ieee80211_media_change, ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+malo_vap_delete(struct ieee80211vap *vap)
+{
+ struct malo_vap *mvp = MALO_VAP(vap);
+
+ ieee80211_vap_detach(vap);
+ free(mvp, M_80211_VAP);
+}
+
int
malo_intr(void *arg)
{
@@ -353,14 +451,12 @@ malo_intr(void *arg)
/* TKIP ICV error */
sc->malo_stats.mst_rx_badtkipicv++;
}
-
#ifdef MALO_DEBUG
if (((status | sc->malo_imask) ^ sc->malo_imask) != 0)
DPRINTF(sc, MALO_DEBUG_INTR,
"%s: can't handle interrupt status 0x%x\n",
__func__, status);
#endif
-
return (FILTER_HANDLED);
}
@@ -1009,8 +1105,8 @@ malo_tx_start(struct malo_softc *sc, struct ieee80211_node *ni,
int error, ismcast, iswep;
int copyhdrlen, hdrlen, pktlen;
struct ieee80211_frame *wh;
- struct ieee80211com *ic = &sc->malo_ic;
struct ifnet *ifp = sc->malo_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct malo_txdesc *ds;
struct malo_txrec *tr;
struct malo_txq *txq;
@@ -1045,7 +1141,7 @@ malo_tx_start(struct malo_softc *sc, struct ieee80211_node *ni,
* ExtIV filled in for CCMP and this also adjusts
* the headers which simplifies our work below.
*/
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
/*
* This can happen when the key is yanked after the
@@ -1068,15 +1164,14 @@ malo_tx_start(struct malo_softc *sc, struct ieee80211_node *ni,
wh = mtod(m0, struct ieee80211_frame *);
}
- if (bpf_peers_present(sc->malo_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
sc->malo_tx_th.wt_flags = 0; /* XXX */
if (iswep)
sc->malo_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP;
sc->malo_tx_th.wt_txpower = ni->ni_txpower;
sc->malo_tx_th.wt_antenna = sc->malo_txantenna;
- bpf_mtap2(sc->malo_drvbpf,
- &sc->malo_tx_th, sc->malo_tx_th_len, m0);
+ bpf_mtap2(ifp->if_bpf, &sc->malo_tx_th, sc->malo_tx_th_len, m0);
}
/*
@@ -1186,137 +1281,40 @@ malo_tx_start(struct malo_softc *sc, struct ieee80211_node *ni,
static void
malo_start(struct ifnet *ifp)
{
- int nqueued = 0;
- struct ether_header *eh;
struct malo_softc *sc = ifp->if_softc;
- struct ieee80211_frame *wh;
struct ieee80211_node *ni;
- struct ieee80211com *ic = &sc->malo_ic;
+ struct malo_txq *txq = &sc->malo_txq[0];
struct malo_txbuf *bf = NULL;
- struct malo_txq *txq = NULL;
struct mbuf *m;
+ int nqueued = 0;
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->malo_invalid)
return;
for (;;) {
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ bf = malo_getbuf(sc, txq);
+ if (bf == NULL) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+
+ /* XXX blocks other traffic */
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ sc->malo_stats.mst_tx_qstop++;
+ break;
+ }
/*
- * Poll the management queue for frames; they
- * have priority over normal data frames.
+ * Encapsulate the packet in prep for transmission.
*/
- IF_DEQUEUE(&ic->ic_mgtq, m);
+ m = ieee80211_encap(ni, m);
if (m == NULL) {
- /*
- * No data frames go out unless we're associated.
- */
- if (ic->ic_state != IEEE80211_S_RUN) {
- DPRINTF(sc, MALO_DEBUG_XMIT,
- "%s: discard data packet, state %s\n",
- __func__,
- ieee80211_state_name[ic->ic_state]);
- sc->malo_stats.mst_tx_discard++;
- break;
- }
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
- if (m == NULL)
- break;
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
-
- /*
- * Find the node for the destination so we can do
- * things like power save and fast frames aggregation.
- */
- if (m->m_len < sizeof(struct ether_header) &&
- (m = m_pullup(m, sizeof(struct ether_header))) ==
- NULL) {
- ic->ic_stats.is_tx_nobuf++; /* XXX */
- ni = NULL;
- goto bad;
- }
- eh = mtod(m, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- /* NB: ieee80211_find_txnode does stat+msg */
- m_freem(m);
- goto bad;
- }
- /* calculate priority so we can find the tx queue */
- if (ieee80211_classify(ic, m, ni)) {
- DPRINTF(sc, MALO_DEBUG_XMIT,
- "%s: discard, classification failure\n",
- __func__);
- m_freem(m);
- goto bad;
- }
-
- txq = &sc->malo_txq[0];
-
- bf = malo_getbuf(sc, txq);
- if (bf == NULL) {
- IFQ_DRV_PREPEND(&ifp->if_snd, m);
- ieee80211_free_node(ni);
-
- /* XXX blocks other traffic */
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- sc->malo_stats.mst_tx_qstop++;
- break;
- }
- ifp->if_opackets++;
-
- if (bpf_peers_present(ifp->if_bpf))
- bpf_mtap(ifp->if_bpf, m);
-
- /*
- * Encapsulate the packet in prep for transmission.
- */
- m = ieee80211_encap(ic, m, ni);
- if (m == NULL) {
- DPRINTF(sc, MALO_DEBUG_XMIT,
- "%s: encapsulation failure\n", __func__);
- sc->malo_stats.mst_tx_encap++;
- goto bad;
- }
- } else {
- /*
- * Grab a TX buffer and associated resources.
- * Note that we depend on the classification
- * by the 802.11 layer to get to the right h/w
- * queue. Management frames must ALWAYS go on
- * queue 1 but we cannot just force that here
- * because we may receive non-mgt frames through
- * the ic_mgtq (e.g. null data frames).
- */
- txq = &sc->malo_txq[0];
- bf = malo_getbuf(sc, txq);
- if (bf == NULL) {
- IF_PREPEND(&ic->ic_mgtq, m);
- /* XXX stat */
- break;
- }
-
- /*
- * Hack! The referenced node pointer is in the
- * rcvif field of the packet header. This is
- * placed there by ieee80211_mgmt_output because
- * we need to hold the reference with the frame
- * and there's no other way (other than packet
- * tags which we consider too expensive to use)
- * to pass it along.
- */
- ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
- m->m_pkthdr.rcvif = NULL;
-
- wh = mtod(m, struct ieee80211_frame *);
- sc->malo_stats.mst_tx_mgmt++;
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m);
+ DPRINTF(sc, MALO_DEBUG_XMIT,
+ "%s: encapsulation failure\n", __func__);
+ sc->malo_stats.mst_tx_encap++;
+ goto bad;
}
-
/*
* Pass the frame to the h/w for transmission.
*/
@@ -1382,7 +1380,8 @@ static int
malo_hal_reset(struct malo_softc *sc)
{
static int first = 0;
- struct ieee80211com *ic = &sc->malo_ic;
+ struct ifnet *ifp = sc->malo_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct malo_hal *mh = sc->malo_mh;
if (first == 0) {
@@ -1511,10 +1510,8 @@ malo_startrecv(struct malo_softc *sc)
}
static void
-malo_init(void *arg)
+malo_init_locked(struct malo_softc *sc)
{
- struct malo_softc *sc = (struct malo_softc *) arg;
- struct ieee80211com *ic = &sc->malo_ic;
struct ifnet *ifp = sc->malo_ifp;
struct malo_hal *mh = sc->malo_mh;
int error;
@@ -1522,56 +1519,7 @@ malo_init(void *arg)
DPRINTF(sc, MALO_DEBUG_ANY, "%s: if_flags 0x%x\n",
__func__, ifp->if_flags);
- if (!sc->malo_fw_loaded) {
- /*
- * Load firmware so we can get setup.
- */
- error = malo_hal_fwload(mh, "malo8335-h", "malo8335-m");
- if (error != 0) {
- if_printf(ifp, "unable to setup firmware\n");
- return;
- }
- /* XXX gethwspecs() extracts correct informations? not maybe! */
- error = malo_hal_gethwspecs(mh, &sc->malo_hwspecs);
- if (error != 0) {
- if_printf(ifp, "unable to fetch h/w specs\n");
- return;
- }
-
- DPRINTF(sc, MALO_DEBUG_FW,
- "malo_hal_gethwspecs: hwversion 0x%x hostif 0x%x"
- "maxnum_wcb 0x%x maxnum_mcaddr 0x%x maxnum_tx_wcb 0x%x"
- "regioncode 0x%x num_antenna 0x%x fw_releasenum 0x%x"
- "wcbbase0 0x%x rxdesc_read 0x%x rxdesc_write 0x%x"
- "ul_fw_awakecookie 0x%x w[4] = %x %x %x %x",
- sc->malo_hwspecs.hwversion,
- sc->malo_hwspecs.hostinterface, sc->malo_hwspecs.maxnum_wcb,
- sc->malo_hwspecs.maxnum_mcaddr,
- sc->malo_hwspecs.maxnum_tx_wcb,
- sc->malo_hwspecs.regioncode, sc->malo_hwspecs.num_antenna,
- sc->malo_hwspecs.fw_releasenum, sc->malo_hwspecs.wcbbase0,
- sc->malo_hwspecs.rxdesc_read, sc->malo_hwspecs.rxdesc_write,
- sc->malo_hwspecs.ul_fw_awakecookie,
- sc->malo_hwspecs.wcbbase[0], sc->malo_hwspecs.wcbbase[1],
- sc->malo_hwspecs.wcbbase[2], sc->malo_hwspecs.wcbbase[3]);
-
- error = malo_setup_hwdma(sc); /* push to firmware */
- /* NB: malo_setupdma prints msg */
- if (error != 0) {
- if_printf(ifp, "%s: failed to set up h/w dma\n",
- __func__);
- return;
- }
-
- /* set reddomain. */
- ic->ic_regdomain = sc->malo_hwspecs.regioncode;
-
- malo_announce(sc);
-
- sc->malo_fw_loaded = 1;
- }
-
- MALO_LOCK(sc);
+ MALO_LOCK_ASSERT(sc);
/*
* Stop anything previously setup. This is safe whether this is
@@ -1584,7 +1532,7 @@ malo_init(void *arg)
*/
if (!malo_hal_reset(sc)) {
if_printf(ifp, "%s: unable to reset hardware\n", __func__);
- goto done;
+ return;
}
/*
@@ -1594,7 +1542,7 @@ malo_init(void *arg)
if (error != 0) {
if_printf(ifp, "%s: unable to start recv logic, error %d\n",
__func__, error);
- goto done;
+ return;
}
/*
@@ -1610,30 +1558,26 @@ malo_init(void *arg)
| MALO_A2HRIC_BIT_CHAN_SWITCH;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
- ic->ic_state = IEEE80211_S_INIT;
- IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
-
malo_hal_intrset(mh, sc->malo_imask);
+}
- /*
- * The hardware should be ready to go now so it's safe to kick
- * the 802.11 state machine as it's likely to immediately call back
- * to us to send mgmt frames.
- */
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+static void
+malo_init(void *arg)
+{
+ struct malo_softc *sc = (struct malo_softc *) arg;
+ struct ifnet *ifp = sc->malo_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ DPRINTF(sc, MALO_DEBUG_ANY, "%s: if_flags 0x%x\n",
+ __func__, ifp->if_flags);
-done:
- if (error != 0)
- if_printf(ifp,
- "error(%d) occurred during the initializing.\n", error);
+ MALO_LOCK(sc);
+ malo_init_locked(sc);
MALO_UNLOCK(sc);
- return;
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vap's */
}
/*
@@ -1642,9 +1586,9 @@ done:
static void
malo_setmcastfilter(struct malo_softc *sc)
{
- struct ieee80211com *ic = &sc->malo_ic;
- struct ifmultiaddr *ifma;
struct ifnet *ifp = sc->malo_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifmultiaddr *ifma;
uint8_t macs[IEEE80211_ADDR_LEN * MALO_HAL_MCAST_MAX];
uint8_t *mp;
int nmc;
@@ -1686,8 +1630,8 @@ all:
static int
malo_mode_init(struct malo_softc *sc)
{
- struct ieee80211com *ic = &sc->malo_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->malo_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct malo_hal *mh = sc->malo_mh;
/*
@@ -1733,11 +1677,12 @@ malo_tx_draintxq(struct malo_softc *sc, struct malo_txq *txq)
MALO_TXQ_UNLOCK(txq);
#ifdef MALO_DEBUG
if (sc->malo_debug & MALO_DEBUG_RESET) {
+ struct ifnet *ifp = sc->malo_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
const struct malo_txrec *tr =
mtod(bf->bf_m, const struct malo_txrec *);
malo_printtxbuf(bf, txq->qnum, ix);
- ieee80211_dump_pkt(&sc->malo_ic,
- (const uint8_t *)&tr->wh,
+ ieee80211_dump_pkt(ic, (const uint8_t *)&tr->wh,
bf->bf_m->m_len - sizeof(tr->fwlen), 0, -1);
}
#endif /* MALO_DEBUG */
@@ -1763,10 +1708,9 @@ malo_tx_draintxq(struct malo_softc *sc, struct malo_txq *txq)
static void
malo_stop_locked(struct ifnet *ifp, int disable)
{
- int i;
struct malo_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->malo_ic;
struct malo_hal *mh = sc->malo_mh;
+ int i;
DPRINTF(sc, MALO_DEBUG_ANY, "%s: invalid %u if_flags 0x%x\n",
__func__, sc->malo_invalid, ifp->if_flags);
@@ -1778,28 +1722,19 @@ malo_stop_locked(struct ifnet *ifp, int disable)
/*
* Shutdown the hardware and driver:
- * reset 802.11 state machine
- * turn off timers
* disable interrupts
* turn off the radio
- * clear transmit machinery
- * clear receive machinery
* drain and release tx queues
- * reclaim beacon resources
- * power down hardware
*
* Note that some of this work is not possible if the hardware
* is gone (invalid).
*/
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
ifp->if_timer = 0;
- if (sc->malo_fw_loaded == 1) {
- /* diable interrupt. */
- malo_hal_intrset(mh, 0);
- /* turn off the radio. */
- malo_hal_setradio(mh, 0, MHP_AUTO_PREAMBLE);
- }
+ /* diable interrupt. */
+ malo_hal_intrset(mh, 0);
+ /* turn off the radio. */
+ malo_hal_setradio(mh, 0, MHP_AUTO_PREAMBLE);
/* drain and release tx queues. */
for (i = 0; i < MALO_NUM_TX_QUEUES; i++)
@@ -1812,11 +1747,11 @@ malo_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
#define MALO_IS_RUNNING(ifp) \
((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
struct malo_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->malo_ic;
- int error = 0;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
MALO_LOCK(sc);
-
switch (cmd) {
case SIOCSIFFLAGS:
if (MALO_IS_RUNNING(ifp)) {
@@ -1836,38 +1771,25 @@ malo_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
* torn down much of our state. There's
* probably a better way to deal with this.
*/
- if (!sc->malo_invalid)
- malo_init(sc);
+ if (!sc->malo_invalid) {
+ malo_init_locked(sc);
+ startall = 1;
+ }
} else
malo_stop_locked(ifp, 1);
break;
- case SIOCADDMULTI:
- case SIOCDELMULTI:
- /*
- * The upper layer has already installed/removed
- * the multicast address(es), just recalculate the
- * multicast filter for the card.
- */
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- malo_mode_init(sc);
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
break;
default:
- error = ieee80211_ioctl(ic, cmd, data);
- if (error == ENETRESET) {
- if (MALO_IS_RUNNING(ifp) &&
- ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- malo_init(sc);
- error = 0;
- }
- if (error == ERESTART) {
- /* XXX we need to reset the device here. */
- error = 0;
- }
+ error = ether_ioctl(ifp, cmd, data);
break;
}
-
MALO_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
return error;
#undef MALO_IS_RUNNING
}
@@ -1882,7 +1804,7 @@ static void
malo_updateslot(struct ifnet *ifp)
{
struct malo_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->malo_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
struct malo_hal *mh = sc->malo_mh;
int error;
@@ -1906,72 +1828,46 @@ malo_updateslot(struct ifnet *ifp)
}
static int
-malo_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+malo_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
- struct ieee80211_node *ni = ic->ic_bss;
- struct ifnet *ifp = ic->ic_ifp;
- struct malo_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct malo_softc *sc = ic->ic_ifp->if_softc;
struct malo_hal *mh = sc->malo_mh;
int error;
DPRINTF(sc, MALO_DEBUG_STATE, "%s: %s -> %s\n", __func__,
- ieee80211_state_name[ic->ic_state],
+ ieee80211_state_name[vap->iv_state],
ieee80211_state_name[nstate]);
/*
- * Carry out firmware actions per-state.
+ * Invoke the net80211 layer first so iv_bss is setup.
*/
- switch (nstate) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- case IEEE80211_S_AUTH:
- /* NB: do nothing. */
- break;
- case IEEE80211_S_ASSOC:
- malo_hal_setradio(mh, 1,
- (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ?
- MHP_SHORT_PREAMBLE : MHP_LONG_PREAMBLE);
- break;
- case IEEE80211_S_RUN:
+ error = MALO_VAP(vap)->malo_newstate(vap, nstate, arg);
+ if (error != 0)
+ return error;
+
+ if (nstate == IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_RUN) {
+ struct ieee80211_node *ni = vap->iv_bss;
+ enum ieee80211_phymode mode = ieee80211_chan2mode(ni->ni_chan);
+ const struct ieee80211_txparam *tp = &vap->iv_txparms[mode];
+
DPRINTF(sc, MALO_DEBUG_STATE,
- "%s: %s(RUN): ic_flags 0x%08x bintvl %d bssid %s "
- "capinfo 0x%04x chan %d\n",
- ifp->if_xname, __func__, ic->ic_flags,
+ "%s: %s(RUN): iv_flags 0x%08x bintvl %d bssid %s "
+ "capinfo 0x%04x chan %d associd 0x%x mode %d rate %d\n",
+ vap->iv_ifp->if_xname, __func__, vap->iv_flags,
ni->ni_intval, ether_sprintf(ni->ni_bssid), ni->ni_capinfo,
- ieee80211_chan2ieee(ic, ic->ic_curchan));
-
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- DPRINTF(sc, MALO_DEBUG_STATE, "%s: %s: aid 0x%x\n",
- ic->ic_ifp->if_xname, __func__, ni->ni_associd);
- malo_hal_setassocid(sc->malo_mh,
- ni->ni_bssid, ni->ni_associd);
-
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)
- /* automatic rate adaption */
- malo_hal_set_rate(mh, ic->ic_curmode, 0);
- else
- /* fixed rate */
- malo_hal_set_rate(mh, ic->ic_curmode,
- malo_fix2rate(ic->ic_fixed_rate));
- break;
- default:
- break;
- }
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ ni->ni_associd, mode, tp->ucastrate);
- break;
- default:
- if_printf(ifp, "%s: can't handle state %s -> %s\n",
- __func__, ieee80211_state_name[ic->ic_state],
- ieee80211_state_name[nstate]);
+ malo_hal_setradio(mh, 1,
+ (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ?
+ MHP_SHORT_PREAMBLE : MHP_LONG_PREAMBLE);
+ malo_hal_setassocid(sc->malo_mh, ni->ni_bssid, ni->ni_associd);
+ malo_hal_set_rate(mh, mode,
+ tp->ucastrate == IEEE80211_FIXED_RATE_NONE ?
+ 0 : malo_fix2rate(tp->ucastrate));
}
-
- /*
- * Invoke the parent method to complete the work.
- */
- error = sc->malo_newstate(ic, nstate, arg);
-
- return error;
+ return 0;
}
static int
@@ -2038,33 +1934,13 @@ malo_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
return 0;
}
-static int
-malo_media_change(struct ifnet *ifp)
-{
-#define IS_UP(ifp) \
- ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
- int error;
-
- error = ieee80211_media_change(ifp);
- if (error == ENETRESET) {
- struct malo_softc *sc = ifp->if_softc;
-
- if (IS_UP(ifp))
- malo_init(sc);
- error = 0;
- }
- return error;
-#undef IS_UP
-}
-
static void
malo_bpfattach(struct malo_softc *sc)
{
struct ifnet *ifp = sc->malo_ifp;
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof(struct ieee80211_frame) + sizeof(sc->malo_tx_th),
- &sc->malo_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof(struct ieee80211_frame) + sizeof(sc->malo_tx_th));
/*
* Initialize constant fields.
@@ -2206,9 +2082,9 @@ malo_rx_proc(void *arg, int npending)
((((const struct ieee80211_frame *)wh)->i_fc[1] & \
IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
struct malo_softc *sc = arg;
- struct malo_rxbuf *bf;
- struct ieee80211com *ic = &sc->malo_ic;
struct ifnet *ifp = sc->malo_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct malo_rxbuf *bf;
struct malo_rxdesc *ds;
struct mbuf *m, *mnew;
struct ieee80211_qosframe *wh;
@@ -2232,8 +2108,7 @@ malo_rx_proc(void *arg, int npending)
return;
bf = sc->malo_rxnext;
- for (ntodo = malo_rxquota; ntodo > 0 && (readptr != writeptr);
- ntodo--) {
+ for (ntodo = malo_rxquota; ntodo > 0 && readptr != writeptr; ntodo--) {
if (bf == NULL) {
bf = STAILQ_FIRST(&sc->malo_rxbuf);
break;
@@ -2281,12 +2156,12 @@ malo_rx_proc(void *arg, int npending)
* payload prior to constructing the header.
*/
m = bf->bf_m;
- data = mtod(m, uint8_t *);
+ data = mtod(m, uint8_t *);;
hdrlen = ieee80211_anyhdrsize(data + sizeof(uint16_t));
off = sizeof(uint16_t) + sizeof(struct ieee80211_frame_addr4);
/*
- * Calculate RSSI. XXX wrong
+ * Calculate RSSI. XXX wrong
*/
rssi = 2 * ((int) ds->snr - ds->nf); /* NB: .5 dBm */
if (rssi > 100)
@@ -2307,7 +2182,6 @@ malo_rx_proc(void *arg, int npending)
ifp->if_ierrors++;
goto rx_next;
}
-
/*
* Attach the dma buffer to the mbuf; malo_rxbuf_init will
* re-setup the rx descriptor using the replacement dma
@@ -2340,8 +2214,8 @@ malo_rx_proc(void *arg, int npending)
sc->malo_rx_th.wr_antsignal = rssi;
sc->malo_rx_th.wr_antnoise = ds->nf;
- bpf_mtap2(sc->malo_drvbpf,
- &sc->malo_rx_th, sc->malo_rx_th_len, m);
+ bpf_mtap2(ifp->if_bpf, &sc->malo_rx_th,
+ sc->malo_rx_th_len, m);
}
#ifdef MALO_DEBUG
if (IFF_DUMPPKTS_RECV(sc, wh)) {
@@ -2353,10 +2227,12 @@ malo_rx_proc(void *arg, int npending)
/* dispatch */
ni = ieee80211_find_rxnode(ic,
- (const struct ieee80211_frame_min *) wh);
- (void) ieee80211_input(ic, m, ni, rssi, ds->nf, 0/*XXX*/);
- ieee80211_free_node(ni);
-
+ (struct ieee80211_frame_min *)wh);
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, rssi, ds->nf, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, ds->nf, 0);
rx_next:
/* NB: ignore ENOMEM so we process more descriptors */
(void) malo_rxbuf_init(sc, bf);
@@ -2378,9 +2254,7 @@ malo_stop(struct ifnet *ifp, int disable)
struct malo_softc *sc = ifp->if_softc;
MALO_LOCK(sc);
-
malo_stop_locked(ifp, disable);
-
MALO_UNLOCK(sc);
}
@@ -2400,6 +2274,7 @@ int
malo_detach(struct malo_softc *sc)
{
struct ifnet *ifp = sc->malo_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
DPRINTF(sc, MALO_DEBUG_ANY, "%s: if_flags %x\n",
__func__, ifp->if_flags);
@@ -2427,7 +2302,7 @@ malo_detach(struct malo_softc *sc)
* it last
* Other than that, it's straightforward...
*/
- ieee80211_ifdetach(&sc->malo_ic);
+ ieee80211_ifdetach(ic);
malo_dma_cleanup(sc);
malo_tx_cleanup(sc);
malo_hal_detach(sc->malo_mh);
@@ -2441,7 +2316,6 @@ malo_detach(struct malo_softc *sc)
void
malo_shutdown(struct malo_softc *sc)
{
-
malo_stop(sc->malo_ifp, 1);
}
@@ -2464,9 +2338,6 @@ malo_resume(struct malo_softc *sc)
DPRINTF(sc, MALO_DEBUG_ANY, "%s: if_flags %x\n",
__func__, ifp->if_flags);
- if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_flags & IFF_UP)
malo_init(sc);
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- malo_start(ifp);
- }
}
diff --git a/sys/dev/malo/if_malo.h b/sys/dev/malo/if_malo.h
index 0cb5bc6..070649a 100644
--- a/sys/dev/malo/if_malo.h
+++ b/sys/dev/malo/if_malo.h
@@ -512,8 +512,14 @@ struct malo_txrec {
struct ieee80211_frame_addr4 wh;
} __packed;
+struct malo_vap {
+ struct ieee80211vap malo_vap;
+ int (*malo_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define MALO_VAP(vap) ((struct malo_vap *)(vap))
+
struct malo_softc {
- struct ieee80211com malo_ic; /* IEEE 802.11 common */
device_t malo_dev;
struct ifnet *malo_ifp; /* interface common */
struct mtx malo_mtx; /* master lock (recursive) */
@@ -527,8 +533,7 @@ struct malo_softc {
unsigned int malo_invalid : 1,/* disable hardware accesses */
malo_recvsetup : 1, /* recv setup */
- malo_fixedrate : 1, /* use fixed tx rate */
- malo_fw_loaded : 1; /* fw loaded */
+ malo_fixedrate: 1; /* use fixed tx rate */
struct malo_hal *malo_mh; /* h/w access layer */
struct malo_hal_hwspec malo_hwspecs; /* h/w capabilities */
@@ -546,9 +551,6 @@ struct malo_softc {
struct malo_txq malo_txq[MALO_NUM_TX_QUEUES];
struct task malo_txtask; /* tx int processing */
- int (*malo_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
-
struct bpf_if *malo_drvbpf;
struct malo_tx_radiotap_header malo_tx_th;
int malo_tx_th_len;
diff --git a/sys/dev/ral/if_ral_pci.c b/sys/dev/ral/if_ral_pci.c
index 2ceafe4..a36218b 100644
--- a/sys/dev/ral/if_ral_pci.c
+++ b/sys/dev/ral/if_ral_pci.c
@@ -50,16 +50,18 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_amrr.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
-#include <dev/ral/if_ralrate.h>
#include <dev/ral/rt2560var.h>
#include <dev/ral/rt2661var.h>
MODULE_DEPEND(ral, pci, 1, 1, 1);
+MODULE_DEPEND(ral, firmware, 1, 1, 1);
MODULE_DEPEND(ral, wlan, 1, 1, 1);
+MODULE_DEPEND(ral, wlan_amrr, 1, 1, 1);
struct ral_pci_ident {
uint16_t vendor;
diff --git a/sys/dev/ral/if_ralrate.c b/sys/dev/ral/if_ralrate.c
deleted file mode 100644
index b8922ba..0000000
--- a/sys/dev/ral/if_ralrate.c
+++ /dev/null
@@ -1,192 +0,0 @@
-/* $FreeBSD$ */
-/* $NetBSD: ieee80211_rssadapt.c,v 1.9 2005/02/26 22:45:09 perry Exp $ */
-/*-
- * Copyright (c) 2003, 2004 David Young. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- * 3. The name of David Young may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David
- * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- */
-
-#include <sys/param.h>
-#include <sys/sockio.h>
-#include <sys/mbuf.h>
-#include <sys/kernel.h>
-#include <sys/socket.h>
-
-#include <net/if.h>
-#include <net/if_arp.h>
-#include <net/ethernet.h>
-#include <net/if_dl.h>
-#include <net/if_media.h>
-#include <net/if_types.h>
-
-#include <net80211/ieee80211_var.h>
-
-#include <dev/ral/if_ralrate.h>
-
-#ifdef interpolate
-#undef interpolate
-#endif
-#define interpolate(parm, old, new) ((parm##_old * (old) + \
- (parm##_denom - parm##_old) * (new)) / \
- parm##_denom)
-
-static struct ral_rssadapt_expavgctl master_expavgctl = {
- rc_decay_denom : 16,
- rc_decay_old : 15,
- rc_thresh_denom : 8,
- rc_thresh_old : 4,
- rc_avgrssi_denom : 8,
- rc_avgrssi_old : 4
-};
-
-int
-ral_rssadapt_choose(struct ral_rssadapt *ra, struct ieee80211_rateset *rs,
- struct ieee80211_frame *wh, u_int len, const char *dvname, int do_not_adapt)
-{
- u_int16_t (*thrs)[IEEE80211_RATE_SIZE];
- int flags = 0, i, rateidx = 0, thridx, top;
-
- if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
- flags |= IEEE80211_RATE_BASIC;
-
- for (i = 0, top = RAL_RSSADAPT_BKT0;
- i < RAL_RSSADAPT_BKTS;
- i++, top <<= RAL_RSSADAPT_BKTPOWER) {
- thridx = i;
- if (len <= top)
- break;
- }
-
- thrs = &ra->ra_rate_thresh[thridx];
-
- i = rs->rs_nrates;
- while (--i >= 0) {
- rateidx = i;
- if ((rs->rs_rates[i] & flags) != flags)
- continue;
- if (do_not_adapt)
- break;
- if ((*thrs)[i] < ra->ra_avg_rssi)
- break;
- }
-
- return rateidx;
-}
-
-void
-ral_rssadapt_updatestats(struct ral_rssadapt *ra)
-{
- long interval;
-
- ra->ra_pktrate =
- (ra->ra_pktrate + 10 * (ra->ra_nfail + ra->ra_nok)) / 2;
- ra->ra_nfail = ra->ra_nok = 0;
-
- /* a node is eligible for its rate to be raised every 1/10 to 10
- * seconds, more eligible in proportion to recent packet rates.
- */
- interval = MAX(100000, 10000000 / MAX(1, 10 * ra->ra_pktrate));
- ra->ra_raise_interval.tv_sec = interval / (1000 * 1000);
- ra->ra_raise_interval.tv_usec = interval % (1000 * 1000);
-}
-
-void
-ral_rssadapt_input(struct ieee80211com *ic, struct ieee80211_node *ni,
- struct ral_rssadapt *ra, int rssi)
-{
- ra->ra_avg_rssi = interpolate(master_expavgctl.rc_avgrssi,
- ra->ra_avg_rssi, (rssi << 8));
-}
-
-/*
- * Adapt the data rate to suit the conditions. When a transmitted
- * packet is dropped after RAL_RSSADAPT_RETRY_LIMIT retransmissions,
- * raise the RSS threshold for transmitting packets of similar length at
- * the same data rate.
- */
-void
-ral_rssadapt_lower_rate(struct ieee80211com *ic, struct ieee80211_node *ni,
- struct ral_rssadapt *ra, struct ral_rssdesc *id)
-{
- struct ieee80211_rateset *rs = &ni->ni_rates;
- u_int16_t last_thr;
- u_int i, thridx, top;
-
- ra->ra_nfail++;
-
- if (id->id_rateidx >= rs->rs_nrates)
- return;
-
- for (i = 0, top = RAL_RSSADAPT_BKT0;
- i < RAL_RSSADAPT_BKTS;
- i++, top <<= RAL_RSSADAPT_BKTPOWER) {
- thridx = i;
- if (id->id_len <= top)
- break;
- }
-
- last_thr = ra->ra_rate_thresh[thridx][id->id_rateidx];
- ra->ra_rate_thresh[thridx][id->id_rateidx] =
- interpolate(master_expavgctl.rc_thresh, last_thr,
- (id->id_rssi << 8));
-}
-
-void
-ral_rssadapt_raise_rate(struct ieee80211com *ic, struct ral_rssadapt *ra,
- struct ral_rssdesc *id)
-{
- u_int16_t (*thrs)[IEEE80211_RATE_SIZE], newthr, oldthr;
- struct ieee80211_node *ni = id->id_node;
- struct ieee80211_rateset *rs = &ni->ni_rates;
- int i, rate, top;
-
- ra->ra_nok++;
-
- if (!ratecheck(&ra->ra_last_raise, &ra->ra_raise_interval))
- return;
-
- for (i = 0, top = RAL_RSSADAPT_BKT0;
- i < RAL_RSSADAPT_BKTS;
- i++, top <<= RAL_RSSADAPT_BKTPOWER) {
- thrs = &ra->ra_rate_thresh[i];
- if (id->id_len <= top)
- break;
- }
-
- if (id->id_rateidx + 1 < rs->rs_nrates &&
- (*thrs)[id->id_rateidx + 1] > (*thrs)[id->id_rateidx]) {
- rate = (rs->rs_rates[id->id_rateidx + 1] & IEEE80211_RATE_VAL);
-
- oldthr = (*thrs)[id->id_rateidx + 1];
- if ((*thrs)[id->id_rateidx] == 0)
- newthr = ra->ra_avg_rssi;
- else
- newthr = (*thrs)[id->id_rateidx];
- (*thrs)[id->id_rateidx + 1] =
- interpolate(master_expavgctl.rc_decay, oldthr, newthr);
- }
-}
diff --git a/sys/dev/ral/rt2560.c b/sys/dev/ral/rt2560.c
index 63f7de0..1536a35 100644
--- a/sys/dev/ral/rt2560.c
+++ b/sys/dev/ral/rt2560.c
@@ -52,8 +52,10 @@ __FBSDID("$FreeBSD$");
#include <net/if_types.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_phy.h>
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_amrr.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
@@ -61,7 +63,6 @@ __FBSDID("$FreeBSD$");
#include <netinet/ip.h>
#include <netinet/if_ether.h>
-#include <dev/ral/if_ralrate.h>
#include <dev/ral/rt2560reg.h>
#include <dev/ral/rt2560var.h>
@@ -69,15 +70,26 @@ __FBSDID("$FreeBSD$");
((rssi) > (RT2560_NOISE_FLOOR + (sc)->rssi_corr) ? \
((rssi) - RT2560_NOISE_FLOOR - (sc)->rssi_corr) : 0)
+#define RAL_DEBUG
#ifdef RAL_DEBUG
-#define DPRINTF(x) do { if (ral_debug > 0) printf x; } while (0)
-#define DPRINTFN(n, x) do { if (ral_debug >= (n)) printf x; } while (0)
-extern int ral_debug;
+#define DPRINTF(sc, fmt, ...) do { \
+ if (sc->sc_debug > 0) \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
+#define DPRINTFN(sc, n, fmt, ...) do { \
+ if (sc->sc_debug >= (n)) \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
#else
-#define DPRINTF(x)
-#define DPRINTFN(n, x)
+#define DPRINTF(sc, fmt, ...)
+#define DPRINTFN(sc, n, fmt, ...)
#endif
+static struct ieee80211vap *rt2560_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void rt2560_vap_delete(struct ieee80211vap *);
static void rt2560_dma_map_addr(void *, bus_dma_segment_t *, int,
int);
static int rt2560_alloc_tx_ring(struct rt2560_softc *,
@@ -94,10 +106,8 @@ static void rt2560_free_rx_ring(struct rt2560_softc *,
struct rt2560_rx_ring *);
static struct ieee80211_node *rt2560_node_alloc(
struct ieee80211_node_table *);
-static int rt2560_media_change(struct ifnet *);
-static void rt2560_iter_func(void *, struct ieee80211_node *);
-static void rt2560_update_rssadapt(void *);
-static int rt2560_newstate(struct ieee80211com *,
+static void rt2560_newassoc(struct ieee80211_node *, int);
+static int rt2560_newstate(struct ieee80211vap *,
enum ieee80211_state, int);
static uint16_t rt2560_eeprom_read(struct rt2560_softc *, uint8_t);
static void rt2560_encryption_intr(struct rt2560_softc *);
@@ -105,16 +115,12 @@ static void rt2560_tx_intr(struct rt2560_softc *);
static void rt2560_prio_intr(struct rt2560_softc *);
static void rt2560_decryption_intr(struct rt2560_softc *);
static void rt2560_rx_intr(struct rt2560_softc *);
-static void rt2560_beacon_update(struct ieee80211com *, int item);
+static void rt2560_beacon_update(struct ieee80211vap *, int item);
static void rt2560_beacon_expire(struct rt2560_softc *);
static void rt2560_wakeup_expire(struct rt2560_softc *);
-static uint8_t rt2560_rxrate(struct rt2560_rx_desc *);
-static int rt2560_ack_rate(struct ieee80211com *, int);
static void rt2560_scan_start(struct ieee80211com *);
static void rt2560_scan_end(struct ieee80211com *);
static void rt2560_set_channel(struct ieee80211com *);
-static uint16_t rt2560_txtime(int, int, uint32_t);
-static uint8_t rt2560_plcp_signal(int);
static void rt2560_setup_tx_desc(struct rt2560_softc *,
struct rt2560_tx_desc *, uint32_t, int, int, int,
bus_addr_t);
@@ -122,13 +128,11 @@ static int rt2560_tx_bcn(struct rt2560_softc *, struct mbuf *,
struct ieee80211_node *);
static int rt2560_tx_mgt(struct rt2560_softc *, struct mbuf *,
struct ieee80211_node *);
-static struct mbuf *rt2560_get_rts(struct rt2560_softc *,
- struct ieee80211_frame *, uint16_t);
static int rt2560_tx_data(struct rt2560_softc *, struct mbuf *,
struct ieee80211_node *);
+static void rt2560_start_locked(struct ifnet *);
static void rt2560_start(struct ifnet *);
static void rt2560_watchdog(void *);
-static int rt2560_reset(struct ifnet *);
static int rt2560_ioctl(struct ifnet *, u_long, caddr_t);
static void rt2560_bbp_write(struct rt2560_softc *, uint8_t,
uint8_t);
@@ -148,13 +152,15 @@ static void rt2560_update_led(struct rt2560_softc *, int, int);
static void rt2560_set_bssid(struct rt2560_softc *, const uint8_t *);
static void rt2560_set_macaddr(struct rt2560_softc *, uint8_t *);
static void rt2560_get_macaddr(struct rt2560_softc *, uint8_t *);
-static void rt2560_update_promisc(struct rt2560_softc *);
+static void rt2560_update_promisc(struct ifnet *);
static const char *rt2560_get_rf(int);
static void rt2560_read_config(struct rt2560_softc *);
static int rt2560_bbp_init(struct rt2560_softc *);
static void rt2560_set_txantenna(struct rt2560_softc *, int);
static void rt2560_set_rxantenna(struct rt2560_softc *, int);
+static void rt2560_init_locked(struct rt2560_softc *);
static void rt2560_init(void *);
+static void rt2560_stop_locked(struct rt2560_softc *);
static int rt2560_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
@@ -192,9 +198,10 @@ int
rt2560_attach(device_t dev, int id)
{
struct rt2560_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
struct ifnet *ifp;
- int error, bands;
+ int error;
+ uint8_t bands;
sc->sc_dev = dev;
@@ -202,14 +209,10 @@ rt2560_attach(device_t dev, int id)
MTX_DEF | MTX_RECURSE);
callout_init_mtx(&sc->watchdog_ch, &sc->sc_mtx, 0);
- callout_init(&sc->rssadapt_ch, CALLOUT_MPSAFE);
/* retrieve RT2560 rev. no */
sc->asic_rev = RAL_READ(sc, RT2560_CSR0);
- /* retrieve MAC address */
- rt2560_get_macaddr(sc, ic->ic_myaddr);
-
/* retrieve RF rev. no and various other things from EEPROM */
rt2560_read_config(sc);
@@ -249,11 +252,15 @@ rt2560_attach(device_t dev, int id)
goto fail5;
}
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(sc->sc_dev, "can not if_alloc()\n");
goto fail6;
}
+ ic = ifp->if_l2com;
+
+ /* retrieve MAC address */
+ rt2560_get_macaddr(sc, ic->ic_myaddr);
ifp->if_softc = sc;
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
@@ -266,48 +273,47 @@ rt2560_attach(device_t dev, int id)
IFQ_SET_READY(&ifp->if_snd);
ic->ic_ifp = ifp;
+ ic->ic_opmode = IEEE80211_M_STA;
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
- ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
- ic->ic_state = IEEE80211_S_INIT;
/* set device capabilities */
ic->ic_caps =
- IEEE80211_C_IBSS | /* IBSS mode supported */
- IEEE80211_C_MONITOR | /* monitor mode supported */
- IEEE80211_C_HOSTAP | /* HostAp mode supported */
- IEEE80211_C_TXPMGT | /* tx power management */
- IEEE80211_C_SHPREAMBLE | /* short preamble supported */
- IEEE80211_C_SHSLOT | /* short slot time supported */
- IEEE80211_C_BGSCAN | /* bg scanning support */
- IEEE80211_C_WPA; /* 802.11i */
+ IEEE80211_C_IBSS /* ibss, nee adhoc, mode */
+ | IEEE80211_C_HOSTAP /* hostap mode */
+ | IEEE80211_C_MONITOR /* monitor mode */
+ | IEEE80211_C_AHDEMO /* adhoc demo mode */
+ | IEEE80211_C_WDS /* 4-address traffic works */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_WPA /* capable of WPA1+WPA2 */
+ | IEEE80211_C_BGSCAN /* capable of bg scanning */
+#ifdef notyet
+ | IEEE80211_C_TXFRAG /* handle tx frags */
+#endif
+ ;
bands = 0;
setbit(&bands, IEEE80211_MODE_11B);
setbit(&bands, IEEE80211_MODE_11G);
if (sc->rf_rev == RT2560_RF_5222)
setbit(&bands, IEEE80211_MODE_11A);
- ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
+ ieee80211_init_channels(ic, NULL, &bands);
ieee80211_ifattach(ic);
+ ic->ic_newassoc = rt2560_newassoc;
+ ic->ic_raw_xmit = rt2560_raw_xmit;
+ ic->ic_updateslot = rt2560_update_slot;
+ ic->ic_update_promisc = rt2560_update_promisc;
+ ic->ic_node_alloc = rt2560_node_alloc;
ic->ic_scan_start = rt2560_scan_start;
ic->ic_scan_end = rt2560_scan_end;
ic->ic_set_channel = rt2560_set_channel;
- ic->ic_node_alloc = rt2560_node_alloc;
- ic->ic_updateslot = rt2560_update_slot;
- ic->ic_reset = rt2560_reset;
- /* enable s/w bmiss handling in sta mode */
- ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;
- /* override state transition machine */
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = rt2560_newstate;
- ic->ic_raw_xmit = rt2560_raw_xmit;
- ic->ic_update_beacon = rt2560_beacon_update;
- ieee80211_media_init(ic, rt2560_media_change, ieee80211_media_status);
+ ic->ic_vap_create = rt2560_vap_create;
+ ic->ic_vap_delete = rt2560_vap_delete;
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap),
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap));
sc->sc_rxtap_len = sizeof sc->sc_rxtap;
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
@@ -320,8 +326,11 @@ rt2560_attach(device_t dev, int id)
/*
* Add a few sysctl knobs.
*/
- sc->dwelltime = 200;
-
+#ifdef RAL_DEBUG
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "debug", CTLFLAG_RW, &sc->sc_debug, 0, "debug msgs");
+#endif
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
"txantenna", CTLFLAG_RW, &sc->tx_ant, 0, "tx antenna (0=auto)");
@@ -330,11 +339,6 @@ rt2560_attach(device_t dev, int id)
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
"rxantenna", CTLFLAG_RW, &sc->rx_ant, 0, "rx antenna (0=auto)");
- SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "dwell",
- CTLFLAG_RW, &sc->dwelltime, 0,
- "channel dwell time (ms) for AP/station scanning");
-
if (bootverbose)
ieee80211_announce(ic);
@@ -354,11 +358,10 @@ int
rt2560_detach(void *xsc)
{
struct rt2560_softc *sc = xsc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
rt2560_stop(sc);
- callout_stop(&sc->rssadapt_ch);
bpfdetach(ifp);
ieee80211_ifdetach(ic);
@@ -376,17 +379,88 @@ rt2560_detach(void *xsc)
return 0;
}
+static struct ieee80211vap *
+rt2560_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct rt2560_vap *rvp;
+ struct ieee80211vap *vap;
+
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
+ case IEEE80211_M_MONITOR:
+ case IEEE80211_M_HOSTAP:
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) {
+ if_printf(ifp, "only 1 vap supported\n");
+ return NULL;
+ }
+ if (opmode == IEEE80211_M_STA)
+ flags |= IEEE80211_CLONE_NOBEACONS;
+ break;
+ case IEEE80211_M_WDS:
+ if (TAILQ_EMPTY(&ic->ic_vaps) ||
+ ic->ic_opmode != IEEE80211_M_HOSTAP) {
+ if_printf(ifp, "wds only supported in ap mode\n");
+ return NULL;
+ }
+ /*
+ * Silently remove any request for a unique
+ * bssid; WDS vap's always share the local
+ * mac address.
+ */
+ flags &= ~IEEE80211_CLONE_BSSID;
+ break;
+ default:
+ if_printf(ifp, "unknown opmode %d\n", opmode);
+ return NULL;
+ }
+ rvp = (struct rt2560_vap *) malloc(sizeof(struct rt2560_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (rvp == NULL)
+ return NULL;
+ vap = &rvp->ral_vap;
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
+
+ /* override state transition machine */
+ rvp->ral_newstate = vap->iv_newstate;
+ vap->iv_newstate = rt2560_newstate;
+ vap->iv_update_beacon = rt2560_beacon_update;
+
+ ieee80211_amrr_init(&rvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 500 /* ms */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ if (TAILQ_FIRST(&ic->ic_vaps) == vap)
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+rt2560_vap_delete(struct ieee80211vap *vap)
+{
+ struct rt2560_vap *rvp = RT2560_VAP(vap);
+
+ ieee80211_amrr_cleanup(&rvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(rvp, M_80211_VAP);
+}
+
void
rt2560_resume(void *xsc)
{
struct rt2560_softc *sc = xsc;
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
- if (ifp->if_flags & IFF_UP) {
- ifp->if_init(ifp->if_softc);
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- ifp->if_start(ifp);
- }
+ if (ifp->if_flags & IFF_UP)
+ rt2560_init(sc);
}
static void
@@ -702,117 +776,68 @@ rt2560_node_alloc(struct ieee80211_node_table *nt)
return (rn != NULL) ? &rn->ni : NULL;
}
-static int
-rt2560_media_change(struct ifnet *ifp)
-{
- struct rt2560_softc *sc = ifp->if_softc;
- int error;
-
- error = ieee80211_media_change(ifp);
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING))
- rt2560_init(sc);
- }
- return error;
-}
-
-/*
- * This function is called for each node present in the node station table.
- */
static void
-rt2560_iter_func(void *arg, struct ieee80211_node *ni)
+rt2560_newassoc(struct ieee80211_node *ni, int isnew)
{
- struct rt2560_node *rn = (struct rt2560_node *)ni;
+ struct ieee80211vap *vap = ni->ni_vap;
- ral_rssadapt_updatestats(&rn->rssadapt);
-}
-
-/*
- * This function is called periodically (every 100ms) in RUN state to update
- * the rate adaptation statistics.
- */
-static void
-rt2560_update_rssadapt(void *arg)
-{
- struct rt2560_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
-
- RAL_LOCK(sc);
-
- ieee80211_iterate_nodes(&ic->ic_sta, rt2560_iter_func, arg);
- callout_reset(&sc->rssadapt_ch, hz / 10, rt2560_update_rssadapt, sc);
-
- RAL_UNLOCK(sc);
+ ieee80211_amrr_node_init(&RT2560_VAP(vap)->amrr,
+ &RT2560_NODE(ni)->amrr, ni);
}
static int
-rt2560_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+rt2560_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
- struct rt2560_softc *sc = ic->ic_ifp->if_softc;
- enum ieee80211_state ostate;
- struct ieee80211_node *ni;
- struct mbuf *m;
- int error = 0;
+ struct rt2560_vap *rvp = RT2560_VAP(vap);
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
+ struct rt2560_softc *sc = ifp->if_softc;
+ int error;
- ostate = ic->ic_state;
+ if (nstate == IEEE80211_S_INIT && vap->iv_state == IEEE80211_S_RUN) {
+ /* abort TSF synchronization */
+ RAL_WRITE(sc, RT2560_CSR14, 0);
- switch (nstate) {
- case IEEE80211_S_INIT:
- callout_stop(&sc->rssadapt_ch);
+ /* turn association led off */
+ rt2560_update_led(sc, 0, 0);
+ }
- if (ostate == IEEE80211_S_RUN) {
- /* abort TSF synchronization */
- RAL_WRITE(sc, RT2560_CSR14, 0);
+ error = rvp->ral_newstate(vap, nstate, arg);
- /* turn association led off */
- rt2560_update_led(sc, 0, 0);
- }
- break;
- case IEEE80211_S_RUN:
- ni = ic->ic_bss;
+ if (error == 0 && nstate == IEEE80211_S_RUN) {
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct mbuf *m;
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
rt2560_update_plcp(sc);
rt2560_set_basicrates(sc);
rt2560_set_bssid(sc, ni->ni_bssid);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS) {
- m = ieee80211_beacon_alloc(ni, &sc->sc_bo);
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS) {
+ m = ieee80211_beacon_alloc(ni, &rvp->ral_bo);
if (m == NULL) {
- device_printf(sc->sc_dev,
- "could not allocate beacon\n");
- error = ENOBUFS;
- break;
+ if_printf(ifp, "could not allocate beacon\n");
+ return ENOBUFS;
}
-
ieee80211_ref_node(ni);
error = rt2560_tx_bcn(sc, m, ni);
if (error != 0)
- break;
+ return error;
}
/* turn assocation led on */
rt2560_update_led(sc, 1, 0);
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- callout_reset(&sc->rssadapt_ch, hz / 10,
- rt2560_update_rssadapt, sc);
-
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+ /* fake a join to init the tx rate */
+ rt2560_newassoc(ni, 1);
+ }
rt2560_enable_tsf_sync(sc);
}
- break;
- case IEEE80211_S_SCAN:
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
- default:
- break;
}
-
- return (error != 0) ? error : sc->sc_newstate(ic, nstate, arg);
+ return error;
}
/*
@@ -912,8 +937,8 @@ rt2560_encryption_intr(struct rt2560_softc *sc)
desc->flags |= htole32(RT2560_TX_VALID);
desc->flags |= htole32(RT2560_TX_BUSY);
- DPRINTFN(15, ("encryption done idx=%u\n",
- sc->txq.next_encrypt));
+ DPRINTFN(sc, 15, "encryption done idx=%u\n",
+ sc->txq.next_encrypt);
sc->txq.next_encrypt =
(sc->txq.next_encrypt + 1) % RT2560_TX_RING_COUNT;
@@ -929,11 +954,13 @@ rt2560_encryption_intr(struct rt2560_softc *sc)
static void
rt2560_tx_intr(struct rt2560_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
struct rt2560_tx_desc *desc;
struct rt2560_tx_data *data;
struct rt2560_node *rn;
+ struct mbuf *m;
+ uint32_t flags;
+ int retrycnt;
bus_dmamap_sync(sc->txq.desc_dmat, sc->txq.desc_map,
BUS_DMASYNC_POSTREAD);
@@ -942,36 +969,43 @@ rt2560_tx_intr(struct rt2560_softc *sc)
desc = &sc->txq.desc[sc->txq.next];
data = &sc->txq.data[sc->txq.next];
- if ((le32toh(desc->flags) & RT2560_TX_BUSY) ||
- (le32toh(desc->flags) & RT2560_TX_CIPHER_BUSY) ||
- !(le32toh(desc->flags) & RT2560_TX_VALID))
+ flags = le32toh(desc->flags);
+ if ((flags & RT2560_TX_BUSY) ||
+ (flags & RT2560_TX_CIPHER_BUSY) ||
+ !(flags & RT2560_TX_VALID))
break;
rn = (struct rt2560_node *)data->ni;
+ m = data->m;
- switch (le32toh(desc->flags) & RT2560_TX_RESULT_MASK) {
+ switch (flags & RT2560_TX_RESULT_MASK) {
case RT2560_TX_SUCCESS:
- DPRINTFN(10, ("data frame sent successfully\n"));
- if (data->id.id_node != NULL) {
- ral_rssadapt_raise_rate(ic, &rn->rssadapt,
- &data->id);
- }
+ DPRINTFN(sc, 10, "%s\n", "data frame sent successfully");
+ if (data->rix != IEEE80211_FIXED_RATE_NONE)
+ ieee80211_amrr_tx_complete(&rn->amrr,
+ IEEE80211_AMRR_SUCCESS, 0);
ifp->if_opackets++;
break;
case RT2560_TX_SUCCESS_RETRY:
- DPRINTFN(9, ("data frame sent after %u retries\n",
- (le32toh(desc->flags) >> 5) & 0x7));
+ retrycnt = RT2560_TX_RETRYCNT(flags);
+
+ DPRINTFN(sc, 9, "data frame sent after %u retries\n",
+ retrycnt);
+ if (data->rix != IEEE80211_FIXED_RATE_NONE)
+ ieee80211_amrr_tx_complete(&rn->amrr,
+ IEEE80211_AMRR_SUCCESS, retrycnt);
ifp->if_opackets++;
break;
case RT2560_TX_FAIL_RETRY:
- DPRINTFN(9, ("sending data frame failed (too much "
- "retries)\n"));
- if (data->id.id_node != NULL) {
- ral_rssadapt_lower_rate(ic, data->ni,
- &rn->rssadapt, &data->id);
- }
+ retrycnt = RT2560_TX_RETRYCNT(flags);
+
+ DPRINTFN(sc, 9, "data frame failed after %d retries\n",
+ retrycnt);
+ if (data->rix != IEEE80211_FIXED_RATE_NONE)
+ ieee80211_amrr_tx_complete(&rn->amrr,
+ IEEE80211_AMRR_FAILURE, retrycnt);
ifp->if_oerrors++;
break;
@@ -979,14 +1013,14 @@ rt2560_tx_intr(struct rt2560_softc *sc)
case RT2560_TX_FAIL_OTHER:
default:
device_printf(sc->sc_dev, "sending data frame failed "
- "0x%08x\n", le32toh(desc->flags));
+ "0x%08x\n", flags);
ifp->if_oerrors++;
}
bus_dmamap_sync(sc->txq.data_dmat, data->map,
BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->txq.data_dmat, data->map);
- m_freem(data->m);
+ m_freem(m);
data->m = NULL;
ieee80211_free_node(data->ni);
data->ni = NULL;
@@ -994,7 +1028,7 @@ rt2560_tx_intr(struct rt2560_softc *sc)
/* descriptor is no longer valid */
desc->flags &= ~htole32(RT2560_TX_VALID);
- DPRINTFN(15, ("tx done idx=%u\n", sc->txq.next));
+ DPRINTFN(sc, 15, "tx done idx=%u\n", sc->txq.next);
sc->txq.queued--;
sc->txq.next = (sc->txq.next + 1) % RT2560_TX_RING_COUNT;
@@ -1011,15 +1045,14 @@ rt2560_tx_intr(struct rt2560_softc *sc)
if ((sc->sc_flags &
(RT2560_F_DATA_OACTIVE | RT2560_F_PRIO_OACTIVE)) == 0)
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- rt2560_start(ifp);
+ rt2560_start_locked(ifp);
}
}
static void
rt2560_prio_intr(struct rt2560_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
struct rt2560_tx_desc *desc;
struct rt2560_tx_data *data;
struct ieee80211_node *ni;
@@ -1039,17 +1072,17 @@ rt2560_prio_intr(struct rt2560_softc *sc)
switch (flags & RT2560_TX_RESULT_MASK) {
case RT2560_TX_SUCCESS:
- DPRINTFN(10, ("mgt frame sent successfully\n"));
+ DPRINTFN(sc, 10, "%s\n", "mgt frame sent successfully");
break;
case RT2560_TX_SUCCESS_RETRY:
- DPRINTFN(9, ("mgt frame sent after %u retries\n",
- (flags >> 5) & 0x7));
+ DPRINTFN(sc, 9, "mgt frame sent after %u retries\n",
+ (flags >> 5) & 0x7);
break;
case RT2560_TX_FAIL_RETRY:
- DPRINTFN(9, ("sending mgt frame failed (too much "
- "retries)\n"));
+ DPRINTFN(sc, 9, "%s\n",
+ "sending mgt frame failed (too much retries)");
break;
case RT2560_TX_FAIL_INVALID:
@@ -1072,7 +1105,7 @@ rt2560_prio_intr(struct rt2560_softc *sc)
/* descriptor is no longer valid */
desc->flags &= ~htole32(RT2560_TX_VALID);
- DPRINTFN(15, ("prio done idx=%u\n", sc->prioq.next));
+ DPRINTFN(sc, 15, "prio done idx=%u\n", sc->prioq.next);
sc->prioq.queued--;
sc->prioq.next = (sc->prioq.next + 1) % RT2560_PRIO_RING_COUNT;
@@ -1096,25 +1129,24 @@ rt2560_prio_intr(struct rt2560_softc *sc)
if ((sc->sc_flags &
(RT2560_F_DATA_OACTIVE | RT2560_F_PRIO_OACTIVE)) == 0)
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- rt2560_start(ifp);
+ rt2560_start_locked(ifp);
}
}
/*
* Some frames were processed by the hardware cipher engine and are ready for
- * transmission to the IEEE802.11 layer.
+ * handoff to the IEEE802.11 layer.
*/
static void
rt2560_decryption_intr(struct rt2560_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rt2560_rx_desc *desc;
struct rt2560_rx_data *data;
bus_addr_t physaddr;
struct ieee80211_frame *wh;
struct ieee80211_node *ni;
- struct rt2560_node *rn;
struct mbuf *mnew, *m;
int hw, error;
@@ -1193,7 +1225,7 @@ rt2560_decryption_intr(struct rt2560_softc *sc)
m->m_pkthdr.len = m->m_len =
(le32toh(desc->flags) >> 16) & 0xfff;
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rt2560_rx_radiotap_header *tap = &sc->sc_rxtap;
uint32_t tsf_lo, tsf_hi;
@@ -1204,13 +1236,12 @@ rt2560_decryption_intr(struct rt2560_softc *sc)
tap->wr_tsf =
htole64(((uint64_t)tsf_hi << 32) | tsf_lo);
tap->wr_flags = 0;
- tap->wr_rate = rt2560_rxrate(desc);
- tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
- tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wr_rate = ieee80211_plcp2rate(desc->rate,
+ le32toh(desc->flags) & RT2560_RX_OFDM);
tap->wr_antenna = sc->rx_ant;
tap->wr_antsignal = RT2560_RSSI(sc, desc->rssi);
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
}
sc->sc_flags |= RT2560_F_INPUT_RUNNING;
@@ -1218,24 +1249,19 @@ rt2560_decryption_intr(struct rt2560_softc *sc)
wh = mtod(m, struct ieee80211_frame *);
ni = ieee80211_find_rxnode(ic,
(struct ieee80211_frame_min *)wh);
-
- /* send the frame to the 802.11 layer */
- ieee80211_input(ic, m, ni, RT2560_RSSI(sc, desc->rssi),
- RT2560_NOISE_FLOOR, 0);
-
- /* give rssi to the rate adatation algorithm */
- rn = (struct rt2560_node *)ni;
- ral_rssadapt_input(ic, ni, &rn->rssadapt,
- RT2560_RSSI(sc, desc->rssi));
-
- /* node is no longer needed */
- ieee80211_free_node(ni);
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m,
+ RT2560_RSSI(sc, desc->rssi), RT2560_NOISE_FLOOR, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m,
+ RT2560_RSSI(sc, desc->rssi), RT2560_NOISE_FLOOR, 0);
RAL_LOCK(sc);
sc->sc_flags &= ~RT2560_F_INPUT_RUNNING;
skip: desc->flags = htole32(RT2560_RX_BUSY);
- DPRINTFN(15, ("decryption done idx=%u\n", sc->rxq.cur_decrypt));
+ DPRINTFN(sc, 15, "decryption done idx=%u\n", sc->rxq.cur_decrypt);
sc->rxq.cur_decrypt =
(sc->rxq.cur_decrypt + 1) % RT2560_RX_RING_COUNT;
@@ -1274,20 +1300,20 @@ rt2560_rx_intr(struct rt2560_softc *sc)
* This should not happen since we did not request
* to receive those frames when we filled RXCSR0.
*/
- DPRINTFN(5, ("PHY or CRC error flags 0x%08x\n",
- le32toh(desc->flags)));
+ DPRINTFN(sc, 5, "PHY or CRC error flags 0x%08x\n",
+ le32toh(desc->flags));
data->drop = 1;
}
if (((le32toh(desc->flags) >> 16) & 0xfff) > MCLBYTES) {
- DPRINTFN(5, ("bad length\n"));
+ DPRINTFN(sc, 5, "%s\n", "bad length");
data->drop = 1;
}
/* mark the frame for decryption */
desc->flags |= htole32(RT2560_RX_CIPHER_BUSY);
- DPRINTFN(15, ("rx done idx=%u\n", sc->rxq.cur));
+ DPRINTFN(sc, 15, "rx done idx=%u\n", sc->rxq.cur);
sc->rxq.cur = (sc->rxq.cur + 1) % RT2560_RX_RING_COUNT;
}
@@ -1300,10 +1326,10 @@ rt2560_rx_intr(struct rt2560_softc *sc)
}
static void
-rt2560_beacon_update(struct ieee80211com *ic, int item)
+rt2560_beacon_update(struct ieee80211vap *vap, int item)
{
- struct rt2560_softc *sc = ic->ic_ifp->if_softc;
- struct ieee80211_beacon_offsets *bo = &sc->sc_bo;
+ struct rt2560_vap *rvp = RT2560_VAP(vap);
+ struct ieee80211_beacon_offsets *bo = &rvp->ral_bo;
setbit(bo->bo_flags, item);
}
@@ -1315,7 +1341,10 @@ rt2560_beacon_update(struct ieee80211com *ic, int item)
static void
rt2560_beacon_expire(struct rt2560_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct rt2560_vap *rvp = RT2560_VAP(vap);
struct rt2560_tx_data *data;
if (ic->ic_opmode != IEEE80211_M_IBSS &&
@@ -1332,14 +1361,12 @@ rt2560_beacon_expire(struct rt2560_softc *sc)
bus_dmamap_sync(sc->bcnq.data_dmat, data->map, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->bcnq.data_dmat, data->map);
- ieee80211_beacon_update(data->ni, &sc->sc_bo, data->m, 1);
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, data->m);
+ /* XXX 1 =>'s mcast frames which means all PS sta's will wakeup! */
+ ieee80211_beacon_update(data->ni, &rvp->ral_bo, data->m, 1);
rt2560_tx_bcn(sc, data->m, data->ni);
- DPRINTFN(15, ("beacon expired\n"));
+ DPRINTFN(sc, 15, "%s", "beacon expired\n");
sc->bcnq.next = (sc->bcnq.next + 1) % RT2560_BEACON_RING_COUNT;
}
@@ -1348,7 +1375,7 @@ rt2560_beacon_expire(struct rt2560_softc *sc)
static void
rt2560_wakeup_expire(struct rt2560_softc *sc)
{
- DPRINTFN(2, ("wakeup expired\n"));
+ DPRINTFN(sc, 2, "%s", "wakeup expired\n");
}
void
@@ -1401,137 +1428,16 @@ rt2560_intr(void *arg)
RAL_UNLOCK(sc);
}
-/* quickly determine if a given rate is CCK or OFDM */
-#define RAL_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
-
-#define RAL_ACK_SIZE 14 /* 10 + 4(FCS) */
-#define RAL_CTS_SIZE 14 /* 10 + 4(FCS) */
-
#define RAL_SIFS 10 /* us */
#define RT2560_TXRX_TURNAROUND 10 /* us */
-/*
- * This function is only used by the Rx radiotap code.
- */
-static uint8_t
-rt2560_rxrate(struct rt2560_rx_desc *desc)
-{
- if (le32toh(desc->flags) & RT2560_RX_OFDM) {
- /* reverse function of rt2560_plcp_signal */
- switch (desc->rate) {
- case 0xb: return 12;
- case 0xf: return 18;
- case 0xa: return 24;
- case 0xe: return 36;
- case 0x9: return 48;
- case 0xd: return 72;
- case 0x8: return 96;
- case 0xc: return 108;
- }
- } else {
- if (desc->rate == 10)
- return 2;
- if (desc->rate == 20)
- return 4;
- if (desc->rate == 55)
- return 11;
- if (desc->rate == 110)
- return 22;
- }
- return 2; /* should not get there */
-}
-
-/*
- * Return the expected ack rate for a frame transmitted at rate `rate'.
- * XXX: this should depend on the destination node basic rate set.
- */
-static int
-rt2560_ack_rate(struct ieee80211com *ic, int rate)
-{
- switch (rate) {
- /* CCK rates */
- case 2:
- return 2;
- case 4:
- case 11:
- case 22:
- return (ic->ic_curmode == IEEE80211_MODE_11B) ? 4 : rate;
-
- /* OFDM rates */
- case 12:
- case 18:
- return 12;
- case 24:
- case 36:
- return 24;
- case 48:
- case 72:
- case 96:
- case 108:
- return 48;
- }
-
- /* default to 1Mbps */
- return 2;
-}
-
-/*
- * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'.
- * The function automatically determines the operating mode depending on the
- * given rate. `flags' indicates whether short preamble is in use or not.
- */
-static uint16_t
-rt2560_txtime(int len, int rate, uint32_t flags)
-{
- uint16_t txtime;
-
- if (RAL_RATE_IS_OFDM(rate)) {
- /* IEEE Std 802.11a-1999, pp. 37 */
- txtime = (8 + 4 * len + 3 + rate - 1) / rate;
- txtime = 16 + 4 + 4 * txtime + 6;
- } else {
- /* IEEE Std 802.11b-1999, pp. 28 */
- txtime = (16 * len + rate - 1) / rate;
- if (rate != 2 && (flags & IEEE80211_F_SHPREAMBLE))
- txtime += 72 + 24;
- else
- txtime += 144 + 48;
- }
-
- return txtime;
-}
-
-static uint8_t
-rt2560_plcp_signal(int rate)
-{
- switch (rate) {
- /* CCK rates (returned values are device-dependent) */
- case 2: return 0x0;
- case 4: return 0x1;
- case 11: return 0x2;
- case 22: return 0x3;
-
- /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
- case 12: return 0xb;
- case 18: return 0xf;
- case 24: return 0xa;
- case 36: return 0xe;
- case 48: return 0x9;
- case 72: return 0xd;
- case 96: return 0x8;
- case 108: return 0xc;
-
- /* unsupported rates (should not get there) */
- default: return 0xff;
- }
-}
-
static void
rt2560_setup_tx_desc(struct rt2560_softc *sc, struct rt2560_tx_desc *desc,
uint32_t flags, int len, int rate, int encrypt, bus_addr_t physaddr)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint16_t plcp_length;
int remainder;
@@ -1545,11 +1451,11 @@ rt2560_setup_tx_desc(struct rt2560_softc *sc, struct rt2560_tx_desc *desc,
RT2560_LOGCWMAX(8));
/* setup PLCP fields */
- desc->plcp_signal = rt2560_plcp_signal(rate);
+ desc->plcp_signal = ieee80211_rate2plcp(rate);
desc->plcp_service = 4;
len += IEEE80211_CRC_LEN;
- if (RAL_RATE_IS_OFDM(rate)) {
+ if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) {
desc->flags |= htole32(RT2560_TX_OFDM);
plcp_length = len & 0xfff;
@@ -1579,7 +1485,9 @@ static int
rt2560_tx_bcn(struct rt2560_softc *sc, struct mbuf *m0,
struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
struct rt2560_tx_desc *desc;
struct rt2560_tx_data *data;
bus_dma_segment_t segs[RT2560_MAX_SCATTER];
@@ -1588,7 +1496,8 @@ rt2560_tx_bcn(struct rt2560_softc *sc, struct mbuf *m0,
desc = &sc->bcnq.desc[sc->bcnq.cur];
data = &sc->bcnq.data[sc->bcnq.cur];
- rate = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? 12 : 2;
+ /* XXX maybe a separate beacon rate? */
+ rate = vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)].mgmtrate;
error = bus_dmamap_load_mbuf_sg(sc->bcnq.data_dmat, data->map, m0,
segs, &nsegs, BUS_DMA_NOWAIT);
@@ -1599,7 +1508,7 @@ rt2560_tx_bcn(struct rt2560_softc *sc, struct mbuf *m0,
return error;
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -1608,7 +1517,7 @@ rt2560_tx_bcn(struct rt2560_softc *sc, struct mbuf *m0,
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data->m = m0;
@@ -1617,8 +1526,8 @@ rt2560_tx_bcn(struct rt2560_softc *sc, struct mbuf *m0,
rt2560_setup_tx_desc(sc, desc, RT2560_TX_IFS_NEWBACKOFF |
RT2560_TX_TIMESTAMP, m0->m_pkthdr.len, rate, 0, segs->ds_addr);
- DPRINTFN(10, ("sending beacon frame len=%u idx=%u rate=%u\n",
- m0->m_pkthdr.len, sc->bcnq.cur, rate));
+ DPRINTFN(sc, 10, "sending beacon frame len=%u idx=%u rate=%u\n",
+ m0->m_pkthdr.len, sc->bcnq.cur, rate);
bus_dmamap_sync(sc->bcnq.data_dmat, data->map, BUS_DMASYNC_PREWRITE);
bus_dmamap_sync(sc->bcnq.desc_dmat, sc->bcnq.desc_map,
@@ -1633,7 +1542,9 @@ static int
rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0,
struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
struct rt2560_tx_desc *desc;
struct rt2560_tx_data *data;
struct ieee80211_frame *wh;
@@ -1646,12 +1557,12 @@ rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0,
desc = &sc->prioq.desc[sc->prioq.cur];
data = &sc->prioq.data[sc->prioq.cur];
- rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
+ rate = vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)].mgmtrate;
wh = mtod(m0, struct ieee80211_frame *);
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -1667,7 +1578,7 @@ rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0,
return error;
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -1676,19 +1587,21 @@ rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0,
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data->m = m0;
data->ni = ni;
+ /* management frames are not taken into account for amrr */
+ data->rix = IEEE80211_FIXED_RATE_NONE;
wh = mtod(m0, struct ieee80211_frame *);
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
flags |= RT2560_TX_ACK;
- dur = rt2560_txtime(RAL_ACK_SIZE, rate, ic->ic_flags) +
- RAL_SIFS;
+ dur = ieee80211_ack_duration(sc->sc_rates,
+ rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
/* tell hardware to add timestamp for probe responses */
@@ -1706,8 +1619,8 @@ rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0,
bus_dmamap_sync(sc->prioq.desc_dmat, sc->prioq.desc_map,
BUS_DMASYNC_PREWRITE);
- DPRINTFN(10, ("sending mgt frame len=%u idx=%u rate=%u\n",
- m0->m_pkthdr.len, sc->prioq.cur, rate));
+ DPRINTFN(sc, 10, "sending mgt frame len=%u idx=%u rate=%u\n",
+ m0->m_pkthdr.len, sc->prioq.cur, rate);
/* kick prio */
sc->prioq.queued++;
@@ -1718,10 +1631,80 @@ rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0,
}
static int
+rt2560_sendprot(struct rt2560_softc *sc,
+ const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_frame *wh;
+ struct rt2560_tx_desc *desc;
+ struct rt2560_tx_data *data;
+ struct mbuf *mprot;
+ int protrate, ackrate, pktlen, flags, isshort, error;
+ uint16_t dur;
+ bus_dma_segment_t segs[RT2560_MAX_SCATTER];
+ int nsegs;
+
+ KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY,
+ ("protection %d", prot));
+
+ wh = mtod(m, const struct ieee80211_frame *);
+ pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ protrate = ieee80211_ctl_rate(sc->sc_rates, rate);
+ ackrate = ieee80211_ack_rate(sc->sc_rates, rate);
+
+ isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
+ dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort);
+ + ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags = RT2560_TX_MORE_FRAG;
+ if (prot == IEEE80211_PROT_RTSCTS) {
+ /* NB: CTS is the same size as an ACK */
+ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags |= RT2560_TX_ACK;
+ mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
+ } else {
+ mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
+ }
+ if (mprot == NULL) {
+ /* XXX stat + msg */
+ return ENOBUFS;
+ }
+
+ desc = &sc->txq.desc[sc->txq.cur_encrypt];
+ data = &sc->txq.data[sc->txq.cur_encrypt];
+
+ error = bus_dmamap_load_mbuf_sg(sc->txq.data_dmat, data->map,
+ mprot, segs, &nsegs, 0);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not map mbuf (error %d)\n", error);
+ m_freem(mprot);
+ return error;
+ }
+
+ data->m = mprot;
+ data->ni = ieee80211_ref_node(ni);
+ /* ctl frames are not taken into account for amrr */
+ data->rix = IEEE80211_FIXED_RATE_NONE;
+
+ rt2560_setup_tx_desc(sc, desc, flags, mprot->m_pkthdr.len, protrate, 1,
+ segs->ds_addr);
+
+ bus_dmamap_sync(sc->txq.data_dmat, data->map,
+ BUS_DMASYNC_PREWRITE);
+
+ sc->txq.queued++;
+ sc->txq.cur_encrypt = (sc->txq.cur_encrypt + 1) % RT2560_TX_RING_COUNT;
+
+ return 0;
+}
+
+static int
rt2560_tx_raw(struct rt2560_softc *sc, struct mbuf *m0,
struct ieee80211_node *ni, const struct ieee80211_bpf_params *params)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rt2560_tx_desc *desc;
struct rt2560_tx_data *data;
bus_dma_segment_t segs[RT2560_MAX_SCATTER];
@@ -1734,10 +1717,26 @@ rt2560_tx_raw(struct rt2560_softc *sc, struct mbuf *m0,
rate = params->ibp_rate0 & IEEE80211_RATE_VAL;
/* XXX validate */
if (rate == 0) {
+ /* XXX fall back to mcast/mgmt rate? */
m_freem(m0);
return EINVAL;
}
+ flags = 0;
+ if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
+ flags |= RT2560_TX_ACK;
+ if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) {
+ error = rt2560_sendprot(sc, m0, ni,
+ params->ibp_flags & IEEE80211_BPF_RTS ?
+ IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY,
+ rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RT2560_TX_LONG_RETRY | RT2560_TX_IFS_SIFS;
+ }
+
error = bus_dmamap_load_mbuf_sg(sc->prioq.data_dmat, data->map, m0,
segs, &nsegs, 0);
if (error != 0) {
@@ -1747,7 +1746,7 @@ rt2560_tx_raw(struct rt2560_softc *sc, struct mbuf *m0,
return error;
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -1756,16 +1755,12 @@ rt2560_tx_raw(struct rt2560_softc *sc, struct mbuf *m0,
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data->m = m0;
data->ni = ni;
- flags = 0;
- if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
- flags |= RT2560_TX_ACK;
-
/* XXX need to setup descriptor ourself */
rt2560_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len,
rate, (params->ibp_flags & IEEE80211_BPF_CRYPTO) != 0,
@@ -1775,8 +1770,8 @@ rt2560_tx_raw(struct rt2560_softc *sc, struct mbuf *m0,
bus_dmamap_sync(sc->prioq.desc_dmat, sc->prioq.desc_map,
BUS_DMASYNC_PREWRITE);
- DPRINTFN(10, ("sending raw frame len=%u idx=%u rate=%u\n",
- m0->m_pkthdr.len, sc->prioq.cur, rate));
+ DPRINTFN(sc, 10, "sending raw frame len=%u idx=%u rate=%u\n",
+ m0->m_pkthdr.len, sc->prioq.cur, rate);
/* kick prio */
sc->prioq.queued++;
@@ -1786,70 +1781,40 @@ rt2560_tx_raw(struct rt2560_softc *sc, struct mbuf *m0,
return 0;
}
-/*
- * Build a RTS control frame.
- */
-static struct mbuf *
-rt2560_get_rts(struct rt2560_softc *sc, struct ieee80211_frame *wh,
- uint16_t dur)
-{
- struct ieee80211_frame_rts *rts;
- struct mbuf *m;
-
- MGETHDR(m, M_DONTWAIT, MT_DATA);
- if (m == NULL) {
- sc->sc_ic.ic_stats.is_tx_nobuf++;
- device_printf(sc->sc_dev, "could not allocate RTS frame\n");
- return NULL;
- }
-
- rts = mtod(m, struct ieee80211_frame_rts *);
-
- rts->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL |
- IEEE80211_FC0_SUBTYPE_RTS;
- rts->i_fc[1] = IEEE80211_FC1_DIR_NODS;
- *(uint16_t *)rts->i_dur = htole16(dur);
- IEEE80211_ADDR_COPY(rts->i_ra, wh->i_addr1);
- IEEE80211_ADDR_COPY(rts->i_ta, wh->i_addr2);
-
- m->m_pkthdr.len = m->m_len = sizeof (struct ieee80211_frame_rts);
-
- return m;
-}
-
static int
rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0,
struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
struct rt2560_tx_desc *desc;
struct rt2560_tx_data *data;
- struct rt2560_node *rn;
struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
struct ieee80211_key *k;
struct mbuf *mnew;
bus_dma_segment_t segs[RT2560_MAX_SCATTER];
uint16_t dur;
- uint32_t flags = 0;
+ uint32_t flags;
int nsegs, rate, error;
wh = mtod(m0, struct ieee80211_frame *);
- if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
- rate = ic->ic_fixed_rate;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ rate = tp->mcastrate;
+ } else if (m0->m_flags & M_EAPOL) {
+ rate = tp->mgmtrate;
+ } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ rate = tp->ucastrate;
} else {
- struct ieee80211_rateset *rs;
-
- rs = &ni->ni_rates;
- rn = (struct rt2560_node *)ni;
- ni->ni_txrate = ral_rssadapt_choose(&rn->rssadapt, rs, wh,
- m0->m_pkthdr.len, NULL, 0);
- rate = rs->rs_rates[ni->ni_txrate];
+ (void) ieee80211_amrr_choose(ni, &RT2560_NODE(ni)->amrr);
+ rate = ni->ni_txrate;
}
- rate &= IEEE80211_RATE_VAL;
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -1859,66 +1824,22 @@ rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0,
wh = mtod(m0, struct ieee80211_frame *);
}
- /*
- * IEEE Std 802.11-1999, pp 82: "A STA shall use an RTS/CTS exchange
- * for directed frames only when the length of the MPDU is greater
- * than the length threshold indicated by [...]" ic_rtsthreshold.
- */
- if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
- m0->m_pkthdr.len > ic->ic_rtsthreshold) {
- struct mbuf *m;
- uint16_t dur;
- int rtsrate, ackrate;
-
- rtsrate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
- ackrate = rt2560_ack_rate(ic, rate);
-
- dur = rt2560_txtime(m0->m_pkthdr.len + 4, rate, ic->ic_flags) +
- rt2560_txtime(RAL_CTS_SIZE, rtsrate, ic->ic_flags) +
- rt2560_txtime(RAL_ACK_SIZE, ackrate, ic->ic_flags) +
- 3 * RAL_SIFS;
-
- m = rt2560_get_rts(sc, wh, dur);
-
- desc = &sc->txq.desc[sc->txq.cur_encrypt];
- data = &sc->txq.data[sc->txq.cur_encrypt];
-
- error = bus_dmamap_load_mbuf_sg(sc->txq.data_dmat, data->map,
- m, segs, &nsegs, 0);
- if (error != 0) {
- device_printf(sc->sc_dev,
- "could not map mbuf (error %d)\n", error);
- m_freem(m);
- m_freem(m0);
- return error;
+ flags = 0;
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ int prot = IEEE80211_PROT_NONE;
+ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
+ prot = IEEE80211_PROT_RTSCTS;
+ else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM)
+ prot = ic->ic_protmode;
+ if (prot != IEEE80211_PROT_NONE) {
+ error = rt2560_sendprot(sc, m0, ni, prot, rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RT2560_TX_LONG_RETRY | RT2560_TX_IFS_SIFS;
}
-
- /* avoid multiple free() of the same node for each fragment */
- ieee80211_ref_node(ni);
-
- data->m = m;
- data->ni = ni;
-
- /* RTS frames are not taken into account for rssadapt */
- data->id.id_node = NULL;
-
- rt2560_setup_tx_desc(sc, desc, RT2560_TX_ACK |
- RT2560_TX_MORE_FRAG, m->m_pkthdr.len, rtsrate, 1,
- segs->ds_addr);
-
- bus_dmamap_sync(sc->txq.data_dmat, data->map,
- BUS_DMASYNC_PREWRITE);
-
- sc->txq.queued++;
- sc->txq.cur_encrypt =
- (sc->txq.cur_encrypt + 1) % RT2560_TX_RING_COUNT;
-
- /*
- * IEEE Std 802.11-1999: when an RTS/CTS exchange is used, the
- * asynchronous data frame shall be transmitted after the CTS
- * frame and a SIFS period.
- */
- flags |= RT2560_TX_LONG_RETRY | RT2560_TX_IFS_SIFS;
}
data = &sc->txq.data[sc->txq.cur_encrypt];
@@ -1955,35 +1876,32 @@ rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0,
wh = mtod(m0, struct ieee80211_frame *);
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
tap->wt_rate = rate;
- tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
- tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data->m = m0;
data->ni = ni;
/* remember link conditions for rate adaptation algorithm */
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
- data->id.id_len = m0->m_pkthdr.len;
- data->id.id_rateidx = ni->ni_txrate;
- data->id.id_node = ni;
- data->id.id_rssi = ni->ni_rssi;
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) {
+ data->rix = ni->ni_txrate;
+ /* XXX probably need last rssi value and not avg */
+ data->rssi = ic->ic_node_getrssi(ni);
} else
- data->id.id_node = NULL;
+ data->rix = IEEE80211_FIXED_RATE_NONE;
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
flags |= RT2560_TX_ACK;
- dur = rt2560_txtime(RAL_ACK_SIZE, rt2560_ack_rate(ic, rate),
- ic->ic_flags) + RAL_SIFS;
+ dur = ieee80211_ack_duration(sc->sc_rates,
+ rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
}
@@ -1994,8 +1912,8 @@ rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0,
bus_dmamap_sync(sc->txq.desc_dmat, sc->txq.desc_map,
BUS_DMASYNC_PREWRITE);
- DPRINTFN(10, ("sending data frame len=%u idx=%u rate=%u\n",
- m0->m_pkthdr.len, sc->txq.cur_encrypt, rate));
+ DPRINTFN(sc, 10, "sending data frame len=%u idx=%u rate=%u\n",
+ m0->m_pkthdr.len, sc->txq.cur_encrypt, rate);
/* kick encrypt */
sc->txq.queued++;
@@ -2006,113 +1924,50 @@ rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0,
}
static void
-rt2560_start(struct ifnet *ifp)
+rt2560_start_locked(struct ifnet *ifp)
{
struct rt2560_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct mbuf *m0;
- struct ether_header *eh;
+ struct mbuf *m;
struct ieee80211_node *ni;
- RAL_LOCK(sc);
-
- /* prevent management frames from being sent if we're not ready */
- if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
- RAL_UNLOCK(sc);
- return;
- }
+ RAL_LOCK_ASSERT(sc);
for (;;) {
- IF_POLL(&ic->ic_mgtq, m0);
- if (m0 != NULL) {
- if (sc->prioq.queued >= RT2560_PRIO_RING_COUNT) {
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- sc->sc_flags |= RT2560_F_PRIO_OACTIVE;
- break;
- }
- IF_DEQUEUE(&ic->ic_mgtq, m0);
-
- ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
- m0->m_pkthdr.rcvif = NULL;
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (rt2560_tx_mgt(sc, m0, ni) != 0) {
- ieee80211_free_node(ni);
- break;
- }
- } else {
- if (ic->ic_state != IEEE80211_S_RUN)
- break;
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
- if (sc->txq.queued >= RT2560_TX_RING_COUNT - 1) {
- IFQ_DRV_PREPEND(&ifp->if_snd, m0);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- sc->sc_flags |= RT2560_F_DATA_OACTIVE;
- break;
- }
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
-
- if (m0->m_len < sizeof (struct ether_header) &&
- !(m0 = m_pullup(m0, sizeof (struct ether_header))))
- continue;
-
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- continue;
- }
- if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
- (m0->m_flags & M_PWR_SAV) == 0) {
- /*
- * Station in power save mode; pass the frame
- * to the 802.11 layer and continue. We'll get
- * the frame back when the time is right.
- */
- ieee80211_pwrsave(ni, m0);
- /*
- * If we're in power save mode 'cuz of a bg
- * scan cancel it so the traffic can flow.
- * The packet we just queued will automatically
- * get sent when we drop out of power save.
- * XXX locking
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
- ieee80211_free_node(ni);
- continue;
- }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ if (sc->txq.queued >= RT2560_TX_RING_COUNT - 1) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ sc->sc_flags |= RT2560_F_DATA_OACTIVE;
+ break;
+ }
- BPF_MTAP(ifp, m0);
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ continue;
+ }
- m0 = ieee80211_encap(ic, m0, ni);
- if (m0 == NULL) {
- ieee80211_free_node(ni);
- continue;
- }
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (rt2560_tx_data(sc, m0, ni) != 0) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- break;
- }
+ if (rt2560_tx_data(sc, m, ni) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
}
sc->sc_tx_timer = 5;
- ic->ic_lastdata = ticks;
}
+}
+static void
+rt2560_start(struct ifnet *ifp)
+{
+ struct rt2560_softc *sc = ifp->if_softc;
+
+ RAL_LOCK(sc);
+ rt2560_start_locked(ifp);
RAL_UNLOCK(sc);
}
@@ -2122,81 +1977,60 @@ rt2560_watchdog(void *arg)
struct rt2560_softc *sc = arg;
struct ifnet *ifp = sc->sc_ifp;
- if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ RAL_LOCK_ASSERT(sc);
+
+ KASSERT(ifp->if_drv_flags & IFF_DRV_RUNNING, ("not running"));
+
+ if (sc->sc_invalid) /* card ejected */
return;
rt2560_encryption_intr(sc);
rt2560_tx_intr(sc);
- if (sc->sc_tx_timer > 0) {
- if (--sc->sc_tx_timer == 0) {
- device_printf(sc->sc_dev, "device timeout\n");
- rt2560_init(sc);
- ifp->if_oerrors++;
- /* watchdog timeout is set in rt2560_init() */
- return;
- }
+ if (sc->sc_tx_timer > 0 && --sc->sc_tx_timer == 0) {
+ if_printf(ifp, "device timeout\n");
+ rt2560_init_locked(sc);
+ ifp->if_oerrors++;
+ /* NB: callout is reset in rt2560_init() */
+ return;
}
callout_reset(&sc->watchdog_ch, hz, rt2560_watchdog, sc);
}
-/*
- * This function allows for fast channel switching in monitor mode (used by
- * net-mgmt/kismet). In IBSS mode, we must explicitly reset the interface to
- * generate a new beacon frame.
- */
-static int
-rt2560_reset(struct ifnet *ifp)
-{
- struct rt2560_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
-
- if (ic->ic_opmode != IEEE80211_M_MONITOR)
- return ENETRESET;
-
- rt2560_set_chan(sc, ic->ic_curchan);
-
- return 0;
-}
-
static int
rt2560_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct rt2560_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
-
-
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
+ RAL_LOCK(sc);
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
- RAL_LOCK(sc);
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- rt2560_update_promisc(sc);
- else
- rt2560_init(sc);
- RAL_UNLOCK(sc);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ rt2560_init_locked(sc);
+ startall = 1;
+ } else
+ rt2560_update_promisc(ifp);
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- rt2560_stop(sc);
+ rt2560_stop_locked(sc);
}
-
break;
-
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
default:
- error = ieee80211_ioctl(ic, cmd, data);
- }
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
- (ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
- rt2560_init(sc);
- error = 0;
+ error = ether_ioctl(ifp, cmd, data);
+ break;
}
+ RAL_UNLOCK(sc);
-
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
@@ -2219,7 +2053,7 @@ rt2560_bbp_write(struct rt2560_softc *sc, uint8_t reg, uint8_t val)
tmp = RT2560_BBP_WRITE | RT2560_BBP_BUSY | reg << 8 | val;
RAL_WRITE(sc, RT2560_BBPCSR, tmp);
- DPRINTFN(15, ("BBP R%u <- 0x%02x\n", reg, val));
+ DPRINTFN(sc, 15, "BBP R%u <- 0x%02x\n", reg, val);
}
static uint8_t
@@ -2275,19 +2109,21 @@ rt2560_rf_write(struct rt2560_softc *sc, uint8_t reg, uint32_t val)
/* remember last written value in sc */
sc->rf_regs[reg] = val;
- DPRINTFN(15, ("RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff));
+ DPRINTFN(sc, 15, "RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff);
}
static void
rt2560_set_chan(struct rt2560_softc *sc, struct ieee80211_channel *c)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint8_t power, tmp;
u_int i, chan;
chan = ieee80211_chan2ieee(ic, c);
- if (chan == 0 || chan == IEEE80211_CHAN_ANY)
- return;
+ KASSERT(chan != 0 && chan != IEEE80211_CHAN_ANY, ("chan 0x%x", chan));
+
+ sc->sc_rates = ieee80211_get_ratetable(c);
if (IEEE80211_IS_CHAN_2GHZ(c))
power = min(sc->txpow[chan - 1], 31);
@@ -2297,7 +2133,7 @@ rt2560_set_chan(struct rt2560_softc *sc, struct ieee80211_channel *c)
/* adjust txpower using ifconfig settings */
power -= (100 - ic->ic_txpowlimit) / 8;
- DPRINTFN(2, ("setting channel to %u, txpower to %u\n", chan, power));
+ DPRINTFN(sc, 2, "setting channel to %u, txpower to %u\n", chan, power);
switch (sc->rf_rev) {
case RT2560_RF_2522:
@@ -2362,7 +2198,8 @@ rt2560_set_chan(struct rt2560_softc *sc, struct ieee80211_channel *c)
printf("unknown ral rev=%d\n", sc->rf_rev);
}
- if (ic->ic_state != IEEE80211_S_SCAN) {
+ /* XXX */
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
/* set Japan filter bit for channel 14 */
tmp = rt2560_bbp_read(sc, 70);
@@ -2385,6 +2222,11 @@ rt2560_set_channel(struct ieee80211com *ic)
RAL_LOCK(sc);
rt2560_set_chan(sc, ic->ic_curchan);
+
+ sc->sc_txtap.wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ sc->sc_txtap.wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ sc->sc_rxtap.wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ sc->sc_rxtap.wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
RAL_UNLOCK(sc);
}
@@ -2406,7 +2248,7 @@ rt2560_disable_rf_tune(struct rt2560_softc *sc)
tmp = sc->rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE;
rt2560_rf_write(sc, RAL_RF3, tmp);
- DPRINTFN(2, ("disabling RF autotune\n"));
+ DPRINTFN(sc, 2, "%s", "disabling RF autotune\n");
}
#endif
@@ -2417,20 +2259,22 @@ rt2560_disable_rf_tune(struct rt2560_softc *sc)
static void
rt2560_enable_tsf_sync(struct rt2560_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
uint16_t logcwmin, preload;
uint32_t tmp;
/* first, disable TSF synchronization */
RAL_WRITE(sc, RT2560_CSR14, 0);
- tmp = 16 * ic->ic_bss->ni_intval;
+ tmp = 16 * vap->iv_bss->ni_intval;
RAL_WRITE(sc, RT2560_CSR12, tmp);
RAL_WRITE(sc, RT2560_CSR13, 0);
logcwmin = 5;
- preload = (ic->ic_opmode == IEEE80211_M_STA) ? 384 : 1024;
+ preload = (vap->iv_opmode == IEEE80211_M_STA) ? 384 : 1024;
tmp = logcwmin << 16 | preload;
RAL_WRITE(sc, RT2560_BCNOCSR, tmp);
@@ -2443,13 +2287,14 @@ rt2560_enable_tsf_sync(struct rt2560_softc *sc)
RT2560_ENABLE_BEACON_GENERATOR;
RAL_WRITE(sc, RT2560_CSR14, tmp);
- DPRINTF(("enabling TSF synchronization\n"));
+ DPRINTF(sc, "%s", "enabling TSF synchronization\n");
}
static void
rt2560_update_plcp(struct rt2560_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
/* no short preamble for 1Mbps */
RAL_WRITE(sc, RT2560_PLCP1MCSR, 0x00700400);
@@ -2466,8 +2311,8 @@ rt2560_update_plcp(struct rt2560_softc *sc)
RAL_WRITE(sc, RT2560_PLCP11MCSR, 0x000b840b);
}
- DPRINTF(("updating PLCP for %s preamble\n",
- (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? "short" : "long"));
+ DPRINTF(sc, "updating PLCP for %s preamble\n",
+ (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? "short" : "long");
}
/*
@@ -2478,7 +2323,7 @@ static void
rt2560_update_slot(struct ifnet *ifp)
{
struct rt2560_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
uint8_t slottime;
uint16_t tx_sifs, tx_pifs, tx_difs, eifs;
uint32_t tmp;
@@ -2521,13 +2366,14 @@ rt2560_update_slot(struct ifnet *ifp)
tmp = eifs << 16 | tx_difs;
RAL_WRITE(sc, RT2560_CSR19, tmp);
- DPRINTF(("setting slottime to %uus\n", slottime));
+ DPRINTF(sc, "setting slottime to %uus\n", slottime);
}
static void
rt2560_set_basicrates(struct rt2560_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
/* update basic rate set */
if (ic->ic_curmode == IEEE80211_MODE_11B) {
@@ -2563,7 +2409,7 @@ rt2560_set_bssid(struct rt2560_softc *sc, const uint8_t *bssid)
tmp = bssid[4] | bssid[5] << 8;
RAL_WRITE(sc, RT2560_CSR6, tmp);
- DPRINTF(("setting BSSID to %6D\n", bssid, ":"));
+ DPRINTF(sc, "setting BSSID to %6D\n", bssid, ":");
}
static void
@@ -2577,7 +2423,7 @@ rt2560_set_macaddr(struct rt2560_softc *sc, uint8_t *addr)
tmp = addr[4] | addr[5] << 8;
RAL_WRITE(sc, RT2560_CSR4, tmp);
- DPRINTF(("setting MAC address to %6D\n", addr, ":"));
+ DPRINTF(sc, "setting MAC address to %6D\n", addr, ":");
}
static void
@@ -2597,9 +2443,9 @@ rt2560_get_macaddr(struct rt2560_softc *sc, uint8_t *addr)
}
static void
-rt2560_update_promisc(struct rt2560_softc *sc)
+rt2560_update_promisc(struct ifnet *ifp)
{
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct rt2560_softc *sc = ifp->if_softc;
uint32_t tmp;
tmp = RAL_READ(sc, RT2560_RXCSR0);
@@ -2610,8 +2456,8 @@ rt2560_update_promisc(struct rt2560_softc *sc)
RAL_WRITE(sc, RT2560_RXCSR0, tmp);
- DPRINTF(("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ?
- "entering" : "leaving"));
+ DPRINTF(sc, "%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ?
+ "entering" : "leaving");
}
static const char *
@@ -2669,8 +2515,8 @@ rt2560_read_config(struct rt2560_softc *sc)
sc->rssi_corr = RT2560_DEFAULT_RSSI_CORR;
else
sc->rssi_corr = val & 0xff;
- DPRINTF(("rssi correction %d, calibrate 0x%02x\n",
- sc->rssi_corr, val));
+ DPRINTF(sc, "rssi correction %d, calibrate 0x%02x\n",
+ sc->rssi_corr, val);
}
@@ -2690,10 +2536,11 @@ rt2560_scan_end(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
struct rt2560_softc *sc = ifp->if_softc;
+ struct ieee80211vap *vap = ic->ic_scan->ss_vap;
rt2560_enable_tsf_sync(sc);
/* XXX keep local copy */
- rt2560_set_bssid(sc, ic->ic_bss->ni_bssid);
+ rt2560_set_bssid(sc, vap->iv_bss->ni_bssid);
}
static int
@@ -2779,20 +2626,18 @@ rt2560_set_rxantenna(struct rt2560_softc *sc, int antenna)
}
static void
-rt2560_init(void *priv)
+rt2560_init_locked(struct rt2560_softc *sc)
{
#define N(a) (sizeof (a) / sizeof ((a)[0]))
- struct rt2560_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp;
int i;
+ RAL_LOCK_ASSERT(sc);
+ rt2560_stop_locked(sc);
- rt2560_stop(sc);
-
- RAL_LOCK(sc);
/* setup tx rings */
tmp = RT2560_PRIO_RING_COUNT << 24 |
RT2560_ATIM_RING_COUNT << 16 |
@@ -2866,35 +2711,38 @@ rt2560_init(void *priv)
ifp->if_drv_flags |= IFF_DRV_RUNNING;
callout_reset(&sc->watchdog_ch, hz, rt2560_watchdog, sc);
+#undef N
+}
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+static void
+rt2560_init(void *priv)
+{
+ struct rt2560_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ RAL_LOCK(sc);
+ rt2560_init_locked(sc);
RAL_UNLOCK(sc);
-#undef N
+
+ ieee80211_start_all(ic);
}
-void
-rt2560_stop(void *arg)
+static void
+rt2560_stop_locked(struct rt2560_softc *sc)
{
- struct rt2560_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
volatile int *flags = &sc->sc_flags;
- while (*flags & RT2560_F_INPUT_RUNNING) {
- tsleep(sc, 0, "ralrunning", hz/10);
- }
+ RAL_LOCK_ASSERT(sc);
- RAL_LOCK(sc);
+ while (*flags & RT2560_F_INPUT_RUNNING)
+ msleep(sc, &sc->sc_mtx, 0, "ralrunning", hz/10);
callout_stop(&sc->watchdog_ch);
+ sc->sc_tx_timer = 0;
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
/* abort Tx */
@@ -2917,9 +2765,16 @@ rt2560_stop(void *arg)
rt2560_reset_tx_ring(sc, &sc->bcnq);
rt2560_reset_rx_ring(sc, &sc->rxq);
}
- sc->sc_tx_timer = 0;
sc->sc_flags &= ~(RT2560_F_PRIO_OACTIVE | RT2560_F_DATA_OACTIVE);
+}
+void
+rt2560_stop(void *arg)
+{
+ struct rt2560_softc *sc = arg;
+
+ RAL_LOCK(sc);
+ rt2560_stop_locked(sc);
RAL_UNLOCK(sc);
}
@@ -2942,15 +2797,13 @@ rt2560_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
}
if (sc->prioq.queued >= RT2560_PRIO_RING_COUNT) {
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ sc->sc_flags |= RT2560_F_PRIO_OACTIVE;
RAL_UNLOCK(sc);
m_freem(m);
ieee80211_free_node(ni);
return ENOBUFS; /* XXX */
}
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m);
-
ifp->if_opackets++;
if (params == NULL) {
diff --git a/sys/dev/ral/rt2560reg.h b/sys/dev/ral/rt2560reg.h
index bab455d..8e501b4 100644
--- a/sys/dev/ral/rt2560reg.h
+++ b/sys/dev/ral/rt2560reg.h
@@ -208,6 +208,8 @@ struct rt2560_tx_desc {
#define RT2560_TX_CIPHER_TKIP (3 << 29)
#define RT2560_TX_CIPHER_AES (4 << 29)
+#define RT2560_TX_RETRYCNT(v) (((v) >> 5) & 0x7)
+
uint32_t physaddr;
uint16_t wme;
#define RT2560_LOGCWMAX(x) (((x) & 0xf) << 12)
diff --git a/sys/dev/ral/rt2560var.h b/sys/dev/ral/rt2560var.h
index 1f68311..0841444 100644
--- a/sys/dev/ral/rt2560var.h
+++ b/sys/dev/ral/rt2560var.h
@@ -55,7 +55,8 @@ struct rt2560_tx_data {
bus_dmamap_t map;
struct mbuf *m;
struct ieee80211_node *ni;
- struct ral_rssdesc id;
+ uint8_t rix;
+ int8_t rssi;
};
struct rt2560_tx_ring {
@@ -94,14 +95,22 @@ struct rt2560_rx_ring {
struct rt2560_node {
struct ieee80211_node ni;
- struct ral_rssadapt rssadapt;
+ struct ieee80211_amrr_node amrr;
};
+#define RT2560_NODE(ni) ((struct rt2560_node *)(ni))
+
+struct rt2560_vap {
+ struct ieee80211vap ral_vap;
+ struct ieee80211_beacon_offsets ral_bo;
+ struct ieee80211_amrr amrr;
+
+ int (*ral_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define RT2560_VAP(vap) ((struct rt2560_vap *)(vap))
struct rt2560_softc {
struct ifnet *sc_ifp;
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
device_t sc_dev;
bus_space_tag_t sc_st;
bus_space_handle_t sc_sh;
@@ -109,10 +118,12 @@ struct rt2560_softc {
struct mtx sc_mtx;
struct callout watchdog_ch;
- struct callout rssadapt_ch;
int sc_tx_timer;
int sc_invalid;
+ int sc_debug;
+
+ const struct ieee80211_rate_table *sc_rates;
/*
* The same in both up to here
* ------------------------------------------------
@@ -128,8 +139,6 @@ struct rt2560_softc {
struct rt2560_tx_ring bcnq;
struct rt2560_rx_ring rxq;
- struct ieee80211_beacon_offsets sc_bo;
-
uint32_t rf_regs[4];
uint8_t txpow[14];
@@ -144,22 +153,10 @@ struct rt2560_softc {
int tx_ant;
int nb_ant;
- int dwelltime;
-
- struct bpf_if *sc_drvbpf;
-
- union {
- struct rt2560_rx_radiotap_header th;
- uint8_t pad[64];
- } sc_rxtapu;
-#define sc_rxtap sc_rxtapu.th
+ struct rt2560_rx_radiotap_header sc_rxtap;
int sc_rxtap_len;
- union {
- struct rt2560_tx_radiotap_header th;
- uint8_t pad[64];
- } sc_txtapu;
-#define sc_txtap sc_txtapu.th
+ struct rt2560_tx_radiotap_header sc_txtap;
int sc_txtap_len;
#define RT2560_F_INPUT_RUNNING 0x1
#define RT2560_F_PRIO_OACTIVE 0x2
@@ -173,5 +170,6 @@ void rt2560_stop(void *);
void rt2560_resume(void *);
void rt2560_intr(void *);
-#define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
-#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define RAL_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
+#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
diff --git a/sys/dev/ral/rt2661.c b/sys/dev/ral/rt2661.c
index 025646d..c94dbe7 100644
--- a/sys/dev/ral/rt2661.c
+++ b/sys/dev/ral/rt2661.c
@@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/endian.h>
+#include <sys/firmware.h>
#include <machine/bus.h>
#include <machine/resource.h>
@@ -52,8 +53,10 @@ __FBSDID("$FreeBSD$");
#include <net/if_types.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_phy.h>
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_amrr.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
@@ -61,21 +64,29 @@ __FBSDID("$FreeBSD$");
#include <netinet/ip.h>
#include <netinet/if_ether.h>
-#include <dev/ral/if_ralrate.h>
#include <dev/ral/rt2661reg.h>
#include <dev/ral/rt2661var.h>
-#include <dev/ral/rt2661_ucode.h>
+#define RAL_DEBUG
#ifdef RAL_DEBUG
-#define DPRINTF(x) do { if (ral_debug > 0) printf x; } while (0)
-#define DPRINTFN(n, x) do { if (ral_debug >= (n)) printf x; } while (0)
-int ral_debug = 0;
-SYSCTL_INT(_debug, OID_AUTO, ral, CTLFLAG_RW, &ral_debug, 0, "ral debug level");
+#define DPRINTF(sc, fmt, ...) do { \
+ if (sc->sc_debug > 0) \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
+#define DPRINTFN(sc, n, fmt, ...) do { \
+ if (sc->sc_debug >= (n)) \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
#else
-#define DPRINTF(x)
-#define DPRINTFN(n, x)
+#define DPRINTF(sc, fmt, ...)
+#define DPRINTFN(sc, n, fmt, ...)
#endif
+static struct ieee80211vap *rt2661_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void rt2661_vap_delete(struct ieee80211vap *);
static void rt2661_dma_map_addr(void *, bus_dma_segment_t *, int,
int);
static int rt2661_alloc_tx_ring(struct rt2661_softc *,
@@ -92,8 +103,8 @@ static void rt2661_free_rx_ring(struct rt2661_softc *,
struct rt2661_rx_ring *);
static struct ieee80211_node *rt2661_node_alloc(
struct ieee80211_node_table *);
-static int rt2661_media_change(struct ifnet *);
-static int rt2661_newstate(struct ieee80211com *,
+static void rt2661_newassoc(struct ieee80211_node *, int);
+static int rt2661_newstate(struct ieee80211vap *,
enum ieee80211_state, int);
static uint16_t rt2661_eeprom_read(struct rt2661_softc *, uint8_t);
static void rt2661_rx_intr(struct rt2661_softc *);
@@ -103,25 +114,21 @@ static void rt2661_tx_dma_intr(struct rt2661_softc *,
static void rt2661_mcu_beacon_expire(struct rt2661_softc *);
static void rt2661_mcu_wakeup(struct rt2661_softc *);
static void rt2661_mcu_cmd_intr(struct rt2661_softc *);
-static int rt2661_ack_rate(struct ieee80211com *, int);
static void rt2661_scan_start(struct ieee80211com *);
static void rt2661_scan_end(struct ieee80211com *);
static void rt2661_set_channel(struct ieee80211com *);
-static uint16_t rt2661_txtime(int, int, uint32_t);
-static uint8_t rt2661_rxrate(struct rt2661_rx_desc *);
-static uint8_t rt2661_plcp_signal(int);
static void rt2661_setup_tx_desc(struct rt2661_softc *,
struct rt2661_tx_desc *, uint32_t, uint16_t, int,
int, const bus_dma_segment_t *, int, int);
-static struct mbuf * rt2661_get_rts(struct rt2661_softc *,
- struct ieee80211_frame *, uint16_t);
static int rt2661_tx_data(struct rt2661_softc *, struct mbuf *,
struct ieee80211_node *, int);
static int rt2661_tx_mgt(struct rt2661_softc *, struct mbuf *,
struct ieee80211_node *);
+static void rt2661_start_locked(struct ifnet *);
static void rt2661_start(struct ifnet *);
+static int rt2661_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
static void rt2661_watchdog(void *);
-static int rt2661_reset(struct ifnet *);
static int rt2661_ioctl(struct ifnet *, u_long, caddr_t);
static void rt2661_bbp_write(struct rt2661_softc *, uint8_t,
uint8_t);
@@ -143,23 +150,25 @@ static void rt2661_set_bssid(struct rt2661_softc *,
const uint8_t *);
static void rt2661_set_macaddr(struct rt2661_softc *,
const uint8_t *);
-static void rt2661_update_promisc(struct rt2661_softc *);
+static void rt2661_update_promisc(struct ifnet *);
static int rt2661_wme_update(struct ieee80211com *) __unused;
static void rt2661_update_slot(struct ifnet *);
static const char *rt2661_get_rf(int);
-static void rt2661_read_eeprom(struct rt2661_softc *);
+static void rt2661_read_eeprom(struct rt2661_softc *,
+ struct ieee80211com *);
static int rt2661_bbp_init(struct rt2661_softc *);
+static void rt2661_init_locked(struct rt2661_softc *);
static void rt2661_init(void *);
-static void rt2661_stop(void *);
static void rt2661_stop_locked(struct rt2661_softc *);
-static int rt2661_load_microcode(struct rt2661_softc *,
- const uint8_t *, int);
+static void rt2661_stop(void *);
+static int rt2661_load_microcode(struct rt2661_softc *);
#ifdef notyet
static void rt2661_rx_tune(struct rt2661_softc *);
static void rt2661_radar_start(struct rt2661_softc *);
static int rt2661_radar_stop(struct rt2661_softc *);
#endif
-static int rt2661_prepare_beacon(struct rt2661_softc *);
+static int rt2661_prepare_beacon(struct rt2661_softc *,
+ struct ieee80211vap *);
static void rt2661_enable_tsf_sync(struct rt2661_softc *);
static int rt2661_get_rssi(struct rt2661_softc *, uint8_t);
@@ -190,19 +199,26 @@ int
rt2661_attach(device_t dev, int id)
{
struct rt2661_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
struct ifnet *ifp;
uint32_t val;
- const uint8_t *ucode = NULL;
- int bands, error, ac, ntries, size = 0;
+ int error, ac, ntries;
+ uint8_t bands;
+ sc->sc_id = id;
sc->sc_dev = dev;
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(sc->sc_dev, "can not if_alloc()\n");
+ return ENOMEM;
+ }
+ ic = ifp->if_l2com;
+
mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
MTX_DEF | MTX_RECURSE);
callout_init_mtx(&sc->watchdog_ch, &sc->sc_mtx, 0);
- callout_init(&sc->rssadapt_ch, CALLOUT_MPSAFE);
/* wait for NIC to initialize */
for (ntries = 0; ntries < 1000; ntries++) {
@@ -218,36 +234,12 @@ rt2661_attach(device_t dev, int id)
}
/* retrieve RF rev. no and various other things from EEPROM */
- rt2661_read_eeprom(sc);
+ rt2661_read_eeprom(sc, ic);
device_printf(dev, "MAC/BBP RT%X, RF %s\n", val,
rt2661_get_rf(sc->rf_rev));
/*
- * Load 8051 microcode into NIC.
- */
- switch (id) {
- case 0x0301:
- ucode = rt2561s_ucode;
- size = sizeof rt2561s_ucode;
- break;
- case 0x0302:
- ucode = rt2561_ucode;
- size = sizeof rt2561_ucode;
- break;
- case 0x0401:
- ucode = rt2661_ucode;
- size = sizeof rt2661_ucode;
- break;
- }
-
- error = rt2661_load_microcode(sc, ucode, size);
- if (error != 0) {
- device_printf(sc->sc_dev, "could not load 8051 microcode\n");
- goto fail1;
- }
-
- /*
* Allocate Tx and Rx rings.
*/
for (ac = 0; ac < 4; ac++) {
@@ -272,13 +264,6 @@ rt2661_attach(device_t dev, int id)
goto fail3;
}
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
- if (ifp == NULL) {
- device_printf(sc->sc_dev, "can not if_alloc()\n");
- error = ENOMEM;
- goto fail4;
- }
-
ifp->if_softc = sc;
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
@@ -290,50 +275,53 @@ rt2661_attach(device_t dev, int id)
IFQ_SET_READY(&ifp->if_snd);
ic->ic_ifp = ifp;
+ ic->ic_opmode = IEEE80211_M_STA;
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
- ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
- ic->ic_state = IEEE80211_S_INIT;
/* set device capabilities */
ic->ic_caps =
- IEEE80211_C_IBSS | /* IBSS mode supported */
- IEEE80211_C_MONITOR | /* monitor mode supported */
- IEEE80211_C_HOSTAP | /* HostAp mode supported */
- IEEE80211_C_TXPMGT | /* tx power management */
- IEEE80211_C_SHPREAMBLE | /* short preamble supported */
- IEEE80211_C_SHSLOT | /* short slot time supported */
+ IEEE80211_C_IBSS /* ibss, nee adhoc, mode */
+ | IEEE80211_C_HOSTAP /* hostap mode */
+ | IEEE80211_C_MONITOR /* monitor mode */
+ | IEEE80211_C_AHDEMO /* adhoc demo mode */
+ | IEEE80211_C_WDS /* 4-address traffic works */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_WPA /* capable of WPA1+WPA2 */
+ | IEEE80211_C_BGSCAN /* capable of bg scanning */
#ifdef notyet
- IEEE80211_C_WME | /* 802.11e */
+ | IEEE80211_C_TXFRAG /* handle tx frags */
+ | IEEE80211_C_WME /* 802.11e */
#endif
- IEEE80211_C_BGSCAN | /* bg scanning support */
- IEEE80211_C_WPA; /* 802.11i */
+ ;
bands = 0;
setbit(&bands, IEEE80211_MODE_11B);
setbit(&bands, IEEE80211_MODE_11G);
if (sc->rf_rev == RT2661_RF_5225 || sc->rf_rev == RT2661_RF_5325)
setbit(&bands, IEEE80211_MODE_11A);
- ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
+ ieee80211_init_channels(ic, NULL, &bands);
ieee80211_ifattach(ic);
+ ic->ic_newassoc = rt2661_newassoc;
ic->ic_node_alloc = rt2661_node_alloc;
-/* ic->ic_wme.wme_update = rt2661_wme_update;*/
+#if 0
+ ic->ic_wme.wme_update = rt2661_wme_update;
+#endif
ic->ic_scan_start = rt2661_scan_start;
ic->ic_scan_end = rt2661_scan_end;
ic->ic_set_channel = rt2661_set_channel;
ic->ic_updateslot = rt2661_update_slot;
- ic->ic_reset = rt2661_reset;
- /* enable s/w bmiss handling in sta mode */
- ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;
+ ic->ic_update_promisc = rt2661_update_promisc;
+ ic->ic_raw_xmit = rt2661_raw_xmit;
- /* override state transition machine */
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = rt2661_newstate;
- ieee80211_media_init(ic, rt2661_media_change, ieee80211_media_status);
+ ic->ic_vap_create = rt2661_vap_create;
+ ic->ic_vap_delete = rt2661_vap_delete;
+
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap),
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap));
sc->sc_rxtap_len = sizeof sc->sc_rxtap;
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
@@ -343,27 +331,21 @@ rt2661_attach(device_t dev, int id)
sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
sc->sc_txtap.wt_ihdr.it_present = htole32(RT2661_TX_RADIOTAP_PRESENT);
-
- /*
- * Add a few sysctl knobs.
- */
- sc->dwelltime = 200;
-
+#ifdef RAL_DEBUG
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "dwell",
- CTLFLAG_RW, &sc->dwelltime, 0,
- "channel dwell time (ms) for AP/station scanning");
-
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "debug", CTLFLAG_RW, &sc->sc_debug, 0, "debug msgs");
+#endif
if (bootverbose)
ieee80211_announce(ic);
return 0;
-fail4: rt2661_free_rx_ring(sc, &sc->rxq);
fail3: rt2661_free_tx_ring(sc, &sc->mgtq);
fail2: while (--ac >= 0)
rt2661_free_tx_ring(sc, &sc->txq[ac]);
fail1: mtx_destroy(&sc->sc_mtx);
+ if_free(ifp);
return error;
}
@@ -371,14 +353,12 @@ int
rt2661_detach(void *xsc)
{
struct rt2661_softc *sc = xsc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
RAL_LOCK(sc);
rt2661_stop_locked(sc);
- callout_stop(&sc->watchdog_ch);
RAL_UNLOCK(sc);
- callout_stop(&sc->rssadapt_ch);
bpfdetach(ifp);
ieee80211_ifdetach(ic);
@@ -397,6 +377,82 @@ rt2661_detach(void *xsc)
return 0;
}
+static struct ieee80211vap *
+rt2661_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct rt2661_vap *rvp;
+ struct ieee80211vap *vap;
+
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_AHDEMO:
+ case IEEE80211_M_MONITOR:
+ case IEEE80211_M_HOSTAP:
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) {
+ if_printf(ifp, "only 1 vap supported\n");
+ return NULL;
+ }
+ if (opmode == IEEE80211_M_STA)
+ flags |= IEEE80211_CLONE_NOBEACONS;
+ break;
+ case IEEE80211_M_WDS:
+ if (TAILQ_EMPTY(&ic->ic_vaps) ||
+ ic->ic_opmode != IEEE80211_M_HOSTAP) {
+ if_printf(ifp, "wds only supported in ap mode\n");
+ return NULL;
+ }
+ /*
+ * Silently remove any request for a unique
+ * bssid; WDS vap's always share the local
+ * mac address.
+ */
+ flags &= ~IEEE80211_CLONE_BSSID;
+ break;
+ default:
+ if_printf(ifp, "unknown opmode %d\n", opmode);
+ return NULL;
+ }
+ rvp = (struct rt2661_vap *) malloc(sizeof(struct rt2661_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (rvp == NULL)
+ return NULL;
+ vap = &rvp->ral_vap;
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
+
+ /* override state transition machine */
+ rvp->ral_newstate = vap->iv_newstate;
+ vap->iv_newstate = rt2661_newstate;
+#if 0
+ vap->iv_update_beacon = rt2661_beacon_update;
+#endif
+
+ ieee80211_amrr_init(&rvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 500 /* ms */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ if (TAILQ_FIRST(&ic->ic_vaps) == vap)
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+rt2661_vap_delete(struct ieee80211vap *vap)
+{
+ struct rt2661_vap *rvp = RT2661_VAP(vap);
+
+ ieee80211_amrr_cleanup(&rvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(rvp, M_80211_VAP);
+}
+
void
rt2661_shutdown(void *xsc)
{
@@ -417,13 +473,10 @@ void
rt2661_resume(void *xsc)
{
struct rt2661_softc *sc = xsc;
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
- if (ifp->if_flags & IFF_UP) {
- ifp->if_init(ifp->if_softc);
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- ifp->if_start(ifp);
- }
+ if (ifp->if_flags & IFF_UP)
+ rt2661_init(sc);
}
static void
@@ -732,102 +785,58 @@ rt2661_node_alloc(struct ieee80211_node_table *nt)
return (rn != NULL) ? &rn->ni : NULL;
}
-static int
-rt2661_media_change(struct ifnet *ifp)
-{
- struct rt2661_softc *sc = ifp->if_softc;
- int error;
-
- error = ieee80211_media_change(ifp);
- if (error != ENETRESET)
- return error;
-
- if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
- rt2661_init(sc);
-
- return 0;
-}
-
-/*
- * This function is called for each node present in the node station table.
- */
-static void
-rt2661_iter_func(void *arg, struct ieee80211_node *ni)
-{
- struct rt2661_node *rn = (struct rt2661_node *)ni;
-
- ral_rssadapt_updatestats(&rn->rssadapt);
-}
-
-/*
- * This function is called periodically (every 100ms) in RUN state to update
- * the rate adaptation statistics.
- */
static void
-rt2661_update_rssadapt(void *arg)
+rt2661_newassoc(struct ieee80211_node *ni, int isnew)
{
- struct rt2661_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
-
- RAL_LOCK(sc);
-
- ieee80211_iterate_nodes(&ic->ic_sta, rt2661_iter_func, arg);
- callout_reset(&sc->rssadapt_ch, hz / 10, rt2661_update_rssadapt, sc);
+ struct ieee80211vap *vap = ni->ni_vap;
- RAL_UNLOCK(sc);
+ ieee80211_amrr_node_init(&RT2661_VAP(vap)->amrr,
+ &RT2661_NODE(ni)->amrr, ni);
}
static int
-rt2661_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+rt2661_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct rt2661_vap *rvp = RT2661_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
struct rt2661_softc *sc = ic->ic_ifp->if_softc;
- enum ieee80211_state ostate;
- struct ieee80211_node *ni;
- uint32_t tmp;
- int error = 0;
+ int error;
- ostate = ic->ic_state;
+ if (nstate == IEEE80211_S_INIT && vap->iv_state == IEEE80211_S_RUN) {
+ uint32_t tmp;
- switch (nstate) {
- case IEEE80211_S_INIT:
- callout_stop(&sc->rssadapt_ch);
+ /* abort TSF synchronization */
+ tmp = RAL_READ(sc, RT2661_TXRX_CSR9);
+ RAL_WRITE(sc, RT2661_TXRX_CSR9, tmp & ~0x00ffffff);
+ }
- if (ostate == IEEE80211_S_RUN) {
- /* abort TSF synchronization */
- tmp = RAL_READ(sc, RT2661_TXRX_CSR9);
- RAL_WRITE(sc, RT2661_TXRX_CSR9, tmp & ~0x00ffffff);
- }
- break;
- case IEEE80211_S_RUN:
- ni = ic->ic_bss;
+ error = rvp->ral_newstate(vap, nstate, arg);
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ if (error == 0 && nstate == IEEE80211_S_RUN) {
+ struct ieee80211_node *ni = vap->iv_bss;
+
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
rt2661_enable_mrr(sc);
rt2661_set_txpreamble(sc);
rt2661_set_basicrates(sc, &ni->ni_rates);
rt2661_set_bssid(sc, ni->ni_bssid);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS) {
- if ((error = rt2661_prepare_beacon(sc)) != 0)
- break;
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS) {
+ error = rt2661_prepare_beacon(sc, vap);
+ if (error != 0)
+ return error;
}
-
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- callout_reset(&sc->rssadapt_ch, hz / 10,
- rt2661_update_rssadapt, sc);
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+ /* fake a join to init the tx rate */
+ rt2661_newassoc(ni, 1);
+ }
rt2661_enable_tsf_sync(sc);
}
- break;
- case IEEE80211_S_SCAN:
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
- default:
- break;
- }
-
- return (error != 0) ? error : sc->sc_newstate(ic, nstate, arg);
+ }
+ return error;
}
/*
@@ -891,8 +900,7 @@ rt2661_eeprom_read(struct rt2661_softc *sc, uint8_t addr)
static void
rt2661_tx_intr(struct rt2661_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
struct rt2661_tx_ring *txq;
struct rt2661_tx_data *data;
struct rt2661_node *rn;
@@ -922,28 +930,28 @@ rt2661_tx_intr(struct rt2661_softc *sc)
if (ni == NULL)
continue;
- rn = (struct rt2661_node *)ni;
+ rn = RT2661_NODE(ni);
switch (RT2661_TX_RESULT(val)) {
case RT2661_TX_SUCCESS:
retrycnt = RT2661_TX_RETRYCNT(val);
- DPRINTFN(10, ("data frame sent successfully after "
- "%d retries\n", retrycnt));
- if (retrycnt == 0 && data->id.id_node != NULL) {
- ral_rssadapt_raise_rate(ic, &rn->rssadapt,
- &data->id);
- }
+ DPRINTFN(sc, 10, "data frame sent successfully after "
+ "%d retries\n", retrycnt);
+ if (data->rix != IEEE80211_FIXED_RATE_NONE)
+ ieee80211_amrr_tx_complete(&rn->amrr,
+ IEEE80211_AMRR_SUCCESS, retrycnt);
ifp->if_opackets++;
break;
case RT2661_TX_RETRY_FAIL:
- DPRINTFN(9, ("sending data frame failed (too much "
- "retries)\n"));
- if (data->id.id_node != NULL) {
- ral_rssadapt_lower_rate(ic, ni,
- &rn->rssadapt, &data->id);
- }
+ retrycnt = RT2661_TX_RETRYCNT(val);
+
+ DPRINTFN(sc, 9, "%s\n",
+ "sending data frame failed (too much retries)");
+ if (data->rix != IEEE80211_FIXED_RATE_NONE)
+ ieee80211_amrr_tx_complete(&rn->amrr,
+ IEEE80211_AMRR_FAILURE, retrycnt);
ifp->if_oerrors++;
break;
@@ -954,7 +962,7 @@ rt2661_tx_intr(struct rt2661_softc *sc)
ifp->if_oerrors++;
}
- DPRINTFN(15, ("tx done q=%d idx=%u\n", qid, txq->stat));
+ DPRINTFN(sc, 15, "tx done q=%d idx=%u\n", qid, txq->stat);
txq->queued--;
if (++txq->stat >= txq->count) /* faster than % count */
@@ -969,7 +977,8 @@ rt2661_tx_intr(struct rt2661_softc *sc)
sc->sc_tx_timer = 0;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- rt2661_start(ifp);
+
+ rt2661_start_locked(ifp);
}
static void
@@ -995,7 +1004,7 @@ rt2661_tx_dma_intr(struct rt2661_softc *sc, struct rt2661_tx_ring *txq)
/* descriptor is no longer valid */
desc->flags &= ~htole32(RT2661_TX_VALID);
- DPRINTFN(15, ("tx dma done q=%p idx=%u\n", txq, txq->next));
+ DPRINTFN(sc, 15, "tx dma done q=%p idx=%u\n", txq, txq->next);
if (++txq->next >= txq->count) /* faster than % count */
txq->next = 0;
@@ -1007,14 +1016,13 @@ rt2661_tx_dma_intr(struct rt2661_softc *sc, struct rt2661_tx_ring *txq)
static void
rt2661_rx_intr(struct rt2661_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rt2661_rx_desc *desc;
struct rt2661_rx_data *data;
bus_addr_t physaddr;
struct ieee80211_frame *wh;
struct ieee80211_node *ni;
- struct rt2661_node *rn;
struct mbuf *mnew, *m;
int error;
@@ -1036,8 +1044,8 @@ rt2661_rx_intr(struct rt2661_softc *sc)
* This should not happen since we did not request
* to receive those frames when we filled TXRX_CSR0.
*/
- DPRINTFN(5, ("PHY or CRC error flags 0x%08x\n",
- le32toh(desc->flags)));
+ DPRINTFN(sc, 5, "PHY or CRC error flags 0x%08x\n",
+ le32toh(desc->flags));
ifp->if_ierrors++;
goto skip;
}
@@ -1098,7 +1106,7 @@ rt2661_rx_intr(struct rt2661_softc *sc)
rssi = rt2661_get_rssi(sc, desc->rssi);
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rt2661_rx_radiotap_header *tap = &sc->sc_rxtap;
uint32_t tsf_lo, tsf_hi;
@@ -1109,38 +1117,37 @@ rt2661_rx_intr(struct rt2661_softc *sc)
tap->wr_tsf =
htole64(((uint64_t)tsf_hi << 32) | tsf_lo);
tap->wr_flags = 0;
- tap->wr_rate = rt2661_rxrate(desc);
- tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
- tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wr_rate = ieee80211_plcp2rate(desc->rate,
+ le32toh(desc->flags) & RT2661_RX_OFDM);
tap->wr_antsignal = rssi < 0 ? 0 : rssi;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
}
sc->sc_flags |= RAL_INPUT_RUNNING;
RAL_UNLOCK(sc);
wh = mtod(m, struct ieee80211_frame *);
- ni = ieee80211_find_rxnode(ic,
- (struct ieee80211_frame_min *)wh);
-
- /* Error happened during RSSI conversion. */
- if (rssi < 0)
- rssi = ni->ni_rssi;
/* send the frame to the 802.11 layer */
- ieee80211_input(ic, m, ni, rssi, RT2661_NOISE_FLOOR, 0);
+ ni = ieee80211_find_rxnode(ic,
+ (struct ieee80211_frame_min *)wh);
+ if (ni != NULL) {
+ /* Error happened during RSSI conversion. */
+ if (rssi < 0)
+ rssi = -30; /* XXX ignored by net80211 */
+
+ (void) ieee80211_input(ni, m, rssi,
+ RT2661_NOISE_FLOOR, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi,
+ RT2661_NOISE_FLOOR, 0);
- /* give rssi to the rate adatation algorithm */
- rn = (struct rt2661_node *)ni;
RAL_LOCK(sc);
sc->sc_flags &= ~RAL_INPUT_RUNNING;
- ral_rssadapt_input(ic, ni, &rn->rssadapt, rssi);
-
- /* node is no longer needed */
- ieee80211_free_node(ni);
skip: desc->flags |= htole32(RT2661_RX_BUSY);
- DPRINTFN(15, ("rx intr idx=%u\n", sc->rxq.cur));
+ DPRINTFN(sc, 15, "rx intr idx=%u\n", sc->rxq.cur);
sc->rxq.cur = (sc->rxq.cur + 1) % RT2661_RX_RING_COUNT;
}
@@ -1238,137 +1245,13 @@ rt2661_intr(void *arg)
RAL_UNLOCK(sc);
}
-/* quickly determine if a given rate is CCK or OFDM */
-#define RAL_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
-
-#define RAL_ACK_SIZE 14 /* 10 + 4(FCS) */
-#define RAL_CTS_SIZE 14 /* 10 + 4(FCS) */
-
-#define RAL_SIFS 10 /* us */
-
-/*
- * This function is only used by the Rx radiotap code. It returns the rate at
- * which a given frame was received.
- */
-static uint8_t
-rt2661_rxrate(struct rt2661_rx_desc *desc)
-{
- if (le32toh(desc->flags) & RT2661_RX_OFDM) {
- /* reverse function of rt2661_plcp_signal */
- switch (desc->rate & 0xf) {
- case 0xb: return 12;
- case 0xf: return 18;
- case 0xa: return 24;
- case 0xe: return 36;
- case 0x9: return 48;
- case 0xd: return 72;
- case 0x8: return 96;
- case 0xc: return 108;
- }
- } else {
- if (desc->rate == 10)
- return 2;
- if (desc->rate == 20)
- return 4;
- if (desc->rate == 55)
- return 11;
- if (desc->rate == 110)
- return 22;
- }
- return 2; /* should not get there */
-}
-
-/*
- * Return the expected ack rate for a frame transmitted at rate `rate'.
- * XXX: this should depend on the destination node basic rate set.
- */
-static int
-rt2661_ack_rate(struct ieee80211com *ic, int rate)
-{
- switch (rate) {
- /* CCK rates */
- case 2:
- return 2;
- case 4:
- case 11:
- case 22:
- return (ic->ic_curmode == IEEE80211_MODE_11B) ? 4 : rate;
-
- /* OFDM rates */
- case 12:
- case 18:
- return 12;
- case 24:
- case 36:
- return 24;
- case 48:
- case 72:
- case 96:
- case 108:
- return 48;
- }
-
- /* default to 1Mbps */
- return 2;
-}
-
-/*
- * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'.
- * The function automatically determines the operating mode depending on the
- * given rate. `flags' indicates whether short preamble is in use or not.
- */
-static uint16_t
-rt2661_txtime(int len, int rate, uint32_t flags)
-{
- uint16_t txtime;
-
- if (RAL_RATE_IS_OFDM(rate)) {
- /* IEEE Std 802.11a-1999, pp. 37 */
- txtime = (8 + 4 * len + 3 + rate - 1) / rate;
- txtime = 16 + 4 + 4 * txtime + 6;
- } else {
- /* IEEE Std 802.11b-1999, pp. 28 */
- txtime = (16 * len + rate - 1) / rate;
- if (rate != 2 && (flags & IEEE80211_F_SHPREAMBLE))
- txtime += 72 + 24;
- else
- txtime += 144 + 48;
- }
-
- return txtime;
-}
-
-static uint8_t
-rt2661_plcp_signal(int rate)
-{
- switch (rate) {
- /* CCK rates (returned values are device-dependent) */
- case 2: return 0x0;
- case 4: return 0x1;
- case 11: return 0x2;
- case 22: return 0x3;
-
- /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
- case 12: return 0xb;
- case 18: return 0xf;
- case 24: return 0xa;
- case 36: return 0xe;
- case 48: return 0x9;
- case 72: return 0xd;
- case 96: return 0x8;
- case 108: return 0xc;
-
- /* unsupported rates (should not get there) */
- default: return 0xff;
- }
-}
-
static void
rt2661_setup_tx_desc(struct rt2661_softc *sc, struct rt2661_tx_desc *desc,
uint32_t flags, uint16_t xflags, int len, int rate,
const bus_dma_segment_t *segs, int nsegs, int ac)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint16_t plcp_length;
int i, remainder;
@@ -1393,11 +1276,11 @@ rt2661_setup_tx_desc(struct rt2661_softc *sc, struct rt2661_tx_desc *desc,
desc->qid = ac;
/* setup PLCP fields */
- desc->plcp_signal = rt2661_plcp_signal(rate);
+ desc->plcp_signal = ieee80211_rate2plcp(rate);
desc->plcp_service = 4;
len += IEEE80211_CRC_LEN;
- if (RAL_RATE_IS_OFDM(rate)) {
+ if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) {
desc->flags |= htole32(RT2661_TX_OFDM);
plcp_length = len & 0xfff;
@@ -1428,7 +1311,9 @@ static int
rt2661_tx_mgt(struct rt2661_softc *sc, struct mbuf *m0,
struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
struct rt2661_tx_desc *desc;
struct rt2661_tx_data *data;
struct ieee80211_frame *wh;
@@ -1441,13 +1326,12 @@ rt2661_tx_mgt(struct rt2661_softc *sc, struct mbuf *m0,
desc = &sc->mgtq.desc[sc->mgtq.cur];
data = &sc->mgtq.data[sc->mgtq.cur];
- /* send mgt frames at the lowest available rate */
- rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
+ rate = vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)].mgmtrate;
wh = mtod(m0, struct ieee80211_frame *);
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -1463,27 +1347,27 @@ rt2661_tx_mgt(struct rt2661_softc *sc, struct mbuf *m0,
return error;
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rt2661_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
tap->wt_rate = rate;
- tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
- tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data->m = m0;
data->ni = ni;
+ /* management frames are not taken into account for amrr */
+ data->rix = IEEE80211_FIXED_RATE_NONE;
wh = mtod(m0, struct ieee80211_frame *);
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
flags |= RT2661_TX_NEED_ACK;
- dur = rt2661_txtime(RAL_ACK_SIZE, rate, ic->ic_flags) +
- RAL_SIFS;
+ dur = ieee80211_ack_duration(sc->sc_rates,
+ rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
/* tell hardware to add timestamp in probe responses */
@@ -1500,8 +1384,8 @@ rt2661_tx_mgt(struct rt2661_softc *sc, struct mbuf *m0,
bus_dmamap_sync(sc->mgtq.desc_dmat, sc->mgtq.desc_map,
BUS_DMASYNC_PREWRITE);
- DPRINTFN(10, ("sending mgt frame len=%u idx=%u rate=%u\n",
- m0->m_pkthdr.len, sc->mgtq.cur, rate));
+ DPRINTFN(sc, 10, "sending mgt frame len=%u idx=%u rate=%u\n",
+ m0->m_pkthdr.len, sc->mgtq.cur, rate);
/* kick mgt */
sc->mgtq.queued++;
@@ -1511,67 +1395,108 @@ rt2661_tx_mgt(struct rt2661_softc *sc, struct mbuf *m0,
return 0;
}
-/*
- * Build a RTS control frame.
- */
-static struct mbuf *
-rt2661_get_rts(struct rt2661_softc *sc, struct ieee80211_frame *wh,
- uint16_t dur)
+static int
+rt2661_sendprot(struct rt2661_softc *sc, int ac,
+ const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate)
{
- struct ieee80211_frame_rts *rts;
- struct mbuf *m;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct rt2661_tx_ring *txq = &sc->txq[ac];
+ const struct ieee80211_frame *wh;
+ struct rt2661_tx_desc *desc;
+ struct rt2661_tx_data *data;
+ struct mbuf *mprot;
+ int protrate, ackrate, pktlen, flags, isshort, error;
+ uint16_t dur;
+ bus_dma_segment_t segs[RT2661_MAX_SCATTER];
+ int nsegs;
- MGETHDR(m, M_DONTWAIT, MT_DATA);
- if (m == NULL) {
- sc->sc_ic.ic_stats.is_tx_nobuf++;
- device_printf(sc->sc_dev, "could not allocate RTS frame\n");
- return NULL;
+ KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY,
+ ("protection %d", prot));
+
+ wh = mtod(m, const struct ieee80211_frame *);
+ pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ protrate = ieee80211_ctl_rate(sc->sc_rates, rate);
+ ackrate = ieee80211_ack_rate(sc->sc_rates, rate);
+
+ isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
+ dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort);
+ + ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags = RT2661_TX_MORE_FRAG;
+ if (prot == IEEE80211_PROT_RTSCTS) {
+ /* NB: CTS is the same size as an ACK */
+ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags |= RT2661_TX_NEED_ACK;
+ mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
+ } else {
+ mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
+ }
+ if (mprot == NULL) {
+ /* XXX stat + msg */
+ return ENOBUFS;
+ }
+
+ data = &txq->data[txq->cur];
+ desc = &txq->desc[txq->cur];
+
+ error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map, mprot, segs,
+ &nsegs, 0);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not map mbuf (error %d)\n", error);
+ m_freem(mprot);
+ return error;
}
- rts = mtod(m, struct ieee80211_frame_rts *);
+ data->m = mprot;
+ data->ni = ieee80211_ref_node(ni);
+ /* ctl frames are not taken into account for amrr */
+ data->rix = IEEE80211_FIXED_RATE_NONE;
+
+ rt2661_setup_tx_desc(sc, desc, flags, 0, mprot->m_pkthdr.len,
+ protrate, segs, 1, ac);
- rts->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL |
- IEEE80211_FC0_SUBTYPE_RTS;
- rts->i_fc[1] = IEEE80211_FC1_DIR_NODS;
- *(uint16_t *)rts->i_dur = htole16(dur);
- IEEE80211_ADDR_COPY(rts->i_ra, wh->i_addr1);
- IEEE80211_ADDR_COPY(rts->i_ta, wh->i_addr2);
+ bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
+ bus_dmamap_sync(txq->desc_dmat, txq->desc_map, BUS_DMASYNC_PREWRITE);
- m->m_pkthdr.len = m->m_len = sizeof (struct ieee80211_frame_rts);
+ txq->queued++;
+ txq->cur = (txq->cur + 1) % RT2661_TX_RING_COUNT;
- return m;
+ return 0;
}
static int
rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0,
struct ieee80211_node *ni, int ac)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rt2661_tx_ring *txq = &sc->txq[ac];
struct rt2661_tx_desc *desc;
struct rt2661_tx_data *data;
- struct rt2661_node *rn;
struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
struct ieee80211_key *k;
const struct chanAccParams *cap;
struct mbuf *mnew;
bus_dma_segment_t segs[RT2661_MAX_SCATTER];
uint16_t dur;
- uint32_t flags = 0;
+ uint32_t flags;
int error, nsegs, rate, noack = 0;
wh = mtod(m0, struct ieee80211_frame *);
- if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
- rate = ic->ic_fixed_rate;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ rate = tp->mcastrate;
+ } else if (m0->m_flags & M_EAPOL) {
+ rate = tp->mgmtrate;
+ } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ rate = tp->ucastrate;
} else {
- struct ieee80211_rateset *rs;
-
- rs = &ni->ni_rates;
- rn = (struct rt2661_node *)ni;
- ni->ni_txrate = ral_rssadapt_choose(&rn->rssadapt, rs,
- wh, m0->m_pkthdr.len, NULL, 0);
- rate = rs->rs_rates[ni->ni_txrate];
+ (void) ieee80211_amrr_choose(ni, &RT2661_NODE(ni)->amrr);
+ rate = ni->ni_txrate;
}
rate &= IEEE80211_RATE_VAL;
@@ -1581,7 +1506,7 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0,
}
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -1591,66 +1516,22 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0,
wh = mtod(m0, struct ieee80211_frame *);
}
- /*
- * IEEE Std 802.11-1999, pp 82: "A STA shall use an RTS/CTS exchange
- * for directed frames only when the length of the MPDU is greater
- * than the length threshold indicated by [...]" ic_rtsthreshold.
- */
- if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
- m0->m_pkthdr.len > ic->ic_rtsthreshold) {
- struct mbuf *m;
- uint16_t dur;
- int rtsrate, ackrate;
-
- rtsrate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
- ackrate = rt2661_ack_rate(ic, rate);
-
- dur = rt2661_txtime(m0->m_pkthdr.len + 4, rate, ic->ic_flags) +
- rt2661_txtime(RAL_CTS_SIZE, rtsrate, ic->ic_flags) +
- /* XXX: noack (QoS)? */
- rt2661_txtime(RAL_ACK_SIZE, ackrate, ic->ic_flags) +
- 3 * RAL_SIFS;
-
- m = rt2661_get_rts(sc, wh, dur);
-
- desc = &txq->desc[txq->cur];
- data = &txq->data[txq->cur];
-
- error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map, m,
- segs, &nsegs, 0);
- if (error != 0) {
- device_printf(sc->sc_dev,
- "could not map mbuf (error %d)\n", error);
- m_freem(m);
- m_freem(m0);
- return error;
+ flags = 0;
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ int prot = IEEE80211_PROT_NONE;
+ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
+ prot = IEEE80211_PROT_RTSCTS;
+ else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM)
+ prot = ic->ic_protmode;
+ if (prot != IEEE80211_PROT_NONE) {
+ error = rt2661_sendprot(sc, ac, m0, ni, prot, rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RT2661_TX_LONG_RETRY | RT2661_TX_IFS;
}
-
- /* avoid multiple free() of the same node for each fragment */
- ieee80211_ref_node(ni);
-
- data->m = m;
- data->ni = ni;
-
- /* RTS frames are not taken into account for rssadapt */
- data->id.id_node = NULL;
-
- rt2661_setup_tx_desc(sc, desc, RT2661_TX_NEED_ACK |
- RT2661_TX_MORE_FRAG, 0, m->m_pkthdr.len, rtsrate, segs,
- nsegs, ac);
-
- bus_dmamap_sync(txq->data_dmat, data->map,
- BUS_DMASYNC_PREWRITE);
-
- txq->queued++;
- txq->cur = (txq->cur + 1) % RT2661_TX_RING_COUNT;
-
- /*
- * IEEE Std 802.11-1999: when an RTS/CTS exchange is used, the
- * asynchronous data frame shall be transmitted after the CTS
- * frame and a SIFS period.
- */
- flags |= RT2661_TX_LONG_RETRY | RT2661_TX_IFS;
}
data = &txq->data[txq->cur];
@@ -1687,7 +1568,7 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0,
wh = mtod(m0, struct ieee80211_frame *);
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rt2661_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -1695,26 +1576,25 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0,
tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data->m = m0;
data->ni = ni;
/* remember link conditions for rate adaptation algorithm */
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
- data->id.id_len = m0->m_pkthdr.len;
- data->id.id_rateidx = ni->ni_txrate;
- data->id.id_node = ni;
- data->id.id_rssi = ni->ni_rssi;
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) {
+ data->rix = ni->ni_txrate;
+ /* XXX probably need last rssi value and not avg */
+ data->rssi = ic->ic_node_getrssi(ni);
} else
- data->id.id_node = NULL;
+ data->rix = IEEE80211_FIXED_RATE_NONE;
if (!noack && !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
flags |= RT2661_TX_NEED_ACK;
- dur = rt2661_txtime(RAL_ACK_SIZE, rt2661_ack_rate(ic, rate),
- ic->ic_flags) + RAL_SIFS;
+ dur = ieee80211_ack_duration(sc->sc_rates,
+ rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
}
@@ -1724,8 +1604,8 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0,
bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
bus_dmamap_sync(txq->desc_dmat, txq->desc_map, BUS_DMASYNC_PREWRITE);
- DPRINTFN(10, ("sending data frame len=%u idx=%u rate=%u\n",
- m0->m_pkthdr.len, txq->cur, rate));
+ DPRINTFN(sc, 10, "sending data frame len=%u idx=%u rate=%u\n",
+ m0->m_pkthdr.len, txq->cur, rate);
/* kick Tx */
txq->queued++;
@@ -1736,181 +1616,163 @@ rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0,
}
static void
-rt2661_start(struct ifnet *ifp)
+rt2661_start_locked(struct ifnet *ifp)
{
struct rt2661_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct mbuf *m0;
- struct ether_header *eh;
+ struct mbuf *m;
struct ieee80211_node *ni;
int ac;
- RAL_LOCK(sc);
+ RAL_LOCK_ASSERT(sc);
/* prevent management frames from being sent if we're not ready */
- if (!(ifp->if_drv_flags & IFF_DRV_RUNNING) || sc->sc_invalid) {
- RAL_UNLOCK(sc);
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING) || sc->sc_invalid)
return;
- }
for (;;) {
- IF_POLL(&ic->ic_mgtq, m0);
- if (m0 != NULL) {
- if (sc->mgtq.queued >= RT2661_MGT_RING_COUNT) {
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- IF_DEQUEUE(&ic->ic_mgtq, m0);
-
- ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
- m0->m_pkthdr.rcvif = NULL;
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
- if (rt2661_tx_mgt(sc, m0, ni) != 0) {
- ieee80211_free_node(ni);
- break;
- }
- } else {
- if (ic->ic_state != IEEE80211_S_RUN)
- break;
+ ac = M_WME_GETAC(m);
+ if (sc->txq[ac].queued >= RT2661_TX_RING_COUNT - 1) {
+ /* there is no place left in this ring */
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ continue;
+ }
- if (m0->m_len < sizeof (struct ether_header) &&
- !(m0 = m_pullup(m0, sizeof (struct ether_header))))
- continue;
+ if (rt2661_tx_data(sc, m, ni, ac) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
+ }
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- ifp->if_oerrors++;
- continue;
- }
+ sc->sc_tx_timer = 5;
+ }
+}
- /* classify mbuf so we can find which tx ring to use */
- if (ieee80211_classify(ic, m0, ni) != 0) {
- m_freem(m0);
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- continue;
- }
+static void
+rt2661_start(struct ifnet *ifp)
+{
+ struct rt2661_softc *sc = ifp->if_softc;
- /* no QoS encapsulation for EAPOL frames */
- ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ?
- M_WME_GETAC(m0) : WME_AC_BE;
+ RAL_LOCK(sc);
+ rt2661_start_locked(ifp);
+ RAL_UNLOCK(sc);
+}
- if (sc->txq[ac].queued >= RT2661_TX_RING_COUNT - 1) {
- /* there is no place left in this ring */
- IFQ_DRV_PREPEND(&ifp->if_snd, m0);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- ieee80211_free_node(ni);
- break;
- }
+static int
+rt2661_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct rt2661_softc *sc = ifp->if_softc;
- BPF_MTAP(ifp, m0);
+ RAL_LOCK(sc);
- m0 = ieee80211_encap(ic, m0, ni);
- if (m0 == NULL) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- continue;
- }
+ /* prevent management frames from being sent if we're not ready */
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ RAL_UNLOCK(sc);
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return ENETDOWN;
+ }
+ if (sc->mgtq.queued >= RT2661_MGT_RING_COUNT) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ RAL_UNLOCK(sc);
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return ENOBUFS; /* XXX */
+ }
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
+ ifp->if_opackets++;
- if (rt2661_tx_data(sc, m0, ni, ac) != 0) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- break;
- }
- }
+ /*
+ * Legacy path; interpret frame contents to decide
+ * precisely how to send the frame.
+ * XXX raw path
+ */
+ if (rt2661_tx_mgt(sc, m, ni) != 0)
+ goto bad;
+ sc->sc_tx_timer = 5;
- sc->sc_tx_timer = 5;
- ic->ic_lastdata = ticks;
- callout_reset(&sc->watchdog_ch, hz, rt2661_watchdog, sc);
- }
+ RAL_UNLOCK(sc);
+ return 0;
+bad:
+ ifp->if_oerrors++;
+ ieee80211_free_node(ni);
RAL_UNLOCK(sc);
+ return EIO; /* XXX */
}
static void
rt2661_watchdog(void *arg)
{
struct rt2661_softc *sc = (struct rt2661_softc *)arg;
+ struct ifnet *ifp = sc->sc_ifp;
- if (sc->sc_tx_timer > 0 && !sc->sc_invalid) {
- if (--sc->sc_tx_timer == 0) {
- device_printf(sc->sc_dev, "device timeout\n");
- rt2661_init(sc);
- sc->sc_ifp->if_oerrors++;
- return;
- }
- callout_reset(&sc->watchdog_ch, hz, rt2661_watchdog, sc);
- }
-}
+ RAL_LOCK_ASSERT(sc);
-/*
- * This function allows for fast channel switching in monitor mode (used by
- * net-mgmt/kismet). In IBSS mode, we must explicitly reset the interface to
- * generate a new beacon frame.
- */
-static int
-rt2661_reset(struct ifnet *ifp)
-{
- struct rt2661_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
-
- if (ic->ic_opmode != IEEE80211_M_MONITOR)
- return ENETRESET;
+ KASSERT(ifp->if_drv_flags & IFF_DRV_RUNNING, ("not running"));
- rt2661_set_chan(sc, ic->ic_curchan);
+ if (sc->sc_invalid) /* card ejected */
+ return;
- return 0;
+ if (sc->sc_tx_timer > 0 && --sc->sc_tx_timer == 0) {
+ if_printf(ifp, "device timeout\n");
+ rt2661_init_locked(sc);
+ ifp->if_oerrors++;
+ /* NB: callout is reset in rt2661_init() */
+ return;
+ }
+ callout_reset(&sc->watchdog_ch, hz, rt2661_watchdog, sc);
}
static int
rt2661_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct rt2661_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
+ RAL_LOCK(sc);
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- rt2661_update_promisc(sc);
- else
- rt2661_init(sc);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ rt2661_init_locked(sc);
+ startall = 1;
+ } else
+ rt2661_update_promisc(ifp);
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- rt2661_stop(sc);
+ rt2661_stop_locked(sc);
}
break;
-
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
default:
- error = ieee80211_ioctl(ic, cmd, data);
- }
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
- (ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
- rt2661_init(sc);
- error = 0;
+ error = ether_ioctl(ifp, cmd, data);
+ break;
}
+ RAL_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
@@ -1933,7 +1795,7 @@ rt2661_bbp_write(struct rt2661_softc *sc, uint8_t reg, uint8_t val)
tmp = RT2661_BBP_BUSY | (reg & 0x7f) << 8 | val;
RAL_WRITE(sc, RT2661_PHY_CSR3, tmp);
- DPRINTFN(15, ("BBP R%u <- 0x%02x\n", reg, val));
+ DPRINTFN(sc, 15, "BBP R%u <- 0x%02x\n", reg, val);
}
static uint8_t
@@ -1989,7 +1851,7 @@ rt2661_rf_write(struct rt2661_softc *sc, uint8_t reg, uint32_t val)
/* remember last written value in sc */
sc->rf_regs[reg] = val;
- DPRINTFN(15, ("RF R[%u] <- 0x%05x\n", reg & 3, val & 0x1fffff));
+ DPRINTFN(sc, 15, "RF R[%u] <- 0x%05x\n", reg & 3, val & 0x1fffff);
}
static int
@@ -2035,13 +1897,14 @@ rt2661_select_antenna(struct rt2661_softc *sc)
static void
rt2661_enable_mrr(struct rt2661_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp;
tmp = RAL_READ(sc, RT2661_TXRX_CSR4);
tmp &= ~RT2661_MRR_CCK_FALLBACK;
- if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan))
+ if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan))
tmp |= RT2661_MRR_CCK_FALLBACK;
tmp |= RT2661_MRR_ENABLED;
@@ -2051,29 +1914,26 @@ rt2661_enable_mrr(struct rt2661_softc *sc)
static void
rt2661_set_txpreamble(struct rt2661_softc *sc)
{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp;
tmp = RAL_READ(sc, RT2661_TXRX_CSR4);
tmp &= ~RT2661_SHORT_PREAMBLE;
- if (sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE)
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
tmp |= RT2661_SHORT_PREAMBLE;
RAL_WRITE(sc, RT2661_TXRX_CSR4, tmp);
}
-/*
- * Supported rates for 802.11g. XXX should use ic_sup_rates.
- */
-static const struct ieee80211_rateset rt2661_rateset_11g =
- { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
-
static void
rt2661_set_basicrates(struct rt2661_softc *sc,
const struct ieee80211_rateset *rs)
{
#define RV(r) ((r) & IEEE80211_RATE_VAL)
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t mask = 0;
uint8_t rate;
int i, j;
@@ -2095,7 +1955,7 @@ rt2661_set_basicrates(struct rt2661_softc *sc,
RAL_WRITE(sc, RT2661_TXRX_CSR5, mask);
- DPRINTF(("Setting basic rate mask to 0x%x\n", mask));
+ DPRINTF(sc, "Setting basic rate mask to 0x%x\n", mask);
#undef RV
}
@@ -2148,15 +2008,17 @@ rt2661_select_band(struct rt2661_softc *sc, struct ieee80211_channel *c)
static void
rt2661_set_chan(struct rt2661_softc *sc, struct ieee80211_channel *c)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
const struct rfprog *rfprog;
uint8_t bbp3, bbp94 = RT2661_BBPR94_DEFAULT;
int8_t power;
u_int i, chan;
chan = ieee80211_chan2ieee(ic, c);
- if (chan == 0 || chan == IEEE80211_CHAN_ANY)
- return;
+ KASSERT(chan != 0 && chan != IEEE80211_CHAN_ANY, ("chan 0x%x", chan));
+
+ sc->sc_rates = ieee80211_get_ratetable(c);
/* select the appropriate RF settings based on what EEPROM says */
rfprog = (sc->rfprog == 0) ? rt2661_rf5225_1 : rt2661_rf5225_2;
@@ -2244,9 +2106,9 @@ rt2661_set_macaddr(struct rt2661_softc *sc, const uint8_t *addr)
}
static void
-rt2661_update_promisc(struct rt2661_softc *sc)
+rt2661_update_promisc(struct ifnet *ifp)
{
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct rt2661_softc *sc = ifp->if_softc;
uint32_t tmp;
tmp = RAL_READ(sc, RT2661_TXRX_CSR0);
@@ -2257,8 +2119,8 @@ rt2661_update_promisc(struct rt2661_softc *sc)
RAL_WRITE(sc, RT2661_TXRX_CSR0, tmp);
- DPRINTF(("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ?
- "entering" : "leaving"));
+ DPRINTF(sc, "%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ?
+ "entering" : "leaving");
}
/*
@@ -2311,7 +2173,7 @@ static void
rt2661_update_slot(struct ifnet *ifp)
{
struct rt2661_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
uint8_t slottime;
uint32_t tmp;
@@ -2335,9 +2197,8 @@ rt2661_get_rf(int rev)
}
static void
-rt2661_read_eeprom(struct rt2661_softc *sc)
+rt2661_read_eeprom(struct rt2661_softc *sc, struct ieee80211com *ic)
{
- struct ieee80211com *ic = &sc->sc_ic;
uint16_t val;
int i;
@@ -2362,14 +2223,14 @@ rt2661_read_eeprom(struct rt2661_softc *sc)
sc->tx_ant = (val >> 2) & 0x3;
sc->nb_ant = val & 0x3;
- DPRINTF(("RF revision=%d\n", sc->rf_rev));
+ DPRINTF(sc, "RF revision=%d\n", sc->rf_rev);
val = rt2661_eeprom_read(sc, RT2661_EEPROM_CONFIG2);
sc->ext_5ghz_lna = (val >> 6) & 0x1;
sc->ext_2ghz_lna = (val >> 4) & 0x1;
- DPRINTF(("External 2GHz LNA=%d\nExternal 5GHz LNA=%d\n",
- sc->ext_2ghz_lna, sc->ext_5ghz_lna));
+ DPRINTF(sc, "External 2GHz LNA=%d\nExternal 5GHz LNA=%d\n",
+ sc->ext_2ghz_lna, sc->ext_5ghz_lna);
val = rt2661_eeprom_read(sc, RT2661_EEPROM_RSSI_2GHZ_OFFSET);
if ((val & 0xff) != 0xff)
@@ -2393,8 +2254,8 @@ rt2661_read_eeprom(struct rt2661_softc *sc)
if (sc->ext_5ghz_lna)
sc->rssi_5ghz_corr -= 14;
- DPRINTF(("RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n",
- sc->rssi_2ghz_corr, sc->rssi_5ghz_corr));
+ DPRINTF(sc, "RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n",
+ sc->rssi_2ghz_corr, sc->rssi_5ghz_corr);
val = rt2661_eeprom_read(sc, RT2661_EEPROM_FREQ_OFFSET);
if ((val >> 8) != 0xff)
@@ -2402,17 +2263,17 @@ rt2661_read_eeprom(struct rt2661_softc *sc)
if ((val & 0xff) != 0xff)
sc->rffreq = val & 0xff;
- DPRINTF(("RF prog=%d\nRF freq=%d\n", sc->rfprog, sc->rffreq));
+ DPRINTF(sc, "RF prog=%d\nRF freq=%d\n", sc->rfprog, sc->rffreq);
/* read Tx power for all a/b/g channels */
for (i = 0; i < 19; i++) {
val = rt2661_eeprom_read(sc, RT2661_EEPROM_TXPOWER + i);
sc->txpow[i * 2] = (int8_t)(val >> 8); /* signed */
- DPRINTF(("Channel=%d Tx power=%d\n",
- rt2661_rf5225_1[i * 2].chan, sc->txpow[i * 2]));
+ DPRINTF(sc, "Channel=%d Tx power=%d\n",
+ rt2661_rf5225_1[i * 2].chan, sc->txpow[i * 2]);
sc->txpow[i * 2 + 1] = (int8_t)(val & 0xff); /* signed */
- DPRINTF(("Channel=%d Tx power=%d\n",
- rt2661_rf5225_1[i * 2 + 1].chan, sc->txpow[i * 2 + 1]));
+ DPRINTF(sc, "Channel=%d Tx power=%d\n",
+ rt2661_rf5225_1[i * 2 + 1].chan, sc->txpow[i * 2 + 1]);
}
/* read vendor-specific BBP values */
@@ -2422,8 +2283,8 @@ rt2661_read_eeprom(struct rt2661_softc *sc)
continue; /* skip invalid entries */
sc->bbp_prom[i].reg = val >> 8;
sc->bbp_prom[i].val = val & 0xff;
- DPRINTF(("BBP R%d=%02x\n", sc->bbp_prom[i].reg,
- sc->bbp_prom[i].val));
+ DPRINTF(sc, "BBP R%d=%02x\n", sc->bbp_prom[i].reg,
+ sc->bbp_prom[i].val);
}
}
@@ -2464,16 +2325,26 @@ rt2661_bbp_init(struct rt2661_softc *sc)
}
static void
-rt2661_init(void *priv)
+rt2661_init_locked(struct rt2661_softc *sc)
{
#define N(a) (sizeof (a) / sizeof ((a)[0]))
- struct rt2661_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp, sta[3];
- int i, ntries;
+ int i, error, ntries;
- RAL_LOCK(sc);
+ RAL_LOCK_ASSERT(sc);
+
+ if ((sc->sc_flags & RAL_FW_LOADED) == 0) {
+ error = rt2661_load_microcode(sc);
+ if (error != 0) {
+ if_printf(ifp,
+ "%s: could not load 8051 microcode, error %d\n",
+ __func__, error);
+ return;
+ }
+ sc->sc_flags |= RAL_FW_LOADED;
+ }
rt2661_stop_locked(sc);
@@ -2536,13 +2407,11 @@ rt2661_init(void *priv)
if (ntries == 1000) {
printf("timeout waiting for BBP/RF to wakeup\n");
rt2661_stop_locked(sc);
- RAL_UNLOCK(sc);
return;
}
if (rt2661_bbp_init(sc) != 0) {
rt2661_stop_locked(sc);
- RAL_UNLOCK(sc);
return;
}
@@ -2582,49 +2451,44 @@ rt2661_init(void *priv)
/* kick Rx */
RAL_WRITE(sc, RT2661_RX_CNTL_CSR, 1);
- RAL_UNLOCK(sc);
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-
-
+ callout_reset(&sc->watchdog_ch, hz, rt2661_watchdog, sc);
#undef N
}
-void
-rt2661_stop(void *priv)
+static void
+rt2661_init(void *priv)
{
struct rt2661_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
RAL_LOCK(sc);
- rt2661_stop_locked(sc);
+ rt2661_init_locked(sc);
RAL_UNLOCK(sc);
+
+ ieee80211_start_all(ic);
}
void
rt2661_stop_locked(struct rt2661_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
uint32_t tmp;
volatile int *flags = &sc->sc_flags;
- while (*flags & RAL_INPUT_RUNNING) {
+ while (*flags & RAL_INPUT_RUNNING)
msleep(sc, &sc->sc_mtx, 0, "ralrunning", hz/10);
- }
+
+ callout_stop(&sc->watchdog_ch);
+ sc->sc_tx_timer = 0;
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- sc->sc_tx_timer = 0;
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
-
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
-
+
/* abort Tx (for all 5 Tx rings) */
RAL_WRITE(sc, RT2661_TX_CNTL_CSR, 0x1f << 16);
@@ -2654,11 +2518,48 @@ rt2661_stop_locked(struct rt2661_softc *sc)
}
}
+void
+rt2661_stop(void *priv)
+{
+ struct rt2661_softc *sc = priv;
+
+ RAL_LOCK(sc);
+ rt2661_stop_locked(sc);
+ RAL_UNLOCK(sc);
+}
+
static int
-rt2661_load_microcode(struct rt2661_softc *sc, const uint8_t *ucode, int size)
+rt2661_load_microcode(struct rt2661_softc *sc)
{
- int ntries;
+ struct ifnet *ifp = sc->sc_ifp;
+ const struct firmware *fp;
+ const char *imagename;
+ int ntries, error;
+
+ RAL_LOCK_ASSERT(sc);
+
+ switch (sc->sc_id) {
+ case 0x0301: imagename = "rt2561sfw"; break;
+ case 0x0302: imagename = "rt2561fw"; break;
+ case 0x0401: imagename = "rt2661fw"; break;
+ default:
+ if_printf(ifp, "%s: unexpected pci device id 0x%x, "
+ "don't know how to retrieve firmware\n",
+ __func__, sc->sc_id);
+ return EINVAL;
+ }
+ RAL_UNLOCK(sc);
+ fp = firmware_get(imagename);
+ RAL_LOCK(sc);
+ if (fp == NULL) {
+ if_printf(ifp, "%s: unable to retrieve firmware image %s\n",
+ __func__, imagename);
+ return EINVAL;
+ }
+ /*
+ * Load 8051 microcode into NIC.
+ */
/* reset 8051 */
RAL_WRITE(sc, RT2661_MCU_CNTL_CSR, RT2661_MCU_RESET);
@@ -2669,7 +2570,7 @@ rt2661_load_microcode(struct rt2661_softc *sc, const uint8_t *ucode, int size)
/* write 8051's microcode */
RAL_WRITE(sc, RT2661_MCU_CNTL_CSR, RT2661_MCU_RESET | RT2661_MCU_SEL);
- RAL_WRITE_REGION_1(sc, RT2661_MCU_CODE_BASE, ucode, size);
+ RAL_WRITE_REGION_1(sc, RT2661_MCU_CODE_BASE, fp->data, fp->datasize);
RAL_WRITE(sc, RT2661_MCU_CNTL_CSR, RT2661_MCU_RESET);
/* kick 8051's ass */
@@ -2682,10 +2583,14 @@ rt2661_load_microcode(struct rt2661_softc *sc, const uint8_t *ucode, int size)
DELAY(100);
}
if (ntries == 500) {
- printf("timeout waiting for MCU to initialize\n");
- return EIO;
- }
- return 0;
+ if_printf(ifp, "%s: timeout waiting for MCU to initialize\n",
+ __func__);
+ error = EIO;
+ } else
+ error = 0;
+
+ firmware_put(fp, FIRMWARE_UNLOAD);
+ return error;
}
#ifdef notyet
@@ -2808,22 +2713,22 @@ rt2661_radar_stop(struct rt2661_softc *sc)
#endif
static int
-rt2661_prepare_beacon(struct rt2661_softc *sc)
+rt2661_prepare_beacon(struct rt2661_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_beacon_offsets bo;
struct rt2661_tx_desc desc;
struct mbuf *m0;
int rate;
- m0 = ieee80211_beacon_alloc(ic->ic_bss, &bo);
+ m0 = ieee80211_beacon_alloc(vap->iv_bss, &bo);
if (m0 == NULL) {
device_printf(sc->sc_dev, "could not allocate beacon frame\n");
return ENOBUFS;
}
/* send beacons at the lowest available rate */
- rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan) ? 12 : 2;
+ rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan) ? 12 : 2;
rt2661_setup_tx_desc(sc, &desc, RT2661_TX_TIMESTAMP, RT2661_TX_HWSEQ,
m0->m_pkthdr.len, rate, NULL, 0, RT2661_QID_MGT);
@@ -2847,10 +2752,12 @@ rt2661_prepare_beacon(struct rt2661_softc *sc)
static void
rt2661_enable_tsf_sync(struct rt2661_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
uint32_t tmp;
- if (ic->ic_opmode != IEEE80211_M_STA) {
+ if (vap->iv_opmode != IEEE80211_M_STA) {
/*
* Change default 16ms TBTT adjustment to 8ms.
* Must be done before enabling beacon generation.
@@ -2861,10 +2768,10 @@ rt2661_enable_tsf_sync(struct rt2661_softc *sc)
tmp = RAL_READ(sc, RT2661_TXRX_CSR9) & 0xff000000;
/* set beacon interval (in 1/16ms unit) */
- tmp |= ic->ic_bss->ni_intval * 16;
+ tmp |= vap->iv_bss->ni_intval * 16;
tmp |= RT2661_TSF_TICKING | RT2661_ENABLE_TBTT;
- if (ic->ic_opmode == IEEE80211_M_STA)
+ if (vap->iv_opmode == IEEE80211_M_STA)
tmp |= RT2661_TSF_MODE(1);
else
tmp |= RT2661_TSF_MODE(2) | RT2661_GENERATE_BEACON;
@@ -2937,10 +2844,11 @@ rt2661_scan_end(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
struct rt2661_softc *sc = ifp->if_softc;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
rt2661_enable_tsf_sync(sc);
/* XXX keep local copy */
- rt2661_set_bssid(sc, ic->ic_bss->ni_bssid);
+ rt2661_set_bssid(sc, vap->iv_bss->ni_bssid);
}
static void
@@ -2951,6 +2859,11 @@ rt2661_set_channel(struct ieee80211com *ic)
RAL_LOCK(sc);
rt2661_set_chan(sc, ic->ic_curchan);
+
+ sc->sc_txtap.wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ sc->sc_txtap.wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ sc->sc_rxtap.wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ sc->sc_rxtap.wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
RAL_UNLOCK(sc);
}
diff --git a/sys/dev/ral/rt2661var.h b/sys/dev/ral/rt2661var.h
index 9f12a15..0c15840 100644
--- a/sys/dev/ral/rt2661var.h
+++ b/sys/dev/ral/rt2661var.h
@@ -51,7 +51,8 @@ struct rt2661_tx_data {
bus_dmamap_t map;
struct mbuf *m;
struct ieee80211_node *ni;
- struct ral_rssdesc id;
+ uint8_t rix;
+ int8_t rssi;
};
struct rt2661_tx_ring {
@@ -87,14 +88,21 @@ struct rt2661_rx_ring {
struct rt2661_node {
struct ieee80211_node ni;
- struct ral_rssadapt rssadapt;
+ struct ieee80211_amrr_node amrr;
};
+#define RT2661_NODE(ni) ((struct rt2661_node *)(ni))
+
+struct rt2661_vap {
+ struct ieee80211vap ral_vap;
+ struct ieee80211_amrr amrr;
+
+ int (*ral_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define RT2661_VAP(vap) ((struct rt2661_vap *)(vap))
struct rt2661_softc {
struct ifnet *sc_ifp;
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
device_t sc_dev;
bus_space_tag_t sc_st;
bus_space_handle_t sc_sh;
@@ -102,15 +110,21 @@ struct rt2661_softc {
struct mtx sc_mtx;
struct callout watchdog_ch;
- struct callout rssadapt_ch;
int sc_tx_timer;
int sc_invalid;
+ int sc_debug;
+
+ const struct ieee80211_rate_table *sc_rates;
/*
* The same in both up to here
* ------------------------------------------------
*/
+ int sc_flags;
+#define RAL_FW_LOADED 0x1
+#define RAL_INPUT_RUNNING 0x2
+ int sc_id;
struct ieee80211_channel *sc_curchan;
uint8_t rf_rev;
@@ -148,23 +162,10 @@ struct rt2661_softc {
int dwelltime;
- struct bpf_if *sc_drvbpf;
-
- union {
- struct rt2661_rx_radiotap_header th;
- uint8_t pad[64];
- } sc_rxtapu;
-#define sc_rxtap sc_rxtapu.th
+ struct rt2661_rx_radiotap_header sc_rxtap;
int sc_rxtap_len;
-
- union {
- struct rt2661_tx_radiotap_header th;
- uint8_t pad[64];
- } sc_txtapu;
-#define sc_txtap sc_txtapu.th
+ struct rt2661_tx_radiotap_header sc_txtap;
int sc_txtap_len;
-#define RAL_INPUT_RUNNING 1
- int sc_flags;
};
int rt2661_attach(device_t, int);
@@ -174,5 +175,6 @@ void rt2661_suspend(void *);
void rt2661_resume(void *);
void rt2661_intr(void *);
-#define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
-#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define RAL_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
+#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
diff --git a/sys/dev/usb/if_rum.c b/sys/dev/usb/if_rum.c
index 7657d10..ec46c84 100644
--- a/sys/dev/usb/if_rum.c
+++ b/sys/dev/usb/if_rum.c
@@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_phy.h>
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_regdomain.h>
@@ -128,23 +129,23 @@ MODULE_DEPEND(rum, wlan, 1, 1, 1);
MODULE_DEPEND(rum, wlan_amrr, 1, 1, 1);
MODULE_DEPEND(rum, usb, 1, 1, 1);
+static struct ieee80211vap *rum_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void rum_vap_delete(struct ieee80211vap *);
static int rum_alloc_tx_list(struct rum_softc *);
static void rum_free_tx_list(struct rum_softc *);
static int rum_alloc_rx_list(struct rum_softc *);
static void rum_free_rx_list(struct rum_softc *);
-static int rum_media_change(struct ifnet *);
static void rum_task(void *);
static void rum_scantask(void *);
-static int rum_newstate(struct ieee80211com *,
+static int rum_newstate(struct ieee80211vap *,
enum ieee80211_state, int);
static void rum_txeof(usbd_xfer_handle, usbd_private_handle,
usbd_status);
static void rum_rxeof(usbd_xfer_handle, usbd_private_handle,
usbd_status);
-static int rum_rxrate(struct rum_rx_desc *);
-static int rum_ack_rate(struct ieee80211com *, int);
-static uint16_t rum_txtime(int, int, uint32_t);
-static uint8_t rum_plcp_signal(int);
static void rum_setup_tx_desc(struct rum_softc *,
struct rum_tx_desc *, uint32_t, uint16_t, int,
int);
@@ -185,13 +186,17 @@ static void rum_update_promisc(struct rum_softc *);
static const char *rum_get_rf(int);
static void rum_read_eeprom(struct rum_softc *);
static int rum_bbp_init(struct rum_softc *);
+static void rum_init_locked(struct rum_softc *);
static void rum_init(void *);
static void rum_stop(void *);
static int rum_load_microcode(struct rum_softc *, const u_char *,
size_t);
-static int rum_prepare_beacon(struct rum_softc *);
+static int rum_prepare_beacon(struct rum_softc *,
+ struct ieee80211vap *);
static int rum_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
+static struct ieee80211_node *rum_node_alloc(struct ieee80211_node_table *);
+static void rum_newassoc(struct ieee80211_node *, int);
static void rum_scan_start(struct ieee80211com *);
static void rum_scan_end(struct ieee80211com *);
static void rum_set_channel(struct ieee80211com *);
@@ -378,21 +383,21 @@ rum_attach(device_t self)
{
struct rum_softc *sc = device_get_softc(self);
struct usb_attach_arg *uaa = device_get_ivars(self);
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
struct ifnet *ifp;
const uint8_t *ucode = NULL;
usb_interface_descriptor_t *id;
usb_endpoint_descriptor_t *ed;
usbd_status error;
- int i, ntries, size, bands;
+ int i, ntries, size;
+ uint8_t bands;
uint32_t tmp;
sc->sc_udev = uaa->device;
sc->sc_dev = self;
if (usbd_set_config_no(sc->sc_udev, RT2573_CONFIG_NO, 0) != 0) {
- printf("%s: could not set configuration no\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(self, "could not set configuration no\n");
return ENXIO;
}
@@ -400,8 +405,7 @@ rum_attach(device_t self)
error = usbd_device2interface_handle(sc->sc_udev, RT2573_IFACE_INDEX,
&sc->sc_iface);
if (error != 0) {
- printf("%s: could not get interface handle\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(self, "could not get interface handle\n");
return ENXIO;
}
@@ -414,8 +418,8 @@ rum_attach(device_t self)
for (i = 0; i < id->bNumEndpoints; i++) {
ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
if (ed == NULL) {
- printf("%s: no endpoint descriptor for iface %d\n",
- device_get_nameunit(sc->sc_dev), i);
+ device_printf(self,
+ "no endpoint descriptor for iface %d\n", i);
return ENXIO;
}
@@ -427,18 +431,23 @@ rum_attach(device_t self)
sc->sc_tx_no = ed->bEndpointAddress;
}
if (sc->sc_rx_no == -1 || sc->sc_tx_no == -1) {
- printf("%s: missing endpoint\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(self, "missing endpoint\n");
return ENXIO;
}
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(self, "can not if_alloc()\n");
+ return ENXIO;
+ }
+ ic = ifp->if_l2com;
+
mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK,
MTX_DEF | MTX_RECURSE);
usb_init_task(&sc->sc_task, rum_task, sc);
usb_init_task(&sc->sc_scantask, rum_scantask, sc);
callout_init(&sc->watchdog_ch, 0);
- callout_init(&sc->amrr_ch, 0);
/* retrieve RT2573 rev. no */
for (ntries = 0; ntries < 1000; ntries++) {
@@ -447,32 +456,22 @@ rum_attach(device_t self)
DELAY(1000);
}
if (ntries == 1000) {
- printf("%s: timeout waiting for chip to settle\n",
- device_get_nameunit(sc->sc_dev));
- return ENXIO;
+ device_printf(self, "timeout waiting for chip to settle\n");
+ goto bad;
}
/* retrieve MAC address and various other things from EEPROM */
rum_read_eeprom(sc);
- printf("%s: MAC/BBP RT2573 (rev 0x%05x), RF %s\n",
- device_get_nameunit(sc->sc_dev), tmp, rum_get_rf(sc->rf_rev));
+ device_printf(self, "MAC/BBP RT2573 (rev 0x%05x), RF %s\n",
+ tmp, rum_get_rf(sc->rf_rev));
ucode = rt2573_ucode;
size = sizeof rt2573_ucode;
error = rum_load_microcode(sc, ucode, size);
if (error != 0) {
- device_printf(sc->sc_dev, "could not load 8051 microcode\n");
- mtx_destroy(&sc->sc_mtx);
- return ENXIO;
- }
-
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
- if (ifp == NULL) {
- printf("%s: can not if_alloc()\n",
- device_get_nameunit(sc->sc_dev));
- mtx_destroy(&sc->sc_mtx);
- return ENXIO;
+ device_printf(self, "could not load 8051 microcode\n");
+ goto bad;
}
ifp->if_softc = sc;
@@ -488,82 +487,47 @@ rum_attach(device_t self)
ic->ic_ifp = ifp;
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
- ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
- ic->ic_state = IEEE80211_S_INIT;
/* set device capabilities */
ic->ic_caps =
- IEEE80211_C_IBSS | /* IBSS mode supported */
- IEEE80211_C_MONITOR | /* monitor mode supported */
- IEEE80211_C_HOSTAP | /* HostAp mode supported */
- IEEE80211_C_TXPMGT | /* tx power management */
- IEEE80211_C_SHPREAMBLE | /* short preamble supported */
- IEEE80211_C_SHSLOT | /* short slot time supported */
- IEEE80211_C_BGSCAN | /* bg scanning supported */
- IEEE80211_C_WPA; /* 802.11i */
+ IEEE80211_C_IBSS /* IBSS mode supported */
+ | IEEE80211_C_MONITOR /* monitor mode supported */
+ | IEEE80211_C_HOSTAP /* HostAp mode supported */
+ | IEEE80211_C_TXPMGT /* tx power management */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_BGSCAN /* bg scanning supported */
+ | IEEE80211_C_WPA /* 802.11i */
+ ;
bands = 0;
setbit(&bands, IEEE80211_MODE_11B);
setbit(&bands, IEEE80211_MODE_11G);
- ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
-
- if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_5226) {
- struct ieee80211_channel *c;
-
- /* set supported .11a channels */
- for (i = 34; i <= 46; i += 4) {
- c = &ic->ic_channels[ic->ic_nchans++];
- c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
- c->ic_flags = IEEE80211_CHAN_A;
- c->ic_ieee = i;
- }
- for (i = 36; i <= 64; i += 4) {
- c = &ic->ic_channels[ic->ic_nchans++];
- c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
- c->ic_flags = IEEE80211_CHAN_A;
- c->ic_ieee = i;
- }
- for (i = 100; i <= 140; i += 4) {
- c = &ic->ic_channels[ic->ic_nchans++];
- c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
- c->ic_flags = IEEE80211_CHAN_A;
- c->ic_ieee = i;
- }
- for (i = 149; i <= 165; i += 4) {
- c = &ic->ic_channels[ic->ic_nchans++];
- c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
- c->ic_flags = IEEE80211_CHAN_A;
- c->ic_ieee = i;
- }
- }
+ if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_5226)
+ setbit(&bands, IEEE80211_MODE_11A);
+ ieee80211_init_channels(ic, NULL, &bands);
ieee80211_ifattach(ic);
+ ic->ic_newassoc = rum_newassoc;
+ ic->ic_raw_xmit = rum_raw_xmit;
+ ic->ic_node_alloc = rum_node_alloc;
ic->ic_scan_start = rum_scan_start;
ic->ic_scan_end = rum_scan_end;
ic->ic_set_channel = rum_set_channel;
- /* enable s/w bmiss handling in sta mode */
- ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;
+ ic->ic_vap_create = rum_vap_create;
+ ic->ic_vap_delete = rum_vap_delete;
- /* override state transition machine */
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = rum_newstate;
- ic->ic_raw_xmit = rum_raw_xmit;
- ieee80211_media_init(ic, rum_media_change, ieee80211_media_status);
-
- ieee80211_amrr_init(&sc->amrr, ic,
- IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
- IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD);
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN,
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof(sc->sc_txtap));
- sc->sc_rxtap_len = sizeof sc->sc_rxtapu;
+ sc->sc_rxtap_len = sizeof sc->sc_rxtap;
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
sc->sc_rxtap.wr_ihdr.it_present = htole32(RT2573_RX_RADIOTAP_PRESENT);
- sc->sc_txtap_len = sizeof sc->sc_txtapu;
+ sc->sc_txtap_len = sizeof sc->sc_txtap;
sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
sc->sc_txtap.wt_ihdr.it_present = htole32(RT2573_TX_RADIOTAP_PRESENT);
@@ -571,20 +535,26 @@ rum_attach(device_t self)
ieee80211_announce(ic);
return 0;
+bad:
+ mtx_destroy(&sc->sc_mtx);
+ if_free(ifp);
+ return ENXIO;
}
static int
rum_detach(device_t self)
{
struct rum_softc *sc = device_get_softc(self);
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
rum_stop(sc);
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+
usb_rem_task(sc->sc_udev, &sc->sc_task);
usb_rem_task(sc->sc_udev, &sc->sc_scantask);
callout_stop(&sc->watchdog_ch);
- callout_stop(&sc->amrr_ch);
if (sc->amrr_xfer != NULL) {
usbd_free_xfer(sc->amrr_xfer);
@@ -603,22 +573,66 @@ rum_detach(device_t self)
rum_free_rx_list(sc);
rum_free_tx_list(sc);
- bpfdetach(ifp);
- ieee80211_ifdetach(ic);
if_free(ifp);
-
mtx_destroy(&sc->sc_mtx);
return 0;
}
+static struct ieee80211vap *
+rum_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct rum_vap *rvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ rvp = (struct rum_vap *) malloc(sizeof(struct rum_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (rvp == NULL)
+ return NULL;
+ vap = &rvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+ ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
+
+ /* override state transition machine */
+ rvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = rum_newstate;
+
+ callout_init(&rvp->amrr_ch, 0);
+ ieee80211_amrr_init(&rvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 1000 /* 1 sec */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+rum_vap_delete(struct ieee80211vap *vap)
+{
+ struct rum_vap *rvp = RUM_VAP(vap);
+
+ callout_stop(&rvp->amrr_ch);
+ ieee80211_amrr_cleanup(&rvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(rvp, M_80211_VAP);
+}
+
static int
rum_alloc_tx_list(struct rum_softc *sc)
{
struct rum_tx_data *data;
int i, error;
- sc->tx_queued = 0;
+ sc->tx_queued = sc->tx_cur = 0;
for (i = 0; i < RUM_TX_LIST_COUNT; i++) {
data = &sc->tx_data[i];
@@ -627,16 +641,16 @@ rum_alloc_tx_list(struct rum_softc *sc)
data->xfer = usbd_alloc_xfer(sc->sc_udev);
if (data->xfer == NULL) {
- printf("%s: could not allocate tx xfer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate tx xfer\n");
error = ENOMEM;
goto fail;
}
data->buf = usbd_alloc_buffer(data->xfer,
RT2573_TX_DESC_SIZE + MCLBYTES);
if (data->buf == NULL) {
- printf("%s: could not allocate tx buffer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate tx buffer\n");
error = ENOMEM;
goto fail;
}
@@ -684,22 +698,22 @@ rum_alloc_rx_list(struct rum_softc *sc)
data->xfer = usbd_alloc_xfer(sc->sc_udev);
if (data->xfer == NULL) {
- printf("%s: could not allocate rx xfer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate rx xfer\n");
error = ENOMEM;
goto fail;
}
if (usbd_alloc_buffer(data->xfer, MCLBYTES) == NULL) {
- printf("%s: could not allocate rx buffer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate rx buffer\n");
error = ENOMEM;
goto fail;
}
data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
if (data->m == NULL) {
- printf("%s: could not allocate rx mbuf\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate rx mbuf\n");
error = ENOMEM;
goto fail;
}
@@ -733,39 +747,20 @@ rum_free_rx_list(struct rum_softc *sc)
}
}
-static int
-rum_media_change(struct ifnet *ifp)
-{
- struct rum_softc *sc = ifp->if_softc;
- int error;
-
- RUM_LOCK(sc);
-
- error = ieee80211_media_change(ifp);
- if (error != ENETRESET) {
- RUM_UNLOCK(sc);
- return error;
- }
-
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING))
- rum_init(sc);
-
- RUM_UNLOCK(sc);
-
- return 0;
-}
-
static void
rum_task(void *arg)
{
struct rum_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct rum_vap *rvp = RUM_VAP(vap);
+ const struct ieee80211_txparam *tp;
enum ieee80211_state ostate;
struct ieee80211_node *ni;
uint32_t tmp;
- ostate = ic->ic_state;
+ ostate = vap->iv_state;
RUM_LOCK(sc);
@@ -779,9 +774,9 @@ rum_task(void *arg)
break;
case IEEE80211_S_RUN:
- ni = ic->ic_bss;
+ ni = vap->iv_bss;
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
rum_update_slot(ic->ic_ifp);
rum_enable_mrr(sc);
rum_set_txpreamble(sc);
@@ -789,16 +784,16 @@ rum_task(void *arg)
rum_set_bssid(sc, ni->ni_bssid);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS)
- rum_prepare_beacon(sc);
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS)
+ rum_prepare_beacon(sc, vap);
- if (ic->ic_opmode != IEEE80211_M_MONITOR)
+ if (vap->iv_opmode != IEEE80211_M_MONITOR)
rum_enable_tsf_sync(sc);
- /* enable automatic rate adaptation in STA mode */
- if (ic->ic_opmode == IEEE80211_M_STA &&
- ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)
+ /* enable automatic rate adaptation */
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
rum_amrr_start(sc, ni);
break;
default:
@@ -807,40 +802,42 @@ rum_task(void *arg)
RUM_UNLOCK(sc);
- sc->sc_newstate(ic, sc->sc_state, sc->sc_arg);
+ IEEE80211_LOCK(ic);
+ rvp->newstate(vap, sc->sc_state, sc->sc_arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
+ IEEE80211_UNLOCK(ic);
}
static int
-rum_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct rum_vap *rvp = RUM_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
struct rum_softc *sc = ic->ic_ifp->if_softc;
- callout_stop(&sc->amrr_ch);
+ callout_stop(&rvp->amrr_ch);
/* do it in a process context */
sc->sc_state = nstate;
sc->sc_arg = arg;
usb_rem_task(sc->sc_udev, &sc->sc_task);
- if (nstate == IEEE80211_S_INIT)
- sc->sc_newstate(ic, nstate, arg);
- else
+ if (nstate == IEEE80211_S_INIT) {
+ rvp->newstate(vap, nstate, arg);
+ return 0;
+ } else {
usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER);
- return 0;
+ return EINPROGRESS;
+ }
}
-/* quickly determine if a given rate is CCK or OFDM */
-#define RUM_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
-
-#define RUM_ACK_SIZE 14 /* 10 + 4(FCS) */
-#define RUM_CTS_SIZE 14 /* 10 + 4(FCS) */
-
static void
rum_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
struct rum_tx_data *data = priv;
struct rum_softc *sc = data->sc;
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
if (data->m != NULL && data->m->m_flags & M_TXCB)
ieee80211_process_callback(data->ni, data->m, 0/*XXX*/);
@@ -849,8 +846,8 @@ rum_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
return;
- printf("%s: could not transmit buffer: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(status));
+ device_printf(sc->sc_dev, "could not transmit buffer: %s\n",
+ usbd_errstr(status));
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(sc->sc_tx_pipeh);
@@ -879,10 +876,9 @@ rum_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
struct rum_rx_data *data = priv;
struct rum_softc *sc = data->sc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rum_rx_desc *desc;
- struct ieee80211_frame *wh;
struct ieee80211_node *ni;
struct mbuf *mnew, *m;
int len, rssi;
@@ -934,31 +930,29 @@ rum_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
rssi = rum_get_rssi(sc, desc->rssi);
- wh = mtod(m, struct ieee80211_frame *);
- ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
-
- /* Error happened during RSSI conversion. */
- if (rssi < 0)
- rssi = ni->ni_rssi;
-
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rum_rx_radiotap_header *tap = &sc->sc_rxtap;
tap->wr_flags = IEEE80211_RADIOTAP_F_FCS;
- tap->wr_rate = rum_rxrate(desc);
+ tap->wr_rate = ieee80211_plcp2rate(desc->rate,
+ le32toh(desc->flags) & RT2573_RX_OFDM);
tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wr_antenna = sc->rx_ant;
tap->wr_antsignal = rssi;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
}
- /* send the frame to the 802.11 layer */
- ieee80211_input(ic, m, ni, rssi, RT2573_NOISE_FLOOR, 0);
-
- /* node is no longer needed */
- ieee80211_free_node(ni);
+ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ /* Error happened during RSSI conversion. */
+ if (rssi < 0)
+ rssi = -30; /* XXX ignored by net80211 */
+ (void) ieee80211_input(ni, m, rssi, RT2573_NOISE_FLOOR, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, RT2573_NOISE_FLOOR, 0);
DPRINTFN(15, ("rx done\n"));
@@ -968,125 +962,12 @@ skip: /* setup a new transfer */
usbd_transfer(xfer);
}
-/*
- * This function is only used by the Rx radiotap code.
- */
-static int
-rum_rxrate(struct rum_rx_desc *desc)
-{
- if (le32toh(desc->flags) & RT2573_RX_OFDM) {
- /* reverse function of rum_plcp_signal */
- switch (desc->rate) {
- case 0xb: return 12;
- case 0xf: return 18;
- case 0xa: return 24;
- case 0xe: return 36;
- case 0x9: return 48;
- case 0xd: return 72;
- case 0x8: return 96;
- case 0xc: return 108;
- }
- } else {
- if (desc->rate == 10)
- return 2;
- if (desc->rate == 20)
- return 4;
- if (desc->rate == 55)
- return 11;
- if (desc->rate == 110)
- return 22;
- }
- return 2; /* should not get there */
-}
-
-/*
- * Return the expected ack rate for a frame transmitted at rate `rate'.
- */
-static int
-rum_ack_rate(struct ieee80211com *ic, int rate)
-{
- switch (rate) {
- /* CCK rates */
- case 2:
- return 2;
- case 4:
- case 11:
- case 22:
- return (ic->ic_curmode == IEEE80211_MODE_11B) ? 4 : rate;
-
- /* OFDM rates */
- case 12:
- case 18:
- return 12;
- case 24:
- case 36:
- return 24;
- case 48:
- case 72:
- case 96:
- case 108:
- return 48;
- }
-
- /* default to 1Mbps */
- return 2;
-}
-
-/*
- * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'.
- * The function automatically determines the operating mode depending on the
- * given rate. `flags' indicates whether short preamble is in use or not.
- */
-static uint16_t
-rum_txtime(int len, int rate, uint32_t flags)
-{
- uint16_t txtime;
-
- if (RUM_RATE_IS_OFDM(rate)) {
- /* IEEE Std 802.11a-1999, pp. 37 */
- txtime = (8 + 4 * len + 3 + rate - 1) / rate;
- txtime = 16 + 4 + 4 * txtime + 6;
- } else {
- /* IEEE Std 802.11b-1999, pp. 28 */
- txtime = (16 * len + rate - 1) / rate;
- if (rate != 2 && (flags & IEEE80211_F_SHPREAMBLE))
- txtime += 72 + 24;
- else
- txtime += 144 + 48;
- }
- return txtime;
-}
-
-static uint8_t
-rum_plcp_signal(int rate)
-{
- switch (rate) {
- /* CCK rates (returned values are device-dependent) */
- case 2: return 0x0;
- case 4: return 0x1;
- case 11: return 0x2;
- case 22: return 0x3;
-
- /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
- case 12: return 0xb;
- case 18: return 0xf;
- case 24: return 0xa;
- case 36: return 0xe;
- case 48: return 0x9;
- case 72: return 0xd;
- case 96: return 0x8;
- case 108: return 0xc;
-
- /* unsupported rates (should not get there) */
- default: return 0xff;
- }
-}
-
static void
rum_setup_tx_desc(struct rum_softc *sc, struct rum_tx_desc *desc,
uint32_t flags, uint16_t xflags, int len, int rate)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint16_t plcp_length;
int remainder;
@@ -1100,11 +981,11 @@ rum_setup_tx_desc(struct rum_softc *sc, struct rum_tx_desc *desc,
RT2573_LOGCWMIN(4) | RT2573_LOGCWMAX(10));
/* setup PLCP fields */
- desc->plcp_signal = rum_plcp_signal(rate);
+ desc->plcp_signal = ieee80211_rate2plcp(rate);
desc->plcp_service = 4;
len += IEEE80211_CRC_LEN;
- if (RUM_RATE_IS_OFDM(rate)) {
+ if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) {
desc->flags |= htole32(RT2573_TX_OFDM);
plcp_length = len & 0xfff;
@@ -1128,43 +1009,108 @@ rum_setup_tx_desc(struct rum_softc *sc, struct rum_tx_desc *desc,
#define RUM_TX_TIMEOUT 5000
static int
+rum_sendprot(struct rum_softc *sc,
+ const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_frame *wh;
+ struct rum_tx_desc *desc;
+ struct rum_tx_data *data;
+ struct mbuf *mprot;
+ int protrate, ackrate, pktlen, flags, isshort;
+ uint16_t dur;
+ usbd_status error;
+
+ KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY,
+ ("protection %d", prot));
+
+ wh = mtod(m, const struct ieee80211_frame *);
+ pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ protrate = ieee80211_ctl_rate(sc->sc_rates, rate);
+ ackrate = ieee80211_ack_rate(sc->sc_rates, rate);
+
+ isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
+ dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort);
+ + ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags = RT2573_TX_MORE_FRAG;
+ if (prot == IEEE80211_PROT_RTSCTS) {
+ /* NB: CTS is the same size as an ACK */
+ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags |= RT2573_TX_NEED_ACK;
+ mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
+ } else {
+ mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
+ }
+ if (mprot == NULL) {
+ /* XXX stat + msg */
+ return ENOBUFS;
+ }
+ data = &sc->tx_data[sc->tx_cur];
+ desc = (struct rum_tx_desc *)data->buf;
+
+ data->m = mprot;
+ data->ni = ieee80211_ref_node(ni);
+ m_copydata(mprot, 0, mprot->m_pkthdr.len,
+ data->buf + RT2573_TX_DESC_SIZE);
+ rum_setup_tx_desc(sc, desc, flags, 0, mprot->m_pkthdr.len, protrate);
+
+ usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf,
+ /* NB: no roundup necessary */
+ RT2573_TX_DESC_SIZE + mprot->m_pkthdr.len,
+ USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RUM_TX_TIMEOUT, rum_txeof);
+
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) {
+ data->m = NULL;
+ data->ni = NULL;
+ return error;
+ }
+
+ sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RUM_TX_LIST_COUNT;
+
+ return 0;
+}
+
+static int
rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rum_tx_desc *desc;
struct rum_tx_data *data;
struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
struct ieee80211_key *k;
uint32_t flags = 0;
uint16_t dur;
usbd_status error;
- int xferlen, rate;
-
- data = &sc->tx_data[0];
- desc = (struct rum_tx_desc *)data->buf;
-
- rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
+ int xferlen;
+ data = &sc->tx_data[sc->tx_cur];
data->m = m0;
data->ni = ni;
+ desc = (struct rum_tx_desc *)data->buf;
wh = mtod(m0, struct ieee80211_frame *);
-
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
}
+ wh = mtod(m0, struct ieee80211_frame *);
}
- wh = mtod(m0, struct ieee80211_frame *);
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
flags |= RT2573_TX_NEED_ACK;
- dur = rum_txtime(RUM_ACK_SIZE, rum_ack_rate(ic, rate),
- ic->ic_flags) + sc->sifs;
+ dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
/* tell hardware to add timestamp for probe responses */
@@ -1174,20 +1120,20 @@ rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
flags |= RT2573_TX_TIMESTAMP;
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rum_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
- tap->wt_rate = rate;
+ tap->wt_rate = tp->mgmtrate;
tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RT2573_TX_DESC_SIZE);
- rum_setup_tx_desc(sc, desc, flags, 0, m0->m_pkthdr.len, rate);
+ rum_setup_tx_desc(sc, desc, flags, 0, m0->m_pkthdr.len, tp->mgmtrate);
/* align end on a 4-bytes boundary */
xferlen = (RT2573_TX_DESC_SIZE + m0->m_pkthdr.len + 3) & ~3;
@@ -1200,7 +1146,7 @@ rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
xferlen += 4;
DPRINTFN(10, ("sending mgt frame len=%d rate=%d xfer len=%d\n",
- m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, rate, xferlen));
+ m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, tp->mgmtrate, xferlen));
usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf, xferlen,
USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RUM_TX_TIMEOUT, rum_txeof);
@@ -1214,6 +1160,7 @@ rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
}
sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RUM_TX_LIST_COUNT;
return 0;
}
@@ -1222,14 +1169,17 @@ static int
rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
const struct ieee80211_bpf_params *params)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rum_tx_desc *desc;
struct rum_tx_data *data;
uint32_t flags;
usbd_status error;
int xferlen, rate;
- data = &sc->tx_data[0];
+ KASSERT(params != NULL, ("no raw xmit params"));
+
+ data = &sc->tx_data[sc->tx_cur];
desc = (struct rum_tx_desc *)data->buf;
rate = params->ibp_rate0 & IEEE80211_RATE_VAL;
@@ -1238,8 +1188,22 @@ rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
m_freem(m0);
return EINVAL;
}
+ flags = 0;
+ if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
+ flags |= RT2573_TX_NEED_ACK;
+ if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) {
+ error = rum_sendprot(sc, m0, ni,
+ params->ibp_flags & IEEE80211_BPF_RTS ?
+ IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY,
+ rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS;
+ }
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rum_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -1248,16 +1212,12 @@ rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data->m = m0;
data->ni = ni;
- flags = 0;
- if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
- flags |= RT2573_TX_NEED_ACK;
-
m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RT2573_TX_DESC_SIZE);
/* XXX need to setup descriptor ourself */
rum_setup_tx_desc(sc, desc, flags, 0, m0->m_pkthdr.len, rate);
@@ -1284,6 +1244,7 @@ rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
return error;
sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RUM_TX_LIST_COUNT;
return 0;
}
@@ -1291,10 +1252,13 @@ rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
static int
rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rum_tx_desc *desc;
struct rum_tx_data *data;
struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
struct ieee80211_key *k;
uint32_t flags = 0;
uint16_t dur;
@@ -1303,15 +1267,18 @@ rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
wh = mtod(m0, struct ieee80211_frame *);
- if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE)
- rate = ic->ic_fixed_rate;
- else
- rate = ni->ni_rates.rs_rates[ni->ni_txrate];
-
- rate &= IEEE80211_RATE_VAL;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ rate = tp->mcastrate;
+ else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
+ rate = tp->ucastrate;
+ else {
+ (void) ieee80211_amrr_choose(ni, &RUM_NODE(ni)->amn);
+ rate = ni->ni_txrate;
+ }
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -1321,7 +1288,24 @@ rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
wh = mtod(m0, struct ieee80211_frame *);
}
- data = &sc->tx_data[0];
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ int prot = IEEE80211_PROT_NONE;
+ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
+ prot = IEEE80211_PROT_RTSCTS;
+ else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM)
+ prot = ic->ic_protmode;
+ if (prot != IEEE80211_PROT_NONE) {
+ error = rum_sendprot(sc, m0, ni, prot, rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS;
+ }
+ }
+
+ data = &sc->tx_data[sc->tx_cur];
desc = (struct rum_tx_desc *)data->buf;
data->m = m0;
@@ -1331,12 +1315,12 @@ rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
flags |= RT2573_TX_NEED_ACK;
flags |= RT2573_TX_MORE_FRAG;
- dur = rum_txtime(RUM_ACK_SIZE, rum_ack_rate(ic, rate),
- ic->ic_flags) + sc->sifs;
+ dur = ieee80211_ack_duration(sc->sc_rates, rate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct rum_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -1345,7 +1329,7 @@ rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RT2573_TX_DESC_SIZE);
@@ -1376,6 +1360,7 @@ rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
}
sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RUM_TX_LIST_COUNT;
return 0;
}
@@ -1384,77 +1369,30 @@ static void
rum_start(struct ifnet *ifp)
{
struct rum_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
struct ieee80211_node *ni;
- struct mbuf *m0;
- struct ether_header *eh;
+ struct mbuf *m;
for (;;) {
- IF_POLL(&ic->ic_mgtq, m0);
- if (m0 != NULL) {
- if (sc->tx_queued >= RUM_TX_LIST_COUNT) {
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- IF_DEQUEUE(&ic->ic_mgtq, m0);
-
- ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
- m0->m_pkthdr.rcvif = NULL;
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (rum_tx_mgt(sc, m0, ni) != 0) {
- ieee80211_free_node(ni);
- break;
- }
- } else {
- if (ic->ic_state != IEEE80211_S_RUN)
- break;
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
- if (sc->tx_queued >= RUM_TX_LIST_COUNT) {
- IFQ_DRV_PREPEND(&ifp->if_snd, m0);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
-
- if (m0->m_len < sizeof (struct ether_header) &&
- !(m0 = m_pullup(m0, sizeof (struct ether_header))))
- continue;
-
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- continue;
- }
- BPF_MTAP(ifp, m0);
-
- m0 = ieee80211_encap(ic, m0, ni);
- if (m0 == NULL) {
- ieee80211_free_node(ni);
- continue;
- }
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (rum_tx_data(sc, m0, ni) != 0) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- break;
- }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ if (sc->tx_queued >= RUM_TX_LIST_COUNT-1) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ continue;
+ }
+ if (rum_tx_data(sc, m, ni) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
}
-
sc->sc_tx_timer = 5;
- ic->ic_lastdata = ticks;
callout_reset(&sc->watchdog_ch, hz, rum_watchdog, sc);
}
}
@@ -1484,37 +1422,35 @@ static int
rum_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct rum_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
RUM_LOCK(sc);
-
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- rum_update_promisc(sc);
- else
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
rum_init(sc);
+ startall = 1;
+ } else
+ rum_update_promisc(sc);
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
rum_stop(sc);
}
break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
default:
- error = ieee80211_ioctl(ic, cmd, data);
- }
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
- (ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
- rum_init(sc);
- error = 0;
+ error = ether_ioctl(ifp, cmd, data);
}
-
RUM_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
@@ -1532,8 +1468,8 @@ rum_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, int len)
error = usbd_do_request(sc->sc_udev, &req, buf);
if (error != 0) {
- printf("%s: could not read EEPROM: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not read EEPROM: %s\n",
+ usbd_errstr(error));
}
}
@@ -1561,8 +1497,9 @@ rum_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, int len)
error = usbd_do_request(sc->sc_udev, &req, buf);
if (error != 0) {
- printf("%s: could not multi read MAC register: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev,
+ "could not multi read MAC register: %s\n",
+ usbd_errstr(error));
}
}
@@ -1588,8 +1525,9 @@ rum_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, size_t len)
error = usbd_do_request(sc->sc_udev, &req, buf);
if (error != 0) {
- printf("%s: could not multi write MAC register: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev,
+ "could not multi write MAC register: %s\n",
+ usbd_errstr(error));
}
}
@@ -1604,8 +1542,7 @@ rum_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val)
break;
}
if (ntries == 5) {
- printf("%s: could not write to BBP\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not write to BBP\n");
return;
}
@@ -1624,8 +1561,7 @@ rum_bbp_read(struct rum_softc *sc, uint8_t reg)
break;
}
if (ntries == 5) {
- printf("%s: could not read BBP\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not read BBP\n");
return 0;
}
@@ -1639,7 +1575,7 @@ rum_bbp_read(struct rum_softc *sc, uint8_t reg)
DELAY(1);
}
- printf("%s: could not read BBP\n", device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not read BBP\n");
return 0;
}
@@ -1654,8 +1590,7 @@ rum_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val)
break;
}
if (ntries == 5) {
- printf("%s: could not write to RF\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not write to RF\n");
return;
}
@@ -1697,13 +1632,14 @@ rum_select_antenna(struct rum_softc *sc)
static void
rum_enable_mrr(struct rum_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp;
tmp = rum_read(sc, RT2573_TXRX_CSR4);
tmp &= ~RT2573_MRR_CCK_FALLBACK;
- if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
+ if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan))
tmp |= RT2573_MRR_CCK_FALLBACK;
tmp |= RT2573_MRR_ENABLED;
@@ -1713,12 +1649,14 @@ rum_enable_mrr(struct rum_softc *sc)
static void
rum_set_txpreamble(struct rum_softc *sc)
{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp;
tmp = rum_read(sc, RT2573_TXRX_CSR4);
tmp &= ~RT2573_SHORT_PREAMBLE;
- if (sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE)
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
tmp |= RT2573_SHORT_PREAMBLE;
rum_write(sc, RT2573_TXRX_CSR4, tmp);
@@ -1727,13 +1665,14 @@ rum_set_txpreamble(struct rum_softc *sc)
static void
rum_set_basicrates(struct rum_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
/* update basic rate set */
if (ic->ic_curmode == IEEE80211_MODE_11B) {
/* 11b basic rates: 1, 2Mbps */
rum_write(sc, RT2573_TXRX_CSR5, 0x3);
- } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan)) {
+ } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) {
/* 11a basic rates: 6, 12, 24Mbps */
rum_write(sc, RT2573_TXRX_CSR5, 0x150);
} else {
@@ -1787,15 +1726,13 @@ rum_select_band(struct rum_softc *sc, struct ieee80211_channel *c)
else
tmp |= RT2573_PA_PE_5GHZ;
rum_write(sc, RT2573_PHY_CSR0, tmp);
-
- /* 802.11a uses a 16 microseconds short interframe space */
- sc->sifs = IEEE80211_IS_CHAN_5GHZ(c) ? 16 : 10;
}
static void
rum_set_chan(struct rum_softc *sc, struct ieee80211_channel *c)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
const struct rfprog *rfprog;
uint8_t bbp3, bbp94 = RT2573_BBPR94_DEFAULT;
int8_t power;
@@ -1868,10 +1805,12 @@ rum_set_chan(struct rum_softc *sc, struct ieee80211_channel *c)
static void
rum_enable_tsf_sync(struct rum_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
uint32_t tmp;
- if (ic->ic_opmode != IEEE80211_M_STA) {
+ if (vap->iv_opmode != IEEE80211_M_STA) {
/*
* Change default 16ms TBTT adjustment to 8ms.
* Must be done before enabling beacon generation.
@@ -1882,10 +1821,10 @@ rum_enable_tsf_sync(struct rum_softc *sc)
tmp = rum_read(sc, RT2573_TXRX_CSR9) & 0xff000000;
/* set beacon interval (in 1/16ms unit) */
- tmp |= ic->ic_bss->ni_intval * 16;
+ tmp |= vap->iv_bss->ni_intval * 16;
tmp |= RT2573_TSF_TICKING | RT2573_ENABLE_TBTT;
- if (ic->ic_opmode == IEEE80211_M_STA)
+ if (vap->iv_opmode == IEEE80211_M_STA)
tmp |= RT2573_TSF_MODE(1);
else
tmp |= RT2573_TSF_MODE(2) | RT2573_GENERATE_BEACON;
@@ -1897,7 +1836,7 @@ static void
rum_update_slot(struct ifnet *ifp)
{
struct rum_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
uint8_t slottime;
uint32_t tmp;
@@ -1937,7 +1876,7 @@ rum_set_macaddr(struct rum_softc *sc, const uint8_t *addr)
static void
rum_update_promisc(struct rum_softc *sc)
{
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
uint32_t tmp;
tmp = rum_read(sc, RT2573_TXRX_CSR0);
@@ -1967,7 +1906,8 @@ rum_get_rf(int rev)
static void
rum_read_eeprom(struct rum_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint16_t val;
#ifdef RUM_DEBUG
int i;
@@ -2082,12 +2022,11 @@ rum_bbp_init(struct rum_softc *sc)
}
static void
-rum_init(void *priv)
+rum_init_locked(struct rum_softc *sc)
{
#define N(a) (sizeof (a) / sizeof ((a)[0]))
- struct rum_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct rum_rx_data *data;
uint32_t tmp;
usbd_status error;
@@ -2111,8 +2050,8 @@ rum_init(void *priv)
DELAY(1000);
}
if (ntries == 1000) {
- printf("%s: timeout waiting for BBP/RF to wakeup\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "timeout waiting for BBP/RF to wakeup\n");
goto fail;
}
@@ -2138,8 +2077,7 @@ rum_init(void *priv)
*/
sc->amrr_xfer = usbd_alloc_xfer(sc->sc_udev);
if (sc->amrr_xfer == NULL) {
- printf("%s: could not allocate AMRR xfer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not allocate AMRR xfer\n");
goto fail;
}
@@ -2149,15 +2087,15 @@ rum_init(void *priv)
error = usbd_open_pipe(sc->sc_iface, sc->sc_tx_no, USBD_EXCLUSIVE_USE,
&sc->sc_tx_pipeh);
if (error != 0) {
- printf("%s: could not open Tx pipe: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not open Tx pipe: %s\n",
+ usbd_errstr(error));
goto fail;
}
error = usbd_open_pipe(sc->sc_iface, sc->sc_rx_no, USBD_EXCLUSIVE_USE,
&sc->sc_rx_pipeh);
if (error != 0) {
- printf("%s: could not open Rx pipe: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not open Rx pipe: %s\n",
+ usbd_errstr(error));
goto fail;
}
@@ -2166,14 +2104,12 @@ rum_init(void *priv)
*/
error = rum_alloc_tx_list(sc);
if (error != 0) {
- printf("%s: could not allocate Tx list\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not allocate Tx list\n");
goto fail;
}
error = rum_alloc_rx_list(sc);
if (error != 0) {
- printf("%s: could not allocate Rx list\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not allocate Rx list\n");
goto fail;
}
@@ -2204,13 +2140,6 @@ rum_init(void *priv)
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
-
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-
return;
fail: rum_stop(sc);
@@ -2218,18 +2147,30 @@ fail: rum_stop(sc);
}
static void
+rum_init(void *priv)
+{
+ struct rum_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ RUM_LOCK(sc);
+ rum_init_locked(sc);
+ RUM_UNLOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vap's */
+}
+
+static void
rum_stop(void *priv)
{
struct rum_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
uint32_t tmp;
sc->sc_tx_timer = 0;
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
-
/* disable Rx */
tmp = rum_read(sc, RT2573_TXRX_CSR0);
rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX);
@@ -2277,30 +2218,28 @@ rum_load_microcode(struct rum_softc *sc, const u_char *ucode, size_t size)
error = usbd_do_request(sc->sc_udev, &req, NULL);
if (error != 0) {
- printf("%s: could not run firmware: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not run firmware: %s\n",
+ usbd_errstr(error));
}
return error;
}
static int
-rum_prepare_beacon(struct rum_softc *sc)
+rum_prepare_beacon(struct rum_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = vap->iv_ic;
+ const struct ieee80211_txparam *tp;
struct rum_tx_desc desc;
struct mbuf *m0;
- int rate;
- m0 = ieee80211_beacon_alloc(ic->ic_bss, &sc->sc_bo);
+ m0 = ieee80211_beacon_alloc(vap->iv_bss, &RUM_VAP(vap)->bo);
if (m0 == NULL) {
return ENOBUFS;
}
- /* send beacons at the lowest available rate */
- rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
-
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
rum_setup_tx_desc(sc, &desc, RT2573_TX_TIMESTAMP, RT2573_TX_HWSEQ,
- m0->m_pkthdr.len, rate);
+ m0->m_pkthdr.len, tp->mgmtrate);
/* copy the first 24 bytes of Tx descriptor into NIC memory */
rum_write_multi(sc, RT2573_HW_BEACON_BASE0, (uint8_t *)&desc, 24);
@@ -2318,8 +2257,7 @@ static int
rum_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
const struct ieee80211_bpf_params *params)
{
- struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = ni->ni_ic->ic_ifp;
struct rum_softc *sc = ifp->if_softc;
/* prevent management frames from being sent if we're not ready */
@@ -2328,16 +2266,13 @@ rum_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
ieee80211_free_node(ni);
return ENETDOWN;
}
- if (sc->tx_queued >= RUM_TX_LIST_COUNT) {
+ if (sc->tx_queued >= RUM_TX_LIST_COUNT-1) {
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
m_freem(m);
ieee80211_free_node(ni);
return EIO;
}
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m);
-
ifp->if_opackets++;
if (params == NULL) {
@@ -2368,26 +2303,22 @@ bad:
static void
rum_amrr_start(struct rum_softc *sc, struct ieee80211_node *ni)
{
- int i;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct rum_vap *rvp = RUM_VAP(vap);
/* clear statistic registers (STA_CSR0 to STA_CSR5) */
rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta);
- ieee80211_amrr_node_init(&sc->amrr, &sc->amn);
-
- /* set rate to some reasonable initial value */
- for (i = ni->ni_rates.rs_nrates - 1;
- i > 0 && (ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) > 72;
- i--);
- ni->ni_txrate = i;
+ ieee80211_amrr_node_init(&rvp->amrr, &RUM_NODE(ni)->amn, ni);
- callout_reset(&sc->amrr_ch, hz, rum_amrr_timeout, sc);
+ callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, vap);
}
static void
rum_amrr_timeout(void *arg)
{
- struct rum_softc *sc = (struct rum_softc *)arg;
+ struct ieee80211vap *vap = arg;
+ struct rum_softc *sc = vap->iv_ic->ic_ifp->if_softc;
usb_device_request_t req;
/*
@@ -2399,7 +2330,7 @@ rum_amrr_timeout(void *arg)
USETW(req.wIndex, RT2573_STA_CSR0);
USETW(req.wLength, sizeof sc->sta);
- usbd_setup_default_xfer(sc->amrr_xfer, sc->sc_udev, sc,
+ usbd_setup_default_xfer(sc->amrr_xfer, sc->sc_udev, vap,
USBD_DEFAULT_TIMEOUT, &req, sc->sta, sizeof sc->sta, 0,
rum_amrr_update);
(void)usbd_transfer(sc->amrr_xfer);
@@ -2409,8 +2340,11 @@ static void
rum_amrr_update(usbd_xfer_handle xfer, usbd_private_handle priv,
usbd_status status)
{
- struct rum_softc *sc = (struct rum_softc *)priv;
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ieee80211vap *vap = priv;
+ struct rum_vap *rvp = RUM_VAP(vap);
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
+ struct rum_softc *sc = ifp->if_softc;
+ int ok, fail;
if (status != USBD_NORMAL_COMPLETION) {
device_printf(sc->sc_dev, "could not retrieve Tx statistics - "
@@ -2418,21 +2352,34 @@ rum_amrr_update(usbd_xfer_handle xfer, usbd_private_handle priv,
return;
}
- /* count TX retry-fail as Tx errors */
- ifp->if_oerrors += le32toh(sc->sta[5]) >> 16;
+ ok = (le32toh(sc->sta[4]) >> 16) + /* TX ok w/o retry */
+ (le32toh(sc->sta[5]) & 0xffff); /* TX ok w/ retry */
+ fail = (le32toh(sc->sta[5]) >> 16); /* TX retry-fail count */
+
+ ieee80211_amrr_tx_update(&RUM_NODE(vap->iv_bss)->amn,
+ ok+fail, ok, (le32toh(sc->sta[5]) & 0xffff) + fail);
+
+ ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */
- sc->amn.amn_retrycnt =
- (le32toh(sc->sta[4]) >> 16) + /* TX one-retry ok count */
- (le32toh(sc->sta[5]) & 0xffff) + /* TX more-retry ok count */
- (le32toh(sc->sta[5]) >> 16); /* TX retry-fail count */
+ callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, vap);
+}
- sc->amn.amn_txcnt =
- sc->amn.amn_retrycnt +
- (le32toh(sc->sta[4]) & 0xffff); /* TX no-retry ok count */
+/* ARGUSED */
+static struct ieee80211_node *
+rum_node_alloc(struct ieee80211_node_table *nt __unused)
+{
+ struct rum_node *rn;
- ieee80211_amrr_choose(&sc->amrr, sc->sc_ic.ic_bss, &sc->amn);
+ rn = malloc(sizeof(struct rum_node), M_80211_NODE, M_NOWAIT | M_ZERO);
+ return rn != NULL ? &rn->ni : NULL;
+}
- callout_reset(&sc->amrr_ch, hz, rum_amrr_timeout, sc);
+static void
+rum_newassoc(struct ieee80211_node *ni, int isnew)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ ieee80211_amrr_node_init(&RUM_VAP(vap)->amrr, &RUM_NODE(ni)->amn, ni);
}
static void
@@ -2469,14 +2416,17 @@ rum_set_channel(struct ieee80211com *ic)
/* do it in a process context */
sc->sc_scan_action = RUM_SET_CHANNEL;
usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
}
static void
rum_scantask(void *arg)
{
struct rum_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
uint32_t tmp;
RUM_LOCK(sc);
@@ -2492,7 +2442,7 @@ rum_scantask(void *arg)
case RUM_SCAN_END:
rum_enable_tsf_sync(sc);
/* XXX keep local copy */
- rum_set_bssid(sc, ic->ic_bss->ni_bssid);
+ rum_set_bssid(sc, vap->iv_bss->ni_bssid);
break;
case RUM_SET_CHANNEL:
@@ -2513,6 +2463,8 @@ rum_scantask(void *arg)
static int
rum_get_rssi(struct rum_softc *sc, uint8_t raw)
{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
int lna, agc, rssi;
lna = (raw >> 5) & 0x3;
@@ -2530,7 +2482,7 @@ rum_get_rssi(struct rum_softc *sc, uint8_t raw)
rssi = (2 * agc) - RT2573_NOISE_FLOOR;
- if (IEEE80211_IS_CHAN_2GHZ(sc->sc_ic.ic_curchan)) {
+ if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) {
rssi += sc->rssi_2ghz_corr;
if (lna == 1)
diff --git a/sys/dev/usb/if_rumvar.h b/sys/dev/usb/if_rumvar.h
index e7d6d15..59980c0 100644
--- a/sys/dev/usb/if_rumvar.h
+++ b/sys/dev/usb/if_rumvar.h
@@ -18,7 +18,7 @@
*/
#define RUM_RX_LIST_COUNT 1
-#define RUM_TX_LIST_COUNT 1
+#define RUM_TX_LIST_COUNT 8
struct rum_rx_radiotap_header {
struct ieee80211_radiotap_header wr_ihdr;
@@ -69,11 +69,26 @@ struct rum_rx_data {
struct mbuf *m;
};
+struct rum_node {
+ struct ieee80211_node ni;
+ struct ieee80211_amrr_node amn;
+};
+#define RUM_NODE(ni) ((struct rum_node *)(ni))
+
+struct rum_vap {
+ struct ieee80211vap vap;
+ struct ieee80211_beacon_offsets bo;
+ struct ieee80211_amrr amrr;
+ struct callout amrr_ch;
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define RUM_VAP(vap) ((struct rum_vap *)(vap))
+
struct rum_softc {
struct ifnet *sc_ifp;
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
+ const struct ieee80211_rate_table *sc_rates;
device_t sc_dev;
usbd_device_handle sc_udev;
@@ -94,9 +109,6 @@ struct rum_softc {
int sc_arg;
struct usb_task sc_task;
- struct ieee80211_amrr amrr;
- struct ieee80211_amrr_node amn;
-
struct usb_task sc_scantask;
int sc_scan_action;
#define RUM_SCAN_START 0
@@ -106,13 +118,11 @@ struct rum_softc {
struct rum_rx_data rx_data[RUM_RX_LIST_COUNT];
struct rum_tx_data tx_data[RUM_TX_LIST_COUNT];
int tx_queued;
-
- struct ieee80211_beacon_offsets sc_bo;
+ int tx_cur;
struct mtx sc_mtx;
struct callout watchdog_ch;
- struct callout amrr_ch;
int sc_tx_timer;
@@ -133,23 +143,12 @@ struct rum_softc {
int ext_5ghz_lna;
int rssi_2ghz_corr;
int rssi_5ghz_corr;
- int sifs;
uint8_t bbp17;
- struct bpf_if *sc_drvbpf;
-
- union {
- struct rum_rx_radiotap_header th;
- uint8_t pad[64];
- } sc_rxtapu;
-#define sc_rxtap sc_rxtapu.th
+ struct rum_rx_radiotap_header sc_rxtap;
int sc_rxtap_len;
- union {
- struct rum_tx_radiotap_header th;
- uint8_t pad[64];
- } sc_txtapu;
-#define sc_txtap sc_txtapu.th
+ struct rum_tx_radiotap_header sc_txtap;
int sc_txtap_len;
};
diff --git a/sys/dev/usb/if_ural.c b/sys/dev/usb/if_ural.c
index c2b063f..40a225b 100644
--- a/sys/dev/usb/if_ural.c
+++ b/sys/dev/usb/if_ural.c
@@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_phy.h>
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_regdomain.h>
@@ -115,23 +116,23 @@ MODULE_DEPEND(ural, wlan, 1, 1, 1);
MODULE_DEPEND(ural, wlan_amrr, 1, 1, 1);
MODULE_DEPEND(ural, usb, 1, 1, 1);
+static struct ieee80211vap *ural_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void ural_vap_delete(struct ieee80211vap *);
static int ural_alloc_tx_list(struct ural_softc *);
static void ural_free_tx_list(struct ural_softc *);
static int ural_alloc_rx_list(struct ural_softc *);
static void ural_free_rx_list(struct ural_softc *);
-static int ural_media_change(struct ifnet *);
static void ural_task(void *);
static void ural_scantask(void *);
-static int ural_newstate(struct ieee80211com *,
+static int ural_newstate(struct ieee80211vap *,
enum ieee80211_state, int);
-static int ural_rxrate(struct ural_rx_desc *);
static void ural_txeof(usbd_xfer_handle, usbd_private_handle,
usbd_status);
static void ural_rxeof(usbd_xfer_handle, usbd_private_handle,
usbd_status);
-static int ural_ack_rate(struct ieee80211com *, int);
-static uint16_t ural_txtime(int, int, uint32_t);
-static uint8_t ural_plcp_signal(int);
static void ural_setup_tx_desc(struct ural_softc *,
struct ural_tx_desc *, uint32_t, int, int);
static int ural_tx_bcn(struct ural_softc *, struct mbuf *,
@@ -142,7 +143,6 @@ static int ural_tx_data(struct ural_softc *, struct mbuf *,
struct ieee80211_node *);
static void ural_start(struct ifnet *);
static void ural_watchdog(void *);
-static int ural_reset(struct ifnet *);
static int ural_ioctl(struct ifnet *, u_long, caddr_t);
static void ural_set_testmode(struct ural_softc *);
static void ural_eeprom_read(struct ural_softc *, uint16_t, void *,
@@ -156,6 +156,8 @@ static void ural_write_multi(struct ural_softc *, uint16_t, void *,
static void ural_bbp_write(struct ural_softc *, uint8_t, uint8_t);
static uint8_t ural_bbp_read(struct ural_softc *, uint8_t);
static void ural_rf_write(struct ural_softc *, uint8_t, uint32_t);
+static struct ieee80211_node *ural_node_alloc(struct ieee80211_node_table *);
+static void ural_newassoc(struct ieee80211_node *, int);
static void ural_scan_start(struct ieee80211com *);
static void ural_scan_end(struct ieee80211com *);
static void ural_set_channel(struct ieee80211com *);
@@ -165,7 +167,8 @@ static void ural_disable_rf_tune(struct ural_softc *);
static void ural_enable_tsf_sync(struct ural_softc *);
static void ural_update_slot(struct ifnet *);
static void ural_set_txpreamble(struct ural_softc *);
-static void ural_set_basicrates(struct ural_softc *);
+static void ural_set_basicrates(struct ural_softc *,
+ const struct ieee80211_channel *);
static void ural_set_bssid(struct ural_softc *, const uint8_t *);
static void ural_set_macaddr(struct ural_softc *, uint8_t *);
static void ural_update_promisc(struct ural_softc *);
@@ -174,6 +177,7 @@ static void ural_read_eeprom(struct ural_softc *);
static int ural_bbp_init(struct ural_softc *);
static void ural_set_txantenna(struct ural_softc *, int);
static void ural_set_rxantenna(struct ural_softc *, int);
+static void ural_init_locked(struct ural_softc *);
static void ural_init(void *);
static void ural_stop(void *);
static int ural_raw_xmit(struct ieee80211_node *, struct mbuf *,
@@ -387,18 +391,18 @@ ural_attach(device_t self)
struct ural_softc *sc = device_get_softc(self);
struct usb_attach_arg *uaa = device_get_ivars(self);
struct ifnet *ifp;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
usb_interface_descriptor_t *id;
usb_endpoint_descriptor_t *ed;
usbd_status error;
- int i, bands;
+ int i;
+ uint8_t bands;
sc->sc_udev = uaa->device;
sc->sc_dev = self;
if (usbd_set_config_no(sc->sc_udev, RAL_CONFIG_NO, 0) != 0) {
- printf("%s: could not set configuration no\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(self, "could not set configuration no\n");
return ENXIO;
}
@@ -406,8 +410,7 @@ ural_attach(device_t self)
error = usbd_device2interface_handle(sc->sc_udev, RAL_IFACE_INDEX,
&sc->sc_iface);
if (error != 0) {
- printf("%s: could not get interface handle\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(self, "could not get interface handle\n");
return ENXIO;
}
@@ -420,8 +423,8 @@ ural_attach(device_t self)
for (i = 0; i < id->bNumEndpoints; i++) {
ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
if (ed == NULL) {
- printf("%s: no endpoint descriptor for %d\n",
- device_get_nameunit(sc->sc_dev), i);
+ device_printf(self, "no endpoint descriptor for %d\n",
+ i);
return ENXIO;
}
@@ -433,10 +436,16 @@ ural_attach(device_t self)
sc->sc_tx_no = ed->bEndpointAddress;
}
if (sc->sc_rx_no == -1 || sc->sc_tx_no == -1) {
- printf("%s: missing endpoint\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(self, "missing endpoint\n");
+ return ENXIO;
+ }
+
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(sc->sc_dev, "can not if_alloc()\n");
return ENXIO;
}
+ ic = ifp->if_l2com;
mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK,
MTX_DEF | MTX_RECURSE);
@@ -444,7 +453,6 @@ ural_attach(device_t self)
usb_init_task(&sc->sc_task, ural_task, sc);
usb_init_task(&sc->sc_scantask, ural_scantask, sc);
callout_init(&sc->watchdog_ch, 0);
- callout_init(&sc->amrr_ch, 0);
/* retrieve RT2570 rev. no */
sc->asic_rev = ural_read(sc, RAL_MAC_CSR0);
@@ -452,16 +460,8 @@ ural_attach(device_t self)
/* retrieve MAC address and various other things from EEPROM */
ural_read_eeprom(sc);
- printf("%s: MAC/BBP RT2570 (rev 0x%02x), RF %s\n",
- device_get_nameunit(sc->sc_dev), sc->asic_rev,
- ural_get_rf(sc->rf_rev));
-
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
- if (ifp == NULL) {
- printf("%s: can not if_alloc()\n",
- device_get_nameunit(sc->sc_dev));
- return ENXIO;
- }
+ device_printf(sc->sc_dev, "MAC/BBP RT2570 (rev 0x%02x), RF %s\n",
+ sc->asic_rev, ural_get_rf(sc->rf_rev));
ifp->if_softc = sc;
if_initname(ifp, "ural", device_get_unit(sc->sc_dev));
@@ -476,8 +476,6 @@ ural_attach(device_t self)
ic->ic_ifp = ifp;
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
- ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
- ic->ic_state = IEEE80211_S_INIT;
/* set device capabilities */
ic->ic_caps =
@@ -496,34 +494,29 @@ ural_attach(device_t self)
setbit(&bands, IEEE80211_MODE_11G);
if (sc->rf_rev == RAL_RF_5222)
setbit(&bands, IEEE80211_MODE_11A);
- ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
+ ieee80211_init_channels(ic, NULL, &bands);
ieee80211_ifattach(ic);
- ic->ic_reset = ural_reset;
- /* enable s/w bmiss handling in sta mode */
- ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;
+ ic->ic_newassoc = ural_newassoc;
+ ic->ic_raw_xmit = ural_raw_xmit;
+ ic->ic_node_alloc = ural_node_alloc;
ic->ic_scan_start = ural_scan_start;
ic->ic_scan_end = ural_scan_end;
ic->ic_set_channel = ural_set_channel;
- /* override state transition machine */
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = ural_newstate;
- ic->ic_raw_xmit = ural_raw_xmit;
- ieee80211_media_init(ic, ural_media_change, ieee80211_media_status);
+ ic->ic_vap_create = ural_vap_create;
+ ic->ic_vap_delete = ural_vap_delete;
- ieee80211_amrr_init(&sc->amrr, ic,
- IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
- IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD);
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof (struct ieee80211_frame) + 64, &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof(sc->sc_txtap));
- sc->sc_rxtap_len = sizeof sc->sc_rxtapu;
+ sc->sc_rxtap_len = sizeof sc->sc_rxtap;
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
sc->sc_rxtap.wr_ihdr.it_present = htole32(RAL_RX_RADIOTAP_PRESENT);
- sc->sc_txtap_len = sizeof sc->sc_txtapu;
+ sc->sc_txtap_len = sizeof sc->sc_txtap;
sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
sc->sc_txtap.wt_ihdr.it_present = htole32(RAL_TX_RADIOTAP_PRESENT);
@@ -537,13 +530,16 @@ static int
ural_detach(device_t self)
{
struct ural_softc *sc = device_get_softc(self);
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
ural_stop(sc);
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+
usb_rem_task(sc->sc_udev, &sc->sc_task);
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
callout_stop(&sc->watchdog_ch);
- callout_stop(&sc->amrr_ch);
if (sc->amrr_xfer != NULL) {
usbd_free_xfer(sc->amrr_xfer);
@@ -563,22 +559,66 @@ ural_detach(device_t self)
ural_free_rx_list(sc);
ural_free_tx_list(sc);
- bpfdetach(ifp);
- ieee80211_ifdetach(ic);
if_free(ifp);
-
mtx_destroy(&sc->sc_mtx);
return 0;
}
+static struct ieee80211vap *
+ural_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ural_vap *uvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ uvp = (struct ural_vap *) malloc(sizeof(struct ural_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (uvp == NULL)
+ return NULL;
+ vap = &uvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+ ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
+
+ /* override state transition machine */
+ uvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = ural_newstate;
+
+ callout_init(&uvp->amrr_ch, 0);
+ ieee80211_amrr_init(&uvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 1000 /* 1 sec */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+ural_vap_delete(struct ieee80211vap *vap)
+{
+ struct ural_vap *uvp = URAL_VAP(vap);
+
+ callout_stop(&uvp->amrr_ch);
+ ieee80211_amrr_cleanup(&uvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(uvp, M_80211_VAP);
+}
+
static int
ural_alloc_tx_list(struct ural_softc *sc)
{
struct ural_tx_data *data;
int i, error;
- sc->tx_queued = 0;
+ sc->tx_queued = sc->tx_cur = 0;
for (i = 0; i < RAL_TX_LIST_COUNT; i++) {
data = &sc->tx_data[i];
@@ -587,8 +627,8 @@ ural_alloc_tx_list(struct ural_softc *sc)
data->xfer = usbd_alloc_xfer(sc->sc_udev);
if (data->xfer == NULL) {
- printf("%s: could not allocate tx xfer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate tx xfer\n");
error = ENOMEM;
goto fail;
}
@@ -596,8 +636,8 @@ ural_alloc_tx_list(struct ural_softc *sc)
data->buf = usbd_alloc_buffer(data->xfer,
RAL_TX_DESC_SIZE + MCLBYTES);
if (data->buf == NULL) {
- printf("%s: could not allocate tx buffer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate tx buffer\n");
error = ENOMEM;
goto fail;
}
@@ -643,23 +683,23 @@ ural_alloc_rx_list(struct ural_softc *sc)
data->xfer = usbd_alloc_xfer(sc->sc_udev);
if (data->xfer == NULL) {
- printf("%s: could not allocate rx xfer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate rx xfer\n");
error = ENOMEM;
goto fail;
}
if (usbd_alloc_buffer(data->xfer, MCLBYTES) == NULL) {
- printf("%s: could not allocate rx buffer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate rx buffer\n");
error = ENOMEM;
goto fail;
}
data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
if (data->m == NULL) {
- printf("%s: could not allocate rx mbuf\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate rx mbuf\n");
error = ENOMEM;
goto fail;
}
@@ -694,39 +734,20 @@ ural_free_rx_list(struct ural_softc *sc)
}
}
-static int
-ural_media_change(struct ifnet *ifp)
-{
- struct ural_softc *sc = ifp->if_softc;
- int error;
-
- RAL_LOCK(sc);
-
- error = ieee80211_media_change(ifp);
- if (error != ENETRESET) {
- RAL_UNLOCK(sc);
- return error;
- }
-
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING))
- ural_init(sc);
-
- RAL_UNLOCK(sc);
-
- return 0;
-}
-
static void
ural_task(void *xarg)
{
struct ural_softc *sc = xarg;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ural_vap *uvp = URAL_VAP(vap);
+ const struct ieee80211_txparam *tp;
enum ieee80211_state ostate;
struct ieee80211_node *ni;
struct mbuf *m;
- ostate = ic->ic_state;
+ ostate = vap->iv_state;
RAL_LOCK(sc);
switch (sc->sc_state) {
@@ -741,27 +762,27 @@ ural_task(void *xarg)
break;
case IEEE80211_S_RUN:
- ni = ic->ic_bss;
+ ni = vap->iv_bss;
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
ural_update_slot(ic->ic_ifp);
ural_set_txpreamble(sc);
- ural_set_basicrates(sc);
+ ural_set_basicrates(sc, ic->ic_bsschan);
ural_set_bssid(sc, ni->ni_bssid);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS) {
- m = ieee80211_beacon_alloc(ni, &sc->sc_bo);
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS) {
+ m = ieee80211_beacon_alloc(ni, &uvp->bo);
if (m == NULL) {
- printf("%s: could not allocate beacon\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not allocate beacon\n");
return;
}
if (ural_tx_bcn(sc, m, ni) != 0) {
- printf("%s: could not send beacon\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "could not send beacon\n");
return;
}
}
@@ -769,12 +790,12 @@ ural_task(void *xarg)
/* make tx led blink on tx (controlled by ASIC) */
ural_write(sc, RAL_MAC_CSR20, 1);
- if (ic->ic_opmode != IEEE80211_M_MONITOR)
+ if (vap->iv_opmode != IEEE80211_M_MONITOR)
ural_enable_tsf_sync(sc);
- /* enable automatic rate adaptation in STA mode */
- if (ic->ic_opmode == IEEE80211_M_STA &&
- ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)
+ /* enable automatic rate adaptation */
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
ural_amrr_start(sc, ni);
break;
@@ -784,15 +805,21 @@ ural_task(void *xarg)
}
RAL_UNLOCK(sc);
- sc->sc_newstate(ic, sc->sc_state, sc->sc_arg);
+
+ IEEE80211_LOCK(ic);
+ uvp->newstate(vap, sc->sc_state, sc->sc_arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
+ IEEE80211_UNLOCK(ic);
}
static void
ural_scantask(void *arg)
{
struct ural_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
RAL_LOCK(sc);
if (sc->sc_scan_action == URAL_SCAN_START) {
@@ -806,77 +833,42 @@ ural_scantask(void *arg)
} else {
ural_enable_tsf_sync(sc);
/* XXX keep local copy */
- ural_set_bssid(sc, ic->ic_bss->ni_bssid);
+ ural_set_bssid(sc, vap->iv_bss->ni_bssid);
}
RAL_UNLOCK(sc);
}
static int
-ural_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+ural_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct ural_vap *uvp = URAL_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
struct ural_softc *sc = ic->ic_ifp->if_softc;
- callout_stop(&sc->amrr_ch);
+ callout_stop(&uvp->amrr_ch);
/* do it in a process context */
sc->sc_state = nstate;
sc->sc_arg = arg;
usb_rem_task(sc->sc_udev, &sc->sc_task);
- if (nstate == IEEE80211_S_INIT)
- sc->sc_newstate(ic, nstate, arg);
- else
+ if (nstate == IEEE80211_S_INIT) {
+ uvp->newstate(vap, nstate, arg);
+ return 0;
+ } else {
usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER);
- return 0;
+ return EINPROGRESS;
+ }
}
-/* quickly determine if a given rate is CCK or OFDM */
-#define RAL_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
-
-#define RAL_ACK_SIZE 14 /* 10 + 4(FCS) */
-#define RAL_CTS_SIZE 14 /* 10 + 4(FCS) */
-
-#define RAL_SIFS 10 /* us */
-
#define RAL_RXTX_TURNAROUND 5 /* us */
-/*
- * This function is only used by the Rx radiotap code.
- */
-static int
-ural_rxrate(struct ural_rx_desc *desc)
-{
- if (le32toh(desc->flags) & RAL_RX_OFDM) {
- /* reverse function of ural_plcp_signal */
- switch (desc->rate) {
- case 0xb: return 12;
- case 0xf: return 18;
- case 0xa: return 24;
- case 0xe: return 36;
- case 0x9: return 48;
- case 0xd: return 72;
- case 0x8: return 96;
- case 0xc: return 108;
- }
- } else {
- if (desc->rate == 10)
- return 2;
- if (desc->rate == 20)
- return 4;
- if (desc->rate == 55)
- return 11;
- if (desc->rate == 110)
- return 22;
- }
- return 2; /* should not get there */
-}
-
static void
ural_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
struct ural_tx_data *data = priv;
struct ural_softc *sc = data->sc;
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
if (data->m->m_flags & M_TXCB)
ieee80211_process_callback(data->ni, data->m,
@@ -885,8 +877,8 @@ ural_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
return;
- printf("%s: could not transmit buffer: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(status));
+ device_printf(sc->sc_dev, "could not transmit buffer: %s\n",
+ usbd_errstr(status));
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh);
@@ -916,13 +908,12 @@ ural_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
struct ural_rx_data *data = priv;
struct ural_softc *sc = data->sc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ural_rx_desc *desc;
- struct ieee80211_frame *wh;
struct ieee80211_node *ni;
struct mbuf *mnew, *m;
- int len;
+ int len, rssi;
if (status != USBD_NORMAL_COMPLETION) {
if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
@@ -970,30 +961,30 @@ ural_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = m->m_len = (le32toh(desc->flags) >> 16) & 0xfff;
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct ural_rx_radiotap_header *tap = &sc->sc_rxtap;
tap->wr_flags = IEEE80211_RADIOTAP_F_FCS;
- tap->wr_rate = ural_rxrate(desc);
+ tap->wr_rate = ieee80211_plcp2rate(desc->rate,
+ le32toh(desc->flags) & RAL_RX_OFDM);
tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wr_antenna = sc->rx_ant;
tap->wr_antsignal = URAL_RSSI(desc->rssi);
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
}
/* Strip trailing 802.11 MAC FCS. */
m_adj(m, -IEEE80211_CRC_LEN);
- wh = mtod(m, struct ieee80211_frame *);
- ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
-
- /* send the frame to the 802.11 layer */
- ieee80211_input(ic, m, ni, URAL_RSSI(desc->rssi), RAL_NOISE_FLOOR, 0);
-
- /* node is no longer needed */
- ieee80211_free_node(ni);
+ rssi = URAL_RSSI(desc->rssi);
+ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, rssi, RAL_NOISE_FLOOR, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, RAL_NOISE_FLOOR, 0);
DPRINTFN(15, ("rx done\n"));
@@ -1003,95 +994,12 @@ skip: /* setup a new transfer */
usbd_transfer(xfer);
}
-/*
- * Return the expected ack rate for a frame transmitted at rate `rate'.
- * XXX: this should depend on the destination node basic rate set.
- */
-static int
-ural_ack_rate(struct ieee80211com *ic, int rate)
-{
- switch (rate) {
- /* CCK rates */
- case 2:
- return 2;
- case 4:
- case 11:
- case 22:
- return (ic->ic_curmode == IEEE80211_MODE_11B) ? 4 : rate;
-
- /* OFDM rates */
- case 12:
- case 18:
- return 12;
- case 24:
- case 36:
- return 24;
- case 48:
- case 72:
- case 96:
- case 108:
- return 48;
- }
-
- /* default to 1Mbps */
- return 2;
-}
-
-/*
- * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'.
- * The function automatically determines the operating mode depending on the
- * given rate. `flags' indicates whether short preamble is in use or not.
- */
-static uint16_t
-ural_txtime(int len, int rate, uint32_t flags)
-{
- uint16_t txtime;
-
- if (RAL_RATE_IS_OFDM(rate)) {
- /* IEEE Std 802.11a-1999, pp. 37 */
- txtime = (8 + 4 * len + 3 + rate - 1) / rate;
- txtime = 16 + 4 + 4 * txtime + 6;
- } else {
- /* IEEE Std 802.11b-1999, pp. 28 */
- txtime = (16 * len + rate - 1) / rate;
- if (rate != 2 && (flags & IEEE80211_F_SHPREAMBLE))
- txtime += 72 + 24;
- else
- txtime += 144 + 48;
- }
- return txtime;
-}
-
-static uint8_t
-ural_plcp_signal(int rate)
-{
- switch (rate) {
- /* CCK rates (returned values are device-dependent) */
- case 2: return 0x0;
- case 4: return 0x1;
- case 11: return 0x2;
- case 22: return 0x3;
-
- /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
- case 12: return 0xb;
- case 18: return 0xf;
- case 24: return 0xa;
- case 36: return 0xe;
- case 48: return 0x9;
- case 72: return 0xd;
- case 96: return 0x8;
- case 108: return 0xc;
-
- /* unsupported rates (should not get there) */
- default: return 0xff;
- }
-}
-
static void
ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc,
uint32_t flags, int len, int rate)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint16_t plcp_length;
int remainder;
@@ -1103,11 +1011,11 @@ ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc,
desc->wme |= htole16(RAL_IVOFFSET(sizeof (struct ieee80211_frame)));
/* setup PLCP fields */
- desc->plcp_signal = ural_plcp_signal(rate);
+ desc->plcp_signal = ieee80211_rate2plcp(rate);
desc->plcp_service = 4;
len += IEEE80211_CRC_LEN;
- if (RAL_RATE_IS_OFDM(rate)) {
+ if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) {
desc->flags |= htole32(RAL_TX_OFDM);
plcp_length = len & 0xfff;
@@ -1136,14 +1044,17 @@ ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc,
static int
ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_txparam *tp;
struct ural_tx_desc *desc;
usbd_xfer_handle xfer;
- uint8_t cmd = 0;
+ uint8_t cmd;
usbd_status error;
uint8_t *buf;
- int xferlen, rate;
+ int xferlen;
- rate = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? 12 : 2;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
xfer = usbd_alloc_xfer(sc->sc_udev);
if (xfer == NULL)
@@ -1158,6 +1069,7 @@ ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
return ENOMEM;
}
+ cmd = 0;
usbd_setup_xfer(xfer, sc->sc_tx_pipeh, NULL, &cmd, sizeof cmd,
USBD_FORCE_SHORT_XFER, RAL_TX_TIMEOUT, NULL);
@@ -1171,10 +1083,10 @@ ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
m_copydata(m0, 0, m0->m_pkthdr.len, buf + RAL_TX_DESC_SIZE);
ural_setup_tx_desc(sc, desc, RAL_TX_IFS_NEWBACKOFF | RAL_TX_TIMESTAMP,
- m0->m_pkthdr.len, rate);
+ m0->m_pkthdr.len, tp->mgmtrate);
DPRINTFN(10, ("sending beacon frame len=%u rate=%u xfer len=%u\n",
- m0->m_pkthdr.len, rate, xferlen));
+ m0->m_pkthdr.len, tp->mgmtrate, xferlen));
usbd_setup_xfer(xfer, sc->sc_tx_pipeh, NULL, buf, xferlen,
USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT, NULL);
@@ -1188,40 +1100,43 @@ ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
static int
ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ const struct ieee80211_txparam *tp;
struct ural_tx_desc *desc;
struct ural_tx_data *data;
struct ieee80211_frame *wh;
struct ieee80211_key *k;
- uint32_t flags = 0;
+ uint32_t flags;
uint16_t dur;
usbd_status error;
- int xferlen, rate;
+ int xferlen;
- data = &sc->tx_data[0];
+ data = &sc->tx_data[sc->tx_cur];
desc = (struct ural_tx_desc *)data->buf;
- rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
wh = mtod(m0, struct ieee80211_frame *);
-
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
}
+ wh = mtod(m0, struct ieee80211_frame *);
}
data->m = m0;
data->ni = ni;
- wh = mtod(m0, struct ieee80211_frame *);
-
+ flags = 0;
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
flags |= RAL_TX_ACK;
- dur = ural_txtime(RAL_ACK_SIZE, rate, ic->ic_flags) + RAL_SIFS;
+ dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
/* tell hardware to add timestamp for probe responses */
@@ -1232,20 +1147,20 @@ ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
flags |= RAL_TX_TIMESTAMP;
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct ural_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
- tap->wt_rate = rate;
+ tap->wt_rate = tp->mgmtrate;
tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE);
- ural_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, rate);
+ ural_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, tp->mgmtrate);
/* align end on a 2-bytes boundary */
xferlen = (RAL_TX_DESC_SIZE + m0->m_pkthdr.len + 1) & ~1;
@@ -1258,7 +1173,7 @@ ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
xferlen += 2;
DPRINTFN(10, ("sending mgt frame len=%u rate=%u xfer len=%u\n",
- m0->m_pkthdr.len, rate, xferlen));
+ m0->m_pkthdr.len, tp->mgmtrate, xferlen));
usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf,
xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT,
@@ -1273,6 +1188,71 @@ ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
}
sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RAL_TX_LIST_COUNT;
+
+ return 0;
+}
+
+static int
+ural_sendprot(struct ural_softc *sc,
+ const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_frame *wh;
+ struct ural_tx_desc *desc;
+ struct ural_tx_data *data;
+ struct mbuf *mprot;
+ int protrate, ackrate, pktlen, flags, isshort;
+ uint16_t dur;
+ usbd_status error;
+
+ KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY,
+ ("protection %d", prot));
+
+ wh = mtod(m, const struct ieee80211_frame *);
+ pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ protrate = ieee80211_ctl_rate(sc->sc_rates, rate);
+ ackrate = ieee80211_ack_rate(sc->sc_rates, rate);
+
+ isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
+ dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort);
+ + ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags = RAL_TX_RETRY(7);
+ if (prot == IEEE80211_PROT_RTSCTS) {
+ /* NB: CTS is the same size as an ACK */
+ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags |= RAL_TX_ACK;
+ mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
+ } else {
+ mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
+ }
+ if (mprot == NULL) {
+ /* XXX stat + msg */
+ return ENOBUFS;
+ }
+ data = &sc->tx_data[sc->tx_cur];
+ desc = (struct ural_tx_desc *)data->buf;
+
+ data->m = mprot;
+ data->ni = ieee80211_ref_node(ni);
+ m_copydata(mprot, 0, mprot->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE);
+ ural_setup_tx_desc(sc, desc, flags, mprot->m_pkthdr.len, protrate);
+
+ usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf,
+ /* NB: no roundup necessary */
+ RAL_TX_DESC_SIZE + mprot->m_pkthdr.len,
+ USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT, ural_txeof);
+
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) {
+ data->m = NULL;
+ data->ni = NULL;
+ return error;
+ }
+
+ sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RAL_TX_LIST_COUNT;
return 0;
}
@@ -1281,14 +1261,17 @@ static int
ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
const struct ieee80211_bpf_params *params)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ural_tx_desc *desc;
struct ural_tx_data *data;
uint32_t flags;
usbd_status error;
int xferlen, rate;
- data = &sc->tx_data[0];
+ KASSERT(params != NULL, ("no raw xmit params"));
+
+ data = &sc->tx_data[sc->tx_cur];
desc = (struct ural_tx_desc *)data->buf;
rate = params->ibp_rate0 & IEEE80211_RATE_VAL;
@@ -1297,8 +1280,22 @@ ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
m_freem(m0);
return EINVAL;
}
+ flags = 0;
+ if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
+ flags |= RAL_TX_ACK;
+ if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) {
+ error = ural_sendprot(sc, m0, ni,
+ params->ibp_flags & IEEE80211_BPF_RTS ?
+ IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY,
+ rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RAL_TX_IFS_SIFS;
+ }
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct ural_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -1307,16 +1304,12 @@ ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
data->m = m0;
data->ni = ni;
- flags = 0;
- if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
- flags |= RAL_TX_ACK;
-
m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE);
/* XXX need to setup descriptor ourself */
ural_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, rate);
@@ -1347,6 +1340,7 @@ ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
}
sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RAL_TX_LIST_COUNT;
return 0;
}
@@ -1354,10 +1348,13 @@ ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
static int
ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
struct ural_tx_desc *desc;
struct ural_tx_data *data;
struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
struct ieee80211_key *k;
uint32_t flags = 0;
uint16_t dur;
@@ -1366,25 +1363,42 @@ ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
wh = mtod(m0, struct ieee80211_frame *);
- if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE)
- rate = ic->ic_fixed_rate;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ rate = tp->mcastrate;
+ else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
+ rate = tp->ucastrate;
else
- rate = ni->ni_rates.rs_rates[ni->ni_txrate];
-
- rate &= IEEE80211_RATE_VAL;
+ rate = ni->ni_txrate;
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
}
-
/* packet header may have moved, reset our local pointer */
wh = mtod(m0, struct ieee80211_frame *);
}
- data = &sc->tx_data[0];
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ int prot = IEEE80211_PROT_NONE;
+ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
+ prot = IEEE80211_PROT_RTSCTS;
+ else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM)
+ prot = ic->ic_protmode;
+ if (prot != IEEE80211_PROT_NONE) {
+ error = ural_sendprot(sc, m0, ni, prot, rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RAL_TX_IFS_SIFS;
+ }
+ }
+
+ data = &sc->tx_data[sc->tx_cur];
desc = (struct ural_tx_desc *)data->buf;
data->m = m0;
@@ -1394,12 +1408,12 @@ ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
flags |= RAL_TX_ACK;
flags |= RAL_TX_RETRY(7);
- dur = ural_txtime(RAL_ACK_SIZE, ural_ack_rate(ic, rate),
- ic->ic_flags) + RAL_SIFS;
+ dur = ieee80211_ack_duration(sc->sc_rates, rate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct ural_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -1408,7 +1422,7 @@ ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wt_antenna = sc->tx_ant;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE);
@@ -1440,6 +1454,7 @@ ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
}
sc->tx_queued++;
+ sc->tx_cur = (sc->tx_cur + 1) % RAL_TX_LIST_COUNT;
return 0;
}
@@ -1448,77 +1463,30 @@ static void
ural_start(struct ifnet *ifp)
{
struct ural_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct mbuf *m0;
- struct ether_header *eh;
struct ieee80211_node *ni;
+ struct mbuf *m;
for (;;) {
- IF_POLL(&ic->ic_mgtq, m0);
- if (m0 != NULL) {
- if (sc->tx_queued >= RAL_TX_LIST_COUNT) {
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- IF_DEQUEUE(&ic->ic_mgtq, m0);
-
- ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
- m0->m_pkthdr.rcvif = NULL;
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (ural_tx_mgt(sc, m0, ni) != 0) {
- ieee80211_free_node(ni);
- break;
- }
- } else {
- if (ic->ic_state != IEEE80211_S_RUN)
- break;
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
- if (sc->tx_queued >= RAL_TX_LIST_COUNT) {
- IFQ_DRV_PREPEND(&ifp->if_snd, m0);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
-
- if (m0->m_len < sizeof (struct ether_header) &&
- !(m0 = m_pullup(m0, sizeof (struct ether_header))))
- continue;
-
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- continue;
- }
- BPF_MTAP(ifp, m0);
-
- m0 = ieee80211_encap(ic, m0, ni);
- if (m0 == NULL) {
- ieee80211_free_node(ni);
- continue;
- }
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (ural_tx_data(sc, m0, ni) != 0) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- break;
- }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ if (sc->tx_queued >= RAL_TX_LIST_COUNT-1) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ continue;
+ }
+ if (ural_tx_data(sc, m, ni) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
}
-
sc->sc_tx_timer = 5;
- ic->ic_lastdata = ticks;
callout_reset(&sc->watchdog_ch, hz, ural_watchdog, sc);
}
}
@@ -1544,61 +1512,40 @@ ural_watchdog(void *arg)
RAL_UNLOCK(sc);
}
-/*
- * This function allows for fast channel switching in monitor mode (used by
- * net-mgmt/kismet). In IBSS mode, we must explicitly reset the interface to
- * generate a new beacon frame.
- */
-static int
-ural_reset(struct ifnet *ifp)
-{
- struct ural_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
-
- if (ic->ic_opmode != IEEE80211_M_MONITOR)
- return ENETRESET;
-
- ural_set_chan(sc, ic->ic_curchan);
-
- return 0;
-}
-
static int
ural_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct ural_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 1;
RAL_LOCK(sc);
-
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ ural_init_locked(sc);
+ startall = 1;
+ } else
ural_update_promisc(sc);
- else
- ural_init(sc);
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
ural_stop(sc);
}
break;
-
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
default:
- error = ieee80211_ioctl(ic, cmd, data);
- }
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
- (ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
- ural_init(sc);
- error = 0;
+ error = ether_ioctl(ifp, cmd, data);
+ break;
}
-
RAL_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
@@ -1616,8 +1563,8 @@ ural_set_testmode(struct ural_softc *sc)
error = usbd_do_request(sc->sc_udev, &req, NULL);
if (error != 0) {
- printf("%s: could not set test mode: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not set test mode: %s\n",
+ usbd_errstr(error));
}
}
@@ -1635,8 +1582,8 @@ ural_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, int len)
error = usbd_do_request(sc->sc_udev, &req, buf);
if (error != 0) {
- printf("%s: could not read EEPROM: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not read EEPROM: %s\n",
+ usbd_errstr(error));
}
}
@@ -1655,8 +1602,8 @@ ural_read(struct ural_softc *sc, uint16_t reg)
error = usbd_do_request(sc->sc_udev, &req, &val);
if (error != 0) {
- printf("%s: could not read MAC register: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not read MAC register: %s\n",
+ usbd_errstr(error));
return 0;
}
@@ -1677,8 +1624,8 @@ ural_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len)
error = usbd_do_request(sc->sc_udev, &req, buf);
if (error != 0) {
- printf("%s: could not read MAC register: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not read MAC register: %s\n",
+ usbd_errstr(error));
}
}
@@ -1696,8 +1643,8 @@ ural_write(struct ural_softc *sc, uint16_t reg, uint16_t val)
error = usbd_do_request(sc->sc_udev, &req, NULL);
if (error != 0) {
- printf("%s: could not write MAC register: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not write MAC register: %s\n",
+ usbd_errstr(error));
}
}
@@ -1715,8 +1662,8 @@ ural_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len)
error = usbd_do_request(sc->sc_udev, &req, buf);
if (error != 0) {
- printf("%s: could not write MAC register: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not write MAC register: %s\n",
+ usbd_errstr(error));
}
}
@@ -1731,7 +1678,7 @@ ural_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val)
break;
}
if (ntries == 5) {
- printf("%s: could not write to BBP\n", device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not write to BBP\n");
return;
}
@@ -1753,7 +1700,7 @@ ural_bbp_read(struct ural_softc *sc, uint8_t reg)
break;
}
if (ntries == 5) {
- printf("%s: could not read BBP\n", device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not read BBP\n");
return 0;
}
@@ -1771,7 +1718,7 @@ ural_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val)
break;
}
if (ntries == 5) {
- printf("%s: could not write to RF\n", device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not write to RF\n");
return;
}
@@ -1785,6 +1732,24 @@ ural_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val)
DPRINTFN(15, ("RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff));
}
+/* ARGUSED */
+static struct ieee80211_node *
+ural_node_alloc(struct ieee80211_node_table *nt __unused)
+{
+ struct ural_node *un;
+
+ un = malloc(sizeof(struct ural_node), M_80211_NODE, M_NOWAIT | M_ZERO);
+ return un != NULL ? &un->ni : NULL;
+}
+
+static void
+ural_newassoc(struct ieee80211_node *ni, int isnew)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ ieee80211_amrr_node_init(&URAL_VAP(vap)->amrr, &URAL_NODE(ni)->amn, ni);
+}
+
static void
ural_scan_start(struct ieee80211com *ic)
{
@@ -1822,12 +1787,15 @@ ural_set_channel(struct ieee80211com *ic)
/* do it in a process context */
sc->sc_scan_action = URAL_SET_CHANNEL;
usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
}
static void
ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint8_t power, tmp;
u_int i, chan;
@@ -1924,17 +1892,9 @@ ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c)
ural_disable_rf_tune(sc);
}
+ /* XXX doesn't belong here */
/* update basic rate set */
- if (IEEE80211_IS_CHAN_B(c)) {
- /* 11b basic rates: 1, 2Mbps */
- ural_write(sc, RAL_TXRX_CSR11, 0x3);
- } else if (IEEE80211_IS_CHAN_A(c)) {
- /* 11a basic rates: 6, 12, 24Mbps */
- ural_write(sc, RAL_TXRX_CSR11, 0x150);
- } else {
- /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */
- ural_write(sc, RAL_TXRX_CSR11, 0x15f);
- }
+ ural_set_basicrates(sc, c);
}
/*
@@ -1963,13 +1923,15 @@ ural_disable_rf_tune(struct ural_softc *sc)
static void
ural_enable_tsf_sync(struct ural_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
uint16_t logcwmin, preload, tmp;
/* first, disable TSF synchronization */
ural_write(sc, RAL_TXRX_CSR19, 0);
- tmp = (16 * ic->ic_bss->ni_intval) << 4;
+ tmp = (16 * vap->iv_bss->ni_intval) << 4;
ural_write(sc, RAL_TXRX_CSR18, tmp);
logcwmin = (ic->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0;
@@ -1992,7 +1954,7 @@ static void
ural_update_slot(struct ifnet *ifp)
{
struct ural_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
uint16_t slottime, sifs, eifs;
slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20;
@@ -2017,32 +1979,33 @@ ural_update_slot(struct ifnet *ifp)
static void
ural_set_txpreamble(struct ural_softc *sc)
{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint16_t tmp;
tmp = ural_read(sc, RAL_TXRX_CSR10);
tmp &= ~RAL_SHORT_PREAMBLE;
- if (sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE)
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
tmp |= RAL_SHORT_PREAMBLE;
ural_write(sc, RAL_TXRX_CSR10, tmp);
}
static void
-ural_set_basicrates(struct ural_softc *sc)
+ural_set_basicrates(struct ural_softc *sc, const struct ieee80211_channel *c)
{
- struct ieee80211com *ic = &sc->sc_ic;
-
+ /* XXX wrong, take from rate set */
/* update basic rate set */
- if (ic->ic_curmode == IEEE80211_MODE_11B) {
- /* 11b basic rates: 1, 2Mbps */
- ural_write(sc, RAL_TXRX_CSR11, 0x3);
- } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan)) {
+ if (IEEE80211_IS_CHAN_5GHZ(c)) {
/* 11a basic rates: 6, 12, 24Mbps */
ural_write(sc, RAL_TXRX_CSR11, 0x150);
- } else {
+ } else if (IEEE80211_IS_CHAN_ANYG(c)) {
/* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */
ural_write(sc, RAL_TXRX_CSR11, 0x15f);
+ } else {
+ /* 11b basic rates: 1, 2Mbps */
+ ural_write(sc, RAL_TXRX_CSR11, 0x3);
}
}
@@ -2083,7 +2046,7 @@ ural_set_macaddr(struct ural_softc *sc, uint8_t *addr)
static void
ural_update_promisc(struct ural_softc *sc)
{
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
uint32_t tmp;
tmp = ural_read(sc, RAL_TXRX_CSR2);
@@ -2116,7 +2079,8 @@ ural_get_rf(int rev)
static void
ural_read_eeprom(struct ural_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint16_t val;
ural_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2);
@@ -2222,12 +2186,11 @@ ural_set_rxantenna(struct ural_softc *sc, int antenna)
}
static void
-ural_init(void *priv)
+ural_init_locked(struct ural_softc *sc)
{
#define N(a) (sizeof (a) / sizeof ((a)[0]))
- struct ural_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ural_rx_data *data;
uint16_t tmp;
usbd_status error;
@@ -2251,8 +2214,8 @@ ural_init(void *priv)
DELAY(1000);
}
if (ntries == 100) {
- printf("%s: timeout waiting for BBP/RF to wakeup\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev,
+ "timeout waiting for BBP/RF to wakeup\n");
goto fail;
}
@@ -2281,8 +2244,7 @@ ural_init(void *priv)
*/
sc->amrr_xfer = usbd_alloc_xfer(sc->sc_udev);
if (sc->amrr_xfer == NULL) {
- printf("%s: could not allocate AMRR xfer\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not allocate AMRR xfer\n");
goto fail;
}
@@ -2292,16 +2254,16 @@ ural_init(void *priv)
error = usbd_open_pipe(sc->sc_iface, sc->sc_tx_no, USBD_EXCLUSIVE_USE,
&sc->sc_tx_pipeh);
if (error != 0) {
- printf("%s: could not open Tx pipe: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not open Tx pipe: %s\n",
+ usbd_errstr(error));
goto fail;
}
error = usbd_open_pipe(sc->sc_iface, sc->sc_rx_no, USBD_EXCLUSIVE_USE,
&sc->sc_rx_pipeh);
if (error != 0) {
- printf("%s: could not open Rx pipe: %s\n",
- device_get_nameunit(sc->sc_dev), usbd_errstr(error));
+ device_printf(sc->sc_dev, "could not open Rx pipe: %s\n",
+ usbd_errstr(error));
goto fail;
}
@@ -2310,15 +2272,13 @@ ural_init(void *priv)
*/
error = ural_alloc_tx_list(sc);
if (error != 0) {
- printf("%s: could not allocate Tx list\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not allocate Tx list\n");
goto fail;
}
error = ural_alloc_rx_list(sc);
if (error != 0) {
- printf("%s: could not allocate Rx list\n",
- device_get_nameunit(sc->sc_dev));
+ device_printf(sc->sc_dev, "could not allocate Rx list\n");
goto fail;
}
@@ -2346,13 +2306,6 @@ ural_init(void *priv)
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
-
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-
return;
fail: ural_stop(sc);
@@ -2360,17 +2313,29 @@ fail: ural_stop(sc);
}
static void
+ural_init(void *priv)
+{
+ struct ural_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ RAL_LOCK(sc);
+ ural_init_locked(sc);
+ RAL_UNLOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vap's */
+}
+
+static void
ural_stop(void *priv)
{
struct ural_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
sc->sc_tx_timer = 0;
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
-
/* disable Rx */
ural_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX);
@@ -2420,9 +2385,6 @@ ural_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
return EIO;
}
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m);
-
ifp->if_opackets++;
if (params == NULL) {
@@ -2453,27 +2415,22 @@ bad:
static void
ural_amrr_start(struct ural_softc *sc, struct ieee80211_node *ni)
{
- int i;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ural_vap *uvp = URAL_VAP(vap);
/* clear statistic registers (STA_CSR0 to STA_CSR10) */
ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta);
- ieee80211_amrr_node_init(&sc->amrr, &sc->amn);
-
- /* set rate to some reasonable initial value */
- for (i = ni->ni_rates.rs_nrates - 1;
- i > 0 && (ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) > 72;
- i--);
-
- ni->ni_txrate = i;
+ ieee80211_amrr_node_init(&uvp->amrr, &URAL_NODE(ni)->amn, ni);
- callout_reset(&sc->amrr_ch, hz, ural_amrr_timeout, sc);
+ callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, vap);
}
static void
ural_amrr_timeout(void *arg)
{
- struct ural_softc *sc = (struct ural_softc *)arg;
+ struct ieee80211vap *vap = arg;
+ struct ural_softc *sc = vap->iv_ic->ic_ifp->if_softc;
usb_device_request_t req;
/*
@@ -2485,7 +2442,7 @@ ural_amrr_timeout(void *arg)
USETW(req.wIndex, RAL_STA_CSR0);
USETW(req.wLength, sizeof sc->sta);
- usbd_setup_default_xfer(sc->amrr_xfer, sc->sc_udev, sc,
+ usbd_setup_default_xfer(sc->amrr_xfer, sc->sc_udev, vap,
USBD_DEFAULT_TIMEOUT, &req, sc->sta, sizeof sc->sta, 0,
ural_amrr_update);
(void)usbd_transfer(sc->amrr_xfer);
@@ -2495,8 +2452,12 @@ static void
ural_amrr_update(usbd_xfer_handle xfer, usbd_private_handle priv,
usbd_status status)
{
- struct ural_softc *sc = (struct ural_softc *)priv;
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ieee80211vap *vap = priv;
+ struct ural_vap *uvp = URAL_VAP(vap);
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
+ struct ural_softc *sc = ifp->if_softc;
+ struct ieee80211_node *ni = vap->iv_bss;
+ int ok, fail;
if (status != USBD_NORMAL_COMPLETION) {
device_printf(sc->sc_dev, "could not retrieve Tx statistics - "
@@ -2504,19 +2465,15 @@ ural_amrr_update(usbd_xfer_handle xfer, usbd_private_handle priv,
return;
}
- /* count TX retry-fail as Tx errors */
- ifp->if_oerrors += sc->sta[9];
-
- sc->amn.amn_retrycnt =
- sc->sta[7] + /* TX one-retry ok count */
- sc->sta[8] + /* TX more-retry ok count */
- sc->sta[9]; /* TX retry-fail count */
+ ok = sc->sta[7] + /* TX ok w/o retry */
+ sc->sta[8]; /* TX ok w/ retry */
+ fail = sc->sta[9]; /* TX retry-fail count */
- sc->amn.amn_txcnt =
- sc->amn.amn_retrycnt +
- sc->sta[6]; /* TX no-retry ok count */
+ ieee80211_amrr_tx_update(&URAL_NODE(ni)->amn,
+ ok+fail, ok, sc->sta[8] + fail);
+ (void) ieee80211_amrr_choose(ni, &URAL_NODE(ni)->amn);
- ieee80211_amrr_choose(&sc->amrr, sc->sc_ic.ic_bss, &sc->amn);
+ ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */
- callout_reset(&sc->amrr_ch, hz, ural_amrr_timeout, sc);
+ callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, vap);
}
diff --git a/sys/dev/usb/if_uralvar.h b/sys/dev/usb/if_uralvar.h
index 665b05b..39aef9e 100644
--- a/sys/dev/usb/if_uralvar.h
+++ b/sys/dev/usb/if_uralvar.h
@@ -18,7 +18,7 @@
*/
#define RAL_RX_LIST_COUNT 1
-#define RAL_TX_LIST_COUNT 1
+#define RAL_TX_LIST_COUNT 8
#define URAL_SCAN_START 1
#define URAL_SCAN_END 2
@@ -74,15 +74,31 @@ struct ural_rx_data {
struct mbuf *m;
};
+struct ural_node {
+ struct ieee80211_node ni;
+ struct ieee80211_amrr_node amn;
+};
+#define URAL_NODE(ni) ((struct ural_node *)(ni))
+
+struct ural_vap {
+ struct ieee80211vap vap;
+ struct ieee80211_beacon_offsets bo;
+ struct ieee80211_amrr amrr;
+ struct callout amrr_ch;
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define URAL_VAP(vap) ((struct ural_vap *)(vap))
+
struct ural_softc {
struct ifnet *sc_ifp;
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
device_t sc_dev;
usbd_device_handle sc_udev;
usbd_interface_handle sc_iface;
+ const struct ieee80211_rate_table *sc_rates;
+
int sc_rx_no;
int sc_tx_no;
@@ -100,19 +116,14 @@ struct ural_softc {
struct usb_task sc_task;
struct usb_task sc_scantask;
- struct ieee80211_amrr amrr;
- struct ieee80211_amrr_node amn;
-
struct ural_rx_data rx_data[RAL_RX_LIST_COUNT];
struct ural_tx_data tx_data[RAL_TX_LIST_COUNT];
int tx_queued;
-
- struct ieee80211_beacon_offsets sc_bo;
+ int tx_cur;
struct mtx sc_mtx;
struct callout watchdog_ch;
- struct callout amrr_ch;
int sc_tx_timer;
uint16_t sta[11];
@@ -130,20 +141,10 @@ struct ural_softc {
int tx_ant;
int nb_ant;
- struct bpf_if *sc_drvbpf;
-
- union {
- struct ural_rx_radiotap_header th;
- uint8_t pad[64];
- } sc_rxtapu;
-#define sc_rxtap sc_rxtapu.th
+ struct ural_rx_radiotap_header sc_rxtap;
int sc_rxtap_len;
- union {
- struct ural_tx_radiotap_header th;
- uint8_t pad[64];
- } sc_txtapu;
-#define sc_txtap sc_txtapu.th
+ struct ural_tx_radiotap_header sc_txtap;
int sc_txtap_len;
};
diff --git a/sys/dev/usb/if_zyd.c b/sys/dev/usb/if_zyd.c
index 509d33c..bf51177 100644
--- a/sys/dev/usb/if_zyd.c
+++ b/sys/dev/usb/if_zyd.c
@@ -47,9 +47,8 @@
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_phy.h>
#include <net80211/ieee80211_radiotap.h>
-#include <net80211/ieee80211_proto.h>
-#include <net80211/ieee80211_node.h>
#include <net80211/ieee80211_regdomain.h>
#include <net/bpf.h>
@@ -67,10 +66,7 @@
#include <dev/usb/if_zydreg.h>
#include <dev/usb/if_zydfw.h>
-#ifdef USB_DEBUG
#define ZYD_DEBUG
-#endif
-
#ifdef ZYD_DEBUG
#define DPRINTF(x) do { if (zyddebug > 0) printf x; } while (0)
#define DPRINTFN(n, x) do { if (zyddebug > (n)) printf x; } while (0)
@@ -154,6 +150,11 @@ static device_probe_t zyd_match;
static device_attach_t zyd_attach;
static device_detach_t zyd_detach;
+static struct ieee80211vap *zyd_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void zyd_vap_delete(struct ieee80211vap *);
static int zyd_attachhook(struct zyd_softc *);
static int zyd_complete_attach(struct zyd_softc *);
static int zyd_open_pipes(struct zyd_softc *);
@@ -163,9 +164,8 @@ static void zyd_free_tx_list(struct zyd_softc *);
static int zyd_alloc_rx_list(struct zyd_softc *);
static void zyd_free_rx_list(struct zyd_softc *);
static struct ieee80211_node *zyd_node_alloc(struct ieee80211_node_table *);
-static int zyd_media_change(struct ifnet *);
static void zyd_task(void *);
-static int zyd_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static int zyd_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static int zyd_cmd(struct zyd_softc *, uint16_t, const void *, int,
void *, int, u_int);
static int zyd_read16(struct zyd_softc *, uint16_t, uint16_t *);
@@ -205,11 +205,11 @@ static int zyd_set_macaddr(struct zyd_softc *, const uint8_t *);
static int zyd_set_bssid(struct zyd_softc *, const uint8_t *);
static int zyd_switch_radio(struct zyd_softc *, int);
static void zyd_set_led(struct zyd_softc *, int, int);
-static void zyd_set_multi(struct zyd_softc *);
+static void zyd_set_multi(void *);
+static void zyd_update_mcast(struct ifnet *);
static int zyd_set_rxfilter(struct zyd_softc *);
static void zyd_set_chan(struct zyd_softc *, struct ieee80211_channel *);
static int zyd_set_beacon_interval(struct zyd_softc *, int);
-static uint8_t zyd_plcp_signal(int);
static void zyd_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
static void zyd_rx_data(struct zyd_softc *, const uint8_t *, uint16_t);
static void zyd_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
@@ -219,13 +219,14 @@ static int zyd_tx_mgt(struct zyd_softc *, struct mbuf *,
static int zyd_tx_data(struct zyd_softc *, struct mbuf *,
struct ieee80211_node *);
static void zyd_start(struct ifnet *);
+static int zyd_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
static void zyd_watchdog(void *);
static int zyd_ioctl(struct ifnet *, u_long, caddr_t);
+static void zyd_init_locked(struct zyd_softc *);
static void zyd_init(void *);
static void zyd_stop(struct zyd_softc *, int);
static int zyd_loadfirmware(struct zyd_softc *, u_char *, size_t);
-static void zyd_iter_func(void *, struct ieee80211_node *);
-static void zyd_amrr_timeout(void *);
static void zyd_newassoc(struct ieee80211_node *, int);
static void zyd_scantask(void *);
static void zyd_scan_start(struct ieee80211com *);
@@ -282,7 +283,7 @@ zyd_attach(device_t dev)
sc->sc_dev = dev;
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(dev, "can not if_alloc()\n");
return ENXIO;
@@ -325,18 +326,18 @@ bad:
static int
zyd_complete_attach(struct zyd_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
usbd_status error;
- int bands;
+ uint8_t bands;
mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK,
MTX_DEF | MTX_RECURSE);
usb_init_task(&sc->sc_scantask, zyd_scantask, sc);
usb_init_task(&sc->sc_task, zyd_task, sc);
+ usb_init_task(&sc->sc_mcasttask, zyd_set_multi, sc);
- callout_init(&sc->sc_amrr_ch, 0);
callout_init(&sc->sc_watchdog_ch, 0);
error = usbd_set_config_no(sc->sc_udev, ZYD_CONFIG_NO, 1);
@@ -381,10 +382,11 @@ zyd_complete_attach(struct zyd_softc *sc)
sc->fw_rev >> 8, sc->fw_rev & 0xff, zyd_rf_name(sc->rf_rev),
sc->pa_rev, ether_sprintf(ic->ic_myaddr));
+ IEEE80211_ADDR_COPY(sc->sc_bssid, ic->ic_myaddr);
+
ic->ic_ifp = ifp;
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
- ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
- ic->ic_state = IEEE80211_S_INIT;
+ ic->ic_opmode = IEEE80211_M_STA;
/* set device capabilities */
ic->ic_caps =
@@ -398,29 +400,22 @@ zyd_complete_attach(struct zyd_softc *sc)
bands = 0;
setbit(&bands, IEEE80211_MODE_11B);
setbit(&bands, IEEE80211_MODE_11G);
- ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
+ ieee80211_init_channels(ic, NULL, &bands);
ieee80211_ifattach(ic);
- ic->ic_node_alloc = zyd_node_alloc;
ic->ic_newassoc = zyd_newassoc;
-
- /* enable s/w bmiss handling in sta mode */
- ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;
+ ic->ic_raw_xmit = zyd_raw_xmit;
+ ic->ic_node_alloc = zyd_node_alloc;
ic->ic_scan_start = zyd_scan_start;
ic->ic_scan_end = zyd_scan_end;
ic->ic_set_channel = zyd_set_channel;
- /* override state transition machine */
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = zyd_newstate;
- ieee80211_media_init(ic, zyd_media_change, ieee80211_media_status);
- ieee80211_amrr_init(&sc->amrr, ic,
- IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
- IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD);
+ ic->ic_vap_create = zyd_vap_create;
+ ic->ic_vap_delete = zyd_vap_delete;
+ ic->ic_update_mcast = zyd_update_mcast;
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap),
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap));
sc->sc_rxtap_len = sizeof(sc->sc_rxtap);
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
@@ -447,8 +442,8 @@ static int
zyd_detach(device_t dev)
{
struct zyd_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
if (!device_is_attached(dev))
return 0;
@@ -457,17 +452,16 @@ zyd_detach(device_t dev)
ifp->if_flags &= ~IFF_UP;
zyd_stop(sc, 1);
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+
usb_rem_task(sc->sc_udev, &sc->sc_scantask);
usb_rem_task(sc->sc_udev, &sc->sc_task);
- callout_stop(&sc->sc_amrr_ch);
callout_stop(&sc->sc_watchdog_ch);
zyd_close_pipes(sc);
- bpfdetach(ifp);
- ieee80211_ifdetach(ic);
if_free(ifp);
-
mtx_destroy(&sc->sc_mtx);
usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
@@ -475,6 +469,51 @@ zyd_detach(device_t dev)
return 0;
}
+static struct ieee80211vap *
+zyd_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct zyd_vap *zvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ zvp = (struct zyd_vap *) malloc(sizeof(struct zyd_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (zvp == NULL)
+ return NULL;
+ vap = &zvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+ ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
+
+ /* override state transition machine */
+ zvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = zyd_newstate;
+
+ ieee80211_amrr_init(&zvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 1000 /* 1 sec */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+zyd_vap_delete(struct ieee80211vap *vap)
+{
+ struct zyd_vap *zvp = ZYD_VAP(vap);
+
+ ieee80211_amrr_cleanup(&zvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(zvp, M_80211_VAP);
+}
+
static int
zyd_open_pipes(struct zyd_softc *sc)
{
@@ -666,86 +705,70 @@ zyd_node_alloc(struct ieee80211_node_table *nt __unused)
return zn != NULL ? &zn->ni : NULL;
}
-static int
-zyd_media_change(struct ifnet *ifp)
-{
- struct zyd_softc *sc = ifp->if_softc;
- int error;
-
- error = ieee80211_media_change(ifp);
- if (error != ENETRESET)
- return error;
-
- if ((ifp->if_flags & IFF_UP) == IFF_UP &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) == IFF_DRV_RUNNING)
- zyd_init(sc);
-
- return 0;
-}
-
static void
zyd_task(void *arg)
{
struct zyd_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
- enum ieee80211_state ostate;
-
- ostate = ic->ic_state;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct zyd_vap *zvp = ZYD_VAP(vap);
switch (sc->sc_state) {
case IEEE80211_S_RUN:
{
- struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211_node *ni = vap->iv_bss;
zyd_set_chan(sc, ic->ic_curchan);
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
/* turn link LED on */
zyd_set_led(sc, ZYD_LED1, 1);
/* make data LED blink upon Tx */
zyd_write32(sc, sc->fwbase + ZYD_FW_LINK_STATUS, 1);
- zyd_set_bssid(sc, ni->ni_bssid);
+ IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid);
+ zyd_set_bssid(sc, sc->sc_bssid);
}
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
/* fake a join to init the tx rate */
zyd_newassoc(ni, 1);
}
-
- /* start automatic rate control timer */
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)
- callout_reset(&sc->sc_amrr_ch, hz,
- zyd_amrr_timeout, sc);
-
break;
}
default:
break;
}
- sc->sc_newstate(ic, sc->sc_state, sc->sc_arg);
+ IEEE80211_LOCK(ic);
+ zvp->newstate(vap, sc->sc_state, sc->sc_arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
+ IEEE80211_UNLOCK(ic);
}
static int
-zyd_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+zyd_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct zyd_vap *zvp = ZYD_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
struct zyd_softc *sc = ic->ic_ifp->if_softc;
usb_rem_task(sc->sc_udev, &sc->sc_task);
- callout_stop(&sc->sc_amrr_ch);
/* do it in a process context */
sc->sc_state = nstate;
sc->sc_arg = arg;
- if (nstate == IEEE80211_S_INIT)
- sc->sc_newstate(ic, nstate, arg);
- else
+ if (nstate == IEEE80211_S_INIT) {
+ zvp->newstate(vap, nstate, arg);
+ return 0;
+ } else {
usb_add_task(sc->sc_udev, &sc->sc_task, USB_TASKQ_DRIVER);
-
- return 0;
+ return EINPROGRESS;
+ }
}
static int
@@ -1582,7 +1605,8 @@ fail: return error;
static int
zyd_read_eeprom(struct zyd_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp;
uint16_t val;
int i;
@@ -1607,6 +1631,7 @@ zyd_read_eeprom(struct zyd_softc *sc)
(void)zyd_read32(sc, ZYD_EEPROM_SUBID, &tmp);
sc->regdomain = tmp >> 16;
DPRINTF(("regulatory domain %x\n", sc->regdomain));
+ /* XXX propagate to net80211 after mapping to SKU */
/* read Tx power calibration tables */
for (i = 0; i < 7; i++) {
@@ -1687,10 +1712,11 @@ zyd_set_led(struct zyd_softc *sc, int which, int on)
}
static void
-zyd_set_multi(struct zyd_softc *sc)
+zyd_set_multi(void *arg)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct zyd_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ifmultiaddr *ifma;
uint32_t low, high;
uint8_t v;
@@ -1725,12 +1751,22 @@ zyd_set_multi(struct zyd_softc *sc)
zyd_write32(sc, ZYD_MAC_GHTBH, high);
}
+static void
+zyd_update_mcast(struct ifnet *ifp)
+{
+ struct zyd_softc *sc = ifp->if_softc;
+
+ usb_add_task(sc->sc_udev, &sc->sc_mcasttask, USB_TASKQ_DRIVER);
+}
+
static int
zyd_set_rxfilter(struct zyd_softc *sc)
{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t rxfilter;
- switch (sc->sc_ic.ic_opmode) {
+ switch (ic->ic_opmode) {
case IEEE80211_M_STA:
rxfilter = ZYD_FILTER_BSS;
break;
@@ -1751,7 +1787,8 @@ zyd_set_rxfilter(struct zyd_softc *sc)
static void
zyd_set_chan(struct zyd_softc *sc, struct ieee80211_channel *c)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct zyd_rf *rf = &sc->sc_rf;
uint32_t tmp;
u_int chan;
@@ -1809,31 +1846,6 @@ zyd_set_beacon_interval(struct zyd_softc *sc, int bintval)
return 0;
}
-static uint8_t
-zyd_plcp_signal(int rate)
-{
- switch (rate) {
- /* CCK rates (returned values are device-dependent) */
- case 2: return 0x0;
- case 4: return 0x1;
- case 11: return 0x2;
- case 22: return 0x3;
-
- /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
- case 12: return 0xb;
- case 18: return 0xf;
- case 24: return 0xa;
- case 36: return 0xe;
- case 48: return 0x9;
- case 72: return 0xd;
- case 96: return 0x8;
- case 108: return 0xc;
-
- /* unsupported rates (should not get there) */
- default: return 0xff;
- }
-}
-
static void
zyd_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
@@ -1857,8 +1869,9 @@ zyd_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
if (le16toh(cmd->code) == ZYD_NOTIF_RETRYSTATUS) {
struct zyd_notif_retry *retry =
(struct zyd_notif_retry *)cmd->data;
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
struct ieee80211_node *ni;
DPRINTF(("retry intr: rate=0x%x addr=%s count=%d (0x%x)\n",
@@ -1870,15 +1883,12 @@ zyd_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
* retry statistics. In BSS mode, this node is the AP we're
* associated to so no lookup is actually needed.
*/
- if (ic->ic_opmode != IEEE80211_M_STA) {
- ni = ieee80211_find_node(&ic->ic_sta, retry->macaddr);
- if (ni == NULL)
- return; /* just ignore */
- } else
- ni = ic->ic_bss;
-
- ((struct zyd_node *)ni)->amn.amn_retrycnt++;
-
+ ni = ieee80211_find_txnode(vap, retry->macaddr);
+ if (ni != NULL) {
+ ieee80211_amrr_tx_complete(&ZYD_NODE(ni)->amn,
+ IEEE80211_AMRR_FAILURE, 1);
+ ieee80211_free_node(ni);
+ }
if (le16toh(retry->count) & 0x100)
ifp->if_oerrors++; /* too many retries */
} else if (le16toh(cmd->code) == ZYD_NOTIF_IORD) {
@@ -1918,30 +1928,16 @@ zyd_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
}
}
-static __inline uint8_t
-zyd_plcp2ieee(int signal, int isofdm)
-{
- if (isofdm) {
- static const uint8_t ofdmrates[16] =
- { 0, 0, 0, 0, 0, 0, 0, 96, 48, 24, 12, 108, 72, 36, 18 };
- return ofdmrates[signal & 0xf];
- } else {
- static const uint8_t cckrates[16] =
- { 0, 0, 0, 0, 4, 0, 0, 11, 0, 0, 2, 0, 0, 0, 22, 0 };
- return cckrates[signal & 0xf];
- }
-}
-
static void
zyd_rx_data(struct zyd_softc *sc, const uint8_t *buf, uint16_t len)
{
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211_node *ni;
const struct zyd_plcphdr *plcp;
const struct zyd_rx_stat *stat;
struct mbuf *m;
- int rlen;
+ int rlen, rssi, nf;
if (len < ZYD_MIN_FRAGSZ) {
DPRINTF(("%s: frame too short (length=%d)\n",
@@ -1980,7 +1976,7 @@ zyd_rx_data(struct zyd_softc *sc, const uint8_t *buf, uint16_t len)
m->m_pkthdr.len = m->m_len = rlen;
bcopy((const uint8_t *)(plcp + 1), mtod(m, uint8_t *), rlen);
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct zyd_rx_radiotap_header *tap = &sc->sc_rxtap;
tap->wr_flags = 0;
@@ -1989,20 +1985,23 @@ zyd_rx_data(struct zyd_softc *sc, const uint8_t *buf, uint16_t len)
/* XXX toss, no way to express errors */
if (stat->flags & ZYD_RX_DECRYPTERR)
tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS;
- tap->wr_rate =
- zyd_plcp2ieee(plcp->signal, stat->flags & ZYD_RX_OFDM);
+ tap->wr_rate = ieee80211_plcp2rate(plcp->signal,
+ stat->flags & ZYD_RX_OFDM);
tap->wr_antsignal = stat->rssi + -95;
tap->wr_antnoise = -95; /* XXX */
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
}
- ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
- ieee80211_input(ic, m, ni,
- stat->rssi > 63 ? 127 : 2 * stat->rssi, -95/*XXX*/, 0);
+ rssi = stat->rssi > 63 ? 127 : 2 * stat->rssi;
+ nf = -95; /* XXX */
- /* node is no longer needed */
- ieee80211_free_node(ni);
+ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, rssi, nf, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, nf, 0);
}
static void
@@ -2067,7 +2066,8 @@ skip: /* setup a new transfer */
static int
zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
struct ifnet *ifp = sc->sc_ifp;
struct zyd_tx_desc *desc;
struct zyd_tx_data *data;
@@ -2085,7 +2085,7 @@ zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
wh = mtod(m0, struct ieee80211_frame *);
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -2106,7 +2106,7 @@ zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
desc->flags = ZYD_TX_FLAG_BACKOFF;
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
/* multicast frames are not sent at OFDM rates in 802.11b/g */
- if (totlen > ic->ic_rtsthreshold) {
+ if (totlen > vap->iv_rtsthreshold) {
desc->flags |= ZYD_TX_FLAG_RTS;
} else if (ZYD_RATE_IS_OFDM(rate) &&
(ic->ic_flags & IEEE80211_F_USEPROT)) {
@@ -2123,7 +2123,7 @@ zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
(IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL))
desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL);
- desc->phy = zyd_plcp_signal(rate);
+ desc->phy = ieee80211_rate2plcp(rate);
if (ZYD_RATE_IS_OFDM(rate)) {
desc->phy |= ZYD_TX_PHY_OFDM;
if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
@@ -2145,13 +2145,13 @@ zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
desc->plcp_service |= ZYD_PLCP_LENGEXT;
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct zyd_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
tap->wt_rate = rate;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
m_copydata(m0, 0, m0->m_pkthdr.len,
@@ -2200,7 +2200,8 @@ zyd_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
ni = data->ni;
/* update rate control statistics */
- ((struct zyd_node *)ni)->amn.amn_txcnt++;
+ ieee80211_amrr_tx_complete(&ZYD_NODE(ni)->amn,
+ IEEE80211_AMRR_SUCCESS, 0);
/*
* Do any tx complete callback. Note this must
@@ -2227,11 +2228,13 @@ zyd_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
static int
zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
struct ifnet *ifp = sc->sc_ifp;
struct zyd_tx_desc *desc;
struct zyd_tx_data *data;
struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
struct ieee80211_key *k;
int xferlen, totlen, rate;
uint16_t pktlen;
@@ -2242,17 +2245,19 @@ zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
desc = (struct zyd_tx_desc *)data->buf;
desc->flags = ZYD_TX_FLAG_BACKOFF;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
- rate = ic->ic_mcast_rate;
+ rate = tp->mcastrate;
desc->flags |= ZYD_TX_FLAG_MULTICAST;
- } else if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE)
- rate = ic->ic_fixed_rate;
- else
- rate = ni->ni_rates.rs_rates[ni->ni_txrate];
- rate &= IEEE80211_RATE_VAL;
+ } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ rate = tp->ucastrate;
+ } else {
+ (void) ieee80211_amrr_choose(ni, &ZYD_NODE(ni)->amn);
+ rate = ni->ni_txrate;
+ }
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -2273,7 +2278,7 @@ zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
/* multicast frames are not sent at OFDM rates in 802.11b/g */
- if (totlen > ic->ic_rtsthreshold) {
+ if (totlen > vap->iv_rtsthreshold) {
desc->flags |= ZYD_TX_FLAG_RTS;
} else if (ZYD_RATE_IS_OFDM(rate) &&
(ic->ic_flags & IEEE80211_F_USEPROT)) {
@@ -2289,7 +2294,7 @@ zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
(IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL))
desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL);
- desc->phy = zyd_plcp_signal(rate);
+ desc->phy = ieee80211_rate2plcp(rate);
if (ZYD_RATE_IS_OFDM(rate)) {
desc->phy |= ZYD_TX_PHY_OFDM;
if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
@@ -2311,7 +2316,7 @@ zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
desc->plcp_service |= ZYD_PLCP_LENGEXT;
}
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct zyd_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
@@ -2319,7 +2324,7 @@ zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
m_copydata(m0, 0, m0->m_pkthdr.len,
@@ -2348,81 +2353,81 @@ static void
zyd_start(struct ifnet *ifp)
{
struct zyd_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ether_header *eh;
struct ieee80211_node *ni;
- struct mbuf *m0;
+ struct mbuf *m;
for (;;) {
- IF_POLL(&ic->ic_mgtq, m0);
- if (m0 != NULL) {
- if (sc->tx_queued >= ZYD_TX_LIST_CNT) {
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- IF_DEQUEUE(&ic->ic_mgtq, m0);
-
- ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
- m0->m_pkthdr.rcvif = NULL;
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
- if (zyd_tx_mgt(sc, m0, ni) != 0)
- break;
- } else {
- if (ic->ic_state != IEEE80211_S_RUN)
- break;
- IFQ_POLL(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
- if (sc->tx_queued >= ZYD_TX_LIST_CNT) {
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- IFQ_DEQUEUE(&ifp->if_snd, m0);
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
-
- if (m0->m_len < sizeof(struct ether_header) &&
- !(m0 = m_pullup(m0, sizeof(struct ether_header))))
- continue;
-
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- continue;
- }
- if (bpf_peers_present(ifp->if_bpf))
- bpf_mtap(ifp->if_bpf, m0);
- if ((m0 = ieee80211_encap(ic, m0, ni)) == NULL) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- continue;
- }
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
- if (zyd_tx_data(sc, m0, ni) != 0) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- break;
- }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ if (sc->tx_queued >= ZYD_TX_LIST_CNT) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ continue;
+ }
+ if (zyd_tx_data(sc, m, ni) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
}
sc->tx_timer = 5;
- ic->ic_lastdata = ticks;
callout_reset(&sc->sc_watchdog_ch, hz, zyd_watchdog, sc);
}
}
+static int
+zyd_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct zyd_softc *sc = ifp->if_softc;
+
+ /* prevent management frames from being sent if we're not ready */
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return ENETDOWN;
+ }
+ if (sc->tx_queued >= ZYD_TX_LIST_CNT) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return ENOBUFS; /* XXX */
+ }
+
+ ifp->if_opackets++;
+
+ /*
+ * Legacy path; interpret frame contents to decide
+ * precisely how to send the frame.
+ * XXX raw path
+ */
+ if (zyd_tx_mgt(sc, m, ni) != 0)
+ goto bad;
+ sc->tx_timer = 5;
+ callout_reset(&sc->sc_watchdog_ch, hz, zyd_watchdog, sc);
+
+ return 0;
+bad:
+ ifp->if_oerrors++;
+ ieee80211_free_node(ni);
+ return EIO; /* XXX */
+}
+
static void
zyd_watchdog(void *arg)
{
struct zyd_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
if (sc->tx_timer > 0) {
if (--sc->tx_timer == 0) {
@@ -2439,11 +2444,11 @@ static int
zyd_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct zyd_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
ZYD_LOCK(sc);
-
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
@@ -2451,43 +2456,36 @@ zyd_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
if ((ifp->if_flags ^ sc->sc_if_flags) &
(IFF_ALLMULTI | IFF_PROMISC))
zyd_set_multi(sc);
- } else
- zyd_init(sc);
+ } else {
+ zyd_init_locked(sc);
+ startall = 1;
+ }
} else {
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
zyd_stop(sc, 1);
}
sc->sc_if_flags = ifp->if_flags;
break;
-
- case SIOCADDMULTI:
- case SIOCDELMULTI:
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- zyd_set_multi(sc);
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
break;
-
default:
- error = ieee80211_ioctl(ic, cmd, data);
- }
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) == IFF_UP &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) == IFF_DRV_RUNNING)
- zyd_init(sc);
- error = 0;
+ error = ether_ioctl(ifp, cmd, data);
+ break;
}
-
ZYD_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
static void
-zyd_init(void *priv)
+zyd_init_locked(struct zyd_softc *sc)
{
- struct zyd_softc *sc = priv;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
int i, error;
zyd_stop(sc, 0);
@@ -2569,13 +2567,6 @@ zyd_init(void *priv)
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
-
- if (ic->ic_opmode != IEEE80211_M_MONITOR) {
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-
return;
fail: zyd_stop(sc, 1);
@@ -2583,12 +2574,24 @@ fail: zyd_stop(sc, 1);
}
static void
-zyd_stop(struct zyd_softc *sc, int disable)
+zyd_init(void *priv)
{
+ struct zyd_softc *sc = priv;
struct ifnet *ifp = sc->sc_ifp;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1); /* free all nodes */
+ ZYD_LOCK(sc);
+ zyd_init_locked(sc);
+ ZYD_UNLOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vap's */
+}
+
+static void
+zyd_stop(struct zyd_softc *sc, int disable)
+{
+ struct ifnet *ifp = sc->sc_ifp;
sc->tx_timer = 0;
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
@@ -2661,43 +2664,11 @@ zyd_loadfirmware(struct zyd_softc *sc, u_char *fw, size_t size)
}
static void
-zyd_iter_func(void *arg, struct ieee80211_node *ni)
-{
- struct zyd_softc *sc = arg;
- struct zyd_node *zn = (struct zyd_node *)ni;
-
- ieee80211_amrr_choose(&sc->amrr, ni, &zn->amn);
-}
-
-static void
-zyd_amrr_timeout(void *arg)
-{
- struct zyd_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
-
- ZYD_LOCK(sc);
- if (ic->ic_opmode == IEEE80211_M_STA)
- zyd_iter_func(sc, ic->ic_bss);
- else
- ieee80211_iterate_nodes(&ic->ic_sta, zyd_iter_func, sc);
- ZYD_UNLOCK(sc);
-
- callout_reset(&sc->sc_amrr_ch, hz, zyd_amrr_timeout, sc);
-}
-
-static void
zyd_newassoc(struct ieee80211_node *ni, int isnew)
{
- struct zyd_softc *sc = ni->ni_ic->ic_ifp->if_softc;
- int i;
-
- ieee80211_amrr_node_init(&sc->amrr, &((struct zyd_node *)ni)->amn);
+ struct ieee80211vap *vap = ni->ni_vap;
- /* set rate to some reasonable initial value */
- for (i = ni->ni_rates.rs_nrates - 1;
- i > 0 && (ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) > 72;
- i--);
- ni->ni_txrate = i;
+ ieee80211_amrr_node_init(&ZYD_VAP(vap)->amrr, &ZYD_NODE(ni)->amn, ni);
}
static void
@@ -2740,18 +2711,20 @@ static void
zyd_scantask(void *arg)
{
struct zyd_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
ZYD_LOCK(sc);
switch (sc->sc_scan_action) {
case ZYD_SCAN_START:
+ /* want broadcast address while scanning */
zyd_set_bssid(sc, ifp->if_broadcastaddr);
break;
case ZYD_SCAN_END:
- zyd_set_bssid(sc, ic->ic_bss->ni_bssid);
+ /* restore previous bssid */
+ zyd_set_bssid(sc, sc->sc_bssid);
break;
case ZYD_SET_CHANNEL:
diff --git a/sys/dev/usb/if_zydreg.h b/sys/dev/usb/if_zydreg.h
index fc736ae..4d9c8fd 100644
--- a/sys/dev/usb/if_zydreg.h
+++ b/sys/dev/usb/if_zydreg.h
@@ -1122,6 +1122,7 @@ struct zyd_node {
struct ieee80211_node ni; /* must be the first */
struct ieee80211_amrr_node amn;
};
+#define ZYD_NODE(ni) ((struct zyd_node *)(ni))
struct zyd_rx_radiotap_header {
struct ieee80211_radiotap_header wr_ihdr;
@@ -1173,12 +1174,18 @@ struct rq {
STAILQ_ENTRY(rq) rq;
};
+struct zyd_vap {
+ struct ieee80211vap vap;
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ struct callout amrr_ch;
+ struct ieee80211_amrr amrr;
+};
+#define ZYD_VAP(vap) ((struct zyd_vap *)(vap))
+
struct zyd_softc {
device_t sc_dev;
struct ifnet *sc_ifp;
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
struct zyd_rf sc_rf;
struct usb_task sc_task;
@@ -1187,22 +1194,20 @@ struct zyd_softc {
#define ZYD_SCAN_START 0
#define ZYD_SCAN_END 1
#define ZYD_SET_CHANNEL 2
+ struct usb_task sc_mcasttask;
usbd_device_handle sc_udev;
usbd_interface_handle sc_iface;
int sc_flags;
int sc_if_flags;
#define ZD1211_FWLOADED (1 << 0)
-
+ uint8_t sc_bssid[IEEE80211_ADDR_LEN];
enum ieee80211_state sc_state;
int sc_arg;
struct mtx sc_mtx;
- struct callout sc_amrr_ch;
struct callout sc_watchdog_ch;
- struct ieee80211_amrr amrr;
-
STAILQ_HEAD(rqh, rq) sc_rqh;
uint16_t fwbase;
@@ -1233,8 +1238,6 @@ struct zyd_softc {
int tx_timer;
- struct bpf_if *sc_drvbpf;
-
struct zyd_rx_radiotap_header sc_rxtap;
int sc_rxtap_len;
diff --git a/sys/dev/wi/if_wavelan_ieee.h b/sys/dev/wi/if_wavelan_ieee.h
index 0a04bed..0061e63 100644
--- a/sys/dev/wi/if_wavelan_ieee.h
+++ b/sys/dev/wi/if_wavelan_ieee.h
@@ -241,10 +241,13 @@ struct wi_counters {
#define WI_RID_CNFAUTHMODE 0xFC2A
#define WI_RID_ROAMING_MODE 0xFC2D
#define WI_RID_OWN_BEACON_INT 0xFC33 /* beacon xmit time for BSS creation */
+#define WI_RID_ENH_SECURITY 0xFC43 /* enhanced security (AP mode) */
#define WI_RID_CNF_DBM_ADJUST 0xFC46
#define WI_RID_DBM_ADJUST 0xFC46 /* RSSI - WI_RID_DBM_ADJUST ~ dBm */
+#define WI_RID_WPA_DATA 0xFC48 /* WPA IE */
#define WI_RID_BASIC_RATE 0xFCB3
#define WI_RID_SUPPORT_RATE 0xFCB4
+#define WI_RID_WPA_HANDLING 0xFCBB /* WPA handling procedures */
/*
* Network parameters, dynamic configuration entities
diff --git a/sys/dev/wi/if_wi.c b/sys/dev/wi/if_wi.c
index 9ff16a4..d750ab1 100644
--- a/sys/dev/wi/if_wi.c
+++ b/sys/dev/wi/if_wi.c
@@ -1,5 +1,3 @@
-/* $NetBSD: wi.c,v 1.109 2003/01/09 08:52:19 dyoung Exp $ */
-
/*-
* Copyright (c) 1997, 1998, 1999
* Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
@@ -64,11 +62,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#define WI_HERMES_AUTOINC_WAR /* Work around data write autoinc bug. */
#define WI_HERMES_STATS_WAR /* Work around stats counter bug. */
-#define NBPFILTER 1
-
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/endian.h>
@@ -83,6 +78,7 @@ __FBSDID("$FreeBSD$");
#include <sys/random.h>
#include <sys/syslog.h>
#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
#include <machine/bus.h>
#include <machine/resource.h>
@@ -112,33 +108,41 @@ __FBSDID("$FreeBSD$");
#include <dev/wi/if_wireg.h>
#include <dev/wi/if_wivar.h>
+static struct ieee80211vap *wi_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void wi_vap_delete(struct ieee80211vap *vap);
+static void wi_stop_locked(struct wi_softc *sc, int disable);
static void wi_start_locked(struct ifnet *);
static void wi_start(struct ifnet *);
static int wi_start_tx(struct ifnet *ifp, struct wi_frame *frmhdr,
struct mbuf *m0);
static int wi_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
-static int wi_reset(struct ifnet *);
+static int wi_newstate_sta(struct ieee80211vap *, enum ieee80211_state, int);
+static int wi_newstate_hostap(struct ieee80211vap *, enum ieee80211_state, int);
+static void wi_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
+ int subtype, int rssi, int noise, u_int32_t rstamp);
+static int wi_reset(struct wi_softc *);
static void wi_watchdog(void *);
static int wi_ioctl(struct ifnet *, u_long, caddr_t);
-static int wi_media_change(struct ifnet *);
static void wi_media_status(struct ifnet *, struct ifmediareq *);
static void wi_rx_intr(struct wi_softc *);
static void wi_tx_intr(struct wi_softc *);
static void wi_tx_ex_intr(struct wi_softc *);
-static void wi_info_intr(struct wi_softc *);
-static int wi_key_alloc(struct ieee80211com *, const struct ieee80211_key *,
- ieee80211_keyix *, ieee80211_keyix *);
+static void wi_status_connected(void *, int);
+static void wi_status_disconnected(void *, int);
+static void wi_status_oor(void *, int);
+static void wi_status_assoc_failed(void *, int);
+static void wi_info_intr(struct wi_softc *);
-#if 0
-static int wi_get_cfg(struct ifnet *, u_long, caddr_t);
-static int wi_set_cfg(struct ifnet *, u_long, caddr_t);
-#endif
-static int wi_write_txrate(struct wi_softc *);
-static int wi_write_wep(struct wi_softc *);
+static int wi_write_txrate(struct wi_softc *, struct ieee80211vap *);
+static int wi_write_wep(struct wi_softc *, struct ieee80211vap *);
static int wi_write_multi(struct wi_softc *);
+static void wi_update_mcast(struct ifnet *);
static int wi_alloc_fid(struct wi_softc *, int, int *);
static void wi_read_nicid(struct wi_softc *);
static int wi_write_ssid(struct wi_softc *, int, u_int8_t *, int);
@@ -150,33 +154,11 @@ static int wi_write_bap(struct wi_softc *, int, int, void *, int);
static int wi_mwrite_bap(struct wi_softc *, int, int, struct mbuf *, int);
static int wi_read_rid(struct wi_softc *, int, void *, int *);
static int wi_write_rid(struct wi_softc *, int, void *, int);
-
-static int wi_newstate(struct ieee80211com *, enum ieee80211_state, int);
-
-static int wi_scan_ap(struct wi_softc *, u_int16_t, u_int16_t);
-static void wi_scan_result(struct wi_softc *, int, int);
-
-static void wi_dump_pkt(struct wi_frame *, struct ieee80211_node *, int rssi);
-
-#if 0
-static int wi_get_debug(struct wi_softc *, struct wi_req *);
-static int wi_set_debug(struct wi_softc *, struct wi_req *);
-#endif
-
-/* support to download firmware for symbol CF card */
-static int wi_symbol_write_firm(struct wi_softc *, const void *, int,
- const void *, int);
-static int wi_symbol_set_hcr(struct wi_softc *, int);
+static int wi_write_appie(struct wi_softc *, int, const struct ieee80211_appie *);
static void wi_scan_start(struct ieee80211com *);
-static void wi_scan_curchan(struct ieee80211com *, unsigned long);
-static void wi_scan_mindwell(struct ieee80211com *);
static void wi_scan_end(struct ieee80211com *);
static void wi_set_channel(struct ieee80211com *);
-static void wi_update_slot(struct ifnet *);
-static struct ieee80211_node *wi_node_alloc(struct ieee80211_node_table *);
-static int wi_ioctl_get(struct ifnet *ifp, u_long command, caddr_t data);
-static int wi_ioctl_set(struct ifnet *ifp, u_long command, caddr_t data);
static __inline int
wi_write_val(struct wi_softc *sc, int rid, u_int16_t val)
@@ -199,15 +181,9 @@ SYSCTL_INT(_hw_wi, OID_AUTO, txerate, CTLFLAG_RW, &wi_txerate,
static int wi_debug = 0;
SYSCTL_INT(_hw_wi, OID_AUTO, debug, CTLFLAG_RW, &wi_debug,
0, "control debugging printfs");
-
#define DPRINTF(X) if (wi_debug) printf X
-#define DPRINTF2(X) if (wi_debug > 1) printf X
-#define IFF_DUMPPKTS(_ifp) \
- (((_ifp)->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2))
#else
#define DPRINTF(X)
-#define DPRINTF2(X)
-#define IFF_DUMPPKTS(_ifp) 0
#endif
#define WI_INTRS (WI_EV_RX | WI_EV_ALLOC | WI_EV_INFO)
@@ -255,7 +231,7 @@ int
wi_attach(device_t dev)
{
struct wi_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
struct ifnet *ifp;
int i, nrates, buflen;
u_int16_t val;
@@ -266,13 +242,13 @@ wi_attach(device_t dev)
};
int error;
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(dev, "can not if_alloc\n");
wi_free(dev);
- return (ENOSPC);
+ return ENOSPC;
}
- ifp->if_softc = sc;
+ ic = ifp->if_l2com;
/*
* NB: no locking is needed here; don't put it here
@@ -284,18 +260,40 @@ wi_attach(device_t dev)
if (error) {
device_printf(dev, "bus_setup_intr() failed! (%d)\n", error);
wi_free(dev);
- return (error);
+ return error;
}
- mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
- MTX_DEF | MTX_RECURSE);
- callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0);
-
sc->sc_firmware_type = WI_NOTYPE;
sc->wi_cmd_count = 500;
/* Reset the NIC. */
- if (wi_reset(ifp) != 0)
+ if (wi_reset(sc) != 0) {
+ wi_free(dev);
return ENXIO; /* XXX */
+ }
+
+ /* Read NIC identification */
+ wi_read_nicid(sc);
+ switch (sc->sc_firmware_type) {
+ case WI_LUCENT:
+ if (sc->sc_sta_firmware_ver < 60006)
+ goto reject;
+ break;
+ case WI_INTERSIL:
+ if (sc->sc_sta_firmware_ver < 800)
+ goto reject;
+ break;
+ default:
+ reject:
+ device_printf(dev, "Sorry, this card is not supported "
+ "(type %d, firmware ver %d)\n",
+ sc->sc_firmware_type, sc->sc_sta_firmware_ver);
+ wi_free(dev);
+ return EOPNOTSUPP;
+ }
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
+ MTX_DEF | MTX_RECURSE);
+ callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0);
/*
* Read the station address.
@@ -320,9 +318,7 @@ wi_attach(device_t dev)
return (error);
}
- /* Read NIC identification */
- wi_read_nicid(sc);
-
+ ifp->if_softc = sc;
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_ioctl = wi_ioctl;
@@ -335,11 +331,9 @@ wi_attach(device_t dev)
ic->ic_ifp = ifp;
ic->ic_phytype = IEEE80211_T_DS;
ic->ic_opmode = IEEE80211_M_STA;
- ic->ic_state = IEEE80211_S_INIT;
ic->ic_caps = IEEE80211_C_PMGT
- | IEEE80211_C_WEP /* everyone supports WEP */
+ | IEEE80211_C_MONITOR
;
- ic->ic_max_aid = WI_MAX_AID;
/*
* Query the card for available channels and setup the
@@ -360,28 +354,7 @@ wi_attach(device_t dev)
c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_B);
c->ic_flags = IEEE80211_CHAN_B;
c->ic_ieee = i;
- }
-
- /*
- * Read the default channel from the NIC. This may vary
- * depending on the country where the NIC was purchased, so
- * we can't hard-code a default and expect it to work for
- * everyone.
- *
- * If no channel is specified, let the 802.11 code select.
- */
- buflen = sizeof(val);
- if (wi_read_rid(sc, WI_RID_OWN_CHNL, &val, &buflen) == 0) {
- val = le16toh(val);
- ic->ic_bsschan = ieee80211_find_channel(ic,
- ieee80211_ieee2mhz(val, IEEE80211_CHAN_B),
- IEEE80211_CHAN_B);
- if (ic->ic_bsschan == NULL)
- ic->ic_bsschan = &ic->ic_channels[0];
- } else {
- device_printf(dev,
- "WI_RID_OWN_CHNL failed, using first channel!\n");
- ic->ic_bsschan = &ic->ic_channels[0];
+ /* XXX txpowers? */
}
/*
@@ -390,31 +363,18 @@ wi_attach(device_t dev)
switch (sc->sc_firmware_type) {
case WI_LUCENT:
sc->sc_ntxbuf = 1;
- sc->sc_flags |= WI_FLAGS_HAS_SYSSCALE;
-#ifdef WI_HERMES_AUTOINC_WAR
- /* XXX: not confirmed, but never seen for recent firmware */
- if (sc->sc_sta_firmware_ver < 40000) {
- sc->sc_flags |= WI_FLAGS_BUG_AUTOINC;
- }
-#endif
- if (sc->sc_sta_firmware_ver >= 60000)
- sc->sc_flags |= WI_FLAGS_HAS_MOR;
- if (sc->sc_sta_firmware_ver >= 60006) {
- ic->ic_caps |= IEEE80211_C_IBSS;
- ic->ic_caps |= IEEE80211_C_MONITOR;
- }
- sc->sc_ibss_port = htole16(1);
+ ic->ic_caps |= IEEE80211_C_IBSS;
+ sc->sc_ibss_port = WI_PORTTYPE_BSS;
+ sc->sc_monitor_port = WI_PORTTYPE_ADHOC;
sc->sc_min_rssi = WI_LUCENT_MIN_RSSI;
sc->sc_max_rssi = WI_LUCENT_MAX_RSSI;
sc->sc_dbm_offset = WI_LUCENT_DBM_OFFSET;
break;
-
case WI_INTERSIL:
sc->sc_ntxbuf = WI_NTXBUF;
- sc->sc_flags |= WI_FLAGS_HAS_FRAGTHR;
- sc->sc_flags |= WI_FLAGS_HAS_ROAMING;
- sc->sc_flags |= WI_FLAGS_HAS_SYSSCALE;
+ sc->sc_flags |= WI_FLAGS_HAS_FRAGTHR
+ | WI_FLAGS_HAS_ROAMING;
/*
* Old firmware are slow, so give peace a chance.
*/
@@ -422,31 +382,26 @@ wi_attach(device_t dev)
sc->wi_cmd_count = 5000;
if (sc->sc_sta_firmware_ver > 10101)
sc->sc_flags |= WI_FLAGS_HAS_DBMADJUST;
- if (sc->sc_sta_firmware_ver >= 800) {
- ic->ic_caps |= IEEE80211_C_IBSS;
- ic->ic_caps |= IEEE80211_C_MONITOR;
- }
+ ic->ic_caps |= IEEE80211_C_IBSS;
/*
* version 0.8.3 and newer are the only ones that are known
* to currently work. Earlier versions can be made to work,
- * at least according to the Linux driver.
+ * at least according to the Linux driver but we require
+ * monitor mode so this is irrelevant.
*/
- if (sc->sc_sta_firmware_ver >= 803)
- ic->ic_caps |= IEEE80211_C_HOSTAP;
- sc->sc_ibss_port = htole16(0);
-
- sc->sc_min_rssi = WI_PRISM_MIN_RSSI;
- sc->sc_max_rssi = WI_PRISM_MAX_RSSI;
- sc->sc_dbm_offset = WI_PRISM_DBM_OFFSET;
- break;
-
- case WI_SYMBOL:
- sc->sc_ntxbuf = 1;
- sc->sc_flags |= WI_FLAGS_HAS_DIVERSITY;
- if (sc->sc_sta_firmware_ver >= 25000)
- ic->ic_caps |= IEEE80211_C_IBSS;
- sc->sc_ibss_port = htole16(4);
+ ic->ic_caps |= IEEE80211_C_HOSTAP;
+ if (sc->sc_sta_firmware_ver >= 10603)
+ sc->sc_flags |= WI_FLAGS_HAS_ENHSECURITY;
+ if (sc->sc_sta_firmware_ver >= 10700) {
+ /*
+ * 1.7.0+ have the necessary support for sta mode WPA.
+ */
+ sc->sc_flags |= WI_FLAGS_HAS_WPASUPPORT;
+ ic->ic_caps |= IEEE80211_C_WPA;
+ }
+ sc->sc_ibss_port = WI_PORTTYPE_IBSS;
+ sc->sc_monitor_port = WI_PORTTYPE_APSILENT;
sc->sc_min_rssi = WI_PRISM_MIN_RSSI;
sc->sc_max_rssi = WI_PRISM_MAX_RSSI;
sc->sc_dbm_offset = WI_PRISM_DBM_OFFSET;
@@ -459,7 +414,7 @@ wi_attach(device_t dev)
buflen = sizeof(val);
if (wi_read_rid(sc, WI_RID_WEP_AVAIL, &val, &buflen) == 0 &&
val != htole16(0))
- ic->ic_caps |= IEEE80211_C_WEP;
+ ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP;
/* Find supported rates. */
buflen = sizeof(ratebuf);
@@ -482,48 +437,21 @@ wi_attach(device_t dev)
sc->sc_dbm_offset = le16toh(val);
}
- sc->sc_max_datalen = 2304;
- sc->sc_system_scale = 1;
- sc->sc_cnfauthmode = IEEE80211_AUTH_OPEN;
- sc->sc_roaming_mode = 1;
- sc->wi_channel = IEEE80211_CHAN_ANYC;
sc->sc_portnum = WI_DEFAULT_PORT;
- sc->sc_authtype = WI_DEFAULT_AUTHTYPE;
-
- bzero(sc->sc_nodename, sizeof(sc->sc_nodename));
- sc->sc_nodelen = sizeof(WI_DEFAULT_NODENAME) - 1;
- bcopy(WI_DEFAULT_NODENAME, sc->sc_nodename, sc->sc_nodelen);
+ TASK_INIT(&sc->sc_oor_task, 0, wi_status_oor, ic);
- bzero(sc->sc_net_name, sizeof(sc->sc_net_name));
- bcopy(WI_DEFAULT_NETNAME, sc->sc_net_name,
- sizeof(WI_DEFAULT_NETNAME) - 1);
-
- /*
- * Call MI attach routine.
- */
ieee80211_ifattach(ic);
- /* override state transition method */
- sc->sc_newstate = ic->ic_newstate;
- sc->sc_key_alloc = ic->ic_crypto.cs_key_alloc;
- ic->ic_crypto.cs_key_alloc = wi_key_alloc;
- ic->ic_newstate = wi_newstate;
ic->ic_raw_xmit = wi_raw_xmit;
-
ic->ic_scan_start = wi_scan_start;
- ic->ic_scan_curchan = wi_scan_curchan;
- ic->ic_scan_mindwell = wi_scan_mindwell;
ic->ic_scan_end = wi_scan_end;
ic->ic_set_channel = wi_set_channel;
- ic->ic_node_alloc = wi_node_alloc;
- ic->ic_updateslot = wi_update_slot;
- ic->ic_reset = wi_reset;
- ieee80211_media_init(ic, wi_media_change, wi_media_status);
+ ic->ic_vap_create = wi_vap_create;
+ ic->ic_vap_delete = wi_vap_delete;
+ ic->ic_update_mcast = wi_update_mcast;
-#if NBPFILTER > 0
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof(struct ieee80211_frame) + sizeof(sc->sc_tx_th),
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof(struct ieee80211_frame) + sizeof(sc->sc_tx_th));
/*
* Initialize constant fields.
* XXX make header lengths a multiple of 32-bits so subsequent
@@ -540,7 +468,6 @@ wi_attach(device_t dev)
sc->sc_rx_th_len = roundup(sizeof(sc->sc_rx_th), sizeof(u_int32_t));
sc->sc_rx_th.wr_ihdr.it_len = htole16(sc->sc_rx_th_len);
sc->sc_rx_th.wr_ihdr.it_present = htole32(WI_RX_RADIOTAP_PRESENT);
-#endif
if (bootverbose)
ieee80211_announce(ic);
@@ -553,19 +480,18 @@ wi_detach(device_t dev)
{
struct wi_softc *sc = device_get_softc(dev);
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
WI_LOCK(sc);
/* check if device was removed */
sc->wi_gone |= !bus_child_present(dev);
- wi_stop(ifp, 0);
+ wi_stop_locked(sc, 0);
WI_UNLOCK(sc);
-
-#if NBPFILTER > 0
bpfdetach(ifp);
-#endif
- ieee80211_ifdetach(&sc->sc_ic);
+ ieee80211_ifdetach(ic);
+
bus_teardown_intr(dev, sc->irq, sc->wi_intrhand);
if_free(sc->sc_ifp);
wi_free(dev);
@@ -573,60 +499,82 @@ wi_detach(device_t dev)
return (0);
}
-#ifdef __NetBSD__
-int
-wi_activate(struct device *self, enum devact act)
+static struct ieee80211vap *
+wi_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
{
- struct wi_softc *sc = (struct wi_softc *)self;
- int rv = 0, s;
+ struct wi_softc *sc = ic->ic_ifp->if_softc;
+ struct wi_vap *wvp;
+ struct ieee80211vap *vap;
- s = splnet();
- switch (act) {
- case DVACT_ACTIVATE:
- rv = EOPNOTSUPP;
- break;
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ wvp = (struct wi_vap *) malloc(sizeof(struct wi_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (wvp == NULL)
+ return NULL;
- case DVACT_DEACTIVATE:
- if_deactivate(sc->sc_ifp);
- break;
- }
- splx(s);
- return rv;
-}
+ vap = &wvp->wv_vap;
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
-void
-wi_power(struct wi_softc *sc, int why)
-{
- struct ifnet *ifp = sc->sc_ifp;
- int s;
+ vap->iv_max_aid = WI_MAX_AID;
- s = splnet();
- switch (why) {
- case PWR_SUSPEND:
- case PWR_STANDBY:
- wi_stop(ifp, 1);
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ sc->sc_porttype = WI_PORTTYPE_BSS;
+ wvp->wv_newstate = vap->iv_newstate;
+ vap->iv_newstate = wi_newstate_sta;
+ /* need to filter mgt frames to avoid confusing state machine */
+ wvp->wv_recv_mgmt = vap->iv_recv_mgmt;
+ vap->iv_recv_mgmt = wi_recv_mgmt;
break;
- case PWR_RESUME:
- if (ifp->if_flags & IFF_UP) {
- wi_init(sc);
- (void)wi_intr(sc);
- }
+ case IEEE80211_M_IBSS:
+ sc->sc_porttype = sc->sc_ibss_port;
+ wvp->wv_newstate = vap->iv_newstate;
+ vap->iv_newstate = wi_newstate_sta;
+ break;
+ case IEEE80211_M_AHDEMO:
+ sc->sc_porttype = WI_PORTTYPE_ADHOC;
+ break;
+ case IEEE80211_M_HOSTAP:
+ sc->sc_porttype = WI_PORTTYPE_HOSTAP;
+ wvp->wv_newstate = vap->iv_newstate;
+ vap->iv_newstate = wi_newstate_hostap;
+ break;
+ case IEEE80211_M_MONITOR:
+ sc->sc_porttype = sc->sc_monitor_port;
break;
- case PWR_SOFTSUSPEND:
- case PWR_SOFTSTANDBY:
- case PWR_SOFTRESUME:
+ default:
break;
}
- splx(s);
+
+ TASK_INIT(&wvp->wv_connected_task, 0, wi_status_connected, vap);
+ TASK_INIT(&wvp->wv_disconnected_task, 0, wi_status_disconnected, vap);
+ TASK_INIT(&wvp->wv_assoc_failed_task, 0, wi_status_assoc_failed, vap);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, wi_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+wi_vap_delete(struct ieee80211vap *vap)
+{
+ struct wi_vap *wvp = WI_VAP(vap);
+
+ ieee80211_vap_detach(vap);
+ free(wvp, M_80211_VAP);
}
-#endif /* __NetBSD__ */
void
wi_shutdown(device_t dev)
{
struct wi_softc *sc = device_get_softc(dev);
- wi_stop(sc->sc_ifp, 1);
+ wi_stop(sc, 1);
}
void
@@ -658,7 +606,6 @@ wi_intr(void *arg)
if (status & WI_EV_INFO)
wi_info_intr(sc);
if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0 &&
- (sc->sc_flags & WI_FLAGS_OUTRANGE) == 0 &&
!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
wi_start_locked(ifp);
@@ -670,257 +617,344 @@ wi_intr(void *arg)
return;
}
+static void
+wi_enable(struct wi_softc *sc)
+{
+ /* Enable interrupts */
+ CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
+
+ /* enable port */
+ wi_cmd(sc, WI_CMD_ENABLE | sc->sc_portnum, 0, 0, 0);
+ sc->sc_enabled = 1;
+}
+
+static int
+wi_setup_locked(struct wi_softc *sc, int porttype, int mode,
+ uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ int i;
+
+ wi_reset(sc);
+
+ wi_write_val(sc, WI_RID_PORTTYPE, porttype);
+ wi_write_val(sc, WI_RID_CREATE_IBSS, mode);
+ wi_write_val(sc, WI_RID_MAX_DATALEN, 2304);
+ /* XXX IEEE80211_BPF_NOACK wants 0 */
+ wi_write_val(sc, WI_RID_ALT_RETRY_CNT, 2);
+ if (sc->sc_flags & WI_FLAGS_HAS_ROAMING)
+ wi_write_val(sc, WI_RID_ROAMING_MODE, 3); /* NB: disabled */
+
+ wi_write_rid(sc, WI_RID_MAC_NODE, mac, IEEE80211_ADDR_LEN);
+
+ /* Allocate fids for the card */
+ sc->sc_buflen = IEEE80211_MAX_LEN + sizeof(struct wi_frame);
+ for (i = 0; i < sc->sc_ntxbuf; i++) {
+ int error = wi_alloc_fid(sc, sc->sc_buflen,
+ &sc->sc_txd[i].d_fid);
+ if (error) {
+ device_printf(sc->sc_dev,
+ "tx buffer allocation failed (error %u)\n",
+ error);
+ return error;
+ }
+ sc->sc_txd[i].d_len = 0;
+ }
+ sc->sc_txcur = sc->sc_txnext = 0;
+
+ return 0;
+}
+
+static void
+wi_init_locked(struct wi_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ int wasenabled;
+
+ WI_LOCK_ASSERT(sc);
+
+ wasenabled = sc->sc_enabled;
+ if (wasenabled)
+ wi_stop_locked(sc, 1);
+
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
+ if (wi_setup_locked(sc, sc->sc_porttype, 3, ic->ic_myaddr) != 0) {
+ if_printf(ifp, "interface not running\n");
+ wi_stop_locked(sc, 1);
+ return;
+ }
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ callout_reset(&sc->sc_watchdog, hz, wi_watchdog, sc);
+
+ wi_enable(sc); /* Enable desired port */
+}
+
void
wi_init(void *arg)
{
struct wi_softc *sc = arg;
struct ifnet *ifp = sc->sc_ifp;
- struct ieee80211com *ic = &sc->sc_ic;
- struct wi_joinreq join;
- struct ieee80211_channel *chan;
- int i;
- int error = 0, wasenabled;
+ struct ieee80211com *ic = ifp->if_l2com;
+ WI_LOCK(sc);
+ wi_init_locked(sc);
+ WI_UNLOCK(sc);
+ ieee80211_start_all(ic);
+}
- if (sc->wi_gone)
- return;
+static void
+wi_stop_locked(struct wi_softc *sc, int disable)
+{
+ struct ifnet *ifp = sc->sc_ifp;
- if ((wasenabled = sc->sc_enabled))
- wi_stop(ifp, 1);
+ WI_LOCK_ASSERT(sc);
- WI_LOCK(sc);
- wi_reset(ifp);
+ if (sc->sc_enabled && !sc->wi_gone) {
+ CSR_WRITE_2(sc, WI_INT_EN, 0);
+ wi_cmd(sc, WI_CMD_DISABLE | sc->sc_portnum, 0, 0, 0);
+ if (disable)
+ sc->sc_enabled = 0;
+ } else if (sc->wi_gone && disable) /* gone --> not enabled */
+ sc->sc_enabled = 0;
- /* common 802.11 configuration */
- ic->ic_flags &= ~IEEE80211_F_IBSSON;
- sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_BSS);
- break;
- case IEEE80211_M_IBSS:
- wi_write_val(sc, WI_RID_PORTTYPE, sc->sc_ibss_port);
- ic->ic_flags |= IEEE80211_F_IBSSON;
- break;
- case IEEE80211_M_AHDEMO:
- wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_ADHOC);
- break;
- case IEEE80211_M_HOSTAP:
- /*
- * For PRISM cards, override the empty SSID, because in
- * HostAP mode the controller will lock up otherwise.
- */
- if (sc->sc_firmware_type == WI_INTERSIL &&
- ic->ic_des_ssid[0].len == 0) {
- ic->ic_des_ssid[0].ssid[0] = ' ';
- ic->ic_des_ssid[0].len = 1;
- }
- wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_HOSTAP);
- break;
- case IEEE80211_M_MONITOR:
- switch (sc->sc_firmware_type) {
- case WI_LUCENT:
- wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_ADHOC);
- break;
-
- case WI_INTERSIL:
- wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_APSILENT);
- break;
- }
+ callout_stop(&sc->sc_watchdog);
+ sc->sc_tx_timer = 0;
+ sc->sc_false_syns = 0;
- wi_cmd(sc, WI_CMD_DEBUG | (WI_TEST_MONITOR << 8), 0, 0, 0);
- break;
- case IEEE80211_M_WDS:
- /* XXXX */
- break;
- }
+ ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE | IFF_DRV_RUNNING);
+}
- /* Intersil interprets this RID as joining ESS even in IBSS mode */
- if (sc->sc_firmware_type == WI_LUCENT &&
- (ic->ic_flags & IEEE80211_F_IBSSON) && ic->ic_des_ssid[0].len > 0)
- wi_write_val(sc, WI_RID_CREATE_IBSS, 1);
- else
- wi_write_val(sc, WI_RID_CREATE_IBSS, 0);
- wi_write_val(sc, WI_RID_MAX_SLEEP, ic->ic_lintval);
- wi_write_ssid(sc, WI_RID_DESIRED_SSID, ic->ic_des_ssid[0].ssid,
- ic->ic_des_ssid[0].len);
- wi_write_val(sc, WI_RID_OWN_CHNL,
- ieee80211_chan2ieee(ic, ic->ic_bsschan));
- wi_write_ssid(sc, WI_RID_OWN_SSID, ic->ic_des_ssid[0].ssid,
- ic->ic_des_ssid[0].len);
+void
+wi_stop(struct wi_softc *sc, int disable)
+{
+ WI_LOCK(sc);
+ wi_stop_locked(sc, disable);
+ WI_UNLOCK(sc);
+}
- IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
- wi_write_rid(sc, WI_RID_MAC_NODE, ic->ic_myaddr, IEEE80211_ADDR_LEN);
+static void
+wi_set_channel(struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct wi_softc *sc = ifp->if_softc;
- if (ic->ic_caps & IEEE80211_C_PMGT)
- wi_write_val(sc, WI_RID_PM_ENABLED,
- (ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0);
+ DPRINTF(("%s: channel %d, %sscanning\n", __func__,
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ ic->ic_flags & IEEE80211_F_SCAN ? "" : "!"));
- /* not yet common 802.11 configuration */
- wi_write_val(sc, WI_RID_MAX_DATALEN, sc->sc_max_datalen);
- wi_write_val(sc, WI_RID_RTS_THRESH, ic->ic_rtsthreshold);
- if (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR)
- wi_write_val(sc, WI_RID_FRAG_THRESH, ic->ic_fragthreshold);
+ WI_LOCK(sc);
+ wi_write_val(sc, WI_RID_OWN_CHNL,
+ ieee80211_chan2ieee(ic, ic->ic_curchan));
- /* driver specific 802.11 configuration */
- if (sc->sc_flags & WI_FLAGS_HAS_SYSSCALE)
- wi_write_val(sc, WI_RID_SYSTEM_SCALE, sc->sc_system_scale);
- if (sc->sc_flags & WI_FLAGS_HAS_ROAMING)
- wi_write_val(sc, WI_RID_ROAMING_MODE, sc->sc_roaming_mode);
- if (sc->sc_flags & WI_FLAGS_HAS_MOR)
- wi_write_val(sc, WI_RID_MICROWAVE_OVEN, sc->sc_microwave_oven);
- wi_write_txrate(sc);
- wi_write_ssid(sc, WI_RID_NODENAME, sc->sc_nodename, sc->sc_nodelen);
- wi_write_val(sc, WI_RID_ALT_RETRY_CNT, 0); /* for IEEE80211_BPF_NOACK */
+ sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq =
+ htole16(ic->ic_curchan->ic_freq);
+ sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags =
+ htole16(ic->ic_curchan->ic_flags);
+ WI_UNLOCK(sc);
+}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
- sc->sc_firmware_type == WI_INTERSIL) {
- wi_write_val(sc, WI_RID_OWN_BEACON_INT, ic->ic_bintval);
- wi_write_val(sc, WI_RID_BASIC_RATE, 0x03); /* 1, 2 */
- wi_write_val(sc, WI_RID_SUPPORT_RATE, 0x0f); /* 1, 2, 5.5, 11 */
- wi_write_val(sc, WI_RID_DTIM_PERIOD, ic->ic_dtim_period);
- }
+static void
+wi_scan_start(struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct wi_softc *sc = ifp->if_softc;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+ DPRINTF(("%s\n", __func__));
+
+ WI_LOCK(sc);
/*
- * Initialize promisc mode.
- * Being in the Host-AP mode causes a great
- * deal of pain if primisc mode is set.
- * Therefore we avoid confusing the firmware
- * and always reset promisc mode in Host-AP
- * mode. Host-AP sees all the packets anyway.
+ * Switch device to monitor mode.
*/
- if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
- (ifp->if_flags & IFF_PROMISC) != 0) {
- wi_write_val(sc, WI_RID_PROMISC, 1);
- } else {
- wi_write_val(sc, WI_RID_PROMISC, 0);
+ wi_write_val(sc, WI_RID_PORTTYPE, sc->sc_monitor_port);
+ if (sc->sc_firmware_type == WI_INTERSIL) {
+ wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0);
+ wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0);
}
+ /* force full dwell time to compensate for firmware overhead */
+ ss->ss_mindwell = ss->ss_maxdwell = msecs_to_ticks(400);
+ WI_UNLOCK(sc);
- /* Configure WEP. */
- if (ic->ic_caps & IEEE80211_C_WEP) {
- sc->sc_cnfauthmode = ic->ic_bss->ni_authmode;
- wi_write_wep(sc);
- } else
- sc->sc_encryption = 0;
+}
+
+static void
+wi_scan_end(struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct wi_softc *sc = ifp->if_softc;
- /* Set multicast filter. */
- wi_write_multi(sc);
+ DPRINTF(("%s: restore port type %d\n", __func__, sc->sc_porttype));
- /* Allocate fids for the card */
- if (sc->sc_firmware_type != WI_SYMBOL || !wasenabled) {
- sc->sc_buflen = IEEE80211_MAX_LEN + sizeof(struct wi_frame);
- if (sc->sc_firmware_type == WI_SYMBOL)
- sc->sc_buflen = 1585; /* XXX */
- for (i = 0; i < sc->sc_ntxbuf; i++) {
- error = wi_alloc_fid(sc, sc->sc_buflen,
- &sc->sc_txd[i].d_fid);
- if (error) {
- device_printf(sc->sc_dev,
- "tx buffer allocation failed (error %u)\n",
- error);
- goto out;
- }
- sc->sc_txd[i].d_len = 0;
- }
+ WI_LOCK(sc);
+ wi_write_val(sc, WI_RID_PORTTYPE, sc->sc_porttype);
+ if (sc->sc_firmware_type == WI_INTERSIL) {
+ wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0);
+ wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0);
}
- sc->sc_txcur = sc->sc_txnext = 0;
+ WI_UNLOCK(sc);
+}
- /* Enable desired port */
- wi_cmd(sc, WI_CMD_ENABLE | sc->sc_portnum, 0, 0, 0);
+static void
+wi_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
+ int subtype, int rssi, int noise, u_int32_t rstamp)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
- sc->sc_enabled = 1;
- ifp->if_drv_flags |= IFF_DRV_RUNNING;
- ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- if (ic->ic_opmode == IEEE80211_M_AHDEMO ||
- ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_MONITOR ||
- ic->ic_opmode == IEEE80211_M_HOSTAP) {
- chan = (sc->wi_channel == IEEE80211_CHAN_ANYC) ?
- ic->ic_curchan : sc->wi_channel;
- ieee80211_create_ibss(ic, chan);
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ /* NB: filter frames that trigger state changes */
+ return;
}
- /* Enable interrupts */
- CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS);
+ WI_VAP(vap)->wv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
+}
- if (!wasenabled &&
- ic->ic_opmode == IEEE80211_M_HOSTAP &&
- sc->sc_firmware_type == WI_INTERSIL) {
- /* XXX: some card need to be re-enabled for hostap */
- wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0);
- wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0);
- }
+static int
+wi_newstate_sta(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_node *bss;
+ struct wi_softc *sc = ifp->if_softc;
+
+ DPRINTF(("%s: %s -> %s\n", __func__,
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate]));
+
+ if (nstate == IEEE80211_S_AUTH) {
+ WI_LOCK(sc);
+ wi_setup_locked(sc, WI_PORTTYPE_BSS, 3, vap->iv_myaddr);
+
+ if (vap->iv_flags & IEEE80211_F_PMGTON) {
+ wi_write_val(sc, WI_RID_MAX_SLEEP, ic->ic_lintval);
+ wi_write_val(sc, WI_RID_PM_ENABLED, 1);
+ }
+ wi_write_val(sc, WI_RID_RTS_THRESH, vap->iv_rtsthreshold);
+ if (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR)
+ wi_write_val(sc, WI_RID_FRAG_THRESH,
+ vap->iv_fragthreshold);
+ wi_write_txrate(sc, vap);
+
+ bss = vap->iv_bss;
+ wi_write_ssid(sc, WI_RID_DESIRED_SSID, bss->ni_essid, bss->ni_esslen);
+ wi_write_val(sc, WI_RID_OWN_CHNL,
+ ieee80211_chan2ieee(ic, bss->ni_chan));
+
+ /* Configure WEP. */
+ if (ic->ic_cryptocaps & IEEE80211_CRYPTO_WEP)
+ wi_write_wep(sc, vap);
+ else
+ sc->sc_encryption = 0;
+
+ if ((sc->sc_flags & WI_FLAGS_HAS_WPASUPPORT) &&
+ (vap->iv_flags & IEEE80211_F_WPA)) {
+ wi_write_val(sc, WI_RID_WPA_HANDLING, 1);
+ if (vap->iv_appie_wpa != NULL)
+ wi_write_appie(sc, WI_RID_WPA_DATA,
+ vap->iv_appie_wpa);
+ }
+
+ wi_enable(sc); /* enable port */
- if (ic->ic_opmode == IEEE80211_M_STA &&
- ((ic->ic_flags & IEEE80211_F_DESBSSID) ||
- ic->ic_des_chan != IEEE80211_CHAN_ANYC)) {
- memset(&join, 0, sizeof(join));
- if (ic->ic_flags & IEEE80211_F_DESBSSID)
- IEEE80211_ADDR_COPY(&join.wi_bssid, ic->ic_des_bssid);
- if (ic->ic_des_chan != IEEE80211_CHAN_ANYC)
- join.wi_chan = htole16(
- ieee80211_chan2ieee(ic, ic->ic_des_chan));
/* Lucent firmware does not support the JOIN RID. */
- if (sc->sc_firmware_type != WI_LUCENT)
- wi_write_rid(sc, WI_RID_JOIN_REQ, &join, sizeof(join));
- }
+ if (sc->sc_firmware_type == WI_INTERSIL) {
+ struct wi_joinreq join;
- callout_reset(&sc->sc_watchdog, hz, wi_watchdog, sc);
+ memset(&join, 0, sizeof(join));
+ IEEE80211_ADDR_COPY(&join.wi_bssid, bss->ni_bssid);
+ join.wi_chan = htole16(
+ ieee80211_chan2ieee(ic, bss->ni_chan));
+ wi_write_rid(sc, WI_RID_JOIN_REQ, &join, sizeof(join));
+ }
+ WI_UNLOCK(sc);
- WI_UNLOCK(sc);
- return;
-out:
- if (error) {
- if_printf(ifp, "interface not running\n");
- wi_stop(ifp, 1);
+ /*
+ * NB: don't go through 802.11 layer, it'll send auth frame;
+ * instead we drive the state machine from the link status
+ * notification we get on association.
+ */
+ vap->iv_state = nstate;
+ return EINPROGRESS;
}
- WI_UNLOCK(sc);
- DPRINTF(("wi_init: return %d\n", error));
- return;
+ return WI_VAP(vap)->wv_newstate(vap, nstate, arg);
}
-void
-wi_stop(struct ifnet *ifp, int disable)
+static int
+wi_newstate_hostap(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_node *bss;
struct wi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ int error;
+
+ DPRINTF(("%s: %s -> %s\n", __func__,
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate]));
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+ error = WI_VAP(vap)->wv_newstate(vap, nstate, arg);
+ if (error == 0 && nstate == IEEE80211_S_RUN) {
+ WI_LOCK(sc);
+ wi_setup_locked(sc, WI_PORTTYPE_HOSTAP, 0, vap->iv_myaddr);
- DELAY(100000);
- WI_LOCK(sc);
- if (sc->sc_enabled && !sc->wi_gone) {
- CSR_WRITE_2(sc, WI_INT_EN, 0);
- wi_cmd(sc, WI_CMD_DISABLE | sc->sc_portnum, 0, 0, 0);
- if (disable) {
-#ifdef __NetBSD__
- if (sc->sc_disable)
- (*sc->sc_disable)(sc);
-#endif
- sc->sc_enabled = 0;
+ bss = vap->iv_bss;
+ wi_write_ssid(sc, WI_RID_OWN_SSID,
+ bss->ni_essid, bss->ni_esslen);
+ wi_write_val(sc, WI_RID_OWN_CHNL,
+ ieee80211_chan2ieee(ic, bss->ni_chan));
+ wi_write_val(sc, WI_RID_BASIC_RATE, 0x3);
+ wi_write_val(sc, WI_RID_SUPPORT_RATE, 0xf);
+ wi_write_txrate(sc, vap);
+
+ wi_write_val(sc, WI_RID_OWN_BEACON_INT, bss->ni_intval);
+ wi_write_val(sc, WI_RID_DTIM_PERIOD, vap->iv_dtim_period);
+
+ wi_write_val(sc, WI_RID_RTS_THRESH, vap->iv_rtsthreshold);
+ if (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR)
+ wi_write_val(sc, WI_RID_FRAG_THRESH,
+ vap->iv_fragthreshold);
+
+ if ((sc->sc_flags & WI_FLAGS_HAS_ENHSECURITY) &&
+ (vap->iv_flags & IEEE80211_F_HIDESSID)) {
+ /*
+ * bit 0 means hide SSID in beacons,
+ * bit 1 means don't respond to bcast probe req
+ */
+ wi_write_val(sc, WI_RID_ENH_SECURITY, 0x3);
}
- } else if (sc->wi_gone && disable) /* gone --> not enabled */
- sc->sc_enabled = 0;
- callout_stop(&sc->sc_watchdog); /* XXX drain */
- sc->sc_tx_timer = 0;
- sc->sc_scan_timer = 0;
- sc->sc_false_syns = 0;
- sc->sc_naps = 0;
- ifp->if_drv_flags &= ~(IFF_DRV_OACTIVE | IFF_DRV_RUNNING);
+ if ((sc->sc_flags & WI_FLAGS_HAS_WPASUPPORT) &&
+ (vap->iv_flags & IEEE80211_F_WPA) &&
+ vap->iv_appie_wpa != NULL)
+ wi_write_appie(sc, WI_RID_WPA_DATA, vap->iv_appie_wpa);
- WI_UNLOCK(sc);
+ wi_write_val(sc, WI_RID_PROMISC, 0);
+
+ /* Configure WEP. */
+ if (ic->ic_cryptocaps & IEEE80211_CRYPTO_WEP)
+ wi_write_wep(sc, vap);
+ else
+ sc->sc_encryption = 0;
+
+ wi_enable(sc); /* enable port */
+ WI_UNLOCK(sc);
+ }
+ return error;
}
static void
wi_start_locked(struct ifnet *ifp)
{
struct wi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
struct ieee80211_node *ni;
struct ieee80211_frame *wh;
- struct ether_header *eh;
struct mbuf *m0;
+ struct ieee80211_key *k;
struct wi_frame frmhdr;
int cur;
@@ -928,83 +962,34 @@ wi_start_locked(struct ifnet *ifp)
if (sc->wi_gone)
return;
- if (sc->sc_flags & WI_FLAGS_OUTRANGE)
- return;
memset(&frmhdr, 0, sizeof(frmhdr));
cur = sc->sc_txnext;
for (;;) {
- IF_POLL(&ic->ic_mgtq, m0);
- if (m0 != NULL) {
- if (sc->sc_txd[cur].d_len != 0) {
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- IF_DEQUEUE(&ic->ic_mgtq, m0);
- /*
- * Hack! The referenced node pointer is in the
- * rcvif field of the packet header. This is
- * placed there by ieee80211_mgmt_output because
- * we need to hold the reference with the frame
- * and there's no other way (other than packet
- * tags which we consider too expensive to use)
- * to pass it along.
- */
- ni = (struct ieee80211_node *) m0->m_pkthdr.rcvif;
- m0->m_pkthdr.rcvif = NULL;
-
- m_copydata(m0, 4, ETHER_ADDR_LEN * 2,
- (caddr_t)&frmhdr.wi_ehdr);
- frmhdr.wi_ehdr.ether_type = 0;
- wh = mtod(m0, struct ieee80211_frame *);
- } else {
- if (ic->ic_state != IEEE80211_S_RUN)
- break;
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
- if (sc->sc_txd[cur].d_len != 0) {
- IFQ_DRV_PREPEND(&ifp->if_snd, m0);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
- if (m0->m_len < sizeof(struct ether_header) &&
- (m0 = m_pullup(m0, sizeof(struct ether_header))) == NULL) {
- ifp->if_oerrors++;
- continue;
- }
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- continue;
- }
- ifp->if_opackets++;
- m_copydata(m0, 0, ETHER_HDR_LEN,
- (caddr_t)&frmhdr.wi_ehdr);
-#if NBPFILTER > 0
- BPF_MTAP(ifp, m0);
-#endif
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
+ if (m0 == NULL)
+ break;
+ if (sc->sc_txd[cur].d_len != 0) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m0);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ /* NB: copy before 802.11 header is prepended */
+ m_copydata(m0, 0, ETHER_HDR_LEN,
+ (caddr_t)&frmhdr.wi_ehdr);
- m0 = ieee80211_encap(ic, m0, ni);
- if (m0 == NULL) {
- ifp->if_oerrors++;
- ieee80211_free_node(ni);
- continue;
- }
- wh = mtod(m0, struct ieee80211_frame *);
+ ni = (struct ieee80211_node *) m0->m_pkthdr.rcvif;
+ m0 = ieee80211_encap(ni, m0);
+ if (m0 == NULL) {
+ ifp->if_oerrors++;
+ ieee80211_free_node(ni);
+ continue;
}
-#if NBPFILTER > 0
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-#endif
- frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX);
- /* XXX check key for SWCRYPT instead of using operating mode */
- if ((wh->i_fc[1] & IEEE80211_FC1_WEP) &&
- (sc->sc_encryption & HOST_ENCRYPT)) {
- struct ieee80211_key *k;
- k = ieee80211_crypto_encap(ic, ni, m0);
+ wh = mtod(m0, struct ieee80211_frame *);
+ frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX);
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
ieee80211_free_node(ni);
m_freem(m0);
@@ -1012,24 +997,23 @@ wi_start_locked(struct ifnet *ifp)
}
frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT);
}
-#if NBPFILTER > 0
- if (bpf_peers_present(sc->sc_drvbpf)) {
- sc->sc_tx_th.wt_rate =
- ni->ni_rates.rs_rates[ni->ni_txrate];
- bpf_mtap2(sc->sc_drvbpf,
- &sc->sc_tx_th, sc->sc_tx_th_len, m0);
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ sc->sc_tx_th.wt_rate = ni->ni_txrate;
+ bpf_mtap2(ifp->if_bpf,
+ &sc->sc_tx_th, sc->sc_tx_th_len, m0);
}
-#endif
+
m_copydata(m0, 0, sizeof(struct ieee80211_frame),
(caddr_t)&frmhdr.wi_whdr);
m_adj(m0, sizeof(struct ieee80211_frame));
frmhdr.wi_dat_len = htole16(m0->m_pkthdr.len);
- if (IFF_DUMPPKTS(ifp))
- wi_dump_pkt(&frmhdr, NULL, -1);
ieee80211_free_node(ni);
if (wi_start_tx(ifp, &frmhdr, m0))
continue;
+
sc->sc_txnext = cur = (cur + 1) % sc->sc_ntxbuf;
+ ifp->if_opackets++;
}
}
@@ -1078,6 +1062,7 @@ wi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m0,
struct ieee80211com *ic = ni->ni_ic;
struct ifnet *ifp = ic->ic_ifp;
struct wi_softc *sc = ifp->if_softc;
+ struct ieee80211_key *k;
struct ieee80211_frame *wh;
struct wi_frame frmhdr;
int cur;
@@ -1089,11 +1074,6 @@ wi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m0,
rc = ENETDOWN;
goto out;
}
- if (sc->sc_flags & WI_FLAGS_OUTRANGE) {
- rc = ENETDOWN;
- goto out;
- }
-
memset(&frmhdr, 0, sizeof(frmhdr));
cur = sc->sc_txnext;
if (sc->sc_txd[cur].d_len != 0) {
@@ -1108,42 +1088,26 @@ wi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m0,
frmhdr.wi_ehdr.ether_type = 0;
wh = mtod(m0, struct ieee80211_frame *);
-#if NBPFILTER > 0
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-#endif
frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX);
if (params && (params->ibp_flags & IEEE80211_BPF_NOACK))
frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_ALTRTRY);
- /* XXX check key for SWCRYPT instead of using operating mode */
if ((wh->i_fc[1] & IEEE80211_FC1_WEP) &&
- (sc->sc_encryption & HOST_ENCRYPT)) {
- if (!params ||
- (params && (params->ibp_flags & IEEE80211_BPF_CRYPTO))) {
- struct ieee80211_key *k;
-
- k = ieee80211_crypto_encap(ic, ni, m0);
- if (k == NULL) {
- rc = ENOMEM;
- goto out;
- }
- frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT);
+ (!params || (params && (params->ibp_flags & IEEE80211_BPF_CRYPTO)))) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ rc = ENOMEM;
+ goto out;
}
+ frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT);
}
-#if NBPFILTER > 0
- if (bpf_peers_present(sc->sc_drvbpf)) {
- sc->sc_tx_th.wt_rate =
- ni->ni_rates.rs_rates[ni->ni_txrate];
- bpf_mtap2(sc->sc_drvbpf,
- &sc->sc_tx_th, sc->sc_tx_th_len, m0);
+ if (bpf_peers_present(ifp->if_bpf)) {
+ sc->sc_tx_th.wt_rate = ni->ni_txrate;
+ bpf_mtap2(ifp->if_bpf, &sc->sc_tx_th, sc->sc_tx_th_len, m0);
}
-#endif
m_copydata(m0, 0, sizeof(struct ieee80211_frame),
(caddr_t)&frmhdr.wi_whdr);
m_adj(m0, sizeof(struct ieee80211_frame));
frmhdr.wi_dat_len = htole16(m0->m_pkthdr.len);
- if (IFF_DUMPPKTS(ifp))
- wi_dump_pkt(&frmhdr, NULL, -1);
if (wi_start_tx(ifp, &frmhdr, m0) < 0) {
m0 = NULL;
rc = EIO;
@@ -1162,32 +1126,21 @@ out:
}
static int
-wi_reset(struct ifnet *ifp)
+wi_reset(struct wi_softc *sc)
{
- struct wi_softc *sc = ifp->if_softc;
#define WI_INIT_TRIES 3
- int i;
- int error = 0;
- int tries;
-
- /* Symbol firmware cannot be initialized more than once */
- if (sc->sc_firmware_type == WI_SYMBOL && sc->sc_reset)
- return (0);
- if (sc->sc_firmware_type == WI_SYMBOL)
- tries = 1;
- else
- tries = WI_INIT_TRIES;
+ int i, error = 0;
- for (i = 0; i < tries; i++) {
- if ((error = wi_cmd(sc, WI_CMD_INI, 0, 0, 0)) == 0)
+ for (i = 0; i < WI_INIT_TRIES; i++) {
+ error = wi_cmd(sc, WI_CMD_INI, 0, 0, 0);
+ if (error == 0)
break;
DELAY(WI_DELAY * 1000);
}
sc->sc_reset = 1;
-
- if (i == tries) {
- if_printf(ifp, "init failed\n");
- return (error);
+ if (i == WI_INIT_TRIES) {
+ if_printf(sc->sc_ifp, "reset failed\n");
+ return error;
}
CSR_WRITE_2(sc, WI_INT_EN, 0);
@@ -1196,7 +1149,7 @@ wi_reset(struct ifnet *ifp)
/* Calibrate timer. */
wi_write_val(sc, WI_RID_TICK_TIME, 8);
- return (0);
+ return 0;
#undef WI_INIT_TRIES
}
@@ -1206,28 +1159,17 @@ wi_watchdog(void *arg)
struct wi_softc *sc = arg;
struct ifnet *ifp = sc->sc_ifp;
+ WI_LOCK_ASSERT(sc);
+
if (!sc->sc_enabled)
return;
- if (sc->sc_tx_timer) {
- if (--sc->sc_tx_timer == 0) {
- if_printf(ifp, "device timeout\n");
- ifp->if_oerrors++;
- wi_init(ifp->if_softc);
- return;
- }
- }
-
- if (sc->sc_scan_timer) {
- if (--sc->sc_scan_timer <= WI_SCAN_WAIT - WI_SCAN_INQWAIT &&
- sc->sc_firmware_type == WI_INTERSIL) {
- DPRINTF(("wi_watchdog: inquire scan\n"));
- wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS, 0, 0);
- }
+ if (sc->sc_tx_timer && --sc->sc_tx_timer == 0) {
+ if_printf(ifp, "device timeout\n");
+ ifp->if_oerrors++;
+ wi_init_locked(ifp->if_softc);
+ return;
}
-
- /* TODO: rate control */
-
callout_reset(&sc->sc_watchdog, hz, wi_watchdog, sc);
}
@@ -1235,17 +1177,11 @@ static int
wi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct wi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
- struct thread *td = curthread;
-#if 0
- struct ifreq *ifr = (struct ifreq *)data;
- struct wi_req wreq;
-#endif
-
- if (sc->wi_gone)
- return (ENODEV);
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
+ WI_LOCK(sc);
switch (cmd) {
case SIOCSIFFLAGS:
/*
@@ -1253,234 +1189,54 @@ wi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
* changing is the promisc flag, try to short-circuit a call to
* wi_init() by just setting PROMISC in the hardware.
*/
- WI_LOCK(sc);
if (ifp->if_flags & IFF_UP) {
if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
ifp->if_drv_flags & IFF_DRV_RUNNING) {
- if (ifp->if_flags & IFF_PROMISC &&
- !(sc->sc_if_flags & IFF_PROMISC)) {
- wi_write_val(sc, WI_RID_PROMISC, 1);
- } else if (!(ifp->if_flags & IFF_PROMISC) &&
- sc->sc_if_flags & IFF_PROMISC) {
- wi_write_val(sc, WI_RID_PROMISC, 0);
+ if ((ifp->if_flags ^ sc->sc_if_flags) & IFF_PROMISC) {
+ wi_write_val(sc, WI_RID_PROMISC,
+ (ifp->if_flags & IFF_PROMISC) != 0);
} else {
- wi_init(sc);
+ wi_init_locked(sc);
+ startall = 1;
}
} else {
- wi_init(sc);
+ wi_init_locked(sc);
+ startall = 1;
}
} else {
- if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
- wi_stop(ifp, 1);
- }
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ wi_stop_locked(sc, 1);
sc->wi_gone = 0;
}
sc->sc_if_flags = ifp->if_flags;
- WI_UNLOCK(sc);
- error = 0;
- break;
- case SIOCADDMULTI:
- case SIOCDELMULTI:
- WI_LOCK(sc);
- error = wi_write_multi(sc);
- WI_UNLOCK(sc);
- break;
-#if 0
- case SIOCGIFGENERIC:
- WI_LOCK(sc);
- error = wi_get_cfg(ifp, cmd, data);
- WI_UNLOCK(sc);
- break;
- case SIOCSIFGENERIC:
- error = priv_check(td, PRIV_DRIVER);
- if (error == 0)
- error = wi_set_cfg(ifp, cmd, data);
- break;
- case SIOCGPRISM2DEBUG:
- error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
- if (error)
- break;
- if (!(ifp->if_drv_flags & IFF_DRV_RUNNING) ||
- sc->sc_firmware_type == WI_LUCENT) {
- error = EIO;
- break;
- }
- error = wi_get_debug(sc, &wreq);
- if (error == 0)
- error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
- break;
- case SIOCSPRISM2DEBUG:
- if ((error = priv_check(td, PRIV_DRIVER)))
- return (error);
- error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
- if (error)
- break;
- WI_LOCK(sc);
- error = wi_set_debug(sc, &wreq);
- WI_UNLOCK(sc);
- break;
-#endif
- case SIOCG80211:
- error = wi_ioctl_get(ifp, cmd, data);
- break;
- case SIOCS80211:
- error = priv_check(td, PRIV_NET80211_MANAGE);
- if (error)
- break;
- error = wi_ioctl_set(ifp, cmd, data);
-
-
- break;
- default:
- error = ieee80211_ioctl(ic, cmd, data);
- WI_LOCK(sc);
- if (error == ENETRESET) {
- if (sc->sc_enabled)
- wi_init(sc); /* XXX no error return */
- error = 0;
- }
- WI_UNLOCK(sc);
- break;
- }
- return (error);
-}
-
-static int
-wi_ioctl_get(struct ifnet *ifp, u_long cmd, caddr_t data)
-{
- int error;
- struct wi_softc *sc;
- struct ieee80211req *ireq;
- struct ieee80211com *ic;
-
-
- sc = ifp->if_softc;
- ic = &sc->sc_ic;
- ireq = (struct ieee80211req *) data;
-
- switch (ireq->i_type) {
- case IEEE80211_IOC_STATIONNAME:
- ireq->i_len = sc->sc_nodelen + 1;
- error = copyout(sc->sc_nodename, ireq->i_data,
- ireq->i_len);
break;
- default:
- error = ieee80211_ioctl(ic, cmd, data);
- WI_LOCK(sc);
- if (error == ENETRESET) {
- if (sc->sc_enabled)
- wi_init(sc); /* XXX no error return */
- error = 0;
- }
- WI_UNLOCK(sc);
-
- break;
- }
-
- return (error);
-}
-
-static int
-wi_ioctl_set(struct ifnet *ifp, u_long cmd, caddr_t data)
-{
- int error;
- struct wi_softc *sc;
- struct ieee80211req *ireq;
- u_int8_t nodename[IEEE80211_NWID_LEN];
-
- sc = ifp->if_softc;
- ireq = (struct ieee80211req *) data;
- switch (ireq->i_type) {
- case IEEE80211_IOC_STATIONNAME:
- if (ireq->i_val != 0 ||
- ireq->i_len > IEEE80211_NWID_LEN) {
- error = EINVAL;
- break;
- }
- memset(nodename, 0, IEEE80211_NWID_LEN);
- error = copyin(ireq->i_data, nodename, ireq->i_len);
- if (error)
- break;
- WI_LOCK(sc);
- if (sc->sc_enabled) {
- error = wi_write_ssid(sc, WI_RID_NODENAME,
- nodename, ireq->i_len);
- }
- if (error == 0) {
- memcpy(sc->sc_nodename, nodename,
- IEEE80211_NWID_LEN);
- sc->sc_nodelen = ireq->i_len;
- }
- WI_UNLOCK(sc);
-
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
break;
default:
- error = ieee80211_ioctl(&sc->sc_ic, cmd, data);
- WI_LOCK(sc);
- if (error == ENETRESET) {
- if (sc->sc_enabled)
- wi_init(sc); /* XXX no error return */
- error = 0;
- }
- WI_UNLOCK(sc);
+ error = ether_ioctl(ifp, cmd, data);
break;
}
+ WI_UNLOCK(sc);
- return (error);
-}
-
-static struct ieee80211_node *
-wi_node_alloc(struct ieee80211_node_table *nt)
-{
- struct wi_node *rn;
-
- rn = malloc(sizeof (struct wi_node), M_80211_NODE,
- M_NOWAIT | M_ZERO);
-
- return (rn != NULL) ? &rn->ni : NULL;
-}
-
-static int
-wi_media_change(struct ifnet *ifp)
-{
- struct wi_softc *sc = ifp->if_softc;
- int error;
-
- error = ieee80211_media_change(ifp);
- if (error == ENETRESET) {
- if (sc->sc_enabled)
- wi_init(sc); /* XXX no error return */
- error = 0;
- }
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
static void
wi_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
- struct wi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct wi_softc *sc = ic->ic_ifp->if_softc;
u_int16_t val;
int rate, len;
- if (sc->wi_gone) { /* hardware gone (e.g. ejected) */
- imr->ifm_active = IFM_IEEE80211 | IFM_NONE;
- imr->ifm_status = 0;
- return;
- }
-
- imr->ifm_status = IFM_AVALID;
- imr->ifm_active = IFM_IEEE80211;
- if (!sc->sc_enabled) { /* port !enabled, have no status */
- imr->ifm_active |= IFM_NONE;
- imr->ifm_status = IFM_AVALID;
- return;
- }
- if (ic->ic_state == IEEE80211_S_RUN &&
- (sc->sc_flags & WI_FLAGS_OUTRANGE) == 0)
- imr->ifm_status |= IFM_ACTIVE;
len = sizeof(val);
- if (wi_read_rid(sc, WI_RID_CUR_TX_RATE, &val, &len) == 0 &&
+ if (sc->sc_enabled &&
+ wi_read_rid(sc, WI_RID_CUR_TX_RATE, &val, &len) == 0 &&
len == sizeof(val)) {
/* convert to 802.11 rate */
val = le16toh(val);
@@ -1494,36 +1250,18 @@ wi_media_status(struct ifnet *ifp, struct ifmediareq *imr)
else if (rate == 8*2)
rate = 22; /* 11Mbps */
}
- } else
- rate = 0;
- imr->ifm_active |= ieee80211_rate2media(ic, rate, IEEE80211_MODE_11B);
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- break;
- case IEEE80211_M_IBSS:
- imr->ifm_active |= IFM_IEEE80211_ADHOC;
- break;
- case IEEE80211_M_AHDEMO:
- imr->ifm_active |= IFM_IEEE80211_ADHOC | IFM_FLAG0;
- break;
- case IEEE80211_M_HOSTAP:
- imr->ifm_active |= IFM_IEEE80211_HOSTAP;
- break;
- case IEEE80211_M_MONITOR:
- imr->ifm_active |= IFM_IEEE80211_MONITOR;
- break;
- case IEEE80211_M_WDS:
- /* XXXX */
- break;
+ vap->iv_bss->ni_txrate = rate;
}
+ ieee80211_media_status(ifp, imr);
}
static void
wi_sync_bssid(struct wi_softc *sc, u_int8_t new_bssid[IEEE80211_ADDR_LEN])
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ieee80211_node *ni = ic->ic_bss;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ieee80211_node *ni = vap->iv_bss;
if (IEEE80211_ADDR_EQ(new_bssid, ni->ni_bssid))
return;
@@ -1553,93 +1291,11 @@ wi_sync_bssid(struct wi_softc *sc, u_int8_t new_bssid[IEEE80211_ADDR_LEN])
#endif
}
-static void
-wi_rx_monitor(struct wi_softc *sc, int fid)
-{
- struct ifnet *ifp = sc->sc_ifp;
- struct wi_frame *rx_frame;
- struct mbuf *m;
- int datlen, hdrlen;
-
- /* first allocate mbuf for packet storage */
- m = m_getcl(M_DONTWAIT, MT_DATA, 0);
- if (m == NULL) {
- ifp->if_ierrors++;
- return;
- }
-
- m->m_pkthdr.rcvif = ifp;
-
- /* now read wi_frame first so we know how much data to read */
- if (wi_read_bap(sc, fid, 0, mtod(m, caddr_t), sizeof(*rx_frame))) {
- ifp->if_ierrors++;
- goto done;
- }
-
- rx_frame = mtod(m, struct wi_frame *);
-
- switch ((rx_frame->wi_status & WI_STAT_MAC_PORT) >> 8) {
- case 7:
- switch (rx_frame->wi_whdr.i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
- case IEEE80211_FC0_TYPE_DATA:
- hdrlen = WI_DATA_HDRLEN;
- datlen = rx_frame->wi_dat_len + WI_FCS_LEN;
- break;
- case IEEE80211_FC0_TYPE_MGT:
- hdrlen = WI_MGMT_HDRLEN;
- datlen = rx_frame->wi_dat_len + WI_FCS_LEN;
- break;
- case IEEE80211_FC0_TYPE_CTL:
- /*
- * prism2 cards don't pass control packets
- * down properly or consistently, so we'll only
- * pass down the header.
- */
- hdrlen = WI_CTL_HDRLEN;
- datlen = 0;
- break;
- default:
- if_printf(ifp, "received packet of unknown type "
- "on port 7\n");
- ifp->if_ierrors++;
- goto done;
- }
- break;
- case 0:
- hdrlen = WI_DATA_HDRLEN;
- datlen = rx_frame->wi_dat_len + WI_FCS_LEN;
- break;
- default:
- if_printf(ifp, "received packet on invalid "
- "port (wi_status=0x%x)\n", rx_frame->wi_status);
- ifp->if_ierrors++;
- goto done;
- }
-
- if (hdrlen + datlen + 2 > MCLBYTES) {
- if_printf(ifp, "oversized packet received "
- "(wi_dat_len=%d, wi_status=0x%x)\n",
- datlen, rx_frame->wi_status);
- ifp->if_ierrors++;
- goto done;
- }
-
- if (wi_read_bap(sc, fid, hdrlen, mtod(m, caddr_t) + hdrlen,
- datlen + 2) == 0) {
- m->m_pkthdr.len = m->m_len = hdrlen + datlen;
- ifp->if_ipackets++;
- BPF_MTAP(ifp, m); /* Handle BPF listeners. */
- } else
- ifp->if_ierrors++;
-done:
- m_freem(m);
-}
-
-static void
+static __noinline void
wi_rx_intr(struct wi_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct wi_frame frmhdr;
struct mbuf *m;
struct ieee80211_frame *wh;
@@ -1651,16 +1307,6 @@ wi_rx_intr(struct wi_softc *sc)
fid = CSR_READ_2(sc, WI_RX_FID);
- if (sc->wi_debug.wi_monitor) {
- /*
- * If we are in monitor mode just
- * read the data from the device.
- */
- wi_rx_monitor(sc, fid);
- CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
- return;
- }
-
/* First read in the frame header */
if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr))) {
CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
@@ -1669,9 +1315,6 @@ wi_rx_intr(struct wi_softc *sc)
return;
}
- if (IFF_DUMPPKTS(ifp))
- wi_dump_pkt(&frmhdr, NULL, frmhdr.wi_rx_signal);
-
/*
* Drop undecryptable or packets with receive errors here
*/
@@ -1703,24 +1346,16 @@ wi_rx_intr(struct wi_softc *sc)
len = 0;
}
- MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (off + len > MHLEN)
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ else
+ m = m_gethdr(M_DONTWAIT, MT_DATA);
if (m == NULL) {
CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
ifp->if_ierrors++;
DPRINTF(("wi_rx_intr: MGET failed\n"));
return;
}
- if (off + len > MHLEN) {
- MCLGET(m, M_DONTWAIT);
- if ((m->m_flags & M_EXT) == 0) {
- CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
- m_freem(m);
- ifp->if_ierrors++;
- DPRINTF(("wi_rx_intr: MCLGET failed\n"));
- return;
- }
- }
-
m->m_data += off - sizeof(struct ieee80211_frame);
memcpy(m->m_data, &frmhdr.wi_whdr, sizeof(struct ieee80211_frame));
wi_read_bap(sc, fid, sizeof(frmhdr),
@@ -1730,8 +1365,7 @@ wi_rx_intr(struct wi_softc *sc)
CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
-#if NBPFILTER > 0
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
/* XXX replace divide by table */
sc->sc_rx_th.wr_rate = frmhdr.wi_rx_rate / 5;
sc->sc_rx_th.wr_antsignal = frmhdr.wi_rx_signal;
@@ -1739,49 +1373,30 @@ wi_rx_intr(struct wi_softc *sc)
sc->sc_rx_th.wr_flags = 0;
if (frmhdr.wi_status & WI_STAT_PCF)
sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_CFP;
- /* XXX IEEE80211_RADIOTAP_F_WEP */
- bpf_mtap2(sc->sc_drvbpf,
- &sc->sc_rx_th, sc->sc_rx_th_len, m);
- }
-#endif
- wh = mtod(m, struct ieee80211_frame *);
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- /*
- * WEP is decrypted by hardware and the IV
- * is stripped. Clear WEP bit so we don't
- * try to process it in ieee80211_input.
- * XXX fix for TKIP, et. al.
- */
- wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ if (m->m_flags & M_WEP)
+ sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_WEP;
+ bpf_mtap2(ifp->if_bpf, &sc->sc_rx_th, sc->sc_rx_th_len, m);
}
/* synchronize driver's BSSID with firmware's BSSID */
+ wh = mtod(m, struct ieee80211_frame *);
dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
if (ic->ic_opmode == IEEE80211_M_IBSS && dir == IEEE80211_FC1_DIR_NODS)
wi_sync_bssid(sc, wh->i_addr3);
WI_UNLOCK(sc);
- /*
- * Locate the node for sender, track state, and
- * then pass this node (referenced) up to the 802.11
- * layer for its use.
- */
- ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *) wh);
- /*
- * Send frame up for processing.
- */
- ieee80211_input(ic, m, ni, rssi, -95/*XXXXwi_rx_silence?*/, rstamp);
- /*
- * The frame may have caused the node to be marked for
- * reclamation (e.g. in response to a DEAUTH message)
- * so use free_node here instead of unref_node.
- */
- ieee80211_free_node(ni);
+
+ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, rssi, -95/*XXX*/, rstamp);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, -95/*XXX*/, rstamp);
WI_LOCK(sc);
}
-static void
+static __noinline void
wi_tx_ex_intr(struct wi_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
@@ -1792,7 +1407,6 @@ wi_tx_ex_intr(struct wi_softc *sc)
/* Read in the frame header */
if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) == 0) {
u_int16_t status = le16toh(frmhdr.wi_status);
-
/*
* Spontaneous station disconnects appear as xmit
* errors. Don't announce them and/or count them
@@ -1825,7 +1439,7 @@ wi_tx_ex_intr(struct wi_softc *sc)
CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC);
}
-static void
+static __noinline void
wi_tx_intr(struct wi_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
@@ -1860,10 +1474,52 @@ wi_tx_intr(struct wi_softc *sc)
}
static void
+wi_status_connected(void *arg, int pending)
+{
+ struct ieee80211vap *vap = arg;
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK(ic);
+ WI_VAP(vap)->wv_newstate(vap, IEEE80211_S_RUN, 0);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, IEEE80211_S_RUN, 0);
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+wi_status_disconnected(void *arg, int pending)
+{
+ struct ieee80211vap *vap = arg;
+
+ if (vap->iv_state == IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_deauth++;
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ }
+}
+
+static void
+wi_status_oor(void *arg, int pending)
+{
+ struct ieee80211com *ic = arg;
+
+ ieee80211_beacon_miss(ic);
+}
+
+static void
+wi_status_assoc_failed(void *arg, int pending)
+{
+ struct ieee80211vap *vap = arg;
+
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, IEEE80211_SCAN_FAIL_TIMEOUT);
+}
+
+static __noinline void
wi_info_intr(struct wi_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct wi_vap *wvp = WI_VAP(vap);
int i, fid, len, off;
u_int16_t ltbuf[2];
u_int16_t stat;
@@ -1873,42 +1529,35 @@ wi_info_intr(struct wi_softc *sc)
wi_read_bap(sc, fid, 0, ltbuf, sizeof(ltbuf));
switch (le16toh(ltbuf[1])) {
-
case WI_INFO_LINK_STAT:
wi_read_bap(sc, fid, sizeof(ltbuf), &stat, sizeof(stat));
DPRINTF(("wi_info_intr: LINK_STAT 0x%x\n", le16toh(stat)));
switch (le16toh(stat)) {
case WI_INFO_LINK_STAT_CONNECTED:
- sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
- if (ic->ic_state == IEEE80211_S_RUN &&
- ic->ic_opmode != IEEE80211_M_IBSS)
+ if (vap->iv_state == IEEE80211_S_RUN &&
+ vap->iv_opmode != IEEE80211_M_IBSS)
break;
- /* FALLTHROUGH */
+ /* fall thru... */
case WI_INFO_LINK_STAT_AP_CHG:
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ taskqueue_enqueue(taskqueue_swi, &wvp->wv_connected_task);
break;
case WI_INFO_LINK_STAT_AP_INR:
- sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
+ break;
+ case WI_INFO_LINK_STAT_DISCONNECTED:
+ /* we dropped off the net; e.g. due to deauth/disassoc */
+ taskqueue_enqueue(taskqueue_swi, &wvp->wv_disconnected_task);
break;
case WI_INFO_LINK_STAT_AP_OOR:
- if (sc->sc_firmware_type == WI_SYMBOL &&
- sc->sc_scan_timer > 0) {
- if (wi_cmd(sc, WI_CMD_INQUIRE,
- WI_INFO_HOST_SCAN_RESULTS, 0, 0) != 0)
- sc->sc_scan_timer = 0;
- break;
- }
- if (ic->ic_opmode == IEEE80211_M_STA)
- sc->sc_flags |= WI_FLAGS_OUTRANGE;
+ /* XXX does this need to be per-vap? */
+ taskqueue_enqueue(taskqueue_swi, &sc->sc_oor_task);
break;
- case WI_INFO_LINK_STAT_DISCONNECTED:
case WI_INFO_LINK_STAT_ASSOC_FAILED:
- if (ic->ic_opmode == IEEE80211_M_STA)
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ taskqueue_enqueue(taskqueue_swi,
+ &wvp->wv_assoc_failed_task);
break;
}
break;
-
case WI_INFO_COUNTERS:
/* some card versions have a larger stats structure */
len = min(le16toh(ltbuf[0]) - 1, sizeof(sc->sc_stats) / 4);
@@ -1926,13 +1575,6 @@ wi_info_intr(struct wi_softc *sc)
sc->sc_stats.wi_tx_multi_retries +
sc->sc_stats.wi_tx_retry_limit;
break;
-
- case WI_INFO_SCAN_RESULTS:
- case WI_INFO_HOST_SCAN_RESULTS:
- wi_scan_result(sc, fid, le16toh(ltbuf[0]));
- ieee80211_scan_done(ic);
- break;
-
default:
DPRINTF(("wi_info_intr: got fid %x type %x len %d\n", fid,
le16toh(ltbuf[1]), le16toh(ltbuf[0])));
@@ -1973,6 +1615,12 @@ allmulti:
}
static void
+wi_update_mcast(struct ifnet *ifp)
+{
+ wi_write_multi(ifp->if_softc);
+}
+
+static void
wi_read_nicid(struct wi_softc *sc)
{
struct wi_card_ident *id;
@@ -2063,502 +1711,37 @@ wi_write_ssid(struct wi_softc *sc, int rid, u_int8_t *buf, int buflen)
return wi_write_rid(sc, rid, &ssid, sizeof(ssid));
}
-#if 0
-static int
-wi_get_cfg(struct ifnet *ifp, u_long cmd, caddr_t data)
-{
- struct wi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifreq *ifr = (struct ifreq *)data;
- struct wi_req wreq;
- struct wi_scan_res *res;
- size_t reslen;
- int len, n, error, mif, val, off, i;
-
- error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
- if (error)
- return error;
- len = (wreq.wi_len - 1) * 2;
- if (len < sizeof(u_int16_t))
- return ENOSPC;
- if (len > sizeof(wreq.wi_val))
- len = sizeof(wreq.wi_val);
-
- switch (wreq.wi_type) {
-
- case WI_RID_IFACE_STATS:
- memcpy(wreq.wi_val, &sc->sc_stats, sizeof(sc->sc_stats));
- if (len < sizeof(sc->sc_stats))
- error = ENOSPC;
- else
- len = sizeof(sc->sc_stats);
- break;
-
- case WI_RID_ENCRYPTION:
- case WI_RID_TX_CRYPT_KEY:
- case WI_RID_DEFLT_CRYPT_KEYS:
- case WI_RID_TX_RATE:
- return ieee80211_ioctl(ic, cmd, data);
-
- case WI_RID_MICROWAVE_OVEN:
- if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_MOR)) {
- error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
- &len);
- break;
- }
- wreq.wi_val[0] = htole16(sc->sc_microwave_oven);
- len = sizeof(u_int16_t);
- break;
-
- case WI_RID_DBM_ADJUST:
- if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_DBMADJUST)) {
- error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
- &len);
- break;
- }
- wreq.wi_val[0] = htole16(sc->sc_dbm_offset);
- len = sizeof(u_int16_t);
- break;
-
- case WI_RID_ROAMING_MODE:
- if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_ROAMING)) {
- error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
- &len);
- break;
- }
- wreq.wi_val[0] = htole16(sc->sc_roaming_mode);
- len = sizeof(u_int16_t);
- break;
-
- case WI_RID_SYSTEM_SCALE:
- if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_SYSSCALE)) {
- error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
- &len);
- break;
- }
- wreq.wi_val[0] = htole16(sc->sc_system_scale);
- len = sizeof(u_int16_t);
- break;
-
- case WI_RID_FRAG_THRESH:
- if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR)) {
- error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
- &len);
- break;
- }
- wreq.wi_val[0] = htole16(ic->ic_fragthreshold);
- len = sizeof(u_int16_t);
- break;
-
- case WI_RID_READ_APS:
- if (ic->ic_opmode == IEEE80211_M_HOSTAP)
- return ieee80211_ioctl(ic, cmd, data);
- if (sc->sc_scan_timer > 0) {
- error = EINPROGRESS;
- break;
- }
- n = sc->sc_naps;
- if (len < sizeof(n)) {
- error = ENOSPC;
- break;
- }
- if (len < sizeof(n) + sizeof(struct wi_apinfo) * n)
- n = (len - sizeof(n)) / sizeof(struct wi_apinfo);
- len = sizeof(n) + sizeof(struct wi_apinfo) * n;
- memcpy(wreq.wi_val, &n, sizeof(n));
- memcpy((caddr_t)wreq.wi_val + sizeof(n), sc->sc_aps,
- sizeof(struct wi_apinfo) * n);
- break;
-
- case WI_RID_PRISM2:
- wreq.wi_val[0] = sc->sc_firmware_type != WI_LUCENT;
- len = sizeof(u_int16_t);
- break;
-
- case WI_RID_MIF:
- mif = wreq.wi_val[0];
- error = wi_cmd(sc, WI_CMD_READMIF, mif, 0, 0);
- val = CSR_READ_2(sc, WI_RESP0);
- wreq.wi_val[0] = val;
- len = sizeof(u_int16_t);
- break;
-
- case WI_RID_ZERO_CACHE:
- case WI_RID_PROCFRAME: /* ignore for compatibility */
- /* XXX ??? */
- break;
-
- case WI_RID_READ_CACHE:
- return ieee80211_ioctl(ic, cmd, data);
-
- case WI_RID_SCAN_RES: /* compatibility interface */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP)
- return ieee80211_ioctl(ic, cmd, data);
- if (sc->sc_scan_timer > 0) {
- error = EINPROGRESS;
- break;
- }
- n = sc->sc_naps;
- if (sc->sc_firmware_type == WI_LUCENT) {
- off = 0;
- reslen = WI_WAVELAN_RES_SIZE;
- } else {
- off = sizeof(struct wi_scan_p2_hdr);
- reslen = WI_PRISM2_RES_SIZE;
- }
- if (len < off + reslen * n)
- n = (len - off) / reslen;
- len = off + reslen * n;
- if (off != 0) {
- struct wi_scan_p2_hdr *p2 = (struct wi_scan_p2_hdr *)wreq.wi_val;
- /*
- * Prepend Prism-specific header.
- */
- if (len < sizeof(struct wi_scan_p2_hdr)) {
- error = ENOSPC;
- break;
- }
- p2 = (struct wi_scan_p2_hdr *)wreq.wi_val;
- p2->wi_rsvd = 0;
- p2->wi_reason = n; /* XXX */
- }
- for (i = 0; i < n; i++, off += reslen) {
- const struct wi_apinfo *ap = &sc->sc_aps[i];
-
- res = (struct wi_scan_res *)((char *)wreq.wi_val + off);
- res->wi_chan = ap->channel;
- res->wi_noise = ap->noise;
- res->wi_signal = ap->signal;
- IEEE80211_ADDR_COPY(res->wi_bssid, ap->bssid);
- res->wi_interval = ap->interval;
- res->wi_capinfo = ap->capinfo;
- res->wi_ssid_len = ap->namelen;
- memcpy(res->wi_ssid, ap->name,
- IEEE80211_NWID_LEN);
- if (sc->sc_firmware_type != WI_LUCENT) {
- /* XXX not saved from Prism cards */
- memset(res->wi_srates, 0,
- sizeof(res->wi_srates));
- res->wi_rate = ap->rate;
- res->wi_rsvd = 0;
- }
- }
- break;
-
- default:
- if (sc->sc_enabled) {
- error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val,
- &len);
- break;
- }
- switch (wreq.wi_type) {
- case WI_RID_MAX_DATALEN:
- wreq.wi_val[0] = htole16(sc->sc_max_datalen);
- len = sizeof(u_int16_t);
- break;
- case WI_RID_RTS_THRESH:
- wreq.wi_val[0] = htole16(ic->ic_rtsthreshold);
- len = sizeof(u_int16_t);
- break;
- case WI_RID_CNFAUTHMODE:
- wreq.wi_val[0] = htole16(sc->sc_cnfauthmode);
- len = sizeof(u_int16_t);
- break;
- case WI_RID_NODENAME:
- if (len < sc->sc_nodelen + sizeof(u_int16_t)) {
- error = ENOSPC;
- break;
- }
- len = sc->sc_nodelen + sizeof(u_int16_t);
- wreq.wi_val[0] = htole16((sc->sc_nodelen + 1) / 2);
- memcpy(&wreq.wi_val[1], sc->sc_nodename,
- sc->sc_nodelen);
- break;
- default:
- return ieee80211_ioctl(ic, cmd, data);
- }
- break;
- }
- if (error)
- return error;
- wreq.wi_len = (len + 1) / 2 + 1;
- return copyout(&wreq, ifr->ifr_data, (wreq.wi_len + 1) * 2);
-}
-
-static int
-wi_set_cfg(struct ifnet *ifp, u_long cmd, caddr_t data)
-{
- struct wi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifreq *ifr = (struct ifreq *)data;
- struct wi_req wreq;
- struct mbuf *m;
- int i, len, error, mif, val;
- struct ieee80211_rateset *rs;
-
- error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
- if (error)
- return error;
- len = wreq.wi_len ? (wreq.wi_len - 1) * 2 : 0;
- switch (wreq.wi_type) {
- case WI_RID_DBM_ADJUST:
- return ENODEV;
-
- case WI_RID_NODENAME:
- if (le16toh(wreq.wi_val[0]) * 2 > len ||
- le16toh(wreq.wi_val[0]) > sizeof(sc->sc_nodename)) {
- error = ENOSPC;
- break;
- }
- WI_LOCK(sc);
- if (sc->sc_enabled)
- error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val,
- len);
- if (error == 0) {
- sc->sc_nodelen = le16toh(wreq.wi_val[0]) * 2;
- memcpy(sc->sc_nodename, &wreq.wi_val[1],
- sc->sc_nodelen);
- }
- WI_UNLOCK(sc);
- break;
-
- case WI_RID_MICROWAVE_OVEN:
- case WI_RID_ROAMING_MODE:
- case WI_RID_SYSTEM_SCALE:
- case WI_RID_FRAG_THRESH:
- /* XXX unlocked reads */
- if (wreq.wi_type == WI_RID_MICROWAVE_OVEN &&
- (sc->sc_flags & WI_FLAGS_HAS_MOR) == 0)
- break;
- if (wreq.wi_type == WI_RID_ROAMING_MODE &&
- (sc->sc_flags & WI_FLAGS_HAS_ROAMING) == 0)
- break;
- if (wreq.wi_type == WI_RID_SYSTEM_SCALE &&
- (sc->sc_flags & WI_FLAGS_HAS_SYSSCALE) == 0)
- break;
- if (wreq.wi_type == WI_RID_FRAG_THRESH &&
- (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR) == 0)
- break;
- /* FALLTHROUGH */
- case WI_RID_RTS_THRESH:
- case WI_RID_CNFAUTHMODE:
- case WI_RID_MAX_DATALEN:
- WI_LOCK(sc);
- if (sc->sc_enabled) {
- error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val,
- sizeof(u_int16_t));
- if (error != 0) {
- WI_UNLOCK(sc);
- break;
- }
- }
- switch (wreq.wi_type) {
- case WI_RID_FRAG_THRESH:
- ic->ic_fragthreshold = le16toh(wreq.wi_val[0]);
- break;
- case WI_RID_RTS_THRESH:
- ic->ic_rtsthreshold = le16toh(wreq.wi_val[0]);
- break;
- case WI_RID_MICROWAVE_OVEN:
- sc->sc_microwave_oven = le16toh(wreq.wi_val[0]);
- break;
- case WI_RID_ROAMING_MODE:
- sc->sc_roaming_mode = le16toh(wreq.wi_val[0]);
- break;
- case WI_RID_SYSTEM_SCALE:
- sc->sc_system_scale = le16toh(wreq.wi_val[0]);
- break;
- case WI_RID_CNFAUTHMODE:
- sc->sc_cnfauthmode = le16toh(wreq.wi_val[0]);
- break;
- case WI_RID_MAX_DATALEN:
- sc->sc_max_datalen = le16toh(wreq.wi_val[0]);
- break;
- }
- WI_UNLOCK(sc);
- break;
-
- case WI_RID_TX_RATE:
- WI_LOCK(sc);
- switch (le16toh(wreq.wi_val[0])) {
- case 3:
- ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
- break;
- default:
- rs = &ic->ic_sup_rates[IEEE80211_MODE_11B];
- for (i = 0; i < rs->rs_nrates; i++) {
- if ((rs->rs_rates[i] & IEEE80211_RATE_VAL)
- / 2 == le16toh(wreq.wi_val[0]))
- break;
- }
- if (i == rs->rs_nrates) {
- WI_UNLOCK(sc);
- return EINVAL;
- }
- ic->ic_fixed_rate = rs->rs_rates[i] & IEEE80211_RATE_VAL;
- }
- if (sc->sc_enabled)
- error = wi_write_txrate(sc);
- WI_UNLOCK(sc);
- break;
-
- case WI_RID_SCAN_APS:
- WI_LOCK(sc);
- if (sc->sc_enabled && ic->ic_opmode != IEEE80211_M_HOSTAP)
- error = wi_scan_ap(sc, 0x3fff, 0x000f);
- WI_UNLOCK(sc);
- break;
-
- case WI_RID_SCAN_REQ: /* compatibility interface */
- WI_LOCK(sc);
- if (sc->sc_enabled && ic->ic_opmode != IEEE80211_M_HOSTAP)
- error = wi_scan_ap(sc, wreq.wi_val[0], wreq.wi_val[1]);
- WI_UNLOCK(sc);
- break;
-
- case WI_RID_MGMT_XMIT:
- WI_LOCK(sc);
- if (!sc->sc_enabled)
- error = ENETDOWN;
- else if (ic->ic_mgtq.ifq_len > 5)
- error = EAGAIN;
- else {
- /* NB: m_devget uses M_DONTWAIT so can hold the lock */
- /* XXX wi_len looks in u_int8_t, not in u_int16_t */
- m = m_devget((char *)&wreq.wi_val, wreq.wi_len, 0,
- ifp, NULL);
- if (m != NULL)
- IF_ENQUEUE(&ic->ic_mgtq, m);
- else
- error = ENOMEM;
- }
- WI_UNLOCK(sc);
- break;
-
- case WI_RID_MIF:
- mif = wreq.wi_val[0];
- val = wreq.wi_val[1];
- WI_LOCK(sc);
- error = wi_cmd(sc, WI_CMD_WRITEMIF, mif, val, 0);
- WI_UNLOCK(sc);
- break;
-
- case WI_RID_PROCFRAME: /* ignore for compatibility */
- break;
-
- case WI_RID_OWN_SSID:
- if (le16toh(wreq.wi_val[0]) * 2 > len ||
- le16toh(wreq.wi_val[0]) > IEEE80211_NWID_LEN) {
- error = ENOSPC;
- break;
- }
- WI_LOCK(sc);
- memset(ic->ic_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
- ic->ic_des_ssid[0].len = le16toh(wreq.wi_val[0]) * 2;
- memcpy(ic->ic_des_ssid[0].ssid, &wreq.wi_val[1],
- ic->ic_des_ssid[0].len);
- if (sc->sc_enabled)
- wi_init(sc); /* XXX no error return */
- WI_UNLOCK(sc);
- break;
-
- default:
- WI_LOCK(sc);
- if (sc->sc_enabled)
- error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val,
- len);
- if (error == 0) {
- /* XXX ieee80211_ioctl does a copyin */
- error = ieee80211_ioctl(ic, cmd, data);
- if (error == ENETRESET) {
- if (sc->sc_enabled)
- wi_init(sc);
- error = 0;
- }
- }
- WI_UNLOCK(sc);
- break;
- }
- return error;
-}
-#endif
-
static int
-wi_write_txrate(struct wi_softc *sc)
+wi_write_txrate(struct wi_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
- int i;
- u_int16_t rate;
-
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)
- rate = 0; /* auto */
- else
- rate = ic->ic_fixed_rate / 2;
-
- /* rate: 0, 1, 2, 5, 11 */
-
- switch (sc->sc_firmware_type) {
- case WI_LUCENT:
- switch (rate) {
- case 0: /* auto == 11mbps auto */
- rate = 3;
- break;
- /* case 1, 2 map to 1, 2*/
- case 5: /* 5.5Mbps -> 4 */
- rate = 4;
- break;
- case 11: /* 11mbps -> 5 */
- rate = 5;
- break;
- default:
- break;
- }
- break;
- default:
- /* Choose a bit according to this table.
- *
- * bit | data rate
- * ----+-------------------
- * 0 | 1Mbps
- * 1 | 2Mbps
- * 2 | 5.5Mbps
- * 3 | 11Mbps
- */
- for (i = 8; i > 0; i >>= 1) {
- if (rate >= i)
- break;
- }
- if (i == 0)
- rate = 0xf; /* auto */
- else
- rate = i;
- break;
- }
- return wi_write_val(sc, WI_RID_TX_RATE, rate);
-}
-
-static int
-wi_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
- ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
-{
- struct wi_softc *sc = ic->ic_ifp->if_softc;
-
- /*
- * When doing host encryption of outbound frames fail requests
- * for keys that are not marked w/ the SWCRYPT flag so the
- * net80211 layer falls back to s/w crypto. Note that we also
- * fixup existing keys below to handle mode changes.
- */
- if ((sc->sc_encryption & HOST_ENCRYPT) &&
- (k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0)
- return 0;
- return sc->sc_key_alloc(ic, k, keyix, rxkeyix);
+ static const uint16_t lucent_rates[12] = {
+ [ 0] = 3, /* auto */
+ [ 1] = 1, /* 1Mb/s */
+ [ 2] = 2, /* 2Mb/s */
+ [ 5] = 4, /* 5.5Mb/s */
+ [11] = 5 /* 11Mb/s */
+ };
+ static const uint16_t intersil_rates[12] = {
+ [ 0] = 0xf, /* auto */
+ [ 1] = 0, /* 1Mb/s */
+ [ 2] = 1, /* 2Mb/s */
+ [ 5] = 2, /* 5.5Mb/s */
+ [11] = 3, /* 11Mb/s */
+ };
+ const uint16_t *rates = sc->sc_firmware_type == WI_LUCENT ?
+ lucent_rates : intersil_rates;
+ struct ieee80211com *ic = vap->iv_ic;
+ const struct ieee80211_txparam *tp;
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
+ return wi_write_val(sc, WI_RID_TX_RATE,
+ (tp->ucastrate == IEEE80211_FIXED_RATE_NONE ?
+ rates[0] : rates[tp->ucastrate / 2]));
}
static int
-wi_write_wep(struct wi_softc *sc)
+wi_write_wep(struct wi_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
int error = 0;
int i, keylen;
u_int16_t val;
@@ -2566,20 +1749,20 @@ wi_write_wep(struct wi_softc *sc)
switch (sc->sc_firmware_type) {
case WI_LUCENT:
- val = (ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0;
+ val = (vap->iv_flags & IEEE80211_F_PRIVACY) ? 1 : 0;
error = wi_write_val(sc, WI_RID_ENCRYPTION, val);
if (error)
break;
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0)
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0)
break;
- error = wi_write_val(sc, WI_RID_TX_CRYPT_KEY, ic->ic_def_txkey);
+ error = wi_write_val(sc, WI_RID_TX_CRYPT_KEY, vap->iv_def_txkey);
if (error)
break;
memset(wkey, 0, sizeof(wkey));
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- keylen = ic->ic_nw_keys[i].wk_keylen;
+ keylen = vap->iv_nw_keys[i].wk_keylen;
wkey[i].wi_keylen = htole16(keylen);
- memcpy(wkey[i].wi_keydat, ic->ic_nw_keys[i].wk_key,
+ memcpy(wkey[i].wi_keydat, vap->iv_nw_keys[i].wk_key,
keylen);
}
error = wi_write_rid(sc, WI_RID_DEFLT_CRYPT_KEYS,
@@ -2588,8 +1771,8 @@ wi_write_wep(struct wi_softc *sc)
break;
case WI_INTERSIL:
- case WI_SYMBOL:
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ val = HOST_ENCRYPT | HOST_DECRYPT;
+ if (vap->iv_flags & IEEE80211_F_PRIVACY) {
/*
* ONLY HWB3163 EVAL-CARD Firmware version
* less than 0.8 variant2
@@ -2599,25 +1782,15 @@ wi_write_wep(struct wi_softc *sc)
* It is under investigation for details.
* (ichiro@netbsd.org)
*/
- if (sc->sc_firmware_type == WI_INTERSIL &&
- sc->sc_sta_firmware_ver < 802 ) {
+ if (sc->sc_sta_firmware_ver < 802 ) {
/* firm ver < 0.8 variant 2 */
wi_write_val(sc, WI_RID_PROMISC, 1);
}
wi_write_val(sc, WI_RID_CNFAUTHMODE,
- sc->sc_cnfauthmode);
- /* XXX should honor IEEE80211_F_DROPUNENC */
- val = PRIVACY_INVOKED | EXCLUDE_UNENCRYPTED;
- /*
- * Encryption firmware has a bug for HostAP mode.
- */
- if (sc->sc_firmware_type == WI_INTERSIL &&
- ic->ic_opmode == IEEE80211_M_HOSTAP)
- val |= HOST_ENCRYPT;
+ vap->iv_bss->ni_authmode);
+ val |= PRIVACY_INVOKED;
} else {
- wi_write_val(sc, WI_RID_CNFAUTHMODE,
- IEEE80211_AUTH_OPEN);
- val = HOST_ENCRYPT | HOST_DECRYPT;
+ wi_write_val(sc, WI_RID_CNFAUTHMODE, IEEE80211_AUTH_OPEN);
}
error = wi_write_val(sc, WI_RID_P2_ENCRYPTION, val);
if (error)
@@ -2625,55 +1798,17 @@ wi_write_wep(struct wi_softc *sc)
sc->sc_encryption = val;
if ((val & PRIVACY_INVOKED) == 0)
break;
- error = wi_write_val(sc, WI_RID_P2_TX_CRYPT_KEY,
- ic->ic_def_txkey);
- if (error)
- break;
- if (val & HOST_DECRYPT)
- break;
- /*
- * It seems that the firmware accept 104bit key only if
- * all the keys have 104bit length. We get the length of
- * the transmit key and use it for all other keys.
- * Perhaps we should use software WEP for such situation.
- */
- if (ic->ic_def_txkey != IEEE80211_KEYIX_NONE)
- keylen = ic->ic_nw_keys[ic->ic_def_txkey].wk_keylen;
- else /* XXX should not hapen */
- keylen = IEEE80211_WEP_KEYLEN;
- if (keylen > IEEE80211_WEP_KEYLEN)
- keylen = 13; /* 104bit keys */
- else
- keylen = IEEE80211_WEP_KEYLEN;
- for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- error = wi_write_rid(sc, WI_RID_P2_CRYPT_KEY0 + i,
- ic->ic_nw_keys[i].wk_key, keylen);
- if (error)
- break;
- }
+ error = wi_write_val(sc, WI_RID_P2_TX_CRYPT_KEY, vap->iv_def_txkey);
break;
}
- /*
- * XXX horrible hack; insure pre-existing keys are
- * setup properly to do s/w crypto.
- */
- for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- struct ieee80211_key *k = &ic->ic_nw_keys[i];
- if (k->wk_flags & IEEE80211_KEY_XMIT) {
- if (sc->sc_encryption & HOST_ENCRYPT)
- k->wk_flags |= IEEE80211_KEY_SWCRYPT;
- else
- k->wk_flags &= ~IEEE80211_KEY_SWCRYPT;
- }
- }
return error;
}
static int
wi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2)
{
- int i, s = 0;
-
+ int i, s = 0;
+
if (sc->wi_gone)
return (ENODEV);
@@ -2684,7 +1819,8 @@ wi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2)
DELAY(1*1000); /* 1ms */
}
if (i == 0) {
- device_printf(sc->sc_dev, "wi_cmd: busy bit won't clear.\n" );
+ device_printf(sc->sc_dev, "%s: busy bit won't clear, cmd 0x%x\n",
+ __func__, cmd);
sc->wi_gone = 1;
return(ETIMEDOUT);
}
@@ -2717,8 +1853,8 @@ wi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2)
}
if (i == WI_TIMEOUT) {
- device_printf(sc->sc_dev,
- "timeout in wi_cmd 0x%04x; event status 0x%04x\n", cmd, s);
+ device_printf(sc->sc_dev, "%s: timeout on cmd 0x%04x; "
+ "event status 0x%04x\n", __func__, cmd, s);
if (s == 0xffff)
sc->wi_gone = 1;
return(ETIMEDOUT);
@@ -2739,8 +1875,8 @@ wi_seek_bap(struct wi_softc *sc, int id, int off)
if ((status & WI_OFF_BUSY) == 0)
break;
if (i == WI_TIMEOUT) {
- device_printf(sc->sc_dev, "timeout in wi_seek to %x/%x\n",
- id, off);
+ device_printf(sc->sc_dev, "%s: timeout, id %x off %x\n",
+ __func__, id, off);
sc->sc_bap_off = WI_OFF_ERR; /* invalidate */
if (status == 0xffff)
sc->wi_gone = 1;
@@ -2749,7 +1885,8 @@ wi_seek_bap(struct wi_softc *sc, int id, int off)
DELAY(1);
}
if (status & WI_OFF_ERR) {
- device_printf(sc->sc_dev, "failed in wi_seek to %x/%x\n", id, off);
+ device_printf(sc->sc_dev, "%s: error, id %x off %x\n",
+ __func__, id, off);
sc->sc_bap_off = WI_OFF_ERR; /* invalidate */
return EIO;
}
@@ -2787,9 +1924,6 @@ wi_write_bap(struct wi_softc *sc, int id, int off, void *buf, int buflen)
if (buflen == 0)
return 0;
-#ifdef WI_HERMES_AUTOINC_WAR
- again:
-#endif
if (id != sc->sc_bap_id || off != sc->sc_bap_off) {
if ((error = wi_seek_bap(sc, id, off)) != 0)
return error;
@@ -2800,33 +1934,6 @@ wi_write_bap(struct wi_softc *sc, int id, int off, void *buf, int buflen)
CSR_WRITE_2(sc, WI_DATA0, ptr[i]);
sc->sc_bap_off += cnt * 2;
-#ifdef WI_HERMES_AUTOINC_WAR
- /*
- * According to the comments in the HCF Light code, there is a bug
- * in the Hermes (or possibly in certain Hermes firmware revisions)
- * where the chip's internal autoincrement counter gets thrown off
- * during data writes: the autoincrement is missed, causing one
- * data word to be overwritten and subsequent words to be written to
- * the wrong memory locations. The end result is that we could end
- * up transmitting bogus frames without realizing it. The workaround
- * for this is to write a couple of extra guard words after the end
- * of the transfer, then attempt to read then back. If we fail to
- * locate the guard words where we expect them, we preform the
- * transfer over again.
- */
- if ((sc->sc_flags & WI_FLAGS_BUG_AUTOINC) && (id & 0xf000) == 0) {
- CSR_WRITE_2(sc, WI_DATA0, 0x1234);
- CSR_WRITE_2(sc, WI_DATA0, 0x5678);
- wi_seek_bap(sc, id, sc->sc_bap_off);
- sc->sc_bap_off = WI_OFF_ERR; /* invalidate */
- if (CSR_READ_2(sc, WI_DATA0) != 0x1234 ||
- CSR_READ_2(sc, WI_DATA0) != 0x5678) {
- device_printf(sc->sc_dev,
- "detect auto increment bug, try again\n");
- goto again;
- }
- }
-#endif
return 0;
}
@@ -2863,8 +1970,8 @@ wi_alloc_fid(struct wi_softc *sc, int len, int *idp)
int i;
if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len, 0, 0)) {
- device_printf(sc->sc_dev, "failed to allocate %d bytes on NIC\n",
- len);
+ device_printf(sc->sc_dev, "%s: failed to allocate %d bytes on NIC\n",
+ __func__, len);
return ENOMEM;
}
@@ -2874,7 +1981,7 @@ wi_alloc_fid(struct wi_softc *sc, int len, int *idp)
DELAY(1);
}
if (i == WI_TIMEOUT) {
- device_printf(sc->sc_dev, "timeout in alloc\n");
+ device_printf(sc->sc_dev, "%s: timeout in alloc\n", __func__);
return ETIMEDOUT;
}
*idp = CSR_READ_2(sc, WI_ALLOC_FID);
@@ -2923,251 +2030,33 @@ wi_write_rid(struct wi_softc *sc, int rid, void *buf, int buflen)
ltbuf[1] = htole16(rid);
error = wi_write_bap(sc, rid, 0, ltbuf, sizeof(ltbuf));
- if (error)
+ if (error) {
+ device_printf(sc->sc_dev, "%s: bap0 write failure, rid 0x%x\n",
+ __func__, rid);
return error;
+ }
error = wi_write_bap(sc, rid, sizeof(ltbuf), buf, buflen);
- if (error)
+ if (error) {
+ device_printf(sc->sc_dev, "%s: bap1 write failure, rid 0x%x\n",
+ __func__, rid);
return error;
+ }
return wi_cmd(sc, WI_CMD_ACCESS | WI_ACCESS_WRITE, rid, 0, 0);
}
static int
-wi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
-{
- struct ifnet *ifp = ic->ic_ifp;
- struct wi_softc *sc = ifp->if_softc;
- struct ieee80211_node *ni;
- int buflen;
- u_int16_t val;
- struct wi_ssid ssid;
- u_int8_t old_bssid[IEEE80211_ADDR_LEN];
-
- DPRINTF(("%s: %s -> %s\n", __func__,
- ieee80211_state_name[ic->ic_state],
- ieee80211_state_name[nstate]));
-
- /*
- * Internal to the driver the INIT and RUN states are used
- * so bypass the net80211 state machine for other states.
- * Beware however that this requires use to net80211 state
- * management that otherwise would be handled for us.
- */
- switch (nstate) {
- case IEEE80211_S_INIT:
- sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
- return (*sc->sc_newstate)(ic, nstate, arg);
-
- case IEEE80211_S_SCAN:
- return (*sc->sc_newstate)(ic, nstate, arg);
-
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
- ic->ic_state = nstate; /* NB: skip normal ieee80211 handling */
- break;
-
- case IEEE80211_S_RUN:
- ni = ic->ic_bss;
- sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
- buflen = IEEE80211_ADDR_LEN;
- IEEE80211_ADDR_COPY(old_bssid, ni->ni_bssid);
- wi_read_rid(sc, WI_RID_CURRENT_BSSID, ni->ni_bssid, &buflen);
- IEEE80211_ADDR_COPY(ni->ni_macaddr, ni->ni_bssid);
- buflen = sizeof(val);
- wi_read_rid(sc, WI_RID_CURRENT_CHAN, &val, &buflen);
- ni->ni_chan = ieee80211_find_channel(ic,
- ieee80211_ieee2mhz(val, IEEE80211_CHAN_B),
- IEEE80211_CHAN_B);
- if (ni->ni_chan == NULL)
- ni->ni_chan = &ic->ic_channels[0];
- ic->ic_curchan = ic->ic_bsschan = ni->ni_chan;
-#if NBPFILTER > 0
- sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq =
- htole16(ni->ni_chan->ic_freq);
- sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags =
- htole16(ni->ni_chan->ic_flags);
-#endif
- /*
- * XXX hack; unceremoniously clear
- * IEEE80211_F_DROPUNENC when operating with
- * wep enabled so we don't drop unencoded frames
- * at the 802.11 layer. This is necessary because
- * we must strip the WEP bit from the 802.11 header
- * before passing frames to ieee80211_input because
- * the card has already stripped the WEP crypto
- * header from the packet.
- */
- if (ic->ic_flags & IEEE80211_F_PRIVACY)
- ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
- if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
- /* XXX check return value */
- buflen = sizeof(ssid);
- wi_read_rid(sc, WI_RID_CURRENT_SSID, &ssid, &buflen);
- ni->ni_esslen = le16toh(ssid.wi_len);
- if (ni->ni_esslen > IEEE80211_NWID_LEN)
- ni->ni_esslen = IEEE80211_NWID_LEN; /*XXX*/
- memcpy(ni->ni_essid, ssid.wi_ssid, ni->ni_esslen);
- }
- return (*sc->sc_newstate)(ic, nstate, arg);
- default:
- break;
- }
- return 0;
-}
-
-static int
-wi_scan_ap(struct wi_softc *sc, u_int16_t chanmask, u_int16_t txrate)
-{
- int error = 0;
- u_int16_t val[2];
-
- if (!sc->sc_enabled)
- return ENXIO;
- switch (sc->sc_firmware_type) {
- case WI_LUCENT:
- (void)wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS, 0, 0);
- break;
- case WI_INTERSIL:
- val[0] = htole16(chanmask); /* channel */
- val[1] = htole16(txrate); /* tx rate */
- error = wi_write_rid(sc, WI_RID_SCAN_REQ, val, sizeof(val));
- break;
- case WI_SYMBOL:
- /*
- * XXX only supported on 3.x ?
- */
- val[0] = BSCAN_BCAST | BSCAN_ONETIME;
- error = wi_write_rid(sc, WI_RID_BCAST_SCAN_REQ,
- val, sizeof(val[0]));
- break;
- }
- if (error == 0) {
- sc->sc_scan_timer = WI_SCAN_WAIT;
- DPRINTF(("wi_scan_ap: start scanning, "
- "chamask 0x%x txrate 0x%x\n", chanmask, txrate));
- }
- return error;
-}
-
-static void
-wi_scan_result(struct wi_softc *sc, int fid, int cnt)
-{
-#define N(a) (sizeof (a) / sizeof (a[0]))
- int i, naps, off, szbuf;
- struct wi_scan_header ws_hdr; /* Prism2 header */
- struct wi_scan_data_p2 ws_dat; /* Prism2 scantable*/
- struct wi_apinfo *ap;
- struct ieee80211_scanparams sp;
- struct ieee80211_frame wh;
- static long rstamp;
- struct ieee80211com *ic;
- uint8_t ssid[2+IEEE80211_NWID_LEN];
-
- ic = &sc->sc_ic;
- rstamp++;
- memset(&sp, 0, sizeof(sp));
-
- off = sizeof(u_int16_t) * 2;
- memset(&ws_hdr, 0, sizeof(ws_hdr));
- switch (sc->sc_firmware_type) {
- case WI_INTERSIL:
- wi_read_bap(sc, fid, off, &ws_hdr, sizeof(ws_hdr));
- off += sizeof(ws_hdr);
- szbuf = sizeof(struct wi_scan_data_p2);
- break;
- case WI_SYMBOL:
- szbuf = sizeof(struct wi_scan_data_p2) + 6;
- break;
- case WI_LUCENT:
- szbuf = sizeof(struct wi_scan_data);
- break;
- default:
- device_printf(sc->sc_dev,
- "wi_scan_result: unknown firmware type %u\n",
- sc->sc_firmware_type);
- naps = 0;
- goto done;
- }
- naps = (cnt * 2 + 2 - off) / szbuf;
- if (naps > N(sc->sc_aps))
- naps = N(sc->sc_aps);
- sc->sc_naps = naps;
- /* Read Data */
- ap = sc->sc_aps;
- memset(&ws_dat, 0, sizeof(ws_dat));
-
- for (i = 0; i < naps; i++, ap++) {
- uint8_t rates[2 + IEEE80211_RATE_MAXSIZE];
- uint16_t *bssid;
- wi_read_bap(sc, fid, off, &ws_dat,
- (sizeof(ws_dat) < szbuf ? sizeof(ws_dat) : szbuf));
- DPRINTF2(("wi_scan_result: #%d: off %d bssid %s\n", i, off,
- ether_sprintf(ws_dat.wi_bssid)));
-
- off += szbuf;
- ap->scanreason = le16toh(ws_hdr.wi_reason);
- memcpy(ap->bssid, ws_dat.wi_bssid, sizeof(ap->bssid));
-
- bssid = (uint16_t *)&ap->bssid;
- if (bssid[0] == 0 && bssid[1] == 0 && bssid[2] == 0)
- break;
-
- memcpy(wh.i_addr2, ws_dat.wi_bssid, sizeof(ap->bssid));
- memcpy(wh.i_addr3, ws_dat.wi_bssid, sizeof(ap->bssid));
- ap->channel = le16toh(ws_dat.wi_chid);
- ap->signal = le16toh(ws_dat.wi_signal);
- ap->noise = le16toh(ws_dat.wi_noise);
- ap->quality = ap->signal - ap->noise;
- sp.capinfo = ap->capinfo = le16toh(ws_dat.wi_capinfo);
- sp.bintval = ap->interval = le16toh(ws_dat.wi_interval);
- ap->rate = le16toh(ws_dat.wi_rate);
- rates[1] = 1;
- rates[2] = (uint8_t)ap->rate / 5;
- ap->namelen = le16toh(ws_dat.wi_namelen);
- if (ap->namelen > sizeof(ap->name))
- ap->namelen = sizeof(ap->name);
- memcpy(ap->name, ws_dat.wi_name, ap->namelen);
- sp.ssid = (uint8_t *)&ssid[0];
- memcpy(sp.ssid + 2, ap->name, ap->namelen);
- sp.ssid[1] = ap->namelen;
- sp.bchan = ap->channel;
- sp.curchan = ieee80211_find_channel(ic,
- ieee80211_ieee2mhz(ap->channel, IEEE80211_CHAN_B),
- IEEE80211_CHAN_B);
- if (sp.curchan == NULL)
- sp.curchan = &ic->ic_channels[0];
- sp.rates = &rates[0];
- sp.tstamp = (uint8_t *)&rstamp;
- DPRINTF(("calling add_scan, bssid %s chan %d signal %d\n",
- ether_sprintf(ws_dat.wi_bssid), ap->channel, ap->signal));
- ieee80211_add_scan(ic, &sp, &wh, 0, ap->signal, ap->noise, rstamp);
- }
-done:
- /* Done scanning */
- sc->sc_scan_timer = 0;
- DPRINTF(("wi_scan_result: scan complete: ap %d\n", naps));
-#undef N
-}
-
-static void
-wi_dump_pkt(struct wi_frame *wh, struct ieee80211_node *ni, int rssi)
+wi_write_appie(struct wi_softc *sc, int rid, const struct ieee80211_appie *ie)
{
- if (ni != NULL)
- ieee80211_dump_pkt(ni->ni_ic,
- (u_int8_t *) &wh->wi_whdr, sizeof(wh->wi_whdr),
- ni->ni_rates.rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL,
- rssi);
- printf(" status 0x%x rx_tstamp1 %u rx_tstamp0 0x%u rx_silence %u\n",
- le16toh(wh->wi_status), le16toh(wh->wi_rx_tstamp1),
- le16toh(wh->wi_rx_tstamp0), wh->wi_rx_silence);
- printf(" rx_signal %u rx_rate %u rx_flow %u\n",
- wh->wi_rx_signal, wh->wi_rx_rate, wh->wi_rx_flow);
- printf(" tx_rtry %u tx_rate %u tx_ctl 0x%x dat_len %u\n",
- wh->wi_tx_rtry, wh->wi_tx_rate,
- le16toh(wh->wi_tx_ctl), le16toh(wh->wi_dat_len));
- printf(" ehdr dst %6D src %6D type 0x%x\n",
- wh->wi_ehdr.ether_dhost, ":", wh->wi_ehdr.ether_shost, ":",
- wh->wi_ehdr.ether_type);
+ /* NB: 42 bytes is probably ok to have on the stack */
+ char buf[sizeof(uint16_t) + 40];
+
+ if (ie->ie_len > 40)
+ return EINVAL;
+ /* NB: firmware requires 16-bit ie length before ie data */
+ *(uint16_t *) buf = htole16(ie->ie_len);
+ memcpy(buf + sizeof(uint16_t), ie->ie_data, ie->ie_len);
+ return wi_write_rid(sc, rid, buf, ie->ie_len + sizeof(uint16_t));
}
int
@@ -3180,9 +2069,9 @@ wi_alloc(device_t dev, int rid)
sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT,
&sc->iobase_rid, 0, ~0, (1 << 6),
rman_make_alignment_flags(1 << 6) | RF_ACTIVE);
- if (!sc->iobase) {
+ if (sc->iobase == NULL) {
device_printf(dev, "No I/O space?!\n");
- return (ENXIO);
+ return ENXIO;
}
sc->wi_io_addr = rman_get_start(sc->iobase);
@@ -3192,32 +2081,28 @@ wi_alloc(device_t dev, int rid)
sc->mem_rid = rid;
sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
&sc->mem_rid, RF_ACTIVE);
-
- if (!sc->mem) {
+ if (sc->mem == NULL) {
device_printf(dev, "No Mem space on prism2.5?\n");
- return (ENXIO);
+ return ENXIO;
}
sc->wi_btag = rman_get_bustag(sc->mem);
sc->wi_bhandle = rman_get_bushandle(sc->mem);
}
-
sc->irq_rid = 0;
sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
RF_ACTIVE |
((sc->wi_bus_type == WI_BUS_PCCARD) ? 0 : RF_SHAREABLE));
-
- if (!sc->irq) {
+ if (sc->irq == NULL) {
wi_free(dev);
device_printf(dev, "No irq?!\n");
- return (ENXIO);
+ return ENXIO;
}
sc->sc_dev = dev;
sc->sc_unit = device_get_unit(dev);
-
- return (0);
+ return 0;
}
void
@@ -3237,356 +2122,4 @@ wi_free(device_t dev)
bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
sc->mem = NULL;
}
-
- return;
-}
-
-#if 0
-static int
-wi_get_debug(struct wi_softc *sc, struct wi_req *wreq)
-{
- int error = 0;
-
- wreq->wi_len = 1;
-
- switch (wreq->wi_type) {
- case WI_DEBUG_SLEEP:
- wreq->wi_len++;
- wreq->wi_val[0] = sc->wi_debug.wi_sleep;
- break;
- case WI_DEBUG_DELAYSUPP:
- wreq->wi_len++;
- wreq->wi_val[0] = sc->wi_debug.wi_delaysupp;
- break;
- case WI_DEBUG_TXSUPP:
- wreq->wi_len++;
- wreq->wi_val[0] = sc->wi_debug.wi_txsupp;
- break;
- case WI_DEBUG_MONITOR:
- wreq->wi_len++;
- wreq->wi_val[0] = sc->wi_debug.wi_monitor;
- break;
- case WI_DEBUG_LEDTEST:
- wreq->wi_len += 3;
- wreq->wi_val[0] = sc->wi_debug.wi_ledtest;
- wreq->wi_val[1] = sc->wi_debug.wi_ledtest_param0;
- wreq->wi_val[2] = sc->wi_debug.wi_ledtest_param1;
- break;
- case WI_DEBUG_CONTTX:
- wreq->wi_len += 2;
- wreq->wi_val[0] = sc->wi_debug.wi_conttx;
- wreq->wi_val[1] = sc->wi_debug.wi_conttx_param0;
- break;
- case WI_DEBUG_CONTRX:
- wreq->wi_len++;
- wreq->wi_val[0] = sc->wi_debug.wi_contrx;
- break;
- case WI_DEBUG_SIGSTATE:
- wreq->wi_len += 2;
- wreq->wi_val[0] = sc->wi_debug.wi_sigstate;
- wreq->wi_val[1] = sc->wi_debug.wi_sigstate_param0;
- break;
- case WI_DEBUG_CONFBITS:
- wreq->wi_len += 2;
- wreq->wi_val[0] = sc->wi_debug.wi_confbits;
- wreq->wi_val[1] = sc->wi_debug.wi_confbits_param0;
- break;
- default:
- error = EIO;
- break;
- }
-
- return (error);
-}
-
-static int
-wi_set_debug(struct wi_softc *sc, struct wi_req *wreq)
-{
- int error = 0;
- u_int16_t cmd, param0 = 0, param1 = 0;
-
- switch (wreq->wi_type) {
- case WI_DEBUG_RESET:
- case WI_DEBUG_INIT:
- case WI_DEBUG_CALENABLE:
- break;
- case WI_DEBUG_SLEEP:
- sc->wi_debug.wi_sleep = 1;
- break;
- case WI_DEBUG_WAKE:
- sc->wi_debug.wi_sleep = 0;
- break;
- case WI_DEBUG_CHAN:
- param0 = wreq->wi_val[0];
- break;
- case WI_DEBUG_DELAYSUPP:
- sc->wi_debug.wi_delaysupp = 1;
- break;
- case WI_DEBUG_TXSUPP:
- sc->wi_debug.wi_txsupp = 1;
- break;
- case WI_DEBUG_MONITOR:
- sc->wi_debug.wi_monitor = 1;
- break;
- case WI_DEBUG_LEDTEST:
- param0 = wreq->wi_val[0];
- param1 = wreq->wi_val[1];
- sc->wi_debug.wi_ledtest = 1;
- sc->wi_debug.wi_ledtest_param0 = param0;
- sc->wi_debug.wi_ledtest_param1 = param1;
- break;
- case WI_DEBUG_CONTTX:
- param0 = wreq->wi_val[0];
- sc->wi_debug.wi_conttx = 1;
- sc->wi_debug.wi_conttx_param0 = param0;
- break;
- case WI_DEBUG_STOPTEST:
- sc->wi_debug.wi_delaysupp = 0;
- sc->wi_debug.wi_txsupp = 0;
- sc->wi_debug.wi_monitor = 0;
- sc->wi_debug.wi_ledtest = 0;
- sc->wi_debug.wi_ledtest_param0 = 0;
- sc->wi_debug.wi_ledtest_param1 = 0;
- sc->wi_debug.wi_conttx = 0;
- sc->wi_debug.wi_conttx_param0 = 0;
- sc->wi_debug.wi_contrx = 0;
- sc->wi_debug.wi_sigstate = 0;
- sc->wi_debug.wi_sigstate_param0 = 0;
- break;
- case WI_DEBUG_CONTRX:
- sc->wi_debug.wi_contrx = 1;
- break;
- case WI_DEBUG_SIGSTATE:
- param0 = wreq->wi_val[0];
- sc->wi_debug.wi_sigstate = 1;
- sc->wi_debug.wi_sigstate_param0 = param0;
- break;
- case WI_DEBUG_CONFBITS:
- param0 = wreq->wi_val[0];
- param1 = wreq->wi_val[1];
- sc->wi_debug.wi_confbits = param0;
- sc->wi_debug.wi_confbits_param0 = param1;
- break;
- default:
- error = EIO;
- break;
- }
-
- if (error)
- return (error);
-
- cmd = WI_CMD_DEBUG | (wreq->wi_type << 8);
- error = wi_cmd(sc, cmd, param0, param1, 0);
-
- return (error);
-}
-#endif
-
-/*
- * Special routines to download firmware for Symbol CF card.
- * XXX: This should be modified generic into any PRISM-2 based card.
- */
-
-#define WI_SBCF_PDIADDR 0x3100
-
-/* unaligned load little endian */
-#define GETLE32(p) ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24))
-#define GETLE16(p) ((p)[0] | ((p)[1]<<8))
-
-int
-wi_symbol_load_firm(struct wi_softc *sc, const void *primsym, int primlen,
- const void *secsym, int seclen)
-{
- uint8_t ebuf[256];
- int i;
-
- /* load primary code and run it */
- wi_symbol_set_hcr(sc, WI_HCR_EEHOLD);
- if (wi_symbol_write_firm(sc, primsym, primlen, NULL, 0))
- return EIO;
- wi_symbol_set_hcr(sc, WI_HCR_RUN);
- for (i = 0; ; i++) {
- if (i == 10)
- return ETIMEDOUT;
- tsleep(sc, PWAIT, "wiinit", 1);
- if (CSR_READ_2(sc, WI_CNTL) == WI_CNTL_AUX_ENA_STAT)
- break;
- /* write the magic key value to unlock aux port */
- CSR_WRITE_2(sc, WI_PARAM0, WI_AUX_KEY0);
- CSR_WRITE_2(sc, WI_PARAM1, WI_AUX_KEY1);
- CSR_WRITE_2(sc, WI_PARAM2, WI_AUX_KEY2);
- CSR_WRITE_2(sc, WI_CNTL, WI_CNTL_AUX_ENA_CNTL);
- }
-
- /* issue read EEPROM command: XXX copied from wi_cmd() */
- CSR_WRITE_2(sc, WI_PARAM0, 0);
- CSR_WRITE_2(sc, WI_PARAM1, 0);
- CSR_WRITE_2(sc, WI_PARAM2, 0);
- CSR_WRITE_2(sc, WI_COMMAND, WI_CMD_READEE);
- for (i = 0; i < WI_TIMEOUT; i++) {
- if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_CMD)
- break;
- DELAY(1);
- }
- CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD);
-
- CSR_WRITE_2(sc, WI_AUX_PAGE, WI_SBCF_PDIADDR / WI_AUX_PGSZ);
- CSR_WRITE_2(sc, WI_AUX_OFFSET, WI_SBCF_PDIADDR % WI_AUX_PGSZ);
- CSR_READ_MULTI_STREAM_2(sc, WI_AUX_DATA,
- (uint16_t *)ebuf, sizeof(ebuf) / 2);
- if (GETLE16(ebuf) > sizeof(ebuf))
- return EIO;
- if (wi_symbol_write_firm(sc, secsym, seclen, ebuf + 4, GETLE16(ebuf)))
- return EIO;
- return 0;
-}
-
-static int
-wi_symbol_write_firm(struct wi_softc *sc, const void *buf, int buflen,
- const void *ebuf, int ebuflen)
-{
- const uint8_t *p, *ep, *q, *eq;
- char *tp;
- uint32_t addr, id, eid;
- int i, len, elen, nblk, pdrlen;
-
- /*
- * Parse the header of the firmware image.
- */
- p = buf;
- ep = p + buflen;
- while (p < ep && *p++ != ' '); /* FILE: */
- while (p < ep && *p++ != ' '); /* filename */
- while (p < ep && *p++ != ' '); /* type of the firmware */
- nblk = strtoul(p, &tp, 10);
- p = tp;
- pdrlen = strtoul(p + 1, &tp, 10);
- p = tp;
- while (p < ep && *p++ != 0x1a); /* skip rest of header */
-
- /*
- * Block records: address[4], length[2], data[length];
- */
- for (i = 0; i < nblk; i++) {
- addr = GETLE32(p); p += 4;
- len = GETLE16(p); p += 2;
- CSR_WRITE_2(sc, WI_AUX_PAGE, addr / WI_AUX_PGSZ);
- CSR_WRITE_2(sc, WI_AUX_OFFSET, addr % WI_AUX_PGSZ);
- CSR_WRITE_MULTI_STREAM_2(sc, WI_AUX_DATA,
- (const uint16_t *)p, len / 2);
- p += len;
- }
-
- /*
- * PDR: id[4], address[4], length[4];
- */
- for (i = 0; i < pdrlen; ) {
- id = GETLE32(p); p += 4; i += 4;
- addr = GETLE32(p); p += 4; i += 4;
- len = GETLE32(p); p += 4; i += 4;
- /* replace PDR entry with the values from EEPROM, if any */
- for (q = ebuf, eq = q + ebuflen; q < eq; q += elen * 2) {
- elen = GETLE16(q); q += 2;
- eid = GETLE16(q); q += 2;
- elen--; /* elen includes eid */
- if (eid == 0)
- break;
- if (eid != id)
- continue;
- CSR_WRITE_2(sc, WI_AUX_PAGE, addr / WI_AUX_PGSZ);
- CSR_WRITE_2(sc, WI_AUX_OFFSET, addr % WI_AUX_PGSZ);
- CSR_WRITE_MULTI_STREAM_2(sc, WI_AUX_DATA,
- (const uint16_t *)q, len / 2);
- break;
- }
- }
- return 0;
-}
-
-static int
-wi_symbol_set_hcr(struct wi_softc *sc, int mode)
-{
- uint16_t hcr;
-
- CSR_WRITE_2(sc, WI_COR, WI_COR_RESET);
- tsleep(sc, PWAIT, "wiinit", 1);
- hcr = CSR_READ_2(sc, WI_HCR);
- hcr = (hcr & WI_HCR_4WIRE) | (mode & ~WI_HCR_4WIRE);
- CSR_WRITE_2(sc, WI_HCR, hcr);
- tsleep(sc, PWAIT, "wiinit", 1);
- CSR_WRITE_2(sc, WI_COR, WI_COR_IOMODE);
- tsleep(sc, PWAIT, "wiinit", 1);
- return 0;
-}
-
-/*
- * This function can be called by ieee80211_set_shortslottime(). Refer to
- * IEEE Std 802.11-1999 pp. 85 to know how these values are computed.
- */
-static void
-wi_update_slot(struct ifnet *ifp)
-{
- DPRINTF(("wi update slot unimplemented\n"));
-}
-
-static void
-wi_set_channel(struct ieee80211com *ic)
-{
- struct ifnet *ifp = ic->ic_ifp;
- struct wi_softc *sc = ifp->if_softc;
-
- WI_LOCK(sc);
- if (sc->sc_enabled && !(sc->sc_flags & WI_FLAGS_SCANNING)) {
- DPRINTF(("wi_set_channel: %d (%dMHz)\n",
- ieee80211_chan2ieee(ic, ic->ic_curchan),
- ic->ic_curchan->ic_freq));
-
- sc->wi_channel = ic->ic_curchan;
- wi_write_val(sc, WI_RID_OWN_CHNL,
- ieee80211_chan2ieee(ic, ic->ic_curchan));
-
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
- sc->sc_firmware_type == WI_INTERSIL) {
- /* XXX: some cards need to be re-enabled */
- wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0);
- wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0);
- }
- }
- WI_UNLOCK(sc);
-}
-
-static void
-wi_scan_start(struct ieee80211com *ic)
-{
- struct ifnet *ifp = ic->ic_ifp;
- struct wi_softc *sc = ifp->if_softc;
-
- WI_LOCK(sc);
- sc->sc_flags |= WI_FLAGS_SCANNING;
- wi_scan_ap(sc, 0x3fff, 0x000f);
- WI_UNLOCK(sc);
-
-}
-
-static void
-wi_scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
-{
- /* The firmware is not capable of scanning a single channel */
-}
-
-static void
-wi_scan_mindwell(struct ieee80211com *ic)
-{
- /* NB: don't try to abort scan; wait for firmware to finish */
-}
-
-static void
-wi_scan_end(struct ieee80211com *ic)
-{
- struct ifnet *ifp = ic->ic_ifp;
- struct wi_softc *sc = ifp->if_softc;
-
- WI_LOCK(sc);
- sc->sc_flags &= ~WI_FLAGS_SCANNING;
- WI_UNLOCK(sc);
}
diff --git a/sys/dev/wi/if_wi_pccard.c b/sys/dev/wi/if_wi_pccard.c
index 53f067b..ad1b508 100644
--- a/sys/dev/wi/if_wi_pccard.c
+++ b/sys/dev/wi/if_wi_pccard.c
@@ -71,9 +71,6 @@ __FBSDID("$FreeBSD$");
#include <dev/wi/if_wavelan_ieee.h>
#include <dev/wi/if_wireg.h>
#include <dev/wi/if_wivar.h>
-#ifdef WI_SYMBOL_FIRMWARE
-#include <dev/wi/spectrum24t_cf.h>
-#endif
#include "card_if.h"
#include "pccarddevs.h"
@@ -152,7 +149,6 @@ static const struct pccard_product wi_pccard_products[] = {
PCMCIA_CARD(SIEMENS, SS1021),
PCMCIA_CARD(SIMPLETECH, SPECTRUM24_ALT),
PCMCIA_CARD(SOCKET, LP_WLAN_CF),
- PCMCIA_CARD(SYMBOL, LA4100),
PCMCIA_CARD(TDK, LAK_CD011WL),
{ NULL }
};
@@ -167,64 +163,39 @@ wi_pccard_probe(device_t dev)
/* Make sure we're a network driver */
error = pccard_get_function(dev, &fcn);
if (error != 0)
- return (error);
+ return error;
if (fcn != PCCARD_FUNCTION_NETWORK)
- return (ENXIO);
+ return ENXIO;
- if ((pp = pccard_product_lookup(dev, wi_pccard_products,
- sizeof(wi_pccard_products[0]), NULL)) != NULL) {
+ pp = pccard_product_lookup(dev, wi_pccard_products,
+ sizeof(wi_pccard_products[0]), NULL);
+ if (pp != NULL) {
if (pp->pp_name != NULL)
device_set_desc(dev, pp->pp_name);
- return (0);
+ return 0;
}
- return (ENXIO);
+ return ENXIO;
}
-
static int
wi_pccard_attach(device_t dev)
{
struct wi_softc *sc;
- int error;
- uint32_t vendor, product;
+ int error;
sc = device_get_softc(dev);
sc->wi_gone = 0;
sc->wi_bus_type = WI_BUS_PCCARD;
error = wi_alloc(dev, 0);
- if (error)
- return (error);
-
- /* Make sure interrupts are disabled. */
- CSR_WRITE_2(sc, WI_INT_EN, 0);
- CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
-
- /*
- * The cute little Symbol LA4100-series CF cards need to have
- * code downloaded to them.
- */
- pccard_get_vendor(dev, &vendor);
- pccard_get_product(dev, &product);
- if (vendor == PCMCIA_VENDOR_SYMBOL &&
- product == PCMCIA_PRODUCT_SYMBOL_LA4100) {
-#ifdef WI_SYMBOL_FIRMWARE
- if (wi_symbol_load_firm(device_get_softc(dev),
- spectrum24t_primsym, sizeof(spectrum24t_primsym),
- spectrum24t_secsym, sizeof(spectrum24t_secsym))) {
- device_printf(dev, "couldn't load firmware\n");
- return (ENXIO);
- }
-#else
- device_printf(dev,
- "Symbol LA4100 needs 'option WI_SYMBOL_FIRMWARE'\n");
- wi_free(dev);
- return (ENXIO);
-#endif
+ if (error == 0) {
+ /* Make sure interrupts are disabled. */
+ CSR_WRITE_2(sc, WI_INT_EN, 0);
+ CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
+
+ error = wi_attach(dev);
+ if (error != 0)
+ wi_free(dev);
}
- pccard_get_ether(dev, sc->sc_hintmacaddr);
- error = wi_attach(dev);
- if (error != 0)
- wi_free(dev);
- return (error);
+ return error;
}
diff --git a/sys/dev/wi/if_wi_pci.c b/sys/dev/wi/if_wi_pci.c
index fa67a6b..cfbe4dc 100644
--- a/sys/dev/wi/if_wi_pci.c
+++ b/sys/dev/wi/if_wi_pci.c
@@ -246,10 +246,8 @@ static int
wi_pci_suspend(device_t dev)
{
struct wi_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
- wi_stop(ifp, 1);
+ wi_stop(sc, 1);
return (0);
}
@@ -258,8 +256,7 @@ static int
wi_pci_resume(device_t dev)
{
struct wi_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
if (sc->wi_bus_type != WI_BUS_PCI_NATIVE)
return (0);
diff --git a/sys/dev/wi/if_wivar.h b/sys/dev/wi/if_wivar.h
index 88238a3..a3e9e66 100644
--- a/sys/dev/wi/if_wivar.h
+++ b/sys/dev/wi/if_wivar.h
@@ -34,11 +34,6 @@
* $FreeBSD$
*/
-#if 0
-#define WICACHE /* turn on signal strength cache code */
-#define MAXWICACHE 10
-#endif
-
/*
* Encryption controls. We can enable or disable encryption as
* well as specify up to 4 encryption keys. We can also specify
@@ -61,17 +56,26 @@
#define WI_MAX_AID 256 /* max stations for ap operation */
+struct wi_vap {
+ struct ieee80211vap wv_vap;
+ struct ieee80211_beacon_offsets wv_bo;
+ struct task wv_connected_task;
+ struct task wv_disconnected_task;
+ struct task wv_assoc_failed_task;
+
+ void (*wv_recv_mgmt)(struct ieee80211_node *,
+ struct mbuf *, int, int, int, u_int32_t);
+ int (*wv_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define WI_VAP(vap) ((struct wi_vap *)(vap))
+
struct wi_softc {
struct ifnet *sc_ifp;
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
- int (*sc_key_alloc)(struct ieee80211com *,
- const struct ieee80211_key *,
- ieee80211_keyix *, ieee80211_keyix *);
device_t sc_dev;
struct mtx sc_mtx;
struct callout sc_watchdog;
+ struct task sc_oor_task;
int sc_unit;
int wi_gone;
int sc_enabled;
@@ -104,33 +108,21 @@ struct wi_softc {
int wi_io_addr;
int wi_cmd_count;
- struct bpf_if *sc_drvbpf;
int sc_flags;
int sc_if_flags;
int sc_bap_id;
int sc_bap_off;
-
- u_int16_t sc_procframe;
+
+ int sc_porttype;
u_int16_t sc_portnum;
+ u_int16_t sc_encryption;
+ u_int16_t sc_monitor_port;
/* RSSI interpretation */
u_int16_t sc_min_rssi; /* clamp sc_min_rssi < RSSI */
u_int16_t sc_max_rssi; /* clamp RSSI < sc_max_rssi */
u_int16_t sc_dbm_offset; /* dBm ~ RSSI - sc_dbm_offset */
- u_int16_t sc_max_datalen;
- u_int16_t sc_system_scale;
- u_int16_t sc_cnfauthmode;
- u_int16_t sc_roaming_mode;
- u_int16_t sc_microwave_oven;
- u_int16_t sc_authtype;
- u_int16_t sc_encryption;
-
- int sc_nodelen;
- char sc_nodename[IEEE80211_NWID_LEN];
- char sc_net_name[IEEE80211_NWID_LEN];
- uint8_t sc_hintmacaddr[IEEE80211_ADDR_LEN];
-
int sc_buflen; /* TX buffer size */
int sc_ntxbuf;
#define WI_NTXBUF 3
@@ -141,74 +133,29 @@ struct wi_softc {
int sc_txnext; /* index of next TX */
int sc_txcur; /* index of current TX*/
int sc_tx_timer;
- int sc_scan_timer;
struct wi_counters sc_stats;
u_int16_t sc_ibss_port;
-#define WI_MAXAPINFO 30
- struct wi_apinfo sc_aps[WI_MAXAPINFO];
- int sc_naps;
-
- struct {
- u_int16_t wi_sleep;
- u_int16_t wi_delaysupp;
- u_int16_t wi_txsupp;
- u_int16_t wi_monitor;
- u_int16_t wi_ledtest;
- u_int16_t wi_ledtest_param0;
- u_int16_t wi_ledtest_param1;
- u_int16_t wi_conttx;
- u_int16_t wi_conttx_param0;
- u_int16_t wi_contrx;
- u_int16_t wi_sigstate;
- u_int16_t wi_sigstate_param0;
- u_int16_t wi_confbits;
- u_int16_t wi_confbits_param0;
- } wi_debug;
-
struct timeval sc_last_syn;
int sc_false_syns;
u_int16_t sc_txbuf[IEEE80211_MAX_LEN/2];
- union {
- struct wi_tx_radiotap_header th;
- u_int8_t pad[64];
- } u_tx_rt;
+ struct wi_tx_radiotap_header sc_tx_th;
int sc_tx_th_len;
- union {
- struct wi_rx_radiotap_header th;
- u_int8_t pad[64];
- } u_rx_rt;
+ struct wi_rx_radiotap_header sc_rx_th;
int sc_rx_th_len;
};
-#define sc_tx_th u_tx_rt.th
-#define sc_rx_th u_rx_rt.th
/* maximum consecutive false change-of-BSSID indications */
#define WI_MAX_FALSE_SYNS 10
-#define WI_SCAN_INQWAIT 3 /* wait sec before inquire */
-#define WI_SCAN_WAIT 5 /* maximum scan wait */
-
-#define WI_FLAGS_ATTACHED 0x0001
-#define WI_FLAGS_INITIALIZED 0x0002
-#define WI_FLAGS_OUTRANGE 0x0004
-#define WI_FLAGS_HAS_MOR 0x0010
+#define WI_FLAGS_HAS_ENHSECURITY 0x0001
+#define WI_FLAGS_HAS_WPASUPPORT 0x0002
#define WI_FLAGS_HAS_ROAMING 0x0020
-#define WI_FLAGS_HAS_DIVERSITY 0x0040
-#define WI_FLAGS_HAS_SYSSCALE 0x0080
-#define WI_FLAGS_BUG_AUTOINC 0x0100
#define WI_FLAGS_HAS_FRAGTHR 0x0200
#define WI_FLAGS_HAS_DBMADJUST 0x0400
-#define WI_FLAGS_SCANNING 0x0800
-
-
-/* driver-specific node state */
-struct wi_node {
- struct ieee80211_node ni; /* base class */
-};
struct wi_card_ident {
u_int16_t card_id;
@@ -240,5 +187,4 @@ extern devclass_t wi_devclass;
void wi_init(void *);
void wi_intr(void *);
int wi_mgmt_xmit(struct wi_softc *, caddr_t, int);
-void wi_stop(struct ifnet *, int);
-int wi_symbol_load_firm(struct wi_softc *, const void *, int, const void *, int);
+void wi_stop(struct wi_softc *, int);
diff --git a/sys/dev/wi/spectrum24t_cf.h b/sys/dev/wi/spectrum24t_cf.h
deleted file mode 100644
index ce053fa..0000000
--- a/sys/dev/wi/spectrum24t_cf.h
+++ /dev/null
@@ -1,4327 +0,0 @@
-/* $NetBSD$ */
-/* $FreeBSD$ */
-
-/*-
- * Copyright (c) 2001 Symbol Technologies Inc. -- http://www.symbol.com
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted provided
- * that the following conditions are met:
- * 1. Redistribution of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistribution in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * Firmware for Symbol Wireless Networker Spectrum24t CF card.
- * Automatically generated from EPRIMSYM and ESECSYM:
- * T3.10-04,F3.10-04,01/17/2002
- */
-
-#ifndef _FIRMWARE_WI_SPECTRUM24T_CF_H_
-#define _FIRMWARE_WI_SPECTRUM24T_CF_H_
-
-const u_int8_t spectrum24t_primsym[] = {
-0x46,0x49,0x4c,0x45,0x3a,0x20,0x45,0x50,0x52,0x49,0x4d,0x53,0x59,0x4d,0x2c,0x54,
-0x33,0x2e,0x31,0x30,0x2d,0x30,0x34,0x2c,0x46,0x33,0x2e,0x31,0x30,0x2d,0x30,0x34,
-0x2c,0x30,0x31,0x2f,0x31,0x37,0x2f,0x32,0x30,0x30,0x32,0x20,0x50,0x52,0x49,0x4d,
-0x41,0x52,0x59,0x20,0x33,0x20,0x37,0x32,0x20,0x33,0x36,0x20,0x34,0x30,0x0a,0x1a,
-0xfe,0x17,0x7e,0x00,0x02,0x00,0xff,0xff,0x00,0x00,0x7e,0x00,0xfe,0x0f,0x00,0x64,
-0x90,0xff,0x00,0xf7,0x20,0xfe,0x00,0x64,0x91,0xff,0x01,0xf7,0x20,0xfe,0x00,0x64,
-0x92,0xff,0x02,0xf7,0x20,0xfe,0x00,0x64,0x93,0xff,0x03,0xf7,0x20,0xfe,0x00,0x64,
-0x94,0xff,0x04,0xf7,0x20,0xfe,0x00,0x64,0x95,0xff,0x05,0xf7,0x20,0xfe,0x00,0x64,
-0x96,0xff,0x06,0xf7,0x20,0xfe,0x00,0x64,0x97,0xff,0x07,0x04,0x30,0x44,0x04,0xa8,
-0xff,0xff,0x11,0x03,0x00,0x60,0xf6,0x78,0xff,0xff,0x87,0xff,0x8a,0xff,0x98,0xff,
-0x20,0xfe,0x44,0x41,0x00,0x64,0x64,0x40,0x10,0x2b,0x04,0x64,0x40,0x51,0x00,0x64,
-0x40,0x50,0x00,0x64,0x40,0x40,0x99,0xff,0x08,0x60,0x2a,0x62,0x04,0x60,0xff,0x64,
-0xa2,0xdb,0x04,0x60,0xff,0xe5,0xff,0xff,0xff,0xff,0x98,0xff,0x08,0x60,0x28,0x62,
-0x28,0x60,0x10,0x64,0xa2,0xdb,0x28,0x60,0x31,0x40,0x04,0x26,0xa8,0x60,0x99,0xff,
-0x10,0xe4,0xff,0xff,0xff,0xff,0x98,0xff,0x55,0x60,0xfc,0xe0,0xa0,0xfe,0xa1,0xfe,
-0xa2,0xfe,0xa3,0xfe,0xa4,0xfe,0xa5,0xfe,0xa6,0xfe,0xa7,0xfe,0xa8,0xfe,0xa9,0xfe,
-0xaa,0xfe,0xab,0xfe,0xac,0xfe,0xad,0xfe,0xae,0xfe,0xaf,0xfe,0xb0,0xfe,0xb1,0xfe,
-0xb2,0xfe,0xb3,0xfe,0xb4,0xfe,0xb5,0xfe,0xb6,0xfe,0xb7,0xfe,0xb8,0xfe,0xb9,0xfe,
-0xba,0xfe,0xbb,0xfe,0xbc,0xfe,0xbd,0xfe,0xbe,0xfe,0xbf,0xfe,0x99,0xff,0x18,0xec,
-0x0e,0xe3,0x12,0xe3,0x1d,0xe3,0x22,0xe3,0x2a,0xe3,0x32,0xe3,0x3a,0xe3,0x10,0xed,
-0x42,0xe3,0x4a,0xe3,0x52,0xe3,0x5a,0xe3,0x62,0xe3,0x68,0xe3,0x70,0xe3,0x7a,0xe3,
-0x04,0xee,0x82,0xe3,0x8a,0xe3,0x92,0xe3,0x9a,0xe3,0xa2,0xe3,0xaa,0xe3,0xb2,0xe3,
-0xba,0xe3,0x21,0x60,0x7d,0xe7,0x68,0x60,0x7d,0xe7,0x10,0x60,0x7d,0xe7,0x7d,0xe7,
-0xf0,0x60,0x7d,0xe7,0xa5,0x60,0x7d,0xe7,0x7d,0xe7,0x36,0x60,0x7d,0xe7,0x6d,0x60,
-0x7d,0xe7,0x98,0x60,0x7d,0xe7,0x39,0x60,0x7d,0xe7,0x3f,0x60,0x7d,0xe7,0x42,0x60,
-0x7d,0xe7,0x0c,0x60,0x7d,0xe7,0xc2,0x60,0x7d,0xe7,0x7d,0xe7,0xa5,0x60,0x7d,0xe7,
-0x7d,0xe7,0x36,0x60,0x7d,0xe7,0x01,0x60,0x7d,0xe7,0xad,0x60,0x7d,0xe7,0x7d,0xe7,
-0x37,0x60,0x7d,0xe7,0x42,0x60,0x7d,0xe7,0x0e,0x60,0x7d,0xe7,0xc2,0x60,0x7d,0xe7,
-0x07,0x60,0x80,0xe7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0x80,0xe7,0xff,0xff,0xff,0xff,0x06,0xe3,0xff,0xff,0x98,0xff,0x30,0x44,
-0x00,0xa8,0xff,0xff,0x09,0x02,0x08,0x60,0x00,0x64,0xc8,0x81,0x3e,0x63,0x00,0x64,
-0x59,0xdb,0xfe,0x1f,0x04,0x60,0x41,0x76,0x00,0x60,0x3e,0x63,0xff,0x60,0xfe,0x61,
-0xfc,0x60,0x00,0x66,0x09,0x60,0xc2,0x64,0x58,0xd0,0x59,0xd9,0xfd,0x1f,0x05,0x60,
-0x08,0x63,0x08,0x60,0xfe,0x61,0xfc,0x60,0x00,0x66,0x0a,0x60,0x02,0x64,0x58,0xd0,
-0x59,0xd9,0xfd,0x1f,0x58,0x4f,0x2e,0x00,0x58,0x4f,0x14,0x00,0x30,0x44,0x00,0xa8,
-0x00,0x64,0x03,0x03,0x0f,0xfb,0xd4,0xfe,0x00,0x00,0x63,0xff,0xff,0xff,0x65,0xff,
-0xff,0xff,0x58,0x4f,0x41,0x00,0x01,0x60,0x9c,0x78,0xff,0xff,0x98,0xff,0x00,0xe1,
-0xa1,0xff,0xfc,0x00,0x00,0x60,0x7c,0x63,0x0e,0x60,0x80,0x64,0xe0,0x87,0x15,0xfb,
-0x04,0x61,0x60,0x46,0xdc,0x84,0x00,0xfa,0xcd,0x81,0x01,0xfc,0xfa,0x02,0x00,0x64,
-0x00,0xfa,0x80,0x60,0x00,0x65,0xb7,0x83,0x01,0xfc,0x15,0xf5,0x3c,0x63,0x01,0xfc,
-0x2f,0x58,0xff,0xff,0x00,0x60,0x7e,0x63,0xfe,0x60,0x00,0x65,0x45,0x4b,0xdc,0x60,
-0xfe,0x61,0xfc,0x60,0x00,0x65,0x0f,0x60,0x0c,0x64,0x65,0x46,0x58,0xd0,0x2b,0x46,
-0x59,0xd8,0xfb,0x1f,0x7e,0x63,0x40,0xa1,0x40,0xa1,0x65,0x46,0x58,0xd0,0x2b,0x46,
-0x59,0xd8,0xfb,0x1f,0x7e,0x63,0x40,0xa1,0x40,0xa1,0x65,0x46,0x58,0xd0,0x2b,0x46,
-0x59,0xd8,0xfb,0x1f,0x2f,0x58,0xff,0xff,0x04,0xee,0x0d,0x60,0x26,0x63,0x0e,0x60,
-0x0a,0x65,0xbd,0xd1,0xff,0xff,0x64,0x48,0x64,0x47,0x00,0x7f,0x60,0x41,0x80,0xbc,
-0x60,0x4a,0xff,0xff,0xff,0xff,0x01,0x16,0xfe,0x00,0xff,0xff,0xff,0xff,0xd7,0x80,
-0xff,0xff,0xef,0x02,0x68,0x40,0x99,0xff,0x3e,0x44,0xfc,0xb4,0x01,0xbc,0x00,0x7f,
-0x40,0x5e,0x98,0xff,0x0d,0x63,0x01,0x60,0x58,0x4e,0x70,0x78,0xff,0xff,0x99,0xff,
-0x3e,0x44,0xfc,0xb4,0x00,0x7f,0x40,0x5e,0x98,0xff,0x0d,0x63,0x01,0x60,0x58,0x4e,
-0x70,0x78,0xff,0xff,0x00,0xee,0x88,0xec,0x00,0xed,0x2f,0x58,0xff,0xff,0xff,0xff,
-0xfe,0x1f,0x2e,0x58,0xff,0xff,0x98,0xff,0x00,0xe1,0x30,0x44,0x01,0xa8,0x02,0xa8,
-0x05,0x03,0x05,0x03,0x83,0xff,0x8d,0xff,0x00,0x64,0x40,0x40,0x43,0xe1,0x00,0x60,
-0x91,0x65,0x78,0x44,0xc4,0x98,0xff,0xff,0x98,0xff,0x88,0xe2,0x00,0xe1,0x30,0x44,
-0x02,0xa8,0x01,0xa8,0x0b,0x03,0x06,0x03,0x85,0xff,0x88,0xff,0xeb,0x60,0x19,0xe2,
-0x00,0x60,0x90,0xe2,0x40,0xe1,0x04,0x60,0x00,0x71,0x8d,0xe2,0x01,0x60,0x3e,0x65,
-0x78,0x44,0xc4,0x98,0xff,0xff,0x0a,0xe1,0xa3,0xff,0xae,0xff,0xff,0xff,0xff,0xff,
-0xae,0xff,0x01,0x60,0x63,0x65,0x78,0x44,0xc4,0x98,0xff,0xff,0xaa,0xff,0x08,0x60,
-0x14,0x64,0xa0,0xd1,0x04,0x60,0x41,0x76,0x3f,0x60,0xff,0x64,0xa0,0x83,0x08,0x60,
-0x14,0x64,0xa0,0xdd,0x64,0x44,0x80,0x2b,0x1d,0x00,0x50,0xfe,0x08,0x60,0x02,0x64,
-0xa0,0xd1,0xfe,0x60,0x01,0x64,0xd0,0x80,0x08,0x60,0x04,0x64,0xa0,0xd1,0xdc,0x60,
-0x23,0x64,0xd0,0x80,0x08,0x60,0x06,0x64,0xa0,0xd1,0xba,0x60,0x45,0x64,0xd0,0x80,
-0x63,0x47,0x01,0x01,0x07,0x00,0xc0,0xbf,0x60,0x43,0x08,0x60,0x14,0x64,0xa0,0xdd,
-0x04,0x60,0x01,0x76,0x41,0x00,0xab,0xff,0xff,0xff,0xff,0xff,0xab,0xff,0x3c,0x00,
-0xa9,0xff,0x77,0x44,0x60,0x57,0x40,0x4a,0x2a,0x44,0x10,0xb0,0x20,0x44,0x04,0x03,
-0xfd,0xb4,0x01,0xb0,0x40,0x40,0x01,0x02,0x2f,0x00,0xfe,0xb4,0x40,0x40,0xa8,0xff,
-0x20,0x44,0x02,0xb0,0x60,0x41,0x08,0x60,0x00,0x64,0xa0,0xd1,0x61,0x44,0x03,0x03,
-0x01,0xbc,0x40,0x40,0x1e,0x00,0x02,0xbc,0x40,0x40,0x64,0x44,0x3f,0xb4,0x0b,0xa8,
-0xff,0xff,0x06,0x02,0x64,0x44,0x80,0xb0,0xff,0xff,0x02,0x03,0x01,0x64,0x13,0xfb,
-0x0f,0xf9,0x08,0x60,0x02,0x64,0xa0,0xd1,0x10,0xf9,0x08,0x60,0x04,0x64,0xa0,0xd1,
-0xd4,0xfe,0x11,0xf9,0x08,0x60,0x06,0x64,0xa0,0xd1,0x12,0xf9,0xae,0xff,0xff,0xff,
-0xae,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xa1,0xff,0xff,0xff,0xba,0x3f,0x75,0x44,
-0x01,0x26,0x08,0xf7,0xff,0xff,0xff,0xff,0x02,0x26,0x09,0xf7,0xff,0xff,0xff,0xff,
-0x04,0x26,0x0a,0xf7,0xff,0xff,0xff,0xff,0x08,0x26,0x0b,0xf7,0xff,0xff,0xff,0xff,
-0x40,0x26,0xa7,0xff,0xe9,0x00,0x0f,0x60,0xfe,0x65,0x08,0x60,0x1c,0x64,0xa0,0xd3,
-0xff,0xff,0x24,0x86,0x80,0x67,0x60,0x5c,0x08,0x60,0x1c,0x64,0xa0,0xd9,0x08,0x60,
-0x18,0x64,0xa0,0xd3,0x15,0xf3,0x40,0x45,0xfc,0x2b,0x02,0x00,0x40,0x45,0x05,0x00,
-0x02,0x60,0x58,0x4f,0xb2,0x78,0xff,0xff,0x07,0x02,0x58,0x4f,0x4a,0x00,0x04,0x05,
-0x66,0x50,0x65,0x52,0x61,0x51,0x0a,0x00,0x40,0x67,0x26,0x45,0xb4,0x84,0x40,0x46,
-0x15,0xf5,0x06,0xf0,0x80,0x60,0x64,0x50,0x20,0x52,0x7e,0x71,0x99,0xff,0xac,0xff,
-0x98,0xff,0x26,0x5c,0x08,0x60,0x1c,0x64,0xa0,0xd9,0xb6,0x00,0x0f,0x60,0xfe,0x65,
-0x08,0x60,0x1e,0x64,0xa0,0xd3,0xff,0xff,0x24,0x86,0x80,0x67,0x60,0x5c,0x08,0x60,
-0x1e,0x64,0xa0,0xd9,0x08,0x60,0x1a,0x64,0xa0,0xd3,0x15,0xf3,0x40,0x45,0xfc,0x2b,
-0x02,0x00,0x40,0x45,0x03,0x00,0x58,0x4f,0x3c,0x00,0x08,0x02,0x58,0x4f,0x19,0x00,
-0x8e,0xff,0x04,0x05,0x66,0x50,0x65,0x52,0x61,0x51,0x0a,0x00,0x40,0x67,0x26,0x45,
-0xb4,0x84,0x40,0x46,0x15,0xf5,0x06,0xf0,0x80,0x60,0x64,0x50,0x20,0x52,0x7e,0x71,
-0x8d,0xff,0x99,0xff,0xad,0xff,0x98,0xff,0x26,0x5c,0x08,0x60,0x1e,0x64,0xa0,0xd9,
-0xcc,0x00,0x25,0x46,0x26,0x41,0x44,0x63,0x01,0xf2,0xff,0xff,0xff,0xb5,0xd5,0x81,
-0xff,0xff,0x07,0x04,0x00,0xf2,0x04,0x63,0x00,0xa8,0x60,0x46,0xf5,0x02,0x42,0xfe,
-0x0e,0x00,0x61,0x44,0xc5,0x81,0x63,0x45,0xc5,0x81,0x60,0x45,0x00,0x64,0xd4,0x84,
-0x01,0xf2,0xf0,0x85,0xf0,0x80,0x65,0x44,0xf8,0x85,0xff,0xff,0x02,0xfe,0x2f,0x58,
-0xff,0xff,0x25,0x44,0x1a,0xf1,0x1b,0xf1,0xd0,0x80,0xd0,0x80,0x0e,0x04,0x08,0x06,
-0x1c,0xf1,0x1d,0xf1,0xd0,0x80,0xd0,0x80,0x08,0x04,0x02,0x06,0x48,0xfe,0x05,0x00,
-0x25,0x46,0x01,0xf0,0x03,0x67,0xa0,0x85,0x94,0x80,0x2f,0x58,0xff,0xff,0x84,0xe2,
-0x04,0x60,0x00,0x71,0x8d,0xe2,0x1e,0xf3,0x14,0xf3,0x00,0xbd,0xcc,0x83,0x08,0x03,
-0x14,0xfd,0x06,0x02,0x65,0x44,0x14,0xfb,0x89,0xff,0x80,0x60,0x00,0x75,0x88,0xff,
-0xa1,0xff,0xff,0xff,0xbc,0x3f,0x7f,0x67,0x01,0x61,0x23,0x58,0xff,0xff,0x0f,0xf3,
-0x10,0xf1,0x40,0x44,0x44,0x45,0x11,0xf1,0x12,0xf1,0x44,0x46,0x44,0x47,0x3f,0xb4,
-0x0b,0xa8,0xff,0xff,0x06,0x02,0x24,0x44,0x80,0xb0,0xff,0xff,0x02,0x03,0x00,0x67,
-0x08,0x00,0x24,0x44,0x3f,0xb4,0xe0,0x85,0x09,0x60,0x00,0x64,0x44,0xd7,0x58,0x43,
-0xff,0xff,0x61,0x43,0x60,0x45,0x24,0x44,0x3f,0xb4,0xb4,0x9c,0xff,0x27,0x01,0x00,
-0x03,0x00,0x08,0x60,0x0a,0x64,0xa0,0xdd,0x08,0x60,0x08,0x64,0xa0,0xd9,0x10,0x75,
-0xa1,0xff,0xff,0xff,0xbe,0x3f,0xb4,0xfe,0xff,0xff,0xd1,0x05,0xb5,0xfe,0xb6,0xfe,
-0xb7,0xfe,0xf6,0x00,0x08,0x60,0x28,0x62,0x12,0x60,0x34,0x64,0xa2,0xdb,0x24,0x45,
-0x01,0x27,0x3d,0x00,0x7f,0x60,0xc0,0x64,0xa4,0x80,0x7f,0x67,0x02,0x61,0x40,0x02,
-0x20,0x44,0x20,0xb0,0xdf,0xb4,0x1b,0x03,0x40,0x40,0xfc,0x60,0x00,0x66,0x00,0x60,
-0x16,0x64,0x60,0x41,0x08,0x63,0x58,0xd0,0x59,0xd9,0xfd,0x1f,0x00,0x60,0x26,0x64,
-0x60,0x41,0x08,0x63,0x58,0xd0,0x59,0xd9,0xfd,0x1f,0x02,0x64,0x40,0x50,0x63,0xff,
-0xff,0xff,0x65,0xff,0xff,0xff,0x04,0x64,0x40,0x50,0x67,0xff,0xff,0xff,0xfc,0x60,
-0x00,0x66,0x17,0x60,0xfe,0x63,0xbd,0xd0,0xc0,0x60,0xde,0x64,0xd0,0x80,0xdb,0x83,
-0x0e,0x02,0x20,0x44,0x10,0xbc,0x40,0x40,0x02,0x64,0x40,0x50,0x63,0xff,0xff,0xff,
-0x65,0xff,0xff,0xff,0x00,0x64,0x40,0x50,0x0c,0x60,0x01,0x78,0xff,0xff,0x08,0x60,
-0x00,0x64,0xc8,0x81,0x3e,0x63,0x00,0x64,0x59,0xdb,0xfe,0x1f,0x04,0x60,0x41,0x76,
-0x23,0x58,0xff,0xff,0x7e,0x60,0xc0,0x64,0x24,0x45,0xa4,0x80,0x7f,0x67,0x02,0x61,
-0x23,0x02,0x25,0x45,0xfc,0x2b,0x1e,0x00,0x0c,0x60,0x70,0x63,0x0e,0x61,0x24,0x44,
-0x01,0x27,0x10,0x00,0xbd,0xd3,0xa3,0xd1,0xd4,0x80,0xcd,0x81,0x08,0x24,0x64,0x58,
-0x08,0xa3,0xf8,0x02,0x04,0x61,0x15,0xf5,0x00,0x64,0x22,0xfa,0x25,0x44,0x5a,0xda,
-0x00,0x67,0x0a,0x00,0xbd,0xd3,0xbe,0xd1,0xd4,0x80,0xcd,0x81,0x08,0x24,0x64,0x58,
-0x08,0xa3,0xf8,0x02,0x7f,0x67,0x04,0x61,0x23,0x58,0xff,0xff,0xbf,0xd3,0xff,0xff,
-0x62,0x43,0xbf,0xd1,0xf8,0xa3,0xa3,0xd1,0x64,0x43,0x60,0x41,0x15,0xf5,0xe8,0x84,
-0xdc,0x84,0x22,0xfa,0x5a,0xd8,0x62,0x44,0xbd,0xd1,0xc9,0x81,0x58,0xd8,0xfc,0x02,
-0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x22,0xf2,0xbf,0xd1,0xff,0xff,
-0x62,0x43,0xcc,0x84,0xe0,0x85,0x0b,0x06,0xbf,0xd1,0x64,0x41,0xd5,0x80,0x64,0x43,
-0x01,0x06,0x65,0x41,0x46,0x64,0x58,0xd0,0xc9,0x81,0xbd,0xd9,0xfc,0x02,0x00,0x67,
-0x00,0x61,0x23,0x58,0xff,0xff,0xfc,0x60,0x00,0x64,0x40,0x4b,0x4b,0xd3,0x15,0xf5,
-0x60,0x41,0xd8,0x84,0xe8,0x84,0x22,0xfa,0x25,0x44,0x23,0xfa,0xbf,0xd3,0x66,0x45,
-0x48,0x63,0xc8,0x84,0x2b,0x46,0x58,0xd0,0x65,0x46,0xc9,0x81,0xbd,0xd8,0xfa,0x02,
-0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x00,0x64,0x40,0x45,0x02,0x60,0x00,0x64,
-0x40,0x46,0x77,0x60,0xfc,0xe0,0x99,0xff,0x00,0xec,0x02,0xe3,0xd8,0xe3,0x0a,0xe1,
-0x2f,0x60,0xfe,0x62,0x04,0x60,0x58,0x4e,0x7b,0x78,0xff,0xff,0x25,0x44,0x60,0x47,
-0x01,0xb4,0xe0,0x85,0xa0,0x7e,0xb4,0x84,0x04,0x60,0x58,0x4e,0xa4,0x78,0xff,0xff,
-0x25,0x44,0x04,0x60,0x58,0x4e,0xa4,0x78,0xff,0xff,0x08,0x61,0x61,0x5c,0x5a,0xd1,
-0xff,0xff,0x64,0x47,0x04,0x60,0x58,0x4e,0xa4,0x78,0xff,0xff,0x2c,0x02,0x64,0x44,
-0x04,0x60,0x58,0x4e,0xa4,0x78,0xff,0xff,0x26,0x02,0xcd,0x81,0xff,0xff,0xef,0x02,
-0x04,0x60,0x58,0x4e,0x89,0x78,0xff,0xff,0x03,0x60,0xe8,0x7a,0x2d,0xe2,0xa1,0xff,
-0xff,0xff,0x24,0xe2,0x04,0x60,0x58,0x4e,0x7b,0x78,0xff,0xff,0xa1,0x7e,0x04,0x60,
-0x58,0x4e,0xa4,0x78,0xff,0xff,0xf0,0x02,0x04,0x60,0x58,0x4e,0x97,0x78,0xff,0xff,
-0x04,0x60,0x58,0x4e,0x89,0x78,0xff,0xff,0x26,0x45,0x25,0x44,0x10,0xa4,0xd4,0x80,
-0x40,0x45,0xb8,0x02,0x28,0xe2,0xff,0x60,0xff,0x64,0xa2,0xdb,0x55,0x60,0xfc,0xe0,
-0x98,0xff,0x8d,0xe2,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x77,0x60,0xfc,0xe0,
-0x99,0xff,0x00,0xec,0x02,0xe3,0xd8,0xe3,0x04,0x60,0x58,0x4e,0x97,0x78,0xff,0xff,
-0x04,0x60,0x58,0x4e,0x7b,0x78,0xff,0xff,0xa0,0x7e,0x04,0x60,0x58,0x4e,0xa4,0x78,
-0xff,0xff,0x00,0x7e,0x04,0x60,0x58,0x4e,0xa4,0x78,0xff,0xff,0x04,0x60,0x58,0x4e,
-0x7b,0x78,0xff,0xff,0xa1,0x7e,0x04,0x60,0x58,0x4e,0xa4,0x78,0xff,0xff,0x2f,0x60,
-0xfe,0x62,0x01,0x60,0x00,0x61,0x61,0x5c,0x04,0x60,0x58,0x4e,0xc7,0x78,0xff,0xff,
-0x60,0x47,0x60,0x45,0x04,0x60,0x58,0x4e,0xc7,0x78,0xff,0xff,0xb4,0x84,0xcd,0x81,
-0x5a,0xdb,0xf2,0x02,0x04,0x60,0x58,0x4e,0x89,0x78,0xff,0xff,0xff,0xff,0x04,0x60,
-0x58,0x4e,0x97,0x78,0xff,0xff,0x55,0x60,0xfc,0xe0,0x98,0xff,0x00,0x67,0x00,0x61,
-0x23,0x58,0xff,0xff,0x01,0x60,0x02,0xe3,0xff,0xff,0xff,0xff,0x01,0xec,0xff,0xff,
-0xff,0xff,0x01,0x60,0x06,0xe3,0xff,0xff,0xff,0xff,0x00,0xec,0x2e,0x58,0xff,0xff,
-0x01,0x60,0x06,0xe3,0xff,0xff,0xff,0xff,0x01,0xec,0xff,0xff,0xff,0xff,0x01,0x60,
-0x02,0xe3,0xff,0xff,0xff,0xff,0x00,0xec,0x2e,0x58,0xff,0xff,0x09,0x63,0x01,0xec,
-0xff,0xff,0xff,0xff,0x3f,0x40,0x08,0x26,0x04,0x00,0x00,0xec,0xcf,0x83,0xff,0xff,
-0xf6,0x02,0x2e,0x58,0xff,0xff,0x0e,0x63,0x60,0x40,0x80,0x2a,0x03,0x00,0x01,0x60,
-0x02,0xe3,0x02,0x00,0x01,0x60,0x06,0xe3,0x01,0xec,0xe0,0x84,0xff,0xff,0xff,0xff,
-0xff,0xff,0x00,0xec,0xf1,0x1f,0x01,0x60,0x02,0xe3,0xff,0xff,0xff,0xff,0x3f,0x40,
-0x08,0x26,0x08,0x00,0x01,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xec,
-0x00,0x64,0x01,0x00,0x01,0x64,0x00,0xbc,0x2e,0x58,0xff,0xff,0x0e,0x63,0xe0,0x84,
-0x3f,0x40,0x08,0x26,0xdc,0x84,0x01,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0x00,0xec,0xf5,0x1f,0xff,0xff,0x01,0x60,0x06,0xe3,0xff,0xff,0x01,0xec,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xec,0x01,0x60,0x02,0xe3,0x00,0x7f,0x2e,0x58,
-0xff,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0x74,0x01,0xff,0x00,0x85,0x01,0xff,0x00,
-0x18,0x02,0xe8,0x01,0xd9,0x01,0xa7,0x01,0xd4,0x01,0xdc,0x01,0xc8,0x02,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,
-0x00,0x00,0x13,0x03,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,
-0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,
-0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,
-0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,
-0xdc,0x02,0xdc,0x02,0x63,0x03,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,
-0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,
-0xdc,0x02,0x37,0x04,0xd5,0x03,0xdc,0x02,0xda,0x03,0xdc,0x02,0xdc,0x02,0xdc,0x02,
-0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,0xdc,0x02,
-0xdc,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x7e,0x00,0x08,0x11,0x00,0xf8,0x7f,0x00,0xfe,0x07,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,
-0xff,0xff,0xff,0xff,0x00,0x00,0xc1,0xc0,0x81,0xc1,0x40,0x01,0x01,0xc3,0xc0,0x03,
-0x80,0x02,0x41,0xc2,0x01,0xc6,0xc0,0x06,0x80,0x07,0x41,0xc7,0x00,0x05,0xc1,0xc5,
-0x81,0xc4,0x40,0x04,0x01,0xcc,0xc0,0x0c,0x80,0x0d,0x41,0xcd,0x00,0x0f,0xc1,0xcf,
-0x81,0xce,0x40,0x0e,0x00,0x0a,0xc1,0xca,0x81,0xcb,0x40,0x0b,0x01,0xc9,0xc0,0x09,
-0x80,0x08,0x41,0xc8,0x01,0xd8,0xc0,0x18,0x80,0x19,0x41,0xd9,0x00,0x1b,0xc1,0xdb,
-0x81,0xda,0x40,0x1a,0x00,0x1e,0xc1,0xde,0x81,0xdf,0x40,0x1f,0x01,0xdd,0xc0,0x1d,
-0x80,0x1c,0x41,0xdc,0x00,0x14,0xc1,0xd4,0x81,0xd5,0x40,0x15,0x01,0xd7,0xc0,0x17,
-0x80,0x16,0x41,0xd6,0x01,0xd2,0xc0,0x12,0x80,0x13,0x41,0xd3,0x00,0x11,0xc1,0xd1,
-0x81,0xd0,0x40,0x10,0x01,0xf0,0xc0,0x30,0x80,0x31,0x41,0xf1,0x00,0x33,0xc1,0xf3,
-0x81,0xf2,0x40,0x32,0x00,0x36,0xc1,0xf6,0x81,0xf7,0x40,0x37,0x01,0xf5,0xc0,0x35,
-0x80,0x34,0x41,0xf4,0x00,0x3c,0xc1,0xfc,0x81,0xfd,0x40,0x3d,0x01,0xff,0xc0,0x3f,
-0x80,0x3e,0x41,0xfe,0x01,0xfa,0xc0,0x3a,0x80,0x3b,0x41,0xfb,0x00,0x39,0xc1,0xf9,
-0x81,0xf8,0x40,0x38,0x00,0x28,0xc1,0xe8,0x81,0xe9,0x40,0x29,0x01,0xeb,0xc0,0x2b,
-0x80,0x2a,0x41,0xea,0x01,0xee,0xc0,0x2e,0x80,0x2f,0x41,0xef,0x00,0x2d,0xc1,0xed,
-0x81,0xec,0x40,0x2c,0x01,0xe4,0xc0,0x24,0x80,0x25,0x41,0xe5,0x00,0x27,0xc1,0xe7,
-0x81,0xe6,0x40,0x26,0x00,0x22,0xc1,0xe2,0x81,0xe3,0x40,0x23,0x01,0xe1,0xc0,0x21,
-0x80,0x20,0x41,0xe0,0x01,0xa0,0xc0,0x60,0x80,0x61,0x41,0xa1,0x00,0x63,0xc1,0xa3,
-0x81,0xa2,0x40,0x62,0x00,0x66,0xc1,0xa6,0x81,0xa7,0x40,0x67,0x01,0xa5,0xc0,0x65,
-0x80,0x64,0x41,0xa4,0x00,0x6c,0xc1,0xac,0x81,0xad,0x40,0x6d,0x01,0xaf,0xc0,0x6f,
-0x80,0x6e,0x41,0xae,0x01,0xaa,0xc0,0x6a,0x80,0x6b,0x41,0xab,0x00,0x69,0xc1,0xa9,
-0x81,0xa8,0x40,0x68,0x00,0x78,0xc1,0xb8,0x81,0xb9,0x40,0x79,0x01,0xbb,0xc0,0x7b,
-0x80,0x7a,0x41,0xba,0x01,0xbe,0xc0,0x7e,0x80,0x7f,0x41,0xbf,0x00,0x7d,0xc1,0xbd,
-0x81,0xbc,0x40,0x7c,0x01,0xb4,0xc0,0x74,0x80,0x75,0x41,0xb5,0x00,0x77,0xc1,0xb7,
-0x81,0xb6,0x40,0x76,0x00,0x72,0xc1,0xb2,0x81,0xb3,0x40,0x73,0x01,0xb1,0xc0,0x71,
-0x80,0x70,0x41,0xb0,0x00,0x50,0xc1,0x90,0x81,0x91,0x40,0x51,0x01,0x93,0xc0,0x53,
-0x80,0x52,0x41,0x92,0x01,0x96,0xc0,0x56,0x80,0x57,0x41,0x97,0x00,0x55,0xc1,0x95,
-0x81,0x94,0x40,0x54,0x01,0x9c,0xc0,0x5c,0x80,0x5d,0x41,0x9d,0x00,0x5f,0xc1,0x9f,
-0x81,0x9e,0x40,0x5e,0x00,0x5a,0xc1,0x9a,0x81,0x9b,0x40,0x5b,0x01,0x99,0xc0,0x59,
-0x80,0x58,0x41,0x98,0x01,0x88,0xc0,0x48,0x80,0x49,0x41,0x89,0x00,0x4b,0xc1,0x8b,
-0x81,0x8a,0x40,0x4a,0x00,0x4e,0xc1,0x8e,0x81,0x8f,0x40,0x4f,0x01,0x8d,0xc0,0x4d,
-0x80,0x4c,0x41,0x8c,0x00,0x44,0xc1,0x84,0x81,0x85,0x40,0x45,0x01,0x87,0xc0,0x47,
-0x80,0x46,0x41,0x86,0x01,0x82,0xc0,0x42,0x80,0x43,0x41,0x83,0x00,0x41,0xc1,0x81,
-0x81,0x80,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x63,0x03,0xff,0xff,0xbc,0x03,0x8b,0x03,0xdc,0x10,0x0e,0x00,0xfe,0xff,0xbc,0x03,
-0x8b,0x03,0xda,0x10,0x02,0x00,0x00,0xfd,0xbc,0x03,0x8b,0x03,0x8e,0x10,0x02,0x00,
-0x01,0xfd,0xbc,0x03,0x8b,0x03,0x90,0x10,0x06,0x00,0x02,0xfd,0xbc,0x03,0x8b,0x03,
-0xaa,0x10,0x08,0x00,0x03,0xfd,0xbc,0x03,0x8b,0x03,0xb2,0x10,0x0a,0x00,0x04,0xfd,
-0xbc,0x03,0x8b,0x03,0xc6,0x10,0x0a,0x00,0x05,0xfd,0xbc,0x03,0x8b,0x03,0xea,0x10,
-0x0c,0x00,0x06,0xfd,0xbc,0x03,0x8b,0x03,0xf8,0x10,0x0c,0x00,0x0a,0xfd,0xbc,0x03,
-0x8b,0x03,0x96,0x10,0x0c,0x00,0x0b,0xfd,0xbc,0x03,0x8b,0x03,0xa2,0x10,0x08,0x00,
-0x0c,0xfd,0xbc,0x03,0x8b,0x03,0xbc,0x10,0x0a,0x00,0x0d,0xfd,0xbc,0x03,0x8b,0x03,
-0xd0,0x10,0x0a,0x00,0xe0,0xfc,0x8f,0x03,0xa5,0x03,0x3c,0x00,0x02,0x00,0x09,0x00,
-0x06,0x00,0x74,0x01,0x0e,0x00,0x18,0x02,0x10,0x00,0xe8,0x01,0x12,0x00,0xd9,0x01,
-0x14,0x00,0xa7,0x01,0x16,0x00,0xd4,0x01,0x18,0x00,0xdc,0x01,0x1a,0x00,0xc8,0x02,
-0x0a,0x00,0x85,0x01,0x00,0x01,0x80,0x00,0x00,0x02,0x01,0x04,0x38,0x06,0x80,0x08,
-0x03,0x0a,0x04,0x0c,0x04,0x0e,0x00,0x10,0x00,0x12,0xc8,0x14,0x13,0x16,0x00,0x18,
-0x00,0x1a,0x00,0x1c,0x5c,0x1e,0xc1,0x20,0x1e,0x22,0x54,0x24,0x07,0x26,0x6a,0x28,
-0x12,0x2a,0x00,0x2c,0x00,0x2e,0x1c,0x30,0x20,0x32,0x82,0x34,0x08,0x36,0x7a,0x38,
-0xca,0x3a,0x24,0x3c,0xd6,0x3e,0x00,0x40,0x00,0x42,0x00,0x44,0x7f,0x46,0x8b,0x48,
-0x0f,0x4a,0x06,0x4c,0x0a,0x4e,0x0f,0x50,0x20,0x52,0x20,0x54,0x10,0x56,0x10,0x58,
-0x20,0x5a,0xee,0x5c,0x1a,0x5e,0x26,0x60,0x5b,0x62,0x00,0x04,0x00,0x2c,0x0c,0x2e,
-0x01,0x2c,0x10,0x2e,0x02,0x2c,0x14,0x2e,0x03,0x2c,0x18,0x2e,0x04,0x2c,0x1c,0x2e,
-0x05,0x2c,0x20,0x2e,0x06,0x2c,0x24,0x2e,0x07,0x2c,0x28,0x2e,0x08,0x2c,0x2e,0x2e,
-0x09,0x2c,0x34,0x2e,0x0a,0x2c,0x38,0x2e,0x0b,0x2c,0x3c,0x2e,0x0c,0x2c,0x3f,0x2e,
-0x0d,0x2c,0x43,0x2e,0x0e,0x2c,0x46,0x2e,0x0f,0x2c,0x48,0x2e,0x10,0x2c,0x4b,0x2e,
-0x11,0x2c,0x50,0x2e,0x12,0x2c,0x55,0x2e,0x13,0x2c,0x5a,0x2e,0x14,0x2c,0x63,0x2e,
-0x15,0x2c,0x6d,0x2e,0x16,0x2c,0x76,0x2e,0x17,0x2c,0x7f,0x2e,0x18,0x2c,0x7f,0x2e,
-0x19,0x2c,0x7f,0x2e,0x1a,0x2c,0x7f,0x2e,0x1b,0x2c,0x7f,0x2e,0x1c,0x2c,0x7f,0x2e,
-0x1d,0x2c,0x7f,0x2e,0x1e,0x2c,0x7f,0x2e,0x1f,0x2c,0x7f,0x2e,0xff,0xff,0x0e,0xf1,
-0x02,0x60,0x5f,0x64,0xc0,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0e,0xf1,
-0x02,0x60,0x2c,0x64,0xc0,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x45,0xff,
-0x0e,0xf1,0x02,0x60,0x15,0x64,0xc0,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0xf7,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x47,0xff,
-0x0e,0xf1,0x02,0x60,0x15,0x64,0xc0,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0x40,0xff,
-0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x41,0xff,
-0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x42,0xff,
-0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x43,0xff,
-0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x44,0xff,
-0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x45,0xff,
-0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0d,0xf7,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0x0f,0x7e,0x00,
-0x0a,0x01,0xff,0xff,0x47,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xb0,0xfe,0xb1,0xfe,0xb2,0xfe,0xb3,0xfe,0xff,0x00,0xff,0xff,
-0xff,0xff,0xff,0xff,0x0e,0xf1,0x03,0x60,0x0c,0x64,0xc0,0x98,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xb8,0xfe,0xb9,0xfe,0xba,0xfe,0xbb,0xfe,0xff,0x00,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0x20,0x4e,0x60,0x00,0x00,0x00,0x00,0x40,0x39,0x39,0x53,0x41,0x30,0x31,
-0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x80,0x01,0x00,0x00,0x00,0x00,0x00,0x17,0x00,
-0x02,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
-0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x02,0x00,0x01,0x00,
-0x01,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00,
-0x50,0x72,0x69,0x6d,0x61,0x72,0x79,0x20,0x46,0x27,0x73,0x20,0x20,0x00,0x46,0x33,
-0x2e,0x31,0x30,0x2d,0x30,0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x31,0x2f,0x31,
-0x37,0x2f,0x32,0x30,0x30,0x32,0x00,0x00,0x00,0x00,0x01,0x80,0xff,0xff,0xff,0xff,
-0xdc,0x10,0x7e,0x00,0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x96,0x10,0x7e,0x00,
-0x0c,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x26,0x0e,0x7e,0x00,0x02,0x00,0x00,0x00,
-0x06,0x00,0x00,0x00,0xbc,0x10,0x7e,0x00,0x0a,0x00,0x00,0x00,0x07,0x00,0x00,0x00,
-0xd0,0x10,0x7e,0x00,0x0a,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0xa2,0x10,0x7e,0x00,
-0x08,0x00,0x00,0x00,0xfe,0x17,0x7e,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0xf8,0x7f,0x00,0xfe,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x00,
-0x08,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x01,0x00,0x17,0x00,0x02,0x00,
-0x02,0x00,0x01,0x00,0x06,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x02,0x00,0x01,0x00,
-0x01,0x00,0x06,0x00,0x02,0x00,0x01,0x00,0x02,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
-0x7f,0xff
-};
-
-const u_int8_t spectrum24t_secsym[] = {
-0x46,0x49,0x4c,0x45,0x3a,0x20,0x45,0x53,0x45,0x43,0x53,0x59,0x4d,0x2c,0x54,0x33,
-0x2e,0x31,0x30,0x2d,0x30,0x34,0x2c,0x46,0x33,0x2e,0x31,0x30,0x2d,0x30,0x34,0x2c,
-0x30,0x31,0x2f,0x31,0x37,0x2f,0x32,0x30,0x30,0x32,0x20,0x53,0x45,0x43,0x4f,0x4e,
-0x44,0x41,0x52,0x59,0x20,0x32,0x32,0x20,0x32,0x34,0x30,0x20,0x31,0x32,0x20,0x35,
-0x34,0x20,0x0a,0x1a,0xfe,0x17,0x7e,0x00,0xf2,0x0f,0xde,0xc0,0xce,0xd0,0x0a,0x60,
-0x04,0x63,0xa3,0xd3,0x06,0xa3,0xdc,0x80,0x00,0xa8,0x0b,0x03,0xfa,0x02,0xf6,0xa3,
-0x18,0x60,0x00,0x64,0xbd,0xdb,0x00,0x60,0x7e,0x64,0xbd,0xdb,0xd0,0x60,0xce,0x64,
-0xa3,0xdb,0x0c,0x60,0x23,0x78,0xff,0xff,0x7f,0x60,0xc0,0x64,0x24,0x45,0xa4,0x80,
-0x7f,0x67,0x02,0x61,0x02,0x03,0x23,0x58,0xff,0xff,0x02,0x64,0x40,0x50,0x61,0xff,
-0xff,0xff,0x99,0xff,0x88,0xec,0x0e,0xe3,0x12,0xe3,0x1d,0xe3,0x22,0xe3,0x2a,0xe3,
-0x32,0xe3,0x3a,0xe3,0x80,0xed,0x42,0xe3,0x4a,0xe3,0x52,0xe3,0x5a,0xe3,0x62,0xe3,
-0x68,0xe3,0x70,0xe3,0x7a,0xe3,0x00,0xee,0x82,0xe3,0x8a,0xe3,0x92,0xe3,0x9a,0xe3,
-0xa2,0xe3,0xaa,0xe3,0xb2,0xe3,0xba,0xe3,0x01,0x60,0x14,0xe3,0x01,0x60,0x19,0xe3,
-0x01,0x60,0x20,0xe3,0x01,0x60,0x28,0xe3,0x21,0x60,0x7d,0xe7,0x68,0x60,0x7d,0xe7,
-0x10,0x60,0x7d,0xe7,0x7d,0xe7,0xf0,0x60,0x7d,0xe7,0xa5,0x60,0x7d,0xe7,0x7d,0xe7,
-0x36,0x60,0x7d,0xe7,0x6d,0x60,0x7d,0xe7,0x98,0x60,0x7d,0xe7,0x39,0x60,0x7d,0xe7,
-0x3f,0x60,0x7d,0xe7,0x42,0x60,0x7d,0xe7,0x0c,0x60,0x7d,0xe7,0xc2,0x60,0x7d,0xe7,
-0x7d,0xe7,0xa5,0x60,0x7d,0xe7,0x7d,0xe7,0x36,0x60,0x7d,0xe7,0x01,0x60,0x7d,0xe7,
-0xad,0x60,0x7d,0xe7,0x7d,0xe7,0x37,0x60,0x7d,0xe7,0x42,0x60,0x7d,0xe7,0x0e,0x60,
-0x7d,0xe7,0xc2,0x60,0x7d,0xe7,0x07,0x60,0x80,0xe7,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x80,0xe7,0xff,0xff,0xff,0xff,0x06,0xe3,
-0xff,0xff,0x98,0xff,0x73,0x60,0xeb,0x78,0xff,0xff,0x04,0xee,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0x00,0x69,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x01,0x16,
-0xfe,0x00,0x68,0x5e,0x00,0x7f,0x1f,0xfb,0x88,0xec,0x00,0xee,0xff,0xff,0xff,0xff,
-0x04,0xee,0xff,0xff,0xff,0xff,0x00,0xee,0xb0,0xfe,0xb1,0xfe,0xb2,0xfe,0xb3,0xfe,
-0xb8,0xfe,0xb9,0xfe,0xba,0xfe,0x10,0x64,0x40,0x40,0x00,0x64,0x40,0x41,0x40,0x42,
-0x40,0x54,0x40,0x55,0x40,0x5e,0x40,0x5a,0x40,0x5b,0x1d,0x60,0x8c,0x62,0xa2,0xd1,
-0x12,0x60,0x34,0x64,0xd0,0x80,0xff,0xff,0x20,0x02,0x1d,0x60,0x8e,0x62,0xa2,0xd1,
-0x55,0x60,0xaa,0x64,0xd0,0x80,0xff,0xff,0x18,0x02,0x1e,0x60,0x50,0x62,0xa2,0xd3,
-0xff,0xff,0x01,0xa4,0xa2,0xdb,0x08,0x60,0x2e,0x64,0xa0,0xd3,0xff,0xff,0x00,0xa4,
-0xe1,0xa0,0x17,0x03,0xe0,0x85,0x15,0x07,0x1e,0x60,0x52,0x62,0xff,0xff,0xc6,0x82,
-0xa2,0xd3,0xff,0xff,0x01,0xa4,0xa2,0xdb,0x0c,0x00,0x1e,0x60,0x50,0x62,0x00,0x64,
-0xa2,0xdb,0x1e,0x60,0x52,0x63,0x00,0x64,0x20,0x61,0xbd,0xdb,0xff,0xa1,0xff,0xff,
-0xfc,0x02,0x01,0x60,0x88,0x63,0x00,0x60,0x3e,0x61,0xfe,0x60,0x00,0x66,0x7f,0x60,
-0xfe,0x64,0x58,0xd0,0x59,0xd9,0xfd,0x1f,0x0f,0x60,0xce,0x63,0x0e,0x60,0x7e,0x61,
-0xfe,0x60,0x00,0x66,0x85,0x60,0x08,0x64,0x58,0xd0,0x59,0xd9,0xfd,0x1f,0x4a,0x60,
-0x8a,0x63,0x1e,0x60,0x90,0x61,0x00,0x64,0x59,0xdb,0xfe,0x1f,0x01,0x63,0x7f,0xfd,
-0x00,0x60,0x2a,0x63,0x0c,0x60,0x40,0x61,0x0e,0x60,0x7e,0x64,0x58,0xd1,0x59,0xd9,
-0xfd,0x1f,0x15,0x60,0xf8,0x61,0xa1,0xd3,0xff,0xff,0xe0,0x83,0xcb,0x83,0x59,0xd1,
-0x59,0xd3,0xa4,0xdb,0xfc,0x1f,0x00,0x60,0xfe,0x63,0xfe,0x60,0x00,0x65,0x45,0x4b,
-0xdb,0x60,0xfe,0x61,0xfe,0x60,0x00,0x65,0x81,0x60,0x88,0x64,0x65,0x46,0x58,0xd0,
-0x2b,0x46,0x59,0xd8,0xfb,0x1f,0x01,0x60,0xbe,0x63,0xdd,0x60,0x4e,0x61,0x82,0x60,
-0xd8,0x64,0x65,0x46,0x58,0xd0,0x2b,0x46,0x59,0xd8,0xfb,0x1f,0x00,0x60,0x0e,0x63,
-0xdf,0x60,0x1e,0x61,0x84,0x60,0xa8,0x64,0x65,0x46,0x58,0xd0,0x2b,0x46,0x59,0xd8,
-0xfb,0x1f,0x69,0x60,0x1e,0x64,0x7f,0xa4,0xe0,0x87,0x00,0x7f,0x1a,0xfb,0x0d,0x60,
-0x22,0x62,0x08,0x60,0x00,0x65,0xa2,0xd3,0xff,0xff,0xd4,0x80,0xff,0xff,0x0b,0x06,
-0xe0,0x84,0xe0,0x84,0xe0,0x84,0xcc,0x84,0x1d,0xfb,0x65,0x44,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0x1c,0xfb,0x65,0x44,0x80,0xa4,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xcc,0x84,
-0x1b,0xfb,0x1b,0xf1,0x1a,0xf3,0xff,0xff,0x9e,0xfb,0x7c,0x63,0x60,0x46,0x01,0xfc,
-0xdc,0x84,0xd0,0x80,0x00,0xfa,0xfa,0x04,0x9f,0xfb,0x1c,0xf3,0x60,0x46,0x00,0xa8,
-0x1d,0xf1,0x09,0x03,0x00,0xfa,0x01,0xfc,0x60,0x46,0x01,0xfc,0xdc,0x84,0xd0,0x80,
-0x00,0xfa,0xfa,0x04,0x9f,0xfb,0x00,0x64,0x00,0xfa,0x63,0x44,0x80,0x7f,0x01,0xfa,
-0x1b,0xf3,0x1a,0xf1,0xdc,0x84,0x50,0x93,0x33,0x44,0xfd,0xfb,0x00,0x60,0x7c,0x61,
-0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,0x46,0x45,0x19,0x60,0x58,0x4f,0xc4,0x78,
-0xff,0xff,0x1e,0x60,0x9e,0x62,0x66,0x44,0xa2,0xdb,0x1f,0xf1,0x01,0x65,0x64,0x40,
-0x10,0x2a,0x07,0x00,0xeb,0x60,0x19,0xe2,0x01,0x60,0x76,0x63,0x20,0x64,0xc5,0xfb,
-0x07,0x00,0xeb,0x60,0x19,0xe2,0x00,0x65,0x01,0x60,0x18,0x63,0x14,0x64,0xc2,0xfb,
-0x17,0x60,0x34,0x62,0xa2,0xdd,0x17,0x60,0x7e,0x62,0x65,0x44,0xa2,0xdb,0x00,0x60,
-0x30,0xe2,0x00,0x60,0x50,0xe2,0x00,0x60,0x79,0xe2,0x01,0x60,0x90,0xe2,0x01,0x60,
-0xd0,0xe2,0x01,0x60,0xf9,0xe2,0xb4,0xf3,0xb5,0xf1,0x60,0x45,0xc4,0x84,0xc0,0x84,
-0x24,0xfb,0x01,0x60,0x30,0x64,0xc0,0x84,0x25,0xfb,0x60,0x45,0xa0,0xa4,0x29,0xfb,
-0x01,0x60,0x60,0x64,0xc0,0x84,0x2d,0xfb,0xa0,0xa4,0x31,0xfb,0x00,0x60,0xf8,0x64,
-0xc0,0x84,0x26,0xfb,0xa0,0xa4,0x2a,0xfb,0x01,0x60,0x10,0x64,0xc0,0x84,0x2e,0xfb,
-0xa0,0xa4,0x32,0xfb,0x00,0x60,0xd5,0x64,0xc0,0x84,0x27,0xfb,0xa0,0xa4,0x2b,0xfb,
-0x00,0x60,0xde,0x64,0xc0,0x84,0x2f,0xfb,0xa0,0xa4,0x33,0xfb,0x00,0x60,0xcb,0x64,
-0xc0,0x84,0x28,0xfb,0xa0,0xa4,0x2c,0xfb,0x00,0x60,0xcf,0x64,0xc0,0x84,0x30,0xfb,
-0xa0,0xa4,0x34,0xfb,0xb5,0xf3,0x24,0xf1,0xc4,0x84,0x35,0xfb,0xc0,0x84,0x37,0xfb,
-0x00,0x64,0x40,0x50,0x63,0xff,0x00,0x64,0x40,0x54,0x40,0x55,0x40,0x41,0x40,0x42,
-0x40,0x5e,0xd1,0xfe,0x82,0xff,0x92,0xff,0x98,0xff,0x00,0x64,0x40,0x52,0x17,0x60,
-0x3a,0x65,0x1f,0xf3,0xa5,0xd1,0x60,0x40,0x10,0x2a,0xff,0xff,0x20,0x26,0x03,0x00,
-0x13,0x60,0x98,0x62,0x02,0x00,0x14,0x60,0x7a,0x62,0x64,0x44,0x3e,0x7f,0xa2,0xdb,
-0x1f,0xf3,0xff,0xff,0x17,0x60,0x5c,0x65,0xa5,0xd1,0x60,0x40,0x20,0x26,0x05,0x00,
-0x13,0x60,0xa6,0x62,0x64,0x44,0x4c,0x7f,0x04,0x00,0x14,0x60,0x82,0x62,0x64,0x44,
-0x46,0x7f,0xa2,0xdb,0x00,0x64,0x40,0x41,0x40,0x46,0x40,0x47,0x00,0xe1,0x11,0x60,
-0x8e,0x63,0x0e,0x60,0xac,0x64,0xa0,0xdd,0x00,0x60,0x13,0x66,0x3c,0x64,0x01,0xfa,
-0x0a,0x64,0x20,0xfa,0x87,0xff,0x97,0xff,0x08,0x60,0x28,0x62,0x23,0x60,0x45,0x64,
-0xa2,0xdb,0x66,0xff,0xff,0xff,0x65,0xff,0xff,0xff,0x64,0xff,0xff,0xff,0x62,0xff,
-0xff,0xff,0x61,0xff,0xff,0xff,0x3c,0x60,0xa6,0x65,0x0c,0x64,0xa5,0xdb,0x0e,0x60,
-0x5a,0x64,0x97,0xfb,0xff,0xff,0x2d,0xff,0x08,0x60,0x00,0x64,0xc8,0x81,0x3e,0x63,
-0x00,0x64,0x59,0xdb,0xfe,0x1f,0x04,0x60,0x41,0x76,0x10,0x60,0x5f,0x78,0xff,0xff,
-0x10,0x75,0x01,0x60,0x03,0xe8,0x99,0xff,0x08,0x60,0x2a,0x62,0x04,0x60,0xff,0x64,
-0xa2,0xdb,0x04,0x60,0xff,0xe5,0xff,0xff,0xff,0xff,0x10,0x60,0xdc,0xe0,0xff,0xff,
-0xff,0xff,0x98,0xff,0x30,0x60,0x7f,0x78,0xff,0xff,0xa1,0xff,0xff,0xff,0xfd,0x00,
-0x98,0xff,0x30,0x44,0x02,0xa8,0x00,0xe1,0x07,0x02,0x62,0xff,0x63,0xff,0x64,0xff,
-0x65,0xff,0x66,0xff,0xa1,0xff,0xff,0xff,0x82,0xff,0x91,0xff,0x99,0xff,0x88,0xff,
-0x6c,0x40,0x41,0xff,0xc4,0xe2,0x43,0xff,0x40,0x49,0x08,0xe1,0x10,0x60,0x81,0x78,
-0xff,0xff,0x98,0xff,0x30,0x44,0x02,0xa8,0x00,0xe1,0x02,0x02,0xa1,0xff,0xff,0xff,
-0x00,0x64,0xdc,0xfb,0x82,0xff,0x92,0xff,0x98,0xff,0x88,0xff,0x72,0x44,0x60,0x52,
-0x03,0x04,0x01,0x64,0x40,0x40,0x05,0x00,0xdc,0x80,0xff,0xff,0x02,0x02,0x01,0x64,
-0x40,0x40,0x48,0xe2,0xe2,0xf3,0xff,0xff,0x60,0x40,0x00,0x3a,0x09,0x00,0x67,0x60,
-0x84,0x65,0xa5,0xd1,0xff,0xff,0x64,0x40,0x00,0x3a,0x02,0x00,0x64,0xe2,0x01,0x70,
-0x6d,0xe2,0xbc,0xff,0xb5,0xff,0xff,0x64,0x40,0x4b,0x00,0x64,0x40,0x4d,0x40,0x47,
-0xd7,0xfb,0x22,0xfb,0x00,0xe1,0x08,0x64,0x40,0x4c,0x26,0x44,0x02,0xb4,0x40,0x46,
-0x65,0xf3,0xff,0xff,0x60,0x40,0x04,0x26,0x02,0x00,0x00,0x3a,0x03,0x00,0x68,0xe2,
-0xc8,0xe2,0x68,0x00,0xb4,0xf1,0x02,0x64,0x64,0x56,0x60,0x54,0xcd,0xe2,0xc4,0xe2,
-0x6c,0x40,0x07,0x60,0x80,0xe8,0x44,0xe2,0x64,0xe2,0x46,0xff,0x47,0xff,0x67,0x60,
-0x5c,0x62,0x01,0x64,0xa2,0xdb,0x9c,0xfe,0xff,0xff,0x0b,0x04,0xcf,0xf3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x06,0x02,0x01,0x64,0x40,0xfb,0x26,0x44,0xfd,0xb4,0x40,0x46,
-0x05,0xff,0x27,0x44,0x06,0x22,0x06,0x00,0xf9,0xb4,0x40,0x47,0x02,0x64,0x21,0xfb,
-0xc0,0xfe,0x03,0x00,0x20,0x64,0x21,0xfb,0xc0,0xfe,0x99,0xff,0x3d,0x44,0xf7,0xb4,
-0x40,0x5d,0x98,0xff,0x99,0xff,0x3c,0x44,0x7f,0xb4,0x10,0xbc,0x40,0x5c,0x3e,0x44,
-0x7c,0xb4,0x08,0xbc,0x40,0x5e,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0x99,0xff,0x3d,0x44,0x10,0xbc,0x00,0x7f,0x40,0x5d,0x98,0xff,0xbc,0xff,
-0xff,0xff,0xb5,0xff,0xff,0xff,0x99,0xff,0x07,0x60,0x80,0xe9,0x98,0xff,0xff,0xff,
-0xff,0xff,0x80,0xe9,0xff,0xff,0xff,0xff,0xb7,0xff,0xb4,0xff,0x99,0xff,0x3e,0x44,
-0x02,0xbc,0x00,0x7f,0x40,0x5e,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0x46,0xff,0x47,0xff,0x0e,0x60,0xac,0x64,0xa0,0xd7,0xff,0xff,0xff,0xff,0x98,0xff,
-0x30,0x44,0x02,0xa8,0x00,0xe1,0x0f,0x03,0x83,0xff,0x8d,0xff,0x00,0x64,0x40,0x40,
-0x40,0x44,0x40,0x43,0x40,0x42,0x40,0x41,0x1a,0x60,0xcc,0x64,0x40,0x4e,0x3c,0x60,
-0x6a,0x64,0x40,0x4d,0xe3,0xe1,0x19,0x60,0x56,0x78,0xff,0xff,0x98,0xff,0x30,0x44,
-0x02,0xa8,0x00,0xe1,0x02,0x02,0xa1,0xff,0xff,0xff,0x84,0xff,0x88,0xff,0x98,0xff,
-0x99,0xff,0xf2,0xe6,0xda,0xe6,0x98,0xff,0x1b,0x60,0x20,0x64,0x40,0x40,0xa8,0xf3,
-0x80,0xfb,0x0a,0x64,0x40,0x4b,0x1e,0x60,0x98,0x65,0xb8,0xf3,0xff,0xff,0xa5,0xdb,
-0x01,0x64,0x8c,0xfb,0x00,0x64,0x8e,0xfb,0x8d,0xfb,0x40,0x5c,0x1b,0x60,0x20,0x78,
-0xff,0xff,0x98,0xff,0x88,0xe2,0x30,0x44,0x00,0xe1,0x02,0xa8,0x85,0xff,0x02,0x02,
-0xa1,0xff,0xff,0xff,0x88,0xff,0x99,0xff,0x00,0x60,0x00,0xeb,0xff,0xff,0xff,0xff,
-0x00,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0x4f,0x60,0xf3,0xea,0x4f,0x60,0x36,0xeb,
-0x43,0x60,0x40,0xea,0x43,0x60,0xe4,0xeb,0x44,0x60,0x52,0xea,0x44,0x60,0x34,0xeb,
-0x45,0x60,0x7d,0xea,0x45,0x60,0x58,0xeb,0x47,0x60,0x8b,0xea,0x47,0x60,0xd0,0xeb,
-0x48,0x60,0x47,0xea,0x48,0x60,0xc3,0xeb,0x49,0x60,0xa0,0xea,0x49,0x60,0xfd,0xeb,
-0x4a,0x60,0xb2,0xea,0x4a,0x60,0x34,0xeb,0x4b,0x60,0xc1,0xea,0x4b,0x60,0x58,0xeb,
-0x4c,0x60,0xd7,0xea,0x4c,0x60,0xc0,0xeb,0x4d,0x60,0xeb,0xea,0x4d,0x60,0xd0,0xeb,
-0x4e,0x60,0xa0,0xea,0x4e,0x60,0x91,0xeb,0x40,0x60,0xf0,0xea,0x40,0x60,0xfc,0xeb,
-0x41,0x60,0x24,0xea,0x41,0x60,0xa2,0xeb,0x42,0x60,0x20,0xea,0x42,0x60,0x20,0xeb,
-0x3a,0x5c,0x80,0x2b,0x12,0x00,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,
-0x09,0x00,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,0x03,0x00,0x8b,0xff,
-0x74,0x40,0x88,0xff,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,0xff,0xff,
-0x31,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0x00,0x60,0x00,0xea,0x3a,0x5c,0x80,0x27,
-0x06,0x00,0x31,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0x00,0x60,0x00,0xea,0x67,0x60,
-0xcc,0x64,0x3a,0x5c,0xa0,0xd9,0xff,0xff,0x30,0x60,0x00,0xea,0xff,0xff,0xff,0xff,
-0x67,0x60,0xcc,0x64,0x3a,0x5c,0xa0,0xd9,0x3a,0x5c,0x40,0x27,0xfc,0x00,0xa0,0xd9,
-0x00,0x60,0x00,0xeb,0xa0,0x60,0x00,0xeb,0xc0,0x60,0x00,0xeb,0x30,0x60,0x00,0xeb,
-0x67,0x60,0xce,0x64,0x3b,0x5c,0xa0,0xd9,0x3b,0x5c,0x40,0x27,0xfc,0x00,0xa0,0xd9,
-0x67,0x60,0xcc,0x64,0x3a,0x5c,0xa0,0xd9,0x98,0xff,0xc0,0x60,0x00,0xeb,0x00,0x64,
-0x3e,0xfb,0x40,0xfb,0xff,0xff,0x67,0x60,0x4c,0x62,0x00,0x64,0xa2,0xdb,0x0a,0x64,
-0x40,0x48,0x03,0x60,0xe8,0x64,0x40,0x4b,0x67,0x60,0x4a,0x62,0x05,0x60,0xdc,0x64,
-0xa2,0xdb,0x1e,0x64,0x40,0x4c,0x69,0xe1,0x04,0x60,0x00,0x71,0x8d,0xe2,0x00,0x64,
-0x40,0x40,0xf8,0x60,0x89,0x78,0xff,0xff,0xa2,0xff,0x98,0xff,0x30,0x44,0x02,0xa8,
-0x00,0xe1,0x28,0x03,0x86,0xff,0x88,0xff,0x18,0x60,0xc6,0x65,0x64,0x64,0xa5,0xdb,
-0xff,0xff,0x00,0x64,0x40,0x46,0x58,0xfb,0x28,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,
-0x28,0x60,0x58,0x4f,0x87,0x78,0xff,0xff,0x29,0x60,0x58,0x4f,0x0f,0x78,0xff,0xff,
-0x28,0x60,0x58,0x4f,0x3b,0x78,0xff,0xff,0x27,0x60,0x58,0x4f,0xe8,0x78,0xff,0xff,
-0x27,0x60,0x58,0x4f,0xd1,0x78,0xff,0xff,0x27,0x60,0x58,0x4f,0xff,0x78,0xff,0xff,
-0x13,0xe1,0xa3,0xff,0x3d,0x60,0x2f,0x78,0xff,0xff,0x0f,0x4e,0x01,0x60,0xe4,0x61,
-0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,
-0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,
-0x66,0x44,0x15,0xfb,0x07,0xe1,0xa3,0xff,0x04,0x60,0x41,0x76,0x00,0x60,0x00,0x7c,
-0x08,0x60,0x14,0x64,0xa0,0xd9,0xae,0xff,0x30,0x60,0x7f,0x78,0xff,0xff,0xa1,0xff,
-0xff,0xff,0x2c,0x45,0xb8,0x3f,0x41,0xff,0x30,0x44,0x20,0xb4,0x34,0x91,0x9f,0xfe,
-0xff,0xff,0x43,0x05,0x29,0x44,0x05,0x22,0xf2,0x00,0x04,0x26,0x3a,0x00,0x01,0x2a,
-0xee,0x00,0x44,0xff,0xc8,0x74,0xcd,0xe2,0x0c,0xe1,0x29,0x44,0xfe,0xb4,0x40,0x49,
-0x24,0x41,0xe1,0x81,0x00,0x60,0xc8,0x65,0xc5,0x94,0x0c,0xe1,0xe0,0x00,0x1a,0xff,
-0xde,0x00,0xdd,0x00,0x41,0xff,0x40,0x64,0xa0,0xfb,0x3e,0x44,0x01,0x26,0xd7,0x00,
-0x08,0x00,0xc4,0xe2,0x41,0x64,0xa0,0xfb,0x3e,0x44,0x01,0x2a,0x02,0x00,0x62,0xff,
-0x09,0x00,0x01,0x64,0xdc,0xfb,0x67,0x60,0x56,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x1a,0xff,0x67,0x60,0xc8,0x62,0x01,0x64,0xa2,0xdb,0x08,0xe1,0x00,0x64,
-0x40,0x49,0x72,0x52,0x32,0x7b,0x4d,0xe2,0x44,0xff,0xb9,0x00,0xb8,0x00,0xb7,0x00,
-0xb6,0x00,0x29,0x44,0xfb,0xb4,0x40,0x49,0xb6,0x00,0x65,0xf3,0xff,0xff,0xff,0xff,
-0x04,0x2a,0x04,0x00,0xbf,0xfe,0x10,0x60,0xb3,0x78,0xff,0xff,0x99,0xf1,0x99,0xff,
-0x64,0x40,0x02,0x3b,0x03,0x00,0x11,0x60,0x5f,0x78,0xff,0xff,0x03,0x60,0xe8,0x74,
-0xcd,0xe2,0x04,0xe1,0xa1,0xff,0xff,0xff,0x3c,0x44,0x6f,0xb4,0x40,0x5c,0x00,0x6b,
-0x3e,0x44,0x74,0xb4,0x04,0xbc,0x40,0x5e,0xff,0xff,0xff,0xff,0x02,0xbd,0x45,0x5e,
-0xff,0xff,0xff,0xff,0x40,0x5e,0x00,0xe1,0x00,0x7c,0x15,0x60,0x22,0x62,0x58,0x4f,
-0x18,0x00,0x58,0x4f,0x16,0x00,0x58,0x4f,0x14,0x00,0x58,0x4f,0x12,0x00,0x01,0x7c,
-0x58,0x4f,0x0f,0x00,0x58,0x4f,0x0d,0x00,0x3d,0x44,0x7f,0xb4,0x40,0x5d,0xff,0xff,
-0xff,0xff,0x80,0xbc,0x40,0x5d,0xbf,0xfe,0x2d,0xff,0x08,0xe1,0x10,0x60,0x81,0x78,
-0xff,0xff,0x02,0x65,0xa2,0xd3,0x02,0xa2,0x60,0x47,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x81,0x06,0x63,0xe1,0x81,0x3d,0x44,0x80,0xb4,0x10,0xbc,0x02,0x24,0x04,0xbc,
-0x40,0x5d,0xff,0xff,0x34,0x9d,0xf6,0x1f,0xff,0xff,0xff,0xff,0x40,0x5d,0xa2,0xd3,
-0x02,0xa2,0x60,0x47,0x60,0x41,0x0e,0x63,0xe1,0x81,0x3d,0x44,0x80,0xb4,0x10,0xbc,
-0x02,0x24,0x04,0xbc,0x40,0x5d,0xff,0xff,0x34,0x9d,0xf6,0x1f,0xff,0xff,0xff,0xff,
-0x40,0x5d,0xa2,0xd3,0x02,0xa2,0x60,0x47,0x60,0x41,0x0e,0x63,0xe1,0x81,0x3d,0x44,
-0x80,0xb4,0x10,0xbc,0x02,0x24,0x04,0xbc,0x40,0x5d,0xff,0xff,0x34,0x9d,0xf6,0x1f,
-0xff,0xff,0xff,0xff,0x40,0x5d,0x64,0x40,0x01,0x26,0x08,0x00,0x3c,0x44,0x5f,0xb4,
-0x20,0x65,0x34,0x9c,0xff,0xff,0xff,0xff,0x40,0x5c,0x05,0x00,0x01,0x65,0x34,0x9d,
-0xff,0xff,0xff,0xff,0x40,0x5d,0x2f,0x58,0xff,0xff,0x99,0xf1,0x00,0xe1,0x64,0x44,
-0x00,0x7f,0xe0,0x85,0xc4,0x84,0xe0,0x85,0x3c,0x44,0x6f,0xb4,0x40,0x5c,0x00,0x6b,
-0x3e,0x44,0x74,0xb4,0x40,0x5e,0x01,0x7c,0x15,0x60,0x46,0x62,0xc6,0x82,0x58,0x4f,
-0xa0,0x00,0x01,0x60,0xf4,0x64,0x60,0x54,0xcd,0xe2,0x32,0x44,0x08,0x2b,0xfd,0x00,
-0x3c,0x44,0x7f,0xb4,0x10,0xbc,0x40,0x5c,0x62,0xff,0x01,0x7c,0x08,0x60,0x2a,0x64,
-0xa0,0xd9,0x9a,0xf3,0xbf,0xfe,0x60,0x40,0x05,0x36,0x2d,0xff,0x07,0x36,0xd8,0xfe,
-0x08,0xe1,0x10,0x60,0x81,0x78,0xff,0xff,0xdc,0xf3,0xff,0xff,0x60,0x40,0x00,0x36,
-0x03,0x00,0x0e,0x60,0x8b,0x78,0xff,0xff,0x00,0x64,0xa0,0xfb,0x0a,0x64,0x40,0x4c,
-0x19,0xff,0x20,0x44,0x01,0x2a,0x04,0x00,0x00,0x64,0x40,0x40,0xa1,0xf3,0x09,0x00,
-0x1a,0xe1,0x00,0x64,0xd0,0xfb,0x31,0x44,0x01,0x26,0x1b,0xe1,0xa1,0xff,0xff,0xff,
-0xb9,0x3f,0x72,0x45,0xdc,0x84,0xa1,0xfb,0x60,0x55,0x65,0x52,0x11,0x64,0xa0,0xfb,
-0xa2,0xf3,0x06,0x04,0xdc,0x84,0xa2,0xfb,0xa3,0xf3,0x02,0x04,0xdc,0x84,0xa3,0xfb,
-0x4b,0xf3,0xff,0xff,0xfe,0xa0,0x65,0xf3,0xe3,0x04,0x60,0x40,0x02,0x2a,0xe0,0x00,
-0x99,0xff,0x3d,0x44,0x7f,0xb4,0x00,0x7f,0x40,0x5d,0x80,0xbc,0xff,0xff,0xff,0xff,
-0x40,0x5d,0x98,0xff,0x00,0x64,0x4b,0xfb,0xd3,0x00,0x22,0xf1,0x43,0xff,0x64,0x40,
-0x07,0x26,0x03,0x00,0x12,0x60,0x2e,0x78,0xff,0xff,0x6c,0x40,0x03,0xe1,0x00,0x6b,
-0x99,0xff,0x3e,0x44,0x01,0xbc,0x00,0x7f,0x40,0x5e,0xdd,0xf1,0x3d,0x44,0xe7,0xb4,
-0x40,0x5d,0x3e,0x44,0xed,0xb4,0xb0,0x84,0x40,0x5e,0x3d,0x44,0x08,0xbc,0x40,0x5d,
-0x98,0xff,0x05,0x64,0xcc,0x84,0xff,0xff,0xfd,0x02,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x99,0xff,0x3e,0x44,0x77,0xb4,0x80,0xbc,
-0x40,0x5e,0x98,0xff,0x3c,0x46,0x1c,0xf0,0x53,0xf3,0x64,0x41,0x08,0xb1,0x60,0x45,
-0x03,0x22,0x00,0x61,0xb5,0x85,0x2b,0x5c,0xd1,0x80,0x1f,0xf1,0x0b,0x03,0x41,0x4b,
-0x38,0x64,0x65,0x40,0x08,0x2a,0x80,0x64,0x60,0x48,0x88,0x6a,0xff,0xff,0xff,0xff,
-0x01,0x16,0xfe,0x00,0x7f,0xf1,0x10,0x64,0x64,0x40,0x0e,0x36,0xb4,0x85,0x65,0x48,
-0x8a,0x6a,0xff,0xff,0xff,0xff,0x01,0x16,0xfe,0x00,0x4b,0xf3,0xff,0xff,0x00,0xa0,
-0xff,0xff,0x0a,0x03,0x99,0xff,0x3d,0x44,0x7f,0xb4,0x00,0x7f,0x40,0x5d,0x80,0xbd,
-0x00,0x64,0x4b,0xfb,0x45,0x5d,0x98,0xff,0x22,0xf1,0x23,0xf3,0x64,0x45,0x00,0xbc,
-0x00,0x64,0x4d,0x03,0x00,0x61,0x23,0xfb,0x5e,0xf1,0xa4,0xf3,0x64,0x40,0x04,0x2a,
-0x06,0x00,0x60,0x45,0x73,0x44,0xd4,0x84,0xe7,0xa0,0x0d,0x0e,0x0c,0x04,0x28,0x60,
-0xda,0x63,0x72,0x45,0x65,0x44,0xd4,0xfb,0x04,0x05,0x65,0x44,0xdc,0x80,0xff,0xff,
-0x05,0x02,0x01,0x64,0x40,0x40,0x11,0x60,0x8e,0x78,0xff,0xff,0xff,0x60,0xf0,0x64,
-0xd4,0x80,0x20,0xa4,0xf8,0x04,0xd4,0x80,0xff,0xff,0xf5,0x07,0x02,0xfe,0xbd,0xd3,
-0xff,0xff,0x44,0x8a,0x02,0x24,0xdd,0x81,0x02,0x24,0xdd,0x81,0xbd,0xd3,0xa1,0xf1,
-0x61,0x45,0xc0,0x84,0x00,0x61,0x02,0x24,0x01,0xb9,0xc4,0x84,0x60,0x55,0x2a,0x52,
-0xa1,0xfb,0x02,0x24,0x01,0xb9,0xbd,0xd3,0xa2,0xf1,0x61,0x45,0xc0,0x84,0x00,0x61,
-0x02,0x24,0x01,0xb9,0xc4,0x84,0xa2,0xfb,0x02,0x24,0x01,0xb9,0xbd,0xd3,0xa3,0xf1,
-0x61,0x45,0xc0,0x84,0xc4,0x84,0xa3,0xfb,0x22,0xf3,0xff,0xff,0x60,0x45,0x65,0x40,
-0x01,0x2a,0x07,0x00,0x3c,0x44,0x40,0x42,0x22,0xf3,0xff,0xff,0xfe,0xb4,0x22,0xfb,
-0x06,0x00,0x11,0x60,0x8e,0x78,0xff,0xff,0x11,0x60,0x8e,0x78,0xff,0xff,0x07,0x64,
-0xa0,0xfb,0x22,0x46,0x0f,0xf0,0xff,0xff,0x64,0x40,0x01,0x2a,0x03,0x00,0x16,0x60,
-0x40,0x78,0xff,0xff,0x15,0x60,0x3b,0x78,0xff,0xff,0x27,0x44,0x04,0x2a,0x09,0x00,
-0xfb,0xb4,0x40,0x47,0x3c,0x46,0x02,0x64,0x21,0xfb,0xc0,0xfe,0x11,0x60,0x8e,0x78,
-0xff,0xff,0x27,0x44,0x02,0x2a,0x08,0x00,0xfd,0xb4,0x40,0x47,0x06,0x64,0x21,0xfb,
-0xc0,0xfe,0x11,0x60,0x8e,0x78,0xff,0xff,0x02,0x0a,0x00,0x64,0x60,0x50,0x11,0x60,
-0x8e,0x78,0xff,0xff,0x01,0x60,0x2c,0x74,0xcd,0xe2,0x46,0xff,0x47,0xff,0x01,0x64,
-0x57,0xfb,0x83,0xe1,0x00,0x65,0x26,0x44,0x02,0x26,0x09,0x00,0x3e,0x44,0x34,0x81,
-0xff,0xff,0x05,0x03,0x45,0x5e,0x26,0x44,0x02,0xbc,0x40,0x46,0xd1,0xfe,0x0c,0x64,
-0x40,0x4c,0x19,0xff,0xa1,0xff,0x4c,0x4e,0x01,0x25,0x00,0x00,0x01,0x64,0xd0,0xfb,
-0x4b,0x74,0xcd,0xe2,0xf0,0x60,0x00,0x78,0x00,0x61,0x46,0xff,0x47,0xff,0x11,0x60,
-0x8e,0x78,0xff,0xff,0x99,0xff,0x3e,0x44,0xfd,0xb4,0x40,0x5e,0x98,0xff,0xb5,0xff,
-0xbc,0xff,0x46,0xff,0xb7,0xff,0xb4,0xff,0xff,0xff,0xff,0xff,0x84,0x60,0x1d,0x7d,
-0xb5,0xff,0xff,0xff,0x99,0xff,0x07,0x60,0x80,0xe9,0x98,0xff,0xff,0xff,0xff,0xff,
-0x80,0xe9,0xff,0xff,0xff,0xff,0xb7,0xff,0xb4,0xff,0xff,0xff,0x99,0xff,0x3e,0x44,
-0x02,0xbc,0x00,0x7f,0x40,0x5e,0x98,0xff,0xff,0xff,0xff,0xff,0x46,0xff,0x47,0xff,
-0x26,0x43,0x04,0x2a,0x50,0x00,0xfb,0xb3,0x43,0x46,0x04,0xbb,0x2a,0x44,0x23,0xfa,
-0x20,0x44,0xe8,0x80,0x00,0x64,0x40,0x40,0xa1,0xf3,0x05,0x04,0x72,0x45,0xdc,0x84,
-0xa1,0xfb,0x60,0x55,0x65,0x52,0x24,0xfa,0xa2,0xf3,0x02,0x04,0xdc,0x84,0xa2,0xfb,
-0x27,0xfa,0xa3,0xf3,0x02,0x04,0xdc,0x84,0xa3,0xfb,0x28,0xfa,0x2a,0x44,0xdc,0x80,
-0xff,0xff,0x01,0x02,0x58,0x80,0xf4,0xb3,0x32,0x40,0x01,0x2a,0x08,0x00,0x04,0xbb,
-0x0f,0xfc,0x01,0x5d,0xdc,0xfe,0x05,0xff,0x11,0x60,0x8e,0x78,0xff,0xff,0x2d,0x44,
-0x0c,0x26,0x0d,0x00,0xcf,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0x04,0x03,0x26,0x44,
-0x02,0xbc,0x40,0x46,0x36,0x00,0x0f,0xfc,0x01,0x5d,0xdc,0xfe,0x05,0xff,0x27,0x44,
-0x04,0x2a,0x09,0x00,0xfb,0xb4,0x40,0x47,0x2d,0x44,0x58,0x36,0x37,0x00,0x02,0x64,
-0x21,0xfb,0xc0,0xfe,0x26,0x00,0x02,0x2a,0x24,0x00,0xfd,0xb4,0x40,0x47,0x06,0x64,
-0x21,0xfb,0xc0,0xfe,0x1e,0x00,0x2a,0x44,0xdc,0x80,0xff,0xff,0x01,0x02,0x58,0x80,
-0x27,0x44,0x80,0x2a,0x13,0x00,0x7f,0xb4,0x40,0x47,0x27,0x44,0x04,0x2a,0x06,0x00,
-0xfb,0xb4,0x40,0x47,0x02,0x64,0x21,0xfb,0xc0,0xfe,0x08,0x00,0x27,0x44,0x02,0x2a,
-0x05,0x00,0xfd,0xb4,0x40,0x47,0x06,0x64,0x21,0xfb,0xc0,0xfe,0x11,0x60,0x8e,0x78,
-0xff,0xff,0x26,0x44,0x80,0x2a,0x07,0x00,0x20,0xf1,0x70,0x44,0xd0,0x80,0xff,0xff,
-0x02,0x05,0x64,0xe2,0x64,0x50,0x11,0x60,0x8e,0x78,0xff,0xff,0x06,0x64,0xa0,0xfb,
-0x22,0x46,0x29,0xf0,0xf7,0x60,0xff,0x64,0xa0,0x84,0xa2,0xda,0x04,0x64,0x03,0xfa,
-0x00,0xf2,0xff,0xff,0x04,0xfa,0x01,0x64,0x21,0xfb,0xc0,0xfe,0x11,0x60,0x8e,0x78,
-0xff,0xff,0x04,0x64,0xa0,0xfb,0x46,0xff,0x47,0xff,0x0a,0x64,0x40,0x4c,0x19,0xff,
-0x03,0xe1,0x29,0xf2,0xff,0xff,0x0c,0xb4,0xff,0xff,0x08,0x3a,0x0f,0x00,0x17,0x60,
-0xea,0x64,0xa0,0xd3,0xff,0xff,0xe8,0x84,0xe0,0x84,0x60,0x45,0x17,0x60,0xee,0x64,
-0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0xff,0xff,0xa1,0xff,0xff,0xff,
-0xb9,0x3f,0x00,0x6b,0x99,0xff,0x3e,0x44,0x01,0xbc,0x00,0x7f,0x40,0x5e,0xdd,0xf1,
-0x3d,0x44,0xe7,0xb4,0x40,0x5d,0x3e,0x44,0xed,0xb4,0xb0,0x84,0x40,0x5e,0x3d,0x44,
-0x08,0xbc,0x40,0x5d,0x98,0xff,0x05,0x64,0xcc,0x84,0xff,0xff,0xfd,0x02,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x99,0xff,0x3e,0x44,
-0x77,0xb4,0x80,0xbc,0x40,0x5e,0x98,0xff,0x22,0xf2,0x08,0x63,0xff,0xff,0x08,0x2a,
-0x00,0x63,0x28,0x44,0x04,0x26,0x0a,0x00,0x25,0x44,0x06,0xfa,0x60,0x46,0x01,0xf2,
-0xff,0xff,0x61,0x5e,0x03,0x2b,0x01,0xfa,0x21,0x46,0x0d,0x00,0x28,0x44,0xb4,0x36,
-0x0a,0x00,0x08,0x63,0x3c,0x46,0x1c,0xf2,0x53,0xf3,0x60,0x40,0xf0,0x27,0x7e,0x00,
-0x5c,0x07,0x08,0x2a,0x00,0x63,0x21,0x46,0x60,0x45,0x1b,0x00,0x20,0xf2,0xff,0xff,
-0x00,0x7f,0xf6,0xa0,0x00,0x65,0x07,0x03,0xec,0xa0,0x01,0x65,0x04,0x03,0xc9,0xa0,
-0x02,0x65,0x01,0x03,0x03,0x65,0x29,0xf2,0xff,0xff,0x0c,0xb4,0xff,0xff,0x00,0x36,
-0x08,0x00,0x59,0x60,0x68,0x62,0xa2,0xd3,0xff,0xff,0xd4,0x80,0xff,0xff,0x01,0x05,
-0x60,0x45,0x45,0x45,0x65,0x40,0x03,0x22,0x00,0x63,0xb7,0x85,0x2b,0x5c,0xd3,0x80,
-0xff,0xff,0x0b,0x03,0x43,0x4b,0x38,0x64,0x65,0x40,0x08,0x2a,0x80,0x64,0x60,0x48,
-0x88,0x6a,0xff,0xff,0xff,0xff,0x01,0x16,0xfe,0x00,0x7f,0xf1,0x00,0x64,0x64,0x40,
-0x0e,0x36,0x10,0x64,0xb4,0x85,0x65,0x48,0x8a,0x6a,0xff,0xff,0xff,0xff,0x01,0x16,
-0xfe,0x00,0x4b,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0x0a,0x03,0x99,0xff,0x3d,0x44,
-0x7f,0xb4,0x00,0x7f,0x40,0x5d,0x80,0xbd,0x00,0x64,0x4b,0xfb,0x45,0x5d,0x98,0xff,
-0x2a,0x44,0x23,0xfa,0x20,0x44,0xe8,0x80,0x00,0x64,0x40,0x40,0xa1,0xf3,0x05,0x04,
-0x72,0x45,0xdc,0x84,0xa1,0xfb,0x60,0x55,0x65,0x52,0x24,0xfa,0xa2,0xf3,0x02,0x04,
-0xdc,0x84,0xa2,0xfb,0x27,0xfa,0xa3,0xf3,0x02,0x04,0xdc,0x84,0xa3,0xfb,0x28,0xfa,
-0x2a,0x44,0xdc,0x80,0xff,0xff,0x01,0x02,0x58,0x80,0x08,0x29,0x09,0x00,0x22,0xf1,
-0xff,0xff,0x64,0x40,0x07,0x2e,0x04,0x00,0x43,0xff,0x10,0x64,0x21,0xfb,0xc0,0xfe,
-0x2d,0x44,0x08,0x22,0x03,0x00,0x0d,0xb0,0x0c,0x3a,0x0a,0x00,0x26,0x43,0x84,0xbb,
-0xf4,0xb3,0x21,0x46,0x0f,0xfc,0x00,0x64,0x40,0x46,0x01,0x5d,0xdc,0xfe,0x05,0xff,
-0x09,0x64,0xa0,0xfb,0x28,0x44,0xb4,0x3a,0x0b,0x00,0x27,0x44,0x06,0x22,0x05,0x00,
-0xf9,0xb4,0x40,0x47,0x02,0x64,0x21,0xfb,0xc0,0xfe,0x17,0x60,0x32,0x78,0xff,0xff,
-0xa4,0x36,0x0a,0x00,0x04,0x26,0x0b,0x00,0x27,0x44,0x06,0x22,0x05,0x00,0xf9,0xb4,
-0x40,0x47,0x02,0x64,0x21,0xfb,0xc0,0xfe,0x16,0x60,0xeb,0x78,0xff,0xff,0x28,0x44,
-0xd4,0x3a,0x5b,0x00,0x48,0xe2,0x1c,0x42,0x22,0x46,0x1c,0xf2,0xff,0xff,0x07,0xb4,
-0xfc,0xa0,0x03,0x64,0x01,0x02,0x1c,0xfa,0x27,0xf0,0x01,0x60,0x00,0x64,0xc0,0x84,
-0x27,0xfa,0x26,0xf0,0xff,0x60,0x00,0x64,0xa0,0x84,0x26,0xfa,0x59,0x60,0xec,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x53,0xf3,0x00,0x7c,0x60,0x43,0xe0,0x84,0xe0,0x84,0x60,0x45,
-0x59,0x60,0xf0,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,
-0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x18,0x60,0xbc,0x65,0xe3,0x83,
-0xc7,0x82,0xa2,0xd9,0x27,0x44,0xf8,0xb4,0x40,0x44,0x29,0xf0,0xf7,0x60,0xff,0x64,
-0xa0,0x84,0xa2,0xda,0x0b,0xf2,0x03,0xfa,0xff,0xff,0x0c,0xf2,0x04,0xfa,0x34,0xf2,
-0xff,0xff,0xdc,0x84,0x34,0xfa,0x14,0xf2,0x0f,0xb5,0x0f,0xb4,0xcc,0x84,0x94,0x80,
-0x29,0xf0,0x04,0x02,0xfb,0x60,0xff,0x64,0xa0,0x84,0x03,0x00,0x04,0x64,0x60,0x47,
-0xb0,0x84,0x29,0xfa,0x00,0x64,0x15,0xfa,0x3f,0x00,0xc4,0x3a,0x1d,0x00,0x27,0x44,
-0xfd,0xb4,0x40,0x47,0x48,0xe2,0x5a,0x60,0x2e,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x5a,0x60,
-0xbe,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x20,0x00,0x28,0x44,0x04,0x2a,0x0b,0x00,0x32,0x44,
-0x04,0x2a,0x08,0x00,0x22,0xf3,0xff,0xff,0xff,0xff,0x02,0x2a,0x03,0x00,0x17,0x60,
-0x98,0x78,0xff,0xff,0x04,0x26,0x08,0x00,0x68,0x3a,0x06,0x00,0x32,0x44,0x00,0x2b,
-0x03,0x00,0x15,0x60,0x36,0x78,0xff,0xff,0x11,0x60,0x8e,0x78,0xff,0xff,0x0a,0x64,
-0xa0,0xfb,0x11,0x60,0x8e,0x78,0xff,0xff,0x1c,0x42,0x22,0x46,0x53,0xf3,0xff,0xff,
-0x40,0x45,0x29,0xf2,0xff,0xff,0xff,0xff,0x04,0x2b,0x61,0x00,0x16,0xf2,0xff,0xff,
-0x40,0x43,0x21,0xf2,0x25,0x40,0x02,0x36,0xe0,0x84,0x55,0xf3,0x60,0x41,0x60,0x45,
-0x23,0x60,0x58,0x4f,0x58,0x78,0xff,0xff,0xae,0x81,0xff,0xff,0x0d,0x03,0xdc,0x84,
-0x03,0x65,0xd5,0x80,0x25,0x40,0x03,0x3a,0x07,0x00,0x06,0x07,0x23,0x5c,0x60,0x41,
-0x00,0x64,0x80,0x7f,0x30,0x83,0x61,0x44,0x40,0x44,0x0f,0x64,0x14,0xf0,0x34,0xf2,
-0xa0,0x81,0x0f,0xb4,0xc9,0x85,0xd4,0x80,0x24,0x44,0x0f,0x02,0x1f,0xf2,0x25,0x40,
-0x02,0x36,0xe0,0x84,0x55,0xf3,0x60,0x41,0x60,0x45,0x23,0x60,0x58,0x4f,0x58,0x78,
-0xff,0xff,0xae,0x81,0xff,0xff,0x01,0x03,0xdc,0x84,0xc0,0x65,0xc4,0x85,0x59,0x60,
-0x68,0x61,0xa1,0xd1,0x25,0x44,0xd0,0x80,0xff,0xff,0x01,0x04,0x64,0x44,0x2b,0x5c,
-0x08,0x26,0x0a,0x00,0x00,0x36,0x25,0xf1,0x01,0x36,0x26,0xf1,0x02,0x36,0x27,0xf1,
-0x03,0x36,0x28,0xf1,0x65,0x44,0x0a,0x00,0x00,0x36,0x29,0xf1,0x01,0x36,0x2a,0xf1,
-0x02,0x36,0x2b,0xf1,0x03,0x36,0x2c,0xf1,0x65,0x44,0xa0,0xa4,0xc0,0x84,0xb5,0xf1,
-0xc0,0x84,0xc0,0x84,0x2a,0xfa,0x27,0x44,0x40,0xbc,0x40,0x47,0x44,0x00,0x17,0xf2,
-0x1f,0xf2,0x40,0x43,0x25,0x40,0x02,0x36,0xe0,0x84,0x55,0xf3,0x60,0x41,0x60,0x45,
-0x23,0x60,0x58,0x4f,0x58,0x78,0xff,0xff,0xae,0x81,0xff,0xff,0x0d,0x03,0xdc,0x84,
-0x03,0x65,0xd5,0x80,0x25,0x40,0x03,0x3a,0x07,0x00,0x06,0x07,0x23,0x5c,0x60,0x41,
-0x00,0x64,0x80,0x7f,0x30,0x83,0x61,0x44,0x40,0x44,0x56,0x64,0xa0,0xd2,0x00,0x7c,
-0x60,0x40,0x01,0x26,0x1c,0x00,0x59,0x60,0x68,0x61,0xa1,0xd1,0x25,0x44,0xd0,0x80,
-0xff,0xff,0x01,0x04,0x64,0x44,0x2b,0x5c,0x08,0x26,0x09,0x00,0x00,0x36,0x25,0xf1,
-0x01,0x36,0x26,0xf1,0x02,0x36,0x27,0xf1,0x03,0x36,0x28,0xf1,0x08,0x00,0x00,0x36,
-0x29,0xf1,0x01,0x36,0x2a,0xf1,0x02,0x36,0x2b,0xf1,0x03,0x36,0x2c,0xf1,0x2a,0xf8,
-0x27,0x44,0xbf,0xb4,0x40,0x47,0x22,0x46,0x29,0xf0,0x6b,0x44,0x64,0x40,0x40,0x27,
-0x80,0xbc,0x60,0x4b,0xf3,0x60,0x58,0x4f,0xba,0x78,0xff,0xff,0xbc,0xff,0x22,0x46,
-0x2b,0xf2,0xff,0xff,0xff,0xff,0x01,0x26,0x0e,0x00,0x27,0x44,0x04,0xbc,0x40,0x47,
-0x35,0xf3,0xb4,0xff,0x60,0x5b,0x4d,0xe2,0x84,0x60,0x1d,0x7d,0x8e,0x60,0x00,0x6b,
-0x13,0x60,0x9b,0x78,0xff,0xff,0xb5,0xff,0xbc,0xff,0x46,0xff,0x47,0xff,0xb7,0xff,
-0xb4,0xff,0x00,0x6b,0x99,0xff,0x3e,0x44,0x7c,0xb4,0x08,0xbc,0x40,0x5e,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x99,0xff,
-0x3d,0x44,0x10,0xbc,0x00,0x7f,0x40,0x5d,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xb7,0xff,
-0xb4,0xff,0xff,0xff,0xff,0xff,0x84,0x60,0x1d,0x7d,0x99,0xff,0x3e,0x44,0x02,0xbc,
-0x00,0x7f,0x40,0x5e,0x98,0xff,0xff,0xff,0x46,0xff,0x47,0xff,0x13,0x60,0x88,0x78,
-0xff,0xff,0x0e,0x64,0xa0,0xfb,0x00,0x60,0x13,0x66,0x46,0x42,0x10,0x60,0x00,0x7c,
-0x3c,0x46,0x29,0xf2,0x22,0x46,0xa0,0x84,0xb4,0xbc,0x29,0xfa,0x59,0x60,0x68,0x61,
-0xa1,0xd1,0x53,0xf3,0xff,0xff,0xd0,0x80,0xff,0xff,0x01,0x06,0x64,0x44,0x40,0x45,
-0x3c,0x46,0x2b,0xf2,0x2c,0xf0,0x60,0x43,0x2d,0xf2,0x22,0x46,0x2b,0xfc,0x2c,0xf8,
-0x2d,0xfa,0x3c,0x46,0x2e,0xf2,0x2f,0xf0,0x60,0x43,0x30,0xf2,0x22,0x46,0x2e,0xfc,
-0x2f,0xf8,0x30,0xfa,0x3c,0x46,0x29,0xf2,0xff,0xff,0xff,0xff,0x04,0x2b,0x13,0x00,
-0x21,0xf2,0x53,0xf1,0xff,0xff,0x64,0x40,0x02,0x36,0xe0,0x84,0x55,0xf3,0x60,0x41,
-0x60,0x45,0x23,0x60,0x58,0x4f,0x58,0x78,0xff,0xff,0xae,0x81,0xff,0xff,0x01,0x03,
-0xdc,0x84,0x40,0x44,0x12,0x00,0x1f,0xf2,0x53,0xf1,0xff,0xff,0x64,0x40,0x02,0x36,
-0xe0,0x84,0x55,0xf3,0x60,0x41,0x60,0x45,0x23,0x60,0x58,0x4f,0x58,0x78,0xff,0xff,
-0xae,0x81,0xff,0xff,0x01,0x03,0xdc,0x84,0x40,0x44,0x00,0x64,0x40,0x43,0x25,0x44,
-0x00,0x3a,0x0b,0x00,0x2b,0x5c,0x08,0x26,0x04,0x00,0x2d,0xf1,0x25,0xf1,0x64,0x43,
-0x2b,0x00,0x31,0xf1,0x29,0xf1,0x64,0x43,0x24,0x00,0x01,0x3a,0x0b,0x00,0x2b,0x5c,
-0x08,0x26,0x04,0x00,0x2e,0xf1,0x26,0xf1,0x64,0x43,0x1e,0x00,0x32,0xf1,0x2a,0xf1,
-0x64,0x43,0x17,0x00,0x02,0x3a,0x0b,0x00,0x2b,0x5c,0x08,0x26,0x04,0x00,0x2f,0xf1,
-0x27,0xf1,0x64,0x43,0x11,0x00,0x33,0xf1,0x2b,0xf1,0x64,0x43,0x0a,0x00,0x2b,0x5c,
-0x08,0x26,0x04,0x00,0x30,0xf1,0x28,0xf1,0x64,0x43,0x06,0x00,0x34,0xf1,0x2c,0xf1,
-0x64,0x43,0xa0,0xa3,0x60,0x65,0x02,0x00,0xc0,0x65,0xd7,0x83,0xb5,0xf3,0xff,0xff,
-0xc0,0x84,0xc0,0x84,0xc4,0x84,0x24,0x45,0xc4,0x84,0x22,0x46,0x2a,0xfa,0x63,0x44,
-0xb5,0xf1,0xff,0xff,0xd0,0x84,0xff,0xff,0x40,0x44,0xf3,0x60,0x58,0x4f,0xba,0x78,
-0xff,0xff,0xbc,0xff,0x27,0x44,0x02,0xbc,0x40,0x47,0x35,0xf3,0xb4,0xff,0x60,0x5b,
-0x4d,0xe2,0x13,0x60,0x9b,0x78,0xff,0xff,0x0d,0x64,0xa0,0xfb,0x00,0x64,0x40,0x43,
-0x2b,0x44,0x08,0x26,0x0f,0x00,0x25,0x44,0x00,0x36,0x25,0xf1,0x01,0x36,0x26,0xf1,
-0x02,0x36,0x27,0xf1,0xfd,0xa0,0xff,0xff,0x15,0x02,0x28,0xf1,0x80,0x60,0x00,0x64,
-0x40,0x43,0x10,0x00,0x25,0x44,0x00,0x36,0x29,0xf1,0x01,0x36,0x2a,0xf1,0x02,0x36,
-0x2b,0xf1,0xfd,0xa0,0xff,0xff,0x04,0x02,0x2c,0xf1,0x80,0x60,0x00,0x64,0x40,0x43,
-0x60,0x65,0x01,0x00,0xc0,0x65,0x00,0x60,0x13,0x66,0x20,0xf3,0x46,0x42,0xd0,0x83,
-0xff,0xff,0x02,0x28,0x00,0x63,0x2a,0xfc,0x64,0x44,0xb5,0xf1,0xd4,0x84,0xd0,0x84,
-0xff,0xff,0x40,0x44,0xd4,0x64,0x29,0xfa,0x21,0x46,0x2e,0xf2,0x2f,0xf0,0x60,0x43,
-0x30,0xf2,0x22,0x46,0x2b,0xfc,0x2c,0xf8,0x2d,0xfa,0xf3,0x60,0x58,0x4f,0xba,0x78,
-0xff,0xff,0xbc,0xff,0x5c,0x00,0x0f,0x64,0xa0,0xfb,0x5a,0x60,0xba,0x64,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x00,0x64,0x40,0x43,0x00,0x60,0x13,0x66,0x46,0x42,0xc4,0x64,0x29,0xfa,
-0x21,0x46,0x2e,0xf2,0x2f,0xf0,0x60,0x43,0x30,0xf2,0x22,0x46,0x2b,0xfc,0x2c,0xf8,
-0x2d,0xfa,0x2b,0x44,0x08,0x26,0x0f,0x00,0x25,0x44,0x00,0x36,0x25,0xf1,0x01,0x36,
-0x26,0xf1,0x02,0x36,0x27,0xf1,0xfd,0xa0,0xff,0xff,0x15,0x02,0x28,0xf1,0x80,0x60,
-0x00,0x64,0x40,0x43,0x10,0x00,0x25,0x44,0x00,0x36,0x29,0xf1,0x01,0x36,0x2a,0xf1,
-0x02,0x36,0x2b,0xf1,0xfd,0xa0,0xff,0xff,0x04,0x02,0x2c,0xf1,0x80,0x60,0x00,0x64,
-0x40,0x43,0x60,0x65,0x01,0x00,0xc0,0x65,0x20,0xf3,0x22,0x46,0xd0,0x84,0x2a,0xfa,
-0x64,0x44,0xb5,0xf1,0xd4,0x84,0xd0,0x84,0xff,0xff,0x40,0x44,0xf3,0x60,0x58,0x4f,
-0xba,0x78,0xff,0xff,0xbc,0xff,0x5a,0x60,0x32,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0xb4,0xff,
-0xff,0xff,0xff,0xff,0x84,0x60,0x1d,0x7d,0x8e,0x60,0x00,0x6b,0x11,0x60,0x8e,0x78,
-0xff,0xff,0x10,0x64,0xa0,0xfb,0xbc,0xff,0x00,0x60,0x13,0x66,0x46,0x42,0xf3,0x60,
-0x58,0x4f,0xba,0x78,0xff,0xff,0x11,0x60,0x8e,0x78,0xff,0xff,0xff,0x00,0x00,0xe0,
-0x7f,0x00,0x1a,0x0e,0xd8,0xf1,0x01,0x64,0xd0,0x80,0xcf,0xfb,0x1a,0x03,0xd7,0xfb,
-0x17,0x60,0xe4,0x65,0xa5,0xd3,0x41,0x4d,0xdc,0x84,0xa5,0xdb,0x26,0x44,0x02,0x26,
-0x17,0x00,0x3e,0x45,0x35,0x81,0xff,0xff,0x0f,0x02,0x5b,0x60,0x06,0x64,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0xf3,0x60,0x8e,0x78,0xff,0xff,0x41,0x5e,0x02,0x64,0x40,0x46,0xd1,0xfe,
-0x21,0x46,0x46,0x45,0x4c,0xe2,0x0e,0x64,0x40,0x4c,0x19,0xff,0x03,0xe1,0x26,0x44,
-0x02,0xb4,0x12,0xbc,0x40,0x46,0x2e,0x44,0x20,0xfa,0x17,0x60,0x7a,0x62,0xa2,0xd1,
-0xff,0xff,0x64,0x40,0x01,0x2a,0x12,0x00,0x66,0x69,0xff,0xff,0xff,0xff,0x01,0x16,
-0xfe,0x00,0x68,0x44,0x00,0x7f,0x60,0x45,0x7c,0x69,0xff,0xff,0xff,0xff,0x01,0x16,
-0xfe,0x00,0x68,0x44,0x00,0x7f,0x60,0x47,0xb4,0x84,0x0f,0x00,0x00,0x65,0x7c,0x69,
-0xff,0xff,0xff,0xff,0x01,0x16,0xfe,0x00,0x68,0x44,0xdf,0xf1,0x00,0x7f,0xd0,0x80,
-0xff,0xff,0x01,0x04,0x64,0x44,0x60,0x47,0xb4,0x84,0x25,0xfa,0x20,0xf2,0xff,0xff,
-0xff,0xb4,0x0a,0x36,0x00,0x7f,0x14,0x36,0x01,0x7f,0x37,0x36,0x02,0x7f,0x6e,0x36,
-0x03,0x7f,0x26,0xfa,0x00,0x7c,0x22,0xf8,0x35,0xf1,0x01,0x64,0x57,0xfb,0x2e,0x44,
-0xa1,0xff,0x6c,0x43,0x21,0xfc,0x7e,0x69,0xc3,0x94,0xcd,0xe2,0x9c,0xfe,0xff,0xff,
-0x0e,0x04,0x67,0x60,0x5e,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x01,0x64,
-0xcf,0xfb,0x27,0x44,0x80,0xbc,0x40,0x47,0xf3,0x60,0x94,0x78,0xff,0xff,0xdc,0xf1,
-0xff,0xff,0x64,0x40,0x00,0x36,0x03,0x00,0x0e,0x60,0x8b,0x78,0xff,0xff,0x00,0x7c,
-0xcf,0xf9,0x40,0x45,0x6e,0x36,0x35,0x00,0x37,0x36,0x26,0x00,0x14,0x36,0x1f,0x00,
-0x0a,0x36,0x17,0x00,0x5a,0x60,0xfa,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x21,0x63,0xa0,0xfd,
-0x27,0x44,0x80,0xbc,0x40,0x47,0x07,0x60,0xd0,0x74,0xcd,0xe2,0xf3,0x60,0x94,0x78,
-0xff,0xff,0xa1,0xff,0x4c,0x48,0xeb,0x83,0xeb,0x83,0xeb,0x83,0x20,0x00,0xa1,0xff,
-0x4c,0x48,0xeb,0x83,0xeb,0x83,0x1b,0x00,0xa1,0xff,0x4c,0x48,0xe3,0x85,0xc7,0x85,
-0xe3,0x83,0xe3,0x83,0xe3,0x83,0xc7,0x83,0xeb,0x83,0xeb,0x83,0xeb,0x83,0xeb,0x83,
-0x0e,0x00,0xe3,0x85,0xc7,0x85,0xe3,0x83,0xe3,0x83,0xe3,0x83,0xa1,0xff,0x4c,0x48,
-0xc7,0x83,0xeb,0x83,0xeb,0x83,0xeb,0x83,0xff,0xff,0x80,0x27,0xcf,0x83,0x1f,0xfc,
-0xfc,0xa3,0x48,0xf3,0x43,0x43,0xa1,0xff,0x4c,0x4e,0x1c,0x7c,0xd0,0x9c,0xd3,0x80,
-0x20,0x44,0x0f,0x04,0x5a,0x60,0xfe,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x27,0x44,0x80,0xbc,0x40,0x47,0x07,0x60,0xd0,0x74,0xcd,0xe2,0xf3,0x60,0x94,0x78,
-0xff,0xff,0xe8,0x84,0x52,0x4a,0x01,0x05,0x70,0x80,0x52,0x63,0x28,0x44,0xbd,0xda,
-0xff,0xff,0xa1,0xff,0x6c,0x45,0x2e,0x44,0x80,0x2b,0x20,0xfb,0xbd,0xda,0x65,0x44,
-0xbd,0xf1,0xbd,0xda,0x50,0xfe,0xff,0xff,0x01,0x2a,0x04,0x00,0x26,0x44,0x20,0xbc,
-0x40,0x46,0x01,0x00,0xd0,0x80,0xa1,0xff,0x6c,0x44,0xbe,0xf1,0xbd,0xda,0xc4,0x85,
-0xd0,0x80,0x00,0x61,0x28,0x44,0x04,0x2a,0x03,0x00,0x40,0x27,0xd1,0x00,0x01,0x61,
-0xbf,0xf1,0xff,0xff,0xa1,0xff,0x6c,0x44,0xbd,0xda,0xc4,0x85,0xd0,0x80,0x32,0x44,
-0x01,0x2a,0x27,0x00,0x28,0x44,0xd4,0x36,0x02,0x00,0xc4,0x3a,0x06,0x00,0x00,0x64,
-0x38,0xfa,0x08,0x64,0xf2,0x60,0xbb,0x78,0x40,0x4c,0xa1,0xff,0x6c,0x44,0xbd,0xda,
-0xff,0xff,0xa1,0xff,0x6c,0x44,0xbd,0xda,0x20,0x61,0x28,0x44,0x03,0x2b,0x00,0x61,
-0x60,0x40,0x40,0x27,0x02,0xb9,0x41,0x4e,0xa1,0xff,0x6c,0x44,0xbd,0xda,0x28,0x44,
-0xb4,0x36,0xe5,0x00,0xa4,0x36,0xe3,0x00,0xe4,0x36,0xe1,0x00,0xf1,0x60,0xb9,0x78,
-0xff,0xff,0x61,0x40,0x01,0x22,0x31,0x00,0xa1,0xff,0x6c,0x44,0xbd,0xda,0x28,0x44,
-0xd4,0x3a,0x02,0x00,0x48,0x61,0x08,0x00,0xc4,0x36,0x05,0x00,0x28,0x44,0xb4,0x3a,
-0x03,0x00,0x4d,0x61,0x01,0x00,0x49,0x61,0x41,0x4d,0x46,0x4e,0x08,0x64,0x40,0x4c,
-0x05,0x01,0x00,0x65,0x2d,0x44,0x04,0x2a,0x0a,0x00,0x0d,0x00,0x2d,0x44,0x49,0x36,
-0x05,0x00,0x48,0x3a,0x07,0x00,0x10,0x65,0x27,0x40,0x40,0x26,0x30,0x65,0xf2,0x60,
-0xbe,0x78,0x35,0x8d,0x30,0x65,0xa1,0xff,0x6c,0x44,0xbd,0xda,0xff,0xff,0xa1,0xff,
-0x6c,0x44,0xbd,0xda,0xf2,0x60,0xbb,0x78,0x35,0x8d,0x45,0x4e,0x23,0x44,0xe8,0xa5,
-0xff,0xff,0x05,0x05,0x60,0x43,0xfa,0xa3,0xf3,0x60,0xa8,0x78,0xff,0xff,0xa1,0xff,
-0x6c,0x44,0xbd,0xda,0xff,0xff,0xa1,0xff,0x6c,0x44,0xbd,0xda,0xff,0xff,0xa1,0xff,
-0x6c,0x44,0xbd,0xda,0x26,0x41,0x20,0x26,0x1d,0x00,0x2d,0x44,0x22,0x01,0x32,0x40,
-0x02,0x2a,0x03,0x00,0x50,0xbc,0x40,0x4d,0x1e,0x00,0x01,0x64,0xcf,0xfb,0x18,0x60,
-0xc8,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x27,0x44,0x06,0x22,0x06,0x00,
-0xf9,0xb4,0x40,0x47,0x02,0x64,0x21,0xfb,0xc0,0xfe,0x48,0xe2,0x27,0x44,0x80,0xbc,
-0x40,0x47,0x09,0x00,0x2e,0x44,0x03,0xa4,0xef,0xb1,0x08,0x24,0x40,0xb9,0x41,0x46,
-0x02,0x00,0x70,0xbc,0x40,0x4d,0xa1,0xff,0x6c,0x44,0xbd,0xda,0x20,0x61,0x28,0x44,
-0x03,0x2b,0x00,0x61,0x60,0x40,0x40,0x2b,0x0d,0x00,0xbf,0x60,0xff,0x65,0x43,0xf3,
-0x02,0xb9,0x80,0xb0,0x28,0x44,0x06,0x03,0xa4,0x84,0x40,0x48,0x29,0xfa,0x04,0x64,
-0x22,0xfa,0x02,0xa9,0x23,0x44,0xe8,0xa4,0x41,0x4e,0x20,0x26,0xfa,0xa4,0x28,0x40,
-0x40,0x27,0xf8,0xa4,0x40,0x43,0x38,0xfa,0xff,0xff,0xa1,0xff,0x6c,0x44,0xbd,0xda,
-0x23,0x44,0x80,0x2b,0x03,0x00,0x14,0x64,0x40,0x43,0x38,0xfa,0x23,0x47,0x3f,0xfa,
-0x08,0x65,0x45,0x4c,0x21,0xf2,0x70,0x45,0xd4,0x80,0xff,0xff,0x02,0x06,0x64,0xe2,
-0x60,0x50,0xa1,0xff,0x6c,0x44,0xbd,0xda,0x2e,0x44,0x22,0x26,0x05,0x00,0x23,0x41,
-0xa1,0xff,0x6c,0x44,0x34,0xfa,0x54,0x00,0xa1,0xff,0x6c,0x44,0xbd,0xda,0x2e,0x40,
-0x20,0x2a,0x12,0x00,0xa1,0xff,0x6c,0x44,0xbd,0xda,0xff,0xff,0xa1,0xff,0x6c,0x44,
-0xbd,0xda,0x2e,0x40,0x02,0x26,0x05,0x00,0x23,0x41,0xa1,0xff,0x6c,0x44,0x37,0xfa,
-0x3f,0x00,0xa1,0xff,0x6c,0x44,0x37,0xfa,0x32,0x44,0x01,0x2a,0x1a,0x00,0x00,0xf4,
-0x02,0x62,0x46,0x45,0xa1,0xff,0xda,0x82,0x6c,0x44,0xa2,0xda,0xff,0xff,0xa1,0xff,
-0xda,0x82,0x6c,0x44,0xa2,0xda,0x23,0x41,0x04,0xa1,0x78,0x7c,0x01,0x65,0x61,0x43,
-0xd1,0x80,0x46,0x45,0x02,0x07,0x04,0xa1,0x37,0x00,0x64,0x43,0xd1,0x81,0x7c,0x7c,
-0x3b,0x00,0xcf,0xf3,0x48,0xf1,0x00,0xa0,0x23,0x44,0x04,0x02,0x00,0xa0,0xd0,0x80,
-0x01,0x03,0x05,0x04,0x60,0x43,0x0c,0xa3,0xf3,0x60,0xa8,0x78,0xff,0xff,0xa1,0xff,
-0x6c,0x44,0xff,0xff,0x1a,0xfa,0xff,0xff,0xa1,0xff,0x6c,0x44,0xff,0xff,0x1b,0xfa,
-0xb0,0xff,0x01,0x5d,0xdc,0xfe,0x05,0xff,0x3e,0xf3,0x23,0x41,0x04,0xbc,0x3e,0xfb,
-0x7c,0x7c,0x01,0x65,0x25,0x44,0xd5,0x80,0x61,0x43,0x45,0x04,0x35,0x03,0xdc,0xf3,
-0xff,0xff,0xff,0xff,0x00,0x36,0x03,0x00,0x0e,0x60,0x8b,0x78,0xff,0xff,0x00,0xf4,
-0x02,0x62,0xd1,0x80,0x46,0x45,0x06,0x07,0x61,0x40,0x01,0x26,0x01,0xa1,0x61,0x5c,
-0x00,0x61,0x02,0x00,0x64,0x43,0xd1,0x81,0xa1,0xff,0xec,0x44,0x5a,0xda,0xfc,0x1d,
-0xe2,0x1e,0x23,0x00,0xa1,0xff,0xd5,0x80,0x61,0x43,0x37,0x04,0x16,0x03,0x00,0xf4,
-0x02,0x62,0xd1,0x80,0x46,0x45,0x07,0x07,0xec,0x44,0x61,0x40,0x01,0x26,0x01,0xa1,
-0x61,0x5c,0x00,0x61,0x05,0x00,0x64,0x43,0xec,0x44,0xd1,0x81,0x01,0x00,0xec,0x44,
-0x7a,0xda,0xfd,0x1d,0xe8,0x1e,0x0a,0x00,0xa1,0xff,0xb6,0xff,0x6c,0x44,0x00,0xf4,
-0x02,0xfa,0x02,0x7c,0x46,0x45,0xb7,0xff,0x06,0x00,0xa1,0xff,0xb6,0xff,0x00,0x64,
-0x6c,0x44,0x5a,0xda,0xb7,0xff,0x64,0x41,0x28,0x44,0x40,0x2b,0x1d,0x00,0xb1,0xff,
-0x32,0x44,0x01,0x26,0x19,0x00,0xa1,0xff,0x6c,0x44,0xff,0xff,0x3c,0xfb,0xff,0xff,
-0xa1,0xff,0x6c,0x44,0xff,0xff,0x3d,0xfb,0x0f,0x00,0x6c,0x44,0x64,0x41,0x28,0x40,
-0x40,0x2b,0x0d,0x00,0xb1,0xff,0x32,0x40,0x01,0x26,0x09,0x00,0x3c,0xfb,0xff,0xff,
-0xa1,0xff,0x6c,0x44,0xff,0xff,0x3d,0xfb,0x21,0x46,0xa1,0xff,0x6c,0x40,0x21,0x46,
-0x6a,0x43,0xa1,0xff,0x6c,0x40,0x24,0xf1,0xff,0xff,0x64,0x54,0xcd,0xe2,0x19,0xff,
-0x01,0x16,0xfe,0x00,0x68,0x44,0x60,0x40,0x10,0x2a,0x04,0x00,0x22,0xf2,0xff,0xff,
-0x08,0xbc,0x22,0xfa,0x32,0x44,0x03,0x22,0x20,0x00,0x47,0xff,0x26,0x44,0xfd,0xb4,
-0x84,0xbc,0x63,0x40,0x40,0x27,0x08,0x00,0x0d,0x63,0x07,0x15,0x06,0x15,0x05,0x15,
-0x04,0x15,0xff,0xa3,0x02,0x15,0xf9,0x02,0x7f,0xb4,0x40,0x46,0x6c,0x40,0x28,0x44,
-0x40,0x2b,0x08,0x00,0x32,0x44,0x01,0x2a,0x05,0x00,0x23,0x44,0x08,0xa4,0x38,0xfa,
-0x60,0x47,0x3f,0xfa,0xf3,0x60,0x74,0x78,0xff,0xff,0x63,0x40,0x40,0x2b,0x05,0x00,
-0xd2,0xf3,0xff,0xff,0x01,0xa4,0xd2,0xfb,0x08,0x00,0x0d,0x64,0x4b,0x15,0x4a,0x15,
-0x49,0x15,0x48,0x15,0xff,0xa4,0x46,0x15,0xf9,0x02,0x48,0xe2,0xcf,0xf3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x73,0x02,0x27,0x44,0x06,0x22,0x05,0x00,0xf9,0xb4,0x40,0x47,
-0x02,0x64,0x21,0xfb,0xc0,0xfe,0x20,0xf2,0xff,0xff,0xff,0xb4,0x0a,0x36,0x00,0x7f,
-0x14,0x36,0x01,0x7f,0x37,0x36,0x02,0x7f,0x6e,0x36,0x03,0x7f,0x26,0xfa,0x5a,0x60,
-0xd2,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x26,0xf2,0xff,0xff,0x60,0x47,0x00,0x7f,0xe0,0x84,
-0xe0,0x84,0x60,0x45,0x5a,0x60,0xd6,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x28,0x40,
-0x04,0x26,0x3c,0x00,0x28,0x40,0x40,0x2b,0x39,0x00,0x26,0x44,0x04,0xbc,0xfd,0xb4,
-0x40,0x46,0x34,0x00,0x6c,0x40,0x2d,0x44,0x20,0x2a,0x18,0x00,0xb5,0xf3,0xff,0xff,
-0xf7,0xa4,0x60,0x54,0xcd,0xe2,0x5d,0x2a,0x05,0x00,0x70,0x44,0x00,0xbc,0xff,0xff,
-0x0d,0x02,0x05,0x00,0x59,0x2a,0x03,0x00,0x27,0x40,0x02,0x2a,0x07,0x00,0x47,0xff,
-0xb5,0xff,0x00,0x64,0xd7,0xfb,0x13,0x60,0xbb,0x78,0xff,0xff,0xcf,0xf3,0xff,0xff,
-0x00,0xa0,0x26,0x44,0x03,0x03,0x84,0xbc,0x40,0x46,0x10,0x00,0x84,0xbc,0x2d,0x40,
-0x0c,0x22,0x02,0x00,0x40,0x46,0x0a,0x00,0xfd,0xb4,0x40,0x46,0x25,0x44,0x06,0xfa,
-0x60,0x46,0x01,0xf2,0xff,0xff,0x61,0x5e,0x03,0x2b,0x01,0xfa,0x21,0x46,0x08,0x29,
-0x09,0x00,0x22,0xf1,0xff,0xff,0x64,0x40,0x07,0x2e,0x04,0x00,0x43,0xff,0x10,0x64,
-0x21,0xfb,0xc0,0xfe,0x00,0x64,0xd7,0xfb,0x47,0xff,0x12,0x60,0xe4,0x78,0xff,0xff,
-0xa1,0xff,0x6c,0x43,0x63,0x54,0xcd,0xe2,0x0e,0x64,0x01,0x00,0x0c,0x64,0x40,0x4c,
-0x19,0xff,0x00,0x64,0x40,0x4a,0xd7,0xfb,0x08,0x64,0x40,0x4c,0x27,0x44,0x06,0x22,
-0x06,0x00,0xf9,0xb4,0x40,0x47,0x02,0x64,0x21,0xfb,0xc0,0xfe,0x48,0xe2,0x82,0xe1,
-0xa1,0xff,0xd4,0x00,0x00,0x60,0x18,0x63,0xa1,0xff,0xec,0x44,0xff,0xff,0xfc,0x1d,
-0x04,0x1e,0xb6,0xff,0xa1,0xff,0x6c,0x44,0xb7,0xff,0x08,0x64,0x40,0x4c,0x19,0xff,
-0x27,0x44,0x80,0xbc,0x40,0x47,0xc2,0x00,0x01,0x64,0xd0,0xfb,0x24,0x44,0x60,0x48,
-0x90,0x6a,0x60,0x47,0x23,0x41,0xe1,0x81,0xff,0xff,0x01,0x16,0xfe,0x00,0x60,0x48,
-0x8e,0x6a,0x00,0x64,0x02,0x24,0x80,0x64,0x69,0x83,0xde,0xf1,0x25,0x45,0x02,0x2a,
-0x03,0x00,0x64,0x40,0x01,0x2a,0x04,0xbc,0x01,0x16,0xfe,0x00,0x60,0x48,0x8c,0x6a,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x01,0x16,0xfe,0x00,0x02,0xe1,0x08,0x64,
-0x40,0x4c,0x29,0x44,0x01,0xbc,0x40,0x49,0x19,0xff,0x52,0x63,0x22,0x46,0xbd,0xd0,
-0x22,0xf3,0x43,0xff,0x60,0x40,0x07,0x22,0x03,0x00,0x10,0x64,0x21,0xfb,0xc0,0xfe,
-0x01,0xe1,0x28,0xf2,0x44,0x48,0x60,0x40,0x01,0x2a,0x03,0x00,0x40,0x67,0xb0,0x84,
-0x60,0x5c,0x99,0xff,0x07,0x60,0x00,0xe8,0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0x80,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x64,0x4d,
-0x64,0x47,0x60,0x4d,0xff,0xff,0xff,0xff,0xa1,0xff,0xb5,0xff,0xbb,0xff,0x32,0x64,
-0xa0,0xfb,0xff,0xff,0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4d,0x60,0x47,0x60,0x4d,
-0x26,0x44,0x02,0xb4,0x40,0x46,0x20,0x61,0x28,0x44,0x80,0x36,0x04,0x00,0x50,0x3a,
-0x03,0x00,0x00,0x65,0xb5,0x81,0x04,0xb9,0x41,0x4e,0xa1,0xff,0xbd,0xd2,0xff,0xff,
-0x60,0x4d,0x60,0x47,0x60,0x4d,0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4d,0x60,0x47,
-0x60,0x4d,0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4c,0x28,0x44,0xc4,0x36,0x13,0x00,
-0xd4,0x36,0x11,0x00,0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4c,0xa1,0xff,0xbd,0xd2,
-0xff,0xff,0x60,0x4c,0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4c,0x28,0x44,0xb4,0x36,
-0x02,0x00,0xa4,0x3a,0x03,0x00,0xf5,0x60,0x03,0x78,0xff,0xff,0xa1,0xff,0xbd,0xd2,
-0xff,0xff,0x60,0x4c,0xff,0x65,0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4c,0x2e,0x41,
-0x28,0x44,0x03,0x2b,0xdf,0xb1,0x60,0x40,0x40,0x27,0x02,0xb9,0xa1,0xff,0xbd,0xd2,
-0xff,0xff,0x60,0x4c,0x23,0x44,0xcc,0x84,0xdc,0x84,0x03,0x03,0x03,0x02,0x08,0xb9,
-0x01,0x00,0x10,0xb9,0xbd,0xd0,0x41,0x4e,0xa1,0xff,0xff,0xff,0x64,0x4c,0x2e,0x44,
-0x22,0x22,0x30,0x00,0x20,0x2a,0x0f,0x00,0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4c,
-0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4c,0xa1,0xff,0xbd,0xd2,0xff,0xff,0x60,0x4c,
-0x2e,0x44,0x02,0x2a,0x1f,0x00,0xa1,0xff,0xff,0xff,0x38,0xf3,0x39,0xf3,0x60,0x4c,
-0x60,0x43,0x03,0xf0,0x04,0xf4,0x01,0xf2,0x64,0x42,0x04,0xa4,0xd0,0x81,0xa1,0xff,
-0x63,0x4c,0x23,0x43,0x2e,0x44,0x10,0x2a,0x06,0x00,0xa2,0xd2,0xff,0xff,0xa1,0xff,
-0xff,0xff,0x60,0x4f,0x63,0x00,0xe2,0xd2,0xff,0xff,0xa1,0xff,0xda,0x82,0xc9,0x81,
-0x60,0x4e,0x51,0x00,0x18,0x22,0x0a,0x00,0x10,0x26,0x03,0x00,0xf5,0x60,0x03,0x78,
-0xff,0xff,0x03,0xf0,0x04,0xf4,0x00,0x61,0x64,0x42,0x4b,0x00,0x04,0x2a,0x32,0x00,
-0x00,0xf4,0x01,0xf2,0xff,0x65,0xa4,0x81,0xa1,0xff,0x02,0xfe,0xff,0xff,0x10,0x25,
-0x42,0xfe,0x72,0x45,0x28,0x40,0x50,0x3a,0x00,0x00,0x65,0x4c,0x23,0x43,0xa1,0xf3,
-0x04,0x04,0xdc,0x84,0xa1,0xfb,0x60,0x55,0x65,0x52,0xa1,0xff,0xff,0xff,0x60,0x4c,
-0xa2,0xf3,0x03,0x04,0xdc,0x84,0xa2,0xfb,0xff,0xff,0xa1,0xff,0xff,0xff,0x60,0x4c,
-0xa3,0xf3,0x03,0x04,0xdc,0x84,0xa3,0xfb,0xff,0xff,0xa1,0xff,0x0c,0x62,0x60,0x4c,
-0xf8,0xa3,0x2e,0x44,0xfb,0xb4,0x40,0x4e,0x65,0x44,0xdc,0x80,0xff,0xff,0x01,0x02,
-0x58,0x80,0x0c,0x00,0x23,0x43,0x03,0xf0,0x04,0xf4,0x01,0xf2,0x64,0x42,0x04,0xa4,
-0xd0,0x81,0x04,0x00,0x00,0xf4,0x01,0xf2,0x04,0x62,0xa4,0x81,0xa1,0xff,0xe2,0xd2,
-0xda,0x82,0xc9,0x81,0x60,0x4c,0xfa,0x1c,0xf5,0x1d,0x08,0x1e,0x02,0x02,0x00,0xf4,
-0x04,0x62,0xa2,0xd2,0xff,0xff,0xa1,0xff,0xff,0xff,0x60,0x4d,0x28,0x44,0x40,0x2b,
-0x04,0x00,0xa1,0xff,0x60,0x4e,0xa1,0xff,0x60,0x4c,0xa1,0xff,0xc3,0x60,0x33,0x64,
-0x60,0x4e,0xa1,0xff,0xff,0xff,0x62,0x5c,0x02,0xe1,0x08,0x64,0x40,0x4c,0x19,0xff,
-0x61,0x40,0x7f,0x26,0x02,0x00,0x00,0xf4,0x04,0x7c,0x66,0x44,0x22,0x46,0x0b,0xf8,
-0x0c,0xfa,0x34,0x64,0xa0,0xfb,0x6a,0x40,0x40,0x26,0x02,0x00,0xfc,0x0b,0x02,0x00,
-0x07,0x60,0x80,0xe8,0x24,0xf3,0xff,0xff,0x60,0x54,0xcd,0xe2,0x03,0x64,0xcc,0x84,
-0xff,0xff,0xfd,0x02,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0x99,0xff,0x3e,0x44,0x7d,0xb4,0x08,0xbc,0x40,0x5e,0x98,0xff,0x05,0x64,
-0xcc,0x84,0xff,0xff,0xfd,0x02,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0x99,0xff,0x3d,0x44,0xf7,0xb4,0x40,0x5d,0x98,0xff,0x00,0x60,
-0x00,0x6b,0x99,0xff,0x3d,0x44,0x10,0xbc,0x40,0x5d,0x3e,0x44,0xfe,0xb4,0x40,0x5e,
-0x98,0xff,0xbc,0xff,0x28,0x44,0x40,0x2b,0x3e,0x00,0x99,0xff,0x3a,0x44,0x98,0xff,
-0x10,0x2b,0xfb,0x00,0x99,0xff,0x00,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0xff,0xff,
-0x3a,0x5c,0x80,0x2b,0x12,0x00,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,
-0x09,0x00,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,0x03,0x00,0x8b,0xff,
-0x74,0x40,0x88,0xff,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,0xff,0xff,
-0x31,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0x00,0x60,0x00,0xea,0x3a,0x5c,0x80,0x27,
-0x06,0x00,0x31,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0x00,0x60,0x00,0xea,0x00,0x64,
-0xdb,0xfb,0x67,0x60,0xcc,0x64,0x3a,0x5c,0xa0,0xd9,0xff,0xff,0x30,0x60,0x00,0xea,
-0xff,0xff,0xff,0xff,0x98,0xff,0xff,0xff,0xb7,0xff,0xb4,0xff,0xff,0xff,0xff,0xff,
-0x84,0x60,0x1d,0x7d,0xb5,0xff,0xff,0xff,0x99,0xff,0x07,0x60,0x80,0xe9,0x98,0xff,
-0xff,0xff,0xff,0xff,0x80,0xe9,0xff,0xff,0xff,0xff,0xb7,0xff,0xb4,0xff,0xff,0xff,
-0x99,0xff,0x3e,0x44,0x02,0xbc,0x00,0x7f,0x40,0x5e,0x98,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0x46,0xff,0x47,0xff,0x2f,0x58,0xff,0xff,0x01,0x64,0x3f,0xfb,
-0xff,0xff,0x00,0xea,0x99,0xff,0x3b,0x44,0x40,0x27,0xfd,0x00,0x98,0xff,0x00,0xeb,
-0x40,0xf3,0xff,0xff,0x60,0x40,0x01,0x2a,0x03,0x00,0x18,0x60,0x17,0x78,0xff,0xff,
-0x1b,0xf2,0x41,0xf1,0x60,0x40,0xc0,0x27,0x07,0x00,0x64,0x40,0x01,0x2a,0x16,0x00,
-0x67,0x60,0x8e,0x63,0x00,0x65,0x77,0x00,0xc0,0x2b,0x07,0x00,0x64,0x40,0x08,0x2a,
-0x0d,0x00,0x67,0x60,0xb8,0x63,0x30,0x65,0x6e,0x00,0x40,0x2b,0x0c,0x00,0x64,0x40,
-0x02,0x2a,0x04,0x00,0x67,0x60,0x9c,0x63,0x10,0x65,0x65,0x00,0x20,0x60,0x00,0xea,
-0xf6,0x60,0xeb,0x78,0xff,0xff,0x64,0x40,0x04,0x2a,0xf8,0x00,0x67,0x60,0xaa,0x63,
-0x95,0xf3,0xd6,0xf3,0x00,0xa0,0x00,0xa0,0x55,0x03,0x54,0x03,0x19,0x60,0x2a,0x65,
-0x30,0xf2,0x2f,0xf0,0x60,0x41,0x64,0x43,0xeb,0x83,0x00,0x7f,0xe0,0x84,0x44,0xd1,
-0x61,0x47,0x93,0x83,0x00,0x7f,0xe0,0x84,0x44,0xd1,0xeb,0x83,0x93,0x83,0x0f,0x60,
-0xf0,0x65,0xa7,0x85,0x44,0x60,0x4e,0x63,0xc7,0x83,0x02,0x65,0xbd,0xd3,0xff,0xff,
-0x60,0x40,0x80,0x2b,0x0e,0x00,0x5c,0x61,0xa1,0xd0,0xbd,0xd3,0x50,0xfe,0x59,0xd0,
-0xd0,0x80,0xbd,0xd3,0x59,0xd0,0xd0,0x80,0xbd,0xd3,0xff,0xff,0xd0,0x80,0xff,0xff,
-0x1f,0x01,0x65,0x44,0xff,0xa5,0xff,0xff,0xe9,0x02,0x54,0x60,0x4e,0x63,0x7e,0x65,
-0xbd,0xd3,0xff,0xff,0x60,0x40,0x80,0x2b,0x0e,0x00,0x5c,0x61,0xa1,0xd0,0xbd,0xd3,
-0x50,0xfe,0x59,0xd0,0xd0,0x80,0xbd,0xd3,0x59,0xd0,0xd0,0x80,0xbd,0xd3,0xff,0xff,
-0xd0,0x80,0xff,0xff,0x05,0x01,0x65,0x44,0xff,0xa5,0xff,0xff,0xe9,0x02,0x0a,0x00,
-0xf8,0xa3,0xa3,0xd3,0xff,0xff,0xe0,0x84,0xe8,0x85,0x3d,0x60,0x4c,0x64,0xc4,0x83,
-0x67,0x44,0xd9,0xfb,0x20,0x65,0x22,0xf2,0xff,0xff,0xb4,0x84,0x22,0xfa,0x1a,0xf0,
-0x99,0xff,0x64,0x44,0xe0,0x7f,0x40,0x5b,0x64,0x47,0xe1,0x7f,0x40,0x5b,0x1b,0xf0,
-0xff,0xff,0x64,0x44,0xe2,0x7f,0x40,0x5b,0x60,0x47,0x60,0x41,0xd9,0xf3,0xd9,0xf9,
-0x45,0xf1,0x90,0x84,0x00,0x37,0x13,0x00,0x63,0x42,0x02,0x63,0x64,0x40,0x07,0x3a,
-0x0a,0x63,0x5a,0xd3,0xdd,0x81,0x60,0x45,0x61,0x5f,0x40,0x5b,0x65,0x47,0xdd,0x81,
-0x61,0x5f,0x40,0x5b,0xf6,0x1f,0x5a,0xd3,0xdd,0x81,0x61,0x5f,0x40,0x5b,0x98,0xff,
-0xa0,0x60,0x00,0xeb,0xc0,0x60,0x00,0xeb,0x64,0x40,0x07,0x3a,0x03,0x00,0x80,0x60,
-0x07,0xeb,0x02,0x00,0x80,0x60,0x0f,0xeb,0x33,0x60,0x00,0xeb,0x00,0x64,0x3f,0xfb,
-0x38,0xf2,0x00,0xf4,0xff,0xff,0x28,0x87,0x04,0x64,0x40,0x49,0x46,0x4a,0xb3,0xff,
-0x02,0x64,0x9c,0xfb,0x99,0xff,0x30,0x44,0x02,0xbc,0x40,0x51,0x98,0xff,0xf8,0x60,
-0x89,0x78,0xff,0xff,0x40,0xf3,0x2a,0x46,0x60,0x40,0x01,0x26,0x65,0x00,0x29,0x43,
-0x27,0x44,0x00,0xa8,0x60,0x41,0xa3,0xd2,0x1b,0x03,0x99,0xff,0x3b,0x40,0x80,0x2b,
-0xfd,0x00,0x98,0xff,0x60,0x57,0xff,0xff,0x77,0x5f,0xc9,0x81,0x60,0x57,0xff,0xff,
-0x77,0x5f,0xbd,0xda,0x01,0x0f,0x25,0x00,0x0b,0x03,0xa3,0xd2,0x7f,0x26,0xf2,0x00,
-0x00,0xf2,0x04,0x63,0x00,0xa8,0x40,0x4a,0x60,0x46,0x3b,0x03,0xa3,0xd2,0xea,0x00,
-0x3d,0x46,0x38,0xf2,0x2a,0x46,0x60,0x40,0x01,0x2a,0x1f,0x00,0x63,0x40,0x7f,0x26,
-0x05,0x00,0x00,0xf2,0x04,0x63,0x00,0xa8,0x60,0x46,0x2b,0x03,0x46,0x4a,0x01,0x0f,
-0x11,0x00,0xa3,0xd2,0xff,0xff,0x60,0x57,0xff,0xff,0x77,0x5f,0x60,0x47,0xa3,0xda,
-0x0c,0x00,0xe6,0x03,0x63,0x40,0x7f,0x26,0x05,0x00,0x00,0xf2,0x04,0x63,0x00,0xa8,
-0x40,0x4a,0x17,0x03,0x43,0x49,0x41,0x47,0x11,0x00,0x00,0x64,0x9c,0xfb,0x99,0xff,
-0x30,0x44,0xfd,0xb4,0x40,0x51,0x98,0xff,0x3e,0xf3,0x00,0x7c,0x02,0xbc,0x3e,0xfb,
-0x3d,0x46,0x0f,0xf2,0x44,0x47,0x60,0x40,0x04,0x26,0x0b,0x00,0xf8,0x60,0x89,0x78,
-0xff,0xff,0x3d,0x46,0x0f,0xf0,0xff,0x60,0xf7,0x65,0x64,0x43,0x17,0x60,0xe5,0x78,
-0xff,0xff,0x17,0x60,0xa6,0x78,0xff,0xff,0x18,0x60,0x17,0x78,0xff,0xff,0x4c,0x2f,
-0x7e,0x00,0x18,0x09,0x3d,0x46,0x0f,0xf0,0x3c,0xf3,0x64,0x43,0x60,0x45,0x1b,0xf2,
-0x41,0xf1,0x60,0x40,0xc0,0x23,0x01,0x64,0xc0,0x2b,0x01,0x00,0x08,0x64,0x80,0x2b,
-0x01,0x00,0x04,0x64,0x40,0x2b,0x01,0x00,0x02,0x64,0xa0,0x84,0x0f,0x22,0x2d,0x00,
-0x65,0x44,0x63,0x5c,0x80,0x2a,0x29,0x00,0x99,0xff,0x3b,0x40,0x80,0x2b,0xfd,0x00,
-0x98,0xff,0x60,0x57,0xff,0xff,0x77,0x5f,0x60,0x57,0xff,0xff,0x77,0x5f,0x99,0xff,
-0x3b,0x40,0x80,0x2b,0xfd,0x00,0x98,0xff,0x3d,0xf3,0xff,0xff,0x60,0x57,0xff,0xff,
-0x77,0x5f,0x60,0x57,0xff,0xff,0x77,0x5f,0xff,0xff,0xff,0x60,0xf7,0x65,0x0b,0x14,
-0x0a,0x14,0x09,0x14,0x08,0x14,0x07,0x14,0x06,0x14,0x05,0x14,0x04,0x14,0x03,0x14,
-0x02,0x14,0xa7,0x83,0x01,0x00,0x08,0xbb,0x0f,0xfc,0x0f,0xf0,0x80,0x60,0x00,0x63,
-0xb3,0x9c,0x0f,0xf8,0x00,0x64,0x9c,0xfb,0x99,0xff,0x30,0x44,0xfd,0xb4,0x40,0x51,
-0x98,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x20,0x60,0x00,0xea,0x00,0xeb,
-0xa0,0x60,0x00,0xeb,0x30,0x60,0x00,0xeb,0x3e,0xf3,0xff,0xff,0xf9,0xb4,0x3e,0xfb,
-0xf8,0x60,0xd7,0x78,0xff,0xff,0x00,0x64,0x9c,0xfb,0x99,0xff,0x30,0x44,0xfd,0xb4,
-0x40,0x51,0x98,0xff,0x00,0xeb,0xa0,0x60,0x00,0xeb,0x30,0x60,0x00,0xeb,0x20,0x60,
-0x00,0xea,0x00,0x64,0x3e,0xf3,0xff,0xff,0xf9,0xb4,0x3e,0xfb,0x40,0xfb,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0x99,0xff,0x3a,0x44,0x3b,0x44,0x98,0xff,0x3d,0x46,
-0x0f,0xf0,0x80,0x60,0x00,0x63,0xb3,0x83,0xff,0x60,0x7f,0x7c,0xa3,0x83,0x0f,0xfc,
-0xf8,0x60,0xd7,0x78,0xff,0xff,0xa9,0xff,0x77,0x44,0x60,0x57,0x40,0x4a,0x01,0x2a,
-0x31,0x00,0x24,0x44,0x00,0xa8,0x24,0x46,0x09,0xf2,0x2c,0x03,0x00,0xa8,0x40,0x44,
-0x13,0x03,0x60,0x46,0x60,0x5c,0x08,0x60,0x20,0x64,0xa0,0xd9,0x64,0x44,0x3a,0x44,
-0x01,0x26,0x02,0x00,0x01,0x75,0x03,0x00,0x3b,0x44,0x01,0xbc,0x40,0x5b,0x0e,0xf2,
-0xff,0xff,0x01,0xbc,0x0e,0xfa,0x0a,0xf4,0x08,0xf2,0x2d,0x45,0xd4,0x80,0x0e,0xf2,
-0x02,0x03,0xd2,0xfe,0x0f,0x00,0x02,0xbc,0x0e,0xfa,0xd0,0xfe,0x5a,0x60,0x70,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x2a,0x44,0x08,0x2a,0x1c,0x00,0x23,0x44,0x00,0xa8,0xff,0xff,
-0x18,0x03,0x3c,0x60,0x40,0x64,0x40,0x47,0x58,0x4f,0x53,0x00,0x46,0x43,0x11,0x02,
-0x0e,0xf2,0x66,0x43,0x60,0x5c,0x08,0x60,0x22,0x64,0xa0,0xdd,0x64,0x44,0x01,0xbc,
-0x0e,0xfa,0x3a,0x44,0x01,0x26,0x02,0x00,0x08,0x75,0x03,0x00,0x3b,0x44,0x08,0xbc,
-0x40,0x5b,0x2a,0x44,0x06,0x22,0x4b,0x00,0x22,0x44,0x00,0xa8,0x60,0x46,0x0e,0xf2,
-0x46,0x03,0x01,0xb0,0x02,0xbc,0x03,0x02,0x00,0x64,0x40,0x42,0x40,0x00,0x0e,0xfa,
-0xd0,0xfe,0x3c,0x60,0x46,0x64,0x40,0x47,0x58,0x4f,0x2b,0x00,0x46,0x42,0x37,0x02,
-0x22,0xf2,0x66,0x43,0x00,0xa8,0x0e,0xf2,0x12,0x02,0x01,0xbc,0x40,0x2a,0xe9,0x00,
-0xf7,0xb4,0x0e,0xfa,0xff,0xff,0x08,0x60,0x24,0x64,0xa0,0xdd,0x3a,0x44,0x01,0x26,
-0x02,0x00,0x02,0x75,0x24,0x00,0x3b,0x44,0x02,0xbc,0x40,0x5b,0x20,0x00,0x01,0xbc,
-0x80,0x2a,0xd7,0x00,0xf7,0xb4,0x0e,0xfa,0xff,0xff,0x08,0x60,0x24,0x64,0xa0,0xdd,
-0x3a,0x44,0x01,0x26,0x02,0x00,0x04,0x75,0x12,0x00,0x3b,0x44,0x04,0xbc,0x40,0x5b,
-0x0e,0x00,0x27,0x42,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x0e,0xf2,0x04,0x03,
-0x05,0xb0,0x09,0xf2,0xf9,0x02,0x01,0x00,0x08,0xfe,0x2f,0x58,0xff,0xff,0x2a,0x44,
-0x80,0x2a,0x1f,0x00,0x21,0x44,0x00,0xa8,0x60,0x46,0x0e,0xf2,0x1a,0x03,0x02,0xbc,
-0x0e,0xfa,0xd0,0xfe,0x3c,0x60,0x70,0x64,0x40,0x47,0x58,0x4f,0xe2,0x00,0x46,0x41,
-0x10,0x02,0x0e,0xf2,0x66,0x43,0x01,0xbc,0x0e,0xfa,0xff,0xff,0x08,0x60,0x10,0x64,
-0xa0,0xdd,0x3a,0x44,0x01,0x26,0x02,0x00,0x80,0x75,0x03,0x00,0x3b,0x44,0x80,0xbc,
-0x40,0x5b,0x34,0x40,0xff,0x26,0x3b,0xff,0x2a,0x44,0x0c,0xf7,0xff,0xff,0xff,0xff,
-0x3a,0x44,0x02,0x2a,0x09,0x00,0x3b,0x44,0x00,0xa0,0xff,0xff,0x01,0x03,0x60,0x55,
-0x00,0x64,0x40,0x5a,0x40,0x5b,0x3d,0x00,0x00,0x64,0x40,0x4a,0x35,0x40,0x01,0x2a,
-0x19,0x00,0x24,0x41,0x00,0xb9,0x40,0x55,0x2c,0x02,0x0d,0x47,0x58,0x4f,0xb1,0x00,
-0x28,0x02,0x0e,0xf2,0x46,0x44,0x01,0xbc,0x0e,0xfa,0x66,0x5c,0x08,0x60,0x20,0x64,
-0xa0,0xd9,0x3a,0x44,0x01,0x26,0x02,0x00,0x01,0x75,0x03,0x00,0x3b,0x44,0x01,0xbc,
-0x40,0x5b,0x17,0x00,0x35,0x40,0x04,0x2a,0x07,0x00,0x23,0x41,0x00,0xb9,0x40,0x55,
-0x10,0x02,0x18,0x60,0x7d,0x78,0xff,0xff,0x35,0x40,0x02,0x2a,0x07,0x00,0x22,0x41,
-0x00,0xb9,0x40,0x55,0x06,0x02,0x18,0x60,0xa5,0x78,0xff,0xff,0x35,0x40,0x08,0x26,
-0x03,0x00,0x34,0x40,0x08,0x2a,0x05,0x00,0x21,0x41,0x00,0xb9,0x40,0x55,0x40,0x54,
-0x99,0x03,0x00,0x00,0xa1,0xff,0xff,0xff,0xba,0x3f,0x19,0x60,0x68,0x61,0x27,0x42,
-0xa2,0xd3,0x0e,0x4c,0x41,0x4e,0x40,0x45,0x60,0x46,0x00,0xa8,0x09,0xf2,0x06,0x03,
-0x40,0x4b,0x1a,0x60,0x5e,0x78,0xff,0xff,0x2b,0x44,0xf5,0x00,0x0c,0x4e,0x2e,0x58,
-0xff,0xff,0x25,0x46,0x08,0xf0,0x27,0x43,0xa4,0xd5,0xbd,0xd3,0x46,0x45,0x63,0x45,
-0x64,0x43,0x60,0x41,0x00,0xa8,0x00,0x64,0xa3,0xdb,0xbe,0xdb,0x04,0x02,0x65,0x43,
-0x25,0x44,0xbf,0xdb,0x05,0x00,0x61,0x44,0x0a,0xfa,0x61,0x46,0x25,0x44,0x09,0xfa,
-0x25,0x44,0x27,0x43,0x08,0xfe,0x05,0x03,0x60,0x46,0x09,0xf2,0x08,0xfc,0x00,0xa8,
-0xfa,0x00,0x66,0x44,0xa5,0xdb,0x2e,0x58,0xff,0xff,0x28,0x41,0x58,0x4f,0x0e,0x00,
-0x2e,0x58,0xff,0xff,0x28,0x41,0x40,0xa1,0x58,0x4f,0x08,0x00,0x05,0x03,0x28,0x41,
-0x58,0x4f,0x26,0x00,0x58,0x4f,0x56,0x00,0x2e,0x58,0xff,0xff,0x9e,0xf3,0x7c,0x63,
-0x00,0xbe,0x40,0x45,0x1b,0x03,0x00,0x65,0x65,0x44,0xdc,0x85,0x84,0xa1,0x00,0xf2,
-0x06,0x06,0x01,0xfc,0x00,0xa8,0x60,0x46,0xf7,0x02,0x40,0x45,0x0f,0x00,0x33,0x44,
-0x54,0x93,0x33,0x44,0xfd,0xfb,0x80,0x60,0x7c,0x64,0x01,0xfa,0x00,0x64,0x00,0xf0,
-0x00,0xfa,0xd0,0x80,0x9e,0xf9,0x02,0x02,0x9f,0xf9,0x08,0xfe,0x2f,0x58,0xff,0xff,
-0x66,0x43,0x25,0x46,0x05,0xfc,0x06,0xfc,0x61,0x44,0x02,0xfa,0x01,0xf0,0x03,0x67,
-0xb0,0x84,0x00,0xf0,0x3c,0x7e,0x01,0xfa,0x04,0x64,0x03,0xfa,0x04,0xf8,0x00,0x64,
-0x23,0xfa,0x0c,0x61,0x10,0x63,0x59,0xda,0xfe,0x1f,0x2f,0x58,0xff,0xff,0x05,0x4c,
-0x7c,0x61,0x58,0x4f,0xc3,0x00,0x80,0x63,0x13,0x03,0x2c,0x46,0xbf,0xd0,0x25,0x46,
-0xff,0xd8,0x2c,0x46,0xfb,0x1d,0x25,0x46,0x00,0x64,0xd0,0x80,0x09,0xfa,0x0a,0xfa,
-0x05,0x03,0x64,0x46,0x01,0xf0,0x08,0x67,0xc0,0x84,0x01,0xfa,0x58,0x4f,0x02,0x00,
-0x2e,0x58,0xff,0xff,0x27,0x43,0x00,0xbb,0x25,0x46,0x12,0x03,0xbe,0xd3,0x08,0xfc,
-0x00,0xa8,0xff,0xff,0x03,0x02,0x25,0x44,0xa3,0xdb,0x04,0x00,0x0a,0xfa,0x60,0x46,
-0x25,0x44,0x09,0xfa,0xbe,0xdb,0x04,0xa3,0xa3,0xd3,0xff,0xff,0xdc,0x84,0xa3,0xdb,
-0x2f,0x58,0xff,0xff,0x07,0x4c,0x58,0x4f,0x28,0x00,0x0c,0x47,0x58,0x4f,0xe2,0x00,
-0x2e,0x58,0xff,0xff,0x07,0x4c,0x58,0x4f,0x20,0x00,0x0c,0x47,0x58,0x4f,0x5e,0x00,
-0x2e,0x58,0xff,0xff,0x07,0x4c,0x58,0x4f,0x18,0x00,0x0c,0x47,0x27,0x44,0x00,0xbe,
-0x08,0xf0,0x11,0x03,0x09,0xf2,0x25,0x43,0x09,0xfc,0x63,0x46,0x27,0x43,0x0a,0xfc,
-0x09,0xfa,0x08,0xf8,0x00,0xa8,0x66,0x43,0x03,0x02,0x64,0x44,0x58,0xdd,0x03,0x00,
-0x60,0x46,0x25,0x44,0x0a,0xfa,0x2e,0x58,0xff,0xff,0x25,0x46,0x08,0xf2,0x09,0xf0,
-0x00,0xa8,0x40,0x47,0x1d,0x03,0x0a,0xf2,0x64,0x43,0x00,0xbb,0x27,0x42,0xda,0x82,
-0x08,0x24,0xa2,0xdb,0x00,0xa8,0xca,0x82,0x02,0x02,0xa2,0xdd,0x02,0x00,0x60,0x46,
-0x09,0xfc,0x00,0xbb,0x63,0x46,0x08,0x28,0x0a,0xfa,0x25,0x46,0x00,0x64,0x09,0xfa,
-0x0a,0xfa,0x08,0xfa,0x27,0x42,0x04,0xa2,0xa2,0xd3,0xff,0xff,0xcc,0x84,0xa2,0xdb,
-0x2f,0x58,0xff,0xff,0x25,0x46,0x01,0xf2,0x00,0xf2,0xff,0xff,0x03,0x23,0x11,0x00,
-0x00,0xa8,0x60,0x46,0x0c,0x03,0x01,0xf0,0x78,0x67,0xa0,0x80,0xf8,0x67,0x07,0x03,
-0xc0,0x84,0x01,0xfa,0x25,0x46,0x00,0x64,0x00,0xfa,0x25,0x44,0x05,0xfa,0x58,0x4f,
-0xc4,0x00,0x58,0x4f,0x27,0x00,0xd1,0xfe,0x2e,0x58,0xff,0xff,0x27,0x43,0x00,0xbb,
-0x25,0x46,0x10,0x03,0xa3,0xd3,0x08,0xfc,0x00,0xa8,0x09,0xfa,0x03,0x02,0x25,0x44,
-0xbe,0xdb,0x04,0x00,0x60,0x46,0x25,0x44,0x0a,0xfa,0x25,0x46,0x00,0x64,0x0a,0xfa,
-0x25,0x44,0xa3,0xdb,0x2f,0x58,0xff,0xff,0x9f,0xf3,0x05,0xf0,0x00,0xbe,0x9f,0xf9,
-0x25,0x44,0x02,0x02,0x9e,0xfb,0x01,0x00,0x00,0xfa,0x25,0x46,0x7c,0x64,0x01,0xfa,
-0x2f,0x58,0xff,0xff,0x9f,0xf3,0x00,0x61,0x00,0xbe,0x25,0x44,0x02,0x03,0x00,0xfa,
-0x01,0x00,0x9e,0xfb,0x60,0x46,0x00,0xf2,0x66,0x43,0x00,0xbe,0xdd,0x81,0xfb,0x02,
-0x9f,0xfd,0x33,0x45,0x45,0x93,0x33,0x44,0xfd,0xfb,0x2f,0x58,0xff,0xff,0x25,0x46,
-0x05,0xf0,0x06,0xf2,0x05,0xfa,0xd0,0x80,0x64,0x43,0x0e,0x03,0x60,0x46,0x01,0xf0,
-0x80,0x67,0xb0,0x84,0x01,0xfa,0x00,0xf0,0x00,0x64,0x00,0xfa,0x64,0x46,0x05,0xfc,
-0x46,0x45,0x58,0x4f,0xd7,0x00,0xd1,0xfe,0x2e,0x58,0xff,0xff,0x00,0x66,0x46,0x45,
-0x29,0x43,0xfc,0xa3,0x66,0x44,0xbd,0xdb,0x25,0x44,0xbd,0xdb,0x00,0x64,0xbd,0xdb,
-0x03,0x61,0x1a,0x65,0x3c,0x60,0x80,0x63,0x43,0x49,0xa3,0xd3,0x06,0xa3,0x00,0xa8,
-0xcd,0x81,0x04,0x02,0xf9,0x02,0x19,0x60,0x56,0x78,0xff,0xff,0x01,0x26,0xe6,0x00,
-0xd4,0x80,0x60,0x45,0xe3,0x05,0xf6,0xa3,0xbd,0xd1,0xbd,0xd1,0x44,0x47,0x44,0x48,
-0x44,0x45,0x13,0x60,0x1a,0x64,0x44,0xd7,0xff,0xff,0xff,0xff,0x9e,0xf3,0x33,0x41,
-0x00,0xa8,0x40,0x45,0x0f,0x03,0x60,0x46,0x80,0x60,0x7c,0x64,0x01,0xfa,0x4d,0x93,
-0x33,0x44,0xfd,0xfb,0x00,0x64,0x00,0xf0,0x00,0xfa,0xd0,0x80,0x9e,0xf9,0x02,0x02,
-0x9f,0xf9,0x08,0xfe,0x2f,0x58,0xff,0xff,0xa2,0xfe,0xff,0xff,0x12,0x05,0xa0,0xfe,
-0xff,0xff,0x07,0x05,0xa3,0xfe,0xff,0xff,0x07,0x05,0xa1,0xfe,0xff,0xff,0x0a,0x04,
-0x18,0x00,0x20,0x58,0xff,0xff,0xff,0xff,0x65,0xf3,0xff,0xff,0x80,0xbc,0x65,0xfb,
-0x10,0x00,0xff,0xff,0xff,0xff,0x0a,0x00,0x99,0xff,0x30,0x44,0x50,0xbc,0x40,0x51,
-0x98,0xff,0x99,0xff,0x30,0x44,0x7d,0xb4,0x40,0x51,0x98,0xff,0xa1,0xff,0xff,0xff,
-0xbb,0x3f,0x3c,0x44,0xac,0x84,0x8b,0xf3,0xf9,0x02,0x02,0xa8,0x00,0x64,0x09,0x02,
-0x8b,0xfb,0x8c,0xfb,0x8d,0xfb,0xca,0xfe,0x00,0x64,0x8e,0xfb,0x1b,0x60,0x20,0x78,
-0xff,0xff,0x8b,0xf3,0x8c,0xf3,0x02,0xa8,0x02,0xa8,0x0a,0x02,0x00,0x64,0x8d,0xfb,
-0x8b,0xfb,0x8c,0xfb,0x00,0x64,0x8e,0xfb,0xca,0xfe,0x1b,0x60,0xd8,0x78,0xff,0xff,
-0x8d,0xf1,0x00,0x64,0x64,0x41,0x02,0x02,0x8c,0xfb,0xca,0xfe,0x61,0x44,0x01,0xa8,
-0xff,0xff,0x09,0x02,0x3c,0x60,0x76,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,
-0x14,0x02,0xfe,0xb4,0x8d,0xfb,0x95,0xf3,0xff,0xff,0x60,0x40,0x01,0x26,0x10,0x00,
-0x8c,0xf3,0x8e,0xf3,0x01,0xa8,0x02,0xb0,0x01,0x02,0x0a,0x03,0x3c,0x60,0x2e,0x62,
-0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x67,0x03,0x1b,0x60,0xef,0x78,0xff,0xff,
-0x3c,0x60,0x2e,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x74,0x02,0xcb,0xf3,
-0xff,0xff,0xfd,0xa0,0x6d,0xf3,0x44,0x02,0x02,0xb0,0x67,0xf1,0x06,0x02,0x64,0x43,
-0x00,0xbb,0xff,0xff,0x02,0x03,0x63,0x44,0x08,0x00,0xfd,0xb4,0x6d,0xfb,0x3c,0x60,
-0x28,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x00,0xbc,0x67,0xfb,0x44,0x03,
-0x60,0x46,0x2b,0xf2,0xff,0xff,0x01,0xb0,0x6d,0xf3,0x04,0x03,0x01,0xb0,0xff,0xff,
-0x19,0x02,0x16,0x00,0xfe,0xb4,0x6d,0xfb,0x69,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,
-0x0f,0x03,0x19,0xf2,0xff,0xff,0x00,0xbc,0xff,0xff,0x0c,0x03,0xa0,0xd3,0xff,0xff,
-0x02,0xb0,0xff,0xff,0x07,0x03,0x69,0x60,0x58,0x4e,0x9b,0x78,0xff,0xff,0x02,0x03,
-0x09,0xf2,0xdb,0x00,0x0d,0xf2,0xff,0xff,0x00,0x7f,0x0d,0xfa,0x09,0xf2,0x67,0xfb,
-0x00,0xbc,0xff,0xff,0x30,0x02,0x6d,0xf3,0xff,0xff,0xfe,0xb4,0x6d,0xfb,0x2b,0x00,
-0x3c,0x60,0x28,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x0d,0x03,0x13,0xf3,
-0x95,0xf3,0x00,0xa8,0x00,0xa0,0x1f,0x03,0x1e,0x03,0x22,0xf0,0x10,0x64,0xb0,0x84,
-0xa2,0xda,0x1e,0x60,0xc5,0x78,0xff,0xff,0x00,0x64,0x40,0x5c,0x65,0xf3,0xff,0xff,
-0xcb,0xf1,0x80,0x2a,0x0d,0x00,0x7f,0xb4,0x65,0xfb,0x64,0x40,0x04,0x3a,0x08,0x00,
-0x29,0x44,0x2a,0x37,0x05,0x00,0x74,0xf3,0xff,0xff,0x04,0xbc,0x74,0xfb,0xc9,0xfe,
-0x1b,0x60,0x20,0x78,0xff,0xff,0x65,0xf3,0xff,0xff,0xff,0xff,0x80,0x26,0x13,0x00,
-0x02,0x26,0x0f,0x00,0x68,0x60,0xcc,0x62,0x90,0x60,0x62,0x64,0xa2,0xdb,0x3c,0x60,
-0x9a,0x62,0x08,0x64,0xa2,0xdb,0x00,0x64,0x5a,0xdb,0x2d,0xff,0x1b,0x60,0x2a,0x78,
-0xff,0xff,0x80,0xbc,0x65,0xfb,0xcb,0xf3,0x0e,0xf0,0xfc,0xa0,0xfd,0xa0,0x01,0x03,
-0x17,0x02,0x60,0x41,0x02,0x65,0x64,0x44,0x40,0x49,0x2a,0x37,0x11,0x00,0x18,0x37,
-0x0f,0x00,0xff,0x37,0x02,0x00,0xfd,0x3b,0x06,0x00,0x61,0x44,0xfd,0xa0,0x01,0x65,
-0x02,0x03,0x00,0x64,0x66,0xfb,0x74,0xf3,0xff,0xff,0xb4,0x84,0x74,0xfb,0xc9,0xfe,
-0x29,0xf2,0x66,0xf1,0x60,0x40,0xb0,0x36,0x06,0x00,0x00,0x36,0x04,0x00,0x20,0x36,
-0x02,0x00,0xb0,0x84,0x29,0xfa,0xf7,0x60,0x0d,0x78,0xff,0xff,0x1a,0xee,0x7f,0x00,
-0x86,0x02,0x67,0x60,0x4e,0x62,0x00,0x64,0xa2,0xdb,0x29,0xf0,0xff,0xff,0x64,0x40,
-0x40,0x27,0x03,0x00,0x1c,0x60,0x32,0x78,0xff,0xff,0x3e,0xf3,0xff,0xff,0xff,0xff,
-0x04,0x2a,0x19,0x00,0x46,0x5c,0x67,0x60,0x58,0x62,0x01,0x64,0xa2,0xdb,0xf7,0x60,
-0x2e,0x64,0x40,0x44,0x99,0xff,0x30,0x44,0x41,0xbc,0x40,0x51,0x98,0xff,0xa1,0xff,
-0xff,0xff,0xbb,0x3f,0x99,0xff,0x30,0x44,0xfe,0xb4,0x40,0x51,0x98,0xff,0x67,0x60,
-0x58,0x62,0x00,0x64,0xa2,0xdb,0x42,0xf3,0x00,0x65,0x60,0x41,0xd6,0xf3,0x95,0xf3,
-0x00,0xa0,0x00,0xa0,0x09,0x03,0x23,0xf2,0x04,0x03,0x60,0x45,0x00,0x7f,0x60,0x41,
-0x03,0x00,0xff,0xff,0xff,0x36,0x03,0x61,0x44,0xf1,0x98,0xff,0x64,0x40,0x08,0x26,
-0x02,0x00,0x00,0x64,0x14,0x00,0x07,0xf2,0xff,0xff,0x00,0xa0,0xff,0xff,0x06,0x03,
-0x1a,0xf2,0x38,0xfb,0xff,0xff,0x1b,0xf2,0x39,0xfb,0x15,0x00,0x38,0xf3,0x39,0xf1,
-0xdc,0x84,0x38,0xfb,0x1a,0xfa,0x64,0x44,0x02,0x24,0xdc,0x84,0xff,0xb4,0x60,0x5c,
-0x02,0xfe,0x61,0x44,0x03,0xb4,0xf8,0x84,0xf8,0x84,0xf8,0x84,0xb0,0x84,0x39,0xfb,
-0x1b,0xfa,0x01,0x64,0x07,0xfa,0x61,0x5c,0x41,0xf3,0x64,0x40,0x03,0x26,0x06,0x00,
-0x60,0x40,0x01,0x2a,0x15,0x00,0x67,0x60,0x90,0x63,0x30,0x00,0x64,0x40,0x03,0x2a,
-0x06,0x00,0x60,0x40,0x08,0x2a,0x0c,0x00,0x67,0x60,0xba,0x63,0x27,0x00,0x64,0x40,
-0x01,0x2a,0x0e,0x00,0x60,0x40,0x02,0x2a,0x03,0x00,0x67,0x60,0x9e,0x63,0x1e,0x00,
-0x46,0x5c,0x22,0xf0,0x04,0x64,0xb0,0x84,0xa2,0xda,0x1e,0x60,0xc5,0x78,0xff,0xff,
-0x60,0x40,0x04,0x2a,0xf5,0x00,0x65,0x47,0x00,0x36,0x0e,0x00,0x00,0x7f,0xe0,0x84,
-0x60,0x45,0xe0,0x84,0xe0,0x81,0xc4,0x85,0xc5,0x85,0x3d,0x60,0x4e,0x64,0xc4,0x83,
-0x02,0x61,0x67,0x44,0xda,0xfb,0x02,0x00,0x67,0x60,0xac,0x63,0x00,0x60,0x00,0xea,
-0xff,0xff,0xff,0xff,0x00,0x60,0x00,0xeb,0xff,0xff,0xff,0xff,0x38,0xf1,0xe0,0x7f,
-0x64,0x5e,0x99,0xff,0x40,0x5a,0x64,0x47,0xe1,0x7f,0x40,0x5a,0x39,0xf3,0xff,0xff,
-0xe2,0x7f,0x40,0x5a,0x61,0x5c,0xda,0xf3,0xda,0xf9,0xd0,0x80,0xbd,0xd3,0x30,0x03,
-0x60,0x45,0xe3,0x7f,0x40,0x5a,0x65,0x47,0xe4,0x7f,0xbd,0xd3,0x40,0x5a,0x60,0x45,
-0xe5,0x7f,0x40,0x5a,0x65,0x47,0xe6,0x7f,0xbd,0xd3,0x40,0x5a,0x60,0x45,0x45,0xf1,
-0xe7,0x7f,0x40,0x5a,0x64,0x40,0x0f,0x3a,0x61,0x00,0x65,0x47,0xe8,0x7f,0xbd,0xd3,
-0x40,0x5a,0x60,0x45,0xe9,0x7f,0x40,0x5a,0x65,0x47,0xea,0x7f,0xbd,0xd3,0x40,0x5a,
-0x60,0x45,0xeb,0x7f,0x40,0x5a,0x65,0x47,0xec,0x7f,0xbd,0xd3,0x40,0x5a,0x60,0x45,
-0xed,0x7f,0x40,0x5a,0x65,0x47,0xee,0x7f,0xbd,0xd3,0x40,0x5a,0xef,0x7f,0x40,0x5a,
-0x20,0x60,0x00,0xeb,0x67,0x60,0xc8,0x62,0xa2,0xd3,0xff,0xff,0xff,0xff,0x01,0x2a,
-0x3d,0x00,0x00,0x64,0xa2,0xdb,0x00,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0xff,0xff,
-0x3a,0x5c,0x80,0x2b,0x12,0x00,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,
-0x09,0x00,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,0x03,0x00,0x8b,0xff,
-0x74,0x40,0x88,0xff,0x8b,0xff,0x74,0x40,0x88,0xff,0x3a,0x5c,0x80,0x2b,0xff,0xff,
-0x31,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0x00,0x60,0x00,0xea,0x3a,0x5c,0x80,0x27,
-0x09,0x00,0x31,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0x00,0x60,0x00,0xea,0x3a,0x5c,
-0x80,0x2b,0xf7,0x00,0x00,0x64,0xdb,0xfb,0x67,0x60,0xcc,0x64,0x3a,0x5c,0xa0,0xd9,
-0xff,0xff,0x30,0x60,0x00,0xea,0xff,0xff,0xff,0xff,0xff,0xff,0x3a,0x40,0x40,0x27,
-0xfd,0x00,0x67,0x60,0x5c,0x62,0x00,0x64,0xa2,0xdb,0x45,0xf1,0x80,0x60,0x00,0x64,
-0xb0,0x84,0x40,0x5a,0x98,0xff,0x33,0x60,0x00,0xea,0x3f,0xf3,0xff,0xff,0x01,0xbc,
-0x3f,0xfb,0x1c,0x60,0x32,0x78,0xff,0xff,0x64,0x38,0x7e,0x00,0xe8,0x06,0x19,0xf2,
-0xff,0xff,0x00,0xb8,0x1d,0xf2,0x06,0x03,0x60,0x47,0x00,0x7f,0x53,0xfb,0x51,0xfb,
-0x60,0x5c,0x07,0x00,0x50,0xf3,0x51,0xf1,0xff,0xff,0x52,0xfb,0x53,0xf9,0x00,0x64,
-0x54,0xfb,0x95,0xf3,0xff,0xff,0x00,0xbc,0xff,0xff,0x06,0x02,0x29,0xf2,0xff,0xff,
-0x0c,0xb4,0xff,0xff,0x00,0x3a,0x02,0x00,0x13,0xf0,0x53,0xf9,0x64,0x44,0x00,0xa0,
-0x01,0x63,0x04,0x03,0xff,0xa0,0x02,0x63,0x01,0x03,0x0b,0x63,0x55,0xfd,0x46,0x5c,
-0x22,0xf2,0xff,0xff,0x60,0x40,0x10,0x26,0x40,0x00,0x21,0xf2,0x16,0xf2,0x60,0x45,
-0x45,0x4c,0xc4,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,0x21,0xfa,0x17,0xf0,0x2c,0x44,
-0xc0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,0x1f,0xfa,0x95,0xf3,0x29,0xf0,0x00,0xbc,
-0x00,0x64,0x03,0x02,0x64,0x40,0x80,0x3a,0x01,0xbc,0x4f,0xfb,0x95,0xf3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x06,0x03,0x27,0xf2,0xff,0xff,0x00,0xa0,0xff,0xff,0x19,0x03,
-0x1c,0x00,0xa6,0xf3,0x56,0x63,0x02,0xa8,0x29,0xf2,0x13,0x03,0x29,0xf0,0x60,0x47,
-0x64,0x40,0x08,0x2a,0x0e,0x00,0x03,0xa8,0x03,0x2e,0x0b,0x00,0x03,0xb0,0x03,0x22,
-0x62,0x63,0x81,0xf1,0xbd,0xd8,0xff,0xff,0x82,0xf1,0xbd,0xd8,0xff,0xff,0x83,0xf1,
-0xa3,0xd8,0x4c,0xf3,0x34,0xfa,0x10,0xa4,0x4c,0xfb,0x29,0xf2,0xcb,0xf3,0x60,0x40,
-0x08,0x3a,0x76,0x00,0xfd,0xa0,0xfc,0xa0,0x05,0x03,0x95,0xf3,0x03,0x03,0x60,0x40,
-0x00,0x36,0x6e,0x00,0x14,0xf2,0x21,0xf0,0xcc,0x84,0x60,0x41,0x04,0x03,0x00,0x64,
-0xcd,0x81,0xc0,0x84,0xfd,0x02,0x1f,0xf0,0x53,0xf1,0xc0,0x84,0x55,0xf1,0x64,0x41,
-0x02,0x36,0xe0,0x84,0x64,0x45,0x00,0x62,0x11,0x61,0xe0,0x84,0xcd,0x81,0xfd,0x04,
-0x01,0x00,0xe0,0x84,0xf2,0x82,0xff,0xff,0x02,0x24,0xc6,0x82,0x02,0x28,0xd6,0x82,
-0xe2,0x80,0xcd,0x81,0x02,0x28,0x01,0xbc,0xf4,0x02,0x01,0x2a,0xc6,0x82,0x14,0xf0,
-0x60,0x43,0x00,0x60,0xaf,0x65,0x00,0x64,0x64,0x41,0xcd,0x81,0xc4,0x84,0xfd,0x02,
-0x63,0x45,0xc4,0x84,0x40,0x48,0x60,0x45,0x68,0x60,0xcc,0x62,0x80,0x60,0x10,0x64,
-0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x28,0x44,0x70,0x45,0xc4,0x85,
-0x02,0x60,0x58,0x64,0xc4,0x84,0xa4,0xf1,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,
-0xe8,0x84,0xe8,0x84,0xc0,0x84,0x60,0x41,0x73,0x45,0xd4,0x80,0xe1,0xf1,0x0d,0x0d,
-0x64,0x44,0x00,0x36,0x38,0x00,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,0x61,0x45,
-0xc4,0x84,0x73,0x45,0xd4,0x80,0xff,0xff,0x2e,0x0e,0x68,0x60,0xcc,0x62,0x80,0x60,
-0x20,0x64,0xa2,0xdb,0xcb,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x0a,0x02,0x00,0x64,
-0x40,0x5c,0x22,0xf0,0x10,0x64,0xb0,0x84,0xa2,0xda,0x1b,0x60,0x20,0x78,0xff,0xff,
-0x26,0x00,0xe2,0xf3,0x28,0x45,0x60,0x40,0x00,0x3a,0x15,0x00,0x07,0x60,0xd0,0x64,
-0xc4,0x84,0x70,0x45,0xd4,0x80,0xff,0xff,0x0e,0x04,0x60,0x50,0x60,0x45,0x68,0x60,
-0xcc,0x62,0x80,0x60,0x30,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,
-0x01,0x64,0xe4,0xfb,0x0c,0x00,0x28,0x44,0x68,0xfb,0x60,0x45,0x68,0x60,0xcc,0x62,
-0x80,0x60,0x10,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x67,0x60,
-0x4e,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0x00,0x64,0x08,0x03,0xa2,0xdb,0x67,0x60,
-0x5a,0x62,0xa2,0xd1,0x1e,0x60,0x9a,0x62,0xa2,0xd9,0x2a,0x00,0x1e,0x60,0x98,0x64,
-0xa0,0xd1,0x95,0xf3,0x64,0x41,0x00,0xa0,0x29,0xf2,0x0b,0x02,0x60,0x40,0x80,0x3a,
-0x08,0x00,0x71,0xf3,0xdd,0x81,0xf6,0xa0,0xec,0xa0,0x03,0x04,0xdd,0x81,0x01,0x04,
-0xdd,0x81,0x25,0xf2,0xff,0xff,0x60,0x47,0x60,0x43,0xff,0xb3,0x61,0x40,0xff,0x22,
-0x04,0x00,0x23,0x60,0x58,0x4f,0x4a,0x78,0xff,0xff,0x1e,0x60,0x9a,0x64,0xa0,0xdd,
-0xb8,0xf1,0x23,0x60,0x58,0x4f,0x49,0x78,0xff,0xff,0x1e,0x60,0x9c,0x64,0xa0,0xdd,
-0x29,0xf2,0xff,0xff,0x60,0x45,0x68,0x60,0xcc,0x62,0x90,0x60,0x60,0x64,0xa2,0xdb,
-0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x22,0xf0,0xff,0x60,0xef,0x64,0xa0,0x84,
-0xa2,0xda,0x8e,0xf3,0x46,0x5c,0xfd,0xb4,0x01,0xbc,0x8e,0xfb,0x66,0x44,0xe0,0xfb,
-0x20,0x60,0x19,0x78,0xff,0xff,0x00,0x64,0x68,0xfb,0xe0,0xfb,0x68,0x60,0xcc,0x62,
-0x90,0x60,0x61,0x64,0xa2,0xdb,0x46,0x5c,0x8e,0xf3,0x3c,0x46,0x4e,0xf1,0xfe,0xb4,
-0x8e,0xfb,0x01,0x65,0x32,0x40,0x04,0x2b,0x64,0x45,0x02,0x22,0x03,0x00,0x1e,0x60,
-0xed,0x78,0xff,0xff,0x65,0x40,0x01,0x26,0x09,0x00,0x5a,0x60,0x5a,0x64,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1e,0x60,0xc5,0x78,0xff,0xff,0x27,0xf0,0x01,0x60,
-0x00,0x64,0xc0,0x84,0x27,0xfa,0x29,0xf0,0x00,0x60,0x0c,0x64,0xa0,0x84,0x04,0x36,
-0x02,0x00,0x0c,0x3a,0x03,0x00,0x1e,0x60,0xc5,0x78,0xff,0xff,0x60,0x41,0x61,0x40,
-0x08,0x36,0x67,0x00,0x29,0xf0,0x00,0x60,0xf0,0x64,0xa0,0x84,0xff,0xff,0x00,0x3a,
-0x07,0x00,0x5a,0x60,0x1e,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x56,0x00,
-0x20,0x3a,0x07,0x00,0x5a,0x60,0x1e,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x4d,0x00,0x40,0x3a,0x07,0x00,0x5a,0x60,0x26,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x44,0x00,0xb0,0x3a,0x07,0x00,0x5a,0x60,0x1c,0x64,0xa0,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x3b,0x00,0x80,0x3a,0x16,0x00,0x68,0x60,0xcc,0x62,0x90,0x60,
-0x31,0x64,0xa2,0xdb,0x71,0xf3,0xff,0xff,0xdc,0x84,0x71,0xfb,0x80,0xf3,0xff,0xff,
-0xfd,0xa4,0x60,0x47,0x01,0xbc,0x6e,0xfb,0x5a,0x60,0x14,0x64,0xa0,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x23,0x00,0xa0,0x3a,0x07,0x00,0x5a,0x60,0x24,0x64,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1a,0x00,0x50,0x3a,0x07,0x00,0x5a,0x60,0x2a,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x11,0x00,0x10,0x3a,0x07,0x00,0x5a,0x60,
-0x20,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x08,0x00,0x30,0x3a,0x06,0x00,
-0x5a,0x60,0x20,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1e,0x60,0xc5,0x78,
-0xff,0xff,0x19,0xf2,0x0e,0x65,0x00,0xbc,0xc4,0x83,0x14,0x03,0xa3,0xd3,0xff,0xff,
-0x60,0x40,0x14,0x3a,0xdc,0x84,0xa3,0xdb,0x14,0x3a,0x0c,0x00,0x60,0x47,0x00,0x7f,
-0xfd,0xa0,0xff,0xff,0x07,0x07,0x19,0xf0,0x1f,0x60,0x58,0x4e,0xd5,0x78,0xff,0xff,
-0x00,0x64,0xa3,0xdb,0x14,0xf2,0x28,0xf2,0x60,0x41,0x60,0x45,0x1e,0x63,0x60,0x47,
-0x18,0x63,0x04,0xa3,0x63,0x45,0x00,0x63,0xcd,0x81,0xc7,0x83,0xfd,0x02,0x38,0xf0,
-0xff,0xff,0xc3,0x83,0x2b,0xf2,0xff,0xff,0xff,0xff,0x01,0x2a,0x31,0x00,0x3d,0x60,
-0x1c,0x64,0xa0,0xd3,0xff,0xff,0x01,0xbc,0xa2,0xdb,0x5a,0x60,0x00,0x64,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x51,0xf3,0xff,0xff,0xe0,0x84,0xe0,0x84,0x60,0x45,0x5a,0x60,0x04,0x64,
-0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x5a,0x60,0x42,0x64,0xa0,0xd3,0x63,0x45,0xc4,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x32,0x00,
-0x59,0x60,0xec,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,
-0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x53,0xf3,0xff,0xff,0xe0,0x84,0xe0,0x84,
-0x60,0x45,0x59,0x60,0xf0,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x18,0x60,0xbc,0x65,
-0x53,0xf3,0xff,0xff,0xe0,0x84,0xc4,0x82,0x00,0x64,0xa2,0xdb,0x5a,0x60,0x42,0x64,
-0xa0,0xd3,0x63,0x45,0xc4,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0xcb,0xf3,0x3c,0x46,0xfd,0xa0,0xfc,0xa0,0x01,0x03,0x13,0x00,
-0x22,0xf2,0xff,0xff,0x60,0x40,0x10,0x2a,0x0e,0x00,0x8c,0xf1,0x00,0x64,0x40,0x5c,
-0x13,0xfb,0xe0,0xfb,0x64,0x40,0x02,0x36,0x03,0x00,0x1b,0x60,0x20,0x78,0xff,0xff,
-0x1b,0x60,0x2d,0x78,0xff,0xff,0x3c,0x60,0x7c,0x62,0x3c,0x60,0x4c,0x64,0xa2,0xdb,
-0x3c,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xce,0xfe,0x1b,0x60,
-0x3d,0x78,0xff,0xff,0x67,0x60,0x4e,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,
-0x03,0x03,0x1f,0x60,0xa1,0x78,0xff,0xff,0x0f,0xf0,0xb7,0xf1,0x64,0x41,0x01,0x2a,
-0xb6,0xf1,0x95,0xf3,0xff,0xff,0x60,0x40,0xff,0x26,0x1d,0xf0,0x26,0xf2,0xff,0xff,
-0xdc,0x84,0x26,0xfa,0x15,0xf2,0xff,0xff,0xdc,0x84,0xd0,0x80,0x15,0xfa,0x03,0x04,
-0x1f,0x60,0xa6,0x78,0xff,0xff,0x13,0xf3,0xff,0xff,0x00,0xa8,0xff,0xff,0x05,0x03,
-0x22,0xf0,0x10,0x64,0xb0,0x84,0xa2,0xda,0xad,0x00,0x95,0xf3,0xff,0xff,0x00,0xbc,
-0x29,0xf2,0x02,0x02,0x0c,0xb4,0x00,0x36,0x7d,0x00,0x19,0xf2,0xff,0xff,0x00,0xbc,
-0x0e,0xa4,0x4f,0x03,0x60,0x43,0xa3,0xd1,0x01,0x60,0x01,0x64,0xc0,0x84,0xa3,0xdb,
-0x60,0x45,0x00,0x7f,0xec,0xa0,0xff,0xff,0x0c,0x04,0x65,0x47,0x00,0x7f,0xf9,0xa0,
-0xff,0xff,0x05,0x04,0x19,0xf0,0x1f,0x60,0x58,0x4e,0xbe,0x78,0xff,0xff,0x00,0x64,
-0xa3,0xdb,0x0d,0xf2,0xff,0xff,0x01,0xa4,0x0d,0xfa,0x00,0x7f,0xfe,0xa0,0xff,0xff,
-0x15,0x04,0x1f,0x60,0x58,0x4e,0xec,0x78,0xff,0xff,0x1d,0xf2,0xff,0xff,0x00,0x7e,
-0x60,0x47,0x53,0xfb,0x0b,0x63,0x60,0x40,0x00,0x36,0x01,0x63,0x60,0x40,0x01,0x36,
-0x02,0x63,0x55,0xfd,0x0d,0xf2,0xff,0xff,0x00,0x7e,0x0d,0xfa,0x0d,0xf2,0xff,0xff,
-0x60,0x47,0x01,0xa4,0x60,0x47,0x0d,0xfa,0x60,0x47,0x00,0x7f,0xfd,0xa0,0xff,0xff,
-0x39,0x04,0x69,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0x04,0x03,0x69,0x60,0x58,0x4e,
-0xcc,0x78,0xff,0xff,0x22,0xf0,0x10,0x64,0xb0,0x84,0xa2,0xda,0x1e,0x60,0xc5,0x78,
-0xff,0xff,0x54,0xf3,0xff,0xff,0xdc,0x84,0xfe,0xa0,0x54,0xfb,0x23,0x04,0x00,0x64,
-0x54,0xfb,0x53,0xf3,0x52,0xf3,0x60,0x41,0xff,0xa0,0xe8,0x84,0x1b,0x03,0xff,0xa1,
-0x60,0x45,0x59,0x60,0x6a,0x63,0xa3,0xd3,0xff,0xff,0xa4,0x80,0x65,0x44,0xf4,0x03,
-0x52,0xfb,0x61,0x43,0x53,0xfd,0x63,0x44,0x00,0x3a,0x02,0x00,0x01,0x63,0x09,0x00,
-0x01,0x3a,0x02,0x00,0x02,0x63,0x05,0x00,0x02,0x3a,0x02,0x00,0x0b,0x63,0x01,0x00,
-0x0b,0x63,0x55,0xfd,0x29,0xf0,0x08,0x67,0xb0,0x84,0xa2,0xda,0x00,0x64,0x4f,0xfb,
-0xf8,0x60,0x50,0x78,0xff,0xff,0xa0,0xf0,0x7f,0x00,0x72,0x00,0x29,0xf0,0xff,0xff,
-0x64,0x40,0x40,0x2b,0x31,0x00,0x3e,0xf3,0xff,0xff,0x60,0x40,0x04,0x2a,0x18,0x00,
-0x67,0x60,0x58,0x62,0x01,0x64,0xa2,0xdb,0xf8,0x60,0x69,0x64,0x40,0x44,0x99,0xff,
-0x30,0x44,0x41,0xbc,0x40,0x51,0x98,0xff,0xa1,0xff,0xff,0xff,0xbb,0x3f,0x99,0xff,
-0x30,0x44,0xfe,0xb4,0x40,0x51,0x98,0xff,0x67,0x60,0x58,0x62,0x00,0x64,0xa2,0xdb,
-0x99,0xff,0x3a,0x44,0x98,0xff,0x8f,0x2b,0x0f,0x00,0x50,0x27,0x0d,0x00,0x67,0x60,
-0x5c,0x62,0x00,0x64,0xa2,0xdb,0x45,0xf1,0x80,0x60,0x00,0x64,0xb0,0x84,0x99,0xff,
-0x40,0x5a,0x98,0xff,0x33,0x60,0x00,0xea,0x1c,0x60,0xa0,0x78,0xff,0xff,0x4c,0x3f,
-0x7e,0x00,0xa8,0x07,0x29,0xf2,0xff,0xff,0x60,0x40,0x08,0x3a,0x06,0x00,0x5a,0x60,
-0x5a,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1e,0x60,0x98,0x65,0xb8,0xf3,
-0xff,0xff,0xa5,0xdb,0x22,0xf0,0x01,0x64,0xb0,0x84,0xa2,0xda,0x1e,0x60,0xc5,0x78,
-0xff,0xff,0xff,0x00,0x64,0x44,0x08,0xa4,0xa0,0xd3,0xff,0xff,0x60,0x41,0x60,0x47,
-0x00,0x7f,0x60,0x45,0x61,0x44,0x00,0x7f,0xe8,0x84,0xa4,0x80,0x00,0xa0,0x02,0x02,
-0x06,0x03,0xfa,0x00,0x60,0x41,0x65,0x47,0x61,0x45,0xb4,0x84,0xa2,0xdb,0x2e,0x58,
-0xff,0xff,0x64,0x44,0x08,0xa4,0xa0,0xd3,0xff,0xff,0x60,0x41,0x60,0x47,0x00,0x7f,
-0x60,0x45,0x61,0x44,0x00,0x7f,0xe0,0x84,0xa4,0x80,0xf0,0xa0,0x02,0x02,0x06,0x03,
-0xfa,0x00,0x60,0x41,0x65,0x47,0x61,0x45,0xb4,0x84,0xa2,0xdb,0x2e,0x58,0xff,0xff,
-0x19,0xf2,0xff,0xff,0x08,0xa4,0xa0,0xd3,0xff,0xff,0x60,0x47,0x00,0x7f,0x60,0x45,
-0x1d,0xf2,0xff,0xff,0x40,0x4d,0x60,0x47,0x00,0x7f,0x60,0x41,0x2d,0x44,0x00,0x7f,
-0xcd,0x81,0xe8,0x84,0x07,0x0e,0xa4,0x80,0xff,0xff,0xfa,0x03,0x60,0x45,0x61,0x47,
-0xb4,0x84,0x1d,0xfa,0x2e,0x58,0xff,0xff,0x22,0x60,0x6b,0x78,0xff,0xff,0x99,0xff,
-0x30,0x44,0xff,0xff,0x98,0xff,0x80,0x2a,0x03,0x00,0x47,0xff,0x21,0x58,0xff,0xff,
-0x47,0xff,0xa1,0xff,0xff,0xff,0xbb,0x3f,0xff,0xff,0x99,0xff,0x32,0x44,0x98,0xff,
-0x10,0x26,0x57,0x00,0x64,0xe2,0x45,0xff,0x47,0xff,0x53,0x0a,0x99,0xff,0x32,0x44,
-0x98,0xff,0x10,0x26,0x4e,0x00,0x04,0x27,0x04,0x00,0xb5,0xf3,0xff,0xff,0x60,0x54,
-0xcd,0xe2,0x20,0x60,0x3a,0x64,0x40,0x42,0xc4,0xe2,0x99,0xff,0x30,0x44,0x04,0xbc,
-0x1d,0xb4,0x40,0x51,0x98,0xff,0xa1,0xff,0xff,0xff,0xbb,0x3f,0x99,0xff,0x32,0x44,
-0x98,0xff,0x10,0x26,0x36,0x00,0x64,0xe2,0x45,0xff,0x47,0xff,0x32,0x0a,0x99,0xff,
-0x32,0x44,0x98,0xff,0x10,0x26,0x2d,0x00,0xd0,0xf3,0x4f,0xf3,0x00,0xa0,0xff,0xff,
-0xea,0x02,0x01,0x3a,0x63,0x00,0x99,0xff,0x3e,0x44,0xfc,0xb4,0x40,0x5e,0x02,0xbc,
-0xff,0xff,0xff,0xff,0x40,0x5e,0x98,0xff,0x20,0x60,0x64,0x64,0x40,0x42,0x99,0xff,
-0x30,0x44,0x44,0xbc,0x5d,0xb4,0x40,0x51,0x98,0xff,0xa1,0xff,0xff,0xff,0xbb,0x3f,
-0x99,0xff,0x32,0x44,0x98,0xff,0x10,0x26,0x0c,0x00,0x64,0xe2,0x45,0xff,0x47,0xff,
-0x08,0x0a,0x99,0xff,0x32,0x44,0x98,0xff,0x10,0x26,0x03,0x00,0x21,0x60,0x3b,0x78,
-0xff,0xff,0x20,0x60,0x84,0x64,0x40,0x40,0x20,0x60,0xb2,0x64,0x40,0x41,0x99,0xff,
-0x30,0x44,0xe0,0xbc,0xf9,0xb4,0x40,0x51,0x98,0xff,0xa1,0xff,0xff,0xff,0xbb,0x3f,
-0x28,0x60,0x60,0x62,0xa2,0xd3,0x13,0xf3,0x00,0xa0,0x00,0xa0,0x13,0x03,0x67,0x60,
-0xc8,0x62,0x01,0x64,0xa2,0xdb,0x22,0xf0,0x01,0x64,0xb0,0x84,0xa2,0xda,0x99,0xff,
-0x30,0x44,0x59,0xb4,0x40,0x51,0x98,0xff,0x1b,0x60,0x20,0x64,0x40,0x40,0x1e,0x60,
-0xc5,0x78,0xff,0xff,0x13,0x03,0x67,0x60,0xc8,0x62,0x01,0x64,0xa2,0xdb,0x22,0xf0,
-0x10,0x64,0xb0,0x84,0xa2,0xda,0x99,0xff,0x30,0x44,0x59,0xb4,0x40,0x51,0x98,0xff,
-0x1b,0x60,0x20,0x64,0x40,0x40,0x1e,0x60,0xc5,0x78,0xff,0xff,0x21,0x60,0x1d,0x64,
-0x40,0x40,0x20,0x60,0xe2,0x64,0x40,0x42,0x99,0xff,0x30,0x44,0x44,0xbc,0x5d,0xb4,
-0x40,0x51,0x98,0xff,0x99,0xff,0x32,0x44,0x98,0xff,0x10,0x26,0xb2,0x00,0xd0,0xf3,
-0x64,0xe2,0x00,0xa0,0x45,0xff,0x47,0xff,0x16,0x02,0xab,0x0a,0x99,0xff,0x32,0x44,
-0x98,0xff,0x10,0x26,0xa6,0x00,0x1e,0x60,0x9a,0x65,0xa5,0xd3,0xff,0xff,0x01,0xa8,
-0xff,0xff,0x09,0x02,0x99,0xff,0x3e,0x44,0xfc,0xb4,0x40,0x5e,0x02,0xbc,0xff,0xff,
-0xff,0xff,0x40,0x5e,0x98,0xff,0xa1,0xff,0xff,0xff,0xbb,0x3f,0xd0,0xf3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0xf8,0x02,0x99,0xff,0x32,0x44,0x98,0xff,0x10,0x26,0x89,0x00,
-0x64,0xe2,0x45,0xff,0x47,0xff,0x29,0x0a,0x99,0xff,0x32,0x44,0x98,0xff,0x10,0x26,
-0x24,0x00,0x13,0xf3,0xff,0xff,0x00,0xa8,0xff,0xff,0x13,0x03,0x67,0x60,0xc8,0x62,
-0x01,0x64,0xa2,0xdb,0x22,0xf0,0x10,0x64,0xb0,0x84,0xa2,0xda,0x99,0xff,0x30,0x44,
-0x59,0xb4,0x40,0x51,0x98,0xff,0x1b,0x60,0x20,0x64,0x40,0x40,0x1e,0x60,0xc5,0x78,
-0xff,0xff,0x1e,0x60,0x9a,0x65,0xa5,0xd3,0xff,0xff,0x00,0xa8,0xcc,0x84,0x27,0x03,
-0xa5,0xdb,0x25,0x03,0x20,0x60,0xcf,0x78,0xff,0xff,0x20,0x60,0x75,0x78,0xff,0xff,
-0x1e,0x00,0x28,0x60,0x60,0x62,0xa2,0xd3,0x13,0xf3,0x00,0xa0,0x00,0xa0,0x13,0x03,
-0x67,0x60,0xc8,0x62,0x01,0x64,0xa2,0xdb,0x22,0xf0,0x01,0x64,0xb0,0x84,0xa2,0xda,
-0x99,0xff,0x30,0x44,0x59,0xb4,0x40,0x51,0x98,0xff,0x1b,0x60,0x20,0x64,0x40,0x40,
-0x1e,0x60,0xc5,0x78,0xff,0xff,0xc2,0x02,0x20,0x60,0xcf,0x78,0xff,0xff,0x13,0xf3,
-0xff,0xff,0x00,0xa8,0xff,0xff,0x13,0x03,0x67,0x60,0xc8,0x62,0x01,0x64,0xa2,0xdb,
-0x22,0xf0,0x10,0x64,0xb0,0x84,0xa2,0xda,0x99,0xff,0x30,0x44,0x59,0xb4,0x40,0x51,
-0x98,0xff,0x1b,0x60,0x20,0x64,0x40,0x40,0x1e,0x60,0xc5,0x78,0xff,0xff,0x29,0xf2,
-0xff,0xff,0x60,0x40,0x40,0x2b,0x25,0x00,0x3e,0xf3,0xff,0xff,0x60,0x40,0x04,0x26,
-0x0b,0x00,0x99,0xff,0x3a,0x5c,0x98,0xff,0x64,0x40,0x40,0x27,0x09,0x00,0x64,0x40,
-0x0f,0x2b,0x02,0x00,0x33,0x60,0x00,0xea,0x98,0xff,0x20,0x60,0xb2,0x78,0xff,0xff,
-0x99,0xff,0x3a,0x5c,0x98,0xff,0x64,0x40,0x10,0x2b,0xf6,0x00,0xdb,0xf3,0xff,0xff,
-0xff,0xff,0x01,0x26,0x06,0x00,0x8b,0xff,0x74,0x40,0x74,0x40,0x88,0xff,0x01,0x64,
-0xdb,0xfb,0x21,0xf3,0xff,0xff,0xc4,0xb4,0x21,0xfb,0x21,0x60,0x9d,0x64,0x40,0x40,
-0x01,0x64,0x22,0xfb,0x99,0xff,0x30,0x44,0x40,0xbc,0x59,0xb4,0x40,0x51,0x98,0xff,
-0xe2,0xf1,0x68,0x60,0xcc,0x62,0x80,0x60,0x70,0x64,0xa2,0xdb,0x64,0x40,0x01,0x26,
-0xff,0xff,0xa0,0xfe,0x1a,0xff,0x00,0x64,0x68,0xfb,0xff,0xff,0xa1,0xff,0xff,0xff,
-0xbb,0x3f,0x95,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0x07,0x02,0x13,0xf3,0xff,0xff,
-0x00,0xa0,0x00,0x64,0x02,0x03,0x13,0xfb,0xe3,0x00,0x28,0x60,0x60,0x62,0xa2,0xd3,
-0xff,0xff,0x00,0xa0,0x00,0x64,0x02,0x03,0xa2,0xdb,0xda,0x00,0x21,0xf3,0xff,0xff,
-0xff,0xff,0x01,0x2a,0x0d,0x00,0xfe,0xb4,0x21,0xfb,0x01,0x64,0x4e,0xfb,0x1e,0x60,
-0x98,0x65,0xb8,0xf3,0xff,0xff,0xa5,0xdb,0xff,0xff,0x22,0x60,0x3c,0x78,0xff,0xff,
-0x02,0x2a,0x62,0x00,0x27,0xf2,0xff,0xff,0xdc,0x84,0x27,0xfa,0x5a,0x60,0x46,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x17,0x60,0xea,0x64,0xa0,0xd3,0xff,0xff,0xe8,0x84,0xe0,0x84,
-0x60,0x45,0x18,0x60,0x56,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x53,0xf3,0xff,0xff,0xe0,0x84,0x60,0x45,0x18,0x60,0xbc,0x64,0xc4,0x84,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x53,0xf3,0xff,0xff,0xe0,0x84,0xe0,0x84,0x60,0x45,
-0x5a,0x60,0x4a,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,
-0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x21,0xf3,0xff,0xff,0xfd,0xb4,
-0x21,0xfb,0x02,0x64,0x4e,0xfb,0x29,0xf2,0xff,0xff,0x0c,0xb4,0xff,0xff,0x00,0x36,
-0x5e,0x00,0x95,0xf1,0x00,0x63,0xd3,0x80,0xff,0xff,0x04,0x02,0xb9,0xf1,0xff,0xff,
-0x64,0x45,0x09,0x00,0x25,0xf2,0xff,0xff,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,
-0x0f,0xb4,0xff,0xff,0x60,0x45,0x1e,0x60,0x98,0x63,0xa3,0xd3,0xff,0xff,0xd4,0x80,
-0xdc,0x84,0x45,0x03,0xa3,0xdb,0x43,0x00,0x10,0x2a,0x07,0x00,0x21,0xf3,0x2b,0xf0,
-0xef,0xb4,0x21,0xfb,0x20,0x60,0x19,0x78,0xff,0xff,0x21,0xf3,0xff,0xff,0xdf,0xb4,
-0x21,0xfb,0x2b,0xf0,0x02,0x64,0x64,0x40,0x01,0x26,0x01,0x64,0x4e,0xfb,0x2f,0x00,
-0x00,0x63,0x95,0xf3,0x68,0xfd,0x00,0xbc,0xff,0xff,0x29,0x02,0x28,0x0a,0x70,0x44,
-0xac,0x80,0xff,0xff,0x24,0x02,0x32,0x40,0x02,0x27,0x21,0x00,0x1e,0x60,0x9c,0x65,
-0xa5,0xd3,0xff,0xff,0x00,0xa8,0xff,0xff,0x1a,0x03,0x22,0x60,0x5d,0x64,0x40,0x42,
-0x99,0xff,0x30,0x44,0x04,0xbc,0x1d,0xb4,0x40,0x51,0x98,0xff,0xa1,0xff,0xff,0xff,
-0xbb,0x3f,0x0d,0x0a,0x70,0x44,0xac,0x80,0xff,0xff,0x09,0x02,0x1e,0x60,0x9c,0x65,
-0xa5,0xd3,0xff,0xff,0xcc,0x84,0xa5,0xdb,0xff,0xff,0xf0,0x02,0x00,0x00,0x99,0xff,
-0x30,0x44,0x59,0xb4,0x40,0x51,0x98,0xff,0x1b,0x60,0x20,0x64,0x40,0x40,0x1d,0x60,
-0x96,0x78,0xff,0xff,0xa4,0xe2,0xff,0xff,0xa4,0xe2,0x73,0x44,0xa4,0xfb,0x80,0xf3,
-0x60,0x45,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xc4,0x93,0x00,0x60,0x01,0x71,
-0x95,0xf1,0xcb,0xf3,0x64,0x40,0x00,0x36,0x05,0x00,0x8a,0xff,0x80,0x60,0x00,0x75,
-0x88,0xff,0x03,0x00,0xfc,0xa0,0xff,0xff,0x18,0x02,0xe1,0xf3,0x80,0xf1,0x00,0xa0,
-0x60,0x45,0x13,0x03,0x67,0x60,0x7c,0x62,0x64,0x41,0xd5,0x84,0xdc,0x84,0xa2,0xdb,
-0x00,0x64,0xe2,0xfb,0x99,0xff,0x3c,0x44,0x00,0x7f,0xbf,0xb4,0x40,0x5c,0x98,0xff,
-0x68,0x60,0xcc,0x62,0x80,0x60,0x60,0x64,0xa2,0xdb,0xe4,0xf3,0xe3,0xf1,0x00,0xa0,
-0xff,0xff,0x03,0x03,0x64,0x50,0x00,0x64,0xe4,0xfb,0x5e,0xf3,0xff,0xff,0x60,0x41,
-0xfd,0xb4,0xa2,0xdb,0x61,0x44,0x01,0xb0,0x02,0xb0,0x0a,0x03,0x09,0x02,0x3c,0x60,
-0xae,0x62,0x28,0x60,0xce,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,
-0x16,0x60,0x42,0x62,0xa2,0xd3,0xff,0xff,0xfc,0xa0,0x01,0x64,0x02,0x02,0x75,0xfb,
-0xc9,0xfe,0x73,0x44,0x60,0x45,0x68,0x60,0xcc,0x62,0x90,0x60,0x10,0x64,0xa2,0xdb,
-0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0xcb,0xf3,0x62,0xf3,0xfc,0xa0,0x00,0xa0,
-0x69,0x02,0x17,0x02,0x64,0xf3,0xff,0xff,0x00,0xa0,0xcc,0x84,0x12,0x03,0x64,0xfb,
-0x60,0x45,0x68,0x60,0xcc,0x62,0x80,0x60,0x40,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,
-0x65,0x44,0xa2,0xdb,0x57,0x02,0x63,0xf3,0x64,0xfb,0x67,0x60,0x8a,0x62,0x01,0x64,
-0xa2,0xdb,0x67,0x60,0x8a,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0x00,0x64,0x4a,0x03,
-0xa2,0xdb,0x67,0x60,0x84,0x65,0x67,0x60,0x82,0x63,0xa5,0xd1,0xa3,0xd3,0x64,0x40,
-0x00,0x36,0x40,0x03,0xcc,0x84,0xa3,0xdb,0x60,0x45,0x68,0x60,0xcc,0x62,0x80,0x60,
-0x50,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x33,0x02,0x64,0x44,
-0xa3,0xdb,0x67,0x60,0x86,0x63,0x67,0x60,0x8c,0x65,0xbd,0xd3,0xa3,0xdb,0xc4,0xa0,
-0xff,0xff,0x11,0x04,0xc4,0xa4,0xa3,0xdb,0xa5,0xdb,0xf0,0x60,0x00,0x64,0x60,0x50,
-0x60,0x45,0x68,0x60,0xcc,0x62,0x80,0x60,0x32,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,
-0x65,0x44,0xa2,0xdb,0x17,0x00,0x60,0x47,0xe0,0x84,0xe0,0x84,0x70,0x45,0xd4,0x80,
-0xff,0xff,0x10,0x04,0x60,0x50,0x60,0x45,0x68,0x60,0xcc,0x62,0x80,0x60,0x32,0x64,
-0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x67,0x60,0x8c,0x65,0x00,0x64,
-0xa3,0xdb,0xa5,0xdb,0x1b,0x60,0x2a,0x78,0xff,0xff,0x64,0x41,0x40,0x60,0x0b,0x65,
-0x2b,0x44,0x00,0x63,0xe8,0x80,0xf8,0x84,0x02,0x24,0x94,0x84,0xf3,0x83,0xcd,0x81,
-0xff,0xff,0xf8,0x02,0x2f,0x58,0x40,0x4b,0x00,0x62,0x01,0x64,0xd4,0x80,0xe0,0x84,
-0x1b,0x03,0xd4,0x80,0xe0,0x84,0x16,0x03,0x61,0x44,0x11,0x61,0xe0,0x84,0xcd,0x81,
-0xfd,0x04,0x01,0x00,0xe0,0x84,0xf2,0x82,0xff,0xff,0x02,0x24,0xc6,0x82,0x02,0x28,
-0xd6,0x82,0xe2,0x80,0xcd,0x81,0x02,0x28,0x01,0xbc,0xf4,0x02,0x01,0x2a,0xc6,0x82,
-0x2f,0x58,0xff,0xff,0xe9,0x81,0xf2,0x82,0x61,0x44,0xfa,0x00,0x12,0xf1,0x7f,0x00,
-0xda,0x00,0xa1,0xff,0xff,0xff,0xbc,0x3f,0x40,0xf3,0xff,0xff,0x60,0x40,0x01,0x2a,
-0x03,0x00,0x18,0x60,0x17,0x78,0xff,0xff,0x3d,0x46,0x29,0xf0,0xff,0xff,0x64,0x40,
-0x40,0x2b,0x27,0x00,0x3e,0xf3,0xff,0xff,0x60,0x40,0x02,0x26,0x1a,0x00,0x0f,0xf2,
-0xff,0xff,0x60,0x40,0x04,0x2a,0x00,0x00,0x99,0xff,0x3b,0x44,0x98,0xff,0xff,0xff,
-0x0f,0x2b,0x05,0x00,0x40,0x27,0xf8,0x00,0xf5,0x60,0xbc,0x78,0xff,0xff,0x9c,0xf3,
-0xff,0xff,0xff,0xff,0x02,0x26,0xd5,0x00,0x67,0x60,0xca,0x62,0x01,0x64,0xa2,0xdb,
-0xd0,0x00,0x0f,0xf2,0xff,0xff,0x60,0x40,0x04,0x2a,0xcb,0x00,0x17,0x60,0xa6,0x78,
-0xff,0xff,0x0f,0xf2,0xff,0xff,0x08,0xbc,0x0f,0xfa,0x3c,0x60,0x64,0x62,0xa2,0xd3,
-0xff,0xff,0x00,0xa8,0x60,0x46,0x01,0x02,0xbc,0x00,0x0f,0xf0,0xff,0xff,0xff,0xff,
-0x64,0x40,0x00,0x3a,0x04,0x00,0x0f,0xf0,0x70,0x64,0xb0,0x84,0xa2,0xda,0x67,0x60,
-0x62,0x62,0x00,0x64,0xa2,0xdb,0x40,0xfb,0x3e,0xf3,0xff,0xff,0xf9,0xb4,0x3e,0xfb,
-0x3d,0x46,0x0f,0xf0,0x04,0x64,0xb0,0x84,0xa2,0xda,0xcb,0xfe,0x40,0xff,0xbc,0xfe,
-0x29,0xf2,0xff,0xff,0xff,0xff,0x40,0x2b,0x9c,0x00,0x67,0x60,0x58,0x64,0xa0,0xd3,
-0xff,0xff,0xff,0xa0,0xff,0xff,0x95,0x02,0x04,0xff,0x93,0x00,0xf4,0x46,0x7e,0x00,
-0x00,0x10,0x7f,0xf1,0x10,0x60,0xdc,0xe0,0x43,0x45,0x65,0xf3,0x44,0x46,0x60,0x40,
-0x02,0x2a,0x03,0x00,0x23,0x60,0xc8,0x78,0xff,0xff,0x01,0x64,0x65,0xfb,0x99,0xff,
-0x00,0x6b,0x3e,0x44,0x70,0xb4,0x40,0x5e,0x3d,0x44,0xf7,0xb4,0x90,0xbc,0x40,0x5d,
-0x3c,0x44,0x6f,0xb4,0x40,0x5c,0x98,0xff,0x20,0x44,0x60,0xbc,0x40,0x40,0x01,0x64,
-0x60,0x47,0x99,0xfb,0x05,0x64,0x9a,0xfb,0xff,0xff,0xdf,0xfe,0x19,0xff,0x23,0x60,
-0xa6,0x64,0x9b,0xfb,0xf8,0x60,0x89,0x78,0xff,0xff,0x1f,0xf3,0xff,0xff,0xff,0xff,
-0x20,0x26,0x05,0x00,0x13,0x60,0x5a,0x63,0x14,0x60,0x3e,0x65,0x04,0x00,0x14,0x60,
-0x3e,0x63,0x15,0x60,0x22,0x65,0x80,0xe1,0x02,0x00,0x01,0x16,0xfe,0x00,0xbd,0xd1,
-0xff,0xff,0x64,0x48,0x64,0x47,0x00,0x7f,0x60,0x41,0x80,0xbc,0x60,0x4a,0xff,0xff,
-0xff,0xff,0xa1,0xff,0xff,0xff,0xd7,0x80,0xff,0xff,0xef,0x02,0x68,0x40,0x67,0x60,
-0x80,0x62,0xa2,0xd3,0x00,0x7c,0xa2,0xd9,0x60,0x40,0x01,0x2a,0x09,0x00,0x40,0x65,
-0xa4,0x85,0x99,0xff,0x3c,0x44,0xbf,0xb4,0xb4,0x84,0x00,0x7f,0x40,0x5c,0x98,0xff,
-0x20,0x44,0x60,0xbc,0x40,0x40,0x80,0xe1,0x14,0x60,0x7a,0x63,0xa3,0xd1,0x67,0x60,
-0x70,0x62,0xa2,0xd3,0xff,0xff,0xd0,0x80,0xa2,0xd9,0x16,0x03,0x01,0x64,0xd8,0xfb,
-0xd7,0xf3,0xff,0xff,0xff,0xff,0x01,0x26,0xfb,0x00,0x64,0x48,0x64,0x47,0x00,0x7f,
-0x80,0xbc,0x60,0x4a,0xff,0xff,0xff,0xff,0xa1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0x68,0x40,0x00,0x64,0xd8,0xfb,0x66,0x60,0xd4,0x62,0x17,0x60,0x52,0x7c,
-0xa2,0xd3,0x99,0xff,0xe0,0x84,0x40,0xd1,0x00,0x63,0x64,0x40,0x01,0x26,0x10,0x63,
-0xdd,0xfd,0x00,0x65,0x64,0x40,0x02,0x26,0x04,0x65,0x3c,0x44,0xfb,0xb4,0xb4,0x84,
-0x40,0x5c,0x98,0xff,0x26,0x44,0x7f,0xfb,0x02,0x7f,0x99,0xfb,0x05,0x64,0x9a,0xfb,
-0xff,0xff,0xdf,0xfe,0x19,0xff,0x24,0x60,0x28,0x64,0x9b,0xfb,0x9c,0xf3,0x69,0x65,
-0xb4,0x84,0x99,0xff,0x40,0x51,0x98,0xff,0xf8,0x60,0x89,0x78,0xff,0xff,0x65,0xf3,
-0xff,0xff,0x02,0xbc,0x65,0xfb,0x25,0x43,0x24,0x60,0x9d,0x78,0xff,0xff,0x20,0x44,
-0x43,0x45,0x20,0xbc,0x40,0x40,0x65,0xf3,0xff,0xff,0x60,0x40,0x80,0x2a,0x03,0x00,
-0x24,0x60,0x55,0x78,0xff,0xff,0x00,0xee,0x04,0xbc,0x65,0xfb,0xff,0xff,0xdf,0xfe,
-0x19,0xff,0xff,0xff,0x99,0xff,0x00,0x6b,0x3c,0x44,0x40,0xb4,0x80,0xbc,0x40,0x5c,
-0x00,0xed,0x04,0xee,0xff,0xff,0xff,0xff,0x00,0xee,0x98,0xff,0x00,0x64,0x65,0xfb,
-0x55,0x60,0xfc,0xe0,0xff,0xff,0xff,0xff,0x25,0x43,0x24,0x60,0x9d,0x78,0xff,0xff,
-0x24,0x60,0x9d,0x78,0xff,0xff,0x20,0x44,0x20,0xbc,0x40,0x40,0x24,0x60,0x9d,0x78,
-0xff,0xff,0x99,0xff,0x3c,0x44,0x7f,0xb4,0x10,0xbc,0x40,0x5c,0x98,0xff,0x99,0xff,
-0x3d,0x44,0x10,0xbc,0x00,0x7f,0x40,0x5d,0x98,0xff,0x99,0xff,0x3e,0x44,0x02,0xbc,
-0x00,0x7f,0x40,0x5e,0x98,0xff,0x24,0x60,0x9d,0x78,0xff,0xff,0x20,0x44,0x20,0xbc,
-0x40,0x40,0x80,0xe1,0x01,0x64,0xd8,0xfb,0xd7,0xf3,0xff,0xff,0xff,0xff,0x01,0x26,
-0xfb,0x00,0x64,0x47,0x3f,0xb4,0xe0,0x85,0x64,0x44,0x80,0x2b,0x09,0x00,0x00,0x7f,
-0x60,0x48,0x65,0x44,0x80,0xbc,0x60,0x4a,0xff,0xff,0xff,0xff,0x0b,0x16,0xfe,0x00,
-0x65,0x49,0xff,0xff,0xff,0xff,0xa1,0xff,0x00,0x64,0x68,0x5e,0x60,0x5c,0x08,0x60,
-0x0a,0x64,0xa0,0xd9,0x00,0x64,0xd8,0xfb,0x9c,0xf3,0x69,0x65,0xb4,0x84,0x99,0xff,
-0x40,0x51,0x98,0xff,0x00,0x64,0xbf,0xdb,0x20,0x44,0x20,0x2a,0x07,0x00,0x07,0xb4,
-0x04,0x36,0xc3,0xfe,0x06,0x36,0xcc,0xfe,0x07,0x36,0xd8,0xfe,0x20,0x44,0xd8,0xb4,
-0x40,0x40,0x20,0x44,0x40,0x2a,0x08,0x00,0x9f,0xfe,0xff,0xff,0x24,0x05,0xbf,0xb4,
-0x40,0x40,0x9b,0xf7,0xff,0xff,0xff,0xff,0x3c,0x60,0x8e,0x62,0xa2,0xd3,0xda,0x83,
-0x00,0xa8,0x02,0x61,0x1b,0x02,0xdb,0x82,0x5a,0xd3,0xda,0x83,0x00,0xa8,0x02,0x61,
-0x15,0x02,0xdb,0x82,0x5a,0xd3,0xda,0x83,0x00,0xa8,0x04,0x61,0x0f,0x02,0xdb,0x82,
-0x5a,0xd3,0xda,0x83,0x00,0xa8,0x06,0x61,0x09,0x02,0xdb,0x82,0x5a,0xd3,0xda,0x83,
-0x00,0xa8,0x07,0x61,0x03,0x02,0xf8,0x60,0x89,0x78,0xff,0xff,0xa3,0xd1,0x40,0x44,
-0x62,0x43,0x20,0x44,0x07,0xb5,0xd4,0x85,0x35,0x80,0x24,0x45,0x13,0x60,0x32,0x64,
-0x44,0xd7,0xff,0xff,0xff,0xff,0x80,0xe1,0x43,0x45,0x20,0x44,0x20,0xbc,0x40,0x40,
-0x64,0x43,0xbd,0xd3,0xbd,0xd1,0xff,0xff,0x10,0x2b,0x01,0x00,0x0b,0x00,0x01,0x16,
-0xfe,0x00,0x64,0x49,0xff,0xff,0xff,0xff,0xff,0xff,0xa1,0xff,0xff,0xff,0x68,0x44,
-0x00,0x7f,0xa3,0xdb,0x25,0x43,0x98,0x00,0x80,0xe1,0x43,0x45,0x20,0x44,0x20,0xbc,
-0x40,0x40,0x64,0x43,0xbd,0xd3,0xbd,0xd1,0x40,0x44,0x10,0x2b,0x01,0x00,0x1a,0x00,
-0xa3,0xd3,0xff,0xff,0x01,0x16,0xfe,0x00,0x60,0x48,0x64,0x44,0x00,0x7f,0x60,0x4a,
-0xff,0xff,0xff,0xff,0xff,0xff,0xa1,0xff,0xff,0xff,0x68,0x40,0x01,0x16,0xfe,0x00,
-0x64,0x49,0xff,0xff,0xff,0xff,0xff,0xff,0xa1,0xff,0xff,0xff,0x68,0x45,0xd4,0x80,
-0xff,0xff,0xe8,0x02,0x25,0x43,0xd7,0x00,0x3c,0x60,0xac,0x61,0xa1,0xd3,0x00,0x63,
-0x00,0xa8,0x59,0xd1,0x31,0x02,0x59,0xd3,0x00,0x63,0x00,0xa8,0x59,0xd1,0x2c,0x02,
-0x59,0xd3,0x00,0x63,0x00,0xa8,0x59,0xd1,0x27,0x02,0x59,0xd3,0x00,0x63,0x00,0xa8,
-0x59,0xd1,0x22,0x02,0x59,0xd3,0x00,0x63,0x00,0xa8,0x59,0xd1,0x1d,0x02,0x59,0xd3,
-0x00,0x63,0x00,0xa8,0x59,0xd1,0x18,0x02,0x59,0xd3,0x00,0x63,0x00,0xa8,0x59,0xd1,
-0x13,0x02,0x59,0xd3,0x00,0x63,0x00,0xa8,0x59,0xd1,0x0e,0x02,0x59,0xd3,0x00,0x63,
-0x00,0xa8,0x59,0xd1,0x09,0x02,0xf8,0x60,0x89,0x78,0xff,0xff,0x27,0x60,0xb5,0x78,
-0xff,0xff,0x27,0x60,0x8f,0x78,0xff,0xff,0x49,0xdd,0x60,0x40,0x02,0x36,0xf9,0x00,
-0x03,0x36,0xf4,0x00,0x01,0x36,0x28,0x00,0x05,0x3a,0xbe,0x00,0xa4,0xd3,0x5a,0xd3,
-0x9c,0x85,0xa4,0x84,0xa2,0xdb,0xb8,0x00,0x84,0xe2,0x04,0x60,0x00,0x71,0x1e,0xf3,
-0x14,0xf3,0x00,0xbd,0xcc,0x84,0x08,0x03,0x14,0xfb,0x06,0x02,0x65,0x44,0x14,0xfb,
-0x8a,0xff,0x80,0x60,0x00,0x75,0x88,0xff,0x73,0x44,0xce,0xfb,0x1e,0x60,0x92,0x62,
-0xa2,0xd3,0xff,0xff,0x01,0xa4,0xa2,0xdb,0x0a,0x02,0x1e,0x60,0x94,0x62,0xa2,0xd3,
-0xff,0xff,0x01,0xa4,0xa2,0xdb,0x03,0x00,0x27,0x60,0x88,0x78,0xff,0xff,0x66,0x60,
-0xb4,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xcc,0x84,0x06,0x03,0xa2,0xdb,0x04,0x02,
-0x3a,0x44,0x02,0xbc,0x40,0x5a,0x3b,0xff,0x6e,0xf3,0xff,0xff,0x60,0x47,0xff,0x23,
-0x05,0x00,0xcc,0x84,0x60,0x47,0xff,0x22,0x00,0x64,0x6e,0xfb,0xa4,0xf3,0xff,0xff,
-0x10,0xa4,0xa4,0xfb,0x68,0xf3,0x10,0xa5,0x00,0xa0,0x73,0x41,0x49,0x03,0xd5,0x80,
-0xff,0xff,0x27,0x03,0x70,0x45,0xc4,0x85,0x02,0x60,0x58,0x64,0xc4,0x84,0xa4,0xf1,
-0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0x40,0x4d,0xc0,0x84,
-0x60,0x41,0x73,0x45,0xd4,0x80,0xe1,0xf1,0x14,0x0d,0x64,0x44,0x00,0x36,0x30,0x00,
-0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,0x61,0x45,0xc4,0x84,0x73,0x45,0xd4,0x80,
-0xff,0xff,0x07,0x0d,0x67,0x60,0x7c,0x62,0xa2,0xd3,0xff,0xff,0xfe,0xa0,0xff,0xff,
-0x1f,0x02,0xcb,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x06,0x02,0x01,0x64,0x13,0xfb,
-0x00,0x64,0x68,0xfb,0xc0,0xfe,0x14,0x00,0xe3,0xf1,0x2d,0x44,0xc0,0x84,0x70,0x45,
-0xd4,0x80,0xff,0xff,0x0d,0x04,0x60,0x50,0x60,0x45,0x68,0x60,0xcc,0x62,0x80,0x60,
-0x31,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x01,0x64,0xe4,0xfb,
-0x66,0x60,0xb2,0x64,0xa0,0xd3,0xff,0xff,0x00,0xa0,0xcc,0x84,0x12,0x03,0xa2,0xdb,
-0x10,0x02,0xe0,0xf3,0x76,0xf3,0x60,0x45,0x77,0xf3,0xd4,0x80,0xd4,0x80,0x01,0x03,
-0x08,0x02,0x28,0x60,0x60,0x62,0x80,0x64,0xa2,0xdb,0xff,0xff,0xc0,0xfe,0x00,0x64,
-0xa2,0xdb,0x3d,0x60,0x1a,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x0c,0x03,
-0xcc,0x84,0xa2,0xdb,0x09,0x02,0x3d,0x60,0x16,0x63,0xbd,0xd1,0x18,0x60,0xc8,0x64,
-0xa0,0xd3,0xff,0xff,0xd0,0x84,0xa3,0xdb,0x32,0x44,0x01,0x2a,0x30,0x00,0x68,0x60,
-0xd4,0x62,0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,0x3b,0x02,0x68,0x60,0xda,0x61,
-0xa1,0xd3,0xff,0xff,0xcc,0x84,0xff,0xff,0xa1,0xdb,0x33,0x02,0x20,0x40,0x40,0x2a,
-0x03,0x00,0x01,0x64,0xa1,0xdb,0x2d,0x00,0xff,0x64,0xa1,0xdb,0x7f,0xf3,0xff,0xff,
-0x01,0xa4,0x7f,0xfb,0xf2,0xa0,0xff,0xff,0x02,0x06,0x01,0x64,0x7f,0xfb,0x60,0x41,
-0x20,0x44,0x40,0xbc,0x40,0x40,0x24,0x60,0xbd,0x64,0x9b,0xfb,0x61,0x44,0x02,0x7f,
-0x99,0xfb,0x05,0x64,0x9a,0xfb,0xff,0xff,0xdf,0xfe,0x19,0xff,0x12,0x00,0x95,0xf3,
-0x65,0xf3,0x00,0xa0,0xff,0xff,0x0d,0x03,0x02,0x2a,0x0b,0x00,0x16,0x60,0xbc,0x62,
-0xa2,0xd3,0x7f,0xf1,0xff,0xff,0xd0,0x80,0xff,0xff,0x03,0x03,0x20,0x40,0x40,0x2a,
-0xd8,0x00,0x67,0x60,0x7c,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xcc,0x84,0x31,0x03,
-0xa2,0xdb,0x2f,0x02,0x01,0x64,0xe2,0xfb,0x99,0xff,0x3c,0x44,0x40,0xbc,0x00,0x7f,
-0x40,0x5c,0x98,0xff,0x68,0x60,0xcc,0x62,0x80,0x60,0x61,0x64,0xa2,0xdb,0x22,0xf3,
-0xff,0xff,0x01,0xb4,0xff,0xff,0x06,0x03,0x67,0x60,0x8e,0x64,0xa0,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0xe1,0xf3,0xe3,0xf1,0x60,0x47,0xe0,0x84,0xe0,0x84,0xc0,0x84,
-0x70,0x45,0xd4,0x80,0xff,0xff,0x0d,0x04,0x60,0x50,0x60,0x45,0x68,0x60,0xcc,0x62,
-0x80,0x60,0x31,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x00,0x64,
-0xe4,0xfb,0x67,0x60,0x7e,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xcc,0x84,0x16,0x03,
-0xa2,0xdb,0x14,0x02,0x00,0x64,0xe2,0xfb,0x99,0xff,0x3c,0x44,0xbf,0xb4,0x00,0x7f,
-0x40,0x5c,0x98,0xff,0x68,0x60,0xcc,0x62,0x80,0x60,0x60,0x64,0xa2,0xdb,0x67,0x60,
-0x7c,0x63,0xe1,0xf3,0x80,0xf3,0x60,0x45,0xd4,0x84,0xa3,0xdb,0x67,0x60,0x8c,0x62,
-0xa2,0xd3,0xff,0xff,0x00,0xa0,0xcc,0x84,0x10,0x03,0xa2,0xdb,0x04,0x60,0x00,0x65,
-0x70,0x44,0xc4,0x84,0x60,0x50,0x60,0x45,0x68,0x60,0xcc,0x62,0x80,0x60,0x32,0x64,
-0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0xe1,0xf3,0xff,0xff,0x00,0xa0,
-0xff,0xff,0x31,0x02,0x28,0x44,0xcc,0x84,0x40,0x48,0x2d,0x02,0x67,0x60,0x50,0x62,
-0xa2,0xd3,0xff,0xff,0xff,0xff,0x03,0x22,0x03,0x00,0x01,0x2a,0x1a,0x00,0x07,0x00,
-0x67,0x60,0x4c,0x62,0xa2,0xd3,0xff,0xff,0x01,0xac,0xa2,0xdb,0x12,0x03,0x99,0xff,
-0x3c,0x44,0x40,0xbc,0x00,0x7f,0x40,0x5c,0x98,0xff,0xcb,0xf3,0x0a,0x65,0xfc,0xa0,
-0xfd,0xa0,0x10,0x03,0x0f,0x03,0xfb,0xa0,0xff,0xff,0x0c,0x03,0x01,0x60,0xf4,0x65,
-0x09,0x00,0x99,0xff,0x3c,0x44,0xbf,0xb4,0x40,0x5c,0x67,0x60,0x4a,0x62,0xa2,0xd3,
-0x98,0xff,0x60,0x45,0x45,0x48,0x2b,0x44,0xcc,0x84,0x40,0x4b,0x40,0x02,0x03,0x60,
-0xe8,0x64,0x40,0x4b,0x2c,0x44,0xcc,0x84,0x40,0x4c,0x05,0x02,0x1e,0x64,0x4b,0xf3,
-0x40,0x4c,0xdc,0x84,0x4b,0xfb,0xcb,0xf3,0xff,0xff,0xfc,0xa0,0xfd,0xa0,0x07,0x03,
-0x06,0x03,0xfb,0xa0,0xff,0xff,0x03,0x03,0x05,0x60,0xdc,0x64,0x24,0x00,0x5a,0x60,
-0x74,0x63,0xa3,0xd1,0x59,0x60,0xec,0x62,0xa2,0xd3,0xff,0xff,0xc0,0x83,0x67,0x60,
-0x44,0x62,0xa2,0xd1,0xa2,0xdd,0xd3,0x84,0xff,0xff,0xfe,0x27,0x13,0x00,0x01,0x27,
-0x0f,0x00,0xc0,0x26,0x0b,0x00,0x30,0x26,0x07,0x00,0x0f,0x26,0x03,0x00,0x03,0x60,
-0xde,0x64,0x09,0x00,0xf0,0x64,0x07,0x00,0x73,0x64,0x05,0x00,0x38,0x64,0x03,0x00,
-0x1e,0x64,0x01,0x00,0x0f,0x64,0x60,0x5c,0x67,0x60,0x4a,0x62,0xa2,0xd9,0x01,0x60,
-0x3a,0x61,0xa1,0xd3,0x61,0x43,0x00,0xa8,0x60,0x41,0x03,0x02,0xf8,0x60,0x89,0x78,
-0xff,0xff,0x59,0xd3,0x00,0x66,0x00,0xa8,0xcc,0x84,0x02,0x03,0xa1,0xdb,0xf6,0x00,
-0x49,0xd3,0xa3,0xdb,0x00,0xa8,0x60,0x43,0x5b,0xd3,0x06,0x03,0x00,0xa8,0xcc,0x84,
-0x02,0x02,0x01,0x66,0x01,0x00,0xa3,0xdb,0x06,0xa1,0xa1,0xd3,0x59,0xd1,0x60,0x45,
-0xa5,0xd3,0x59,0xd1,0xb0,0x84,0xa5,0xdb,0x64,0x47,0x06,0x36,0xcd,0xfe,0x07,0x37,
-0xd9,0xfe,0x66,0x40,0x00,0x3a,0xd3,0x00,0xf8,0x60,0x89,0x78,0xff,0xff,0x01,0x60,
-0x3a,0x61,0x00,0x64,0xa1,0xdb,0x25,0x60,0x2d,0x78,0xff,0xff,0x27,0x60,0x94,0x64,
-0x40,0x41,0x44,0x42,0x24,0x00,0x01,0x60,0x3a,0x66,0xa6,0xd3,0x04,0xa1,0x60,0x43,
-0xa1,0xd3,0xc9,0x81,0x60,0x45,0x00,0xbb,0xa1,0xdb,0xbe,0xd3,0x09,0x03,0xd4,0x84,
-0x9c,0x84,0xdc,0x84,0xff,0xff,0x04,0x0e,0xa3,0xd1,0x63,0x46,0x64,0x43,0xf2,0x00,
-0x9c,0x84,0xdc,0x85,0x49,0xdd,0x61,0x44,0x00,0xbb,0xa6,0xdb,0x02,0x03,0x65,0x44,
-0xbe,0xdb,0x25,0x60,0x2d,0x78,0xff,0xff,0x25,0x60,0x2d,0x64,0x40,0x41,0x01,0x60,
-0x3a,0x66,0xa6,0xd3,0xff,0xff,0x00,0xa8,0xd0,0x80,0x10,0x03,0x02,0x03,0x60,0x46,
-0xf8,0x00,0x58,0xd3,0xa4,0xd3,0x60,0x45,0x00,0x63,0xa4,0xdd,0x58,0xd3,0x02,0xa8,
-0xc4,0x83,0x01,0x03,0xa2,0xdd,0x62,0x44,0xc8,0x84,0xa6,0xdb,0x21,0x58,0x22,0x41,
-0x28,0x60,0xaa,0x63,0x00,0x64,0xa3,0xdb,0x06,0xa3,0x1f,0x60,0x1a,0x64,0xbd,0xdb,
-0xbd,0xdb,0x06,0x64,0xa3,0xdb,0x1f,0x60,0x18,0x62,0x58,0x60,0xd2,0x64,0xa2,0xdb,
-0x1e,0x60,0xea,0x62,0x52,0x60,0x85,0x64,0xa2,0xdb,0x2f,0x58,0xff,0xff,0x28,0x60,
-0x92,0x63,0x00,0x64,0xa3,0xdb,0x06,0xa3,0x1f,0x60,0x12,0x64,0xbd,0xdb,0xbd,0xdb,
-0x06,0x64,0xa3,0xdb,0x1f,0x60,0x10,0x62,0x5c,0x60,0x14,0x64,0xa2,0xdb,0x1e,0x60,
-0xea,0x62,0x52,0x60,0x85,0x64,0xa2,0xdb,0x2f,0x58,0xff,0xff,0x00,0x60,0x7a,0x66,
-0x32,0x64,0x61,0xfb,0x1e,0x60,0x92,0x64,0xa0,0xd3,0x03,0xfa,0x0f,0x4e,0x00,0x60,
-0x3c,0x61,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,
-0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,0xc4,0x78,0xff,0xff,0x08,0xfe,
-0x0e,0x4f,0x66,0x44,0x7b,0xfb,0x00,0x64,0x28,0xfa,0x01,0x60,0x48,0x64,0x29,0xfa,
-0x00,0x64,0x38,0xfa,0x28,0x60,0x9e,0x63,0x00,0x64,0xa3,0xdb,0x06,0xa3,0x1f,0x60,
-0x16,0x64,0xbd,0xdb,0x02,0x64,0xbd,0xdb,0x06,0x64,0xa3,0xdb,0x1f,0x60,0x14,0x62,
-0x64,0x60,0x3f,0x64,0xa2,0xdb,0x1e,0x60,0xf0,0x62,0x64,0x60,0x4a,0x64,0xa2,0xdb,
-0x2f,0x58,0xff,0xff,0x0f,0x4e,0x00,0x60,0x48,0x61,0x41,0x4d,0x40,0xa1,0xa2,0xff,
-0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,
-0x58,0x4f,0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,0x66,0x44,0x7a,0xfb,0x08,0x64,
-0x28,0xfa,0xff,0x60,0xff,0x64,0x2b,0xfa,0x2c,0xfa,0x2d,0xfa,0xff,0xff,0x31,0xfa,
-0x32,0xfa,0x33,0xfa,0x12,0x60,0x20,0x64,0x0e,0xfa,0x28,0x60,0x62,0x63,0x00,0x64,
-0xa3,0xdb,0x06,0xa3,0x1e,0x60,0xf6,0x64,0xbd,0xdb,0x04,0x64,0xbd,0xdb,0x06,0x64,
-0xa3,0xdb,0x1e,0x60,0xf4,0x62,0x56,0x60,0x3d,0x64,0xa2,0xdb,0x28,0x60,0x6e,0x63,
-0x00,0x64,0xa3,0xdb,0x06,0xa3,0x1e,0x60,0xfa,0x64,0xbd,0xdb,0x08,0x64,0xbd,0xdb,
-0x06,0x64,0xa3,0xdb,0x1e,0x60,0xf8,0x62,0x56,0x60,0x48,0x64,0xa2,0xdb,0x1e,0x60,
-0xe8,0x62,0x56,0x60,0x2d,0x64,0xa2,0xdb,0x2f,0x58,0xff,0xff,0x28,0x60,0x7a,0x63,
-0x00,0x64,0xa3,0xdb,0x06,0xa3,0x1f,0x60,0x0a,0x64,0xbd,0xdb,0xbd,0xdb,0x06,0x64,
-0xa3,0xdb,0x1f,0x60,0x08,0x62,0x52,0x60,0x9f,0x64,0xa2,0xdb,0x1e,0x60,0xea,0x62,
-0x52,0x60,0x85,0x64,0xa2,0xdb,0x2f,0x58,0xff,0xff,0x00,0x64,0x40,0x40,0x0f,0x4e,
-0x00,0x60,0x6c,0x61,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,0x58,0x4f,0xa2,0x78,
-0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,0xc4,0x78,0xff,0xff,
-0x08,0xfe,0x0e,0x4f,0x66,0x44,0x79,0xfb,0x0f,0x4e,0x00,0x60,0x6c,0x61,0x41,0x4d,
-0x40,0xa1,0xa2,0xff,0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,
-0x2d,0x41,0x19,0x60,0x58,0x4f,0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,0x66,0x44,
-0x78,0xfb,0x0f,0x4e,0x00,0x60,0x3c,0x61,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,
-0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,
-0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,0x66,0x44,0x77,0xfb,0x08,0x64,0x28,0xfa,
-0xf0,0x60,0x20,0x64,0x0e,0xfa,0x00,0x64,0x38,0xfa,0x00,0x60,0x90,0x64,0x29,0xfa,
-0x0f,0x4e,0x00,0x60,0xab,0x61,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,0x58,0x4f,
-0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,0xc4,0x78,
-0xff,0xff,0x08,0xfe,0x0e,0x4f,0x66,0x44,0x76,0xfb,0x08,0x64,0x28,0xfa,0x18,0x60,
-0x20,0x64,0x0e,0xfa,0x00,0x60,0x80,0x64,0x29,0xfa,0x00,0x64,0x19,0xfa,0x1e,0x60,
-0xec,0x62,0x43,0x60,0x6e,0x64,0xa2,0xdb,0x2f,0x58,0xff,0xff,0x1e,0x60,0xe4,0x62,
-0x2c,0x60,0x33,0x64,0xa2,0xdb,0x28,0x60,0xb6,0x62,0x00,0x64,0xa2,0xdb,0x06,0xa2,
-0x1e,0x60,0xfe,0x64,0xa2,0xdb,0x06,0x64,0x5a,0xdb,0x5a,0xdb,0x28,0x60,0xc2,0x62,
-0x00,0x64,0xa2,0xdb,0x06,0xa2,0x1f,0x60,0x02,0x64,0xa2,0xdb,0x06,0x64,0x5a,0xdb,
-0x5a,0xdb,0x28,0x60,0xce,0x62,0x00,0x64,0xa2,0xdb,0x06,0xa2,0x1f,0x60,0x06,0x64,
-0xa2,0xdb,0x06,0x64,0x5a,0xdb,0x5a,0xdb,0xab,0xf1,0x28,0x60,0xd2,0x62,0xa2,0xd9,
-0x1e,0x60,0xfc,0x62,0x2d,0x60,0x2a,0x64,0xa2,0xdb,0x1f,0x60,0x00,0x62,0x2d,0x60,
-0x35,0x64,0xa2,0xdb,0x1f,0x60,0x04,0x62,0x2d,0x60,0x40,0x64,0xa2,0xdb,0x1e,0x60,
-0xb2,0x62,0x00,0x60,0x02,0x64,0xa2,0xdb,0x29,0x60,0x53,0x64,0x5a,0xdb,0xcf,0xfe,
-0x2f,0x58,0xff,0xff,0x1e,0x60,0xb0,0x62,0x00,0x64,0xa2,0xdb,0x03,0x64,0x5e,0xfb,
-0x2d,0x60,0x58,0x4e,0x1e,0x78,0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0xff,0x60,
-0x8f,0x64,0xa0,0x84,0xa2,0xdb,0xac,0xf1,0x28,0x60,0xc6,0x62,0xa2,0xd9,0x3c,0x60,
-0xb6,0x62,0x28,0x60,0xc2,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,
-0x1e,0x60,0xb2,0x62,0x00,0x60,0x74,0x64,0xa2,0xdb,0x29,0x60,0x7c,0x64,0x5a,0xdb,
-0xcf,0xfe,0x2f,0x58,0xff,0xff,0x67,0x60,0x76,0x62,0xa2,0xd3,0xff,0xff,0x60,0x40,
-0x02,0x2a,0x03,0x00,0x2b,0x60,0x0f,0x78,0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,
-0x00,0x60,0x04,0x64,0xa0,0x80,0x9c,0x84,0x05,0x03,0xa0,0x84,0xa2,0xdb,0x2c,0x60,
-0x33,0x78,0xff,0xff,0x00,0x60,0x40,0x64,0xa0,0x80,0x9c,0x84,0x03,0x03,0xa0,0x84,
-0xa2,0xdb,0xd6,0x00,0x00,0x60,0x20,0x64,0xa0,0x80,0x9c,0x84,0x0e,0x03,0xa0,0x84,
-0xa2,0xdb,0x01,0x65,0x2e,0x60,0x58,0x4e,0xd4,0x78,0xff,0xff,0xff,0x60,0xf7,0x65,
-0x5e,0xf3,0xff,0xff,0xa4,0x84,0xa2,0xdb,0xc3,0x00,0x00,0x60,0x10,0x64,0xa0,0x80,
-0x9c,0x84,0xc7,0x03,0xa0,0x84,0xa2,0xdb,0x1e,0x60,0x92,0x63,0xa3,0xd1,0x66,0x60,
-0xf0,0x65,0xa5,0xd3,0xff,0xff,0xd0,0x80,0xff,0xff,0x1e,0x0d,0xad,0xf3,0xff,0xff,
-0xc0,0x84,0xa5,0xdb,0x66,0x60,0xee,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x66,0x60,0xfc,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xf8,0xa0,0xff,0xff,0x01,0x04,
-0x08,0x64,0xa2,0xdb,0x67,0x60,0x02,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xf8,0xa0,
-0xff,0xff,0x01,0x04,0x08,0x64,0xa2,0xdb,0x66,0x60,0xf2,0x65,0xa5,0xd1,0x58,0xf3,
-0xff,0xff,0xd0,0x80,0x60,0x41,0x0d,0x06,0xa5,0xdb,0x66,0x60,0xf8,0x63,0x66,0x60,
-0xf6,0x65,0xa5,0xd1,0x61,0x44,0xd0,0x84,0xff,0xff,0x01,0x05,0x00,0x64,0xa3,0xdb,
-0x16,0x00,0x66,0x60,0xf8,0x63,0xa3,0xd1,0x58,0xf3,0xff,0xff,0xd0,0x80,0x60,0x41,
-0x0e,0x05,0xa5,0xdb,0x66,0x60,0xf6,0x65,0xa5,0xd1,0x61,0x44,0xd0,0x84,0xff,0xff,
-0x01,0x05,0x00,0x64,0xa3,0xdb,0x66,0x60,0xfa,0x62,0x01,0x64,0xa2,0xdb,0x66,0x60,
-0xfa,0x63,0xbd,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x2e,0x03,0xbd,0xd3,0xa3,0xd1,
-0xff,0xff,0xd0,0x80,0xff,0xff,0x28,0x06,0x66,0x60,0xee,0x62,0xa2,0xd3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x21,0x03,0x61,0x60,0xc8,0x62,0x06,0x64,0xa2,0xdb,0x66,0x60,
-0xfa,0x63,0x00,0x64,0xbd,0xdb,0xbd,0xdb,0xa3,0xd3,0xff,0xff,0xe0,0x84,0xf8,0xa0,
-0xff,0xff,0x01,0x04,0x08,0x64,0xa3,0xdb,0x66,0x60,0xf6,0x62,0xa2,0xd3,0xff,0xff,
-0xe8,0x84,0xe8,0x84,0xc4,0xfb,0x66,0x60,0xe8,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x2b,0x60,0x5e,0x78,0xff,0xff,0x67,0x60,0x00,0x63,0xbd,0xd3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x49,0x03,0xbd,0xd3,0xa3,0xd1,0xff,0xff,0xd0,0x80,0xff,0xff,
-0x43,0x06,0x66,0x60,0xee,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x3c,0x03,
-0x66,0x60,0xf4,0x63,0xbd,0xd1,0xa3,0xd3,0xff,0xff,0xe8,0x84,0xe8,0x85,0x64,0x44,
-0xd4,0x85,0x58,0xf3,0x01,0x05,0x00,0x65,0xd4,0x80,0xff,0xff,0x10,0x06,0x66,0x60,
-0xf6,0x65,0xa5,0xd3,0xff,0xff,0xe8,0x84,0xc2,0xf3,0xe8,0x85,0x58,0xf3,0xc4,0x85,
-0xd4,0x80,0xff,0xff,0x21,0x05,0x61,0x60,0xc8,0x62,0x08,0x64,0xa2,0xdb,0x67,0x60,
-0x00,0x63,0x00,0x64,0xbd,0xdb,0xbd,0xdb,0xa3,0xd3,0xff,0xff,0xe0,0x84,0xf8,0xa0,
-0xff,0xff,0x01,0x04,0x08,0x64,0xa3,0xdb,0x66,0x60,0xf6,0x62,0xa2,0xd3,0xff,0xff,
-0xe8,0x84,0xe8,0x84,0xc4,0xfb,0x66,0x60,0xea,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x2b,0x60,0x5e,0x78,0xff,0xff,0xc2,0xf3,0x58,0xf1,0xff,0xff,0xd0,0x80,
-0xff,0xff,0x0d,0x04,0x61,0x60,0xc8,0x62,0x06,0x64,0xa2,0xdb,0x59,0x60,0xb6,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x2b,0x60,0x5e,0x78,0xff,0xff,0x59,0x60,
-0x9c,0x62,0xa2,0xd1,0xc3,0xf3,0xff,0xff,0xd0,0x80,0xff,0xff,0x0d,0x07,0x61,0x60,
-0xc8,0x62,0x02,0x64,0xa2,0xdb,0x59,0x60,0xb6,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x2b,0x60,0x5e,0x78,0xff,0xff,0x66,0x60,0xba,0x62,0xa2,0xd3,0xff,0xff,
-0x00,0xa0,0x60,0x45,0x0b,0x03,0x1e,0x60,0x94,0x62,0xa2,0xd3,0xff,0xff,0xd4,0x80,
-0xff,0xff,0x35,0x04,0x66,0x60,0xba,0x62,0x00,0x64,0xa2,0xdb,0x66,0x60,0xb8,0x62,
-0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,0x2a,0x02,0x66,0x60,0xbc,0x62,0xa2,0xd1,
-0x66,0x60,0xbe,0x63,0xa3,0xd3,0xff,0xff,0xd0,0x84,0xfe,0xa0,0xff,0xff,0x1f,0x04,
-0xe0,0x84,0xe0,0x84,0xd0,0x80,0xff,0xff,0x1a,0x04,0x66,0x60,0xbc,0x62,0x64,0x44,
-0x01,0xa4,0xa2,0xdb,0x1e,0x60,0x94,0x62,0x66,0x60,0xba,0x63,0xa2,0xd3,0xff,0xff,
-0x03,0xa4,0xa3,0xdb,0x61,0x60,0xc8,0x62,0x04,0x64,0xa2,0xdb,0x59,0x60,0xb8,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x2b,0x60,0x5e,0x78,0xff,0xff,0x66,0x60,
-0xb6,0x62,0xa2,0xd1,0x1e,0x60,0x94,0x62,0xa2,0xd3,0xff,0xff,0xd0,0x80,0xff,0xff,
-0x0d,0x04,0x61,0x60,0xc8,0x62,0x04,0x64,0xa2,0xdb,0x59,0x60,0xba,0x64,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x2b,0x60,0x5e,0x78,0xff,0xff,0x67,0x60,0x76,0x62,
-0xa2,0xd3,0xff,0xff,0x60,0x40,0x03,0x22,0x11,0x00,0x60,0x45,0xfd,0xb4,0xa2,0xdb,
-0x20,0x44,0xb4,0x84,0x40,0x40,0x61,0x60,0xc8,0x62,0x10,0x64,0xa2,0xdb,0x59,0x60,
-0xbc,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x37,0x00,0x1e,0x60,0xb2,0x62,
-0x00,0x60,0x64,0x64,0xa2,0xdb,0x2b,0x60,0x32,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0x00,0x60,0x04,0x64,0xa0,0x80,0x9c,0x84,
-0x05,0x03,0xa0,0x84,0xa2,0xdb,0x2c,0x60,0x33,0x78,0xff,0xff,0x00,0x60,0x40,0x64,
-0xa0,0x80,0x9c,0x84,0x05,0x03,0xa0,0x84,0xa2,0xdb,0x29,0x60,0xb5,0x78,0xff,0xff,
-0x00,0x60,0x20,0x64,0xa0,0x80,0x9c,0x84,0xe2,0x03,0xa0,0x84,0xa2,0xdb,0x01,0x65,
-0x2e,0x60,0x58,0x4e,0xd4,0x78,0xff,0xff,0xff,0x60,0xf7,0x65,0x5e,0xf3,0xff,0xff,
-0xa4,0x84,0xa2,0xdb,0x29,0x60,0xb5,0x78,0xff,0xff,0xad,0xf1,0x28,0x60,0xc6,0x62,
-0xa2,0xd9,0x2b,0x60,0x68,0x64,0x7c,0xfb,0x2c,0x60,0x68,0x78,0xff,0xff,0x3c,0x60,
-0xb2,0x62,0x28,0x60,0xce,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,
-0x1e,0x60,0xb0,0x62,0xa2,0xd1,0xff,0x60,0xdf,0x64,0xa0,0x84,0xa2,0xdb,0x1e,0x60,
-0x92,0x65,0xf4,0x56,0x7e,0x00,0x00,0x10,0x66,0x60,0xee,0x63,0x00,0x64,0xbd,0xdb,
-0xa5,0xd3,0xa3,0xdb,0x61,0x60,0xc8,0x62,0xa2,0xd3,0xff,0xff,0xf0,0xa0,0xfc,0xa0,
-0x08,0x03,0x13,0x02,0x66,0x60,0xb8,0x62,0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,
-0x0c,0x03,0x3c,0x60,0xb2,0x62,0x28,0x60,0xc2,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,
-0xff,0xff,0x1d,0xff,0x29,0x60,0x5d,0x78,0xff,0xff,0x88,0xf1,0x27,0x60,0xe0,0x63,
-0xd3,0x80,0xc4,0xf1,0x25,0x03,0x00,0x64,0xc4,0xfb,0x66,0x60,0xf4,0x65,0x58,0xf3,
-0xa5,0xdb,0xa3,0xd3,0xc0,0x85,0xd4,0x80,0x5b,0xd3,0x1a,0x04,0x60,0x43,0x63,0x42,
-0x06,0x65,0x46,0xd3,0x5a,0xd3,0x40,0x48,0x5a,0xd3,0x40,0x4c,0x40,0x4d,0x81,0xf3,
-0x28,0x45,0xd4,0x80,0x5a,0xd3,0x09,0x02,0x2c,0x45,0xd4,0x80,0x5a,0xd3,0x05,0x02,
-0x2d,0x45,0xd4,0x80,0x63,0x42,0x01,0x02,0x03,0x00,0x2c,0x60,0x2a,0x78,0xff,0xff,
-0xc2,0xf1,0x58,0xf3,0xff,0xff,0xd0,0x80,0xff,0xff,0x14,0x06,0x59,0x60,0x9c,0x62,
-0xa2,0xd3,0xc3,0xf1,0xff,0xff,0xd0,0x80,0xff,0xff,0x0c,0x05,0x3c,0x60,0xb2,0x62,
-0x28,0x60,0xc2,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x29,0x60,
-0x5d,0x78,0xff,0xff,0x1e,0x60,0xb2,0x62,0x00,0x60,0x74,0x64,0xa2,0xdb,0x2b,0x60,
-0xeb,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,
-0x00,0x60,0x04,0x64,0xa0,0x80,0x9c,0x84,0x05,0x03,0xa0,0x84,0xa2,0xdb,0x2c,0x60,
-0x33,0x78,0xff,0xff,0x00,0x60,0x40,0x64,0xa0,0x80,0x9c,0x84,0x05,0x03,0xa0,0x84,
-0xa2,0xdb,0x2b,0x60,0x9b,0x78,0xff,0xff,0x00,0x60,0x20,0x64,0xa0,0x80,0x9c,0x84,
-0x10,0x03,0xa0,0x84,0xa2,0xdb,0x01,0x65,0x2e,0x60,0x58,0x4e,0xd4,0x78,0xff,0xff,
-0xff,0x60,0xf7,0x65,0x5e,0xf3,0xff,0xff,0xa4,0x84,0xa2,0xdb,0x2b,0x60,0x9b,0x78,
-0xff,0xff,0x00,0x60,0x10,0x64,0xa0,0x80,0x9c,0x84,0xcd,0x03,0xa0,0x84,0xa2,0xdb,
-0x3c,0x60,0xb2,0x62,0x28,0x60,0xce,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,
-0x1d,0xff,0x2b,0x60,0x5e,0x78,0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,
-0x20,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x3c,0x60,0xb2,0x62,0x28,0x60,
-0xb6,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x3c,0x60,0xb2,0x62,
-0x28,0x60,0xc2,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x3c,0x60,
-0xb2,0x62,0x28,0x60,0xce,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,
-0x1e,0x60,0xb0,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x00,0x64,0x5e,0xfb,0x1e,0x60,
-0xa8,0x62,0xa2,0xd1,0x08,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x1e,0x60,
-0xb2,0x62,0x00,0x60,0x02,0x64,0xa2,0xdb,0x29,0x60,0x53,0x64,0x5a,0xdb,0xcf,0xfe,
-0x2f,0x58,0xff,0xff,0x1e,0x60,0xb2,0x62,0x80,0x60,0x00,0x64,0xa2,0xdb,0x2c,0x60,
-0x71,0x64,0x5a,0xdb,0xcf,0xfe,0x8c,0xf3,0xff,0xff,0xff,0xa0,0x02,0x64,0x2a,0x02,
-0x8c,0xfb,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0x7f,0x60,0xff,0x64,0xa0,0x84,0xa2,0xdb,
-0x1e,0x60,0xb2,0x62,0x80,0x60,0x00,0x64,0xa2,0xdb,0x2c,0x60,0x8a,0x64,0x5a,0xdb,
-0xcf,0xfe,0xc1,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0x7f,0x60,
-0xff,0x64,0xa0,0x84,0xa2,0xdb,0x8c,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0xf2,0x02,
-0x1e,0x60,0xb0,0x62,0xa2,0xd1,0x7f,0x60,0xff,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,
-0xa1,0x84,0x5a,0xdb,0x20,0x44,0x40,0x2a,0x10,0x00,0x20,0xbc,0x40,0x40,0x11,0x60,
-0x48,0x65,0x2f,0x60,0x58,0x4e,0x23,0x78,0xff,0xff,0x3a,0x60,0x58,0x4e,0x14,0x78,
-0xff,0xff,0x2f,0x60,0x58,0x4e,0x42,0x78,0xff,0xff,0x59,0x60,0x20,0x64,0x5b,0xfb,
-0x1e,0x60,0xb0,0x62,0xa2,0xd1,0xef,0x60,0xff,0x64,0xa0,0x84,0xa2,0xdb,0x0f,0x4e,
-0x52,0x60,0x58,0x4f,0xaa,0x78,0xff,0xff,0x0e,0x4f,0x1e,0x60,0xb2,0x62,0x10,0x60,
-0x00,0x64,0xa2,0xdb,0x2c,0x60,0xce,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,
-0x20,0x44,0x20,0x2a,0x10,0x00,0xdf,0xb4,0x40,0x40,0x01,0x60,0x48,0x65,0x2f,0x60,
-0x58,0x4e,0x23,0x78,0xff,0xff,0x3a,0x60,0x58,0x4e,0x14,0x78,0xff,0xff,0x2f,0x60,
-0x58,0x4e,0x42,0x78,0xff,0xff,0x3c,0x60,0xb6,0x62,0x28,0x60,0xc2,0x64,0xa2,0xdb,
-0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0xef,0x60,
-0xef,0x64,0xa0,0x84,0xa2,0xdb,0x01,0x64,0x8c,0xfb,0xff,0xff,0xc1,0xfe,0x7c,0xf7,
-0xff,0xff,0xff,0xff,0x59,0xf1,0x28,0x44,0xd0,0x84,0x0f,0xa4,0x03,0x0e,0xe8,0x84,
-0xe8,0x84,0x04,0x00,0xe2,0xa4,0xe8,0x84,0xe8,0x87,0xf0,0xbf,0xc0,0x84,0xa2,0xdb,
-0x2e,0x58,0xff,0xff,0x5a,0xf1,0x28,0x44,0xd0,0x84,0x1f,0xa4,0x06,0x0e,0xe8,0x84,
-0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0x07,0x00,0xc2,0xa4,0xe8,0x84,0xe8,0x84,
-0xe8,0x84,0xe8,0x84,0xe8,0x87,0xf8,0xbf,0xc0,0x84,0xa2,0xdb,0x2e,0x58,0xff,0xff,
-0x5a,0xf1,0x59,0xf3,0x64,0x45,0xd4,0x84,0x80,0x65,0xc4,0x87,0x01,0x05,0x00,0x64,
-0xff,0xb4,0x58,0xfb,0x2e,0x58,0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0x00,0x60,
-0x08,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,
-0xb0,0x62,0xa2,0xd1,0x00,0x60,0x10,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,
-0x2f,0x58,0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0x00,0x60,0x20,0x64,0xb0,0x84,
-0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x2e,0x60,0xc1,0x78,0xff,0xff,
-0xcb,0xf3,0xff,0xff,0x60,0x40,0x03,0x36,0x2e,0x00,0x1e,0x60,0xb2,0x62,0xa2,0xd3,
-0xff,0xff,0x60,0x40,0x40,0x22,0xf1,0x00,0x66,0x60,0xb8,0x62,0xa2,0xd3,0xff,0xff,
-0xff,0xa0,0xff,0xff,0x11,0x02,0x26,0x46,0x00,0xf4,0x57,0x60,0x58,0x4e,0x59,0x78,
-0xff,0xff,0x66,0x60,0xbe,0x62,0xa2,0xd9,0x66,0x60,0xbc,0x63,0xa3,0xd3,0xff,0xff,
-0xd0,0x80,0xff,0xff,0x01,0x06,0xa3,0xd9,0x58,0x60,0x58,0x4e,0x6c,0x78,0xff,0xff,
-0x65,0x44,0x00,0xa0,0xff,0xff,0x01,0x03,0xe1,0xfb,0x00,0x65,0x2e,0x60,0x58,0x4e,
-0xd4,0x78,0xff,0xff,0x09,0x00,0x6d,0xf3,0xff,0xff,0x04,0xb4,0x04,0xbc,0x03,0x03,
-0x2e,0x60,0xc1,0x78,0xff,0xff,0x6d,0xfb,0x26,0x46,0x20,0xf2,0xa0,0x65,0x01,0x37,
-0x50,0x65,0x02,0x37,0x1e,0x65,0x03,0x37,0x0f,0x65,0x28,0x60,0xda,0x63,0x00,0xf4,
-0x02,0xf2,0xff,0xff,0xd4,0x84,0xbd,0xdb,0x03,0xf2,0x01,0x05,0xcc,0x84,0xbd,0xdb,
-0x04,0xf2,0x01,0x05,0xcc,0x84,0xbd,0xdb,0x05,0xf2,0x01,0x05,0xcc,0x84,0xa3,0xdb,
-0xfa,0xa3,0x26,0x46,0x00,0x60,0x00,0x65,0xa3,0xd3,0x23,0xf0,0x00,0x61,0xd0,0x84,
-0xf1,0x81,0xd4,0x84,0xf1,0x81,0xbd,0xdb,0xa3,0xd3,0x03,0xb1,0x03,0xa9,0x24,0xf0,
-0x42,0xfe,0x01,0x03,0xcc,0x84,0xf1,0x81,0xd0,0x84,0xf1,0x81,0xbd,0xdb,0xa3,0xd3,
-0x03,0xb1,0x03,0xa9,0x27,0xf0,0x42,0xfe,0x01,0x03,0xcc,0x84,0xf1,0x81,0xd0,0x84,
-0xf1,0x81,0xbd,0xdb,0xa3,0xd3,0x03,0xb1,0x03,0xa9,0x28,0xf0,0x01,0x03,0xcc,0x84,
-0xd0,0x84,0xa3,0xdb,0x60,0x45,0x68,0x60,0xcc,0x62,0x90,0x60,0x92,0x64,0xa2,0xdb,
-0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x5e,0xf3,0xcb,0xf3,0x04,0xb0,0xff,0xff,
-0x35,0x03,0x03,0x3a,0x35,0x00,0x73,0xf3,0xff,0xff,0x60,0x40,0x04,0x26,0x30,0x00,
-0xa3,0xd3,0x4b,0xd1,0x4b,0xd3,0xc0,0x9c,0xc0,0x84,0x4b,0xd1,0x00,0xa0,0x03,0xa0,
-0x01,0x03,0x1f,0x02,0x80,0x60,0x00,0x65,0x64,0x44,0xa4,0x85,0xe8,0x84,0xb4,0x84,
-0xe8,0x84,0xb4,0x84,0xa3,0xdb,0x60,0x45,0xfa,0x64,0xd4,0x80,0xff,0x60,0x06,0x64,
-0xd4,0x80,0x14,0x07,0x13,0x04,0x66,0x60,0xa0,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x4d,0xf3,0xff,0xff,0x60,0x40,0x00,0x3a,0x03,0x00,0x01,0x64,0x4d,0xfb,
-0x07,0x00,0x66,0x60,0x9a,0x62,0x10,0x64,0xa2,0xdb,0x06,0x00,0x00,0x64,0x4d,0xfb,
-0x01,0x64,0x23,0xfb,0xff,0xff,0x1a,0xff,0x5e,0xf3,0xff,0xff,0x04,0xb0,0x08,0xb0,
-0x06,0x03,0x7e,0x02,0x5f,0xf3,0xff,0xff,0xff,0xa4,0x5f,0xfb,0x7b,0x02,0x26,0x46,
-0x00,0xf4,0x02,0xf2,0x5a,0xd2,0x40,0x48,0x40,0x4c,0x5a,0xd2,0x5a,0xd2,0x40,0x4d,
-0x60,0x41,0x5a,0xd0,0x80,0xf9,0x40,0x63,0xad,0x80,0xf0,0xa3,0x09,0x02,0x3c,0x03,
-0x2d,0x41,0x2c,0x44,0x40,0x4d,0x28,0x44,0x40,0x4c,0x00,0x64,0x40,0x48,0xf4,0x00,
-0xd1,0x80,0x01,0x02,0x31,0x04,0x10,0xa3,0x80,0x60,0x00,0x65,0xa5,0x80,0xcf,0x83,
-0x08,0x02,0x28,0x44,0x60,0x88,0x2c,0x44,0x70,0x8c,0x2d,0x44,0x70,0x8d,0xf1,0x81,
-0xf5,0x00,0xe7,0xa3,0x64,0x44,0x00,0xa0,0x00,0x62,0x02,0x02,0x00,0x61,0x1c,0x00,
-0xe0,0x84,0xde,0x82,0xfd,0x04,0x42,0xfe,0xf8,0x84,0x62,0x45,0xc7,0x83,0x60,0x45,
-0x02,0xfe,0xd5,0x84,0x02,0x05,0x01,0x05,0x61,0x44,0xcf,0x83,0x60,0x41,0x08,0x03,
-0x28,0x44,0x60,0x88,0x2c,0x44,0x70,0x8c,0x2d,0x44,0x70,0x8d,0xf1,0x81,0xf1,0x00,
-0xce,0x82,0xe9,0x81,0xfd,0x02,0xf1,0x81,0x02,0xf2,0xff,0xff,0x60,0x47,0xe8,0x84,
-0xe8,0x84,0x5a,0xd2,0x3f,0xb5,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0xb4,0x84,0x61,0x45,0xd4,0x84,0xc0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0x60,0x53,0x80,0xf3,0x60,0x41,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0x60,0x45,0x61,0x44,0xd4,0x84,0xa4,0xfb,0x04,0x60,0x00,0x71,0x60,0x45,0x68,0x60,
-0xcc,0x62,0x90,0x60,0x95,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,
-0x05,0x64,0x5f,0xfb,0x5e,0xf3,0x26,0x46,0x0c,0xbc,0xa2,0xdb,0xff,0x60,0x00,0x65,
-0x25,0xf2,0xff,0xff,0x24,0x88,0x60,0x47,0x24,0x8c,0x2c,0x60,0x58,0x4e,0xf8,0x78,
-0xff,0xff,0x0c,0x48,0x2d,0x60,0x58,0x4e,0x08,0x78,0xff,0xff,0x2d,0x60,0x58,0x4e,
-0x1e,0x78,0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0x00,0x60,0x40,0x64,0xb0,0x84,
-0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x3c,0x60,0xb2,0x62,0x28,0x60,0xce,0x64,0xa2,0xdb,
-0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0xff,0x60,
-0xdf,0x64,0xa0,0x84,0xa2,0xdb,0x26,0x46,0x2f,0x58,0xff,0xff,0x65,0x44,0x00,0xa0,
-0x40,0x48,0x13,0x03,0x66,0x60,0x2a,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xf6,0xa0,
-0xa2,0xdb,0x0f,0x04,0x00,0x64,0xa2,0xdb,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,
-0x40,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x66,0x60,0x2a,0x62,0x00,0x64,
-0xa2,0xdb,0x66,0x60,0x2c,0x62,0xa2,0xd3,0x28,0x45,0xc4,0x84,0xa2,0xdb,0x66,0x60,
-0x2e,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xce,0xa0,0xa2,0xdb,0x14,0x06,0x32,0x64,
-0xa2,0xdb,0x66,0x60,0x30,0x62,0xa2,0xd1,0x66,0x60,0x32,0x64,0xc0,0x82,0xa2,0xd1,
-0x66,0x60,0x2c,0x62,0xa2,0xd3,0xff,0xff,0xd0,0x84,0xa2,0xdb,0xe0,0x85,0x59,0x60,
-0x9c,0x62,0x65,0x44,0xa2,0xdb,0x66,0x60,0x30,0x62,0xa2,0xd1,0x66,0x60,0x32,0x64,
-0xc0,0x82,0x28,0x44,0xa2,0xdb,0x66,0x60,0x30,0x62,0x64,0x44,0x9e,0xa0,0x02,0xa4,
-0x01,0x02,0x00,0x64,0xa2,0xdb,0x2e,0x58,0xff,0xff,0x7b,0xf5,0xff,0xff,0x81,0xf1,
-0x2b,0xf8,0x31,0xf8,0xff,0xff,0x82,0xf1,0x2c,0xf8,0x32,0xf8,0xff,0xff,0x83,0xf1,
-0x2d,0xf8,0x33,0xf8,0xff,0xff,0xbd,0xf1,0x2e,0xf8,0xbe,0xf1,0xff,0xff,0x2f,0xf8,
-0xbf,0xf1,0x30,0xf8,0xff,0xff,0x00,0x64,0x22,0xfa,0x06,0x60,0x20,0x64,0x0e,0xfa,
-0x65,0x44,0x29,0xfa,0x2e,0x58,0xff,0xff,0x66,0x60,0xcc,0x62,0x2e,0x44,0xa2,0xdb,
-0x1e,0x60,0xb0,0x62,0x00,0x64,0xa2,0xdb,0x3c,0x60,0x82,0x62,0x3c,0x60,0x2e,0x64,
-0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xc1,0xfe,
-0x1e,0x60,0xb2,0x62,0x00,0x60,0x01,0x64,0xa2,0xdb,0x2f,0x60,0x61,0x64,0x5a,0xdb,
-0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0xff,0x60,0xfe,0x61,
-0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,0x66,0x60,0xcc,0x62,0xa2,0xd3,
-0xff,0xff,0x40,0x4e,0x2e,0x58,0xff,0xff,0x27,0x42,0xa2,0xd3,0xa2,0xd1,0xac,0x86,
-0x0e,0xf2,0x57,0x03,0x60,0x40,0x02,0x2a,0x54,0x00,0x0b,0xf2,0xff,0xff,0x00,0xa4,
-0x44,0x45,0x39,0x02,0x21,0x44,0xf7,0xa0,0xff,0xff,0x35,0x07,0x5c,0x81,0x22,0x44,
-0x00,0x7c,0xd0,0x80,0xff,0xff,0x01,0x02,0x46,0x42,0x48,0xf3,0xff,0xff,0x60,0x41,
-0x02,0xfa,0x40,0xa1,0x7c,0x63,0x84,0xa1,0x00,0xf2,0x03,0x06,0x01,0xfc,0x60,0x46,
-0xfa,0x00,0x80,0x60,0x7c,0x64,0x01,0xfa,0x66,0x43,0x25,0x46,0x05,0xfc,0x06,0xfc,
-0x01,0xf0,0x03,0x67,0xb0,0x84,0x00,0xf0,0x3c,0x7e,0x01,0xfa,0x04,0x64,0x03,0xfa,
-0x04,0xf8,0x00,0x64,0x0b,0xfa,0x0c,0xfa,0xff,0xff,0x0e,0xfa,0x0f,0xfa,0x3c,0x60,
-0x88,0x62,0x3c,0x60,0x64,0x64,0xa2,0xdb,0x25,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0xb9,0x00,0x0f,0x4e,0x44,0x45,0x64,0x46,0x3c,0x60,0x88,0x62,
-0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,
-0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,0xd1,0xfe,0x0e,0x4f,
-0xa3,0x00,0x2f,0x58,0xff,0xff,0x3c,0x60,0x6a,0x64,0x40,0x47,0x58,0x4f,0x9c,0x00,
-0x3c,0x60,0x46,0x64,0x40,0x47,0x58,0x4f,0x08,0x00,0x3c,0x60,0x70,0x64,0x40,0x47,
-0x58,0x4f,0x03,0x00,0x30,0x60,0x7f,0x78,0xff,0xff,0x27,0x42,0xa2,0xd3,0xa2,0xd1,
-0xac,0x86,0x0e,0xf2,0x46,0x03,0x60,0x40,0x02,0x2a,0x43,0x00,0x95,0xf3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x28,0x03,0x3c,0x60,0x46,0x64,0x27,0x45,0xd4,0x80,0xff,0xff,
-0x22,0x02,0x00,0x64,0x13,0xfb,0x22,0xf2,0xff,0xff,0xff,0xff,0x10,0x26,0x0f,0x00,
-0x1c,0xf2,0xff,0xff,0x03,0xb4,0xff,0xff,0x00,0x36,0x15,0x00,0x01,0x36,0x13,0x00,
-0x02,0x36,0x05,0x00,0x22,0xf2,0xff,0xff,0x00,0xa8,0xff,0xff,0x0c,0x03,0x3c,0x60,
-0x88,0x62,0x3c,0x60,0x40,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0x16,0x00,0x0f,0x4e,0x44,0x45,0x64,0x46,0x3c,0x60,0x88,0x62,
-0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,
-0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,0xd1,0xfe,0x0e,0x4f,
-0xb4,0x00,0x2f,0x58,0xff,0xff,0x02,0x64,0x01,0x00,0x01,0x64,0x40,0x55,0x3b,0xff,
-0x48,0x00,0xb2,0xfe,0xff,0xff,0xf9,0x05,0xb3,0xfe,0xff,0xff,0xf4,0x05,0xb0,0xfe,
-0xff,0xff,0x91,0x05,0xb1,0xfe,0xff,0xff,0x26,0x05,0x3b,0x00,0x48,0xf1,0x0f,0x4e,
-0x64,0x41,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,
-0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,0xc4,0x78,0xff,0xff,0x08,0xfe,
-0x0e,0x4f,0x26,0x03,0x3c,0x60,0x88,0x62,0x3c,0x60,0x64,0x64,0xa2,0xdb,0x66,0x44,
-0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x22,0x41,0x00,0xb9,0x21,0x44,
-0x08,0x24,0x46,0x42,0x5c,0x81,0x3e,0x41,0x22,0x44,0x00,0xb9,0xac,0x86,0x09,0x02,
-0xd5,0x03,0x31,0x40,0x01,0x2a,0x05,0x00,0x09,0xf0,0x02,0x5e,0x44,0x42,0x21,0x44,
-0x4c,0x81,0x96,0xf3,0x21,0x45,0xd4,0x80,0xff,0xff,0xc8,0x07,0x58,0x4f,0x04,0x00,
-0x00,0x00,0xa1,0xff,0xff,0xff,0xbe,0x3f,0x3c,0x60,0x34,0x62,0xa2,0xd3,0xff,0xff,
-0x00,0xa8,0x60,0x46,0x40,0x03,0x46,0x48,0x03,0x60,0x3c,0x64,0x01,0xfa,0x02,0xf0,
-0x0f,0x4e,0x64,0x41,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,0x58,0x4f,0xa2,0x78,
-0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,0xc4,0x78,0xff,0xff,
-0x08,0xfe,0x0e,0x4f,0x76,0x03,0x3c,0x60,0x88,0x62,0x3c,0x60,0x3a,0x64,0xa2,0xdb,
-0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x00,0xf2,0x00,0x63,
-0x00,0xfc,0x05,0xf0,0x06,0xfc,0x66,0x43,0x05,0xfc,0x28,0x46,0x00,0xfa,0x04,0xfa,
-0x04,0x64,0x03,0xfa,0x05,0xf8,0x06,0xf8,0x08,0x64,0x0e,0xfa,0x3c,0x60,0x88,0x62,
-0x3c,0x60,0x40,0x64,0xa2,0xdb,0x28,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0xd1,0xfe,0x4b,0x00,0x20,0x44,0x01,0x2a,0x4b,0x00,0x02,0x2a,0x22,0x00,
-0x0f,0x4e,0x00,0x60,0x3c,0x61,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,0x58,0x4f,
-0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,0xc4,0x78,
-0xff,0xff,0x08,0xfe,0x0e,0x4f,0x35,0x03,0x3c,0x60,0x88,0x62,0x3c,0x60,0x3a,0x64,
-0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x20,0x44,
-0xfd,0xb4,0x40,0x40,0x91,0xf1,0x0f,0x4e,0x64,0x41,0x41,0x4d,0x40,0xa1,0xa2,0xff,
-0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,
-0x58,0x4f,0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,0x13,0x03,0x3c,0x60,0x88,0x62,
-0x3c,0x60,0x40,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0x08,0x64,0x0e,0xfa,0x20,0x44,0xfe,0xb4,0x40,0x40,0x04,0x64,0x40,0x55,
-0x3b,0xff,0x2f,0x58,0xff,0xff,0xb8,0xfe,0xff,0xff,0x02,0x24,0x97,0xf7,0xff,0xff,
-0xff,0xff,0xba,0xfe,0xff,0xff,0x05,0x05,0xb9,0xfe,0xbb,0xfe,0x30,0x60,0x7f,0x78,
-0xff,0xff,0x36,0x44,0x00,0x7f,0xfc,0xa0,0x60,0x45,0x05,0x05,0x0e,0x60,0xd4,0x64,
-0x44,0xd7,0xff,0xff,0xff,0xff,0x30,0x60,0x7f,0x78,0xff,0xff,0x7f,0x60,0xc0,0x64,
-0x24,0x45,0xa4,0x80,0x7f,0x67,0x02,0x61,0x13,0x02,0x20,0x44,0x01,0x2a,0x03,0x00,
-0x7f,0x67,0x07,0x61,0x0d,0x00,0x48,0xf1,0x25,0x44,0x64,0x45,0x91,0xfb,0xd4,0x80,
-0x7f,0x67,0x05,0x61,0x05,0x07,0x20,0x44,0x03,0xbc,0x40,0x40,0xd1,0xfe,0x00,0x67,
-0x23,0x58,0xff,0xff,0x24,0x44,0x36,0x60,0x58,0x4f,0x2b,0x78,0xff,0xff,0x03,0x61,
-0x03,0x03,0x31,0x60,0xfa,0x78,0xff,0xff,0x24,0x44,0x40,0xb0,0xff,0xff,0x48,0x03,
-0x25,0x46,0x66,0x5c,0xd1,0xf9,0x0e,0xf0,0xff,0xff,0x64,0x40,0x08,0x2a,0x1f,0x00,
-0x3c,0x60,0x3a,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x18,0x03,0x0f,0x4e,
-0x46,0x45,0x3c,0x60,0x88,0x62,0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,
-0x5a,0xdb,0xff,0xff,0x2b,0xff,0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,
-0xa3,0xff,0xd1,0xfe,0x0e,0x4f,0x0e,0xf2,0xff,0xff,0xf7,0xb4,0x0e,0xfa,0xd1,0xf5,
-0x22,0xf0,0xff,0x60,0xef,0x64,0xa0,0x84,0xa2,0xda,0x00,0x64,0x1c,0xfa,0x3c,0x60,
-0x88,0x62,0x3c,0x60,0x46,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0x0e,0xf2,0xff,0xff,0x02,0xbc,0x0e,0xfa,0x3c,0x60,0x46,0x64,
-0x40,0x47,0x2f,0x60,0x58,0x4f,0xe3,0x78,0xff,0xff,0x32,0x60,0xf0,0x78,0xff,0xff,
-0x59,0x60,0xe4,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,
-0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x25,0x46,0x3c,0x60,0x28,0x65,0x08,0xf2,
-0xff,0xff,0xd4,0x80,0x03,0x61,0x40,0x03,0x3c,0x60,0x88,0x62,0x00,0x64,0xa2,0xdb,
-0x25,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x28,0xf0,0xfd,0x7f,
-0x04,0x7e,0x64,0x40,0x02,0x26,0x40,0xbc,0x0e,0xf0,0x64,0x40,0x04,0x26,0x80,0xbc,
-0xc0,0x22,0xff,0x7f,0x64,0x40,0x08,0x26,0x08,0xbc,0x0e,0xfa,0x3f,0xf0,0xff,0xff,
-0x28,0xf2,0x38,0xf2,0x60,0x41,0x08,0x2a,0x64,0x47,0x38,0xfa,0x60,0x45,0x49,0xf3,
-0x00,0x63,0xd4,0x80,0x22,0xfc,0x01,0x04,0x07,0x00,0x22,0xf0,0x08,0x64,0xb0,0x84,
-0xa2,0xda,0x32,0x60,0x08,0x78,0xff,0xff,0x39,0x60,0x58,0x4f,0x90,0x78,0xff,0xff,
-0x05,0x04,0x22,0xf0,0x04,0x64,0xb0,0x84,0xa2,0xda,0x14,0x00,0x32,0x60,0x58,0x4f,
-0xf5,0x78,0xff,0xff,0x05,0x61,0x03,0x04,0x32,0x60,0xf2,0x78,0xff,0xff,0x25,0x46,
-0xcb,0xf3,0x95,0xf3,0xfe,0xa0,0x00,0xa0,0x05,0x07,0x04,0x02,0x22,0xf0,0x04,0x64,
-0xb0,0x84,0xa2,0xda,0x24,0x44,0x01,0x2b,0x3a,0x00,0x02,0x27,0x38,0x00,0x3c,0x60,
-0x3a,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x46,0x48,0x10,0x63,0x25,0x46,
-0xbf,0xd0,0x28,0x46,0xff,0xd8,0x25,0x46,0xfb,0x1d,0x64,0x44,0x00,0xa8,0x28,0x44,
-0x03,0x02,0x28,0x46,0x05,0xfa,0x06,0xfa,0x16,0x63,0x6a,0x61,0x25,0x46,0xa3,0xd0,
-0x28,0x46,0xc9,0x81,0xbd,0xd8,0xfa,0x02,0x0e,0xf0,0xff,0x60,0xfc,0x64,0xa0,0x84,
-0x0e,0xfa,0x00,0x64,0x0f,0xfa,0x00,0x64,0x25,0x46,0x00,0xfa,0x66,0x44,0x05,0xfa,
-0x3c,0x60,0x88,0x62,0x3c,0x60,0x34,0x64,0xa2,0xdb,0x25,0x44,0x5a,0xdb,0x0a,0x64,
-0x5a,0xdb,0xff,0xff,0x2b,0xff,0xd1,0xfe,0x00,0x64,0x0e,0xfa,0x28,0x46,0x0e,0xf0,
-0xff,0x60,0xfb,0x64,0xa0,0x84,0x0e,0xfa,0x22,0xf2,0x66,0x43,0x00,0xa8,0x60,0x5c,
-0x08,0x60,0x0a,0x64,0xa0,0xdd,0x64,0x44,0x69,0x02,0x95,0xf3,0xff,0xff,0x00,0xa0,
-0xff,0xff,0x44,0x03,0x26,0x44,0x0a,0x36,0x00,0x63,0x14,0x36,0x01,0x63,0x37,0x36,
-0x02,0x63,0x6e,0x36,0x03,0x63,0x13,0xfc,0x26,0x44,0xff,0x27,0x06,0x00,0x26,0xf2,
-0x26,0xf2,0x60,0x45,0x60,0x47,0xd4,0x84,0x01,0x00,0x60,0x47,0xff,0x65,0xa4,0x84,
-0x1d,0xfa,0x00,0x64,0x15,0xfa,0x27,0xf2,0xff,0xff,0x00,0xa0,0xff,0xff,0x15,0x02,
-0x1e,0x60,0x98,0x65,0x25,0xf2,0xff,0xff,0x0f,0xb4,0xb8,0xf1,0x00,0x7f,0xd0,0x80,
-0x60,0x5c,0x06,0x05,0x67,0x60,0x6e,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0xa5,0xd9,0x64,0x40,0x00,0x3a,0xb8,0xf9,0x11,0x00,0x29,0xf0,0x08,0x67,0xb0,0x84,
-0xa2,0xda,0x1e,0x65,0x29,0xf2,0xff,0xff,0x60,0x40,0x03,0x2b,0x18,0x65,0x65,0x44,
-0x04,0xa4,0x64,0x40,0x40,0x27,0x08,0xa4,0x21,0xfa,0x08,0x00,0x3a,0x60,0x58,0x4e,
-0x14,0x78,0xff,0xff,0x3a,0x60,0x58,0x4e,0x72,0x78,0xff,0xff,0x95,0xf3,0xff,0xff,
-0x00,0xa0,0x47,0xf3,0x03,0x03,0x24,0x47,0x0f,0xb4,0x02,0x00,0xe8,0x84,0xe8,0x84,
-0x1c,0xfa,0x3c,0x60,0x88,0x62,0x3c,0x60,0x28,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,
-0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xc1,0xfe,0x0c,0x00,0x3c,0x60,0x88,0x62,
-0x3c,0x60,0x46,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0xd3,0xfe,0x0e,0xf0,0x24,0x44,0x02,0x27,0x02,0x00,0x01,0x27,0x22,0x00,
-0x64,0x40,0x08,0x2a,0x1f,0x00,0x3c,0x60,0x3a,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,
-0x60,0x46,0x18,0x03,0x0f,0x4e,0x46,0x45,0x3c,0x60,0x88,0x62,0x00,0x64,0xa2,0xdb,
-0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xa2,0xff,0x1a,0x60,
-0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,0xd1,0xfe,0x0e,0x4f,0x0e,0xf2,0xff,0xff,
-0xf7,0xb4,0x0e,0xfa,0x00,0x67,0x01,0x00,0x7f,0x67,0x23,0x58,0xff,0xff,0x0f,0x4e,
-0x25,0x46,0x38,0xf2,0x05,0x48,0x00,0xa8,0x60,0x41,0x66,0x44,0x0a,0x03,0x00,0xf2,
-0x42,0xfe,0xac,0x86,0x01,0xf2,0x1e,0x03,0x7f,0xb5,0xd5,0x81,0x66,0x44,0xf7,0x07,
-0x25,0x46,0x05,0xf0,0x06,0xfa,0x05,0xfa,0xd0,0x80,0x64,0x43,0x12,0x03,0x60,0x46,
-0x01,0xf0,0x80,0x67,0xb0,0x84,0x01,0xfa,0x00,0xf0,0x00,0x64,0x00,0xfa,0x64,0x46,
-0x05,0xfc,0x46,0x45,0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,
-0x08,0x45,0x02,0xfe,0x2e,0x58,0xff,0xff,0x20,0x44,0x40,0xb0,0x7f,0x67,0x02,0x61,
-0x03,0x03,0x34,0x60,0x2d,0x78,0xff,0xff,0x00,0x64,0x24,0x45,0x80,0x26,0x01,0x64,
-0x95,0xfb,0x59,0x60,0x54,0x62,0xa2,0xd3,0xff,0xff,0x01,0xa4,0xa2,0xdb,0x4a,0xf1,
-0x43,0xf1,0x64,0x40,0x01,0x2a,0x08,0x00,0x64,0x40,0x01,0x2a,0x05,0x00,0x1e,0x60,
-0xa0,0x63,0x09,0x60,0x2b,0x64,0x19,0x00,0xc7,0xf1,0x1e,0x60,0xa0,0x63,0x64,0x45,
-0x80,0x27,0x19,0x00,0x64,0x44,0x00,0xac,0xff,0xff,0x0d,0x02,0x02,0x60,0x52,0x64,
-0xbd,0xdb,0x03,0x60,0x1c,0x64,0xbd,0xdb,0x7f,0x60,0xff,0x64,0xbd,0xdb,0x7f,0x60,
-0xff,0x64,0xbd,0xdb,0x07,0x00,0xe8,0x84,0xe0,0x84,0xbd,0xdb,0xbd,0xdb,0xbd,0xdb,
-0xff,0xff,0xbd,0xdb,0x36,0x00,0x80,0x67,0x94,0x81,0x61,0x44,0xe8,0x84,0xe8,0x84,
-0xe8,0x84,0xe8,0x84,0xe0,0x84,0xbd,0xdb,0x61,0x44,0xe8,0x84,0xe8,0x84,0xe8,0x84,
-0xe0,0x84,0xbd,0xdb,0x0d,0x60,0x18,0x65,0x61,0x44,0xd4,0x80,0xff,0xff,0x01,0x06,
-0x65,0x44,0xe0,0x85,0xc4,0x85,0xe0,0x84,0xf4,0x66,0x7e,0x00,0x00,0x10,0xe0,0x84,
-0xe0,0x84,0xc4,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe0,0x84,
-0xbd,0xdb,0x06,0x60,0x8c,0x65,0x61,0x44,0xd4,0x80,0xff,0xff,0x01,0x06,0x65,0x44,
-0xe0,0x85,0xc4,0x85,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xc4,0x84,0xe8,0x84,0xe8,0x84,
-0xe8,0x84,0xe8,0x84,0xe0,0x84,0xbd,0xdb,0x1e,0x60,0xa0,0x63,0x04,0x61,0xbd,0xd1,
-0x90,0x65,0x64,0x44,0xd4,0x80,0x65,0x44,0x01,0x05,0xbf,0xdb,0x09,0x60,0x2b,0x65,
-0x64,0x44,0xd4,0x80,0xcd,0x81,0x02,0x06,0x65,0x44,0xbf,0xdb,0xf0,0x02,0x00,0x61,
-0x41,0x56,0xc7,0xfe,0x30,0x60,0x7f,0x78,0xff,0xff,0x36,0x47,0xff,0x23,0x06,0x00,
-0x00,0x7f,0x60,0x41,0x7f,0x67,0x34,0x60,0x2d,0x78,0xff,0xff,0x99,0xff,0x00,0x60,
-0x00,0xeb,0x00,0x60,0x00,0xea,0x98,0xff,0x20,0x44,0x80,0xbc,0x40,0x40,0x59,0x60,
-0x6a,0x63,0xc9,0xf3,0xa3,0xdb,0x00,0x63,0x60,0x40,0x01,0x26,0x09,0x00,0x01,0xa3,
-0x60,0x40,0x02,0x26,0x05,0x00,0x01,0xa3,0x60,0x40,0x04,0x26,0x01,0x00,0x01,0xa3,
-0x60,0x41,0x17,0x60,0xde,0x65,0xa5,0xdd,0x61,0x44,0x08,0x2a,0x03,0x00,0x03,0x63,
-0x08,0x64,0x0c,0x00,0x04,0x2a,0x03,0x00,0x02,0x63,0x04,0x64,0x07,0x00,0x02,0x2a,
-0x03,0x00,0x01,0x63,0x02,0x64,0x02,0x00,0x00,0x63,0x01,0x64,0x50,0xfb,0x51,0xfd,
-0x95,0xf3,0xff,0xff,0x00,0xa0,0x00,0x64,0x2d,0x03,0xa1,0xfb,0xa2,0xfb,0xa3,0xfb,
-0xff,0xff,0x80,0xf3,0x88,0xff,0x00,0x75,0x00,0x72,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0x60,0x53,0xed,0xe2,0xbf,0xf3,0xff,0xff,0xff,0xb4,0x60,0x52,0x8a,0xff,
-0xbd,0xf1,0x81,0xf9,0xbe,0xf1,0xff,0xff,0x82,0xf9,0xbf,0xf1,0x83,0xf9,0x17,0x60,
-0xdc,0x63,0xa3,0xd3,0x00,0x65,0x60,0x40,0x02,0x26,0x01,0x65,0x60,0x40,0x04,0x26,
-0x02,0x65,0x60,0x40,0x08,0x26,0x03,0x65,0x59,0x60,0x68,0x62,0x65,0x44,0xa2,0xdb,
-0x00,0x67,0x10,0x00,0xc9,0xf3,0x00,0x65,0x60,0x40,0x02,0x26,0x01,0x65,0x60,0x40,
-0x04,0x26,0x02,0x65,0x60,0x40,0x08,0x26,0x03,0x65,0x59,0x60,0x68,0x62,0x65,0x44,
-0xa2,0xdb,0x00,0x67,0x23,0x58,0xff,0xff,0x7f,0x60,0xc0,0x64,0x24,0x45,0xa4,0x80,
-0x7f,0x67,0x02,0x61,0x3a,0x02,0x59,0x60,0x56,0x62,0xa2,0xd3,0xff,0xff,0x01,0xa4,
-0xa2,0xdb,0xff,0x60,0xfe,0x64,0x32,0x45,0x24,0x92,0x02,0x61,0x41,0x56,0xc7,0xfe,
-0x30,0x60,0x7f,0x78,0xff,0xff,0x94,0xf1,0x20,0x44,0x64,0x40,0xff,0x26,0x24,0x00,
-0x7f,0xb4,0x40,0x40,0x00,0x64,0x40,0x5e,0x3c,0x60,0x64,0x62,0xa2,0xd3,0xff,0xff,
-0x00,0xa8,0x60,0x46,0x0f,0xf2,0x18,0x03,0x00,0xa8,0xff,0xff,0x15,0x03,0x0f,0x4e,
-0x46,0x45,0x3c,0x60,0x88,0x62,0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,
-0x5a,0xdb,0xff,0xff,0x2b,0xff,0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,
-0xa3,0xff,0xd1,0xfe,0x0e,0x4f,0xe0,0x00,0x00,0x67,0x23,0x58,0xff,0xff,0x00,0x61,
-0x00,0x7c,0x08,0x60,0x0a,0x64,0xa0,0xd9,0x00,0x67,0x23,0x58,0xff,0xff,0x25,0x44,
-0xa0,0xd1,0x08,0x60,0x0a,0x64,0xa0,0xd9,0x00,0x67,0x23,0x58,0xff,0xff,0x7f,0x60,
-0xc0,0x64,0x24,0x45,0xa4,0x80,0x02,0x61,0x25,0x02,0x25,0x45,0x12,0x60,0xfc,0x63,
-0x05,0x61,0xbd,0xd3,0xbd,0xd1,0xd4,0x80,0xbd,0xd3,0xcd,0x81,0x02,0x03,0x19,0x03,
-0xf8,0x00,0x40,0x4c,0x0f,0x4e,0x64,0x41,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,
-0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,
-0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,0x01,0x03,0x2c,0x58,0x0c,0x61,0x05,0x67,
-0x02,0x00,0x04,0x61,0x7f,0x67,0x23,0x58,0xff,0xff,0x03,0x4e,0x0c,0x60,0x6e,0x62,
-0xa2,0xd7,0x58,0x43,0xff,0xff,0x0e,0x43,0x41,0x47,0x7e,0x60,0xc0,0x64,0x24,0x45,
-0xa4,0x80,0x02,0x61,0x26,0x02,0x25,0x45,0xfc,0x2b,0x22,0x00,0x0e,0x60,0xd8,0x63,
-0x6a,0x61,0x24,0x44,0x01,0x27,0x11,0x00,0xbd,0xd3,0xa3,0xd1,0xd4,0x80,0xcd,0x81,
-0x08,0x24,0x64,0x58,0x08,0xa3,0xf8,0x02,0x15,0xf5,0x22,0xf2,0xff,0xff,0x00,0xa8,
-0x00,0x61,0x01,0x02,0x04,0x61,0x00,0x67,0x0d,0x00,0x27,0x40,0x04,0x3a,0xfb,0x00,
-0xbd,0xd3,0xbe,0xd1,0xd4,0x80,0xcd,0x81,0x08,0x24,0x64,0x58,0x08,0xa3,0xf5,0x02,
-0x04,0x61,0x7f,0x67,0x23,0x58,0xff,0xff,0x4b,0xd3,0x15,0xf5,0x60,0x41,0x22,0xf0,
-0xe9,0x85,0x64,0x44,0xff,0x22,0xdc,0x84,0xc4,0x84,0x22,0xfa,0x64,0x44,0xc2,0x82,
-0x00,0xa8,0xc2,0x84,0x08,0x24,0xd8,0x84,0xbf,0xd1,0xd8,0x85,0x64,0x43,0x58,0x4f,
-0x61,0x00,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x67,0x60,0x6a,0x62,0x01,0x64,
-0xa2,0xdb,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,0x80,0x64,0xb0,0x84,0xa2,0xdb,
-0xff,0xff,0xcf,0xfe,0x05,0x00,0x01,0x64,0x90,0xfb,0x01,0x67,0x85,0xfb,0xff,0xff,
-0x15,0xf5,0xff,0xff,0x22,0xf2,0xbf,0xd1,0xff,0xff,0x62,0x43,0xcc,0x84,0xe0,0x85,
-0x09,0x06,0xbf,0xd1,0x64,0x41,0xd5,0x80,0x64,0x43,0x01,0x06,0x65,0x41,0x48,0x65,
-0x58,0x4f,0x55,0x00,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x58,0x60,0x5a,0x63,
-0xa3,0xd3,0x15,0xf5,0x60,0x41,0xe8,0x84,0xdc,0x84,0x22,0xfa,0xfc,0x60,0x80,0x64,
-0x5a,0xda,0xda,0x85,0x04,0xa3,0x58,0x4f,0x25,0x00,0x00,0x67,0x00,0x61,0x23,0x58,
-0xff,0xff,0x15,0xf5,0x22,0xf2,0xbf,0xd1,0xff,0xff,0x62,0x43,0xcc,0x84,0xe0,0x81,
-0x15,0x06,0xbf,0xd1,0x64,0x45,0xd5,0x80,0x64,0x43,0xfc,0xa3,0x04,0x06,0x65,0x41,
-0xe9,0x84,0xdc,0x84,0x22,0xfa,0x44,0x65,0x04,0xa1,0x58,0x4f,0x28,0x00,0x58,0x60,
-0x5a,0x62,0xa2,0xd3,0xff,0xff,0xcc,0x84,0xe0,0x84,0xa2,0xdb,0x00,0x67,0x00,0x61,
-0x23,0x58,0xff,0xff,0x41,0x4d,0x00,0xa1,0x80,0x64,0x17,0x03,0x65,0x42,0xd4,0x85,
-0x2d,0x41,0x55,0x8d,0xff,0xff,0x02,0x04,0x65,0x41,0x02,0x00,0x00,0x64,0x40,0x4d,
-0xca,0x84,0xbd,0xd1,0xc9,0x81,0x58,0xd8,0xfc,0x02,0x2d,0x41,0x00,0xa1,0xd8,0x85,
-0x04,0x03,0x00,0xf4,0x7c,0x65,0x04,0x62,0xeb,0x00,0x2f,0x58,0xff,0xff,0x41,0x4d,
-0x01,0xf2,0x65,0x42,0x7f,0xb5,0x2d,0x41,0x00,0xa1,0x55,0x8d,0x0e,0x03,0x02,0x04,
-0x65,0x41,0x02,0x00,0x00,0x64,0x40,0x4d,0xca,0x84,0x58,0xd0,0xc9,0x81,0xbd,0xd9,
-0xfc,0x02,0x00,0xf4,0x01,0xf2,0x04,0x62,0xed,0x00,0x2f,0x58,0xff,0xff,0x66,0x44,
-0x93,0xfb,0x8a,0xf1,0x02,0x64,0xc0,0x84,0xe8,0x84,0x22,0xfa,0xf1,0x60,0x01,0x64,
-0x23,0xfa,0x5a,0x8d,0x89,0xf1,0x27,0x60,0xe0,0x63,0x44,0x4b,0x43,0x4c,0x2b,0x45,
-0xd7,0x80,0xbe,0xd1,0x0b,0x05,0x2d,0x45,0x64,0x43,0x44,0x61,0x35,0x60,0x58,0x4f,
-0x5d,0x78,0xff,0xff,0x45,0x4d,0x2c,0x43,0x04,0xa3,0xf0,0x00,0x93,0xf1,0x3c,0x60,
-0x88,0x62,0x3c,0x60,0x70,0x64,0xa2,0xdb,0x5a,0xd9,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0x08,0x65,0x45,0x55,0x3b,0xff,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,
-0x66,0x44,0x92,0xfb,0xc6,0xfe,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x66,0x44,
-0x93,0xfb,0x72,0xf1,0x01,0x60,0x00,0x64,0xc0,0x81,0x28,0x60,0xe2,0x63,0x00,0x64,
-0x40,0x4b,0xf1,0x60,0x02,0x64,0x23,0xfa,0xda,0x85,0xa3,0xd3,0xff,0xff,0xff,0xff,
-0x01,0x2a,0x0b,0x00,0x41,0x4c,0x10,0x61,0x35,0x60,0x58,0x4f,0x5d,0x78,0xff,0xff,
-0x2b,0x44,0xdc,0x84,0x40,0x4b,0x2c,0x41,0xf0,0xa3,0xcd,0x81,0x10,0xa3,0xed,0x02,
-0x93,0xf1,0xff,0xff,0x64,0x46,0x2b,0x44,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xdc,0x84,
-0x22,0xfa,0x3c,0x60,0x88,0x62,0x3c,0x60,0x70,0x64,0xa2,0xdb,0x5a,0xd9,0x0a,0x64,
-0x5a,0xdb,0xff,0xff,0x2b,0xff,0x08,0x65,0x45,0x55,0x3b,0xff,0x00,0x67,0x00,0x61,
-0x23,0x58,0xff,0xff,0x66,0x44,0x93,0xfb,0x3d,0x60,0x4c,0x64,0xa0,0xd1,0x02,0x64,
-0xc0,0x84,0xe8,0x84,0x22,0xfa,0xf1,0x60,0x04,0x64,0x23,0xfa,0xda,0x85,0x3d,0x60,
-0x4e,0x63,0x64,0x41,0x35,0x60,0x58,0x4f,0x5d,0x78,0xff,0xff,0x93,0xf1,0x3c,0x60,
-0x88,0x62,0x3c,0x60,0x70,0x64,0xa2,0xdb,0x5a,0xd9,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0x08,0x65,0x45,0x55,0x3b,0xff,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,
-0x25,0x44,0x1a,0xf1,0x1b,0xf1,0xd0,0x80,0xd0,0x80,0x0e,0x04,0x08,0x06,0x1c,0xf1,
-0x1d,0xf1,0xd0,0x80,0xd0,0x80,0x08,0x04,0x02,0x06,0x48,0xfe,0x05,0x00,0x25,0x46,
-0x01,0xf0,0x03,0x67,0xa0,0x85,0x94,0x80,0x2f,0x58,0xff,0xff,0x15,0xf5,0x00,0x60,
-0xf1,0x64,0x22,0xfa,0x25,0x44,0x23,0xfa,0x01,0x60,0xa8,0x64,0x40,0x4d,0x46,0x4c,
-0xfc,0x60,0x00,0x64,0x40,0x4b,0xfe,0x60,0x00,0x64,0x36,0x63,0x46,0x61,0xc8,0x84,
-0x2b,0x46,0x58,0xd0,0x2c,0x46,0x59,0xd8,0xfb,0x1f,0x2d,0x41,0x00,0xb9,0x84,0xa1,
-0x08,0x03,0x04,0x24,0x00,0x61,0x41,0x4d,0x00,0xf4,0x02,0x61,0x7a,0x63,0x46,0x4c,
-0xef,0x00,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0xfc,0x60,0x00,0x64,0x40,0x4b,
-0x4b,0xd3,0x15,0xf5,0x60,0x41,0xd8,0x84,0xe8,0x84,0x22,0xfa,0x25,0x44,0x23,0xfa,
-0xbf,0xd3,0x66,0x45,0x48,0x63,0xc8,0x84,0x2b,0x46,0x58,0xd0,0x65,0x46,0xc9,0x81,
-0xbd,0xd8,0xfa,0x02,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0xfc,0x60,0x00,0x64,
-0x40,0x4b,0x4b,0xd3,0x15,0xf5,0x60,0x41,0x22,0xf0,0xe9,0x85,0x64,0x44,0xff,0x22,
-0xdc,0x84,0xc4,0x84,0x22,0xfa,0x64,0x44,0xc2,0x82,0x00,0xa8,0xc2,0x84,0x08,0x24,
-0xd8,0x84,0xbf,0xd1,0xc9,0x83,0x64,0x41,0xc9,0x81,0x66,0x45,0x2b,0x46,0x59,0xd0,
-0x65,0x46,0x58,0xd8,0xfb,0x1f,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,
-0x02,0x64,0x22,0xfa,0xfc,0xa3,0xa3,0xd3,0x25,0x43,0xa0,0xd3,0x23,0xfc,0xdc,0x84,
-0x24,0xfa,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x02,0x64,0x22,0xfa,
-0x25,0x44,0x23,0xfa,0x65,0xf3,0xff,0xff,0x02,0xb4,0x01,0x64,0x08,0x24,0x02,0x64,
-0x24,0xfa,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x02,0x64,0x22,0xfa,
-0x25,0x44,0x23,0xfa,0x50,0xf3,0x24,0xfa,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,
-0x15,0xf5,0x04,0x64,0x22,0xfa,0x25,0x44,0x23,0xfa,0x58,0xf3,0x24,0xfa,0xff,0xff,
-0x59,0xf3,0x5a,0xf1,0x80,0x65,0xc4,0x87,0x00,0x7f,0x25,0xfa,0x64,0x44,0xc4,0x87,
-0x00,0x7f,0x26,0xfa,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x24,0xf0,
-0x17,0x60,0xc6,0x65,0x22,0xf2,0xa5,0xd9,0x02,0xa8,0x64,0x41,0x0f,0x02,0x00,0xb9,
-0xff,0xff,0x0c,0x03,0x16,0x60,0xbc,0x62,0xa2,0xd9,0x7f,0xf3,0xff,0xff,0xd0,0x80,
-0xff,0xff,0x04,0x02,0x01,0x63,0x08,0x60,0x2a,0x64,0xa0,0xdd,0x00,0x67,0x00,0x61,
-0x23,0x58,0xff,0xff,0x15,0xf5,0x20,0x63,0x17,0x60,0x0e,0x61,0x46,0x64,0x58,0xd0,
-0x59,0xd9,0xfd,0x1f,0x24,0xf0,0x20,0x64,0xd0,0x81,0x17,0x60,0x12,0x64,0x0d,0x06,
-0xc0,0x83,0x01,0x2a,0x06,0x00,0xcf,0x83,0xa3,0xd3,0xcd,0x81,0x00,0x7f,0xbd,0xdb,
-0x04,0x03,0x00,0x64,0xc9,0x81,0xbd,0xdb,0xfd,0x02,0x00,0x67,0x00,0x61,0x23,0x58,
-0xff,0xff,0x15,0xf5,0x22,0xf2,0x24,0xf0,0x02,0xa8,0x59,0x60,0x1e,0x62,0x09,0x02,
-0xa2,0xd9,0x64,0x41,0x32,0x44,0x02,0xb5,0x00,0xb9,0xd4,0x84,0x08,0x28,0x02,0xbc,
-0x40,0x52,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x22,0xf2,0x24,0xf0,
-0x02,0xa8,0x01,0x60,0x92,0x65,0x37,0x02,0xa5,0xd9,0x17,0x60,0xd4,0x62,0x00,0x61,
-0x00,0x64,0x01,0x65,0x64,0x40,0x01,0x2a,0x02,0x00,0x01,0xa1,0x02,0x7e,0x64,0x40,
-0x02,0x2a,0x09,0x00,0x01,0xa1,0xa5,0x80,0xff,0xff,0x02,0x03,0x04,0x7e,0x03,0x00,
-0x04,0x7f,0xa2,0xdb,0x02,0xa2,0x64,0x40,0x04,0x2a,0x09,0x00,0x01,0xa1,0xa5,0x80,
-0xff,0xff,0x02,0x03,0x0b,0x7e,0x03,0x00,0x0b,0x7f,0xa2,0xdb,0x02,0xa2,0x64,0x40,
-0x08,0x2a,0x08,0x00,0x01,0xa1,0xa5,0x80,0xff,0xff,0x02,0x03,0x16,0x7e,0x02,0x00,
-0x16,0x7f,0xa2,0xdb,0xa5,0x80,0xff,0xff,0x02,0x03,0x00,0x7f,0xa2,0xdb,0x17,0x60,
-0xd2,0x62,0x61,0x43,0xa2,0xdd,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x59,0x60,
-0xa6,0x63,0x00,0x60,0xd5,0x61,0x00,0x64,0xcd,0x81,0xbd,0xdb,0xfd,0x02,0x00,0x67,
-0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x24,0xf0,0x3d,0x60,0x20,0x62,0xa2,0xd9,
-0x17,0x60,0x06,0x62,0xa2,0xd9,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,
-0x24,0xf0,0x66,0x60,0xb4,0x65,0x03,0x60,0xe8,0x64,0x64,0x40,0x00,0x36,0x03,0x00,
-0xa5,0xdb,0x01,0x64,0x40,0x5a,0x17,0x60,0x74,0x64,0xa0,0xd9,0x00,0x67,0x00,0x61,
-0x23,0x58,0xff,0xff,0x15,0xf5,0x24,0xf2,0x99,0xff,0x40,0x5b,0x98,0xff,0x00,0x67,
-0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x24,0xf2,0x99,0xff,0x40,0x5a,0x98,0xff,
-0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x01,0x65,0x67,0x60,0x8e,0x61,0x0b,0x00,
-0x02,0x65,0x67,0x60,0x9c,0x61,0x07,0x00,0x04,0x65,0x67,0x60,0xaa,0x61,0x03,0x00,
-0x08,0x65,0x67,0x60,0xb8,0x61,0x41,0xf3,0xff,0xff,0xb4,0x84,0x41,0xfb,0x15,0xf5,
-0x46,0x64,0x00,0x60,0x0c,0x63,0x58,0xd0,0x59,0xd9,0xfd,0x1f,0x22,0xf2,0xff,0xff,
-0xf8,0xa0,0x0f,0x64,0x01,0x03,0x07,0x64,0x45,0xfb,0x67,0x44,0xd9,0xfb,0xda,0xfb,
-0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x02,0x64,0x22,0xfa,0x25,0x44,
-0x23,0xfa,0x43,0xf3,0x83,0xb4,0x24,0xfa,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,
-0xbc,0xf3,0xff,0xff,0x00,0xa4,0xff,0xff,0x16,0x03,0x15,0xf5,0x43,0xf3,0x24,0xf2,
-0x60,0x41,0x83,0xb5,0xff,0x60,0x7c,0x7c,0xa1,0x81,0xb5,0x84,0x43,0xfb,0xff,0xff,
-0x01,0x2a,0x09,0x00,0x1e,0x60,0xa0,0x63,0x09,0x60,0x2b,0x64,0xbd,0xdb,0xbd,0xdb,
-0xbd,0xdb,0xff,0xff,0xbd,0xdb,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,
-0x22,0xf2,0x24,0xf0,0x02,0xa8,0xff,0xff,0x05,0x02,0x00,0x64,0x64,0x40,0x00,0x3a,
-0x03,0x64,0xd5,0xfb,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x66,0x60,0xd8,0x62,
-0x01,0x64,0xa2,0xdb,0x17,0x60,0x06,0x62,0x05,0x64,0xa2,0xdb,0x01,0x60,0x7a,0x63,
-0x66,0x60,0xda,0x65,0x03,0x61,0xbd,0xd1,0x00,0x7f,0x64,0x5e,0xa5,0xdb,0xda,0x85,
-0x64,0x47,0x00,0x7f,0xa5,0xdb,0xcd,0x81,0xda,0x85,0xf5,0x02,0x00,0x67,0x00,0x61,
-0x23,0x58,0xff,0xff,0x15,0xf5,0x22,0xf2,0x24,0xf0,0x02,0xa8,0x1f,0xf3,0x14,0x02,
-0x60,0x40,0x10,0x2a,0x11,0x00,0x17,0x60,0x7e,0x62,0xa2,0xd9,0x00,0x64,0x64,0x40,
-0x01,0x26,0x20,0x64,0xc5,0xfb,0x16,0x60,0x42,0x62,0xa2,0xd3,0xff,0xff,0x03,0xa8,
-0xff,0xff,0x02,0x02,0xc5,0xf3,0x47,0xfb,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,
-0x15,0xf5,0x19,0x60,0x2a,0x65,0x26,0xf2,0x25,0xf0,0x60,0x41,0x64,0x43,0xeb,0x83,
-0x00,0x7f,0xe0,0x84,0x44,0xd1,0x61,0x47,0x93,0x83,0x00,0x7f,0xe0,0x84,0x44,0xd1,
-0xeb,0x83,0x93,0x83,0x0f,0x60,0xf0,0x65,0xa7,0x85,0x02,0x61,0x44,0x60,0x4e,0x63,
-0xc7,0x83,0xa3,0xd3,0xff,0xff,0x60,0x40,0x80,0x2b,0x13,0x00,0x65,0x44,0xff,0xa1,
-0x08,0xa5,0xf4,0x02,0x00,0x65,0x7e,0x61,0x54,0x60,0x4e,0x63,0xc7,0x83,0xa3,0xd3,
-0xff,0xff,0x60,0x40,0x80,0x2b,0x05,0x00,0x65,0x44,0xff,0xa1,0x08,0xa5,0xf4,0x02,
-0x2e,0x00,0x2d,0xf0,0xff,0xff,0x64,0x47,0x00,0x7f,0xe0,0x84,0x60,0x45,0xe0,0x84,
-0xe0,0x81,0xc4,0x85,0xc5,0x85,0x80,0x67,0xb4,0x84,0xbd,0xdb,0x24,0xf2,0xbd,0xdb,
-0xff,0xff,0x25,0xf2,0xbd,0xdb,0x26,0xf0,0xff,0xff,0xa3,0xd9,0x3d,0x60,0x4e,0x63,
-0x27,0xf2,0xc7,0x83,0xbd,0xdb,0x28,0xf2,0xbd,0xdb,0xff,0xff,0x29,0xf2,0xbd,0xdb,
-0x2a,0xf2,0xff,0xff,0xbd,0xdb,0x2b,0xf2,0xbd,0xdb,0xff,0xff,0x2c,0xf2,0xbd,0xdb,
-0x2d,0xf2,0xff,0xff,0xbd,0xdb,0x41,0xf3,0x04,0x65,0xb4,0x84,0x41,0xfb,0x00,0x67,
-0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,0x19,0x60,0x2a,0x65,0x26,0xf2,0x25,0xf0,
-0x60,0x41,0x64,0x43,0xeb,0x83,0x00,0x7f,0xe0,0x84,0x44,0xd1,0x61,0x47,0x93,0x83,
-0x00,0x7f,0xe0,0x84,0x44,0xd1,0xeb,0x83,0x93,0x83,0x0f,0x60,0xf0,0x65,0xa7,0x85,
-0x02,0x61,0x44,0x60,0x4e,0x63,0xc7,0x83,0xa3,0xd3,0x02,0xa3,0x60,0x40,0x80,0x2b,
-0x0d,0x00,0x24,0xf0,0xbd,0xd3,0x50,0xfe,0x25,0xf0,0xd0,0x80,0xbd,0xd3,0x26,0xf0,
-0xd0,0x80,0xa3,0xd3,0xff,0xff,0xd0,0x80,0xff,0xff,0x20,0x01,0x65,0x44,0xff,0xa1,
-0x08,0xa5,0xe7,0x02,0x00,0x65,0x7e,0x61,0x54,0x60,0x4e,0x63,0xc7,0x83,0xa3,0xd3,
-0x02,0xa3,0x60,0x40,0x80,0x2b,0x0d,0x00,0x24,0xf0,0xbd,0xd3,0x50,0xfe,0x25,0xf0,
-0xd0,0x80,0xbd,0xd3,0x26,0xf0,0xd0,0x80,0xa3,0xd3,0xff,0xff,0xd0,0x80,0xff,0xff,
-0x05,0x01,0x65,0x44,0xff,0xa1,0x08,0xa5,0xe7,0x02,0x06,0x00,0xfa,0xa3,0xa3,0xd3,
-0xff,0xff,0xe0,0x84,0xe8,0x84,0xa3,0xdb,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,
-0x66,0x44,0x93,0xfb,0x00,0x60,0x92,0x64,0x22,0xfa,0xf1,0x60,0x03,0x64,0x23,0xfa,
-0x48,0x65,0x38,0x64,0x40,0x4c,0x1d,0x60,0x90,0x63,0xbd,0xd3,0xff,0xff,0x00,0xa0,
-0xbd,0xd1,0x12,0x03,0x43,0x48,0x60,0x43,0x64,0x41,0xbd,0xd3,0xa5,0xda,0x2c,0x44,
-0xc8,0x84,0x40,0x4c,0x04,0x02,0x00,0xf4,0x78,0x64,0x40,0x4c,0x02,0x62,0xcd,0x81,
-0xda,0x85,0xf3,0x02,0x28,0x43,0xe9,0x00,0x93,0xf1,0x3c,0x60,0x88,0x62,0x3c,0x60,
-0x70,0x64,0xa2,0xdb,0x5a,0xd9,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x08,0x65,
-0x45,0x55,0x3b,0xff,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x1f,0xf3,0x15,0xf5,
-0x24,0xf0,0x60,0x40,0x20,0x2a,0x13,0x00,0x64,0x47,0x00,0x7f,0xfe,0xa4,0x82,0xa0,
-0x60,0x45,0x0d,0x05,0x14,0x60,0x3e,0x62,0x46,0xd9,0x67,0x60,0x72,0x62,0xa2,0xd3,
-0x64,0x40,0x12,0x37,0x01,0xbc,0x64,0x40,0x14,0x37,0x02,0xbc,0xa2,0xdb,0x00,0x67,
-0x00,0x61,0x23,0x58,0xff,0xff,0x28,0x60,0xda,0x63,0x00,0x64,0xbd,0xdb,0xbd,0xdb,
-0xff,0xff,0xbd,0xdb,0xbd,0xdb,0x01,0x64,0x23,0xfb,0xff,0xff,0x1a,0xff,0x23,0xf3,
-0xff,0xff,0x00,0xa0,0xff,0xff,0xfb,0x02,0x15,0xf5,0x05,0x64,0x22,0xfa,0x25,0x44,
-0x23,0xfa,0xd4,0xf3,0x24,0xfa,0xff,0xff,0xa1,0xf3,0x25,0xfa,0xa2,0xf3,0xff,0xff,
-0x26,0xfa,0xa3,0xfb,0x27,0xfa,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x15,0xf5,
-0x22,0xf2,0xff,0xff,0xfb,0xa0,0x28,0x60,0xda,0x63,0x0b,0x02,0x24,0xf2,0xbd,0xdb,
-0x25,0xf2,0xff,0xff,0xbd,0xdb,0xbd,0xdb,0xbd,0xdb,0x01,0x64,0x23,0xfb,0xff,0xff,
-0x1a,0xff,0x00,0x67,0x00,0x61,0x23,0x58,0xff,0xff,0x38,0xf2,0xff,0xff,0xff,0xa0,
-0x02,0x64,0x03,0x02,0x38,0xfa,0x60,0x47,0x3f,0xfa,0x28,0xf2,0xff,0xff,0x08,0xb0,
-0x60,0x47,0x07,0xb5,0x20,0x02,0xa6,0xf3,0x71,0x02,0x02,0xa8,0x01,0xa8,0x1c,0x02,
-0x39,0xf0,0x2b,0xf8,0x3a,0xf0,0xff,0xff,0x2c,0xf8,0x3b,0xf0,0x2d,0xf8,0xff,0xff,
-0x81,0xf1,0x2e,0xf8,0x82,0xf1,0xff,0xff,0x2f,0xf8,0x83,0xf1,0x30,0xf8,0xff,0xff,
-0x3c,0xf0,0x31,0xf8,0x3d,0xf0,0xff,0xff,0x32,0xf8,0x3e,0xf0,0x85,0xf3,0xff,0xff,
-0x33,0xf8,0x08,0xbc,0x29,0xfa,0x52,0x00,0x50,0xfe,0x3c,0xf0,0xbd,0xf3,0x2e,0xf8,
-0xd0,0x80,0x3d,0xf0,0xbe,0xf3,0x2f,0xf8,0xd0,0x80,0x3e,0xf0,0xbf,0xf3,0x30,0xf8,
-0xd0,0x80,0x85,0xf3,0x07,0x01,0x90,0xf3,0x60,0x41,0x04,0xb0,0x61,0x44,0x02,0x02,
-0x42,0xfe,0x3d,0x00,0x4a,0xf1,0x43,0xf1,0x64,0x40,0x01,0x2a,0x0f,0x00,0x28,0xf0,
-0x64,0x40,0x01,0x2a,0x0b,0x00,0x64,0x40,0x81,0x26,0x08,0x00,0x38,0xf2,0x60,0x5c,
-0x00,0xa8,0x64,0x44,0x03,0x03,0x60,0x47,0x40,0xbc,0x60,0x47,0x08,0xbc,0x29,0xfa,
-0x90,0xf3,0xff,0xff,0x04,0xb0,0x39,0xf0,0x21,0x02,0x02,0xb0,0x39,0xf0,0x0f,0x03,
-0x2b,0xf8,0x3a,0xf0,0xff,0xff,0x2c,0xf8,0x3b,0xf0,0x2d,0xf8,0xff,0xff,0x81,0xf1,
-0x31,0xf8,0x82,0xf1,0xff,0xff,0x32,0xf8,0x83,0xf1,0x33,0xf8,0x0f,0x00,0x31,0xf8,
-0x3a,0xf0,0xff,0xff,0x32,0xf8,0x3b,0xf0,0x33,0xf8,0xff,0xff,0x81,0xf1,0x2b,0xf8,
-0x82,0xf1,0xff,0xff,0x2c,0xf8,0x83,0xf1,0x2d,0xf8,0x00,0x00,0x02,0xfe,0x2f,0x58,
-0xff,0xff,0x00,0x64,0x15,0xfa,0x16,0xfa,0x1c,0xfa,0xff,0xff,0x07,0xfa,0x19,0xfa,
-0x1e,0x60,0xa0,0x65,0x95,0xf1,0x51,0xf3,0x64,0x40,0x01,0x2a,0x02,0x00,0x13,0xf2,
-0xff,0xff,0xe0,0x84,0x44,0xd1,0xca,0xf9,0x1e,0x63,0x29,0xf0,0x73,0x60,0xff,0x64,
-0xa0,0x84,0x03,0x2b,0x18,0x63,0x29,0xfa,0x04,0xa3,0x64,0x40,0x40,0x27,0x08,0xa3,
-0x43,0x4b,0x21,0xfc,0x56,0x61,0x64,0x40,0x01,0x27,0x62,0x61,0x38,0xf0,0xa1,0xd2,
-0x44,0x4d,0x60,0x40,0x01,0x26,0x22,0x00,0xca,0xf1,0xc3,0x81,0xd1,0x80,0x63,0x45,
-0x20,0x06,0x64,0x43,0xd7,0x85,0x45,0x4c,0xc8,0xf1,0x0f,0xf2,0xd3,0x80,0x01,0x65,
-0x01,0x05,0x00,0x65,0xb4,0x84,0x0f,0xfa,0x00,0x63,0x2d,0x44,0x2c,0x45,0x60,0x41,
-0xd4,0x84,0xdf,0x83,0xfc,0x07,0x14,0xfc,0x61,0x44,0x17,0xfa,0x29,0xf0,0x04,0x64,
-0x60,0x47,0xb0,0x84,0x29,0xfa,0x2c,0x43,0x16,0xfc,0x0f,0x00,0x2d,0x44,0x17,0xfa,
-0x0a,0x00,0x2d,0x44,0x17,0xfa,0x2b,0x45,0xc8,0xf1,0xc4,0x81,0xd1,0x80,0x0f,0xf2,
-0x01,0x04,0x01,0xbc,0x0f,0xfa,0x01,0x64,0x14,0xfa,0x2e,0x58,0xff,0xff,0xcb,0xf3,
-0x2b,0xf2,0xfd,0xa0,0xff,0xff,0x4c,0x02,0x60,0x40,0x01,0x26,0x39,0x00,0x2b,0xf2,
-0xff,0xff,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,0xe1,0x81,0x5a,0xd2,
-0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,0xe1,0x81,
-0x5a,0xd2,0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,
-0xe1,0x81,0xf0,0x84,0x0e,0x4c,0x3a,0x60,0x58,0x4e,0xc5,0x78,0xff,0xff,0x0c,0x4e,
-0x14,0x03,0x19,0xfc,0x0a,0xa3,0x3c,0x64,0xa3,0xdb,0xfe,0xa3,0xa3,0xd3,0xff,0xff,
-0xff,0xff,0x01,0x26,0x00,0x7f,0x02,0x26,0x01,0x7f,0x04,0x26,0x02,0x7f,0x08,0x26,
-0x03,0x7f,0x1d,0xfa,0x00,0x64,0x0d,0xfa,0x13,0x00,0x19,0xfc,0x50,0xf3,0xf1,0x00,
-0x17,0x60,0xdc,0x64,0xa0,0xd1,0x39,0x60,0xe2,0x64,0x19,0xfa,0x64,0x44,0x08,0x26,
-0x03,0x7f,0x04,0x26,0x02,0x7f,0x02,0x26,0x01,0x7f,0x01,0x26,0x00,0x7f,0xe9,0x00,
-0x2e,0x58,0xff,0xff,0x28,0x60,0xe2,0x65,0x00,0x7f,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0x44,0xd3,0x62,0x43,0x60,0x40,0x01,0x2a,0x10,0x00,0x02,0xa3,0x2b,0xf2,
-0x50,0xfe,0xbd,0xd1,0x2c,0xf2,0xd0,0x80,0xbd,0xd1,0x2d,0xf2,0xd0,0x80,0xbd,0xd1,
-0xff,0xff,0xd0,0x80,0xff,0xff,0x02,0x02,0xf8,0xa3,0x1e,0x00,0x72,0xf1,0x38,0x60,
-0xe2,0x63,0x64,0x41,0xff,0x22,0x17,0x00,0xbd,0xd1,0x2b,0xf2,0x50,0xfe,0x64,0x40,
-0x01,0x26,0x04,0x00,0xcd,0x81,0x0e,0xa3,0xf7,0x02,0x0d,0x00,0xbd,0xd1,0x2c,0xf2,
-0xd0,0x80,0xbd,0xd1,0x2d,0xf2,0xd0,0x80,0xbd,0xd1,0xff,0xff,0xd0,0x80,0xcd,0x81,
-0xe3,0x01,0x08,0xa3,0xe9,0x02,0x00,0x63,0x00,0xbb,0x2e,0x58,0xff,0xff,0x0f,0xf3,
-0x2c,0x65,0x60,0x47,0xff,0xb4,0xd4,0x80,0xff,0xff,0x04,0x28,0x06,0x00,0xe0,0x85,
-0x15,0x60,0xa0,0x64,0x44,0xd7,0xff,0xff,0xff,0xff,0xff,0x67,0x23,0x58,0xff,0xff,
-0x3b,0x60,0x2d,0x64,0x97,0xfb,0xff,0xff,0x2d,0xff,0x30,0x60,0x7f,0x78,0xff,0xff,
-0x10,0xf3,0x7f,0xfb,0x02,0x7f,0x99,0xfb,0x02,0x60,0xee,0x64,0x98,0xfb,0x07,0x64,
-0x9a,0xfb,0x3b,0x60,0x2d,0x64,0x97,0xfb,0xdf,0xfe,0x00,0x64,0x19,0xff,0x30,0x60,
-0x7f,0x78,0xff,0xff,0x00,0x67,0x23,0x58,0xff,0xff,0x68,0x60,0xdc,0x61,0x11,0xf3,
-0xff,0xff,0xa1,0xdb,0x68,0x60,0xd2,0x61,0x10,0xf3,0xff,0xff,0xa1,0xdb,0x68,0x60,
-0xd4,0x61,0xff,0xff,0x02,0x36,0x06,0x00,0x03,0x36,0x06,0x00,0x04,0x36,0x13,0x00,
-0x00,0x64,0x16,0x00,0x00,0x64,0x14,0x00,0x16,0x60,0xb6,0x63,0xbd,0xd3,0x81,0xfb,
-0xbd,0xd3,0xff,0xff,0x82,0xfb,0xa3,0xd3,0x83,0xfb,0x68,0x60,0xda,0x62,0xff,0x64,
-0xa2,0xdb,0x01,0x64,0x05,0x00,0x68,0x60,0xda,0x62,0xff,0x64,0xa2,0xdb,0x01,0x64,
-0xa1,0xdb,0x00,0x60,0x01,0x64,0x32,0x45,0x34,0x92,0x00,0x67,0x23,0x58,0xff,0xff,
-0x00,0x60,0x02,0xe8,0x3c,0x60,0x6b,0x63,0x0e,0x60,0xac,0x64,0xa0,0xdd,0xff,0xff,
-0x62,0xff,0xff,0xff,0x1a,0xff,0x00,0x67,0x23,0x58,0xff,0xff,0x99,0xff,0x3e,0x44,
-0xfc,0xb4,0x00,0x7f,0x40,0x5e,0x98,0xff,0xbc,0xff,0x0d,0x63,0x58,0x4f,0xf4,0x76,
-0x7e,0x00,0x00,0x10,0x46,0x00,0x99,0xff,0x3d,0x44,0xf7,0xb4,0x40,0x5d,0x98,0xff,
-0x0d,0x63,0x58,0x4f,0x3e,0x00,0x99,0xff,0x3e,0x44,0x77,0xb4,0x08,0xbc,0x00,0x7f,
-0x40,0x5e,0x98,0xff,0x0d,0x63,0x58,0x4f,0x34,0x00,0x99,0xff,0x3c,0x44,0x10,0xbc,
-0x00,0x7f,0x40,0x5c,0x98,0xff,0x99,0xff,0x3d,0x44,0xef,0xb4,0x40,0x5d,0x98,0xff,
-0xb5,0xff,0xff,0xff,0x6c,0x40,0x11,0x60,0x03,0xe8,0x01,0x60,0x03,0xe8,0xff,0xff,
-0xff,0xff,0xff,0xff,0x0e,0x60,0x00,0x6b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0x46,0xff,0x47,0xff,0xf9,0x60,0xfe,0x64,0x32,0x45,0x24,0x92,0x99,0xff,0x35,0x47,
-0xff,0xb4,0x60,0x5c,0x08,0x60,0x0e,0x64,0xa0,0xd9,0x64,0x44,0x98,0xff,0x11,0x60,
-0x8e,0x63,0x0e,0x60,0xac,0x64,0xa0,0xdd,0xff,0xff,0x62,0xff,0x00,0x67,0x23,0x58,
-0xff,0xff,0xff,0xff,0xfe,0x1f,0x2f,0x58,0xff,0xff,0x99,0xff,0x1e,0x65,0x3d,0x44,
-0xe1,0x81,0xf9,0xb4,0x02,0x24,0x04,0xbc,0x00,0x7f,0x40,0x5d,0x02,0x63,0xff,0xff,
-0xfe,0x1f,0x02,0xbc,0x40,0x5d,0xf4,0x1f,0x98,0xff,0x99,0xff,0x10,0xf3,0xff,0xff,
-0x60,0x47,0x60,0x45,0x60,0x47,0xa4,0x81,0x65,0x44,0xff,0xad,0x04,0x60,0xff,0xe5,
-0x3c,0x44,0x04,0x60,0xff,0xe5,0xa4,0x85,0xb5,0x84,0x00,0x7f,0x40,0x5c,0x04,0x60,
-0xff,0xe5,0x3c,0x44,0x04,0x60,0xff,0xe5,0x00,0x7f,0x60,0x5c,0x08,0x60,0x0a,0x64,
-0xa0,0xd9,0x11,0xf3,0xff,0xff,0x60,0x47,0x60,0x45,0x60,0x47,0xa4,0x81,0x65,0x44,
-0xff,0xad,0x3d,0x44,0xa4,0x85,0xb5,0x84,0x00,0x7f,0x40,0x5d,0x3d,0x44,0x00,0x7f,
-0x60,0x5c,0x08,0x60,0x0c,0x64,0xa0,0xd9,0x12,0xf3,0xff,0xff,0x60,0x47,0x60,0x45,
-0x60,0x47,0xa4,0x81,0x65,0x44,0xff,0xad,0x04,0x60,0xff,0xe5,0x3e,0x44,0x04,0x60,
-0xff,0xe5,0xa4,0x85,0xb5,0x84,0x00,0x7f,0x40,0x5e,0x00,0x6b,0x04,0x60,0xff,0xe5,
-0x3e,0x44,0x04,0x60,0xff,0xe5,0x00,0x7f,0x60,0x5c,0x08,0x60,0x0e,0x64,0xa0,0xd9,
-0x98,0xff,0x00,0x67,0x23,0x58,0xff,0xff,0x10,0xf1,0x3c,0x60,0xa8,0x62,0xa2,0xd9,
-0xca,0x82,0x22,0x64,0xa2,0xdb,0x3b,0x60,0x13,0x78,0xff,0xff,0x10,0xf1,0xff,0xff,
-0x7f,0xf9,0x3c,0x60,0xa6,0x62,0x08,0x64,0xa2,0xdb,0x3b,0x60,0x13,0x78,0xff,0xff,
-0x10,0xf1,0x3c,0x60,0xa8,0x62,0xa2,0xd9,0xca,0x82,0x1e,0x64,0xa2,0xdb,0x3b,0x60,
-0x13,0x78,0xff,0xff,0x3c,0x60,0xa6,0x62,0x02,0x64,0xa2,0xdb,0x3b,0x60,0x13,0x78,
-0xff,0xff,0x01,0x65,0x01,0x00,0x00,0x65,0x10,0xf1,0x15,0x60,0x46,0x63,0xbd,0xd9,
-0x11,0xf1,0xbd,0xd9,0xff,0xff,0x12,0xf1,0xa3,0xd9,0x65,0x40,0x01,0x2a,0x06,0x00,
-0x00,0x64,0x10,0xfb,0xff,0xff,0x3b,0x60,0x1b,0x78,0xff,0xff,0x00,0x67,0x23,0x58,
-0xff,0xff,0x10,0xf1,0x67,0x60,0x50,0x62,0xa2,0xd9,0x00,0x67,0x23,0x58,0xff,0xff,
-0x00,0x67,0x23,0x58,0xff,0xff,0x08,0xe1,0xa1,0xff,0xff,0xff,0x43,0xff,0x01,0xe1,
-0x99,0xff,0x3c,0x44,0x7f,0xb4,0x10,0xbc,0x40,0x5c,0x98,0xff,0x99,0xff,0x3d,0x44,
-0xef,0xb4,0x40,0x5d,0x98,0xff,0x0d,0x63,0x58,0x4f,0x76,0x00,0x99,0xff,0x3e,0x44,
-0x77,0xb4,0x80,0xbc,0x00,0x7f,0x40,0x5e,0x98,0xff,0x0d,0x63,0x58,0x4f,0x6c,0x00,
-0xff,0x6d,0x99,0xff,0x3e,0x44,0xfc,0xb4,0x01,0xbc,0x00,0x7f,0x40,0x5e,0x01,0x60,
-0x00,0x6b,0x98,0xff,0x0d,0x63,0x58,0x4f,0x5f,0x00,0x99,0xff,0x3d,0x44,0xf7,0xb4,
-0x40,0x5d,0x98,0xff,0x99,0xff,0x3d,0x44,0x08,0xbc,0x00,0x7f,0x40,0x5d,0x98,0xff,
-0x10,0xf3,0xff,0xff,0x60,0x5c,0x99,0xff,0x07,0x60,0x00,0xe8,0x98,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x80,0xe8,0xff,0xff,0xff,0xff,
-0xff,0xff,0x64,0x4d,0xa1,0xff,0x64,0x47,0x60,0x4d,0xff,0xff,0xff,0xff,0xa1,0xff,
-0xbb,0xff,0x64,0x44,0xa1,0xff,0x60,0x4d,0xa1,0xff,0x60,0x47,0x60,0x4d,0x64,0x44,
-0xa1,0xff,0x60,0x4d,0xa1,0xff,0x60,0x47,0x60,0x4d,0x64,0x44,0xa1,0xff,0x60,0x4d,
-0xa1,0xff,0x60,0x47,0x60,0x4d,0x11,0xf3,0xff,0xff,0x00,0xa8,0x60,0x43,0x05,0x02,
-0x64,0x44,0xa1,0xff,0xff,0xff,0x60,0x4c,0xfc,0x00,0x63,0x46,0x43,0x4f,0x3f,0xf2,
-0xff,0xff,0x60,0x47,0x60,0x5c,0xff,0x65,0x2f,0x46,0x64,0x43,0x00,0xf4,0x01,0xf2,
-0x04,0x62,0xa4,0x81,0xe2,0xd2,0xff,0xff,0xa1,0xff,0xda,0x82,0xc9,0x81,0x60,0x4c,
-0xf9,0x1c,0xf4,0x1d,0xf1,0x1e,0x02,0x02,0x00,0xf4,0x04,0x62,0xa2,0xd2,0xff,0xff,
-0xa1,0xff,0xff,0xff,0x60,0x4d,0xe8,0x00,0xff,0xff,0xfe,0x1f,0x2f,0x58,0xff,0xff,
-0x6a,0x60,0x0c,0x78,0xff,0xff,0x1e,0x60,0xa8,0x62,0xa2,0xd1,0x80,0x60,0x00,0x64,
-0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x2b,0x00,0x74,0xf3,0x60,0xf1,0x00,0xa0,0xb0,0x84,
-0x0c,0x03,0x60,0xfb,0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x00,0x60,0x20,0x64,0xb0,0x84,
-0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x00,0x64,0x74,0xfb,0x75,0xf3,0xff,0xff,0x00,0xa0,
-0x00,0x64,0x15,0x03,0x75,0xfb,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,0x02,0x64,
-0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x0a,0x00,0xab,0xfe,0xff,0xff,0xd0,0x05,
-0xaa,0xfe,0xff,0xff,0xd0,0x05,0xa9,0xfe,0xff,0xff,0xd6,0x05,0xff,0xff,0xa1,0xff,
-0xff,0xff,0xbd,0x3f,0x0e,0x57,0x67,0x60,0xca,0x62,0xa2,0xd3,0xff,0xff,0xff,0xff,
-0x01,0x26,0x0c,0x65,0x45,0x48,0x0f,0x4e,0x00,0x60,0x06,0x61,0x41,0x4d,0x40,0xa1,
-0xa2,0xff,0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,
-0x19,0x60,0x58,0x4f,0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,0x15,0x03,0x02,0x64,
-0x22,0xfa,0xf2,0x60,0x00,0x64,0x5a,0xda,0x28,0x44,0x5a,0xda,0x08,0x65,0x3c,0x60,
-0x82,0x62,0x3c,0x60,0x70,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0x45,0x54,0x3b,0xff,0x37,0x58,0xff,0xff,0x92,0xf3,0x15,0x61,
-0x00,0xa8,0x60,0x46,0x15,0x02,0x0f,0x4e,0x00,0x60,0x2e,0x61,0x41,0x4d,0x40,0xa1,
-0xa2,0xff,0x19,0x60,0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,
-0x19,0x60,0x58,0x4f,0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,0x21,0x03,0x15,0x61,
-0x00,0x64,0x92,0xfb,0x58,0x60,0x2e,0x63,0x16,0x64,0x22,0xfa,0xf1,0x60,0x00,0x64,
-0x23,0xfa,0x48,0x65,0x00,0x64,0xa3,0xd1,0xbd,0xdb,0xa5,0xd8,0xcd,0x81,0xda,0x85,
-0xfa,0x02,0x3c,0x60,0x82,0x62,0x3c,0x60,0x70,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,
-0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x08,0x65,0x45,0x54,0x3b,0xff,0xa6,0xfe,
-0x8e,0x00,0xa6,0xfe,0xff,0xff,0xc2,0x05,0xa7,0xfe,0xff,0xff,0x08,0x05,0xa5,0xfe,
-0xff,0xff,0x1f,0x05,0xa4,0xfe,0xff,0xff,0x01,0x05,0x81,0x00,0x80,0x00,0x36,0x45,
-0x0e,0x60,0xd0,0x64,0x44,0xd7,0xff,0xff,0xff,0xff,0x94,0xf3,0xff,0xff,0x01,0xb0,
-0x00,0x64,0x0f,0x03,0x94,0xfb,0x31,0x44,0xfe,0xb4,0x40,0x51,0x1e,0x60,0xe0,0x62,
-0xa2,0xd1,0x00,0x60,0x20,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x3d,0x60,0x2f,0x78,
-0xff,0xff,0x3e,0x60,0x8d,0x78,0xff,0xff,0x16,0x60,0xb6,0x63,0xbd,0xd3,0xbd,0xd1,
-0xbd,0xd1,0xb0,0x84,0xb0,0x84,0xff,0xff,0x0b,0x02,0x8c,0xfb,0x31,0x44,0xfe,0xb4,
-0x40,0x51,0x0d,0x7c,0x08,0x60,0x0a,0x64,0xa0,0xd9,0x3e,0x60,0x8d,0x78,0xff,0xff,
-0x0f,0xf3,0x94,0xf1,0x60,0x47,0x07,0xb4,0x01,0x61,0x03,0x03,0xcc,0x84,0xe1,0x81,
-0xfd,0x02,0xa1,0x80,0xb1,0x83,0x03,0x03,0x3e,0x60,0x8d,0x78,0xff,0xff,0x94,0xfd,
-0x31,0x44,0x01,0xbc,0x40,0x51,0xd1,0xfe,0x1f,0xf3,0xff,0xff,0xff,0xff,0x20,0x26,
-0x18,0x00,0x17,0x60,0x7c,0x62,0xa2,0xd3,0xff,0xff,0x60,0x40,0x01,0x3a,0x03,0x00,
-0xe4,0x65,0xbb,0x63,0x0b,0x00,0x60,0x40,0x02,0x3a,0x03,0x00,0xe0,0x65,0xb9,0x63,
-0x05,0x00,0xb8,0x63,0xe6,0x65,0x60,0x40,0x03,0x36,0xe4,0x65,0x13,0x60,0x6c,0x62,
-0x15,0x00,0x17,0x60,0x7c,0x62,0xa2,0xd3,0xff,0xff,0x60,0x40,0x01,0x3a,0x03,0x00,
-0xe4,0x65,0xcb,0x63,0x0b,0x00,0x60,0x40,0x02,0x3a,0x03,0x00,0xe0,0x65,0xc9,0x63,
-0x05,0x00,0xc8,0x63,0xe6,0x65,0x60,0x40,0x03,0x36,0xe4,0x65,0x67,0x60,0x72,0x62,
-0xa2,0xd3,0xff,0xff,0x14,0x60,0x4e,0x62,0x60,0x41,0x01,0x26,0x03,0x00,0x65,0x5e,
-0x12,0x7f,0xa2,0xdb,0x61,0x40,0x02,0x26,0x03,0x00,0x63,0x5e,0x14,0x7f,0x5a,0xdb,
-0x00,0x64,0x65,0xfb,0x17,0x60,0x44,0x62,0xa2,0xd3,0xff,0xff,0xfc,0xa0,0xff,0xff,
-0x01,0x06,0x04,0x64,0xa2,0xdb,0x3e,0x60,0x58,0x4e,0xae,0x78,0xff,0xff,0x95,0xf3,
-0xff,0xff,0x00,0xa0,0xff,0xff,0x26,0x03,0x00,0x60,0x08,0x63,0x01,0x60,0x78,0x61,
-0x16,0x60,0xb4,0x64,0x58,0xd1,0x59,0xd9,0xfd,0x1f,0x00,0x60,0x72,0x63,0x16,0x60,
-0x40,0x61,0x16,0x60,0xbe,0x64,0x58,0xd1,0x59,0xd9,0xfd,0x1f,0x00,0x60,0x1a,0x63,
-0x01,0x60,0x00,0x61,0x00,0x64,0x59,0xdb,0xfe,0x1f,0x00,0x60,0xfa,0x64,0xe3,0xfb,
-0xc0,0xf1,0x3c,0x60,0xa2,0x62,0xa2,0xd9,0xca,0x82,0x1e,0x64,0xa2,0xdb,0xff,0xff,
-0x2d,0xff,0x1b,0x00,0x0d,0x60,0xac,0x64,0xe3,0xfb,0x32,0x40,0x01,0x26,0x0f,0x00,
-0x16,0x60,0xbc,0x62,0xa2,0xd3,0xa5,0xf3,0x60,0x41,0x00,0x36,0x04,0x00,0xcd,0x81,
-0xe8,0x84,0xfd,0x02,0x04,0x05,0xc0,0xf1,0x16,0x60,0xbc,0x62,0xa2,0xd9,0x0f,0x4e,
-0x42,0x60,0x58,0x4f,0x60,0x78,0xff,0xff,0x0e,0x4f,0xda,0xfe,0x3d,0x60,0x2f,0x78,
-0xff,0xff,0x66,0x44,0x00,0xa8,0x0e,0x57,0x17,0x03,0x00,0x64,0x40,0x46,0xcb,0xfe,
-0x0f,0x4e,0x46,0x45,0x3c,0x60,0x82,0x62,0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,
-0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,
-0xff,0xff,0xa3,0xff,0xd1,0xfe,0x0e,0x4f,0x37,0x58,0xff,0xff,0x1f,0xf3,0xff,0xff,
-0xff,0xff,0x10,0x2a,0x34,0x00,0x17,0x60,0x44,0x62,0xa2,0xd3,0xff,0xff,0x60,0x45,
-0xd5,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0x19,0x03,0x17,0x60,0x80,0x62,0xa2,0xd3,
-0xff,0xff,0x60,0x45,0x17,0x60,0x48,0x63,0xff,0x60,0xff,0x61,0xbd,0xd3,0xdd,0x81,
-0xd4,0x80,0x61,0x44,0x03,0x06,0xfc,0xa0,0xff,0xff,0xf8,0x02,0x17,0x60,0x44,0x62,
-0xa2,0xd3,0x61,0x45,0xd4,0x80,0xff,0xff,0x01,0x06,0x60,0x45,0x66,0x60,0xd4,0x62,
-0x65,0x44,0xa2,0xdb,0xe0,0x85,0x17,0x60,0x3a,0x64,0x44,0xd3,0xff,0xff,0x13,0x60,
-0x98,0x62,0x3e,0x7f,0xa2,0xdb,0x14,0x60,0x7a,0x62,0x3e,0x7f,0xa2,0xdb,0x2e,0x58,
-0xff,0xff,0x3c,0x60,0x4c,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x0e,0xf2,
-0x62,0x03,0x60,0x47,0xfd,0x37,0x3a,0x00,0xff,0x36,0x17,0x00,0xf0,0x36,0x0a,0x00,
-0xff,0xb5,0x1e,0x60,0xaa,0x62,0x46,0xd1,0x00,0x60,0x01,0x64,0xb0,0x84,0xa2,0xdb,
-0xff,0xff,0xcf,0xfe,0x3c,0x60,0x82,0x62,0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,
-0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xdc,0x00,0x0f,0x4e,0x46,0x45,0x3c,0x60,
-0x82,0x62,0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,0xd1,0xfe,
-0x0e,0x4f,0x59,0x60,0xe8,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,
-0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0xbb,0x00,0x3c,0x60,0x82,0x62,
-0x3c,0x60,0x46,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0xd3,0xfe,0x59,0x60,0xe8,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0xa2,0x00,0xac,0xfe,
-0xff,0xff,0x0c,0x05,0xad,0xfe,0xff,0xff,0x12,0x05,0xae,0xfe,0xff,0xff,0x99,0x05,
-0xaf,0xfe,0xff,0xff,0x3a,0x05,0x3d,0x60,0x2f,0x78,0xff,0xff,0x1e,0x60,0xa8,0x62,
-0xa2,0xd1,0x20,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0xf4,0x00,0x1e,0x60,
-0xf4,0x65,0x0a,0x61,0x07,0x00,0xa2,0xdd,0x58,0x4f,0x64,0x58,0xff,0xff,0x00,0xb9,
-0xff,0xff,0x08,0x03,0x00,0x63,0xa5,0xd1,0x5a,0xd3,0xda,0x85,0x00,0xa8,0xcd,0x81,
-0xf2,0x02,0xf8,0x02,0xe0,0x00,0x1e,0x60,0xa6,0x62,0x1e,0x60,0xde,0x65,0x3f,0x60,
-0x8b,0x63,0x00,0x64,0x5a,0xdb,0xd6,0x80,0xff,0xff,0x04,0x03,0x5a,0xdb,0x5a,0xdb,
-0x5a,0xdd,0xf9,0x00,0x1e,0x60,0xf2,0x65,0x00,0x64,0x5a,0xdb,0xd6,0x80,0xff,0xff,
-0x02,0x03,0x5a,0xdd,0xfb,0x00,0x2f,0x58,0xff,0xff,0x1e,0x60,0xaa,0x64,0x40,0x41,
-0x1e,0x60,0xa8,0x63,0xa3,0xd1,0x00,0x64,0xd0,0x80,0x09,0x61,0x08,0x03,0xbd,0xdb,
-0xa3,0xd3,0xff,0xff,0xb0,0x84,0xcd,0x81,0xa3,0xdb,0x06,0xa3,0xf9,0x02,0x1e,0x60,
-0xe0,0x63,0xa3,0xd1,0x00,0x64,0xd0,0x80,0x0a,0x61,0x16,0x03,0xbd,0xdb,0x64,0x44,
-0xfe,0xa3,0x02,0xa3,0xcd,0x81,0xe8,0x84,0xe3,0x03,0x02,0x05,0xe1,0x03,0xf9,0x00,
-0x40,0x42,0xa3,0xd3,0x43,0x44,0x00,0xa8,0x41,0x43,0x02,0x03,0x58,0x4f,0x60,0x58,
-0x22,0x44,0x23,0x41,0x24,0x43,0xed,0x00,0x21,0x43,0x1e,0x60,0xe0,0x65,0xd7,0x80,
-0xbd,0xd1,0xbd,0xd3,0x01,0x02,0x8f,0x00,0xa0,0x84,0xbd,0xd1,0x43,0x41,0xf7,0x03,
-0x3f,0x60,0x90,0x64,0x64,0x58,0x40,0x4f,0x29,0xf2,0xff,0xff,0x60,0x40,0x08,0x26,
-0x03,0x00,0x40,0x60,0x2e,0x78,0xff,0xff,0x60,0x40,0x18,0x36,0x17,0x00,0x0c,0x60,
-0x44,0x64,0xa0,0xd7,0x58,0x4f,0xff,0xff,0x0f,0xf0,0xbd,0xf1,0x64,0x44,0x60,0x22,
-0x10,0x00,0x31,0xf2,0x32,0xf2,0xd0,0x80,0xbe,0xf1,0x0b,0x02,0xd0,0x80,0x33,0xf2,
-0x08,0x02,0xbf,0xf1,0xff,0xff,0xd0,0x80,0x0f,0xf0,0x03,0x02,0x41,0x60,0x76,0x78,
-0xff,0xff,0x00,0xf4,0xaa,0x60,0xaa,0x65,0x02,0xf2,0x03,0xf0,0xd4,0x80,0x03,0x64,
-0x16,0x02,0xd0,0x80,0x00,0x64,0x04,0xf0,0x12,0x02,0xd0,0x80,0xf8,0x7f,0x06,0x02,
-0x26,0x46,0x22,0xf0,0x20,0x67,0xb0,0x84,0xa2,0xda,0x09,0x00,0xd0,0x80,0xff,0xff,
-0x06,0x02,0x26,0x46,0x22,0xf0,0x40,0x67,0xb0,0x84,0xa2,0xda,0x00,0x00,0x26,0x46,
-0x0f,0xf2,0x81,0xf1,0x29,0xf2,0x60,0x40,0x20,0x2a,0x11,0x00,0x5c,0x63,0x60,0x40,
-0x02,0x2b,0x62,0x63,0xbd,0xd2,0xbd,0xd2,0xd0,0x80,0x82,0xf1,0x5c,0x02,0xd0,0x80,
-0xa3,0xd2,0x83,0xf1,0x58,0x02,0xd0,0x80,0xff,0xff,0x55,0x02,0x00,0x00,0x64,0x60,
-0x58,0x4f,0x5a,0x78,0xff,0xff,0x41,0x60,0x4c,0x78,0xff,0xff,0x26,0x46,0x29,0xf2,
-0xff,0xff,0xff,0xff,0x0c,0x26,0x47,0x00,0x95,0xf1,0x00,0x63,0xd3,0x80,0xff,0xff,
-0xf2,0x02,0x60,0x40,0xb0,0x3a,0x07,0x00,0x5a,0x60,0xa8,0x64,0xa0,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x23,0x00,0x10,0x3a,0x07,0x00,0x5a,0x60,0xac,0x64,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1a,0x00,0x30,0x3a,0x07,0x00,0x5a,0x60,0xac,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x11,0x00,0xc0,0x3a,0x07,0x00,0x5a,0x60,
-0xae,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x08,0x00,0xa0,0x3a,0x1e,0x00,
-0x5a,0x60,0xb0,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x0c,0x60,0x48,0x64,
-0xa0,0xd7,0x58,0x4f,0xff,0xff,0x2d,0x00,0xcb,0xf3,0xff,0xff,0x60,0x40,0x03,0x3a,
-0x0a,0x00,0x68,0x60,0x58,0x4e,0x89,0x78,0xff,0xff,0x26,0x46,0x04,0x02,0x41,0x60,
-0x58,0x4e,0x8c,0x78,0xff,0xff,0x41,0x60,0x76,0x78,0xff,0xff,0x50,0x3a,0x0c,0x00,
-0x5a,0x60,0xb6,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x0c,0x60,0x50,0x64,
-0xa0,0xd7,0x58,0x4f,0xff,0xff,0x0d,0x00,0x40,0x3a,0x0e,0x00,0x5a,0x60,0xb2,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x0c,0x60,0x4a,0x64,0xa0,0xd7,0x58,0x4f,
-0xff,0xff,0x41,0x60,0x72,0x78,0xff,0xff,0x90,0x3a,0x10,0x00,0x41,0x60,0x58,0x4e,
-0x7d,0x78,0xff,0xff,0x0c,0x60,0x52,0x64,0xa0,0xd7,0x58,0x4f,0xff,0xff,0x5a,0x60,
-0xa4,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0xeb,0x00,0x80,0x3a,0x6e,0x00,
-0xcb,0xf3,0x20,0x40,0x40,0x26,0x32,0x00,0xfd,0xa0,0xfc,0xa0,0x01,0x03,0x29,0x02,
-0x81,0xf1,0x31,0xf2,0x32,0xf2,0xd0,0x80,0x82,0xf1,0xae,0x02,0xd0,0x80,0x33,0xf2,
-0x83,0xf1,0xaa,0x02,0xd0,0x80,0x70,0xf3,0xa7,0x02,0xdc,0x84,0xcb,0xf1,0x70,0xfb,
-0x00,0x64,0x71,0xfb,0x0a,0x60,0x02,0x64,0x6e,0xfb,0x64,0x40,0x03,0x36,0x50,0x00,
-0x5a,0x60,0x9c,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x0c,0x60,0x4c,0x64,
-0xa0,0xd7,0x58,0x4f,0xff,0xff,0x0c,0x60,0x4e,0x64,0xa0,0xd7,0x58,0x4f,0xff,0xff,
-0x3d,0x00,0xd5,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x08,0x04,0x0c,0x60,0x50,0x64,
-0xa0,0xd7,0x58,0x4f,0xff,0xff,0x41,0x60,0x72,0x78,0xff,0xff,0x16,0x60,0x42,0x62,
-0xa2,0xd3,0xff,0xff,0xfc,0xa0,0x81,0xf3,0x29,0x02,0x00,0xa0,0x31,0xf0,0x13,0x03,
-0xd0,0x80,0x82,0xf3,0x32,0xf0,0x0f,0x02,0xd0,0x80,0x83,0xf3,0x33,0xf0,0x0b,0x02,
-0xd0,0x80,0x70,0xf3,0x08,0x02,0xdc,0x84,0x70,0xfb,0x00,0x64,0x71,0xfb,0x0a,0x60,
-0x02,0x64,0x6e,0xfb,0x13,0x00,0x59,0x60,0x20,0x64,0xa0,0xd3,0xff,0xff,0x60,0x40,
-0xff,0x22,0x0c,0x00,0x68,0x60,0x58,0x4e,0x89,0x78,0xff,0xff,0x07,0x02,0x0f,0x4e,
-0x26,0x46,0x67,0x60,0x58,0x4f,0x13,0x78,0xff,0xff,0x0e,0x4f,0x26,0x46,0x56,0x00,
-0x29,0xf0,0x3d,0x60,0x0e,0x65,0x0a,0x64,0x64,0x40,0x10,0x2b,0xa5,0xdb,0x68,0x60,
-0xcc,0x62,0x90,0x60,0x30,0x64,0xa2,0xdb,0x41,0x60,0x58,0x4e,0x7d,0x78,0xff,0xff,
-0xa4,0xf3,0x5e,0xf1,0x60,0x45,0x73,0x44,0x64,0x40,0x04,0x2a,0x04,0x00,0xd4,0x84,
-0xe7,0xa0,0x96,0x0e,0x95,0x04,0x5a,0x60,0x9c,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x0c,0x60,0x4c,0x64,0xa0,0xd7,0x58,0x4f,0xff,0xff,0x0c,0x60,0x4e,0x64,
-0xa0,0xd7,0x58,0x4f,0xff,0xff,0xd2,0x00,0x66,0x60,0xd8,0x62,0xa2,0xd3,0xff,0xff,
-0x81,0xa0,0xff,0xa0,0x23,0x03,0x0f,0x02,0x41,0x60,0x58,0x4e,0xee,0x78,0xff,0xff,
-0xff,0xa1,0x26,0x46,0x1b,0x02,0x66,0x60,0xd8,0x62,0x7f,0x64,0xa2,0xdb,0xba,0xff,
-0xff,0xff,0xaf,0xff,0x13,0x00,0x3c,0x60,0x82,0x62,0x3c,0x60,0x6a,0x64,0xa2,0xdb,
-0x26,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x00,0x64,0x40,0x46,
-0xd2,0xfe,0x08,0x00,0x66,0x44,0x00,0xbc,0xff,0xff,0x04,0x03,0x3e,0x60,0x58,0x4e,
-0x91,0x78,0xff,0xff,0x56,0xf7,0xff,0xff,0xff,0xff,0x76,0xf3,0x3c,0x45,0xd4,0x80,
-0x28,0x60,0x60,0x62,0x07,0x02,0x80,0x64,0xa2,0xdb,0xff,0xff,0xc0,0xfe,0x00,0x64,
-0xa2,0xdb,0x71,0xfb,0x2e,0x58,0xff,0xff,0x26,0x46,0x28,0x60,0xda,0x63,0x00,0xf4,
-0x02,0xf2,0xbd,0xdb,0xff,0xff,0x03,0xf2,0xbd,0xdb,0x04,0xf2,0xff,0xff,0xbd,0xdb,
-0x05,0xf2,0xa3,0xdb,0xfa,0xa3,0x26,0x46,0x00,0x60,0x00,0x65,0xa3,0xd3,0x23,0xf0,
-0x00,0x61,0xd0,0x84,0xf1,0x81,0xd4,0x84,0xf1,0x81,0xbd,0xdb,0xa3,0xd3,0x03,0xb1,
-0x03,0xa9,0x24,0xf0,0x42,0xfe,0x01,0x03,0xcc,0x84,0xf1,0x81,0xd0,0x84,0xf1,0x81,
-0xbd,0xdb,0xa3,0xd3,0x03,0xb1,0x03,0xa9,0x27,0xf0,0x42,0xfe,0x01,0x03,0xcc,0x84,
-0xf1,0x81,0xd0,0x84,0xf1,0x81,0xbd,0xdb,0xa3,0xd3,0x03,0xb1,0x03,0xa9,0x28,0xf0,
-0x01,0x03,0xcc,0x84,0xd0,0x84,0xa3,0xdb,0x26,0x0e,0x28,0x60,0xda,0x63,0xbd,0xd3,
-0x00,0x65,0x00,0x61,0xd4,0x84,0xbd,0xd3,0xf1,0x81,0x01,0xa9,0x42,0xfe,0x01,0x03,
-0xcc,0x84,0xf1,0x81,0x01,0x65,0xd4,0x84,0xf1,0x81,0xbd,0xd3,0x03,0xb1,0x03,0xa9,
-0x42,0xfe,0x01,0x03,0xcc,0x84,0xf1,0x81,0x00,0x65,0xd4,0x84,0xf1,0x81,0xa3,0xd3,
-0x03,0xb1,0x03,0xa9,0x42,0xfe,0x01,0x03,0xcc,0x84,0xd4,0x84,0x04,0x0e,0x66,0x60,
-0x9a,0x62,0x01,0x64,0xa2,0xdb,0x26,0x46,0x2e,0x58,0xff,0xff,0x66,0x60,0xd6,0x62,
-0x2e,0x44,0xa2,0xdb,0x00,0x64,0x38,0xf2,0x40,0x48,0x40,0x4c,0xa0,0xa0,0xff,0xff,
-0x43,0x04,0x00,0xf4,0x01,0xf2,0x04,0x63,0x60,0x41,0x66,0x60,0xe6,0x62,0x00,0x64,
-0xa2,0xdb,0x42,0x60,0x58,0x4e,0x44,0x78,0xff,0xff,0x36,0x03,0xff,0x65,0xd4,0x80,
-0xff,0xff,0xf7,0x02,0x00,0x64,0xdc,0x84,0x40,0x4d,0x42,0x60,0x58,0x4e,0x44,0x78,
-0xff,0xff,0x2a,0x03,0xff,0x65,0xd4,0x80,0x60,0x42,0x2d,0x44,0xf4,0x03,0xfa,0xa0,
-0xff,0xff,0xe3,0x04,0x06,0x64,0x40,0x4d,0x66,0x60,0xda,0x65,0x62,0x44,0x05,0x00,
-0x42,0x60,0x58,0x4e,0x44,0x78,0xff,0xff,0x17,0x03,0xa5,0xd1,0xda,0x85,0xd0,0x80,
-0xff,0xff,0xdc,0x02,0x2d,0x44,0xcc,0x84,0x40,0x4d,0xf2,0x02,0x06,0x64,0x40,0x4d,
-0x66,0x60,0xda,0x65,0x66,0x60,0xe6,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xf0,0xa0,
-0xa2,0xdb,0xe6,0x02,0x01,0x61,0x01,0x00,0x00,0x61,0x66,0x60,0xd6,0x62,0xa2,0xd3,
-0xff,0xff,0x40,0x4e,0x2e,0x58,0xff,0xff,0x2c,0x44,0xcc,0x84,0x40,0x4c,0x11,0x0e,
-0xcd,0x81,0xff,0xff,0x05,0x0d,0x00,0xf4,0x01,0xf2,0x04,0x63,0xcc,0x84,0x60,0x41,
-0x28,0x44,0x01,0xac,0x40,0x48,0x07,0x02,0xbd,0xd2,0x01,0xb8,0x60,0x47,0x00,0x7f,
-0x05,0x00,0xdc,0x84,0x03,0x00,0xa3,0xd2,0xff,0xff,0x00,0x7f,0x2e,0x58,0xff,0xff,
-0x01,0x64,0xcb,0xfb,0x1e,0x60,0xc8,0x62,0x00,0x64,0xa2,0xdb,0x00,0x60,0x08,0x63,
-0x01,0x60,0x78,0x61,0x16,0x60,0xb4,0x64,0x58,0xd1,0x59,0xd9,0xfd,0x1f,0x00,0x60,
-0x72,0x63,0x16,0x60,0x40,0x61,0x16,0x60,0xbe,0x64,0x58,0xd1,0x59,0xd9,0xfd,0x1f,
-0xd5,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0x14,0x03,0x18,0x60,0xd0,0x63,0x17,0x60,
-0x82,0x61,0x13,0x64,0xbd,0xd1,0xa1,0xd9,0xff,0xa4,0x02,0xa1,0xfb,0x02,0x1f,0x60,
-0x38,0x63,0x0a,0xa3,0x05,0x64,0xa3,0xdb,0x0c,0xa3,0xa3,0xdb,0x04,0xa3,0xa3,0xdb,
-0x25,0x00,0x1f,0x60,0x56,0x62,0x00,0x64,0xa2,0xdb,0x1f,0x60,0x1c,0x63,0xa5,0xf3,
-0x01,0x61,0x60,0x45,0x65,0x44,0xe8,0x85,0x05,0x64,0x02,0x28,0x00,0x64,0xbd,0xdb,
-0x00,0xa0,0xff,0xff,0x0d,0x03,0x1f,0x60,0x56,0x62,0xa2,0xd3,0xff,0xff,0x01,0xa4,
-0xa2,0xdb,0xff,0xa0,0xff,0xff,0x04,0x02,0x1f,0x60,0x54,0x62,0x61,0x44,0xa2,0xdb,
-0xdd,0x81,0xff,0xff,0x61,0x44,0xf2,0xa0,0xff,0xff,0xe4,0x06,0x59,0x60,0x1e,0x61,
-0x16,0x60,0x42,0x64,0x20,0x63,0x58,0xd1,0x59,0xd9,0xfd,0x1f,0xa8,0xf1,0x80,0xf9,
-0x1f,0xf1,0x66,0x60,0xf6,0x65,0x0a,0x64,0x64,0x40,0x10,0x2a,0x04,0x64,0xa5,0xdb,
-0x32,0x40,0x01,0x26,0x07,0x00,0x00,0x60,0x1a,0x63,0x01,0x60,0x00,0x61,0x00,0x64,
-0x59,0xdb,0xfe,0x1f,0x40,0x40,0xc0,0xf3,0xff,0xff,0x40,0x4a,0x1e,0x60,0xc8,0x62,
-0x00,0x64,0xa2,0xdb,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,0xca,0x62,0x40,0x60,
-0x00,0x64,0xa2,0xdb,0x42,0x60,0xd6,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,
-0x3c,0x60,0xa2,0x62,0x2a,0x44,0xa2,0xdb,0xca,0x82,0x08,0x64,0xa2,0xdb,0xff,0xff,
-0x2d,0xff,0x1e,0x60,0xca,0x62,0x20,0x60,0x00,0x64,0xa2,0xdb,0x42,0x60,0xfc,0x64,
-0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc8,0x62,0x00,0x64,0xa2,0xdb,
-0xbe,0xfe,0x1e,0x60,0xa8,0x62,0xa2,0xd1,0x40,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,
-0xcf,0xfe,0x61,0x60,0xc8,0x62,0x0c,0x64,0xa2,0xdb,0x64,0x60,0x58,0x62,0x01,0x64,
-0xa2,0xdb,0xd5,0xf1,0x03,0x64,0x64,0x40,0x00,0x3a,0xd5,0xfb,0x95,0xf3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x02,0x02,0x00,0x64,0x62,0xfb,0x67,0x60,0x74,0x62,0xa2,0xd3,
-0xff,0xff,0x60,0x40,0x00,0x3a,0x2b,0x00,0x01,0x64,0xa2,0xdb,0x12,0x60,0x34,0x65,
-0x72,0x44,0xb4,0x83,0x00,0x7f,0x60,0x5c,0x10,0x61,0x40,0x60,0x0b,0x65,0x63,0x44,
-0x00,0x63,0xe8,0x80,0xf8,0x84,0x02,0x24,0x94,0x84,0xf3,0x83,0xcd,0x81,0xff,0xff,
-0xf8,0x02,0x60,0x47,0x60,0x45,0x00,0x7f,0x39,0xfb,0x65,0x44,0x00,0x7e,0xb0,0x84,
-0x38,0xfb,0x66,0x60,0xaa,0x63,0xbd,0xf3,0xff,0xff,0x02,0xbc,0xbd,0xdb,0x39,0xf3,
-0xbe,0xf1,0x60,0x47,0x00,0x7e,0xb0,0x84,0xbd,0xdb,0x38,0xf3,0xa3,0xdb,0x0f,0x4e,
-0x5c,0x60,0x58,0x4f,0x1f,0x78,0xff,0xff,0x0e,0x4f,0x0f,0x4e,0x58,0x60,0x58,0x4f,
-0xdd,0x78,0xff,0xff,0x0e,0x4f,0x0f,0x4e,0x5e,0x60,0x58,0x4f,0x53,0x78,0xff,0xff,
-0x0e,0x4f,0x0f,0x4e,0x44,0x60,0x58,0x4f,0x1e,0x78,0xff,0xff,0x0e,0x4f,0x1e,0x60,
-0xc8,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc8,0x62,
-0x00,0x64,0xa2,0xdb,0x02,0x64,0x8b,0xfb,0xff,0xff,0xc1,0xfe,0x1e,0x60,0xe0,0x62,
-0xa2,0xd1,0x00,0x60,0xf4,0x86,0x7e,0x00,0x00,0x10,0x1f,0x64,0xb0,0x84,0xa2,0xdb,
-0xcf,0xfe,0x1e,0x60,0xc8,0x62,0xa2,0xd1,0x00,0x60,0x08,0x64,0xb0,0x84,0xa2,0xdb,
-0xff,0xff,0xcf,0xfe,0x1e,0x60,0xca,0x62,0x00,0x60,0x08,0x64,0xa2,0xdb,0x43,0x60,
-0x92,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x8b,0xf3,0x00,0x65,0xd4,0x80,
-0xff,0xff,0x0b,0x03,0x1e,0x60,0xca,0x62,0x80,0x60,0x00,0x64,0xa2,0xdb,0x43,0x60,
-0x92,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc8,0x62,0x00,0x64,
-0xa2,0xdb,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,0xca,0x62,0x20,0x60,0x00,0x64,
-0xa2,0xdb,0x43,0x60,0xb4,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x3c,0x60,
-0xa2,0x62,0xca,0x82,0x06,0x64,0xa2,0xdb,0xff,0xff,0x2d,0xff,0x1e,0x60,0xc8,0x62,
-0x00,0x64,0xa2,0xdb,0x5a,0xdb,0xbe,0xfe,0x3c,0x60,0x28,0x61,0x43,0x60,0x58,0x4e,
-0xef,0x78,0xff,0xff,0x3c,0x60,0x2e,0x61,0x43,0x60,0x58,0x4e,0xef,0x78,0xff,0xff,
-0x3c,0x60,0x46,0x61,0x43,0x60,0x58,0x4e,0xef,0x78,0xff,0xff,0x3c,0x60,0x6a,0x61,
-0x43,0x60,0x58,0x4e,0xef,0x78,0xff,0xff,0x3c,0x60,0x4c,0x61,0x43,0x60,0x58,0x4e,
-0xef,0x78,0xff,0xff,0x3c,0x60,0x76,0x61,0x43,0x60,0x58,0x4e,0xef,0x78,0xff,0xff,
-0xc5,0xfe,0x01,0x64,0xcb,0xfb,0x02,0x65,0x3d,0x60,0x58,0x4e,0x32,0x78,0xff,0xff,
-0x2f,0x58,0xff,0xff,0xa1,0xd3,0x0e,0x57,0x00,0xa8,0x60,0x46,0x28,0x03,0x09,0xf0,
-0x0e,0xf2,0x44,0x4c,0x20,0xb0,0x01,0xb0,0x0b,0x03,0x3c,0x60,0x82,0x62,0x00,0x64,
-0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x15,0x00,
-0x14,0x02,0x0f,0x4e,0x46,0x45,0x3c,0x60,0x82,0x62,0x00,0x64,0xa2,0xdb,0x66,0x44,
-0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xa2,0xff,0x1a,0x60,0x58,0x4f,
-0x9e,0x78,0xff,0xff,0xa3,0xff,0xd1,0xfe,0x0e,0x4f,0x2c,0x44,0xd5,0x00,0x37,0x58,
-0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x1e,0x60,0xc2,0x62,0xa2,0xd1,
-0x00,0x60,0x02,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x1e,0x60,0xc4,0x62,
-0x00,0x60,0x02,0x64,0xa2,0xdb,0x44,0x60,0x36,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x16,0x60,0x42,0x62,0xa2,0xd3,
-0xff,0xff,0x03,0xa8,0x04,0xa8,0x02,0x03,0x04,0x03,0x06,0x00,0x45,0x60,0x25,0x78,
-0xff,0xff,0x45,0x60,0x6b,0x78,0xff,0xff,0x02,0x64,0xcb,0xfb,0x00,0x64,0x81,0xfb,
-0x82,0xfb,0x83,0xfb,0x66,0x60,0xc0,0x62,0x01,0x64,0xa2,0xdb,0x16,0x60,0x44,0x64,
-0x5b,0xfb,0x0f,0x4e,0x52,0x60,0x58,0x4f,0xaa,0x78,0xff,0xff,0x0e,0x4f,0x1e,0x60,
-0xc4,0x62,0x10,0x60,0x00,0x64,0xa2,0xdb,0x44,0x60,0x67,0x64,0x5a,0xdb,0xcf,0xfe,
-0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x59,0x60,0xb4,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x88,0xf1,0x27,0x60,0xe0,0x63,0xd3,0x80,
-0xff,0xff,0x03,0x03,0x4b,0x60,0x47,0x78,0xff,0xff,0xc0,0xf3,0xc6,0xf3,0x40,0x4a,
-0x00,0xa8,0xff,0xff,0x03,0x03,0x45,0x60,0x6b,0x78,0xff,0xff,0x3e,0x60,0x58,0x4e,
-0xae,0x78,0xff,0xff,0x02,0x64,0xcb,0xfb,0x64,0x60,0x58,0x62,0xa2,0xd3,0xff,0xff,
-0x9d,0xa0,0x01,0xa4,0x03,0x05,0xec,0xa0,0xa2,0xdb,0x18,0x06,0xa9,0xf1,0x28,0x60,
-0x7e,0x64,0xa0,0xd9,0x3c,0x60,0xb2,0x62,0x28,0x60,0x7a,0x64,0xa2,0xdb,0x02,0x64,
-0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xc4,0x62,0x00,0x60,0x04,0x64,0xa2,0xdb,
-0x44,0x60,0xc7,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0xe1,0xf3,0xff,0xff,
-0x00,0xa0,0x60,0x5c,0x17,0x03,0x28,0x60,0x7e,0x64,0xa0,0xd9,0x3c,0x60,0xb2,0x62,
-0x28,0x60,0x7a,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,
-0xc4,0x62,0x00,0x60,0x04,0x64,0xa2,0xdb,0x44,0x60,0xc7,0x64,0x5a,0xdb,0xcf,0xfe,
-0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0xd5,0xf3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x18,0x03,0x66,0x60,0xc2,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,
-0x88,0xa0,0xa2,0xdb,0x10,0x04,0x00,0x64,0xa2,0xdb,0x03,0x64,0xd5,0xfb,0x0a,0x65,
-0x3d,0x60,0x58,0x4e,0x32,0x78,0xff,0xff,0x1f,0x60,0x1c,0x63,0x0e,0x64,0x00,0x7c,
-0xcc,0x84,0xbd,0xd9,0xfd,0x02,0x67,0x60,0x76,0x65,0xa5,0xd3,0xff,0xff,0x60,0x40,
-0x03,0x22,0x33,0x00,0x01,0x2a,0x0c,0x00,0x1e,0x60,0x92,0x63,0xa3,0xd1,0x66,0x60,
-0xf0,0x65,0xad,0xf3,0xa5,0xd3,0x60,0x45,0xc4,0x84,0xd0,0x80,0xff,0xff,0x25,0x0d,
-0x67,0x60,0x76,0x65,0xa5,0xd3,0xff,0xff,0x60,0x45,0xfd,0xb4,0xa2,0xdb,0x20,0x44,
-0xb4,0x84,0x40,0x40,0x59,0x60,0xbc,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x0f,0x4e,0x52,0x60,0x58,0x4f,0xaa,0x78,0xff,0xff,0x0e,0x4f,0x1e,0x60,0xc4,0x62,
-0x10,0x60,0x00,0x64,0xa2,0xdb,0x45,0x60,0x1e,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x44,0x60,0x49,0x78,0xff,0xff,
-0x05,0x64,0xcb,0xfb,0xc5,0xf3,0x47,0xfb,0x00,0x64,0xc0,0xf1,0x85,0xfb,0x44,0x4a,
-0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,
-0xc4,0x62,0x40,0x60,0x00,0x64,0xa2,0xdb,0x45,0x60,0x2d,0x64,0x5a,0xdb,0xcf,0xfe,
-0x2f,0x58,0xff,0xff,0x3c,0x60,0xa2,0x62,0x2a,0x44,0xa2,0xdb,0xca,0x82,0x1e,0x64,
-0xa2,0xdb,0xff,0xff,0x2d,0xff,0x1e,0x60,0xc4,0x62,0x20,0x60,0x00,0x64,0xa2,0xdb,
-0x45,0x60,0x53,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,
-0x00,0x64,0xa2,0xdb,0xbe,0xfe,0x1e,0x60,0xa8,0x62,0xa2,0xd1,0x40,0x60,0x00,0x64,
-0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x01,0x64,0x8c,0xfb,0x02,0x64,0x90,0xfb,0x01,0x65,
-0x3d,0x60,0x58,0x4e,0x32,0x78,0xff,0xff,0x2f,0x58,0xff,0xff,0xd5,0xf3,0xff,0xff,
-0xfd,0xa0,0xff,0xff,0x05,0x05,0x0c,0x60,0x50,0x62,0x67,0x60,0x13,0x64,0xa2,0xdb,
-0x0c,0x60,0x48,0x62,0x67,0x60,0xd6,0x64,0xa2,0xdb,0x59,0x60,0x74,0x62,0x28,0x60,
-0xe2,0x64,0xa2,0xdb,0x77,0xf5,0x00,0x64,0x22,0xfa,0x38,0xfa,0x08,0x64,0x28,0xfa,
-0x18,0x60,0x20,0x64,0x0e,0xfa,0x90,0x64,0x29,0xfa,0x66,0x60,0xa4,0x62,0x12,0x60,
-0x34,0x65,0x72,0x44,0xb4,0x84,0xa2,0xdb,0xc0,0xf3,0x7f,0xfb,0x66,0x60,0x96,0x63,
-0x00,0x64,0xbd,0xdb,0xbd,0xdb,0xa3,0xdb,0xff,0xff,0x71,0xfb,0x70,0xfb,0x81,0xfb,
-0xff,0xff,0x82,0xfb,0x83,0xfb,0xff,0xff,0xc5,0xf3,0x47,0xfb,0x16,0x60,0xbe,0x64,
-0xa0,0xd3,0x86,0xfb,0x66,0x60,0xa2,0x63,0x06,0x64,0xa3,0xdb,0x01,0x64,0x73,0xfb,
-0x39,0x60,0xf2,0x62,0x00,0x64,0xa2,0xdb,0x5e,0xfb,0x68,0x60,0xcc,0x62,0x90,0x60,
-0x90,0x64,0xa2,0xdb,0x66,0x60,0xa6,0x63,0x02,0x64,0xcb,0xfb,0xa3,0xdb,0x28,0x60,
-0xd2,0x63,0x39,0x60,0xb2,0x7c,0x10,0x65,0x00,0x64,0x47,0xdb,0xd3,0x80,0x47,0xdb,
-0xfc,0x04,0x16,0x60,0x44,0x64,0x5b,0xfb,0x0f,0x4e,0x52,0x60,0x58,0x4f,0xaa,0x78,
-0xff,0xff,0x0e,0x4f,0x1e,0x60,0xc4,0x62,0x10,0x60,0x00,0x64,0xa2,0xdb,0x45,0x60,
-0xda,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,
-0xa2,0xdb,0x59,0x60,0xb4,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x39,0x60,
-0xf2,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x09,0x03,0x4b,0x60,0x58,0x4e,
-0x2b,0x78,0xff,0xff,0x04,0x02,0x46,0x60,0x97,0x78,0xff,0xff,0xd2,0x00,0x59,0x60,
-0x20,0x62,0xa2,0xd3,0x01,0x63,0x60,0x40,0x00,0x3a,0x6e,0xfd,0x0a,0x61,0x66,0x60,
-0xa4,0x62,0x40,0x60,0x0b,0x65,0xa2,0xd3,0x00,0x63,0xe8,0x80,0xf8,0x84,0x02,0x24,
-0x94,0x84,0xf3,0x83,0xcd,0x81,0xff,0xff,0xf8,0x02,0xa2,0xdb,0x64,0xa3,0x28,0x60,
-0x7e,0x62,0xa2,0xdd,0x3c,0x60,0xb2,0x62,0x28,0x60,0x7a,0x64,0xa2,0xdb,0x02,0x64,
-0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xc4,0x62,0x00,0x60,0x04,0x64,0xa2,0xdb,
-0x46,0x60,0x23,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,
-0x00,0x64,0xa2,0xdb,0x00,0x64,0x6e,0xfb,0x39,0x60,0xf2,0x62,0xa2,0xd3,0xff,0xff,
-0x00,0xa0,0x60,0x45,0x67,0x02,0x66,0x60,0xa2,0x62,0xa2,0xd3,0xff,0xff,0xcc,0x84,
-0xa2,0xdb,0xbc,0x02,0x59,0x60,0x20,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,
-0x10,0x03,0x66,0x60,0xa8,0x62,0xa2,0xd1,0x73,0xf3,0xff,0xff,0xfd,0xa0,0xfe,0xa0,
-0x2e,0x03,0x07,0x03,0x64,0x44,0x00,0x3a,0x2a,0x00,0x65,0x44,0x00,0xa0,0xff,0xff,
-0x26,0x03,0x16,0x60,0x88,0x62,0xa2,0xd1,0x04,0x7f,0x64,0x40,0xff,0x26,0x08,0x7f,
-0x60,0x43,0x28,0x60,0x7e,0x62,0xa2,0xdd,0x3c,0x60,0xb2,0x62,0x28,0x60,0x7a,0x64,
-0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xc4,0x62,0x00,0x60,
-0x04,0x64,0xa2,0xdb,0x46,0x60,0x6d,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,
-0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x45,0x60,0x93,0x78,0xff,0xff,0x66,0x60,
-0xaa,0x63,0xbd,0xd3,0x81,0xfb,0xbd,0xd3,0xff,0xff,0x82,0xfb,0xa3,0xd3,0xff,0xff,
-0xdc,0x84,0xa3,0xdb,0x83,0xfb,0xff,0xff,0xc1,0xf3,0x86,0xfb,0x73,0xf3,0xff,0xff,
-0x04,0xbc,0x73,0xfb,0x04,0x64,0x5e,0xfb,0x68,0x60,0xcc,0x62,0x90,0x60,0x91,0x64,
-0xa2,0xdb,0x66,0x60,0x9e,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x47,0x60,
-0x1a,0x78,0xff,0xff,0x39,0x60,0xf2,0x63,0xbd,0xd1,0x81,0xf9,0xbd,0xd1,0xff,0xff,
-0x82,0xf9,0xbd,0xd1,0x83,0xf9,0x08,0xa3,0xbd,0xd1,0x80,0xf9,0xff,0xff,0xbd,0xd1,
-0xbd,0xd1,0x59,0x60,0x20,0x65,0xa5,0xd9,0x02,0xa2,0x62,0x45,0x64,0x41,0xdd,0x81,
-0xe9,0x81,0xbd,0xd1,0xa5,0xd9,0xda,0x82,0xcd,0x81,0x62,0x45,0xfa,0x02,0x3a,0x60,
-0x26,0x63,0xbd,0xd3,0x00,0x61,0x60,0x45,0xbd,0xd3,0xff,0xff,0x7f,0xb4,0x02,0x36,
-0x01,0xb9,0x04,0x36,0x02,0xb9,0x0b,0x36,0x04,0xb9,0x16,0x36,0x08,0xb9,0x65,0x44,
-0xcc,0x84,0x60,0x45,0xf1,0x02,0xc9,0xf1,0x61,0x44,0xa0,0x84,0xc9,0xfb,0x00,0x61,
-0x60,0x45,0x3a,0x60,0x28,0x63,0x65,0x40,0x01,0x2a,0x03,0x00,0x02,0x64,0xbd,0xdb,
-0xdd,0x81,0x65,0x40,0x02,0x2a,0x03,0x00,0x04,0x64,0xbd,0xdb,0xdd,0x81,0x65,0x40,
-0x04,0x2a,0x03,0x00,0x0b,0x64,0xbd,0xdb,0xdd,0x81,0x65,0x40,0x08,0x2a,0x03,0x00,
-0x16,0x64,0xbd,0xdb,0xdd,0x81,0x3a,0x60,0x26,0x63,0x61,0x44,0xbd,0xdb,0x17,0x60,
-0xd2,0x65,0x61,0x44,0xa5,0xdb,0xda,0x82,0x62,0x45,0xbd,0xd3,0xbd,0xd1,0x60,0x47,
-0xb0,0x87,0xa5,0xda,0xda,0x85,0xcd,0x81,0xcd,0x81,0x01,0x03,0xf6,0x02,0x3a,0x60,
-0x32,0x63,0xbd,0xd1,0x17,0x60,0xdc,0x62,0xa2,0xd3,0xff,0xff,0xa0,0x84,0xa2,0xdb,
-0x60,0x45,0xbd,0xd3,0x7f,0xfb,0xff,0xff,0xbd,0xd3,0x86,0xfb,0x65,0x44,0x00,0x65,
-0x60,0x40,0x02,0x26,0x01,0x65,0x60,0x40,0x04,0x26,0x02,0x65,0x60,0x40,0x08,0x26,
-0x03,0x65,0x59,0x60,0x68,0x62,0x65,0x44,0xa2,0xdb,0x16,0x60,0x88,0x63,0xa3,0xd1,
-0x17,0x60,0x06,0x63,0xa3,0xd9,0x86,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0x02,0x02,
-0xa3,0xdb,0x05,0x00,0xfd,0xa0,0xff,0xff,0x02,0x05,0x03,0x64,0x86,0xfb,0xff,0x60,
-0xff,0x64,0x62,0xfb,0x76,0xf5,0x66,0x60,0x58,0x4e,0x3a,0x78,0xff,0xff,0x17,0x60,
-0xdc,0x62,0xa2,0xd3,0xff,0xff,0xff,0xff,0x01,0x2a,0x02,0x00,0x00,0x64,0x09,0x00,
-0x02,0x2a,0x02,0x00,0x01,0x64,0x05,0x00,0x04,0x2a,0x02,0x00,0x02,0x64,0x01,0x00,
-0x03,0x64,0x13,0xfa,0x77,0xf5,0x13,0xfa,0xff,0xff,0xbd,0xf1,0x2e,0xf8,0xbe,0xf1,
-0xff,0xff,0x2f,0xf8,0xbf,0xf1,0x30,0xf8,0xff,0xff,0x81,0xf1,0x31,0xf8,0x82,0xf1,
-0xff,0xff,0x32,0xf8,0x83,0xf1,0x33,0xf8,0x66,0x60,0xa6,0x63,0x03,0x64,0xcb,0xfb,
-0xa3,0xdb,0x00,0x64,0x7f,0xf1,0x85,0xfb,0x44,0x4a,0x1e,0x60,0xc2,0x62,0x00,0x64,
-0xa2,0xdb,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,0xc4,0x62,0x40,0x60,0x00,0x64,
-0xa2,0xdb,0x47,0x60,0x62,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x3c,0x60,
-0xa2,0x62,0x2a,0x44,0xa2,0xdb,0xca,0x82,0x1e,0x64,0xa2,0xdb,0xff,0xff,0x2d,0xff,
-0x1e,0x60,0xc4,0x62,0x20,0x60,0x00,0x64,0xa2,0xdb,0x47,0x60,0x88,0x64,0x5a,0xdb,
-0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0xbe,0xfe,
-0x1e,0x60,0xa8,0x62,0xa2,0xd1,0x40,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,
-0x01,0x64,0x8c,0xfb,0x02,0x64,0x90,0xfb,0x68,0x60,0xcc,0x62,0x90,0x60,0x70,0x64,
-0xa2,0xdb,0x20,0x44,0x10,0x26,0x05,0x00,0x01,0x65,0x3d,0x60,0x58,0x4e,0x32,0x78,
-0xff,0xff,0x20,0x44,0xef,0xb4,0x40,0x40,0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x00,0x60,
-0x80,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x1e,0x60,0xc4,0x62,0x00,0x60,
-0x02,0x64,0xa2,0xdb,0x47,0x60,0xbd,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,
-0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x3d,0x60,0x1c,0x62,0xa2,0xd3,0x10,0x61,
-0x00,0x63,0x60,0x45,0xe0,0x84,0xa2,0xdb,0x65,0x44,0x01,0x26,0xdf,0x83,0xcd,0x81,
-0xe8,0x84,0xfb,0x02,0x63,0x44,0xfc,0xa0,0xff,0xff,0x0d,0x04,0x3d,0x60,0x1e,0x62,
-0x32,0x64,0xa2,0xdb,0x62,0xf3,0xff,0xff,0x60,0x40,0x00,0x36,0x13,0x00,0x17,0x60,
-0x06,0x62,0x00,0x64,0xa2,0xdb,0x3d,0x60,0x1e,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,
-0xcc,0x84,0x08,0x03,0xa2,0xdb,0x06,0x02,0x3d,0x60,0x20,0x62,0xa2,0xd1,0x17,0x60,
-0x06,0x62,0xa2,0xd9,0x65,0xf1,0x00,0x64,0x6a,0xfb,0x6b,0xfb,0xff,0xff,0x6d,0xfb,
-0x6f,0xfb,0x64,0x40,0x02,0x26,0x03,0x00,0x4a,0x60,0xfc,0x78,0xff,0xff,0x1e,0x60,
-0xd4,0x62,0xa2,0xd1,0x00,0x60,0x08,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,
-0x66,0x60,0xb2,0x65,0x86,0xf3,0xa4,0xf1,0xa5,0xdb,0xff,0xff,0xff,0x22,0x06,0x00,
-0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xc0,0x84,0x6c,0xfb,0x5e,0xf3,0xff,0xff,
-0x04,0xb0,0xff,0xff,0x15,0x03,0x02,0x64,0x8c,0xfb,0x1e,0x60,0xc2,0x62,0xa2,0xd1,
-0x7f,0x60,0xff,0x64,0xa0,0x84,0xa2,0xdb,0x1e,0x60,0xc4,0x62,0x80,0x60,0x00,0x64,
-0xa2,0xdb,0x48,0x60,0x30,0x64,0x5a,0xdb,0xcf,0xfe,0xc1,0xfe,0x2f,0x58,0xff,0xff,
-0x49,0x60,0xcc,0x78,0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x7f,0x60,0xff,0x64,
-0xa0,0x84,0xa2,0xdb,0x8c,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0xef,0x02,0x1e,0x60,
-0xc2,0x62,0xa2,0xd1,0x7f,0x60,0xff,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,
-0x5a,0xdb,0x6e,0xf3,0x60,0x40,0x02,0x26,0x32,0x00,0x76,0xf5,0x80,0x64,0x29,0xfa,
-0x00,0x63,0x38,0xf2,0x22,0xfc,0x17,0xfa,0x1c,0x64,0x21,0xfa,0x15,0xfc,0x16,0xfc,
-0x01,0x64,0x14,0xfa,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x3c,0x60,0x82,0x62,
-0x3c,0x60,0x2e,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0xc1,0xfe,0x1e,0x60,0xc4,0x62,0x00,0x60,0x01,0x64,0xa2,0xdb,0x48,0x60,
-0x72,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,
-0xff,0x60,0xfe,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,0x86,0xf3,
-0xff,0xff,0x00,0xa0,0xff,0xff,0x2f,0x03,0x3c,0x60,0x28,0x62,0xa2,0xd3,0xff,0xff,
-0x00,0xa8,0x60,0x46,0x25,0x03,0x40,0x48,0x00,0x64,0x40,0x4c,0x3c,0x60,0x2a,0x62,
-0xa2,0xd3,0x28,0x45,0xd4,0x80,0x60,0x46,0x23,0x03,0x2b,0xf2,0xff,0xff,0xff,0xff,
-0x01,0x2a,0x14,0x00,0x09,0xf2,0xff,0xff,0x40,0x4d,0x3c,0x60,0x82,0x62,0x3c,0x60,
-0x28,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0e,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,
-0x01,0x64,0x40,0x4c,0x2d,0x44,0x00,0xbc,0x60,0x46,0xe0,0x03,0x0a,0xf2,0xe1,0x00,
-0x49,0x60,0x99,0x78,0xff,0xff,0x01,0x64,0x6d,0xfb,0x49,0x60,0xc2,0x78,0xff,0xff,
-0x2c,0x44,0x01,0x2a,0x11,0x00,0x28,0x46,0x2b,0xf2,0xff,0xff,0xff,0xff,0x01,0x2a,
-0x0b,0x00,0x3c,0x60,0x82,0x62,0x3c,0x60,0x28,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,
-0x0e,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x3c,0x60,0x28,0x62,0xa2,0xd3,0xff,0xff,
-0x00,0xa8,0x60,0x46,0x2b,0xf2,0xff,0xff,0xff,0xff,0x01,0x2a,0x31,0x00,0x4b,0x60,
-0x58,0x4e,0x03,0x78,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x3c,0x60,
-0x82,0x62,0x3c,0x60,0x2e,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0xc1,0xfe,0x1e,0x60,0xc4,0x62,0x00,0x60,0x01,0x64,0xa2,0xdb,
-0x48,0x60,0xf3,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,
-0xa2,0xd1,0xff,0x60,0xfe,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,
-0x77,0xf5,0x22,0xf2,0xff,0xff,0xff,0xff,0x0f,0x26,0x02,0x00,0x01,0x64,0x6d,0xfb,
-0x01,0x64,0x6f,0xfb,0x3c,0x60,0x28,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,
-0x40,0x48,0x00,0xbc,0x60,0x46,0x03,0x02,0x49,0x60,0x99,0x78,0xff,0xff,0x6c,0xf3,
-0xa4,0xf1,0xff,0xff,0xd0,0x80,0xff,0xff,0x7f,0x0e,0x2b,0xf2,0xff,0xff,0x01,0xb0,
-0x19,0xf2,0x77,0x02,0x00,0xbc,0x60,0x43,0x6b,0x03,0xa3,0xd3,0xff,0xff,0xff,0xff,
-0x02,0x2a,0x63,0x00,0x69,0x60,0x58,0x4e,0x9b,0x78,0xff,0xff,0x6a,0x03,0x69,0x60,
-0x58,0x4e,0xb5,0x78,0xff,0xff,0x65,0x03,0x66,0x44,0x69,0xfb,0x4b,0x60,0x58,0x4e,
-0x0a,0x78,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x3c,0x60,0x82,0x62,
-0x3c,0x60,0x2e,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0xc1,0xfe,0x1e,0x60,0xc4,0x62,0x00,0x60,0x01,0x64,0xa2,0xdb,0x49,0x60,
-0x52,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,
-0xff,0x60,0xfe,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,0x77,0xf5,
-0x22,0xf2,0x69,0xf5,0x0f,0xb0,0x46,0x48,0x07,0x02,0x69,0x60,0x58,0x4e,0xb9,0x78,
-0xff,0xff,0x00,0x64,0x15,0xfa,0x21,0x00,0x69,0x60,0x58,0x4e,0xcc,0x78,0xff,0xff,
-0x15,0xf2,0xff,0xff,0x01,0xa4,0xe7,0xa0,0x15,0xfa,0x23,0x04,0x66,0x60,0xb0,0x62,
-0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x09,0xf2,0xff,0xff,0x40,0x48,0x3c,0x60,
-0x82,0x62,0x3c,0x60,0x4c,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0xce,0xfe,0x28,0x44,0x83,0x00,0x01,0x64,0x6f,0xfb,0x09,0x00,
-0x69,0x60,0x58,0x4e,0x9b,0x78,0xff,0xff,0x04,0x03,0x69,0x60,0x58,0x4e,0xb9,0x78,
-0xff,0xff,0x28,0x46,0x09,0xf2,0xf0,0x00,0x6c,0xf3,0xa4,0xf1,0xff,0xff,0xd0,0x84,
-0xff,0xff,0x23,0x0e,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0x60,0x40,0xff,0x22,
-0x01,0x64,0x60,0x43,0x28,0x60,0x7e,0x62,0xa2,0xdd,0x3c,0x60,0xb2,0x62,0x28,0x60,
-0x7a,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xc4,0x62,
-0x00,0x60,0x04,0x64,0xa2,0xdb,0x49,0x60,0xbe,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x6d,0xf3,0xff,0xff,0x02,0xbc,
-0x6d,0xfb,0x00,0x64,0x6a,0xfb,0x01,0x64,0x8c,0xfb,0xff,0xff,0xc1,0xfe,0x66,0x60,
-0x9a,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x3c,0x03,0x10,0xb4,0x20,0x45,
-0xb4,0x84,0x40,0x40,0x66,0x60,0x9c,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x8c,0xf3,0xff,0xff,0x00,0xa0,0x02,0x64,0x2a,0x03,0x8c,0xfb,0x1e,0x60,0xc2,0x62,
-0xa2,0xd1,0x7f,0x60,0xff,0x64,0xa0,0x84,0xa2,0xdb,0x1e,0x60,0xc4,0x62,0x80,0x60,
-0x00,0x64,0xa2,0xdb,0x49,0x60,0xf6,0x64,0x5a,0xdb,0xcf,0xfe,0xc1,0xfe,0x2f,0x58,
-0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x7f,0x60,0xff,0x64,0xa0,0x84,0xa2,0xdb,
-0x8c,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0xf2,0x02,0x1e,0x60,0xc2,0x62,0xa2,0xd1,
-0x7f,0x60,0xff,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,0x45,0x60,
-0x93,0x78,0xff,0xff,0x67,0x60,0x76,0x65,0xa5,0xd3,0xff,0xff,0x60,0x40,0x03,0x22,
-0x3d,0x00,0x01,0x2a,0x0c,0x00,0x1e,0x60,0x92,0x63,0xa3,0xd1,0x66,0x60,0xf0,0x65,
-0xad,0xf3,0xa5,0xd3,0x60,0x45,0xc4,0x84,0xd0,0x80,0xff,0xff,0x26,0x0d,0x67,0x60,
-0x76,0x65,0xa5,0xd3,0xff,0xff,0x60,0x45,0xfd,0xb4,0xa2,0xdb,0x20,0x44,0xb4,0x84,
-0x40,0x40,0x59,0x60,0xbc,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x0c,0x60,
-0x50,0x62,0x56,0x60,0x53,0x64,0xa2,0xdb,0x0f,0x4e,0x52,0x60,0x58,0x4f,0xaa,0x78,
-0xff,0xff,0x0e,0x4f,0x1e,0x60,0xc4,0x62,0x10,0x60,0x00,0x64,0xa2,0xdb,0x4a,0x60,
-0x4a,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,
-0xa2,0xdb,0x0c,0x60,0x50,0x62,0x67,0x60,0x13,0x64,0xa2,0xdb,0x71,0xf3,0x70,0xf3,
-0x9c,0xa0,0xff,0xff,0x7f,0x04,0x64,0x64,0x71,0xfb,0x66,0x60,0xa8,0x62,0xa2,0xd3,
-0xff,0xff,0x00,0xa0,0xff,0xff,0x76,0x02,0xcb,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,
-0x44,0x02,0x8c,0xf3,0xff,0xff,0xff,0xa0,0x02,0x64,0x2a,0x02,0x8c,0xfb,0x1e,0x60,
-0xc2,0x62,0xa2,0xd1,0x7f,0x60,0xff,0x64,0xa0,0x84,0xa2,0xdb,0x1e,0x60,0xc4,0x62,
-0x80,0x60,0x00,0x64,0xa2,0xdb,0x4a,0x60,0x7f,0x64,0x5a,0xdb,0xcf,0xfe,0xc1,0xfe,
-0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x7f,0x60,0xff,0x64,0xa0,0x84,
-0xa2,0xdb,0x8c,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0xf2,0x02,0x1e,0x60,0xc2,0x62,
-0xa2,0xd1,0x7f,0x60,0xff,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,
-0x68,0x60,0xcc,0x62,0x90,0x60,0x71,0x64,0xa2,0xdb,0x66,0x60,0xa6,0x64,0xa0,0xd3,
-0xff,0xff,0xfd,0xa0,0xff,0xff,0x09,0x02,0x02,0x64,0xa2,0xdb,0x00,0x64,0x70,0xfb,
-0x02,0x65,0x3d,0x60,0x58,0x4e,0x32,0x78,0xff,0xff,0x66,0x60,0x96,0x65,0xa5,0xd1,
-0x01,0x60,0xf4,0x64,0x64,0x43,0xdf,0x83,0xd0,0x80,0xa5,0xdd,0x48,0x05,0x66,0x60,
-0x98,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0x00,0x64,0xa5,0xdb,0x40,0x03,0xd5,0xf3,
-0x03,0x7c,0x00,0xa0,0xff,0xff,0x13,0x03,0xd5,0xf9,0x0a,0x65,0x3d,0x60,0x58,0x4e,
-0x32,0x78,0xff,0xff,0x0c,0x60,0x50,0x62,0x56,0x60,0x53,0x64,0xa2,0xdb,0x1f,0x60,
-0x1c,0x63,0x0e,0x64,0x00,0x7c,0xbd,0xd9,0xcc,0x84,0xff,0xff,0xfc,0x02,0x45,0x60,
-0x93,0x78,0xff,0xff,0xfd,0xa0,0xff,0xff,0x22,0x04,0x66,0x60,0x96,0x62,0x00,0x64,
-0xa2,0xdb,0x70,0xfb,0x66,0x60,0x98,0x62,0x01,0x64,0xa2,0xdb,0x66,0x60,0xa6,0x64,
-0xa0,0xd3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x12,0x03,0x03,0x64,0xa2,0xdb,0x03,0x64,
-0xcb,0xfb,0x01,0x64,0x8c,0xfb,0xff,0xff,0xc1,0xfe,0x68,0x60,0xcc,0x62,0x90,0x60,
-0x70,0x64,0xa2,0xdb,0x01,0x65,0x3d,0x60,0x58,0x4e,0x32,0x78,0xff,0xff,0x69,0x60,
-0x58,0x4e,0x58,0x78,0xff,0xff,0x47,0x60,0xb2,0x78,0xff,0xff,0x77,0xf5,0xff,0x60,
-0xff,0x64,0x2b,0xfa,0x2c,0xfa,0x2d,0xfa,0x0b,0x00,0x77,0xf1,0x2b,0xf2,0x2c,0xf2,
-0x60,0x43,0x64,0x46,0x2c,0xfa,0x2b,0xfc,0x28,0x46,0x2d,0xf2,0x64,0x46,0x2d,0xfa,
-0x90,0x64,0x29,0xfa,0x47,0xf3,0xff,0xff,0xe8,0x84,0xe8,0x84,0x1c,0xfa,0x00,0x63,
-0x38,0xf2,0x22,0xfc,0xff,0xff,0x19,0xfc,0x16,0xfc,0x17,0xfa,0x1c,0x64,0x21,0xfa,
-0x0e,0x64,0x15,0xfa,0x01,0x64,0x14,0xfa,0x2e,0x58,0xff,0xff,0x3a,0x60,0x02,0x62,
-0xa2,0xd3,0x43,0xf1,0x60,0x41,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0x90,0x84,
-0x01,0xb4,0xc5,0xf1,0x0d,0x02,0x61,0x44,0x90,0x84,0x20,0xb4,0xff,0xff,0x08,0x03,
-0x64,0x44,0x20,0x2a,0x04,0x00,0x00,0x64,0x47,0xfb,0x00,0xbc,0x01,0x00,0x01,0xbc,
-0x2e,0x58,0xff,0xff,0x4c,0x60,0x3f,0x78,0xff,0xff,0x78,0xf5,0x51,0x60,0x58,0x4e,
-0x28,0x78,0xff,0xff,0x51,0x60,0x58,0x4e,0xb2,0x78,0xff,0xff,0x85,0xf1,0x10,0x67,
-0xa0,0x84,0xb0,0xbc,0x29,0xfa,0x51,0x60,0x58,0x4e,0xa3,0x78,0xff,0xff,0x06,0x63,
-0x38,0xfc,0x00,0x64,0x22,0xfa,0x3a,0x60,0x58,0x4e,0x14,0x78,0xff,0xff,0x66,0x45,
-0x00,0xf4,0x04,0x61,0x7d,0xf1,0x00,0x64,0x64,0x40,0x02,0x26,0x01,0x64,0xa1,0xda,
-0x01,0x63,0x59,0xdc,0x00,0x64,0x59,0xda,0xa7,0xf1,0x28,0x60,0x7e,0x62,0xa2,0xd9,
-0x3c,0x60,0x82,0x62,0x3c,0x60,0x2e,0x64,0xa2,0xdb,0xf4,0x96,0x7e,0x00,0x00,0x10,
-0x65,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xc1,0xfe,0x01,0x64,
-0x5d,0xfb,0x3c,0x60,0xb2,0x62,0x28,0x60,0x7a,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,
-0xff,0xff,0x1d,0xff,0x1e,0x60,0xc4,0x62,0x00,0x60,0x0c,0x64,0xa2,0xdb,0x4b,0x60,
-0x97,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,
-0x00,0x60,0x08,0x64,0xa0,0x80,0x9c,0x84,0x2e,0x03,0xa0,0x84,0xa2,0xdb,0x3c,0x60,
-0xb2,0x62,0x28,0x60,0x7a,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,
-0x00,0x64,0x5d,0xfb,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x26,0x46,
-0x38,0xf2,0x00,0xf4,0x04,0xf2,0x60,0x41,0x00,0xa8,0xff,0xff,0x03,0x03,0x4c,0x60,
-0x30,0x78,0xff,0xff,0x7d,0xf3,0xff,0xff,0x02,0xb4,0x03,0xf2,0x03,0x02,0x4c,0x60,
-0x02,0x78,0xff,0xff,0x02,0xa8,0x88,0x65,0x38,0x02,0xd5,0x80,0xff,0xff,0x04,0x05,
-0x4c,0x60,0x30,0x78,0xff,0xff,0x43,0x00,0x26,0x46,0x51,0x60,0x58,0x4e,0x28,0x78,
-0xff,0xff,0xff,0x7f,0x00,0x7e,0x0e,0xfa,0x08,0x64,0x28,0xfa,0x85,0xf1,0x10,0x67,
-0xa0,0x84,0xb0,0xbd,0x40,0x67,0xb4,0x84,0x29,0xfa,0x2b,0xf0,0x2e,0xf2,0xff,0xff,
-0x2e,0xf8,0x2b,0xfa,0x2c,0xf0,0xff,0xff,0x2f,0xf2,0x2f,0xf8,0x2c,0xfa,0xff,0xff,
-0x2d,0xf0,0x30,0xf2,0x30,0xf8,0xff,0xff,0x2d,0xfa,0x00,0x64,0x22,0xfa,0x3a,0x60,
-0x58,0x4e,0x14,0x78,0xff,0xff,0x66,0x45,0x00,0xf4,0x04,0x61,0x01,0x64,0xa1,0xda,
-0x03,0x63,0x59,0xdc,0x4b,0x60,0x71,0x78,0xff,0xff,0x04,0xa8,0xff,0xff,0x2e,0x02,
-0x26,0x46,0x3e,0x60,0x58,0x4e,0x91,0x78,0xff,0xff,0x20,0x44,0x08,0xb0,0xff,0xff,
-0x03,0x03,0x4c,0x60,0xde,0x78,0xff,0xff,0x4f,0x60,0xbb,0x78,0xff,0xff,0x00,0x60,
-0x04,0x64,0xa0,0x80,0x9c,0x84,0x17,0x03,0xa0,0x84,0xa2,0xdb,0x00,0x64,0x5d,0xfb,
-0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x51,0x60,0x58,0x4e,0xc1,0x78,
-0xff,0xff,0x20,0x44,0x08,0xb0,0xff,0xff,0x03,0x03,0x4c,0x60,0xde,0x78,0xff,0xff,
-0x4f,0x60,0xbb,0x78,0xff,0xff,0x4b,0x60,0x95,0x78,0xff,0xff,0x26,0x46,0x3e,0x60,
-0x58,0x4e,0x91,0x78,0xff,0xff,0x20,0x44,0x08,0xb0,0xff,0xff,0x03,0x03,0x4c,0x60,
-0x59,0x78,0xff,0xff,0x4e,0x60,0xac,0x78,0xff,0xff,0xa7,0xf1,0x28,0x60,0x7e,0x62,
-0xa2,0xd9,0x79,0xf5,0x51,0x60,0x58,0x4e,0xb2,0x78,0xff,0xff,0x00,0x60,0xb8,0x63,
-0x27,0x60,0xde,0x64,0xa3,0xdb,0x61,0x60,0xbc,0x63,0xbd,0xd3,0xbd,0xd1,0xff,0xff,
-0xb0,0x84,0xa3,0xd1,0xff,0xff,0xb0,0x83,0x61,0x60,0xba,0x62,0xa2,0xdd,0x00,0x60,
-0xb8,0x63,0x01,0x60,0x10,0x65,0xa3,0xd3,0xa5,0xd1,0x04,0xa4,0xa3,0xdb,0xd0,0x80,
-0xa0,0xd3,0x07,0x07,0x40,0x47,0x52,0x60,0x58,0x4e,0x11,0x78,0xff,0xff,0xef,0x02,
-0x0c,0x00,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x66,0x60,0xc2,0x65,
-0x00,0x64,0xa5,0xdb,0x44,0x60,0x83,0x78,0xff,0xff,0x27,0x43,0x3c,0xa3,0xa3,0xd1,
-0x67,0x60,0x48,0x63,0xc9,0xf3,0xa3,0xd9,0xa0,0x84,0xd0,0x80,0xff,0xff,0xd7,0x02,
-0x27,0x43,0x40,0xa3,0xa3,0xd3,0xff,0xff,0x01,0xa0,0x60,0x41,0x05,0x02,0x3e,0x60,
-0x58,0x4e,0xae,0x78,0xff,0xff,0x0d,0x00,0x17,0x60,0x46,0x62,0xa2,0xd3,0xff,0xff,
-0x60,0x45,0xd5,0x84,0x60,0x45,0x01,0x0d,0x00,0x65,0x3e,0x60,0x58,0x4e,0xd6,0x78,
-0xff,0xff,0xcb,0xf3,0xe1,0xf3,0xfc,0xa0,0x00,0xa0,0x05,0x03,0x04,0x03,0x67,0x60,
-0x80,0x62,0x01,0x64,0xa2,0xdb,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0xde,0xfe,
-0xff,0xff,0x0b,0x04,0x1e,0x60,0xc4,0x62,0x40,0x60,0x00,0x64,0xa2,0xdb,0x4c,0x60,
-0x77,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x27,0xd1,0x3c,0x60,0xa2,0x62,
-0xa2,0xd9,0xca,0x82,0x1e,0x64,0xa2,0xdb,0xff,0xff,0x2d,0xff,0x1e,0x60,0xc4,0x62,
-0x20,0x60,0x00,0x64,0xa2,0xdb,0x4c,0x60,0xcb,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0xbe,0xfe,0x1e,0x60,0xa8,0x62,
-0xa2,0xd1,0x40,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x20,0x44,0x08,0xbc,
-0x40,0x40,0x4b,0x60,0x4a,0x78,0xff,0xff,0x79,0xf5,0x51,0x60,0x58,0x4e,0x28,0x78,
-0xff,0xff,0x00,0x64,0x29,0xfa,0x51,0x60,0x58,0x4e,0xa3,0x78,0xff,0xff,0xff,0xff,
-0x47,0xf1,0x00,0xf4,0x04,0x62,0x00,0x60,0x01,0x64,0xb0,0x84,0xa2,0xda,0x0f,0x63,
-0x04,0x61,0x59,0xdc,0x50,0x60,0x58,0x4e,0xef,0x78,0xff,0xff,0x79,0xf5,0x2d,0x44,
-0x08,0xa4,0x38,0xfa,0x00,0x64,0x22,0xfa,0x3a,0x60,0x58,0x4e,0x14,0x78,0xff,0xff,
-0x3c,0x60,0x82,0x62,0x3c,0x60,0x2e,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,
-0x5a,0xdb,0xff,0xff,0x2b,0xff,0xc1,0xfe,0x06,0x64,0x5d,0xfb,0x3c,0x60,0xb2,0x62,
-0x28,0x60,0x7a,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,
-0xc4,0x62,0x00,0x60,0x1c,0x64,0xa2,0xdb,0x4d,0x60,0x24,0x64,0x5a,0xdb,0xcf,0xfe,
-0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,0x08,0x64,0xa0,0x80,
-0x9c,0x84,0x21,0x03,0xa0,0x84,0xa2,0xdb,0x3c,0x60,0xb2,0x62,0x28,0x60,0x7a,0x64,
-0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x00,0x63,0x5d,0xfd,0x1e,0x60,
-0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x26,0x46,0x00,0xf4,0x03,0xf2,0xff,0xff,
-0x00,0xb8,0xff,0xff,0x3d,0x03,0x26,0x46,0x3e,0x60,0x58,0x4e,0x91,0x78,0xff,0xff,
-0x4c,0x60,0x59,0x78,0xff,0xff,0x00,0x60,0x10,0x64,0xa0,0x80,0x9c,0x84,0x1a,0x03,
-0xa0,0x84,0xa2,0xdb,0x3c,0x60,0xb2,0x62,0x28,0x60,0x7a,0x64,0xa2,0xdb,0x03,0x64,
-0x4a,0xdb,0xff,0xff,0x1d,0xff,0x26,0x46,0x3e,0x60,0x58,0x4e,0x91,0x78,0xff,0xff,
-0x00,0x64,0x5d,0xfb,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x4c,0x60,
-0xd8,0x78,0xff,0xff,0x00,0x60,0x04,0x64,0xa0,0x80,0x9c,0x84,0x10,0x03,0xa0,0x84,
-0xa2,0xdb,0x00,0x64,0x5d,0xfb,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,
-0x51,0x60,0x58,0x4e,0xc1,0x78,0xff,0xff,0x4c,0x60,0x59,0x78,0xff,0xff,0xa0,0x00,
-0x1e,0x60,0xda,0x62,0xa2,0xd1,0x00,0x60,0x08,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,
-0xcf,0xfe,0x51,0x60,0x58,0x4e,0x3d,0x78,0xff,0xff,0x51,0x60,0x58,0x4e,0x54,0x78,
-0xff,0xff,0x5a,0x60,0x58,0x4e,0xdb,0x78,0xff,0xff,0x52,0x60,0x58,0x4e,0x48,0x78,
-0xff,0xff,0x51,0x60,0x58,0x4e,0xd4,0x78,0xff,0xff,0x27,0x43,0x11,0x61,0x10,0x65,
-0xc7,0x83,0x59,0x60,0x1e,0x64,0xbd,0xd1,0xcd,0x81,0x58,0xd9,0xfc,0x02,0x26,0x46,
-0x3e,0x60,0x58,0x4e,0x91,0x78,0xff,0xff,0x01,0x64,0x8c,0xfb,0xff,0xff,0xc1,0xfe,
-0x01,0x64,0x90,0xfb,0x01,0x67,0x85,0xfb,0x04,0x64,0xcb,0xfb,0x01,0x65,0x3d,0x60,
-0x58,0x4e,0x32,0x78,0xff,0xff,0x52,0x60,0x58,0x4e,0x74,0x78,0xff,0xff,0x1e,0x60,
-0xb0,0x62,0xa2,0xd1,0x00,0x60,0x02,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,
-0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x00,0x60,0x80,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,
-0xcf,0xfe,0x0c,0x64,0x5d,0xfb,0x1e,0x60,0xc4,0x62,0x00,0x60,0xf0,0x64,0xa2,0xdb,
-0x4d,0x60,0xe0,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,
-0xa2,0xd1,0x00,0x60,0x80,0x64,0xa0,0x80,0x9c,0x84,0x07,0x03,0xa0,0x84,0xa2,0xdb,
-0x61,0x60,0xc8,0x62,0x0e,0x64,0xa2,0xdb,0x17,0x00,0x00,0x60,0x40,0x64,0xa0,0x80,
-0x9c,0x84,0x07,0x03,0xa0,0x84,0xa2,0xdb,0x61,0x60,0xc8,0x62,0x00,0x64,0xa2,0xdb,
-0x0b,0x00,0x00,0x60,0x10,0x64,0xa0,0x80,0x9c,0x84,0x49,0x03,0xa0,0x84,0xa2,0xdb,
-0x61,0x60,0xc8,0x62,0x0a,0x64,0xa2,0xdb,0x5e,0xf3,0xff,0xff,0x01,0xb0,0xff,0xff,
-0x14,0x03,0x1e,0x60,0xb0,0x62,0xa2,0xd1,0x00,0x60,0x04,0x64,0xb0,0x84,0xa2,0xdb,
-0xff,0xff,0xcf,0xfe,0x1e,0x60,0xc4,0x62,0x08,0x60,0x00,0x64,0xa2,0xdb,0x4e,0x60,
-0x1f,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,
-0xa2,0xdb,0x5a,0xdb,0x00,0x64,0x5d,0xfb,0x1e,0x60,0x92,0x62,0xa2,0xd1,0x61,0x60,
-0xd2,0x62,0xa2,0xd9,0x1e,0x60,0x94,0x62,0xa2,0xd1,0x61,0x60,0xd4,0x62,0xa2,0xd9,
-0x4e,0x60,0x58,0x4e,0x5d,0x78,0xff,0xff,0x02,0x64,0x8c,0xfb,0xff,0xff,0xc1,0xfe,
-0x02,0x65,0x3d,0x60,0x58,0x4e,0x32,0x78,0xff,0xff,0x66,0x60,0xc2,0x65,0x64,0x60,
-0x58,0x62,0x00,0x64,0xa2,0xdb,0xa5,0xdb,0x44,0x60,0x83,0x78,0xff,0xff,0x00,0x60,
-0x20,0x64,0xa0,0x80,0x9c,0x84,0x0a,0x03,0xa0,0x84,0xa2,0xdb,0x00,0x63,0x5d,0xfd,
-0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x1b,0x00,0x4d,0x60,0xde,0x78,
-0xff,0xff,0x2f,0x58,0xff,0xff,0x3c,0x60,0xb2,0x62,0x28,0x60,0x7a,0x64,0xa2,0xdb,
-0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x00,0x64,0x5d,0xfb,0x1e,0x60,0xc2,0x62,
-0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x01,0x64,0x8c,0xfb,0xff,0xff,0xc1,0xfe,0x2e,0x58,
-0xff,0xff,0x02,0x64,0x8c,0xfb,0x47,0xf3,0x46,0xfb,0x00,0x60,0xfe,0x65,0x00,0x60,
-0xfc,0x63,0xa5,0xd3,0xa3,0xdb,0xa7,0xf1,0x28,0x60,0x7e,0x62,0xa2,0xd9,0x8c,0xf3,
-0x00,0x65,0xd4,0x80,0xff,0xff,0x0c,0x03,0x1e,0x60,0xc4,0x62,0x80,0x60,0x00,0x64,
-0xa2,0xdb,0x4e,0x60,0x81,0x64,0x5a,0xdb,0xcf,0xfe,0xc1,0xfe,0x2f,0x58,0xff,0xff,
-0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x00,0x60,0xb8,0x63,0x27,0x60,
-0xde,0x64,0xa3,0xdb,0x64,0x60,0x5a,0x62,0x00,0x64,0xa2,0xdb,0x61,0x60,0xbc,0x63,
-0xbd,0xd3,0xbd,0xd1,0xff,0xff,0xb0,0x84,0xa3,0xd1,0xff,0xff,0xb0,0x83,0x61,0x60,
-0xba,0x62,0xa2,0xdd,0x00,0x60,0xb8,0x63,0x01,0x60,0x10,0x65,0xa3,0xd3,0xff,0xff,
-0x02,0xa4,0xa0,0xd1,0xff,0xff,0x64,0x41,0xa5,0xd1,0x02,0xa4,0xa3,0xdb,0xd0,0x80,
-0xa0,0xd3,0x49,0x07,0x40,0x47,0xc4,0xf1,0x58,0xf3,0xff,0xff,0xc0,0x85,0xd5,0x80,
-0x60,0x45,0x0e,0x05,0x61,0x60,0xc8,0x62,0xa2,0xd3,0x1f,0xf1,0xfc,0xa0,0x65,0x44,
-0x3a,0x02,0x64,0x40,0x10,0x2a,0x37,0x00,0x03,0xa5,0xd5,0x80,0x34,0x04,0x07,0x00,
-0x61,0x60,0xc8,0x62,0xa2,0xd3,0xff,0xff,0xfc,0xa0,0xff,0xff,0x13,0x02,0x66,0x60,
-0xbe,0x62,0xa2,0xd1,0x27,0x44,0x3e,0xa4,0xa0,0xd3,0xff,0xff,0x60,0x41,0x02,0xa4,
-0xd0,0x80,0xff,0xff,0xc7,0x07,0xe1,0x85,0xc5,0x85,0x64,0x44,0xe0,0x84,0xd4,0x80,
-0xff,0xff,0xc0,0x04,0x52,0x60,0x58,0x4e,0x11,0x78,0xff,0xff,0xbb,0x02,0x27,0x44,
-0x06,0xa4,0x60,0x41,0xa1,0xd1,0x81,0xf3,0xff,0xff,0xd0,0x80,0x82,0xf1,0x59,0xd3,
-0x68,0x02,0xd0,0x80,0x83,0xf3,0x59,0xd1,0x64,0x02,0xd0,0x80,0xff,0xff,0x61,0x02,
-0x4e,0x60,0xac,0x78,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0xde,0xfe,
-0xff,0xff,0x0b,0x04,0x1e,0x60,0xc4,0x62,0x40,0x60,0x00,0x64,0xa2,0xdb,0x4f,0x60,
-0x05,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x46,0xf3,0x47,0xfb,0x00,0x60,
-0xfc,0x63,0xa3,0xd1,0x3c,0x60,0xa2,0x62,0xa2,0xd9,0xca,0x82,0x1e,0x64,0xa2,0xdb,
-0xff,0xff,0x2d,0xff,0x1e,0x60,0xc4,0x62,0x20,0x60,0x00,0x64,0xa2,0xdb,0x4f,0x60,
-0x2f,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,
-0xa2,0xdb,0xbe,0xfe,0x1e,0x60,0xa8,0x62,0xa2,0xd1,0x40,0x60,0x00,0x64,0xb0,0x84,
-0xa2,0xdb,0xcf,0xfe,0x64,0x60,0x5a,0x62,0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,
-0x10,0x02,0x3c,0x60,0x28,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,0x60,0x46,0x09,0x02,
-0x1e,0x60,0xda,0x62,0xa2,0xd1,0x00,0x60,0x08,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,
-0xcf,0xfe,0x01,0x63,0x8c,0xfd,0xff,0xff,0xc1,0xfe,0x1e,0x60,0xb0,0x62,0xa2,0xd1,
-0x00,0x60,0x02,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x4d,0x60,0xd3,0x78,
-0xff,0xff,0x27,0x43,0x3c,0xa3,0xa3,0xd1,0xc9,0xf3,0xff,0xff,0xa0,0x84,0xd0,0x80,
-0xff,0xff,0x03,0x03,0x4e,0x60,0xac,0x78,0xff,0xff,0x27,0x43,0x40,0xa3,0xa3,0xd3,
-0xff,0xff,0x01,0xa0,0x60,0x41,0x05,0x02,0x3e,0x60,0x58,0x4e,0xae,0x78,0xff,0xff,
-0x0d,0x00,0x17,0x60,0x46,0x62,0xa2,0xd3,0xff,0xff,0x60,0x45,0xd5,0x84,0x60,0x45,
-0x01,0x0d,0x00,0x65,0x3e,0x60,0x58,0x4e,0xd6,0x78,0xff,0xff,0x1e,0x60,0xc2,0x62,
-0x00,0x64,0xa2,0xdb,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,0xc4,0x62,0x40,0x60,
-0x00,0x64,0xa2,0xdb,0x4f,0x60,0x6f,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,
-0x27,0xd1,0x3c,0x60,0xa2,0x62,0xa2,0xd9,0xca,0x82,0x1e,0x64,0xa2,0xdb,0xff,0xff,
-0x2d,0xff,0x1e,0x60,0xc4,0x62,0x20,0x60,0x00,0x64,0xa2,0xdb,0x4f,0x60,0xae,0x64,
-0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,
-0xbe,0xfe,0x1e,0x60,0xa8,0x62,0xa2,0xd1,0x40,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,
-0xcf,0xfe,0x79,0xf5,0x51,0x60,0x58,0x4e,0x28,0x78,0xff,0xff,0x00,0x60,0x20,0x64,
-0x29,0xfa,0x51,0x60,0x58,0x4e,0xa3,0x78,0xff,0xff,0x51,0x60,0x58,0x4e,0xb2,0x78,
-0xff,0xff,0x00,0xf4,0x04,0x61,0x47,0xf1,0x01,0x64,0xb0,0x84,0xa1,0xda,0x0f,0x64,
-0x59,0xda,0x81,0xf1,0xff,0xff,0x59,0xd8,0x82,0xf1,0x59,0xd8,0xff,0xff,0x83,0xf1,
-0x59,0xd8,0x50,0x60,0x58,0x4e,0xef,0x78,0xff,0xff,0x79,0xf5,0x2d,0x44,0x0e,0xa4,
-0x38,0xfa,0x00,0x64,0x22,0xfa,0x3a,0x60,0x58,0x4e,0x14,0x78,0xff,0xff,0x3c,0x60,
-0x82,0x62,0x3c,0x60,0x2e,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0xc1,0xfe,0x14,0x64,0x5d,0xfb,0x3c,0x60,0xb2,0x62,0x28,0x60,
-0x7a,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xc4,0x62,
-0x00,0x60,0x1c,0x64,0xa2,0xdb,0x50,0x60,0x0b,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,0x04,0x64,0xa0,0x80,0x9c,0x84,
-0x14,0x03,0xa0,0x84,0xa2,0xdb,0x00,0x64,0x5d,0xfb,0x1e,0x60,0xc2,0x62,0x00,0x64,
-0xa2,0xdb,0x5a,0xdb,0x51,0x60,0x58,0x4e,0xc1,0x78,0xff,0xff,0x64,0x60,0x5a,0x62,
-0x01,0x64,0xa2,0xdb,0x4e,0x60,0xac,0x78,0xff,0xff,0x00,0x60,0x10,0x64,0xa0,0x80,
-0x9c,0x84,0x15,0x03,0xa0,0x84,0xa2,0xdb,0x3c,0x60,0xb2,0x62,0x28,0x60,0x7a,0x64,
-0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x00,0x64,0x5d,0xfb,0x1e,0x60,
-0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x50,0x60,0xad,0x78,0xff,0xff,0x00,0x60,
-0x08,0x64,0xa0,0x80,0x9c,0x84,0x22,0x03,0xa0,0x84,0xa2,0xdb,0x3c,0x60,0xb2,0x62,
-0x28,0x60,0x7a,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x00,0x64,
-0x5d,0xfb,0x1e,0x60,0xc2,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x26,0x46,0x00,0xf4,
-0xff,0xff,0x03,0xf2,0xff,0xff,0x00,0xb8,0xff,0xff,0x09,0x03,0x26,0x46,0x3e,0x60,
-0x58,0x4e,0x91,0x78,0xff,0xff,0x4e,0x60,0xac,0x78,0xff,0xff,0xa0,0x00,0x1e,0x60,
-0xda,0x62,0xa2,0xd1,0x00,0x60,0x08,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,
-0x51,0x60,0x58,0x4e,0x3d,0x78,0xff,0xff,0x51,0x60,0x58,0x4e,0x54,0x78,0xff,0xff,
-0x5a,0x60,0x58,0x4e,0xdb,0x78,0xff,0xff,0x52,0x60,0x58,0x4e,0x48,0x78,0xff,0xff,
-0x51,0x60,0x58,0x4e,0xd4,0x78,0xff,0xff,0x26,0x46,0x3e,0x60,0x58,0x4e,0x91,0x78,
-0xff,0xff,0x03,0x65,0x3d,0x60,0x58,0x4e,0x32,0x78,0xff,0xff,0x52,0x60,0x58,0x4e,
-0x74,0x78,0xff,0xff,0x01,0x63,0x8c,0xfd,0xff,0xff,0xc1,0xfe,0x1e,0x60,0xb0,0x62,
-0xa2,0xd1,0x00,0x60,0x02,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x1e,0x60,
-0xd4,0x62,0xa2,0xd1,0x00,0x60,0x80,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,
-0x4d,0x60,0xd3,0x78,0xff,0xff,0x20,0x44,0xf7,0xb4,0x40,0x40,0x4b,0x60,0x4a,0x78,
-0xff,0xff,0x5d,0xf1,0x29,0xf2,0x64,0x41,0x60,0x40,0xa0,0x3a,0x0d,0x00,0x08,0xb1,
-0xff,0xff,0x31,0x03,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,0x10,0x64,0xb0,0x84,
-0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x27,0x00,0xc0,0x3a,0x0d,0x00,0x04,0xb1,0xff,0xff,
-0x22,0x03,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,0x10,0x64,0xb0,0x84,0xa2,0xdb,
-0xff,0xff,0xcf,0xfe,0x18,0x00,0xb0,0x3a,0x02,0x00,0x01,0x65,0x07,0x00,0x10,0x3a,
-0x02,0x00,0x02,0x65,0x03,0x00,0x30,0x3a,0x0e,0x00,0x10,0x65,0xa5,0x80,0xff,0xff,
-0x0a,0x03,0x1e,0x60,0xc2,0x62,0xa2,0xd1,0x00,0x60,0x08,0x64,0xb0,0x84,0xa2,0xdb,
-0xff,0xff,0xcf,0xfe,0x00,0x66,0x2f,0x58,0xff,0xff,0x27,0x43,0x12,0xa3,0xbf,0xd1,
-0xff,0xff,0x64,0x47,0x59,0xda,0x64,0x41,0xdd,0x81,0xe9,0x81,0x62,0x44,0x04,0x03,
-0xbd,0xd1,0xcd,0x81,0x58,0xd8,0xfc,0x02,0x58,0x8d,0x17,0x60,0xd2,0x63,0xa3,0xd1,
-0x2d,0x44,0xc8,0x84,0x64,0x45,0x64,0x41,0x03,0xa1,0xe9,0x81,0x41,0x4c,0xbd,0xd1,
-0xcd,0x81,0x58,0xd8,0xfc,0x02,0x2d,0xd2,0x2d,0x43,0x60,0x47,0x01,0x7e,0xa3,0xda,
-0x27,0x44,0x10,0xa4,0xa0,0xd3,0xcb,0x83,0x44,0x8d,0xf8,0x84,0x2c,0x41,0x0c,0x04,
-0xbe,0xd2,0xff,0xff,0x60,0x47,0xbe,0xda,0x00,0x7e,0xa3,0xd2,0x60,0x45,0x00,0x7f,
-0xb4,0x84,0xcd,0x81,0xbd,0xda,0xf4,0x02,0x2e,0x58,0xff,0xff,0x67,0x60,0x48,0x62,
-0xa2,0xd3,0xff,0xff,0xff,0xff,0x01,0x2a,0x02,0x00,0x00,0x64,0x09,0x00,0x02,0x2a,
-0x02,0x00,0x01,0x64,0x05,0x00,0x04,0x2a,0x02,0x00,0x02,0x64,0x01,0x00,0x03,0x64,
-0x13,0xfa,0x2e,0x58,0xff,0xff,0xff,0x60,0xff,0x65,0x66,0x60,0xbe,0x63,0x27,0x42,
-0x3e,0xa2,0xa2,0xd3,0x7f,0x7c,0xa3,0xdb,0xd4,0x80,0x01,0x61,0x02,0x02,0x00,0x61,
-0x65,0x5c,0x66,0x60,0xc0,0x62,0x61,0x44,0xa2,0xdb,0x66,0x60,0xbc,0x62,0xa2,0xd9,
-0x2e,0x58,0xff,0xff,0x27,0x42,0x32,0xa2,0x00,0x61,0x00,0x63,0xa2,0xd3,0xff,0xff,
-0x00,0xbc,0xe0,0x84,0x24,0x03,0x04,0x3a,0x02,0x00,0x01,0xb9,0x1e,0x00,0x08,0x3a,
-0x0a,0x00,0x02,0xb9,0x60,0x40,0x01,0x2b,0x18,0x00,0x01,0x65,0xd7,0x80,0xff,0xff,
-0x14,0x05,0x01,0x63,0x12,0x00,0x16,0x3a,0x0a,0x00,0x04,0xb9,0x60,0x40,0x01,0x2b,
-0x0c,0x00,0x02,0x65,0xd7,0x80,0xff,0xff,0x08,0x05,0x02,0x63,0x06,0x00,0x2c,0x3a,
-0x04,0x00,0x08,0xb9,0x60,0x40,0x01,0x27,0x03,0x63,0x02,0xa2,0xd7,0x00,0x59,0x60,
-0x68,0x62,0xa2,0xdd,0xc9,0xf1,0x59,0x60,0x6a,0x63,0xa1,0x84,0xa3,0xdb,0x60,0x40,
-0x08,0x2a,0x03,0x00,0x03,0x63,0x08,0x64,0x0c,0x00,0x04,0x2a,0x03,0x00,0x02,0x63,
-0x04,0x64,0x07,0x00,0x02,0x2a,0x03,0x00,0x01,0x63,0x02,0x64,0x02,0x00,0x00,0x63,
-0x01,0x64,0x50,0xfb,0x51,0xf1,0x61,0x60,0xd0,0x62,0xa2,0xd9,0x51,0xfd,0x2e,0x58,
-0xff,0xff,0x27,0x43,0x06,0xa3,0xbd,0xd1,0x2b,0xf8,0x31,0xf8,0xff,0xff,0xbd,0xd1,
-0x2c,0xf8,0x32,0xf8,0xff,0xff,0xa3,0xd1,0x2d,0xf8,0x33,0xf8,0x2e,0x58,0xff,0xff,
-0xbd,0xf1,0xff,0xff,0x2e,0xf8,0xbe,0xf1,0x2f,0xf8,0xff,0xff,0xbf,0xf1,0x30,0xf8,
-0xf0,0x60,0x20,0x64,0x0e,0xfa,0x08,0x64,0x28,0xfa,0x2e,0x58,0xff,0xff,0x67,0x60,
-0x66,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xff,0xff,0xa2,0xdb,0x28,0x60,0x60,0x62,
-0x01,0x64,0xa2,0xdb,0xff,0xff,0xc0,0xfe,0x28,0x60,0x60,0x62,0x00,0x64,0xa2,0xdb,
-0x2e,0x58,0xff,0xff,0x27,0x43,0x02,0x65,0xc7,0x85,0xa5,0xd3,0xff,0xff,0x60,0x47,
-0x5a,0xfb,0x04,0x65,0xc7,0x85,0xa5,0xd3,0xff,0xff,0x60,0x47,0x59,0xfb,0x0c,0x65,
-0xc7,0x85,0xa5,0xd3,0x80,0xfb,0xf1,0xa4,0xab,0xfb,0xab,0xf1,0x28,0x60,0xd2,0x62,
-0xa2,0xd9,0x3c,0x60,0xd2,0x65,0x04,0xf0,0x3f,0x60,0xff,0x64,0x84,0xf9,0xa0,0x84,
-0x60,0x41,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xa5,0xdb,0x3c,0x60,0xd0,0x65,0x01,0x64,
-0x07,0xb1,0x03,0x00,0xe0,0x84,0xcd,0x81,0xff,0xff,0xfc,0x02,0xa5,0xdb,0x61,0x60,
-0xca,0x63,0x26,0x46,0x31,0xf0,0x81,0xf9,0xbd,0xd9,0xff,0xff,0x32,0xf0,0x82,0xf9,
-0xbd,0xd9,0xff,0xff,0x33,0xf0,0x83,0xf9,0xa3,0xd9,0x2e,0x58,0xff,0xff,0x27,0x44,
-0x0e,0xa4,0xa0,0xd3,0xff,0xff,0x60,0x41,0xe8,0x84,0xe8,0x84,0xe8,0x84,0x43,0xf3,
-0xe8,0x85,0x94,0x84,0x01,0x26,0x26,0x00,0xc5,0xf1,0x1f,0xf3,0x91,0x80,0x20,0x2a,
-0x05,0x00,0x60,0x40,0x10,0x2a,0x1e,0x00,0x20,0xb1,0x61,0x5c,0x47,0xf9,0x61,0x60,
-0xba,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x12,0x03,0x50,0xfe,0x27,0x41,
-0x06,0xa1,0x61,0x60,0xbc,0x63,0xa1,0xd3,0xbd,0xd1,0x59,0xd3,0xd0,0x80,0xbd,0xd1,
-0x59,0xd3,0xd0,0x80,0xbd,0xd1,0xff,0xff,0xd0,0x80,0xff,0xff,0x01,0x01,0x02,0x00,
-0x00,0x64,0x01,0x00,0x01,0x64,0x00,0xbc,0x2e,0x58,0xff,0xff,0x66,0x60,0xfa,0x63,
-0x00,0x64,0xbd,0xdb,0x01,0x64,0xbd,0xdb,0xa3,0xdb,0x67,0x60,0x00,0x63,0x00,0x64,
-0xbd,0xdb,0x01,0x64,0xbd,0xdb,0xa3,0xdb,0x27,0x44,0x02,0xa4,0xa0,0xd1,0x27,0x44,
-0x04,0xa4,0xa0,0xd3,0xff,0xff,0xd0,0x81,0xff,0xff,0x01,0x05,0x00,0x61,0x66,0x60,
-0xf2,0x63,0x61,0x44,0xbd,0xdb,0xa3,0xdb,0x66,0x60,0xf6,0x62,0xa2,0xd1,0xff,0xff,
-0xd1,0x81,0xff,0xff,0x01,0x05,0x00,0x61,0x66,0x60,0xf8,0x62,0x61,0x44,0xa2,0xdb,
-0x2e,0x58,0xff,0xff,0x59,0x60,0x9c,0x62,0x66,0x60,0x2a,0x63,0x00,0x64,0xa2,0xdb,
-0xbd,0xdb,0xff,0xff,0xbd,0xdb,0xbd,0xdb,0xbd,0xdb,0x67,0x60,0x84,0x62,0x00,0x64,
-0xa2,0xdb,0x2e,0x58,0xff,0xff,0x00,0x64,0x5d,0xfb,0x3c,0x60,0xb0,0x63,0x21,0x44,
-0xbd,0xdb,0xff,0xff,0x1d,0xff,0x01,0x64,0xcb,0xfb,0x1e,0x60,0xc2,0x62,0x00,0x64,
-0xa2,0xdb,0x5a,0xdb,0x00,0x60,0x2a,0x63,0x0c,0x60,0x40,0x61,0x0e,0x60,0x7e,0x64,
-0x58,0xd1,0x59,0xd9,0xfd,0x1f,0x2f,0x58,0xff,0xff,0x1e,0x60,0xc2,0x62,0xa2,0xd1,
-0x00,0x60,0x04,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x2f,0x58,0xff,0xff,
-0x7a,0xf5,0xbd,0xf1,0x2e,0xf8,0xff,0xff,0xbe,0xf1,0x2f,0xf8,0xbf,0xf1,0xff,0xff,
-0x30,0xf8,0x1e,0x60,0x92,0x62,0x67,0x60,0x12,0x63,0xa2,0xd3,0xa3,0xdb,0x7f,0xf3,
-0x00,0x65,0x7e,0xfb,0xcb,0xf3,0xe1,0xf3,0xfc,0xa0,0x00,0xa0,0x05,0x03,0x04,0x03,
-0x67,0x60,0x80,0x62,0x01,0x64,0xa2,0xdb,0xcb,0xf3,0x14,0x7c,0xfc,0xa0,0x0f,0x64,
-0x0a,0x03,0xd5,0xf3,0x28,0x7c,0xfd,0xa0,0x60,0x45,0x2d,0x64,0x04,0x04,0x01,0x60,
-0x86,0x64,0x01,0x03,0x78,0x64,0xaf,0xf9,0xb1,0xfb,0x1e,0x60,0xbc,0x62,0x00,0x64,
-0xa2,0xdb,0x20,0x44,0x03,0x26,0x07,0x00,0x00,0x64,0x8a,0xfb,0x27,0x60,0xe0,0x64,
-0x88,0xfb,0x89,0xfb,0x0a,0x00,0x40,0x2a,0x04,0x00,0x18,0x60,0xf6,0x62,0xa2,0xd3,
-0xb1,0xfb,0x3d,0x60,0x4c,0x62,0x00,0x64,0xa2,0xdb,0x65,0x44,0xfd,0xa0,0xff,0xff,
-0x71,0x05,0x20,0x40,0x40,0x26,0x6e,0x00,0x10,0x60,0x00,0x65,0x85,0xf3,0x7a,0xf5,
-0xa4,0x84,0x40,0x7e,0x29,0xfa,0x17,0x60,0xde,0x64,0xa0,0xd1,0x13,0xf8,0xff,0xff,
-0x5b,0xf3,0x00,0xf4,0x60,0x43,0xbd,0xd1,0x04,0x65,0x20,0x40,0x80,0x26,0x00,0x7c,
-0x64,0x47,0xa5,0xda,0x64,0x41,0xdd,0x81,0xe9,0x81,0x62,0x44,0x04,0x03,0xbd,0xd1,
-0xcd,0x81,0x58,0xd8,0xfc,0x02,0x58,0x8d,0x17,0x60,0xd2,0x63,0xa3,0xd1,0x2d,0x44,
-0xc8,0x84,0x64,0x45,0x64,0x41,0x03,0xa1,0xe9,0x81,0x41,0x4c,0xbd,0xd1,0xcd,0x81,
-0x58,0xd8,0xfc,0x02,0x2d,0xd2,0x2d,0x43,0x60,0x47,0x01,0x7e,0x5b,0xf1,0xa3,0xda,
-0xa4,0xd3,0xcb,0x83,0x20,0x40,0x80,0x26,0x00,0x64,0x44,0x8d,0xf8,0x84,0x2c,0x41,
-0x0c,0x04,0xbe,0xd2,0xff,0xff,0x60,0x47,0xbe,0xda,0x00,0x7e,0xa3,0xd2,0x60,0x45,
-0x00,0x7f,0xb4,0x84,0xcd,0x81,0xbd,0xda,0xf4,0x02,0x7a,0xf5,0x2d,0x44,0x04,0xa4,
-0x38,0xfa,0x66,0x60,0xc0,0x62,0xa2,0xd1,0x66,0x60,0xb8,0x62,0xa2,0xd9,0x1f,0x60,
-0x5a,0x63,0xbf,0xf3,0x71,0x5c,0x60,0x47,0xc0,0x84,0x1f,0xb5,0x01,0xb4,0xa3,0xdb,
-0x1f,0x60,0x56,0x62,0xa2,0xd3,0x65,0x41,0x60,0x45,0x61,0x44,0xd4,0x80,0xff,0xff,
-0x02,0x04,0xd4,0x84,0xfb,0x00,0x60,0x45,0x1f,0x60,0x54,0x62,0xa2,0xd3,0xff,0xff,
-0xc4,0x84,0x40,0x4a,0x1f,0x60,0x58,0x62,0x00,0x64,0xa2,0xdb,0x13,0x00,0xd5,0xf3,
-0xff,0xff,0xfd,0xa0,0xfc,0xa0,0x09,0x03,0x05,0x02,0x01,0x60,0x86,0x64,0xb1,0xfb,
-0x03,0x64,0xd5,0xfb,0x54,0x60,0xfb,0x78,0xff,0xff,0x04,0x64,0xd5,0xfb,0x52,0x60,
-0xf4,0xa6,0x7e,0x00,0x00,0x10,0xb3,0x78,0xff,0xff,0xd5,0xf1,0x1f,0x60,0x58,0x62,
-0xa2,0xd3,0xff,0xff,0xf2,0xa0,0xff,0xff,0xe5,0x03,0x01,0xa4,0xa2,0xdb,0x1f,0x60,
-0x5a,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x07,0x03,0x2a,0x44,0xdc,0x84,
-0xf2,0xa0,0xff,0xff,0x08,0x06,0x01,0x64,0x06,0x00,0x2a,0x44,0xcc,0x84,0xff,0xa0,
-0xff,0xff,0x01,0x05,0x0e,0x64,0x40,0x4a,0x1f,0x60,0x1a,0x63,0x20,0x40,0x40,0x26,
-0x0e,0x00,0x64,0x44,0x04,0x36,0x0b,0x00,0x03,0x3a,0x02,0x00,0x1f,0x60,0x36,0x63,
-0x2a,0x44,0xe0,0x85,0x47,0xd3,0xff,0xff,0x01,0xb0,0xff,0xff,0xce,0x03,0x1e,0x60,
-0xbc,0x62,0x00,0x64,0xa2,0xdb,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,0xbe,0x62,
-0x40,0x60,0x00,0x64,0xa2,0xdb,0x53,0x60,0xae,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x3c,0x60,0xa2,0x62,0x2a,0x44,0xa2,0xdb,0xca,0x82,0x1e,0x64,0xa2,0xdb,
-0xff,0xff,0x2d,0xff,0x1e,0x60,0xbe,0x62,0x20,0x60,0x00,0x64,0xa2,0xdb,0x53,0x60,
-0xd4,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0xbe,0xfe,0x1e,0x60,0xa8,0x62,
-0xa2,0xd1,0x40,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x02,0x0a,0x00,0x64,
-0x57,0xfb,0x20,0x44,0x40,0x2a,0x09,0x00,0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x00,0x60,
-0x80,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x20,0x44,0x40,0x22,0x03,0x00,
-0x54,0x60,0x72,0x78,0xff,0xff,0xd5,0xf3,0xff,0xff,0xfd,0xa0,0x7a,0xf5,0x7b,0x05,
-0x00,0x64,0x22,0xfa,0x3a,0x60,0x58,0x4e,0x14,0x78,0xff,0xff,0x3c,0x60,0x82,0x62,
-0x3c,0x60,0x2e,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,
-0x2b,0xff,0x1e,0x60,0xbc,0x62,0x00,0x64,0xa2,0xdb,0xaf,0xf1,0x28,0x60,0x66,0x62,
-0xa2,0xd9,0x3c,0x60,0xba,0x62,0x28,0x60,0x62,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,
-0xff,0xff,0x1d,0xff,0xc1,0xfe,0x1e,0x60,0xbe,0x62,0x00,0x60,0x05,0x64,0xa2,0xdb,
-0x54,0x60,0x25,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xbc,0x62,
-0xa2,0xd1,0x00,0x60,0x01,0x64,0xa0,0x80,0x9c,0x84,0x0c,0x03,0xa0,0x84,0xa2,0xdb,
-0x3c,0x60,0xba,0x62,0x28,0x60,0x62,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,
-0x1d,0xff,0x11,0x00,0x67,0x60,0x10,0x62,0xa2,0xd3,0xff,0xff,0x01,0xa4,0xa2,0xdb,
-0x28,0x60,0x60,0x62,0x01,0x64,0xa2,0xdb,0xff,0xff,0xc0,0xfe,0x00,0x64,0xa2,0xdb,
-0x53,0x60,0x7c,0x78,0xff,0xff,0x1e,0x60,0xbc,0x62,0x00,0x64,0xa2,0xdb,0xb1,0xf1,
-0x28,0x60,0x66,0x62,0xa2,0xd9,0x3c,0x60,0xba,0x62,0x28,0x60,0x62,0x64,0xa2,0xdb,
-0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xbe,0x62,0x00,0x60,0x04,0x64,
-0xa2,0xdb,0x54,0x60,0x66,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,
-0xbc,0x62,0x00,0x64,0xa2,0xdb,0x57,0xf3,0xff,0xff,0x00,0xa8,0xff,0xff,0x2e,0x02,
-0x53,0x60,0x7c,0x78,0xff,0xff,0x1e,0x60,0xbc,0x62,0x00,0x64,0xa2,0xdb,0xb1,0xf1,
-0x28,0x60,0x66,0x62,0xa2,0xd9,0x3c,0x60,0xba,0x62,0x28,0x60,0x62,0x64,0xa2,0xdb,
-0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xbe,0x62,0x00,0x60,0x04,0x64,
-0xa2,0xdb,0x54,0x60,0x8e,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,
-0xbc,0x62,0x00,0x64,0xa2,0xdb,0xd5,0xf3,0xff,0xff,0xff,0xa0,0xff,0xff,0x03,0x03,
-0x53,0x60,0x7c,0x78,0xff,0xff,0x52,0x60,0xb3,0x78,0xff,0xff,0xb1,0xf1,0x28,0x60,
-0x66,0x62,0xa2,0xd9,0xb2,0xf1,0x28,0x60,0x72,0x62,0xa2,0xd9,0x3c,0x60,0xba,0x62,
-0x28,0x60,0x62,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x3c,0x60,
-0xbe,0x62,0x28,0x60,0x6e,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,
-0x1e,0x60,0xbe,0x62,0x00,0x60,0x0c,0x64,0xa2,0xdb,0x54,0x60,0xc2,0x64,0x5a,0xdb,
-0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xbc,0x62,0xa2,0xd1,0x00,0x60,0x04,0x64,
-0xa0,0x80,0x9c,0x84,0x0e,0x03,0xa0,0x84,0xa2,0xdb,0x3c,0x60,0xbe,0x62,0x28,0x60,
-0x6e,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x53,0x60,0x7c,0x78,
-0xff,0xff,0x00,0x60,0x08,0x64,0xa0,0x80,0x9c,0x84,0xe3,0x03,0xa0,0x84,0xa2,0xdb,
-0x57,0xf3,0x10,0x0a,0x00,0xa0,0x00,0x64,0x0c,0x02,0x3c,0x60,0xba,0x62,0x28,0x60,
-0x62,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x53,0x60,0x7c,0x78,
-0xff,0xff,0x57,0xfb,0x3c,0x60,0xbe,0x62,0x28,0x60,0x6e,0x64,0xa2,0xdb,0x02,0x64,
-0x4a,0xdb,0xff,0xff,0x1d,0xff,0xc5,0x00,0x1e,0x60,0x92,0x62,0x67,0x60,0x12,0x63,
-0xa2,0xd3,0xa3,0xd1,0xff,0xff,0xd0,0x85,0x67,0x60,0x0e,0x62,0xa2,0xd3,0xff,0xff,
-0xd4,0x80,0xff,0xff,0x02,0x05,0x65,0x44,0xa2,0xdb,0xcb,0xf3,0xe1,0xf3,0xfc,0xa0,
-0x00,0xa0,0x05,0x03,0x04,0x03,0x67,0x60,0x80,0x62,0x41,0x64,0xa2,0xdb,0x1e,0x60,
-0x94,0x62,0x66,0x60,0xb6,0x63,0xa2,0xd3,0xff,0xff,0x0c,0xa4,0xa3,0xdb,0x59,0x60,
-0xb2,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1e,0x60,0xbc,0x62,0x00,0x64,
-0xa2,0xdb,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,0xbe,0x62,0x40,0x60,0x00,0x64,
-0xa2,0xdb,0x55,0x60,0x24,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x7e,0xf1,
-0x3c,0x60,0xa2,0x62,0xa2,0xd9,0xca,0x82,0x1e,0x64,0xa2,0xdb,0xff,0xff,0x2d,0xff,
-0x1e,0x60,0xbe,0x62,0x20,0x60,0x00,0x64,0xa2,0xdb,0x55,0x60,0x4a,0x64,0x5a,0xdb,
-0xcf,0xfe,0x2f,0x58,0xff,0xff,0xbe,0xfe,0x1e,0x60,0xa8,0x62,0xa2,0xd1,0x40,0x60,
-0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x20,0x44,0x03,0x22,0x08,0x00,0x04,0x65,
-0x3d,0x60,0x58,0x4e,0x32,0x78,0xff,0xff,0x56,0x60,0x1b,0x78,0xff,0xff,0x8a,0xf1,
-0x1f,0x60,0x60,0x63,0xc3,0x85,0x45,0x4d,0x27,0x60,0xe0,0x65,0x89,0xf3,0x45,0x4c,
-0x40,0x48,0x59,0x60,0x8a,0x62,0x28,0x44,0xd4,0x84,0xe8,0x84,0xe8,0x84,0xa2,0xdb,
-0x2d,0x45,0xd7,0x80,0x02,0x65,0x18,0x05,0x47,0xd1,0x02,0x65,0x47,0xd3,0x0a,0x65,
-0xd0,0x81,0x47,0xd3,0x01,0x05,0x00,0x61,0xf2,0xa3,0x01,0xb0,0x61,0x44,0x06,0x03,
-0x2c,0x42,0xa2,0xdb,0x5a,0xdd,0x5a,0x8c,0x44,0xa3,0xea,0x00,0x28,0x42,0x4a,0xdd,
-0x4a,0xdb,0x42,0x48,0x44,0xa3,0xe4,0x00,0x28,0x44,0x88,0xfb,0x88,0xf1,0x27,0x60,
-0xe0,0x63,0x66,0x60,0xb8,0x62,0xa2,0xd3,0xff,0x65,0xff,0xa0,0xff,0xff,0x01,0x03,
-0x00,0x65,0x45,0x4c,0x44,0x48,0x28,0x45,0xd7,0x80,0xa3,0xd1,0x36,0x05,0x5a,0xd3,
-0xff,0xff,0x3e,0xa4,0xa0,0xd3,0xff,0xff,0x60,0x45,0xff,0x64,0xd4,0x85,0x2c,0x44,
-0xa4,0x85,0x64,0x47,0xb4,0x9c,0x63,0x42,0x04,0x65,0x46,0xd3,0x28,0x45,0xd6,0x80,
-0xff,0xff,0x02,0x04,0x04,0xa3,0xe7,0x00,0x62,0x46,0x60,0x41,0x5a,0xd3,0xff,0xff,
-0x3e,0xa4,0xa0,0xd3,0xff,0xff,0x60,0x45,0xff,0x64,0xd4,0x85,0x2c,0x44,0xa4,0x85,
-0x61,0x47,0xb4,0x84,0xd0,0x80,0x66,0x42,0xe7,0x06,0x60,0x41,0xa2,0xd3,0x5a,0xd1,
-0xa3,0xd1,0x64,0x45,0xbd,0xdb,0xa3,0xd3,0x66,0x42,0xa2,0xd9,0x5a,0xdb,0x65,0x44,
-0xa3,0xdb,0x61,0x5c,0xfe,0xa3,0x66,0x42,0xd7,0x00,0x88,0xf3,0x89,0xf1,0x60,0x43,
-0x66,0x60,0xb8,0x62,0xa2,0xd3,0xff,0x65,0xff,0xa0,0xff,0xff,0x01,0x03,0x00,0x65,
-0x45,0x4c,0x44,0x48,0x28,0x45,0xd7,0x80,0xa3,0xd1,0x36,0x05,0x5a,0xd3,0xff,0xff,
-0x3e,0xa4,0xa0,0xd3,0xff,0xff,0x60,0x45,0xff,0x64,0xd4,0x85,0x2c,0x44,0xa4,0x85,
-0x64,0x47,0xb4,0x9c,0x63,0x42,0x04,0x65,0x46,0xd3,0x28,0x45,0xd6,0x80,0xff,0xff,
-0x02,0x04,0x04,0xa3,0xe7,0x00,0x62,0x46,0x60,0x41,0x5a,0xd3,0xff,0xff,0x3e,0xa4,
-0xa0,0xd3,0xff,0xff,0x60,0x45,0xff,0x64,0xd4,0x85,0x2c,0x44,0xa4,0x85,0x61,0x47,
-0xb4,0x84,0xd0,0x80,0x66,0x42,0xe7,0x06,0x60,0x41,0xa2,0xd3,0x5a,0xd1,0xa3,0xd1,
-0x64,0x45,0xbd,0xdb,0xa3,0xd3,0x66,0x42,0xa2,0xd9,0x5a,0xdb,0x65,0x44,0xa3,0xdb,
-0x61,0x5c,0xfe,0xa3,0x66,0x42,0xd7,0x00,0x20,0x44,0x3c,0xb4,0x40,0x40,0x1e,0x60,
-0xa8,0x62,0xa2,0xd1,0x10,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x1e,0x60,
-0xbc,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x2f,0x58,0xff,0xff,0x3c,0x60,0xb2,0x62,
-0x28,0x60,0x62,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,
-0xbc,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x2f,0x58,0xff,0xff,0x1e,0x60,0xbc,0x62,
-0xa2,0xd1,0x00,0x60,0x04,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x1e,0x60,0xbc,0x62,0xa2,0xd1,0x00,0x60,0x08,0x64,0xb0,0x84,0xa2,0xdb,
-0xff,0xff,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x20,0x40,0x03,0x26,0x06,0x00,0x1f,0x60,
-0x60,0x63,0x8a,0xf1,0x08,0x60,0x80,0x64,0x23,0x00,0x3d,0x60,0x4c,0x63,0xbd,0xd3,
-0xff,0xff,0x00,0xa0,0x60,0x45,0x15,0x03,0xc7,0x85,0x63,0x5c,0x04,0x64,0xc0,0x81,
-0x31,0xf2,0x50,0xfe,0x59,0xd1,0x32,0xf2,0xd0,0x80,0x59,0xd1,0x33,0xf2,0xd0,0x80,
-0x59,0xd1,0xff,0xff,0xd0,0x80,0xff,0xff,0x76,0x03,0x44,0xa3,0xd7,0x80,0xff,0xff,
-0xec,0x02,0x3d,0x60,0x4e,0x63,0x3d,0x60,0x4c,0x62,0xa2,0xd1,0x1a,0x60,0x90,0x64,
-0xd0,0x80,0xff,0xff,0x68,0x06,0xc3,0x83,0x87,0xfd,0xff,0xff,0x7f,0xf3,0x25,0xf0,
-0xbd,0xdb,0x64,0x44,0x00,0x7f,0xbd,0xdb,0x64,0x47,0x00,0x7f,0xbd,0xdb,0x31,0xf0,
-0xbd,0xd9,0xff,0xff,0x32,0xf0,0xbd,0xd9,0x33,0xf0,0xff,0xff,0xbd,0xd9,0xff,0x60,
-0xff,0x7c,0x38,0xf2,0x00,0xf4,0x05,0xa4,0xa0,0xd8,0x06,0xf0,0xff,0xff,0xbd,0xd9,
-0x07,0xf0,0xbd,0xd9,0x20,0x44,0x03,0xb4,0xff,0xff,0x0a,0x02,0x16,0x60,0x42,0x62,
-0xa2,0xd3,0xff,0xff,0x60,0x40,0x01,0x3a,0x03,0x00,0x64,0x40,0x01,0x2a,0x3b,0x00,
-0x57,0x60,0x58,0x4e,0x59,0x78,0xff,0xff,0xff,0x60,0xfe,0x64,0xd0,0x80,0xff,0xff,
-0x32,0x03,0xd5,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x2d,0x05,0x00,0x36,0x12,0x00,
-0x66,0x60,0xc4,0x64,0xa0,0xd3,0xff,0xff,0x00,0xa0,0x60,0x43,0x0b,0x03,0x58,0x60,
-0x58,0x4e,0xb5,0x78,0xff,0xff,0x17,0x60,0x82,0x64,0xa0,0xd1,0x65,0x44,0xd0,0x80,
-0xff,0xff,0x60,0x02,0x87,0xf3,0xff,0xff,0x3e,0xa4,0x60,0x43,0xbd,0xd9,0x61,0x44,
-0xbd,0xdb,0xff,0x60,0xff,0x64,0xd0,0x80,0xff,0xff,0x04,0x02,0x66,0x60,0xb8,0x62,
-0x00,0x64,0xa2,0xdb,0x87,0xf3,0x10,0x65,0xc4,0x83,0x00,0x64,0x08,0xf0,0xa3,0xdb,
-0x64,0x47,0x60,0x45,0x00,0x3b,0x46,0x00,0xbd,0xdb,0xdc,0x84,0xe8,0x81,0x10,0x64,
-0x58,0xd0,0xcd,0x81,0xbd,0xd9,0xfc,0x02,0xd8,0x83,0x04,0x64,0x40,0x4d,0x07,0x61,
-0x65,0x40,0x01,0x2a,0xbd,0xd0,0xff,0xff,0x64,0x44,0x00,0x7f,0x2d,0xda,0x5a,0x8d,
-0x64,0x47,0x00,0x7f,0x2d,0xda,0xcd,0x81,0x5a,0x8d,0xf4,0x02,0x87,0xf1,0x32,0x63,
-0xc3,0x83,0x04,0x61,0x65,0x40,0x01,0x26,0x02,0xa1,0xa1,0xd2,0xff,0xff,0x01,0xa8,
-0x59,0xd2,0x20,0x02,0x59,0xd0,0xcc,0x84,0xbd,0xd9,0xfc,0x02,0x00,0x64,0xbd,0xdb,
-0x59,0xd2,0x59,0xd0,0x03,0xa8,0x7f,0xf3,0x15,0x02,0x59,0xd0,0xff,0xff,0xd0,0x80,
-0xff,0xff,0x10,0x02,0x87,0xf3,0x32,0x65,0xc4,0x83,0x00,0x61,0xa3,0xd3,0xff,0xff,
-0x60,0x40,0xff,0x22,0x14,0x00,0x80,0x2a,0x10,0x00,0x60,0x40,0x82,0x3a,0x03,0x00,
-0x01,0xb9,0x0b,0x00,0x24,0x00,0x84,0x3a,0x02,0x00,0x02,0xb9,0x06,0x00,0x8b,0x3a,
-0x02,0x00,0x04,0xb9,0x02,0x00,0x96,0x36,0x08,0xb9,0x02,0xa3,0xe7,0x00,0x87,0xf3,
-0x3c,0x65,0xc4,0x82,0x61,0x43,0xa2,0xdd,0x20,0x40,0x03,0x26,0x09,0x00,0x8a,0xf3,
-0xff,0xff,0x44,0xa4,0x8a,0xfb,0x89,0xf3,0xff,0xff,0x04,0xa4,0xa2,0xdb,0x07,0x00,
-0x3d,0x60,0x4c,0x62,0xa2,0xd3,0xff,0xff,0x44,0xa4,0xa2,0xdb,0xff,0xff,0x26,0x46,
-0x2f,0x58,0xff,0xff,0x66,0x60,0xca,0x62,0x2e,0x44,0xa2,0xdb,0x66,0x60,0xce,0x65,
-0x66,0x60,0xc4,0x62,0x00,0x64,0xa2,0xdb,0xa5,0xdb,0x01,0xf2,0x10,0x63,0x00,0x7f,
-0xf4,0xa4,0x60,0x41,0x10,0x63,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,
-0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0x60,0x5c,0x63,0x44,
-0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,
-0x01,0xa3,0x00,0x7f,0x60,0x45,0x64,0x44,0xad,0xa8,0x07,0xa8,0x0c,0x03,0x15,0x03,
-0xc9,0x81,0xd5,0x81,0x61,0x44,0x80,0x27,0x03,0x00,0xff,0xa0,0xc7,0x83,0xdb,0x07,
-0x58,0x60,0x61,0x78,0xff,0xff,0x66,0x60,0xcc,0x62,0x5a,0xdd,0x65,0x44,0x5a,0xdb,
-0x61,0x44,0x5a,0xdb,0x58,0x60,0x1e,0x78,0xff,0xff,0x66,0x60,0xc4,0x62,0xa2,0xdd,
-0xd5,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0xe3,0x04,0x17,0x60,0x82,0x61,0x58,0x60,
-0x58,0x4e,0xb5,0x78,0xff,0xff,0x65,0x44,0xa1,0xdb,0x08,0xa1,0xa1,0xdb,0x02,0xa1,
-0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,
-0xa0,0xd2,0x01,0xa3,0x00,0x7f,0xa1,0xdb,0x1f,0x60,0x54,0x61,0x63,0x44,0x01,0x22,
-0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,
-0x00,0x7f,0xa1,0xdb,0x1f,0x60,0x56,0x61,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,
-0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0xf2,0xa0,
-0xff,0xff,0x01,0x06,0x0e,0x64,0xa1,0xdb,0x17,0x60,0x80,0x61,0x63,0x44,0x01,0x22,
-0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,
-0x00,0x7f,0xa1,0xdb,0x3e,0x60,0x58,0x4e,0xae,0x78,0xff,0xff,0x1f,0x60,0x56,0x61,
-0xa1,0xd1,0x1f,0x60,0x1c,0x63,0x1f,0x60,0x54,0x62,0xa2,0xd3,0x01,0x61,0x00,0x65,
-0xff,0xa0,0xff,0xff,0x04,0x03,0xe1,0x81,0xcc,0x84,0x02,0xa3,0xf9,0x00,0x64,0x44,
-0x05,0x7c,0xb5,0x85,0xbd,0xd9,0xcc,0x84,0x00,0xa0,0xe1,0x81,0xfa,0x02,0x65,0x44,
-0xa5,0xfb,0x16,0x60,0x42,0x62,0xa2,0xd3,0x01,0x7c,0x04,0xa8,0xd5,0xf9,0x05,0x02,
-0x0c,0x60,0x50,0x62,0x67,0x60,0x13,0x64,0xa2,0xdb,0x0b,0x65,0x3d,0x60,0x58,0x4e,
-0x32,0x78,0xff,0xff,0xff,0x60,0xfe,0x7c,0x58,0x60,0x65,0x78,0xff,0xff,0x45,0x4d,
-0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0xa0,0x60,0x00,0x64,0xd4,0x80,0xff,0xff,
-0x39,0x02,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,
-0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0xf8,0x65,0xd4,0x80,0xff,0xff,0x2a,0x02,
-0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0x65,0x5c,0x58,0x60,0x58,0x4e,0xb5,0x78,
-0xff,0xff,0x67,0x41,0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0x2d,0x44,0xf1,0xa0,
-0xff,0xff,0x1c,0x04,0x45,0x4d,0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0x65,0x41,
-0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0x66,0x60,0xc6,0x62,0x65,0x44,0xa2,0xdb,
-0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0x66,0x60,0xc8,0x62,0x65,0x44,0xa2,0xdb,
-0x2d,0x45,0x04,0x00,0xff,0x60,0xff,0x7c,0x64,0x41,0x00,0x65,0x66,0x60,0xca,0x62,
-0xa2,0xd3,0xff,0xff,0x40,0x4e,0x2e,0x58,0xff,0xff,0x66,0x60,0xca,0x62,0x2e,0x44,
-0xa2,0xdb,0x66,0x60,0xcc,0x62,0x5a,0xd3,0x5a,0xd1,0x00,0xa0,0x60,0x43,0x36,0x03,
-0x5a,0xd3,0x64,0x45,0x60,0x41,0x1c,0x00,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,
-0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0x60,0x5c,
-0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,
-0xa0,0xd2,0x01,0xa3,0x00,0x7f,0x60,0x45,0x64,0x44,0xae,0xa8,0xff,0xff,0x07,0x03,
-0xc9,0x81,0xd5,0x81,0x61,0x44,0xff,0xa0,0xc7,0x83,0xde,0x07,0x0f,0x00,0xdf,0x83,
-0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0xf8,0x60,0xa0,0x64,0xd4,0x80,0xff,0xff,
-0x05,0x02,0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0x01,0x00,0x00,0x65,0x66,0x60,
-0xca,0x62,0xa2,0xd3,0xff,0xff,0x40,0x4e,0x2e,0x58,0xff,0xff,0x63,0x44,0x01,0x22,
-0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,
-0x00,0x7f,0x60,0x45,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,
-0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0x60,0x47,0xb4,0x85,0x2e,0x58,
-0xff,0xff,0x2f,0x58,0xff,0xff,0x1e,0x60,0xda,0x62,0xa2,0xd1,0x00,0x60,0x04,0x64,
-0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x2f,0x58,0xff,0xff,0xbb,0xf1,0x28,0x60,
-0xae,0x62,0xa2,0xd9,0x3c,0x60,0xc2,0x62,0x28,0x60,0xaa,0x64,0xa2,0xdb,0x02,0x64,
-0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xdc,0x62,0x00,0x60,0x0c,0x64,0xa2,0xdb,
-0x58,0x60,0xf5,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xda,0x62,
-0xa2,0xd1,0x00,0x60,0x08,0x64,0xa0,0x80,0x9c,0x84,0x0c,0x03,0xa0,0x84,0xa2,0xdb,
-0x0f,0x47,0x67,0x60,0x42,0x62,0x6f,0x60,0x00,0x64,0xa2,0xdb,0x58,0x4f,0x29,0x00,
-0x07,0x4f,0xd4,0x00,0x1e,0x60,0xda,0x62,0xa2,0xd1,0xff,0x60,0xfb,0x61,0xa1,0x84,
-0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,0xcb,0xf3,0xff,0xff,0xfc,0xa0,0xff,0xff,
-0xc5,0x02,0x17,0x60,0x7a,0x62,0xa2,0xd3,0xff,0xff,0xff,0xff,0x02,0x2a,0xbe,0x00,
-0x0f,0x47,0x67,0x60,0x42,0x62,0x66,0x60,0x00,0x64,0xa2,0xdb,0x58,0x4f,0x09,0x00,
-0x67,0x60,0x42,0x62,0x69,0x60,0x00,0x64,0xa2,0xdb,0x58,0x4f,0x02,0x00,0x07,0x4f,
-0xad,0x00,0x48,0xf1,0x0f,0x4e,0x64,0x41,0x41,0x4d,0x40,0xa1,0xa2,0xff,0x19,0x60,
-0x58,0x4f,0xa2,0x78,0xff,0xff,0xa3,0xff,0x06,0x03,0x2d,0x41,0x19,0x60,0x58,0x4f,
-0xc4,0x78,0xff,0xff,0x08,0xfe,0x0e,0x4f,0x03,0x02,0x5a,0x60,0xd9,0x78,0xff,0xff,
-0x67,0x60,0x42,0x64,0xa0,0xd3,0xff,0xff,0x60,0x47,0xff,0xff,0x66,0x3a,0x59,0x00,
-0x50,0xf1,0x5b,0x60,0x38,0x64,0xa0,0xd9,0x7f,0xf1,0x5b,0x60,0x3a,0x64,0xa0,0xd9,
-0x84,0xf1,0x5b,0x60,0x42,0x64,0xa0,0xd9,0xcb,0xf1,0x5b,0x60,0x44,0x64,0xa0,0xd9,
-0xbd,0xf1,0x5b,0x60,0x46,0x64,0xa0,0xd9,0xc7,0xf1,0x5b,0x60,0x4c,0x64,0xa0,0xd9,
-0xc8,0xf1,0x5b,0x60,0x4e,0x64,0xa0,0xd9,0x5b,0x60,0x3c,0x63,0x81,0xf1,0xbd,0xd9,
-0x81,0xf1,0xff,0xff,0xbd,0xd9,0x81,0xf1,0xa3,0xd9,0x59,0x60,0xa6,0x65,0x5b,0x60,
-0x50,0x64,0x65,0x41,0xd4,0x85,0xfe,0xa1,0x65,0x43,0x0a,0xa3,0x38,0xfc,0x46,0x48,
-0x00,0xf4,0x04,0x63,0x0a,0xa3,0x81,0x60,0x87,0x64,0x02,0xfa,0x67,0x60,0x42,0x64,
-0xa0,0xd3,0xff,0xff,0x03,0xfa,0x65,0x47,0x05,0xfa,0x60,0x47,0x8e,0xa0,0xff,0xff,
-0x09,0x04,0x8e,0xa5,0x72,0x64,0x07,0x00,0x84,0xa0,0xff,0xff,0x03,0x04,0x84,0xa5,
-0x7c,0x64,0x01,0x00,0x00,0x65,0x59,0xd1,0xbd,0xd8,0xfe,0xa4,0xff,0xff,0xfb,0x07,
-0x65,0x44,0x00,0xa0,0x00,0xf4,0x02,0x03,0x04,0x63,0xee,0x00,0x5a,0x60,0x8b,0x78,
-0xff,0xff,0x6e,0x3a,0x00,0x00,0x67,0x3a,0x00,0x00,0x68,0x3a,0x00,0x00,0x69,0x36,
-0x03,0x00,0x5a,0x60,0x4a,0x78,0xff,0xff,0x61,0x60,0xc2,0x63,0x61,0x60,0xc6,0x62,
-0xa2,0xd3,0xa3,0xd1,0x00,0xa0,0xff,0xff,0x56,0x03,0x02,0x60,0x80,0x63,0x64,0x41,
-0x61,0x60,0xd8,0x65,0xc5,0x81,0xfe,0xa1,0xd3,0x85,0x0a,0xa3,0x38,0xfc,0x46,0x48,
-0x00,0xf4,0x81,0x60,0x87,0x64,0x02,0xfa,0x67,0x60,0x42,0x64,0xa0,0xd3,0xff,0xff,
-0x03,0xfa,0x63,0x47,0x05,0xfa,0x04,0x63,0x0a,0xa3,0x65,0x44,0x8e,0xa0,0xff,0xff,
-0x09,0x04,0x8e,0xa5,0x72,0x64,0x08,0x00,0x84,0xa0,0xff,0xff,0x03,0x04,0x84,0xa5,
-0x7c,0x64,0x02,0x00,0x00,0x65,0x40,0x49,0x59,0xd1,0xbd,0xd8,0xfe,0xa4,0xff,0xff,
-0xfb,0x07,0x65,0x44,0x00,0xa0,0xff,0xff,0x03,0x03,0x00,0xf4,0x04,0x63,0xec,0x00,
-0x61,0x60,0xc2,0x62,0x61,0x60,0xd8,0x61,0xfe,0xa1,0xa2,0xd3,0x29,0x45,0x00,0xa0,
-0x7c,0x62,0x4e,0x03,0xd6,0x85,0xd4,0x80,0x60,0x42,0x09,0x06,0x65,0x44,0xd6,0x85,
-0x07,0x00,0x84,0xa0,0xff,0xff,0x03,0x04,0x84,0xa5,0x7c,0x64,0x01,0x00,0x00,0x65,
-0x59,0xd1,0xbd,0xd8,0xfe,0xa4,0xff,0xff,0xfb,0x07,0x65,0x44,0x00,0xa0,0x00,0xf4,
-0x37,0x03,0x04,0x63,0xee,0x00,0x00,0x64,0xd0,0x80,0xff,0xff,0x03,0x02,0x5a,0x60,
-0xc5,0x78,0xff,0xff,0x64,0x45,0x64,0x44,0x0a,0xa4,0x38,0xfa,0x61,0x60,0xd8,0x61,
-0xfe,0xa1,0x46,0x48,0x00,0xf4,0x04,0x63,0x81,0x60,0x87,0x64,0x02,0xfa,0x67,0x60,
-0x42,0x64,0xa0,0xd3,0xff,0xff,0x03,0xfa,0x65,0x47,0x05,0xfa,0x0a,0xa3,0x65,0x44,
-0x8e,0xa0,0xff,0xff,0x09,0x04,0x8e,0xa5,0x72,0x64,0x07,0x00,0x84,0xa0,0xff,0xff,
-0x03,0x04,0x84,0xa5,0x7c,0x64,0x01,0x00,0x00,0x65,0x59,0xd1,0xbd,0xd8,0xfe,0xa4,
-0xff,0xff,0xfb,0x07,0x65,0x44,0x00,0xa0,0x00,0xf4,0x02,0x03,0x04,0x63,0xee,0x00,
-0x5a,0x60,0x8b,0x78,0xff,0xff,0x6a,0x3a,0x00,0x00,0x64,0x3a,0x0f,0x00,0x0a,0x63,
-0x38,0xfc,0x46,0x48,0x00,0xf4,0x81,0x60,0x87,0x64,0x02,0xfa,0x67,0x60,0x42,0x64,
-0xa0,0xd3,0xff,0xff,0x03,0xfa,0x63,0x47,0x05,0xfa,0x2e,0x00,0x6f,0x3a,0x66,0x00,
-0x12,0x63,0x38,0xfc,0xa0,0x60,0x01,0x64,0x31,0xfa,0xf0,0x60,0xf8,0x64,0x32,0xfa,
-0x04,0x60,0xf0,0x64,0x33,0xfa,0xff,0xff,0x81,0xf1,0x2b,0xf8,0x82,0xf1,0xff,0xff,
-0x2c,0xf8,0x83,0xf1,0x2d,0xf8,0x46,0x48,0x00,0xf4,0xaa,0x60,0xaa,0x64,0x02,0xfa,
-0x00,0x60,0x03,0x64,0x03,0xfa,0x00,0x60,0x00,0x64,0x04,0xfa,0x81,0x60,0x87,0x64,
-0x05,0xfa,0x00,0x64,0x0a,0xfa,0x67,0x60,0x42,0x64,0xa0,0xd3,0xff,0xff,0x06,0xfa,
-0x63,0x47,0x08,0xfa,0x28,0x46,0x0d,0x00,0x28,0x46,0x81,0xf1,0x2b,0xf8,0x31,0xf8,
-0xff,0xff,0x82,0xf1,0x2c,0xf8,0x32,0xf8,0xff,0xff,0x83,0xf1,0x2d,0xf8,0x33,0xf8,
-0xff,0xff,0xbd,0xf1,0x2e,0xf8,0xbe,0xf1,0xff,0xff,0x2f,0xf8,0xbf,0xf1,0x30,0xf8,
-0xff,0xff,0x85,0xf3,0xff,0xff,0x08,0xbc,0x43,0xf1,0xff,0xff,0x64,0x40,0x01,0x2a,
-0x03,0x00,0x60,0x47,0x40,0xbc,0x60,0x47,0x29,0xfa,0x00,0x63,0x28,0xfc,0x22,0xfc,
-0x3a,0x60,0x58,0x4e,0x14,0x78,0xff,0xff,0xff,0x64,0x23,0xfa,0xff,0x7f,0x00,0x7e,
-0x0e,0xfa,0x3c,0x60,0x82,0x62,0x3c,0x60,0x28,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,
-0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xc1,0xfe,0x14,0x00,0x0f,0x4e,0x46,0x45,
-0x3c,0x60,0x82,0x62,0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,
-0xd1,0xfe,0x0e,0x4f,0x2f,0x58,0xff,0xff,0x61,0x60,0xc2,0x62,0xa2,0xd1,0x61,0x60,
-0xd8,0x64,0xc0,0x83,0x61,0x60,0xc8,0x62,0xa2,0xd3,0xbd,0xdb,0xf6,0xa0,0x00,0xa0,
-0x01,0x03,0x34,0x02,0x61,0x60,0xd6,0x62,0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,
-0x2d,0x03,0x1e,0x60,0x92,0x62,0x61,0x60,0xd2,0x61,0xa2,0xd3,0xa1,0xd1,0xff,0xff,
-0xd0,0x85,0x1e,0x60,0x94,0x62,0x61,0x60,0xd4,0x61,0xa2,0xd3,0xa1,0xd1,0x01,0x05,
-0xff,0xa4,0xd0,0x84,0x65,0x44,0x02,0x02,0xfc,0x23,0x14,0x00,0x61,0x60,0xd2,0x62,
-0xa2,0xd3,0xbd,0xdb,0x61,0x60,0xd4,0x62,0xa2,0xd3,0xbd,0xdb,0x00,0x64,0xbd,0xdb,
-0xbd,0xdb,0xbd,0xdb,0xff,0xff,0xbd,0xdb,0xbd,0xdb,0xbd,0xdb,0xff,0xff,0xbd,0xdb,
-0xbd,0xdb,0x2f,0x00,0x61,0x60,0xd6,0x62,0x02,0x64,0xa2,0xdb,0x1e,0x60,0x92,0x62,
-0xa2,0xd3,0xbd,0xdb,0x1e,0x60,0x94,0x62,0xa2,0xd3,0xbd,0xdb,0x27,0x60,0xe0,0x65,
-0x88,0xf3,0xff,0xff,0xd4,0x84,0xe8,0x84,0xe8,0x84,0xbd,0xdb,0x64,0x60,0x58,0x62,
-0xa2,0xd1,0x00,0x64,0xa2,0xdb,0xbd,0xd9,0x27,0x41,0x06,0xa1,0xa1,0xd1,0xbd,0xd9,
-0x59,0xd1,0xff,0xff,0xbd,0xd9,0x59,0xd1,0xbd,0xd9,0x27,0x41,0x04,0xa1,0xa1,0xd3,
-0xbd,0xdb,0x27,0x41,0x02,0xa1,0xa1,0xd1,0xff,0xff,0xd0,0x84,0xbd,0xdb,0x51,0xf3,
-0xbd,0xdb,0x61,0x60,0xd6,0x62,0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,0x05,0x02,
-0x61,0x60,0xc4,0x62,0xa2,0xd3,0xbd,0xdb,0x09,0x00,0x61,0x60,0xc4,0x62,0xa2,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0xff,0xa0,0xbd,0xdb,0x09,0x02,0x00,0x64,0xbd,0xdb,
-0xbd,0xdb,0xbd,0xdb,0xff,0xff,0xbd,0xdb,0xbd,0xdb,0xbd,0xdb,0x20,0x00,0x61,0x60,
-0xca,0x62,0xa2,0xd1,0xbd,0xd9,0x61,0x60,0xcc,0x62,0xa2,0xd1,0xbd,0xd9,0x61,0x60,
-0xce,0x62,0xa2,0xd1,0xbd,0xd9,0xff,0xff,0x59,0xf3,0x80,0x65,0xc4,0x87,0xff,0xb4,
-0xbd,0xdb,0x58,0xf3,0xbd,0xdb,0xf4,0xb6,0x7e,0x00,0x00,0x10,0x61,0x60,0xd0,0x62,
-0xa2,0xd3,0xbd,0xdb,0x59,0x60,0x9c,0x62,0xa2,0xd3,0xbd,0xdb,0x59,0x60,0x9e,0x62,
-0xa2,0xd3,0xbd,0xdb,0x61,0x60,0xc2,0x63,0x02,0x60,0x80,0x65,0xa3,0xd3,0xff,0xff,
-0x28,0xa4,0xd4,0x80,0xff,0xff,0x05,0x04,0x61,0x60,0xc6,0x62,0x01,0x64,0xa2,0xdb,
-0x00,0x64,0xa3,0xdb,0x61,0x60,0xc8,0x62,0xa2,0xd3,0xff,0xff,0xf6,0xa0,0x00,0xa0,
-0x01,0x03,0x0f,0x02,0x61,0x60,0xd6,0x62,0xa2,0xd3,0xff,0xff,0xff,0xa0,0xfe,0xa0,
-0x06,0x03,0x05,0x03,0x01,0x64,0xa2,0xdb,0x5a,0x60,0xdb,0x78,0xff,0xff,0x00,0x64,
-0xa2,0xdb,0x61,0x60,0xc8,0x62,0xa2,0xd1,0xff,0xff,0x64,0x44,0xf4,0xa0,0xff,0xff,
-0x07,0x03,0x59,0x60,0xc0,0x64,0xc0,0x83,0xa3,0xd3,0xff,0xff,0xdc,0x84,0xa3,0xdb,
-0x59,0x60,0xa6,0x62,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x2e,0x58,0xff,0xff,
-0x64,0x60,0x5c,0x62,0xa2,0xd1,0x64,0x60,0x62,0x64,0xc0,0x83,0x64,0x60,0x5e,0x62,
-0xa2,0xd3,0xff,0xff,0x01,0xa4,0xa2,0xdb,0xbd,0xdb,0x1e,0x60,0x92,0x62,0xa2,0xd3,
-0xbd,0xdb,0x1e,0x60,0x94,0x62,0xa2,0xd3,0xbd,0xdb,0x66,0x60,0x24,0x62,0xa2,0xd3,
-0xbd,0xdb,0x66,0x60,0x26,0x62,0xa2,0xd3,0xbd,0xdb,0x66,0x60,0x28,0x62,0xa2,0xd3,
-0xbd,0xdb,0x66,0x60,0x22,0x62,0xa2,0xd3,0xbd,0xdb,0x59,0x60,0x9c,0x62,0xa2,0xd3,
-0xbd,0xdb,0xff,0xff,0x58,0xf3,0xbd,0xdb,0x59,0xf3,0x80,0x65,0xc4,0x87,0xff,0xb4,
-0xbd,0xdb,0x5a,0xf3,0x80,0x65,0xc4,0x87,0xff,0xb4,0xbd,0xdb,0xff,0xff,0x81,0xf3,
-0xbd,0xdb,0x82,0xf3,0xff,0xff,0xbd,0xdb,0x83,0xf3,0xa3,0xdb,0x64,0x60,0x5c,0x63,
-0x01,0x60,0xc0,0x65,0xa3,0xd3,0xff,0xff,0x1c,0xa4,0xd4,0x80,0xff,0xff,0x05,0x04,
-0x64,0x60,0x60,0x62,0x01,0x64,0xa2,0xdb,0x00,0x64,0xa3,0xdb,0x2e,0x58,0xff,0xff,
-0x1e,0x60,0xce,0x62,0xa2,0xd1,0x00,0x60,0x04,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,
-0xcf,0xfe,0x2f,0x58,0xff,0xff,0xba,0xf1,0x28,0x60,0x96,0x62,0xa2,0xd9,0x3c,0x60,
-0xc6,0x62,0x28,0x60,0x92,0x64,0xa2,0xdb,0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,
-0x1e,0x60,0xd0,0x62,0x00,0x60,0x04,0x64,0xa2,0xdb,0x5c,0x60,0x37,0x64,0x5a,0xdb,
-0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xce,0x62,0x00,0x64,0xa2,0xdb,0xcb,0xf3,
-0xff,0xff,0xfc,0xa0,0xff,0xff,0xdf,0x02,0x66,0x60,0x28,0x62,0x0f,0x64,0xa2,0xdb,
-0x17,0x60,0xea,0x64,0xa0,0xd3,0xff,0xff,0x60,0x41,0x02,0xa4,0xff,0xff,0x9c,0xa0,
-0xff,0xff,0x01,0x04,0x00,0x64,0x60,0x45,0x18,0x60,0x56,0x62,0xc6,0x82,0xa2,0xd1,
-0x00,0x63,0xa2,0xdd,0x18,0x60,0x54,0x63,0xa3,0xd3,0xff,0xff,0xd0,0x84,0xa3,0xdb,
-0x17,0x60,0xee,0x62,0xc6,0x82,0xa2,0xd1,0x00,0x63,0xa2,0xdd,0x17,0x60,0xec,0x63,
-0xa3,0xd3,0xff,0xff,0xd0,0x84,0xa3,0xdb,0x17,0x60,0xea,0x63,0x65,0x44,0xa3,0xdb,
-0x61,0x45,0x18,0x60,0xba,0x63,0xa3,0xd1,0x18,0x60,0x56,0x64,0xc4,0x84,0xa0,0xd3,
-0xff,0xff,0x60,0x41,0xc0,0x84,0xa3,0xdb,0x18,0x60,0x54,0x63,0xa3,0xd1,0xff,0xff,
-0xc1,0x84,0xa3,0xdb,0x18,0x60,0x52,0x63,0xa3,0xd1,0x17,0x60,0xee,0x64,0xc4,0x84,
-0xa0,0xd3,0xff,0xff,0x60,0x41,0xc0,0x84,0xa3,0xdb,0x17,0x60,0xec,0x63,0xa3,0xd1,
-0xff,0xff,0xc1,0x84,0xa3,0xdb,0xe2,0xa0,0x00,0x64,0x03,0x05,0x05,0x7c,0xb8,0xf9,
-0x3f,0x00,0x00,0x63,0x18,0x60,0x52,0x64,0xa0,0xdd,0x18,0x60,0xba,0x64,0xa0,0xdd,
-0x18,0x60,0xc6,0x64,0x64,0x63,0xa0,0xdd,0x18,0x60,0x54,0x63,0xa3,0xd3,0xff,0xff,
-0x00,0xbc,0x60,0x41,0x2d,0x03,0x02,0x60,0x8f,0x65,0x17,0x60,0xec,0x62,0xa2,0xd3,
-0xd5,0x80,0xff,0xff,0x03,0x06,0xe9,0x81,0xe8,0x84,0xfa,0x00,0x60,0x5c,0x61,0x44,
-0xe0,0x84,0xe0,0x84,0x60,0x41,0xe0,0x84,0xe0,0x84,0xe0,0x84,0x60,0x45,0xe0,0x84,
-0xc4,0x85,0xc5,0x85,0x00,0x62,0x65,0x44,0x64,0x45,0x11,0x61,0xe0,0x84,0xcd,0x81,
-0xfd,0x04,0x01,0x00,0xe0,0x84,0xf2,0x82,0xff,0xff,0x02,0x24,0xc6,0x82,0x02,0x28,
-0xd6,0x82,0xe2,0x80,0xcd,0x81,0x02,0x28,0x01,0xbc,0xf4,0x02,0x01,0x2a,0xc6,0x82,
-0x60,0x43,0x59,0x60,0x9e,0x62,0xa2,0xdd,0x66,0x60,0x22,0x62,0xa2,0xdd,0x63,0x44,
-0xd3,0xa0,0x01,0x65,0x0e,0x04,0x1f,0xf3,0x59,0xf3,0x60,0x40,0x10,0x2a,0x67,0x00,
-0xec,0xa0,0x67,0x60,0x6c,0x64,0x63,0x04,0xa0,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,
-0x5e,0x03,0x18,0x60,0xbc,0x65,0x51,0xf3,0xff,0xff,0xe0,0x84,0xc4,0x83,0xa3,0xd3,
-0xff,0xff,0xf8,0xa0,0x03,0x65,0x53,0x05,0x18,0x60,0x52,0x64,0xa0,0xd3,0xff,0xff,
-0xe2,0xa0,0x18,0x60,0xba,0x63,0x33,0x04,0xa3,0xd3,0xff,0xff,0x00,0xbc,0x60,0x41,
-0x2f,0x03,0x02,0x60,0x8f,0x65,0x18,0x60,0x52,0x62,0xa2,0xd3,0xd5,0x80,0xff,0xff,
-0x03,0x06,0xe9,0x81,0xe8,0x84,0xfa,0x00,0x60,0x5c,0x61,0x44,0xe0,0x84,0xe0,0x84,
-0x60,0x41,0xe0,0x84,0xe0,0x84,0xe0,0x84,0x60,0x45,0xe0,0x84,0xc4,0x85,0xc5,0x85,
-0x00,0x62,0x65,0x44,0x64,0x45,0x11,0x61,0xe0,0x84,0xcd,0x81,0xfd,0x04,0x01,0x00,
-0xe0,0x84,0xf2,0x82,0xff,0xff,0x02,0x24,0xc6,0x82,0x02,0x28,0xd6,0x82,0xe2,0x80,
-0xcd,0x81,0x02,0x28,0x01,0xbc,0xf4,0x02,0x01,0x2a,0xc6,0x82,0x01,0x00,0x69,0x00,
-0x60,0x43,0x18,0x60,0xc6,0x62,0xa2,0xdd,0x63,0x44,0xd3,0xa0,0x00,0x63,0x18,0x60,
-0x52,0x62,0xa2,0xdd,0x18,0x60,0xba,0x62,0xa2,0xdd,0x5b,0x04,0x18,0x60,0xc6,0x63,
-0xa3,0xd1,0x66,0x60,0x22,0x62,0xa2,0xd9,0x64,0x64,0xa3,0xdb,0x02,0x65,0x05,0x64,
-0xb8,0xfb,0x66,0x60,0x24,0x63,0x65,0x44,0xbd,0xdb,0x51,0xf3,0xa3,0xdb,0x51,0xf3,
-0x50,0xf3,0x60,0x41,0xff,0xa0,0xe8,0x85,0x41,0x03,0xff,0xa1,0x59,0x60,0x6a,0x64,
-0xa0,0xd3,0xff,0xff,0xa4,0x80,0x65,0x44,0xf5,0x03,0x50,0xfb,0x61,0x43,0x66,0x60,
-0x28,0x62,0xa2,0xdd,0x51,0xfd,0x61,0x45,0x00,0x63,0x17,0x60,0xe6,0x62,0xa2,0xdd,
-0x17,0x60,0xe4,0x62,0xa2,0xdd,0x17,0x60,0xec,0x62,0xa2,0xdd,0x18,0x60,0x54,0x62,
-0xa2,0xdd,0x17,0x60,0xe8,0x62,0xa2,0xdd,0x67,0x60,0x6c,0x62,0xa2,0xdd,0x64,0x61,
-0x17,0x60,0xee,0x63,0x00,0x64,0xc9,0x81,0xbd,0xdb,0xfd,0x02,0x64,0x61,0x18,0x60,
-0x56,0x63,0x00,0x64,0xc9,0x81,0xbd,0xdb,0xfd,0x02,0x65,0x41,0xe1,0x85,0x18,0x60,
-0xbc,0x63,0xc7,0x83,0xa3,0xd3,0xff,0xff,0xf8,0xa0,0xff,0xff,0xc0,0x05,0x04,0x61,
-0x18,0x60,0xbc,0x63,0x00,0x64,0xcd,0x81,0xbd,0xdb,0xfd,0x02,0x5e,0x60,0x32,0x78,
-0xff,0xff,0x17,0x60,0xec,0x63,0xa3,0xd3,0xff,0xff,0xe2,0xa0,0x59,0x60,0x9e,0x63,
-0xa3,0xd3,0x1d,0x04,0xdd,0xa0,0xff,0xff,0x23,0x05,0x50,0xf3,0x04,0x65,0xf8,0xa0,
-0xff,0xff,0x32,0x02,0xb8,0xf3,0xff,0xff,0xfd,0xa0,0x03,0x64,0x2d,0x03,0xb8,0xfb,
-0x67,0x60,0x6e,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x25,0x00,0x17,0x60,
-0xe8,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x1e,0x02,0x6f,0x00,0x18,0x60,
-0xc6,0x63,0xa3,0xd3,0xff,0xff,0xdd,0xa0,0x64,0x64,0xa3,0xdb,0x05,0x65,0xef,0x04,
-0x05,0x64,0xb8,0xfb,0x51,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x5f,0x03,0x03,0x60,
-0xe8,0x65,0x17,0x60,0xe4,0x64,0xa0,0xd3,0x59,0xf3,0xd4,0x80,0x06,0x65,0x56,0x04,
-0xf1,0xa0,0xff,0xff,0xdc,0x05,0x52,0x00,0x66,0x60,0x24,0x63,0x65,0x44,0xbd,0xdb,
-0xfa,0xa0,0x51,0xf3,0x15,0x02,0xbd,0xdb,0x60,0x5c,0x08,0x65,0x03,0x61,0x67,0x60,
-0x6c,0x62,0x01,0x64,0xa2,0xdb,0x04,0x00,0xd0,0x80,0xe8,0x85,0x3f,0x03,0xff,0xa1,
-0x59,0x60,0x6a,0x64,0xa0,0xd3,0xff,0xff,0xa4,0x80,0x65,0x44,0xf5,0x03,0x0e,0x00,
-0xbd,0xdb,0x50,0xf3,0x60,0x41,0xf8,0xa0,0xe0,0x85,0x30,0x03,0x01,0xa1,0x59,0x60,
-0x6a,0x64,0xa0,0xd3,0xff,0xff,0xa4,0x80,0x65,0x44,0xf5,0x03,0x50,0xfb,0x61,0x43,
-0x51,0xfd,0x66,0x60,0x28,0x62,0xa2,0xdd,0x00,0x63,0x17,0x60,0xe6,0x62,0xa2,0xdd,
-0x17,0x60,0xe4,0x62,0xa2,0xdd,0x17,0x60,0xec,0x62,0xa2,0xdd,0x18,0x60,0x54,0x62,
-0xa2,0xdd,0x64,0x61,0x17,0x60,0xee,0x63,0x00,0x64,0xc9,0x81,0xbd,0xdb,0xfd,0x02,
-0x64,0x61,0x18,0x60,0x56,0x63,0x00,0x64,0xc9,0x81,0xbd,0xdb,0xfd,0x02,0x04,0x61,
-0x18,0x60,0xbc,0x63,0x00,0x64,0xcd,0x81,0xbd,0xdb,0xfd,0x02,0x00,0x60,0x64,0x65,
-0x17,0x60,0xe6,0x62,0xa2,0xd3,0xff,0xff,0x01,0xa4,0xd4,0x80,0xa2,0xdb,0x09,0x04,
-0x17,0x60,0xe4,0x63,0x00,0x64,0xa2,0xdb,0xa3,0xdb,0x17,0x60,0xe8,0x62,0x01,0x64,
-0xa2,0xdb,0x66,0x60,0x28,0x62,0xa2,0xd3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x04,0x07,
-0x5b,0x60,0x58,0x4e,0xc4,0x78,0xff,0xff,0x5c,0x60,0x1f,0x78,0xff,0xff,0x61,0xf1,
-0x28,0x60,0xa2,0x62,0xa2,0xd9,0x3c,0x60,0xca,0x62,0x28,0x60,0x9e,0x64,0xa2,0xdb,
-0x02,0x64,0x4a,0xdb,0xff,0xff,0x1d,0xff,0x1e,0x60,0xd6,0x62,0x00,0x60,0xfe,0x64,
-0xa2,0xdb,0x5e,0x60,0x6b,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x60,0xf3,
-0x65,0xf3,0x60,0x45,0x60,0x47,0xb4,0x84,0x60,0x45,0x68,0x60,0xcc,0x62,0x10,0x60,
-0x00,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x00,0x60,0x7a,0x66,
-0x00,0x64,0x61,0xfb,0x01,0xf2,0xff,0xff,0x00,0xa0,0xff,0xff,0x1e,0x03,0x1e,0x60,
-0x92,0x64,0xa0,0xd3,0x03,0xf0,0xff,0xff,0xd0,0x84,0x00,0xfa,0x01,0xf2,0x60,0x45,
-0xd4,0x80,0xff,0xff,0x05,0x06,0xd4,0x84,0x01,0xfa,0xfe,0xa0,0xff,0xff,0x0d,0x05,
-0x00,0x64,0x01,0xfa,0x02,0xfa,0x60,0x45,0x68,0x60,0xcc,0x62,0x90,0x60,0x80,0x64,
-0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x62,0xf1,0x17,0x60,0x06,0x64,
-0xa0,0xd3,0xff,0xff,0xd0,0x80,0x60,0x45,0x69,0x03,0x16,0x60,0x42,0x64,0xa0,0xd3,
-0xff,0xff,0xff,0xa0,0xfc,0xa0,0x06,0x03,0x62,0x02,0x86,0xf3,0xff,0xff,0x00,0xa0,
-0xff,0xff,0x5d,0x03,0x65,0x44,0x60,0x45,0x68,0x60,0xcc,0x62,0x60,0x60,0x00,0x64,
-0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x19,0xf2,0xff,0xff,0xdc,0x84,
-0x19,0xfa,0x65,0x44,0xff,0x22,0x4b,0x00,0x12,0xfa,0xcc,0x84,0xfc,0xa0,0x60,0x41,
-0x01,0x06,0x04,0x64,0xe0,0x84,0x60,0x45,0xe0,0x84,0xe0,0x84,0xc4,0x85,0x18,0x60,
-0xf8,0x64,0xc4,0x83,0xbd,0xd3,0xff,0xff,0x14,0xfa,0xbd,0xd3,0xff,0xff,0x15,0xfa,
-0xbd,0xd3,0xff,0xff,0x16,0xfa,0xbd,0xd3,0xff,0xff,0x17,0xfa,0xbd,0xd3,0xff,0xff,
-0x18,0xfa,0x16,0x60,0x42,0x64,0xa0,0xd3,0xff,0xff,0xfc,0xa0,0x61,0x44,0x05,0x02,
-0xfd,0xa0,0x14,0xf2,0x02,0x05,0xfd,0xa4,0x14,0xfa,0xcb,0xf3,0xff,0xff,0xfc,0xa0,
-0x62,0xf3,0x08,0x02,0xff,0xff,0xff,0x26,0x05,0x00,0x01,0x64,0x63,0x60,0x58,0x4e,
-0x03,0x78,0xff,0xff,0x62,0xf3,0xff,0xff,0x60,0x40,0x00,0x3a,0x2f,0x00,0x15,0xf2,
-0x01,0xfa,0x61,0xfb,0x02,0x64,0x02,0xfa,0x08,0x64,0x60,0xfb,0x01,0x64,0x13,0xfa,
-0x05,0xfa,0x04,0xfa,0x10,0x60,0x00,0x64,0x66,0xfb,0x20,0x00,0x24,0x00,0x65,0xf3,
-0xff,0xff,0x02,0xb0,0xff,0xff,0x04,0x02,0x63,0x60,0x58,0x4e,0x7d,0x78,0xff,0xff,
-0xcb,0xf3,0xff,0xff,0xfc,0xa0,0xff,0xff,0x05,0x02,0x00,0x64,0x63,0x60,0x58,0x4e,
-0x03,0x78,0xff,0xff,0x80,0x64,0x60,0xfb,0x32,0x64,0x61,0xfb,0x17,0x60,0x06,0x63,
-0x00,0x64,0x66,0xfb,0x01,0xfa,0x02,0xfa,0xff,0xff,0xa3,0xdb,0x17,0x60,0x06,0x64,
-0xa0,0xd3,0x62,0xfb,0x05,0x00,0x00,0xa0,0xff,0xff,0x02,0x02,0x32,0x64,0x61,0xfb,
-0xff,0xff,0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x00,0x60,0x01,0x64,0xa0,0x84,0xa2,0xdb,
-0x09,0xf8,0x16,0x60,0x42,0x62,0xa2,0xd3,0xff,0xff,0xfc,0xa0,0x62,0xf3,0x03,0x03,
-0x60,0x60,0xa2,0x78,0xff,0xff,0x00,0xa0,0xff,0xff,0x6c,0x03,0x64,0x44,0x80,0x2a,
-0x1d,0x00,0x20,0xf2,0xff,0xff,0xdc,0x84,0x20,0xfa,0x01,0x60,0x5e,0x64,0x20,0x40,
-0x40,0x2a,0x08,0x00,0x18,0x60,0xf6,0x62,0xa2,0xd3,0xff,0xff,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0xe0,0x84,0x01,0xfa,0x01,0x64,0x02,0xfa,0x65,0xf3,0xff,0xff,0xff,0xff,
-0xff,0x26,0x04,0x00,0x63,0x60,0x58,0x4e,0x7d,0x78,0xff,0xff,0x6e,0xf3,0x6f,0xf1,
-0xff,0xff,0x01,0xb4,0xb0,0x84,0xff,0xff,0x19,0x03,0x60,0xf3,0xff,0xff,0x08,0xbc,
-0x60,0xfb,0x62,0x60,0x58,0x4e,0xdc,0x78,0xff,0xff,0x00,0xfa,0x60,0x45,0x68,0x60,
-0xcc,0x62,0x90,0x60,0x21,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,
-0x01,0xf0,0xff,0xff,0xd0,0x80,0xff,0xff,0x01,0x06,0x01,0xfa,0x09,0xf2,0x06,0xf2,
-0x60,0x40,0x08,0x2a,0x28,0x00,0x60,0x40,0xff,0x22,0x02,0x00,0xcc,0x84,0x06,0xfa,
-0x00,0xa0,0xff,0xff,0x13,0x02,0x05,0xf2,0x14,0xf0,0x03,0xa4,0xd0,0x80,0xff,0xff,
-0x01,0x06,0x64,0x44,0x05,0xfa,0x07,0xf2,0xff,0xff,0x60,0x40,0xff,0x22,0x02,0x00,
-0xcc,0x84,0x07,0xfa,0x00,0xa0,0x01,0x64,0x01,0x02,0x05,0xfa,0x86,0xf3,0x01,0xf0,
-0x00,0xfa,0xd0,0x80,0xff,0xff,0x01,0x06,0x01,0xfa,0x60,0xf3,0xff,0xff,0x08,0xbc,
-0x60,0xfb,0x01,0x00,0x56,0x00,0x09,0xf2,0x60,0xf1,0x60,0x40,0x04,0x2a,0x2c,0x00,
-0x64,0x40,0x20,0x2a,0x19,0x00,0x01,0x64,0x05,0xfa,0x04,0xfa,0x62,0x60,0x58,0x4e,
-0xdc,0x78,0xff,0xff,0x00,0xfa,0x60,0x45,0x68,0x60,0xcc,0x62,0x90,0x60,0x22,0x64,
-0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x01,0xf0,0xff,0xff,0xd0,0x80,
-0xff,0xff,0x0d,0x06,0x01,0xfa,0x0b,0x00,0x64,0x40,0x10,0x2a,0x08,0x00,0x14,0x64,
-0x00,0xfa,0x61,0xf1,0xff,0xff,0xd0,0x80,0xff,0xff,0x01,0x06,0x61,0xfb,0x60,0xf3,
-0xff,0xff,0xcf,0xb4,0x08,0xbc,0x60,0xfb,0x09,0xf2,0x60,0xf1,0x60,0x40,0x10,0x2a,
-0x0b,0x00,0x64,0x44,0xbf,0xb4,0x08,0xbc,0x60,0xfb,0x14,0x64,0x61,0xf1,0x00,0xfa,
-0xd0,0x80,0xff,0xff,0x01,0x06,0x61,0xfb,0x09,0xf2,0x18,0xf0,0x60,0x40,0x20,0x2a,
-0x04,0x00,0x01,0x63,0x05,0xfc,0x04,0xfc,0x06,0xf8,0x60,0x40,0x40,0x2a,0x01,0x00,
-0x06,0xf8,0x61,0xf3,0x65,0xf1,0x00,0xa0,0x05,0x64,0x03,0x02,0x64,0x40,0x80,0x26,
-0x61,0xfb,0x01,0xf2,0x61,0xf1,0xff,0xff,0xd0,0x80,0xff,0xff,0x01,0x06,0x61,0xfb,
-0x09,0xf0,0x61,0xf3,0x64,0x40,0x02,0x26,0x03,0x00,0x00,0xa0,0xff,0xff,0x7b,0x02,
-0x62,0xf3,0xff,0xff,0x00,0xa0,0x60,0xf3,0x76,0x03,0x00,0xa0,0xcb,0xf3,0x5d,0x02,
-0xfd,0xa0,0x04,0xf2,0x48,0x02,0x60,0x40,0xff,0x22,0x02,0x00,0xcc,0x84,0x04,0xfa,
-0x60,0x40,0x00,0x36,0x0b,0x00,0x71,0xf3,0xff,0xff,0xfb,0xa0,0x3c,0x60,0x28,0x62,
-0x05,0x05,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x12,0x03,0x05,0xf2,0x65,0xf1,
-0x04,0xfa,0x64,0x40,0xff,0x26,0x04,0x00,0x63,0x60,0x58,0x4e,0x7d,0x78,0xff,0xff,
-0x60,0xf3,0x86,0xf1,0x08,0xbc,0x60,0xfb,0x05,0x64,0xc0,0x84,0x00,0xfa,0x44,0x00,
-0xa4,0xf3,0xff,0xff,0x60,0x41,0x80,0xf3,0xff,0xff,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x85,0x73,0x44,0xc4,0x84,0x61,0x45,0xd4,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,
-0xe8,0x84,0x60,0x45,0x01,0x60,0xf4,0x64,0xd4,0x80,0x65,0x44,0x05,0x05,0x23,0xf2,
-0xff,0xff,0xdc,0x84,0x23,0xfa,0x32,0x64,0xfa,0xa0,0xff,0xff,0x01,0x05,0x06,0x64,
-0xfb,0xa4,0x00,0xfa,0x21,0x00,0x61,0xf3,0xff,0xff,0x00,0xa0,0x65,0xf3,0x1c,0x02,
-0xfd,0xa0,0xff,0xff,0x04,0x02,0x63,0x60,0x58,0x4e,0xd5,0x78,0xff,0xff,0x32,0x64,
-0x00,0xfa,0x01,0x64,0x05,0xfa,0x04,0xfa,0x0f,0x00,0x61,0xf3,0xff,0xff,0x00,0xa0,
-0x00,0x64,0x0a,0x02,0x60,0xfb,0x63,0x60,0x58,0x4e,0xd5,0x78,0xff,0xff,0x62,0x60,
-0x58,0x4e,0xe8,0x78,0xff,0xff,0x00,0xfa,0x00,0xf2,0x61,0xf1,0xff,0xff,0xd0,0x80,
-0xff,0xff,0x01,0x06,0x61,0xfb,0x62,0x60,0xc8,0x78,0xff,0xff,0x62,0xf3,0xff,0xff,
-0x00,0xa0,0xff,0xff,0x03,0x02,0x61,0x60,0xab,0x78,0xff,0xff,0x64,0x44,0x80,0x2a,
-0x1d,0x00,0x20,0xf2,0xff,0xff,0xdc,0x84,0x20,0xfa,0x01,0x60,0x5e,0x64,0x20,0x40,
-0x40,0x2a,0x08,0x00,0x18,0x60,0xf6,0x62,0xa2,0xd3,0xff,0xff,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0xe0,0x84,0x01,0xfa,0x01,0x64,0x02,0xfa,0x65,0xf3,0xff,0xff,0xff,0xff,
-0xff,0x26,0x04,0x00,0x63,0x60,0x58,0x4e,0x7d,0x78,0xff,0xff,0x09,0xf2,0xff,0xff,
-0xff,0xff,0x04,0x2a,0x62,0x00,0x1c,0xf2,0xff,0xff,0xdc,0x84,0x1c,0xfa,0x00,0x64,
-0x00,0xfa,0x01,0x64,0x08,0xfa,0x06,0xf2,0xff,0xff,0xff,0xff,0xff,0x22,0x03,0x00,
-0xcc,0x84,0x06,0xfa,0x0b,0x02,0x00,0xa0,0xff,0xff,0x07,0x02,0x05,0xf2,0x14,0xf0,
-0x03,0xa4,0xd0,0x80,0xff,0xff,0x01,0x06,0x64,0x44,0x05,0xfa,0x60,0xf3,0xff,0xff,
-0x60,0x40,0x20,0x2a,0x22,0x00,0x01,0x60,0x2c,0x64,0x00,0xfa,0x5b,0x60,0x28,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x13,0xf2,0xff,0xff,0x05,0xfa,0x04,0xfa,0x00,0x64,0x63,0x60,
-0x58,0x4e,0x03,0x78,0xff,0xff,0x00,0xf2,0x01,0xf0,0xff,0xff,0xd0,0x80,0xff,0xff,
-0x18,0x06,0x01,0xfa,0x04,0x64,0x02,0xfa,0x14,0x00,0x60,0x40,0x10,0x2a,0x11,0x00,
-0x5b,0x60,0x2c,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,
-0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x32,0x64,0x00,0xfa,0xff,0xff,0x63,0xf3,
-0x64,0xfb,0x60,0xf3,0x61,0xf1,0xcf,0xb4,0x60,0xfb,0x00,0xf2,0xff,0xff,0xd0,0x80,
-0xff,0xff,0x01,0x06,0x61,0xfb,0x00,0x64,0x0a,0xfa,0x09,0xf2,0xff,0xff,0xff,0xff,
-0x10,0x2a,0x12,0x00,0x1d,0xf2,0xff,0xff,0xdc,0x84,0x1d,0xfa,0x60,0xf3,0x32,0x65,
-0x60,0x40,0x40,0x2a,0x05,0x65,0xbf,0xb4,0x60,0xfb,0x65,0x44,0x61,0xf1,0x00,0xfa,
-0xd0,0x80,0xff,0xff,0x01,0x06,0x61,0xfb,0x09,0xf2,0xff,0xff,0xff,0xff,0x20,0x2a,
-0x38,0x00,0x1a,0xf2,0xff,0xff,0xdc,0x84,0x1a,0xfa,0x5d,0xf3,0xa7,0xf1,0x60,0x40,
-0xff,0x22,0x14,0x7c,0x64,0x44,0x60,0xf1,0x00,0xfa,0x64,0x40,0x01,0x2a,0x09,0x00,
-0x17,0xf2,0x13,0xf0,0x00,0xfa,0xff,0xff,0x05,0xf8,0x04,0xf8,0x18,0xf0,0xff,0xff,
-0x06,0xf8,0x60,0xf1,0xff,0xff,0x64,0x40,0x04,0x2a,0x09,0x00,0x17,0xf2,0x64,0x40,
-0x01,0x2a,0x05,0x64,0x00,0xfa,0x60,0xf3,0xff,0xff,0xfe,0xb4,0x60,0xfb,0x60,0xf3,
-0xff,0xff,0x08,0xbc,0xf9,0xb4,0x60,0xfb,0x00,0xf2,0x01,0xf0,0xff,0xff,0xd0,0x80,
-0xff,0xff,0x01,0x06,0x01,0xfa,0x61,0xf1,0xff,0xff,0xd0,0x80,0xff,0xff,0x01,0x06,
-0x61,0xfb,0x09,0xf2,0xff,0xff,0xff,0xff,0x40,0x22,0x16,0x00,0x1b,0xf2,0xff,0xff,
-0xdc,0x84,0x1b,0xfa,0x18,0xf2,0xff,0xff,0x06,0xfa,0x02,0xf2,0xff,0xff,0xfc,0xa0,
-0x00,0x64,0x03,0x02,0x01,0xfa,0x02,0xfa,0xff,0xff,0x16,0xf2,0x01,0xf0,0x00,0xfa,
-0xd0,0x80,0xff,0xff,0x01,0x06,0x01,0xfa,0x61,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,
-0x05,0x02,0x65,0xf1,0x05,0x64,0x64,0x40,0x80,0x26,0x61,0xfb,0x04,0x00,0x60,0xf3,
-0xff,0xff,0x80,0xb4,0x60,0xfb,0x60,0xf3,0x65,0xf3,0x60,0x45,0x60,0x47,0xb4,0x84,
-0x01,0xf2,0x61,0xf1,0xff,0xff,0xd0,0x80,0xff,0xff,0x01,0x06,0x61,0xfb,0x09,0xf2,
-0x61,0xf1,0x02,0xb0,0x00,0x64,0x06,0x02,0xd0,0x80,0xff,0xff,0x03,0x03,0x62,0x60,
-0xc8,0x78,0xff,0xff,0x62,0xf3,0x60,0xf3,0x00,0xa0,0xff,0xff,0x03,0x02,0x62,0x60,
-0xc6,0x78,0xff,0xff,0x60,0x40,0xff,0x26,0x6d,0x00,0xcb,0xf3,0xff,0xff,0xfc,0xa0,
-0x64,0xf3,0x67,0x02,0x00,0xb8,0xcc,0x84,0x16,0x03,0x64,0xfb,0x60,0x45,0x68,0x60,
-0xcc,0x62,0x80,0x60,0x40,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,
-0x67,0x60,0x84,0x64,0xa0,0xd3,0x07,0x02,0x00,0xa0,0xff,0xff,0x04,0x03,0x67,0x60,
-0x8a,0x62,0x01,0x64,0xa2,0xdb,0x04,0xf2,0xff,0xff,0x00,0xb8,0xcc,0x84,0x01,0x03,
-0x04,0xfa,0x00,0x65,0x64,0xf3,0x63,0xf3,0x00,0xa0,0xff,0xff,0x0d,0x02,0x64,0xfb,
-0x60,0x45,0x60,0x45,0x68,0x60,0xcc,0x62,0x90,0x60,0x05,0x64,0xa2,0xdb,0x68,0x60,
-0xce,0x62,0x65,0x44,0xa2,0xdb,0x01,0x65,0x04,0xf2,0x05,0xf2,0x00,0xa0,0xff,0xff,
-0x02,0x02,0x04,0xfa,0x01,0x65,0x65,0x40,0x00,0x36,0x2d,0x00,0x60,0xf3,0xff,0xff,
-0x08,0xbc,0x60,0xfb,0x00,0x64,0x08,0xfa,0x0a,0xf2,0x15,0xf0,0x60,0x40,0x01,0x26,
-0x0d,0x00,0x66,0x60,0x2a,0x62,0xa2,0xd3,0x37,0x7c,0xfe,0xa0,0xff,0xff,0x06,0x05,
-0x0c,0xf2,0x25,0x7c,0xfe,0xa0,0xff,0xff,0x01,0x05,0x15,0x7c,0x00,0xf8,0x65,0xf3,
-0xff,0xff,0xff,0xff,0xff,0x26,0x04,0x00,0x63,0x60,0x58,0x4e,0x7d,0x78,0xff,0xff,
-0x18,0x60,0xc8,0x62,0xa2,0xd3,0x10,0x7c,0x0b,0xfa,0x0d,0xf8,0x62,0x60,0xbe,0x78,
-0xff,0xff,0x19,0x00,0x2e,0x00,0x80,0xf3,0xff,0xff,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0x73,0x45,0xc4,0x84,0x60,0x53,0x62,0x60,0x58,0x4e,0xe8,0x78,0xff,0xff,
-0x00,0xfa,0xe1,0xf1,0x67,0x60,0x7e,0x65,0x05,0x64,0x64,0x40,0x00,0x3a,0xa5,0xdb,
-0x62,0x60,0xbe,0x78,0xff,0xff,0x61,0xf3,0xff,0xff,0x00,0xa0,0x32,0x64,0x0a,0x02,
-0x61,0xfb,0x65,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x04,0x02,0x63,0x60,0x58,0x4e,
-0xd5,0x78,0xff,0xff,0x13,0xf2,0xff,0xff,0x05,0xfa,0x04,0xfa,0x01,0x64,0x0a,0xfa,
-0x51,0x00,0x61,0xf3,0xff,0xff,0x00,0xa0,0x00,0x64,0x4c,0x02,0x60,0xfb,0x66,0xf3,
-0xff,0xff,0x00,0xa0,0x01,0x64,0x04,0x02,0x63,0x60,0x58,0x4e,0x03,0x78,0xff,0xff,
-0x63,0x60,0x58,0x4e,0xd5,0x78,0xff,0xff,0xcb,0xf3,0xff,0xff,0xfc,0xa0,0xff,0xff,
-0x26,0x02,0x73,0x44,0x80,0xf3,0xff,0xff,0xe0,0x84,0xe0,0x84,0xe0,0x84,0x60,0x8c,
-0xa4,0xf3,0xff,0xff,0x60,0x41,0x73,0x44,0x61,0x45,0xd4,0x80,0xff,0xff,0x03,0x0d,
-0x2c,0x45,0xc4,0x84,0xf9,0x00,0x60,0x53,0x08,0xf2,0xff,0xff,0x00,0xa0,0xff,0xff,
-0x08,0x02,0x13,0xf2,0xff,0xff,0x05,0xfa,0x04,0xfa,0x24,0xf2,0xff,0xff,0xdc,0x84,
-0x24,0xfa,0x62,0x60,0x58,0x4e,0xe8,0x78,0xff,0xff,0x00,0xfa,0x08,0x00,0x32,0x64,
-0x00,0xfa,0x13,0xf2,0xff,0xff,0x05,0xfa,0x04,0xfa,0x01,0x64,0x0a,0xfa,0x65,0xf3,
-0xff,0xff,0x00,0xa0,0xff,0xff,0x06,0x03,0x00,0xf2,0x14,0x65,0xd4,0x80,0x02,0x06,
-0x65,0x44,0x00,0xfa,0x00,0xf2,0x61,0xf1,0xff,0xff,0xd0,0x80,0xff,0xff,0x04,0x06,
-0x61,0xfb,0x02,0x00,0x32,0x64,0x61,0xfb,0x1e,0x60,0x92,0x64,0xa0,0xd3,0x03,0xfa,
-0xff,0xff,0x61,0xf3,0x60,0x45,0x60,0x45,0x68,0x60,0xcc,0x62,0x50,0x60,0x00,0x64,
-0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x5e,0x60,0x53,0x78,0xff,0xff,
-0xa4,0xf1,0x73,0x44,0x64,0x45,0x86,0xf1,0xd4,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,
-0xe8,0x84,0xc0,0x84,0x2e,0x58,0xff,0xff,0xa4,0xf3,0xff,0xff,0x60,0x45,0x73,0x44,
-0xd4,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0xe8,0x84,0x60,0x45,0x01,0x60,0xf4,0x64,
-0xd4,0x80,0x65,0x44,0x05,0x05,0x23,0xf2,0xff,0xff,0xdc,0x84,0x23,0xfa,0x32,0x64,
-0xfa,0xa0,0xff,0xff,0x01,0x05,0x06,0x64,0xfb,0xa4,0x2e,0x58,0xff,0xff,0x60,0x45,
-0x68,0x60,0xcc,0x62,0x20,0x60,0x00,0x64,0xb4,0x84,0xa2,0xdb,0x65,0x44,0x2e,0x43,
-0x11,0xfc,0x10,0x60,0x00,0x65,0x60,0x40,0xff,0x22,0x00,0x65,0x65,0x44,0x66,0xfb,
-0x21,0xf2,0xff,0xff,0xdc,0x84,0x21,0xfa,0x7b,0xf5,0xff,0xff,0x81,0xf1,0x2b,0xf8,
-0x31,0xf8,0xff,0xff,0x82,0xf1,0x2c,0xf8,0x32,0xf8,0xff,0xff,0x83,0xf1,0x2d,0xf8,
-0x33,0xf8,0xff,0xff,0xbd,0xf1,0x2e,0xf8,0xbe,0xf1,0xff,0xff,0x2f,0xf8,0xbf,0xf1,
-0x30,0xf8,0xff,0xff,0x01,0x60,0x48,0x64,0xb4,0x84,0x29,0xfa,0x00,0x63,0x22,0xfc,
-0x2a,0x60,0x20,0x64,0x0e,0xfa,0x3a,0x60,0x58,0x4e,0x14,0x78,0xff,0xff,0x3c,0x60,
-0x82,0x62,0x3c,0x60,0x28,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0xc1,0xfe,0x1e,0x60,0xd6,0x62,0x00,0x60,0x01,0x64,0xa2,0xdb,
-0x63,0x60,0x52,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xd4,0x62,
-0xa2,0xd1,0xff,0x60,0xfe,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,
-0x7b,0xf5,0x22,0xf0,0x00,0x60,0x7a,0x66,0x64,0x44,0x0f,0x22,0x04,0x00,0x22,0xf2,
-0xff,0xff,0xdc,0x84,0x22,0xfa,0x5a,0x60,0x3a,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x11,0xf0,
-0x68,0x60,0xcc,0x62,0x20,0x60,0x02,0x64,0xa2,0xdb,0xff,0xff,0xf4,0xc6,0x7e,0x00,
-0x00,0x10,0x44,0x4e,0x2e,0x58,0xff,0xff,0x1e,0xf2,0xff,0xff,0xdc,0x84,0x1e,0xfa,
-0x68,0x60,0xcc,0x62,0x30,0x60,0x00,0x64,0xa2,0xdb,0x2e,0x43,0x11,0xfc,0x1e,0x60,
-0xd4,0x62,0xa2,0xd1,0xbf,0x60,0xff,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,
-0x5a,0xdb,0xff,0xff,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,0xd6,0x62,0x40,0x60,
-0x00,0x64,0xa2,0xdb,0x63,0x60,0xa1,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,
-0x3c,0x60,0xa2,0x62,0x2a,0x44,0xa2,0xdb,0xca,0x82,0x08,0x64,0xa2,0xdb,0xff,0xff,
-0x2d,0xff,0x1e,0x60,0xd6,0x62,0x20,0x60,0x00,0x64,0xa2,0xdb,0x63,0x60,0xb5,0x64,
-0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x9f,0x60,
-0xff,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,0xff,0xff,0xbe,0xfe,
-0x1e,0x60,0xa8,0x62,0xa2,0xd1,0x40,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,
-0x00,0x60,0x7a,0x66,0x68,0x60,0xcc,0x62,0x30,0x60,0x01,0x64,0xa2,0xdb,0x11,0xf2,
-0xff,0xff,0x40,0x4e,0x2e,0x58,0xff,0xff,0x1f,0xf2,0xff,0xff,0xdc,0x84,0x1f,0xfa,
-0x68,0x60,0xcc,0x62,0x40,0x60,0x00,0x64,0xa2,0xdb,0x2e,0x43,0x11,0xfc,0x1e,0x60,
-0xd4,0x62,0xa2,0xd1,0xbf,0x60,0xff,0x61,0xa1,0x84,0x5a,0xd1,0x4a,0xdb,0xa1,0x84,
-0x5a,0xdb,0xff,0xff,0xde,0xfe,0xff,0xff,0x0b,0x04,0x1e,0x60,0xd6,0x62,0x40,0x60,
-0x00,0x64,0xa2,0xdb,0x63,0x60,0xf9,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,0xff,0xff,
-0x65,0xf3,0xff,0xff,0xff,0xff,0x80,0x26,0x14,0x00,0x3c,0x60,0xa2,0x62,0x2a,0x44,
-0xa2,0xdb,0xca,0x82,0x02,0x64,0xa2,0xdb,0xff,0xff,0x2d,0xff,0x1e,0x60,0xd6,0x62,
-0x20,0x60,0x00,0x64,0xa2,0xdb,0x64,0x60,0x13,0x64,0x5a,0xdb,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0xff,0xff,0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x9f,0x60,0xff,0x61,0xa1,0x84,
-0x5a,0xd1,0x4a,0xdb,0xa1,0x84,0x5a,0xdb,0xff,0xff,0xbe,0xfe,0x1e,0x60,0xa8,0x62,
-0xa2,0xd1,0x40,0x60,0x00,0x64,0xb0,0x84,0xa2,0xdb,0xcf,0xfe,0x5b,0x60,0x24,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x00,0x60,0x7a,0x66,0x68,0x60,0xcc,0x62,0x40,0x60,0x01,0x64,
-0xa2,0xdb,0x11,0xf2,0xff,0xff,0x40,0x4e,0x2e,0x58,0xff,0xff,0x1e,0x60,0xd4,0x62,
-0xa2,0xd1,0x00,0x60,0x02,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x2f,0x58,
-0xff,0xff,0x3c,0x60,0xb2,0x62,0x28,0x60,0x9e,0x64,0xa2,0xdb,0x03,0x64,0x4a,0xdb,
-0xff,0xff,0x1d,0xff,0x1e,0x60,0xd4,0x62,0x00,0x64,0xa2,0xdb,0x5a,0xdb,0x2f,0x58,
-0xff,0xff,0x62,0xf3,0x26,0x46,0x60,0x40,0xff,0x22,0x37,0x00,0x60,0xf3,0x0f,0xf2,
-0x60,0x43,0x29,0xf0,0x60,0x40,0x10,0x2a,0x15,0x00,0x63,0x44,0x60,0x43,0x68,0x60,
-0xcc,0x62,0x00,0x60,0x40,0x64,0xa2,0xdb,0x68,0x60,0xce,0x62,0x63,0x44,0xa2,0xdb,
-0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x00,0x60,0x40,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,
-0xcf,0xfe,0x1b,0x00,0x64,0x40,0x20,0x2b,0x02,0x00,0x40,0xbb,0x01,0x00,0xbf,0xb3,
-0x60,0xfd,0x63,0x44,0x60,0x43,0x68,0x60,0xcc,0x62,0x00,0x60,0x10,0x64,0xa2,0xdb,
-0x68,0x60,0xce,0x62,0x63,0x44,0xa2,0xdb,0x1e,0x60,0xd4,0x62,0xa2,0xd1,0x00,0x60,
-0x10,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x26,0x46,0x2f,0x58,0xff,0xff,
-0x22,0x02,0x2e,0xf2,0xff,0xff,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,
-0xe1,0x81,0x5a,0xd2,0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,
-0xf0,0x84,0xe1,0x81,0x5a,0xd2,0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,
-0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,0x65,0x60,0x58,0x4e,0x92,0x78,0xff,0xff,
-0x65,0x60,0x8f,0x78,0xff,0xff,0x65,0x60,0x81,0x78,0xff,0xff,0xcb,0xf3,0xff,0xff,
-0xfc,0xa0,0xfd,0xa0,0xd5,0x02,0x00,0x64,0x40,0x48,0x26,0x46,0x38,0xf2,0x00,0xf4,
-0x10,0x63,0xf4,0xa4,0x60,0x41,0x00,0x65,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,
-0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0x40,0x4c,
-0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,
-0xa0,0xd2,0x01,0xa3,0x00,0x7f,0x60,0x45,0x2c,0x44,0x04,0xa8,0x05,0xa8,0x06,0x03,
-0xc9,0x81,0x2e,0x03,0xd5,0x81,0xc7,0x83,0xce,0x06,0xde,0x00,0x41,0x4c,0x67,0x60,
-0x80,0x61,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,
-0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0x59,0xdb,0x63,0x44,0x01,0x22,0x05,0x00,
-0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,
-0x59,0xdb,0x58,0x60,0x58,0x4e,0xb5,0x78,0xff,0xff,0x65,0x44,0x59,0xdb,0x58,0x60,
-0x58,0x4e,0xb5,0x78,0xff,0xff,0x65,0x44,0x59,0xdb,0x2c,0x41,0xf8,0xa1,0xb4,0x00,
-0x65,0x41,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,
-0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0x40,0x4c,0x64,0xfb,0x63,0x44,0x01,0x22,
-0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,
-0x00,0x7f,0x63,0xfb,0x62,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,0x49,0x03,0x63,0x44,
-0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,
-0x01,0xa3,0x00,0x7f,0x40,0x4d,0x2c,0x44,0x00,0xb8,0xff,0xff,0x05,0x02,0x2d,0x44,
-0x01,0x2a,0x02,0x00,0x10,0x65,0x45,0x48,0x66,0xf3,0xff,0xff,0x00,0xa0,0xff,0xff,
-0x2f,0x03,0x2d,0x44,0xfe,0xb5,0x3c,0x60,0xd2,0x64,0xa0,0xd3,0xff,0xff,0x00,0x7f,
-0xd4,0x84,0x04,0xa4,0x19,0x04,0x60,0x45,0xd5,0x80,0xfc,0xa5,0x15,0x04,0x3c,0x60,
-0xd0,0x64,0xa0,0xd1,0xc7,0x83,0x63,0x44,0x01,0x22,0x05,0x00,0x01,0xac,0xa0,0xd2,
-0x01,0xa3,0x60,0x47,0x02,0x00,0xa0,0xd2,0x01,0xa3,0x00,0x7f,0xa0,0x80,0xff,0xff,
-0x03,0x03,0x28,0x44,0x20,0xbc,0x40,0x48,0x28,0x44,0xff,0x22,0x09,0x00,0x60,0xf1,
-0xff,0xff,0xb0,0x84,0x60,0xfb,0x60,0x45,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,
-0x68,0x60,0xcc,0x62,0x00,0x60,0x04,0x64,0xa2,0xdb,0x1e,0x60,0xd4,0x62,0xa2,0xd1,
-0x00,0x60,0x04,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x26,0x46,0x2f,0x58,
-0xff,0xff,0x0e,0x48,0x65,0x60,0x58,0x4e,0xfc,0x78,0xff,0xff,0x10,0x03,0x29,0xf2,
-0xa3,0xd1,0x60,0x40,0x10,0x2b,0x03,0x00,0x64,0x44,0x02,0xbc,0x02,0x00,0x64,0x44,
-0xfd,0xb4,0xa3,0xdb,0x0a,0xa3,0x3c,0x64,0xa3,0xdb,0xf6,0xa3,0x50,0x00,0x2c,0x43,
-0xa3,0xd3,0xff,0xff,0x60,0x40,0x01,0x26,0x37,0x00,0x43,0x4c,0x29,0xf0,0x01,0x64,
-0x64,0x40,0x10,0x27,0x03,0x64,0xbd,0xdb,0x2e,0xf2,0xff,0xff,0xbd,0xdb,0x2f,0xf2,
-0xbd,0xdb,0xff,0xff,0x30,0xf2,0xbd,0xdb,0x01,0x60,0x92,0x64,0xa0,0xd3,0xff,0xff,
-0x60,0x40,0x08,0x2a,0x02,0x00,0x08,0x7f,0x0a,0x00,0x04,0x2a,0x02,0x00,0x04,0x7f,
-0x06,0x00,0x02,0x2a,0x02,0x00,0x02,0x7f,0x02,0x00,0x01,0x7f,0x01,0x7e,0x60,0x47,
-0xbd,0xdb,0x3c,0x64,0xbd,0xdb,0x29,0xf0,0x34,0xf2,0x64,0x40,0x08,0x27,0xcc,0x84,
-0xbd,0xdb,0x00,0x64,0xbd,0xdb,0x2c,0x43,0x1a,0x00,0x61,0x44,0xdc,0x84,0xd0,0x80,
-0xff,0xff,0xcb,0x06,0x72,0xfb,0xc9,0x00,0x72,0xf1,0x38,0x60,0xe2,0x63,0x00,0x61,
-0xa3,0xd3,0xff,0xff,0xff,0xff,0x01,0x2a,0xf0,0x00,0x10,0xa3,0x61,0x44,0xf1,0xa0,
-0xdd,0x81,0xf6,0x04,0x00,0x63,0x3d,0x60,0x4a,0x62,0x01,0x64,0xa2,0xdb,0x08,0x4e,
-0x00,0xbb,0x2e,0x58,0xff,0xff,0x28,0x60,0xe2,0x65,0x00,0x7f,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0xe0,0x84,0x44,0xd3,0x62,0x43,0x43,0x4c,0x60,0x40,0x01,0x2a,0x10,0x00,
-0x02,0xa3,0x2e,0xf2,0x50,0xfe,0xbd,0xd1,0x2f,0xf2,0xd0,0x80,0xbd,0xd1,0x30,0xf2,
-0xd0,0x80,0xbd,0xd1,0xff,0xff,0xd0,0x80,0xff,0xff,0x02,0x02,0xf8,0xa3,0x1e,0x00,
-0x72,0xf1,0x38,0x60,0xe2,0x63,0x64,0x41,0xff,0x22,0x17,0x00,0xbd,0xd1,0x2e,0xf2,
-0x50,0xfe,0x64,0x40,0x01,0x26,0x04,0x00,0xcd,0x81,0x0e,0xa3,0xf7,0x02,0x0d,0x00,
-0xbd,0xd1,0x2f,0xf2,0xd0,0x80,0xbd,0xd1,0x30,0xf2,0xd0,0x80,0xbd,0xd1,0xff,0xff,
-0xd0,0x80,0xcd,0x81,0xe3,0x01,0x08,0xa3,0xe9,0x02,0x00,0x63,0x00,0xbb,0x2e,0x58,
-0xff,0xff,0xff,0x60,0xff,0x64,0x2b,0xfa,0x2c,0xfa,0x2d,0xfa,0xff,0xff,0x47,0xf3,
-0xff,0xff,0xe8,0x84,0xe8,0x84,0x01,0x00,0x00,0x64,0x1c,0xfa,0x46,0x4d,0xbd,0xf1,
-0x2e,0xf8,0xbe,0xf1,0xff,0xff,0x2f,0xf8,0xbf,0xf1,0x30,0xf8,0xff,0xff,0x81,0xf1,
-0x31,0xf8,0x82,0xf1,0xff,0xff,0x32,0xf8,0x83,0xf1,0x33,0xf8,0x3b,0x60,0x28,0x63,
-0x80,0xf1,0x00,0x64,0x64,0x5e,0xbd,0xdb,0x64,0x47,0x00,0x7f,0xbd,0xdb,0x43,0xf3,
-0x47,0xf1,0x01,0xb4,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xb0,0x84,0x02,0xbc,
-0xbd,0xdb,0x00,0x64,0xbd,0xdb,0x00,0x64,0xbd,0xdb,0x59,0x60,0x20,0x62,0xa2,0xd3,
-0xda,0x85,0xbd,0xdb,0x60,0x41,0x06,0xa4,0x8f,0xfb,0xa5,0xd1,0xda,0x85,0x64,0x44,
-0x00,0x7f,0xcd,0x81,0xbd,0xdb,0x05,0x03,0x64,0x47,0x00,0x7f,0xcd,0x81,0xbd,0xdb,
-0xf4,0x02,0x01,0x64,0xbd,0xdb,0x8f,0xf1,0x17,0x60,0xd2,0x62,0xa2,0xd3,0xda,0x85,
-0xbd,0xdb,0x43,0x48,0x60,0x41,0x41,0x4c,0xc0,0x84,0x02,0xa4,0x8f,0xfb,0xa5,0xd1,
-0xda,0x85,0x64,0x44,0x00,0x7f,0xcd,0x81,0xbd,0xdb,0x05,0x03,0x64,0x47,0x00,0x7f,
-0xcd,0x81,0xbd,0xdb,0xf4,0x02,0x03,0x64,0xbd,0xdb,0x01,0x64,0xbd,0xdb,0x7f,0xf3,
-0xbd,0xdb,0xff,0xff,0x8f,0xf3,0xff,0xff,0x03,0xa4,0x8f,0xfb,0x06,0x64,0xbd,0xdb,
-0x02,0x64,0xbd,0xdb,0x86,0xf1,0x00,0x64,0x64,0x5e,0xbd,0xdb,0x64,0x47,0x00,0x7f,
-0xbd,0xdb,0x8f,0xf3,0xff,0xff,0x04,0xa4,0x8f,0xfb,0x07,0x64,0xbd,0xdb,0x06,0x64,
-0xbd,0xdb,0x17,0x60,0x82,0x62,0xa2,0xd1,0x00,0x64,0x64,0x5e,0xbd,0xdb,0x64,0x47,
-0x00,0x7f,0xbd,0xdb,0x00,0x64,0xbd,0xdb,0x1f,0x60,0x54,0x62,0xa2,0xd3,0xbd,0xdb,
-0x1f,0x60,0x56,0x62,0xa2,0xd3,0xbd,0xdb,0x00,0x64,0xbd,0xdb,0x8f,0xf3,0xff,0xff,
-0x08,0xa4,0x8f,0xfb,0x00,0x64,0xa3,0xdb,0x17,0x60,0xdc,0x62,0xa2,0xd1,0x28,0x43,
-0x2c,0x41,0xa3,0xd3,0xff,0xff,0x60,0x40,0x02,0x3a,0x04,0x00,0x64,0x40,0x01,0x2a,
-0x14,0x00,0x11,0x00,0x04,0x3a,0x04,0x00,0x64,0x40,0x02,0x2a,0x0e,0x00,0x0b,0x00,
-0x0b,0x3a,0x04,0x00,0x64,0x40,0x04,0x2a,0x08,0x00,0x05,0x00,0x16,0x3a,0x05,0x00,
-0x64,0x40,0x08,0x2a,0x02,0x00,0x80,0xbc,0xa3,0xdb,0xcd,0x81,0xdb,0x83,0xe1,0x02,
-0x8f,0xf3,0xff,0xff,0x60,0x41,0x08,0xa4,0x38,0xfa,0x00,0xf4,0x3b,0x60,0x28,0x63,
-0x01,0xf2,0xff,0xff,0x7c,0x7e,0x01,0xfa,0x0c,0x65,0xbd,0xd3,0xbd,0xd1,0x60,0x47,
-0xb0,0x87,0xa5,0xda,0xda,0x85,0xcd,0x81,0xcd,0x81,0x01,0x03,0xf6,0x02,0x2d,0x46,
-0x2e,0x58,0xff,0xff,0x00,0xf4,0x07,0xf0,0x73,0xf3,0x64,0x40,0x02,0x2a,0x49,0x00,
-0x02,0xbc,0x73,0xfb,0x60,0x45,0x26,0x46,0x2e,0xf2,0xff,0xff,0x60,0x47,0xbd,0xf3,
-0x60,0x5c,0x60,0x47,0x2f,0xf2,0xd0,0x80,0x60,0x47,0xbe,0xf3,0x60,0x5c,0x60,0x47,
-0x10,0x07,0x0b,0x04,0x30,0xf2,0xd0,0x80,0x60,0x47,0x60,0x5c,0xbf,0xf3,0x09,0x07,
-0x04,0x04,0x60,0x47,0xd0,0x80,0xff,0xff,0x04,0x07,0xfe,0x64,0xa4,0x84,0x73,0xfb,
-0xff,0xff,0x31,0xf2,0x32,0xf0,0x33,0xf0,0xb0,0x84,0xb0,0x84,0xff,0xff,0x21,0x03,
-0x3b,0x60,0x28,0x63,0x31,0xf0,0xbd,0xd9,0x32,0xf0,0xff,0xff,0xbd,0xd9,0x33,0xf0,
-0xbd,0xd9,0xff,0xff,0x00,0xf4,0x02,0xf0,0xbd,0xd9,0xff,0xff,0x03,0xf0,0xbd,0xd9,
-0x04,0xf0,0xff,0xff,0xbd,0xd9,0x05,0xf0,0xbd,0xd9,0xff,0xff,0x06,0xf0,0xbd,0xd9,
-0x07,0xf0,0xff,0xff,0xbd,0xd9,0x00,0x64,0x08,0xf0,0xa3,0xdb,0x64,0x47,0x60,0x45,
-0x00,0x3b,0x62,0x00,0xbd,0xdb,0xdc,0x84,0xe8,0x81,0x10,0x64,0x58,0xd0,0xcd,0x81,
-0xbd,0xd9,0xfc,0x02,0xd8,0x83,0x04,0x64,0x40,0x4d,0x09,0x61,0x65,0x40,0x01,0x2a,
-0xbd,0xd0,0xff,0xff,0x64,0x44,0x00,0x7f,0x2d,0xda,0x5a,0x8d,0x64,0x47,0x00,0x7f,
-0x2d,0xda,0xcd,0x81,0x5a,0x8d,0xf4,0x02,0x3b,0x60,0x5c,0x63,0x04,0x61,0x65,0x40,
-0x01,0x26,0x02,0xa1,0xa1,0xd2,0xff,0xff,0x01,0xa8,0x59,0xd2,0x4b,0x02,0xfc,0xa0,
-0xff,0xff,0x48,0x07,0xbd,0xdb,0x59,0xd0,0xcc,0x84,0xbd,0xd9,0xfc,0x02,0x00,0x64,
-0xbd,0xdb,0x3b,0x60,0x6a,0x63,0x59,0xd2,0x59,0xd0,0x03,0xa8,0x7f,0xf3,0x3a,0x02,
-0x59,0xd0,0xff,0xff,0xd0,0x80,0xbd,0xd9,0x35,0x02,0x59,0xd2,0x59,0xd0,0x06,0xa8,
-0x59,0xd0,0x30,0x02,0x59,0xd2,0xff,0xff,0x60,0x47,0xb0,0x84,0xbd,0xdb,0x3b,0x60,
-0x5e,0x63,0x00,0x61,0xa3,0xd3,0xff,0xff,0x60,0x40,0xff,0x22,0x16,0x00,0x80,0x2a,
-0x11,0x00,0x7f,0xb4,0xa3,0xdb,0x60,0x40,0x02,0x3a,0x02,0x00,0x01,0xb9,0x0a,0x00,
-0x04,0x3a,0x02,0x00,0x02,0xb9,0x06,0x00,0x0b,0x3a,0x02,0x00,0x04,0xb9,0x02,0x00,
-0x16,0x36,0x08,0xb9,0x02,0xa3,0xe6,0x00,0x0d,0x00,0x3b,0x60,0x68,0x63,0x61,0x44,
-0xa3,0xdb,0x3b,0x60,0x28,0x63,0x39,0x60,0xf0,0x64,0x23,0x61,0xbd,0xd1,0xcd,0x81,
-0x58,0xd9,0xfc,0x02,0x26,0x46,0x2f,0x58,0xff,0xff,0xcb,0xf3,0x0f,0xf0,0xfd,0xa0,
-0xff,0xff,0x07,0x02,0x64,0x40,0x60,0x26,0x04,0x00,0x68,0x60,0x58,0x4e,0xba,0x78,
-0xff,0xff,0x2f,0x58,0xff,0xff,0xd5,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x4d,0x05,
-0x16,0x60,0x42,0x62,0xa2,0xd3,0x6e,0xf1,0xfc,0xa0,0xff,0xff,0x46,0x02,0x64,0x40,
-0x01,0x2a,0x43,0x00,0x68,0x60,0x58,0x4e,0x86,0x78,0xff,0xff,0x26,0x46,0x3d,0x02,
-0x2e,0xf0,0x2b,0xf8,0x2f,0xf0,0xff,0xff,0x2c,0xf8,0x30,0xf0,0x2d,0xf8,0x66,0x60,
-0x58,0x4e,0x45,0x78,0xff,0xff,0x26,0x46,0x00,0xf0,0x04,0x64,0x03,0xfa,0x04,0xf8,
-0x00,0x64,0x0b,0xfa,0x0c,0xfa,0x0f,0xfa,0xff,0xff,0x85,0xf3,0x38,0xf0,0x50,0xbc,
-0x29,0xfa,0x17,0xf8,0x0c,0x64,0x15,0xfa,0x20,0xf2,0xff,0xff,0x60,0x47,0x00,0x7f,
-0x13,0xfa,0x1c,0x64,0x21,0xfa,0x08,0x64,0x28,0xfa,0x00,0x63,0x22,0xfc,0x16,0xfc,
-0x07,0xfc,0x01,0x64,0x19,0xfc,0x1c,0xfc,0x14,0xfa,0xff,0x67,0x0e,0xfa,0x3c,0x60,
-0x82,0x62,0x3c,0x60,0x2e,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0e,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0xc1,0xfe,0x00,0x66,0x46,0x46,0x2f,0x58,0xff,0xff,0xcb,0xf3,
-0x0f,0xf0,0xfd,0xa0,0x20,0x64,0x47,0x02,0x64,0x40,0x60,0x26,0x10,0x64,0x60,0xf1,
-0xff,0xff,0xb0,0x84,0x60,0xfb,0x60,0x45,0x68,0x60,0xcc,0x62,0x90,0x60,0x50,0x64,
-0xa2,0xdb,0x68,0x60,0xce,0x62,0x65,0x44,0xa2,0xdb,0x1e,0x60,0xd4,0x62,0xa2,0xd1,
-0x00,0x60,0x04,0x64,0xb0,0x84,0xa2,0xdb,0xff,0xff,0xcf,0xfe,0x2e,0xf2,0xff,0xff,
-0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,0xe1,0x81,0x5a,0xd2,0xf0,0x85,
-0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,0xe1,0x81,0x5a,0xd2,
-0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,0xe1,0x81,
-0xf0,0x84,0x65,0x60,0x58,0x4e,0x92,0x78,0xff,0xff,0x29,0xf2,0xff,0xff,0x60,0x40,
-0x10,0x2b,0x09,0x00,0x69,0x60,0x58,0x4e,0xdf,0x78,0xff,0xff,0x04,0x03,0x69,0x60,
-0x58,0x4e,0xf9,0x78,0xff,0xff,0x2f,0x58,0xff,0xff,0x00,0xf4,0x04,0x63,0x06,0x00,
-0x00,0xf4,0x07,0xf0,0x10,0x63,0x64,0x40,0x02,0x2a,0x27,0x00,0xbd,0xd2,0xff,0xff,
-0x60,0x47,0x00,0x3a,0x22,0x00,0x60,0x41,0x00,0x36,0x1d,0x00,0x59,0x60,0x20,0x62,
-0xa2,0xd3,0x61,0x45,0xd4,0x80,0xff,0xff,0x18,0x02,0xda,0x82,0x61,0x40,0xfe,0x22,
-0x08,0x00,0x62,0x45,0xbd,0xd2,0xa5,0xd1,0xda,0x82,0xd0,0x80,0xc9,0x81,0xf6,0x03,
-0x0c,0x00,0x61,0x40,0x00,0x36,0x07,0x00,0x62,0x45,0xa3,0xd2,0xa5,0xd1,0xff,0xff,
-0x90,0x80,0xff,0x26,0x02,0x00,0x00,0x64,0x01,0x00,0x01,0x64,0x01,0xb4,0x2e,0x58,
-0xff,0xff,0x2e,0xf0,0x2b,0xf8,0x2f,0xf0,0xff,0xff,0x2c,0xf8,0x30,0xf0,0x2d,0xf8,
-0xff,0xff,0xbd,0xf1,0x2e,0xf8,0xff,0xff,0xbe,0xf1,0x2f,0xf8,0xbf,0xf1,0xff,0xff,
-0x30,0xf8,0x81,0xf1,0x31,0xf8,0xff,0xff,0x82,0xf1,0x32,0xf8,0x83,0xf1,0xff,0xff,
-0x33,0xf8,0x00,0xf0,0x04,0x64,0x03,0xfa,0x04,0xf8,0x00,0x64,0x0b,0xfa,0x0c,0xfa,
-0x0f,0xfa,0xff,0xff,0x85,0xf3,0xff,0xff,0xb0,0xbc,0x29,0xfa,0x0c,0x64,0x15,0xfa,
-0x20,0xf2,0xff,0xff,0x60,0x47,0x00,0x7f,0x13,0xfa,0x1c,0x64,0x21,0xfa,0x08,0x64,
-0x28,0xfa,0x00,0x63,0x22,0xfc,0x16,0xfc,0x07,0xfc,0x01,0x64,0x19,0xfc,0x1c,0xfc,
-0x14,0xfa,0xff,0x67,0x0e,0xfa,0x38,0xf0,0x06,0x64,0x38,0xfa,0x17,0xfa,0x44,0x48,
-0x00,0xf4,0x01,0xf2,0xff,0xff,0x7c,0x7e,0x01,0xfa,0x02,0xf2,0x00,0x63,0x00,0xa0,
-0x04,0xfc,0x07,0x02,0x03,0xf2,0x00,0x63,0xff,0xa0,0xff,0xff,0x3b,0x03,0x0e,0x63,
-0x39,0x00,0xff,0xa0,0x0d,0x63,0x36,0x02,0x43,0xf3,0xff,0xff,0x60,0x40,0x01,0x2a,
-0x31,0x00,0x03,0xf2,0x0e,0x63,0xff,0xa0,0xff,0xff,0x1c,0x02,0x0a,0x63,0x80,0x60,
-0x10,0x64,0xbd,0xda,0x00,0x60,0x3a,0x61,0x01,0x60,0x02,0x65,0x55,0x60,0xaa,0x64,
-0xcd,0x81,0xbd,0xda,0xc4,0x84,0xfc,0x02,0x00,0xf4,0x04,0x63,0x06,0x61,0xcd,0x81,
-0xbd,0xda,0xc4,0x84,0xfc,0x02,0x26,0x46,0x88,0x64,0x38,0xfa,0x17,0xfa,0x00,0xf4,
-0x00,0x63,0x10,0x00,0xfd,0xa0,0xff,0xff,0x0d,0x02,0x0f,0x63,0x55,0x60,0xaa,0x65,
-0x05,0xf2,0xff,0xff,0xd4,0x80,0x88,0x64,0x05,0x02,0x28,0x45,0xd4,0x80,0xff,0xff,
-0x01,0x02,0x00,0x63,0x03,0xf2,0x04,0xfc,0xdc,0x84,0x03,0xfa,0x26,0x46,0x3c,0x60,
-0x82,0x62,0x3c,0x60,0x2e,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0e,0x64,0x5a,0xdb,
-0xff,0xff,0x2b,0xff,0xc1,0xfe,0x00,0x66,0x46,0x46,0x2e,0x58,0xff,0xff,0x1e,0x60,
-0x92,0x62,0xa2,0xd1,0x59,0x60,0x76,0x64,0xa0,0xd3,0xfa,0x65,0xd0,0x80,0xff,0xff,
-0x37,0x0d,0xc4,0x84,0xa2,0xdb,0x59,0x60,0x74,0x64,0xa0,0xd1,0x39,0x60,0xe2,0x64,
-0x64,0x43,0xd0,0x80,0x28,0x60,0xe2,0x64,0x01,0x07,0x60,0x43,0x10,0x61,0xa3,0xd3,
-0xff,0xff,0xff,0xff,0x01,0x2a,0x1e,0x00,0x0a,0x65,0x46,0xd3,0xff,0xff,0xcc,0x84,
-0xa2,0xdb,0x03,0x02,0x00,0x64,0xa3,0xdb,0x15,0x00,0xfe,0xa2,0xa2,0xd3,0xff,0xff,
-0x60,0x47,0x60,0x40,0x08,0x2a,0x02,0x00,0x08,0x7f,0x0a,0x00,0x04,0x2a,0x02,0x00,
-0x04,0x7f,0x06,0x00,0x02,0x2a,0x02,0x00,0x02,0x7f,0x02,0x00,0x01,0x7f,0x01,0x7e,
-0x60,0x47,0xa2,0xdb,0xcd,0x81,0x10,0xa3,0xda,0x02,0x59,0x60,0x74,0x64,0xa0,0xdd,
-0x2e,0x58,0xff,0xff,0x3a,0x60,0xb0,0x63,0x6b,0xf3,0xff,0xff,0x00,0xbc,0x60,0x41,
-0x10,0x03,0x2b,0xf2,0x50,0xfe,0xbd,0xd1,0x2c,0xf2,0xd0,0x80,0xbd,0xd1,0x2d,0xf2,
-0xd0,0x80,0xbd,0xd1,0xff,0xff,0xd0,0x80,0xff,0xff,0x04,0x03,0xfa,0xa1,0xff,0xff,
-0xf0,0x02,0x01,0xbc,0x2e,0x58,0xff,0xff,0x3a,0x60,0x38,0x63,0x6a,0xf3,0xe6,0x00,
-0x3a,0x60,0xb0,0x65,0x6b,0xf3,0xff,0xff,0xc4,0x83,0x88,0xa0,0x06,0xa4,0x09,0x05,
-0x6b,0xfb,0xff,0xff,0x2b,0xf2,0xbd,0xdb,0x2c,0xf2,0xff,0xff,0xbd,0xdb,0x2d,0xf2,
-0xbd,0xdb,0x2e,0x58,0xff,0xff,0x3a,0x60,0x38,0x65,0x6a,0xf3,0xff,0xff,0xc4,0x83,
-0x88,0xa0,0x06,0xa4,0x09,0x05,0x6a,0xfb,0xff,0xff,0x2b,0xf2,0xbd,0xdb,0x2c,0xf2,
-0xff,0xff,0xbd,0xdb,0x2d,0xf2,0xbd,0xdb,0x2e,0x58,0xff,0xff,0x3a,0x60,0xb0,0x63,
-0x6b,0xf3,0xff,0xff,0x00,0xbc,0x60,0x41,0x10,0x03,0x2e,0xf2,0x50,0xfe,0xbd,0xd1,
-0x2f,0xf2,0xd0,0x80,0xbd,0xd1,0x30,0xf2,0xd0,0x80,0xbd,0xd1,0xff,0xff,0xd0,0x80,
-0xff,0xff,0x04,0x03,0xfa,0xa1,0xff,0xff,0xf0,0x02,0x01,0xbc,0x2e,0x58,0xff,0xff,
-0x3a,0x60,0xb0,0x65,0x6b,0xf3,0xff,0xff,0xc4,0x83,0x88,0xa0,0x06,0xa4,0x09,0x05,
-0x6b,0xfb,0xff,0xff,0x2e,0xf2,0xbd,0xdb,0x2f,0xf2,0xff,0xff,0xbd,0xdb,0x30,0xf2,
-0xbd,0xdb,0x2e,0x58,0xff,0xff,0x3c,0x60,0x64,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa8,
-0x60,0x46,0x03,0x02,0x3d,0x60,0x2f,0x78,0xff,0xff,0x26,0x45,0xd4,0x80,0x0f,0xf0,
-0xf9,0x03,0x64,0x44,0x70,0xb0,0x70,0x2a,0x03,0x00,0x6f,0x60,0x7a,0x78,0xff,0xff,
-0x64,0x40,0x04,0x2a,0x13,0x00,0x67,0x60,0x60,0x62,0x00,0x64,0xa2,0xdb,0x29,0xf2,
-0xff,0xff,0xff,0xff,0x40,0x2b,0x0f,0x00,0x64,0x40,0x80,0x2b,0x07,0x00,0x1b,0xf2,
-0x22,0xf0,0x60,0x47,0xc0,0xb4,0xb0,0x84,0x22,0xfa,0x05,0x00,0x00,0x64,0x40,0x46,
-0x3d,0x60,0x2f,0x78,0xff,0xff,0x32,0x40,0x01,0x2a,0x07,0x00,0x70,0x60,0xb6,0x78,
-0xff,0xff,0x03,0x03,0x6f,0x60,0x7a,0x78,0xff,0xff,0x46,0x46,0x0f,0xf0,0xff,0xff,
-0x64,0x44,0x80,0x26,0x0e,0x00,0x32,0x40,0x01,0x2a,0x08,0x00,0x22,0xf0,0x07,0x60,
-0x01,0x64,0xb0,0x84,0x22,0xfa,0x6f,0x60,0x93,0x78,0xff,0xff,0x6f,0x60,0x7a,0x78,
-0xff,0xff,0x08,0x26,0x2a,0x00,0x5b,0x60,0x22,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x29,0xf2,
-0xff,0xff,0xff,0xff,0x03,0x27,0x06,0x00,0x67,0x60,0x78,0x62,0xa2,0xd3,0xff,0xff,
-0x01,0xa4,0xa2,0xdb,0x32,0x44,0x01,0x2a,0x05,0x00,0x22,0xf0,0x07,0x60,0x02,0x64,
-0xb0,0x84,0x04,0x00,0x02,0x2a,0x06,0x00,0x00,0x60,0x02,0x64,0x22,0xfa,0x6f,0x60,
-0x93,0x78,0xff,0xff,0x6f,0x60,0x7a,0x78,0xff,0xff,0x32,0x40,0x01,0x2a,0x08,0x00,
-0x22,0xf0,0x07,0x60,0x00,0x64,0xb0,0x84,0x22,0xfa,0x6f,0x60,0x93,0x78,0xff,0xff,
-0x29,0xf2,0x0f,0xf0,0x60,0x40,0xa4,0x36,0x08,0x00,0x0c,0xb4,0x04,0x36,0x02,0x00,
-0x0c,0x3a,0x06,0x00,0x6f,0x60,0x7a,0x78,0xff,0xff,0x6f,0x60,0x75,0x78,0xff,0xff,
-0x64,0x40,0x60,0x26,0x03,0x00,0x6b,0x60,0x84,0x78,0xff,0xff,0x95,0xf3,0x29,0xf2,
-0x00,0xbc,0xd3,0xf1,0x20,0x03,0x64,0x40,0x00,0x3a,0x4c,0x00,0x60,0x40,0x40,0x36,
-0x49,0x00,0x80,0x3a,0x15,0x00,0x5c,0x63,0x61,0x60,0xbc,0x61,0xbd,0xd2,0xa1,0xd1,
-0x02,0xa1,0xbd,0xd2,0xd0,0x80,0xa1,0xd1,0x02,0xa1,0x0a,0x02,0xd0,0x80,0xbd,0xd2,
-0xa1,0xd1,0x06,0x02,0xd0,0x80,0xff,0xff,0x03,0x02,0x6d,0x60,0xd3,0x78,0xff,0xff,
-0x6f,0x60,0x7a,0x78,0xff,0xff,0x5c,0x63,0x60,0x40,0x02,0x2b,0x62,0x63,0xbd,0xd2,
-0x81,0xf1,0xbd,0xd2,0xd0,0x80,0x82,0xf1,0x07,0x02,0xd0,0x80,0xbd,0xd2,0x83,0xf1,
-0x03,0x02,0xd0,0x80,0xff,0xff,0x1e,0x03,0xd5,0xf3,0xff,0xff,0xfd,0xa0,0x29,0xf2,
-0x04,0x04,0x60,0x40,0x80,0x36,0x16,0x00,0x12,0x00,0x20,0x40,0x40,0x26,0xf9,0x00,
-0x16,0x60,0x42,0x62,0xa2,0xd3,0x29,0xf2,0xfc,0xa0,0x2b,0xf0,0x08,0x02,0x60,0x40,
-0x80,0x36,0x02,0x00,0x40,0x3a,0x03,0x00,0x64,0x40,0x01,0x26,0x03,0x00,0x6f,0x60,
-0x7a,0x78,0xff,0xff,0x29,0xf2,0xff,0xff,0x0c,0xb4,0x08,0x3a,0x3a,0x00,0x70,0x60,
-0x15,0x78,0xff,0xff,0x17,0x60,0x74,0x64,0xa0,0xd3,0xcb,0xf3,0x00,0xa0,0xfe,0xa0,
-0xee,0x02,0xed,0x03,0x5a,0x60,0x88,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1f,0xf2,0xff,0xff,
-0x60,0x45,0x5a,0x60,0xce,0x64,0xa0,0xd3,0xff,0xff,0xc4,0x84,0xa2,0xdb,0x05,0x04,
-0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x26,0xf2,0xff,0xff,0x60,0x47,
-0x00,0x7f,0xe0,0x84,0xe0,0x84,0x60,0x45,0x5a,0x60,0x8c,0x64,0xc4,0x84,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x0f,0xf2,0xff,0xff,0x60,0x40,0x40,0x26,0x42,0x00,0x32,0x44,0x02,0x26,
-0x3f,0x00,0x58,0x60,0x5a,0x64,0xa0,0xd3,0xff,0xff,0x00,0xa0,0x60,0x41,0x13,0x03,
-0x58,0x60,0x5e,0x63,0x2b,0xf2,0x50,0xfe,0xbd,0xd1,0x2c,0xf2,0xd0,0x80,0xbd,0xd1,
-0x2d,0xf2,0xd0,0x80,0xbd,0xd1,0xff,0xff,0xd0,0x80,0xfa,0xa1,0x04,0x01,0xf2,0x02,
-0x6f,0x60,0x7a,0x78,0xff,0xff,0xcb,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x20,0x02,
-0x2e,0xf2,0xff,0xff,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,0xe1,0x81,
-0x5a,0xd2,0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,
-0xe1,0x81,0x5a,0xd2,0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,
-0xf0,0x84,0xf4,0xd6,0x7e,0x00,0x00,0x10,0xe1,0x81,0xf0,0x84,0x65,0x60,0x58,0x4e,
-0x92,0x78,0xff,0xff,0x00,0x03,0x6f,0x60,0x64,0x78,0xff,0xff,0x32,0x40,0x02,0x26,
-0x2e,0x00,0x29,0xf0,0x43,0xf3,0x64,0x40,0x08,0x2a,0x29,0x00,0x64,0x40,0x40,0x27,
-0x0a,0x00,0x02,0x2a,0x08,0x00,0x38,0xf2,0xff,0xff,0x00,0xa8,0xff,0xff,0x03,0x03,
-0x6f,0x60,0x7a,0x78,0xff,0xff,0x29,0xf0,0x03,0x67,0xa0,0x84,0xff,0xff,0x00,0x37,
-0x62,0x63,0x02,0x37,0x5c,0x63,0x01,0x37,0x56,0x63,0x03,0x37,0x10,0x00,0xbd,0xd2,
-0x81,0xf1,0xbd,0xd2,0xd0,0x80,0x82,0xf1,0x07,0x02,0xd0,0x80,0xbd,0xd2,0x83,0xf1,
-0x03,0x02,0xd0,0x80,0xff,0xff,0x03,0x03,0x6f,0x60,0x7a,0x78,0xff,0xff,0x1e,0x60,
-0x9e,0x62,0xa2,0xd5,0x1e,0x60,0x92,0x62,0xa2,0xd3,0xff,0xff,0x40,0x48,0x09,0xf2,
-0x46,0x4b,0x00,0xbe,0x12,0xf2,0x19,0x03,0x60,0x45,0x28,0x44,0xd4,0x81,0x27,0x60,
-0x10,0x65,0xd5,0x80,0x46,0x45,0xf3,0x04,0x09,0xf2,0x2b,0x46,0x09,0xfa,0x5b,0x60,
-0x18,0x62,0xa2,0xd3,0xff,0xff,0x01,0xa4,0xa2,0xdb,0xa2,0xff,0x1a,0x60,0x58,0x4f,
-0x9e,0x78,0xff,0xff,0xa3,0xff,0x2b,0x46,0xe2,0x00,0x26,0x46,0x34,0xf2,0xff,0xff,
-0x0f,0xb4,0x29,0xf0,0x03,0x02,0x64,0x40,0x04,0x2b,0x67,0x00,0x60,0x40,0x0f,0x26,
-0x7d,0x00,0x5a,0x60,0x74,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,
-0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x26,0xf2,0xff,0xff,0x60,0x47,
-0x00,0x7f,0xe0,0x84,0xe0,0x84,0x60,0x45,0x5a,0x60,0x78,0x64,0xc4,0x84,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x1f,0xf2,0xff,0xff,0x60,0x45,0x5a,0x60,0xce,0x64,0xa0,0xd3,0xff,0xff,
-0xc4,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x1e,0x60,0x92,0x62,0xa2,0xd1,0xff,0xff,0x12,0xf8,0x3c,0x60,0x82,0x62,0x00,0x64,
-0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x00,0x64,
-0x09,0xfa,0x05,0xf2,0x06,0xf2,0x60,0x43,0x05,0xfa,0x60,0x46,0x01,0xf0,0x7f,0x60,
-0xff,0x64,0xa0,0x84,0x01,0xfa,0x00,0x64,0x00,0xf0,0x00,0xfa,0xc0,0x80,0x44,0x45,
-0x08,0x03,0x25,0x46,0x05,0xfc,0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,
-0xa3,0xff,0x26,0x46,0x70,0x60,0x58,0x4e,0x99,0x78,0xff,0xff,0x09,0x02,0x2b,0x46,
-0x26,0x44,0x09,0xfa,0x6f,0x60,0x8e,0x78,0xff,0xff,0x6d,0x60,0xb4,0x78,0xff,0xff,
-0x09,0x45,0x09,0xf0,0x26,0x46,0x09,0xf8,0x2b,0x46,0x26,0x44,0x09,0xfa,0xa2,0xff,
-0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,0x5b,0x60,0x12,0x62,0xa2,0xd3,
-0xff,0xff,0x01,0xa4,0xa2,0xdb,0x6f,0x60,0x8e,0x78,0xff,0xff,0x70,0x60,0x58,0x4e,
-0x99,0x78,0xff,0xff,0x39,0x02,0x5a,0x60,0x74,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x26,0xf2,
-0xff,0xff,0x60,0x47,0x00,0x7f,0xe0,0x84,0xe0,0x84,0x60,0x45,0x5a,0x60,0x78,0x64,
-0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1f,0xf2,0xff,0xff,0x60,0x45,0x5a,0x60,0xce,0x64,
-0xa0,0xd3,0xff,0xff,0xc4,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x5b,0x60,0x16,0x62,0xa2,0xd3,0xff,0xff,0x01,0xa4,0xa2,0xdb,
-0x26,0x46,0x6f,0x60,0x7a,0x78,0xff,0xff,0x34,0xf2,0x26,0x46,0x34,0xf2,0x01,0xa5,
-0xd4,0x80,0x29,0x46,0x6a,0x03,0x01,0xa4,0xd4,0x80,0x26,0x46,0x23,0x02,0x5a,0x60,
-0xe6,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x26,0xf2,0xff,0xff,0x60,0x47,0x00,0x7f,0xe0,0x84,
-0xe0,0x84,0x60,0x45,0x5a,0x60,0xea,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x6f,0x60,
-0x7a,0x78,0xff,0xff,0x29,0x46,0x05,0xf2,0x09,0xf0,0x2b,0x46,0x09,0xf8,0x26,0x46,
-0x05,0xf4,0x29,0x43,0x00,0xfc,0x26,0x46,0x05,0xfa,0x5a,0x60,0x74,0x64,0xa0,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x26,0xf2,0xff,0xff,0x60,0x47,0x00,0x7f,0xe0,0x84,0xe0,0x84,0x60,0x45,
-0x5a,0x60,0x78,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,
-0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1f,0xf2,0xff,0xff,0x60,0x45,
-0x5a,0x60,0xce,0x64,0xa0,0xd3,0xff,0xff,0xc4,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,
-0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x5b,0x60,0x14,0x62,0xa2,0xd3,0xff,0xff,
-0x01,0xa4,0xa2,0xdb,0x6f,0x60,0x7a,0x78,0xff,0xff,0x34,0xfa,0x5a,0x60,0x74,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x26,0xf2,0xff,0xff,0x60,0x47,0x00,0x7f,0xe0,0x84,0xe0,0x84,
-0x60,0x45,0x5a,0x60,0x78,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1f,0xf2,0xff,0xff,
-0x60,0x45,0x5a,0x60,0xce,0x64,0xa0,0xd3,0xff,0xff,0xc4,0x84,0xa2,0xdb,0x05,0x04,
-0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x3c,0x60,0x82,0x62,0x00,0x64,
-0xa2,0xdb,0x26,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0x26,0x46,
-0x7f,0x60,0xff,0x65,0x00,0xf0,0x38,0xf2,0xff,0xff,0x06,0xf4,0x01,0xf2,0x60,0x41,
-0xa4,0x84,0x01,0xfa,0x00,0xf2,0x00,0x63,0x00,0xfc,0x66,0x45,0x26,0x46,0x00,0xfa,
-0x29,0x46,0x65,0x44,0x05,0xfa,0x64,0x45,0x06,0xf0,0x06,0xfa,0x64,0x46,0x65,0x44,
-0x00,0xfa,0x29,0x46,0x38,0xf0,0x61,0x44,0xc0,0x84,0x38,0xfa,0x26,0x46,0x29,0xf0,
-0x00,0xf2,0x06,0x45,0x00,0xa8,0x66,0x44,0x01,0x02,0x05,0xfa,0x64,0x40,0x04,0x2b,
-0x0e,0x00,0x1e,0x60,0x92,0x62,0xa2,0xd3,0x29,0x46,0x12,0xfa,0xa2,0xff,0x1a,0x60,
-0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,0x6f,0x60,0x8e,0x78,0xff,0xff,0x29,0x46,
-0x38,0xf2,0x09,0xf0,0x60,0x47,0x3f,0xfa,0x2b,0x46,0x09,0xf8,0x26,0x46,0xff,0x60,
-0xf0,0x65,0x34,0xf2,0x29,0xf0,0xa4,0x84,0x29,0x46,0x34,0xfa,0xf7,0x60,0xff,0x64,
-0x0b,0xfa,0xa0,0x9c,0x29,0xf8,0x00,0x64,0x09,0xfa,0x06,0xf4,0x80,0x67,0x01,0xf2,
-0x60,0x45,0xb4,0x83,0x01,0xfc,0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,
-0xa3,0xff,0x09,0x46,0x29,0x46,0x95,0xf3,0xff,0xff,0x60,0x40,0x01,0x26,0x03,0x00,
-0x6f,0x60,0x51,0x78,0xff,0xff,0x6f,0x60,0x64,0x78,0xff,0xff,0x95,0xf3,0xff,0xff,
-0x60,0x40,0x01,0x26,0x03,0x00,0x6e,0x60,0x7e,0x78,0xff,0xff,0x29,0xf2,0xff,0xff,
-0xff,0xff,0x50,0x3a,0xf0,0x00,0x5c,0x63,0x61,0x60,0xbc,0x61,0xbd,0xd2,0xa1,0xd1,
-0x02,0xa1,0xbd,0xd2,0xd0,0x80,0xa1,0xd1,0x02,0xa1,0xe5,0x02,0xd0,0x80,0xbd,0xd2,
-0xa1,0xd1,0xe1,0x02,0xd0,0x80,0xff,0xff,0xde,0x02,0x26,0x46,0x28,0x60,0xda,0x63,
-0x00,0xf4,0x02,0xf2,0xbd,0xdb,0xff,0xff,0x03,0xf2,0xbd,0xdb,0x04,0xf2,0xff,0xff,
-0xbd,0xdb,0x05,0xf2,0xa3,0xdb,0xfa,0xa3,0x26,0x46,0x00,0x60,0x00,0x65,0xa3,0xd3,
-0x23,0xf0,0x00,0x61,0xd0,0x84,0xf1,0x81,0xd4,0x84,0xf1,0x81,0xbd,0xdb,0xa3,0xd3,
-0x03,0xb1,0x03,0xa9,0x24,0xf0,0x42,0xfe,0x01,0x03,0xcc,0x84,0xf1,0x81,0xd0,0x84,
-0xf1,0x81,0xbd,0xdb,0xa3,0xd3,0x03,0xb1,0x03,0xa9,0x27,0xf0,0x42,0xfe,0x01,0x03,
-0xcc,0x84,0xf1,0x81,0xd0,0x84,0xf1,0x81,0xbd,0xdb,0xa3,0xd3,0x03,0xb1,0x03,0xa9,
-0x28,0xf0,0x01,0x03,0xcc,0x84,0xd0,0x84,0xa3,0xdb,0x01,0x64,0x23,0xfb,0xff,0xff,
-0x1a,0xff,0x67,0x60,0x6a,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0xff,0xff,0x68,0x03,
-0x26,0x46,0x00,0xf4,0x02,0xf2,0x5a,0xd2,0x40,0x47,0x40,0x48,0x5a,0xd2,0x5a,0xd2,
-0x40,0x49,0x60,0x41,0x5a,0xd0,0x80,0xf9,0x40,0x63,0xad,0x80,0xf0,0xa3,0x09,0x02,
-0x3c,0x03,0x29,0x41,0x28,0x44,0x40,0x49,0x27,0x44,0x40,0x48,0x00,0x64,0x40,0x47,
-0xf4,0x00,0xd1,0x80,0x01,0x02,0x31,0x04,0x10,0xa3,0x80,0x60,0x00,0x65,0xa5,0x80,
-0xcf,0x83,0x08,0x02,0x27,0x44,0x60,0x87,0x28,0x44,0x70,0x88,0x29,0x44,0x70,0x89,
-0xf1,0x81,0xf5,0x00,0xe7,0xa3,0x64,0x44,0x00,0xa0,0x00,0x62,0x02,0x02,0x00,0x61,
-0x1c,0x00,0xe0,0x84,0xde,0x82,0xfd,0x04,0x42,0xfe,0xf8,0x84,0x62,0x45,0xc7,0x83,
-0x60,0x45,0x02,0xfe,0xd5,0x84,0x02,0x05,0x01,0x05,0x61,0x44,0xcf,0x83,0x60,0x41,
-0x08,0x03,0x27,0x44,0x60,0x87,0x28,0x44,0x70,0x88,0x29,0x44,0x70,0x89,0xf1,0x81,
-0xf1,0x00,0xce,0x82,0xe9,0x81,0xfd,0x02,0xf1,0x81,0x02,0xf2,0xff,0xff,0x60,0x47,
-0xe8,0x84,0xe8,0x84,0x5a,0xd2,0x3f,0xb5,0xe0,0x84,0xe0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0xe0,0x84,0xb4,0x84,0x61,0x45,0xd4,0x84,0xc0,0x84,0xe0,0x84,0xe0,0x84,
-0xe0,0x84,0xe0,0x93,0x67,0x60,0x6a,0x62,0x00,0x64,0xa2,0xdb,0x26,0x46,0x6d,0x00,
-0xcb,0xf3,0xff,0xff,0xfd,0xa0,0xff,0xff,0x68,0x02,0x3d,0x60,0x1c,0x64,0xa0,0xd3,
-0xff,0xff,0x01,0xbc,0xa2,0xdb,0x29,0xf2,0xff,0xff,0x60,0x40,0x10,0x2b,0x09,0x00,
-0x69,0x60,0x58,0x4e,0xdf,0x78,0xff,0xff,0x04,0x03,0x69,0x60,0x58,0x4e,0xf9,0x78,
-0xff,0xff,0x2e,0xf2,0xff,0xff,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,
-0xe1,0x81,0x5a,0xd2,0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,0xe1,0x81,
-0xf0,0x84,0xe1,0x81,0x5a,0xd2,0xf0,0x85,0x94,0x84,0x60,0x41,0xe1,0x81,0xf0,0x84,
-0xe1,0x81,0xf0,0x84,0xe1,0x81,0xf0,0x84,0x65,0x60,0x58,0x4e,0x92,0x78,0xff,0xff,
-0x34,0x03,0x29,0xf2,0x34,0xf0,0x60,0x40,0x08,0x3a,0x61,0x00,0x08,0x2b,0x2a,0x00,
-0x0c,0xa3,0xa3,0xd3,0xff,0xff,0xd0,0x80,0xff,0xff,0x25,0x02,0x5a,0x60,0xe6,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x26,0xf2,0xff,0xff,0x60,0x47,0x00,0x7f,0xe0,0x84,0xe0,0x84,
-0x60,0x45,0x5a,0x60,0xea,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x6f,0x60,0x7a,0x78,
-0xff,0xff,0x69,0x00,0x0c,0xa3,0xa3,0xd9,0x32,0x00,0x67,0x60,0x46,0x65,0x29,0xf2,
-0x34,0xf0,0x60,0x40,0xa4,0x36,0x72,0x00,0x08,0x2b,0x28,0x00,0xa5,0xd3,0xff,0xff,
-0xd0,0x80,0xff,0xff,0x23,0x02,0x5a,0x60,0xe6,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x26,0xf2,
-0xff,0xff,0x60,0x47,0x00,0x7f,0xe0,0x84,0xe0,0x84,0x60,0x45,0x5a,0x60,0xea,0x64,
-0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x6f,0x60,0x7a,0x78,0xff,0xff,0xa5,0xd9,0x29,0xf2,
-0xff,0xff,0xff,0xff,0x0c,0x22,0x42,0x00,0x5a,0x60,0x74,0x64,0xa0,0xd3,0xff,0xff,
-0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,
-0x26,0xf2,0xff,0xff,0x60,0x47,0x00,0x7f,0xe0,0x84,0xe0,0x84,0x60,0x45,0x5a,0x60,
-0x78,0x64,0xc4,0x84,0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,
-0xa2,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x1f,0xf2,0xff,0xff,0x60,0x45,0x5a,0x60,
-0xce,0x64,0xa0,0xd3,0xff,0xff,0xc4,0x84,0xa2,0xdb,0x05,0x04,0xda,0x82,0xa2,0xd3,
-0xff,0xff,0xdc,0x84,0xa2,0xdb,0x46,0x48,0x00,0xf4,0x80,0x60,0x87,0x65,0x66,0x44,
-0xac,0x80,0x05,0xf2,0xff,0xff,0xd4,0x80,0x08,0x03,0x07,0x02,0x6f,0x60,0xc4,0x78,
-0xff,0xff,0x03,0x02,0x6a,0x60,0x0c,0x78,0xff,0xff,0x28,0x46,0x38,0xf2,0x49,0xf1,
-0xff,0xff,0xd0,0x80,0xff,0xff,0x10,0x07,0x78,0x43,0x04,0xa3,0x56,0xfd,0x0c,0x60,
-0x46,0x64,0xa0,0xd7,0xff,0xff,0xff,0xff,0x6a,0x60,0x0c,0x78,0xff,0xff,0x95,0xf3,
-0xff,0xff,0x60,0x40,0x01,0x26,0x19,0x00,0x0f,0x4e,0x46,0x45,0x3c,0x60,0x82,0x62,
-0x00,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,
-0xa2,0xff,0x1a,0x60,0x58,0x4f,0x9e,0x78,0xff,0xff,0xa3,0xff,0xd1,0xfe,0x0e,0x4f,
-0x00,0x64,0x40,0x46,0x6a,0x60,0x0c,0x78,0xff,0xff,0x3c,0x60,0x82,0x62,0x3c,0x60,
-0x6a,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,
-0xd2,0xfe,0x00,0x64,0x40,0x46,0x6a,0x60,0x0c,0x78,0xff,0xff,0x2f,0x58,0xff,0xff,
-0x29,0xf2,0xff,0xff,0x60,0x47,0x56,0x63,0x01,0xb0,0x01,0x26,0x62,0x63,0xbd,0xd0,
-0x39,0xf8,0xbd,0xd0,0xff,0xff,0x3a,0xf8,0xbd,0xd0,0x3b,0xf8,0x02,0xb0,0x5c,0x63,
-0x04,0x03,0x62,0x63,0x03,0xb0,0x02,0x3a,0x6a,0x63,0xbd,0xd0,0x3c,0xf8,0xbd,0xd0,
-0xff,0xff,0x3d,0xf8,0xbd,0xd0,0x3e,0xf8,0x2f,0x58,0xff,0xff,0x06,0x67,0x06,0xf2,
-0x60,0x45,0xd4,0x80,0xff,0xff,0x48,0x02,0x17,0x60,0x7a,0x63,0xa3,0xd3,0x08,0xfe,
-0xff,0xff,0x04,0x26,0x41,0x00,0x07,0x67,0x06,0xfa,0x28,0x46,0x00,0xf0,0x04,0x64,
-0x03,0xfa,0x04,0xf8,0x00,0x64,0x0b,0xfa,0x0c,0xfa,0xff,0xff,0x0f,0xfa,0x2e,0xf2,
-0x2b,0xfa,0xff,0xff,0x2f,0xf2,0x2c,0xfa,0x30,0xf2,0xff,0xff,0x2d,0xfa,0xbd,0xf1,
-0x2e,0xf8,0xff,0xff,0xbe,0xf1,0x2f,0xf8,0xbf,0xf1,0xff,0xff,0x30,0xf8,0x85,0xf3,
-0xff,0xff,0x08,0xbc,0x43,0xf1,0xff,0xff,0x64,0x40,0x01,0x2a,0x03,0x00,0x60,0x47,
-0x40,0xbc,0x60,0x47,0x29,0xfa,0x00,0x63,0x28,0xfc,0x22,0xfc,0x3a,0x60,0x58,0x4e,
-0x14,0x78,0xff,0xff,0xff,0x7f,0x00,0x7e,0x0e,0xfa,0x3c,0x60,0x82,0x62,0x3c,0x60,
-0x28,0x64,0xa2,0xdb,0x66,0x44,0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,
-0xc1,0xfe,0x00,0x64,0x40,0x46,0x48,0xfe,0x6f,0x60,0x5f,0x78,0xff,0xff,0x2b,0xf0,
-0x67,0x44,0xd0,0x80,0x38,0xf2,0x7b,0x02,0xdc,0xa0,0x00,0xf4,0x78,0x04,0x06,0x60,
-0x08,0x65,0x05,0xf2,0x09,0xf0,0xd4,0x80,0x01,0x60,0x00,0x64,0x70,0x02,0xd0,0x80,
-0x67,0x60,0x06,0x63,0x6c,0x02,0x67,0x60,0x0a,0x64,0xa0,0xd3,0xff,0xff,0xdc,0x84,
-0xa2,0xdb,0x12,0xf0,0xbd,0xd3,0x13,0xf0,0xd0,0x80,0xa3,0xd3,0x60,0x02,0xd0,0x80,
-0x00,0xa0,0x5d,0x02,0x5c,0x03,0xbd,0xf3,0x0f,0xfa,0xbe,0xf3,0xff,0xff,0x10,0xfa,
-0xbf,0xf3,0x11,0xfa,0x02,0x60,0x00,0x64,0x09,0xfa,0x14,0x63,0x1e,0x61,0x26,0x65,
-0xa3,0xd2,0xa1,0xd0,0xa1,0xda,0x64,0x44,0xbd,0xda,0xd5,0x80,0xd9,0x81,0xf8,0x02,
-0x26,0x46,0x85,0xf3,0x43,0xf1,0x08,0xbc,0x60,0x47,0x64,0x40,0x01,0x26,0x40,0xbc,
-0x60,0x47,0x29,0xfa,0xff,0x7f,0x00,0x7e,0x0e,0xfa,0x28,0x64,0x38,0xfa,0x00,0x64,
-0x22,0xfa,0x28,0xfa,0xff,0xff,0x81,0xf3,0x2b,0xfa,0x82,0xf3,0xff,0xff,0x2c,0xfa,
-0x83,0xf3,0x2d,0xfa,0xff,0xff,0xbd,0xf3,0x2e,0xfa,0xbe,0xf3,0xff,0xff,0x2f,0xfa,
-0xbf,0xf3,0x30,0xfa,0xff,0xff,0x00,0xf4,0x0f,0xf2,0x10,0xf0,0x60,0x45,0x11,0xf2,
-0x26,0x46,0x33,0xfa,0x64,0x44,0x32,0xfa,0x65,0x44,0x31,0xfa,0x3a,0x60,0x58,0x4e,
-0x14,0x78,0xff,0xff,0x3c,0x60,0x82,0x62,0x3c,0x60,0x28,0x64,0xa2,0xdb,0x66,0x44,
-0x5a,0xdb,0x0a,0x64,0x5a,0xdb,0xff,0xff,0x2b,0xff,0xc1,0xfe,0x67,0x60,0x0c,0x64,
-0xa0,0xd3,0xff,0xff,0xdc,0x84,0xa2,0xdb,0x6f,0x60,0x8e,0x78,0xff,0xff,0x26,0x46,
-0x6b,0x60,0x03,0x78,0xff,0xff,0x1e,0x60,0x9e,0x64,0xa0,0xd5,0x66,0x45,0x09,0xf2,
-0x46,0x4b,0x00,0xbe,0x46,0x49,0x12,0x03,0x30,0xf0,0x65,0x46,0x30,0xf2,0x2f,0xf0,
-0xd0,0x80,0x29,0x46,0xf4,0x02,0x2f,0xf2,0x2e,0xf2,0xd0,0x80,0x65,0x46,0x03,0x02,
-0x2e,0xf0,0xff,0xff,0xd0,0x80,0x29,0x46,0xea,0x02,0x08,0xfe,0x2e,0x58,0xff,0xff,
-0x00,0x64,0x00,0xa0,0x68,0x60,0xd4,0x62,0xa2,0xd3,0x29,0xf0,0x60,0x40,0x00,0x36,
-0x34,0x00,0x01,0x3a,0x07,0x00,0x64,0x44,0x00,0x7f,0x80,0x65,0xd4,0x80,0xff,0xff,
-0x2f,0x03,0x2b,0x00,0x64,0x44,0x0c,0xb4,0xf8,0xa0,0xff,0xff,0x44,0x03,0x64,0x44,
-0x80,0x36,0x26,0x00,0xb4,0x36,0x27,0x00,0xc4,0x36,0x28,0x00,0xd4,0x36,0x29,0x00,
-0x40,0x36,0x2a,0x00,0xe4,0x36,0x2b,0x00,0x00,0x36,0x2c,0x00,0x10,0x36,0x2d,0x00,
-0x20,0x36,0x28,0x00,0x30,0x36,0x29,0x00,0x50,0x36,0x27,0x00,0xa0,0x36,0x28,0x00,
-0xa4,0x36,0x20,0x00,0xb0,0x36,0x24,0x00,0xc0,0x36,0x22,0x00,0x68,0x60,0xd6,0x62,
-0x68,0x60,0xd8,0x63,0x00,0x64,0xa2,0xdb,0xa3,0xdb,0x6a,0x60,0x42,0x78,0xff,0xff,
-0x72,0x60,0xb8,0x78,0xff,0xff,0x71,0x60,0x5a,0x78,0xff,0xff,0x71,0x60,0x98,0x78,
-0xff,0xff,0x71,0x60,0x8b,0x78,0xff,0xff,0x71,0x60,0xa5,0x78,0xff,0xff,0x71,0x60,
-0xfd,0x78,0xff,0xff,0x71,0x60,0x14,0x78,0xff,0xff,0x71,0x60,0x37,0x78,0xff,0xff,
-0x72,0x60,0x1b,0x78,0xff,0xff,0x72,0x60,0x52,0x78,0xff,0xff,0x68,0x60,0xd6,0x62,
-0x68,0x60,0xd8,0x63,0x00,0x64,0xa2,0xdb,0xa3,0xdb,0xff,0xff,0x2b,0xf2,0x81,0xf1,
-0xff,0xff,0xd0,0x80,0x2c,0xf2,0x82,0xf1,0x07,0x02,0xd0,0x80,0x2d,0xf2,0x83,0xf1,
-0x03,0x02,0xd0,0x80,0xff,0xff,0x06,0x03,0x56,0x65,0x73,0x60,0x58,0x4f,0xc2,0x78,
-0xff,0xff,0x04,0x02,0x68,0x60,0xd6,0x62,0x01,0x64,0xa2,0xdb,0x70,0x60,0xf3,0x78,
-0xff,0xff,0x68,0x60,0xd6,0x62,0x68,0x60,0xd8,0x63,0x00,0x64,0xa2,0xdb,0xa3,0xdb,
-0xff,0xff,0x2e,0xf2,0x81,0xf1,0xff,0xff,0xd0,0x80,0x2f,0xf2,0x82,0xf1,0x07,0x02,
-0xd0,0x80,0x30,0xf2,0x83,0xf1,0x03,0x02,0xd0,0x80,0xff,0xff,0x06,0x03,0x5c,0x65,
-0x73,0x60,0x58,0x4f,0xc2,0x78,0xff,0xff,0x04,0x02,0x68,0x60,0xd6,0x62,0x01,0x64,
-0xa2,0xdb,0x70,0x60,0xf3,0x78,0xff,0xff,0x68,0x60,0xd6,0x62,0x68,0x60,0xd8,0x63,
-0x00,0x64,0xa2,0xdb,0xa3,0xdb,0xff,0xff,0x2b,0xf2,0x81,0xf1,0xff,0xff,0xd0,0x80,
-0x2c,0xf2,0x82,0xf1,0x15,0x02,0xd0,0x80,0x2d,0xf2,0x83,0xf1,0x11,0x02,0xd0,0x80,
-0xff,0xff,0x14,0x03,0x2e,0xf2,0x81,0xf1,0xff,0xff,0xd0,0x80,0x2f,0xf2,0x82,0xf1,
-0x07,0x02,0xd0,0x80,0x30,0xf2,0x83,0xf1,0x03,0x02,0xd0,0x80,0xff,0xff,0x06,0x03,
-0x56,0x65,0x73,0x60,0x58,0x4f,0xc2,0x78,0xff,0xff,0x04,0x02,0x68,0x60,0xd8,0x62,
-0x01,0x64,0xa2,0xdb,0x70,0x60,0xf3,0x78,0xff,0xff,0x68,0x60,0xd8,0x65,0x68,0x60,
-0xd6,0x63,0x00,0x64,0x01,0x61,0xa3,0xd1,0xa5,0xdb,0xd1,0x80,0xa3,0xdb,0x70,0x60,
-0xf3,0x78,0xff,0xff,0x68,0x60,0xd8,0x65,0x68,0x60,0xd6,0x63,0x00,0x64,0x01,0x61,
-0xa5,0xd1,0xa3,0xdb,0xd1,0x80,0xa5,0xdb,0x70,0x60,0xf3,0x78,0xff,0xff,0x68,0x60,
-0xd6,0x62,0x68,0x60,0xd8,0x63,0x00,0x64,0xa2,0xdb,0xa3,0xdb,0x46,0x4a,0x2b,0xf2,
-0x81,0xf1,0xff,0xff,0xd0,0x80,0x2c,0xf2,0x82,0xf1,0x0c,0x02,0xd0,0x80,0x2d,0xf2,
-0x83,0xf1,0x08,0x02,0xd0,0x80,0xff,0xff,0x05,0x02,0x68,0x60,0xd6,0x62,0x01,0x64,
-0xa2,0xdb,0x0c,0x00,0x2b,0xf0,0xff,0x60,0xff,0x64,0xd0,0x80,0x2c,0xf0,0x33,0x02,
-0xd0,0x80,0x2d,0xf0,0x30,0x02,0xd0,0x80,0xff,0xff,0x2d,0x02,0x38,0xf2,0xff,0xff,
-0xfe,0xa0,0xff,0xff,0x28,0x04,0x00,0xf4,0x02,0xf0,0x16,0x60,0x44,0x62,0xa2,0xd1,
-0x64,0x47,0xd0,0x80,0xff,0xff,0x1f,0x02,0x60,0x41,0xe9,0x81,0x06,0x63,0x0c,0x03,
-0x16,0x60,0x46,0x64,0x60,0x45,0xbd,0xd0,0xa5,0xd3,0xff,0xff,0xd0,0x80,0x65,0x44,
-0x12,0x02,0xcd,0x81,0x02,0xa4,0xf6,0x02,0x02,0xf0,0xff,0xff,0x64,0x40,0x01,0x27,
-0x02,0x00,0x48,0xfe,0x08,0x00,0xa3,0xd0,0xa0,0xd1,0x64,0x44,0x00,0x7f,0x60,0x45,
-0x64,0x44,0x00,0x7f,0xd4,0x80,0x2a,0x46,0x70,0x60,0xf3,0x78,0xff,0xff,0x68,0x60,
-0xd6,0x62,0x68,0x60,0xd8,0x63,0x00,0x64,0xa2,0xdb,0xa3,0xdb,0xff,0xff,0x2e,0xf2,
-0x81,0xf1,0xff,0xff,0xd0,0x80,0x2f,0xf2,0x82,0xf1,0x07,0x02,0xd0,0x80,0x30,0xf2,
-0x83,0xf1,0x03,0x02,0xd0,0x80,0xff,0xff,0x05,0x03,0x5c,0x65,0x73,0x60,0x58,0x4f,
-0xc2,0x78,0xff,0xff,0x70,0x60,0xf3,0x78,0xff,0xff,0x68,0x60,0xd6,0x62,0x68,0x60,
-0xd8,0x63,0x00,0x64,0xa2,0xdb,0xa3,0xdb,0xff,0xff,0x2b,0xf2,0x81,0xf1,0xff,0xff,
-0xd0,0x80,0x2c,0xf2,0x82,0xf1,0x07,0x02,0xd0,0x80,0x2d,0xf2,0x83,0xf1,0x03,0x02,
-0xd0,0x80,0xff,0xff,0x1a,0x03,0x56,0x65,0x73,0x60,0x58,0x4f,0xc2,0x78,0xff,0xff,
-0x14,0x03,0x2e,0xf2,0x81,0xf1,0xff,0xff,0xd0,0x80,0x2f,0xf2,0x82,0xf1,0x07,0x02,
-0xd0,0x80,0x30,0xf2,0x83,0xf1,0x03,0x02,0xd0,0x80,0xff,0xff,0x06,0x03,0x5c,0x65,
-0x73,0x60,0x58,0x4f,0xc2,0x78,0xff,0xff,0x04,0x00,0x68,0x60,0xd6,0x62,0x01,0x64,
-0xa2,0xdb,0x70,0x60,0xf3,0x78,0xff,0xff,0x68,0x60,0xd6,0x62,0x68,0x60,0xd8,0x63,
-0x00,0x64,0xa2,0xdb,0xa3,0xdb,0x29,0xf2,0xff,0xff,0xff,0xff,0x01,0x2b,0x3a,0x00,
-0x2b,0xf2,0x81,0xf1,0xff,0xff,0xd0,0x80,0x2c,0xf2,0x82,0xf1,0x07,0x02,0xd0,0x80,
-0x2d,0xf2,0x83,0xf1,0x03,0x02,0xd0,0x80,0xff,0xff,0x40,0x03,0x68,0x60,0xdc,0x62,
-0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,0x42,0x02,0x68,0x60,0xde,0x62,0xa2,0xd3,
-0xff,0xff,0x00,0xa0,0x40,0x47,0x1e,0x03,0x56,0x65,0x68,0x60,0xe0,0x61,0x65,0x43,
-0x50,0xfe,0xbd,0xd2,0xa1,0xd1,0x02,0xa1,0xd0,0x80,0xbd,0xd2,0xa1,0xd1,0x02,0xa1,
-0xd0,0x80,0xa3,0xd2,0xa1,0xd1,0x02,0xa1,0xd0,0x80,0xff,0xff,0x1f,0x01,0x27,0x44,
-0xcc,0x84,0x40,0x47,0xec,0x02,0x29,0xf2,0xff,0xff,0xff,0xff,0x01,0x27,0x02,0x00,
-0x08,0xfe,0x1d,0x00,0x2e,0xf2,0x81,0xf1,0xff,0xff,0xd0,0x80,0x2f,0xf2,0x82,0xf1,
-0x07,0x02,0xd0,0x80,0x30,0xf2,0x83,0xf1,0x03,0x02,0xd0,0x80,0xff,0xff,0x06,0x03,
-0x5c,0x65,0x73,0x60,0x58,0x4f,0xc2,0x78,0xff,0xff,0x09,0x02,0x2b,0xf2,0xff,0xff,
-0xff,0xff,0x01,0x26,0x04,0x00,0x68,0x60,0xd6,0x62,0x01,0x64,0xa2,0xdb,0x70,0x60,
-0xf3,0x78,0xff,0xff,0x68,0x60,0xd6,0x62,0x68,0x60,0xd8,0x63,0x00,0x64,0xa2,0xdb,
-0xa3,0xdb,0x38,0xf2,0x46,0x4a,0x60,0x41,0x68,0x60,0xd4,0x62,0xa2,0xd3,0xff,0xff,
-0xff,0xff,0x02,0x36,0x07,0x00,0x68,0x60,0xd2,0x62,0xa2,0xd3,0xff,0xff,0xff,0xff,
-0x04,0x36,0x07,0x00,0x61,0x44,0xf2,0xa0,0xff,0xff,0x06,0x05,0x73,0x60,0xbe,0x78,
-0xff,0xff,0x73,0x60,0x15,0x78,0xff,0xff,0x2e,0xf2,0x81,0xf1,0xff,0xff,0xd0,0x80,
-0x2f,0xf2,0x82,0xf1,0x07,0x02,0xd0,0x80,0x30,0xf2,0x83,0xf1,0x03,0x02,0xd0,0x80,
-0xff,0xff,0xef,0x03,0x68,0x60,0xd4,0x62,0xa2,0xd3,0xff,0xff,0xfe,0xa0,0xff,0xff,
-0xe5,0x02,0x68,0x60,0xdc,0x62,0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,0xde,0x02,
-0x68,0x60,0xde,0x62,0xa2,0xd3,0xff,0xff,0x00,0xa0,0x40,0x47,0x22,0x03,0x5c,0x65,
-0x68,0x60,0xe0,0x61,0x65,0x43,0x50,0xfe,0xbd,0xd2,0xa1,0xd1,0x02,0xa1,0xd0,0x80,
-0xbd,0xd2,0xa1,0xd1,0x02,0xa1,0xd0,0x80,0xa3,0xd2,0xa1,0xd1,0x02,0xa1,0xd0,0x80,
-0xff,0xff,0xc4,0x01,0x27,0x44,0xcc,0x84,0x40,0x47,0xec,0x02,0x0a,0x00,0x68,0x60,
-0xd4,0x62,0xa2,0xd3,0xff,0xff,0xff,0xff,0x02,0x3a,0x03,0x00,0x73,0x60,0xbe,0x78,
-0xff,0xff,0x38,0xf2,0x00,0xf4,0x08,0xf0,0xf2,0xa0,0xff,0xff,0x03,0x05,0x73,0x60,
-0xbe,0x78,0xff,0xff,0x68,0x60,0xd2,0x62,0xa2,0xd3,0xff,0xff,0xff,0xff,0x04,0x36,
-0x1b,0x00,0x16,0x60,0x44,0x62,0x64,0x47,0x00,0xa0,0xff,0xff,0x3c,0x06,0xe0,0xa0,
-0xff,0xff,0x39,0x07,0xa2,0xdb,0x12,0x63,0x60,0x41,0x16,0x60,0x46,0x64,0xbd,0xd0,
-0xa0,0xd9,0xcd,0x81,0x02,0xa4,0xfb,0x02,0x08,0xf0,0xff,0xff,0x64,0x40,0x01,0x2b,
-0x2a,0x00,0xa3,0xd0,0xa0,0xd9,0x27,0x00,0x16,0x60,0x44,0x62,0x64,0x47,0xa2,0xd1,
-0xff,0xff,0xd0,0x80,0xff,0xff,0x6c,0x02,0x60,0x41,0xe9,0x81,0x12,0x63,0x0c,0x03,
-0x16,0x60,0x46,0x64,0x60,0x45,0xbd,0xd0,0xa5,0xd3,0xff,0xff,0xd0,0x80,0x65,0x44,
-0x5f,0x02,0xcd,0x81,0x02,0xa4,0xf6,0x02,0x08,0xf0,0xff,0xff,0x64,0x40,0x01,0x2b,
-0x0a,0x00,0xa3,0xd0,0xa0,0xd1,0x64,0x44,0x00,0x7f,0x60,0x45,0x64,0x44,0x00,0x7f,
-0xd4,0x80,0xff,0xff,0x4d,0x02,0x08,0xf2,0x12,0x65,0x00,0x7e,0x60,0x47,0xc4,0x81,
-0x61,0x45,0x01,0x26,0x04,0x00,0xa1,0xd2,0xf4,0xe6,0x7e,0x00,0xda,0x01,0xff,0xff,
-0x60,0x47,0x02,0x00,0x01,0xa1,0xa1,0xd2,0xff,0xff,0x00,0x7f,0x02,0xa4,0xc4,0x81,
-0x02,0xa1,0x01,0x26,0x02,0x00,0xa1,0xd2,0x04,0x00,0xff,0xa1,0xa1,0xd2,0xff,0xff,
-0x60,0x47,0x7f,0xf1,0x00,0x7f,0xd0,0x80,0xff,0xff,0x2d,0x02,0x68,0x60,0xd4,0x62,
-0xa2,0xd3,0xff,0xff,0xfe,0xa0,0xff,0xff,0x19,0x02,0x68,0x60,0xde,0x62,0xa2,0xd3,
-0xff,0xff,0xf6,0xa0,0x60,0x41,0x12,0x05,0x01,0xa4,0xa2,0xdb,0xe1,0x81,0xe1,0x85,
-0xc5,0x85,0x68,0x60,0xe0,0x64,0xc4,0x81,0x2a,0x46,0x5c,0x63,0xbd,0xd2,0xa1,0xdb,
-0xbd,0xd2,0x02,0xa1,0xa1,0xdb,0xa3,0xd2,0x02,0xa1,0xa1,0xdb,0x68,0x60,0xd4,0x62,
-0x02,0x64,0xa2,0xdb,0x2a,0x46,0xff,0xff,0x2e,0xf0,0x81,0xf9,0x2f,0xf0,0xff,0xff,
-0x82,0xf9,0x30,0xf0,0x83,0xf9,0x2a,0x46,0x70,0x60,0xf3,0x78,0xff,0xff,0x68,0x60,
-0xdc,0x62,0xa2,0xd3,0xff,0xff,0xff,0xa0,0xff,0xff,0x1d,0x02,0x68,0x60,0xde,0x62,
-0xa2,0xd3,0xff,0xff,0xff,0xa0,0x40,0x47,0x16,0x04,0x68,0x60,0xe0,0x61,0x65,0x43,
-0x50,0xfe,0xbd,0xd2,0xa1,0xd1,0x02,0xa1,0xd0,0x80,0xbd,0xd2,0xa1,0xd1,0x02,0xa1,
-0xd0,0x80,0xa3,0xd2,0xa1,0xd1,0x02,0xa1,0xd0,0x80,0xff,0xff,0x06,0x01,0x27,0x44,
-0xcc,0x84,0x40,0x47,0xec,0x02,0x08,0xfe,0x01,0x00,0x48,0xfe,0x2f,0x58,0xff,0xff,
-0x99,0xff,0x08,0x60,0x2a,0x62,0x05,0x60,0xff,0x64,0xa2,0xdb,0x05,0x60,0xff,0xe5,
-0xff,0xff,0xff,0xff,0x98,0xff,0xe0,0x60,0x00,0x63,0xfe,0x60,0x00,0x66,0x0c,0x60,
-0x7b,0x64,0xa3,0xd0,0xcc,0x84,0xbd,0xd8,0xfc,0x02,0x99,0xff,0x08,0x60,0x2a,0x62,
-0x04,0x60,0xff,0x64,0xa2,0xdb,0x04,0x60,0xff,0xe5,0xff,0xff,0xff,0xff,0x98,0xff,
-0x0c,0x60,0x87,0x78,0xff,0xff,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
-0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x02,0x00,0x01,0x00,
-0x01,0x00,0x01,0x00,0x21,0x00,0x02,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x04,0x00,
-0x01,0x00,0x01,0x00,0x01,0x00,0x02,0x00,0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,
-0x53,0x65,0x63,0x6f,0x6e,0x64,0x61,0x72,0x79,0x20,0x46,0x27,0x73,0x00,0x0f,0x01,
-0x00,0x18,0x7e,0x00,0xce,0xd0,0x0c,0x01,0x38,0x81,0x7f,0x00,0x02,0x00,0x0e,0x01,
-0xfa,0x8d,0x7f,0x00,0x02,0x00,0x10,0x01,0x00,0x80,0x7f,0x00,0xda,0x14,0x0a,0x01,
-0xc2,0x8d,0x7f,0x00,0x02,0x00,0x0b,0x01,0x0c,0x8e,0x7f,0x00,0x24,0x00,0x08,0x01,
-0xe8,0x8d,0x7f,0x00,0x12,0x00,0x09,0x01,0xe6,0x8d,0x7f,0x00,0x02,0x00,0x04,0x01,
-0x0a,0x81,0x7f,0x00,0x02,0x00,0x05,0x01,0x46,0x8d,0x7f,0x00,0x02,0x00,0x05,0x01,
-0x40,0x81,0x7f,0x00,0x02,0x00,0x02,0x00,0x00,0x80,0x7f,0x00,0xfa,0x0f,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x70,0x09,
-0x34,0x09,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x1b,0x00,0x1b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x27,
-0xe0,0x27,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x07,0x01,0x00,0x64,0x00,0x64,0x00,
-0xe8,0x03,0x10,0x27,0x14,0x00,0x88,0x13,0x88,0x13,0x2f,0x00,0x14,0x00,0x04,0x00,
-0x0f,0x00,0x02,0x00,0x02,0x00,0x14,0x00,0x0a,0x00,0x0f,0x00,0x0f,0x00,0x05,0x00,
-0x0a,0x00,0x64,0x00,0x88,0x13,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,
-0x05,0x00,0x08,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x09,
-0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x10,0x60,0xa3,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x41,0xff,0x10,0x60,0xa4,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xc4,0xe2,0x10,0x60,0xab,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x10,0x60,0x85,0x78,0x43,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x44,0xff,0x08,0xe1,0x10,0x60,
-0xc6,0x78,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x44,0xe2,0x10,0x60,0xc8,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x46,0xff,0x10,0x60,0xc9,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x10,0x60,0xca,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x12,0x60,0xd8,0x78,0x4c,0x4e,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x12,0x60,0x9f,0x78,0x4c,0xe2,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x11,0x60,0xcf,0x78,0x43,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x11,0x60,0xab,0x78,0xa1,0xf3,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x12,0x60,0xbc,0x78,0x46,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x11,0x60,0x8e,0x78,0x47,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x42,0xff,0x19,0x60,0x56,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x43,0xff,0x19,0x60,0x56,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x44,0xff,0x19,0x60,0x56,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x45,0xff,0x1a,0x60,0xd4,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0xf7,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x83,0x7c,0x08,0x60,0x12,0x64,0x80,0x29,
-0xa0,0xd9,0x47,0xff,0x19,0x60,0x0c,0x78,0xff,0xff,0x40,0xff,0x24,0x58,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x41,0xff,0x21,0x58,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xc4,0xe2,0x22,0x58,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x20,0x60,0x08,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x22,0x60,0x76,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x45,0xff,0x21,0x58,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x1b,0x60,0x08,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x20,0x60,0x0b,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf8,0x60,0x8c,0x78,0x40,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xf6,0x60,0xa0,0x78,0x41,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xf8,0x60,0xf5,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0x25,0x60,0x2d,0x78,0x43,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xf8,0x60,0xf5,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0x24,0x60,0xb2,0x78,0x45,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0x25,0x60,0x75,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xf8,0x60,0xf5,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0x3d,0x60,0x25,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f,0x60,0x47,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x79,0x3d,0x60,0x2e,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x24,0xe2,0x3d,0x60,0x2e,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3d,0x60,0xa1,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x44,0xe2,0x3d,0x60,0x2e,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x84,0xe2,0x3d,0x60,0x2e,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x47,0xff,0x3d,0x60,0x2e,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x30,0x60,0x37,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x31,0x60,0x19,0x78,0xff,0xff,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x28,0xe2,0x30,0x60,0x7f,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x44,0xff,0x30,0x60,0x7f,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x45,0xff,0x30,0x60,0x7f,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x46,0xff,0x30,0x60,0x7f,0x78,
-0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x87,0x7c,0x08,0x60,0x12,0x64,0x80,0x29,
-0xa0,0xd9,0x47,0xff,0x30,0x60,0x7f,0x78,0x93,0x6f,0xa6,0x6f,0xcc,0x3f,0xb3,0x50,
-0xe4,0x67,0xbf,0x64,0x4e,0x2d,0x53,0x56,0x38,0x68,0xd0,0x58,0xa4,0x6f,0xa4,0x6f,
-0xa4,0x6f,0xa4,0x6f,0xa4,0x6f,0xa4,0x6f,0xa4,0x6f,0xa4,0x6f,0xa4,0x6f,0xa4,0x6f,
-0xa4,0x6f,0xa4,0x6f,0x8e,0x11,0x00,0x0a,0x10,0x01,0x68,0xa4,0xb0,0x01,0x84,0x01,
-0x30,0x33,0x31,0x33,0x44,0x44,0x30,0x33,0x31,0x33,0x30,0x33,0x31,0x33,0x32,0x33,
-0x32,0x33,0x90,0x00,0x78,0x04,0xae,0xe4,0xcc,0x3d,0xb5,0x3d,0xb0,0x33,0x46,0x34,
-0x03,0xfc,0xe7,0x34,0xe1,0x36,0xc6,0x17,0x02,0x00,0x04,0xfc,0xe7,0x34,0x13,0x35,
-0xe4,0x16,0x22,0x00,0x07,0xfc,0xe7,0x34,0x13,0x35,0x92,0x00,0x02,0x00,0x0e,0xfc,
-0xe7,0x34,0xfd,0x36,0x10,0x17,0x22,0x00,0x00,0xfc,0xe7,0x34,0x13,0x35,0xc0,0x16,
-0x02,0x00,0x01,0xfc,0xe7,0x34,0x04,0x35,0xb6,0x16,0x06,0x00,0x02,0xfc,0xe7,0x34,
-0x04,0x35,0xc2,0x16,0x22,0x00,0x05,0xfc,0xe7,0x34,0x13,0x35,0xbe,0x16,0x02,0x00,
-0x09,0xfc,0xe7,0x34,0x7e,0x37,0x06,0x17,0x02,0x00,0x0a,0xfc,0xe7,0x34,0x13,0x35,
-0x08,0x17,0x02,0x00,0x0b,0xfc,0xe7,0x34,0x13,0x35,0x0a,0x17,0x02,0x00,0x0c,0xfc,
-0xe7,0x34,0x13,0x35,0x0c,0x17,0x02,0x00,0x0f,0xfc,0xe7,0x34,0x13,0x35,0x5e,0x17,
-0x02,0x00,0x19,0xfd,0xe7,0x34,0x13,0x35,0x70,0x17,0x02,0x00,0xb0,0xfc,0xe7,0x34,
-0x13,0x35,0xbe,0x01,0x02,0x00,0x1a,0xfd,0xe7,0x34,0x13,0x35,0x3a,0x17,0x02,0x00,
-0x8e,0xfc,0xe7,0x34,0x13,0x35,0x44,0x17,0x02,0x00,0xa8,0xfc,0xe7,0x34,0x13,0x35,
-0x46,0x17,0x02,0x00,0xb3,0xfc,0xe7,0x34,0x13,0x35,0xdc,0x17,0x02,0x00,0xb4,0xfc,
-0xe7,0x34,0x30,0x37,0x92,0x01,0x02,0x00,0xa9,0xfc,0xe7,0x34,0xcf,0x34,0xd4,0x66,
-0x02,0x00,0xad,0xfc,0xe7,0x34,0x13,0x35,0x6a,0x00,0x02,0x00,0xaa,0xfc,0xe7,0x34,
-0x13,0x35,0xc2,0x01,0x02,0x00,0xab,0xfc,0xe7,0x34,0x13,0x35,0x76,0x67,0x02,0x00,
-0xac,0xfc,0xe7,0x34,0x13,0x35,0xf6,0x18,0x02,0x00,0xaf,0xfc,0xcf,0x34,0x39,0x39,
-0x00,0x00,0x02,0x00,0x0d,0xfc,0xe7,0x34,0x13,0x35,0x0e,0x17,0x02,0x00,0x19,0xfc,
-0xe7,0x34,0x13,0x35,0xa6,0x01,0x02,0x00,0x20,0xfc,0xe7,0x34,0x13,0x35,0xb4,0x61,
-0x06,0x00,0x21,0xfc,0xe7,0x34,0x00,0x35,0xbc,0x61,0x06,0x00,0x24,0xfc,0x13,0x35,
-0xaf,0x37,0x00,0x00,0x0e,0x00,0x25,0xfc,0x13,0x35,0xb3,0x37,0x00,0x00,0x0e,0x00,
-0x26,0xfc,0x13,0x35,0xb7,0x37,0x00,0x00,0x0e,0x00,0x27,0xfc,0x13,0x35,0xbb,0x37,
-0x00,0x00,0x0e,0x00,0x23,0xfc,0xe7,0x34,0x13,0x35,0x84,0x00,0x02,0x00,0x28,0xfc,
-0xd7,0x37,0xe3,0x37,0x00,0x00,0x06,0x00,0x2a,0xfc,0xe7,0x34,0x13,0x35,0xfa,0x00,
-0x02,0x00,0x2b,0xfc,0xe7,0x34,0xe3,0x34,0x78,0x01,0x02,0x00,0x2c,0xfc,0xe7,0x34,
-0x11,0x38,0xd8,0x66,0x02,0x00,0x2d,0xfc,0xe7,0x34,0x02,0x38,0xaa,0x01,0x02,0x00,
-0x2e,0xfc,0xe7,0x34,0x13,0x35,0xa8,0x66,0x02,0x00,0x3a,0xfc,0xe7,0x34,0x13,0x35,
-0x06,0x67,0x04,0x00,0x80,0xfc,0x29,0x35,0x3c,0x35,0x5e,0x58,0xc0,0x00,0x81,0xfc,
-0xe7,0x34,0x13,0x35,0x8c,0x01,0x02,0x00,0x82,0xfc,0xe7,0x34,0x13,0x35,0x8e,0x01,
-0x02,0x00,0x83,0xfc,0xe7,0x34,0x13,0x35,0x90,0x01,0x02,0x00,0x84,0xfc,0xe7,0x34,
-0x30,0x37,0x92,0x01,0x02,0x00,0x85,0xfc,0xe7,0x34,0x1c,0x37,0x1e,0x59,0x02,0x00,
-0x87,0xfc,0xe7,0x34,0x13,0x35,0x7c,0x17,0x02,0x00,0x88,0xfc,0xe7,0x34,0x13,0x35,
-0x7a,0x17,0x02,0x00,0x89,0xfc,0xe7,0x34,0x13,0x35,0x34,0x17,0x02,0x00,0x8a,0xfc,
-0xe7,0x34,0x13,0x35,0xdc,0x17,0x02,0x00,0x8b,0xfc,0xe7,0x34,0x13,0x35,0x00,0x01,
-0x02,0x00,0x8c,0xfc,0xe7,0x34,0x2d,0x38,0x7e,0x17,0x02,0x00,0xa5,0xfc,0xe7,0x34,
-0x8a,0x37,0x74,0x17,0x02,0x00,0xa6,0xfc,0xcf,0x34,0x72,0x37,0x00,0x00,0x02,0x00,
-0x18,0xfc,0xe7,0x34,0x13,0x35,0xbc,0x16,0x02,0x00,0xae,0xfc,0xe7,0x34,0x13,0x35,
-0xbc,0x01,0x02,0x00,0x2f,0xfc,0xe7,0x34,0x13,0x35,0xac,0x01,0x02,0x00,0x30,0xfc,
-0xcf,0x34,0x4b,0x38,0x00,0x00,0x14,0x00,0x31,0xfc,0xcf,0x34,0xae,0x38,0x00,0x00,
-0x06,0x00,0x10,0xfd,0xe7,0x34,0xe3,0x34,0x4a,0x01,0x02,0x00,0x11,0xfd,0x81,0x36,
-0xe3,0x34,0x4c,0xe8,0x0c,0x00,0x12,0xfd,0x81,0x36,0xe3,0x34,0x5a,0xe8,0x02,0x00,
-0x13,0xfd,0x41,0x36,0xe3,0x34,0x00,0x04,0xe0,0x01,0x14,0xfd,0xe7,0x34,0xe3,0x34,
-0x38,0x17,0x02,0x00,0x15,0xfd,0xe7,0x34,0xe3,0x34,0x82,0x17,0x24,0x00,0x16,0xfd,
-0xe7,0x34,0xe3,0x34,0x60,0x17,0x10,0x00,0x17,0xfd,0xe7,0x34,0xe3,0x34,0x5c,0x17,
-0x02,0x00,0x18,0xfd,0xe7,0x34,0xe3,0x34,0x76,0x17,0x04,0x00,0x1b,0xfd,0xe7,0x34,
-0xe3,0x34,0x72,0x17,0x02,0x00,0x1c,0xfd,0xe7,0x34,0xe3,0x34,0x3e,0x00,0x02,0x00,
-0x24,0xfd,0xe7,0x34,0xe3,0x34,0xaa,0x17,0x0c,0x00,0x25,0xfd,0xe7,0x34,0xe3,0x34,
-0xb8,0x17,0x0c,0x00,0x26,0xfd,0x81,0x36,0xe3,0x34,0x74,0xe8,0x20,0x00,0x27,0xfd,
-0x81,0x36,0xe3,0x34,0x94,0xe8,0x38,0x00,0x45,0xfd,0xe7,0x34,0xe3,0x34,0x00,0x01,
-0x02,0x00,0x47,0xfd,0xe7,0x34,0xe3,0x34,0x4e,0x01,0x02,0x00,0x48,0xfd,0xa2,0x36,
-0xe3,0x34,0x6c,0x01,0x02,0x00,0x49,0xfd,0xa2,0x36,0xe3,0x34,0x6e,0x01,0x02,0x00,
-0x4a,0xfd,0xe7,0x34,0xe3,0x34,0xe0,0x17,0x02,0x00,0x4b,0xfd,0xe7,0x34,0xe3,0x34,
-0xe2,0x17,0x02,0x00,0x4d,0xfd,0x81,0x36,0xe3,0x34,0x5c,0xe8,0x04,0x00,0x50,0xfd,
-0xe7,0x34,0xe3,0x34,0xb2,0x59,0x12,0x00,0x4f,0xfd,0x81,0x36,0xe3,0x34,0x58,0xe8,
-0x02,0x00,0xc0,0xfd,0x81,0x36,0xe3,0x34,0x60,0xe8,0x02,0x00,0xc1,0xfd,0xe7,0x34,
-0xe3,0x34,0xfe,0x00,0x02,0x00,0xc2,0xfd,0xb0,0x36,0xe3,0x34,0x00,0x00,0x02,0x00,
-0xc3,0xfd,0x81,0x36,0xe3,0x34,0x62,0xe8,0x02,0x00,0xc6,0xfd,0xe7,0x34,0xe3,0x34,
-0xd2,0x17,0x0a,0x00,0x20,0xfd,0x68,0x36,0xe3,0x34,0x3a,0xe8,0x08,0x00,0x21,0xfd,
-0x68,0x36,0xe3,0x34,0x42,0xe8,0x0a,0x00,0x22,0xfd,0x68,0x36,0xe3,0x34,0x1c,0xe8,
-0x0a,0x00,0x23,0xfd,0x68,0x36,0xe3,0x34,0x30,0xe8,0x0a,0x00,0x40,0xfd,0xe7,0x34,
-0xe3,0x34,0x96,0x01,0x02,0x00,0x41,0xfd,0xe7,0x34,0xe3,0x34,0x20,0x59,0x22,0x00,
-0x42,0xfd,0xe7,0x34,0x0e,0x35,0x02,0x01,0x06,0x00,0x43,0xfd,0xcb,0x36,0xe3,0x34,
-0x00,0x00,0x06,0x00,0x44,0xfd,0xc0,0x36,0xe3,0x34,0xa0,0x00,0x02,0x00,0x46,0xfd,
-0xe7,0x34,0xe3,0x34,0x84,0x01,0x0c,0x00,0x4c,0xfd,0xe7,0x34,0xe3,0x34,0xca,0x18,
-0x02,0x00,0x8d,0xfc,0xe7,0x34,0xe3,0x34,0x8e,0x00,0x02,0x00,0x8f,0xfc,0xe7,0x34,
-0xe3,0x34,0x80,0x17,0x02,0x00,0xa7,0xfc,0xe7,0x34,0xe3,0x34,0xc6,0x66,0x04,0x00,
-0xfe,0xff,0x81,0x36,0xe3,0x34,0x64,0xe8,0x02,0x00,0xff,0xff,0x81,0x36,0xe3,0x34,
-0x66,0xe8,0x0e,0x00,0x00,0xf1,0x2a,0x00,0xc3,0x35,0x01,0xf1,0x80,0x08,0x92,0x35,
-0x02,0xf1,0x80,0x08,0xca,0x35,0x03,0xf1,0x96,0x00,0x03,0x39,0x04,0xf1,0x90,0x1a,
-0x05,0x36,0xd8,0x1a,0x91,0x19,0x96,0x19,0x5e,0x1a,0xb3,0x1a,0x0e,0x1a,0x6d,0x19,
-0x16,0x1a,0x1e,0x1a,0xdb,0x19,0x59,0x19,0xca,0x1a,0xca,0x1a,0x30,0x24,0x5c,0x24,
-0x59,0x24,0x7a,0x23,0x5c,0x24,0x5c,0x24,0x00,0x00,0x62,0x24,0x62,0x24,0x62,0x24,
-0x00,0x00,0x00,0x00,0xec,0x24,0x05,0x25,0x7b,0x23,0x00,0x00,0x77,0x24,0x00,0x00,
-0x00,0x00,0x9b,0x16,0x00,0x02,0x48,0x04,0x48,0x06,0x80,0x08,0x03,0x0a,0x04,0x0c,
-0x04,0x0e,0x00,0x10,0xe4,0x12,0xbb,0x14,0x1b,0x16,0x00,0x18,0x00,0x1a,0x00,0x1c,
-0x5c,0x1e,0xc1,0x20,0x20,0x22,0x74,0x24,0x07,0x26,0x0a,0x28,0x16,0x2a,0x00,0x2c,
-0x00,0x2e,0x1c,0x30,0x20,0x32,0x98,0x34,0x08,0x36,0x7a,0x38,0x0a,0x3a,0x24,0x3c,
-0xb2,0x3e,0x00,0x40,0x00,0x42,0x00,0x44,0x0c,0x46,0x26,0x48,0x5b,0x4a,0x7f,0x4c,
-0x29,0x4e,0x0f,0x50,0x20,0x52,0x20,0x54,0x10,0x56,0x10,0x58,0x10,0x5a,0x10,0x5c,
-0x1e,0x5e,0x1a,0x60,0x18,0x62,0x00,0x2c,0x0c,0x2e,0x01,0x2c,0x10,0x2e,0x02,0x2c,
-0x14,0x2e,0x03,0x2c,0x18,0x2e,0x04,0x2c,0x1c,0x2e,0x05,0x2c,0x20,0x2e,0x06,0x2c,
-0x24,0x2e,0x07,0x2c,0x28,0x2e,0x08,0x2c,0x2e,0x2e,0x09,0x2c,0x34,0x2e,0x0a,0x2c,
-0x38,0x2e,0x0b,0x2c,0x3c,0x2e,0x0c,0x2c,0x3f,0x2e,0x0d,0x2c,0x43,0x2e,0x0e,0x2c,
-0x46,0x2e,0x0f,0x2c,0x48,0x2e,0x10,0x2c,0x4b,0x2e,0x11,0x2c,0x50,0x2e,0x12,0x2c,
-0x55,0x2e,0x13,0x2c,0x5a,0x2e,0x14,0x2c,0x63,0x2e,0x15,0x2c,0x6d,0x2e,0x16,0x2c,
-0x76,0x2e,0x17,0x2c,0x7f,0x2e,0x18,0x2c,0x7f,0x2e,0x19,0x2c,0x7f,0x2e,0x1a,0x2c,
-0x7f,0x2e,0x1b,0x2c,0x7f,0x2e,0x1c,0x2c,0x7f,0x2e,0x1d,0x2c,0x7f,0x2e,0x1e,0x2c,
-0x7f,0x2e,0x1f,0x2c,0x7f,0x2e,0x00,0x02,0x01,0x04,0x38,0x06,0x80,0x08,0x03,0x0a,
-0x04,0x0c,0x04,0x0e,0x00,0x10,0xa2,0x12,0xc8,0x14,0x1b,0x16,0x00,0x18,0x00,0x1a,
-0x00,0x1c,0x5c,0x1e,0xc1,0x20,0x1e,0x22,0x54,0x24,0x07,0x26,0x6a,0x28,0x12,0x2a,
-0x00,0x2c,0x00,0x2e,0x1c,0x30,0x20,0x32,0x82,0x34,0x08,0x36,0x7a,0x38,0xca,0x3a,
-0x24,0x3c,0xb6,0x3e,0x00,0x40,0x00,0x42,0x00,0x44,0x7f,0x46,0x8b,0x48,0x0f,0x4a,
-0x06,0x4c,0x0a,0x4e,0x0f,0x50,0x20,0x52,0x20,0x54,0x10,0x56,0x10,0x58,0x20,0x5a,
-0xee,0x5c,0x1a,0x5e,0x26,0x60,0x5b,0x62,0x00,0x04,0x00,0x2c,0x0c,0x2e,0x01,0x2c,
-0x10,0x2e,0x02,0x2c,0x14,0x2e,0x03,0x2c,0x18,0x2e,0x04,0x2c,0x1c,0x2e,0x05,0x2c,
-0x20,0x2e,0x06,0x2c,0x24,0x2e,0x07,0x2c,0x28,0x2e,0x08,0x2c,0x2e,0x2e,0x09,0x2c,
-0x34,0x2e,0x0a,0x2c,0x38,0x2e,0x0b,0x2c,0x3c,0x2e,0x0c,0x2c,0x3f,0x2e,0x0d,0x2c,
-0x43,0x2e,0x0e,0x2c,0x46,0x2e,0x0f,0x2c,0x48,0x2e,0x10,0x2c,0x4b,0x2e,0x11,0x2c,
-0x50,0x2e,0x12,0x2c,0x55,0x2e,0x13,0x2c,0x5a,0x2e,0x14,0x2c,0x63,0x2e,0x15,0x2c,
-0x6d,0x2e,0x16,0x2c,0x76,0x2e,0x17,0x2c,0x7f,0x2e,0x18,0x2c,0x7f,0x2e,0x19,0x2c,
-0x7f,0x2e,0x1a,0x2c,0x7f,0x2e,0x1b,0x2c,0x7f,0x2e,0x1c,0x2c,0x7f,0x2e,0x1d,0x2c,
-0x7f,0x2e,0x1e,0x2c,0x7f,0x2e,0x1f,0x2c,0x7f,0x2e,0x00,0x00,0x04,0x00,0x65,0x00,
-0x00,0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x00,0x00,0xb0,0x00,0x00,0x00,0x5c,0x00,
-0x32,0x00,0x00,0x00,0x04,0x00,0x65,0x00,0x00,0x00,0x00,0x00,0xb0,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x5a,0x00,0x00,0x00,0x7e,0x00,0x6e,0x00,
-0x00,0x00,0x80,0x00,0x02,0x00,0x00,0x00,0x80,0x00,0x16,0x00,0x00,0x00,0x80,0x00,
-0x2a,0x00,0x00,0x00,0x80,0x00,0x3e,0x00,0x00,0x00,0x80,0x00,0x52,0x00,0x00,0x00,
-0x80,0x00,0x66,0x00,0x00,0x00,0x80,0x00,0x7a,0x00,0x00,0x00,0x82,0x00,0x0e,0x00,
-0x00,0x00,0x82,0x00,0x22,0x00,0x00,0x00,0x82,0x00,0x36,0x00,0x00,0x00,0x82,0x00,
-0x4a,0x00,0x00,0x00,0x82,0x00,0x7a,0x00,0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,
-0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,0x1b,0x3b,0x10,0x3b,0x10,0x3b,0x30,0x3b,
-0x10,0x3b,0x10,0x3b,0x63,0x3b,0x71,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,
-0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,
-0x10,0x3b,0x10,0x3b,0x10,0x3b,0x10,0x3b,0xd5,0x3b,0x24,0x3c,0x49,0x3c,0x42,0x3c,
-0x2e,0x3c,0x38,0x3c,0x10,0x3b,0x10,0x3b,0x10,0x3b,0x4b,0x3c,0x61,0x3c,0x68,0x3c,
-0x12,0x00,0x02,0x00,0x72,0x0e,0x04,0x00,0x8b,0x0e,0x06,0x00,0x39,0x0f,0x08,0x00,
-0x50,0x0f,0x0a,0x00,0x73,0x0f,0x0c,0x00,0x2e,0x10,0x12,0x00,0x3f,0x18,0x1a,0x00,
-0x75,0x25,0x00,0x09,0x16,0x0c,0x02,0x09,0x22,0x33,0x04,0x09,0x2f,0x34,0x06,0x09,
-0x72,0x34,0x14,0x09,0x34,0x31,0x16,0x09,0x50,0x31,0x42,0x09,0xb0,0x34,0x22,0x09,
-0x82,0x34,0x64,0x09,0x7a,0x34,0x70,0x09,0x02,0x3b,0x03,0x00,0x00,0x00,0x53,0x70,
-0x65,0x63,0x74,0x72,0x75,0x6d,0x32,0x34,0x2f,0x48,0x44,0x52,0x20,0x20,0x20,0x20,
-0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x14,0x00,
-0x6e,0x6f,0x6e,0x2d,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,0x64,0x20,0x53,0x53,
-0x49,0x44,0x20,0x21,0x21,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
-0x00,0x00,0x01,0x00,0x01,0x00,0x64,0x00,0x64,0x00,0x00,0x00,0x50,0x72,0x69,0x73,
-0x6d,0x20,0x20,0x49,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
-0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x01,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x53,0x70,0x65,0x63,
-0x74,0x72,0x75,0x6d,0x32,0x34,0x2f,0x48,0x44,0x52,0x20,0x20,0x20,0x20,0x20,0x20,
-0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x14,0x00,0x6e,0x6f,
-0x6e,0x2d,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,0x64,0x20,0x53,0x53,0x49,0x44,
-0x20,0x21,0x21,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,
-0x01,0x00,0x01,0x00,0x64,0x00,0x64,0x00,0x00,0x00,0x50,0x72,0x69,0x73,0x6d,0x20,
-0x20,0x49,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
-0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x80,0x01,0x2c,0x00,
-0x01,0x00,0xb6,0x00,0xc0,0x00,0xd4,0x00,0xee,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,
-0x12,0x00,0x0f,0x00,0x0c,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x01,0x00,
-0x03,0x00,0x03,0x00,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x2a,0x1b,
-0x20,0x1d,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x00,0x20,0x20,0x53,0x74,0x61,0x6e,
-0x64,0x61,0x72,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x46,0x33,0x2e,0x31,0x30,0x2d,0x30,0x34,0x00,0x00,0x00,0x00,0x00,0x00,
-0x30,0x31,0x2f,0x31,0x37,0x2f,0x32,0x30,0x30,0x32,0x00,0x00,0x00,0x00,0x01,0x00,
-0x04,0x00,0x82,0x84,0x8b,0x96,0x00,0x00,0x00,0x00,0x04,0x00,0x02,0x04,0x0b,0x16,
-0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-0x00,0x00,0x00,0x00,0x60,0x1f,0xe0,0x27,0x3f,0x3f,0x49,0x6e,0x74,0x27,0x6c,0x2d,
-0x73,0x65,0x61,0x72,0x63,0x68,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
-0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x00,
-0x04,0x00,0xfa,0x00,0xc8,0x00,0xf4,0x01,0x05,0x00,0x07,0x00,0xfa,0x00,0xc8,0x00,
-0x2c,0x01,0x05,0x00,0x0a,0x00,0xfa,0x00,0x32,0x00,0x64,0x00,0x05,0x00,0x0a,0x00,
-0xfa,0x00,0x32,0x00,0x32,0x00,0x05,0x00,0x0a,0x00,0xfa,0x00,0x19,0x00,0x19,0x00,
-0x05,0x00,0x35,0x44,0x5d,0x65,0xf7,0x98,0xd9,0x6e,0x42,0xf7,0xb8,0x74,0xdc,0x15,
-0xfc,0xf8,0x82,0x05,0x79,0xf3,0x09,0x59,0x97,0x30,0xee,0x53,0x7b,0x83,0x15,0x46,
-0xbd,0x2c,0xd1,0x91,0x43,0x08,0x1e,0x7f,0xc9,0x60,0x53,0x68,0xf4,0xd2,0x88,0x06,
-0x22,0x14,0x39,0x09,0xf5,0x9f,0x05,0x27,0x74,0xfe,0xec,0x75,0x1f,0x42,0xf2,0x0c,
-0xae,0xb0,0x1b,0xfd,0xb5,0x76,0x03,0x39,0xfa,0x8f,0x7f,0x00,0xe0,0x04,0x35,0x2a,
-0x7f,0x42,0x2f,0x45,0xd9,0xd4,0x49,0xe6,0xf8,0xd9,0x86,0xee,0x78,0x42,0xca,0x54,
-0x39,0x10,0x17,0x89,0x15,0xfc,0xcd,0x61,0x9c,0x76,0xdc,0xbe,0x02,0x0a,0x46,0xb6,
-0xea,0xad,0x47,0xaa,0x89,0x11,0x97,0xf1,0x0b,0xec,0x22,0x6d,0xf3,0x33,0xd3,0xef,
-0x02,0x6f,0x58,0xb8,0x73,0x50,0x2a,0x8b,0xe8,0x3a,0x53,0xa8,0xe9,0x09,0xbf,0xbc,
-0x57,0x47,0x83,0xdb,0x5e,0xb1,0x62,0x82,0x5c,0xb2,0x71,0x5f,0x23,0x67,0xfd,0xcb,
-0xe0,0xd1,0x0d,0xe8,0xab,0x44,0x33,0x0f,0x4c,0x76,0x92,0x32,0x65,0xb1,0x7e,0xca,
-0xed,0x21,0x5e,0x45,0xba,0x92,0xa4,0x67,0x67,0x9e,0x23,0x33,0x8e,0x6a,0xa1,0xe9,
-0x94,0x3a,0x39,0xef,0x34,0xb3,0x65,0x96,0x3b,0x6e,0x46,0xbd,0xd0,0xc3,0x22,0x87,
-0x54,0xad,0xbc,0x89,0xd6,0x3a,0x3a,0x99,0xe0,0x89,0x0e,0xcf,0xfe,0xa3,0x0a,0x1a,
-0x68,0x6f,0xcb,0xd7,0xa6,0x25,0xd1,0x25,0x4d,0xc0,0x86,0xd8,0x9b,0xf7,0xe2,0xd5,
-0xf7,0xa3,0x0c,0x33,0xe3,0x83,0x87,0xff,0x4c,0x9d,0xd6,0xd6,0x89,0x9a,0x0c,0x38,
-0xa0,0xe8,0xef,0x52,0x1b,0x0e,0xbb,0x52,0xbc,0x9f,0xde,0xbf,0x1a,0xc8,0xf0,0xd0,
-0xd9,0x54,0xc2,0x6c,0x53,0xa8,0x1b,0xc4,0x17,0x42,0x1d,0x51,0x2e,0xc8,0x8e,0xe7,
-0x63,0x5c,0x00,0xd6,0xc5,0x0d,0xcd,0xeb,0xab,0x59,0x13,0xf3,0x02,0x82,0x0c,0x2c,
-0x36,0x3c,0xe7,0x21,0xb8,0xf9,0x57,0x67,0xdd,0xae,0x1d,0x6e,0x39,0x06,0xe2,0xd2,
-0x16,0x1f,0x8e,0x2c,0xec,0x5f,0x71,0xf1,0x01,0x8b,0x6b,0x52,0x72,0x04,0x49,0xa8,
-0x8b,0xa7,0x27,0xe3,0x3c,0xfa,0x55,0x45,0x94,0x54,0xcc,0x68,0xc9,0xf9,0x35,0xa8,
-0xa5,0x8c,0xa2,0x5c,0xb8,0x7f,0x24,0xe5,0x6e,0x15,0xe4,0xdd,0x97,0xf2,0xe8,0x84,
-0xf9,0xcf,0x92,0x98,0x70,0x25,0x24,0xf3,0x6a,0x39,0x15,0x11,0xa0,0x40,0xa2,0x99,
-0x58,0x7a,0xa2,0x9b,0x2a,0xb2,0x74,0xa4,0x7d,0x2d,0x7e,0x5e,0x0e,0xf5,0x59,0x37,
-0xf6,0x50,0x9f,0x06,0xf3,0x0d,0x03,0x87,0xcc,0x18,0xb5,0xca,0x44,0xf1,0x57,0x55,
-0xe2,0x5f,0x8c,0x82,0x6c,0x1e,0xb2,0x83,0x40,0x0f,0xef,0x6d,0x61,0x34,0x9d,0x8f,
-0x68,0x59,0xcb,0x22,0x9b,0x29,0x55,0x1a,0x87,0x3f,0x64,0x22,0x4a,0xbd,0x6c,0x7c,
-0x0e,0xc3,0x89,0x5d,0x2a,0x86,0xa2,0xb6,0xf5,0xa3,0xd8,0x36,0x90,0x08,0x95,0xee,
-0xca,0xb4,0x95,0xcc,0xa1,0x8b,0x98,0xc7,0x48,0xfd,0xa7,0xce,0xd6,0xc5,0x3b,0x7a,
-0x2e,0x3a,0x0e,0xf8,0x11,0x63,0xef,0xb5,0xd1,0x10,0xed,0x67,0xb7,0x25,0x26,0x23,
-0x2d,0xdb,0x8b,0xf8,0x50,0x8d,0xd1,0x2c,0x32,0x14,0xe5,0x92,0xd8,0x88,0x31,0x82,
-0x7c,0xa1,0x6e,0x59,0xf1,0x54,0xb7,0x6f,0xfa,0x00,0xe4,0x59,0xe8,0x59,0xec,0x59,
-0xf0,0x59,0xf4,0x59,0x00,0x5a,0x04,0x5a,0x08,0x5a,0x14,0x5a,0x18,0x5a,0x3e,0x5a,
-0x42,0x5a,0x46,0x5a,0x4a,0x5a,0x4e,0x5a,0x5a,0xda,0x3e,0x67,0x3e,0x67,0x3e,0x67,
-0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x5c,0xda,0x5e,0x5a,
-0x3e,0xe7,0x3e,0xe7,0x62,0x5a,0x66,0x5a,0x3e,0x67,0x70,0x5a,0x74,0x5a,0x78,0x5a,
-0x7c,0x5a,0x88,0x5a,0x8c,0x5a,0x90,0x5a,0x9c,0x5a,0xa0,0x5a,0xca,0x5a,0xce,0x5a,
-0xd2,0x5a,0xd6,0x5a,0xda,0x5a,0xe6,0x5a,0x3e,0x67,0x3e,0xe7,0xfa,0x5a,0xfe,0xda,
-0x00,0xdb,0x02,0xdb,0x04,0xdb,0x06,0x5b,0x3e,0xe7,0x0a,0x5b,0x0e,0xdb,0x10,0xdb,
-0x12,0xdb,0x14,0xdb,0x16,0xdb,0x18,0xdb,0x3e,0xe7,0x3e,0xe7,0x1a,0xdb,0x1c,0xdb,
-0x1e,0xdb,0x20,0xdb,0x24,0x5b,0x28,0x5b,0x2c,0x5b,0x30,0x5b,0x34,0xdb,0x36,0xdb,
-0x8e,0x59,0x92,0x59,0x9c,0xd9,0x9e,0xd9,0x3e,0xe7,0x88,0xd9,0x8a,0xd9,0x8c,0xd9,
-0xa6,0xd9,0xa8,0xd9,0xaa,0xd9,0xb4,0xd9,0xb6,0xd9,0xbe,0xd9,0xa0,0xd9,0xa2,0xd9,
-0xa4,0xd9,0xc0,0xd9,0xc2,0xd9,0xc4,0xd9,0xc6,0xd9,0xc8,0xd9,0xca,0xd9,0xd0,0xd9,
-0xd2,0xd9,0xd4,0xd9,0xd6,0xd9,0xd8,0xd9,0xda,0xd9,0xdc,0xd9,0xde,0xd9,0xe0,0xd9,
-0xe2,0xd9,0x6c,0xd9,0x6e,0xd9,0x46,0xd9,0x4a,0xd9,0x4c,0xd9,0x4e,0xd9,0x50,0xd9,
-0x3e,0xe7,0x38,0x97,0x3e,0xe7,0x08,0x81,0x50,0x9e,0x5a,0xd9,0x5c,0xd9,0x3e,0xe7,
-0x5e,0xd9,0x70,0xd9,0x72,0xd9,0x3e,0xe7,0x78,0xd9,0x7a,0xd9,0x7c,0xd9,0x7e,0xd9,
-0x80,0xd9,0x82,0xd9,0x84,0xd9,0x86,0xd9,0x3a,0x5a,0x1c,0xda,0x1e,0xda,0x20,0xda,
-0x22,0xda,0x24,0xda,0x26,0x5a,0x2a,0x5a,0x2e,0x5a,0x32,0x5a,0x36,0x5a,0xc6,0x5a,
-0xa8,0xda,0xaa,0xda,0xac,0xda,0xae,0xda,0xb0,0xda,0xb2,0x5a,0xb6,0x5a,0xba,0x5a,
-0xbe,0x5a,0xc2,0x5a,0xea,0x5a,0xee,0x5a,0xac,0xd9,0xae,0xd9,0xcc,0xd9,0xce,0xd9,
-0x3e,0x67,0x42,0xd9,0x44,0xd9,0x38,0xdf,0x3e,0xe7,0x48,0xd9,0xa0,0x80,0x3e,0xe7,
-0x3e,0xe7,0x60,0xd9,0x6a,0xda,0x3e,0xe7,0xb0,0xd9,0x3a,0xdf,0x64,0xd9,0x22,0xdb,
-0x62,0xd9,0x66,0xd9,0x6c,0x5a,0x90,0x81,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,
-0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,
-0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,
-0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0x3e,0x67,0xbe,0xe6,
-0xbc,0xe6,0xb8,0xd9,0xba,0xd9,0xce,0x98,0x5c,0xe4,0x60,0xe4,0x54,0xd9,0x56,0xd9,
-0xf8,0x59,0xfc,0x59,0x0c,0x5a,0x10,0x5a,0x52,0x5a,0x56,0x5a,0x80,0x5a,0x84,0x5a,
-0x94,0x5a,0x98,0x5a,0xde,0x5a,0xe2,0x5a,0xf2,0x5a,0xf6,0x5a,0x92,0x1e,0x68,0x59,
-0x6a,0x59,0xc2,0xe1,0xc6,0xe1,0xfa,0x81,0x2c,0xbc,0x32,0xbc,0x38,0xbc,0x44,0xbc,
-0x4a,0xbc,0x68,0xbc,0x6e,0xbc,0xcc,0x98,0xb0,0x80,0xb2,0x80,0xb4,0x80,0x11,0x00,
-0x00,0x00,0x00,0x00,0xb6,0x16,0x06,0x00,0x01,0x00,0xb4,0x61,0x06,0x00,0x01,0x00,
-0xbc,0x61,0x06,0x00,0x01,0x00,0x60,0x17,0x10,0x00,0x01,0x00,0x82,0x17,0x24,0x00,
-0x01,0x00,0xaa,0x17,0x0e,0x00,0x01,0x00,0xc4,0x16,0x20,0x00,0x01,0x00,0x50,0x5b,
-0xe8,0x03,0x14,0x00,0x3c,0x5f,0x78,0x00,0x14,0x00,0x62,0x64,0xc0,0x01,0x10,0x00,
-0xd8,0x61,0x80,0x02,0x10,0x00,0x2a,0x67,0x38,0x00,0x07,0x00,0xa6,0x59,0x99,0x01,
-0x01,0x00,0xaa,0x17,0x0e,0x00,0x01,0x00,0xb8,0x17,0x0e,0x00,0x01,0x00,0x02,0x01,
-0x06,0x00,0x01,0x00,0x16,0x67,0x00,0x00,0x00,0x00,0x34,0x12,0xaa,0x55,0xe8,0x59,
-0x02,0x00,0xec,0x59,0x02,0x00,0xf0,0x59,0x02,0x00,0xf4,0x59,0x02,0x00,0x00,0x5a,
-0x02,0x00,0x04,0x5a,0x02,0x00,0x08,0x5a,0x02,0x00,0x42,0x5a,0x02,0x00,0x5a,0x5a,
-0x01,0x00,0x70,0x5a,0x02,0x00,0x74,0x5a,0x02,0x00,0x78,0x5a,0x02,0x00,0x7c,0x5a,
-0x02,0x00,0x88,0x5a,0x02,0x00,0x8c,0x5a,0x02,0x00,0x90,0x5a,0x02,0x00,0x9c,0x5a,
-0x02,0x00,0xce,0x5a,0x02,0x00,0x9c,0x59,0x01,0x00,0x9e,0x59,0x01,0x00,0x3e,0x67,
-0x01,0x00,0x8a,0x59,0x01,0x00,0xa6,0x59,0x01,0x00,0xb4,0x59,0x01,0x00,0xb6,0x59,
-0x01,0x00,0xc0,0x59,0x01,0x00,0xc2,0x59,0x01,0x00,0xc4,0x59,0x01,0x00,0xc6,0x59,
-0x01,0x00,0xc8,0x59,0x01,0x00,0xca,0x59,0x01,0x00,0x6c,0x59,0x01,0x00,0x46,0x59,
-0x01,0x00,0x3e,0x67,0x01,0x00,0x38,0x17,0x01,0x00,0xfe,0x00,0x01,0x00,0xa0,0x00,
-0x01,0x00,0xf8,0x59,0x02,0x00,0xfc,0x59,0x02,0x00,0x0c,0x5a,0x02,0x00,0x10,0x5a,
-0x02,0x00,0x80,0x5a,0x02,0x00,0x84,0x5a,0x02,0x00,0x94,0x5a,0x02,0x00,0x98,0x5a,
-0x02,0x00,0xcc,0x18,0x01,0x00,0xb0,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0xff,0xff,
-0xff,0xff,0x66,0xe8,0x7e,0x00,0x0e,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x40,0x8d,
-0x7f,0x00,0x06,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0xe8,0x8d,0x7f,0x00,0x12,0x00,
-0x00,0x00,0x09,0x01,0x00,0x00,0xe6,0x8d,0x7f,0x00,0x02,0x00,0x00,0x00,0x0a,0x01,
-0x00,0x00,0xc2,0x8d,0x7f,0x00,0x02,0x00,0x00,0x00,0x0b,0x01,0x00,0x00,0x0c,0x8e,
-0x7f,0x00,0x24,0x00,0x00,0x00,0x03,0x01,0x00,0x00,0x4c,0xe8,0x7e,0x00,0x0c,0x00,
-0x00,0x00,0x04,0x01,0x00,0x00,0x0a,0x81,0x7f,0x00,0x02,0x00,0x00,0x00,0x05,0x01,
-0x00,0x00,0x46,0x8d,0x7f,0x00,0x02,0x00,0x00,0x00,0x05,0x01,0x00,0x00,0x40,0x81,
-0x7f,0x00,0x02,0x00,0x00,0x00,0x05,0x01,0x00,0x00,0x50,0x8e,0x7f,0x00,0x02,0x00,
-0x00,0x00,0x06,0x01,0x00,0x00,0x58,0xe8,0x7e,0x00,0x02,0x00,0x00,0x00,0x07,0x01,
-0x00,0x00,0x5a,0xe8,0x7e,0x00,0x02,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x26,0xe8,
-0x7e,0x00,0x0a,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xbe,0x8d,0x7f,0x00,0x02,0x00,
-0x00,0x00,0x0a,0x00,0x00,0x00,0xc0,0x8d,0x7f,0x00,0x02,0x00,0x00,0x00,0x0c,0x01,
-0x00,0x00,0x38,0x81,0x7f,0x00,0x02,0x00,0x00,0x00,0x0d,0x01,0x00,0x00,0xc4,0x8d,
-0x7f,0x00,0x02,0x00,0x00,0x00,0x0e,0x01,0x00,0x00,0xfa,0x8d,0x7f,0x00,0x02,0x00,
-0x00,0x00,0x11,0x01,0x00,0x00,0xfc,0x8d,0x7f,0x00,0x02,0x00,0x00,0x00,0x00,0x18,
-0x7e,0x00,0xce,0xd0,0x00,0x00,0x01,0x00,0x00,0x00,0x05,0x00,0x01,0x00,0x21,0x00,
-0x02,0x00,0x02,0x00,0x01,0x00,0x06,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x02,0x00,
-0x01,0x00,0x01,0x00,0x06,0x00,0x02,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
-0x01,0x00,0x06,0x00,0x02,0x00,0x01,0x00,0x02,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
-0x5a,0xd7
-};
-
-#endif /* _FIRMWARE_WI_SPECTRUM24T_CF_H_ */
diff --git a/sys/dev/wpi/if_wpi.c b/sys/dev/wpi/if_wpi.c
index 7df317b..f801493 100644
--- a/sys/dev/wpi/if_wpi.c
+++ b/sys/dev/wpi/if_wpi.c
@@ -152,6 +152,11 @@ static const struct wpi_ident wpi_ident_table[] = {
{ 0, 0, 0, NULL }
};
+static struct ieee80211vap *wpi_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void wpi_vap_delete(struct ieee80211vap *);
static int wpi_dma_contig_alloc(struct wpi_softc *, struct wpi_dma_info *,
void **, bus_size_t, bus_size_t, int);
static void wpi_dma_contig_free(struct wpi_dma_info *);
@@ -166,8 +171,7 @@ static int wpi_alloc_tx_ring(struct wpi_softc *, struct wpi_tx_ring *,
static void wpi_reset_tx_ring(struct wpi_softc *, struct wpi_tx_ring *);
static void wpi_free_tx_ring(struct wpi_softc *, struct wpi_tx_ring *);
static struct ieee80211_node *wpi_node_alloc(struct ieee80211_node_table *);
-static int wpi_media_change(struct ifnet *);
-static int wpi_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static int wpi_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static void wpi_mem_lock(struct wpi_softc *);
static void wpi_mem_unlock(struct wpi_softc *);
static uint32_t wpi_mem_read(struct wpi_softc *, uint16_t);
@@ -193,11 +197,14 @@ static void wpi_watchdog(void *);
static int wpi_tx_data(struct wpi_softc *, struct mbuf *,
struct ieee80211_node *, int);
static void wpi_start(struct ifnet *);
+static void wpi_start_locked(struct ifnet *);
+static int wpi_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
static void wpi_scan_start(struct ieee80211com *);
static void wpi_scan_end(struct ieee80211com *);
static void wpi_set_channel(struct ieee80211com *);
-static void wpi_scan_curchan(struct ieee80211com *, unsigned long);
-static void wpi_scan_mindwell(struct ieee80211com *);
+static void wpi_scan_curchan(struct ieee80211_scan_state *, unsigned long);
+static void wpi_scan_mindwell(struct ieee80211_scan_state *);
static int wpi_ioctl(struct ifnet *, u_long, caddr_t);
static void wpi_read_eeprom(struct wpi_softc *);
static void wpi_read_eeprom_channels(struct wpi_softc *, int);
@@ -210,8 +217,8 @@ static void wpi_enable_tsf(struct wpi_softc *, struct ieee80211_node *);
#if 0
static int wpi_setup_beacon(struct wpi_softc *, struct ieee80211_node *);
#endif
-static int wpi_auth(struct wpi_softc *);
-static int wpi_run(struct wpi_softc *);
+static int wpi_auth(struct wpi_softc *, struct ieee80211vap *);
+static int wpi_run(struct wpi_softc *, struct ieee80211vap *);
static int wpi_scan(struct wpi_softc *);
static int wpi_config(struct wpi_softc *);
static void wpi_stop_master(struct wpi_softc *);
@@ -222,7 +229,6 @@ static void wpi_init(void *);
static void wpi_init_locked(struct wpi_softc *, int);
static void wpi_stop(struct wpi_softc *);
static void wpi_stop_locked(struct wpi_softc *);
-static void wpi_iter_func(void *, struct ieee80211_node *);
static void wpi_newassoc(struct ieee80211_node *, int);
static int wpi_set_txpower(struct wpi_softc *, struct ieee80211_channel *,
@@ -301,7 +307,7 @@ wpi_probe(device_t dev)
static int
wpi_load_firmware(struct wpi_softc *sc)
{
- const struct firmware *fp ;
+ const struct firmware *fp;
struct wpi_dma_info *dma = &sc->fw_dma;
const struct wpi_firmware_hdr *hdr;
const uint8_t *itext, *idata, *rtext, *rdata, *btext;
@@ -476,7 +482,7 @@ wpi_attach(device_t dev)
{
struct wpi_softc *sc = device_get_softc(dev);
struct ifnet *ifp;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic;
int ac, error, supportsa = 1;
uint32_t tmp;
const struct wpi_ident *ident;
@@ -499,7 +505,6 @@ wpi_attach(device_t dev)
}
}
-#if __FreeBSD_version >= 700000
/*
* Create the taskqueues used by the driver. Primarily
* sc_tq handles most the task
@@ -508,9 +513,6 @@ wpi_attach(device_t dev)
taskqueue_thread_enqueue, &sc->sc_tq);
taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq",
device_get_nameunit(dev));
-#else
-#error "Sorry, this driver is not yet ready for FreeBSD < 7.0"
-#endif
/* Create the tasks that can be queued */
TASK_INIT(&sc->sc_opstask, 0, wpi_ops, sc);
@@ -607,17 +609,17 @@ wpi_attach(device_t dev)
goto fail;
}
- ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
if (ifp == NULL) {
device_printf(dev, "can not if_alloc()\n");
error = ENOMEM;
goto fail;
}
+ ic = ifp->if_l2com;
ic->ic_ifp = ifp;
- ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
- ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
- ic->ic_state = IEEE80211_S_INIT;
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
/* set device capabilities */
ic->ic_caps =
@@ -663,11 +665,12 @@ wpi_attach(device_t dev)
IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
IFQ_SET_READY(&ifp->if_snd);
- ieee80211_ifattach(ic);
+ ieee80211_ifattach(ic);
/* override default methods */
ic->ic_node_alloc = wpi_node_alloc;
ic->ic_newassoc = wpi_newassoc;
+ ic->ic_raw_xmit = wpi_raw_xmit;
ic->ic_wme.wme_update = wpi_wme_update;
ic->ic_scan_start = wpi_scan_start;
ic->ic_scan_end = wpi_scan_end;
@@ -675,21 +678,11 @@ wpi_attach(device_t dev)
ic->ic_scan_curchan = wpi_scan_curchan;
ic->ic_scan_mindwell = wpi_scan_mindwell;
- /* override state transition machine */
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = wpi_newstate;
- ieee80211_media_init(ic, wpi_media_change, ieee80211_media_status);
+ ic->ic_vap_create = wpi_vap_create;
+ ic->ic_vap_delete = wpi_vap_delete;
- ieee80211_amrr_init(&sc->amrr, ic,
- IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
- IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD);
-
- /* whilst ieee80211_ifattach will listen for ieee80211 frames,
- * we also want to listen for the lower level radio frames
- */
- bpfattach2(ifp, DLT_IEEE802_11_RADIO,
- sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap),
- &sc->sc_drvbpf);
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap));
sc->sc_rxtap_len = sizeof sc->sc_rxtap;
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
@@ -714,7 +707,6 @@ wpi_attach(device_t dev)
#ifdef XXX_DEBUG
ieee80211_announce_channels(ic);
#endif
-
return 0;
fail: wpi_detach(dev);
@@ -725,8 +717,8 @@ static int
wpi_detach(device_t dev)
{
struct wpi_softc *sc = device_get_softc(dev);
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
int ac;
if (ifp != NULL) {
@@ -774,6 +766,48 @@ wpi_detach(device_t dev)
return 0;
}
+static struct ieee80211vap *
+wpi_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct wpi_vap *wvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ wvp = (struct wpi_vap *) malloc(sizeof(struct wpi_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (wvp == NULL)
+ return NULL;
+ vap = &wvp->vap;
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
+ /* override with driver methods */
+ wvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = wpi_newstate;
+
+ ieee80211_amrr_init(&wvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 500 /*ms*/);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+wpi_vap_delete(struct ieee80211vap *vap)
+{
+ struct wpi_vap *wvp = WPI_VAP(vap);
+
+ ieee80211_amrr_cleanup(&wvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(wvp, M_80211_VAP);
+}
+
static void
wpi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
{
@@ -1195,7 +1229,7 @@ static int
wpi_resume(device_t dev)
{
struct wpi_softc *sc = device_get_softc(dev);
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
pci_write_config(dev, 0x41, 0, 1);
@@ -1213,72 +1247,43 @@ wpi_node_alloc(struct ieee80211_node_table *ic)
{
struct wpi_node *wn;
- wn = malloc(sizeof (struct wpi_node), M_80211_NODE, M_NOWAIT |M_ZERO);
+ wn = malloc(sizeof (struct wpi_node), M_80211_NODE, M_NOWAIT | M_ZERO);
return &wn->ni;
}
-static int
-wpi_media_change(struct ifnet *ifp)
-{
- int error;
-
- error = ieee80211_media_change(ifp);
- if (error != ENETRESET)
- return error;
-
- if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
- wpi_init(ifp->if_softc);
-
- return 0;
-}
-
/**
* Called by net80211 when ever there is a change to 80211 state machine
*/
static int
-wpi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+wpi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
+ struct wpi_vap *wvp = WPI_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
struct ifnet *ifp = ic->ic_ifp;
struct wpi_softc *sc = ifp->if_softc;
+ int error;
- DPRINTF(("%s: %s -> %s\n", __func__,
- ieee80211_state_name[ic->ic_state],
- ieee80211_state_name[nstate]));
-
- switch (nstate) {
- case IEEE80211_S_SCAN:
- /*
- * Scanning is handled in net80211 via the scan_start,
- * scan_end, scan_curchan functions. Hence all we do when
- * changing to the SCAN state is update the leds
- */
-
- /* make the link LED blink while we're scanning */
- wpi_set_led(sc, WPI_LED_LINK, 20, 2);
- break;
+ DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__,
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate], sc->flags));
- case IEEE80211_S_AUTH:
+ if (nstate == IEEE80211_S_AUTH) {
/* Delay the auth transition until we can update the firmware */
- return wpi_queue_cmd(sc, WPI_AUTH, arg, WPI_QUEUE_NORMAL);
-
- case IEEE80211_S_RUN:
- if (ic->ic_opmode == IEEE80211_M_MONITOR) {
- /* link LED blinks while monitoring */
- wpi_set_led(sc, WPI_LED_LINK, 5, 5);
- break;
- }
- if (ic->ic_state != IEEE80211_S_RUN)
- /* set the association id first */
- return wpi_queue_cmd(sc, WPI_RUN, arg,
- WPI_QUEUE_NORMAL);
- break;
-
- default:
- break;
+ error = wpi_queue_cmd(sc, WPI_AUTH, arg, WPI_QUEUE_NORMAL);
+ return (error != 0 ? error : EINPROGRESS);
}
-
- return sc->sc_newstate(ic, nstate, arg);
+ if (nstate == IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_RUN) {
+ /* set the association id first */
+ error = wpi_queue_cmd(sc, WPI_RUN, arg, WPI_QUEUE_NORMAL);
+ return (error != 0 ? error : EINPROGRESS);
+ }
+ if (nstate == IEEE80211_S_RUN) {
+ /* RUN -> RUN transition; just restart the timers */
+ wpi_calib_timeout(sc);
+ /* XXX split out rate control timer */
+ }
+ return wvp->newstate(vap, nstate, arg);
}
/*
@@ -1431,8 +1436,8 @@ static void
wpi_rx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc,
struct wpi_rx_data *data)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct wpi_rx_ring *ring = &sc->rxq;
struct wpi_rx_stat *stat;
struct wpi_rx_head *head;
@@ -1490,7 +1495,7 @@ wpi_rx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc,
/* update Rx descriptor */
ring->desc[ring->cur] = htole32(paddr);
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct wpi_rx_radiotap_header *tap = &sc->sc_rxtap;
tap->wr_flags = 0;
@@ -1523,27 +1528,25 @@ wpi_rx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc,
if (le16toh(head->flags) & 0x4)
tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
}
WPI_UNLOCK(sc);
- /* XXX frame length > sizeof(struct ieee80211_frame_min)? */
- /* grab a reference to the source node */
ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, stat->rssi, 0, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, stat->rssi, 0, 0);
- /* send the frame to the 802.11 layer */
- ieee80211_input(ic, m, ni, stat->rssi, 0, 0);
-
- /* release node reference */
- ieee80211_free_node(ni);
WPI_LOCK(sc);
}
static void
wpi_tx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc)
{
- struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
struct wpi_tx_ring *ring = &sc->txq[desc->qid & 0x3];
struct wpi_tx_data *txdata = &ring->data[desc->idx];
struct wpi_tx_stat *stat = (struct wpi_tx_stat *)(desc + 1);
@@ -1584,7 +1587,7 @@ wpi_tx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc)
sc->sc_tx_timer = 0;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- wpi_start(ifp);
+ wpi_start_locked(ifp);
}
static void
@@ -1617,8 +1620,8 @@ wpi_cmd_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc)
static void
wpi_notif_intr(struct wpi_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct wpi_rx_desc *desc;
struct wpi_rx_data *data;
uint32_t hw;
@@ -1698,28 +1701,30 @@ wpi_notif_intr(struct wpi_softc *sc)
{
struct wpi_stop_scan *scan =
(struct wpi_stop_scan *)(desc + 1);
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
DPRINTFN(WPI_DEBUG_SCANNING,
("scan finished nchan=%d status=%d chan=%d\n",
scan->nchan, scan->status, scan->chan));
sc->sc_scan_timer = 0;
- ieee80211_scan_next(ic);
+ ieee80211_scan_next(vap);
break;
}
case WPI_MISSED_BEACON:
{
- struct wpi_missed_beacon *beacon =
+ struct wpi_missed_beacon *beacon =
(struct wpi_missed_beacon *)(desc + 1);
-
- if (le32toh(beacon->consecutive) >=
- ic->ic_bmissthreshold) {
- DPRINTF(("Beacon miss: %u >= %u\n",
- le32toh(beacon->consecutive),
- ic->ic_bmissthreshold));
- ieee80211_beacon_miss(ic);
- }
- break;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+
+ if (le32toh(beacon->consecutive) >=
+ vap->iv_bmissthreshold) {
+ DPRINTF(("Beacon miss: %u >= %u\n",
+ le32toh(beacon->consecutive),
+ vap->iv_bmissthreshold));
+ ieee80211_beacon_miss(ic);
+ }
+ break;
}
}
@@ -1811,7 +1816,9 @@ static int
wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
int ac)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams;
struct wpi_tx_ring *ring = &sc->txq[ac];
struct wpi_tx_desc *desc;
@@ -1819,6 +1826,7 @@ wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
struct wpi_tx_cmd *cmd;
struct wpi_cmd_data *tx;
struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
struct ieee80211_key *k;
struct mbuf *mnew;
int i, error, nsegs, rate, hdrlen, ismcast;
@@ -1833,7 +1841,7 @@ wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- k = ieee80211_crypto_encap(ic, ni, m0);
+ k = ieee80211_crypto_encap(ni, m0);
if (k == NULL) {
m_freem(m0);
return ENOBUFS;
@@ -1850,7 +1858,7 @@ wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
tx = (struct wpi_cmd_data *)cmd->data;
tx->flags = htole32(WPI_TX_AUTO_SEQ);
- tx->timeout= htole16(0);
+ tx->timeout = htole16(0);
tx->ofdm_mask = 0xff;
tx->cck_mask = 0x0f;
tx->lifetime = htole32(WPI_LIFETIME_INFINITE);
@@ -1861,40 +1869,42 @@ wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0 ||
!cap->cap_wmeParams[ac].wmep_noackPolicy)
tx->flags |= htole32(WPI_TX_NEED_ACK);
- if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) {
+ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) {
tx->flags |= htole32(WPI_TX_NEED_RTS|WPI_TX_FULL_TXOP);
tx->rts_ntries = 7;
}
}
-
/* pick a rate */
- if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MASK) {
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT) {
uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
/* tell h/w to set timestamp in probe responses */
if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
tx->flags |= htole32(WPI_TX_INSERT_TSTAMP);
-
if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
tx->timeout = htole16(3);
else
tx->timeout = htole16(2);
-
- rate = ni->ni_rates.rs_rates[0] & IEEE80211_RATE_VAL;
+ rate = tp->mgmtrate;
} else if (ismcast) {
- rate = ic->ic_mcast_rate;
- } else if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
- rate = ic->ic_fixed_rate;
+ rate = tp->mcastrate;
+ } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ rate = tp->ucastrate;
} else {
- rate = ni->ni_rates.rs_rates[ni->ni_txrate];
- rate &= IEEE80211_RATE_VAL;
+ (void) ieee80211_amrr_choose(ni, &WPI_NODE(ni)->amn);
+ rate = ni->ni_txrate;
}
tx->rate = wpi_plcp_signal(rate);
/* be very persistant at sending frames out */
- tx->data_ntries = 15; /* XXX Way too high */
+#if 0
+ tx->data_ntries = tp->maxretry;
+#else
+ tx->data_ntries = 15; /* XXX way too high */
+#endif
- if (bpf_peers_present(sc->sc_drvbpf)) {
+ if (bpf_peers_present(ifp->if_bpf)) {
struct wpi_tx_radiotap_header *tap = &sc->sc_txtap;
tap->wt_flags = 0;
tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq);
@@ -1903,7 +1913,8 @@ wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
tap->wt_hwqueue = ac;
if (wh->i_fc[1] & IEEE80211_FC1_WEP)
tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
- bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
}
/* save and trim IEEE802.11 header */
@@ -1976,139 +1987,126 @@ static void
wpi_start(struct ifnet *ifp)
{
struct wpi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
+
+ WPI_LOCK(sc);
+ wpi_start_locked(ifp);
+ WPI_UNLOCK(sc);
+}
+
+static void
+wpi_start_locked(struct ifnet *ifp)
+{
+ struct wpi_softc *sc = ifp->if_softc;
struct ieee80211_node *ni;
- struct ether_header *eh;
- struct mbuf *m0;
- int ac, waslocked;
+ struct mbuf *m;
+ int ac;
+
+ WPI_LOCK_ASSERT(sc);
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
return;
- waslocked = WPI_LOCK_OWNED(sc);
- if (!waslocked)
- WPI_LOCK(sc);
-
for (;;) {
- IF_DEQUEUE(&ic->ic_mgtq, m0);
- if (m0 != NULL) {
- ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
- m0->m_pkthdr.rcvif = NULL;
-
- /* management frames go into ring 0 */
- if (sc->txq[0].queued > sc->txq[0].count - 8) {
- ifp->if_oerrors++;
- continue;
- }
-
- if (wpi_tx_data(sc, m0, ni, 0) != 0) {
- ifp->if_oerrors++;
- break;
- }
- } else {
- if (ic->ic_state != IEEE80211_S_RUN)
- break;
-
- IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
- if (m0 == NULL)
- break;
-
- /*
- * Cancel any background scan.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(ic);
-
- if (m0->m_len < sizeof (*eh) &&
- (m0 = m_pullup(m0, sizeof (*eh))) != NULL) {
- ifp->if_oerrors++;
- continue;
- }
- eh = mtod(m0, struct ether_header *);
- ni = ieee80211_find_txnode(ic, eh->ether_dhost);
- if (ni == NULL) {
- m_freem(m0);
- ifp->if_oerrors++;
- continue;
- }
-
- /* classify mbuf so we can find which tx ring to use */
- if (ieee80211_classify(ic, m0, ni) != 0) {
- m_freem(m0);
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- continue;
- }
-
- ac = M_WME_GETAC(m0);
- if (sc->txq[ac].queued > sc->txq[ac].count - 8) {
- /* there is no place left in this ring */
- IFQ_DRV_PREPEND(&ifp->if_snd, m0);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
-
- BPF_MTAP(ifp, m0);
-
- m0 = ieee80211_encap(ic, m0, ni);
- if (m0 == NULL) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- continue;
- }
-
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m0);
-
- if (wpi_tx_data(sc, m0, ni, ac) != 0) {
- ieee80211_free_node(ni);
- ifp->if_oerrors++;
- break;
- }
+ IFQ_POLL(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ /* no QoS encapsulation for EAPOL frames */
+ ac = M_WME_GETAC(m);
+ if (sc->txq[ac].queued > sc->txq[ac].count - 8) {
+ /* there is no place left in this ring */
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ continue;
+ }
+ if (wpi_tx_data(sc, m, ni, ac) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
}
-
sc->sc_tx_timer = 5;
- ic->ic_lastdata = ticks;
}
+}
- if (!waslocked)
+static int
+wpi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct wpi_softc *sc = ifp->if_softc;
+
+ /* prevent management frames from being sent if we're not ready */
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return ENETDOWN;
+ }
+ WPI_LOCK(sc);
+
+ /* management frames go into ring 0 */
+ if (sc->txq[0].queued > sc->txq[0].count - 8) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ m_freem(m);
WPI_UNLOCK(sc);
+ ieee80211_free_node(ni);
+ return ENOBUFS; /* XXX */
+ }
+
+ ifp->if_opackets++;
+ if (wpi_tx_data(sc, m, ni, 0) != 0)
+ goto bad;
+ sc->sc_tx_timer = 5;
+ callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc);
+
+ WPI_UNLOCK(sc);
+ return 0;
+bad:
+ ifp->if_oerrors++;
+ WPI_UNLOCK(sc);
+ ieee80211_free_node(ni);
+ return EIO; /* XXX */
}
static int
wpi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct wpi_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- int error = 0;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
WPI_LOCK(sc);
-
switch (cmd) {
case SIOCSIFFLAGS:
if ((ifp->if_flags & IFF_UP)) {
- if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
wpi_init_locked(sc, 0);
+ startall = 1;
+ }
} else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) ||
(sc->flags & WPI_FLAG_HW_RADIO_OFF))
wpi_stop_locked(sc);
break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
default:
- WPI_UNLOCK(sc);
- error = ieee80211_ioctl(ic, cmd, data);
- WPI_LOCK(sc);
- }
-
- if (error == ENETRESET) {
- if ((ifp->if_flags & IFF_UP) &&
- (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
- ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- wpi_init_locked(sc, 0);
- error = 0;
+ error = ether_ioctl(ifp, cmd, data);
+ break;
}
-
WPI_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
return error;
}
@@ -2118,7 +2116,8 @@ wpi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
static void
wpi_read_eeprom(struct wpi_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
int i;
/* read the hardware capabilities, revision and SKU type */
@@ -2222,7 +2221,6 @@ wpi_wme_update(struct ieee80211com *ic)
"txop=%d\n", ac, wme.ac[ac].aifsn, wme.ac[ac].cwmin,
wme.ac[ac].cwmax, wme.ac[ac].txop));
}
-
return wpi_cmd(sc, WPI_CMD_SET_WME, &wme, sizeof wme, 1);
#undef WPI_USEC
#undef WPI_EXP2
@@ -2234,7 +2232,8 @@ wpi_wme_update(struct ieee80211com *ic)
static int
wpi_mrr_setup(struct wpi_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct wpi_mrr_setup mrr;
int i, error;
@@ -2326,7 +2325,8 @@ wpi_enable_tsf(struct wpi_softc *sc, struct ieee80211_node *ni)
static int
wpi_setup_beacon(struct wpi_softc *sc, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct wpi_tx_ring *ring = &sc->cmdq;
struct wpi_tx_desc *desc;
struct wpi_tx_data *data;
@@ -2395,10 +2395,10 @@ wpi_setup_beacon(struct wpi_softc *sc, struct ieee80211_node *ni)
#endif
static int
-wpi_auth(struct wpi_softc *sc)
+wpi_auth(struct wpi_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni = vap->iv_bss;
struct wpi_node_info node;
int error;
@@ -2412,16 +2412,14 @@ wpi_auth(struct wpi_softc *sc)
sc->config.flags |= htole32(WPI_CONFIG_AUTO |
WPI_CONFIG_24GHZ);
}
- switch (ic->ic_curmode) {
- case IEEE80211_MODE_11A:
+ if (IEEE80211_IS_CHAN_A(ni->ni_chan)) {
sc->config.cck_mask = 0;
sc->config.ofdm_mask = 0x15;
- break;
- case IEEE80211_MODE_11B:
+ } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) {
sc->config.cck_mask = 0x03;
sc->config.ofdm_mask = 0;
- break;
- default: /* assume 802.11b/g */
+ } else {
+ /* XXX assume 802.11b/g */
sc->config.cck_mask = 0x0f;
sc->config.ofdm_mask = 0x15;
}
@@ -2457,13 +2455,18 @@ wpi_auth(struct wpi_softc *sc)
}
static int
-wpi_run(struct wpi_softc *sc)
+wpi_run(struct wpi_softc *sc, struct ieee80211vap *vap)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni = vap->iv_bss;
int error;
- ni = ic->ic_bss;
+ if (vap->iv_opmode == IEEE80211_M_MONITOR) {
+ /* link LED blinks while monitoring */
+ wpi_set_led(sc, WPI_LED_LINK, 5, 5);
+ return 0;
+ }
+
wpi_enable_tsf(sc, ni);
/* update adapter's configuration */
@@ -2488,22 +2491,22 @@ wpi_run(struct wpi_softc *sc)
return error;
}
- error = wpi_set_txpower(sc, ic->ic_bsschan, 1);
+ error = wpi_set_txpower(sc, ni->ni_chan, 1);
if (error != 0) {
device_printf(sc->sc_dev, "could set txpower\n");
return error;
}
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
/* fake a join to init the tx rate */
- wpi_newassoc(ic->ic_bss, 1);
+ wpi_newassoc(ni, 1);
}
/* link LED always on while associated */
wpi_set_led(sc, WPI_LED_LINK, 0, 1);
/* start automatic rate control timer */
- callout_reset(&sc->calib_to, hz/2, wpi_calib_timeout, sc);
+ callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc);
return (error);
}
@@ -2519,7 +2522,8 @@ wpi_run(struct wpi_softc *sc)
static int
wpi_scan(struct wpi_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211_scan_state *ss = ic->ic_scan;
struct wpi_tx_ring *ring = &sc->cmdq;
struct wpi_tx_desc *desc;
@@ -2534,7 +2538,6 @@ wpi_scan(struct wpi_softc *sc)
uint8_t *frm;
int nrates, pktlen, error, i, nssid;
bus_addr_t physaddr;
- struct ifnet *ifp = ic->ic_ifp;
desc = &ring->desc[ring->cur];
data = &ring->data[ring->cur];
@@ -2578,22 +2581,19 @@ wpi_scan(struct wpi_softc *sc)
hdr->tx.lifetime = htole32(WPI_LIFETIME_INFINITE);
hdr->tx.flags = htole32(WPI_TX_AUTO_SEQ);
- /*XXX Need to cater for multiple essids */
- memset(&hdr->scan_essids, 0, sizeof(hdr->scan_essids));
+ memset(hdr->scan_essids, 0, sizeof(hdr->scan_essids));
nssid = MIN(ss->ss_nssid, WPI_SCAN_MAX_ESSIDS);
- for (i = 0; i < nssid; i++ ){
+ for (i = 0; i < nssid; i++) {
hdr->scan_essids[i].id = IEEE80211_ELEMID_SSID;
hdr->scan_essids[i].esslen = MIN(ss->ss_ssid[i].len, 32);
memcpy(hdr->scan_essids[i].essid, ss->ss_ssid[i].ssid,
hdr->scan_essids[i].esslen);
-#ifdef WPI_DEBUG
if (wpi_debug & WPI_DEBUG_SCANNING) {
printf("Scanning Essid: ");
- ieee80211_print_essid(ic->ic_des_ssid[i].ssid,
- ic->ic_des_ssid[i].len);
+ ieee80211_print_essid(hdr->scan_essids[i].essid,
+ hdr->scan_essids[i].esslen);
printf("\n");
}
-#endif
}
/*
@@ -2651,17 +2651,17 @@ wpi_scan(struct wpi_softc *sc)
chan->flags = 0;
if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) {
chan->flags |= WPI_CHAN_ACTIVE;
- if (ic->ic_des_ssid[0].len != 0)
+ if (nssid != 0)
chan->flags |= WPI_CHAN_DIRECT;
}
chan->gain_dsp = 0x6e; /* Default level */
if (IEEE80211_IS_CHAN_5GHZ(c)) {
chan->active = htole16(10);
- chan->passive = htole16(sc->maxdwell);
+ chan->passive = htole16(ss->ss_maxdwell);
chan->gain_radio = 0x3b;
} else {
chan->active = htole16(20);
- chan->passive = htole16(sc->maxdwell);
+ chan->passive = htole16(ss->ss_maxdwell);
chan->gain_radio = 0x28;
}
@@ -2746,8 +2746,8 @@ wpi_scan(struct wpi_softc *sc)
static int
wpi_config(struct wpi_softc *sc)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct wpi_power power;
struct wpi_bluetooth bluetooth;
struct wpi_node_info node;
@@ -2968,7 +2968,8 @@ static void
wpi_rfkill_resume(struct wpi_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
int ntries;
/* enable firmware again */
@@ -3000,46 +3001,26 @@ wpi_rfkill_resume(struct wpi_softc *sc)
ifp->if_drv_flags |= IFF_DRV_RUNNING;
sc->flags &= ~WPI_FLAG_HW_RADIO_OFF;
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_scan_next(ic);
-
- ieee80211_beacon_miss(ic);
-
- /* reset the led sequence */
- switch (ic->ic_state) {
- case IEEE80211_S_SCAN:
- wpi_set_led(sc, WPI_LED_LINK, 20, 2);
- break;
-
- case IEEE80211_S_RUN:
- if (ic->ic_opmode == IEEE80211_M_MONITOR)
- wpi_set_led(sc, WPI_LED_LINK, 5, 5);
- else
+ if (vap != NULL) {
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
+ ieee80211_beacon_miss(ic);
wpi_set_led(sc, WPI_LED_LINK, 0, 1);
- break;
-
- default:
- break; /* please compiler */
+ } else
+ wpi_set_led(sc, WPI_LED_LINK, 5, 5);
+ } else {
+ ieee80211_scan_next(vap);
+ wpi_set_led(sc, WPI_LED_LINK, 20, 2);
+ }
}
callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc);
}
static void
-wpi_init(void *arg)
-{
- struct wpi_softc *sc = arg;
-
- WPI_LOCK(sc);
- wpi_init_locked(sc, 0);
- WPI_UNLOCK(sc);
-}
-
-static void
wpi_init_locked(struct wpi_softc *sc, int force)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
uint32_t tmp;
int ntries, qid;
@@ -3143,29 +3124,27 @@ wpi_init_locked(struct wpi_softc *sc, int force)
ifp->if_drv_flags |= IFF_DRV_RUNNING;
out:
callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc);
-
- if (ic->ic_opmode == IEEE80211_M_MONITOR)
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
- else if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- return;
}
static void
-wpi_stop(struct wpi_softc *sc)
+wpi_init(void *arg)
{
+ struct wpi_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
WPI_LOCK(sc);
- wpi_stop_locked(sc);
+ wpi_init_locked(sc, 0);
WPI_UNLOCK(sc);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vaps */
}
+
static void
wpi_stop_locked(struct wpi_softc *sc)
-
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = sc->sc_ifp;
uint32_t tmp;
int ac;
@@ -3212,66 +3191,44 @@ wpi_stop_locked(struct wpi_softc *sc)
tmp = WPI_READ(sc, WPI_RESET);
WPI_WRITE(sc, WPI_RESET, tmp | WPI_SW_RESET);
sc->flags &= ~WPI_FLAG_BUSY;
-
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
}
static void
-wpi_iter_func(void *arg, struct ieee80211_node *ni)
+wpi_stop(struct wpi_softc *sc)
{
- struct wpi_softc *sc = arg;
- struct wpi_node *wn = (struct wpi_node *)ni;
-
- ieee80211_amrr_choose(&sc->amrr, ni, &wn->amn);
+ WPI_LOCK(sc);
+ wpi_stop_locked(sc);
+ WPI_UNLOCK(sc);
}
static void
wpi_newassoc(struct ieee80211_node *ni, int isnew)
{
- struct wpi_softc *sc = ni->ni_ic->ic_ifp->if_softc;
- int i;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct wpi_vap *wvp = WPI_VAP(vap);
- ieee80211_amrr_node_init(&sc->amrr, &((struct wpi_node *)ni)->amn);
-
- for (i = ni->ni_rates.rs_nrates - 1;
- i > 0 && (ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) > 72;
- i--);
- ni->ni_txrate = i;
+ ieee80211_amrr_node_init(&wvp->amrr, &WPI_NODE(ni)->amn, ni);
}
static void
wpi_calib_timeout(void *arg)
{
struct wpi_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
int temp;
- if (ic->ic_state != IEEE80211_S_RUN)
+ if (vap->iv_state != IEEE80211_S_RUN)
return;
- /* automatic rate control triggered every 500ms */
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
- if (ic->ic_opmode == IEEE80211_M_STA)
- wpi_iter_func(sc, ic->ic_bss);
- else
- ieee80211_iterate_nodes(&ic->ic_sta, wpi_iter_func, sc);
- }
-
/* update sensor data */
temp = (int)WPI_READ(sc, WPI_TEMPERATURE);
DPRINTFN(WPI_DEBUG_TEMP,("Temp in calibration is: %d\n", temp));
-#if 0
- //XXX Used by OpenBSD Sensor Framework
- sc->sensor.value = temp + 260;
-#endif
- /* automatic power calibration every 60s */
- if (++sc->calib_cnt >= 120) {
- wpi_power_calibration(sc, temp);
- sc->calib_cnt = 0;
- }
+ wpi_power_calibration(sc, temp);
- callout_reset(&sc->calib_to, hz/2, wpi_calib_timeout, sc);
+ callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc);
}
/*
@@ -3281,6 +3238,10 @@ wpi_calib_timeout(void *arg)
static void
wpi_power_calibration(struct wpi_softc *sc, int temp)
{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+
/* sanity-check read value */
if (temp < -260 || temp > 25) {
/* this can't be correct, ignore */
@@ -3297,7 +3258,7 @@ wpi_power_calibration(struct wpi_softc *sc, int temp)
sc->temp = temp;
- if (wpi_set_txpower(sc, sc->sc_ic.ic_bss->ni_chan,1) != 0) {
+ if (wpi_set_txpower(sc, vap->iv_bss->ni_chan, 1) != 0) {
/* just warn, too bad for the automatic calibration... */
device_printf(sc->sc_dev,"could not adjust Tx power\n");
}
@@ -3310,7 +3271,8 @@ wpi_power_calibration(struct wpi_softc *sc, int temp)
static void
wpi_read_eeprom_channels(struct wpi_softc *sc, int n)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
const struct wpi_chan_band *band = &wpi_bands[n];
struct wpi_eeprom_chan channels[WPI_MAX_CHAN_PER_BAND];
int chan, i, offset, passive;
@@ -3416,7 +3378,8 @@ wpi_read_eeprom_group(struct wpi_softc *sc, int n)
static int
wpi_set_txpower(struct wpi_softc *sc, struct ieee80211_channel *c, int async)
{
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct wpi_power_group *group;
struct wpi_cmd_txpower txpower;
u_int chan;
@@ -3476,7 +3439,8 @@ wpi_get_power_index(struct wpi_softc *sc, struct wpi_power_group *group,
#define interpolate(x, x1, y1, x2, y2, n) \
((y1) + fdivround(((x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n))
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct wpi_power_sample *sample;
int pwr, idx;
u_int chan;
@@ -3587,13 +3551,12 @@ wpi_set_channel(struct ieee80211com *ic)
* callback.
*/
static void
-wpi_scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
+wpi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap = ss->ss_vap;
+ struct ifnet *ifp = vap->iv_ic->ic_ifp;
struct wpi_softc *sc = ifp->if_softc;
- sc->maxdwell = maxdwell;
-
wpi_queue_cmd(sc, WPI_SCAN_CURCHAN, 0, WPI_QUEUE_NORMAL);
}
@@ -3604,7 +3567,7 @@ wpi_scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
* us when it's finished and we have no way to interrupt it.
*/
static void
-wpi_scan_mindwell(struct ieee80211com *ic)
+wpi_scan_mindwell(struct ieee80211_scan_state *ss)
{
/* NB: don't try to abort scan; wait for firmware to finish */
}
@@ -3619,8 +3582,10 @@ static void
wpi_ops(void *arg0, int pending)
{
struct wpi_softc *sc = arg0;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
int cmd, arg, error;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
again:
WPI_CMD_LOCK(sc);
@@ -3659,6 +3624,8 @@ again:
switch (cmd) {
case WPI_SCAN_START:
+ /* make the link LED blink while we're scanning */
+ wpi_set_led(sc, WPI_LED_LINK, 20, 2);
sc->flags |= WPI_FLAG_SCANNING;
break;
@@ -3668,7 +3635,7 @@ again:
case WPI_SCAN_CURCHAN:
if (wpi_scan(sc))
- ieee80211_cancel_scan(ic);
+ ieee80211_cancel_scan(vap);
break;
case WPI_SET_CHAN:
@@ -3680,29 +3647,36 @@ again:
case WPI_AUTH:
/* The node must be registered in the firmware before auth */
- error = wpi_auth(sc);
+ error = wpi_auth(sc, vap);
+ WPI_UNLOCK(sc);
if (error != 0) {
device_printf(sc->sc_dev,
"%s: could not move to auth state, error %d\n",
__func__, error);
- WPI_UNLOCK(sc);
return;
}
- /* Send the auth frame now */
- sc->sc_newstate(ic, IEEE80211_S_AUTH, arg);
- break;
+ IEEE80211_LOCK(ic);
+ WPI_VAP(vap)->newstate(vap, IEEE80211_S_AUTH, arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, IEEE80211_S_AUTH, arg);
+ IEEE80211_UNLOCK(ic);
+ goto again;
case WPI_RUN:
- error = wpi_run(sc);
+ error = wpi_run(sc, vap);
+ WPI_UNLOCK(sc);
if (error != 0) {
device_printf(sc->sc_dev,
"%s: could not move to run state, error %d\n",
__func__, error);
- WPI_UNLOCK(sc);
return;
}
- sc->sc_newstate(ic, IEEE80211_S_RUN, arg);
- break;
+ IEEE80211_LOCK(ic);
+ WPI_VAP(vap)->newstate(vap, IEEE80211_S_RUN, arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, IEEE80211_S_RUN, arg);
+ IEEE80211_UNLOCK(ic);
+ goto again;
}
WPI_UNLOCK(sc);
@@ -3800,9 +3774,12 @@ wpi_watchdog(void *arg)
}
}
if (sc->sc_scan_timer > 0) {
- if (--sc->sc_scan_timer == 0) {
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ if (--sc->sc_scan_timer == 0 && vap != NULL) {
device_printf(sc->sc_dev,"scan timeout\n");
- ieee80211_cancel_scan(&sc->sc_ic);
+ ieee80211_cancel_scan(vap);
wpi_queue_cmd(sc, WPI_RESTART, 0, WPI_QUEUE_CLEAR);
}
}
@@ -3814,25 +3791,25 @@ wpi_watchdog(void *arg)
#ifdef WPI_DEBUG
static const char *wpi_cmd_str(int cmd)
{
- switch(cmd) {
- case WPI_DISABLE_CMD: return "WPI_DISABLE_CMD";
- case WPI_CMD_CONFIGURE: return "WPI_CMD_CONFIGURE";
- case WPI_CMD_ASSOCIATE: return "WPI_CMD_ASSOCIATE";
- case WPI_CMD_SET_WME: return "WPI_CMD_SET_WME";
- case WPI_CMD_TSF: return "WPI_CMD_TSF";
- case WPI_CMD_ADD_NODE: return "WPI_CMD_ADD_NODE";
- case WPI_CMD_TX_DATA: return "WPI_CMD_TX_DATA";
- case WPI_CMD_MRR_SETUP: return "WPI_CMD_MRR_SETUP";
- case WPI_CMD_SET_LED: return "WPI_CMD_SET_LED";
- case WPI_CMD_SET_POWER_MODE: return "WPI_CMD_SET_POWER_MODE";
- case WPI_CMD_SCAN: return "WPI_CMD_SCAN";
- case WPI_CMD_SET_BEACON:return "WPI_CMD_SET_BEACON";
- case WPI_CMD_TXPOWER: return "WPI_CMD_TXPOWER";
- case WPI_CMD_BLUETOOTH: return "WPI_CMD_BLUETOOTH";
-
- default:
+ switch (cmd) {
+ case WPI_DISABLE_CMD: return "WPI_DISABLE_CMD";
+ case WPI_CMD_CONFIGURE: return "WPI_CMD_CONFIGURE";
+ case WPI_CMD_ASSOCIATE: return "WPI_CMD_ASSOCIATE";
+ case WPI_CMD_SET_WME: return "WPI_CMD_SET_WME";
+ case WPI_CMD_TSF: return "WPI_CMD_TSF";
+ case WPI_CMD_ADD_NODE: return "WPI_CMD_ADD_NODE";
+ case WPI_CMD_TX_DATA: return "WPI_CMD_TX_DATA";
+ case WPI_CMD_MRR_SETUP: return "WPI_CMD_MRR_SETUP";
+ case WPI_CMD_SET_LED: return "WPI_CMD_SET_LED";
+ case WPI_CMD_SET_POWER_MODE: return "WPI_CMD_SET_POWER_MODE";
+ case WPI_CMD_SCAN: return "WPI_CMD_SCAN";
+ case WPI_CMD_SET_BEACON:return "WPI_CMD_SET_BEACON";
+ case WPI_CMD_TXPOWER: return "WPI_CMD_TXPOWER";
+ case WPI_CMD_BLUETOOTH: return "WPI_CMD_BLUETOOTH";
+
+ default:
KASSERT(1, ("Unknown Command: %d\n", cmd));
- return "UNKNOWN CMD"; // Make the compiler happy
+ return "UNKNOWN CMD"; /* Make the compiler happy */
}
}
#endif
diff --git a/sys/dev/wpi/if_wpivar.h b/sys/dev/wpi/if_wpivar.h
index 39020df..1436592 100644
--- a/sys/dev/wpi/if_wpivar.h
+++ b/sys/dev/wpi/if_wpivar.h
@@ -110,6 +110,7 @@ struct wpi_node {
struct ieee80211_node ni; /* must be the first */
struct ieee80211_amrr_node amn;
};
+#define WPI_NODE(ni) ((struct wpi_node *)(ni))
struct wpi_power_sample {
uint8_t index;
@@ -118,26 +119,26 @@ struct wpi_power_sample {
struct wpi_power_group {
#define WPI_SAMPLES_COUNT 5
- struct wpi_power_sample samples[WPI_SAMPLES_COUNT];
- uint8_t chan;
- int8_t maxpwr;
- int16_t temp;
+ struct wpi_power_sample samples[WPI_SAMPLES_COUNT];
+ uint8_t chan;
+ int8_t maxpwr;
+ int16_t temp;
};
-struct wpi_softc {
- device_t sc_dev;
- struct ifnet *sc_ifp;
+struct wpi_vap {
+ struct ieee80211vap vap;
+ struct ieee80211_amrr amrr;
- /* net80211 driver specifics */
- struct ieee80211com sc_ic;
- int (*sc_newstate)(struct ieee80211com *,
+ int (*newstate)(struct ieee80211vap *,
enum ieee80211_state, int);
- unsigned long maxdwell; /* Max dwell time whilst scanning */
+};
+#define WPI_VAP(vap) ((struct wpi_vap *)(vap))
+struct wpi_softc {
+ device_t sc_dev;
+ struct ifnet *sc_ifp;
struct mtx sc_mtx;
- struct ieee80211_amrr amrr;
-
/* Flags indicating the current state the driver
* expects the hardware to be in
*/
@@ -212,15 +213,15 @@ struct wpi_softc {
int sc_cmd_next; /* last queued scan task */
struct mtx sc_cmdlock;
- /* Task queues used to control the driver */
- struct taskqueue *sc_tq; /* Main command task queue */
- struct taskqueue *sc_tq2;/* firmware reset task queue */
+ /* Task queues used to control the driver */
+ struct taskqueue *sc_tq; /* Main command task queue */
+ struct taskqueue *sc_tq2; /* firmware reset task queue */
- /* Tasks used by the driver */
- struct task sc_radioontask; /* enable rf transmitter task*/
- struct task sc_radioofftask;/* disable rf transmitter task*/
- struct task sc_opstask; /* operation handling task */
- struct task sc_restarttask; /* reset firmware task */
+ /* Tasks used by the driver */
+ struct task sc_radioontask; /* enable rf transmitter task*/
+ struct task sc_radioofftask;/* disable rf transmitter task*/
+ struct task sc_opstask; /* operation handling task */
+ struct task sc_restarttask; /* reset firmware task */
/* Eeprom info */
uint8_t cap;
@@ -228,7 +229,7 @@ struct wpi_softc {
uint8_t type;
struct wpi_power_group groups[WPI_POWER_GROUPS_COUNT];
int8_t maxpwr[IEEE80211_CHAN_MAX];
- char domain[4]; //reglatory domain //XXX
+ char domain[4]; /*reglatory domain XXX */
};
#define WPI_LOCK_INIT(_sc) \
mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
@@ -236,7 +237,6 @@ struct wpi_softc {
#define WPI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
#define WPI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
#define WPI_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
-#define WPI_LOCK_OWNED(_sc) mtx_owned(&(_sc)->sc_mtx)
#define WPI_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
#define WPI_CMD_LOCK_INIT(_sc) \
diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC
index 36d68d1..2027692 100644
--- a/sys/i386/conf/GENERIC
+++ b/sys/i386/conf/GENERIC
@@ -253,8 +253,6 @@ device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
device wlan_amrr # AMRR transmit rate control algorithm
-device wlan_scan_ap # 802.11 AP mode scanning
-device wlan_scan_sta # 802.11 STA mode scanning
device an # Aironet 4500/4800 802.11 wireless NICs.
device ath # Atheros pci/cardbus NIC's
device ath_hal # Atheros HAL (Hardware Access Layer)
diff --git a/sys/mips/conf/IDT b/sys/mips/conf/IDT
index 8b63260..73af1ee 100644
--- a/sys/mips/conf/IDT
+++ b/sys/mips/conf/IDT
@@ -46,8 +46,6 @@ device md
device wlan # 802.11 support
device wlan_wep # 802.11 WEP support
device wlan_tkip # 802.11 TKIP support
-device wlan_scan_ap #802.11 AP mode scanning
-device wlan_scan_sta #802.11 STA mode scanning
device ath # Atheros pci/cardbus NIC's
device ath_hal # Atheros HAL (Hardware Access Layer)
device ath_rate_sample # SampleRate tx rate control for ath
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index b5de9b8..46b5eb0 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -311,8 +311,7 @@ SUBDIR= ${_3dfx} \
wlan_acl \
wlan_amrr \
wlan_ccmp \
- wlan_scan_ap \
- wlan_scan_sta \
+ wlan_rssadapt \
wlan_tkip \
wlan_wep \
wlan_xauth \
diff --git a/sys/modules/ath_rate_amrr/Makefile b/sys/modules/ath_rate_amrr/Makefile
index 5523815..0405da2 100644
--- a/sys/modules/ath_rate_amrr/Makefile
+++ b/sys/modules/ath_rate_amrr/Makefile
@@ -40,7 +40,7 @@
KMOD= ath_rate
SRCS= amrr.c
-SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ah.h
+SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ah.h opt_wlan.h
HAL= ${.CURDIR}/../../contrib/dev/ath
CFLAGS+= -I. -I${.CURDIR}/../../dev/ath -I${HAL}
@@ -56,7 +56,13 @@ ATH_MODULE_ARCH=powerpc-be
ATH_MODULE_ARCH=${MACHINE_ARCH}
.endif
+.if !defined(KERNBUILDDIR)
opt_ah.h: ${HAL}/public/${ATH_MODULE_ARCH}-elf.opt_ah.h
cp ${HAL}/public/${ATH_MODULE_ARCH}-elf.opt_ah.h ${.TARGET}
+opt_wlan.h:
+# echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+ echo > opt_wlan.h
+.endif
+
.include <bsd.kmod.mk>
diff --git a/sys/modules/ath_rate_onoe/Makefile b/sys/modules/ath_rate_onoe/Makefile
index c111e26..2edd73f 100644
--- a/sys/modules/ath_rate_onoe/Makefile
+++ b/sys/modules/ath_rate_onoe/Makefile
@@ -40,7 +40,7 @@
KMOD= ath_rate
SRCS= onoe.c
-SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ah.h
+SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ah.h opt_wlan.h
HAL= ${.CURDIR}/../../contrib/dev/ath
CFLAGS+= -I. -I${.CURDIR}/../../dev/ath -I${HAL}
@@ -56,7 +56,13 @@ ATH_MODULE_ARCH=powerpc-be
ATH_MODULE_ARCH=${MACHINE_ARCH}
.endif
+.if !defined(KERNBUILDDIR)
opt_ah.h: ${HAL}/public/${ATH_MODULE_ARCH}-elf.opt_ah.h
cp ${HAL}/public/${ATH_MODULE_ARCH}-elf.opt_ah.h ${.TARGET}
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+# echo > opt_wlan.h
+.endif
+
.include <bsd.kmod.mk>
diff --git a/sys/modules/ath_rate_sample/Makefile b/sys/modules/ath_rate_sample/Makefile
index fc91e2b..4a13f23 100644
--- a/sys/modules/ath_rate_sample/Makefile
+++ b/sys/modules/ath_rate_sample/Makefile
@@ -40,7 +40,7 @@
KMOD= ath_rate
SRCS= sample.c
-SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ah.h
+SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ah.h opt_wlan.h
HAL= ${.CURDIR}/../../contrib/dev/ath
CFLAGS+= -I. -I${.CURDIR}/../../dev/ath -I${HAL}
@@ -56,7 +56,13 @@ ATH_MODULE_ARCH=powerpc-be
ATH_MODULE_ARCH=${MACHINE_ARCH}
.endif
+.if !defined(KERNBUILDDIR)
opt_ah.h: ${HAL}/public/${ATH_MODULE_ARCH}-elf.opt_ah.h
cp ${HAL}/public/${ATH_MODULE_ARCH}-elf.opt_ah.h ${.TARGET}
+opt_wlan.h:
+# echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+ echo > opt_wlan.h
+.endif
+
.include <bsd.kmod.mk>
diff --git a/sys/modules/malo/Makefile b/sys/modules/malo/Makefile
index c0e88c5..7886102 100644
--- a/sys/modules/malo/Makefile
+++ b/sys/modules/malo/Makefile
@@ -3,6 +3,10 @@
.PATH: ${.CURDIR}/../../dev/malo
KMOD = if_malo
-SRCS = if_malo.c if_malohal.c if_malo_pci.c device_if.h bus_if.h pci_if.h
+SRCS = if_malo.c if_malohal.c if_malo_pci.c
+SRCS+= device_if.h bus_if.h pci_if.h opt_malo.h
+
+opt_malo.h:
+ echo '#define MALO_DEBUG 1'> $@
.include <bsd.kmod.mk>
diff --git a/sys/modules/ral/Makefile b/sys/modules/ral/Makefile
index 2ec55bf..6a3b269 100644
--- a/sys/modules/ral/Makefile
+++ b/sys/modules/ral/Makefile
@@ -2,8 +2,8 @@
.PATH: ${.CURDIR}/../../dev/ral
-KMOD = if_ral
-SRCS = rt2560.c rt2661.c if_ralrate.c if_ral_pci.c \
- device_if.h bus_if.h pci_if.h
+KMOD= if_ral
+SRCS= rt2560.c rt2661.c if_ral_pci.c
+SRCS+= device_if.h bus_if.h pci_if.h
.include <bsd.kmod.mk>
diff --git a/sys/modules/ralfw/Makefile b/sys/modules/ralfw/Makefile
new file mode 100644
index 0000000..3db53ad
--- /dev/null
+++ b/sys/modules/ralfw/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= rt2561 rt2561s rt2661 rt2860
+
+.include <bsd.subdir.mk>
diff --git a/sys/modules/ralfw/Makefile.inc b/sys/modules/ralfw/Makefile.inc
new file mode 100644
index 0000000..2dc6b47
--- /dev/null
+++ b/sys/modules/ralfw/Makefile.inc
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+#
+# Common rules for building firmware. Note this gets auto-included
+# by the subdir Makefile's as a consequence of included bsd.kmod.mk.
+#
+KMOD= ${IMG}fw
+_FIRM= ${IMG}.fw
+
+CLEANFILES+= ${_FIRM}
+
+FIRMWS= ${_FIRM}:${KMOD}
+
+${_FIRM}: ${.CURDIR}/../../../contrib/dev/ral/${_FIRM}.uu
+ uudecode -p $? > ${.TARGET}
diff --git a/sys/modules/ralfw/rt2561/Makefile b/sys/modules/ralfw/rt2561/Makefile
new file mode 100644
index 0000000..71044a6
--- /dev/null
+++ b/sys/modules/ralfw/rt2561/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+IMG= rt2561
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/ralfw/rt2561s/Makefile b/sys/modules/ralfw/rt2561s/Makefile
new file mode 100644
index 0000000..40c6bc1
--- /dev/null
+++ b/sys/modules/ralfw/rt2561s/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+IMG= rt2561s
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/ralfw/rt2661/Makefile b/sys/modules/ralfw/rt2661/Makefile
new file mode 100644
index 0000000..b372786
--- /dev/null
+++ b/sys/modules/ralfw/rt2661/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+IMG= rt2661
+
+.include <bsd.kmod.mk>
+
diff --git a/sys/modules/wlan/Makefile b/sys/modules/wlan/Makefile
index 5712218..b6f10e3 100644
--- a/sys/modules/wlan/Makefile
+++ b/sys/modules/wlan/Makefile
@@ -3,16 +3,18 @@
.PATH: ${.CURDIR}/../../net80211
KMOD= wlan
-SRCS= ieee80211.c ieee80211_crypto.c ieee80211_crypto_none.c \
+SRCS= ieee80211.c ieee80211_crypto.c ieee80211_crypto_none.c ieee80211_dfs.c \
ieee80211_freebsd.c ieee80211_input.c ieee80211_ioctl.c \
- ieee80211_node.c ieee80211_output.c ieee80211_power.c \
- ieee80211_proto.c ieee80211_scan.c ieee80211_regdomain.c \
- ieee80211_ht.c
-SRCS+= bus_if.h device_if.h opt_compat.h opt_inet.h opt_ipx.h
+ ieee80211_node.c ieee80211_output.c ieee80211_phy.c ieee80211_power.c \
+ ieee80211_proto.c ieee80211_scan.c ieee80211_scan_sta.c \
+ ieee80211_regdomain.c ieee80211_ht.c \
+ ieee80211_adhoc.c ieee80211_hostap.c ieee80211_monitor.c \
+ ieee80211_sta.c ieee80211_wds.c
+SRCS+= bus_if.h device_if.h opt_inet.h opt_ipx.h opt_wlan.h
.if !defined(KERNBUILDDIR)
-opt_compat.h:
- echo "#define COMPAT_FREEBSD6 1" > ${.TARGET}
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
opt_inet.h:
echo "#define INET 1" > opt_inet.h
diff --git a/sys/modules/wlan_acl/Makefile b/sys/modules/wlan_acl/Makefile
index aa71de2..5922859 100644
--- a/sys/modules/wlan_acl/Makefile
+++ b/sys/modules/wlan_acl/Makefile
@@ -4,5 +4,11 @@
KMOD= wlan_acl
SRCS= ieee80211_acl.c
+SRCS+= opt_wlan.h
+
+.if !defined(KERNBUILDDIR)
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+.endif
.include <bsd.kmod.mk>
diff --git a/sys/modules/wlan_amrr/Makefile b/sys/modules/wlan_amrr/Makefile
index ac7b5ce..481d124 100644
--- a/sys/modules/wlan_amrr/Makefile
+++ b/sys/modules/wlan_amrr/Makefile
@@ -4,5 +4,11 @@
KMOD= wlan_amrr
SRCS= ieee80211_amrr.c
+SRCS+= opt_wlan.h
+
+.if !defined(KERNBUILDDIR)
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+.endif
.include <bsd.kmod.mk>
diff --git a/sys/modules/wlan_ccmp/Makefile b/sys/modules/wlan_ccmp/Makefile
index 06bafce..bbf8f62 100644
--- a/sys/modules/wlan_ccmp/Makefile
+++ b/sys/modules/wlan_ccmp/Makefile
@@ -6,5 +6,11 @@
KMOD= wlan_ccmp
SRCS= ieee80211_crypto_ccmp.c
SRCS+= rijndael-alg-fst.c rijndael-api.c
+SRCS+= opt_wlan.h
+
+.if !defined(KERNBUILDDIR)
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+.endif
.include <bsd.kmod.mk>
diff --git a/sys/modules/wlan_rssadapt/Makefile b/sys/modules/wlan_rssadapt/Makefile
new file mode 100644
index 0000000..2285afc
--- /dev/null
+++ b/sys/modules/wlan_rssadapt/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../net80211
+
+KMOD= wlan_rssadapt
+SRCS= ieee80211_rssadapt.c
+SRCS+= opt_wlan.h
+
+.if !defined(KERNBUILDDIR)
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+.endif
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/wlan_scan_ap/Makefile b/sys/modules/wlan_scan_ap/Makefile
deleted file mode 100644
index 117a6bc..0000000
--- a/sys/modules/wlan_scan_ap/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-# $FreeBSD$
-
-.PATH: ${.CURDIR}/../../net80211
-
-KMOD= wlan_scan_ap
-SRCS= ieee80211_scan_ap.c
-
-.include <bsd.kmod.mk>
diff --git a/sys/modules/wlan_scan_sta/Makefile b/sys/modules/wlan_scan_sta/Makefile
deleted file mode 100644
index 8d96875..0000000
--- a/sys/modules/wlan_scan_sta/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-# $FreeBSD$
-
-.PATH: ${.CURDIR}/../../net80211
-
-KMOD= wlan_scan_sta
-SRCS= ieee80211_scan_sta.c
-
-.include <bsd.kmod.mk>
diff --git a/sys/modules/wlan_tkip/Makefile b/sys/modules/wlan_tkip/Makefile
index 1449b78..80c19f8 100644
--- a/sys/modules/wlan_tkip/Makefile
+++ b/sys/modules/wlan_tkip/Makefile
@@ -4,5 +4,11 @@
KMOD= wlan_tkip
SRCS= ieee80211_crypto_tkip.c
+SRCS+= opt_wlan.h
+
+.if !defined(KERNBUILDDIR)
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+.endif
.include <bsd.kmod.mk>
diff --git a/sys/modules/wlan_wep/Makefile b/sys/modules/wlan_wep/Makefile
index 88ad322..3a5c6dc 100644
--- a/sys/modules/wlan_wep/Makefile
+++ b/sys/modules/wlan_wep/Makefile
@@ -4,5 +4,11 @@
KMOD= wlan_wep
SRCS= ieee80211_crypto_wep.c
+SRCS+= opt_wlan.h
+
+.if !defined(KERNBUILDDIR)
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+.endif
.include <bsd.kmod.mk>
diff --git a/sys/modules/wlan_xauth/Makefile b/sys/modules/wlan_xauth/Makefile
index fba6f8a..1877fd1 100644
--- a/sys/modules/wlan_xauth/Makefile
+++ b/sys/modules/wlan_xauth/Makefile
@@ -4,5 +4,11 @@
KMOD= wlan_xauth
SRCS= ieee80211_xauth.c
+SRCS+= opt_wlan.h
+
+.if !defined(KERNBUILDDIR)
+opt_wlan.h:
+ echo "#define IEEE80211_DEBUG 1" > opt_wlan.h
+.endif
.include <bsd.kmod.mk>
diff --git a/sys/net80211/_ieee80211.h b/sys/net80211/_ieee80211.h
index 9f50e3c4..d41659f 100644
--- a/sys/net80211/_ieee80211.h
+++ b/sys/net80211/_ieee80211.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,16 +28,31 @@
#ifndef _NET80211__IEEE80211_H_
#define _NET80211__IEEE80211_H_
+/*
+ * 802.11 implementation definitions.
+ *
+ * NB: this file is used by applications.
+ */
+
+/*
+ * PHY type; mostly used to identify FH phys.
+ */
enum ieee80211_phytype {
IEEE80211_T_DS, /* direct sequence spread spectrum */
IEEE80211_T_FH, /* frequency hopping */
IEEE80211_T_OFDM, /* frequency division multiplexing */
IEEE80211_T_TURBO, /* high rate OFDM, aka turbo mode */
- IEEE80211_T_HT, /* high throughput, full GI */
+ IEEE80211_T_HT, /* high throughput */
};
#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */
-/* XXX not really a mode; there are really multiple PHY's */
+/*
+ * PHY mode; this is not really a mode as multi-mode devices
+ * have multiple PHY's. Mode is mostly used as a shorthand
+ * for constraining which channels to consider in setting up
+ * operation. Modes used to be used more extensively when
+ * channels were identified as IEEE channel numbers.
+ */
enum ieee80211_phymode {
IEEE80211_MODE_AUTO = 0, /* autoselect */
IEEE80211_MODE_11A = 1, /* 5GHz, OFDM */
@@ -52,13 +67,18 @@ enum ieee80211_phymode {
};
#define IEEE80211_MODE_MAX (IEEE80211_MODE_11NG+1)
+/*
+ * Operating mode. Devices do not necessarily support
+ * all modes; they indicate which are supported in their
+ * capabilities.
+ */
enum ieee80211_opmode {
- IEEE80211_M_STA = 1, /* infrastructure station */
IEEE80211_M_IBSS = 0, /* IBSS (adhoc) station */
+ IEEE80211_M_STA = 1, /* infrastructure station */
+ IEEE80211_M_WDS = 2, /* WDS link */
IEEE80211_M_AHDEMO = 3, /* Old lucent compatible adhoc demo */
- IEEE80211_M_HOSTAP = 6, /* Software Access Point */
- IEEE80211_M_MONITOR = 8, /* Monitor mode */
- IEEE80211_M_WDS = 2 /* WDS link */
+ IEEE80211_M_HOSTAP = 4, /* Software Access Point */
+ IEEE80211_M_MONITOR = 5, /* Monitor mode */
};
#define IEEE80211_OPMODE_MAX (IEEE80211_M_MONITOR+1)
@@ -72,7 +92,11 @@ enum ieee80211_protmode {
};
/*
- * Authentication mode.
+ * Authentication mode. The open and shared key authentication
+ * modes are implemented within the 802.11 layer. 802.1x and
+ * WPA/802.11i are implemented in user mode by setting the
+ * 802.11 layer into IEEE80211_AUTH_8021X and deferring
+ * authentication to user space programs.
*/
enum ieee80211_authmode {
IEEE80211_AUTH_NONE = 0,
@@ -265,18 +289,28 @@ struct ieee80211_channel {
#define IEEE80211_NONQOS_TID WME_NUM_TID /* index for non-QoS sta */
/*
+ * The 802.11 spec says at most 2007 stations may be
+ * associated at once. For most AP's this is way more
+ * than is feasible so we use a default of 128. This
+ * number may be overridden by the driver and/or by
+ * user configuration but may not be less than IEEE80211_AID_MIN.
+ */
+#define IEEE80211_AID_DEF 128
+#define IEEE80211_AID_MIN 16
+
+/*
* 802.11 rate set.
*/
#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */
#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */
struct ieee80211_rateset {
- uint8_t rs_nrates;
- uint8_t rs_rates[IEEE80211_RATE_MAXSIZE];
+ uint8_t rs_nrates;
+ uint8_t rs_rates[IEEE80211_RATE_MAXSIZE];
};
/*
- * 802.11n variant of ieee80211_rateset. Instead
+ * 802.11n variant of ieee80211_rateset. Instead of
* legacy rates the entries are MCS rates. We define
* the structure such that it can be used interchangeably
* with an ieee80211_rateset (modulo structure size).
@@ -284,27 +318,58 @@ struct ieee80211_rateset {
#define IEEE80211_HTRATE_MAXSIZE 127
struct ieee80211_htrateset {
- uint8_t rs_nrates;
- uint8_t rs_rates[IEEE80211_HTRATE_MAXSIZE];
+ uint8_t rs_nrates;
+ uint8_t rs_rates[IEEE80211_HTRATE_MAXSIZE];
};
#define IEEE80211_RATE_MCS 0x80
/*
- * Roaming state visible to user space. There are two
- * thresholds that control whether roaming is considered;
- * when either is exceeded the 802.11 layer will check
- * the scan cache for another AP. If the cache is stale
- * then a scan may be triggered.
+ * Per-mode transmit parameters/controls visible to user space.
+ * These can be used to set fixed transmit rate for all operating
+ * modes or on a per-client basis according to the capabilities
+ * of the client (e.g. an 11b client associated to an 11g ap).
+ *
+ * MCS are distinguished from legacy rates by or'ing in 0x80.
+ */
+struct ieee80211_txparam {
+ uint8_t ucastrate; /* ucast data rate (legacy/MCS|0x80) */
+ uint8_t mgmtrate; /* mgmt frame rate (legacy/MCS|0x80) */
+ uint8_t mcastrate; /* multicast rate (legacy/MCS|0x80) */
+ uint8_t maxretry; /* max unicast data retry count */
+};
+
+/*
+ * Per-mode roaming state visible to user space. There are two
+ * thresholds that control whether roaming is considered; when
+ * either is exceeded the 802.11 layer will check the scan cache
+ * for another AP. If the cache is stale then a scan may be
+ * triggered.
+ */
+struct ieee80211_roamparam {
+ int8_t rssi; /* rssi thresh (.5 dBm) */
+ uint8_t rate; /* tx rate thresh (.5 Mb/s or MCS) */
+ uint16_t pad; /* reserve */
+};
+
+/*
+ * Regulatory Information.
+ */
+struct ieee80211_regdomain {
+ uint16_t regdomain; /* SKU */
+ uint16_t country; /* ISO country code */
+ uint8_t location; /* I (indoor), O (outdoor), other */
+ uint8_t ecm; /* Extended Channel Mode */
+ char isocc[2]; /* country code string */
+ short pad[2];
+};
+
+/*
+ * MIMO antenna/radio state.
*/
-struct ieee80211_roam {
- int8_t rssi11a; /* rssi thresh for 11a bss */
- int8_t rssi11b; /* for 11g sta in 11b bss */
- int8_t rssi11bOnly; /* for 11b sta */
- uint8_t pad1;
- uint8_t rate11a; /* rate thresh for 11a bss */
- uint8_t rate11b; /* for 11g sta in 11b bss */
- uint8_t rate11bOnly; /* for 11b sta */
- uint8_t pad2;
+struct ieee80211_mimo_info {
+ int8_t rssi[3]; /* per-antenna rssi */
+ int8_t noise[3]; /* per-antenna noise floor */
+ uint32_t evm[3]; /* EVM data */
};
#endif /* _NET80211__IEEE80211_H_ */
diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c
index dbeb7a3..952f420 100644
--- a/sys/net80211/ieee80211.c
+++ b/sys/net80211/ieee80211.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 generic handler
*/
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -38,10 +39,13 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <net/if.h>
+#include <net/if_dl.h>
#include <net/if_media.h>
+#include <net/if_types.h>
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
#include <net/bpf.h>
@@ -57,6 +61,20 @@ const char *ieee80211_phymode_name[] = {
"11na", /* IEEE80211_MODE_11NA */
"11ng", /* IEEE80211_MODE_11NG */
};
+static const uint8_t ieee80211broadcastaddr[IEEE80211_ADDR_LEN] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+static void ieee80211_syncflag_locked(struct ieee80211com *ic, int flag);
+static void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag);
+static int ieee80211_media_setup(struct ieee80211com *ic,
+ struct ifmedia *media, int caps, int addsta,
+ ifm_change_cb_t media_change, ifm_stat_cb_t media_stat);
+static void ieee80211com_media_status(struct ifnet *, struct ifmediareq *);
+static int ieee80211com_media_change(struct ifnet *);
+static int media_status(enum ieee80211_opmode,
+ const struct ieee80211_channel *);
+
+MALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state");
/*
* Default supported rates for 802.11 operation (in IEEE .5Mb units).
@@ -75,66 +93,6 @@ static const struct ieee80211_rateset ieee80211_rateset_11g =
{ 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } };
#undef B
-static int media_status(enum ieee80211_opmode ,
- const struct ieee80211_channel *);
-
-/* list of all instances */
-SLIST_HEAD(ieee80211_list, ieee80211com);
-static struct ieee80211_list ieee80211_list =
- SLIST_HEAD_INITIALIZER(ieee80211_list);
-static uint8_t ieee80211_vapmap[32]; /* enough for 256 */
-static struct mtx ieee80211_vap_mtx;
-MTX_SYSINIT(ieee80211, &ieee80211_vap_mtx, "net80211 instances", MTX_DEF);
-
-static void
-ieee80211_add_vap(struct ieee80211com *ic)
-{
-#define N(a) (sizeof(a)/sizeof(a[0]))
- int i;
- uint8_t b;
-
- mtx_lock(&ieee80211_vap_mtx);
- ic->ic_vap = 0;
- for (i = 0; i < N(ieee80211_vapmap) && ieee80211_vapmap[i] == 0xff; i++)
- ic->ic_vap += NBBY;
- if (i == N(ieee80211_vapmap))
- panic("vap table full");
- for (b = ieee80211_vapmap[i]; b & 1; b >>= 1)
- ic->ic_vap++;
- setbit(ieee80211_vapmap, ic->ic_vap);
- SLIST_INSERT_HEAD(&ieee80211_list, ic, ic_next);
- mtx_unlock(&ieee80211_vap_mtx);
-#undef N
-}
-
-static void
-ieee80211_remove_vap(struct ieee80211com *ic)
-{
- mtx_lock(&ieee80211_vap_mtx);
- SLIST_REMOVE(&ieee80211_list, ic, ieee80211com, ic_next);
- KASSERT(ic->ic_vap < sizeof(ieee80211_vapmap)*NBBY,
- ("invalid vap id %d", ic->ic_vap));
- KASSERT(isset(ieee80211_vapmap, ic->ic_vap),
- ("vap id %d not allocated", ic->ic_vap));
- clrbit(ieee80211_vapmap, ic->ic_vap);
- mtx_unlock(&ieee80211_vap_mtx);
-}
-
-/*
- * Default reset method for use with the ioctl support. This
- * method is invoked after any state change in the 802.11
- * layer that should be propagated to the hardware but not
- * require re-initialization of the 802.11 state machine (e.g
- * rescanning for an ap). We always return ENETRESET which
- * should cause the driver to re-initialize the device. Drivers
- * can override this method to implement more optimized support.
- */
-static int
-ieee80211_default_reset(struct ifnet *ifp)
-{
- return ENETRESET;
-}
-
/*
* Fill in 802.11 available channel set, mark
* all available channels as active, and pick
@@ -153,6 +111,7 @@ ieee80211_chan_init(struct ieee80211com *ic)
KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX,
("invalid number of channels specified: %u", ic->ic_nchans));
memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail));
+ memset(ic->ic_modecaps, 0, sizeof(ic->ic_modecaps));
setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO);
for (i = 0; i < ic->ic_nchans; i++) {
c = &ic->ic_channels[i];
@@ -186,9 +145,13 @@ ieee80211_chan_init(struct ieee80211com *ic)
memcpy(ic->ic_chan_active, ic->ic_chan_avail,
sizeof(ic->ic_chan_avail));
- ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */
+ /* sort channel table to allow lookup optimizations */
+ ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans);
+
+ /* invalidate any previous state */
ic->ic_bsschan = IEEE80211_CHAN_ANYC;
ic->ic_prevchan = NULL;
+ ic->ic_csa_newchan = NULL;
/* arbitrarily pick the first channel */
ic->ic_curchan = &ic->ic_channels[0];
@@ -206,54 +169,44 @@ ieee80211_chan_init(struct ieee80211com *ic)
#undef DEFAULTRATES
}
+static void
+null_update_mcast(struct ifnet *ifp)
+{
+ if_printf(ifp, "need multicast update callback\n");
+}
+
+static void
+null_update_promisc(struct ifnet *ifp)
+{
+ if_printf(ifp, "need promiscuous mode update callback\n");
+}
+
+/*
+ * Attach/setup the common net80211 state. Called by
+ * the driver on attach to prior to creating any vap's.
+ */
void
ieee80211_ifattach(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
+ struct sockaddr_dl *sdl;
+ struct ifaddr *ifa;
- ether_ifattach(ifp, ic->ic_myaddr);
- ifp->if_output = ieee80211_output;
-
- bpfattach2(ifp, DLT_IEEE802_11,
- sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf);
-
- /* override the 802.3 setting */
- ifp->if_hdrlen = ic->ic_headroom
- + sizeof(struct ieee80211_qosframe_addr4)
- + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN
- + IEEE80211_WEP_EXTIVLEN;
- /* XXX no way to recalculate on ifdetach */
- if (ALIGN(ifp->if_hdrlen) > max_linkhdr) {
- /* XXX sanity check... */
- max_linkhdr = ALIGN(ifp->if_hdrlen);
- max_hdr = max_linkhdr + max_protohdr;
- max_datalen = MHLEN - max_hdr;
- }
+ KASSERT(ifp->if_type == IFT_IEEE80211, ("if_type %d", ifp->if_type));
+ IEEE80211_LOCK_INIT(ic, "ieee80211com");
+ TAILQ_INIT(&ic->ic_vaps);
/*
* Fill in 802.11 available channel set, mark all
* available channels as active, and pick a default
* channel if not already specified.
*/
- ieee80211_chan_init(ic);
+ ieee80211_media_init(ic);
- if (ic->ic_caps & IEEE80211_C_BGSCAN) /* enable if capable */
- ic->ic_flags |= IEEE80211_F_BGSCAN;
-#if 0
- /* XXX not until WME+WPA issues resolved */
- if (ic->ic_caps & IEEE80211_C_WME) /* enable if capable */
- ic->ic_flags |= IEEE80211_F_WME;
-#endif
- if (ic->ic_caps & IEEE80211_C_BURST)
- ic->ic_flags |= IEEE80211_F_BURST;
- ic->ic_flags |= IEEE80211_F_DOTH; /* XXX out of caps, just ena */
+ ic->ic_update_mcast = null_update_mcast;
+ ic->ic_update_promisc = null_update_promisc;
ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT;
- ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT;
- ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT;
- IEEE80211_LOCK_INIT(ic, "ieee80211com");
- IEEE80211_BEACON_LOCK_INIT(ic, "beacon");
-
ic->ic_lintval = ic->ic_bintval;
ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX;
@@ -263,30 +216,42 @@ ieee80211_ifattach(struct ieee80211com *ic)
ieee80211_proto_attach(ic);
ieee80211_ht_attach(ic);
ieee80211_scan_attach(ic);
-
- ieee80211_add_vap(ic);
-
- ieee80211_sysctl_attach(ic); /* NB: requires ic_vap */
-
- /*
- * Install a default reset method for the ioctl support.
- * The driver is expected to fill this in before calling us.
- */
- if (ic->ic_reset == NULL)
- ic->ic_reset = ieee80211_default_reset;
-
- KASSERT(ifp->if_llsoftc == NULL, ("oops, hosed"));
- ifp->if_llsoftc = ic;
+ ieee80211_regdomain_attach(ic);
+
+ ieee80211_sysctl_attach(ic);
+
+ ifp->if_addrlen = IEEE80211_ADDR_LEN;
+ ifp->if_hdrlen = 0;
+ if_attach(ifp);
+ ifp->if_mtu = IEEE80211_MTU_MAX;
+ ifp->if_broadcastaddr = ieee80211broadcastaddr;
+
+ ifa = ifaddr_byindex(ifp->if_index);
+ KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__));
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ sdl->sdl_type = IFT_ETHER; /* XXX IFT_IEEE80211? */
+ sdl->sdl_alen = IEEE80211_ADDR_LEN;
+ IEEE80211_ADDR_COPY(LLADDR(sdl), ic->ic_myaddr);
}
+/*
+ * Detach net80211 state on device detach. Tear down
+ * all vap's and reclaim all common state prior to the
+ * device state going away. Note we may call back into
+ * driver; it must be prepared for this.
+ */
void
ieee80211_ifdetach(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap;
- ieee80211_remove_vap(ic);
+ /* XXX ieee80211_stop_all? */
+ while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL)
+ ieee80211_vap_destroy(vap);
ieee80211_sysctl_detach(ic);
+ ieee80211_regdomain_detach(ic);
ieee80211_scan_detach(ic);
ieee80211_ht_detach(ic);
/* NB: must be called before ieee80211_node_detach */
@@ -297,10 +262,386 @@ ieee80211_ifdetach(struct ieee80211com *ic)
ifmedia_removeall(&ic->ic_media);
IEEE80211_LOCK_DESTROY(ic);
- IEEE80211_BEACON_LOCK_DESTROY(ic);
+ if_detach(ifp);
+}
+
+/*
+ * Default reset method for use with the ioctl support. This
+ * method is invoked after any state change in the 802.11
+ * layer that should be propagated to the hardware but not
+ * require re-initialization of the 802.11 state machine (e.g
+ * rescanning for an ap). We always return ENETRESET which
+ * should cause the driver to re-initialize the device. Drivers
+ * can override this method to implement more optimized support.
+ */
+static int
+default_reset(struct ieee80211vap *vap, u_long cmd)
+{
+ return ENETRESET;
+}
+
+/*
+ * Prepare a vap for use. Drivers use this call to
+ * setup net80211 state in new vap's prior attaching
+ * them with ieee80211_vap_attach (below).
+ */
+int
+ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
+{
+#define IEEE80211_C_OPMODE \
+ (IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | IEEE80211_C_AHDEMO | \
+ IEEE80211_C_MONITOR | IEEE80211_C_WDS)
+ struct ifnet *ifp;
+
+ ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ if_printf(ic->ic_ifp, "%s: unable to allocate ifnet\n",
+ __func__);
+ return ENOMEM;
+ }
+ if_initname(ifp, name, unit);
+ ifp->if_softc = vap; /* back pointer */
+ ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
+ ifp->if_start = ieee80211_start;
+ ifp->if_ioctl = ieee80211_ioctl;
+ ifp->if_watchdog = NULL; /* NB: no watchdog routine */
+ ifp->if_init = ieee80211_init;
+ /* NB: input+output filled in by ether_ifattach */
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ vap->iv_ifp = ifp;
+ vap->iv_ic = ic;
+ vap->iv_flags = ic->ic_flags; /* propagate common flags */
+ vap->iv_flags_ext = ic->ic_flags_ext;
+ vap->iv_flags_ven = ic->ic_flags_ven;
+ vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE;
+ vap->iv_htcaps = ic->ic_htcaps;
+ vap->iv_opmode = opmode;
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ /* auto-enable s/w beacon miss support */
+ if (flags & IEEE80211_CLONE_NOBEACONS)
+ vap->iv_flags_ext |= IEEE80211_FEXT_SWBMISS;
+ break;
+ case IEEE80211_M_IBSS:
+ vap->iv_caps |= IEEE80211_C_IBSS;
+ break;
+ case IEEE80211_M_AHDEMO:
+ vap->iv_caps |= IEEE80211_C_AHDEMO;
+ break;
+ case IEEE80211_M_HOSTAP:
+ vap->iv_caps |= IEEE80211_C_HOSTAP;
+ break;
+ case IEEE80211_M_MONITOR:
+ vap->iv_caps |= IEEE80211_C_MONITOR;
+ break;
+ case IEEE80211_M_WDS:
+ vap->iv_caps |= IEEE80211_C_WDS;
+ /*
+ * WDS links must specify the bssid of the far end.
+ * For legacy operation this is a static relationship.
+ * For non-legacy operation the station must associate
+ * and be authorized to pass traffic. Plumbing the
+ * vap to the proper node happens when the vap
+ * transitions to RUN state.
+ */
+ IEEE80211_ADDR_COPY(vap->iv_des_bssid, bssid);
+ vap->iv_flags |= IEEE80211_F_DESBSSID;
+ if (flags & IEEE80211_CLONE_WDSLEGACY)
+ vap->iv_flags_ext |= IEEE80211_FEXT_WDSLEGACY;
+ break;
+ }
+ /*
+ * Enable various functionality by default if we're
+ * capable; the driver can override us if it knows better.
+ */
+ if (vap->iv_caps & IEEE80211_C_WME)
+ vap->iv_flags |= IEEE80211_F_WME;
+ if (vap->iv_caps & IEEE80211_C_BURST)
+ vap->iv_flags |= IEEE80211_F_BURST;
+ if (vap->iv_caps & IEEE80211_C_FF)
+ vap->iv_flags |= IEEE80211_F_FF;
+ if (vap->iv_caps & IEEE80211_C_TURBOP)
+ vap->iv_flags |= IEEE80211_F_TURBOP;
+ /* NB: bg scanning only makes sense for station mode right now */
+ if (vap->iv_opmode == IEEE80211_M_STA &&
+ (vap->iv_caps & IEEE80211_C_BGSCAN))
+ vap->iv_flags |= IEEE80211_F_BGSCAN;
+ vap->iv_flags |= IEEE80211_F_DOTH; /* XXX out of caps, just ena */
+ /* XXX out of caps, just ena */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ vap->iv_flags_ext |= IEEE80211_FEXT_DFS;
+
+ vap->iv_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */
+ vap->iv_bmissthreshold = IEEE80211_HWBMISS_DEFAULT;
+ vap->iv_dtim_period = IEEE80211_DTIM_DEFAULT;
+ /*
+ * Install a default reset method for the ioctl support;
+ * the driver can override this.
+ */
+ vap->iv_reset = default_reset;
+
+ IEEE80211_ADDR_COPY(vap->iv_myaddr, macaddr);
+
+ ieee80211_sysctl_vattach(vap);
+ ieee80211_crypto_vattach(vap);
+ ieee80211_node_vattach(vap);
+ ieee80211_power_vattach(vap);
+ ieee80211_proto_vattach(vap);
+ ieee80211_ht_vattach(vap);
+ ieee80211_scan_vattach(vap);
+ ieee80211_regdomain_vattach(vap);
+
+ return 0;
+#undef IEEE80211_C_OPMODE
+}
+
+/*
+ * Activate a vap. State should have been prepared with a
+ * call to ieee80211_vap_setup and by the driver. On return
+ * from this call the vap is ready for use.
+ */
+int
+ieee80211_vap_attach(struct ieee80211vap *vap,
+ ifm_change_cb_t media_change, ifm_stat_cb_t media_stat)
+{
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifmediareq imr;
+ int maxrate;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: %s parent %s flags 0x%x flags_ext 0x%x\n",
+ __func__, ieee80211_opmode_name[vap->iv_opmode],
+ ic->ic_ifp->if_xname, vap->iv_flags, vap->iv_flags_ext);
- bpfdetach(ifp);
+ /*
+ * Do late attach work that cannot happen until after
+ * the driver has had a chance to override defaults.
+ */
+ ieee80211_node_latevattach(vap);
+ ieee80211_power_latevattach(vap);
+
+ maxrate = ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps,
+ vap->iv_opmode == IEEE80211_M_STA, media_change, media_stat);
+ ieee80211_media_status(ifp, &imr);
+ /* NB: strip explicit mode; we're actually in autoselect */
+ ifmedia_set(&vap->iv_media, imr.ifm_active &~ IFM_MMASK);
+ if (maxrate)
+ ifp->if_baudrate = IF_Mbps(maxrate);
+
+ ether_ifattach(ifp, vap->iv_myaddr);
+ /* hook output method setup by ether_ifattach */
+ vap->iv_output = ifp->if_output;
+ ifp->if_output = ieee80211_output;
+ /* NB: if_mtu set by ether_ifattach to ETHERMTU */
+ bpfattach2(ifp, DLT_IEEE802_11, ifp->if_hdrlen, &vap->iv_rawbpf);
+
+ IEEE80211_LOCK(ic);
+ TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_WME);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_PCF);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_BURST);
+ ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT);
+ ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40);
+ ieee80211_syncifflag_locked(ic, IFF_PROMISC);
+ ieee80211_syncifflag_locked(ic, IFF_ALLMULTI);
+ IEEE80211_UNLOCK(ic);
+
+ return 1;
+}
+
+/*
+ * Tear down vap state and reclaim the ifnet.
+ * The driver is assumed to have prepared for
+ * this; e.g. by turning off interrupts for the
+ * underlying device.
+ */
+void
+ieee80211_vap_detach(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n",
+ __func__, ieee80211_opmode_name[vap->iv_opmode],
+ ic->ic_ifp->if_xname);
+
+ IEEE80211_LOCK(ic);
+ /* block traffic from above */
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ /*
+ * Evil hack. Clear the backpointer from the ifnet to the
+ * vap so any requests from above will return an error or
+ * be ignored. In particular this short-circuits requests
+ * by the bridge to turn off promiscuous mode as a result
+ * of calling ether_ifdetach.
+ */
+ ifp->if_softc = NULL;
+ /*
+ * Stop the vap before detaching the ifnet. Ideally we'd
+ * do this in the other order so the ifnet is inaccessible
+ * while we cleanup internal state but that is hard.
+ */
+ ieee80211_stop_locked(vap);
+
+ /* XXX accumulate iv_stats in ic_stats? */
+ TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_WME);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_PCF);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_BURST);
+ ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT);
+ ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40);
+ ieee80211_syncifflag_locked(ic, IFF_PROMISC);
+ ieee80211_syncifflag_locked(ic, IFF_ALLMULTI);
+ IEEE80211_UNLOCK(ic);
+
+ /* XXX can't hold com lock */
+ /* NB: bpfattach is called by ether_ifdetach and claims all taps */
ether_ifdetach(ifp);
+
+ ifmedia_removeall(&vap->iv_media);
+
+ ieee80211_regdomain_vdetach(vap);
+ ieee80211_scan_vdetach(vap);
+ ieee80211_ht_vdetach(vap);
+ /* NB: must be before ieee80211_node_vdetach */
+ ieee80211_proto_vdetach(vap);
+ ieee80211_crypto_vdetach(vap);
+ ieee80211_power_vdetach(vap);
+ ieee80211_node_vdetach(vap);
+ ieee80211_sysctl_vdetach(vap);
+}
+
+/*
+ * Synchronize flag bit state in the parent ifnet structure
+ * according to the state of all vap ifnet's. This is used,
+ * for example, to handle IFF_PROMISC and IFF_ALLMULTI.
+ */
+void
+ieee80211_syncifflag_locked(struct ieee80211com *ic, int flag)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap;
+ int bit, oflags;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ bit = 0;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_ifp->if_flags & flag) {
+ /*
+ * XXX the bridge sets PROMISC but we don't want to
+ * enable it on the device, discard here so all the
+ * drivers don't need to special-case it
+ */
+ if (flag == IFF_PROMISC &&
+ vap->iv_opmode == IEEE80211_M_HOSTAP)
+ continue;
+ bit = 1;
+ break;
+ }
+ oflags = ifp->if_flags;
+ if (bit)
+ ifp->if_flags |= flag;
+ else
+ ifp->if_flags &= ~flag;
+ if ((ifp->if_flags ^ oflags) & flag) {
+ /* XXX should we return 1/0 and let caller do this? */
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ if (flag == IFF_PROMISC)
+ ic->ic_update_promisc(ifp);
+ else if (flag == IFF_ALLMULTI)
+ ic->ic_update_mcast(ifp);
+ }
+ }
+}
+
+/*
+ * Synchronize flag bit state in the com structure
+ * according to the state of all vap's. This is used,
+ * for example, to handle state changes via ioctls.
+ */
+static void
+ieee80211_syncflag_locked(struct ieee80211com *ic, int flag)
+{
+ struct ieee80211vap *vap;
+ int bit;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ bit = 0;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_flags & flag) {
+ bit = 1;
+ break;
+ }
+ if (bit)
+ ic->ic_flags |= flag;
+ else
+ ic->ic_flags &= ~flag;
+}
+
+void
+ieee80211_syncflag(struct ieee80211vap *vap, int flag)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK(ic);
+ if (flag < 0) {
+ flag = -flag;
+ vap->iv_flags &= ~flag;
+ } else
+ vap->iv_flags |= flag;
+ ieee80211_syncflag_locked(ic, flag);
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
+ * Synchronize flag bit state in the com structure
+ * according to the state of all vap's. This is used,
+ * for example, to handle state changes via ioctls.
+ */
+static void
+ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag)
+{
+ struct ieee80211vap *vap;
+ int bit;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ bit = 0;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_flags_ext & flag) {
+ bit = 1;
+ break;
+ }
+ if (bit)
+ ic->ic_flags_ext |= flag;
+ else
+ ic->ic_flags_ext &= ~flag;
+}
+
+void
+ieee80211_syncflag_ext(struct ieee80211vap *vap, int flag)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK(ic);
+ if (flag < 0) {
+ flag = -flag;
+ vap->iv_flags_ext &= ~flag;
+ } else
+ vap->iv_flags_ext |= flag;
+ ieee80211_syncflag_ext_locked(ic, flag);
+ IEEE80211_UNLOCK(ic);
}
static __inline int
@@ -416,7 +757,7 @@ ieee80211_ieee2mhz(u_int chan, u_int flags)
/*
* Locate a channel given a frequency+flags. We cache
- * the previous lookup to optimize swithing between two
+ * the previous lookup to optimize switching between two
* channels--as happens with dynamic turbo.
*/
struct ieee80211_channel *
@@ -467,80 +808,58 @@ ieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags)
}
static void
-addmedia(struct ieee80211com *ic, int mode, int mword)
+addmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword)
{
-#define TURBO(m) ((m) | IFM_IEEE80211_TURBO)
#define ADD(_ic, _s, _o) \
- ifmedia_add(&(_ic)->ic_media, \
+ ifmedia_add(media, \
IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL)
static const u_int mopts[IEEE80211_MODE_MAX] = {
- IFM_AUTO, /* IEEE80211_MODE_AUTO */
- IFM_IEEE80211_11A, /* IEEE80211_MODE_11A */
- IFM_IEEE80211_11B, /* IEEE80211_MODE_11B */
- IFM_IEEE80211_11G, /* IEEE80211_MODE_11G */
- IFM_IEEE80211_FH, /* IEEE80211_MODE_FH */
- TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_TURBO_A */
- TURBO(IFM_IEEE80211_11G), /* IEEE80211_MODE_TURBO_G */
- TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_STURBO_A */
- IFM_IEEE80211_11NA, /* IEEE80211_MODE_11NA */
- IFM_IEEE80211_11NG, /* IEEE80211_MODE_11NG */
+ IFM_AUTO,
+ IFM_IEEE80211_11A,
+ IFM_IEEE80211_11B,
+ IFM_IEEE80211_11G,
+ IFM_IEEE80211_FH,
+ IFM_IEEE80211_11A | IFM_IEEE80211_TURBO,
+ IFM_IEEE80211_11G | IFM_IEEE80211_TURBO,
+ IFM_IEEE80211_11A | IFM_IEEE80211_TURBO,
+ IFM_IEEE80211_11NA,
+ IFM_IEEE80211_11NG,
};
u_int mopt;
- KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode));
mopt = mopts[mode];
- KASSERT(mopt != 0 || mode == IEEE80211_MODE_AUTO,
- ("no media mapping for mode %u", mode));
-
- ADD(ic, mword, mopt); /* e.g. 11a auto */
- if (ic->ic_caps & IEEE80211_C_IBSS)
- ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC);
- if (ic->ic_caps & IEEE80211_C_HOSTAP)
- ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP);
- if (ic->ic_caps & IEEE80211_C_AHDEMO)
- ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
- if (ic->ic_caps & IEEE80211_C_MONITOR)
- ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR);
+ if (addsta)
+ ADD(ic, mword, mopt); /* STA mode has no cap */
+ if (caps & IEEE80211_C_IBSS)
+ ADD(media, mword, mopt | IFM_IEEE80211_ADHOC);
+ if (caps & IEEE80211_C_HOSTAP)
+ ADD(media, mword, mopt | IFM_IEEE80211_HOSTAP);
+ if (caps & IEEE80211_C_AHDEMO)
+ ADD(media, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
+ if (caps & IEEE80211_C_MONITOR)
+ ADD(media, mword, mopt | IFM_IEEE80211_MONITOR);
+ if (caps & IEEE80211_C_WDS)
+ ADD(media, mword, mopt | IFM_IEEE80211_WDS);
#undef ADD
-#undef TURBO
}
/*
* Setup the media data structures according to the channel and
- * rate tables. This must be called by the driver after
- * ieee80211_attach and before most anything else.
+ * rate tables.
*/
-void
-ieee80211_media_init(struct ieee80211com *ic,
+static int
+ieee80211_media_setup(struct ieee80211com *ic,
+ struct ifmedia *media, int caps, int addsta,
ifm_change_cb_t media_change, ifm_stat_cb_t media_stat)
{
- struct ifnet *ifp = ic->ic_ifp;
int i, j, mode, rate, maxrate, mword, r;
const struct ieee80211_rateset *rs;
struct ieee80211_rateset allrates;
- /* NB: this works because the structure is initialized to zero */
- if (LIST_EMPTY(&ic->ic_media.ifm_list)) {
- /*
- * Do late attach work that must wait for any subclass
- * (i.e. driver) work such as overriding methods.
- */
- ieee80211_node_lateattach(ic);
- } else {
- /*
- * We are re-initializing the channel list; clear
- * the existing media state as the media routines
- * don't suppress duplicates.
- */
- ifmedia_removeall(&ic->ic_media);
- ieee80211_chan_init(ic);
- }
- ieee80211_power_lateattach(ic);
-
/*
* Fill in media characteristics.
*/
- ifmedia_init(&ic->ic_media, 0, media_change, media_stat);
+ ifmedia_init(media, 0, media_change, media_stat);
maxrate = 0;
/*
* Add media for legacy operating modes.
@@ -549,7 +868,7 @@ ieee80211_media_init(struct ieee80211com *ic,
for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) {
if (isclr(ic->ic_modecaps, mode))
continue;
- addmedia(ic, mode, IFM_AUTO);
+ addmedia(media, caps, addsta, mode, IFM_AUTO);
if (mode == IEEE80211_MODE_AUTO)
continue;
rs = &ic->ic_sup_rates[mode];
@@ -558,7 +877,7 @@ ieee80211_media_init(struct ieee80211com *ic,
mword = ieee80211_rate2media(ic, rate, mode);
if (mword == 0)
continue;
- addmedia(ic, mode, mword);
+ addmedia(media, caps, addsta, mode, mword);
/*
* Add legacy rate to the collection of all rates.
*/
@@ -582,7 +901,8 @@ ieee80211_media_init(struct ieee80211com *ic,
if (mword == 0)
continue;
/* NB: remove media options from mword */
- addmedia(ic, IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword));
+ addmedia(media, caps, addsta,
+ IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword));
}
/*
* Add HT/11n media. Note that we do not have enough
@@ -593,24 +913,51 @@ ieee80211_media_init(struct ieee80211com *ic,
for (; mode < IEEE80211_MODE_MAX; mode++) {
if (isclr(ic->ic_modecaps, mode))
continue;
- addmedia(ic, mode, IFM_AUTO);
- addmedia(ic, mode, IFM_IEEE80211_MCS);
+ addmedia(media, caps, addsta, mode, IFM_AUTO);
+ addmedia(media, caps, addsta, mode, IFM_IEEE80211_MCS);
}
if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) ||
isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) {
- addmedia(ic, IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS);
+ addmedia(media, caps, addsta,
+ IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS);
/* XXX could walk htrates */
/* XXX known array size */
- if (ieee80211_htrates[15] > maxrate)
- maxrate = ieee80211_htrates[15];
+ if (ieee80211_htrates[15].ht40_rate_400ns > maxrate)
+ maxrate = ieee80211_htrates[15].ht40_rate_400ns;
+ }
+ return maxrate;
+}
+
+void
+ieee80211_media_init(struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ int maxrate;
+
+ /* NB: this works because the structure is initialized to zero */
+ if (!LIST_EMPTY(&ic->ic_media.ifm_list)) {
+ /*
+ * We are re-initializing the channel list; clear
+ * the existing media state as the media routines
+ * don't suppress duplicates.
+ */
+ ifmedia_removeall(&ic->ic_media);
}
+ ieee80211_chan_init(ic);
+ /*
+ * Recalculate media settings in case new channel list changes
+ * the set of available modes.
+ */
+ maxrate = ieee80211_media_setup(ic, &ic->ic_media, ic->ic_caps, 1,
+ ieee80211com_media_change, ieee80211com_media_status);
/* NB: strip explicit mode; we're actually in autoselect */
ifmedia_set(&ic->ic_media,
media_status(ic->ic_opmode, ic->ic_curchan) &~ IFM_MMASK);
-
if (maxrate)
ifp->if_baudrate = IF_Mbps(maxrate);
+
+ /* XXX need to propagate new media settings to vap's */
}
const struct ieee80211_rateset *
@@ -701,216 +1048,133 @@ ieee80211_announce_channels(struct ieee80211com *ic)
}
}
-/*
- * Find an instance by it's mac address.
- */
-struct ieee80211com *
-ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN])
-{
- struct ieee80211com *ic;
-
- /* XXX lock */
- SLIST_FOREACH(ic, &ieee80211_list, ic_next)
- if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr))
- return ic;
- return NULL;
-}
-
-static struct ieee80211com *
-ieee80211_find_instance(struct ifnet *ifp)
-{
- struct ieee80211com *ic;
-
- /* XXX lock */
- /* XXX not right for multiple instances but works for now */
- SLIST_FOREACH(ic, &ieee80211_list, ic_next)
- if (ic->ic_ifp == ifp)
- return ic;
- return NULL;
-}
-
-static int
-findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
-{
-#define IEEERATE(_ic,_m,_i) \
- ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
- int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
- for (i = 0; i < nrates; i++)
- if (IEEERATE(ic, mode, i) == rate)
- return i;
- return -1;
-#undef IEEERATE
-}
-
-/*
- * Convert a media specification to a rate index and possibly a mode
- * (if the rate is fixed and the mode is specified as ``auto'' then
- * we need to lock down the mode so the index is meanginful).
- */
static int
-checkrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
+media2mode(const struct ieee80211com *ic,
+ const struct ifmedia_entry *ime, enum ieee80211_phymode *mode)
{
-
- /*
- * Check the rate table for the specified/current phy.
- */
- if (mode == IEEE80211_MODE_AUTO) {
- int i;
- /*
- * In autoselect mode search for the rate.
- */
- for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) {
- if (isset(ic->ic_modecaps, i) &&
- findrate(ic, i, rate) != -1)
- return 1;
- }
- return 0;
- } else {
- /*
- * Mode is fixed, check for rate.
- */
- return (findrate(ic, mode, rate) != -1);
- }
-}
-
-/*
- * Handle a media change request.
- */
-int
-ieee80211_media_change(struct ifnet *ifp)
-{
- struct ieee80211com *ic;
- struct ifmedia_entry *ime;
- enum ieee80211_opmode newopmode;
- enum ieee80211_phymode newphymode;
- int newrate, error = 0;
-
- ic = ieee80211_find_instance(ifp);
- if (!ic) {
- if_printf(ifp, "%s: no 802.11 instance!\n", __func__);
- return EINVAL;
- }
- ime = ic->ic_media.ifm_cur;
- /*
- * First, identify the phy mode.
- */
switch (IFM_MODE(ime->ifm_media)) {
case IFM_IEEE80211_11A:
- newphymode = IEEE80211_MODE_11A;
+ *mode = IEEE80211_MODE_11A;
break;
case IFM_IEEE80211_11B:
- newphymode = IEEE80211_MODE_11B;
+ *mode = IEEE80211_MODE_11B;
break;
case IFM_IEEE80211_11G:
- newphymode = IEEE80211_MODE_11G;
+ *mode = IEEE80211_MODE_11G;
break;
case IFM_IEEE80211_FH:
- newphymode = IEEE80211_MODE_FH;
+ *mode = IEEE80211_MODE_FH;
break;
case IFM_IEEE80211_11NA:
- newphymode = IEEE80211_MODE_11NA;
+ *mode = IEEE80211_MODE_11NA;
break;
case IFM_IEEE80211_11NG:
- newphymode = IEEE80211_MODE_11NG;
+ *mode = IEEE80211_MODE_11NG;
break;
case IFM_AUTO:
- newphymode = IEEE80211_MODE_AUTO;
+ *mode = IEEE80211_MODE_AUTO;
break;
default:
- return EINVAL;
+ return 0;
}
/*
* Turbo mode is an ``option''.
* XXX does not apply to AUTO
*/
if (ime->ifm_media & IFM_IEEE80211_TURBO) {
- if (newphymode == IEEE80211_MODE_11A) {
+ if (*mode == IEEE80211_MODE_11A) {
if (ic->ic_flags & IEEE80211_F_TURBOP)
- newphymode = IEEE80211_MODE_TURBO_A;
+ *mode = IEEE80211_MODE_TURBO_A;
else
- newphymode = IEEE80211_MODE_STURBO_A;
- } else if (newphymode == IEEE80211_MODE_11G)
- newphymode = IEEE80211_MODE_TURBO_G;
+ *mode = IEEE80211_MODE_STURBO_A;
+ } else if (*mode == IEEE80211_MODE_11G)
+ *mode = IEEE80211_MODE_TURBO_G;
else
- return EINVAL;
+ return 0;
}
/* XXX HT40 +/- */
- /*
- * Next, the fixed/variable rate.
- */
- newrate = ic->ic_fixed_rate;
- if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) {
- /*
- * Convert media subtype to rate.
- */
- newrate = ieee80211_media2rate(ime->ifm_media);
- if (newrate == 0 || !checkrate(ic, newphymode, newrate))
- return EINVAL;
- } else
- newrate = IEEE80211_FIXED_RATE_NONE;
+ return 1;
+}
+
+/*
+ * Handle a media change request on the underlying
+ * interface; we accept mode changes only.
+ */
+int
+ieee80211com_media_change(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifmedia_entry *ime = ic->ic_media.ifm_cur;
+ enum ieee80211_phymode newphymode;
+ int error = 0;
/*
- * Deduce new operating mode but don't install it just yet.
+ * First, identify the phy mode.
*/
- if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) ==
- (IFM_IEEE80211_ADHOC|IFM_FLAG0))
- newopmode = IEEE80211_M_AHDEMO;
- else if (ime->ifm_media & IFM_IEEE80211_HOSTAP)
- newopmode = IEEE80211_M_HOSTAP;
- else if (ime->ifm_media & IFM_IEEE80211_ADHOC)
- newopmode = IEEE80211_M_IBSS;
- else if (ime->ifm_media & IFM_IEEE80211_MONITOR)
- newopmode = IEEE80211_M_MONITOR;
- else
- newopmode = IEEE80211_M_STA;
+ if (!media2mode(ic, ime, &newphymode))
+ return EINVAL;
+ /* NB: mode must be supported, no need to check */
/*
* Handle phy mode change.
*/
- if (ic->ic_des_mode != newphymode) { /* change phy mode */
- ic->ic_des_mode = newphymode;
- error = ENETRESET;
- }
+ IEEE80211_LOCK(ic);
+ if (ic->ic_curmode != newphymode) { /* change phy mode */
+ struct ieee80211vap *vap;
- /*
- * Committed to changes, install the rate setting.
- */
- if (ic->ic_fixed_rate != newrate) {
- ic->ic_fixed_rate = newrate; /* set fixed tx rate */
- error = ENETRESET;
+ (void) ieee80211_setmode(ic, newphymode);
+ /*
+ * Propagate new state to each vap.
+ */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ }
}
+ IEEE80211_UNLOCK(ic);
+ return error;
+}
- /*
- * Handle operating mode change.
- */
- if (ic->ic_opmode != newopmode) {
- ic->ic_opmode = newopmode;
- switch (newopmode) {
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_HOSTAP:
- case IEEE80211_M_STA:
- case IEEE80211_M_MONITOR:
- case IEEE80211_M_WDS:
- ic->ic_flags &= ~IEEE80211_F_IBSSON;
- break;
- case IEEE80211_M_IBSS:
- ic->ic_flags |= IEEE80211_F_IBSSON;
- break;
- }
+static int
+findrate(const struct ieee80211com *ic, enum ieee80211_phymode m, int r)
+{
+ int i, nrates;
+
+ for (i = 0, nrates = ic->ic_sup_rates[m].rs_nrates; i < nrates; i++)
+ if ((ic->ic_sup_rates[m].rs_rates[i] & IEEE80211_RATE_VAL) == r)
+ return i;
+ return -1;
+}
+
+/*
+ * Handle a media change request on the vap interface.
+ */
+int
+ieee80211_media_change(struct ifnet *ifp)
+{
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ifmedia_entry *ime = vap->iv_media.ifm_cur;
+ struct ieee80211com *ic = vap->iv_ic;
+ int newrate;
+
+ /* XXX this won't work unless ic_curmode is != IEEE80211_MODE_AUTO */
+ if (ic->ic_curmode == IEEE80211_MODE_AUTO)
+ return EINVAL;
+ if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) {
/*
- * Yech, slot time may change depending on the
- * operating mode so reset it to be sure everything
- * is setup appropriately.
+ * NB: this can only be used to specify a legacy rate.
*/
- ieee80211_reset_erp(ic);
- ieee80211_wme_initparams(ic); /* after opmode change */
- error = ENETRESET;
+ newrate = ieee80211_media2rate(ime->ifm_media);
+ if (newrate == 0)
+ return EINVAL;
+ if (findrate(ic, ic->ic_curmode, newrate) == -1)
+ return EINVAL;
+ } else {
+ newrate = IEEE80211_FIXED_RATE_NONE;
}
-#ifdef notdef
- if (error == 0)
- ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media);
-#endif
- return error;
+ if (newrate != vap->iv_txparms[ic->ic_curmode].ucastrate) {
+ vap->iv_txparms[ic->ic_curmode].ucastrate = newrate;
+ return ENETRESET;
+ }
+ return 0;
}
/*
@@ -939,7 +1203,7 @@ media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan)
status |= IFM_IEEE80211_ADHOC | IFM_FLAG0;
break;
case IEEE80211_M_WDS:
- /* should not come here */
+ status |= IFM_IEEE80211_WDS;
break;
}
if (IEEE80211_IS_CHAN_HTA(chan)) {
@@ -959,53 +1223,70 @@ media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan)
if (IEEE80211_IS_CHAN_TURBO(chan))
status |= IFM_IEEE80211_TURBO;
-
+#if 0
+ if (IEEE80211_IS_CHAN_HT20(chan))
+ status |= IFM_IEEE80211_HT20;
+ if (IEEE80211_IS_CHAN_HT40(chan))
+ status |= IFM_IEEE80211_HT40;
+#endif
return status;
}
+static void
+ieee80211com_media_status(struct ifnet *ifp, struct ifmediareq *imr)
+{
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap;
+
+ imr->ifm_status = IFM_AVALID;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_ifp->if_flags & IFF_UP) {
+ imr->ifm_status |= IFM_ACTIVE;
+ break;
+ }
+ imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan);
+ if (imr->ifm_status & IFM_ACTIVE)
+ imr->ifm_current = imr->ifm_active;
+}
+
void
ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
- struct ieee80211com *ic;
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
enum ieee80211_phymode mode;
- const struct ieee80211_rateset *rs;
- ic = ieee80211_find_instance(ifp);
- if (!ic) {
- if_printf(ifp, "%s: no 802.11 instance!\n", __func__);
- return;
- }
imr->ifm_status = IFM_AVALID;
/*
* NB: use the current channel's mode to lock down a xmit
* rate only when running; otherwise we may have a mismatch
* in which case the rate will not be convertible.
*/
- if (ic->ic_state == IEEE80211_S_RUN) {
+ if (vap->iv_state == IEEE80211_S_RUN) {
imr->ifm_status |= IFM_ACTIVE;
mode = ieee80211_chan2mode(ic->ic_curchan);
} else
mode = IEEE80211_MODE_AUTO;
- imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan);
+ imr->ifm_active = media_status(vap->iv_opmode, ic->ic_curchan);
/*
* Calculate a current rate if possible.
*/
- if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
+ if (vap->iv_txparms[mode].ucastrate != IEEE80211_FIXED_RATE_NONE) {
/*
* A fixed rate is set, report that.
*/
imr->ifm_active |= ieee80211_rate2media(ic,
- ic->ic_fixed_rate, mode);
- } else if (ic->ic_opmode == IEEE80211_M_STA) {
+ vap->iv_txparms[mode].ucastrate, mode);
+ } else if (vap->iv_opmode == IEEE80211_M_STA) {
/*
* In station mode report the current transmit rate.
- * XXX HT rate
*/
- rs = &ic->ic_bss->ni_rates;
imr->ifm_active |= ieee80211_rate2media(ic,
- rs->rs_rates[ic->ic_bss->ni_txrate], mode);
+ vap->iv_bss->ni_txrate, mode);
} else
imr->ifm_active |= IFM_AUTO;
+ if (imr->ifm_status & IFM_ACTIVE)
+ imr->ifm_current = imr->ifm_active;
}
/*
@@ -1024,11 +1305,10 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode)
* and used instead.
*/
if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B)
- ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], mode);
+ ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode);
ic->ic_curmode = mode;
ieee80211_reset_erp(ic); /* reset ERP state */
- ieee80211_wme_initparams(ic); /* reset WME stat */
return 0;
}
diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h
index 97f9365..3e91068 100644
--- a/sys/net80211/ieee80211.h
+++ b/sys/net80211/ieee80211.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -696,9 +696,13 @@ struct ieee80211_country_ie {
uint8_t schan; /* starting channel */
uint8_t nchan; /* number channels */
uint8_t maxtxpwr; /* tx power cap */
- } __packed band[10]; /* sub bands */
+ } __packed band[1]; /* sub bands (NB: var size) */
} __packed;
+#define IEEE80211_COUNTRY_MAX_BANDS 84 /* max possible bands */
+#define IEEE80211_COUNTRY_MAX_SIZE \
+ (sizeof(struct ieee80211_country_ie) + 3*(IEEE80211_COUNTRY_MAX_BANDS-1))
+
/*
* 802.11h Channel Switch Announcement (CSA).
*/
@@ -889,6 +893,9 @@ enum {
#define IEEE80211_WEP_IVLEN 3 /* 24bit */
#define IEEE80211_WEP_KIDLEN 1 /* 1 octet */
#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */
+#define IEEE80211_WEP_TOTLEN (IEEE80211_WEP_IVLEN + \
+ IEEE80211_WEP_KIDLEN + \
+ IEEE80211_WEP_CRCLEN)
#define IEEE80211_WEP_NKID 4 /* number of key ids */
/*
@@ -924,12 +931,12 @@ enum {
/*
* The 802.11 spec says at most 2007 stations may be
* associated at once. For most AP's this is way more
- * than is feasible so we use a default of 128. This
- * number may be overridden by the driver and/or by
- * user configuration.
+ * than is feasible so we use a default of IEEE80211_AID_DEF.
+ * This number may be overridden by the driver and/or by
+ * user configuration but may not be less than IEEE80211_AID_MIN
+ * (see _ieee80211.h for implementation-specific settings).
*/
#define IEEE80211_AID_MAX 2007
-#define IEEE80211_AID_DEF 128
#define IEEE80211_AID(b) ((b) &~ 0xc000)
diff --git a/sys/net80211/ieee80211_acl.c b/sys/net80211/ieee80211_acl.c
index c5305d0..13407a4 100644
--- a/sys/net80211/ieee80211_acl.c
+++ b/sys/net80211/ieee80211_acl.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,7 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 MAC ACL support.
*
- * When this module is loaded the sender address of each received
+ * When this module is loaded the sender address of each auth mgt
* frame is passed to the iac_check method and the module indicates
* if the frame should be accepted or rejected. If the policy is
* set to ACL_POLICY_OPEN then all frames are accepted w/o checking
@@ -37,6 +37,8 @@ __FBSDID("$FreeBSD$");
* and if found the frame is either accepted (ACL_POLICY_ALLOW)
* or rejected (ACL_POLICY_DENT).
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
@@ -57,6 +59,12 @@ enum {
ACL_POLICY_OPEN = 0, /* open, don't check ACL's */
ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */
ACL_POLICY_DENY = 2, /* deny traffic from MAC */
+ /*
+ * NB: ACL_POLICY_RADIUS must be the same value as
+ * IEEE80211_MACCMD_POLICY_RADIUS because of the way
+ * acl_getpolicy() works.
+ */
+ ACL_POLICY_RADIUS = 7, /* defer to RADIUS ACL server */
};
#define ACL_HASHSIZE 32
@@ -72,7 +80,7 @@ struct aclstate {
int as_nacls;
TAILQ_HEAD(, acl) as_list; /* list of all ACL's */
LIST_HEAD(, acl) as_hash[ACL_HASHSIZE];
- struct ieee80211com *as_ic;
+ struct ieee80211vap *as_vap;
};
/* simple hash is enough for variation of macaddr */
@@ -81,10 +89,13 @@ struct aclstate {
MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl");
-static int acl_free_all(struct ieee80211com *);
+static int acl_free_all(struct ieee80211vap *);
+
+/* number of references from net80211 layer */
+static int nrefs = 0;
static int
-acl_attach(struct ieee80211com *ic)
+acl_attach(struct ieee80211vap *vap)
{
struct aclstate *as;
@@ -95,20 +106,24 @@ acl_attach(struct ieee80211com *ic)
ACL_LOCK_INIT(as, "acl");
TAILQ_INIT(&as->as_list);
as->as_policy = ACL_POLICY_OPEN;
- as->as_ic = ic;
- ic->ic_as = as;
+ as->as_vap = vap;
+ vap->iv_as = as;
+ nrefs++; /* NB: we assume caller locking */
return 1;
}
static void
-acl_detach(struct ieee80211com *ic)
+acl_detach(struct ieee80211vap *vap)
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
- acl_free_all(ic);
- ic->ic_as = NULL;
+ KASSERT(nrefs > 0, ("imbalanced attach/detach"));
+ nrefs--; /* NB: we assume caller locking */
+
+ acl_free_all(vap);
+ vap->iv_as = NULL;
ACL_LOCK_DESTROY(as);
- FREE(as, M_DEVBUF);
+ FREE(as, M_80211_ACL);
}
static __inline struct acl *
@@ -137,12 +152,13 @@ _acl_free(struct aclstate *as, struct acl *acl)
}
static int
-acl_check(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
+acl_check(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
switch (as->as_policy) {
case ACL_POLICY_OPEN:
+ case ACL_POLICY_RADIUS:
return 1;
case ACL_POLICY_ALLOW:
return _find_acl(as, mac) != NULL;
@@ -153,15 +169,15 @@ acl_check(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
}
static int
-acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
+acl_add(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
struct acl *acl, *new;
int hash;
MALLOC(new, struct acl *, sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO);
if (new == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: add %s failed, no memory\n", ether_sprintf(mac));
/* XXX statistic */
return ENOMEM;
@@ -173,7 +189,7 @@ acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) {
ACL_UNLOCK(as);
FREE(new, M_80211_ACL);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: add %s failed, already present\n",
ether_sprintf(mac));
return EEXIST;
@@ -185,15 +201,15 @@ acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
as->as_nacls++;
ACL_UNLOCK(as);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: add %s\n", ether_sprintf(mac));
return 0;
}
static int
-acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
+acl_remove(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
struct acl *acl;
ACL_LOCK(as);
@@ -202,7 +218,7 @@ acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
_acl_free(as, acl);
ACL_UNLOCK(as);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: remove %s%s\n", ether_sprintf(mac),
acl == NULL ? ", not present" : "");
@@ -210,12 +226,12 @@ acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
}
static int
-acl_free_all(struct ieee80211com *ic)
+acl_free_all(struct ieee80211vap *vap)
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
struct acl *acl;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, "ACL: %s\n", "free all");
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: %s\n", "free all");
ACL_LOCK(as);
while ((acl = TAILQ_FIRST(&as->as_list)) != NULL)
@@ -226,11 +242,11 @@ acl_free_all(struct ieee80211com *ic)
}
static int
-acl_setpolicy(struct ieee80211com *ic, int policy)
+acl_setpolicy(struct ieee80211vap *vap, int policy)
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: set policy to %u\n", policy);
switch (policy) {
@@ -243,6 +259,9 @@ acl_setpolicy(struct ieee80211com *ic, int policy)
case IEEE80211_MACCMD_POLICY_DENY:
as->as_policy = ACL_POLICY_DENY;
break;
+ case IEEE80211_MACCMD_POLICY_RADIUS:
+ as->as_policy = ACL_POLICY_RADIUS;
+ break;
default:
return EINVAL;
}
@@ -250,24 +269,24 @@ acl_setpolicy(struct ieee80211com *ic, int policy)
}
static int
-acl_getpolicy(struct ieee80211com *ic)
+acl_getpolicy(struct ieee80211vap *vap)
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
return as->as_policy;
}
static int
-acl_setioctl(struct ieee80211com *ic, struct ieee80211req *ireq)
+acl_setioctl(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
return EINVAL;
}
static int
-acl_getioctl(struct ieee80211com *ic, struct ieee80211req *ireq)
+acl_getioctl(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
struct acl *acl;
struct ieee80211req_maclist *ap;
int error, space, i;
@@ -283,7 +302,7 @@ acl_getioctl(struct ieee80211com *ic, struct ieee80211req *ireq)
return 0; /* NB: must not error */
}
MALLOC(ap, struct ieee80211req_maclist *, space,
- M_TEMP, M_NOWAIT);
+ M_TEMP, M_NOWAIT);
if (ap == NULL)
return ENOMEM;
i = 0;
@@ -317,31 +336,4 @@ static const struct ieee80211_aclator mac = {
.iac_setioctl = acl_setioctl,
.iac_getioctl = acl_getioctl,
};
-
-/*
- * Module glue.
- */
-static int
-wlan_acl_modevent(module_t mod, int type, void *unused)
-{
- switch (type) {
- case MOD_LOAD:
- if (bootverbose)
- printf("wlan: <802.11 MAC ACL support>\n");
- ieee80211_aclator_register(&mac);
- return 0;
- case MOD_UNLOAD:
- ieee80211_aclator_unregister(&mac);
- return 0;
- }
- return EINVAL;
-}
-
-static moduledata_t wlan_acl_mod = {
- "wlan_acl",
- wlan_acl_modevent,
- 0
-};
-DECLARE_MODULE(wlan_acl, wlan_acl_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_acl, 1);
-MODULE_DEPEND(wlan_acl, wlan, 1, 1, 1);
+IEEE80211_ACL_MODULE(wlan_acl, mac, 1);
diff --git a/sys/net80211/ieee80211_adhoc.c b/sys/net80211/ieee80211_adhoc.c
new file mode 100644
index 0000000..ce9c4cb
--- /dev/null
+++ b/sys/net80211/ieee80211_adhoc.c
@@ -0,0 +1,877 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 IBSS mode support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_adhoc.h>
+#include <net80211/ieee80211_input.h>
+
+#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2)
+
+static void adhoc_vattach(struct ieee80211vap *);
+static int adhoc_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int adhoc_input(struct ieee80211_node *, struct mbuf *,
+ int rssi, int noise, uint32_t rstamp);
+static void adhoc_recv_mgmt(struct ieee80211_node *, struct mbuf *,
+ int subtype, int rssi, int noise, uint32_t rstamp);
+
+void
+ieee80211_adhoc_attach(struct ieee80211com *ic)
+{
+ ic->ic_vattach[IEEE80211_M_IBSS] = adhoc_vattach;
+ ic->ic_vattach[IEEE80211_M_AHDEMO] = adhoc_vattach;
+}
+
+void
+ieee80211_adhoc_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+adhoc_vdetach(struct ieee80211vap *vap)
+{
+}
+
+static void
+adhoc_vattach(struct ieee80211vap *vap)
+{
+ vap->iv_newstate = adhoc_newstate;
+ vap->iv_input = adhoc_input;
+ vap->iv_recv_mgmt = adhoc_recv_mgmt;
+ vap->iv_opdetach = adhoc_vdetach;
+}
+
+/*
+ * IEEE80211_M_IBSS+IEEE80211_M_AHDEMO vap state machine handler.
+ */
+static int
+adhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+#ifdef IEEE80211_DEBUG
+ struct ieee80211com *ic = vap->iv_ic;
+#endif
+ struct ieee80211_node *ni;
+ enum ieee80211_state ostate;
+
+ IEEE80211_LOCK_ASSERT(vap->iv_ic);
+
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate], arg);
+ vap->iv_state = nstate; /* state transition */
+ if (ostate != IEEE80211_S_SCAN)
+ ieee80211_cancel_scan(vap); /* background scan */
+ ni = vap->iv_bss; /* NB: no reference held */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
+ callout_stop(&vap->iv_swbmiss);
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ switch (ostate) {
+ case IEEE80211_S_SCAN:
+ ieee80211_cancel_scan(vap);
+ break;
+ default:
+ break;
+ }
+ if (ostate != IEEE80211_S_INIT) {
+ /* NB: optimize INIT -> INIT case */
+ ieee80211_reset_bss(vap);
+ }
+ break;
+ case IEEE80211_S_SCAN:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_RUN: /* beacon miss */
+ if (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
+ !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
+ /*
+ * Already have a channel; bypass the
+ * scan and startup immediately.
+ */
+ ieee80211_create_ibss(vap, vap->iv_des_chan);
+ break;
+ }
+ /*
+ * Initiate a scan. We can come here as a result
+ * of an IEEE80211_IOC_SCAN_REQ too in which case
+ * the vap will be marked with IEEE80211_FEXT_SCANREQ
+ * and the scan request parameters will be present
+ * in iv_scanreq. Otherwise we do the default.
+ */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
+ ieee80211_check_scan(vap,
+ vap->iv_scanreq_flags,
+ vap->iv_scanreq_duration,
+ vap->iv_scanreq_mindwell,
+ vap->iv_scanreq_maxdwell,
+ vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
+ } else
+ ieee80211_check_scan_current(vap);
+ break;
+ case IEEE80211_S_SCAN:
+ /*
+ * This can happen because of a change in state
+ * that requires a reset. Trigger a new scan
+ * unless we're in manual roaming mode in which
+ * case an application must issue an explicit request.
+ */
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
+ ieee80211_check_scan_current(vap);
+ break;
+ default:
+ goto invalid;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ if (vap->iv_flags & IEEE80211_F_WPA) {
+ /* XXX validate prerequisites */
+ }
+ switch (ostate) {
+ case IEEE80211_S_SCAN:
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_debug(vap)) {
+ ieee80211_note(vap,
+ "synchronized with %s ssid ",
+ ether_sprintf(ni->ni_bssid));
+ ieee80211_print_essid(vap->iv_bss->ni_essid,
+ ni->ni_esslen);
+ /* XXX MCS/HT */
+ printf(" channel %d start %uMb\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ IEEE80211_RATE2MBS(ni->ni_txrate));
+ }
+#endif
+ break;
+ default:
+ goto invalid;
+ }
+ /*
+ * When 802.1x is not in use mark the port authorized
+ * at this point so traffic can flow.
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+ ieee80211_node_authorize(ni);
+ break;
+ case IEEE80211_S_SLEEP:
+ ieee80211_sta_pwrsave(vap, 0);
+ break;
+ default:
+ invalid:
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
+ "%s: invalid state transition %s -> %s\n", __func__,
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Decide if a received management frame should be
+ * printed when debugging is enabled. This filters some
+ * of the less interesting frames that come frequently
+ * (e.g. beacons).
+ */
+static __inline int
+doprint(struct ieee80211vap *vap, int subtype)
+{
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ return 1;
+ }
+ return 1;
+}
+
+/*
+ * Process a received frame. The node associated with the sender
+ * should be supplied. If nothing was found in the node table then
+ * the caller is assumed to supply a reference to iv_bss instead.
+ * The RSSI and a timestamp are also supplied. The RSSI data is used
+ * during AP scanning to select a AP to associate with; it can have
+ * any units so long as values have consistent units and higher values
+ * mean ``better signal''. The receive timestamp is currently not used
+ * by the 802.11 layer.
+ */
+static int
+adhoc_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp)
+{
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define HAS_SEQ(type) ((type & 0x4) == 0)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
+ struct ether_header *eh;
+ int hdrspace, need_tap;
+ uint8_t dir, type, subtype, qos;
+ uint8_t *bssid;
+ uint16_t rxseq;
+
+ if (m->m_flags & M_AMPDU) {
+ /*
+ * Fastpath for A-MPDU reorder q resubmission. Frames
+ * w/ M_AMPDU marked have already passed through here
+ * but were received out of order and been held on the
+ * reorder queue. When resubmitted they are marked
+ * with the M_AMPDU flag and we can bypass most of the
+ * normal processing.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ type = IEEE80211_FC0_TYPE_DATA;
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ subtype = IEEE80211_FC0_SUBTYPE_QOS;
+ hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
+ goto resubmit_ampdu;
+ }
+
+ KASSERT(ni != NULL, ("null node"));
+ ni->ni_inact = ni->ni_inact_reload;
+
+ need_tap = 1; /* mbuf need to be tapped. */
+ type = -1; /* undefined */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "too short (1): len %u", m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ /*
+ * Bit of a cheat here, we use a pointer for a 3-address
+ * frame format but don't reference fields past outside
+ * ieee80211_frame_min w/o first validating the data is
+ * present.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
+ vap->iv_stats.is_rx_badversion++;
+ goto err;
+ }
+
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ if (dir != IEEE80211_FC1_DIR_NODS)
+ bssid = wh->i_addr1;
+ else if (type == IEEE80211_FC0_TYPE_CTL)
+ bssid = wh->i_addr1;
+ else {
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap,
+ IEEE80211_MSG_ANY, ni->ni_macaddr,
+ NULL, "too short (2): len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ bssid = wh->i_addr3;
+ }
+ /*
+ * Validate the bssid.
+ */
+ if (!IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) &&
+ !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
+ /* not interested in */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, NULL, "%s", "not to bss");
+ vap->iv_stats.is_rx_wrongbss++;
+ goto out;
+ }
+ /*
+ * Data frame, cons up a node when it doesn't
+ * exist. This should probably done after an ACL check.
+ */
+ if (type == IEEE80211_FC0_TYPE_DATA &&
+ ni == vap->iv_bss &&
+ !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
+ /*
+ * Fake up a node for this newly
+ * discovered member of the IBSS.
+ */
+ ni = ieee80211_fakeup_adhoc_node(vap, wh->i_addr2);
+ if (ni == NULL) {
+ /* NB: stat kept for alloc failure */
+ goto err;
+ }
+ }
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ if (HAS_SEQ(type)) {
+ uint8_t tid = ieee80211_gettid(wh);
+ if (IEEE80211_QOS_HAS_SEQ(wh) &&
+ TID_TO_WME_AC(tid) >= WME_AC_VI)
+ ic->ic_wme.wme_hipri_traffic++;
+ rxseq = le16toh(*(uint16_t *)wh->i_seq);
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
+ (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
+ /* duplicate, discard */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, "duplicate",
+ "seqno <%u,%u> fragno <%u,%u> tid %u",
+ rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[tid] >>
+ IEEE80211_SEQ_SEQ_SHIFT,
+ rxseq & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxseqs[tid] &
+ IEEE80211_SEQ_FRAG_MASK,
+ tid);
+ vap->iv_stats.is_rx_dup++;
+ IEEE80211_NODE_STAT(ni, rx_dup);
+ goto out;
+ }
+ ni->ni_rxseqs[tid] = rxseq;
+ }
+ }
+
+ switch (type) {
+ case IEEE80211_FC0_TYPE_DATA:
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ if (m->m_len < hdrspace &&
+ (m = m_pullup(m, hdrspace)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "data too short: expecting %u", hdrspace);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out; /* XXX */
+ }
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto out;
+ }
+ /* XXX no power-save support */
+
+ /*
+ * Handle A-MPDU re-ordering. The station must be
+ * associated and negotiated HT. The frame must be
+ * a QoS frame (not QoS null data) and not previously
+ * processed for A-MPDU re-ordering. If the frame is
+ * to be processed directly then ieee80211_ampdu_reorder
+ * will return 0; otherwise it has consumed the mbuf
+ * and we should do nothing more with it.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+ ieee80211_ampdu_reorder(ni, m) != 0) {
+ m = NULL;
+ goto out;
+ }
+ resubmit_ampdu:
+
+ /*
+ * Handle privacy requirements. Note that we
+ * must not be preempted from here until after
+ * we (potentially) call ieee80211_crypto_demic;
+ * otherwise we may violate assumptions in the
+ * crypto cipher modules used to do delayed update
+ * of replay sequence numbers.
+ */
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "WEP", "%s", "PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ IEEE80211_NODE_STAT(ni, rx_noprivacy);
+ goto out;
+ }
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ IEEE80211_NODE_STAT(ni, rx_wepfail);
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ } else {
+ /* XXX M_WEP and IEEE80211_F_PRIVACY */
+ key = NULL;
+ }
+
+ /*
+ * Save QoS bits for use below--before we strip the header.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+ qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+ ((struct ieee80211_qosframe *)wh)->i_qos[0];
+ } else
+ qos = 0;
+
+ /*
+ * Next up, any fragmentation.
+ */
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ m = ieee80211_defrag(ni, m, hdrspace);
+ if (m == NULL) {
+ /* Fragment dropped or frame not complete yet */
+ goto out;
+ }
+ }
+ wh = NULL; /* no longer valid, catch any uses */
+
+ /*
+ * Next strip any MSDU crypto bits.
+ */
+ if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "demic error");
+ vap->iv_stats.is_rx_demicfail++;
+ IEEE80211_NODE_STAT(ni, rx_demicfail);
+ goto out;
+ }
+
+ /* copy to listener after decrypt */
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ need_tap = 0;
+
+ /*
+ * Finally, strip the 802.11 header.
+ */
+ m = ieee80211_decap(vap, m, hdrspace);
+ if (m == NULL) {
+ /* XXX mask bit to check for both */
+ /* don't count Null data frames as errors */
+ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
+ subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
+ goto out;
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "decap error");
+ vap->iv_stats.is_rx_decap++;
+ IEEE80211_NODE_STAT(ni, rx_decap);
+ goto err;
+ }
+ eh = mtod(m, struct ether_header *);
+ if (!ieee80211_node_is_authorized(ni)) {
+ /*
+ * Deny any non-PAE frames received prior to
+ * authorization. For open/shared-key
+ * authentication the port is mark authorized
+ * after authentication completes. For 802.1x
+ * the port is not marked authorized by the
+ * authenticator until the handshake has completed.
+ */
+ if (eh->ether_type != htons(ETHERTYPE_PAE)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ eh->ether_shost, "data",
+ "unauthorized port: ether type 0x%x len %u",
+ eh->ether_type, m->m_pkthdr.len);
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(ni, rx_unauth);
+ goto err;
+ }
+ } else {
+ /*
+ * When denying unencrypted frames, discard
+ * any non-PAE frames received without encryption.
+ */
+ if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
+ (key == NULL && (m->m_flags & M_WEP) == 0) &&
+ eh->ether_type != htons(ETHERTYPE_PAE)) {
+ /*
+ * Drop unencrypted frames.
+ */
+ vap->iv_stats.is_rx_unencrypted++;
+ IEEE80211_NODE_STAT(ni, rx_unencrypted);
+ goto out;
+ }
+ }
+ /* XXX require HT? */
+ if (qos & IEEE80211_QOS_AMSDU) {
+ m = ieee80211_decap_amsdu(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
+#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
+ m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
+ struct llc *llc;
+
+ /*
+ * Check for fast-frame tunnel encapsulation.
+ */
+ if (m->m_len < FF_LLC_SIZE &&
+ (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame",
+ "%s", "m_pullup(llc) failed");
+ vap->iv_stats.is_rx_tooshort++;
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ llc = (struct llc *)(mtod(m, uint8_t *) +
+ sizeof(struct ether_header));
+ if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
+ m_adj(m, FF_LLC_SIZE);
+ m = ieee80211_decap_fastframe(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ }
+#undef FF_LLC_SIZE
+ if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL)
+ ieee80211_deliver_data(ni->ni_wdsvap, ni, m);
+ else
+ ieee80211_deliver_data(vap, ni, m);
+ return IEEE80211_FC0_TYPE_DATA;
+
+ case IEEE80211_FC0_TYPE_MGT:
+ vap->iv_stats.is_rx_mgmt++;
+ IEEE80211_NODE_STAT(ni, rx_mgmt);
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto err;
+ }
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "mgt", "too short: len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+#ifdef IEEE80211_DEBUG
+ if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
+ ieee80211_msg_dumppkts(vap)) {
+ if_printf(ifp, "received %s from %s rssi %d\n",
+ ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(wh->i_addr2), rssi);
+ }
+#endif
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL, "%s", "WEP set but not permitted");
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
+ goto out;
+ }
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ /* NB: only IBSS mode gets mgt frames */
+ if (vap->iv_opmode == IEEE80211_M_IBSS)
+ vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
+ m_freem(m);
+ return IEEE80211_FC0_TYPE_MGT;
+
+ case IEEE80211_FC0_TYPE_CTL:
+ vap->iv_stats.is_rx_ctl++;
+ IEEE80211_NODE_STAT(ni, rx_ctrl);
+ goto out;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "bad", "frame type 0x%x", type);
+ /* should not come here */
+ break;
+ }
+err:
+ ifp->if_ierrors++;
+out:
+ if (m != NULL) {
+ if (bpf_peers_present(vap->iv_rawbpf) && need_tap)
+ bpf_mtap(vap->iv_rawbpf, m);
+ m_freem(m);
+ }
+ return type;
+#undef SEQ_LEQ
+}
+
+static int
+is11bclient(const uint8_t *rates, const uint8_t *xrates)
+{
+ static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11);
+ int i;
+
+ /* NB: the 11b clients we care about will not have xrates */
+ if (xrates != NULL || rates == NULL)
+ return 0;
+ for (i = 0; i < rates[1]; i++) {
+ int r = rates[2+i] & IEEE80211_RATE_VAL;
+ if (r > 2*11 || ((1<<r) & brates) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+static void
+adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
+ int subtype, int rssi, int noise, uint32_t rstamp)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame *wh;
+ uint8_t *frm, *efrm, *sfrm;
+ uint8_t *ssid, *rates, *xrates;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (uint8_t *)&wh[1];
+ efrm = mtod(m0, uint8_t *) + m0->m_len;
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_BEACON: {
+ struct ieee80211_scanparams scan;
+ /*
+ * We process beacon/probe response
+ * frames to discover neighbors.
+ */
+ if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
+ return;
+ /*
+ * Count frame now that we know it's to be processed.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+ vap->iv_stats.is_rx_beacon++; /* XXX remove */
+ IEEE80211_NODE_STAT(ni, rx_beacons);
+ } else
+ IEEE80211_NODE_STAT(ni, rx_proberesp);
+ /*
+ * If scanning, just pass information to the scan module.
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
+ /*
+ * Actively scanning a channel marked passive;
+ * send a probe request now that we know there
+ * is 802.11 traffic present.
+ *
+ * XXX check if the beacon we recv'd gives
+ * us what we need and suppress the probe req
+ */
+ ieee80211_probe_curchan(vap, 1);
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
+ }
+ ieee80211_add_scan(vap, &scan, wh,
+ subtype, rssi, noise, rstamp);
+ return;
+ }
+ if (scan.capinfo & IEEE80211_CAPINFO_IBSS) {
+ if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
+ /*
+ * Create a new entry in the neighbor table.
+ */
+ ni = ieee80211_add_neighbor(vap, wh, &scan);
+ } else if (ni->ni_capinfo == 0) {
+ /*
+ * Update faked node created on transmit.
+ * Note this also updates the tsf.
+ */
+ ieee80211_init_neighbor(ni, wh, &scan);
+ } else {
+ /*
+ * Record tsf for potential resync.
+ */
+ memcpy(ni->ni_tstamp.data, scan.tstamp,
+ sizeof(ni->ni_tstamp));
+ }
+ if (ni != NULL) {
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ }
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
+ /* frame must be directed */
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */
+ return;
+ }
+
+ /*
+ * prreq frame format
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ */
+ ssid = rates = xrates = NULL;
+ sfrm = frm;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
+ if (xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(xrates,
+ IEEE80211_RATE_MAXSIZE - rates[1], return);
+ IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
+ IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return);
+ if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL,
+ "%s", "no ssid with ssid suppression enabled");
+ vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/
+ return;
+ }
+
+ /* XXX find a better class or define it's own */
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2,
+ "%s", "recv probe req");
+ /*
+ * Some legacy 11b clients cannot hack a complete
+ * probe response frame. When the request includes
+ * only a bare-bones rate set, communicate this to
+ * the transmit side.
+ */
+ ieee80211_send_proberesp(vap, wh->i_addr2,
+ is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0);
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_ACTION: {
+ const struct ieee80211_action *ia;
+
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ /*
+ * action frame format:
+ * [1] category
+ * [1] action
+ * [tlv] parameters
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action), return);
+ ia = (const struct ieee80211_action *) frm;
+
+ vap->iv_stats.is_rx_action++;
+ IEEE80211_NODE_STAT(ni, rx_action);
+
+ /* verify frame payloads but defer processing */
+ /* XXX maybe push this to method */
+ switch (ia->ia_category) {
+ case IEEE80211_ACTION_CAT_BA:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_addbarequest),
+ return);
+ break;
+ case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_addbaresponse),
+ return);
+ break;
+ case IEEE80211_ACTION_BA_DELBA:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_delba),
+ return);
+ break;
+ }
+ break;
+ case IEEE80211_ACTION_CAT_HT:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_HT_TXCHWIDTH:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ht_txchwidth),
+ return);
+ break;
+ }
+ break;
+ }
+ ic->ic_recv_action(ni, frm, efrm);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ case IEEE80211_FC0_SUBTYPE_DISASSOC:
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "mgt", "subtype 0x%x not handled", subtype);
+ vap->iv_stats.is_rx_badsubtype++;
+ break;
+ }
+}
+#undef IEEE80211_VERIFY_LENGTH
+#undef IEEE80211_VERIFY_ELEMENT
diff --git a/sys/net80211/ieee80211_adhoc.h b/sys/net80211/ieee80211_adhoc.h
new file mode 100644
index 0000000..d8e19e5
--- /dev/null
+++ b/sys/net80211/ieee80211_adhoc.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_ADHOC_H_
+#define _NET80211_IEEE80211_ADHOC_H_
+
+/*
+ * Adhoc-mode (ibss+ahdemo) implementation definitions.
+ */
+void ieee80211_adhoc_attach(struct ieee80211com *);
+void ieee80211_adhoc_detach(struct ieee80211com *);
+#endif /* !_NET80211_IEEE80211_STA_H_ */
diff --git a/sys/net80211/ieee80211_amrr.c b/sys/net80211/ieee80211_amrr.c
index 5b34768..a759e64 100644
--- a/sys/net80211/ieee80211_amrr.c
+++ b/sys/net80211/ieee80211_amrr.c
@@ -28,6 +28,8 @@ __FBSDID("$FreeBSD$");
* INRIA Sophia - Projet Planete
* http://www-sop.inria.fr/rapports/sophia/RR-5208.html
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
@@ -51,66 +53,90 @@ __FBSDID("$FreeBSD$");
((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
#define is_enough(amn) \
((amn)->amn_txcnt > 10)
-#define is_min_rate(ni) \
- ((ni)->ni_txrate == 0)
-#define is_max_rate(ni) \
- ((ni)->ni_txrate == (ni)->ni_rates.rs_nrates - 1)
-#define increase_rate(ni) \
- ((ni)->ni_txrate++)
-#define decrease_rate(ni) \
- ((ni)->ni_txrate--)
-#define reset_cnt(amn) \
- do { (amn)->amn_txcnt = (amn)->amn_retrycnt = 0; } while (0)
+
+static void amrr_sysctlattach(struct ieee80211_amrr *amrr,
+ struct sysctl_ctx_list *ctx, struct sysctl_oid *tree);
+
+/* number of references from net80211 layer */
+static int nrefs = 0;
+
+void
+ieee80211_amrr_setinterval(struct ieee80211_amrr *amrr, int msecs)
+{
+ int t;
+
+ if (msecs < 100)
+ msecs = 100;
+ t = msecs_to_ticks(msecs);
+ amrr->amrr_interval = (t < 1) ? 1 : t;
+}
void
ieee80211_amrr_init(struct ieee80211_amrr *amrr,
- struct ieee80211com *ic, int amin, int amax)
+ struct ieee80211vap *vap, int amin, int amax, int interval)
{
/* XXX bounds check? */
amrr->amrr_min_success_threshold = amin;
amrr->amrr_max_success_threshold = amax;
- amrr->amrr_ic = ic;
+ ieee80211_amrr_setinterval(amrr, interval);
+
+ amrr_sysctlattach(amrr, vap->iv_sysctl, vap->iv_oid);
+}
+
+void
+ieee80211_amrr_cleanup(struct ieee80211_amrr *amrr)
+{
}
void
ieee80211_amrr_node_init(struct ieee80211_amrr *amrr,
- struct ieee80211_amrr_node *amn)
+ struct ieee80211_amrr_node *amn, struct ieee80211_node *ni)
{
+ const struct ieee80211_rateset *rs = &ni->ni_rates;
+
+ amn->amn_amrr = amrr;
amn->amn_success = 0;
amn->amn_recovery = 0;
amn->amn_txcnt = amn->amn_retrycnt = 0;
amn->amn_success_threshold = amrr->amrr_min_success_threshold;
+
+ /* pick initial rate */
+ for (amn->amn_rix = rs->rs_nrates - 1;
+ amn->amn_rix > 0 && (rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) > 72;
+ amn->amn_rix--)
+ ;
+ ni->ni_txrate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
+ amn->amn_ticks = ticks;
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "AMRR initial rate %d", ni->ni_txrate);
}
-/*
- * Update ni->ni_txrate.
- */
-void
-ieee80211_amrr_choose(struct ieee80211_amrr *amrr, struct ieee80211_node *ni,
- struct ieee80211_amrr_node *amn)
+static int
+amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
+ struct ieee80211_node *ni)
{
- int need_change = 0;
+ int rix = amn->amn_rix;
+
+ KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
- if (is_success(amn) && is_enough(amn)) {
+ if (is_success(amn)) {
amn->amn_success++;
if (amn->amn_success >= amn->amn_success_threshold &&
- !is_max_rate(ni)) {
+ rix + 1 < ni->ni_rates.rs_nrates) {
amn->amn_recovery = 1;
amn->amn_success = 0;
- increase_rate(ni);
- IEEE80211_DPRINTF(amrr->amrr_ic, IEEE80211_MSG_RATECTL,
- "AMRR increasing rate %d (txcnt=%d "
- "retrycnt=%d)\n",
- ni->ni_rates.rs_rates[ni->ni_txrate] &
- IEEE80211_RATE_VAL,
+ rix++;
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
+ ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL,
amn->amn_txcnt, amn->amn_retrycnt);
- need_change = 1;
} else {
amn->amn_recovery = 0;
}
} else if (is_failure(amn)) {
amn->amn_success = 0;
- if (!is_min_rate(ni)) {
+ if (rix > 0) {
if (amn->amn_recovery) {
amn->amn_success_threshold *= 2;
if (amn->amn_success_threshold >
@@ -121,44 +147,80 @@ ieee80211_amrr_choose(struct ieee80211_amrr *amrr, struct ieee80211_node *ni,
amn->amn_success_threshold =
amrr->amrr_min_success_threshold;
}
- decrease_rate(ni);
- IEEE80211_DPRINTF(amrr->amrr_ic, IEEE80211_MSG_RATECTL,
- "AMRR decreasing rate %d (txcnt=%d "
- "retrycnt=%d)\n",
- ni->ni_rates.rs_rates[ni->ni_txrate] &
- IEEE80211_RATE_VAL,
+ rix--;
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
+ ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL,
amn->amn_txcnt, amn->amn_retrycnt);
- need_change = 1;
}
amn->amn_recovery = 0;
}
- if (is_enough(amn) || need_change)
- reset_cnt(amn);
+ /* reset counters */
+ amn->amn_txcnt = 0;
+ amn->amn_retrycnt = 0;
+
+ return rix;
}
/*
- * Module glue.
+ * Return the rate index to use in sending a data frame.
+ * Update our internal state if it's been long enough.
+ * If the rate changes we also update ni_txrate to match.
*/
+int
+ieee80211_amrr_choose(struct ieee80211_node *ni,
+ struct ieee80211_amrr_node *amn)
+{
+ struct ieee80211_amrr *amrr = amn->amn_amrr;
+ int rix;
+
+ if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
+ rix = amrr_update(amrr, amn, ni);
+ if (rix != amn->amn_rix) {
+ /* update public rate */
+ ni->ni_txrate =
+ ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL;
+ amn->amn_rix = rix;
+ }
+ amn->amn_ticks = ticks;
+ } else
+ rix = amn->amn_rix;
+ return rix;
+}
+
static int
-amrr_modevent(module_t mod, int type, void *unused)
+amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
{
- switch (type) {
- case MOD_LOAD:
- if (bootverbose)
- printf("wlan_amrr: <AMRR Transmit Rate Control Algorithm>\n");
- return 0;
- case MOD_UNLOAD:
- return 0;
- }
- return EINVAL;
+ struct ieee80211_amrr *amrr = arg1;
+ int msecs = ticks_to_msecs(amrr->amrr_interval);
+ int error;
+
+ error = sysctl_handle_int(oidp, &msecs, 0, req);
+ if (error || !req->newptr)
+ return error;
+ ieee80211_amrr_setinterval(amrr, msecs);
+ return 0;
+}
+
+static void
+amrr_sysctlattach(struct ieee80211_amrr *amrr,
+ struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
+{
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, amrr,
+ 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)");
+ /* XXX bounds check values */
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "amrr_max_sucess_threshold", CTLFLAG_RW,
+ &amrr->amrr_max_success_threshold, 0, "");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "amrr_min_sucess_threshold", CTLFLAG_RW,
+ &amrr->amrr_min_success_threshold, 0, "");
}
-static moduledata_t amrr_mod = {
- "wlan_amrr",
- amrr_modevent,
- 0
-};
-DECLARE_MODULE(wlan_amrr, amrr_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_amrr, 1);
-MODULE_DEPEND(wlan_amrr, wlan, 1, 1, 1);
+/*
+ * Module glue.
+ */
+IEEE80211_RATE_MODULE(amrr, 1);
diff --git a/sys/net80211/ieee80211_amrr.h b/sys/net80211/ieee80211_amrr.h
index 947d617..c03f699 100644
--- a/sys/net80211/ieee80211_amrr.h
+++ b/sys/net80211/ieee80211_amrr.h
@@ -32,12 +32,12 @@
/*
* Rate control settings.
*/
-struct ieee80211com;
+struct ieee80211vap;
struct ieee80211_amrr {
u_int amrr_min_success_threshold;
u_int amrr_max_success_threshold;
- struct ieee80211com *amrr_ic;
+ int amrr_interval; /* update interval (ticks) */
};
#define IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD 1
@@ -47,18 +47,55 @@ struct ieee80211_amrr {
* Rate control state for a given node.
*/
struct ieee80211_amrr_node {
+ struct ieee80211_amrr *amn_amrr;/* backpointer */
+ int amn_rix; /* current rate index */
+ int amn_ticks; /* time of last update */
+ /* statistics */
+ u_int amn_txcnt;
u_int amn_success;
- u_int amn_recovery;
u_int amn_success_threshold;
- u_int amn_txcnt;
+ u_int amn_recovery;
u_int amn_retrycnt;
};
-void ieee80211_amrr_init(struct ieee80211_amrr *,
- struct ieee80211com *ic, int, int);
+void ieee80211_amrr_init(struct ieee80211_amrr *, struct ieee80211vap *,
+ int, int, int);
+void ieee80211_amrr_cleanup(struct ieee80211_amrr *);
+void ieee80211_amrr_setinterval(struct ieee80211_amrr *, int);
void ieee80211_amrr_node_init(struct ieee80211_amrr *,
+ struct ieee80211_amrr_node *, struct ieee80211_node *);
+int ieee80211_amrr_choose(struct ieee80211_node *,
struct ieee80211_amrr_node *);
-void ieee80211_amrr_choose(struct ieee80211_amrr *, struct ieee80211_node *,
- struct ieee80211_amrr_node *);
+#define IEEE80211_AMRR_SUCCESS 1
+#define IEEE80211_AMRR_FAILURE 0
+
+/*
+ * Update statistics with tx complete status. Ok is non-zero
+ * if the packet is known to be ACK'd. Retries has the number
+ * retransmissions (i.e. xmit attempts - 1).
+ */
+static __inline void
+ieee80211_amrr_tx_complete(struct ieee80211_amrr_node *amn,
+ int ok, int retries)
+{
+ amn->amn_txcnt++;
+ if (ok)
+ amn->amn_success++;
+ amn->amn_retrycnt += retries;
+}
+
+/*
+ * Set tx count/retry statistics explicitly. Intended for
+ * drivers that poll the device for statistics maintained
+ * in the device.
+ */
+static __inline void
+ieee80211_amrr_tx_update(struct ieee80211_amrr_node *amn,
+ int txcnt, int success, int retrycnt)
+{
+ amn->amn_txcnt = txcnt;
+ amn->amn_success = success;
+ amn->amn_retrycnt = retrycnt;
+}
#endif /* _NET80211_IEEE80211_AMRR_H_ */
diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c
index 83d7c3f..d644d0c 100644
--- a/sys/net80211/ieee80211_crypto.c
+++ b/sys/net80211/ieee80211_crypto.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,7 +30,11 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 generic crypto support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
@@ -41,23 +45,25 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
+MALLOC_DEFINE(M_80211_CRYPTO, "80211crypto", "802.11 crypto state");
+
+static int _ieee80211_crypto_delkey(struct ieee80211vap *,
+ struct ieee80211_key *);
+
/*
* Table of registered cipher modules.
*/
static const struct ieee80211_cipher *ciphers[IEEE80211_CIPHER_MAX];
-static int _ieee80211_crypto_delkey(struct ieee80211com *,
- struct ieee80211_key *);
-
/*
* Default "null" key management routines.
*/
static int
-null_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
+null_key_alloc(struct ieee80211vap *vap, const struct ieee80211_key *k,
ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
{
- if (!(&ic->ic_nw_keys[0] <= k &&
- k < &ic->ic_nw_keys[IEEE80211_WEP_NKID])) {
+ if (!(&vap->iv_nw_keys[0] <= k &&
+ k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) {
/*
* Not in the global key table, the driver should handle this
* by allocating a slot in the h/w key table/cache. In
@@ -72,23 +78,23 @@ null_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
return 0;
*keyix = 0; /* NB: use key index 0 for ucast key */
} else {
- *keyix = k - ic->ic_nw_keys;
+ *keyix = k - vap->iv_nw_keys;
}
*rxkeyix = IEEE80211_KEYIX_NONE; /* XXX maybe *keyix? */
return 1;
}
static int
-null_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
+null_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
{
return 1;
}
static int
-null_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
+null_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k,
const uint8_t mac[IEEE80211_ADDR_LEN])
{
return 1;
}
-static void null_key_update(struct ieee80211com *ic) {}
+static void null_key_update(struct ieee80211vap *vap) {}
/*
* Write-arounds for common operations.
@@ -100,70 +106,86 @@ cipher_detach(struct ieee80211_key *key)
}
static __inline void *
-cipher_attach(struct ieee80211com *ic, struct ieee80211_key *key)
+cipher_attach(struct ieee80211vap *vap, struct ieee80211_key *key)
{
- return key->wk_cipher->ic_attach(ic, key);
+ return key->wk_cipher->ic_attach(vap, key);
}
/*
* Wrappers for driver key management methods.
*/
static __inline int
-dev_key_alloc(struct ieee80211com *ic,
+dev_key_alloc(struct ieee80211vap *vap,
const struct ieee80211_key *key,
ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
{
- return ic->ic_crypto.cs_key_alloc(ic, key, keyix, rxkeyix);
+ return vap->iv_key_alloc(vap, key, keyix, rxkeyix);
}
static __inline int
-dev_key_delete(struct ieee80211com *ic,
+dev_key_delete(struct ieee80211vap *vap,
const struct ieee80211_key *key)
{
- return ic->ic_crypto.cs_key_delete(ic, key);
+ return vap->iv_key_delete(vap, key);
}
static __inline int
-dev_key_set(struct ieee80211com *ic, const struct ieee80211_key *key,
+dev_key_set(struct ieee80211vap *vap, const struct ieee80211_key *key,
const uint8_t mac[IEEE80211_ADDR_LEN])
{
- return ic->ic_crypto.cs_key_set(ic, key, mac);
+ return vap->iv_key_set(vap, key, mac);
}
/*
- * Setup crypto support.
+ * Setup crypto support for a device/shared instance.
*/
void
ieee80211_crypto_attach(struct ieee80211com *ic)
{
- struct ieee80211_crypto_state *cs = &ic->ic_crypto;
+ /* NB: we assume everything is pre-zero'd */
+ ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none;
+}
+
+/*
+ * Teardown crypto support.
+ */
+void
+ieee80211_crypto_detach(struct ieee80211com *ic)
+{
+}
+
+/*
+ * Setup crypto support for a vap.
+ */
+void
+ieee80211_crypto_vattach(struct ieee80211vap *vap)
+{
int i;
/* NB: we assume everything is pre-zero'd */
- cs->cs_def_txkey = IEEE80211_KEYIX_NONE;
- cs->cs_max_keyix = IEEE80211_WEP_NKID;
- ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none;
+ vap->iv_max_keyix = IEEE80211_WEP_NKID;
+ vap->iv_def_txkey = IEEE80211_KEYIX_NONE;
for (i = 0; i < IEEE80211_WEP_NKID; i++)
- ieee80211_crypto_resetkey(ic, &cs->cs_nw_keys[i],
+ ieee80211_crypto_resetkey(vap, &vap->iv_nw_keys[i],
IEEE80211_KEYIX_NONE);
/*
* Initialize the driver key support routines to noop entries.
* This is useful especially for the cipher test modules.
*/
- cs->cs_key_alloc = null_key_alloc;
- cs->cs_key_set = null_key_set;
- cs->cs_key_delete = null_key_delete;
- cs->cs_key_update_begin = null_key_update;
- cs->cs_key_update_end = null_key_update;
+ vap->iv_key_alloc = null_key_alloc;
+ vap->iv_key_set = null_key_set;
+ vap->iv_key_delete = null_key_delete;
+ vap->iv_key_update_begin = null_key_update;
+ vap->iv_key_update_end = null_key_update;
}
/*
- * Teardown crypto support.
+ * Teardown crypto support for a vap.
*/
void
-ieee80211_crypto_detach(struct ieee80211com *ic)
+ieee80211_crypto_vdetach(struct ieee80211vap *vap)
{
- ieee80211_crypto_delglobalkeys(ic);
+ ieee80211_crypto_delglobalkeys(vap);
}
/*
@@ -213,12 +235,14 @@ ieee80211_crypto_available(u_int cipher)
}
/* XXX well-known names! */
-static const char *cipher_modnames[] = {
+static const char *cipher_modnames[IEEE80211_CIPHER_MAX] = {
"wlan_wep", /* IEEE80211_CIPHER_WEP */
"wlan_tkip", /* IEEE80211_CIPHER_TKIP */
"wlan_aes_ocb", /* IEEE80211_CIPHER_AES_OCB */
"wlan_ccmp", /* IEEE80211_CIPHER_AES_CCM */
+ "#4", /* reserved */
"wlan_ckip", /* IEEE80211_CIPHER_CKIP */
+ "wlan_none", /* IEEE80211_CIPHER_NONE */
};
/*
@@ -231,14 +255,14 @@ static const char *cipher_modnames[] = {
* routines assume wk_cipher is setup.
*
* Locking must be handled by the caller using:
- * ieee80211_key_update_begin(ic);
- * ieee80211_key_update_end(ic);
+ * ieee80211_key_update_begin(vap);
+ * ieee80211_key_update_end(vap);
*/
int
-ieee80211_crypto_newkey(struct ieee80211com *ic,
+ieee80211_crypto_newkey(struct ieee80211vap *vap,
int cipher, int flags, struct ieee80211_key *key)
{
-#define N(a) (sizeof(a) / sizeof(a[0]))
+ struct ieee80211com *ic = vap->iv_ic;
const struct ieee80211_cipher *cip;
ieee80211_keyix keyix, rxkeyix;
void *keyctx;
@@ -248,9 +272,9 @@ ieee80211_crypto_newkey(struct ieee80211com *ic,
* Validate cipher and set reference to cipher routines.
*/
if (cipher >= IEEE80211_CIPHER_MAX) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "%s: invalid cipher %u\n", __func__, cipher);
- ic->ic_stats.is_crypto_badcipher++;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
+ "%s: invalid cipher %u\n", __func__, cipher);
+ vap->iv_stats.is_crypto_badcipher++;
return 0;
}
cip = ciphers[cipher];
@@ -261,25 +285,21 @@ ieee80211_crypto_newkey(struct ieee80211com *ic,
* than numbers and craft a module name based on the cipher
* name; e.g. wlan_cipher_<cipher-name>.
*/
- if (cipher < N(cipher_modnames)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "%s: unregistered cipher %u, load module %s\n",
- __func__, cipher, cipher_modnames[cipher]);
- ieee80211_load_module(cipher_modnames[cipher]);
- /*
- * If cipher module loaded it should immediately
- * call ieee80211_crypto_register which will fill
- * in the entry in the ciphers array.
- */
- cip = ciphers[cipher];
- }
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
+ "%s: unregistered cipher %u, load module %s\n",
+ __func__, cipher, cipher_modnames[cipher]);
+ ieee80211_load_module(cipher_modnames[cipher]);
+ /*
+ * If cipher module loaded it should immediately
+ * call ieee80211_crypto_register which will fill
+ * in the entry in the ciphers array.
+ */
+ cip = ciphers[cipher];
if (cip == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "%s: unable to load cipher %u, module %s\n",
- __func__, cipher,
- cipher < N(cipher_modnames) ?
- cipher_modnames[cipher] : "<unknown>");
- ic->ic_stats.is_crypto_nocipher++;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
+ "%s: unable to load cipher %u, module %s\n",
+ __func__, cipher, cipher_modnames[cipher]);
+ vap->iv_stats.is_crypto_nocipher++;
return 0;
}
}
@@ -290,8 +310,8 @@ ieee80211_crypto_newkey(struct ieee80211com *ic,
* If the hardware does not support the cipher then
* fallback to a host-based implementation.
*/
- if ((ic->ic_caps & (1<<cipher)) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ if ((ic->ic_cryptocaps & (1<<cipher)) == 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: no h/w support for cipher %s, falling back to s/w\n",
__func__, cip->ic_name);
flags |= IEEE80211_KEY_SWCRYPT;
@@ -302,8 +322,8 @@ ieee80211_crypto_newkey(struct ieee80211com *ic,
* the cipher modules honor it.
*/
if (cipher == IEEE80211_CIPHER_TKIP &&
- (ic->ic_caps & IEEE80211_C_TKIPMIC) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ (ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIPMIC) == 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: no h/w support for TKIP MIC, falling back to s/w\n",
__func__);
flags |= IEEE80211_KEY_SWMIC;
@@ -327,13 +347,13 @@ again:
* fails and we try to restore previous state.
*/
key->wk_flags = flags;
- keyctx = cip->ic_attach(ic, key);
+ keyctx = cip->ic_attach(vap, key);
if (keyctx == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: unable to attach cipher %s\n",
__func__, cip->ic_name);
key->wk_flags = oflags; /* restore old flags */
- ic->ic_stats.is_crypto_attachfail++;
+ vap->iv_stats.is_crypto_attachfail++;
return 0;
}
cipher_detach(key);
@@ -354,7 +374,7 @@ again:
* crypto we also call the driver to give us a key index.
*/
if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
- if (!dev_key_alloc(ic, key, &keyix, &rxkeyix)) {
+ if (!dev_key_alloc(vap, key, &keyix, &rxkeyix)) {
/*
* Driver has no room; fallback to doing crypto
* in the host. We change the flags and start the
@@ -364,8 +384,8 @@ again:
* continues to use it.
*/
if ((key->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
- ic->ic_stats.is_crypto_swfallback++;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ vap->iv_stats.is_crypto_swfallback++;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: no h/w resources for cipher %s, "
"falling back to s/w\n", __func__,
cip->ic_name);
@@ -375,8 +395,8 @@ again:
flags |= IEEE80211_KEY_SWMIC;
goto again;
}
- ic->ic_stats.is_crypto_keyfail++;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ vap->iv_stats.is_crypto_keyfail++;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: unable to setup cipher %s\n",
__func__, cip->ic_name);
return 0;
@@ -385,24 +405,24 @@ again:
key->wk_rxkeyix = rxkeyix;
}
return 1;
-#undef N
}
/*
* Remove the key (no locking, for internal use).
*/
static int
-_ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
+_ieee80211_crypto_delkey(struct ieee80211vap *vap, struct ieee80211_key *key)
{
ieee80211_keyix keyix;
KASSERT(key->wk_cipher != NULL, ("No cipher!"));
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: %s keyix %u flags 0x%x rsc %ju tsc %ju len %u\n",
__func__, key->wk_cipher->ic_name,
key->wk_keyix, key->wk_flags,
- key->wk_keyrsc, key->wk_keytsc, key->wk_keylen);
+ key->wk_keyrsc[IEEE80211_NONQOS_TID], key->wk_keytsc,
+ key->wk_keylen);
keyix = key->wk_keyix;
if (keyix != IEEE80211_KEYIX_NONE) {
@@ -410,17 +430,17 @@ _ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
* Remove hardware entry.
*/
/* XXX key cache */
- if (!dev_key_delete(ic, key)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ if (!dev_key_delete(vap, key)) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: driver did not delete key index %u\n",
__func__, keyix);
- ic->ic_stats.is_crypto_delkey++;
+ vap->iv_stats.is_crypto_delkey++;
/* XXX recovery? */
}
}
cipher_detach(key);
memset(key, 0, sizeof(*key));
- ieee80211_crypto_resetkey(ic, key, IEEE80211_KEYIX_NONE);
+ ieee80211_crypto_resetkey(vap, key, IEEE80211_KEYIX_NONE);
return 1;
}
@@ -428,13 +448,13 @@ _ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
* Remove the specified key.
*/
int
-ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
+ieee80211_crypto_delkey(struct ieee80211vap *vap, struct ieee80211_key *key)
{
int status;
- ieee80211_key_update_begin(ic);
- status = _ieee80211_crypto_delkey(ic, key);
- ieee80211_key_update_end(ic);
+ ieee80211_key_update_begin(vap);
+ status = _ieee80211_crypto_delkey(vap, key);
+ ieee80211_key_update_end(vap);
return status;
}
@@ -442,66 +462,67 @@ ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
* Clear the global key table.
*/
void
-ieee80211_crypto_delglobalkeys(struct ieee80211com *ic)
+ieee80211_crypto_delglobalkeys(struct ieee80211vap *vap)
{
int i;
- ieee80211_key_update_begin(ic);
+ ieee80211_key_update_begin(vap);
for (i = 0; i < IEEE80211_WEP_NKID; i++)
- (void) _ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[i]);
- ieee80211_key_update_end(ic);
+ (void) _ieee80211_crypto_delkey(vap, &vap->iv_nw_keys[i]);
+ ieee80211_key_update_end(vap);
}
/*
* Set the contents of the specified key.
*
* Locking must be handled by the caller using:
- * ieee80211_key_update_begin(ic);
- * ieee80211_key_update_end(ic);
+ * ieee80211_key_update_begin(vap);
+ * ieee80211_key_update_end(vap);
*/
int
-ieee80211_crypto_setkey(struct ieee80211com *ic, struct ieee80211_key *key,
+ieee80211_crypto_setkey(struct ieee80211vap *vap, struct ieee80211_key *key,
const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
const struct ieee80211_cipher *cip = key->wk_cipher;
KASSERT(cip != NULL, ("No cipher!"));
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: %s keyix %u flags 0x%x mac %s rsc %ju tsc %ju len %u\n",
__func__, cip->ic_name, key->wk_keyix,
key->wk_flags, ether_sprintf(macaddr),
- key->wk_keyrsc, key->wk_keytsc, key->wk_keylen);
+ key->wk_keyrsc[IEEE80211_NONQOS_TID], key->wk_keytsc,
+ key->wk_keylen);
/*
* Give cipher a chance to validate key contents.
* XXX should happen before modifying state.
*/
if (!cip->ic_setkey(key)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: cipher %s rejected key index %u len %u flags 0x%x\n",
__func__, cip->ic_name, key->wk_keyix,
key->wk_keylen, key->wk_flags);
- ic->ic_stats.is_crypto_setkey_cipher++;
+ vap->iv_stats.is_crypto_setkey_cipher++;
return 0;
}
if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
/* XXX nothing allocated, should not happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: no key index; should not happen!\n", __func__);
- ic->ic_stats.is_crypto_setkey_nokey++;
+ vap->iv_stats.is_crypto_setkey_nokey++;
return 0;
}
- return dev_key_set(ic, key, macaddr);
+ return dev_key_set(vap, key, macaddr);
}
/*
* Add privacy headers appropriate for the specified key.
*/
struct ieee80211_key *
-ieee80211_crypto_encap(struct ieee80211com *ic,
- struct ieee80211_node *ni, struct mbuf *m)
+ieee80211_crypto_encap(struct ieee80211_node *ni, struct mbuf *m)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_key *k;
struct ieee80211_frame *wh;
const struct ieee80211_cipher *cip;
@@ -516,16 +537,16 @@ ieee80211_crypto_encap(struct ieee80211com *ic,
wh = mtod(m, struct ieee80211_frame *);
if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) {
- if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] no default transmit key (%s) deftxkey %u\n",
- ether_sprintf(wh->i_addr1), __func__,
- ic->ic_def_txkey);
- ic->ic_stats.is_tx_nodefkey++;
+ if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
+ wh->i_addr1,
+ "no default transmit key (%s) deftxkey %u",
+ __func__, vap->iv_def_txkey);
+ vap->iv_stats.is_tx_nodefkey++;
return NULL;
}
- keyid = ic->ic_def_txkey;
- k = &ic->ic_nw_keys[ic->ic_def_txkey];
+ keyid = vap->iv_def_txkey;
+ k = &vap->iv_nw_keys[vap->iv_def_txkey];
} else {
keyid = 0;
k = &ni->ni_ucastkey;
@@ -539,25 +560,24 @@ ieee80211_crypto_encap(struct ieee80211com *ic,
* received frame that has the WEP/Privacy bit set.
*/
struct ieee80211_key *
-ieee80211_crypto_decap(struct ieee80211com *ic,
- struct ieee80211_node *ni, struct mbuf *m, int hdrlen)
+ieee80211_crypto_decap(struct ieee80211_node *ni, struct mbuf *m, int hdrlen)
{
#define IEEE80211_WEP_HDRLEN (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
#define IEEE80211_WEP_MINLEN \
(sizeof(struct ieee80211_frame) + \
IEEE80211_WEP_HDRLEN + IEEE80211_WEP_CRCLEN)
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_key *k;
struct ieee80211_frame *wh;
const struct ieee80211_cipher *cip;
- const uint8_t *ivp;
uint8_t keyid;
/* NB: this minimum size data frame could be bigger */
if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
"%s: WEP data frame too short, len %u\n",
__func__, m->m_pkthdr.len);
- ic->ic_stats.is_rx_tooshort++; /* XXX need unique stat? */
+ vap->iv_stats.is_rx_tooshort++; /* XXX need unique stat? */
return NULL;
}
@@ -568,11 +588,10 @@ ieee80211_crypto_decap(struct ieee80211com *ic,
* the key id in the header is meaningless (typically 0).
*/
wh = mtod(m, struct ieee80211_frame *);
- ivp = mtod(m, const uint8_t *) + hdrlen; /* XXX contig */
- keyid = ivp[IEEE80211_WEP_IVLEN];
+ m_copydata(m, hdrlen + IEEE80211_WEP_IVLEN, sizeof(keyid), &keyid);
if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey))
- k = &ic->ic_nw_keys[keyid >> 6];
+ k = &vap->iv_nw_keys[keyid >> 6];
else
k = &ni->ni_ucastkey;
@@ -582,10 +601,9 @@ ieee80211_crypto_decap(struct ieee80211com *ic,
cip = k->wk_cipher;
if (m->m_len < hdrlen + cip->ic_header &&
(m = m_pullup(m, hdrlen + cip->ic_header)) == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] unable to pullup %s header\n",
- ether_sprintf(wh->i_addr2), cip->ic_name);
- ic->ic_stats.is_rx_wepfail++; /* XXX */
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "unable to pullup %s header", cip->ic_name);
+ vap->iv_stats.is_rx_wepfail++; /* XXX */
return NULL;
}
diff --git a/sys/net80211/ieee80211_crypto.h b/sys/net80211/ieee80211_crypto.h
index ee99caa..999f139 100644
--- a/sys/net80211/ieee80211_crypto.h
+++ b/sys/net80211/ieee80211_crypto.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,6 +42,15 @@ struct ieee80211_wepkey {
uint8_t wk_key[IEEE80211_KEYBUF_SIZE];
};
+struct ieee80211_rsnparms {
+ uint8_t rsn_mcastcipher; /* mcast/group cipher */
+ uint8_t rsn_mcastkeylen; /* mcast key length */
+ uint8_t rsn_ucastcipher; /* selected unicast cipher */
+ uint8_t rsn_ucastkeylen; /* unicast key length */
+ uint8_t rsn_keymgmt; /* selected key mgmt algo */
+ uint16_t rsn_caps; /* capabilities */
+};
+
struct ieee80211_cipher;
/*
@@ -76,7 +85,8 @@ struct ieee80211_key {
uint8_t wk_key[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
#define wk_txmic wk_key+IEEE80211_KEYBUF_SIZE+0 /* XXX can't () right */
#define wk_rxmic wk_key+IEEE80211_KEYBUF_SIZE+8 /* XXX can't () right */
- uint64_t wk_keyrsc; /* key receive sequence counter */
+ /* key receive sequence counter */
+ uint64_t wk_keyrsc[IEEE80211_TID_SIZE];
uint64_t wk_keytsc; /* key transmit sequence counter */
const struct ieee80211_cipher *wk_cipher;
void *wk_private; /* private cipher state */
@@ -84,61 +94,54 @@ struct ieee80211_key {
#define IEEE80211_KEY_COMMON /* common flags passed in by apps */\
(IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV | IEEE80211_KEY_GROUP)
+#define IEEE80211_KEYIX_NONE ((ieee80211_keyix) -1)
+
/*
* NB: these values are ordered carefully; there are lots of
- * of implications in any reordering. In particular beware
- * that 4 is not used to avoid conflicting with IEEE80211_F_PRIVACY.
+ * of implications in any reordering. Beware that 4 is used
+ * only to indicate h/w TKIP MIC support in driver capabilities;
+ * there is no separate cipher support (it's rolled into the
+ * TKIP cipher support).
*/
#define IEEE80211_CIPHER_WEP 0
#define IEEE80211_CIPHER_TKIP 1
#define IEEE80211_CIPHER_AES_OCB 2
#define IEEE80211_CIPHER_AES_CCM 3
+#define IEEE80211_CIPHER_TKIPMIC 4 /* TKIP MIC capability */
#define IEEE80211_CIPHER_CKIP 5
#define IEEE80211_CIPHER_NONE 6 /* pseudo value */
#define IEEE80211_CIPHER_MAX (IEEE80211_CIPHER_NONE+1)
-#define IEEE80211_KEYIX_NONE ((ieee80211_keyix) -1)
+/* capability bits in ic_cryptocaps/iv_cryptocaps */
+#define IEEE80211_CRYPTO_WEP (1<<IEEE80211_CIPHER_WEP)
+#define IEEE80211_CRYPTO_TKIP (1<<IEEE80211_CIPHER_TKIP)
+#define IEEE80211_CRYPTO_AES_OCB (1<<IEEE80211_CIPHER_AES_OCB)
+#define IEEE80211_CRYPTO_AES_CCM (1<<IEEE80211_CIPHER_AES_CCM)
+#define IEEE80211_CRYPTO_TKIPMIC (1<<IEEE80211_CIPHER_TKIPMIC)
+#define IEEE80211_CRYPTO_CKIP (1<<IEEE80211_CIPHER_CKIP)
#if defined(__KERNEL__) || defined(_KERNEL)
struct ieee80211com;
+struct ieee80211vap;
struct ieee80211_node;
struct mbuf;
-/*
- * Crypto state kept in each ieee80211com. Some of this
- * can/should be shared when virtual AP's are supported.
- *
- * XXX save reference to ieee80211com to properly encapsulate state.
- * XXX split out crypto capabilities from ic_caps
- */
-struct ieee80211_crypto_state {
- struct ieee80211_key cs_nw_keys[IEEE80211_WEP_NKID];
- ieee80211_keyix cs_def_txkey; /* default/group tx key index */
- uint16_t cs_max_keyix; /* max h/w key index */
-
- int (*cs_key_alloc)(struct ieee80211com *,
- const struct ieee80211_key *,
- ieee80211_keyix *, ieee80211_keyix *);
- int (*cs_key_delete)(struct ieee80211com *,
- const struct ieee80211_key *);
- int (*cs_key_set)(struct ieee80211com *,
- const struct ieee80211_key *,
- const uint8_t mac[IEEE80211_ADDR_LEN]);
- void (*cs_key_update_begin)(struct ieee80211com *);
- void (*cs_key_update_end)(struct ieee80211com *);
-};
+MALLOC_DECLARE(M_80211_CRYPTO);
void ieee80211_crypto_attach(struct ieee80211com *);
void ieee80211_crypto_detach(struct ieee80211com *);
-int ieee80211_crypto_newkey(struct ieee80211com *,
+void ieee80211_crypto_vattach(struct ieee80211vap *);
+void ieee80211_crypto_vdetach(struct ieee80211vap *);
+int ieee80211_crypto_newkey(struct ieee80211vap *,
int cipher, int flags, struct ieee80211_key *);
-int ieee80211_crypto_delkey(struct ieee80211com *,
+int ieee80211_crypto_delkey(struct ieee80211vap *,
struct ieee80211_key *);
-int ieee80211_crypto_setkey(struct ieee80211com *,
- struct ieee80211_key *, const uint8_t macaddr[IEEE80211_ADDR_LEN]);
-void ieee80211_crypto_delglobalkeys(struct ieee80211com *);
+int ieee80211_crypto_setkey(struct ieee80211vap *,
+ struct ieee80211_key *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+void ieee80211_crypto_delglobalkeys(struct ieee80211vap *);
/*
* Template for a supported cipher. Ciphers register with the
@@ -152,7 +155,7 @@ struct ieee80211_cipher {
u_int ic_header; /* size of privacy header (bytes) */
u_int ic_trailer; /* size of privacy trailer (bytes) */
u_int ic_miclen; /* size of mic trailer (bytes) */
- void* (*ic_attach)(struct ieee80211com *, struct ieee80211_key *);
+ void* (*ic_attach)(struct ieee80211vap *, struct ieee80211_key *);
void (*ic_detach)(struct ieee80211_key *);
int (*ic_setkey)(struct ieee80211_key *);
int (*ic_encap)(struct ieee80211_key *, struct mbuf *,
@@ -170,16 +173,16 @@ void ieee80211_crypto_register(const struct ieee80211_cipher *);
void ieee80211_crypto_unregister(const struct ieee80211_cipher *);
int ieee80211_crypto_available(u_int cipher);
-struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *);
-struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *, int);
+struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211_node *,
+ struct mbuf *);
+struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211_node *,
+ struct mbuf *, int);
/*
* Check and remove any MIC.
*/
static __inline int
-ieee80211_crypto_demic(struct ieee80211com *ic, struct ieee80211_key *k,
+ieee80211_crypto_demic(struct ieee80211vap *vap, struct ieee80211_key *k,
struct mbuf *m, int force)
{
const struct ieee80211_cipher *cip = k->wk_cipher;
@@ -190,7 +193,7 @@ ieee80211_crypto_demic(struct ieee80211com *ic, struct ieee80211_key *k,
* Add any MIC.
*/
static __inline int
-ieee80211_crypto_enmic(struct ieee80211com *ic,
+ieee80211_crypto_enmic(struct ieee80211vap *vap,
struct ieee80211_key *k, struct mbuf *m, int force)
{
const struct ieee80211_cipher *cip = k->wk_cipher;
@@ -203,11 +206,11 @@ ieee80211_crypto_enmic(struct ieee80211com *ic,
* key data) is properly setup before a key is used.
*/
static __inline void
-ieee80211_crypto_resetkey(struct ieee80211com *ic,
+ieee80211_crypto_resetkey(struct ieee80211vap *vap,
struct ieee80211_key *k, ieee80211_keyix ix)
{
k->wk_cipher = &ieee80211_cipher_none;;
- k->wk_private = k->wk_cipher->ic_attach(ic, k);
+ k->wk_private = k->wk_cipher->ic_attach(vap, k);
k->wk_keyix = k->wk_rxkeyix = ix;
k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
}
@@ -215,10 +218,10 @@ ieee80211_crypto_resetkey(struct ieee80211com *ic,
/*
* Crypt-related notification methods.
*/
-void ieee80211_notify_replay_failure(struct ieee80211com *,
+void ieee80211_notify_replay_failure(struct ieee80211vap *,
const struct ieee80211_frame *, const struct ieee80211_key *,
- u_int64_t rsc);
-void ieee80211_notify_michael_failure(struct ieee80211com *,
+ uint64_t rsc);
+void ieee80211_notify_michael_failure(struct ieee80211vap *,
const struct ieee80211_frame *, u_int keyix);
#endif /* defined(__KERNEL__) || defined(_KERNEL) */
#endif /* _NET80211_IEEE80211_CRYPTO_H_ */
diff --git a/sys/net80211/ieee80211_crypto_ccmp.c b/sys/net80211/ieee80211_crypto_ccmp.c
index 525fe9a..d3b1af5 100644
--- a/sys/net80211/ieee80211_crypto_ccmp.c
+++ b/sys/net80211/ieee80211_crypto_ccmp.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,6 +33,8 @@ __FBSDID("$FreeBSD$");
* AP driver. The code is used with the consent of the author and
* it's license is included below.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -53,11 +55,12 @@ __FBSDID("$FreeBSD$");
#define AES_BLOCK_LEN 16
struct ccmp_ctx {
- struct ieee80211com *cc_ic; /* for diagnostics */
+ struct ieee80211vap *cc_vap; /* for diagnostics+statistics */
+ struct ieee80211com *cc_ic;
rijndael_ctx cc_aes;
};
-static void *ccmp_attach(struct ieee80211com *, struct ieee80211_key *);
+static void *ccmp_attach(struct ieee80211vap *, struct ieee80211_key *);
static void ccmp_detach(struct ieee80211_key *);
static int ccmp_setkey(struct ieee80211_key *);
static int ccmp_encap(struct ieee80211_key *k, struct mbuf *, uint8_t keyid);
@@ -89,17 +92,18 @@ static int ccmp_decrypt(struct ieee80211_key *, u_int64_t pn,
static int nrefs = 0;
static void *
-ccmp_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+ccmp_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
{
struct ccmp_ctx *ctx;
MALLOC(ctx, struct ccmp_ctx *, sizeof(struct ccmp_ctx),
- M_DEVBUF, M_NOWAIT | M_ZERO);
+ M_80211_CRYPTO, M_NOWAIT | M_ZERO);
if (ctx == NULL) {
- ic->ic_stats.is_crypto_nomem++;
+ vap->iv_stats.is_crypto_nomem++;
return NULL;
}
- ctx->cc_ic = ic;
+ ctx->cc_vap = vap;
+ ctx->cc_ic = vap->iv_ic;
nrefs++; /* NB: we assume caller locking */
return ctx;
}
@@ -109,7 +113,7 @@ ccmp_detach(struct ieee80211_key *k)
{
struct ccmp_ctx *ctx = k->wk_private;
- FREE(ctx, M_DEVBUF);
+ FREE(ctx, M_80211_CRYPTO);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
}
@@ -120,7 +124,7 @@ ccmp_setkey(struct ieee80211_key *k)
struct ccmp_ctx *ctx = k->wk_private;
if (k->wk_keylen != (128/NBBY)) {
- IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO,
"%s: Invalid key length %u, expecting %u\n",
__func__, k->wk_keylen, 128/NBBY);
return 0;
@@ -200,8 +204,9 @@ static int
ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
struct ccmp_ctx *ctx = k->wk_private;
+ struct ieee80211vap *vap = ctx->cc_vap;
struct ieee80211_frame *wh;
- uint8_t *ivp;
+ uint8_t *ivp, tid;
uint64_t pn;
/*
@@ -214,19 +219,19 @@ ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
/*
* No extended IV; discard frame.
*/
- IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
- "[%s] Missing ExtIV for AES-CCM cipher\n",
- ether_sprintf(wh->i_addr2));
- ctx->cc_ic->ic_stats.is_rx_ccmpformat++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s", "missing ExtIV for AES-CCM cipher");
+ vap->iv_stats.is_rx_ccmpformat++;
return 0;
}
+ tid = ieee80211_gettid(wh);
pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);
- if (pn <= k->wk_keyrsc) {
+ if (pn <= k->wk_keyrsc[tid]) {
/*
* Replay violation.
*/
- ieee80211_notify_replay_failure(ctx->cc_ic, wh, k, pn);
- ctx->cc_ic->ic_stats.is_rx_ccmpreplay++;
+ ieee80211_notify_replay_failure(vap, wh, k, pn);
+ vap->iv_stats.is_rx_ccmpreplay++;
return 0;
}
@@ -251,7 +256,7 @@ ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
/*
* Ok to update rsc now.
*/
- k->wk_keyrsc = pn;
+ k->wk_keyrsc[tid] = pn;
return 1;
}
@@ -406,7 +411,7 @@ ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
e[AES_BLOCK_LEN], s0[AES_BLOCK_LEN];
uint8_t *pos;
- ctx->cc_ic->ic_stats.is_crypto_ccmp++;
+ ctx->cc_vap->iv_stats.is_crypto_ccmp++;
wh = mtod(m, struct ieee80211_frame *);
data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header);
@@ -544,6 +549,7 @@ static int
ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen)
{
struct ccmp_ctx *ctx = key->wk_private;
+ struct ieee80211vap *vap = ctx->cc_vap;
struct ieee80211_frame *wh;
uint8_t aad[2 * AES_BLOCK_LEN];
uint8_t b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], a[AES_BLOCK_LEN];
@@ -553,7 +559,7 @@ ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen
uint8_t *pos;
u_int space;
- ctx->cc_ic->ic_stats.is_crypto_ccmp++;
+ ctx->cc_vap->iv_stats.is_crypto_ccmp++;
wh = mtod(m, struct ieee80211_frame *);
data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header + ccmp.ic_trailer);
@@ -616,10 +622,9 @@ ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen
}
}
if (memcmp(mic, a, ccmp.ic_trailer) != 0) {
- IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
- "[%s] AES-CCM decrypt failed; MIC mismatch\n",
- ether_sprintf(wh->i_addr2));
- ctx->cc_ic->ic_stats.is_rx_ccmpmic++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s", "AES-CCM decrypt failed; MIC mismatch");
+ vap->iv_stats.is_rx_ccmpmic++;
return 0;
}
return 1;
diff --git a/sys/net80211/ieee80211_crypto_none.c b/sys/net80211/ieee80211_crypto_none.c
index 7fbb53d..b1ffbb4 100644
--- a/sys/net80211/ieee80211_crypto_none.c
+++ b/sys/net80211/ieee80211_crypto_none.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,10 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 NULL crypto support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
+#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/module.h>
@@ -42,7 +45,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
-static void *none_attach(struct ieee80211com *, struct ieee80211_key *);
+static void *none_attach(struct ieee80211vap *, struct ieee80211_key *);
static void none_detach(struct ieee80211_key *);
static int none_setkey(struct ieee80211_key *);
static int none_encap(struct ieee80211_key *, struct mbuf *, uint8_t);
@@ -66,9 +69,9 @@ const struct ieee80211_cipher ieee80211_cipher_none = {
};
static void *
-none_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+none_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
{
- return ic; /* for diagnostics+stats */
+ return vap; /* for diagnostics+stats */
}
static void
@@ -87,7 +90,7 @@ none_setkey(struct ieee80211_key *k)
static int
none_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
{
- struct ieee80211com *ic = k->wk_private;
+ struct ieee80211vap *vap = k->wk_private;
#ifdef IEEE80211_DEBUG
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
#endif
@@ -96,17 +99,16 @@ none_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
* The specified key is not setup; this can
* happen, at least, when changing keys.
*/
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] key id %u is not set (encap)\n",
- ether_sprintf(wh->i_addr1), keyid>>6);
- ic->ic_stats.is_tx_badcipher++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr1,
+ "key id %u is not set (encap)", keyid>>6);
+ vap->iv_stats.is_tx_badcipher++;
return 0;
}
static int
none_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
- struct ieee80211com *ic = k->wk_private;
+ struct ieee80211vap *vap = k->wk_private;
#ifdef IEEE80211_DEBUG
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
const uint8_t *ivp = (const uint8_t *)&wh[1];
@@ -117,27 +119,26 @@ none_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
* happen, at least, when changing keys.
*/
/* XXX useful to know dst too */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] key id %u is not set (decap)\n",
- ether_sprintf(wh->i_addr2), ivp[IEEE80211_WEP_IVLEN] >> 6);
- ic->ic_stats.is_rx_badkeyid++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "key id %u is not set (decap)", ivp[IEEE80211_WEP_IVLEN] >> 6);
+ vap->iv_stats.is_rx_badkeyid++;
return 0;
}
static int
none_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
{
- struct ieee80211com *ic = k->wk_private;
+ struct ieee80211vap *vap = k->wk_private;
- ic->ic_stats.is_tx_badcipher++;
+ vap->iv_stats.is_tx_badcipher++;
return 0;
}
static int
none_demic(struct ieee80211_key *k, struct mbuf *m, int force)
{
- struct ieee80211com *ic = k->wk_private;
+ struct ieee80211vap *vap = k->wk_private;
- ic->ic_stats.is_rx_badkeyid++;
+ vap->iv_stats.is_rx_badkeyid++;
return 0;
}
diff --git a/sys/net80211/ieee80211_crypto_tkip.c b/sys/net80211/ieee80211_crypto_tkip.c
index 327306a..398246f 100644
--- a/sys/net80211/ieee80211_crypto_tkip.c
+++ b/sys/net80211/ieee80211_crypto_tkip.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,6 +33,8 @@ __FBSDID("$FreeBSD$");
* AP driver. The code is used with the consent of the author and
* it's license is included below.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -49,7 +51,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
-static void *tkip_attach(struct ieee80211com *, struct ieee80211_key *);
+static void *tkip_attach(struct ieee80211vap *, struct ieee80211_key *);
static void tkip_detach(struct ieee80211_key *);
static int tkip_setkey(struct ieee80211_key *);
static int tkip_encap(struct ieee80211_key *, struct mbuf *m, uint8_t keyid);
@@ -77,10 +79,9 @@ typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t __u32;
typedef uint32_t u32;
-#define memmove(dst, src, n) ovbcopy(src, dst, n)
struct tkip_ctx {
- struct ieee80211com *tc_ic; /* for diagnostics */
+ struct ieee80211vap *tc_vap; /* for diagnostics+statistics */
u16 tx_ttak[5];
int tx_phase1_done;
@@ -104,18 +105,18 @@ static int tkip_decrypt(struct tkip_ctx *, struct ieee80211_key *,
static int nrefs = 0;
static void *
-tkip_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+tkip_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
{
struct tkip_ctx *ctx;
MALLOC(ctx, struct tkip_ctx *, sizeof(struct tkip_ctx),
- M_DEVBUF, M_NOWAIT | M_ZERO);
+ M_80211_CRYPTO, M_NOWAIT | M_ZERO);
if (ctx == NULL) {
- ic->ic_stats.is_crypto_nomem++;
+ vap->iv_stats.is_crypto_nomem++;
return NULL;
}
- ctx->tc_ic = ic;
+ ctx->tc_vap = vap;
nrefs++; /* NB: we assume caller locking */
return ctx;
}
@@ -125,7 +126,7 @@ tkip_detach(struct ieee80211_key *k)
{
struct tkip_ctx *ctx = k->wk_private;
- FREE(ctx, M_DEVBUF);
+ FREE(ctx, M_80211_CRYPTO);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
}
@@ -137,7 +138,7 @@ tkip_setkey(struct ieee80211_key *k)
if (k->wk_keylen != (128/NBBY)) {
(void) ctx; /* XXX */
- IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(ctx->tc_vap, IEEE80211_MSG_CRYPTO,
"%s: Invalid key length %u, expecting %u\n",
__func__, k->wk_keylen, 128/NBBY);
return 0;
@@ -153,22 +154,22 @@ static int
tkip_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
{
struct tkip_ctx *ctx = k->wk_private;
- struct ieee80211com *ic = ctx->tc_ic;
+ struct ieee80211vap *vap = ctx->tc_vap;
+ struct ieee80211com *ic = vap->iv_ic;
uint8_t *ivp;
int hdrlen;
/*
* Handle TKIP counter measures requirement.
*/
- if (ic->ic_flags & IEEE80211_F_COUNTERM) {
+ if (vap->iv_flags & IEEE80211_F_COUNTERM) {
#ifdef IEEE80211_DEBUG
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
#endif
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] Discard frame due to countermeasures (%s)\n",
- ether_sprintf(wh->i_addr2), __func__);
- ic->ic_stats.is_crypto_tkipcm++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "discard frame due to countermeasures (%s)", __func__);
+ vap->iv_stats.is_crypto_tkipcm++;
return 0;
}
hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
@@ -215,11 +216,12 @@ tkip_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
if (force || (k->wk_flags & IEEE80211_KEY_SWMIC)) {
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
- struct ieee80211com *ic = ctx->tc_ic;
+ struct ieee80211vap *vap = ctx->tc_vap;
+ struct ieee80211com *ic = vap->iv_ic;
int hdrlen;
uint8_t mic[IEEE80211_WEP_MICLEN];
- ic->ic_stats.is_crypto_tkipenmic++;
+ vap->iv_stats.is_crypto_tkipenmic++;
hdrlen = ieee80211_hdrspace(ic, wh);
@@ -247,9 +249,9 @@ static int
tkip_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
struct tkip_ctx *ctx = k->wk_private;
- struct ieee80211com *ic = ctx->tc_ic;
+ struct ieee80211vap *vap = ctx->tc_vap;
struct ieee80211_frame *wh;
- uint8_t *ivp;
+ uint8_t *ivp, tid;
/*
* Header should have extended IV and sequence number;
@@ -261,30 +263,29 @@ tkip_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
/*
* No extended IV; discard frame.
*/
- IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
- "[%s] missing ExtIV for TKIP cipher\n",
- ether_sprintf(wh->i_addr2));
- ctx->tc_ic->ic_stats.is_rx_tkipformat++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s", "missing ExtIV for TKIP cipher");
+ vap->iv_stats.is_rx_tkipformat++;
return 0;
}
/*
* Handle TKIP counter measures requirement.
*/
- if (ic->ic_flags & IEEE80211_F_COUNTERM) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] discard frame due to countermeasures (%s)\n",
- ether_sprintf(wh->i_addr2), __func__);
- ic->ic_stats.is_crypto_tkipcm++;
+ if (vap->iv_flags & IEEE80211_F_COUNTERM) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "discard frame due to countermeasures (%s)", __func__);
+ vap->iv_stats.is_crypto_tkipcm++;
return 0;
}
+ tid = ieee80211_gettid(wh);
ctx->rx_rsc = READ_6(ivp[2], ivp[0], ivp[4], ivp[5], ivp[6], ivp[7]);
- if (ctx->rx_rsc <= k->wk_keyrsc) {
+ if (ctx->rx_rsc <= k->wk_keyrsc[tid]) {
/*
* Replay violation; notify upper layer.
*/
- ieee80211_notify_replay_failure(ctx->tc_ic, wh, k, ctx->rx_rsc);
- ctx->tc_ic->ic_stats.is_rx_tkipreplay++;
+ ieee80211_notify_replay_failure(vap, wh, k, ctx->rx_rsc);
+ vap->iv_stats.is_rx_tkipreplay++;
return 0;
}
/*
@@ -322,15 +323,17 @@ static int
tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force)
{
struct tkip_ctx *ctx = k->wk_private;
+ struct ieee80211_frame *wh;
+ uint8_t tid;
+ wh = mtod(m, struct ieee80211_frame *);
if (force || (k->wk_flags & IEEE80211_KEY_SWMIC)) {
- struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
- struct ieee80211com *ic = ctx->tc_ic;
- int hdrlen = ieee80211_hdrspace(ic, wh);
+ struct ieee80211vap *vap = ctx->tc_vap;
+ int hdrlen = ieee80211_hdrspace(vap->iv_ic, wh);
u8 mic[IEEE80211_WEP_MICLEN];
u8 mic0[IEEE80211_WEP_MICLEN];
- ic->ic_stats.is_crypto_tkipdemic++;
+ vap->iv_stats.is_crypto_tkipdemic++;
michael_mic(ctx, k->wk_rxmic,
m, hdrlen, m->m_pkthdr.len - (hdrlen + tkip.ic_miclen),
@@ -339,7 +342,7 @@ tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force)
tkip.ic_miclen, mic0);
if (memcmp(mic, mic0, tkip.ic_miclen)) {
/* NB: 802.11 layer handles statistic and debug msg */
- ieee80211_notify_michael_failure(ic, wh,
+ ieee80211_notify_michael_failure(vap, wh,
k->wk_rxkeyix != IEEE80211_KEYIX_NONE ?
k->wk_rxkeyix : k->wk_keyix);
return 0;
@@ -353,7 +356,8 @@ tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force)
/*
* Ok to update rsc now that MIC has been verified.
*/
- k->wk_keyrsc = ctx->rx_rsc;
+ tid = ieee80211_gettid(wh);
+ k->wk_keyrsc[tid] = ctx->rx_rsc;
return 1;
}
@@ -904,7 +908,7 @@ tkip_encrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
struct ieee80211_frame *wh;
uint8_t icv[IEEE80211_WEP_CRCLEN];
- ctx->tc_ic->ic_stats.is_crypto_tkip++;
+ ctx->tc_vap->iv_stats.is_crypto_tkip++;
wh = mtod(m, struct ieee80211_frame *);
if (!ctx->tx_phase1_done) {
@@ -932,17 +936,20 @@ tkip_decrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
struct mbuf *m, int hdrlen)
{
struct ieee80211_frame *wh;
+ struct ieee80211vap *vap = ctx->tc_vap;
u32 iv32;
u16 iv16;
+ u8 tid;
- ctx->tc_ic->ic_stats.is_crypto_tkip++;
+ vap->iv_stats.is_crypto_tkip++;
wh = mtod(m, struct ieee80211_frame *);
/* NB: tkip_decap already verified header and left seq in rx_rsc */
iv16 = (u16) ctx->rx_rsc;
iv32 = (u32) (ctx->rx_rsc >> 16);
- if (iv32 != (u32)(key->wk_keyrsc >> 16) || !ctx->rx_phase1_done) {
+ tid = ieee80211_gettid(wh);
+ if (iv32 != (u32)(key->wk_keyrsc[tid] >> 16) || !ctx->rx_phase1_done) {
tkip_mixing_phase1(ctx->rx_ttak, key->wk_key,
wh->i_addr2, iv32);
ctx->rx_phase1_done = 1;
@@ -953,15 +960,14 @@ tkip_decrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
if (wep_decrypt(ctx->rx_rc4key,
m, hdrlen + tkip.ic_header,
m->m_pkthdr.len - (hdrlen + tkip.ic_header + tkip.ic_trailer))) {
- if (iv32 != (u32)(key->wk_keyrsc >> 16)) {
+ if (iv32 != (u32)(key->wk_keyrsc[tid] >> 16)) {
/* Previously cached Phase1 result was already lost, so
* it needs to be recalculated for the next packet. */
ctx->rx_phase1_done = 0;
}
- IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
- "[%s] TKIP ICV mismatch on decrypt\n",
- ether_sprintf(wh->i_addr2));
- ctx->tc_ic->ic_stats.is_rx_tkipicv++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s", "TKIP ICV mismatch on decrypt");
+ vap->iv_stats.is_rx_tkipicv++;
return 0;
}
return 1;
diff --git a/sys/net80211/ieee80211_crypto_wep.c b/sys/net80211/ieee80211_crypto_wep.c
index 81d15cc..df988a6 100644
--- a/sys/net80211/ieee80211_crypto_wep.c
+++ b/sys/net80211/ieee80211_crypto_wep.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 WEP crypto support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -45,7 +47,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
-static void *wep_attach(struct ieee80211com *, struct ieee80211_key *);
+static void *wep_attach(struct ieee80211vap *, struct ieee80211_key *);
static void wep_detach(struct ieee80211_key *);
static int wep_setkey(struct ieee80211_key *);
static int wep_encap(struct ieee80211_key *, struct mbuf *, uint8_t keyid);
@@ -72,7 +74,8 @@ static int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
static int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
struct wep_ctx {
- struct ieee80211com *wc_ic; /* for diagnostics */
+ struct ieee80211vap *wc_vap; /* for diagnostics+statistics */
+ struct ieee80211com *wc_ic;
uint32_t wc_iv; /* initial vector for crypto */
};
@@ -80,18 +83,19 @@ struct wep_ctx {
static int nrefs = 0;
static void *
-wep_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+wep_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
{
struct wep_ctx *ctx;
MALLOC(ctx, struct wep_ctx *, sizeof(struct wep_ctx),
- M_DEVBUF, M_NOWAIT | M_ZERO);
+ M_80211_CRYPTO, M_NOWAIT | M_ZERO);
if (ctx == NULL) {
- ic->ic_stats.is_crypto_nomem++;
+ vap->iv_stats.is_crypto_nomem++;
return NULL;
}
- ctx->wc_ic = ic;
+ ctx->wc_vap = vap;
+ ctx->wc_ic = vap->iv_ic;
get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv));
nrefs++; /* NB: we assume caller locking */
return ctx;
@@ -102,7 +106,7 @@ wep_detach(struct ieee80211_key *k)
{
struct wep_ctx *ctx = k->wk_private;
- FREE(ctx, M_DEVBUF);
+ FREE(ctx, M_80211_CRYPTO);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
}
@@ -208,6 +212,7 @@ static int
wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
struct wep_ctx *ctx = k->wk_private;
+ struct ieee80211vap *vap = ctx->wc_vap;
struct ieee80211_frame *wh;
wh = mtod(m, struct ieee80211_frame *);
@@ -219,10 +224,9 @@ wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
*/
if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
!wep_decrypt(k, m, hdrlen)) {
- IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO,
- "[%s] WEP ICV mismatch on decrypt\n",
- ether_sprintf(wh->i_addr2));
- ctx->wc_ic->ic_stats.is_rx_wepfail++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s", "WEP ICV mismatch on decrypt");
+ vap->iv_stats.is_rx_wepfail++;
return 0;
}
@@ -305,6 +309,7 @@ wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
{
#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
struct wep_ctx *ctx = key->wk_private;
+ struct ieee80211vap *vap = ctx->wc_vap;
struct mbuf *m = m0;
uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
uint8_t icv[IEEE80211_WEP_CRCLEN];
@@ -314,7 +319,7 @@ wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
uint8_t *pos;
u_int off, keylen;
- ctx->wc_ic->ic_stats.is_crypto_wep++;
+ vap->iv_stats.is_crypto_wep++;
/* NB: this assumes the header was pulled up */
memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
@@ -351,12 +356,12 @@ wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
}
if (m->m_next == NULL) {
if (data_len != 0) { /* out of data */
- IEEE80211_DPRINTF(ctx->wc_ic,
- IEEE80211_MSG_CRYPTO,
- "[%s] out of data for WEP (data_len %zu)\n",
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
ether_sprintf(mtod(m0,
struct ieee80211_frame *)->i_addr2),
+ "out of data for WEP (data_len %zu)",
data_len);
+ /* XXX stat */
return 0;
}
break;
@@ -387,6 +392,7 @@ wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
{
#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
struct wep_ctx *ctx = key->wk_private;
+ struct ieee80211vap *vap = ctx->wc_vap;
struct mbuf *m = m0;
uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
uint8_t icv[IEEE80211_WEP_CRCLEN];
@@ -396,7 +402,7 @@ wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
uint8_t *pos;
u_int off, keylen;
- ctx->wc_ic->ic_stats.is_crypto_wep++;
+ vap->iv_stats.is_crypto_wep++;
/* NB: this assumes the header was pulled up */
memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
@@ -435,11 +441,9 @@ wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
m = m->m_next;
if (m == NULL) {
if (data_len != 0) { /* out of data */
- IEEE80211_DPRINTF(ctx->wc_ic,
- IEEE80211_MSG_CRYPTO,
- "[%s] out of data for WEP (data_len %zu)\n",
- ether_sprintf(mtod(m0,
- struct ieee80211_frame *)->i_addr2),
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
+ mtod(m0, struct ieee80211_frame *)->i_addr2,
+ "out of data for WEP (data_len %zu)",
data_len);
return 0;
}
diff --git a/sys/net80211/ieee80211_ddb.c b/sys/net80211/ieee80211_ddb.c
new file mode 100644
index 0000000..5b78283
--- /dev/null
+++ b/sys/net80211/ieee80211_ddb.c
@@ -0,0 +1,789 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ddb.h"
+#include "opt_wlan.h"
+
+#ifdef DDB
+/*
+ * IEEE 802.11 DDB support
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <ddb/ddb.h>
+
+#define IEEE80211_MSG_BITS \
+ "\20\3IOCTL\4WDS\5ACTION\6RATECTL\7ROAM\10INACT\11DOTH\12SUPERG" \
+ "\13WME\14ACL\15WPA\16RADKEYS\17RADDUMP\20RADIUS\21DOT1X\22POWER" \
+ "\23STATE\24OUTPUT\25SCAN\26AUTH\27ASSOC\30NODE\31ELEMID\32XRATE" \
+ "\33INPUT\34CRYPTO\35DUPMPKTS\36DEBUG\3711N"
+
+#define IEEE80211_F_BITS \
+ "\20\1TURBOP\2COMP\3FF\4BURST\5PRIVACY\6PUREG\10SCAN\11ASCAN\12SIBSS" \
+ "\13SHSLOT\14PMGTON\15DESBSSID\16WME\17BGSCAN\20SWRETRY\21TXPOW_FIXED" \
+ "\22IBSSON\23SHPREAMBLE\24DATAPAD\25USEPROT\26USERBARKER\27CSAPENDING" \
+ "\30WPA1\31WPA2\32DROPUNENC\33COUNTERM\34HIDESSID\35NOBRIDG\36PCF" \
+ "\37DOTH\40DWDS"
+
+#define IEEE80211_FEXT_BITS \
+ "\20\1NONHT_PR\2INACT\3SCANWAIT\4BGSCAN\5WPS\6TSN\7SCANREQ\12NONEPR_PR"\
+ "\13SWBMISS\14DFS\15DOTD\22WDSLEGACY\23PROBECHAN\24HT\25AMDPU_TX" \
+ "\26AMPDU_TX\27AMSDU_TX\30AMSDU_RX\31USEHT40\32PUREN\33SHORTGI20" \
+ "\34SHORTGI40\35HTCOMPAT"
+
+#define IEEE80211_FVEN_BITS "\20"
+
+#define IEEE80211_C_BITS \
+ "\20\7FF\10TURBOP\11IBSS\12PMGT" \
+ "\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \
+ "\21MONITOR\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \
+ "\37TXFRAG"
+
+#define IEEE80211_C_CRYPTO_BITS \
+ "\20\1WEP\2TKIP\3AES\4AES_CCM\5TKIPMIC\6CKIP\12PMGT"
+
+#define IEEE80211_C_HTCAP_BITS \
+ "\20\1LDPC\2CHWIDTH40\5GREENFIELD\6SHORTGI20\7SHORTGI40\10TXSTBC" \
+ "\21AMPDU\22AMSDU\23HT"
+
+/* NB: policy bits not included */
+#define IEEE80211_CHAN_BITS \
+ "\20\5TURBO\6CCK\7OFDM\0102GHZ\0115GHZ\12PASSIVE\13DYN\14GFSK" \
+ "\15STURBO\16HALF\17QUARTER\20HT20\21HT40U\22HT40D\23DFS"
+
+#define IEEE80211_NODE_BITS \
+ "\20\1AUTH\2QOS\3ERP\5PWR_MGT\6AREF\7HT\10HTCOMPAT\11WPS\12TSN" \
+ "\13AMPDU_RX\14AMPDU_TX"
+
+#define IEEE80211_ERP_BITS \
+ "\20\1NON_ERP_PRESENT\2USE_PROTECTION\3LONG_PREAMBLE"
+
+#define IEEE80211_CAPINFO_BITS \
+ "\20\1ESS\2IBSS\3CF_POLLABLE\4CF_POLLREQ\5PRIVACY\6SHORT_PREAMBLE" \
+ "\7PBCC\10CHNL_AGILITY\11SPECTRUM_MGMT\13SHORT_SLOTTIME\14RSN" \
+ "\16DSSOFDM"
+
+#define IEEE80211_HTCAP_BITS \
+ "\20\1LDPC\2CHWIDTH40\5GREENFIELD\6SHORTGI20\7SHORTGI40\10TXSTBC" \
+ "\13DELBA\14AMSDU(7935)\15DSSSCCK40\16PSMP\1740INTOLERANT" \
+ "\20LSIGTXOPPROT"
+
+#define IEEE80211_AGGR_BITS \
+ "\20\1IMMEDIATE\2XCHGPEND\3RUNNING\4SETUP\5NAK"
+
+static void _db_show_sta(const struct ieee80211_node *);
+static void _db_show_vap(const struct ieee80211vap *, int);
+static void _db_show_com(const struct ieee80211com *,
+ int showvaps, int showsta, int showprocs);
+
+static void _db_show_channel(const char *tag, const struct ieee80211_channel *);
+static void _db_show_ssid(const char *tag, int ix, int len, const uint8_t *);
+static void _db_show_appie(const char *tag, const struct ieee80211_appie *);
+static void _db_show_key(const char *tag, int ix, const struct ieee80211_key *);
+static void _db_show_roamparams(const char *tag, const void *arg,
+ const struct ieee80211_roamparam *rp);
+static void _db_show_txparams(const char *tag, const void *arg,
+ const struct ieee80211_txparam *tp);
+static void _db_show_stats(const struct ieee80211_stats *);
+
+DB_SHOW_COMMAND(sta, db_show_sta)
+{
+ if (!have_addr) {
+ db_printf("usage: show sta <addr>\n");
+ return;
+ }
+ _db_show_sta((const struct ieee80211_node *) addr);
+}
+
+DB_SHOW_COMMAND(vap, db_show_vap)
+{
+ int i, showprocs = 0;
+
+ if (!have_addr) {
+ db_printf("usage: show vap <addr>\n");
+ return;
+ }
+ for (i = 0; modif[i] != '\0'; i++)
+ switch (modif[i]) {
+ case 'a':
+ showprocs = 1;
+ break;
+ case 'p':
+ showprocs = 1;
+ break;
+ }
+ _db_show_vap((const struct ieee80211vap *) addr, showprocs);
+}
+
+DB_SHOW_COMMAND(com, db_show_com)
+{
+ const struct ieee80211com *ic;
+ int i, showprocs = 0, showvaps = 0, showsta = 0;
+
+ if (!have_addr) {
+ db_printf("usage: show com <addr>\n");
+ return;
+ }
+ for (i = 0; modif[i] != '\0'; i++)
+ switch (modif[i]) {
+ case 'a':
+ showsta = showvaps = showprocs = 1;
+ break;
+ case 's':
+ showsta = 1;
+ break;
+ case 'v':
+ showvaps = 1;
+ break;
+ case 'p':
+ showprocs = 1;
+ break;
+ }
+
+ ic = (const struct ieee80211com *) addr;
+ _db_show_com(ic, showvaps, showsta, showprocs);
+}
+
+DB_SHOW_ALL_COMMAND(vaps, db_show_all_vaps)
+{
+ const struct ifnet *ifp;
+ int i, showall = 0;
+
+ for (i = 0; modif[i] != '\0'; i++)
+ switch (modif[i]) {
+ case 'a':
+ showall = 1;
+ break;
+ }
+
+ TAILQ_FOREACH(ifp, &ifnet, if_list)
+ if (ifp->if_type == IFT_IEEE80211) {
+ const struct ieee80211com *ic = ifp->if_l2com;
+
+ if (!showall) {
+ const struct ieee80211vap *vap;
+ db_printf("%s: com %p vaps:",
+ ifp->if_xname, ic);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ db_printf(" %s(%p)",
+ vap->iv_ifp->if_xname, vap);
+ db_printf("\n");
+ } else
+ _db_show_com(ic, 1, 1, 1);
+ }
+}
+
+static void
+_db_show_txampdu(const char *sep, int ix, const struct ieee80211_tx_ampdu *tap)
+{
+ db_printf("%stxampdu[%d]: %p flags %b ac %u\n",
+ sep, ix, tap, tap->txa_flags, IEEE80211_AGGR_BITS, tap->txa_ac);
+ db_printf("%s token %u qbytes %d qframes %d seqstart %u start %u wnd %u\n",
+ sep, tap->txa_token, tap->txa_qbytes, tap->txa_qframes,
+ tap->txa_seqstart, tap->txa_start, tap->txa_wnd);
+ db_printf("%s attempts %d nextrequest %d\n",
+ sep, tap->txa_attempts, tap->txa_nextrequest);
+ /* XXX packet q + timer */
+}
+
+static void
+_db_show_rxampdu(const char *sep, int ix, const struct ieee80211_rx_ampdu *rap)
+{
+ db_printf("%srxampdu[%d]: %p flags 0x%x tid %u\n",
+ sep, ix, rap, rap->rxa_flags, ix /*XXX */);
+ db_printf("%s qbytes %d qframes %d seqstart %u start %u wnd %u\n",
+ sep, rap->rxa_qbytes, rap->rxa_qframes,
+ rap->rxa_seqstart, rap->rxa_start, rap->rxa_wnd);
+ db_printf("%s age %d nframes %d\n",
+ sep, rap->rxa_age, rap->rxa_nframes);
+}
+
+static void
+_db_show_sta(const struct ieee80211_node *ni)
+{
+ int i;
+
+ db_printf("0x%p: mac %s refcnt %d\n", ni,
+ ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni));
+ db_printf("\tvap %p wdsvap %p ic %p table %p\n",
+ ni->ni_vap, ni->ni_wdsvap, ni->ni_ic, ni->ni_table);
+ db_printf("\tflags=%b\n", ni->ni_flags, IEEE80211_NODE_BITS);
+ db_printf("\tscangen %u authmode %u ath_flags 0x%x ath_defkeyix %u\n",
+ ni->ni_scangen, ni->ni_authmode,
+ ni->ni_ath_flags, ni->ni_ath_defkeyix);
+ db_printf("\tassocid 0x%x txpower %u vlan %u\n",
+ ni->ni_associd, ni->ni_txpower, ni->ni_vlan);
+ db_printf("\tjointime %d (%lu secs) challenge %p\n",
+ ni->ni_jointime, (unsigned long)(time_uptime - ni->ni_jointime),
+ ni->ni_challenge);
+ db_printf("\ties: data %p len %d\n", ni->ni_ies.data, ni->ni_ies.len);
+ db_printf("\t[wpa_ie %p rsn_ie %p wme_ie %p ath_ie %p\n",
+ ni->ni_ies.wpa_ie, ni->ni_ies.rsn_ie, ni->ni_ies.wme_ie,
+ ni->ni_ies.ath_ie);
+ db_printf("\t htcap_ie %p htinfo_ie %p]\n",
+ ni->ni_ies.htcap_ie, ni->ni_ies.htinfo_ie);
+ db_printf("\ttxseq %u rxseq %u fragno %u rxfragstamp %u\n",
+ ni->ni_txseqs[IEEE80211_NONQOS_TID],
+ ni->ni_rxseqs[IEEE80211_NONQOS_TID] >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[IEEE80211_NONQOS_TID] & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxfragstamp);
+ db_printf("\trxfrag[0] %p rxfrag[1] %p rxfrag[2] %p\n",
+ ni->ni_rxfrag[0], ni->ni_rxfrag[1], ni->ni_rxfrag[2]);
+ db_printf("\trstamp %u avgrssi 0x%x (rssi %d) noise %d\n",
+ ni->ni_rstamp, ni->ni_avgrssi,
+ IEEE80211_RSSI_GET(ni->ni_avgrssi), ni->ni_noise);
+ db_printf("\tintval %u capinfo %b\n",
+ ni->ni_intval, ni->ni_capinfo, IEEE80211_CAPINFO_BITS);
+ db_printf("\tbssid %s", ether_sprintf(ni->ni_bssid));
+ _db_show_ssid(" essid ", 0, ni->ni_esslen, ni->ni_essid);
+ db_printf("\n");
+ _db_show_channel("\tchannel", ni->ni_chan);
+ db_printf("\n");
+ db_printf("\terp %b dtim_period %u dtim_count %u\n",
+ ni->ni_erp, IEEE80211_ERP_BITS,
+ ni->ni_dtim_period, ni->ni_dtim_count);
+
+ db_printf("\thtcap %b htparam 0x%x htctlchan %u ht2ndchan %u\n",
+ ni->ni_htcap, IEEE80211_HTCAP_BITS,
+ ni->ni_htparam, ni->ni_htctlchan, ni->ni_ht2ndchan);
+ db_printf("\thtopmode 0x%x htstbc 0x%x reqcw %u chw %u\n",
+ ni->ni_htopmode, ni->ni_htstbc, ni->ni_reqcw, ni->ni_chw);
+
+ /* XXX ampdu state */
+ for (i = 0; i < WME_NUM_AC; i++)
+ if (ni->ni_tx_ampdu[i].txa_flags & IEEE80211_AGGR_SETUP)
+ _db_show_txampdu("\t", i, &ni->ni_tx_ampdu[i]);
+ for (i = 0; i < WME_NUM_TID; i++)
+ if (ni->ni_rx_ampdu[i].rxa_nframes)
+ _db_show_rxampdu("\t", i, &ni->ni_rx_ampdu[i]);
+
+ db_printf("\tinact %u inact_reload %u txrate %u\n",
+ ni->ni_inact, ni->ni_inact_reload, ni->ni_txrate);
+ /* XXX savedq */
+ /* XXX wdsq */
+}
+
+static void
+_db_show_vap(const struct ieee80211vap *vap, int showprocs)
+{
+ const struct ieee80211com *ic = vap->iv_ic;
+ int i;
+
+ db_printf("%p:", vap);
+ db_printf(" bss %p", vap->iv_bss);
+ db_printf(" myaddr %s", ether_sprintf(vap->iv_myaddr));
+ db_printf("\n");
+
+ db_printf("\topmode %s", ieee80211_opmode_name[vap->iv_opmode]);
+ db_printf(" state %s", ieee80211_state_name[vap->iv_state]);
+ db_printf(" ifp %p", vap->iv_ifp);
+ db_printf("\n");
+
+ db_printf("\tic %p", vap->iv_ic);
+ db_printf(" media %p", &vap->iv_media);
+ db_printf(" bpf_if %p", vap->iv_rawbpf);
+ db_printf(" mgtsend %p", &vap->iv_mgtsend);
+#if 0
+ struct sysctllog *iv_sysctl; /* dynamic sysctl context */
+#endif
+ db_printf("\n");
+ db_printf("\tdebug=%b\n", vap->iv_debug, IEEE80211_MSG_BITS);
+
+ db_printf("\tflags=%b\n", vap->iv_flags, IEEE80211_F_BITS);
+ db_printf("\tflags_ext=%b\n", vap->iv_flags_ext, IEEE80211_FEXT_BITS);
+ db_printf("\tflags_ven=%b\n", vap->iv_flags_ven, IEEE80211_FVEN_BITS);
+ db_printf("\tcaps=%b\n", vap->iv_caps, IEEE80211_C_BITS);
+ db_printf("\thtcaps=%b\n", vap->iv_htcaps, IEEE80211_C_HTCAP_BITS);
+
+ _db_show_stats(&vap->iv_stats);
+
+ db_printf("\tinact_init %d", vap->iv_inact_init);
+ db_printf(" inact_auth %d", vap->iv_inact_auth);
+ db_printf(" inact_run %d", vap->iv_inact_run);
+ db_printf(" inact_probe %d", vap->iv_inact_probe);
+ db_printf("\n");
+
+ db_printf("\tdes_nssid %d", vap->iv_des_nssid);
+ if (vap->iv_des_nssid)
+ _db_show_ssid(" des_ssid[%u] ", 0,
+ vap->iv_des_ssid[0].len, vap->iv_des_ssid[0].ssid);
+ db_printf(" des_bssid %s", ether_sprintf(vap->iv_des_bssid));
+ db_printf("\n");
+ db_printf("\tdes_mode %d", vap->iv_des_mode);
+ _db_show_channel(" des_chan", vap->iv_des_chan);
+ db_printf("\n");
+#if 0
+ int iv_nicknamelen; /* XXX junk */
+ uint8_t iv_nickname[IEEE80211_NWID_LEN];
+#endif
+ db_printf("\tbgscanidle %u", vap->iv_bgscanidle);
+ db_printf(" bgscanintvl %u", vap->iv_bgscanintvl);
+ db_printf(" scanvalid %u", vap->iv_scanvalid);
+ db_printf("\n");
+ db_printf("\tscanreq_duration %u", vap->iv_scanreq_duration);
+ db_printf(" scanreq_mindwell %u", vap->iv_scanreq_mindwell);
+ db_printf(" scanreq_maxdwell %u", vap->iv_scanreq_maxdwell);
+ db_printf("\n");
+ db_printf(" scanreq_flags 0x%x", vap->iv_scanreq_flags);
+ db_printf("\tscanreq_nssid %d", vap->iv_scanreq_nssid);
+ for (i = 0; i < vap->iv_scanreq_nssid; i++)
+ _db_show_ssid(" scanreq_ssid[%u]", i,
+ vap->iv_scanreq_ssid[i].len, vap->iv_scanreq_ssid[i].ssid);
+ db_printf(" roaming %d", vap->iv_roaming);
+ db_printf("\n");
+ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++)
+ if (isset(ic->ic_modecaps, i)) {
+ _db_show_roamparams("\troamparms[%s]",
+ ieee80211_phymode_name[i], &vap->iv_roamparms[i]);
+ db_printf("\n");
+ }
+
+ db_printf("\tbmissthreshold %u", vap->iv_bmissthreshold);
+ db_printf(" bmiss_max %u", vap->iv_bmiss_count);
+ db_printf(" bmiss_max %d", vap->iv_bmiss_max);
+ db_printf("\n");
+ db_printf("\tswbmiss_count %u", vap->iv_swbmiss_count);
+ db_printf(" swbmiss_period %u", vap->iv_swbmiss_period);
+ db_printf(" swbmiss %p", &vap->iv_swbmiss);
+ db_printf("\n");
+
+ db_printf("\tampdu_rxmax %d", vap->iv_ampdu_rxmax);
+ db_printf(" ampdu_density %d", vap->iv_ampdu_density);
+ db_printf(" ampdu_limit %d", vap->iv_ampdu_limit);
+ db_printf(" amsdu_limit %d", vap->iv_amsdu_limit);
+ db_printf("\n");
+
+ db_printf("\tmax_aid %u", vap->iv_max_aid);
+ db_printf(" aid_bitmap %p", vap->iv_aid_bitmap);
+ db_printf("\n");
+ db_printf("\tsta_assoc %u", vap->iv_sta_assoc);
+ db_printf(" ps_sta %u", vap->iv_ps_sta);
+ db_printf(" ps_pending %u", vap->iv_ps_pending);
+ db_printf(" tim_len %u", vap->iv_tim_len);
+ db_printf(" tim_bitmap %p", vap->iv_tim_bitmap);
+ db_printf("\n");
+ db_printf("\tdtim_period %u", vap->iv_dtim_period);
+ db_printf(" dtim_count %u", vap->iv_dtim_count);
+ db_printf(" set_tim %p", vap->iv_set_tim);
+ db_printf(" csa_count %d", vap->iv_csa_count);
+ db_printf("\n");
+
+ db_printf("\trtsthreshold %u", vap->iv_rtsthreshold);
+ db_printf(" fragthreshold %u", vap->iv_fragthreshold);
+ db_printf(" inact_timer %d", vap->iv_inact_timer);
+ db_printf("\n");
+ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++)
+ if (isset(ic->ic_modecaps, i)) {
+ _db_show_txparams("\ttxparms[%s]",
+ ieee80211_phymode_name[i], &vap->iv_txparms[i]);
+ db_printf("\n");
+ }
+
+ /* application-specified IE's to attach to mgt frames */
+ _db_show_appie("\tappie_beacon", vap->iv_appie_beacon);
+ _db_show_appie("\tappie_probereq", vap->iv_appie_probereq);
+ _db_show_appie("\tappie_proberesp", vap->iv_appie_proberesp);
+ _db_show_appie("\tappie_assocreq", vap->iv_appie_assocreq);
+ _db_show_appie("\tappie_asscoresp", vap->iv_appie_assocresp);
+ _db_show_appie("\tappie_wpa", vap->iv_appie_wpa);
+ if (vap->iv_wpa_ie != NULL || vap->iv_rsn_ie != NULL) {
+ if (vap->iv_wpa_ie != NULL)
+ db_printf("\twpa_ie %p", vap->iv_wpa_ie);
+ if (vap->iv_rsn_ie != NULL)
+ db_printf("\trsn_ie %p", vap->iv_rsn_ie);
+ db_printf("\n");
+ }
+ db_printf("\tmax_keyix %u", vap->iv_max_keyix);
+ db_printf(" def_txkey %d", vap->iv_def_txkey);
+ db_printf("\n");
+ for (i = 0; i < IEEE80211_WEP_NKID; i++)
+ _db_show_key("\tnw_keys[%u]", i, &vap->iv_nw_keys[i]);
+
+ db_printf("\tauth %p", vap->iv_auth);
+ db_printf(" ec %p", vap->iv_ec);
+
+ db_printf(" acl %p", vap->iv_acl);
+ db_printf(" as %p", vap->iv_as);
+ db_printf("\n");
+
+ if (showprocs) {
+ db_printf("\tiv_key_alloc %p\n", vap->iv_key_alloc);
+ db_printf("\tiv_key_delete %p\n", vap->iv_key_delete);
+ db_printf("\tiv_key_set %p\n", vap->iv_key_set);
+ db_printf("\tiv_key_update_begin %p\n", vap->iv_key_update_begin);
+ db_printf("\tiv_key_update_end %p\n", vap->iv_key_update_end);
+ db_printf("\tiv_opdetach %p\n", vap->iv_opdetach);
+ db_printf("\tiv_input %p\n", vap->iv_input);
+ db_printf("\tiv_recv_mgmt %p\n", vap->iv_recv_mgmt);
+ db_printf("\tiv_deliver_data %p\n", vap->iv_deliver_data);
+ db_printf("\tiv_bmiss %p\n", vap->iv_bmiss);
+ db_printf("\tiv_reset %p\n", vap->iv_reset);
+ db_printf("\tiv_update_beacon %p\n", vap->iv_update_beacon);
+ db_printf("\tiv_newstate %p\n", vap->iv_newstate);
+ db_printf("\tiv_output %p\n", vap->iv_output);
+ }
+}
+
+static void
+_db_show_com(const struct ieee80211com *ic, int showvaps, int showsta, int showprocs)
+{
+ struct ieee80211vap *vap;
+
+ db_printf("%p:", ic);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ db_printf(" %s(%p)", vap->iv_ifp->if_xname, vap);
+ db_printf("\n");
+ db_printf("\tifp %p", ic->ic_ifp);
+ db_printf(" comlock %p", &ic->ic_comlock);
+ db_printf("\n");
+ _db_show_stats(&ic->ic_stats);
+ db_printf("\theadroom %d", ic->ic_headroom);
+ db_printf(" phytype %d", ic->ic_phytype);
+ db_printf(" opmode %s", ieee80211_opmode_name[ic->ic_opmode]);
+ db_printf("\n");
+ db_printf("\tmedia %p", &ic->ic_media);
+ db_printf(" myaddr %s", ether_sprintf(ic->ic_myaddr));
+ db_printf(" inact %p", &ic->ic_inact);
+ db_printf("\n");
+
+ db_printf("\tflags=%b\n", ic->ic_flags, IEEE80211_F_BITS);
+ db_printf("\tflags_ext=%b\n", ic->ic_flags_ext, IEEE80211_FEXT_BITS);
+ db_printf("\tflags_ven=%b\n", ic->ic_flags_ven, IEEE80211_FVEN_BITS);
+ db_printf("\tcaps=%b\n", ic->ic_caps, IEEE80211_C_BITS);
+ db_printf("\tcryptocaps=%b\n",
+ ic->ic_cryptocaps, IEEE80211_C_CRYPTO_BITS);
+ db_printf("\thtcaps=%b\n", ic->ic_htcaps, IEEE80211_HTCAP_BITS);
+
+#if 0
+ uint8_t ic_modecaps[2]; /* set of mode capabilities */
+#endif
+ db_printf("\tcurmode %u", ic->ic_curmode);
+ db_printf(" promisc %u", ic->ic_promisc);
+ db_printf(" allmulti %u", ic->ic_allmulti);
+ db_printf(" nrunning %u", ic->ic_nrunning);
+ db_printf("\n");
+ db_printf("\tbintval %u", ic->ic_bintval);
+ db_printf(" lintval %u", ic->ic_lintval);
+ db_printf(" holdover %u", ic->ic_holdover);
+ db_printf(" txpowlimit %u", ic->ic_txpowlimit);
+ db_printf("\n");
+#if 0
+ struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
+#endif
+ /*
+ * Channel state:
+ *
+ * ic_channels is the set of available channels for the device;
+ * it is setup by the driver
+ * ic_nchans is the number of valid entries in ic_channels
+ * ic_chan_avail is a bit vector of these channels used to check
+ * whether a channel is available w/o searching the channel table.
+ * ic_chan_active is a (potentially) constrained subset of
+ * ic_chan_avail that reflects any mode setting or user-specified
+ * limit on the set of channels to use/scan
+ * ic_curchan is the current channel the device is set to; it may
+ * be different from ic_bsschan when we are off-channel scanning
+ * or otherwise doing background work
+ * ic_bsschan is the channel selected for operation; it may
+ * be undefined (IEEE80211_CHAN_ANYC)
+ * ic_prevchan is a cached ``previous channel'' used to optimize
+ * lookups when switching back+forth between two channels
+ * (e.g. for dynamic turbo)
+ */
+ db_printf("\tnchans %d", ic->ic_nchans);
+#if 0
+ struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1];
+ uint8_t ic_chan_avail[IEEE80211_CHAN_BYTES];
+ uint8_t ic_chan_active[IEEE80211_CHAN_BYTES];
+ uint8_t ic_chan_scan[IEEE80211_CHAN_BYTES];
+#endif
+ db_printf("\n");
+ _db_show_channel("\tcurchan", ic->ic_curchan);
+ db_printf("\n");
+ _db_show_channel("\tbsschan", ic->ic_bsschan);
+ db_printf("\n");
+ _db_show_channel("\tprevchan", ic->ic_prevchan);
+ db_printf("\n");
+ db_printf("\tregdomain %p", &ic->ic_regdomain);
+ db_printf("\n");
+
+ _db_show_channel("\tcsa_newchan", ic->ic_csa_newchan);
+ db_printf(" csa_count %d", ic->ic_csa_count);
+ db_printf( "dfs %p", &ic->ic_dfs);
+ db_printf("\n");
+
+ db_printf("\tscan %p", ic->ic_scan);
+ db_printf(" lastdata %d", ic->ic_lastdata);
+ db_printf(" lastscan %d", ic->ic_lastscan);
+ db_printf("\n");
+
+ db_printf("\tmax_keyix %d", ic->ic_max_keyix);
+ db_printf(" sta %p", &ic->ic_sta);
+ db_printf(" wme %p", &ic->ic_wme);
+ db_printf("\n");
+
+ db_printf("\tprotmode %d", ic->ic_protmode);
+ db_printf(" nonerpsta %u", ic->ic_nonerpsta);
+ db_printf(" longslotsta %u", ic->ic_longslotsta);
+ db_printf(" lastnonerp %d", ic->ic_lastnonerp);
+ db_printf("\n");
+ db_printf("\tsta_assoc %u", ic->ic_sta_assoc);
+ db_printf(" ht_sta_assoc %u", ic->ic_ht_sta_assoc);
+ db_printf(" ht40_sta_assoc %u", ic->ic_ht40_sta_assoc);
+ db_printf("\n");
+ db_printf("\tcurhtprotmode 0x%x", ic->ic_curhtprotmode);
+ db_printf(" htprotmode %d", ic->ic_htprotmode);
+ db_printf(" lastnonht %d", ic->ic_lastnonht);
+ db_printf("\n");
+
+ if (showprocs) {
+ db_printf("\tic_vap_create %p\n", ic->ic_vap_create);
+ db_printf("\tic_vap_delete %p\n", ic->ic_vap_delete);
+#if 0
+ /* operating mode attachment */
+ ieee80211vap_attach ic_vattach[IEEE80211_OPMODE_MAX];
+#endif
+ db_printf("\tic_newassoc %p\n", ic->ic_newassoc);
+ db_printf("\tic_getradiocaps %p\n", ic->ic_getradiocaps);
+ db_printf("\tic_setregdomain %p\n", ic->ic_setregdomain);
+ db_printf("\tic_send_mgmt %p\n", ic->ic_send_mgmt);
+ db_printf("\tic_raw_xmit %p\n", ic->ic_raw_xmit);
+ db_printf("\tic_updateslot %p\n", ic->ic_updateslot);
+ db_printf("\tic_update_mcast %p\n", ic->ic_update_mcast);
+ db_printf("\tic_update_promisc %p\n", ic->ic_update_promisc);
+ db_printf("\tic_node_alloc %p\n", ic->ic_node_alloc);
+ db_printf("\tic_node_free %p\n", ic->ic_node_free);
+ db_printf("\tic_node_cleanup %p\n", ic->ic_node_cleanup);
+ db_printf("\tic_node_getrssi %p\n", ic->ic_node_getrssi);
+ db_printf("\tic_node_getsignal %p\n", ic->ic_node_getsignal);
+ db_printf("\tic_node_getmimoinfo %p\n", ic->ic_node_getmimoinfo);
+ db_printf("\tic_scan_start %p\n", ic->ic_scan_start);
+ db_printf("\tic_scan_end %p\n", ic->ic_scan_end);
+ db_printf("\tic_set_channel %p\n", ic->ic_set_channel);
+ db_printf("\tic_scan_curchan %p\n", ic->ic_scan_curchan);
+ db_printf("\tic_scan_mindwell %p\n", ic->ic_scan_mindwell);
+ db_printf("\tic_recv_action %p\n", ic->ic_recv_action);
+ db_printf("\tic_send_action %p\n", ic->ic_send_action);
+ db_printf("\tic_addba_request %p\n", ic->ic_addba_request);
+ db_printf("\tic_addba_response %p\n", ic->ic_addba_response);
+ db_printf("\tic_addba_stop %p\n", ic->ic_addba_stop);
+ }
+ if (showvaps && !TAILQ_EMPTY(&ic->ic_vaps)) {
+ db_printf("\n");
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ _db_show_vap(vap, showprocs);
+ }
+ if (showsta && !TAILQ_EMPTY(&ic->ic_sta.nt_node)) {
+ const struct ieee80211_node_table *nt = &ic->ic_sta;
+ const struct ieee80211_node *ni;
+
+ TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+ db_printf("\n");
+ _db_show_sta(ni);
+ }
+ }
+}
+
+static void
+_db_show_channel(const char *tag, const struct ieee80211_channel *c)
+{
+ db_printf("%s ", tag);
+ if (c == NULL)
+ db_printf("<NULL>");
+ else if (c == IEEE80211_CHAN_ANYC)
+ db_printf("<ANY>");
+ else
+ db_printf("[%u (%u) flags=%b maxreg %u maxpow %u minpow %u state 0x%x extieee %u]",
+ c->ic_freq, c->ic_ieee,
+ c->ic_flags, IEEE80211_CHAN_BITS,
+ c->ic_maxregpower, c->ic_maxpower, c->ic_minpower,
+ c->ic_state, c->ic_extieee);
+}
+
+static void
+_db_show_ssid(const char *tag, int ix, int len, const uint8_t *ssid)
+{
+ const uint8_t *p;
+ int i;
+
+ db_printf(tag, ix);
+
+ if (len > IEEE80211_NWID_LEN)
+ len = IEEE80211_NWID_LEN;
+ /* determine printable or not */
+ for (i = 0, p = ssid; i < len; i++, p++) {
+ if (*p < ' ' || *p > 0x7e)
+ break;
+ }
+ if (i == len) {
+ db_printf("\"");
+ for (i = 0, p = ssid; i < len; i++, p++)
+ db_printf("%c", *p);
+ db_printf("\"");
+ } else {
+ db_printf("0x");
+ for (i = 0, p = ssid; i < len; i++, p++)
+ db_printf("%02x", *p);
+ }
+}
+
+static void
+_db_show_appie(const char *tag, const struct ieee80211_appie *ie)
+{
+ const uint8_t *p;
+ int i;
+
+ if (ie == NULL)
+ return;
+ db_printf("%s [0x", tag);
+ for (i = 0, p = ie->ie_data; i < ie->ie_len; i++, p++)
+ db_printf("%02x", *p);
+ db_printf("]\n");
+}
+
+static void
+_db_show_key(const char *tag, int ix, const struct ieee80211_key *wk)
+{
+ static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
+ const struct ieee80211_cipher *cip = wk->wk_cipher;
+ int keylen = wk->wk_keylen;
+
+ if (wk->wk_keyix == IEEE80211_KEYIX_NONE)
+ return;
+ db_printf(tag, ix);
+ switch (cip->ic_cipher) {
+ case IEEE80211_CIPHER_WEP:
+ /* compatibility */
+ db_printf(" wepkey %u:%s", wk->wk_keyix+1,
+ keylen <= 5 ? "40-bit" :
+ keylen <= 13 ? "104-bit" : "128-bit");
+ break;
+ case IEEE80211_CIPHER_TKIP:
+ if (keylen > 128/8)
+ keylen -= 128/8; /* ignore MIC for now */
+ db_printf(" TKIP %u:%u-bit", wk->wk_keyix+1, 8*keylen);
+ break;
+ case IEEE80211_CIPHER_AES_OCB:
+ db_printf(" AES-OCB %u:%u-bit", wk->wk_keyix+1, 8*keylen);
+ break;
+ case IEEE80211_CIPHER_AES_CCM:
+ db_printf(" AES-CCM %u:%u-bit", wk->wk_keyix+1, 8*keylen);
+ break;
+ case IEEE80211_CIPHER_CKIP:
+ db_printf(" CKIP %u:%u-bit", wk->wk_keyix+1, 8*keylen);
+ break;
+ case IEEE80211_CIPHER_NONE:
+ db_printf(" NULL %u:%u-bit", wk->wk_keyix+1, 8*keylen);
+ break;
+ default:
+ db_printf(" UNKNOWN (0x%x) %u:%u-bit",
+ cip->ic_cipher, wk->wk_keyix+1, 8*keylen);
+ break;
+ }
+ if (memcmp(wk->wk_key, zerodata, keylen) != 0) {
+ int i;
+
+ db_printf(" <");
+ for (i = 0; i < keylen; i++)
+ db_printf("%02x", wk->wk_key[i]);
+ db_printf(">");
+ if (cip->ic_cipher != IEEE80211_CIPHER_WEP &&
+ wk->wk_keyrsc[IEEE80211_NONQOS_TID] != 0)
+ db_printf(" rsc %ju", (uintmax_t)wk->wk_keyrsc[IEEE80211_NONQOS_TID]);
+ if (cip->ic_cipher != IEEE80211_CIPHER_WEP &&
+ wk->wk_keytsc != 0)
+ db_printf(" tsc %ju", (uintmax_t)wk->wk_keytsc);
+ if (wk->wk_flags != 0) {
+ const char *sep = " ";
+
+ if (wk->wk_flags & IEEE80211_KEY_XMIT)
+ db_printf("%stx", sep), sep = "+";
+ if (wk->wk_flags & IEEE80211_KEY_RECV)
+ db_printf("%srx", sep), sep = "+";
+ if (wk->wk_flags & IEEE80211_KEY_DEFAULT)
+ db_printf("%sdef", sep), sep = "+";
+ }
+ db_printf("\n");
+ }
+}
+
+static void
+printrate(const char *tag, int v)
+{
+ if (v == IEEE80211_FIXED_RATE_NONE)
+ db_printf(" %s <none>", tag);
+ else if (v == 11)
+ db_printf(" %s 5.5", tag);
+ else if (v & IEEE80211_RATE_MCS)
+ db_printf(" %s MCS%d", tag, v &~ IEEE80211_RATE_MCS);
+ else
+ db_printf(" %s %d", tag, v/2);
+}
+
+static void
+_db_show_roamparams(const char *tag, const void *arg,
+ const struct ieee80211_roamparam *rp)
+{
+
+ db_printf(tag, arg);
+ if (rp->rssi & 1)
+ db_printf(" rssi %u.5", rp->rssi/2);
+ else
+ db_printf(" rssi %u", rp->rssi/2);
+ printrate("rate", rp->rate);
+}
+
+static void
+_db_show_txparams(const char *tag, const void *arg,
+ const struct ieee80211_txparam *tp)
+{
+
+ db_printf(tag, arg);
+ printrate("ucastrate", tp->ucastrate);
+ printrate("mcastrate", tp->mcastrate);
+ printrate("mgmtrate", tp->mgmtrate);
+ db_printf(" maxretry %d", tp->maxretry);
+}
+
+static void
+_db_show_stats(const struct ieee80211_stats *is)
+{
+}
+#endif /* DDB */
diff --git a/sys/net80211/ieee80211_dfs.c b/sys/net80211/ieee80211_dfs.c
new file mode 100644
index 0000000..0351cb8
--- /dev/null
+++ b/sys/net80211/ieee80211_dfs.c
@@ -0,0 +1,372 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 DFS/Radar support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+
+MALLOC_DEFINE(M_80211_DFS, "80211dfs", "802.11 DFS state");
+
+/* XXX public for sysctl hookup */
+int ieee80211_nol_timeout = 30*60; /* 30 minutes */
+#define NOL_TIMEOUT msecs_to_ticks(ieee80211_nol_timeout*1000)
+int ieee80211_cac_timeout = 60; /* 60 seconds */
+#define CAC_TIMEOUT msecs_to_ticks(ieee80211_cac_timeout*1000)
+
+void
+ieee80211_dfs_attach(struct ieee80211com *ic)
+{
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+
+ callout_init(&dfs->nol_timer, CALLOUT_MPSAFE);
+ callout_init(&dfs->cac_timer, CALLOUT_MPSAFE);
+}
+
+void
+ieee80211_dfs_detach(struct ieee80211com *ic)
+{
+ /* NB: we assume no locking is needed */
+ ieee80211_dfs_reset(ic);
+}
+
+void
+ieee80211_dfs_reset(struct ieee80211com *ic)
+{
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+ int i;
+
+ /* NB: we assume no locking is needed */
+ /* NB: cac_timer should be cleared by the state machine */
+ callout_drain(&dfs->nol_timer);
+ for (i = 0; i < ic->ic_nchans; i++)
+ ic->ic_channels[i].ic_state = 0;
+ dfs->lastchan = NULL;
+}
+
+static void
+cac_timeout(void *arg)
+{
+ struct ieee80211vap *vap = arg;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+ int i;
+
+ if (vap->iv_state != IEEE80211_S_CAC) /* NB: just in case */
+ return;
+ /*
+ * When radar is detected during a CAC we are woken
+ * up prematurely to switch to a new channel.
+ * Check the channel to decide how to act.
+ */
+ if (IEEE80211_IS_CHAN_RADAR(ic->ic_curchan)) {
+ ieee80211_notify_cac(ic, ic->ic_curchan,
+ IEEE80211_NOTIFY_CAC_RADAR);
+
+ if_printf(vap->iv_ifp,
+ "CAC timer on channel %u (%u MHz) stopped due to radar\n",
+ ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
+
+ /* XXX clobbers any existing desired channel */
+ /* NB: dfs->newchan may be NULL, that's ok */
+ vap->iv_des_chan = dfs->newchan;
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ } else {
+ if_printf(vap->iv_ifp,
+ "CAC timer on channel %u (%u MHz) expired; "
+ "no radar detected\n",
+ ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
+ /*
+ * Mark all channels with the current frequency
+ * as having completed CAC; this keeps us from
+ * doing it again until we change channels.
+ */
+ for (i = 0; i < ic->ic_nchans; i++) {
+ struct ieee80211_channel *c = &ic->ic_channels[i];
+ if (c->ic_freq == ic->ic_curchan->ic_freq)
+ c->ic_state |= IEEE80211_CHANSTATE_CACDONE;
+ }
+ ieee80211_notify_cac(ic, ic->ic_curchan,
+ IEEE80211_NOTIFY_CAC_EXPIRE);
+ ieee80211_cac_completeswitch(vap);
+ }
+}
+
+/*
+ * Initiate the CAC timer. The driver is responsible
+ * for setting up the hardware to scan for radar on the
+ * channnel, we just handle timing things out.
+ */
+void
+ieee80211_dfs_cac_start(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ callout_reset(&dfs->cac_timer, CAC_TIMEOUT, cac_timeout, vap);
+ if_printf(vap->iv_ifp, "start %d second CAC timer on channel %u (%u MHz)\n",
+ ticks_to_secs(CAC_TIMEOUT),
+ ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
+ ieee80211_notify_cac(ic, ic->ic_curchan, IEEE80211_NOTIFY_CAC_START);
+}
+
+/*
+ * Clear the CAC timer.
+ */
+void
+ieee80211_dfs_cac_stop(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ /* NB: racey but not important */
+ if (callout_pending(&dfs->cac_timer)) {
+ if_printf(vap->iv_ifp, "stop CAC timer on channel %u (%u MHz)\n",
+ ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
+ ieee80211_notify_cac(ic, ic->ic_curchan,
+ IEEE80211_NOTIFY_CAC_STOP);
+ }
+ /* XXX cannot use drain 'cuz holding a lock */
+ callout_stop(&dfs->cac_timer);
+}
+
+void
+ieee80211_dfs_cac_clear(struct ieee80211com *ic,
+ const struct ieee80211_channel *chan)
+{
+ int i;
+
+ for (i = 0; i < ic->ic_nchans; i++) {
+ struct ieee80211_channel *c = &ic->ic_channels[i];
+ if (c->ic_freq == chan->ic_freq)
+ c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
+ }
+}
+
+static void
+dfs_timeout(void *arg)
+{
+ struct ieee80211com *ic = arg;
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+ struct ieee80211_channel *c;
+ int i, oldest, now;
+
+ IEEE80211_LOCK(ic);
+ now = oldest = ticks;
+ for (i = 0; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
+ if (IEEE80211_IS_CHAN_RADAR(c)) {
+ if (time_after_eq(now, dfs->nol_event[i]+NOL_TIMEOUT)) {
+ c->ic_state &= ~IEEE80211_CHANSTATE_RADAR;
+ if (c->ic_state & IEEE80211_CHANSTATE_NORADAR) {
+ /*
+ * NB: do this here so we get only one
+ * msg instead of one for every channel
+ * table entry.
+ */
+ if_printf(ic->ic_ifp, "radar on channel"
+ " %u (%u MHz) cleared after timeout\n",
+ c->ic_ieee, c->ic_freq);
+ /* notify user space */
+ c->ic_state &=
+ ~IEEE80211_CHANSTATE_NORADAR;
+ ieee80211_notify_radar(ic, c);
+ }
+ } else if (dfs->nol_event[i] < oldest)
+ oldest = dfs->nol_event[i];
+ }
+ }
+ if (oldest != now) {
+ /* arrange to process next channel up for a status change */
+ callout_reset(&dfs->nol_timer, oldest + NOL_TIMEOUT,
+ dfs_timeout, ic);
+ }
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+announce_radar(struct ifnet *ifp, const struct ieee80211_channel *curchan,
+ const struct ieee80211_channel *newchan)
+{
+ if (newchan == NULL)
+ if_printf(ifp, "radar detected on channel %u (%u MHz)\n",
+ curchan->ic_ieee, curchan->ic_freq);
+ else
+ if_printf(ifp, "radar detected on channel %u (%u MHz), "
+ "moving to channel %u (%u MHz)\n",
+ curchan->ic_ieee, curchan->ic_freq,
+ newchan->ic_ieee, newchan->ic_freq);
+}
+
+/*
+ * Handle a radar detection event on a channel. The channel is
+ * added to the NOL list and we record the time of the event.
+ * Entries are aged out after NOL_TIMEOUT. If radar was
+ * detected while doing CAC we force a state/channel change.
+ * Otherwise radar triggers a channel switch using the CSA
+ * mechanism (when the channel is the bss channel).
+ */
+void
+ieee80211_dfs_notify_radar(struct ieee80211com *ic, struct ieee80211_channel *chan)
+{
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+ int i, now;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ /*
+ * Mark all entries with this frequency. Notify user
+ * space and arrange for notification when the radar
+ * indication is cleared. Then kick the NOL processing
+ * thread if not already running.
+ */
+ now = ticks;
+ for (i = 0; i < ic->ic_nchans; i++) {
+ struct ieee80211_channel *c = &ic->ic_channels[i];
+ if (c->ic_freq == chan->ic_freq) {
+ c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
+ c->ic_state |= IEEE80211_CHANSTATE_RADAR;
+ dfs->nol_event[i] = now;
+ }
+ }
+ ieee80211_notify_radar(ic, chan);
+ chan->ic_state |= IEEE80211_CHANSTATE_NORADAR;
+ if (!callout_pending(&dfs->nol_timer))
+ callout_reset(&dfs->nol_timer, NOL_TIMEOUT, dfs_timeout, ic);
+
+ /*
+ * If radar is detected on the bss channel while
+ * doing CAC; force a state change by scheduling the
+ * callout to be dispatched asap. Otherwise, if this
+ * event is for the bss channel then we must quiet
+ * traffic and schedule a channel switch.
+ *
+ * Note this allows us to receive notification about
+ * channels other than the bss channel; not sure
+ * that can/will happen but it's simple to support.
+ */
+ if (chan == ic->ic_bsschan) {
+ /* XXX need a way to defer to user app */
+ dfs->newchan = ieee80211_dfs_pickchannel(ic);
+
+ announce_radar(ic->ic_ifp, chan, dfs->newchan);
+
+ if (callout_pending(&dfs->cac_timer))
+ callout_reset(&dfs->nol_timer, 0, dfs_timeout, ic);
+ else if (dfs->newchan != NULL) {
+ /* XXX mode 1, switch count 2 */
+ /* XXX calculate switch count based on max
+ switch time and beacon interval? */
+ ieee80211_csa_startswitch(ic, dfs->newchan, 1, 2);
+ } else {
+ /*
+ * Spec says to stop all transmissions and
+ * wait on the current channel for an entry
+ * on the NOL to expire.
+ */
+ /*XXX*/
+ }
+ } else {
+ /*
+ * Issue rate-limited console msgs.
+ */
+ if (dfs->lastchan != chan) {
+ dfs->lastchan = chan;
+ dfs->cureps = 0;
+ announce_radar(ic->ic_ifp, chan, NULL);
+ } else if (ppsratecheck(&dfs->lastevent, &dfs->cureps, 1)) {
+ announce_radar(ic->ic_ifp, chan, NULL);
+ }
+ }
+}
+
+struct ieee80211_channel *
+ieee80211_dfs_pickchannel(struct ieee80211com *ic)
+{
+ struct ieee80211_channel *c;
+ int i, flags;
+ uint16_t v;
+
+ /*
+ * Consult the scan cache first.
+ */
+ flags = ic->ic_curchan->ic_flags & IEEE80211_CHAN_ALL;
+ /*
+ * XXX if curchan is HT this will never find a channel
+ * XXX 'cuz we scan only legacy channels
+ */
+ c = ieee80211_scan_pickchannel(ic, flags);
+ if (c != NULL)
+ return c;
+ /*
+ * No channel found in scan cache; select a compatible
+ * one at random (skipping channels where radar has
+ * been detected).
+ */
+ get_random_bytes(&v, sizeof(v));
+ v %= ic->ic_nchans;
+ for (i = v; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
+ if (!IEEE80211_IS_CHAN_RADAR(c) &&
+ (c->ic_flags & flags) == flags)
+ return c;
+ }
+ for (i = 0; i < v; i++) {
+ c = &ic->ic_channels[i];
+ if (!IEEE80211_IS_CHAN_RADAR(c) &&
+ (c->ic_flags & flags) == flags)
+ return c;
+ }
+ if_printf(ic->ic_ifp, "HELP, no channel located to switch to!\n");
+ return NULL;
+}
diff --git a/sys/net80211/ieee80211_dfs.h b/sys/net80211/ieee80211_dfs.h
new file mode 100644
index 0000000..36d14aa
--- /dev/null
+++ b/sys/net80211/ieee80211_dfs.h
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_DFS_H_
+#define _NET80211_IEEE80211_DFS_H_
+
+/*
+ * 802.11h/DFS definitions.
+ */
+
+struct ieee80211_dfs_state {
+ int nol_event[IEEE80211_CHAN_MAX+1];
+ struct callout nol_timer; /* NOL list processing */
+ struct callout cac_timer; /* CAC timer */
+ struct timeval lastevent; /* time of last radar event */
+ int cureps; /* current events/second */
+ const struct ieee80211_channel *lastchan;/* chan w/ last radar event */
+ struct ieee80211_channel *newchan; /* chan selected next */
+};
+
+void ieee80211_dfs_attach(struct ieee80211com *);
+void ieee80211_dfs_detach(struct ieee80211com *);
+
+void ieee80211_dfs_reset(struct ieee80211com *);
+
+void ieee80211_dfs_cac_start(struct ieee80211vap *);
+void ieee80211_dfs_cac_stop(struct ieee80211vap *);
+void ieee80211_dfs_cac_clear(struct ieee80211com *,
+ const struct ieee80211_channel *);
+
+void ieee80211_dfs_notify_radar(struct ieee80211com *,
+ struct ieee80211_channel *);
+struct ieee80211_channel *ieee80211_dfs_pickchannel(struct ieee80211com *);
+#endif /* _NET80211_IEEE80211_DFS_H_ */
diff --git a/sys/net80211/ieee80211_freebsd.c b/sys/net80211/ieee80211_freebsd.c
index 1cb7813..d0b1b69 100644
--- a/sys/net80211/ieee80211_freebsd.c
+++ b/sys/net80211/ieee80211_freebsd.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2003-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2003-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 support (FreeBSD-specific code)
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
@@ -41,7 +43,9 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <net/if.h>
+#include <net/if_clone.h>
#include <net/if_media.h>
+#include <net/if_types.h>
#include <net/ethernet.h>
#include <net/route.h>
@@ -57,24 +61,112 @@ SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug,
extern int ieee80211_recv_bar_ena;
SYSCTL_INT(_net_wlan, OID_AUTO, recv_bar, CTLFLAG_RW, &ieee80211_recv_bar_ena,
0, "BAR frame processing (ena/dis)");
+extern int ieee80211_nol_timeout;
+SYSCTL_INT(_net_wlan, OID_AUTO, nol_timeout, CTLFLAG_RW,
+ &ieee80211_nol_timeout, 0, "NOL timeout (secs)");
+extern int ieee80211_cac_timeout;
+SYSCTL_INT(_net_wlan, OID_AUTO, cac_timeout, CTLFLAG_RW,
+ &ieee80211_cac_timeout, 0, "CAC timeout (secs)");
+
+MALLOC_DEFINE(M_80211_COM, "80211com", "802.11 com state");
+
+/*
+ * Allocate/free com structure in conjunction with ifnet;
+ * these routines are registered with if_register_com_alloc
+ * below and are called automatically by the ifnet code
+ * when the ifnet of the parent device is created.
+ */
+static void *
+wlan_alloc(u_char type, struct ifnet *ifp)
+{
+ struct ieee80211com *ic;
+
+ ic = malloc(sizeof(struct ieee80211com), M_80211_COM, M_WAITOK|M_ZERO);
+ ic->ic_ifp = ifp;
+
+ return (ic);
+}
+
+static void
+wlan_free(void *ic, u_char type)
+{
+ free(ic, M_80211_COM);
+}
-#ifdef IEEE80211_AMPDU_AGE
static int
-ieee80211_sysctl_ampdu_age(SYSCTL_HANDLER_ARGS)
+wlan_clone_create(struct if_clone *ifc, int unit, caddr_t params)
{
- extern int ieee80211_ampdu_age;
- int ampdu_age = ticks_to_msecs(ieee80211_ampdu_age);
+ struct ieee80211_clone_params cp;
+ struct ieee80211vap *vap;
+ struct ieee80211com *ic;
+ struct ifnet *ifp;
int error;
- error = sysctl_handle_int(oidp, &ampdu_age, 0, req);
+ error = copyin(params, &cp, sizeof(cp));
+ if (error)
+ return error;
+ ifp = ifunit(cp.icp_parent);
+ if (ifp == NULL)
+ return ENXIO;
+ if (ifp->if_type != IFT_IEEE80211) {
+ if_printf(ifp, "%s: reject, not an 802.11 device\n", __func__);
+ return EINVAL;
+ }
+ ic = ifp->if_l2com;
+ vap = ic->ic_vap_create(ic, ifc->ifc_name, unit,
+ cp.icp_opmode, cp.icp_flags, cp.icp_bssid,
+ cp.icp_flags & IEEE80211_CLONE_MACADDR ?
+ cp.icp_macaddr : ic->ic_myaddr);
+ return (vap == NULL ? EIO : 0);
+}
+
+static void
+wlan_clone_destroy(struct ifnet *ifp)
+{
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+
+ ic->ic_vap_delete(vap);
+}
+IFC_SIMPLE_DECLARE(wlan, 0);
+
+void
+ieee80211_vap_destroy(struct ieee80211vap *vap)
+{
+ ifc_simple_destroy(&wlan_cloner, vap->iv_ifp);
+}
+
+static int
+ieee80211_sysctl_msecs_ticks(SYSCTL_HANDLER_ARGS)
+{
+ int msecs = ticks_to_msecs(*(int *)arg1);
+ int error, t;
+
+ error = sysctl_handle_int(oidp, &msecs, 0, req);
if (error || !req->newptr)
return error;
- ieee80211_ampdu_age = msecs_to_ticks(ampdu_age);
+ t = msecs_to_ticks(msecs);
+ *(int *)arg1 = (t < 1) ? 1 : t;
return 0;
}
-SYSCTL_PROC(_net_wlan, OID_AUTO, "ampdu_age", CTLFLAG_RW, NULL, 0,
- ieee80211_sysctl_ampdu_age, "A", "AMPDU max reorder age (ms)");
+
+#ifdef IEEE80211_AMPDU_AGE
+extern int ieee80211_ampdu_age;
+SYSCTL_PROC(_net_wlan, OID_AUTO, ampdu_age, CTLFLAG_RW,
+ &ieee80211_ampdu_age, 0, ieee80211_sysctl_msecs_ticks, "I",
+ "AMPDU max reorder age (ms)");
#endif
+extern int ieee80211_addba_timeout;
+SYSCTL_PROC(_net_wlan, OID_AUTO, addba_timeout, CTLFLAG_RW,
+ &ieee80211_addba_timeout, 0, ieee80211_sysctl_msecs_ticks, "I",
+ "ADDBA request timeout (ms)");
+extern int ieee80211_addba_backoff;
+SYSCTL_PROC(_net_wlan, OID_AUTO, addba_backoff, CTLFLAG_RW,
+ &ieee80211_addba_backoff, 0, ieee80211_sysctl_msecs_ticks, "I",
+ "ADDBA request backoff (ms)");
+extern int ieee80211_addba_maxtries;
+SYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLFLAG_RW,
+ &ieee80211_addba_maxtries, 0, "max ADDBA requests sent before backoff");
static int
ieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS)
@@ -101,6 +193,17 @@ ieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS)
void
ieee80211_sysctl_attach(struct ieee80211com *ic)
{
+}
+
+void
+ieee80211_sysctl_detach(struct ieee80211com *ic)
+{
+}
+
+void
+ieee80211_sysctl_vattach(struct ieee80211vap *vap)
+{
+ struct ifnet *ifp = vap->iv_ifp;
struct sysctl_ctx_list *ctx;
struct sysctl_oid *oid;
char num[14]; /* sufficient for 32 bits */
@@ -108,57 +211,76 @@ ieee80211_sysctl_attach(struct ieee80211com *ic)
MALLOC(ctx, struct sysctl_ctx_list *, sizeof(struct sysctl_ctx_list),
M_DEVBUF, M_NOWAIT | M_ZERO);
if (ctx == NULL) {
- if_printf(ic->ic_ifp, "%s: cannot allocate sysctl context!\n",
+ if_printf(ifp, "%s: cannot allocate sysctl context!\n",
__func__);
return;
}
sysctl_ctx_init(ctx);
- snprintf(num, sizeof(num), "%u", ic->ic_vap);
+ snprintf(num, sizeof(num), "%u", ifp->if_dunit);
oid = SYSCTL_ADD_NODE(ctx, &SYSCTL_NODE_CHILDREN(_net, wlan),
OID_AUTO, num, CTLFLAG_RD, NULL, "");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "%parent", CTLFLAG_RD, ic, 0, ieee80211_sysctl_parent, "A",
- "parent device");
+ "%parent", CTLFLAG_RD, vap->iv_ic, 0,
+ ieee80211_sysctl_parent, "A", "parent device");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "driver_caps", CTLFLAG_RW, &vap->iv_caps, 0,
+ "driver capabilities");
#ifdef IEEE80211_DEBUG
- ic->ic_debug = ieee80211_debug;
+ vap->iv_debug = ieee80211_debug;
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "debug", CTLFLAG_RW, &ic->ic_debug, 0,
+ "debug", CTLFLAG_RW, &vap->iv_debug, 0,
"control debugging printfs");
#endif
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "bmiss_max", CTLFLAG_RW, &vap->iv_bmiss_max, 0,
+ "consecutive beacon misses before scanning");
/* XXX inherit from tunables */
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "inact_run", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_run, 0,
+ "inact_run", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_run, 0,
ieee80211_sysctl_inact, "I",
"station inactivity timeout (sec)");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "inact_probe", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_probe, 0,
+ "inact_probe", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_probe, 0,
ieee80211_sysctl_inact, "I",
"station inactivity probe timeout (sec)");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "inact_auth", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_auth, 0,
+ "inact_auth", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_auth, 0,
ieee80211_sysctl_inact, "I",
"station authentication timeout (sec)");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "inact_init", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_init, 0,
+ "inact_init", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_init, 0,
ieee80211_sysctl_inact, "I",
"station initial state timeout (sec)");
- SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "driver_caps", CTLFLAG_RW, &ic->ic_caps, 0,
- "driver capabilities");
- SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "bmiss_max", CTLFLAG_RW, &ic->ic_bmiss_max, 0,
- "consecutive beacon misses before scanning");
- ic->ic_sysctl = ctx;
+ if (vap->iv_htcaps & IEEE80211_HTC_HT) {
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "ampdu_mintraffic_bk", CTLFLAG_RW,
+ &vap->iv_ampdu_mintraffic[WME_AC_BK], 0,
+ "BK traffic tx aggr threshold (pps)");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "ampdu_mintraffic_be", CTLFLAG_RW,
+ &vap->iv_ampdu_mintraffic[WME_AC_BE], 0,
+ "BE traffic tx aggr threshold (pps)");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "ampdu_mintraffic_vo", CTLFLAG_RW,
+ &vap->iv_ampdu_mintraffic[WME_AC_VO], 0,
+ "VO traffic tx aggr threshold (pps)");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "ampdu_mintraffic_vi", CTLFLAG_RW,
+ &vap->iv_ampdu_mintraffic[WME_AC_VI], 0,
+ "VI traffic tx aggr threshold (pps)");
+ }
+ vap->iv_sysctl = ctx;
+ vap->iv_oid = oid;
}
void
-ieee80211_sysctl_detach(struct ieee80211com *ic)
+ieee80211_sysctl_vdetach(struct ieee80211vap *vap)
{
- if (ic->ic_sysctl != NULL) {
- sysctl_ctx_free(ic->ic_sysctl);
- FREE(ic->ic_sysctl, M_DEVBUF);
- ic->ic_sysctl = NULL;
+ if (vap->iv_sysctl != NULL) {
+ sysctl_ctx_free(vap->iv_sysctl);
+ FREE(vap->iv_sysctl, M_DEVBUF);
+ vap->iv_sysctl = NULL;
}
}
@@ -190,6 +312,33 @@ ieee80211_drain_ifq(struct ifqueue *ifq)
}
}
+void
+ieee80211_flush_ifq(struct ifqueue *ifq, struct ieee80211vap *vap)
+{
+ struct ieee80211_node *ni;
+ struct mbuf *m, **mprev;
+
+ IF_LOCK(ifq);
+ mprev = &ifq->ifq_head;
+ while ((m = *mprev) != NULL) {
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ if (ni != NULL && ni->ni_vap == vap) {
+ *mprev = m->m_nextpkt; /* remove from list */
+ ifq->ifq_len--;
+
+ m_freem(m);
+ ieee80211_free_node(ni); /* reclaim ref */
+ } else
+ mprev = &m->m_nextpkt;
+ }
+ /* recalculate tail ptr */
+ m = ifq->ifq_head;
+ for (; m != NULL && m->m_nextpkt != NULL; m = m->m_nextpkt)
+ ;
+ ifq->ifq_tail = m;
+ IF_UNLOCK(ifq);
+}
+
/*
* As above, for mbufs allocated with m_gethdr/MGETHDR
* or initialized by M_COPY_PKTHDR.
@@ -290,66 +439,78 @@ get_random_bytes(void *p, size_t n)
}
}
-void
-ieee80211_notify_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int newassoc)
+/*
+ * Helper function for events that pass just a single mac address.
+ */
+static void
+notify_macaddr(struct ifnet *ifp, int op, const uint8_t mac[IEEE80211_ADDR_LEN])
{
- struct ifnet *ifp = ic->ic_ifp;
struct ieee80211_join_event iev;
memset(&iev, 0, sizeof(iev));
- if (ni == ic->ic_bss) {
- IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_bssid);
- rt_ieee80211msg(ifp, newassoc ?
- RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC,
- &iev, sizeof(iev));
+ IEEE80211_ADDR_COPY(iev.iev_addr, mac);
+ rt_ieee80211msg(ifp, op, &iev, sizeof(iev));
+}
+
+void
+ieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode join",
+ (ni == vap->iv_bss) ? "bss " : "");
+
+ if (ni == vap->iv_bss) {
+ notify_macaddr(ifp, newassoc ?
+ RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC, ni->ni_bssid);
if_link_state_change(ifp, LINK_STATE_UP);
} else {
- IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
- rt_ieee80211msg(ifp, newassoc ?
- RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN,
- &iev, sizeof(iev));
+ notify_macaddr(ifp, newassoc ?
+ RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN, ni->ni_macaddr);
}
}
void
-ieee80211_notify_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_notify_node_leave(struct ieee80211_node *ni)
{
- struct ifnet *ifp = ic->ic_ifp;
- struct ieee80211_leave_event iev;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
- if (ni == ic->ic_bss) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode leave",
+ (ni == vap->iv_bss) ? "bss " : "");
+
+ if (ni == vap->iv_bss) {
rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0);
if_link_state_change(ifp, LINK_STATE_DOWN);
} else {
/* fire off wireless event station leaving */
- memset(&iev, 0, sizeof(iev));
- IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
- rt_ieee80211msg(ifp, RTM_IEEE80211_LEAVE, &iev, sizeof(iev));
+ notify_macaddr(ifp, RTM_IEEE80211_LEAVE, ni->ni_macaddr);
}
}
void
-ieee80211_notify_scan_done(struct ieee80211com *ic)
+ieee80211_notify_scan_done(struct ieee80211vap *vap)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = vap->iv_ifp;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "%s\n", "notify scan done");
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", "notify scan done");
/* dispatch wireless event indicating scan completed */
rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0);
}
void
-ieee80211_notify_replay_failure(struct ieee80211com *ic,
+ieee80211_notify_replay_failure(struct ieee80211vap *vap,
const struct ieee80211_frame *wh, const struct ieee80211_key *k,
u_int64_t rsc)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = vap->iv_ifp;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] %s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>\n",
- ether_sprintf(wh->i_addr2), k->wk_cipher->ic_name,
- (intmax_t) rsc, (intmax_t) k->wk_keyrsc,
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>",
+ k->wk_cipher->ic_name, (intmax_t) rsc,
+ (intmax_t) k->wk_keyrsc[IEEE80211_NONQOS_TID],
k->wk_keyix, k->wk_rxkeyix);
if (ifp != NULL) { /* NB: for cipher test modules */
@@ -362,22 +523,21 @@ ieee80211_notify_replay_failure(struct ieee80211com *ic,
iev.iev_keyix = k->wk_rxkeyix;
else
iev.iev_keyix = k->wk_keyix;
- iev.iev_keyrsc = k->wk_keyrsc;
+ iev.iev_keyrsc = k->wk_keyrsc[0]; /* XXX need tid */
iev.iev_rsc = rsc;
rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev));
}
}
void
-ieee80211_notify_michael_failure(struct ieee80211com *ic,
+ieee80211_notify_michael_failure(struct ieee80211vap *vap,
const struct ieee80211_frame *wh, u_int keyix)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = vap->iv_ifp;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] michael MIC verification failed <keyix %u>\n",
- ether_sprintf(wh->i_addr2), keyix);
- ic->ic_stats.is_rx_tkipmic++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "michael MIC verification failed <keyix %u>", keyix);
+ vap->iv_stats.is_rx_tkipmic++;
if (ifp != NULL) { /* NB: for cipher test modules */
struct ieee80211_michael_event iev;
@@ -391,6 +551,107 @@ ieee80211_notify_michael_failure(struct ieee80211com *ic,
}
void
+ieee80211_notify_wds_discover(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
+
+ notify_macaddr(ifp, RTM_IEEE80211_WDS, ni->ni_macaddr);
+}
+
+void
+ieee80211_notify_csa(struct ieee80211com *ic,
+ const struct ieee80211_channel *c, int mode, int count)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_csa_event iev;
+
+ memset(&iev, 0, sizeof(iev));
+ iev.iev_flags = c->ic_flags;
+ iev.iev_freq = c->ic_freq;
+ iev.iev_ieee = c->ic_ieee;
+ iev.iev_mode = mode;
+ iev.iev_count = count;
+ rt_ieee80211msg(ifp, RTM_IEEE80211_CSA, &iev, sizeof(iev));
+}
+
+void
+ieee80211_notify_radar(struct ieee80211com *ic,
+ const struct ieee80211_channel *c)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_radar_event iev;
+
+ memset(&iev, 0, sizeof(iev));
+ iev.iev_flags = c->ic_flags;
+ iev.iev_freq = c->ic_freq;
+ iev.iev_ieee = c->ic_ieee;
+ rt_ieee80211msg(ifp, RTM_IEEE80211_RADAR, &iev, sizeof(iev));
+}
+
+void
+ieee80211_notify_cac(struct ieee80211com *ic,
+ const struct ieee80211_channel *c, enum ieee80211_notify_cac_event type)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_cac_event iev;
+
+ memset(&iev, 0, sizeof(iev));
+ iev.iev_flags = c->ic_flags;
+ iev.iev_freq = c->ic_freq;
+ iev.iev_ieee = c->ic_ieee;
+ iev.iev_type = type;
+ rt_ieee80211msg(ifp, RTM_IEEE80211_CAC, &iev, sizeof(iev));
+}
+
+void
+ieee80211_notify_node_deauth(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node deauth");
+
+ notify_macaddr(ifp, RTM_IEEE80211_DEAUTH, ni->ni_macaddr);
+}
+
+void
+ieee80211_notify_node_auth(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node auth");
+
+ notify_macaddr(ifp, RTM_IEEE80211_AUTH, ni->ni_macaddr);
+}
+
+void
+ieee80211_notify_country(struct ieee80211vap *vap,
+ const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t cc[2])
+{
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_country_event iev;
+
+ memset(&iev, 0, sizeof(iev));
+ IEEE80211_ADDR_COPY(iev.iev_addr, bssid);
+ iev.iev_cc[0] = cc[0];
+ iev.iev_cc[1] = cc[1];
+ rt_ieee80211msg(ifp, RTM_IEEE80211_COUNTRY, &iev, sizeof(iev));
+}
+
+void
+ieee80211_notify_radio(struct ieee80211com *ic, int state)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_radio_event iev;
+
+ memset(&iev, 0, sizeof(iev));
+ iev.iev_state = state;
+ rt_ieee80211msg(ifp, RTM_IEEE80211_RADIO, &iev, sizeof(iev));
+}
+
+void
ieee80211_load_module(const char *modname)
{
@@ -413,8 +674,12 @@ wlan_modevent(module_t mod, int type, void *unused)
case MOD_LOAD:
if (bootverbose)
printf("wlan: <802.11 Link Layer>\n");
+ if_clone_attach(&wlan_cloner);
+ if_register_com_alloc(IFT_IEEE80211, wlan_alloc, wlan_free);
return 0;
case MOD_UNLOAD:
+ if_deregister_com_alloc(IFT_IEEE80211);
+ if_clone_detach(&wlan_cloner);
return 0;
}
return EINVAL;
diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h
index 0052dfe..bccc0cf 100644
--- a/sys/net80211/ieee80211_freebsd.h
+++ b/sys/net80211/ieee80211_freebsd.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2003-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2003-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,12 +28,18 @@
#define _NET80211_IEEE80211_FREEBSD_H_
#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/rwlock.h>
+
/*
* Common state locking definitions.
*/
typedef struct mtx ieee80211_com_lock_t;
#define IEEE80211_LOCK_INIT(_ic, _name) \
- mtx_init(&(_ic)->ic_comlock, _name, "802.11 com lock", MTX_DEF)
+ mtx_init(&(_ic)->ic_comlock, _name, "802.11 com lock", \
+ MTX_DEF | MTX_RECURSE)
#define IEEE80211_LOCK_DESTROY(_ic) mtx_destroy(&(_ic)->ic_comlock)
#define IEEE80211_LOCK(_ic) mtx_lock(&(_ic)->ic_comlock)
#define IEEE80211_UNLOCK(_ic) mtx_unlock(&(_ic)->ic_comlock)
@@ -41,43 +47,61 @@ typedef struct mtx ieee80211_com_lock_t;
mtx_assert(&(_ic)->ic_comlock, MA_OWNED)
/*
- * Beacon locking definitions.
- */
-typedef struct mtx ieee80211_beacon_lock_t;
-#define IEEE80211_BEACON_LOCK_INIT(_ic, _name) \
- mtx_init(&(_ic)->ic_beaconlock, _name, "802.11 beacon lock", MTX_DEF)
-#define IEEE80211_BEACON_LOCK_DESTROY(_ic) mtx_destroy(&(_ic)->ic_beaconlock)
-#define IEEE80211_BEACON_LOCK(_ic) mtx_lock(&(_ic)->ic_beaconlock)
-#define IEEE80211_BEACON_UNLOCK(_ic) mtx_unlock(&(_ic)->ic_beaconlock)
-#define IEEE80211_BEACON_LOCK_ASSERT(_ic) \
- mtx_assert(&(_ic)->ic_beaconlock, MA_OWNED)
-
-/*
* Node locking definitions.
- * NB: MTX_DUPOK is because we don't generate per-interface strings.
*/
-typedef struct mtx ieee80211_node_lock_t;
-#define IEEE80211_NODE_LOCK_INIT(_nt, _name) \
- mtx_init(&(_nt)->nt_nodelock, _name, "802.11 node table", \
- MTX_DEF | MTX_DUPOK)
-#define IEEE80211_NODE_LOCK_DESTROY(_nt) mtx_destroy(&(_nt)->nt_nodelock)
-#define IEEE80211_NODE_LOCK(_nt) mtx_lock(&(_nt)->nt_nodelock)
-#define IEEE80211_NODE_IS_LOCKED(_nt) mtx_owned(&(_nt)->nt_nodelock)
-#define IEEE80211_NODE_UNLOCK(_nt) mtx_unlock(&(_nt)->nt_nodelock)
-#define IEEE80211_NODE_LOCK_ASSERT(_nt) \
- mtx_assert(&(_nt)->nt_nodelock, MA_OWNED)
+typedef struct {
+ char name[16]; /* e.g. "ath0_node_lock" */
+ struct mtx mtx;
+} ieee80211_node_lock_t;
+#define IEEE80211_NODE_LOCK_INIT(_nt, _name) do { \
+ ieee80211_node_lock_t *nl = &(_nt)->nt_nodelock; \
+ snprintf(nl->name, sizeof(nl->name), "%s_node_lock", _name); \
+ mtx_init(&nl->mtx, NULL, nl->name, MTX_DEF | MTX_RECURSE); \
+} while (0)
+#define IEEE80211_NODE_LOCK_DESTROY(_nt) \
+ mtx_destroy(&(_nt)->nt_nodelock.mtx)
+#define IEEE80211_NODE_LOCK(_nt) \
+ mtx_lock(&(_nt)->nt_nodelock.mtx)
+#define IEEE80211_NODE_IS_LOCKED(_nt) \
+ mtx_owned(&(_nt)->nt_nodelock.mtx)
+#define IEEE80211_NODE_UNLOCK(_nt) \
+ mtx_unlock(&(_nt)->nt_nodelock.mtx)
+#define IEEE80211_NODE_LOCK_ASSERT(_nt) \
+ mtx_assert(&(_nt)->nt_nodelock.mtx, MA_OWNED)
/*
- * Node table scangen locking definitions.
+ * Node table iteration locking definitions; this protects the
+ * scan generation # used to iterate over the station table
+ * while grabbing+releasing the node lock.
*/
-typedef struct mtx ieee80211_scan_lock_t;
-#define IEEE80211_SCAN_LOCK_INIT(_nt, _name) \
- mtx_init(&(_nt)->nt_scanlock, _name, "802.11 node scangen", MTX_DEF)
-#define IEEE80211_SCAN_LOCK_DESTROY(_nt) mtx_destroy(&(_nt)->nt_scanlock)
-#define IEEE80211_SCAN_LOCK(_nt) mtx_lock(&(_nt)->nt_scanlock)
-#define IEEE80211_SCAN_UNLOCK(_nt) mtx_unlock(&(_nt)->nt_scanlock)
-#define IEEE80211_SCAN_LOCK_ASSERT(_nt) \
- mtx_assert(&(_nt)->nt_scanlock, MA_OWNED)
+typedef struct {
+ char name[16]; /* e.g. "ath0_scan_lock" */
+ struct mtx mtx;
+} ieee80211_scan_lock_t;
+#define IEEE80211_NODE_ITERATE_LOCK_INIT(_nt, _name) do { \
+ ieee80211_scan_lock_t *sl = &(_nt)->nt_scanlock; \
+ snprintf(sl->name, sizeof(sl->name), "%s_scan_lock", _name); \
+ mtx_init(&sl->mtx, NULL, sl->name, MTX_DEF); \
+} while (0)
+#define IEEE80211_NODE_ITERATE_LOCK_DESTROY(_nt) \
+ mtx_destroy(&(_nt)->nt_scanlock.mtx)
+#define IEEE80211_NODE_ITERATE_LOCK(_nt) \
+ mtx_lock(&(_nt)->nt_scanlock.mtx)
+#define IEEE80211_NODE_ITERATE_UNLOCK(_nt) \
+ mtx_unlock(&(_nt)->nt_scanlock.mtx)
+
+#define _AGEQ_ENQUEUE(_ifq, _m, _qlen, _age) do { \
+ (_m)->m_nextpkt = NULL; \
+ if ((_ifq)->ifq_tail != NULL) { \
+ _age -= M_AGE_GET((_ifq)->ifq_tail); \
+ (_ifq)->ifq_tail->m_nextpkt = (_m); \
+ } else { \
+ (_ifq)->ifq_head = (_m); \
+ } \
+ M_AGE_SET(_m, _age); \
+ (_ifq)->ifq_tail = (_m); \
+ (_qlen) = ++(_ifq)->ifq_len; \
+} while (0)
/*
* Per-node power-save queue definitions.
@@ -113,16 +137,7 @@ typedef struct mtx ieee80211_scan_lock_t;
_IF_DEQUEUE(&(_ni)->ni_savedq, m); \
} while (0)
#define _IEEE80211_NODE_SAVEQ_ENQUEUE(_ni, _m, _qlen, _age) do {\
- (_m)->m_nextpkt = NULL; \
- if ((_ni)->ni_savedq.ifq_tail != NULL) { \
- _age -= M_AGE_GET((_ni)->ni_savedq.ifq_tail); \
- (_ni)->ni_savedq.ifq_tail->m_nextpkt = (_m); \
- } else { \
- (_ni)->ni_savedq.ifq_head = (_m); \
- } \
- M_AGE_SET(_m, _age); \
- (_ni)->ni_savedq.ifq_tail = (_m); \
- (_qlen) = ++(_ni)->ni_savedq.ifq_len; \
+ _AGEQ_ENQUEUE(&ni->ni_savedq, _m, _qlen, _age); \
} while (0)
#define IEEE80211_TAPQ_INIT(_tap) do { \
@@ -147,6 +162,24 @@ typedef struct mtx ieee80211_scan_lock_t;
} while (0)
#endif /* IF_PREPEND_LIST */
+/* XXX temporary */
+#define IEEE80211_NODE_WDSQ_INIT(_ni, _name) do { \
+ mtx_init(&(_ni)->ni_wdsq.ifq_mtx, _name, "802.11 wds queue", MTX_DEF);\
+ (_ni)->ni_wdsq.ifq_maxlen = IEEE80211_PS_MAX_QUEUE; \
+} while (0)
+#define IEEE80211_NODE_WDSQ_DESTROY(_ni) do { \
+ mtx_destroy(&(_ni)->ni_wdsq.ifq_mtx); \
+} while (0)
+#define IEEE80211_NODE_WDSQ_QLEN(_ni) _IF_QLEN(&(_ni)->ni_wdsq)
+#define IEEE80211_NODE_WDSQ_LOCK(_ni) IF_LOCK(&(_ni)->ni_wdsq)
+#define IEEE80211_NODE_WDSQ_UNLOCK(_ni) IF_UNLOCK(&(_ni)->ni_wdsq)
+#define _IEEE80211_NODE_WDSQ_DEQUEUE_HEAD(_ni, _m) do { \
+ _IF_DEQUEUE(&(_ni)->ni_wdsq, m); \
+} while (0)
+#define _IEEE80211_NODE_WDSQ_ENQUEUE(_ni, _m, _qlen, _age) do { \
+ _AGEQ_ENQUEUE(&ni->ni_wdsq, _m, _qlen, _age); \
+} while (0)
+
/*
* 802.1x MAC ACL database locking definitions.
*/
@@ -182,43 +215,53 @@ int ieee80211_node_dectestref(struct ieee80211_node *ni);
#define ieee80211_node_refcnt(_ni) (_ni)->ni_refcnt
struct ifqueue;
+struct ieee80211vap;
void ieee80211_drain_ifq(struct ifqueue *);
+void ieee80211_flush_ifq(struct ifqueue *, struct ieee80211vap *);
+
+void ieee80211_vap_destroy(struct ieee80211vap *);
+
+#define IFNET_IS_UP_RUNNING(_ifp) \
+ (((_ifp)->if_flags & IFF_UP) && \
+ ((_ifp)->if_drv_flags & IFF_DRV_RUNNING))
#define msecs_to_ticks(ms) (((ms)*hz)/1000)
-#define ticks_to_msecs(t) ((t) / hz)
+#define ticks_to_msecs(t) (1000*(t) / hz)
+#define ticks_to_secs(t) ((t) / hz)
#define time_after(a,b) ((long)(b) - (long)(a) < 0)
#define time_before(a,b) time_after(b,a)
#define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0)
#define time_before_eq(a,b) time_after_eq(b,a)
+#define memmove(dst, src, n) ovbcopy(src, dst, n)
+
struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen);
/* tx path usage */
#define M_LINK0 M_PROTO1 /* WEP requested */
+#define M_WDS M_PROTO2 /* WDS frame */
+#define M_EAPOL M_PROTO3 /* PAE/EAPOL frame */
#define M_PWR_SAV M_PROTO4 /* bypass PS handling */
#define M_MORE_DATA M_PROTO5 /* more data frames to follow */
-#define M_FF 0x20000 /* fast frame */
-#define M_TXCB 0x40000 /* do tx complete callback */
-#define M_80211_TX (0x60000|M_PROTO1|M_WME_AC_MASK|M_PROTO4|M_PROTO5)
+#define M_FF M_PROTO6 /* fast frame */
+#define M_TXCB M_PROTO7 /* do tx complete callback */
+#define M_80211_TX \
+ (M_LINK0|M_WDS|M_EAPOL|M_PWR_SAV|M_MORE_DATA|M_FF|M_TXCB)
/* rx path usage */
#define M_AMPDU M_PROTO1 /* A-MPDU processing done */
#define M_WEP M_PROTO2 /* WEP done by hardware */
#define M_80211_RX (M_AMPDU|M_WEP)
/*
- * Encode WME access control bits in the PROTO flags.
- * This is safe since it's passed directly in to the
- * driver and there's no chance someone else will clobber
- * them on us.
+ * Store WME access control bits in the vlan tag.
+ * This is safe since it's done after the packet is classified
+ * (where we use any previous tag) and because it's passed
+ * directly in to the driver and there's no chance someone
+ * else will clobber them on us.
*/
-#define M_WME_AC_MASK (M_PROTO2|M_PROTO3)
-/* XXX 5 is wrong if M_PROTO* are redefined */
-#define M_WME_AC_SHIFT 5
-
#define M_WME_SETAC(m, ac) \
- ((m)->m_flags = ((m)->m_flags &~ M_WME_AC_MASK) | \
- ((ac) << M_WME_AC_SHIFT))
-#define M_WME_GETAC(m) (((m)->m_flags >> M_WME_AC_SHIFT) & 0x3)
+ ((m)->m_pkthdr.ether_vtag = (ac))
+#define M_WME_GETAC(m) ((m)->m_pkthdr.ether_vtag)
/*
* Mbufs on the power save queue are tagged with an age and
@@ -246,16 +289,29 @@ struct ieee80211com;
void ieee80211_sysctl_attach(struct ieee80211com *);
void ieee80211_sysctl_detach(struct ieee80211com *);
+void ieee80211_sysctl_vattach(struct ieee80211vap *);
+void ieee80211_sysctl_vdetach(struct ieee80211vap *);
void ieee80211_load_module(const char *);
-#define IEEE80211_CRYPTO_MODULE(name, version) \
+/*
+ * A "policy module" is an adjunct module to net80211 that provides
+ * functionality that typically includes policy decisions. This
+ * modularity enables extensibility and vendor-supplied functionality.
+ */
+#define _IEEE80211_POLICY_MODULE(policy, name, version) \
+typedef void (*policy##_setup)(int); \
+SET_DECLARE(policy##_set, policy##_setup); \
static int \
-name##_modevent(module_t mod, int type, void *unused) \
+wlan_##name##_modevent(module_t mod, int type, void *unused) \
{ \
+ policy##_setup * const *iter, f; \
switch (type) { \
case MOD_LOAD: \
- ieee80211_crypto_register(&name); \
+ SET_FOREACH(iter, policy##_set) { \
+ f = (void*) *iter; \
+ f(type); \
+ } \
return 0; \
case MOD_UNLOAD: \
case MOD_QUIESCE: \
@@ -264,20 +320,100 @@ name##_modevent(module_t mod, int type, void *unused) \
nrefs); \
return EBUSY; \
} \
- if (type == MOD_UNLOAD) \
- ieee80211_crypto_unregister(&name); \
+ if (type == MOD_UNLOAD) { \
+ SET_FOREACH(iter, policy##_set) { \
+ f = (void*) *iter; \
+ f(type); \
+ } \
+ } \
return 0; \
} \
return EINVAL; \
} \
static moduledata_t name##_mod = { \
"wlan_" #name, \
- name##_modevent, \
+ wlan_##name##_modevent, \
0 \
}; \
DECLARE_MODULE(wlan_##name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);\
MODULE_VERSION(wlan_##name, version); \
MODULE_DEPEND(wlan_##name, wlan, 1, 1, 1)
+
+/*
+ * Crypto modules implement cipher support.
+ */
+#define IEEE80211_CRYPTO_MODULE(name, version) \
+_IEEE80211_POLICY_MODULE(crypto, name, version); \
+static void \
+name##_modevent(int type) \
+{ \
+ if (type == MOD_LOAD) \
+ ieee80211_crypto_register(&name); \
+ else \
+ ieee80211_crypto_unregister(&name); \
+} \
+TEXT_SET(crypto##_set, name##_modevent)
+
+/*
+ * Scanner modules provide scanning policy.
+ */
+#define IEEE80211_SCANNER_MODULE(name, version) \
+ _IEEE80211_POLICY_MODULE(scanner, name, version)
+
+#define IEEE80211_SCANNER_ALG(name, alg, v) \
+static void \
+name##_modevent(int type) \
+{ \
+ if (type == MOD_LOAD) \
+ ieee80211_scanner_register(alg, &v); \
+ else \
+ ieee80211_scanner_unregister(alg, &v); \
+} \
+TEXT_SET(scanner_set, name##_modevent); \
+
+/*
+ * ACL modules implement acl policy.
+ */
+#define IEEE80211_ACL_MODULE(name, alg, version) \
+_IEEE80211_POLICY_MODULE(acl, name, version); \
+static void \
+alg##_modevent(int type) \
+{ \
+ if (type == MOD_LOAD) \
+ ieee80211_aclator_register(&alg); \
+ else \
+ ieee80211_aclator_unregister(&alg); \
+} \
+TEXT_SET(acl_set, alg##_modevent); \
+
+/*
+ * Authenticator modules handle 802.1x/WPA authentication.
+ */
+#define IEEE80211_AUTH_MODULE(name, version) \
+ _IEEE80211_POLICY_MODULE(auth, name, version)
+
+#define IEEE80211_AUTH_ALG(name, alg, v) \
+static void \
+name##_modevent(int type) \
+{ \
+ if (type == MOD_LOAD) \
+ ieee80211_authenticator_register(alg, &v); \
+ else \
+ ieee80211_authenticator_unregister(alg); \
+} \
+TEXT_SET(auth_set, name##_modevent)
+
+/*
+ * Rate control modules provide tx rate control support.
+ */
+#define IEEE80211_RATE_MODULE(alg, version) \
+_IEEE80211_POLICY_MODULE(rate, alg, version); \
+static void \
+alg##_modevent(int type) \
+{ \
+ /* XXX nothing to do until the rate control framework arrives */\
+} \
+TEXT_SET(rate##_set, alg##_modevent)
#endif /* _KERNEL */
/* XXX this stuff belongs elsewhere */
@@ -310,6 +446,50 @@ struct ieee80211_michael_event {
uint8_t iev_keyix; /* key id/index */
};
+struct ieee80211_wds_event {
+ uint8_t iev_addr[6];
+};
+
+struct ieee80211_csa_event {
+ uint32_t iev_flags; /* channel flags */
+ uint16_t iev_freq; /* setting in Mhz */
+ uint8_t iev_ieee; /* IEEE channel number */
+ uint8_t iev_mode; /* CSA mode */
+ uint8_t iev_count; /* CSA count */
+};
+
+struct ieee80211_cac_event {
+ uint32_t iev_flags; /* channel flags */
+ uint16_t iev_freq; /* setting in Mhz */
+ uint8_t iev_ieee; /* IEEE channel number */
+ /* XXX timestamp? */
+ uint8_t iev_type; /* IEEE80211_NOTIFY_CAC_* */
+};
+
+struct ieee80211_radar_event {
+ uint32_t iev_flags; /* channel flags */
+ uint16_t iev_freq; /* setting in Mhz */
+ uint8_t iev_ieee; /* IEEE channel number */
+ /* XXX timestamp? */
+};
+
+struct ieee80211_auth_event {
+ uint8_t iev_addr[6];
+};
+
+struct ieee80211_deauth_event {
+ uint8_t iev_addr[6];
+};
+
+struct ieee80211_country_event {
+ uint8_t iev_addr[6];
+ uint8_t iev_cc[2]; /* ISO country code */
+};
+
+struct ieee80211_radio_event {
+ uint8_t iev_state; /* 1 on, 0 off */
+};
+
#define RTM_IEEE80211_ASSOC 100 /* station associate (bss mode) */
#define RTM_IEEE80211_REASSOC 101 /* station re-associate (bss mode) */
#define RTM_IEEE80211_DISASSOC 102 /* station disassociate (bss mode) */
@@ -319,6 +499,14 @@ struct ieee80211_michael_event {
#define RTM_IEEE80211_REPLAY 106 /* sequence counter replay detected */
#define RTM_IEEE80211_MICHAEL 107 /* Michael MIC failure detected */
#define RTM_IEEE80211_REJOIN 108 /* station re-associate (ap mode) */
+#define RTM_IEEE80211_WDS 109 /* WDS discovery (ap mode) */
+#define RTM_IEEE80211_CSA 110 /* Channel Switch Announcement event */
+#define RTM_IEEE80211_RADAR 111 /* radar event */
+#define RTM_IEEE80211_CAC 112 /* Channel Availability Check event */
+#define RTM_IEEE80211_DEAUTH 113 /* station deauthenticate */
+#define RTM_IEEE80211_AUTH 114 /* station authenticate (ap mode) */
+#define RTM_IEEE80211_COUNTRY 115 /* discovered country code (sta mode) */
+#define RTM_IEEE80211_RADIO 116 /* RF kill switch state change */
/*
* Structure prepended to raw packets sent through the bpf
diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c
new file mode 100644
index 0000000..3c15505
--- /dev/null
+++ b/sys/net80211/ieee80211_hostap.c
@@ -0,0 +1,2236 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 HOSTAP mode support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_hostap.h>
+#include <net80211/ieee80211_input.h>
+#include <net80211/ieee80211_wds.h>
+
+#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2)
+
+static void hostap_vattach(struct ieee80211vap *);
+static int hostap_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int hostap_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp);
+static void hostap_deliver_data(struct ieee80211vap *,
+ struct ieee80211_node *, struct mbuf *);
+static void hostap_recv_mgmt(struct ieee80211_node *, struct mbuf *,
+ int subtype, int rssi, int noise, uint32_t rstamp);
+static void hostap_recv_pspoll(struct ieee80211_node *, struct mbuf *);
+
+void
+ieee80211_hostap_attach(struct ieee80211com *ic)
+{
+ ic->ic_vattach[IEEE80211_M_HOSTAP] = hostap_vattach;
+}
+
+void
+ieee80211_hostap_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+hostap_vdetach(struct ieee80211vap *vap)
+{
+}
+
+static void
+hostap_vattach(struct ieee80211vap *vap)
+{
+ vap->iv_newstate = hostap_newstate;
+ vap->iv_input = hostap_input;
+ vap->iv_recv_mgmt = hostap_recv_mgmt;
+ vap->iv_opdetach = hostap_vdetach;
+ vap->iv_deliver_data = hostap_deliver_data;
+}
+
+static void
+sta_disassoc(void *arg, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = arg;
+
+ if (ni->ni_vap == vap && ni->ni_associd != 0) {
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_ASSOC_LEAVE);
+ ieee80211_node_leave(ni);
+ }
+}
+
+/*
+ * IEEE80211_M_HOSTAP vap state machine handler.
+ */
+static int
+hostap_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ enum ieee80211_state ostate;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate], arg);
+ vap->iv_state = nstate; /* state transition */
+ if (ostate != IEEE80211_S_SCAN)
+ ieee80211_cancel_scan(vap); /* background scan */
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ switch (ostate) {
+ case IEEE80211_S_SCAN:
+ ieee80211_cancel_scan(vap);
+ break;
+ case IEEE80211_S_CAC:
+ ieee80211_dfs_cac_stop(vap);
+ break;
+ case IEEE80211_S_RUN:
+ ieee80211_iterate_nodes(&ic->ic_sta, sta_disassoc, vap);
+ break;
+ default:
+ break;
+ }
+ if (ostate != IEEE80211_S_INIT) {
+ /* NB: optimize INIT -> INIT case */
+ ieee80211_reset_bss(vap);
+ }
+ if (vap->iv_auth->ia_detach != NULL)
+ vap->iv_auth->ia_detach(vap);
+ break;
+ case IEEE80211_S_SCAN:
+ switch (ostate) {
+ case IEEE80211_S_CSA:
+ case IEEE80211_S_RUN:
+ ieee80211_iterate_nodes(&ic->ic_sta, sta_disassoc, vap);
+ /*
+ * Clear overlapping BSS state; the beacon frame
+ * will be reconstructed on transition to the RUN
+ * state and the timeout routines check if the flag
+ * is set before doing anything so this is sufficient.
+ */
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_NONHT_PR;
+ /* fall thru... */
+ case IEEE80211_S_CAC:
+ /*
+ * NB: We may get here because of a manual channel
+ * change in which case we need to stop CAC
+ * XXX no need to stop if ostate RUN but it's ok
+ */
+ ieee80211_dfs_cac_stop(vap);
+ /* fall thru... */
+ case IEEE80211_S_INIT:
+ if (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
+ !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
+ /*
+ * Already have a channel; bypass the
+ * scan and startup immediately.
+ * ieee80211_create_ibss will call back to
+ * move us to RUN state.
+ */
+ ieee80211_create_ibss(vap, vap->iv_des_chan);
+ break;
+ }
+ /*
+ * Initiate a scan. We can come here as a result
+ * of an IEEE80211_IOC_SCAN_REQ too in which case
+ * the vap will be marked with IEEE80211_FEXT_SCANREQ
+ * and the scan request parameters will be present
+ * in iv_scanreq. Otherwise we do the default.
+ */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
+ ieee80211_check_scan(vap,
+ vap->iv_scanreq_flags,
+ vap->iv_scanreq_duration,
+ vap->iv_scanreq_mindwell,
+ vap->iv_scanreq_maxdwell,
+ vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
+ } else
+ ieee80211_check_scan_current(vap);
+ break;
+ case IEEE80211_S_SCAN:
+ /*
+ * A state change requires a reset; scan.
+ */
+ ieee80211_check_scan_current(vap);
+ break;
+ default:
+ break;
+ }
+ break;
+ case IEEE80211_S_CAC:
+ /*
+ * Start CAC on a DFS channel. We come here when starting
+ * a bss on a DFS channel (see ieee80211_create_ibss).
+ */
+ ieee80211_dfs_cac_start(vap);
+ break;
+ case IEEE80211_S_RUN:
+ if (vap->iv_flags & IEEE80211_F_WPA) {
+ /* XXX validate prerequisites */
+ }
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ /*
+ * Already have a channel; bypass the
+ * scan and startup immediately.
+ * Note that ieee80211_create_ibss will call
+ * back to do a RUN->RUN state change.
+ */
+ ieee80211_create_ibss(vap,
+ ieee80211_ht_adjust_channel(ic,
+ ic->ic_curchan, vap->iv_flags_ext));
+ /* NB: iv_bss is changed on return */
+ break;
+ case IEEE80211_S_CAC:
+ /*
+ * NB: This is the normal state change when CAC
+ * expires and no radar was detected; no need to
+ * clear the CAC timer as it's already expired.
+ */
+ /* fall thru... */
+ case IEEE80211_S_CSA:
+ /*
+ * Update bss node channel to reflect where
+ * we landed after CSA.
+ */
+ ieee80211_node_set_chan(vap->iv_bss,
+ ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
+ ieee80211_htchanflags(vap->iv_bss->ni_chan)));
+ /* XXX bypass debug msgs */
+ break;
+ case IEEE80211_S_SCAN:
+ case IEEE80211_S_RUN:
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_debug(vap)) {
+ struct ieee80211_node *ni = vap->iv_bss;
+ ieee80211_note(vap,
+ "synchronized with %s ssid ",
+ ether_sprintf(ni->ni_bssid));
+ ieee80211_print_essid(ni->ni_essid,
+ ni->ni_esslen);
+ /* XXX MCS/HT */
+ printf(" channel %d start %uMb\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ IEEE80211_RATE2MBS(ni->ni_txrate));
+ }
+#endif
+ break;
+ default:
+ break;
+ }
+ /*
+ * Start/stop the authenticator. We delay until here
+ * to allow configuration to happen out of order.
+ */
+ if (vap->iv_auth->ia_attach != NULL) {
+ /* XXX check failure */
+ vap->iv_auth->ia_attach(vap);
+ } else if (vap->iv_auth->ia_detach != NULL) {
+ vap->iv_auth->ia_detach(vap);
+ }
+ ieee80211_node_authorize(vap->iv_bss);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void
+hostap_deliver_data(struct ieee80211vap *vap,
+ struct ieee80211_node *ni, struct mbuf *m)
+{
+ struct ether_header *eh = mtod(m, struct ether_header *);
+ struct ifnet *ifp = vap->iv_ifp;
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP,
+ ("gack, opmode %d", vap->iv_opmode));
+ /*
+ * Do accounting.
+ */
+ ifp->if_ipackets++;
+ IEEE80211_NODE_STAT(ni, rx_data);
+ IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len);
+ if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
+ m->m_flags |= M_MCAST; /* XXX M_BCAST? */
+ IEEE80211_NODE_STAT(ni, rx_mcast);
+ } else
+ IEEE80211_NODE_STAT(ni, rx_ucast);
+
+ /* clear driver/net80211 flags before passing up */
+ m->m_flags &= ~M_80211_RX;
+
+ /* perform as a bridge within the AP */
+ if ((vap->iv_flags & IEEE80211_F_NOBRIDGE) == 0) {
+ struct mbuf *mcopy = NULL;
+
+ if (m->m_flags & M_MCAST) {
+ mcopy = m_copypacket(m, M_DONTWAIT);
+ if (mcopy == NULL)
+ ifp->if_oerrors++;
+ else
+ mcopy->m_flags |= M_MCAST;
+ } else {
+ /*
+ * Check if the destination is associated with the
+ * same vap and authorized to receive traffic.
+ * Beware of traffic destined for the vap itself;
+ * sending it will not work; just let it be delivered
+ * normally.
+ */
+ struct ieee80211_node *sta = ieee80211_find_vap_node(
+ &vap->iv_ic->ic_sta, vap, eh->ether_dhost);
+ if (sta != NULL) {
+ if (ieee80211_node_is_authorized(sta)) {
+ /*
+ * Beware of sending to ourself; this
+ * needs to happen via the normal
+ * input path.
+ */
+ if (sta != vap->iv_bss) {
+ mcopy = m;
+ m = NULL;
+ }
+ } else {
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(sta, rx_unauth);
+ }
+ ieee80211_free_node(sta);
+ }
+ }
+ if (mcopy != NULL) {
+ int len, err;
+ len = mcopy->m_pkthdr.len;
+ IFQ_HANDOFF(ifp, mcopy, err);
+ if (err) {
+ /* NB: IFQ_HANDOFF reclaims mcopy */
+ } else {
+ ifp->if_opackets++;
+ }
+ }
+ }
+ if (m != NULL) {
+ /*
+ * Mark frame as coming from vap's interface.
+ */
+ m->m_pkthdr.rcvif = ifp;
+ if (m->m_flags & M_MCAST) {
+ /*
+ * Spam DWDS vap's w/ multicast traffic.
+ */
+ /* XXX only if dwds in use? */
+ ieee80211_dwds_mcast(vap, m);
+ }
+ if (ni->ni_vlan != 0) {
+ /* attach vlan tag */
+ m->m_pkthdr.ether_vtag = ni->ni_vlan;
+ m->m_flags |= M_VLANTAG;
+ }
+ ifp->if_input(ifp, m);
+ }
+}
+
+/*
+ * Decide if a received management frame should be
+ * printed when debugging is enabled. This filters some
+ * of the less interesting frames that come frequently
+ * (e.g. beacons).
+ */
+static __inline int
+doprint(struct ieee80211vap *vap, int subtype)
+{
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Process a received frame. The node associated with the sender
+ * should be supplied. If nothing was found in the node table then
+ * the caller is assumed to supply a reference to iv_bss instead.
+ * The RSSI and a timestamp are also supplied. The RSSI data is used
+ * during AP scanning to select a AP to associate with; it can have
+ * any units so long as values have consistent units and higher values
+ * mean ``better signal''. The receive timestamp is currently not used
+ * by the 802.11 layer.
+ */
+static int
+hostap_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp)
+{
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define HAS_SEQ(type) ((type & 0x4) == 0)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
+ struct ether_header *eh;
+ int hdrspace, need_tap;
+ uint8_t dir, type, subtype, qos;
+ uint8_t *bssid;
+ uint16_t rxseq;
+
+ if (m->m_flags & M_AMPDU) {
+ /*
+ * Fastpath for A-MPDU reorder q resubmission. Frames
+ * w/ M_AMPDU marked have already passed through here
+ * but were received out of order and been held on the
+ * reorder queue. When resubmitted they are marked
+ * with the M_AMPDU flag and we can bypass most of the
+ * normal processing.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ type = IEEE80211_FC0_TYPE_DATA;
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ subtype = IEEE80211_FC0_SUBTYPE_QOS;
+ hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
+ goto resubmit_ampdu;
+ }
+
+ KASSERT(ni != NULL, ("null node"));
+ ni->ni_inact = ni->ni_inact_reload;
+
+ need_tap = 1; /* mbuf need to be tapped. */
+ type = -1; /* undefined */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "too short (1): len %u", m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ /*
+ * Bit of a cheat here, we use a pointer for a 3-address
+ * frame format but don't reference fields past outside
+ * ieee80211_frame_min w/o first validating the data is
+ * present.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
+ vap->iv_stats.is_rx_badversion++;
+ goto err;
+ }
+
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ if (dir != IEEE80211_FC1_DIR_NODS)
+ bssid = wh->i_addr1;
+ else if (type == IEEE80211_FC0_TYPE_CTL)
+ bssid = wh->i_addr1;
+ else {
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap,
+ IEEE80211_MSG_ANY, ni->ni_macaddr,
+ NULL, "too short (2): len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ bssid = wh->i_addr3;
+ }
+ /*
+ * Validate the bssid.
+ */
+ if (!(type == IEEE80211_FC0_TYPE_MGT &&
+ subtype == IEEE80211_FC0_SUBTYPE_BEACON) &&
+ !IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) &&
+ !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
+ /* not interested in */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, NULL, "%s", "not to bss");
+ vap->iv_stats.is_rx_wrongbss++;
+ goto out;
+ }
+
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ if (HAS_SEQ(type)) {
+ uint8_t tid = ieee80211_gettid(wh);
+ if (IEEE80211_QOS_HAS_SEQ(wh) &&
+ TID_TO_WME_AC(tid) >= WME_AC_VI)
+ ic->ic_wme.wme_hipri_traffic++;
+ rxseq = le16toh(*(uint16_t *)wh->i_seq);
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
+ (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
+ /* duplicate, discard */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, "duplicate",
+ "seqno <%u,%u> fragno <%u,%u> tid %u",
+ rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[tid] >>
+ IEEE80211_SEQ_SEQ_SHIFT,
+ rxseq & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxseqs[tid] &
+ IEEE80211_SEQ_FRAG_MASK,
+ tid);
+ vap->iv_stats.is_rx_dup++;
+ IEEE80211_NODE_STAT(ni, rx_dup);
+ goto out;
+ }
+ ni->ni_rxseqs[tid] = rxseq;
+ }
+ }
+
+ switch (type) {
+ case IEEE80211_FC0_TYPE_DATA:
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ if (m->m_len < hdrspace &&
+ (m = m_pullup(m, hdrspace)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "data too short: expecting %u", hdrspace);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out; /* XXX */
+ }
+ if (!(dir == IEEE80211_FC1_DIR_TODS ||
+ (dir == IEEE80211_FC1_DIR_DSTODS &&
+ (vap->iv_flags & IEEE80211_F_DWDS)))) {
+ if (dir != IEEE80211_FC1_DIR_DSTODS) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_INPUT, wh, "data",
+ "incorrect dir 0x%x", dir);
+ } else {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_INPUT |
+ IEEE80211_MSG_WDS, wh,
+ "4-address data",
+ "%s", "DWDS not enabled");
+ }
+ vap->iv_stats.is_rx_wrongdir++;
+ goto out;
+ }
+ /* check if source STA is associated */
+ if (ni == vap->iv_bss) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "%s", "unknown src");
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_NOT_AUTHED);
+ vap->iv_stats.is_rx_notassoc++;
+ goto err;
+ }
+ if (ni->ni_associd == 0) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "%s", "unassoc src");
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_NOT_ASSOCED);
+ vap->iv_stats.is_rx_notassoc++;
+ goto err;
+ }
+
+ /*
+ * Check for power save state change.
+ * XXX out-of-order A-MPDU frames?
+ */
+ if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
+ (ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
+ ieee80211_node_pwrsave(ni,
+ wh->i_fc[1] & IEEE80211_FC1_PWR_MGT);
+ /*
+ * For 4-address packets handle WDS discovery
+ * notifications. Once a WDS link is setup frames
+ * are just delivered to the WDS vap (see below).
+ */
+ if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap == NULL) {
+ if (!ieee80211_node_is_authorized(ni)) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_INPUT |
+ IEEE80211_MSG_WDS, wh,
+ "4-address data",
+ "%s", "unauthorized port");
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(ni, rx_unauth);
+ goto err;
+ }
+ ieee80211_dwds_discover(ni, m);
+ return type;
+ }
+
+ /*
+ * Handle A-MPDU re-ordering. The station must be
+ * associated and negotiated HT. The frame must be
+ * a QoS frame (not QoS null data) and not previously
+ * processed for A-MPDU re-ordering. If the frame is
+ * to be processed directly then ieee80211_ampdu_reorder
+ * will return 0; otherwise it has consumed the mbuf
+ * and we should do nothing more with it.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+ ieee80211_ampdu_reorder(ni, m) != 0) {
+ m = NULL;
+ goto out;
+ }
+ resubmit_ampdu:
+
+ /*
+ * Handle privacy requirements. Note that we
+ * must not be preempted from here until after
+ * we (potentially) call ieee80211_crypto_demic;
+ * otherwise we may violate assumptions in the
+ * crypto cipher modules used to do delayed update
+ * of replay sequence numbers.
+ */
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "WEP", "%s", "PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ IEEE80211_NODE_STAT(ni, rx_noprivacy);
+ goto out;
+ }
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ IEEE80211_NODE_STAT(ni, rx_wepfail);
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ } else {
+ /* XXX M_WEP and IEEE80211_F_PRIVACY */
+ key = NULL;
+ }
+
+ /*
+ * Save QoS bits for use below--before we strip the header.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+ qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+ ((struct ieee80211_qosframe *)wh)->i_qos[0];
+ } else
+ qos = 0;
+
+ /*
+ * Next up, any fragmentation.
+ */
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ m = ieee80211_defrag(ni, m, hdrspace);
+ if (m == NULL) {
+ /* Fragment dropped or frame not complete yet */
+ goto out;
+ }
+ }
+ wh = NULL; /* no longer valid, catch any uses */
+
+ /*
+ * Next strip any MSDU crypto bits.
+ */
+ if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "demic error");
+ vap->iv_stats.is_rx_demicfail++;
+ IEEE80211_NODE_STAT(ni, rx_demicfail);
+ goto out;
+ }
+ /* copy to listener after decrypt */
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ need_tap = 0;
+ /*
+ * Finally, strip the 802.11 header.
+ */
+ m = ieee80211_decap(vap, m, hdrspace);
+ if (m == NULL) {
+ /* XXX mask bit to check for both */
+ /* don't count Null data frames as errors */
+ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
+ subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
+ goto out;
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "decap error");
+ vap->iv_stats.is_rx_decap++;
+ IEEE80211_NODE_STAT(ni, rx_decap);
+ goto err;
+ }
+ eh = mtod(m, struct ether_header *);
+ if (!ieee80211_node_is_authorized(ni)) {
+ /*
+ * Deny any non-PAE frames received prior to
+ * authorization. For open/shared-key
+ * authentication the port is mark authorized
+ * after authentication completes. For 802.1x
+ * the port is not marked authorized by the
+ * authenticator until the handshake has completed.
+ */
+ if (eh->ether_type != htons(ETHERTYPE_PAE)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ eh->ether_shost, "data",
+ "unauthorized port: ether type 0x%x len %u",
+ eh->ether_type, m->m_pkthdr.len);
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(ni, rx_unauth);
+ goto err;
+ }
+ } else {
+ /*
+ * When denying unencrypted frames, discard
+ * any non-PAE frames received without encryption.
+ */
+ if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
+ (key == NULL && (m->m_flags & M_WEP) == 0) &&
+ eh->ether_type != htons(ETHERTYPE_PAE)) {
+ /*
+ * Drop unencrypted frames.
+ */
+ vap->iv_stats.is_rx_unencrypted++;
+ IEEE80211_NODE_STAT(ni, rx_unencrypted);
+ goto out;
+ }
+ }
+ /* XXX require HT? */
+ if (qos & IEEE80211_QOS_AMSDU) {
+ m = ieee80211_decap_amsdu(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
+#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
+ m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
+ struct llc *llc;
+
+ /*
+ * Check for fast-frame tunnel encapsulation.
+ */
+ if (m->m_len < FF_LLC_SIZE &&
+ (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame",
+ "%s", "m_pullup(llc) failed");
+ vap->iv_stats.is_rx_tooshort++;
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ llc = (struct llc *)(mtod(m, uint8_t *) +
+ sizeof(struct ether_header));
+ if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
+ m_adj(m, FF_LLC_SIZE);
+ m = ieee80211_decap_fastframe(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ }
+#undef FF_LLC_SIZE
+ if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL)
+ ieee80211_deliver_data(ni->ni_wdsvap, ni, m);
+ else
+ hostap_deliver_data(vap, ni, m);
+ return IEEE80211_FC0_TYPE_DATA;
+
+ case IEEE80211_FC0_TYPE_MGT:
+ vap->iv_stats.is_rx_mgmt++;
+ IEEE80211_NODE_STAT(ni, rx_mgmt);
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "mgt", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto err;
+ }
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "mgt", "too short: len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
+ /* ensure return frames are unicast */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, NULL, "source is multicast: %s",
+ ether_sprintf(wh->i_addr2));
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */
+ goto out;
+ }
+#ifdef IEEE80211_DEBUG
+ if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
+ ieee80211_msg_dumppkts(vap)) {
+ if_printf(ifp, "received %s from %s rssi %d\n",
+ ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(wh->i_addr2), rssi);
+ }
+#endif
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
+ /*
+ * Only shared key auth frames with a challenge
+ * should be encrypted, discard all others.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL,
+ "%s", "WEP set but not permitted");
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
+ goto out;
+ }
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL, "%s", "WEP set but PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ goto out;
+ }
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ }
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
+ m_freem(m);
+ return IEEE80211_FC0_TYPE_MGT;
+
+ case IEEE80211_FC0_TYPE_CTL:
+ vap->iv_stats.is_rx_ctl++;
+ IEEE80211_NODE_STAT(ni, rx_ctrl);
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PS_POLL:
+ hostap_recv_pspoll(ni, m);
+ break;
+ case IEEE80211_FC0_SUBTYPE_BAR:
+ ieee80211_recv_bar(ni, m);
+ break;
+ }
+ goto out;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "bad", "frame type 0x%x", type);
+ /* should not come here */
+ break;
+ }
+err:
+ ifp->if_ierrors++;
+out:
+ if (m != NULL) {
+ if (bpf_peers_present(vap->iv_rawbpf) && need_tap)
+ bpf_mtap(vap->iv_rawbpf, m);
+ m_freem(m);
+ }
+ return type;
+#undef SEQ_LEQ
+}
+
+static void
+hostap_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh,
+ int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ KASSERT(vap->iv_state == IEEE80211_S_RUN, ("state %d", vap->iv_state));
+
+ if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "open auth",
+ "bad sta auth mode %u", ni->ni_authmode);
+ vap->iv_stats.is_rx_bad_auth++; /* XXX */
+ /*
+ * Clear any challenge text that may be there if
+ * a previous shared key auth failed and then an
+ * open auth is attempted.
+ */
+ if (ni->ni_challenge != NULL) {
+ FREE(ni->ni_challenge, M_80211_NODE);
+ ni->ni_challenge = NULL;
+ }
+ /* XXX hack to workaround calling convention */
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ (seq + 1) | (IEEE80211_STATUS_ALG<<16));
+ return;
+ }
+ if (seq != IEEE80211_AUTH_OPEN_REQUEST) {
+ vap->iv_stats.is_rx_bad_auth++;
+ return;
+ }
+ /* always accept open authentication requests */
+ if (ni == vap->iv_bss) {
+ ni = ieee80211_dup_bss(vap, wh->i_addr2);
+ if (ni == NULL)
+ return;
+ } else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0)
+ (void) ieee80211_ref_node(ni);
+ /*
+ * Mark the node as referenced to reflect that it's
+ * reference count has been bumped to insure it remains
+ * after the transaction completes.
+ */
+ ni->ni_flags |= IEEE80211_NODE_AREF;
+
+ if (vap->iv_acl != NULL &&
+ vap->iv_acl->iac_getpolicy(vap) == IEEE80211_MACCMD_POLICY_RADIUS) {
+ /*
+ * When the ACL policy is set to RADIUS we defer the
+ * authorization to a user agent. Dispatch an event,
+ * a subsequent MLME call will decide the fate of the
+ * station. If the user agent is not present then the
+ * node will be reclaimed due to inactivity.
+ */
+ IEEE80211_NOTE_MAC(vap,
+ IEEE80211_MSG_AUTH | IEEE80211_MSG_ACL, ni->ni_macaddr,
+ "%s", "station authentication defered (radius acl)");
+ ieee80211_notify_node_auth(ni);
+ } else {
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+ IEEE80211_NOTE_MAC(vap,
+ IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, ni->ni_macaddr,
+ "%s", "station authenticated (open)");
+ /*
+ * When 802.1x is not in use mark the port
+ * authorized at this point so traffic can flow.
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+ ieee80211_node_authorize(ni);
+ }
+}
+
+static void
+hostap_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
+ uint8_t *frm, uint8_t *efrm, int rssi, int noise, uint32_t rstamp,
+ uint16_t seq, uint16_t status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ uint8_t *challenge;
+ int allocbs, estatus;
+
+ KASSERT(vap->iv_state == IEEE80211_S_RUN, ("state %d", vap->iv_state));
+
+ /*
+ * NB: this can happen as we allow pre-shared key
+ * authentication to be enabled w/o wep being turned
+ * on so that configuration of these can be done
+ * in any order. It may be better to enforce the
+ * ordering in which case this check would just be
+ * for sanity/consistency.
+ */
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "%s", " PRIVACY is disabled");
+ estatus = IEEE80211_STATUS_ALG;
+ goto bad;
+ }
+ /*
+ * Pre-shared key authentication is evil; accept
+ * it only if explicitly configured (it is supported
+ * mainly for compatibility with clients like Mac OS X).
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
+ ni->ni_authmode != IEEE80211_AUTH_SHARED) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "bad sta auth mode %u", ni->ni_authmode);
+ vap->iv_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */
+ estatus = IEEE80211_STATUS_ALG;
+ goto bad;
+ }
+
+ challenge = NULL;
+ if (frm + 1 < efrm) {
+ if ((frm[1] + 2) > (efrm - frm)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "ie %d/%d too long",
+ frm[0], (frm[1] + 2) - (efrm - frm));
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ if (*frm == IEEE80211_ELEMID_CHALLENGE)
+ challenge = frm;
+ frm += frm[1] + 2;
+ }
+ switch (seq) {
+ case IEEE80211_AUTH_SHARED_CHALLENGE:
+ case IEEE80211_AUTH_SHARED_RESPONSE:
+ if (challenge == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "%s", "no challenge");
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "bad challenge len %d", challenge[1]);
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ default:
+ break;
+ }
+ switch (seq) {
+ case IEEE80211_AUTH_SHARED_REQUEST:
+ if (ni == vap->iv_bss) {
+ ni = ieee80211_dup_bss(vap, wh->i_addr2);
+ if (ni == NULL) {
+ /* NB: no way to return an error */
+ return;
+ }
+ allocbs = 1;
+ } else {
+ if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0)
+ (void) ieee80211_ref_node(ni);
+ allocbs = 0;
+ }
+ /*
+ * Mark the node as referenced to reflect that it's
+ * reference count has been bumped to insure it remains
+ * after the transaction completes.
+ */
+ ni->ni_flags |= IEEE80211_NODE_AREF;
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ if (!ieee80211_alloc_challenge(ni)) {
+ /* NB: don't return error so they rexmit */
+ return;
+ }
+ get_random_bytes(ni->ni_challenge,
+ IEEE80211_CHALLENGE_LEN);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+ ni, "shared key %sauth request", allocbs ? "" : "re");
+ /*
+ * When the ACL policy is set to RADIUS we defer the
+ * authorization to a user agent. Dispatch an event,
+ * a subsequent MLME call will decide the fate of the
+ * station. If the user agent is not present then the
+ * node will be reclaimed due to inactivity.
+ */
+ if (vap->iv_acl != NULL &&
+ vap->iv_acl->iac_getpolicy(vap) == IEEE80211_MACCMD_POLICY_RADIUS) {
+ IEEE80211_NOTE_MAC(vap,
+ IEEE80211_MSG_AUTH | IEEE80211_MSG_ACL,
+ ni->ni_macaddr,
+ "%s", "station authentication defered (radius acl)");
+ ieee80211_notify_node_auth(ni);
+ return;
+ }
+ break;
+ case IEEE80211_AUTH_SHARED_RESPONSE:
+ if (ni == vap->iv_bss) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key response",
+ "%s", "unknown station");
+ /* NB: don't send a response */
+ return;
+ }
+ if (ni->ni_challenge == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key response",
+ "%s", "no challenge recorded");
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ if (memcmp(ni->ni_challenge, &challenge[2],
+ challenge[1]) != 0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key response",
+ "%s", "challenge mismatch");
+ vap->iv_stats.is_rx_auth_fail++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+ ni, "%s", "station authenticated (shared key)");
+ ieee80211_node_authorize(ni);
+ break;
+ default:
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "bad seq %d", seq);
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_SEQUENCE;
+ goto bad;
+ }
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+ return;
+bad:
+ /*
+ * Send an error response; but only when operating as an AP.
+ */
+ /* XXX hack to workaround calling convention */
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ (seq + 1) | (estatus<<16));
+}
+
+/*
+ * Convert a WPA cipher selector OUI to an internal
+ * cipher algorithm. Where appropriate we also
+ * record any key length.
+ */
+static int
+wpa_cipher(const uint8_t *sel, uint8_t *keylen)
+{
+#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
+ uint32_t w = LE_READ_4(sel);
+
+ switch (w) {
+ case WPA_SEL(WPA_CSE_NULL):
+ return IEEE80211_CIPHER_NONE;
+ case WPA_SEL(WPA_CSE_WEP40):
+ if (keylen)
+ *keylen = 40 / NBBY;
+ return IEEE80211_CIPHER_WEP;
+ case WPA_SEL(WPA_CSE_WEP104):
+ if (keylen)
+ *keylen = 104 / NBBY;
+ return IEEE80211_CIPHER_WEP;
+ case WPA_SEL(WPA_CSE_TKIP):
+ return IEEE80211_CIPHER_TKIP;
+ case WPA_SEL(WPA_CSE_CCMP):
+ return IEEE80211_CIPHER_AES_CCM;
+ }
+ return 32; /* NB: so 1<< is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Convert a WPA key management/authentication algorithm
+ * to an internal code.
+ */
+static int
+wpa_keymgmt(const uint8_t *sel)
+{
+#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
+ uint32_t w = LE_READ_4(sel);
+
+ switch (w) {
+ case WPA_SEL(WPA_ASE_8021X_UNSPEC):
+ return WPA_ASE_8021X_UNSPEC;
+ case WPA_SEL(WPA_ASE_8021X_PSK):
+ return WPA_ASE_8021X_PSK;
+ case WPA_SEL(WPA_ASE_NONE):
+ return WPA_ASE_NONE;
+ }
+ return 0; /* NB: so is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Parse a WPA information element to collect parameters.
+ * Note that we do not validate security parameters; that
+ * is handled by the authenticator; the parsing done here
+ * is just for internal use in making operational decisions.
+ */
+static int
+ieee80211_parse_wpa(struct ieee80211vap *vap, const uint8_t *frm,
+ struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
+{
+ uint8_t len = frm[1];
+ uint32_t w;
+ int n;
+
+ /*
+ * Check the length once for fixed parts: OUI, type,
+ * version, mcast cipher, and 2 selector counts.
+ * Other, variable-length data, must be checked separately.
+ */
+ if ((vap->iv_flags & IEEE80211_F_WPA1) == 0) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "not WPA, flags 0x%x", vap->iv_flags);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ if (len < 14) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "too short, len %u", len);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ frm += 6, len -= 4; /* NB: len is payload only */
+ /* NB: iswapoui already validated the OUI and type */
+ w = LE_READ_2(frm);
+ if (w != WPA_VERSION) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "bad version %u", w);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ frm += 2, len -= 2;
+
+ memset(rsn, 0, sizeof(*rsn));
+
+ /* multicast/group cipher */
+ rsn->rsn_mcastcipher = wpa_cipher(frm, &rsn->rsn_mcastkeylen);
+ frm += 4, len -= 4;
+
+ /* unicast ciphers */
+ n = LE_READ_2(frm);
+ frm += 2, len -= 2;
+ if (len < n*4+2) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "ucast cipher data too short; len %u, n %u",
+ len, n);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ w = 0;
+ for (; n > 0; n--) {
+ w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen);
+ frm += 4, len -= 4;
+ }
+ if (w & (1<<IEEE80211_CIPHER_TKIP))
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
+ else
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+
+ /* key management algorithms */
+ n = LE_READ_2(frm);
+ frm += 2, len -= 2;
+ if (len < n*4) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "key mgmt alg data too short; len %u, n %u",
+ len, n);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ w = 0;
+ for (; n > 0; n--) {
+ w |= wpa_keymgmt(frm);
+ frm += 4, len -= 4;
+ }
+ if (w & WPA_ASE_8021X_UNSPEC)
+ rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC;
+ else
+ rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
+
+ if (len > 2) /* optional capabilities */
+ rsn->rsn_caps = LE_READ_2(frm);
+
+ return 0;
+}
+
+/*
+ * Convert an RSN cipher selector OUI to an internal
+ * cipher algorithm. Where appropriate we also
+ * record any key length.
+ */
+static int
+rsn_cipher(const uint8_t *sel, uint8_t *keylen)
+{
+#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
+ uint32_t w = LE_READ_4(sel);
+
+ switch (w) {
+ case RSN_SEL(RSN_CSE_NULL):
+ return IEEE80211_CIPHER_NONE;
+ case RSN_SEL(RSN_CSE_WEP40):
+ if (keylen)
+ *keylen = 40 / NBBY;
+ return IEEE80211_CIPHER_WEP;
+ case RSN_SEL(RSN_CSE_WEP104):
+ if (keylen)
+ *keylen = 104 / NBBY;
+ return IEEE80211_CIPHER_WEP;
+ case RSN_SEL(RSN_CSE_TKIP):
+ return IEEE80211_CIPHER_TKIP;
+ case RSN_SEL(RSN_CSE_CCMP):
+ return IEEE80211_CIPHER_AES_CCM;
+ case RSN_SEL(RSN_CSE_WRAP):
+ return IEEE80211_CIPHER_AES_OCB;
+ }
+ return 32; /* NB: so 1<< is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Convert an RSN key management/authentication algorithm
+ * to an internal code.
+ */
+static int
+rsn_keymgmt(const uint8_t *sel)
+{
+#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
+ uint32_t w = LE_READ_4(sel);
+
+ switch (w) {
+ case RSN_SEL(RSN_ASE_8021X_UNSPEC):
+ return RSN_ASE_8021X_UNSPEC;
+ case RSN_SEL(RSN_ASE_8021X_PSK):
+ return RSN_ASE_8021X_PSK;
+ case RSN_SEL(RSN_ASE_NONE):
+ return RSN_ASE_NONE;
+ }
+ return 0; /* NB: so is discarded */
+#undef RSN_SEL
+}
+
+/*
+ * Parse a WPA/RSN information element to collect parameters
+ * and validate the parameters against what has been
+ * configured for the system.
+ */
+static int
+ieee80211_parse_rsn(struct ieee80211vap *vap, const uint8_t *frm,
+ struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
+{
+ uint8_t len = frm[1];
+ uint32_t w;
+ int n;
+
+ /*
+ * Check the length once for fixed parts:
+ * version, mcast cipher, and 2 selector counts.
+ * Other, variable-length data, must be checked separately.
+ */
+ if ((vap->iv_flags & IEEE80211_F_WPA2) == 0) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "not RSN, flags 0x%x", vap->iv_flags);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ if (len < 10) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "RSN", "too short, len %u", len);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ frm += 2;
+ w = LE_READ_2(frm);
+ if (w != RSN_VERSION) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "RSN", "bad version %u", w);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ frm += 2, len -= 2;
+
+ memset(rsn, 0, sizeof(*rsn));
+
+ /* multicast/group cipher */
+ rsn->rsn_mcastcipher = rsn_cipher(frm, &rsn->rsn_mcastkeylen);
+ frm += 4, len -= 4;
+
+ /* unicast ciphers */
+ n = LE_READ_2(frm);
+ frm += 2, len -= 2;
+ if (len < n*4+2) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "RSN", "ucast cipher data too short; len %u, n %u",
+ len, n);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ w = 0;
+ for (; n > 0; n--) {
+ w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen);
+ frm += 4, len -= 4;
+ }
+ if (w & (1<<IEEE80211_CIPHER_TKIP))
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
+ else
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+
+ /* key management algorithms */
+ n = LE_READ_2(frm);
+ frm += 2, len -= 2;
+ if (len < n*4) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "RSN", "key mgmt alg data too short; len %u, n %u",
+ len, n);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ w = 0;
+ for (; n > 0; n--) {
+ w |= rsn_keymgmt(frm);
+ frm += 4, len -= 4;
+ }
+ if (w & RSN_ASE_8021X_UNSPEC)
+ rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC;
+ else
+ rsn->rsn_keymgmt = RSN_ASE_8021X_PSK;
+
+ /* optional RSN capabilities */
+ if (len > 2)
+ rsn->rsn_caps = LE_READ_2(frm);
+ /* XXXPMKID */
+
+ return 0;
+}
+
+/*
+ * WPA/802.11i assocation request processing.
+ */
+static int
+wpa_assocreq(struct ieee80211_node *ni, struct ieee80211_rsnparms *rsnparms,
+ const struct ieee80211_frame *wh, const uint8_t *wpa,
+ const uint8_t *rsn, uint16_t capinfo)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ uint8_t reason;
+ int badwparsn;
+
+ ni->ni_flags &= ~(IEEE80211_NODE_WPS|IEEE80211_NODE_TSN);
+ if (wpa == NULL && rsn == NULL) {
+ if (vap->iv_flags_ext & IEEE80211_FEXT_WPS) {
+ /*
+ * W-Fi Protected Setup (WPS) permits
+ * clients to associate and pass EAPOL frames
+ * to establish initial credentials.
+ */
+ ni->ni_flags |= IEEE80211_NODE_WPS;
+ return 1;
+ }
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_TSN) &&
+ (capinfo & IEEE80211_CAPINFO_PRIVACY)) {
+ /*
+ * Transitional Security Network. Permits clients
+ * to associate and use WEP while WPA is configured.
+ */
+ ni->ni_flags |= IEEE80211_NODE_TSN;
+ return 1;
+ }
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
+ wh, NULL, "%s", "no WPA/RSN IE in association request");
+ vap->iv_stats.is_rx_assoc_badwpaie++;
+ reason = IEEE80211_REASON_IE_INVALID;
+ goto bad;
+ }
+ /* assert right association security credentials */
+ badwparsn = 0; /* NB: to silence compiler */
+ switch (vap->iv_flags & IEEE80211_F_WPA) {
+ case IEEE80211_F_WPA1:
+ badwparsn = (wpa == NULL);
+ break;
+ case IEEE80211_F_WPA2:
+ badwparsn = (rsn == NULL);
+ break;
+ case IEEE80211_F_WPA1|IEEE80211_F_WPA2:
+ badwparsn = (wpa == NULL && rsn == NULL);
+ break;
+ }
+ if (badwparsn) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
+ wh, NULL,
+ "%s", "missing WPA/RSN IE in association request");
+ vap->iv_stats.is_rx_assoc_badwpaie++;
+ reason = IEEE80211_REASON_IE_INVALID;
+ goto bad;
+ }
+ /*
+ * Parse WPA/RSN information element.
+ */
+ if (wpa != NULL)
+ reason = ieee80211_parse_wpa(vap, wpa, rsnparms, wh);
+ else
+ reason = ieee80211_parse_rsn(vap, rsn, rsnparms, wh);
+ if (reason != 0) {
+ /* XXX distinguish WPA/RSN? */
+ vap->iv_stats.is_rx_assoc_badwpaie++;
+ goto bad;
+ }
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, ni,
+ "%s ie: mc %u/%u uc %u/%u key %u caps 0x%x",
+ wpa != NULL ? "WPA" : "RSN",
+ rsnparms->rsn_mcastcipher, rsnparms->rsn_mcastkeylen,
+ rsnparms->rsn_ucastcipher, rsnparms->rsn_ucastkeylen,
+ rsnparms->rsn_keymgmt, rsnparms->rsn_caps);
+
+ return 1;
+bad:
+ ieee80211_node_deauth(ni, reason);
+ return 0;
+}
+
+/* XXX find a better place for definition */
+struct l2_update_frame {
+ struct ether_header eh;
+ uint8_t dsap;
+ uint8_t ssap;
+ uint8_t control;
+ uint8_t xid[3];
+} __packed;
+
+/*
+ * Deliver a TGf L2UF frame on behalf of a station.
+ * This primes any bridge when the station is roaming
+ * between ap's on the same wired network.
+ */
+static void
+ieee80211_deliver_l2uf(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct mbuf *m;
+ struct l2_update_frame *l2uf;
+ struct ether_header *eh;
+
+ m = m_gethdr(M_NOWAIT, MT_DATA);
+ if (m == NULL) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
+ "%s", "no mbuf for l2uf frame");
+ vap->iv_stats.is_rx_nobuf++; /* XXX not right */
+ return;
+ }
+ l2uf = mtod(m, struct l2_update_frame *);
+ eh = &l2uf->eh;
+ /* dst: Broadcast address */
+ IEEE80211_ADDR_COPY(eh->ether_dhost, ifp->if_broadcastaddr);
+ /* src: associated STA */
+ IEEE80211_ADDR_COPY(eh->ether_shost, ni->ni_macaddr);
+ eh->ether_type = htons(sizeof(*l2uf) - sizeof(*eh));
+
+ l2uf->dsap = 0;
+ l2uf->ssap = 0;
+ l2uf->control = 0xf5;
+ l2uf->xid[0] = 0x81;
+ l2uf->xid[1] = 0x80;
+ l2uf->xid[2] = 0x00;
+
+ m->m_pkthdr.len = m->m_len = sizeof(*l2uf);
+ hostap_deliver_data(vap, ni, m);
+}
+
+static void
+ratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+ int reassoc, int resp, const char *tag, int rate)
+{
+ IEEE80211_NOTE_MAC(ni->ni_vap, IEEE80211_MSG_ANY, wh->i_addr2,
+ "deny %s request, %s rate set mismatch, rate/MCS %d",
+ reassoc ? "reassoc" : "assoc", tag, rate & IEEE80211_RATE_VAL);
+ IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_BASIC_RATE);
+ ieee80211_node_leave(ni);
+}
+
+static void
+capinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+ int reassoc, int resp, const char *tag, int capinfo)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, wh->i_addr2,
+ "deny %s request, %s mismatch 0x%x",
+ reassoc ? "reassoc" : "assoc", tag, capinfo);
+ IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_CAPINFO);
+ ieee80211_node_leave(ni);
+ vap->iv_stats.is_rx_assoc_capmismatch++;
+}
+
+static void
+htcapmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+ int reassoc, int resp)
+{
+ IEEE80211_NOTE_MAC(ni->ni_vap, IEEE80211_MSG_ANY, wh->i_addr2,
+ "deny %s request, %s missing HT ie", reassoc ? "reassoc" : "assoc");
+ /* XXX no better code */
+ IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_OTHER);
+ ieee80211_node_leave(ni);
+}
+
+static void
+authalgreject(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+ int algo, int seq, int status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, NULL, "unsupported alg %d", algo);
+ vap->iv_stats.is_rx_auth_unsupported++;
+ ieee80211_send_error(ni, wh->i_addr2, IEEE80211_FC0_SUBTYPE_AUTH,
+ seq | (status << 16));
+}
+
+static __inline int
+ishtmixed(const uint8_t *ie)
+{
+ const struct ieee80211_ie_htinfo *ht =
+ (const struct ieee80211_ie_htinfo *) ie;
+ return (ht->hi_byte2 & IEEE80211_HTINFO_OPMODE) ==
+ IEEE80211_HTINFO_OPMODE_MIXED;
+}
+
+static int
+is11bclient(const uint8_t *rates, const uint8_t *xrates)
+{
+ static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11);
+ int i;
+
+ /* NB: the 11b clients we care about will not have xrates */
+ if (xrates != NULL || rates == NULL)
+ return 0;
+ for (i = 0; i < rates[1]; i++) {
+ int r = rates[2+i] & IEEE80211_RATE_VAL;
+ if (r > 2*11 || ((1<<r) & brates) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+static void
+hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
+ int subtype, int rssi, int noise, uint32_t rstamp)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame *wh;
+ uint8_t *frm, *efrm, *sfrm;
+ uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap;
+ int reassoc, resp;
+ uint8_t rate;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (uint8_t *)&wh[1];
+ efrm = mtod(m0, uint8_t *) + m0->m_len;
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_BEACON: {
+ struct ieee80211_scanparams scan;
+ /*
+ * We process beacon/probe response frames when scanning;
+ * otherwise we check beacon frames for overlapping non-ERP
+ * BSS in 11g and/or overlapping legacy BSS when in HT.
+ */
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 &&
+ subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ /* NB: accept off-channel frames */
+ if (ieee80211_parse_beacon(ni, m0, &scan) &~ IEEE80211_BPARSE_OFFCHAN)
+ return;
+ /*
+ * Count frame now that we know it's to be processed.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+ vap->iv_stats.is_rx_beacon++; /* XXX remove */
+ IEEE80211_NODE_STAT(ni, rx_beacons);
+ } else
+ IEEE80211_NODE_STAT(ni, rx_proberesp);
+ /*
+ * If scanning, just pass information to the scan module.
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ if (scan.status == 0 && /* NB: on channel */
+ (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN)) {
+ /*
+ * Actively scanning a channel marked passive;
+ * send a probe request now that we know there
+ * is 802.11 traffic present.
+ *
+ * XXX check if the beacon we recv'd gives
+ * us what we need and suppress the probe req
+ */
+ ieee80211_probe_curchan(vap, 1);
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
+ }
+ ieee80211_add_scan(vap, &scan, wh,
+ subtype, rssi, noise, rstamp);
+ return;
+ }
+ /*
+ * Check beacon for overlapping bss w/ non ERP stations.
+ * If we detect one and protection is configured but not
+ * enabled, enable it and start a timer that'll bring us
+ * out if we stop seeing the bss.
+ */
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
+ scan.status == 0 && /* NB: on-channel */
+ ((scan.erp & 0x100) == 0 || /* NB: no ERP, 11b sta*/
+ (scan.erp & IEEE80211_ERP_NON_ERP_PRESENT))) {
+ ic->ic_lastnonerp = ticks;
+ ic->ic_flags_ext |= IEEE80211_FEXT_NONERP_PR;
+ if (ic->ic_protmode != IEEE80211_PROT_NONE &&
+ (ic->ic_flags & IEEE80211_F_USEPROT) == 0) {
+ IEEE80211_NOTE_FRAME(vap,
+ IEEE80211_MSG_ASSOC, wh,
+ "non-ERP present on channel %d "
+ "(saw erp 0x%x from channel %d), "
+ "enable use of protection",
+ ic->ic_curchan->ic_ieee,
+ scan.erp, scan.chan);
+ ic->ic_flags |= IEEE80211_F_USEPROT;
+ ieee80211_notify_erp(ic);
+ }
+ }
+ /*
+ * Check beacon for non-HT station on HT channel
+ * and update HT BSS occupancy as appropriate.
+ */
+ if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) {
+ if (scan.status & IEEE80211_BPARSE_OFFCHAN) {
+ /*
+ * Off control channel; only check frames
+ * that come in the extension channel when
+ * operating w/ HT40.
+ */
+ if (!IEEE80211_IS_CHAN_HT40(ic->ic_curchan))
+ break;
+ if (scan.chan != ic->ic_curchan->ic_extieee)
+ break;
+ }
+ if (scan.htinfo == NULL) {
+ ieee80211_htprot_update(ic,
+ IEEE80211_HTINFO_OPMODE_PROTOPT |
+ IEEE80211_HTINFO_NONHT_PRESENT);
+ } else if (ishtmixed(scan.htinfo)) {
+ /* XXX? take NONHT_PRESENT from beacon? */
+ ieee80211_htprot_update(ic,
+ IEEE80211_HTINFO_OPMODE_MIXED |
+ IEEE80211_HTINFO_NONHT_PRESENT);
+ }
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ /*
+ * prreq frame format
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ */
+ ssid = rates = xrates = NULL;
+ sfrm = frm;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
+ if (xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(xrates,
+ IEEE80211_RATE_MAXSIZE - rates[1], return);
+ IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
+ IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return);
+ if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL,
+ "%s", "no ssid with ssid suppression enabled");
+ vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/
+ return;
+ }
+
+ /* XXX find a better class or define it's own */
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2,
+ "%s", "recv probe req");
+ /*
+ * Some legacy 11b clients cannot hack a complete
+ * probe response frame. When the request includes
+ * only a bare-bones rate set, communicate this to
+ * the transmit side.
+ */
+ ieee80211_send_proberesp(vap, wh->i_addr2,
+ is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0);
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_AUTH: {
+ uint16_t algo, seq, status;
+
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bss->ni_bssid)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, NULL, "%s", "wrong bssid");
+ vap->iv_stats.is_rx_wrongbss++; /*XXX unique stat?*/
+ return;
+ }
+ /*
+ * auth frame format
+ * [2] algorithm
+ * [2] sequence
+ * [2] status
+ * [tlv*] challenge
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
+ algo = le16toh(*(uint16_t *)frm);
+ seq = le16toh(*(uint16_t *)(frm + 2));
+ status = le16toh(*(uint16_t *)(frm + 4));
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2,
+ "recv auth frame with algorithm %d seq %d", algo, seq);
+ /*
+ * Consult the ACL policy module if setup.
+ */
+ if (vap->iv_acl != NULL &&
+ !vap->iv_acl->iac_check(vap, wh->i_addr2)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
+ wh, NULL, "%s", "disallowed by ACL");
+ vap->iv_stats.is_rx_acl++;
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ (seq+1) | (IEEE80211_STATUS_UNSPECIFIED<<16));
+ return;
+ }
+ if (vap->iv_flags & IEEE80211_F_COUNTERM) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
+ wh, NULL, "%s", "TKIP countermeasures enabled");
+ vap->iv_stats.is_rx_auth_countermeasures++;
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ IEEE80211_REASON_MIC_FAILURE);
+ return;
+ }
+ if (algo == IEEE80211_AUTH_ALG_SHARED)
+ hostap_auth_shared(ni, wh, frm + 6, efrm, rssi,
+ noise, rstamp, seq, status);
+ else if (algo == IEEE80211_AUTH_ALG_OPEN)
+ hostap_auth_open(ni, wh, rssi, noise, rstamp,
+ seq, status);
+ else if (algo == IEEE80211_AUTH_ALG_LEAP) {
+ authalgreject(ni, wh, algo,
+ seq+1, IEEE80211_STATUS_ALG);
+ return;
+ } else {
+ /*
+ * We assume that an unknown algorithm is the result
+ * of a decryption failure on a shared key auth frame;
+ * return a status code appropriate for that instead
+ * of IEEE80211_STATUS_ALG.
+ *
+ * NB: a seq# of 4 is intentional; the decrypted
+ * frame likely has a bogus seq value.
+ */
+ authalgreject(ni, wh, algo,
+ 4, IEEE80211_STATUS_CHALLENGE);
+ return;
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: {
+ uint16_t capinfo, lintval;
+ struct ieee80211_rsnparms rsnparms;
+
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bss->ni_bssid)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, NULL, "%s", "wrong bssid");
+ vap->iv_stats.is_rx_assoc_bss++;
+ return;
+ }
+ if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
+ reassoc = 1;
+ resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
+ } else {
+ reassoc = 0;
+ resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
+ }
+ if (ni == vap->iv_bss) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, wh->i_addr2,
+ "deny %s request, sta not authenticated",
+ reassoc ? "reassoc" : "assoc");
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_ASSOC_NOT_AUTHED);
+ vap->iv_stats.is_rx_assoc_notauth++;
+ return;
+ }
+
+ /*
+ * asreq frame format
+ * [2] capability information
+ * [2] listen interval
+ * [6*] current AP address (reassoc only)
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ * [tlv] WPA or RSN
+ * [tlv] HT capabilities
+ * [tlv] Atheros capabilities
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4), return);
+ capinfo = le16toh(*(uint16_t *)frm); frm += 2;
+ lintval = le16toh(*(uint16_t *)frm); frm += 2;
+ if (reassoc)
+ frm += 6; /* ignore current AP info */
+ ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL;
+ sfrm = frm;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ case IEEE80211_ELEMID_RSN:
+ rsn = frm;
+ break;
+ case IEEE80211_ELEMID_HTCAP:
+ htcap = frm;
+ break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswpaoui(frm))
+ wpa = frm;
+ else if (iswmeinfo(frm))
+ wme = frm;
+ else if (isatherosoui(frm))
+ ath = frm;
+ else if (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
+ if (ishtcapoui(frm) && htcap == NULL)
+ htcap = frm;
+ }
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
+ if (xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(xrates,
+ IEEE80211_RATE_MAXSIZE - rates[1], return);
+ IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
+ IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return);
+ if (htcap != NULL) {
+ IEEE80211_VERIFY_LENGTH(htcap[1],
+ htcap[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof(struct ieee80211_ie_htcap)-2 :
+ sizeof(struct ieee80211_ie_htcap)-2,
+ return); /* XXX just NULL out? */
+ }
+
+ if ((vap->iv_flags & IEEE80211_F_WPA) &&
+ !wpa_assocreq(ni, &rsnparms, wh, wpa, rsn, capinfo))
+ return;
+ /* discard challenge after association */
+ if (ni->ni_challenge != NULL) {
+ FREE(ni->ni_challenge, M_80211_NODE);
+ ni->ni_challenge = NULL;
+ }
+ /* NB: 802.11 spec says to ignore station's privacy bit */
+ if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) {
+ capinfomismatch(ni, wh, reassoc, resp,
+ "capability", capinfo);
+ return;
+ }
+ /*
+ * Disallow re-associate w/ invalid slot time setting.
+ */
+ if (ni->ni_associd != 0 &&
+ IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
+ ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME)) {
+ capinfomismatch(ni, wh, reassoc, resp,
+ "slot time", capinfo);
+ return;
+ }
+ rate = ieee80211_setup_rates(ni, rates, xrates,
+ IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
+ IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (rate & IEEE80211_RATE_BASIC) {
+ ratesetmismatch(ni, wh, reassoc, resp, "legacy", rate);
+ vap->iv_stats.is_rx_assoc_norate++;
+ return;
+ }
+ /*
+ * If constrained to 11g-only stations reject an
+ * 11b-only station. We cheat a bit here by looking
+ * at the max negotiated xmit rate and assuming anyone
+ * with a best rate <24Mb/s is an 11b station.
+ */
+ if ((vap->iv_flags & IEEE80211_F_PUREG) && rate < 48) {
+ ratesetmismatch(ni, wh, reassoc, resp, "11g", rate);
+ vap->iv_stats.is_rx_assoc_norate++;
+ return;
+ }
+ /*
+ * Do HT rate set handling and setup HT node state.
+ */
+ ni->ni_chan = vap->iv_bss->ni_chan;
+ if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && htcap != NULL) {
+ rate = ieee80211_setup_htrates(ni, htcap,
+ IEEE80211_F_DOFMCS | IEEE80211_F_DONEGO |
+ IEEE80211_F_DOBRS);
+ if (rate & IEEE80211_RATE_BASIC) {
+ ratesetmismatch(ni, wh, reassoc, resp,
+ "HT", rate);
+ vap->iv_stats.is_ht_assoc_norate++;
+ return;
+ }
+ ieee80211_ht_node_init(ni, htcap);
+ } else if (ni->ni_flags & IEEE80211_NODE_HT)
+ ieee80211_ht_node_cleanup(ni);
+ /*
+ * Allow AMPDU operation only with unencrypted traffic
+ * or AES-CCM; the 11n spec only specifies these ciphers
+ * so permitting any others is undefined and can lead
+ * to interoperability problems.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ (((vap->iv_flags & IEEE80211_F_WPA) &&
+ rsnparms.rsn_ucastcipher != IEEE80211_CIPHER_AES_CCM) ||
+ (vap->iv_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) {
+ IEEE80211_NOTE(vap,
+ IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
+ "disallow HT use because WEP or TKIP requested, "
+ "capinfo 0x%x ucastcipher %d", capinfo,
+ rsnparms.rsn_ucastcipher);
+ ieee80211_ht_node_cleanup(ni);
+ vap->iv_stats.is_ht_assoc_downgrade++;
+ }
+ /*
+ * If constrained to 11n-only stations reject legacy stations.
+ */
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_PUREN) &&
+ (ni->ni_flags & IEEE80211_NODE_HT) == 0) {
+ htcapmismatch(ni, wh, reassoc, resp);
+ vap->iv_stats.is_ht_assoc_nohtcap++;
+ return;
+ }
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ ni->ni_intval = lintval;
+ ni->ni_capinfo = capinfo;
+ ni->ni_fhdwell = vap->iv_bss->ni_fhdwell;
+ ni->ni_fhindex = vap->iv_bss->ni_fhindex;
+ /*
+ * Store the IEs.
+ * XXX maybe better to just expand
+ */
+ if (ieee80211_ies_init(&ni->ni_ies, sfrm, efrm - sfrm)) {
+#define setie(_ie, _off) ieee80211_ies_setie(ni->ni_ies, _ie, _off)
+ if (wpa != NULL)
+ setie(wpa_ie, wpa - sfrm);
+ if (rsn != NULL)
+ setie(rsn_ie, rsn - sfrm);
+ if (htcap != NULL)
+ setie(htcap_ie, htcap - sfrm);
+ if (wme != NULL) {
+ setie(wme_ie, wme - sfrm);
+ /*
+ * Mark node as capable of QoS.
+ */
+ ni->ni_flags |= IEEE80211_NODE_QOS;
+ } else
+ ni->ni_flags &= ~IEEE80211_NODE_QOS;
+ if (ath != NULL) {
+ setie(ath_ie, ath - sfrm);
+ /*
+ * Parse ATH station parameters.
+ */
+ ieee80211_parse_ath(ni, ni->ni_ies.ath_ie);
+ } else
+ ni->ni_ath_flags = 0;
+#undef setie
+ } else {
+ ni->ni_flags &= ~IEEE80211_NODE_QOS;
+ ni->ni_ath_flags = 0;
+ }
+ ieee80211_node_join(ni, resp);
+ ieee80211_deliver_l2uf(ni);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ case IEEE80211_FC0_SUBTYPE_DISASSOC: {
+ uint16_t reason;
+
+ if (vap->iv_state != IEEE80211_S_RUN ||
+ /* NB: can happen when in promiscuous mode */
+ !IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ }
+ /*
+ * deauth/disassoc frame format
+ * [2] reason
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
+ reason = le16toh(*(uint16_t *)frm);
+ if (subtype == IEEE80211_FC0_SUBTYPE_DEAUTH) {
+ vap->iv_stats.is_rx_deauth++;
+ IEEE80211_NODE_STAT(ni, rx_deauth);
+ } else {
+ vap->iv_stats.is_rx_disassoc++;
+ IEEE80211_NODE_STAT(ni, rx_disassoc);
+ }
+ IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
+ "recv %s (reason %d)", ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT], reason);
+ if (ni != vap->iv_bss)
+ ieee80211_node_leave(ni);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_ACTION:
+ if (vap->iv_state == IEEE80211_S_RUN) {
+ if (ieee80211_parse_action(ni, m0) == 0)
+ ic->ic_recv_action(ni, frm, efrm);
+ } else
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "mgt", "subtype 0x%x not handled", subtype);
+ vap->iv_stats.is_rx_badsubtype++;
+ break;
+ }
+}
+
+/*
+ * Process a received ps-poll frame.
+ */
+static void
+hostap_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m0)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_frame_min *wh;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct mbuf *m;
+ uint16_t aid;
+ int qlen;
+
+ wh = mtod(m0, struct ieee80211_frame_min *);
+ if (ni->ni_associd == 0) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
+ (struct ieee80211_frame *) wh, NULL,
+ "%s", "unassociated station");
+ vap->iv_stats.is_ps_unassoc++;
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_NOT_ASSOCED);
+ return;
+ }
+
+ aid = le16toh(*(uint16_t *)wh->i_dur);
+ if (aid != ni->ni_associd) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
+ (struct ieee80211_frame *) wh, NULL,
+ "aid mismatch: sta aid 0x%x poll aid 0x%x",
+ ni->ni_associd, aid);
+ vap->iv_stats.is_ps_badaid++;
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_NOT_ASSOCED);
+ return;
+ }
+
+ /* Okay, take the first queued packet and put it out... */
+ IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
+ if (m == NULL) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_POWER, wh->i_addr2,
+ "%s", "recv ps-poll, but queue empty");
+ ieee80211_send_nulldata(ieee80211_ref_node(ni));
+ vap->iv_stats.is_ps_qempty++; /* XXX node stat */
+ if (vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 0); /* just in case */
+ return;
+ }
+ /*
+ * If there are more packets, set the more packets bit
+ * in the packet dispatched to the station; otherwise
+ * turn off the TIM bit.
+ */
+ if (qlen != 0) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "recv ps-poll, send packet, %u still queued", qlen);
+ m->m_flags |= M_MORE_DATA;
+ } else {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "%s", "recv ps-poll, send packet, queue empty");
+ if (vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 0);
+ }
+ m->m_flags |= M_PWR_SAV; /* bypass PS handling */
+ IF_ENQUEUE(&ifp->if_snd, m);
+ if_start(ifp);
+}
diff --git a/sys/net80211/ieee80211_hostap.h b/sys/net80211/ieee80211_hostap.h
new file mode 100644
index 0000000..87f858d
--- /dev/null
+++ b/sys/net80211/ieee80211_hostap.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_HOSTAP_H_
+#define _NET80211_IEEE80211_HOSTAP_H_
+
+/*
+ * Hostap implementation definitions.
+ */
+void ieee80211_hostap_attach(struct ieee80211com *);
+void ieee80211_hostap_detach(struct ieee80211com *);
+#endif /* !_NET80211_IEEE80211_HOSTAP_H_ */
diff --git a/sys/net80211/ieee80211_ht.c b/sys/net80211/ieee80211_ht.c
index 63fb39a..d8b2d3d 100644
--- a/sys/net80211/ieee80211_ht.c
+++ b/sys/net80211/ieee80211_ht.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
*/
#include "opt_inet.h"
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/kernel.h>
@@ -46,37 +47,34 @@ __FBSDID("$FreeBSD$");
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_input.h>
/* define here, used throughout file */
#define MS(_v, _f) (((_v) & _f) >> _f##_S)
#define SM(_v, _f) (((_v) << _f##_S) & _f)
-/* XXX need max array size */
-/* NB: these are for HT20 w/ long GI */
-const int ieee80211_htrates[16] = {
- 13, /* IFM_IEEE80211_MCS0 */
- 26, /* IFM_IEEE80211_MCS1 */
- 39, /* IFM_IEEE80211_MCS2 */
- 52, /* IFM_IEEE80211_MCS3 */
- 78, /* IFM_IEEE80211_MCS4 */
- 104, /* IFM_IEEE80211_MCS5 */
- 117, /* IFM_IEEE80211_MCS6 */
- 130, /* IFM_IEEE80211_MCS7 */
- 26, /* IFM_IEEE80211_MCS8 */
- 52, /* IFM_IEEE80211_MCS9 */
- 78, /* IFM_IEEE80211_MCS10 */
- 104, /* IFM_IEEE80211_MCS11 */
- 156, /* IFM_IEEE80211_MCS12 */
- 208, /* IFM_IEEE80211_MCS13 */
- 234, /* IFM_IEEE80211_MCS14 */
- 260, /* IFM_IEEE80211_MCS15 */
+const struct ieee80211_mcs_rates ieee80211_htrates[16] = {
+ { 13, 14, 27, 30 }, /* MCS 0 */
+ { 26, 29, 54, 60 }, /* MCS 1 */
+ { 39, 43, 81, 90 }, /* MCS 2 */
+ { 52, 58, 108, 120 }, /* MCS 3 */
+ { 78, 87, 162, 180 }, /* MCS 4 */
+ { 104, 116, 216, 240 }, /* MCS 5 */
+ { 117, 130, 243, 270 }, /* MCS 6 */
+ { 130, 144, 270, 300 }, /* MCS 7 */
+ { 26, 29, 54, 60 }, /* MCS 8 */
+ { 52, 58, 108, 120 }, /* MCS 9 */
+ { 78, 87, 162, 180 }, /* MCS 10 */
+ { 104, 116, 216, 240 }, /* MCS 11 */
+ { 156, 173, 324, 360 }, /* MCS 12 */
+ { 208, 231, 432, 480 }, /* MCS 13 */
+ { 234, 260, 486, 540 }, /* MCS 14 */
+ { 260, 289, 540, 600 } /* MCS 15 */
};
static const struct ieee80211_htrateset ieee80211_rateset_11n =
{ 16, {
- /* MCS: 6.5 13 19.5 26 39 52 58.5 65 13 26 */
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
- /* 39 52 78 104 117, 130 */
10, 11, 12, 13, 14, 15 }
};
@@ -85,11 +83,26 @@ static const struct ieee80211_htrateset ieee80211_rateset_11n =
int ieee80211_ampdu_age = -1; /* threshold for ampdu reorder q (ms) */
#endif
int ieee80211_recv_bar_ena = 1;
+int ieee80211_addba_timeout = -1; /* timeout waiting for ADDBA response */
+int ieee80211_addba_backoff = -1; /* backoff after max ADDBA requests */
+int ieee80211_addba_maxtries = 3; /* max ADDBA requests before backoff */
-#define IEEE80211_AGGR_TIMEOUT msecs_to_ticks(250)
-#define IEEE80211_AGGR_MINRETRY msecs_to_ticks(10*1000)
-#define IEEE80211_AGGR_MAXTRIES 3
+/*
+ * Setup HT parameters that depends on the clock frequency.
+ */
+static void
+ieee80211_ht_setup(void)
+{
+#ifdef IEEE80211_AMPDU_AGE
+ ieee80211_ampdu_age = msecs_to_ticks(500);
+#endif
+ ieee80211_addba_timeout = msecs_to_ticks(250);
+ ieee80211_addba_backoff = msecs_to_ticks(10*1000);
+}
+SYSINIT(wlan_ht, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_ht_setup, NULL);
+static int ieee80211_ampdu_enable(struct ieee80211_node *ni,
+ struct ieee80211_tx_ampdu *tap);
static int ieee80211_addba_request(struct ieee80211_node *ni,
struct ieee80211_tx_ampdu *tap,
int dialogtoken, int baparamset, int batimeout);
@@ -104,56 +117,70 @@ static void ieee80211_aggr_recv_action(struct ieee80211_node *ni,
void
ieee80211_ht_attach(struct ieee80211com *ic)
{
-#ifdef IEEE80211_AMPDU_AGE
- if (ieee80211_ampdu_age == -1)
- ieee80211_ampdu_age = msecs_to_ticks(500);
-#endif
-
/* setup default aggregation policy */
ic->ic_recv_action = ieee80211_aggr_recv_action;
ic->ic_send_action = ieee80211_send_action;
+ ic->ic_ampdu_enable = ieee80211_ampdu_enable;
ic->ic_addba_request = ieee80211_addba_request;
ic->ic_addba_response = ieee80211_addba_response;
ic->ic_addba_stop = ieee80211_addba_stop;
ic->ic_htprotmode = IEEE80211_PROT_RTSCTS;
ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE;
+}
+
+void
+ieee80211_ht_detach(struct ieee80211com *ic)
+{
+}
- /* XXX get from driver */
- ic->ic_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K;
- ic->ic_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA;
- ic->ic_ampdu_limit = ic->ic_ampdu_rxmax;
- ic->ic_amsdu_limit = IEEE80211_HTCAP_MAXAMSDU_3839;
+void
+ieee80211_ht_vattach(struct ieee80211vap *vap)
+{
- if (ic->ic_htcaps & IEEE80211_HTC_HT) {
+ /* driver can override defaults */
+ vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K;
+ vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA;
+ vap->iv_ampdu_limit = vap->iv_ampdu_rxmax;
+ vap->iv_amsdu_limit = vap->iv_htcaps & IEEE80211_HTCAP_MAXAMSDU;
+ /* tx aggregation traffic thresholds */
+ vap->iv_ampdu_mintraffic[WME_AC_BK] = 128;
+ vap->iv_ampdu_mintraffic[WME_AC_BE] = 64;
+ vap->iv_ampdu_mintraffic[WME_AC_VO] = 32;
+ vap->iv_ampdu_mintraffic[WME_AC_VI] = 32;
+
+ if (vap->iv_htcaps & IEEE80211_HTC_HT) {
/*
* Device is HT capable; enable all HT-related
* facilities by default.
* XXX these choices may be too aggressive.
*/
- ic->ic_flags_ext |= IEEE80211_FEXT_HT
- | IEEE80211_FEXT_HTCOMPAT
- ;
- if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20)
- ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
+ vap->iv_flags_ext |= IEEE80211_FEXT_HT
+ | IEEE80211_FEXT_HTCOMPAT
+ ;
+ if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI20)
+ vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI20;
/* XXX infer from channel list? */
- if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
- ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40;
- if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)
- ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
+ if (vap->iv_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
+ vap->iv_flags_ext |= IEEE80211_FEXT_USEHT40;
+ if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI40)
+ vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI40;
}
/* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */
- ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
- if (ic->ic_htcaps & IEEE80211_HTC_AMPDU)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
- ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
- if (ic->ic_htcaps & IEEE80211_HTC_AMSDU)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
+ if (vap->iv_htcaps & IEEE80211_HTC_AMPDU)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
+ if (vap->iv_htcaps & IEEE80211_HTC_AMSDU)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
}
+ /* NB: disable default legacy WDS, too many issues right now */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY)
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_HT;
}
void
-ieee80211_ht_detach(struct ieee80211com *ic)
+ieee80211_ht_vdetach(struct ieee80211vap *vap)
{
}
@@ -170,7 +197,7 @@ ht_announce(struct ieee80211com *ic, int mode,
rs->rs_rates[i] | IEEE80211_RATE_MCS, mode);
if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS)
continue;
- rate = ieee80211_htrates[rs->rs_rates[i]];
+ rate = ieee80211_htrates[rs->rs_rates[i]].ht40_rate_400ns;
printf("%s%d%sMbps", (i != 0 ? " " : ""),
rate / 2, ((rate & 0x1) != 0 ? ".5" : ""));
}
@@ -205,14 +232,14 @@ ieee80211_get_suphtrates(struct ieee80211com *ic,
struct mbuf *
ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
int framelen;
struct mbuf *n;
/* discard 802.3 header inserted by ieee80211_decap */
m_adj(m, sizeof(struct ether_header));
- ic->ic_stats.is_amsdu_decap++;
+ vap->iv_stats.is_amsdu_decap++;
for (;;) {
/*
@@ -223,23 +250,23 @@ ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m)
*/
m = ieee80211_decap1(m, &framelen);
if (m == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "a-msdu", "%s", "decap failed");
- ic->ic_stats.is_amsdu_tooshort++;
+ vap->iv_stats.is_amsdu_tooshort++;
return NULL;
}
if (m->m_pkthdr.len == framelen)
break;
n = m_split(m, framelen, M_NOWAIT);
if (n == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "a-msdu",
"%s", "unable to split encapsulated frames");
- ic->ic_stats.is_amsdu_split++;
+ vap->iv_stats.is_amsdu_split++;
m_freem(m); /* NB: must reclaim */
return NULL;
}
- ieee80211_deliver_data(ic, ni, m);
+ vap->iv_deliver_data(vap, ni, m);
/*
* Remove frame contents; each intermediate frame
@@ -252,19 +279,6 @@ ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m)
}
/*
- * Start A-MPDU rx/re-order processing for the specified TID.
- */
-static void
-ampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start)
-{
- memset(rap, 0, sizeof(*rap));
- rap->rxa_wnd = (bufsiz == 0) ?
- IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
- rap->rxa_start = start;
- rap->rxa_flags |= IEEE80211_AGGR_XCHGPEND;
-}
-
-/*
* Purge all frames in the A-MPDU re-order queue.
*/
static void
@@ -289,13 +303,33 @@ ampdu_rx_purge(struct ieee80211_rx_ampdu *rap)
}
/*
+ * Start A-MPDU rx/re-order processing for the specified TID.
+ */
+static void
+ampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start)
+{
+ if (rap->rxa_flags & IEEE80211_AGGR_RUNNING) {
+ /*
+ * AMPDU previously setup and not terminated with a DELBA,
+ * flush the reorder q's in case anything remains.
+ */
+ ampdu_rx_purge(rap);
+ }
+ memset(rap, 0, sizeof(*rap));
+ rap->rxa_wnd = (bufsiz == 0) ?
+ IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
+ rap->rxa_start = start;
+ rap->rxa_flags |= IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND;
+}
+
+/*
* Stop A-MPDU rx processing for the specified TID.
*/
static void
ampdu_rx_stop(struct ieee80211_rx_ampdu *rap)
{
- rap->rxa_flags &= ~IEEE80211_AGGR_XCHGPEND;
ampdu_rx_purge(rap);
+ rap->rxa_flags &= ~(IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND);
}
/*
@@ -309,7 +343,7 @@ ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m)
{
m->m_flags |= M_AMPDU; /* bypass normal processing */
/* NB: rssi, noise, and rstamp are ignored w/ M_AMPDU set */
- (void) ieee80211_input(ni->ni_ic, m, ni, 0, 0, 0);
+ (void) ieee80211_input(ni, m, 0, 0, 0);
}
/*
@@ -323,7 +357,7 @@ ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m)
static void
ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct mbuf *m;
int i;
@@ -353,14 +387,14 @@ ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni)
}
}
KASSERT(n == 0, ("lost %d frames", n));
- ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes;
+ vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes;
}
/*
* Adjust the start of the BA window to
* reflect the frames just dispatched.
*/
rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i);
- ic->ic_stats.is_ampdu_rx_oor += i;
+ vap->iv_stats.is_ampdu_rx_oor += i;
}
#ifdef IEEE80211_AMPDU_AGE
@@ -370,7 +404,7 @@ ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni)
static void
ampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct mbuf *m;
int i;
@@ -381,7 +415,7 @@ ampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
rap->rxa_m[i] = NULL;
rap->rxa_qbytes -= m->m_pkthdr.len;
rap->rxa_qframes--;
- ic->ic_stats.is_ampdu_rx_oor++;
+ vap->iv_stats.is_ampdu_rx_oor++;
ampdu_dispatch(ni, m);
if (rap->rxa_qframes == 0)
@@ -399,7 +433,7 @@ static void
ampdu_rx_flush_upto(struct ieee80211_node *ni,
struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct mbuf *m;
ieee80211_seq seqno;
int i;
@@ -418,7 +452,7 @@ ampdu_rx_flush_upto(struct ieee80211_node *ni,
rap->rxa_m[i] = NULL;
rap->rxa_qbytes -= m->m_pkthdr.len;
rap->rxa_qframes--;
- ic->ic_stats.is_ampdu_rx_oor++;
+ vap->iv_stats.is_ampdu_rx_oor++;
ampdu_dispatch(ni, m);
} else {
@@ -433,6 +467,10 @@ ampdu_rx_flush_upto(struct ieee80211_node *ni,
*/
if (rap->rxa_qframes != 0) {
int n = rap->rxa_qframes, j;
+
+ /* NB: this loop assumes i > 0 and/or rxa_m[0] is NULL */
+ KASSERT(rap->rxa_m[0] == NULL,
+ ("%s: BA window slot 0 occupied", __func__));
for (j = i+1; j < rap->rxa_wnd; j++) {
if (rap->rxa_m[j] != NULL) {
rap->rxa_m[j-i] = rap->rxa_m[j];
@@ -446,7 +484,7 @@ ampdu_rx_flush_upto(struct ieee80211_node *ni,
__func__, n, rap->rxa_qframes, i, rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
winstart));
- ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes;
+ vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes;
}
/*
* Move the start of the BA window; we use the
@@ -472,7 +510,7 @@ ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m)
(IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0)
#define PROCESS 0 /* caller should process frame */
#define CONSUMED 1 /* frame consumed, caller does nothing */
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_qosframe *wh;
struct ieee80211_rx_ampdu *rap;
ieee80211_seq rxseq;
@@ -557,7 +595,7 @@ again:
* frame; flush the reorder buffer.
*/
if (rap->rxa_qframes != 0) {
- ic->ic_stats.is_ampdu_rx_age +=
+ vap->iv_stats.is_ampdu_rx_age +=
rap->rxa_qframes;
ampdu_rx_flush(ni, rap);
}
@@ -576,15 +614,15 @@ again:
rap->rxa_m[off] = m;
rap->rxa_qframes++;
rap->rxa_qbytes += m->m_pkthdr.len;
- ic->ic_stats.is_ampdu_rx_reorder++;
+ vap->iv_stats.is_ampdu_rx_reorder++;
} else {
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
ni->ni_macaddr, "a-mpdu duplicate",
"seqno %u tid %u BA win <%u:%u>",
rxseq, tid, rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1));
- ic->ic_stats.is_rx_dup++;
+ vap->iv_stats.is_rx_dup++;
IEEE80211_NODE_STAT(ni, rx_dup);
m_freem(m);
}
@@ -596,12 +634,12 @@ again:
* flush the reorder q and move the window.
* Sec 9.10.7.6 b) (D2.04 p.118 line 60)
*/
- IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
"move BA win <%u:%u> (%u frames) rxseq %u tid %u",
rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
rap->rxa_qframes, rxseq, tid);
- ic->ic_stats.is_ampdu_rx_move++;
+ vap->iv_stats.is_ampdu_rx_move++;
/*
* The spec says to flush frames up to but not including:
@@ -620,14 +658,14 @@ again:
* Outside the BA window and out of range; toss.
* Sec 9.10.7.6 c) (D2.04 p.119 line 16)
*/
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
- "MSDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
+ "MPDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
rap->rxa_qframes, rxseq, tid,
wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
- ic->ic_stats.is_ampdu_rx_drop++;
+ vap->iv_stats.is_ampdu_rx_drop++;
IEEE80211_NODE_STAT(ni, rx_drop);
m_freem(m);
return CONSUMED;
@@ -645,7 +683,7 @@ again:
void
ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_frame_bar *wh;
struct ieee80211_rx_ampdu *rap;
ieee80211_seq rxseq;
@@ -653,10 +691,10 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
if (!ieee80211_recv_bar_ena) {
#if 0
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_11N,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_11N,
ni->ni_macaddr, "BAR", "%s", "processing disabled");
#endif
- ic->ic_stats.is_ampdu_bar_bad++;
+ vap->iv_stats.is_ampdu_bar_bad++;
return;
}
wh = mtod(m0, struct ieee80211_frame_bar *);
@@ -667,13 +705,13 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
/*
* No ADDBA request yet, don't touch.
*/
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
ni->ni_macaddr, "BAR", "no BA stream, tid %u", tid);
- ic->ic_stats.is_ampdu_bar_bad++;
+ vap->iv_stats.is_ampdu_bar_bad++;
return;
}
- ic->ic_stats.is_ampdu_bar_rx++;
+ vap->iv_stats.is_ampdu_bar_rx++;
rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
if (rxseq == rap->rxa_start)
return;
@@ -684,12 +722,12 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
* Flush the reorder q up to rxseq and move the window.
* Sec 9.10.7.6 a) (D2.04 p.119 line 22)
*/
- IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
"BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u",
rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
rap->rxa_qframes, rxseq, tid);
- ic->ic_stats.is_ampdu_bar_move++;
+ vap->iv_stats.is_ampdu_bar_move++;
ampdu_rx_flush_upto(ni, rap, rxseq);
if (off >= rap->rxa_wnd) {
@@ -705,14 +743,14 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
* Out of range; toss.
* Sec 9.10.7.6 b) (D2.04 p.119 line 41)
*/
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
"BAR", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
rap->rxa_qframes, rxseq, tid,
wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
- ic->ic_stats.is_ampdu_bar_oow++;
+ vap->iv_stats.is_ampdu_bar_oow++;
IEEE80211_NODE_STAT(ni, rx_drop);
}
}
@@ -767,6 +805,8 @@ ieee80211_ht_node_cleanup(struct ieee80211_node *ni)
*/
ic->ic_addba_stop(ni, &ni->ni_tx_ampdu[i]);
IEEE80211_TAPQ_DESTROY(tap);
+ tap->txa_lastsample = 0;
+ tap->txa_avgpps = 0;
/* NB: clearing NAK means we may re-send ADDBA */
tap->txa_flags &=
~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK);
@@ -780,6 +820,45 @@ ieee80211_ht_node_cleanup(struct ieee80211_node *ni)
IEEE80211_NODE_AMPDU);
}
+/*
+ * Age out HT resources for a station.
+ */
+void
+ieee80211_ht_node_age(struct ieee80211_node *ni)
+{
+#ifdef IEEE80211_AMPDU_AGE
+ struct ieee80211vap *vap = ni->ni_vap;
+ uint8_t tid;
+#endif
+
+ KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta"));
+
+#ifdef IEEE80211_AMPDU_AGE
+ for (tid = 0; tid < WME_NUM_TID; tid++) {
+ struct ieee80211_rx_ampdu *rap;
+
+ rap = &ni->ni_rx_ampdu[tid];
+ if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0)
+ continue;
+ if (rap->rxa_qframes == 0)
+ continue;
+ /*
+ * Check for frames sitting too long in the reorder queue.
+ * See above for more details on what's happening here.
+ */
+ /* XXX honor batimeout? */
+ if (ticks - rap->rxa_age > ieee80211_ampdu_age) {
+ /*
+ * Too long since we received the first
+ * frame; flush the reorder buffer.
+ */
+ vap->iv_stats.is_ampdu_rx_age += rap->rxa_qframes;
+ ampdu_rx_flush(ni, rap);
+ }
+ }
+#endif /* IEEE80211_AMPDU_AGE */
+}
+
static struct ieee80211_channel *
findhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int htflags)
{
@@ -832,11 +911,11 @@ ieee80211_ht_adjust_channel(struct ieee80211com *ic,
void
ieee80211_ht_wds_init(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_tx_ampdu *tap;
int ac;
- KASSERT(ic->ic_flags_ext & IEEE80211_FEXT_HT, ("no HT requested"));
+ KASSERT(vap->iv_flags_ext & IEEE80211_FEXT_HT, ("no HT requested"));
/* XXX check scan cache in case peer has an ap and we have info */
/*
@@ -845,11 +924,11 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni)
* AP) is suitable use it so we use the same location
* for the extension channel).
*/
- ni->ni_chan = ieee80211_ht_adjust_channel(ic, ni->ni_chan,
- ic->ic_flags_ext);
+ ni->ni_chan = ieee80211_ht_adjust_channel(ni->ni_ic,
+ ni->ni_chan, ieee80211_htchanflags(ni->ni_chan));
ni->ni_htcap = 0;
- if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI20)
ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI20;
if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
ni->ni_htcap |= IEEE80211_HTCAP_CHWIDTH40;
@@ -858,7 +937,7 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni)
ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE;
else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW;
- if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI40)
ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI40;
} else {
ni->ni_chw = 20;
@@ -883,20 +962,30 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni)
static void
htinfo_notify(struct ieee80211com *ic)
{
- if (ic->ic_opmode != IEEE80211_M_HOSTAP)
- return;
- IEEE80211_NOTE(ic,
- IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
- ic->ic_bss,
- "HT bss occupancy change: %d sta, %d ht, "
- "%d ht40%s, HT protmode now 0x%x"
- , ic->ic_sta_assoc
- , ic->ic_ht_sta_assoc
- , ic->ic_ht40_sta_assoc
- , (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) ?
- ", non-HT sta present" : ""
- , ic->ic_curhtprotmode);
- ieee80211_beacon_notify(ic, IEEE80211_BEACON_HTINFO);
+ struct ieee80211vap *vap;
+ int first = 1;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP)
+ continue;
+ if (first) {
+ IEEE80211_NOTE(vap,
+ IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
+ vap->iv_bss,
+ "HT bss occupancy change: %d sta, %d ht, "
+ "%d ht40%s, HT protmode now 0x%x"
+ , ic->ic_sta_assoc
+ , ic->ic_ht_sta_assoc
+ , ic->ic_ht40_sta_assoc
+ , (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) ?
+ ", non-HT sta present" : ""
+ , ic->ic_curhtprotmode);
+ first = 0;
+ }
+ ieee80211_beacon_notify(vap, IEEE80211_BEACON_HTINFO);
+ }
}
/*
@@ -908,13 +997,14 @@ htinfo_update(struct ieee80211com *ic)
{
uint8_t protmode;
- if (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) {
- protmode = IEEE80211_HTINFO_OPMODE_PROTOPT
- | IEEE80211_HTINFO_NONHT_PRESENT;
- } else if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) {
+ if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) {
protmode = IEEE80211_HTINFO_OPMODE_MIXED
- | IEEE80211_HTINFO_NONHT_PRESENT;
- } else if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) &&
+ | IEEE80211_HTINFO_NONHT_PRESENT;
+ } else if (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) {
+ protmode = IEEE80211_HTINFO_OPMODE_PROTOPT
+ | IEEE80211_HTINFO_NONHT_PRESENT;
+ } else if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+ IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) &&
ic->ic_sta_assoc != ic->ic_ht40_sta_assoc) {
protmode = IEEE80211_HTINFO_OPMODE_HT20PR;
} else {
@@ -964,15 +1054,36 @@ ieee80211_ht_node_leave(struct ieee80211_node *ni)
/*
* Public version of htinfo_update; used for processing
- * beacon frames from overlapping bss in hostap_recv_mgmt.
+ * beacon frames from overlapping bss.
+ *
+ * Caller can specify either IEEE80211_HTINFO_OPMODE_MIXED
+ * (on receipt of a beacon that advertises MIXED) or
+ * IEEE80211_HTINFO_OPMODE_PROTOPT (on receipt of a beacon
+ * from an overlapping legacy bss). We treat MIXED with
+ * a higher precedence than PROTOPT (i.e. we will not change
+ * change PROTOPT -> MIXED; only MIXED -> PROTOPT). This
+ * corresponds to how we handle things in htinfo_update.
*/
void
-ieee80211_htinfo_update(struct ieee80211com *ic, int protmode)
+ieee80211_htprot_update(struct ieee80211com *ic, int protmode)
{
- if (protmode != ic->ic_curhtprotmode) {
- ic->ic_curhtprotmode = protmode;
- htinfo_notify(ic);
- }
+#define OPMODE(x) SM(x, IEEE80211_HTINFO_OPMODE)
+ if (protmode == ic->ic_curhtprotmode)
+ return;
+ if (OPMODE(ic->ic_curhtprotmode) == IEEE80211_HTINFO_OPMODE_MIXED &&
+ OPMODE(protmode) == IEEE80211_HTINFO_OPMODE_PROTOPT)
+ return;
+
+ /* track non-HT station presence */
+ KASSERT(protmode & IEEE80211_HTINFO_NONHT_PRESENT,
+ ("missing NONHT_PRESENT"));
+ ic->ic_flags_ext |= IEEE80211_FEXT_NONHT_PR;
+ ic->ic_lastnonht = ticks;
+
+ /* push beacon update */
+ ic->ic_curhtprotmode = protmode;
+ htinfo_notify(ic);
+#undef OPMODE
}
/*
@@ -991,7 +1102,7 @@ ieee80211_ht_timeout(struct ieee80211com *ic)
if ((ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) &&
time_after(ticks, ic->ic_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) {
#if 0
- IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
"%s", "time out non-HT STA present on channel");
#endif
ic->ic_flags_ext &= ~IEEE80211_FEXT_NONHT_PR;
@@ -1011,7 +1122,7 @@ ieee80211_ht_timeout(struct ieee80211com *ic)
void
ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
if (ie[0] == IEEE80211_ELEMID_VENDOR) {
/*
@@ -1029,7 +1140,7 @@ ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie)
ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)];
/* XXX needed or will ieee80211_parse_htinfo always be called? */
ni->ni_chw = (ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40) ? 40 : 20;
+ (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40) ? 40 : 20;
}
/*
@@ -1044,6 +1155,7 @@ void
ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
{
struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_ie_htinfo *htinfo;
struct ieee80211_channel *c;
uint16_t w;
@@ -1063,11 +1175,11 @@ ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
* identify the right channel to use. If we cannot locate it
* in the channel table then fallback to legacy operation.
*/
- htflags = (ic->ic_flags_ext & IEEE80211_FEXT_HT) ?
- IEEE80211_CHAN_HT20 : 0;
/* NB: honor operating mode constraint */
+ htflags = (vap->iv_flags_ext & IEEE80211_FEXT_HT) ?
+ IEEE80211_CHAN_HT20 : 0;
if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)) {
+ (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40)) {
if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE)
htflags = IEEE80211_CHAN_HT40U;
else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW)
@@ -1076,20 +1188,20 @@ ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
chanflags = (ni->ni_chan->ic_flags &~ IEEE80211_CHAN_HT) | htflags;
if (chanflags != ni->ni_chan->ic_flags) {
c = ieee80211_find_channel(ic, ni->ni_chan->ic_freq, chanflags);
- if (c == NULL && htflags != IEEE80211_CHAN_HT20) {
+ if (c == NULL && (htflags & IEEE80211_CHAN_HT40)) {
/*
* No HT40 channel entry in our table; fall back
* to HT20 operation. This should not happen.
*/
c = findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT20);
- IEEE80211_NOTE(ni->ni_ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
"no HT40 channel (freq %u), falling back to HT20",
ni->ni_chan->ic_freq);
/* XXX stat */
}
if (c != NULL && c != ni->ni_chan) {
- IEEE80211_NOTE(ni->ni_ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
"switch station to HT%d channel %u/0x%x",
IEEE80211_IS_CHAN_HT40(c) ? 40 : 20,
@@ -1108,7 +1220,7 @@ ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
int
ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_ie_htcap *htcap;
struct ieee80211_htrateset *rs;
int i;
@@ -1123,11 +1235,11 @@ ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags)
if (isclr(htcap->hc_mcsset, i))
continue;
if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) {
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
"WARNING, HT rate set too large; only "
"using %u rates", IEEE80211_HTRATE_MAXSIZE);
- ic->ic_stats.is_rx_rstoobig++;
+ vap->iv_stats.is_rx_rstoobig++;
break;
}
rs->rs_rates[rs->rs_nrates++] = i;
@@ -1152,7 +1264,7 @@ ieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie)
htinfo = (const struct ieee80211_ie_htinfo *) ie;
rs = &ni->ni_htrates;
if (rs->rs_nrates == 0) {
- IEEE80211_NOTE(ni->ni_ic,
+ IEEE80211_NOTE(ni->ni_vap,
IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
"%s", "WARNING, empty HT rate set");
return;
@@ -1180,10 +1292,10 @@ static void
addba_start_timeout(struct ieee80211_tx_ampdu *tap)
{
/* XXX use CALLOUT_PENDING instead? */
- callout_reset(&tap->txa_timer, IEEE80211_AGGR_TIMEOUT,
+ callout_reset(&tap->txa_timer, ieee80211_addba_timeout,
addba_timeout, tap);
tap->txa_flags |= IEEE80211_AGGR_XCHGPEND;
- tap->txa_lastrequest = ticks;
+ tap->txa_nextrequest = ticks + ieee80211_addba_timeout;
}
static void
@@ -1274,6 +1386,7 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni,
const uint8_t *frm, const uint8_t *efrm)
{
struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_action *ia;
struct ieee80211_rx_ampdu *rap;
struct ieee80211_tx_ampdu *tap;
@@ -1295,7 +1408,7 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni,
tid = MS(baparamset, IEEE80211_BAPS_TID);
bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"recv ADDBA request: dialogtoken %u "
"baparamset 0x%x (tid %d bufsiz %d) batimeout %d "
@@ -1314,19 +1427,19 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni,
* violates the 11n spec and is mostly for testing).
*/
if ((ni->ni_flags & IEEE80211_NODE_AMPDU_RX) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)) {
+ (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_RX)) {
ampdu_rx_start(rap, bufsiz,
MS(baseqctl, IEEE80211_BASEQ_START));
args[1] = IEEE80211_STATUS_SUCCESS;
} else {
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni, "reject ADDBA request: %s",
ni->ni_flags & IEEE80211_NODE_AMPDU_RX ?
"administratively disabled" :
"not negotiated for station");
- ic->ic_stats.is_addba_reject++;
+ vap->iv_stats.is_addba_reject++;
args[1] = IEEE80211_STATUS_UNSPECIFIED;
}
/* XXX honor rap flags? */
@@ -1350,26 +1463,26 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni,
ac = TID_TO_WME_AC(tid);
tap = &ni->ni_tx_ampdu[ac];
if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni->ni_macaddr, "ADDBA response",
"no pending ADDBA, tid %d dialogtoken %u "
"code %d", tid, dialogtoken, code);
- ic->ic_stats.is_addba_norequest++;
+ vap->iv_stats.is_addba_norequest++;
return;
}
if (dialogtoken != tap->txa_token) {
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni->ni_macaddr, "ADDBA response",
"dialogtoken mismatch: waiting for %d, "
"received %d, tid %d code %d",
tap->txa_token, dialogtoken, tid, code);
- ic->ic_stats.is_addba_badtoken++;
+ vap->iv_stats.is_addba_badtoken++;
return;
}
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"recv ADDBA response: dialogtoken %u code %d "
"baparamset 0x%x (tid %d bufsiz %d) batimeout %d",
@@ -1385,7 +1498,7 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni,
tid = MS(baparamset, IEEE80211_DELBAPS_TID);
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"recv DELBA: baparamset 0x%x (tid %d initiator %d) "
"code %d", baparamset, tid,
@@ -1416,18 +1529,18 @@ void
ieee80211_recv_action(struct ieee80211_node *ni,
const uint8_t *frm, const uint8_t *efrm)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_action *ia;
int chw;
ia = (const struct ieee80211_action *) frm;
switch (ia->ia_category) {
case IEEE80211_ACTION_CAT_BA:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: BA action %d not implemented", __func__,
ia->ia_action);
- ic->ic_stats.is_rx_mgtdiscard++;
+ vap->iv_stats.is_rx_mgtdiscard++;
break;
case IEEE80211_ACTION_CAT_HT:
switch (ia->ia_action) {
@@ -1437,7 +1550,7 @@ ieee80211_recv_action(struct ieee80211_node *ni,
ni->ni_chw = chw;
ni->ni_flags |= IEEE80211_NODE_CHWUPDATE;
}
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: HT txchwidth, width %d (%s)",
__func__, chw,
@@ -1445,25 +1558,25 @@ ieee80211_recv_action(struct ieee80211_node *ni,
"new" : "no change");
break;
case IEEE80211_ACTION_HT_MIMOPWRSAVE:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: HT MIMO PS", __func__);
break;
default:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: HT action %d not implemented", __func__,
ia->ia_action);
- ic->ic_stats.is_rx_mgtdiscard++;
+ vap->iv_stats.is_rx_mgtdiscard++;
break;
}
break;
default:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: category %d not implemented", __func__,
ia->ia_category);
- ic->ic_stats.is_rx_mgtdiscard++;
+ vap->iv_stats.is_rx_mgtdiscard++;
break;
}
}
@@ -1473,6 +1586,39 @@ ieee80211_recv_action(struct ieee80211_node *ni,
*/
/*
+ * Check if A-MPDU should be requested/enabled for a stream.
+ * We require a traffic rate above a per-AC threshold and we
+ * also handle backoff from previous failed attempts.
+ *
+ * Drivers may override this method to bring in information
+ * such as link state conditions in making the decision.
+ */
+static int
+ieee80211_ampdu_enable(struct ieee80211_node *ni,
+ struct ieee80211_tx_ampdu *tap)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (tap->txa_avgpps < vap->iv_ampdu_mintraffic[tap->txa_ac])
+ return 0;
+ /* XXX check rssi? */
+ if (tap->txa_attempts >= ieee80211_addba_maxtries &&
+ ticks < tap->txa_nextrequest) {
+ /*
+ * Don't retry too often; txa_nextrequest is set
+ * to the minimum interval we'll retry after
+ * ieee80211_addba_maxtries failed attempts are made.
+ */
+ return 0;
+ }
+ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
+ "%s: enable AMPDU on %s, avgpps %d pkts %d",
+ __func__, ieee80211_wme_acnames[tap->txa_ac],
+ tap->txa_avgpps, tap->txa_pkts);
+ return 1;
+}
+
+/*
* Request A-MPDU tx aggregation. Setup local state and
* issue an ADDBA request. BA use will only happen after
* the other end replies with ADDBA response.
@@ -1493,16 +1639,6 @@ ieee80211_ampdu_request(struct ieee80211_node *ni,
callout_init(&tap->txa_timer, CALLOUT_MPSAFE);
tap->txa_flags |= IEEE80211_AGGR_SETUP;
}
- if (tap->txa_attempts >= IEEE80211_AGGR_MAXTRIES &&
- (ticks - tap->txa_lastrequest) < IEEE80211_AGGR_MINRETRY) {
- /*
- * Don't retry too often; IEEE80211_AGGR_MINRETRY
- * defines the minimum interval we'll retry after
- * IEEE80211_AGGR_MAXTRIES failed attempts to
- * negotiate use.
- */
- return 0;
- }
/* XXX hack for not doing proper locking */
tap->txa_flags &= ~IEEE80211_AGGR_NAK;
@@ -1521,12 +1657,14 @@ ieee80211_ampdu_request(struct ieee80211_node *ni,
/* NB: do first so there's no race against reply */
if (!ic->ic_addba_request(ni, tap, dialogtoken, args[1], args[2])) {
/* unable to setup state, don't make request */
- IEEE80211_NOTE(ni->ni_ic, IEEE80211_MSG_11N,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
ni, "%s: could not setup BA stream for AC %d",
__func__, tap->txa_ac);
/* defer next try so we don't slam the driver with requests */
- tap->txa_attempts = IEEE80211_AGGR_MAXTRIES;
- tap->txa_lastrequest = ticks;
+ tap->txa_attempts = ieee80211_addba_maxtries;
+ /* NB: check in case driver wants to override */
+ if (tap->txa_nextrequest <= ticks)
+ tap->txa_nextrequest = ticks + ieee80211_addba_backoff;
return 0;
}
tokens = dialogtoken; /* allocate token */
@@ -1542,13 +1680,14 @@ void
ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
{
struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
uint16_t args[4];
/* XXX locking */
if (IEEE80211_AMPDU_RUNNING(tap)) {
- IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni, "%s: stop BA stream for AC %d", __func__, tap->txa_ac);
- ic->ic_stats.is_ampdu_stop++;
+ vap->iv_stats.is_ampdu_stop++;
ic->ic_addba_stop(ni, tap);
args[0] = WME_AC_TO_TID(tap->txa_ac);
@@ -1557,10 +1696,10 @@ ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
ieee80211_send_action(ni, IEEE80211_ACTION_CAT_BA,
IEEE80211_ACTION_BA_DELBA, args);
} else {
- IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni, "%s: BA stream for AC %d not running",
__func__, tap->txa_ac);
- ic->ic_stats.is_ampdu_stop_failed++;
+ vap->iv_stats.is_ampdu_stop_failed++;
}
}
@@ -1573,14 +1712,14 @@ int
ieee80211_send_bar(struct ieee80211_node *ni,
const struct ieee80211_tx_ampdu *tap)
{
-#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
+#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0)
#define ADDSHORT(frm, v) do { \
frm[0] = (v) & 0xff; \
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
struct ieee80211_frame_min *wh;
struct mbuf *m;
uint8_t *frm;
@@ -1601,7 +1740,7 @@ ieee80211_send_bar(struct ieee80211_node *ni,
IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR;
wh->i_fc[1] = 0;
IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
- IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
tid = WME_AC_TO_TID(tap->txa_ac);
barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ?
@@ -1617,17 +1756,15 @@ ieee80211_send_bar(struct ieee80211_node *ni,
ADDSHORT(frm, barseqctl);
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+ M_WME_SETAC(m, WME_AC_VO);
+
IEEE80211_NODE_STAT(ni, tx_mgmt); /* XXX tx_ctl? */
- IEEE80211_NOTE(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
ni, "send bar frame (tid %u start %u) on channel %u",
tid, tap->txa_start, ieee80211_chan2ieee(ic, ic->ic_curchan));
- m->m_pkthdr.rcvif = (void *)ni;
- IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */
- if_start(ifp);
-
- return 0;
+ return ic->ic_raw_xmit(ni, m, NULL);
bad:
ieee80211_free_node(ni);
return ret;
@@ -1644,12 +1781,13 @@ int
ieee80211_send_action(struct ieee80211_node *ni,
int category, int action, uint16_t args[4])
{
-#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
+#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0)
#define ADDSHORT(frm, v) do { \
frm[0] = (v) & 0xff; \
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct mbuf *m;
uint8_t *frm;
@@ -1663,7 +1801,7 @@ ieee80211_send_action(struct ieee80211_node *ni,
* the xmit is complete all the way in the driver. On error we
* will remove our reference.
*/
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
__func__, __LINE__,
ni, ether_sprintf(ni->ni_macaddr),
@@ -1685,7 +1823,7 @@ ieee80211_send_action(struct ieee80211_node *ni,
case IEEE80211_ACTION_CAT_BA:
switch (action) {
case IEEE80211_ACTION_BA_ADDBA_REQUEST:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"send ADDBA request: dialogtoken %d "
"baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x",
@@ -1698,7 +1836,7 @@ ieee80211_send_action(struct ieee80211_node *ni,
ADDSHORT(frm, args[3]); /* baseqctl */
break;
case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"send ADDBA response: dialogtoken %d status %d "
"baparamset 0x%x (tid %d) batimeout %d",
@@ -1718,7 +1856,7 @@ ieee80211_send_action(struct ieee80211_node *ni,
ADDSHORT(frm, baparamset);
ADDSHORT(frm, args[2]); /* reason code */
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"send DELBA action: tid %d, initiator %d reason %d",
args[0], args[1], args[2]);
@@ -1730,12 +1868,12 @@ ieee80211_send_action(struct ieee80211_node *ni,
case IEEE80211_ACTION_CAT_HT:
switch (action) {
case IEEE80211_ACTION_HT_TXCHWIDTH:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni, "send HT txchwidth: width %d",
- IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? 40 : 20
+ IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? 40 : 20
);
- *frm++ = IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ?
+ *frm++ = IEEE80211_IS_CHAN_HT40(ni->ni_chan) ?
IEEE80211_A_HT_TXCHWIDTH_2040 :
IEEE80211_A_HT_TXCHWIDTH_20;
break;
@@ -1745,7 +1883,7 @@ ieee80211_send_action(struct ieee80211_node *ni,
break;
default:
badaction:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: unsupported category %d action %d", __func__,
category, action);
@@ -1754,12 +1892,11 @@ ieee80211_send_action(struct ieee80211_node *ni,
}
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
- ret = ieee80211_mgmt_output(ic, ni, m, IEEE80211_FC0_SUBTYPE_ACTION);
- if (ret != 0)
- goto bad;
- return 0;
+ return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION);
bad:
ieee80211_free_node(ni);
+ if (m != NULL)
+ m_freem(m);
return ret;
#undef ADDSHORT
#undef senderr
@@ -1794,12 +1931,12 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
uint16_t caps;
int rxmax, density;
/* HT capabilities */
- caps = ic->ic_htcaps & 0xffff;
+ caps = vap->iv_htcaps & 0xffff;
/*
* Note channel width depends on whether we are operating as
* a sta or not. When operating as a sta we are generating
@@ -1808,9 +1945,9 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
* how we've been setup (which might be different if a fixed
* channel is specified).
*/
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
/* override 20/40 use based on config */
- if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40)
caps |= IEEE80211_HTCAP_CHWIDTH40;
else
caps &= ~IEEE80211_HTCAP_CHWIDTH40;
@@ -1819,17 +1956,17 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
density = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY);
} else {
/* override 20/40 use based on current channel */
- if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan))
+ if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
caps |= IEEE80211_HTCAP_CHWIDTH40;
else
caps &= ~IEEE80211_HTCAP_CHWIDTH40;
- rxmax = ic->ic_ampdu_rxmax;
- density = ic->ic_ampdu_density;
+ rxmax = vap->iv_ampdu_rxmax;
+ density = vap->iv_ampdu_density;
}
/* adjust short GI based on channel and config */
- if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0)
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0)
caps &= ~IEEE80211_HTCAP_SHORTGI20;
- if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 ||
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 ||
(caps & IEEE80211_HTCAP_CHWIDTH40) == 0)
caps &= ~IEEE80211_HTCAP_SHORTGI40;
ADDSHORT(frm, caps);
@@ -1909,23 +2046,25 @@ ieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
* Update the HTINFO ie for a beacon frame.
*/
void
-ieee80211_ht_update_beacon(struct ieee80211com *ic,
+ieee80211_ht_update_beacon(struct ieee80211vap *vap,
struct ieee80211_beacon_offsets *bo)
{
#define PROTMODE (IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT)
+ const struct ieee80211_channel *bsschan = vap->iv_bss->ni_chan;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_ie_htinfo *ht =
(struct ieee80211_ie_htinfo *) bo->bo_htinfo;
/* XXX only update on channel change */
- ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, ic->ic_bsschan);
+ ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, bsschan);
ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH;
- if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan))
+ if (IEEE80211_IS_CHAN_HT40U(bsschan))
ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
- else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan))
+ else if (IEEE80211_IS_CHAN_HT40D(bsschan))
ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW;
else
ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE;
- if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan))
+ if (IEEE80211_IS_CHAN_HT40(bsschan))
ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040;
/* protection mode */
@@ -1951,16 +2090,16 @@ ieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni)
memset(frm, 0, sizeof(struct ieee80211_ie_htinfo) - 2);
/* primary/control channel center */
- *frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan);
+ *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH;
- if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan))
+ if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan))
frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
- else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan))
+ else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW;
else
frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE;
- if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan))
+ if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040;
frm[1] = ic->ic_curhtprotmode;
diff --git a/sys/net80211/ieee80211_ht.h b/sys/net80211/ieee80211_ht.h
index d1d6699..80052cf 100644
--- a/sys/net80211/ieee80211_ht.h
+++ b/sys/net80211/ieee80211_ht.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -46,13 +46,16 @@ struct ieee80211_tx_ampdu {
#define IEEE80211_AGGR_NAK 0x0010 /* peer NAK'd ADDBA request */
uint8_t txa_ac;
uint8_t txa_token; /* dialog token */
+ int txa_lastsample; /* ticks @ last traffic sample */
+ int txa_pkts; /* packets over last sample interval */
+ int txa_avgpps; /* filtered traffic over window */
int txa_qbytes; /* data queued (bytes) */
short txa_qframes; /* data queued (frames) */
ieee80211_seq txa_seqstart;
ieee80211_seq txa_start;
uint16_t txa_wnd; /* BA window size */
- uint8_t txa_attempts; /* # setup attempts */
- int txa_lastrequest;/* time of last ADDBA request */
+ uint8_t txa_attempts; /* # ADDBA requests w/o a response */
+ int txa_nextrequest;/* soonest to make next ADDBA request */
struct ifqueue txa_q; /* packet queue */
struct callout txa_timer;
void *txa_private; /* driver-private storage */
@@ -67,6 +70,65 @@ struct ieee80211_tx_ampdu {
(((tap)->txa_flags & \
(IEEE80211_AGGR_RUNNING|IEEE80211_AGGR_XCHGPEND|IEEE80211_AGGR_NAK)) != 0)
+/*
+ * Traffic estimator support. We estimate packets/sec for
+ * each AC that is setup for AMPDU or will potentially be
+ * setup for AMPDU. The traffic rate can be used to decide
+ * when AMPDU should be setup (according to a threshold)
+ * and is available for drivers to do things like cache
+ * eviction when only a limited number of BA streams are
+ * available and more streams are requested than available.
+ */
+
+static void __inline
+ieee80211_txampdu_update_pps(struct ieee80211_tx_ampdu *tap)
+{
+ /* NB: scale factor of 2 was picked heuristically */
+ tap->txa_avgpps = ((tap->txa_avgpps << 2) -
+ tap->txa_avgpps + tap->txa_pkts) >> 2;
+}
+
+/*
+ * Count a packet towards the pps estimate.
+ */
+static void __inline
+ieee80211_txampdu_count_packet(struct ieee80211_tx_ampdu *tap)
+{
+ /* XXX bound loop/do more crude estimate? */
+ while (ticks - tap->txa_lastsample >= hz) {
+ ieee80211_txampdu_update_pps(tap);
+ /* reset to start new sample interval */
+ tap->txa_pkts = 0;
+ if (tap->txa_avgpps == 0) {
+ tap->txa_lastsample = ticks;
+ break;
+ }
+ tap->txa_lastsample += hz;
+ }
+ tap->txa_pkts++;
+}
+
+/*
+ * Get the current pps estimate. If the average is out of
+ * date due to lack of traffic then we decay the estimate
+ * to account for the idle time.
+ */
+static int __inline
+ieee80211_txampdu_getpps(struct ieee80211_tx_ampdu *tap)
+{
+ /* XXX bound loop/do more crude estimate? */
+ while (ticks - tap->txa_lastsample >= hz) {
+ ieee80211_txampdu_update_pps(tap);
+ tap->txa_pkts = 0;
+ if (tap->txa_avgpps == 0) {
+ tap->txa_lastsample = ticks;
+ break;
+ }
+ tap->txa_lastsample += hz;
+ }
+ return tap->txa_avgpps;
+}
+
struct ieee80211_rx_ampdu {
int rxa_flags;
int rxa_qbytes; /* data queued (bytes) */
@@ -81,10 +143,18 @@ struct ieee80211_rx_ampdu {
void ieee80211_ht_attach(struct ieee80211com *);
void ieee80211_ht_detach(struct ieee80211com *);
+void ieee80211_ht_vattach(struct ieee80211vap *);
+void ieee80211_ht_vdetach(struct ieee80211vap *);
void ieee80211_ht_announce(struct ieee80211com *);
-extern const int ieee80211_htrates[16];
+struct ieee80211_mcs_rates {
+ uint16_t ht20_rate_800ns;
+ uint16_t ht20_rate_400ns;
+ uint16_t ht40_rate_800ns;
+ uint16_t ht40_rate_400ns;
+};
+extern const struct ieee80211_mcs_rates ieee80211_htrates[16];
const struct ieee80211_htrateset *ieee80211_get_suphtrates(
struct ieee80211com *, const struct ieee80211_channel *);
@@ -98,12 +168,14 @@ int ieee80211_ampdu_reorder(struct ieee80211_node *, struct mbuf *);
void ieee80211_recv_bar(struct ieee80211_node *, struct mbuf *);
void ieee80211_ht_node_init(struct ieee80211_node *, const uint8_t *);
void ieee80211_ht_node_cleanup(struct ieee80211_node *);
+void ieee80211_ht_node_age(struct ieee80211_node *);
+
struct ieee80211_channel *ieee80211_ht_adjust_channel(struct ieee80211com *,
struct ieee80211_channel *, int);
void ieee80211_ht_wds_init(struct ieee80211_node *);
void ieee80211_ht_node_join(struct ieee80211_node *);
void ieee80211_ht_node_leave(struct ieee80211_node *);
-void ieee80211_htinfo_update(struct ieee80211com *, int protmode);
+void ieee80211_htprot_update(struct ieee80211com *, int protmode);
void ieee80211_ht_timeout(struct ieee80211com *);
void ieee80211_parse_htcap(struct ieee80211_node *, const uint8_t *);
void ieee80211_parse_htinfo(struct ieee80211_node *, const uint8_t *);
@@ -122,6 +194,6 @@ uint8_t *ieee80211_add_htcap_vendor(uint8_t *, struct ieee80211_node *);
uint8_t *ieee80211_add_htinfo(uint8_t *, struct ieee80211_node *);
uint8_t *ieee80211_add_htinfo_vendor(uint8_t *, struct ieee80211_node *);
struct ieee80211_beacon_offsets;
-void ieee80211_ht_update_beacon(struct ieee80211com *,
+void ieee80211_ht_update_beacon(struct ieee80211vap *,
struct ieee80211_beacon_offsets *);
#endif /* _NET80211_IEEE80211_HT_H_ */
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c
index 9238bdf..9ae653f 100644
--- a/sys/net80211/ieee80211_input.c
+++ b/sys/net80211/ieee80211_input.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -27,6 +27,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -35,591 +37,75 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/socket.h>
-
-#include <net/if.h>
-#include <net/if_media.h>
+
#include <net/ethernet.h>
+#include <net/if.h>
#include <net/if_llc.h>
+#include <net/if_media.h>
#include <net/if_vlan_var.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_input.h>
#include <net/bpf.h>
-#ifdef IEEE80211_DEBUG
-#include <machine/stdarg.h>
-
-/*
- * Decide if a received management frame should be
- * printed when debugging is enabled. This filters some
- * of the less interesting frames that come frequently
- * (e.g. beacons).
- */
-static __inline int
-doprint(struct ieee80211com *ic, int subtype)
-{
- switch (subtype) {
- case IEEE80211_FC0_SUBTYPE_BEACON:
- return (ic->ic_flags & IEEE80211_F_SCAN);
- case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
- return (ic->ic_opmode == IEEE80211_M_IBSS);
- }
- return 1;
-}
-
-static const uint8_t *ieee80211_getbssid(struct ieee80211com *,
- const struct ieee80211_frame *);
-#endif /* IEEE80211_DEBUG */
-
-static struct mbuf *ieee80211_defrag(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *, int);
-static struct mbuf *ieee80211_decap(struct ieee80211com *, struct mbuf *, int);
-static void ieee80211_send_error(struct ieee80211com *, struct ieee80211_node *,
- const uint8_t *mac, int subtype, int arg);
-static struct mbuf *ieee80211_decap_fastframe(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *);
-static void ieee80211_recv_pspoll(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *);
+#ifdef INET
+#include <netinet/in.h>
+#include <net/ethernet.h>
+#endif
-/*
- * Process a received frame. The node associated with the sender
- * should be supplied. If nothing was found in the node table then
- * the caller is assumed to supply a reference to ic_bss instead.
- * The RSSI and a timestamp are also supplied. The RSSI data is used
- * during AP scanning to select a AP to associate with; it can have
- * any units so long as values have consistent units and higher values
- * mean ``better signal''. The receive timestamp is currently not used
- * by the 802.11 layer.
- */
int
-ieee80211_input(struct ieee80211com *ic, struct mbuf *m,
- struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp)
+ieee80211_input_all(struct ieee80211com *ic,
+ struct mbuf *m, int rssi, int noise, u_int32_t rstamp)
{
-#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
-#define HAS_SEQ(type) ((type & 0x4) == 0)
- struct ifnet *ifp = ic->ic_ifp;
- struct ieee80211_frame *wh;
- struct ieee80211_key *key;
- struct ether_header *eh;
- int hdrspace, need_tap;
- uint8_t dir, type, subtype, qos;
- uint8_t *bssid;
- uint16_t rxseq;
-
- if (m->m_flags & M_AMPDU) {
- /*
- * Fastpath for A-MPDU reorder q resubmission. Frames
- * w/ M_AMPDU marked have already passed through here
- * but were received out of order and been held on the
- * reorder queue. When resubmitted they are marked
- * with the M_AMPDU flag and we can bypass most of the
- * normal processing.
- */
- wh = mtod(m, struct ieee80211_frame *);
- type = IEEE80211_FC0_TYPE_DATA;
- dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
- subtype = IEEE80211_FC0_SUBTYPE_QOS;
- hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
- need_tap = 0;
- goto resubmit_ampdu;
- }
-
- KASSERT(ni != NULL, ("null node"));
- ni->ni_inact = ni->ni_inact_reload;
-
- need_tap = 1; /* mbuf need to be tapped. */
- type = -1; /* undefined */
- /*
- * In monitor mode, send everything directly to bpf.
- * XXX may want to include the CRC
- */
- if (ic->ic_opmode == IEEE80211_M_MONITOR)
- goto out;
-
- if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
- ni->ni_macaddr, NULL,
- "too short (1): len %u", m->m_pkthdr.len);
- ic->ic_stats.is_rx_tooshort++;
- goto out;
- }
- /*
- * Bit of a cheat here, we use a pointer for a 3-address
- * frame format but don't reference fields past outside
- * ieee80211_frame_min w/o first validating the data is
- * present.
- */
- wh = mtod(m, struct ieee80211_frame *);
-
- if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
- IEEE80211_FC0_VERSION_0) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
- ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
- ic->ic_stats.is_rx_badversion++;
- goto err;
- }
-
- dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
- type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
- subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
- if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- bssid = wh->i_addr2;
- if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
- /* not interested in */
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- bssid, NULL, "%s", "not to bss");
- ic->ic_stats.is_rx_wrongbss++;
- goto out;
- }
- break;
- case IEEE80211_M_IBSS:
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_HOSTAP:
- if (dir != IEEE80211_FC1_DIR_NODS)
- bssid = wh->i_addr1;
- else if (type == IEEE80211_FC0_TYPE_CTL)
- bssid = wh->i_addr1;
- else {
- if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
- IEEE80211_DISCARD_MAC(ic,
- IEEE80211_MSG_ANY, ni->ni_macaddr,
- NULL, "too short (2): len %u",
- m->m_pkthdr.len);
- ic->ic_stats.is_rx_tooshort++;
- goto out;
- }
- bssid = wh->i_addr3;
- }
- if (type != IEEE80211_FC0_TYPE_DATA)
- break;
- /*
- * Data frame, validate the bssid.
- */
- if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) &&
- !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
- /* not interested in */
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- bssid, NULL, "%s", "not to bss");
- ic->ic_stats.is_rx_wrongbss++;
- goto out;
- }
- /*
- * For adhoc mode we cons up a node when it doesn't
- * exist. This should probably done after an ACL check.
- */
- if (ni == ic->ic_bss &&
- ic->ic_opmode != IEEE80211_M_HOSTAP &&
- !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
- /*
- * Fake up a node for this newly
- * discovered member of the IBSS.
- */
- ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
- wh->i_addr2);
- if (ni == NULL) {
- /* NB: stat kept for alloc failure */
- goto err;
- }
- }
- break;
- default:
- goto out;
- }
- ni->ni_rssi = rssi;
- ni->ni_noise = noise;
- ni->ni_rstamp = rstamp;
- if (HAS_SEQ(type)) {
- uint8_t tid;
- if (IEEE80211_QOS_HAS_SEQ(wh)) {
- tid = ((struct ieee80211_qosframe *)wh)->
- i_qos[0] & IEEE80211_QOS_TID;
- if (TID_TO_WME_AC(tid) >= WME_AC_VI)
- ic->ic_wme.wme_hipri_traffic++;
- tid++;
- } else
- tid = IEEE80211_NONQOS_TID;
- rxseq = le16toh(*(uint16_t *)wh->i_seq);
- if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
- (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
- SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
- /* duplicate, discard */
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- bssid, "duplicate",
- "seqno <%u,%u> fragno <%u,%u> tid %u",
- rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
- ni->ni_rxseqs[tid] >>
- IEEE80211_SEQ_SEQ_SHIFT,
- rxseq & IEEE80211_SEQ_FRAG_MASK,
- ni->ni_rxseqs[tid] &
- IEEE80211_SEQ_FRAG_MASK,
- tid);
- ic->ic_stats.is_rx_dup++;
- IEEE80211_NODE_STAT(ni, rx_dup);
- goto out;
- }
- ni->ni_rxseqs[tid] = rxseq;
- }
- }
-
- switch (type) {
- case IEEE80211_FC0_TYPE_DATA:
- hdrspace = ieee80211_hdrspace(ic, wh);
- if (m->m_len < hdrspace &&
- (m = m_pullup(m, hdrspace)) == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
- ni->ni_macaddr, NULL,
- "data too short: expecting %u", hdrspace);
- ic->ic_stats.is_rx_tooshort++;
- goto out; /* XXX */
- }
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- if (dir != IEEE80211_FC1_DIR_FROMDS) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "unknown dir 0x%x", dir);
- ic->ic_stats.is_rx_wrongdir++;
- goto out;
- }
- if ((ifp->if_flags & IFF_SIMPLEX) &&
- IEEE80211_IS_MULTICAST(wh->i_addr1) &&
- IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) {
- /*
- * In IEEE802.11 network, multicast packet
- * sent from me is broadcasted from AP.
- * It should be silently discarded for
- * SIMPLEX interface.
- */
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, NULL, "%s", "multicast echo");
- ic->ic_stats.is_rx_mcastecho++;
- goto out;
- }
- break;
- case IEEE80211_M_IBSS:
- case IEEE80211_M_AHDEMO:
- if (dir != IEEE80211_FC1_DIR_NODS) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "unknown dir 0x%x", dir);
- ic->ic_stats.is_rx_wrongdir++;
- goto out;
- }
- /* XXX no power-save support */
- break;
- case IEEE80211_M_HOSTAP:
- if (dir != IEEE80211_FC1_DIR_TODS) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "unknown dir 0x%x", dir);
- ic->ic_stats.is_rx_wrongdir++;
- goto out;
- }
- /* check if source STA is associated */
- if (ni == ic->ic_bss) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "%s", "unknown src");
- ieee80211_send_error(ic, ni, wh->i_addr2,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_NOT_AUTHED);
- ic->ic_stats.is_rx_notassoc++;
- goto err;
- }
- if (ni->ni_associd == 0) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "%s", "unassoc src");
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DISASSOC,
- IEEE80211_REASON_NOT_ASSOCED);
- ic->ic_stats.is_rx_notassoc++;
- goto err;
- }
-
- /*
- * Check for power save state change.
- * XXX out-of-order A-MPDU frames?
- */
- if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
- (ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
- ieee80211_node_pwrsave(ni,
- wh->i_fc[1] & IEEE80211_FC1_PWR_MGT);
- break;
- default:
- /* XXX here to keep compiler happy */
- goto out;
- }
-
- /*
- * Handle A-MPDU re-ordering. The station must be
- * associated and negotiated HT. The frame must be
- * a QoS frame (not QoS null data) and not previously
- * processed for A-MPDU re-ordering. If the frame is
- * to be processed directly then ieee80211_ampdu_reorder
- * will return 0; otherwise it has consumed the mbuf
- * and we should do nothing more with it.
- */
- if ((ni->ni_flags & IEEE80211_NODE_HT) &&
- subtype == IEEE80211_FC0_SUBTYPE_QOS &&
- ieee80211_ampdu_reorder(ni, m) != 0) {
- m = NULL;
- goto out;
- }
- resubmit_ampdu:
-
- /*
- * Handle privacy requirements. Note that we
- * must not be preempted from here until after
- * we (potentially) call ieee80211_crypto_demic;
- * otherwise we may violate assumptions in the
- * crypto cipher modules used to do delayed update
- * of replay sequence numbers.
- */
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
- /*
- * Discard encrypted frames when privacy is off.
- */
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "WEP", "%s", "PRIVACY off");
- ic->ic_stats.is_rx_noprivacy++;
- IEEE80211_NODE_STAT(ni, rx_noprivacy);
- goto out;
- }
- key = ieee80211_crypto_decap(ic, ni, m, hdrspace);
- if (key == NULL) {
- /* NB: stats+msgs handled in crypto_decap */
- IEEE80211_NODE_STAT(ni, rx_wepfail);
- goto out;
- }
- wh = mtod(m, struct ieee80211_frame *);
- wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
- } else {
- /* XXX M_WEP and IEEE80211_F_PRIVACY */
- key = NULL;
- }
-
- /*
- * Save QoS bits for use below--before we strip the header.
- */
- if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
- qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
- ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
- ((struct ieee80211_qosframe *)wh)->i_qos[0];
- } else
- qos = 0;
+ struct ieee80211vap *vap;
+ int type = -1;
- /*
- * Next up, any fragmentation.
- */
- if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
- m = ieee80211_defrag(ic, ni, m, hdrspace);
- if (m == NULL) {
- /* Fragment dropped or frame not complete yet */
- goto out;
- }
- }
- wh = NULL; /* no longer valid, catch any uses */
+ /* XXX locking */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ struct ieee80211_node *ni;
+ struct mbuf *mcopy;
/*
- * Next strip any MSDU crypto bits.
+ * WDS vap's only receive directed traffic from the
+ * station at the ``far end''. That traffic should
+ * be passed through the AP vap the station is associated
+ * to--so don't spam them with mcast frames.
*/
- if (key != NULL && !ieee80211_crypto_demic(ic, key, m, 0)) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- ni->ni_macaddr, "data", "%s", "demic error");
- ic->ic_stats.is_rx_demicfail++;
- IEEE80211_NODE_STAT(ni, rx_demicfail);
- goto out;
- }
-
- /* copy to listener after decrypt */
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m);
- need_tap = 0;
-
- /*
- * Finally, strip the 802.11 header.
- */
- m = ieee80211_decap(ic, m, hdrspace);
- if (m == NULL) {
- /* don't count Null data frames as errors */
- if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
- subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
- goto out;
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- ni->ni_macaddr, "data", "%s", "decap error");
- ic->ic_stats.is_rx_decap++;
- IEEE80211_NODE_STAT(ni, rx_decap);
- goto err;
- }
- eh = mtod(m, struct ether_header *);
- if (!ieee80211_node_is_authorized(ni)) {
+ if (vap->iv_opmode == IEEE80211_M_WDS)
+ continue;
+ if (TAILQ_NEXT(vap, iv_next) != NULL) {
/*
- * Deny any non-PAE frames received prior to
- * authorization. For open/shared-key
- * authentication the port is mark authorized
- * after authentication completes. For 802.1x
- * the port is not marked authorized by the
- * authenticator until the handshake has completed.
+ * Packet contents are changed by ieee80211_decap
+ * so do a deep copy of the packet.
*/
- if (eh->ether_type != htons(ETHERTYPE_PAE)) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- eh->ether_shost, "data",
- "unauthorized port: ether type 0x%x len %u",
- eh->ether_type, m->m_pkthdr.len);
- ic->ic_stats.is_rx_unauth++;
- IEEE80211_NODE_STAT(ni, rx_unauth);
- goto err;
+ mcopy = m_dup(m, M_DONTWAIT);
+ if (mcopy == NULL) {
+ /* XXX stat+msg */
+ continue;
}
} else {
- /*
- * When denying unencrypted frames, discard
- * any non-PAE frames received without encryption.
- */
- if ((ic->ic_flags & IEEE80211_F_DROPUNENC) &&
- (key == NULL && (m->m_flags & M_WEP) == 0) &&
- eh->ether_type != htons(ETHERTYPE_PAE)) {
- /*
- * Drop unencrypted frames.
- */
- ic->ic_stats.is_rx_unencrypted++;
- IEEE80211_NODE_STAT(ni, rx_unencrypted);
- goto out;
- }
- }
- /* XXX require HT? */
- if (qos & IEEE80211_QOS_AMSDU) {
- m = ieee80211_decap_amsdu(ni, m);
- if (m == NULL)
- return IEEE80211_FC0_TYPE_DATA;
- } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
-#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
- m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
- struct llc *llc;
-
- /*
- * Check for fast-frame tunnel encapsulation.
- */
- if (m->m_len < FF_LLC_SIZE &&
- (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
- ni->ni_macaddr, "fast-frame",
- "%s", "m_pullup(llc) failed");
- ic->ic_stats.is_rx_tooshort++;
- return IEEE80211_FC0_TYPE_DATA;
- }
- llc = (struct llc *)(mtod(m, uint8_t *) +
- sizeof(struct ether_header));
- if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
- m_adj(m, FF_LLC_SIZE);
- m = ieee80211_decap_fastframe(ic, ni, m);
- if (m == NULL)
- return IEEE80211_FC0_TYPE_DATA;
- }
- }
-#undef FF_LLC_SIZE
- ieee80211_deliver_data(ic, ni, m);
- return IEEE80211_FC0_TYPE_DATA;
-
- case IEEE80211_FC0_TYPE_MGT:
- ic->ic_stats.is_rx_mgmt++;
- IEEE80211_NODE_STAT(ni, rx_mgmt);
- if (dir != IEEE80211_FC1_DIR_NODS) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "unknown dir 0x%x", dir);
- ic->ic_stats.is_rx_wrongdir++;
- goto err;
- }
- if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
- ni->ni_macaddr, "mgt", "too short: len %u",
- m->m_pkthdr.len);
- ic->ic_stats.is_rx_tooshort++;
- goto out;
- }
-#ifdef IEEE80211_DEBUG
- if ((ieee80211_msg_debug(ic) && doprint(ic, subtype)) ||
- ieee80211_msg_dumppkts(ic)) {
- if_printf(ic->ic_ifp, "received %s from %s rssi %d\n",
- ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- ether_sprintf(wh->i_addr2), rssi);
- }
-#endif
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
- /*
- * Only shared key auth frames with a challenge
- * should be encrypted, discard all others.
- */
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "%s", "WEP set but not permitted");
- ic->ic_stats.is_rx_mgtdiscard++; /* XXX */
- goto out;
- }
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
- /*
- * Discard encrypted frames when privacy is off.
- */
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "mgt", "%s", "WEP set but PRIVACY off");
- ic->ic_stats.is_rx_noprivacy++;
- goto out;
- }
- hdrspace = ieee80211_hdrspace(ic, wh);
- key = ieee80211_crypto_decap(ic, ni, m, hdrspace);
- if (key == NULL) {
- /* NB: stats+msgs handled in crypto_decap */
- goto out;
- }
- wh = mtod(m, struct ieee80211_frame *);
- wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
- }
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m);
- (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, noise, rstamp);
- m_freem(m);
- return IEEE80211_FC0_TYPE_MGT;
-
- case IEEE80211_FC0_TYPE_CTL:
- ic->ic_stats.is_rx_ctl++;
- IEEE80211_NODE_STAT(ni, rx_ctrl);
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- switch (subtype) {
- case IEEE80211_FC0_SUBTYPE_PS_POLL:
- ieee80211_recv_pspoll(ic, ni, m);
- break;
- case IEEE80211_FC0_SUBTYPE_BAR:
- ieee80211_recv_bar(ni, m);
- break;
- }
+ mcopy = m;
+ m = NULL;
}
- goto out;
- default:
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
- wh, NULL, "bad frame type 0x%x", type);
- /* should not come here */
- break;
+ ni = ieee80211_ref_node(vap->iv_bss);
+ type = ieee80211_input(ni, mcopy, rssi, noise, rstamp);
+ ieee80211_free_node(ni);
}
-err:
- ifp->if_ierrors++;
-out:
- if (m != NULL) {
- if (bpf_peers_present(ic->ic_rawbpf) && need_tap)
- bpf_mtap(ic->ic_rawbpf, m);
+ if (m != NULL) /* no vaps, reclaim mbuf */
m_freem(m);
- }
return type;
-#undef SEQ_LEQ
}
/*
* This function reassemble fragments.
+ *
+ * XXX should handle 3 concurrent reassemblies per-spec.
*/
-static struct mbuf *
-ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni,
- struct mbuf *m, int hdrspace)
+struct mbuf *
+ieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
struct ieee80211_frame *lwh;
uint16_t rxseq;
@@ -681,7 +167,7 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni,
if (mfrag == NULL) {
if (fragno != 0) { /* !first fragment, discard */
- ic->ic_stats.is_rx_defrag++;
+ vap->iv_stats.is_rx_defrag++;
IEEE80211_NODE_STAT(ni, rx_defrag);
m_freem(m);
return NULL;
@@ -705,12 +191,14 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni,
}
void
-ieee80211_deliver_data(struct ieee80211com *ic,
+ieee80211_deliver_data(struct ieee80211vap *vap,
struct ieee80211_node *ni, struct mbuf *m)
{
struct ether_header *eh = mtod(m, struct ether_header *);
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = vap->iv_ifp;
+ /* NB: see hostap_deliver_data, this path doesn't handle hostap */
+ KASSERT(vap->iv_opmode != IEEE80211_M_HOSTAP, ("gack, hostap"));
/*
* Do accounting.
*/
@@ -722,66 +210,21 @@ ieee80211_deliver_data(struct ieee80211com *ic,
IEEE80211_NODE_STAT(ni, rx_mcast);
} else
IEEE80211_NODE_STAT(ni, rx_ucast);
+ m->m_pkthdr.rcvif = ifp;
/* clear driver/net80211 flags before passing up */
m->m_flags &= ~M_80211_RX;
- /* perform as a bridge within the AP */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
- (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) {
- struct mbuf *m1 = NULL;
-
- if (m->m_flags & M_MCAST) {
- m1 = m_dup(m, M_DONTWAIT);
- if (m1 == NULL)
- ifp->if_oerrors++;
- else
- m1->m_flags |= M_MCAST;
- } else {
- /*
- * Check if the destination is known; if so
- * and the port is authorized dispatch directly.
- */
- struct ieee80211_node *sta =
- ieee80211_find_node(&ic->ic_sta, eh->ether_dhost);
- if (sta != NULL) {
- if (ieee80211_node_is_authorized(sta)) {
- /*
- * Beware of sending to ourself; this
- * needs to happen via the normal
- * input path.
- */
- if (sta != ic->ic_bss) {
- m1 = m;
- m = NULL;
- }
- } else {
- ic->ic_stats.is_rx_unauth++;
- IEEE80211_NODE_STAT(sta, rx_unauth);
- }
- ieee80211_free_node(sta);
- }
- }
- if (m1 != NULL) {
- int error;
-
- /* XXX does not work well with WME */
- IFQ_HANDOFF(ifp, m1, error);
- }
- }
- if (m != NULL) {
- m->m_pkthdr.rcvif = ifp;
- if (ni->ni_vlan != 0) {
- /* attach vlan tag */
- m->m_pkthdr.ether_vtag = ni->ni_vlan;
- m->m_flags |= M_VLANTAG;
- }
- (*ifp->if_input)(ifp, m);
+ if (ni->ni_vlan != 0) {
+ /* attach vlan tag */
+ m->m_pkthdr.ether_vtag = ni->ni_vlan;
+ m->m_flags |= M_VLANTAG;
}
+ ifp->if_input(ifp, m);
}
-static struct mbuf *
-ieee80211_decap(struct ieee80211com *ic, struct mbuf *m, int hdrlen)
+struct mbuf *
+ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen)
{
struct ieee80211_qosframe_addr4 wh; /* Max size address frames */
struct ether_header *eh;
@@ -916,28 +359,28 @@ ieee80211_decap1(struct mbuf *m, int *framelen)
* for delivery. The second frame is returned for delivery
* via the normal path.
*/
-static struct mbuf *
-ieee80211_decap_fastframe(struct ieee80211com *ic,
- struct ieee80211_node *ni, struct mbuf *m)
+struct mbuf *
+ieee80211_decap_fastframe(struct ieee80211_node *ni, struct mbuf *m)
{
#define MS(x,f) (((x) & f) >> f##_S)
+ struct ieee80211vap *vap = ni->ni_vap;
uint32_t ath;
struct mbuf *n;
int framelen;
m_copydata(m, 0, sizeof(uint32_t), (caddr_t) &ath);
if (MS(ath, ATH_FF_PROTO) != ATH_FF_PROTO_L2TUNNEL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame",
"unsupport tunnel protocol, header 0x%x", ath);
- ic->ic_stats.is_ff_badhdr++;
+ vap->iv_stats.is_ff_badhdr++;
m_freem(m);
return NULL;
}
/* NB: skip header and alignment padding */
m_adj(m, roundup(sizeof(uint32_t) - 2, 4) + 2);
- ic->ic_stats.is_ff_decap++;
+ vap->iv_stats.is_ff_decap++;
/*
* Decap the first frame, bust it apart from the
@@ -946,21 +389,22 @@ ieee80211_decap_fastframe(struct ieee80211com *ic,
*/
m = ieee80211_decap1(m, &framelen);
if (m == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame", "%s", "first decap failed");
- ic->ic_stats.is_ff_tooshort++;
+ vap->iv_stats.is_ff_tooshort++;
return NULL;
}
n = m_split(m, framelen, M_NOWAIT);
if (n == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame",
"%s", "unable to split encapsulated frames");
- ic->ic_stats.is_ff_split++;
+ vap->iv_stats.is_ff_split++;
m_freem(m); /* NB: must reclaim */
return NULL;
}
- ieee80211_deliver_data(ic, ni, m); /* 1st of pair */
+ /* XXX not right for WDS */
+ vap->iv_deliver_data(vap, ni, m); /* 1st of pair */
/*
* Decap second frame.
@@ -968,9 +412,9 @@ ieee80211_decap_fastframe(struct ieee80211com *ic,
m_adj(n, roundup2(framelen, 4) - framelen); /* padding */
n = ieee80211_decap1(n, &framelen);
if (n == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame", "%s", "second decap failed");
- ic->ic_stats.is_ff_tooshort++;
+ vap->iv_stats.is_ff_tooshort++;
}
/* XXX verify framelen against mbuf contents */
return n; /* 2nd delivered by caller */
@@ -984,7 +428,7 @@ int
ieee80211_setup_rates(struct ieee80211_node *ni,
const uint8_t *rates, const uint8_t *xrates, int flags)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_rateset *rs = &ni->ni_rates;
memset(rs, 0, sizeof(*rs));
@@ -998,11 +442,10 @@ ieee80211_setup_rates(struct ieee80211_node *ni,
nxrates = xrates[1];
if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) {
nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_XRATE,
- "[%s] extended rate set too large;"
- " only using %u of %u rates\n",
- ether_sprintf(ni->ni_macaddr), nxrates, xrates[1]);
- ic->ic_stats.is_rx_rstoobig++;
+ IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE, ni,
+ "extended rate set too large; only using "
+ "%u of %u rates", nxrates, xrates[1]);
+ vap->iv_stats.is_rx_rstoobig++;
}
memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates);
rs->rs_nrates += nxrates;
@@ -1010,101 +453,6 @@ ieee80211_setup_rates(struct ieee80211_node *ni,
return ieee80211_fix_rate(ni, rs, flags);
}
-static void
-ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh,
- struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp,
- uint16_t seq, uint16_t status)
-{
-
- if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "open auth",
- "bad sta auth mode %u", ni->ni_authmode);
- ic->ic_stats.is_rx_bad_auth++; /* XXX */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- /*
- * Clear any challenge text that may be there if
- * a previous shared key auth failed and then an
- * open auth is attempted.
- */
- if (ni->ni_challenge != NULL) {
- FREE(ni->ni_challenge, M_80211_NODE);
- ni->ni_challenge = NULL;
- }
- /* XXX hack to workaround calling convention */
- ieee80211_send_error(ic, ni, wh->i_addr2,
- IEEE80211_FC0_SUBTYPE_AUTH,
- (seq + 1) | (IEEE80211_STATUS_ALG<<16));
- }
- return;
- }
- switch (ic->ic_opmode) {
- case IEEE80211_M_IBSS:
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_MONITOR:
- case IEEE80211_M_WDS:
- /* should not come here */
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "open auth",
- "bad operating mode %u", ic->ic_opmode);
- break;
-
- case IEEE80211_M_HOSTAP:
- if (ic->ic_state != IEEE80211_S_RUN ||
- seq != IEEE80211_AUTH_OPEN_REQUEST) {
- ic->ic_stats.is_rx_bad_auth++;
- return;
- }
- /* always accept open authentication requests */
- if (ni == ic->ic_bss) {
- ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
- if (ni == NULL)
- return;
- } else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0)
- (void) ieee80211_ref_node(ni);
- /*
- * Mark the node as referenced to reflect that it's
- * reference count has been bumped to insure it remains
- * after the transaction completes.
- */
- ni->ni_flags |= IEEE80211_NODE_AREF;
-
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] station authenticated (open)\n",
- ether_sprintf(ni->ni_macaddr));
- /*
- * When 802.1x is not in use mark the port
- * authorized at this point so traffic can flow.
- */
- if (ni->ni_authmode != IEEE80211_AUTH_8021X)
- ieee80211_node_authorize(ni);
- break;
-
- case IEEE80211_M_STA:
- if (ic->ic_state != IEEE80211_S_AUTH ||
- seq != IEEE80211_AUTH_OPEN_RESPONSE) {
- ic->ic_stats.is_rx_bad_auth++;
- return;
- }
- if (status != 0) {
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] open auth failed (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), status);
- /* XXX can this happen? */
- if (ni != ic->ic_bss)
- ni->ni_fails++;
- ic->ic_stats.is_rx_auth_fail++;
- ieee80211_new_state(ic, IEEE80211_S_SCAN,
- IEEE80211_SCAN_FAIL_STATUS);
- } else
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
- break;
- }
-}
-
/*
* Send a management frame error response to the specified
* station. If ni is associated with the station then use
@@ -1112,14 +460,26 @@ ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh,
* transmitting the frame and then free the reference so
* it will go away as soon as the frame has been transmitted.
*/
-static void
-ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni,
- const uint8_t *mac, int subtype, int arg)
+void
+ieee80211_send_error(struct ieee80211_node *ni,
+ const uint8_t mac[IEEE80211_ADDR_LEN], int subtype, int arg)
{
+ struct ieee80211vap *vap = ni->ni_vap;
int istmp;
- if (ni == ic->ic_bss) {
- ni = ieee80211_tmp_node(ic, mac);
+ if (ni == vap->iv_bss) {
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ /*
+ * XXX hack until we get rid of this routine.
+ * We can be called prior to the vap reaching
+ * run state under certain conditions in which
+ * case iv_bss->ni_chan will not be setup.
+ * Check for this explicitly and and just ignore
+ * the request.
+ */
+ return;
+ }
+ ni = ieee80211_tmp_node(vap, mac);
if (ni == NULL) {
/* XXX msg */
return;
@@ -1127,2204 +487,342 @@ ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni,
istmp = 1;
} else
istmp = 0;
- IEEE80211_SEND_MGMT(ic, ni, subtype, arg);
+ IEEE80211_SEND_MGMT(ni, subtype, arg);
if (istmp)
ieee80211_free_node(ni);
}
-static int
-alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni)
+int
+ieee80211_alloc_challenge(struct ieee80211_node *ni)
{
if (ni->ni_challenge == NULL)
MALLOC(ni->ni_challenge, uint32_t*, IEEE80211_CHALLENGE_LEN,
M_80211_NODE, M_NOWAIT);
if (ni->ni_challenge == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] shared key challenge alloc failed\n",
- ether_sprintf(ni->ni_macaddr));
+ IEEE80211_NOTE(ni->ni_vap,
+ IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, ni,
+ "%s", "shared key challenge alloc failed");
/* XXX statistic */
}
return (ni->ni_challenge != NULL);
}
-/* XXX TODO: add statistics */
-static void
-ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh,
- uint8_t *frm, uint8_t *efrm, struct ieee80211_node *ni,
- int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status)
-{
- uint8_t *challenge;
- int allocbs, estatus;
-
- /*
- * NB: this can happen as we allow pre-shared key
- * authentication to be enabled w/o wep being turned
- * on so that configuration of these can be done
- * in any order. It may be better to enforce the
- * ordering in which case this check would just be
- * for sanity/consistency.
- */
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "%s", " PRIVACY is disabled");
- estatus = IEEE80211_STATUS_ALG;
- goto bad;
- }
- /*
- * Pre-shared key authentication is evil; accept
- * it only if explicitly configured (it is supported
- * mainly for compatibility with clients like OS X).
- */
- if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
- ni->ni_authmode != IEEE80211_AUTH_SHARED) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "bad sta auth mode %u", ni->ni_authmode);
- ic->ic_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */
- estatus = IEEE80211_STATUS_ALG;
- goto bad;
- }
-
- challenge = NULL;
- if (frm + 1 < efrm) {
- if ((frm[1] + 2) > (efrm - frm)) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "ie %d/%d too long",
- frm[0], (frm[1] + 2) - (efrm - frm));
- ic->ic_stats.is_rx_bad_auth++;
- estatus = IEEE80211_STATUS_CHALLENGE;
- goto bad;
- }
- if (*frm == IEEE80211_ELEMID_CHALLENGE)
- challenge = frm;
- frm += frm[1] + 2;
- }
- switch (seq) {
- case IEEE80211_AUTH_SHARED_CHALLENGE:
- case IEEE80211_AUTH_SHARED_RESPONSE:
- if (challenge == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "%s", "no challenge");
- ic->ic_stats.is_rx_bad_auth++;
- estatus = IEEE80211_STATUS_CHALLENGE;
- goto bad;
- }
- if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "bad challenge len %d", challenge[1]);
- ic->ic_stats.is_rx_bad_auth++;
- estatus = IEEE80211_STATUS_CHALLENGE;
- goto bad;
- }
- default:
- break;
- }
- switch (ic->ic_opmode) {
- case IEEE80211_M_MONITOR:
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_IBSS:
- case IEEE80211_M_WDS:
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "bad operating mode %u", ic->ic_opmode);
- return;
- case IEEE80211_M_HOSTAP:
- if (ic->ic_state != IEEE80211_S_RUN) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "bad state %u", ic->ic_state);
- estatus = IEEE80211_STATUS_ALG; /* XXX */
- goto bad;
- }
- switch (seq) {
- case IEEE80211_AUTH_SHARED_REQUEST:
- if (ni == ic->ic_bss) {
- ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
- if (ni == NULL) {
- /* NB: no way to return an error */
- return;
- }
- allocbs = 1;
- } else {
- if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0)
- (void) ieee80211_ref_node(ni);
- allocbs = 0;
- }
- /*
- * Mark the node as referenced to reflect that it's
- * reference count has been bumped to insure it remains
- * after the transaction completes.
- */
- ni->ni_flags |= IEEE80211_NODE_AREF;
- ni->ni_rssi = rssi;
- ni->ni_noise = noise;
- ni->ni_rstamp = rstamp;
- if (!alloc_challenge(ic, ni)) {
- /* NB: don't return error so they rexmit */
- return;
- }
- get_random_bytes(ni->ni_challenge,
- IEEE80211_CHALLENGE_LEN);
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] shared key %sauth request\n",
- ether_sprintf(ni->ni_macaddr),
- allocbs ? "" : "re");
- break;
- case IEEE80211_AUTH_SHARED_RESPONSE:
- if (ni == ic->ic_bss) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key response",
- "%s", "unknown station");
- /* NB: don't send a response */
- return;
- }
- if (ni->ni_challenge == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key response",
- "%s", "no challenge recorded");
- ic->ic_stats.is_rx_bad_auth++;
- estatus = IEEE80211_STATUS_CHALLENGE;
- goto bad;
- }
- if (memcmp(ni->ni_challenge, &challenge[2],
- challenge[1]) != 0) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key response",
- "%s", "challenge mismatch");
- ic->ic_stats.is_rx_auth_fail++;
- estatus = IEEE80211_STATUS_CHALLENGE;
- goto bad;
- }
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] station authenticated (shared key)\n",
- ether_sprintf(ni->ni_macaddr));
- ieee80211_node_authorize(ni);
- break;
- default:
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "bad seq %d", seq);
- ic->ic_stats.is_rx_bad_auth++;
- estatus = IEEE80211_STATUS_SEQUENCE;
- goto bad;
- }
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
- break;
-
- case IEEE80211_M_STA:
- if (ic->ic_state != IEEE80211_S_AUTH)
- return;
- switch (seq) {
- case IEEE80211_AUTH_SHARED_PASS:
- if (ni->ni_challenge != NULL) {
- FREE(ni->ni_challenge, M_80211_NODE);
- ni->ni_challenge = NULL;
- }
- if (status != 0) {
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] shared key auth failed (reason %d)\n",
- ether_sprintf(ieee80211_getbssid(ic, wh)),
- status);
- /* XXX can this happen? */
- if (ni != ic->ic_bss)
- ni->ni_fails++;
- ic->ic_stats.is_rx_auth_fail++;
- return;
- }
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
- break;
- case IEEE80211_AUTH_SHARED_CHALLENGE:
- if (!alloc_challenge(ic, ni))
- return;
- /* XXX could optimize by passing recvd challenge */
- memcpy(ni->ni_challenge, &challenge[2], challenge[1]);
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
- break;
- default:
- IEEE80211_DISCARD(ic, IEEE80211_MSG_AUTH,
- wh, "shared key auth", "bad seq %d", seq);
- ic->ic_stats.is_rx_bad_auth++;
- return;
- }
- break;
- }
- return;
-bad:
- /*
- * Send an error response; but only when operating as an AP.
- */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- /* XXX hack to workaround calling convention */
- ieee80211_send_error(ic, ni, wh->i_addr2,
- IEEE80211_FC0_SUBTYPE_AUTH,
- (seq + 1) | (estatus<<16));
- } else if (ic->ic_opmode == IEEE80211_M_STA) {
- /*
- * Kick the state machine. This short-circuits
- * using the mgt frame timeout to trigger the
- * state transition.
- */
- if (ic->ic_state == IEEE80211_S_AUTH)
- ieee80211_new_state(ic, IEEE80211_S_SCAN,
- IEEE80211_SCAN_FAIL_STATUS);
- }
-}
-
-/* Verify the existence and length of __elem or get out. */
-#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \
- if ((__elem) == NULL) { \
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \
- wh, ieee80211_mgt_subtype_name[subtype >> \
- IEEE80211_FC0_SUBTYPE_SHIFT], \
- "%s", "no " #__elem ); \
- ic->ic_stats.is_rx_elem_missing++; \
- return; \
- } \
- if ((__elem)[1] > (__maxlen)) { \
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \
- wh, ieee80211_mgt_subtype_name[subtype >> \
- IEEE80211_FC0_SUBTYPE_SHIFT], \
- "bad " #__elem " len %d", (__elem)[1]); \
- ic->ic_stats.is_rx_elem_toobig++; \
- return; \
- } \
-} while (0)
-
-#define IEEE80211_VERIFY_LENGTH(_len, _minlen, _action) do { \
- if ((_len) < (_minlen)) { \
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \
- wh, ieee80211_mgt_subtype_name[subtype >> \
- IEEE80211_FC0_SUBTYPE_SHIFT], \
- "ie too short, got %d, expected %d", \
- (_len), (_minlen)); \
- ic->ic_stats.is_rx_elem_toosmall++; \
- _action; \
- } \
-} while (0)
-
-#ifdef IEEE80211_DEBUG
-static void
-ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag,
- uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid)
-{
- printf("[%s] discard %s frame, ssid mismatch: ",
- ether_sprintf(mac), tag);
- ieee80211_print_essid(ssid + 2, ssid[1]);
- printf("\n");
-}
-
-#define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \
- if ((_ssid)[1] != 0 && \
- ((_ssid)[1] != (_ni)->ni_esslen || \
- memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
- if (ieee80211_msg_input(ic)) \
- ieee80211_ssid_mismatch(ic, \
- ieee80211_mgt_subtype_name[subtype >> \
- IEEE80211_FC0_SUBTYPE_SHIFT], \
- wh->i_addr2, _ssid); \
- ic->ic_stats.is_rx_ssidmismatch++; \
- return; \
- } \
-} while (0)
-#else /* !IEEE80211_DEBUG */
-#define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \
- if ((_ssid)[1] != 0 && \
- ((_ssid)[1] != (_ni)->ni_esslen || \
- memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
- ic->ic_stats.is_rx_ssidmismatch++; \
- return; \
- } \
-} while (0)
-#endif /* !IEEE80211_DEBUG */
-
-/* unalligned little endian access */
-#define LE_READ_2(p) \
- ((uint16_t) \
- ((((const uint8_t *)(p))[0] ) | \
- (((const uint8_t *)(p))[1] << 8)))
-#define LE_READ_4(p) \
- ((uint32_t) \
- ((((const uint8_t *)(p))[0] ) | \
- (((const uint8_t *)(p))[1] << 8) | \
- (((const uint8_t *)(p))[2] << 16) | \
- (((const uint8_t *)(p))[3] << 24)))
-
-static __inline int
-iswpaoui(const uint8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
-}
-
-static __inline int
-iswmeoui(const uint8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
-}
-
-static __inline int
-iswmeparam(const uint8_t *frm)
-{
- return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
- frm[6] == WME_PARAM_OUI_SUBTYPE;
-}
-
-static __inline int
-iswmeinfo(const uint8_t *frm)
-{
- return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
- frm[6] == WME_INFO_OUI_SUBTYPE;
-}
-
-static __inline int
-isatherosoui(const uint8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
-}
-
-static __inline int
-ishtcapoui(const uint8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTCAP<<24)|BCM_OUI);
-}
-
-static __inline int
-ishtinfooui(const uint8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI);
-}
-
-/*
- * Convert a WPA cipher selector OUI to an internal
- * cipher algorithm. Where appropriate we also
- * record any key length.
- */
-static int
-wpa_cipher(uint8_t *sel, uint8_t *keylen)
-{
-#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
- uint32_t w = LE_READ_4(sel);
-
- switch (w) {
- case WPA_SEL(WPA_CSE_NULL):
- return IEEE80211_CIPHER_NONE;
- case WPA_SEL(WPA_CSE_WEP40):
- if (keylen)
- *keylen = 40 / NBBY;
- return IEEE80211_CIPHER_WEP;
- case WPA_SEL(WPA_CSE_WEP104):
- if (keylen)
- *keylen = 104 / NBBY;
- return IEEE80211_CIPHER_WEP;
- case WPA_SEL(WPA_CSE_TKIP):
- return IEEE80211_CIPHER_TKIP;
- case WPA_SEL(WPA_CSE_CCMP):
- return IEEE80211_CIPHER_AES_CCM;
- }
- return 32; /* NB: so 1<< is discarded */
-#undef WPA_SEL
-}
-
-/*
- * Convert a WPA key management/authentication algorithm
- * to an internal code.
- */
-static int
-wpa_keymgmt(uint8_t *sel)
-{
-#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
- uint32_t w = LE_READ_4(sel);
-
- switch (w) {
- case WPA_SEL(WPA_ASE_8021X_UNSPEC):
- return WPA_ASE_8021X_UNSPEC;
- case WPA_SEL(WPA_ASE_8021X_PSK):
- return WPA_ASE_8021X_PSK;
- case WPA_SEL(WPA_ASE_NONE):
- return WPA_ASE_NONE;
- }
- return 0; /* NB: so is discarded */
-#undef WPA_SEL
-}
-
-/*
- * Parse a WPA information element to collect parameters
- * and validate the parameters against what has been
- * configured for the system.
- */
-static int
-ieee80211_parse_wpa(struct ieee80211com *ic, uint8_t *frm,
- struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
-{
- uint8_t len = frm[1];
- uint32_t w;
- int n;
-
- /*
- * Check the length once for fixed parts: OUI, type,
- * version, mcast cipher, and 2 selector counts.
- * Other, variable-length data, must be checked separately.
- */
- if ((ic->ic_flags & IEEE80211_F_WPA1) == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "not WPA, flags 0x%x", ic->ic_flags);
- return IEEE80211_REASON_IE_INVALID;
- }
- if (len < 14) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "too short, len %u", len);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 6, len -= 4; /* NB: len is payload only */
- /* NB: iswapoui already validated the OUI and type */
- w = LE_READ_2(frm);
- if (w != WPA_VERSION) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "bad version %u", w);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 2, len -= 2;
-
- /* multicast/group cipher */
- w = wpa_cipher(frm, &rsn->rsn_mcastkeylen);
- if (w != rsn->rsn_mcastcipher) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "mcast cipher mismatch; got %u, expected %u",
- w, rsn->rsn_mcastcipher);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 4, len -= 4;
-
- /* unicast ciphers */
- n = LE_READ_2(frm);
- frm += 2, len -= 2;
- if (len < n*4+2) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "ucast cipher data too short; len %u, n %u",
- len, n);
- return IEEE80211_REASON_IE_INVALID;
- }
- w = 0;
- for (; n > 0; n--) {
- w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen);
- frm += 4, len -= 4;
- }
- w &= rsn->rsn_ucastcipherset;
- if (w == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "%s", "ucast cipher set empty");
- return IEEE80211_REASON_IE_INVALID;
- }
- if (w & (1<<IEEE80211_CIPHER_TKIP))
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
- else
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
-
- /* key management algorithms */
- n = LE_READ_2(frm);
- frm += 2, len -= 2;
- if (len < n*4) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "key mgmt alg data too short; len %u, n %u",
- len, n);
- return IEEE80211_REASON_IE_INVALID;
- }
- w = 0;
- for (; n > 0; n--) {
- w |= wpa_keymgmt(frm);
- frm += 4, len -= 4;
- }
- w &= rsn->rsn_keymgmtset;
- if (w == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "%s", "no acceptable key mgmt alg");
- return IEEE80211_REASON_IE_INVALID;
- }
- if (w & WPA_ASE_8021X_UNSPEC)
- rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC;
- else
- rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
-
- if (len > 2) /* optional capabilities */
- rsn->rsn_caps = LE_READ_2(frm);
-
- return 0;
-}
-
-/*
- * Convert an RSN cipher selector OUI to an internal
- * cipher algorithm. Where appropriate we also
- * record any key length.
- */
-static int
-rsn_cipher(uint8_t *sel, uint8_t *keylen)
-{
-#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
- uint32_t w = LE_READ_4(sel);
-
- switch (w) {
- case RSN_SEL(RSN_CSE_NULL):
- return IEEE80211_CIPHER_NONE;
- case RSN_SEL(RSN_CSE_WEP40):
- if (keylen)
- *keylen = 40 / NBBY;
- return IEEE80211_CIPHER_WEP;
- case RSN_SEL(RSN_CSE_WEP104):
- if (keylen)
- *keylen = 104 / NBBY;
- return IEEE80211_CIPHER_WEP;
- case RSN_SEL(RSN_CSE_TKIP):
- return IEEE80211_CIPHER_TKIP;
- case RSN_SEL(RSN_CSE_CCMP):
- return IEEE80211_CIPHER_AES_CCM;
- case RSN_SEL(RSN_CSE_WRAP):
- return IEEE80211_CIPHER_AES_OCB;
- }
- return 32; /* NB: so 1<< is discarded */
-#undef WPA_SEL
-}
-
-/*
- * Convert an RSN key management/authentication algorithm
- * to an internal code.
- */
-static int
-rsn_keymgmt(uint8_t *sel)
-{
-#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
- uint32_t w = LE_READ_4(sel);
-
- switch (w) {
- case RSN_SEL(RSN_ASE_8021X_UNSPEC):
- return RSN_ASE_8021X_UNSPEC;
- case RSN_SEL(RSN_ASE_8021X_PSK):
- return RSN_ASE_8021X_PSK;
- case RSN_SEL(RSN_ASE_NONE):
- return RSN_ASE_NONE;
- }
- return 0; /* NB: so is discarded */
-#undef RSN_SEL
-}
-
-/*
- * Parse a WPA/RSN information element to collect parameters
- * and validate the parameters against what has been
- * configured for the system.
- */
-static int
-ieee80211_parse_rsn(struct ieee80211com *ic, uint8_t *frm,
- struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
-{
- uint8_t len = frm[1];
- uint32_t w;
- int n;
-
- /*
- * Check the length once for fixed parts:
- * version, mcast cipher, and 2 selector counts.
- * Other, variable-length data, must be checked separately.
- */
- if ((ic->ic_flags & IEEE80211_F_WPA2) == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "not RSN, flags 0x%x", ic->ic_flags);
- return IEEE80211_REASON_IE_INVALID;
- }
- if (len < 10) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "too short, len %u", len);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 2;
- w = LE_READ_2(frm);
- if (w != RSN_VERSION) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "bad version %u", w);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 2, len -= 2;
-
- /* multicast/group cipher */
- w = rsn_cipher(frm, &rsn->rsn_mcastkeylen);
- if (w != rsn->rsn_mcastcipher) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "mcast cipher mismatch; got %u, expected %u",
- w, rsn->rsn_mcastcipher);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 4, len -= 4;
-
- /* unicast ciphers */
- n = LE_READ_2(frm);
- frm += 2, len -= 2;
- if (len < n*4+2) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "ucast cipher data too short; len %u, n %u",
- len, n);
- return IEEE80211_REASON_IE_INVALID;
- }
- w = 0;
- for (; n > 0; n--) {
- w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen);
- frm += 4, len -= 4;
- }
- w &= rsn->rsn_ucastcipherset;
- if (w == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "%s", "ucast cipher set empty");
- return IEEE80211_REASON_IE_INVALID;
- }
- if (w & (1<<IEEE80211_CIPHER_TKIP))
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
- else
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
-
- /* key management algorithms */
- n = LE_READ_2(frm);
- frm += 2, len -= 2;
- if (len < n*4) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "key mgmt alg data too short; len %u, n %u",
- len, n);
- return IEEE80211_REASON_IE_INVALID;
- }
- w = 0;
- for (; n > 0; n--) {
- w |= rsn_keymgmt(frm);
- frm += 4, len -= 4;
- }
- w &= rsn->rsn_keymgmtset;
- if (w == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "%s", "no acceptable key mgmt alg");
- return IEEE80211_REASON_IE_INVALID;
- }
- if (w & RSN_ASE_8021X_UNSPEC)
- rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC;
- else
- rsn->rsn_keymgmt = RSN_ASE_8021X_PSK;
-
- /* optional RSN capabilities */
- if (len > 2)
- rsn->rsn_caps = LE_READ_2(frm);
- /* XXXPMKID */
-
- return 0;
-}
-
-static int
-ieee80211_parse_wmeparams(struct ieee80211com *ic, uint8_t *frm,
- const struct ieee80211_frame *wh)
-{
-#define MS(_v, _f) (((_v) & _f) >> _f##_S)
- struct ieee80211_wme_state *wme = &ic->ic_wme;
- u_int len = frm[1], qosinfo;
- int i;
-
- if (len < sizeof(struct ieee80211_wme_param)-2) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME,
- wh, "WME", "too short, len %u", len);
- return -1;
- }
- qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)];
- qosinfo &= WME_QOSINFO_COUNT;
- /* XXX do proper check for wraparound */
- if (qosinfo == wme->wme_wmeChanParams.cap_info)
- return 0;
- frm += __offsetof(struct ieee80211_wme_param, params_acParams);
- for (i = 0; i < WME_NUM_AC; i++) {
- struct wmeParams *wmep =
- &wme->wme_wmeChanParams.cap_wmeParams[i];
- /* NB: ACI not used */
- wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM);
- wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN);
- wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN);
- wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX);
- wmep->wmep_txopLimit = LE_READ_2(frm+2);
- frm += 4;
- }
- wme->wme_wmeChanParams.cap_info = qosinfo;
- return 1;
-#undef MS
-}
-
-static int
-ieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm,
- const struct ieee80211_frame *wh)
-{
- struct ieee80211com *ic = ni->ni_ic;
- const struct ieee80211_ath_ie *ath;
- u_int len = frm[1];
- int capschanged;
- uint16_t defkeyix;
-
- if (len < sizeof(struct ieee80211_ath_ie)-2) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_SUPERG,
- wh, "Atheros", "too short, len %u", len);
- return -1;
- }
- ath = (const struct ieee80211_ath_ie *)frm;
- capschanged = (ni->ni_ath_flags != ath->ath_capability);
- defkeyix = LE_READ_2(ath->ath_defkeyix);
- if (capschanged || defkeyix != ni->ni_ath_defkeyix) {
- ni->ni_ath_flags = ath->ath_capability;
- ni->ni_ath_defkeyix = defkeyix;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
- "[%s] ath ie change: new caps 0x%x defkeyix 0x%x\n",
- ether_sprintf(ni->ni_macaddr),
- ni->ni_ath_flags, ni->ni_ath_defkeyix);
- }
- if (IEEE80211_ATH_CAP(ic, ni, ATHEROS_CAP_TURBO_PRIME)) {
- uint16_t curflags, newflags;
-
- /*
- * Check for turbo mode switch. Calculate flags
- * for the new mode and effect the switch.
- */
- newflags = curflags = ic->ic_bsschan->ic_flags;
- /* NB: BOOST is not in ic_flags, so get it from the ie */
- if (ath->ath_capability & ATHEROS_CAP_BOOST)
- newflags |= IEEE80211_CHAN_TURBO;
- else
- newflags &= ~IEEE80211_CHAN_TURBO;
- if (newflags != curflags)
- ieee80211_dturbo_switch(ic, newflags);
- }
- return capschanged;
-}
-
void
-ieee80211_saveath(struct ieee80211_node *ni, uint8_t *ie)
+ieee80211_parse_ath(struct ieee80211_node *ni, uint8_t *ie)
{
const struct ieee80211_ath_ie *ath =
(const struct ieee80211_ath_ie *) ie;
ni->ni_ath_flags = ath->ath_capability;
ni->ni_ath_defkeyix = LE_READ_2(&ath->ath_defkeyix);
- ieee80211_saveie(&ni->ni_ath_ie, ie);
-}
-
-void
-ieee80211_saveie(uint8_t **iep, const uint8_t *ie)
-{
- u_int ielen = ie[1]+2;
- /*
- * Record information element for later use.
- */
- if (*iep == NULL || (*iep)[1] != ie[1]) {
- if (*iep != NULL)
- FREE(*iep, M_80211_NODE);
- MALLOC(*iep, void*, ielen, M_80211_NODE, M_NOWAIT);
- }
- if (*iep != NULL)
- memcpy(*iep, ie, ielen);
- /* XXX note failure */
}
-/* XXX find a better place for definition */
-struct l2_update_frame {
- struct ether_header eh;
- uint8_t dsap;
- uint8_t ssap;
- uint8_t control;
- uint8_t xid[3];
-} __packed;
-
/*
- * Deliver a TGf L2UF frame on behalf of a station.
- * This primes any bridge when the station is roaming
- * between ap's on the same wired network.
+ * Parse a Beacon or ProbeResponse frame and return the
+ * useful information in an ieee80211_scanparams structure.
+ * Status is set to 0 if no problems were found; otherwise
+ * a bitmask of IEEE80211_BPARSE_* items is returned that
+ * describes the problems detected.
*/
-static void
-ieee80211_deliver_l2uf(struct ieee80211_node *ni)
-{
- struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
- struct mbuf *m;
- struct l2_update_frame *l2uf;
- struct ether_header *eh;
-
- m = m_gethdr(M_NOWAIT, MT_DATA);
- if (m == NULL) {
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
- "%s", "no mbuf for l2uf frame");
- ic->ic_stats.is_rx_nobuf++; /* XXX not right */
- return;
- }
- l2uf = mtod(m, struct l2_update_frame *);
- eh = &l2uf->eh;
- /* dst: Broadcast address */
- IEEE80211_ADDR_COPY(eh->ether_dhost, ifp->if_broadcastaddr);
- /* src: associated STA */
- IEEE80211_ADDR_COPY(eh->ether_shost, ni->ni_macaddr);
- eh->ether_type = htons(sizeof(*l2uf) - sizeof(*eh));
-
- l2uf->dsap = 0;
- l2uf->ssap = 0;
- l2uf->control = 0xf5;
- l2uf->xid[0] = 0x81;
- l2uf->xid[1] = 0x80;
- l2uf->xid[2] = 0x00;
-
- m->m_pkthdr.len = m->m_len = sizeof(*l2uf);
- ieee80211_deliver_data(ic, ni, m);
-}
-
-static __inline int
-contbgscan(struct ieee80211com *ic)
-{
- return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) &&
- time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle));
-}
-
-static __inline int
-startbgscan(struct ieee80211com *ic)
-{
- return ((ic->ic_flags & IEEE80211_F_BGSCAN) &&
- !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
- time_after(ticks, ic->ic_lastscan + ic->ic_bgscanintvl) &&
- time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle));
-}
-
-static void
-ratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
- int reassoc, int resp, const char *tag, int rate)
-{
- struct ieee80211com *ic = ni->ni_ic;
-
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "[%s] deny %s request, %s rate set mismatch, rate 0x%x\n",
- ether_sprintf(wh->i_addr2),
- reassoc ? "reassoc" : "assoc", tag, rate);
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE);
- ieee80211_node_leave(ic, ni);
- ic->ic_stats.is_rx_assoc_norate++;
-}
-
-static void
-capinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
- int reassoc, int resp, const char *tag, int capinfo)
-{
- struct ieee80211com *ic = ni->ni_ic;
-
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "[%s] deny %s request, %s mismatch 0x%x\n",
- ether_sprintf(wh->i_addr2),
- reassoc ? "reassoc" : "assoc", tag, capinfo);
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_CAPINFO);
- ieee80211_node_leave(ic, ni);
- ic->ic_stats.is_rx_assoc_capmismatch++;
-}
-
-static void
-htcapmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
- int reassoc, int resp)
+int
+ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m,
+ struct ieee80211_scanparams *scan)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
-
- IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_ANY, wh->i_addr2,
- "deny %s request, %s missing HT ie", reassoc ? "reassoc" : "assoc");
- /* XXX no better code */
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_OTHER);
- ieee80211_node_leave(ic, ni);
-}
-
-void
-ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
- struct ieee80211_node *ni,
- int subtype, int rssi, int noise, uint32_t rstamp)
-{
-#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
-#define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
struct ieee80211_frame *wh;
uint8_t *frm, *efrm;
- uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap, *htinfo;
- int reassoc, resp, allocbs;
- uint8_t rate;
- wh = mtod(m0, struct ieee80211_frame *);
+ wh = mtod(m, struct ieee80211_frame *);
frm = (uint8_t *)&wh[1];
- efrm = mtod(m0, uint8_t *) + m0->m_len;
- switch (subtype) {
- case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
- case IEEE80211_FC0_SUBTYPE_BEACON: {
- struct ieee80211_scanparams scan;
-
- /*
- * We process beacon/probe response frames:
- * o when scanning, or
- * o station mode when associated (to collect state
- * updates such as 802.11g slot time), or
- * o adhoc mode (to discover neighbors)
- * Frames otherwise received are discarded.
- */
- if (!((ic->ic_flags & IEEE80211_F_SCAN) ||
- (ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd) ||
- ic->ic_opmode == IEEE80211_M_IBSS)) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
- /*
- * beacon/probe response frame format
- * [8] time stamp
- * [2] beacon interval
- * [2] capability information
- * [tlv] ssid
- * [tlv] supported rates
- * [tlv] country information
- * [tlv] parameter set (FH/DS)
- * [tlv] erp information
- * [tlv] extended supported rates
- * [tlv] WME
- * [tlv] WPA or RSN
- * [tlv] HT capabilities
- * [tlv] HT information
- * [tlv] Atheros capabilities
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, 12, return);
- memset(&scan, 0, sizeof(scan));
- scan.tstamp = frm; frm += 8;
- scan.bintval = le16toh(*(uint16_t *)frm); frm += 2;
- scan.capinfo = le16toh(*(uint16_t *)frm); frm += 2;
- scan.bchan = IEEE80211_CHAN2IEEE(ic->ic_curchan);
- scan.curchan = ic->ic_curchan;
-
- while (efrm - frm > 1) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
- switch (*frm) {
- case IEEE80211_ELEMID_SSID:
- scan.ssid = frm;
- break;
- case IEEE80211_ELEMID_RATES:
- scan.rates = frm;
- break;
- case IEEE80211_ELEMID_COUNTRY:
- scan.country = frm;
- break;
- case IEEE80211_ELEMID_FHPARMS:
- if (ic->ic_phytype == IEEE80211_T_FH) {
- scan.fhdwell = LE_READ_2(&frm[2]);
- scan.bchan = IEEE80211_FH_CHAN(frm[4], frm[5]);
- scan.fhindex = frm[6];
- }
- break;
- case IEEE80211_ELEMID_DSPARMS:
- /*
- * XXX hack this since depending on phytype
- * is problematic for multi-mode devices.
- */
- if (ic->ic_phytype != IEEE80211_T_FH)
- scan.bchan = frm[2];
- break;
- case IEEE80211_ELEMID_TIM:
- /* XXX ATIM? */
- scan.tim = frm;
- scan.timoff = frm - mtod(m0, uint8_t *);
- break;
- case IEEE80211_ELEMID_IBSSPARMS:
- break;
- case IEEE80211_ELEMID_XRATES:
- scan.xrates = frm;
- break;
- case IEEE80211_ELEMID_ERP:
- if (frm[1] != 1) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID, wh, "ERP",
- "bad len %u", frm[1]);
- ic->ic_stats.is_rx_elem_toobig++;
- break;
- }
- scan.erp = frm[2];
- break;
- case IEEE80211_ELEMID_HTCAP:
- scan.htcap = frm;
- break;
- case IEEE80211_ELEMID_RSN:
- scan.rsn = frm;
- break;
- case IEEE80211_ELEMID_HTINFO:
- scan.htinfo = frm;
- break;
- case IEEE80211_ELEMID_VENDOR:
- if (iswpaoui(frm))
- scan.wpa = frm;
- else if (iswmeparam(frm) || iswmeinfo(frm))
- scan.wme = frm;
- else if (isatherosoui(frm))
- scan.ath = frm;
- else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
- /*
- * Accept pre-draft HT ie's if the
- * standard ones have not been seen.
- */
- if (ishtcapoui(frm)) {
- if (scan.htcap == NULL)
- scan.htcap = frm;
- } else if (ishtinfooui(frm)) {
- if (scan.htinfo == NULL)
- scan.htcap = frm;
- }
- }
- break;
- default:
- IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID,
- wh, "unhandled",
- "id %u, len %u", *frm, frm[1]);
- ic->ic_stats.is_rx_elem_unknown++;
- break;
- }
- frm += frm[1] + 2;
- }
- IEEE80211_VERIFY_ELEMENT(scan.rates, IEEE80211_RATE_MAXSIZE);
- if (scan.xrates != NULL)
- IEEE80211_VERIFY_ELEMENT(scan.xrates,
- IEEE80211_RATE_MAXSIZE - scan.rates[1]);
- IEEE80211_VERIFY_ELEMENT(scan.ssid, IEEE80211_NWID_LEN);
-#if IEEE80211_CHAN_MAX < 255
- if (scan.chan > IEEE80211_CHAN_MAX) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "invalid channel %u", scan.chan);
- ic->ic_stats.is_rx_badchan++;
- return;
- }
-#endif
- if (IEEE80211_CHAN2IEEE(scan.curchan) != scan.bchan &&
- ic->ic_phytype != IEEE80211_T_FH) {
- /*
- * Frame was received on a channel different from the
- * one indicated in the DS params element id;
- * silently discard it.
- *
- * NB: this can happen due to signal leakage.
- * But we should take it for FH phy because
- * the rssi value should be correct even for
- * different hop pattern in FH.
- */
- IEEE80211_DISCARD(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "for off-channel %u",
- IEEE80211_CHAN2IEEE(scan.curchan));
- ic->ic_stats.is_rx_chanmismatch++;
- return;
- }
- if (!(IEEE80211_BINTVAL_MIN <= scan.bintval &&
- scan.bintval <= IEEE80211_BINTVAL_MAX)) {
- IEEE80211_DISCARD(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "bogus beacon interval", scan.bintval);
- ic->ic_stats.is_rx_badbintval++;
- return;
- }
- /*
- * Process HT ie's. This is complicated by our
- * accepting both the standard ie's and the pre-draft
- * vendor OUI ie's that some vendors still use/require.
- */
- if (scan.htcap != NULL) {
- IEEE80211_VERIFY_LENGTH(scan.htcap[1],
- scan.htcap[0] == IEEE80211_ELEMID_VENDOR ?
- 4 + sizeof(struct ieee80211_ie_htcap)-2 :
- sizeof(struct ieee80211_ie_htcap)-2,
- scan.htcap = NULL);
- }
- if (scan.htinfo != NULL) {
- IEEE80211_VERIFY_LENGTH(scan.htinfo[1],
- scan.htinfo[0] == IEEE80211_ELEMID_VENDOR ?
- 4 + sizeof(struct ieee80211_ie_htinfo)-2 :
- sizeof(struct ieee80211_ie_htinfo)-2,
- scan.htinfo = NULL);
- }
-
- /*
- * Count frame now that we know it's to be processed.
- */
- if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
- ic->ic_stats.is_rx_beacon++; /* XXX remove */
- IEEE80211_NODE_STAT(ni, rx_beacons);
- } else
- IEEE80211_NODE_STAT(ni, rx_proberesp);
-
- /*
- * When operating in station mode, check for state updates.
- * Be careful to ignore beacons received while doing a
- * background scan. We consider only 11g/WMM stuff right now.
- */
- if (ic->ic_opmode == IEEE80211_M_STA &&
- ni->ni_associd != 0 &&
- ((ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
- IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) {
- /* record tsf of last beacon */
- memcpy(ni->ni_tstamp.data, scan.tstamp,
- sizeof(ni->ni_tstamp));
- /* count beacon frame for s/w bmiss handling */
- ic->ic_swbmiss_count++;
- ic->ic_bmiss_count = 0;
- if (ni->ni_erp != scan.erp) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] erp change: was 0x%x, now 0x%x\n",
- ether_sprintf(wh->i_addr2),
- ni->ni_erp, scan.erp);
- if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
- (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
- ic->ic_flags |= IEEE80211_F_USEPROT;
- else
- ic->ic_flags &= ~IEEE80211_F_USEPROT;
- ni->ni_erp = scan.erp;
- /* XXX statistic */
- }
- if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] capabilities change: before 0x%x,"
- " now 0x%x\n",
- ether_sprintf(wh->i_addr2),
- ni->ni_capinfo, scan.capinfo);
- /*
- * NB: we assume short preamble doesn't
- * change dynamically
- */
- ieee80211_set_shortslottime(ic,
- IEEE80211_IS_CHAN_A(ic->ic_bsschan) ||
- (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
- ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME)
- | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME);
- /* XXX statistic */
- }
- if (scan.wme != NULL &&
- (ni->ni_flags & IEEE80211_NODE_QOS) &&
- ieee80211_parse_wmeparams(ic, scan.wme, wh) > 0)
- ieee80211_wme_updateparams(ic);
- if (scan.ath != NULL)
- ieee80211_parse_athparams(ni, scan.ath, wh);
- if (scan.htcap != NULL)
- ieee80211_parse_htcap(ni, scan.htcap);
- if (scan.htinfo != NULL) {
- ieee80211_parse_htinfo(ni, scan.htinfo);
- if (ni->ni_chan != ic->ic_bsschan) {
- /*
- * Channel has been adjusted based on
- * negotiated HT parameters; force the
- * channel state to follow.
- */
- ieee80211_setbsschan(ic, ni->ni_chan);
- }
- }
- if (scan.tim != NULL) {
- struct ieee80211_tim_ie *tim =
- (struct ieee80211_tim_ie *) scan.tim;
-#if 0
- int aid = IEEE80211_AID(ni->ni_associd);
- int ix = aid / NBBY;
- int min = tim->tim_bitctl &~ 1;
- int max = tim->tim_len + min - 4;
- if ((tim->tim_bitctl&1) ||
- (min <= ix && ix <= max &&
- isset(tim->tim_bitmap - min, aid))) {
- /*
- * XXX Do not let bg scan kick off
- * we are expecting data.
- */
- ic->ic_lastdata = ticks;
- ieee80211_sta_pwrsave(ic, 0);
- }
-#endif
- ni->ni_dtim_count = tim->tim_count;
- ni->ni_dtim_period = tim->tim_period;
+ efrm = mtod(m, uint8_t *) + m->m_len;
+ scan->status = 0;
+ /*
+ * beacon/probe response frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] capability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] country information
+ * [tlv] parameter set (FH/DS)
+ * [tlv] erp information
+ * [tlv] extended supported rates
+ * [tlv] WME
+ * [tlv] WPA or RSN
+ * [tlv] HT capabilities
+ * [tlv] HT information
+ * [tlv] Atheros capabilities
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 12,
+ return (scan->status = IEEE80211_BPARSE_BADIELEN));
+ memset(scan, 0, sizeof(*scan));
+ scan->tstamp = frm; frm += 8;
+ scan->bintval = le16toh(*(uint16_t *)frm); frm += 2;
+ scan->capinfo = le16toh(*(uint16_t *)frm); frm += 2;
+ scan->bchan = ieee80211_chan2ieee(ic, ic->ic_curchan);
+ scan->chan = scan->bchan;
+ scan->ies = frm;
+ scan->ies_len = efrm - frm;
+
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2,
+ return (scan->status = IEEE80211_BPARSE_BADIELEN));
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ scan->ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ scan->rates = frm;
+ break;
+ case IEEE80211_ELEMID_COUNTRY:
+ scan->country = frm;
+ break;
+ case IEEE80211_ELEMID_FHPARMS:
+ if (ic->ic_phytype == IEEE80211_T_FH) {
+ scan->fhdwell = LE_READ_2(&frm[2]);
+ scan->chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
+ scan->fhindex = frm[6];
}
+ break;
+ case IEEE80211_ELEMID_DSPARMS:
/*
- * If scanning, pass the info to the scan module.
- * Otherwise, check if it's the right time to do
- * a background scan. Background scanning must
- * be enabled and we must not be operating in the
- * turbo phase of dynamic turbo mode. Then,
- * it's been a while since the last background
- * scan and if no data frames have come through
- * recently, kick off a scan. Note that this
- * is the mechanism by which a background scan
- * is started _and_ continued each time we
- * return on-channel to receive a beacon from
- * our ap.
+ * XXX hack this since depending on phytype
+ * is problematic for multi-mode devices.
*/
- if (ic->ic_flags & IEEE80211_F_SCAN) {
- ieee80211_add_scan(ic, &scan, wh,
- subtype, rssi, noise, rstamp);
- } else if (contbgscan(ic)) {
- ieee80211_bg_scan(ic);
- } else if (startbgscan(ic)) {
-#if 0
- /* wakeup if we are sleeing */
- ieee80211_set_pwrsave(ic, 0);
-#endif
- ieee80211_bg_scan(ic);
- }
- return;
- }
- /*
- * If scanning, just pass information to the scan module.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN) {
- if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
- /*
- * Actively scanning a channel marked passive;
- * send a probe request now that we know there
- * is 802.11 traffic present.
- *
- * XXX check if the beacon we recv'd gives
- * us what we need and suppress the probe req
- */
- ieee80211_probe_curchan(ic, 1);
- ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
- }
- ieee80211_add_scan(ic, &scan, wh,
- subtype, rssi, noise, rstamp);
- return;
- }
- if (scan.capinfo & IEEE80211_CAPINFO_IBSS) {
- if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
- /*
- * Create a new entry in the neighbor table.
- */
- ni = ieee80211_add_neighbor(ic, wh, &scan);
- } else if (ni->ni_capinfo == 0) {
- /*
- * Update faked node created on transmit.
- * Note this also updates the tsf.
- */
- ieee80211_init_neighbor(ni, wh, &scan);
- } else {
- /*
- * Record tsf for potential resync.
- */
- memcpy(ni->ni_tstamp.data, scan.tstamp,
- sizeof(ni->ni_tstamp));
- }
- if (ni != NULL) {
- ni->ni_rssi = rssi;
- ni->ni_noise = noise;
- ni->ni_rstamp = rstamp;
- }
- }
- break;
- }
-
- case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
- if (ic->ic_opmode == IEEE80211_M_STA ||
- ic->ic_state != IEEE80211_S_RUN) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
- if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
- /* frame must be directed */
- ic->ic_stats.is_rx_mgtdiscard++; /* XXX stat */
- return;
- }
-
- /*
- * prreq frame format
- * [tlv] ssid
- * [tlv] supported rates
- * [tlv] extended supported rates
- * [tlv] Atheros capabilities
- */
- ssid = rates = xrates = ath = NULL;
- while (efrm - frm > 1) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
- switch (*frm) {
- case IEEE80211_ELEMID_SSID:
- ssid = frm;
- break;
- case IEEE80211_ELEMID_RATES:
- rates = frm;
- break;
- case IEEE80211_ELEMID_XRATES:
- xrates = frm;
- break;
- case IEEE80211_ELEMID_VENDOR:
- if (isatherosoui(frm))
- ath = frm;
+ if (ic->ic_phytype != IEEE80211_T_FH)
+ scan->chan = frm[2];
+ break;
+ case IEEE80211_ELEMID_TIM:
+ /* XXX ATIM? */
+ scan->tim = frm;
+ scan->timoff = frm - mtod(m, uint8_t *);
+ break;
+ case IEEE80211_ELEMID_IBSSPARMS:
+ case IEEE80211_ELEMID_CFPARMS:
+ /* NB: avoid debugging complaints */
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ scan->xrates = frm;
+ break;
+ case IEEE80211_ELEMID_ERP:
+ if (frm[1] != 1) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID, wh, "ERP",
+ "bad len %u", frm[1]);
+ vap->iv_stats.is_rx_elem_toobig++;
break;
}
- frm += frm[1] + 2;
- }
- IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
- if (xrates != NULL)
- IEEE80211_VERIFY_ELEMENT(xrates,
- IEEE80211_RATE_MAXSIZE - rates[1]);
- IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
- IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
- if ((ic->ic_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "%s", "no ssid with ssid suppression enabled");
- ic->ic_stats.is_rx_ssidmismatch++; /*XXX*/
- return;
- }
-
- allocbs = 0;
- if (ni == ic->ic_bss) {
- if (ic->ic_opmode != IEEE80211_M_IBSS) {
- ni = ieee80211_tmp_node(ic, wh->i_addr2);
- allocbs = 1;
- } else if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
+ scan->erp = frm[2] | 0x100;
+ break;
+ case IEEE80211_ELEMID_HTCAP:
+ scan->htcap = frm;
+ break;
+ case IEEE80211_ELEMID_RSN:
+ scan->rsn = frm;
+ break;
+ case IEEE80211_ELEMID_HTINFO:
+ scan->htinfo = frm;
+ break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswpaoui(frm))
+ scan->wpa = frm;
+ else if (iswmeparam(frm) || iswmeinfo(frm))
+ scan->wme = frm;
+ else if (isatherosoui(frm))
+ scan->ath = frm;
+ else if (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
/*
- * XXX Cannot tell if the sender is operating
- * in ibss mode. But we need a new node to
- * send the response so blindly add them to the
- * neighbor table.
+ * Accept pre-draft HT ie's if the
+ * standard ones have not been seen.
*/
- ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
- wh->i_addr2);
- }
- if (ni == NULL)
- return;
- }
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] recv probe req\n", ether_sprintf(wh->i_addr2));
- ni->ni_rssi = rssi;
- ni->ni_rstamp = rstamp;
- rate = ieee80211_setup_rates(ni, rates, xrates,
- IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE
- | IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
- if (rate & IEEE80211_RATE_BASIC) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_XRATE,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "%s", "recv'd rate set invalid");
- } else {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
- }
- if (allocbs) {
- /*
- * Temporary node created just to send a
- * response, reclaim immediately.
- */
- ieee80211_free_node(ni);
- } else if (ath != NULL)
- ieee80211_saveath(ni, ath);
- break;
-
- case IEEE80211_FC0_SUBTYPE_AUTH: {
- uint16_t algo, seq, status;
- /*
- * auth frame format
- * [2] algorithm
- * [2] sequence
- * [2] status
- * [tlv*] challenge
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
- algo = le16toh(*(uint16_t *)frm);
- seq = le16toh(*(uint16_t *)(frm + 2));
- status = le16toh(*(uint16_t *)(frm + 4));
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
- "[%s] recv auth frame with algorithm %d seq %d\n",
- ether_sprintf(wh->i_addr2), algo, seq);
- /*
- * Consult the ACL policy module if setup.
- */
- if (ic->ic_acl != NULL &&
- !ic->ic_acl->iac_check(ic, wh->i_addr2)) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ACL,
- wh, "auth", "%s", "disallowed by ACL");
- ic->ic_stats.is_rx_acl++;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH,
- (seq+1) | (IEEE80211_STATUS_UNSPECIFIED<<16));
- }
- return;
- }
- if (ic->ic_flags & IEEE80211_F_COUNTERM) {
- IEEE80211_DISCARD(ic,
- IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
- wh, "auth", "%s", "TKIP countermeasures enabled");
- ic->ic_stats.is_rx_auth_countermeasures++;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH,
- IEEE80211_REASON_MIC_FAILURE);
- }
- return;
- }
- if (algo == IEEE80211_AUTH_ALG_SHARED)
- ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi,
- noise, rstamp, seq, status);
- else if (algo == IEEE80211_AUTH_ALG_OPEN)
- ieee80211_auth_open(ic, wh, ni, rssi, noise, rstamp,
- seq, status);
- else {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
- wh, "auth", "unsupported alg %d", algo);
- ic->ic_stats.is_rx_auth_unsupported++;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- /* XXX not right */
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH,
- (seq+1) | (IEEE80211_STATUS_ALG<<16));
- }
- return;
- }
- break;
- }
-
- case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
- case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: {
- uint16_t capinfo, lintval;
- struct ieee80211_rsnparms rsnparms;
- uint8_t reason;
- int badwparsn;
-
- if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
- ic->ic_state != IEEE80211_S_RUN) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
-
- if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
- reassoc = 1;
- resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
- } else {
- reassoc = 0;
- resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
- }
- /*
- * asreq frame format
- * [2] capability information
- * [2] listen interval
- * [6*] current AP address (reassoc only)
- * [tlv] ssid
- * [tlv] supported rates
- * [tlv] extended supported rates
- * [tlv] WPA or RSN
- * [tlv] HT capabilities
- * [tlv] Atheros capabilities
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4), return);
- if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "%s", "wrong bssid");
- ic->ic_stats.is_rx_assoc_bss++;
- return;
- }
- capinfo = le16toh(*(uint16_t *)frm); frm += 2;
- lintval = le16toh(*(uint16_t *)frm); frm += 2;
- if (reassoc)
- frm += 6; /* ignore current AP info */
- ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL;
- while (efrm - frm > 1) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
- switch (*frm) {
- case IEEE80211_ELEMID_SSID:
- ssid = frm;
- break;
- case IEEE80211_ELEMID_RATES:
- rates = frm;
- break;
- case IEEE80211_ELEMID_XRATES:
- xrates = frm;
- break;
- /* XXX verify only one of RSN and WPA ie's? */
- case IEEE80211_ELEMID_RSN:
- rsn = frm;
- break;
- case IEEE80211_ELEMID_HTCAP:
- htcap = frm;
- break;
- case IEEE80211_ELEMID_VENDOR:
- if (iswpaoui(frm))
- wpa = frm;
- else if (iswmeinfo(frm))
- wme = frm;
- else if (isatherosoui(frm))
- ath = frm;
- else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
- if (ishtcapoui(frm) && htcap == NULL)
- htcap = frm;
+ if (ishtcapoui(frm)) {
+ if (scan->htcap == NULL)
+ scan->htcap = frm;
+ } else if (ishtinfooui(frm)) {
+ if (scan->htinfo == NULL)
+ scan->htcap = frm;
}
- break;
}
- frm += frm[1] + 2;
- }
- IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
- if (xrates != NULL)
- IEEE80211_VERIFY_ELEMENT(xrates,
- IEEE80211_RATE_MAXSIZE - rates[1]);
- IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
- IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
- if (htcap != NULL) {
- IEEE80211_VERIFY_LENGTH(htcap[1],
- htcap[0] == IEEE80211_ELEMID_VENDOR ?
- 4 + sizeof(struct ieee80211_ie_htcap)-2 :
- sizeof(struct ieee80211_ie_htcap)-2,
- return); /* XXX just NULL out? */
- }
-
- if (ni == ic->ic_bss) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "[%s] deny %s request, sta not authenticated\n",
- ether_sprintf(wh->i_addr2),
- reassoc ? "reassoc" : "assoc");
- ieee80211_send_error(ic, ni, wh->i_addr2,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_ASSOC_NOT_AUTHED);
- ic->ic_stats.is_rx_assoc_notauth++;
- return;
- }
- /* assert right association security credentials */
- badwparsn = 0;
- switch (ic->ic_flags & IEEE80211_F_WPA) {
- case IEEE80211_F_WPA1:
- if (wpa == NULL)
- badwparsn = 1;
- break;
- case IEEE80211_F_WPA2:
- if (rsn == NULL)
- badwparsn = 1;
break;
- case IEEE80211_F_WPA1|IEEE80211_F_WPA2:
- if (wpa == NULL && rsn == NULL)
- badwparsn = 1;
+ default:
+ IEEE80211_DISCARD_IE(vap, IEEE80211_MSG_ELEMID,
+ wh, "unhandled",
+ "id %u, len %u", *frm, frm[1]);
+ vap->iv_stats.is_rx_elem_unknown++;
break;
}
- if (badwparsn) {
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
- "[%s] no WPA/RSN IE in association request\n",
- ether_sprintf(wh->i_addr2));
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_IE_INVALID);
- ieee80211_node_leave(ic, ni);
- ic->ic_stats.is_rx_assoc_badwpaie++;
- return;
- }
- if (wpa != NULL || rsn != NULL) {
- /*
- * Parse WPA/RSN information element. Note that
- * we initialize the param block from the node
- * state so that information in the IE overrides
- * our defaults. The resulting parameters are
- * installed below after the association is assured.
- */
- rsnparms = ni->ni_rsn;
- if (wpa != NULL)
- reason = ieee80211_parse_wpa(ic, wpa, &rsnparms, wh);
- else
- reason = ieee80211_parse_rsn(ic, rsn, &rsnparms, wh);
- if (reason != 0) {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
- ieee80211_node_leave(ic, ni);
- /* XXX distinguish WPA/RSN? */
- ic->ic_stats.is_rx_assoc_badwpaie++;
- return;
- }
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
- "[%s] %s ie: mc %u/%u uc %u/%u key %u caps 0x%x\n",
- ether_sprintf(wh->i_addr2),
- wpa != NULL ? "WPA" : "RSN",
- rsnparms.rsn_mcastcipher, rsnparms.rsn_mcastkeylen,
- rsnparms.rsn_ucastcipher, rsnparms.rsn_ucastkeylen,
- rsnparms.rsn_keymgmt, rsnparms.rsn_caps);
- }
- /* discard challenge after association */
- if (ni->ni_challenge != NULL) {
- FREE(ni->ni_challenge, M_80211_NODE);
- ni->ni_challenge = NULL;
- }
- /* NB: 802.11 spec says to ignore station's privacy bit */
- if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) {
- capinfomismatch(ni, wh, reassoc, resp,
- "capability", capinfo);
- return;
- }
- /*
- * Disallow re-associate w/ invalid slot time setting.
- */
- if (ni->ni_associd != 0 &&
- IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
- ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME)) {
- capinfomismatch(ni, wh, reassoc, resp,
- "slot time", capinfo);
- return;
- }
- rate = ieee80211_setup_rates(ni, rates, xrates,
- IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
- IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
- if (rate & IEEE80211_RATE_BASIC) {
- ratesetmismatch(ni, wh, reassoc, resp, "basic", rate);
- return;
- }
- /*
- * If constrained to 11g-only stations reject an
- * 11b-only station. We cheat a bit here by looking
- * at the max negotiated xmit rate and assuming anyone
- * with a best rate <24Mb/s is an 11b station.
- */
- if ((ic->ic_flags & IEEE80211_F_PUREG) && rate < 48) {
- ratesetmismatch(ni, wh, reassoc, resp, "11g", rate);
- return;
- }
- /* XXX enforce PUREN */
- /* 802.11n-specific rateset handling */
- if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) && htcap != NULL) {
- rate = ieee80211_setup_htrates(ni, htcap,
- IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO |
- IEEE80211_F_DOBRS);
- if (rate & IEEE80211_RATE_BASIC) {
- /* XXX 11n-specific stat */
- ratesetmismatch(ni, wh, reassoc, resp,
- "HT", rate);
- return;
- }
- ieee80211_ht_node_init(ni, htcap);
- } else if (ni->ni_flags & IEEE80211_NODE_HT)
- ieee80211_ht_node_cleanup(ni);
- /*
- * Allow AMPDU operation only with unencrypted traffic
- * or AES-CCM; the 11n spec only specifies these ciphers
- * so permitting any others is undefined and can lead
- * to interoperability problems.
- *
- * NB: We check for AES by looking at the GTK cipher
- * since the WPA/11i specs say the PTK cipher has
- * to be "as good or better".
- */
- if ((ni->ni_flags & IEEE80211_NODE_HT) &&
- (((ic->ic_flags & IEEE80211_F_WPA) &&
- rsnparms.rsn_mcastcipher != IEEE80211_CIPHER_AES_CCM) ||
- (ic->ic_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) {
- IEEE80211_NOTE(ic,
- IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
- "disallow HT use because WEP or TKIP requested, "
- "capinfo 0x%x mcastcipher %d", capinfo,
- rsnparms.rsn_mcastcipher);
- ieee80211_ht_node_cleanup(ni);
- ic->ic_stats.is_ht_assoc_downgrade++;
- }
- /*
- * If constrained to 11n-only stations reject legacy stations.
- */
- if ((ic->ic_flags_ext & IEEE80211_FEXT_PUREN) &&
- (ni->ni_flags & IEEE80211_NODE_HT) == 0) {
- htcapmismatch(ni, wh, reassoc, resp);
- ic->ic_stats.is_ht_assoc_nohtcap++;
- return;
- }
- ni->ni_rssi = rssi;
- ni->ni_noise = noise;
- ni->ni_rstamp = rstamp;
- ni->ni_intval = lintval;
- ni->ni_capinfo = capinfo;
- ni->ni_chan = ic->ic_bsschan;
- ni->ni_fhdwell = ic->ic_bss->ni_fhdwell;
- ni->ni_fhindex = ic->ic_bss->ni_fhindex;
- if (wpa != NULL) {
- /*
- * Record WPA parameters for station, mark
- * node as using WPA and record information element
- * for applications that require it.
- */
- ni->ni_rsn = rsnparms;
- ieee80211_saveie(&ni->ni_wpa_ie, wpa);
- } else if (ni->ni_wpa_ie != NULL) {
- /*
- * Flush any state from a previous association.
- */
- FREE(ni->ni_wpa_ie, M_80211_NODE);
- ni->ni_wpa_ie = NULL;
- }
- if (rsn != NULL) {
- /*
- * Record RSN parameters for station, mark
- * node as using WPA and record information element
- * for applications that require it.
- */
- ni->ni_rsn = rsnparms;
- ieee80211_saveie(&ni->ni_rsn_ie, rsn);
- } else if (ni->ni_rsn_ie != NULL) {
- /*
- * Flush any state from a previous association.
- */
- FREE(ni->ni_rsn_ie, M_80211_NODE);
- ni->ni_rsn_ie = NULL;
- }
- if (wme != NULL) {
- /*
- * Record WME parameters for station, mark node
- * as capable of QoS and record information
- * element for applications that require it.
- */
- ieee80211_saveie(&ni->ni_wme_ie, wme);
- ni->ni_flags |= IEEE80211_NODE_QOS;
- } else if (ni->ni_wme_ie != NULL) {
- /*
- * Flush any state from a previous association.
- */
- FREE(ni->ni_wme_ie, M_80211_NODE);
- ni->ni_wme_ie = NULL;
- ni->ni_flags &= ~IEEE80211_NODE_QOS;
- }
- if (ath != NULL) {
- /*
- * Record ATH parameters for station, mark
- * node with appropriate capabilities, and
- * record the information element for
- * applications that require it.
- */
- ieee80211_saveath(ni, ath);
- } else if (ni->ni_ath_ie != NULL) {
- /*
- * Flush any state from a previous association.
- */
- FREE(ni->ni_ath_ie, M_80211_NODE);
- ni->ni_ath_ie = NULL;
- ni->ni_ath_flags = 0;
- }
- ieee80211_node_join(ic, ni, resp);
- ieee80211_deliver_l2uf(ni);
- break;
+ frm += frm[1] + 2;
}
-
- case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
- case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
- uint16_t capinfo, associd;
- uint16_t status;
-
- if (ic->ic_opmode != IEEE80211_M_STA ||
- ic->ic_state != IEEE80211_S_ASSOC) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
-
- /*
- * asresp frame format
- * [2] capability information
- * [2] status
- * [2] association ID
- * [tlv] supported rates
- * [tlv] extended supported rates
- * [tlv] WME
- * [tlv] HT capabilities
- * [tlv] HT info
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
- ni = ic->ic_bss;
- capinfo = le16toh(*(uint16_t *)frm);
- frm += 2;
- status = le16toh(*(uint16_t *)frm);
- frm += 2;
- if (status != 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] %sassoc failed (reason %d)\n",
- ether_sprintf(wh->i_addr2),
- ISREASSOC(subtype) ? "re" : "", status);
- if (ni != ic->ic_bss) /* XXX never true? */
- ni->ni_fails++;
- ic->ic_stats.is_rx_auth_fail++; /* XXX */
- return;
- }
- associd = le16toh(*(uint16_t *)frm);
- frm += 2;
-
- rates = xrates = wme = htcap = htinfo = NULL;
- while (efrm - frm > 1) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
- switch (*frm) {
- case IEEE80211_ELEMID_RATES:
- rates = frm;
- break;
- case IEEE80211_ELEMID_XRATES:
- xrates = frm;
- break;
- case IEEE80211_ELEMID_HTCAP:
- htcap = frm;
- break;
- case IEEE80211_ELEMID_HTINFO:
- htinfo = frm;
- break;
- case IEEE80211_ELEMID_VENDOR:
- if (iswmeoui(frm))
- wme = frm;
- else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
- /*
- * Accept pre-draft HT ie's if the
- * standard ones have not been seen.
- */
- if (ishtcapoui(frm)) {
- if (htcap == NULL)
- htcap = frm;
- } else if (ishtinfooui(frm)) {
- if (htinfo == NULL)
- htcap = frm;
- }
- }
- /* XXX Atheros OUI support */
- break;
- }
- frm += frm[1] + 2;
- }
-
- IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
- if (xrates != NULL)
- IEEE80211_VERIFY_ELEMENT(xrates,
- IEEE80211_RATE_MAXSIZE - rates[1]);
- rate = ieee80211_setup_rates(ni, rates, xrates,
- IEEE80211_F_JOIN |
- IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
- IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
- if (rate & IEEE80211_RATE_BASIC) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] %sassoc failed (rate set mismatch)\n",
- ether_sprintf(wh->i_addr2),
- ISREASSOC(subtype) ? "re" : "");
- if (ni != ic->ic_bss) /* XXX never true? */
- ni->ni_fails++;
- ic->ic_stats.is_rx_assoc_norate++;
- ieee80211_new_state(ic, IEEE80211_S_SCAN,
- IEEE80211_SCAN_FAIL_STATUS);
- return;
- }
-
- ni->ni_capinfo = capinfo;
- ni->ni_associd = associd;
- if (wme != NULL &&
- ieee80211_parse_wmeparams(ic, wme, wh) >= 0) {
- ni->ni_flags |= IEEE80211_NODE_QOS;
- ieee80211_wme_updateparams(ic);
- } else
- ni->ni_flags &= ~IEEE80211_NODE_QOS;
+ IEEE80211_VERIFY_ELEMENT(scan->rates, IEEE80211_RATE_MAXSIZE,
+ scan->status |= IEEE80211_BPARSE_RATES_INVALID);
+ if (scan->rates != NULL && scan->xrates != NULL) {
/*
- * Setup HT state according to the negotiation.
+ * NB: don't process XRATES if RATES is missing. This
+ * avoids a potential null ptr deref and should be ok
+ * as the return code will already note RATES is missing
+ * (so callers shouldn't otherwise process the frame).
*/
- if ((ic->ic_htcaps & IEEE80211_HTC_HT) &&
- htcap != NULL && htinfo != NULL) {
- ieee80211_ht_node_init(ni, htcap);
- ieee80211_parse_htinfo(ni, htinfo);
- ieee80211_setup_htrates(ni,
- htcap, IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
- ieee80211_setup_basic_htrates(ni, htinfo);
- if (ni->ni_chan != ic->ic_bsschan) {
- /*
- * Channel has been adjusted based on
- * negotiated HT parameters; force the
- * channel state to follow.
- */
- ieee80211_setbsschan(ic, ni->ni_chan);
- }
- }
- /*
- * Configure state now that we are associated.
- *
- * XXX may need different/additional driver callbacks?
- */
- if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
- (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
- ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
- ic->ic_flags &= ~IEEE80211_F_USEBARKER;
- } else {
- ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
- ic->ic_flags |= IEEE80211_F_USEBARKER;
- }
- ieee80211_set_shortslottime(ic,
- IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
- (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+ IEEE80211_VERIFY_ELEMENT(scan->xrates,
+ IEEE80211_RATE_MAXSIZE - scan->rates[1],
+ scan->status |= IEEE80211_BPARSE_XRATES_INVALID);
+ }
+ IEEE80211_VERIFY_ELEMENT(scan->ssid, IEEE80211_NWID_LEN,
+ scan->status |= IEEE80211_BPARSE_SSID_INVALID);
+#if IEEE80211_CHAN_MAX < 255
+ if (scan->chan > IEEE80211_CHAN_MAX) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID,
+ wh, NULL, "invalid channel %u", scan->chan);
+ vap->iv_stats.is_rx_badchan++;
+ scan->status |= IEEE80211_BPARSE_CHAN_INVALID;
+ }
+#endif
+ if (scan->chan != scan->bchan && ic->ic_phytype != IEEE80211_T_FH) {
/*
- * Honor ERP protection.
+ * Frame was received on a channel different from the
+ * one indicated in the DS params element id;
+ * silently discard it.
*
- * NB: ni_erp should zero for non-11g operation.
- */
- if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
- (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
- ic->ic_flags |= IEEE80211_F_USEPROT;
- else
- ic->ic_flags &= ~IEEE80211_F_USEPROT;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] %sassoc success: %s preamble, %s slot time%s%s%s%s\n",
- ether_sprintf(wh->i_addr2),
- ISREASSOC(subtype) ? "re" : "",
- ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
- ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
- ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
- ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
- ni->ni_flags & IEEE80211_NODE_HT ?
- (ni->ni_chw == 20 ? ", HT20" : ", HT40") : "",
- ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ?
- ", fast-frames" : "",
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ?
- ", turbo" : ""
- );
- ieee80211_new_state(ic, IEEE80211_S_RUN, subtype);
- break;
+ * NB: this can happen due to signal leakage.
+ * But we should take it for FH phy because
+ * the rssi value should be correct even for
+ * different hop pattern in FH.
+ */
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
+ wh, NULL, "for off-channel %u", scan->chan);
+ vap->iv_stats.is_rx_chanmismatch++;
+ scan->status |= IEEE80211_BPARSE_OFFCHAN;
+ }
+ if (!(IEEE80211_BINTVAL_MIN <= scan->bintval &&
+ scan->bintval <= IEEE80211_BINTVAL_MAX)) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
+ wh, NULL, "bogus beacon interval", scan->bintval);
+ vap->iv_stats.is_rx_badbintval++;
+ scan->status |= IEEE80211_BPARSE_BINTVAL_INVALID;
+ }
+ if (scan->country != NULL) {
+ /*
+ * Validate we have at least enough data to extract
+ * the country code. Not sure if we should return an
+ * error instead of discarding the IE; consider this
+ * being lenient as we don't depend on the data for
+ * correct operation.
+ */
+ IEEE80211_VERIFY_LENGTH(scan->country[1], 3 * sizeof(uint8_t),
+ scan->country = NULL);
}
-
- case IEEE80211_FC0_SUBTYPE_DEAUTH: {
- uint16_t reason;
-
- if (ic->ic_state == IEEE80211_S_SCAN) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
- /*
- * deauth frame format
- * [2] reason
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
- reason = le16toh(*(uint16_t *)frm);
- ic->ic_stats.is_rx_deauth++;
- IEEE80211_NODE_STAT(ni, rx_deauth);
-
- if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) {
- /* NB: can happen when in promiscuous mode */
- ic->ic_stats.is_rx_mgtdiscard++;
- break;
- }
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
- "[%s] recv deauthenticate (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), reason);
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- ieee80211_new_state(ic, IEEE80211_S_AUTH,
- (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH);
- break;
- case IEEE80211_M_HOSTAP:
- if (ni != ic->ic_bss)
- ieee80211_node_leave(ic, ni);
- break;
- default:
- ic->ic_stats.is_rx_mgtdiscard++;
- break;
- }
- break;
+ /*
+ * Process HT ie's. This is complicated by our
+ * accepting both the standard ie's and the pre-draft
+ * vendor OUI ie's that some vendors still use/require.
+ */
+ if (scan->htcap != NULL) {
+ IEEE80211_VERIFY_LENGTH(scan->htcap[1],
+ scan->htcap[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof(struct ieee80211_ie_htcap)-2 :
+ sizeof(struct ieee80211_ie_htcap)-2,
+ scan->htcap = NULL);
}
+ if (scan->htinfo != NULL) {
+ IEEE80211_VERIFY_LENGTH(scan->htinfo[1],
+ scan->htinfo[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof(struct ieee80211_ie_htinfo)-2 :
+ sizeof(struct ieee80211_ie_htinfo)-2,
+ scan->htinfo = NULL);
+ }
+ return scan->status;
+}
- case IEEE80211_FC0_SUBTYPE_DISASSOC: {
- uint16_t reason;
-
- if (ic->ic_state != IEEE80211_S_RUN &&
- ic->ic_state != IEEE80211_S_ASSOC &&
- ic->ic_state != IEEE80211_S_AUTH) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
- /*
- * disassoc frame format
- * [2] reason
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
- reason = le16toh(*(uint16_t *)frm);
- ic->ic_stats.is_rx_disassoc++;
- IEEE80211_NODE_STAT(ni, rx_disassoc);
+/*
+ * Parse an Action frame. Return 0 on success, non-zero on failure.
+ */
+int
+ieee80211_parse_action(struct ieee80211_node *ni, struct mbuf *m)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ const struct ieee80211_action *ia;
+ struct ieee80211_frame *wh;
+ uint8_t *frm, *efrm;
- if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) {
- /* NB: can happen when in promiscuous mode */
- ic->ic_stats.is_rx_mgtdiscard++;
- break;
- }
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] recv disassociate (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), reason);
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
+ /*
+ * action frame format:
+ * [1] category
+ * [1] action
+ * [tlv] parameters
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ frm = (u_int8_t *)&wh[1];
+ efrm = mtod(m, u_int8_t *) + m->m_len;
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action), return EINVAL);
+ ia = (const struct ieee80211_action *) frm;
+
+ vap->iv_stats.is_rx_action++;
+ IEEE80211_NODE_STAT(ni, rx_action);
+
+ /* verify frame payloads but defer processing */
+ /* XXX maybe push this to method */
+ switch (ia->ia_category) {
+ case IEEE80211_ACTION_CAT_BA:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_addbarequest),
+ return EINVAL);
break;
- case IEEE80211_M_HOSTAP:
- if (ni != ic->ic_bss)
- ieee80211_node_leave(ic, ni);
+ case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_addbaresponse),
+ return EINVAL);
break;
- default:
- ic->ic_stats.is_rx_mgtdiscard++;
+ case IEEE80211_ACTION_BA_DELBA:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_delba),
+ return EINVAL);
break;
}
break;
- }
-
- case IEEE80211_FC0_SUBTYPE_ACTION: {
- const struct ieee80211_action *ia;
-
- if (ic->ic_state != IEEE80211_S_RUN &&
- ic->ic_state != IEEE80211_S_ASSOC &&
- ic->ic_state != IEEE80211_S_AUTH) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
- /*
- * action frame format:
- * [1] category
- * [1] action
- * [tlv] parameters
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm,
- sizeof(struct ieee80211_action), return);
- ia = (const struct ieee80211_action *) frm;
-
- ic->ic_stats.is_rx_action++;
- IEEE80211_NODE_STAT(ni, rx_action);
-
- /* verify frame payloads but defer processing */
- /* XXX maybe push this to method */
- switch (ia->ia_category) {
- case IEEE80211_ACTION_CAT_BA:
- switch (ia->ia_action) {
- case IEEE80211_ACTION_BA_ADDBA_REQUEST:
- IEEE80211_VERIFY_LENGTH(efrm - frm,
- sizeof(struct ieee80211_action_ba_addbarequest),
- return);
- break;
- case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
- IEEE80211_VERIFY_LENGTH(efrm - frm,
- sizeof(struct ieee80211_action_ba_addbaresponse),
- return);
- break;
- case IEEE80211_ACTION_BA_DELBA:
- IEEE80211_VERIFY_LENGTH(efrm - frm,
- sizeof(struct ieee80211_action_ba_delba),
- return);
- break;
- }
+ case IEEE80211_ACTION_CAT_HT:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_HT_TXCHWIDTH:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ht_txchwidth),
+ return EINVAL);
break;
- case IEEE80211_ACTION_CAT_HT:
- switch (ia->ia_action) {
- case IEEE80211_ACTION_HT_TXCHWIDTH:
- IEEE80211_VERIFY_LENGTH(efrm - frm,
- sizeof(struct ieee80211_action_ht_txchwidth),
- return);
- break;
- }
+ case IEEE80211_ACTION_HT_MIMOPWRSAVE:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ht_mimopowersave),
+ return EINVAL);
break;
}
- ic->ic_recv_action(ni, frm, efrm);
break;
}
-
- default:
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
- wh, "mgt", "subtype 0x%x not handled", subtype);
- ic->ic_stats.is_rx_badsubtype++;
- break;
- }
-#undef ISREASSOC
-#undef ISPROBE
-}
-#undef IEEE80211_VERIFY_LENGTH
-#undef IEEE80211_VERIFY_ELEMENT
-
-/*
- * Process a received ps-poll frame.
- */
-static void
-ieee80211_recv_pspoll(struct ieee80211com *ic,
- struct ieee80211_node *ni, struct mbuf *m0)
-{
- struct ieee80211_frame_min *wh;
- struct mbuf *m;
- uint16_t aid;
- int qlen;
-
- wh = mtod(m0, struct ieee80211_frame_min *);
- if (ni->ni_associd == 0) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
- (struct ieee80211_frame *) wh, "ps-poll",
- "%s", "unassociated station");
- ic->ic_stats.is_ps_unassoc++;
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_NOT_ASSOCED);
- return;
- }
-
- aid = le16toh(*(uint16_t *)wh->i_dur);
- if (aid != ni->ni_associd) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
- (struct ieee80211_frame *) wh, "ps-poll",
- "aid mismatch: sta aid 0x%x poll aid 0x%x",
- ni->ni_associd, aid);
- ic->ic_stats.is_ps_badaid++;
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_NOT_ASSOCED);
- return;
- }
-
- /* Okay, take the first queued packet and put it out... */
- IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
- if (m == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
- "[%s] recv ps-poll, but queue empty\n",
- ether_sprintf(wh->i_addr2));
- ieee80211_send_nulldata(ieee80211_ref_node(ni));
- ic->ic_stats.is_ps_qempty++; /* XXX node stat */
- if (ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0); /* just in case */
- return;
- }
- /*
- * If there are more packets, set the more packets bit
- * in the packet dispatched to the station; otherwise
- * turn off the TIM bit.
- */
- if (qlen != 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
- "[%s] recv ps-poll, send packet, %u still queued\n",
- ether_sprintf(ni->ni_macaddr), qlen);
- m->m_flags |= M_MORE_DATA;
- } else {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
- "[%s] recv ps-poll, send packet, queue empty\n",
- ether_sprintf(ni->ni_macaddr));
- if (ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0);
- }
- m->m_flags |= M_PWR_SAV; /* bypass PS handling */
- IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
+ return 0;
}
#ifdef IEEE80211_DEBUG
/*
* Debugging support.
*/
+void
+ieee80211_ssid_mismatch(struct ieee80211vap *vap, const char *tag,
+ uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid)
+{
+ printf("[%s] discard %s frame, ssid mismatch: ",
+ ether_sprintf(mac), tag);
+ ieee80211_print_essid(ssid + 2, ssid[1]);
+ printf("\n");
+}
/*
* Return the bssid of a frame.
*/
static const uint8_t *
-ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh)
+ieee80211_getbssid(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
{
- if (ic->ic_opmode == IEEE80211_M_STA)
+ if (vap->iv_opmode == IEEE80211_M_STA)
return wh->i_addr2;
if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS)
return wh->i_addr1;
@@ -3333,8 +831,10 @@ ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh)
return wh->i_addr3;
}
+#include <machine/stdarg.h>
+
void
-ieee80211_note(struct ieee80211com *ic, const char *fmt, ...)
+ieee80211_note(struct ieee80211vap *vap, const char *fmt, ...)
{
char buf[128]; /* XXX */
va_list ap;
@@ -3343,11 +843,11 @@ ieee80211_note(struct ieee80211com *ic, const char *fmt, ...)
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
- if_printf(ic->ic_ifp, "%s", buf); /* NB: no \n */
+ if_printf(vap->iv_ifp, "%s", buf); /* NB: no \n */
}
void
-ieee80211_note_frame(struct ieee80211com *ic,
+ieee80211_note_frame(struct ieee80211vap *vap,
const struct ieee80211_frame *wh,
const char *fmt, ...)
{
@@ -3357,12 +857,12 @@ ieee80211_note_frame(struct ieee80211com *ic,
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
- if_printf(ic->ic_ifp, "[%s] %s\n",
- ether_sprintf(ieee80211_getbssid(ic, wh)), buf);
+ if_printf(vap->iv_ifp, "[%s] %s\n",
+ ether_sprintf(ieee80211_getbssid(vap, wh)), buf);
}
void
-ieee80211_note_mac(struct ieee80211com *ic,
+ieee80211_note_mac(struct ieee80211vap *vap,
const uint8_t mac[IEEE80211_ADDR_LEN],
const char *fmt, ...)
{
@@ -3372,22 +872,24 @@ ieee80211_note_mac(struct ieee80211com *ic,
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
- if_printf(ic->ic_ifp, "[%s] %s\n", ether_sprintf(mac), buf);
+ if_printf(vap->iv_ifp, "[%s] %s\n", ether_sprintf(mac), buf);
}
void
-ieee80211_discard_frame(struct ieee80211com *ic,
+ieee80211_discard_frame(struct ieee80211vap *vap,
const struct ieee80211_frame *wh,
const char *type, const char *fmt, ...)
{
va_list ap;
- printf("[%s:%s] discard ", ic->ic_ifp->if_xname,
- ether_sprintf(ieee80211_getbssid(ic, wh)));
- if (type != NULL)
+ if_printf(vap->iv_ifp, "[%s] discard ",
+ ether_sprintf(ieee80211_getbssid(vap, wh)));
+ if (type == NULL) {
+ printf("%s frame, ", ieee80211_mgt_subtype_name[
+ (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) >>
+ IEEE80211_FC0_SUBTYPE_SHIFT]);
+ } else
printf("%s frame, ", type);
- else
- printf("frame, ");
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
@@ -3395,14 +897,14 @@ ieee80211_discard_frame(struct ieee80211com *ic,
}
void
-ieee80211_discard_ie(struct ieee80211com *ic,
+ieee80211_discard_ie(struct ieee80211vap *vap,
const struct ieee80211_frame *wh,
const char *type, const char *fmt, ...)
{
va_list ap;
- printf("[%s:%s] discard ", ic->ic_ifp->if_xname,
- ether_sprintf(ieee80211_getbssid(ic, wh)));
+ if_printf(vap->iv_ifp, "[%s] discard ",
+ ether_sprintf(ieee80211_getbssid(vap, wh)));
if (type != NULL)
printf("%s information element, ", type);
else
@@ -3414,13 +916,13 @@ ieee80211_discard_ie(struct ieee80211com *ic,
}
void
-ieee80211_discard_mac(struct ieee80211com *ic,
+ieee80211_discard_mac(struct ieee80211vap *vap,
const uint8_t mac[IEEE80211_ADDR_LEN],
const char *type, const char *fmt, ...)
{
va_list ap;
- printf("[%s:%s] discard ", ic->ic_ifp->if_xname, ether_sprintf(mac));
+ if_printf(vap->iv_ifp, "[%s] discard ", ether_sprintf(mac));
if (type != NULL)
printf("%s frame, ", type);
else
diff --git a/sys/net80211/ieee80211_input.h b/sys/net80211/ieee80211_input.h
new file mode 100644
index 0000000..dd41d7c
--- /dev/null
+++ b/sys/net80211/ieee80211_input.h
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_INPUT_H_
+#define _NET80211_IEEE80211_INPUT_H_
+
+/* Verify the existence and length of __elem or get out. */
+#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen, _action) do { \
+ if ((__elem) == NULL) { \
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, \
+ wh, NULL, "%s", "no " #__elem ); \
+ vap->iv_stats.is_rx_elem_missing++; \
+ _action; \
+ } else if ((__elem)[1] > (__maxlen)) { \
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, \
+ wh, NULL, "bad " #__elem " len %d", (__elem)[1]); \
+ vap->iv_stats.is_rx_elem_toobig++; \
+ _action; \
+ } \
+} while (0)
+
+#define IEEE80211_VERIFY_LENGTH(_len, _minlen, _action) do { \
+ if ((_len) < (_minlen)) { \
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, \
+ wh, NULL, "ie too short, got %d, expected %d", \
+ (_len), (_minlen)); \
+ vap->iv_stats.is_rx_elem_toosmall++; \
+ _action; \
+ } \
+} while (0)
+
+#ifdef IEEE80211_DEBUG
+void ieee80211_ssid_mismatch(struct ieee80211vap *, const char *tag,
+ uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid);
+
+#define IEEE80211_VERIFY_SSID(_ni, _ssid, _action) do { \
+ if ((_ssid)[1] != 0 && \
+ ((_ssid)[1] != (_ni)->ni_esslen || \
+ memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
+ if (ieee80211_msg_input(vap)) \
+ ieee80211_ssid_mismatch(vap, \
+ ieee80211_mgt_subtype_name[subtype >> \
+ IEEE80211_FC0_SUBTYPE_SHIFT], \
+ wh->i_addr2, _ssid); \
+ vap->iv_stats.is_rx_ssidmismatch++; \
+ _action; \
+ } \
+} while (0)
+#else /* !IEEE80211_DEBUG */
+#define IEEE80211_VERIFY_SSID(_ni, _ssid, _action) do { \
+ if ((_ssid)[1] != 0 && \
+ ((_ssid)[1] != (_ni)->ni_esslen || \
+ memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
+ vap->iv_stats.is_rx_ssidmismatch++; \
+ _action; \
+ } \
+} while (0)
+#endif /* !IEEE80211_DEBUG */
+
+/* unalligned little endian access */
+#define LE_READ_2(p) \
+ ((uint16_t) \
+ ((((const uint8_t *)(p))[0] ) | \
+ (((const uint8_t *)(p))[1] << 8)))
+#define LE_READ_4(p) \
+ ((uint32_t) \
+ ((((const uint8_t *)(p))[0] ) | \
+ (((const uint8_t *)(p))[1] << 8) | \
+ (((const uint8_t *)(p))[2] << 16) | \
+ (((const uint8_t *)(p))[3] << 24)))
+
+static __inline int
+iswpaoui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
+}
+
+static __inline int
+iswmeoui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
+}
+
+static __inline int
+iswmeparam(const uint8_t *frm)
+{
+ return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
+ frm[6] == WME_PARAM_OUI_SUBTYPE;
+}
+
+static __inline int
+iswmeinfo(const uint8_t *frm)
+{
+ return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
+ frm[6] == WME_INFO_OUI_SUBTYPE;
+}
+
+static __inline int
+isatherosoui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
+}
+
+static __inline int
+ishtcapoui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTCAP<<24)|BCM_OUI);
+}
+
+static __inline int
+ishtinfooui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI);
+}
+
+void ieee80211_deliver_data(struct ieee80211vap *,
+ struct ieee80211_node *, struct mbuf *);
+struct mbuf *ieee80211_defrag(struct ieee80211_node *,
+ struct mbuf *, int);
+struct mbuf *ieee80211_decap(struct ieee80211vap *, struct mbuf *, int);
+struct mbuf *ieee80211_decap1(struct mbuf *, int *);
+struct mbuf *ieee80211_decap_fastframe(struct ieee80211_node *,
+ struct mbuf *);
+int ieee80211_setup_rates(struct ieee80211_node *ni,
+ const uint8_t *rates, const uint8_t *xrates, int flags);
+void ieee80211_send_error(struct ieee80211_node *,
+ const uint8_t mac[IEEE80211_ADDR_LEN], int subtype, int arg);
+int ieee80211_alloc_challenge(struct ieee80211_node *);
+void ieee80211_parse_ath(struct ieee80211_node *, uint8_t *);
+int ieee80211_parse_beacon(struct ieee80211_node *, struct mbuf *,
+ struct ieee80211_scanparams *);
+int ieee80211_parse_action(struct ieee80211_node *, struct mbuf *);
+#endif /* _NET80211_IEEE80211_INPUT_H_ */
diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c
index 83627e8..3b088b1 100644
--- a/sys/net80211/ieee80211_ioctl.c
+++ b/sys/net80211/ieee80211_ioctl.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -27,14 +27,13 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#include "opt_compat.h"
-
/*
* IEEE 802.11 ioctl support (FreeBSD-specific)
*/
#include "opt_inet.h"
#include "opt_ipx.h"
+#include "opt_wlan.h"
#include <sys/endian.h>
#include <sys/param.h>
@@ -61,33 +60,21 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_ioctl.h>
+#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_input.h>
-#define IS_UP(_ic) \
- (((_ic)->ic_ifp->if_flags & IFF_UP) && \
- ((_ic)->ic_ifp->if_drv_flags & IFF_DRV_RUNNING))
-#define IS_UP_AUTO(_ic) \
- (IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO)
-#define RESCAN 1
+#define IS_UP_AUTO(_vap) \
+ (IFNET_IS_UP_RUNNING(vap->iv_ifp) && \
+ (_vap)->iv_roaming == IEEE80211_ROAMING_AUTO)
+static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
static struct ieee80211_channel *findchannel(struct ieee80211com *,
int ieee, int mode);
-static int
-cap2cipher(int flag)
-{
- switch (flag) {
- case IEEE80211_C_WEP: return IEEE80211_CIPHER_WEP;
- case IEEE80211_C_AES: return IEEE80211_CIPHER_AES_OCB;
- case IEEE80211_C_AES_CCM: return IEEE80211_CIPHER_AES_CCM;
- case IEEE80211_C_CKIP: return IEEE80211_CIPHER_CKIP;
- case IEEE80211_C_TKIP: return IEEE80211_CIPHER_TKIP;
- }
- return -1;
-}
-
-static int
-ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
struct ieee80211req_key ik;
struct ieee80211_key *wk;
@@ -102,26 +89,26 @@ ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq)
return error;
kid = ik.ik_keyix;
if (kid == IEEE80211_KEYIX_NONE) {
- ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
+ ni = ieee80211_find_vap_node(&ic->ic_sta, vap, ik.ik_macaddr);
if (ni == NULL)
- return EINVAL; /* XXX */
+ return ENOENT;
wk = &ni->ni_ucastkey;
} else {
if (kid >= IEEE80211_WEP_NKID)
return EINVAL;
- wk = &ic->ic_nw_keys[kid];
- IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr);
+ wk = &vap->iv_nw_keys[kid];
+ IEEE80211_ADDR_COPY(&ik.ik_macaddr, vap->iv_bss->ni_macaddr);
ni = NULL;
}
cip = wk->wk_cipher;
ik.ik_type = cip->ic_cipher;
ik.ik_keylen = wk->wk_keylen;
ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV);
- if (wk->wk_keyix == ic->ic_def_txkey)
+ if (wk->wk_keyix == vap->iv_def_txkey)
ik.ik_flags |= IEEE80211_KEY_DEFAULT;
if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) {
/* NB: only root can read key data */
- ik.ik_keyrsc = wk->wk_keyrsc;
+ ik.ik_keyrsc = wk->wk_keyrsc[IEEE80211_NONQOS_TID];
ik.ik_keytsc = wk->wk_keytsc;
memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen);
if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) {
@@ -140,18 +127,20 @@ ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq)
return copyout(&ik, ireq->i_data, sizeof(ik));
}
-static int
-ieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
if (sizeof(ic->ic_chan_active) < ireq->i_len)
ireq->i_len = sizeof(ic->ic_chan_active);
return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len);
}
-static int
-ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getchaninfo(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
int space;
space = __offsetof(struct ieee80211req_chaninfo,
@@ -162,8 +151,9 @@ ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq)
return copyout(&ic->ic_nchans, ireq->i_data, space);
}
-static int
-ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int req)
+static __noinline int
+ieee80211_ioctl_getwpaie(struct ieee80211vap *vap,
+ struct ieee80211req *ireq, int req)
{
struct ieee80211_node *ni;
struct ieee80211req_wpaie2 wpaie;
@@ -174,34 +164,34 @@ ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int
error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN);
if (error != 0)
return error;
- ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, wpaie.wpa_macaddr);
if (ni == NULL)
- return ENOENT; /* XXX */
+ return ENOENT;
memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie));
- if (ni->ni_wpa_ie != NULL) {
- int ielen = ni->ni_wpa_ie[1] + 2;
+ if (ni->ni_ies.wpa_ie != NULL) {
+ int ielen = ni->ni_ies.wpa_ie[1] + 2;
if (ielen > sizeof(wpaie.wpa_ie))
ielen = sizeof(wpaie.wpa_ie);
- memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen);
+ memcpy(wpaie.wpa_ie, ni->ni_ies.wpa_ie, ielen);
}
if (req == IEEE80211_IOC_WPAIE2) {
memset(wpaie.rsn_ie, 0, sizeof(wpaie.rsn_ie));
- if (ni->ni_rsn_ie != NULL) {
- int ielen = ni->ni_rsn_ie[1] + 2;
+ if (ni->ni_ies.rsn_ie != NULL) {
+ int ielen = ni->ni_ies.rsn_ie[1] + 2;
if (ielen > sizeof(wpaie.rsn_ie))
ielen = sizeof(wpaie.rsn_ie);
- memcpy(wpaie.rsn_ie, ni->ni_rsn_ie, ielen);
+ memcpy(wpaie.rsn_ie, ni->ni_ies.rsn_ie, ielen);
}
if (ireq->i_len > sizeof(struct ieee80211req_wpaie2))
ireq->i_len = sizeof(struct ieee80211req_wpaie2);
} else {
/* compatibility op, may overwrite wpa ie */
/* XXX check ic_flags? */
- if (ni->ni_rsn_ie != NULL) {
- int ielen = ni->ni_rsn_ie[1] + 2;
+ if (ni->ni_ies.rsn_ie != NULL) {
+ int ielen = ni->ni_ies.rsn_ie[1] + 2;
if (ielen > sizeof(wpaie.wpa_ie))
ielen = sizeof(wpaie.wpa_ie);
- memcpy(wpaie.wpa_ie, ni->ni_rsn_ie, ielen);
+ memcpy(wpaie.wpa_ie, ni->ni_ies.rsn_ie, ielen);
}
if (ireq->i_len > sizeof(struct ieee80211req_wpaie))
ireq->i_len = sizeof(struct ieee80211req_wpaie);
@@ -210,8 +200,8 @@ ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int
return copyout(&wpaie, ireq->i_data, ireq->i_len);
}
-static int
-ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getstastats(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
uint8_t macaddr[IEEE80211_ADDR_LEN];
@@ -223,9 +213,9 @@ ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
if (error != 0)
return error;
- ni = ieee80211_find_node(&ic->ic_sta, macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr);
if (ni == NULL)
- return EINVAL;
+ return ENOENT;
if (ireq->i_len > sizeof(struct ieee80211req_sta_stats))
ireq->i_len = sizeof(struct ieee80211req_sta_stats);
/* NB: copy out only the statistics */
@@ -235,149 +225,6 @@ ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
return error;
}
-static __inline uint8_t *
-copyie(uint8_t *cp, const uint8_t *ie)
-{
- if (ie != NULL) {
- memcpy(cp, ie, 2+ie[1]);
- cp += 2+ie[1];
- }
- return cp;
-}
-
-#ifdef COMPAT_FREEBSD6
-#define IEEE80211_IOC_SCAN_RESULTS_OLD 24
-
-struct scan_result_old {
- uint16_t isr_len; /* length (mult of 4) */
- uint16_t isr_freq; /* MHz */
- uint16_t isr_flags; /* channel flags */
- uint8_t isr_noise;
- uint8_t isr_rssi;
- uint8_t isr_intval; /* beacon interval */
- uint8_t isr_capinfo; /* capabilities */
- uint8_t isr_erp; /* ERP element */
- uint8_t isr_bssid[IEEE80211_ADDR_LEN];
- uint8_t isr_nrates;
- uint8_t isr_rates[IEEE80211_RATE_MAXSIZE];
- uint8_t isr_ssid_len; /* SSID length */
- uint8_t isr_ie_len; /* IE length */
- uint8_t isr_pad[5];
- /* variable length SSID followed by IE data */
-};
-
-struct oscanreq {
- struct scan_result_old *sr;
- size_t space;
-};
-
-static size_t
-old_scan_space(const struct ieee80211_scan_entry *se, int *ielen)
-{
- size_t len;
-
- *ielen = 0;
- if (se->se_wpa_ie != NULL)
- *ielen += 2+se->se_wpa_ie[1];
- if (se->se_wme_ie != NULL)
- *ielen += 2+se->se_wme_ie[1];
- /*
- * NB: ie's can be no more than 255 bytes and the max 802.11
- * packet is <3Kbytes so we are sure this doesn't overflow
- * 16-bits; if this is a concern we can drop the ie's.
- */
- len = sizeof(struct scan_result_old) + se->se_ssid[1] + *ielen;
- return roundup(len, sizeof(uint32_t));
-}
-
-static void
-old_get_scan_space(void *arg, const struct ieee80211_scan_entry *se)
-{
- struct oscanreq *req = arg;
- int ielen;
-
- req->space += old_scan_space(se, &ielen);
-}
-
-static void
-old_get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
-{
- struct oscanreq *req = arg;
- struct scan_result_old *sr;
- int ielen, len, nr, nxr;
- uint8_t *cp;
-
- len = old_scan_space(se, &ielen);
- if (len > req->space)
- return;
-
- sr = req->sr;
- memset(sr, 0, sizeof(*sr));
- sr->isr_ssid_len = se->se_ssid[1];
- /* NB: beware of overflow, isr_ie_len is 8 bits */
- sr->isr_ie_len = (ielen > 255 ? 0 : ielen);
- sr->isr_len = len;
- sr->isr_freq = se->se_chan->ic_freq;
- sr->isr_flags = se->se_chan->ic_flags;
- sr->isr_rssi = se->se_rssi;
- sr->isr_noise = se->se_noise;
- sr->isr_intval = se->se_intval;
- sr->isr_capinfo = se->se_capinfo;
- sr->isr_erp = se->se_erp;
- IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid);
- nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE);
- memcpy(sr->isr_rates, se->se_rates+2, nr);
- nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr);
- memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr);
- sr->isr_nrates = nr + nxr;
-
- cp = (uint8_t *)(sr+1);
- memcpy(cp, se->se_ssid+2, sr->isr_ssid_len);
- cp += sr->isr_ssid_len;
- if (sr->isr_ie_len) {
- cp = copyie(cp, se->se_wpa_ie);
- cp = copyie(cp, se->se_wme_ie);
- }
-
- req->space -= len;
- req->sr = (struct scan_result_old *)(((uint8_t *)sr) + len);
-}
-
-static int
-old_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
-{
- struct oscanreq req;
- int error;
-
- if (ireq->i_len < sizeof(struct scan_result_old))
- return EFAULT;
-
- error = 0;
- req.space = 0;
- ieee80211_scan_iterate(ic, old_get_scan_space, &req);
- if (req.space > ireq->i_len)
- req.space = ireq->i_len;
- if (req.space > 0) {
- size_t space;
- void *p;
-
- space = req.space;
- /* XXX M_WAITOK after driver lock released */
- MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO);
- if (p == NULL)
- return ENOMEM;
- req.sr = p;
- ieee80211_scan_iterate(ic, old_get_scan_result, &req);
- ireq->i_len = space - req.space;
- error = copyout(p, ireq->i_data, ireq->i_len);
- FREE(p, M_TEMP);
- } else
- ireq->i_len = 0;
-
- return error;
-}
-#endif /* COMPAT_FREEBSD6 */
-
struct scanreq {
struct ieee80211req_scan_result *sr;
size_t space;
@@ -388,15 +235,7 @@ scan_space(const struct ieee80211_scan_entry *se, int *ielen)
{
size_t len;
- *ielen = 0;
- if (se->se_wpa_ie != NULL)
- *ielen += 2+se->se_wpa_ie[1];
- if (se->se_rsn_ie != NULL)
- *ielen += 2+se->se_rsn_ie[1];
- if (se->se_wme_ie != NULL)
- *ielen += 2+se->se_wme_ie[1];
- if (se->se_ath_ie != NULL)
- *ielen += 2+se->se_ath_ie[1];
+ *ielen = se->se_ies.len;
/*
* NB: ie's can be no more than 255 bytes and the max 802.11
* packet is <3Kbytes so we are sure this doesn't overflow
@@ -415,7 +254,7 @@ get_scan_space(void *arg, const struct ieee80211_scan_entry *se)
req->space += scan_space(se, &ielen);
}
-static void
+static __noinline void
get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
{
struct scanreq *req = arg;
@@ -430,9 +269,9 @@ get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
sr = req->sr;
KASSERT(len <= 65535 && ielen <= 65535,
("len %u ssid %u ie %u", len, se->se_ssid[1], ielen));
+ sr->isr_len = len;
sr->isr_ie_off = sizeof(struct ieee80211req_scan_result);
sr->isr_ie_len = ielen;
- sr->isr_len = len;
sr->isr_freq = se->se_chan->ic_freq;
sr->isr_flags = se->se_chan->ic_flags;
sr->isr_rssi = se->se_rssi;
@@ -453,29 +292,26 @@ get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
if (ielen) {
cp += sr->isr_ssid_len;
- cp = copyie(cp, se->se_wpa_ie);
- cp = copyie(cp, se->se_rsn_ie);
- cp = copyie(cp, se->se_wme_ie);
- cp = copyie(cp, se->se_ath_ie);
- cp = copyie(cp, se->se_htcap_ie);
+ memcpy(cp, se->se_ies.data, ielen);
}
req->space -= len;
req->sr = (struct ieee80211req_scan_result *)(((uint8_t *)sr) + len);
}
-static int
-ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getscanresults(struct ieee80211vap *vap,
+ struct ieee80211req *ireq)
{
struct scanreq req;
int error;
- if (ireq->i_len < sizeof(struct ieee80211req_scan_result))
+ if (ireq->i_len < sizeof(struct scanreq))
return EFAULT;
error = 0;
req.space = 0;
- ieee80211_scan_iterate(ic, get_scan_space, &req);
+ ieee80211_scan_iterate(vap, get_scan_space, &req);
if (req.space > ireq->i_len)
req.space = ireq->i_len;
if (req.space > 0) {
@@ -488,7 +324,7 @@ ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ire
if (p == NULL)
return ENOMEM;
req.sr = p;
- ieee80211_scan_iterate(ic, get_scan_result, &req);
+ ieee80211_scan_iterate(vap, get_scan_result, &req);
ireq->i_len = space - req.space;
error = copyout(p, ireq->i_data, ireq->i_len);
FREE(p, M_TEMP);
@@ -499,7 +335,7 @@ ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ire
}
struct stainforeq {
- struct ieee80211com *ic;
+ struct ieee80211vap *vap;
struct ieee80211req_sta_info *si;
size_t space;
};
@@ -507,15 +343,7 @@ struct stainforeq {
static size_t
sta_space(const struct ieee80211_node *ni, size_t *ielen)
{
- *ielen = 0;
- if (ni->ni_wpa_ie != NULL)
- *ielen += 2+ni->ni_wpa_ie[1];
- if (ni->ni_rsn_ie != NULL)
- *ielen += 2+ni->ni_rsn_ie[1];
- if (ni->ni_wme_ie != NULL)
- *ielen += 2+ni->ni_wme_ie[1];
- if (ni->ni_ath_ie != NULL)
- *ielen += 2+ni->ni_ath_ie[1];
+ *ielen = ni->ni_ies.len;
return roundup(sizeof(struct ieee80211req_sta_info) + *ielen,
sizeof(uint32_t));
}
@@ -524,25 +352,28 @@ static void
get_sta_space(void *arg, struct ieee80211_node *ni)
{
struct stainforeq *req = arg;
- struct ieee80211com *ic = ni->ni_ic;
size_t ielen;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ if (req->vap != ni->ni_vap)
+ return;
+ if (ni->ni_vap->iv_opmode == IEEE80211_M_HOSTAP &&
ni->ni_associd == 0) /* only associated stations */
return;
req->space += sta_space(ni, &ielen);
}
-static void
+static __noinline void
get_sta_info(void *arg, struct ieee80211_node *ni)
{
struct stainforeq *req = arg;
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211req_sta_info *si;
size_t ielen, len;
uint8_t *cp;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ if (req->vap != ni->ni_vap)
+ return;
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
ni->ni_associd == 0) /* only associated stations */
return;
if (ni->ni_chan == IEEE80211_CHAN_ANYC) /* XXX bogus entry */
@@ -558,8 +389,8 @@ get_sta_info(void *arg, struct ieee80211_node *ni)
si->isi_flags = ni->ni_chan->ic_flags;
si->isi_state = ni->ni_flags;
si->isi_authmode = ni->ni_authmode;
- ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise);
- si->isi_noise = 0; /* XXX */
+ vap->iv_ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise);
+ vap->iv_ic->ic_node_getmimoinfo(ni, &si->isi_mimo);
si->isi_capinfo = ni->ni_capinfo;
si->isi_erp = ni->ni_erp;
IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr);
@@ -568,7 +399,22 @@ get_sta_info(void *arg, struct ieee80211_node *ni)
si->isi_nrates = 15;
memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates);
si->isi_txrate = ni->ni_txrate;
- si->isi_ie_len = ielen;
+ if (si->isi_txrate & IEEE80211_RATE_MCS) {
+ const struct ieee80211_mcs_rates *mcs =
+ &ieee80211_htrates[ni->ni_txrate &~ IEEE80211_RATE_MCS];
+ if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
+ if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
+ si->isi_txmbps = mcs->ht40_rate_800ns;
+ else
+ si->isi_txmbps = mcs->ht40_rate_400ns;
+ } else {
+ if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20)
+ si->isi_txmbps = mcs->ht20_rate_800ns;
+ else
+ si->isi_txmbps = mcs->ht20_rate_400ns;
+ }
+ } else
+ si->isi_txmbps = si->isi_txrate;
si->isi_associd = ni->ni_associd;
si->isi_txpower = ni->ni_txpower;
si->isi_vlan = ni->ni_vlan;
@@ -581,29 +427,29 @@ get_sta_info(void *arg, struct ieee80211_node *ni)
}
/* NB: leave all cases in case we relax ni_associd == 0 check */
if (ieee80211_node_is_authorized(ni))
- si->isi_inact = ic->ic_inact_run;
- else if (ni->ni_associd != 0)
- si->isi_inact = ic->ic_inact_auth;
+ si->isi_inact = vap->iv_inact_run;
+ else if (ni->ni_associd != 0 ||
+ (vap->iv_opmode == IEEE80211_M_WDS &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY)))
+ si->isi_inact = vap->iv_inact_auth;
else
- si->isi_inact = ic->ic_inact_init;
+ si->isi_inact = vap->iv_inact_init;
si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT;
if (ielen) {
cp = ((uint8_t *)si) + si->isi_ie_off;
- cp = copyie(cp, ni->ni_wpa_ie);
- cp = copyie(cp, ni->ni_rsn_ie);
- cp = copyie(cp, ni->ni_wme_ie);
- cp = copyie(cp, ni->ni_ath_ie);
+ memcpy(cp, ni->ni_ies.data, ielen);
}
req->si = (struct ieee80211req_sta_info *)(((uint8_t *)si) + len);
req->space -= len;
}
-static int
-getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq,
+static __noinline int
+getstainfo_common(struct ieee80211vap *vap, struct ieee80211req *ireq,
struct ieee80211_node *ni, int off)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct stainforeq req;
size_t space;
void *p;
@@ -611,6 +457,7 @@ getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq,
error = 0;
req.space = 0;
+ req.vap = vap;
if (ni == NULL)
ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req);
else
@@ -620,7 +467,7 @@ getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq,
if (req.space > 0) {
space = req.space;
/* XXX M_WAITOK after driver lock released */
- MALLOC(p, void *, space, M_TEMP, M_NOWAIT);
+ MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO);
if (p == NULL) {
error = ENOMEM;
goto bad;
@@ -641,8 +488,8 @@ bad:
return error;
}
-static int
-ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getstainfo(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
uint8_t macaddr[IEEE80211_ADDR_LEN];
const int off = __offsetof(struct ieee80211req_sta_req, info);
@@ -654,30 +501,18 @@ ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
if (error != 0)
return error;
- if (IEEE80211_ADDR_EQ(macaddr, ic->ic_ifp->if_broadcastaddr)) {
+ if (IEEE80211_ADDR_EQ(macaddr, vap->iv_ifp->if_broadcastaddr)) {
ni = NULL;
} else {
- ni = ieee80211_find_node(&ic->ic_sta, macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr);
if (ni == NULL)
- return EINVAL;
+ return ENOENT;
}
- return getstainfo_common(ic, ireq, ni, off);
+ return getstainfo_common(vap, ireq, ni, off);
}
-#ifdef COMPAT_FREEBSD6
-#define IEEE80211_IOC_STA_INFO_OLD 45
-
-static int
-old_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
-{
- if (ireq->i_len < sizeof(struct ieee80211req_sta_info))
- return EFAULT;
- return getstainfo_common(ic, ireq, NULL, 0);
-}
-#endif /* COMPAT_FREEBSD6 */
-
-static int
-ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getstatxpow(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
struct ieee80211req_sta_txpow txpow;
@@ -688,18 +523,19 @@ ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, &txpow, sizeof(txpow));
if (error != 0)
return error;
- ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, txpow.it_macaddr);
if (ni == NULL)
- return EINVAL; /* XXX */
+ return ENOENT;
txpow.it_txpow = ni->ni_txpower;
error = copyout(&txpow, ireq->i_data, sizeof(txpow));
ieee80211_free_node(ni);
return error;
}
-static int
-ieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getwmeparam(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
struct wmeParams *wmep;
int ac;
@@ -739,12 +575,12 @@ ieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
return 0;
}
-static int
-ieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getmaccmd(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
- const struct ieee80211_aclator *acl = ic->ic_acl;
+ const struct ieee80211_aclator *acl = vap->iv_acl;
- return (acl == NULL ? EINVAL : acl->iac_getioctl(ic, ireq));
+ return (acl == NULL ? EINVAL : acl->iac_getioctl(vap, ireq));
}
/*
@@ -753,20 +589,151 @@ ieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
* setting. Otherwise report the current setting.
*/
static int
-getathcap(struct ieee80211com *ic, int cap)
+getathcap(struct ieee80211vap *vap, int cap)
{
- if (ic->ic_opmode == IEEE80211_M_STA && ic->ic_state == IEEE80211_S_RUN)
- return IEEE80211_ATH_CAP(ic, ic->ic_bss, cap) != 0;
+ if (vap->iv_opmode == IEEE80211_M_STA &&
+ vap->iv_state == IEEE80211_S_RUN)
+ return IEEE80211_ATH_CAP(vap, vap->iv_bss, cap) != 0;
else
- return (ic->ic_flags & cap) != 0;
+ return (vap->iv_flags & cap) != 0;
}
-static int
-ieee80211_ioctl_getcurchan(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getcurchan(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_channel *c;
+
if (ireq->i_len != sizeof(struct ieee80211_channel))
return EINVAL;
- return copyout(ic->ic_curchan, ireq->i_data, sizeof(*ic->ic_curchan));
+ /*
+ * vap's may have different operating channels when HT is
+ * in use. When in RUN state report the vap-specific channel.
+ * Otherwise return curchan.
+ */
+ if (vap->iv_state == IEEE80211_S_RUN)
+ c = vap->iv_bss->ni_chan;
+ else
+ c = ic->ic_curchan;
+ return copyout(c, ireq->i_data, sizeof(*c));
+}
+
+static int
+getappie(const struct ieee80211_appie *aie, struct ieee80211req *ireq)
+{
+ if (aie == NULL)
+ return EINVAL;
+ /* NB: truncate, caller can check length */
+ if (ireq->i_len > aie->ie_len)
+ ireq->i_len = aie->ie_len;
+ return copyout(aie->ie_data, ireq->i_data, ireq->i_len);
+}
+
+static int
+ieee80211_ioctl_getappie(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ uint8_t fc0;
+
+ fc0 = ireq->i_val & 0xff;
+ if ((fc0 & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
+ return EINVAL;
+ /* NB: could check iv_opmode and reject but hardly worth the effort */
+ switch (fc0 & IEEE80211_FC0_SUBTYPE_MASK) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ return getappie(vap->iv_appie_beacon, ireq);
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ return getappie(vap->iv_appie_proberesp, ireq);
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ return getappie(vap->iv_appie_assocresp, ireq);
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ return getappie(vap->iv_appie_probereq, ireq);
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ return getappie(vap->iv_appie_assocreq, ireq);
+ case IEEE80211_FC0_SUBTYPE_BEACON|IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ return getappie(vap->iv_appie_wpa, ireq);
+ }
+ return EINVAL;
+}
+
+static __noinline int
+ieee80211_ioctl_getregdomain(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ if (ireq->i_len != sizeof(ic->ic_regdomain))
+ return EINVAL;
+ return copyout(&ic->ic_regdomain, ireq->i_data,
+ sizeof(ic->ic_regdomain));
+}
+
+static __noinline int
+ieee80211_ioctl_getroam(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ if (ireq->i_len != sizeof(vap->iv_roamparms))
+ return EINVAL;
+ return copyout(vap->iv_roamparms, ireq->i_data,
+ sizeof(vap->iv_roamparms));
+}
+
+static __noinline int
+ieee80211_ioctl_gettxparams(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ if (ireq->i_len != sizeof(vap->iv_txparms))
+ return EINVAL;
+ return copyout(vap->iv_txparms, ireq->i_data, sizeof(vap->iv_txparms));
+}
+
+static __noinline int
+ieee80211_ioctl_getdevcaps(struct ieee80211com *ic,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211_devcaps_req *dc;
+ struct ieee80211req_chaninfo *ci;
+ int error;
+
+ if (ireq->i_len != sizeof(struct ieee80211_devcaps_req))
+ return EINVAL;
+ MALLOC(dc, struct ieee80211_devcaps_req *,
+ sizeof(struct ieee80211_devcaps_req), M_TEMP, M_NOWAIT | M_ZERO);
+ if (dc == NULL)
+ return ENOMEM;
+ dc->dc_drivercaps = ic->ic_caps;
+ dc->dc_cryptocaps = ic->ic_cryptocaps;
+ dc->dc_htcaps = ic->ic_htcaps;
+ ci = &dc->dc_chaninfo;
+ ic->ic_getradiocaps(ic, &ci->ic_nchans, ci->ic_chans);
+ ieee80211_sort_channels(ci->ic_chans, ci->ic_nchans);
+ error = copyout(dc, ireq->i_data, sizeof(*dc));
+ FREE(dc, M_TEMP);
+ return error;
+}
+
+static __noinline int
+ieee80211_ioctl_getstavlan(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ struct ieee80211_node *ni;
+ struct ieee80211req_sta_vlan vlan;
+ int error;
+
+ if (ireq->i_len != sizeof(vlan))
+ return EINVAL;
+ error = copyin(ireq->i_data, &vlan, sizeof(vlan));
+ if (error != 0)
+ return error;
+ if (!IEEE80211_ADDR_EQ(vlan.sv_macaddr, zerobssid)) {
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap,
+ vlan.sv_macaddr);
+ if (ni == NULL)
+ return ENOENT;
+ } else
+ ni = ieee80211_ref_node(vap->iv_bss);
+ vlan.sv_vlan = ni->ni_vlan;
+ error = copyout(&vlan, ireq->i_data, sizeof(vlan));
+ ieee80211_free_node(ni);
+ return error;
}
/*
@@ -785,30 +752,28 @@ ieee80211_ioctl_getcurchan(struct ieee80211com *ic, struct ieee80211req *ireq)
* but special-casing the compilation of this one module in the
* build system would be awkward.
*/
-#ifdef __GNUC__
-__attribute__ ((noinline))
-#endif
-static int
-ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
+ struct ieee80211req *ireq)
{
- const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
- int error = 0;
- u_int kid, len, m;
+#define MS(_v, _f) (((_v) & _f) >> _f##_S)
+ struct ieee80211com *ic = vap->iv_ic;
+ u_int kid, len;
uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
char tmpssid[IEEE80211_NWID_LEN];
+ int error = 0;
switch (ireq->i_type) {
case IEEE80211_IOC_SSID:
- switch (ic->ic_state) {
+ switch (vap->iv_state) {
case IEEE80211_S_INIT:
case IEEE80211_S_SCAN:
- ireq->i_len = ic->ic_des_ssid[0].len;
- memcpy(tmpssid, ic->ic_des_ssid[0].ssid, ireq->i_len);
+ ireq->i_len = vap->iv_des_ssid[0].len;
+ memcpy(tmpssid, vap->iv_des_ssid[0].ssid, ireq->i_len);
break;
default:
- ireq->i_len = ic->ic_bss->ni_esslen;
- memcpy(tmpssid, ic->ic_bss->ni_essid,
- ireq->i_len);
+ ireq->i_len = vap->iv_bss->ni_esslen;
+ memcpy(tmpssid, vap->iv_bss->ni_essid, ireq->i_len);
break;
}
error = copyout(tmpssid, ireq->i_data, ireq->i_len);
@@ -817,9 +782,9 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
ireq->i_val = 1;
break;
case IEEE80211_IOC_WEP:
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0)
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0)
ireq->i_val = IEEE80211_WEP_OFF;
- else if (ic->ic_flags & IEEE80211_F_DROPUNENC)
+ else if (vap->iv_flags & IEEE80211_F_DROPUNENC)
ireq->i_val = IEEE80211_WEP_ON;
else
ireq->i_val = IEEE80211_WEP_MIXED;
@@ -828,10 +793,10 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
kid = (u_int) ireq->i_val;
if (kid >= IEEE80211_WEP_NKID)
return EINVAL;
- len = (u_int) ic->ic_nw_keys[kid].wk_keylen;
+ len = (u_int) vap->iv_nw_keys[kid].wk_keylen;
/* NB: only root can read WEP keys */
if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) {
- bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
+ bcopy(vap->iv_nw_keys[kid].wk_key, tmpkey, len);
} else {
bzero(tmpkey, len);
}
@@ -842,19 +807,19 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
ireq->i_val = IEEE80211_WEP_NKID;
break;
case IEEE80211_IOC_WEPTXKEY:
- ireq->i_val = ic->ic_def_txkey;
+ ireq->i_val = vap->iv_def_txkey;
break;
case IEEE80211_IOC_AUTHMODE:
- if (ic->ic_flags & IEEE80211_F_WPA)
+ if (vap->iv_flags & IEEE80211_F_WPA)
ireq->i_val = IEEE80211_AUTH_WPA;
else
- ireq->i_val = ic->ic_bss->ni_authmode;
+ ireq->i_val = vap->iv_bss->ni_authmode;
break;
case IEEE80211_IOC_CHANNEL:
ireq->i_val = ieee80211_chan2ieee(ic, ic->ic_curchan);
break;
case IEEE80211_IOC_POWERSAVE:
- if (ic->ic_flags & IEEE80211_F_PMGTON)
+ if (vap->iv_flags & IEEE80211_F_PMGTON)
ireq->i_val = IEEE80211_POWERSAVE_ON;
else
ireq->i_val = IEEE80211_POWERSAVE_OFF;
@@ -863,42 +828,25 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
ireq->i_val = ic->ic_lintval;
break;
case IEEE80211_IOC_RTSTHRESHOLD:
- ireq->i_val = ic->ic_rtsthreshold;
+ ireq->i_val = vap->iv_rtsthreshold;
break;
case IEEE80211_IOC_PROTMODE:
ireq->i_val = ic->ic_protmode;
break;
case IEEE80211_IOC_TXPOWER:
- if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
- return EINVAL;
- ireq->i_val = ic->ic_txpowlimit;
- break;
- case IEEE80211_IOC_MCASTCIPHER:
- ireq->i_val = rsn->rsn_mcastcipher;
- break;
- case IEEE80211_IOC_MCASTKEYLEN:
- ireq->i_val = rsn->rsn_mcastkeylen;
- break;
- case IEEE80211_IOC_UCASTCIPHERS:
- ireq->i_val = 0;
- for (m = 0x1; m != 0; m <<= 1)
- if (rsn->rsn_ucastcipherset & m)
- ireq->i_val |= 1<<cap2cipher(m);
- break;
- case IEEE80211_IOC_UCASTCIPHER:
- ireq->i_val = rsn->rsn_ucastcipher;
- break;
- case IEEE80211_IOC_UCASTKEYLEN:
- ireq->i_val = rsn->rsn_ucastkeylen;
- break;
- case IEEE80211_IOC_KEYMGTALGS:
- ireq->i_val = rsn->rsn_keymgmtset;
- break;
- case IEEE80211_IOC_RSNCAPS:
- ireq->i_val = rsn->rsn_caps;
+ /*
+ * Tx power limit is the min of max regulatory
+ * power, any user-set limit, and the max the
+ * radio can do.
+ */
+ ireq->i_val = 2*ic->ic_curchan->ic_maxregpower;
+ if (ireq->i_val > ic->ic_txpowlimit)
+ ireq->i_val = ic->ic_txpowlimit;
+ if (ireq->i_val > ic->ic_curchan->ic_maxpower)
+ ireq->i_val = ic->ic_curchan->ic_maxpower;
break;
case IEEE80211_IOC_WPA:
- switch (ic->ic_flags & IEEE80211_F_WPA) {
+ switch (vap->iv_flags & IEEE80211_F_WPA) {
case IEEE80211_F_WPA1:
ireq->i_val = 1;
break;
@@ -914,85 +862,63 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
}
break;
case IEEE80211_IOC_CHANLIST:
- error = ieee80211_ioctl_getchanlist(ic, ireq);
+ error = ieee80211_ioctl_getchanlist(vap, ireq);
break;
case IEEE80211_IOC_ROAMING:
- ireq->i_val = ic->ic_roaming;
+ ireq->i_val = vap->iv_roaming;
break;
case IEEE80211_IOC_PRIVACY:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_PRIVACY) != 0;
break;
case IEEE80211_IOC_DROPUNENCRYPTED:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_DROPUNENC) != 0;
break;
case IEEE80211_IOC_COUNTERMEASURES:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0;
- break;
- case IEEE80211_IOC_DRIVER_CAPS:
- ireq->i_val = ic->ic_caps>>16;
- ireq->i_len = ic->ic_caps&0xffff;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_COUNTERM) != 0;
break;
case IEEE80211_IOC_WME:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_WME) != 0;
break;
case IEEE80211_IOC_HIDESSID:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_HIDESSID) != 0;
break;
case IEEE80211_IOC_APBRIDGE:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0;
- break;
- case IEEE80211_IOC_OPTIE:
- if (ic->ic_opt_ie == NULL)
- return EINVAL;
- /* NB: truncate, caller can check length */
- if (ireq->i_len > ic->ic_opt_ie_len)
- ireq->i_len = ic->ic_opt_ie_len;
- error = copyout(ic->ic_opt_ie, ireq->i_data, ireq->i_len);
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_NOBRIDGE) == 0;
break;
case IEEE80211_IOC_WPAKEY:
- error = ieee80211_ioctl_getkey(ic, ireq);
+ error = ieee80211_ioctl_getkey(vap, ireq);
break;
case IEEE80211_IOC_CHANINFO:
- error = ieee80211_ioctl_getchaninfo(ic, ireq);
+ error = ieee80211_ioctl_getchaninfo(vap, ireq);
break;
case IEEE80211_IOC_BSSID:
if (ireq->i_len != IEEE80211_ADDR_LEN)
return EINVAL;
- error = copyout(ic->ic_state == IEEE80211_S_RUN ?
- ic->ic_bss->ni_bssid :
- ic->ic_des_bssid,
+ error = copyout(vap->iv_state == IEEE80211_S_RUN ?
+ vap->iv_bss->ni_bssid :
+ vap->iv_des_bssid,
ireq->i_data, ireq->i_len);
break;
case IEEE80211_IOC_WPAIE:
- error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type);
+ error = ieee80211_ioctl_getwpaie(vap, ireq, ireq->i_type);
break;
case IEEE80211_IOC_WPAIE2:
- error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type);
- break;
-#ifdef COMPAT_FREEBSD6
- case IEEE80211_IOC_SCAN_RESULTS_OLD:
- error = old_getscanresults(ic, ireq);
+ error = ieee80211_ioctl_getwpaie(vap, ireq, ireq->i_type);
break;
-#endif
case IEEE80211_IOC_SCAN_RESULTS:
- error = ieee80211_ioctl_getscanresults(ic, ireq);
+ error = ieee80211_ioctl_getscanresults(vap, ireq);
break;
case IEEE80211_IOC_STA_STATS:
- error = ieee80211_ioctl_getstastats(ic, ireq);
+ error = ieee80211_ioctl_getstastats(vap, ireq);
break;
case IEEE80211_IOC_TXPOWMAX:
- ireq->i_val = ic->ic_bss->ni_txpower;
+ ireq->i_val = vap->iv_bss->ni_txpower;
break;
case IEEE80211_IOC_STA_TXPOW:
- error = ieee80211_ioctl_getstatxpow(ic, ireq);
- break;
-#ifdef COMPAT_FREEBSD6
- case IEEE80211_IOC_STA_INFO_OLD:
- error = old_getstainfo(ic, ireq);
+ error = ieee80211_ioctl_getstatxpow(vap, ireq);
break;
-#endif
case IEEE80211_IOC_STA_INFO:
- error = ieee80211_ioctl_getstainfo(ic, ireq);
+ error = ieee80211_ioctl_getstainfo(vap, ireq);
break;
case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
@@ -1000,180 +926,163 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */
- error = ieee80211_ioctl_getwmeparam(ic, ireq);
+ error = ieee80211_ioctl_getwmeparam(vap, ireq);
break;
case IEEE80211_IOC_DTIM_PERIOD:
- ireq->i_val = ic->ic_dtim_period;
+ ireq->i_val = vap->iv_dtim_period;
break;
case IEEE80211_IOC_BEACON_INTERVAL:
/* NB: get from ic_bss for station mode */
- ireq->i_val = ic->ic_bss->ni_intval;
+ ireq->i_val = vap->iv_bss->ni_intval;
break;
case IEEE80211_IOC_PUREG:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_PUREG) != 0;
break;
case IEEE80211_IOC_FF:
- ireq->i_val = getathcap(ic, IEEE80211_F_FF);
+ ireq->i_val = getathcap(vap, IEEE80211_F_FF);
break;
case IEEE80211_IOC_TURBOP:
- ireq->i_val = getathcap(ic, IEEE80211_F_TURBOP);
+ ireq->i_val = getathcap(vap, IEEE80211_F_TURBOP);
break;
case IEEE80211_IOC_BGSCAN:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_BGSCAN) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_BGSCAN) != 0;
break;
case IEEE80211_IOC_BGSCAN_IDLE:
- ireq->i_val = ic->ic_bgscanidle*hz/1000; /* ms */
+ ireq->i_val = vap->iv_bgscanidle*hz/1000; /* ms */
break;
case IEEE80211_IOC_BGSCAN_INTERVAL:
- ireq->i_val = ic->ic_bgscanintvl/hz; /* seconds */
+ ireq->i_val = vap->iv_bgscanintvl/hz; /* seconds */
break;
case IEEE80211_IOC_SCANVALID:
- ireq->i_val = ic->ic_scanvalid/hz; /* seconds */
- break;
- case IEEE80211_IOC_ROAM_RSSI_11A:
- ireq->i_val = ic->ic_roam.rssi11a;
- break;
- case IEEE80211_IOC_ROAM_RSSI_11B:
- ireq->i_val = ic->ic_roam.rssi11bOnly;
- break;
- case IEEE80211_IOC_ROAM_RSSI_11G:
- ireq->i_val = ic->ic_roam.rssi11b;
- break;
- case IEEE80211_IOC_ROAM_RATE_11A:
- ireq->i_val = ic->ic_roam.rate11a;
- break;
- case IEEE80211_IOC_ROAM_RATE_11B:
- ireq->i_val = ic->ic_roam.rate11bOnly;
- break;
- case IEEE80211_IOC_ROAM_RATE_11G:
- ireq->i_val = ic->ic_roam.rate11b;
- break;
- case IEEE80211_IOC_MCAST_RATE:
- ireq->i_val = ic->ic_mcast_rate;
+ ireq->i_val = vap->iv_scanvalid/hz; /* seconds */
break;
case IEEE80211_IOC_FRAGTHRESHOLD:
- ireq->i_val = ic->ic_fragthreshold;
+ ireq->i_val = vap->iv_fragthreshold;
break;
case IEEE80211_IOC_MACCMD:
- error = ieee80211_ioctl_getmaccmd(ic, ireq);
+ error = ieee80211_ioctl_getmaccmd(vap, ireq);
break;
case IEEE80211_IOC_BURST:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_BURST) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_BURST) != 0;
break;
case IEEE80211_IOC_BMISSTHRESHOLD:
- ireq->i_val = ic->ic_bmissthreshold;
+ ireq->i_val = vap->iv_bmissthreshold;
break;
case IEEE80211_IOC_CURCHAN:
- error = ieee80211_ioctl_getcurchan(ic, ireq);
+ error = ieee80211_ioctl_getcurchan(vap, ireq);
break;
case IEEE80211_IOC_SHORTGI:
ireq->i_val = 0;
- if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI20)
ireq->i_val |= IEEE80211_HTCAP_SHORTGI20;
- if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI40)
ireq->i_val |= IEEE80211_HTCAP_SHORTGI40;
break;
case IEEE80211_IOC_AMPDU:
ireq->i_val = 0;
- if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_TX)
ireq->i_val |= 1;
- if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_RX)
ireq->i_val |= 2;
break;
case IEEE80211_IOC_AMPDU_LIMIT:
- ireq->i_val = ic->ic_ampdu_limit; /* XXX truncation? */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ ireq->i_val = vap->iv_ampdu_rxmax;
+ else if (vap->iv_state == IEEE80211_S_RUN)
+ ireq->i_val = MS(vap->iv_bss->ni_htparam,
+ IEEE80211_HTCAP_MAXRXAMPDU);
+ else
+ ireq->i_val = vap->iv_ampdu_limit;
break;
case IEEE80211_IOC_AMPDU_DENSITY:
- ireq->i_val = ic->ic_ampdu_density;
+ if (vap->iv_state == IEEE80211_S_RUN)
+ ireq->i_val = MS(vap->iv_bss->ni_htparam,
+ IEEE80211_HTCAP_MPDUDENSITY);
+ else
+ ireq->i_val = vap->iv_ampdu_density;
break;
case IEEE80211_IOC_AMSDU:
ireq->i_val = 0;
- if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_TX)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_AMSDU_TX)
ireq->i_val |= 1;
- if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_RX)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_AMSDU_RX)
ireq->i_val |= 2;
break;
case IEEE80211_IOC_AMSDU_LIMIT:
- ireq->i_val = ic->ic_amsdu_limit; /* XXX truncation? */
+ ireq->i_val = vap->iv_amsdu_limit; /* XXX truncation? */
break;
case IEEE80211_IOC_PUREN:
- ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_PUREN) != 0;
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_PUREN) != 0;
break;
case IEEE80211_IOC_DOTH:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_DOTH) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_DOTH) != 0;
+ break;
+ case IEEE80211_IOC_REGDOMAIN:
+ error = ieee80211_ioctl_getregdomain(vap, ireq);
+ break;
+ case IEEE80211_IOC_ROAM:
+ error = ieee80211_ioctl_getroam(vap, ireq);
+ break;
+ case IEEE80211_IOC_TXPARAMS:
+ error = ieee80211_ioctl_gettxparams(vap, ireq);
break;
case IEEE80211_IOC_HTCOMPAT:
- ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0;
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0;
+ break;
+ case IEEE80211_IOC_DWDS:
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_DWDS) != 0;
break;
case IEEE80211_IOC_INACTIVITY:
- ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_INACT) != 0;
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_INACT) != 0;
+ break;
+ case IEEE80211_IOC_APPIE:
+ error = ieee80211_ioctl_getappie(vap, ireq);
+ break;
+ case IEEE80211_IOC_WPS:
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_WPS) != 0;
+ break;
+ case IEEE80211_IOC_TSN:
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_TSN) != 0;
+ break;
+ case IEEE80211_IOC_DFS:
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_DFS) != 0;
+ break;
+ case IEEE80211_IOC_DOTD:
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_DOTD) != 0;
+ break;
+ case IEEE80211_IOC_DEVCAPS:
+ error = ieee80211_ioctl_getdevcaps(ic, ireq);
break;
case IEEE80211_IOC_HTPROTMODE:
ireq->i_val = ic->ic_htprotmode;
break;
case IEEE80211_IOC_HTCONF:
- if (ic->ic_flags_ext & IEEE80211_FEXT_HT) {
+ if (vap->iv_flags_ext & IEEE80211_FEXT_HT) {
ireq->i_val = 1;
- if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40)
ireq->i_val |= 2;
} else
ireq->i_val = 0;
break;
+ case IEEE80211_IOC_STA_VLAN:
+ error = ieee80211_ioctl_getstavlan(vap, ireq);
+ break;
default:
error = EINVAL;
break;
}
return error;
+#undef MS
}
-static int
-ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq)
-{
- int error;
- void *ie, *oie;
-
- /*
- * NB: Doing this for ap operation could be useful (e.g. for
- * WPA and/or WME) except that it typically is worthless
- * without being able to intervene when processing
- * association response frames--so disallow it for now.
- */
- if (ic->ic_opmode != IEEE80211_M_STA)
- return EINVAL;
- if (ireq->i_len > IEEE80211_MAX_OPT_IE)
- return EINVAL;
- /* NB: data.length is validated by the wireless extensions code */
- /* XXX M_WAITOK after driver lock released */
- if (ireq->i_len > 0) {
- MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_NOWAIT);
- if (ie == NULL)
- return ENOMEM;
- error = copyin(ireq->i_data, ie, ireq->i_len);
- if (error) {
- FREE(ie, M_DEVBUF);
- return error;
- }
- } else {
- ie = NULL;
- ireq->i_len = 0;
- }
- /* XXX sanity check data? */
- oie = ic->ic_opt_ie;
- ic->ic_opt_ie = ie;
- ic->ic_opt_ie_len = ireq->i_len;
- if (oie != NULL)
- FREE(oie, M_DEVBUF);
- return 0;
-}
-
-static int
-ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211req_key ik;
struct ieee80211_node *ni;
struct ieee80211_key *wk;
uint16_t kid;
- int error;
+ int error, i;
if (ireq->i_len != sizeof(ik))
return EINVAL;
@@ -1189,14 +1098,15 @@ ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
/* XXX unicast keys currently must be tx/rx */
if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))
return EINVAL;
- if (ic->ic_opmode == IEEE80211_M_STA) {
- ni = ieee80211_ref_node(ic->ic_bss);
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+ ni = ieee80211_ref_node(vap->iv_bss);
if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) {
ieee80211_free_node(ni);
return EADDRNOTAVAIL;
}
} else {
- ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap,
+ ik.ik_macaddr);
if (ni == NULL)
return ENOENT;
}
@@ -1204,7 +1114,7 @@ ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
} else {
if (kid >= IEEE80211_WEP_NKID)
return EINVAL;
- wk = &ic->ic_nw_keys[kid];
+ wk = &vap->iv_nw_keys[kid];
/*
* Global slots start off w/o any assigned key index.
* Force one here for consistency with IEEE80211_IOC_WEPKEY.
@@ -1214,31 +1124,32 @@ ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
ni = NULL;
}
error = 0;
- ieee80211_key_update_begin(ic);
- if (ieee80211_crypto_newkey(ic, ik.ik_type, ik.ik_flags, wk)) {
+ ieee80211_key_update_begin(vap);
+ if (ieee80211_crypto_newkey(vap, ik.ik_type, ik.ik_flags, wk)) {
wk->wk_keylen = ik.ik_keylen;
/* NB: MIC presence is implied by cipher type */
if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
- wk->wk_keyrsc = ik.ik_keyrsc;
+ for (i = 0; i < IEEE80211_TID_SIZE; i++)
+ wk->wk_keyrsc[i] = ik.ik_keyrsc;
wk->wk_keytsc = 0; /* new key, reset */
memset(wk->wk_key, 0, sizeof(wk->wk_key));
memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen);
- if (!ieee80211_crypto_setkey(ic, wk,
+ if (!ieee80211_crypto_setkey(vap, wk,
ni != NULL ? ni->ni_macaddr : ik.ik_macaddr))
error = EIO;
else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT))
- ic->ic_def_txkey = kid;
+ vap->iv_def_txkey = kid;
} else
error = ENXIO;
- ieee80211_key_update_end(ic);
+ ieee80211_key_update_end(vap);
if (ni != NULL)
ieee80211_free_node(ni);
return error;
}
-static int
-ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_delkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211req_del_key dk;
int kid, error;
@@ -1253,14 +1164,15 @@ ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
if (dk.idk_keyix == (uint8_t) IEEE80211_KEYIX_NONE) {
struct ieee80211_node *ni;
- if (ic->ic_opmode == IEEE80211_M_STA) {
- ni = ieee80211_ref_node(ic->ic_bss);
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+ ni = ieee80211_ref_node(vap->iv_bss);
if (!IEEE80211_ADDR_EQ(dk.idk_macaddr, ni->ni_bssid)) {
ieee80211_free_node(ni);
return EADDRNOTAVAIL;
}
} else {
- ni = ieee80211_find_node(&ic->ic_sta, dk.idk_macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap,
+ dk.idk_macaddr);
if (ni == NULL)
return ENOENT;
}
@@ -1271,25 +1183,216 @@ ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
if (kid >= IEEE80211_WEP_NKID)
return EINVAL;
/* XXX error return */
- ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]);
+ ieee80211_crypto_delkey(vap, &vap->iv_nw_keys[kid]);
}
return 0;
}
+struct mlmeop {
+ struct ieee80211vap *vap;
+ int op;
+ int reason;
+};
+
+static void
+mlmedebug(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
+ int op, int reason)
+{
+#ifdef IEEE80211_DEBUG
+ static const struct {
+ int mask;
+ const char *opstr;
+ } ops[] = {
+ { 0, "op#0" },
+ { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
+ IEEE80211_MSG_ASSOC, "assoc" },
+ { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
+ IEEE80211_MSG_ASSOC, "disassoc" },
+ { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
+ IEEE80211_MSG_AUTH, "deauth" },
+ { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
+ IEEE80211_MSG_AUTH, "authorize" },
+ { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
+ IEEE80211_MSG_AUTH, "unauthorize" },
+ };
+
+ if (op == IEEE80211_MLME_AUTH) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_IOCTL |
+ IEEE80211_MSG_STATE | IEEE80211_MSG_AUTH, mac,
+ "station authenticate %s via MLME (reason %d)",
+ reason == IEEE80211_STATUS_SUCCESS ? "ACCEPT" : "REJECT",
+ reason);
+ } else if (!(IEEE80211_MLME_ASSOC <= op && op <= IEEE80211_MLME_AUTH)) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, mac,
+ "unknown MLME request %d (reason %d)", op, reason);
+ } else if (reason == IEEE80211_STATUS_SUCCESS) {
+ IEEE80211_NOTE_MAC(vap, ops[op].mask, mac,
+ "station %s via MLME", ops[op].opstr);
+ } else {
+ IEEE80211_NOTE_MAC(vap, ops[op].mask, mac,
+ "station %s via MLME (reason %d)", ops[op].opstr, reason);
+ }
+#endif /* IEEE80211_DEBUG */
+}
+
static void
domlme(void *arg, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
- struct ieee80211req_mlme *mlme = arg;
+ struct mlmeop *mop = arg;
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (vap != mop->vap)
+ return;
+ /*
+ * NB: if ni_associd is zero then the node is already cleaned
+ * up and we don't need to do this (we're safely holding a
+ * reference but should otherwise not modify it's state).
+ */
+ if (ni->ni_associd == 0)
+ return;
+ mlmedebug(vap, ni->ni_macaddr, mop->op, mop->reason);
+ if (mop->op == IEEE80211_MLME_DEAUTH) {
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+ mop->reason);
+ } else {
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC,
+ mop->reason);
+ }
+ ieee80211_node_leave(ni);
+}
+
+static int
+setmlme_dropsta(struct ieee80211vap *vap,
+ const uint8_t mac[IEEE80211_ADDR_LEN], struct mlmeop *mlmeop)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211_node *ni;
+ int error = 0;
+
+ /* NB: the broadcast address means do 'em all */
+ if (!IEEE80211_ADDR_EQ(mac, ic->ic_ifp->if_broadcastaddr)) {
+ IEEE80211_NODE_LOCK(nt);
+ ni = ieee80211_find_node_locked(nt, mac);
+ if (ni != NULL) {
+ domlme(mlmeop, ni);
+ ieee80211_free_node(ni);
+ } else
+ error = ENOENT;
+ IEEE80211_NODE_UNLOCK(nt);
+ } else {
+ ieee80211_iterate_nodes(nt, domlme, mlmeop);
+ }
+ return error;
+}
+
+static __noinline int
+setmlme_common(struct ieee80211vap *vap, int op,
+ const uint8_t mac[IEEE80211_ADDR_LEN], int reason)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211_node *ni;
+ struct mlmeop mlmeop;
+ int error;
- if (ni->ni_associd != 0) {
- IEEE80211_SEND_MGMT(ic, ni,
- mlme->im_op == IEEE80211_MLME_DEAUTH ?
- IEEE80211_FC0_SUBTYPE_DEAUTH :
- IEEE80211_FC0_SUBTYPE_DISASSOC,
- mlme->im_reason);
+ error = 0;
+ switch (op) {
+ case IEEE80211_MLME_DISASSOC:
+ case IEEE80211_MLME_DEAUTH:
+ switch (vap->iv_opmode) {
+ case IEEE80211_M_STA:
+ mlmedebug(vap, vap->iv_bss->ni_macaddr, op, reason);
+ /* XXX not quite right */
+ ieee80211_new_state(vap, IEEE80211_S_INIT, reason);
+ break;
+ case IEEE80211_M_HOSTAP:
+ mlmeop.vap = vap;
+ mlmeop.op = op;
+ mlmeop.reason = reason;
+ error = setmlme_dropsta(vap, mac, &mlmeop);
+ break;
+ case IEEE80211_M_WDS:
+ /* XXX user app should send raw frame? */
+ if (op != IEEE80211_MLME_DEAUTH) {
+ error = EINVAL;
+ break;
+ }
+#if 0
+ /* XXX accept any address, simplifies user code */
+ if (!IEEE80211_ADDR_EQ(mac, vap->iv_bss->ni_macaddr)) {
+ error = EINVAL;
+ break;
+ }
+#endif
+ mlmedebug(vap, vap->iv_bss->ni_macaddr, op, reason);
+ ni = ieee80211_ref_node(vap->iv_bss);
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
+ ieee80211_free_node(ni);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+ case IEEE80211_MLME_AUTHORIZE:
+ case IEEE80211_MLME_UNAUTHORIZE:
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_WDS) {
+ error = EINVAL;
+ break;
+ }
+ IEEE80211_NODE_LOCK(nt);
+ ni = ieee80211_find_vap_node_locked(nt, vap, mac);
+ if (ni != NULL) {
+ mlmedebug(vap, mac, op, reason);
+ if (op == IEEE80211_MLME_AUTHORIZE)
+ ieee80211_node_authorize(ni);
+ else
+ ieee80211_node_unauthorize(ni);
+ ieee80211_free_node(ni);
+ } else
+ error = ENOENT;
+ IEEE80211_NODE_UNLOCK(nt);
+ break;
+ case IEEE80211_MLME_AUTH:
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP) {
+ error = EINVAL;
+ break;
+ }
+ IEEE80211_NODE_LOCK(nt);
+ ni = ieee80211_find_vap_node_locked(nt, vap, mac);
+ if (ni != NULL) {
+ mlmedebug(vap, mac, op, reason);
+ if (reason == IEEE80211_STATUS_SUCCESS) {
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2);
+ /*
+ * For shared key auth, just continue the
+ * exchange. Otherwise when 802.1x is not in
+ * use mark the port authorized at this point
+ * so traffic can flow.
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_8021X &&
+ ni->ni_challenge == NULL)
+ ieee80211_node_authorize(ni);
+ } else {
+ vap->iv_stats.is_rx_acl++;
+ ieee80211_send_error(ni, ni->ni_macaddr,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2|(reason<<16));
+ ieee80211_node_leave(ni);
+ }
+ ieee80211_free_node(ni);
+ } else
+ error = ENOENT;
+ IEEE80211_NODE_UNLOCK(nt);
+ break;
+ default:
+ error = EINVAL;
+ break;
}
- ieee80211_node_leave(ic, ni);
+ return error;
}
struct scanlookup {
@@ -1318,11 +1421,34 @@ mlmelookup(void *arg, const struct ieee80211_scan_entry *se)
look->se = se;
}
-static int
-ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+setmlme_assoc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
+ int ssid_len, const uint8_t ssid[IEEE80211_NWID_LEN])
+{
+ struct scanlookup lookup;
+
+ /* XXX ibss/ahdemo */
+ if (vap->iv_opmode != IEEE80211_M_STA)
+ return EINVAL;
+
+ /* NB: this is racey if roaming is !manual */
+ lookup.se = NULL;
+ lookup.mac = mac;
+ lookup.esslen = ssid_len;
+ lookup.essid = ssid;
+ ieee80211_scan_iterate(vap, mlmelookup, &lookup);
+ if (lookup.se == NULL)
+ return ENOENT;
+ mlmedebug(vap, mac, IEEE80211_MLME_ASSOC, 0);
+ if (!ieee80211_sta_join(vap, lookup.se))
+ return EIO; /* XXX unique but could be better */
+ return 0;
+}
+
+static __noinline int
+ieee80211_ioctl_setmlme(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211req_mlme mlme;
- struct ieee80211_node *ni;
int error;
if (ireq->i_len != sizeof(mlme))
@@ -1330,72 +1456,19 @@ ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, &mlme, sizeof(mlme));
if (error)
return error;
- switch (mlme.im_op) {
- case IEEE80211_MLME_ASSOC:
- /* XXX ibss/ahdemo */
- if (ic->ic_opmode == IEEE80211_M_STA) {
- struct scanlookup lookup;
-
- lookup.se = NULL;
- lookup.mac = mlme.im_macaddr;
- /* XXX use revised api w/ explicit ssid */
- lookup.esslen = ic->ic_des_ssid[0].len;
- lookup.essid = ic->ic_des_ssid[0].ssid;
- ieee80211_scan_iterate(ic, mlmelookup, &lookup);
- if (lookup.se != NULL &&
- ieee80211_sta_join(ic, lookup.se))
- return 0;
- }
- return EINVAL;
- case IEEE80211_MLME_DISASSOC:
- case IEEE80211_MLME_DEAUTH:
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- /* XXX not quite right */
- ieee80211_new_state(ic, IEEE80211_S_INIT,
- mlme.im_reason);
- break;
- case IEEE80211_M_HOSTAP:
- /* NB: the broadcast address means do 'em all */
- if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) {
- if ((ni = ieee80211_find_node(&ic->ic_sta,
- mlme.im_macaddr)) == NULL)
- return EINVAL;
- domlme(&mlme, ni);
- ieee80211_free_node(ni);
- } else {
- ieee80211_iterate_nodes(&ic->ic_sta,
- domlme, &mlme);
- }
- break;
- default:
- return EINVAL;
- }
- break;
- case IEEE80211_MLME_AUTHORIZE:
- case IEEE80211_MLME_UNAUTHORIZE:
- if (ic->ic_opmode != IEEE80211_M_HOSTAP)
- return EINVAL;
- ni = ieee80211_find_node(&ic->ic_sta, mlme.im_macaddr);
- if (ni == NULL)
- return EINVAL;
- if (mlme.im_op == IEEE80211_MLME_AUTHORIZE)
- ieee80211_node_authorize(ni);
- else
- ieee80211_node_unauthorize(ni);
- ieee80211_free_node(ni);
- break;
- default:
- return EINVAL;
- }
- return 0;
+ if (mlme.im_op == IEEE80211_MLME_ASSOC)
+ return setmlme_assoc(vap, mlme.im_macaddr,
+ vap->iv_des_ssid[0].len, vap->iv_des_ssid[0].ssid);
+ else
+ return setmlme_common(vap, mlme.im_op,
+ mlme.im_macaddr, mlme.im_reason);
}
-static int
-ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_macmac(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
uint8_t mac[IEEE80211_ADDR_LEN];
- const struct ieee80211_aclator *acl = ic->ic_acl;
+ const struct ieee80211_aclator *acl = vap->iv_acl;
int error;
if (ireq->i_len != sizeof(mac))
@@ -1405,57 +1478,59 @@ ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq)
return error;
if (acl == NULL) {
acl = ieee80211_aclator_get("mac");
- if (acl == NULL || !acl->iac_attach(ic))
+ if (acl == NULL || !acl->iac_attach(vap))
return EINVAL;
- ic->ic_acl = acl;
+ vap->iv_acl = acl;
}
if (ireq->i_type == IEEE80211_IOC_ADDMAC)
- acl->iac_add(ic, mac);
+ acl->iac_add(vap, mac);
else
- acl->iac_remove(ic, mac);
+ acl->iac_remove(vap, mac);
return 0;
}
-static int
-ieee80211_ioctl_setmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setmaccmd(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
- const struct ieee80211_aclator *acl = ic->ic_acl;
+ const struct ieee80211_aclator *acl = vap->iv_acl;
switch (ireq->i_val) {
case IEEE80211_MACCMD_POLICY_OPEN:
case IEEE80211_MACCMD_POLICY_ALLOW:
case IEEE80211_MACCMD_POLICY_DENY:
+ case IEEE80211_MACCMD_POLICY_RADIUS:
if (acl == NULL) {
acl = ieee80211_aclator_get("mac");
- if (acl == NULL || !acl->iac_attach(ic))
+ if (acl == NULL || !acl->iac_attach(vap))
return EINVAL;
- ic->ic_acl = acl;
+ vap->iv_acl = acl;
}
- acl->iac_setpolicy(ic, ireq->i_val);
+ acl->iac_setpolicy(vap, ireq->i_val);
break;
case IEEE80211_MACCMD_FLUSH:
if (acl != NULL)
- acl->iac_flush(ic);
+ acl->iac_flush(vap);
/* NB: silently ignore when not in use */
break;
case IEEE80211_MACCMD_DETACH:
if (acl != NULL) {
- ic->ic_acl = NULL;
- acl->iac_detach(ic);
+ vap->iv_acl = NULL;
+ acl->iac_detach(vap);
}
break;
default:
if (acl == NULL)
return EINVAL;
else
- return acl->iac_setioctl(ic, ireq);
+ return acl->iac_setioctl(vap, ireq);
}
return 0;
}
-static int
-ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211req_chanlist list;
u_char chanlist[IEEE80211_CHAN_BYTES];
int i, j, nchan, error;
@@ -1491,11 +1566,12 @@ ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
isclr(chanlist, ic->ic_bsschan->ic_ieee))
ic->ic_bsschan = IEEE80211_CHAN_ANYC;
memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
- return IS_UP_AUTO(ic) ? ieee80211_init(ic, RESCAN) : 0;
+ ieee80211_scan_flush(vap);
+ return ENETRESET;
}
-static int
-ieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setstastats(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
uint8_t macaddr[IEEE80211_ADDR_LEN];
@@ -1511,16 +1587,17 @@ ieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
if (error != 0)
return error;
- ni = ieee80211_find_node(&ic->ic_sta, macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr);
if (ni == NULL)
- return EINVAL; /* XXX */
+ return ENOENT;
+ /* XXX require ni_vap == vap? */
memset(&ni->ni_stats, 0, sizeof(ni->ni_stats));
ieee80211_free_node(ni);
return 0;
}
-static int
-ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setstatxpow(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
struct ieee80211req_sta_txpow txpow;
@@ -1531,23 +1608,24 @@ ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, &txpow, sizeof(txpow));
if (error != 0)
return error;
- ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, txpow.it_macaddr);
if (ni == NULL)
- return EINVAL; /* XXX */
+ return ENOENT;
ni->ni_txpower = txpow.it_txpow;
ieee80211_free_node(ni);
return error;
}
-static int
-ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setwmeparam(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
struct wmeParams *wmep, *chanp;
int isbss, ac;
if ((ic->ic_caps & IEEE80211_C_WME) == 0)
- return EINVAL;
+ return EOPNOTSUPP;
isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS);
ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
@@ -1610,20 +1688,7 @@ ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
(ireq->i_val) == 0;
break;
}
- ieee80211_wme_updateparams(ic);
- return 0;
-}
-
-static int
-cipher2cap(int cipher)
-{
- switch (cipher) {
- case IEEE80211_CIPHER_WEP: return IEEE80211_C_WEP;
- case IEEE80211_CIPHER_AES_OCB: return IEEE80211_C_AES;
- case IEEE80211_CIPHER_AES_CCM: return IEEE80211_C_AES_CCM;
- case IEEE80211_CIPHER_CKIP: return IEEE80211_C_CKIP;
- case IEEE80211_CIPHER_TKIP: return IEEE80211_C_TKIP;
- }
+ ieee80211_wme_updateparams(vap);
return 0;
}
@@ -1666,10 +1731,7 @@ findchannel(struct ieee80211com *ic, int ieee, int mode)
u_int modeflags;
int i;
- KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode));
modeflags = chanflags[mode];
- KASSERT(modeflags != 0 || mode == IEEE80211_MODE_AUTO,
- ("no chanflags for mode %u", mode));
for (i = 0; i < ic->ic_nchans; i++) {
struct ieee80211_channel *c = &ic->ic_channels[i];
@@ -1735,44 +1797,56 @@ check_mode_consistency(const struct ieee80211_channel *c, int mode)
* change or a kick of the state machine.
*/
static int
-setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
+setcurchan(struct ieee80211vap *vap, struct ieee80211_channel *c)
{
+ struct ieee80211com *ic = vap->iv_ic;
int error;
if (c != IEEE80211_CHAN_ANYC) {
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
- !check_mode_consistency(c, ic->ic_des_mode))
- return EINVAL;
- if (ic->ic_state == IEEE80211_S_RUN && c == ic->ic_curchan)
+ if (IEEE80211_IS_CHAN_RADAR(c))
+ return EBUSY; /* XXX better code? */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ if (IEEE80211_IS_CHAN_NOHOSTAP(c))
+ return EINVAL;
+ if (!check_mode_consistency(c, vap->iv_des_mode))
+ return EINVAL;
+ } else if (vap->iv_opmode == IEEE80211_M_IBSS) {
+ if (IEEE80211_IS_CHAN_NOADHOC(c))
+ return EINVAL;
+ }
+ if (vap->iv_state == IEEE80211_S_RUN &&
+ vap->iv_bss->ni_chan == c)
return 0; /* NB: nothing to do */
}
- ic->ic_des_chan = c;
+ vap->iv_des_chan = c;
error = 0;
- if ((ic->ic_opmode == IEEE80211_M_MONITOR ||
- ic->ic_opmode == IEEE80211_M_WDS) &&
- ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
+ if (vap->iv_opmode == IEEE80211_M_MONITOR &&
+ vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
/*
- * Monitor and wds modes can switch directly.
+ * Monitor mode can switch directly.
*/
- ic->ic_curchan = ic->ic_des_chan;
- if (ic->ic_state == IEEE80211_S_RUN)
- ic->ic_set_channel(ic);
+ if (IFNET_IS_UP_RUNNING(vap->iv_ifp)) {
+ /* XXX need state machine for other vap's to follow */
+ ieee80211_setcurchan(ic, vap->iv_des_chan);
+ vap->iv_bss->ni_chan = ic->ic_curchan;
+ } else
+ ic->ic_curchan = vap->iv_des_chan;
} else {
/*
* Need to go through the state machine in case we
* need to reassociate or the like. The state machine
* will pickup the desired channel and avoid scanning.
*/
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, RESCAN);
- else if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
+ if (IS_UP_AUTO(vap))
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ else if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
/*
* When not up+running and a real channel has
* been specified fix the current channel so
* there is immediate feedback; e.g. via ifconfig.
*/
- ic->ic_curchan = ic->ic_des_chan;
+ ic->ic_curchan = vap->iv_des_chan;
}
}
return error;
@@ -1782,10 +1856,11 @@ setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
* Old api for setting the current channel; this is
* deprecated because channel numbers are ambiguous.
*/
-static int
-ieee80211_ioctl_setchannel(struct ieee80211com *ic,
+static __noinline int
+ieee80211_ioctl_setchannel(struct ieee80211vap *vap,
const struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *c;
/* XXX 0xffff overflows 16-bit signed */
@@ -1797,7 +1872,7 @@ ieee80211_ioctl_setchannel(struct ieee80211com *ic,
} else {
struct ieee80211_channel *c2;
- c = findchannel(ic, ireq->i_val, ic->ic_des_mode);
+ c = findchannel(ic, ireq->i_val, vap->iv_des_mode);
if (c == NULL) {
c = findchannel(ic, ireq->i_val,
IEEE80211_MODE_AUTO);
@@ -1816,7 +1891,7 @@ ieee80211_ioctl_setchannel(struct ieee80211com *ic,
* 11g channel returned,
* otherwise we should be ok with what we've got.
*/
- switch (ic->ic_des_mode) {
+ switch (vap->iv_des_mode) {
case IEEE80211_MODE_11B:
if (IEEE80211_IS_CHAN_ANYG(c)) {
c2 = findchannel(ic, ireq->i_val,
@@ -1854,7 +1929,7 @@ ieee80211_ioctl_setchannel(struct ieee80211com *ic,
break;
}
}
- return setcurchan(ic, c);
+ return setcurchan(vap, c);
}
/*
@@ -1862,10 +1937,11 @@ ieee80211_ioctl_setchannel(struct ieee80211com *ic,
* channel description is provide so there is no ambiguity in
* identifying the channel.
*/
-static int
-ieee80211_ioctl_setcurchan(struct ieee80211com *ic,
+static __noinline int
+ieee80211_ioctl_setcurchan(struct ieee80211vap *vap,
const struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel chan, *c;
int error;
@@ -1882,21 +1958,494 @@ ieee80211_ioctl_setcurchan(struct ieee80211com *ic,
if (c == NULL)
return EINVAL;
}
- return setcurchan(ic, c);
+ return setcurchan(vap, c);
+}
+
+static __noinline int
+ieee80211_ioctl_setregdomain(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211_regdomain_req *reg;
+ int error;
+
+ if (ireq->i_len != sizeof(struct ieee80211_regdomain_req))
+ return EINVAL;
+ MALLOC(reg, struct ieee80211_regdomain_req *,
+ sizeof(struct ieee80211_regdomain_req), M_TEMP, M_NOWAIT);
+ if (reg == NULL)
+ return ENOMEM;
+ error = copyin(ireq->i_data, reg, sizeof(*reg));
+ if (error == 0)
+ error = ieee80211_setregdomain(vap, reg);
+ FREE(reg, M_TEMP);
+
+ return (error == 0 ? ENETRESET : error);
+}
+
+static int
+ieee80211_ioctl_setroam(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ if (ireq->i_len != sizeof(vap->iv_roamparms))
+ return EINVAL;
+ /* XXX validate params */
+ /* XXX? ENETRESET to push to device? */
+ return copyin(ireq->i_data, vap->iv_roamparms,
+ sizeof(vap->iv_roamparms));
+}
+
+static int
+checkrate(const struct ieee80211_rateset *rs, int rate)
+{
+ int i;
+
+ if (rate == IEEE80211_FIXED_RATE_NONE)
+ return 1;
+ for (i = 0; i < rs->rs_nrates; i++)
+ if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == rate)
+ return 1;
+ return 0;
+}
+
+static int
+checkmcs(int mcs)
+{
+ if (mcs == IEEE80211_FIXED_RATE_NONE)
+ return 1;
+ if ((mcs & IEEE80211_RATE_MCS) == 0) /* MCS always have 0x80 set */
+ return 0;
+ return (mcs & 0x7f) <= 15; /* XXX could search ht rate set */
+}
+
+static __noinline int
+ieee80211_ioctl_settxparams(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_txparams_req parms; /* XXX stack use? */
+ struct ieee80211_txparam *src, *dst;
+ const struct ieee80211_rateset *rs;
+ int error, i, changed;
+
+ if (ireq->i_len != sizeof(parms))
+ return EINVAL;
+ error = copyin(ireq->i_data, &parms, sizeof(parms));
+ if (error != 0)
+ return error;
+ changed = 0;
+ /* validate parameters and check if anything changed */
+ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_11NA; i++) {
+ if (isclr(ic->ic_modecaps, i))
+ continue;
+ src = &parms.params[i];
+ dst = &vap->iv_txparms[i];
+ rs = &ic->ic_sup_rates[i];
+ if (src->ucastrate != dst->ucastrate) {
+ if (!checkrate(rs, src->ucastrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->mcastrate != dst->mcastrate) {
+ if (!checkrate(rs, src->mcastrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->mgmtrate != dst->mgmtrate) {
+ if (!checkrate(rs, src->mgmtrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->maxretry != dst->maxretry) /* NB: no bounds */
+ changed++;
+ }
+ /* 11n parameters are handled differently */
+ for (; i < IEEE80211_MODE_MAX; i++) {
+ if (isclr(ic->ic_modecaps, i))
+ continue;
+ src = &parms.params[i];
+ dst = &vap->iv_txparms[i];
+ rs = &ic->ic_sup_rates[i == IEEE80211_MODE_11NA ?
+ IEEE80211_MODE_11A : IEEE80211_MODE_11G];
+ if (src->ucastrate != dst->ucastrate) {
+ if (!checkmcs(src->ucastrate) &&
+ !checkrate(rs, src->ucastrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->mcastrate != dst->mcastrate) {
+ if (!checkmcs(src->mcastrate) &&
+ !checkrate(rs, src->mcastrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->mgmtrate != dst->mgmtrate) {
+ if (!checkmcs(src->mgmtrate) &&
+ !checkrate(rs, src->mgmtrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->maxretry != dst->maxretry) /* NB: no bounds */
+ changed++;
+ }
+ if (changed) {
+ /*
+ * Copy new parameters in place and notify the
+ * driver so it can push state to the device.
+ */
+ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) {
+ if (isset(ic->ic_modecaps, i))
+ vap->iv_txparms[i] = parms.params[i];
+ }
+ /* XXX could be more intelligent,
+ e.g. don't reset if setting not being used */
+ return ENETRESET;
+ }
+ return 0;
+}
+
+/*
+ * Application Information Element support.
+ */
+static int
+setappie(struct ieee80211_appie **aie, const struct ieee80211req *ireq)
+{
+ struct ieee80211_appie *app = *aie;
+ struct ieee80211_appie *napp;
+ int error;
+
+ if (ireq->i_len == 0) { /* delete any existing ie */
+ if (app != NULL) {
+ *aie = NULL; /* XXX racey */
+ FREE(app, M_80211_NODE_IE);
+ }
+ return 0;
+ }
+ if (!(2 <= ireq->i_len && ireq->i_len <= IEEE80211_MAX_APPIE))
+ return EINVAL;
+ /*
+ * Allocate a new appie structure and copy in the user data.
+ * When done swap in the new structure. Note that we do not
+ * guard against users holding a ref to the old structure;
+ * this must be handled outside this code.
+ *
+ * XXX bad bad bad
+ */
+ MALLOC(napp, struct ieee80211_appie *,
+ sizeof(struct ieee80211_appie) + ireq->i_len, M_80211_NODE_IE, M_NOWAIT);
+ if (napp == NULL)
+ return ENOMEM;
+ /* XXX holding ic lock */
+ error = copyin(ireq->i_data, napp->ie_data, ireq->i_len);
+ if (error) {
+ FREE(napp, M_80211_NODE_IE);
+ return error;
+ }
+ napp->ie_len = ireq->i_len;
+ *aie = napp;
+ if (app != NULL)
+ FREE(app, M_80211_NODE_IE);
+ return 0;
+}
+
+static void
+setwparsnie(struct ieee80211vap *vap, uint8_t *ie, int space)
+{
+ /* validate data is present as best we can */
+ if (space == 0 || 2+ie[1] > space)
+ return;
+ if (ie[0] == IEEE80211_ELEMID_VENDOR)
+ vap->iv_wpa_ie = ie;
+ else if (ie[0] == IEEE80211_ELEMID_RSN)
+ vap->iv_rsn_ie = ie;
+}
+
+static __noinline int
+ieee80211_ioctl_setappie_locked(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq, int fc0)
+{
+ int error;
+
+ IEEE80211_LOCK_ASSERT(vap->iv_ic);
+
+ switch (fc0 & IEEE80211_FC0_SUBTYPE_MASK) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_IBSS) {
+ error = EINVAL;
+ break;
+ }
+ error = setappie(&vap->iv_appie_beacon, ireq);
+ if (error == 0)
+ ieee80211_beacon_notify(vap, IEEE80211_BEACON_APPIE);
+ break;
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ error = setappie(&vap->iv_appie_proberesp, ireq);
+ break;
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ error = setappie(&vap->iv_appie_assocresp, ireq);
+ else
+ error = EINVAL;
+ break;
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ error = setappie(&vap->iv_appie_probereq, ireq);
+ break;
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ error = setappie(&vap->iv_appie_assocreq, ireq);
+ else
+ error = EINVAL;
+ break;
+ case (IEEE80211_APPIE_WPA & IEEE80211_FC0_SUBTYPE_MASK):
+ error = setappie(&vap->iv_appie_wpa, ireq);
+ if (error == 0) {
+ /*
+ * Must split single blob of data into separate
+ * WPA and RSN ie's because they go in different
+ * locations in the mgt frames.
+ * XXX use IEEE80211_IOC_WPA2 so user code does split
+ */
+ vap->iv_wpa_ie = NULL;
+ vap->iv_rsn_ie = NULL;
+ if (vap->iv_appie_wpa != NULL) {
+ struct ieee80211_appie *appie =
+ vap->iv_appie_wpa;
+ uint8_t *data = appie->ie_data;
+
+ /* XXX ie length validate is painful, cheat */
+ setwparsnie(vap, data, appie->ie_len);
+ setwparsnie(vap, data + 2 + data[1],
+ appie->ie_len - (2 + data[1]));
+ }
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS) {
+ /*
+ * Must rebuild beacon frame as the update
+ * mechanism doesn't handle WPA/RSN ie's.
+ * Could extend it but it doesn't normally
+ * change; this is just to deal with hostapd
+ * plumbing the ie after the interface is up.
+ */
+ error = ENETRESET;
+ }
+ }
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+static __noinline int
+ieee80211_ioctl_setappie(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ int error;
+ uint8_t fc0;
+
+ fc0 = ireq->i_val & 0xff;
+ if ((fc0 & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
+ return EINVAL;
+ /* NB: could check iv_opmode and reject but hardly worth the effort */
+ IEEE80211_LOCK(ic);
+ error = ieee80211_ioctl_setappie_locked(vap, ireq, fc0);
+ IEEE80211_UNLOCK(ic);
+ return error;
+}
+
+static __noinline int
+ieee80211_ioctl_chanswitch(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_chanswitch_req csr;
+ struct ieee80211_channel *c;
+ int error;
+
+ if (ireq->i_len != sizeof(csr))
+ return EINVAL;
+ error = copyin(ireq->i_data, &csr, sizeof(csr));
+ if (error != 0)
+ return error;
+ if ((vap->iv_flags & IEEE80211_F_DOTH) == 0)
+ return EINVAL;
+ c = ieee80211_find_channel(ic,
+ csr.csa_chan.ic_freq, csr.csa_chan.ic_flags);
+ if (c == NULL)
+ return ENOENT;
+ IEEE80211_LOCK(ic);
+ if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0)
+ ieee80211_csa_startswitch(ic, c, csr.csa_mode, csr.csa_count);
+ else
+ error = EBUSY;
+ IEEE80211_UNLOCK(ic);
+ return error;
+}
+
+static __noinline int
+ieee80211_ioctl_scanreq(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+#define IEEE80211_IOC_SCAN_FLAGS \
+ (IEEE80211_IOC_SCAN_NOPICK | IEEE80211_IOC_SCAN_ACTIVE | \
+ IEEE80211_IOC_SCAN_PICK1ST | IEEE80211_IOC_SCAN_BGSCAN | \
+ IEEE80211_IOC_SCAN_ONCE | IEEE80211_IOC_SCAN_NOBCAST | \
+ IEEE80211_IOC_SCAN_NOJOIN | IEEE80211_IOC_SCAN_FLUSH | \
+ IEEE80211_IOC_SCAN_CHECK)
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_req sr; /* XXX off stack? */
+ int error, i;
+
+ /* NB: parent must be running */
+ if ((ic->ic_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return ENXIO;
+
+ if (ireq->i_len != sizeof(sr))
+ return EINVAL;
+ error = copyin(ireq->i_data, &sr, sizeof(sr));
+ if (error != 0)
+ return error;
+ /* convert duration */
+ if (sr.sr_duration == IEEE80211_IOC_SCAN_FOREVER)
+ sr.sr_duration = IEEE80211_SCAN_FOREVER;
+ else {
+ if (sr.sr_duration < IEEE80211_IOC_SCAN_DURATION_MIN ||
+ sr.sr_duration > IEEE80211_IOC_SCAN_DURATION_MAX)
+ return EINVAL;
+ sr.sr_duration = msecs_to_ticks(sr.sr_duration);
+ if (sr.sr_duration < 1)
+ sr.sr_duration = 1;
+ }
+ /* convert min/max channel dwell */
+ if (sr.sr_mindwell != 0) {
+ sr.sr_mindwell = msecs_to_ticks(sr.sr_mindwell);
+ if (sr.sr_mindwell < 1)
+ sr.sr_mindwell = 1;
+ }
+ if (sr.sr_maxdwell != 0) {
+ sr.sr_maxdwell = msecs_to_ticks(sr.sr_maxdwell);
+ if (sr.sr_maxdwell < 1)
+ sr.sr_maxdwell = 1;
+ }
+ /* NB: silently reduce ssid count to what is supported */
+ if (sr.sr_nssid > IEEE80211_SCAN_MAX_SSID)
+ sr.sr_nssid = IEEE80211_SCAN_MAX_SSID;
+ for (i = 0; i < sr.sr_nssid; i++)
+ if (sr.sr_ssid[i].len > IEEE80211_NWID_LEN)
+ return EINVAL;
+ /* cleanse flags just in case, could reject if invalid flags */
+ sr.sr_flags &= IEEE80211_IOC_SCAN_FLAGS;
+ /*
+ * Add an implicit NOPICK if the vap is not marked UP. This
+ * allows applications to scan without joining a bss (or picking
+ * a channel and setting up a bss) and without forcing manual
+ * roaming mode--you just need to mark the parent device UP.
+ */
+ if ((vap->iv_ifp->if_flags & IFF_UP) == 0)
+ sr.sr_flags |= IEEE80211_IOC_SCAN_NOPICK;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: flags 0x%x%s duration 0x%x mindwell %u maxdwell %u nssid %d\n",
+ __func__, sr.sr_flags,
+ (vap->iv_ifp->if_flags & IFF_UP) == 0 ? " (!IFF_UP)" : "",
+ sr.sr_duration, sr.sr_mindwell, sr.sr_maxdwell, sr.sr_nssid);
+ /*
+ * If we are in INIT state then the driver has never had a chance
+ * to setup hardware state to do a scan; we must use the state
+ * machine to get us up to the SCAN state but once we reach SCAN
+ * state we then want to use the supplied params. Stash the
+ * parameters in the vap and mark IEEE80211_FEXT_SCANREQ; the
+ * state machines will recognize this and use the stashed params
+ * to issue the scan request.
+ *
+ * Otherwise just invoke the scan machinery directly.
+ */
+ IEEE80211_LOCK(ic);
+ if (vap->iv_state == IEEE80211_S_INIT) {
+ /* NB: clobbers previous settings */
+ vap->iv_scanreq_flags = sr.sr_flags;
+ vap->iv_scanreq_duration = sr.sr_duration;
+ vap->iv_scanreq_nssid = sr.sr_nssid;
+ for (i = 0; i < sr.sr_nssid; i++) {
+ vap->iv_scanreq_ssid[i].len = sr.sr_ssid[i].len;
+ memcpy(vap->iv_scanreq_ssid[i].ssid, sr.sr_ssid[i].ssid,
+ sr.sr_ssid[i].len);
+ }
+ vap->iv_flags_ext |= IEEE80211_FEXT_SCANREQ;
+ IEEE80211_UNLOCK(ic);
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ } else {
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
+ IEEE80211_UNLOCK(ic);
+ /* XXX neeed error return codes */
+ if (sr.sr_flags & IEEE80211_IOC_SCAN_CHECK) {
+ (void) ieee80211_check_scan(vap, sr.sr_flags,
+ sr.sr_duration, sr.sr_mindwell, sr.sr_maxdwell,
+ sr.sr_nssid,
+ /* NB: cheat, we assume structures are compatible */
+ (const struct ieee80211_scan_ssid *) &sr.sr_ssid[0]);
+ } else {
+ (void) ieee80211_start_scan(vap, sr.sr_flags,
+ sr.sr_duration, sr.sr_mindwell, sr.sr_maxdwell,
+ sr.sr_nssid,
+ /* NB: cheat, we assume structures are compatible */
+ (const struct ieee80211_scan_ssid *) &sr.sr_ssid[0]);
+ }
+ }
+ return error;
+#undef IEEE80211_IOC_SCAN_FLAGS
+}
+
+static __noinline int
+ieee80211_ioctl_setstavlan(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ struct ieee80211_node *ni;
+ struct ieee80211req_sta_vlan vlan;
+ int error;
+
+ if (ireq->i_len != sizeof(vlan))
+ return EINVAL;
+ error = copyin(ireq->i_data, &vlan, sizeof(vlan));
+ if (error != 0)
+ return error;
+ if (!IEEE80211_ADDR_EQ(vlan.sv_macaddr, zerobssid)) {
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap,
+ vlan.sv_macaddr);
+ if (ni == NULL)
+ return ENOENT;
+ } else
+ ni = ieee80211_ref_node(vap->iv_bss);
+ ni->ni_vlan = vlan.sv_vlan;
+ ieee80211_free_node(ni);
+ return error;
+}
+
+static int
+isvap11g(const struct ieee80211vap *vap)
+{
+ const struct ieee80211_node *bss = vap->iv_bss;
+ return bss->ni_chan != IEEE80211_CHAN_ANYC &&
+ IEEE80211_IS_CHAN_ANYG(bss->ni_chan);
}
static int
-ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
+isvapht(const struct ieee80211vap *vap)
{
- static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
- struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
+ const struct ieee80211_node *bss = vap->iv_bss;
+ return bss->ni_chan != IEEE80211_CHAN_ANYC &&
+ IEEE80211_IS_CHAN_HT(bss->ni_chan);
+}
+
+static __noinline int
+ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211req *ireq)
+{
+ struct ieee80211com *ic = vap->iv_ic;
int error;
const struct ieee80211_authenticator *auth;
uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
char tmpssid[IEEE80211_NWID_LEN];
uint8_t tmpbssid[IEEE80211_ADDR_LEN];
struct ieee80211_key *k;
- int j, caps;
u_int kid;
error = 0;
@@ -1908,39 +2457,37 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
error = copyin(ireq->i_data, tmpssid, ireq->i_len);
if (error)
break;
- memset(ic->ic_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
- ic->ic_des_ssid[0].len = ireq->i_len;
- memcpy(ic->ic_des_ssid[0].ssid, tmpssid, ireq->i_len);
- ic->ic_des_nssid = (ireq->i_len > 0);
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, RESCAN);
+ memset(vap->iv_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
+ vap->iv_des_ssid[0].len = ireq->i_len;
+ memcpy(vap->iv_des_ssid[0].ssid, tmpssid, ireq->i_len);
+ vap->iv_des_nssid = (ireq->i_len > 0);
+ error = ENETRESET;
break;
case IEEE80211_IOC_WEP:
switch (ireq->i_val) {
case IEEE80211_WEP_OFF:
- ic->ic_flags &= ~IEEE80211_F_PRIVACY;
- ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+ vap->iv_flags &= ~IEEE80211_F_PRIVACY;
+ vap->iv_flags &= ~IEEE80211_F_DROPUNENC;
break;
case IEEE80211_WEP_ON:
- ic->ic_flags |= IEEE80211_F_PRIVACY;
- ic->ic_flags |= IEEE80211_F_DROPUNENC;
+ vap->iv_flags |= IEEE80211_F_PRIVACY;
+ vap->iv_flags |= IEEE80211_F_DROPUNENC;
break;
case IEEE80211_WEP_MIXED:
- ic->ic_flags |= IEEE80211_F_PRIVACY;
- ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+ vap->iv_flags |= IEEE80211_F_PRIVACY;
+ vap->iv_flags &= ~IEEE80211_F_DROPUNENC;
break;
}
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, RESCAN);
+ error = ENETRESET;
break;
case IEEE80211_IOC_WEPKEY:
kid = (u_int) ireq->i_val;
if (kid >= IEEE80211_WEP_NKID)
return EINVAL;
- k = &ic->ic_nw_keys[kid];
+ k = &vap->iv_nw_keys[kid];
if (ireq->i_len == 0) {
/* zero-len =>'s delete any existing key */
- (void) ieee80211_crypto_delkey(ic, k);
+ (void) ieee80211_crypto_delkey(vap, k);
break;
}
if (ireq->i_len > sizeof(tmpkey))
@@ -1949,24 +2496,24 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
error = copyin(ireq->i_data, tmpkey, ireq->i_len);
if (error)
break;
- ieee80211_key_update_begin(ic);
+ ieee80211_key_update_begin(vap);
k->wk_keyix = kid; /* NB: force fixed key id */
- if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP,
+ if (ieee80211_crypto_newkey(vap, IEEE80211_CIPHER_WEP,
IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k)) {
k->wk_keylen = ireq->i_len;
memcpy(k->wk_key, tmpkey, sizeof(tmpkey));
- if (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr))
+ if (!ieee80211_crypto_setkey(vap, k, vap->iv_myaddr))
error = EINVAL;
} else
error = EINVAL;
- ieee80211_key_update_end(ic);
+ ieee80211_key_update_end(vap);
break;
case IEEE80211_IOC_WEPTXKEY:
kid = (u_int) ireq->i_val;
if (kid >= IEEE80211_WEP_NKID &&
(uint16_t) kid != IEEE80211_KEYIX_NONE)
return EINVAL;
- ic->ic_def_txkey = kid;
+ vap->iv_def_txkey = kid;
break;
case IEEE80211_IOC_AUTHMODE:
switch (ireq->i_val) {
@@ -1984,74 +2531,66 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
}
switch (ireq->i_val) {
case IEEE80211_AUTH_WPA: /* WPA w/ 802.1x */
- ic->ic_flags |= IEEE80211_F_PRIVACY;
+ vap->iv_flags |= IEEE80211_F_PRIVACY;
ireq->i_val = IEEE80211_AUTH_8021X;
break;
case IEEE80211_AUTH_OPEN: /* open */
- ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY);
+ vap->iv_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY);
break;
case IEEE80211_AUTH_SHARED: /* shared-key */
case IEEE80211_AUTH_8021X: /* 802.1x */
- ic->ic_flags &= ~IEEE80211_F_WPA;
+ vap->iv_flags &= ~IEEE80211_F_WPA;
/* both require a key so mark the PRIVACY capability */
- ic->ic_flags |= IEEE80211_F_PRIVACY;
+ vap->iv_flags |= IEEE80211_F_PRIVACY;
break;
case IEEE80211_AUTH_AUTO: /* auto */
- ic->ic_flags &= ~IEEE80211_F_WPA;
+ vap->iv_flags &= ~IEEE80211_F_WPA;
/* XXX PRIVACY handling? */
/* XXX what's the right way to do this? */
break;
}
/* NB: authenticator attach/detach happens on state change */
- ic->ic_bss->ni_authmode = ireq->i_val;
+ vap->iv_bss->ni_authmode = ireq->i_val;
/* XXX mixed/mode/usage? */
- ic->ic_auth = auth;
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, RESCAN);
+ vap->iv_auth = auth;
+ error = ENETRESET;
break;
case IEEE80211_IOC_CHANNEL:
- error = ieee80211_ioctl_setchannel(ic, ireq);
+ error = ieee80211_ioctl_setchannel(vap, ireq);
break;
case IEEE80211_IOC_POWERSAVE:
switch (ireq->i_val) {
case IEEE80211_POWERSAVE_OFF:
- if (ic->ic_flags & IEEE80211_F_PMGTON) {
- ic->ic_flags &= ~IEEE80211_F_PMGTON;
- error = ENETRESET;
+ if (vap->iv_flags & IEEE80211_F_PMGTON) {
+ ieee80211_syncflag(vap, -IEEE80211_F_PMGTON);
+ error = ERESTART;
}
break;
case IEEE80211_POWERSAVE_ON:
- if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
- error = EINVAL;
- else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
- ic->ic_flags |= IEEE80211_F_PMGTON;
- error = ENETRESET;
+ if ((vap->iv_caps & IEEE80211_C_PMGT) == 0)
+ error = EOPNOTSUPP;
+ else if ((vap->iv_flags & IEEE80211_F_PMGTON) == 0) {
+ ieee80211_syncflag(vap, IEEE80211_F_PMGTON);
+ error = ERESTART;
}
break;
default:
error = EINVAL;
break;
}
- if (error == ENETRESET) {
- /*
- * Switching in+out of power save mode
- * should not require a state change.
- */
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
- }
break;
case IEEE80211_IOC_POWERSAVESLEEP:
if (ireq->i_val < 0)
return EINVAL;
ic->ic_lintval = ireq->i_val;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ error = ERESTART;
break;
case IEEE80211_IOC_RTSTHRESHOLD:
if (!(IEEE80211_RTS_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_RTS_MAX))
return EINVAL;
- ic->ic_rtsthreshold = ireq->i_val;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ vap->iv_rtsthreshold = ireq->i_val;
+ error = ERESTART;
break;
case IEEE80211_IOC_PROTMODE:
if (ireq->i_val > IEEE80211_PROT_RTSCTS)
@@ -2060,158 +2599,96 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
/* NB: if not operating in 11g this can wait */
if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ error = ERESTART;
break;
case IEEE80211_IOC_TXPOWER:
if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
- return EINVAL;
+ return EOPNOTSUPP;
if (!(IEEE80211_TXPOWER_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_TXPOWER_MAX))
return EINVAL;
ic->ic_txpowlimit = ireq->i_val;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ error = ERESTART;
break;
case IEEE80211_IOC_ROAMING:
if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val &&
ireq->i_val <= IEEE80211_ROAMING_MANUAL))
return EINVAL;
- ic->ic_roaming = ireq->i_val;
+ vap->iv_roaming = ireq->i_val;
/* XXXX reset? */
break;
case IEEE80211_IOC_PRIVACY:
if (ireq->i_val) {
/* XXX check for key state? */
- ic->ic_flags |= IEEE80211_F_PRIVACY;
+ vap->iv_flags |= IEEE80211_F_PRIVACY;
} else
- ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+ vap->iv_flags &= ~IEEE80211_F_PRIVACY;
+ /* XXX ERESTART? */
break;
case IEEE80211_IOC_DROPUNENCRYPTED:
if (ireq->i_val)
- ic->ic_flags |= IEEE80211_F_DROPUNENC;
+ vap->iv_flags |= IEEE80211_F_DROPUNENC;
else
- ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+ vap->iv_flags &= ~IEEE80211_F_DROPUNENC;
+ /* XXX ERESTART? */
break;
case IEEE80211_IOC_WPAKEY:
- error = ieee80211_ioctl_setkey(ic, ireq);
+ error = ieee80211_ioctl_setkey(vap, ireq);
break;
case IEEE80211_IOC_DELKEY:
- error = ieee80211_ioctl_delkey(ic, ireq);
+ error = ieee80211_ioctl_delkey(vap, ireq);
break;
case IEEE80211_IOC_MLME:
- error = ieee80211_ioctl_setmlme(ic, ireq);
- break;
- case IEEE80211_IOC_OPTIE:
- error = ieee80211_ioctl_setoptie(ic, ireq);
+ error = ieee80211_ioctl_setmlme(vap, ireq);
break;
case IEEE80211_IOC_COUNTERMEASURES:
if (ireq->i_val) {
- if ((ic->ic_flags & IEEE80211_F_WPA) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_COUNTERM;
+ if ((vap->iv_flags & IEEE80211_F_WPA) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags |= IEEE80211_F_COUNTERM;
} else
- ic->ic_flags &= ~IEEE80211_F_COUNTERM;
+ vap->iv_flags &= ~IEEE80211_F_COUNTERM;
+ /* XXX ERESTART? */
break;
case IEEE80211_IOC_WPA:
if (ireq->i_val > 3)
return EINVAL;
/* XXX verify ciphers available */
- ic->ic_flags &= ~IEEE80211_F_WPA;
+ vap->iv_flags &= ~IEEE80211_F_WPA;
switch (ireq->i_val) {
case 1:
- ic->ic_flags |= IEEE80211_F_WPA1;
+ vap->iv_flags |= IEEE80211_F_WPA1;
break;
case 2:
- ic->ic_flags |= IEEE80211_F_WPA2;
+ vap->iv_flags |= IEEE80211_F_WPA2;
break;
case 3:
- ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
+ vap->iv_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
break;
}
- error = ENETRESET;
+ error = ERESTART; /* NB: can change beacon frame */
break;
case IEEE80211_IOC_WME:
if (ireq->i_val) {
- if ((ic->ic_caps & IEEE80211_C_WME) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_WME;
+ if ((vap->iv_caps & IEEE80211_C_WME) == 0)
+ return EOPNOTSUPP;
+ ieee80211_syncflag(vap, IEEE80211_F_WME);
} else
- ic->ic_flags &= ~IEEE80211_F_WME;
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, 0);
+ ieee80211_syncflag(vap, -IEEE80211_F_WME);
+ error = ERESTART; /* NB: can change beacon frame */
break;
case IEEE80211_IOC_HIDESSID:
if (ireq->i_val)
- ic->ic_flags |= IEEE80211_F_HIDESSID;
+ vap->iv_flags |= IEEE80211_F_HIDESSID;
else
- ic->ic_flags &= ~IEEE80211_F_HIDESSID;
- error = ENETRESET;
+ vap->iv_flags &= ~IEEE80211_F_HIDESSID;
+ error = ERESTART; /* XXX ENETRESET? */
break;
case IEEE80211_IOC_APBRIDGE:
if (ireq->i_val == 0)
- ic->ic_flags |= IEEE80211_F_NOBRIDGE;
+ vap->iv_flags |= IEEE80211_F_NOBRIDGE;
else
- ic->ic_flags &= ~IEEE80211_F_NOBRIDGE;
- break;
- case IEEE80211_IOC_MCASTCIPHER:
- if ((ic->ic_caps & cipher2cap(ireq->i_val)) == 0 &&
- !ieee80211_crypto_available(ireq->i_val))
- return EINVAL;
- rsn->rsn_mcastcipher = ireq->i_val;
- error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
- break;
- case IEEE80211_IOC_MCASTKEYLEN:
- if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
- return EINVAL;
- /* XXX no way to verify driver capability */
- rsn->rsn_mcastkeylen = ireq->i_val;
- error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
- break;
- case IEEE80211_IOC_UCASTCIPHERS:
- /*
- * Convert user-specified cipher set to the set
- * we can support (via hardware or software).
- * NB: this logic intentionally ignores unknown and
- * unsupported ciphers so folks can specify 0xff or
- * similar and get all available ciphers.
- */
- caps = 0;
- for (j = 1; j < 32; j++) /* NB: skip WEP */
- if ((ireq->i_val & (1<<j)) &&
- ((ic->ic_caps & cipher2cap(j)) ||
- ieee80211_crypto_available(j)))
- caps |= 1<<j;
- if (caps == 0) /* nothing available */
- return EINVAL;
- /* XXX verify ciphers ok for unicast use? */
- /* XXX disallow if running as it'll have no effect */
- rsn->rsn_ucastcipherset = caps;
- error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
- break;
- case IEEE80211_IOC_UCASTCIPHER:
- if ((rsn->rsn_ucastcipherset & cipher2cap(ireq->i_val)) == 0)
- return EINVAL;
- rsn->rsn_ucastcipher = ireq->i_val;
- break;
- case IEEE80211_IOC_UCASTKEYLEN:
- if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
- return EINVAL;
- /* XXX no way to verify driver capability */
- rsn->rsn_ucastkeylen = ireq->i_val;
- break;
- case IEEE80211_IOC_DRIVER_CAPS:
- /* NB: for testing */
- ic->ic_caps = (((uint16_t) ireq->i_val) << 16) |
- ((uint16_t) ireq->i_len);
- break;
- case IEEE80211_IOC_KEYMGTALGS:
- /* XXX check */
- rsn->rsn_keymgmtset = ireq->i_val;
- error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
- break;
- case IEEE80211_IOC_RSNCAPS:
- /* XXX check */
- rsn->rsn_caps = ireq->i_val;
- error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+ vap->iv_flags &= ~IEEE80211_F_NOBRIDGE;
break;
case IEEE80211_IOC_BSSID:
if (ireq->i_len != sizeof(tmpbssid))
@@ -2219,39 +2696,71 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
error = copyin(ireq->i_data, tmpbssid, ireq->i_len);
if (error)
break;
- IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid);
- if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid))
- ic->ic_flags &= ~IEEE80211_F_DESBSSID;
+ IEEE80211_ADDR_COPY(vap->iv_des_bssid, tmpbssid);
+ if (IEEE80211_ADDR_EQ(vap->iv_des_bssid, zerobssid))
+ vap->iv_flags &= ~IEEE80211_F_DESBSSID;
else
- ic->ic_flags |= IEEE80211_F_DESBSSID;
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, RESCAN);
+ vap->iv_flags |= IEEE80211_F_DESBSSID;
+ error = ENETRESET;
break;
case IEEE80211_IOC_CHANLIST:
- error = ieee80211_ioctl_setchanlist(ic, ireq);
+ error = ieee80211_ioctl_setchanlist(vap, ireq);
break;
+#define OLD_IEEE80211_IOC_SCAN_REQ 23
+#ifdef OLD_IEEE80211_IOC_SCAN_REQ
+ case OLD_IEEE80211_IOC_SCAN_REQ:
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: active scan request\n", __func__);
+ /*
+ * If we are in INIT state then the driver has never
+ * had a chance to setup hardware state to do a scan;
+ * use the state machine to get us up the SCAN state.
+ * Otherwise just invoke the scan machinery to start
+ * a one-time scan.
+ */
+ if (vap->iv_state == IEEE80211_S_INIT)
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ else
+ (void) ieee80211_start_scan(vap,
+ IEEE80211_SCAN_ACTIVE |
+ IEEE80211_SCAN_NOPICK |
+ IEEE80211_SCAN_ONCE,
+ IEEE80211_SCAN_FOREVER, 0, 0,
+ /* XXX use ioctl params */
+ vap->iv_des_nssid, vap->iv_des_ssid);
+ break;
+#endif /* OLD_IEEE80211_IOC_SCAN_REQ */
case IEEE80211_IOC_SCAN_REQ:
- if (!IS_UP(ic))
- return EINVAL;
- (void) ieee80211_start_scan(ic,
- IEEE80211_SCAN_ACTIVE |
- IEEE80211_SCAN_NOPICK |
- IEEE80211_SCAN_ONCE, IEEE80211_SCAN_FOREVER,
- /* XXX use ioctl params */
- ic->ic_des_nssid, ic->ic_des_ssid);
+ error = ieee80211_ioctl_scanreq(vap, ireq);
+ break;
+ case IEEE80211_IOC_SCAN_CANCEL:
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: cancel scan\n", __func__);
+ ieee80211_cancel_scan(vap);
+ break;
+ case IEEE80211_IOC_HTCONF:
+ if (ireq->i_val & 1)
+ ieee80211_syncflag_ext(vap, IEEE80211_FEXT_HT);
+ else
+ ieee80211_syncflag_ext(vap, -IEEE80211_FEXT_HT);
+ if (ireq->i_val & 2)
+ ieee80211_syncflag_ext(vap, IEEE80211_FEXT_USEHT40);
+ else
+ ieee80211_syncflag_ext(vap, -IEEE80211_FEXT_USEHT40);
+ error = ENETRESET;
break;
case IEEE80211_IOC_ADDMAC:
case IEEE80211_IOC_DELMAC:
- error = ieee80211_ioctl_macmac(ic, ireq);
+ error = ieee80211_ioctl_macmac(vap, ireq);
break;
case IEEE80211_IOC_MACCMD:
- error = ieee80211_ioctl_setmaccmd(ic, ireq);
+ error = ieee80211_ioctl_setmaccmd(vap, ireq);
break;
case IEEE80211_IOC_STA_STATS:
- error = ieee80211_ioctl_setstastats(ic, ireq);
+ error = ieee80211_ioctl_setstastats(vap, ireq);
break;
case IEEE80211_IOC_STA_TXPOW:
- error = ieee80211_ioctl_setstatxpow(ic, ireq);
+ error = ieee80211_ioctl_setstatxpow(vap, ireq);
break;
case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
@@ -2259,22 +2768,22 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */
- error = ieee80211_ioctl_setwmeparam(ic, ireq);
+ error = ieee80211_ioctl_setwmeparam(vap, ireq);
break;
case IEEE80211_IOC_DTIM_PERIOD:
- if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
- ic->ic_opmode != IEEE80211_M_IBSS)
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_IBSS)
return EINVAL;
if (IEEE80211_DTIM_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_DTIM_MAX) {
- ic->ic_dtim_period = ireq->i_val;
+ vap->iv_dtim_period = ireq->i_val;
error = ENETRESET; /* requires restart */
} else
error = EINVAL;
break;
case IEEE80211_IOC_BEACON_INTERVAL:
- if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
- ic->ic_opmode != IEEE80211_M_IBSS)
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_IBSS)
return EINVAL;
if (IEEE80211_BINTVAL_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_BINTVAL_MAX) {
@@ -2285,210 +2794,258 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
break;
case IEEE80211_IOC_PUREG:
if (ireq->i_val)
- ic->ic_flags |= IEEE80211_F_PUREG;
+ vap->iv_flags |= IEEE80211_F_PUREG;
else
- ic->ic_flags &= ~IEEE80211_F_PUREG;
+ vap->iv_flags &= ~IEEE80211_F_PUREG;
/* NB: reset only if we're operating on an 11g channel */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
+ if (isvap11g(vap))
error = ENETRESET;
break;
case IEEE80211_IOC_FF:
if (ireq->i_val) {
- if ((ic->ic_caps & IEEE80211_C_FF) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_FF;
+ if ((vap->iv_caps & IEEE80211_C_FF) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags |= IEEE80211_F_FF;
} else
- ic->ic_flags &= ~IEEE80211_F_FF;
- error = ENETRESET;
+ vap->iv_flags &= ~IEEE80211_F_FF;
+ error = ERESTART;
break;
case IEEE80211_IOC_TURBOP:
if (ireq->i_val) {
- if ((ic->ic_caps & IEEE80211_C_TURBOP) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_TURBOP;
+ if ((vap->iv_caps & IEEE80211_C_TURBOP) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags |= IEEE80211_F_TURBOP;
} else
- ic->ic_flags &= ~IEEE80211_F_TURBOP;
+ vap->iv_flags &= ~IEEE80211_F_TURBOP;
error = ENETRESET;
break;
case IEEE80211_IOC_BGSCAN:
if (ireq->i_val) {
- if ((ic->ic_caps & IEEE80211_C_BGSCAN) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_BGSCAN;
+ if ((vap->iv_caps & IEEE80211_C_BGSCAN) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags |= IEEE80211_F_BGSCAN;
} else
- ic->ic_flags &= ~IEEE80211_F_BGSCAN;
+ vap->iv_flags &= ~IEEE80211_F_BGSCAN;
break;
case IEEE80211_IOC_BGSCAN_IDLE:
if (ireq->i_val >= IEEE80211_BGSCAN_IDLE_MIN)
- ic->ic_bgscanidle = ireq->i_val*hz/1000;
+ vap->iv_bgscanidle = ireq->i_val*hz/1000;
else
error = EINVAL;
break;
case IEEE80211_IOC_BGSCAN_INTERVAL:
if (ireq->i_val >= IEEE80211_BGSCAN_INTVAL_MIN)
- ic->ic_bgscanintvl = ireq->i_val*hz;
+ vap->iv_bgscanintvl = ireq->i_val*hz;
else
error = EINVAL;
break;
case IEEE80211_IOC_SCANVALID:
if (ireq->i_val >= IEEE80211_SCAN_VALID_MIN)
- ic->ic_scanvalid = ireq->i_val*hz;
+ vap->iv_scanvalid = ireq->i_val*hz;
else
error = EINVAL;
break;
- case IEEE80211_IOC_ROAM_RSSI_11A:
- ic->ic_roam.rssi11a = ireq->i_val;
- break;
- case IEEE80211_IOC_ROAM_RSSI_11B:
- ic->ic_roam.rssi11bOnly = ireq->i_val;
- break;
- case IEEE80211_IOC_ROAM_RSSI_11G:
- ic->ic_roam.rssi11b = ireq->i_val;
- break;
- case IEEE80211_IOC_ROAM_RATE_11A:
- ic->ic_roam.rate11a = ireq->i_val & IEEE80211_RATE_VAL;
- break;
- case IEEE80211_IOC_ROAM_RATE_11B:
- ic->ic_roam.rate11bOnly = ireq->i_val & IEEE80211_RATE_VAL;
- break;
- case IEEE80211_IOC_ROAM_RATE_11G:
- ic->ic_roam.rate11b = ireq->i_val & IEEE80211_RATE_VAL;
- break;
- case IEEE80211_IOC_MCAST_RATE:
- ic->ic_mcast_rate = ireq->i_val & IEEE80211_RATE_VAL;
- break;
case IEEE80211_IOC_FRAGTHRESHOLD:
- if ((ic->ic_caps & IEEE80211_C_TXFRAG) == 0 &&
+ if ((vap->iv_caps & IEEE80211_C_TXFRAG) == 0 &&
ireq->i_val != IEEE80211_FRAG_MAX)
- return EINVAL;
+ return EOPNOTSUPP;
if (!(IEEE80211_FRAG_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_FRAG_MAX))
return EINVAL;
- ic->ic_fragthreshold = ireq->i_val;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ vap->iv_fragthreshold = ireq->i_val;
+ error = ERESTART;
break;
case IEEE80211_IOC_BURST:
if (ireq->i_val) {
- if ((ic->ic_caps & IEEE80211_C_BURST) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_BURST;
+ if ((vap->iv_caps & IEEE80211_C_BURST) == 0)
+ return EOPNOTSUPP;
+ ieee80211_syncflag(vap, IEEE80211_F_BURST);
} else
- ic->ic_flags &= ~IEEE80211_F_BURST;
- error = ENETRESET; /* XXX maybe not for station? */
+ ieee80211_syncflag(vap, -IEEE80211_F_BURST);
+ error = ERESTART;
break;
case IEEE80211_IOC_BMISSTHRESHOLD:
if (!(IEEE80211_HWBMISS_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_HWBMISS_MAX))
return EINVAL;
- ic->ic_bmissthreshold = ireq->i_val;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ vap->iv_bmissthreshold = ireq->i_val;
+ error = ERESTART;
break;
case IEEE80211_IOC_CURCHAN:
- error = ieee80211_ioctl_setcurchan(ic, ireq);
+ error = ieee80211_ioctl_setcurchan(vap, ireq);
break;
case IEEE80211_IOC_SHORTGI:
if (ireq->i_val) {
#define IEEE80211_HTCAP_SHORTGI \
(IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40)
- if (((ireq->i_val ^ ic->ic_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0)
+ if (((ireq->i_val ^ vap->iv_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0)
return EINVAL;
if (ireq->i_val & IEEE80211_HTCAP_SHORTGI20)
- ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
+ vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI20;
if (ireq->i_val & IEEE80211_HTCAP_SHORTGI40)
- ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
+ vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI40;
#undef IEEE80211_HTCAP_SHORTGI
} else
- ic->ic_flags_ext &=
+ vap->iv_flags_ext &=
~(IEEE80211_FEXT_SHORTGI20 | IEEE80211_FEXT_SHORTGI40);
- /* XXX kick state machine? */
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ error = ERESTART;
break;
case IEEE80211_IOC_AMPDU:
- if (ireq->i_val) {
- if ((ic->ic_htcaps & IEEE80211_HTC_AMPDU) == 0)
- return EINVAL;
- if (ireq->i_val & 1)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
- if (ireq->i_val & 2)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
- } else
- ic->ic_flags_ext &=
- ~(IEEE80211_FEXT_AMPDU_TX|IEEE80211_FEXT_AMPDU_RX);
+ if (ireq->i_val && (vap->iv_htcaps & IEEE80211_HTC_AMPDU) == 0)
+ return EINVAL;
+ if (ireq->i_val & 1)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
+ else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_AMPDU_TX;
+ if (ireq->i_val & 2)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
+ else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_AMPDU_RX;
/* NB: reset only if we're operating on an 11n channel */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
- error = ENETRESET;
+ if (isvapht(vap))
+ error = ERESTART;
break;
case IEEE80211_IOC_AMPDU_LIMIT:
- /* XXX validate */
- ic->ic_ampdu_limit = ireq->i_val;
+ if (!(IEEE80211_HTCAP_MAXRXAMPDU_8K <= ireq->i_val &&
+ ireq->i_val <= IEEE80211_HTCAP_MAXRXAMPDU_64K))
+ return EINVAL;
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ vap->iv_ampdu_rxmax = ireq->i_val;
+ else
+ vap->iv_ampdu_limit = ireq->i_val;
+ error = ERESTART;
break;
case IEEE80211_IOC_AMPDU_DENSITY:
- /* XXX validate */
- ic->ic_ampdu_density = ireq->i_val;
+ if (!(IEEE80211_HTCAP_MPDUDENSITY_NA <= ireq->i_val &&
+ ireq->i_val <= IEEE80211_HTCAP_MPDUDENSITY_16))
+ return EINVAL;
+ vap->iv_ampdu_density = ireq->i_val;
+ error = ERESTART;
break;
case IEEE80211_IOC_AMSDU:
- if (ireq->i_val) {
- if ((ic->ic_htcaps & IEEE80211_HTC_AMSDU) == 0)
- return EINVAL;
- if (ireq->i_val & 1)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
- if (ireq->i_val & 2)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
- } else
- ic->ic_flags_ext &=
- ~(IEEE80211_FEXT_AMSDU_TX|IEEE80211_FEXT_AMSDU_RX);
+ if (ireq->i_val && (vap->iv_htcaps & IEEE80211_HTC_AMSDU) == 0)
+ return EINVAL;
+ if (ireq->i_val & 1)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
+ else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_AMSDU_TX;
+ if (ireq->i_val & 2)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
+ else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_AMSDU_RX;
/* NB: reset only if we're operating on an 11n channel */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
- error = ENETRESET;
+ if (isvapht(vap))
+ error = ERESTART;
break;
case IEEE80211_IOC_AMSDU_LIMIT:
/* XXX validate */
- ic->ic_amsdu_limit = ireq->i_val; /* XXX truncation? */
+ vap->iv_amsdu_limit = ireq->i_val; /* XXX truncation? */
break;
case IEEE80211_IOC_PUREN:
if (ireq->i_val) {
- if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) == 0)
return EINVAL;
- ic->ic_flags_ext |= IEEE80211_FEXT_PUREN;
+ vap->iv_flags_ext |= IEEE80211_FEXT_PUREN;
} else
- ic->ic_flags_ext &= ~IEEE80211_FEXT_PUREN;
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_PUREN;
/* NB: reset only if we're operating on an 11n channel */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
- error = ENETRESET;
+ if (isvapht(vap))
+ error = ERESTART;
break;
case IEEE80211_IOC_DOTH:
if (ireq->i_val) {
#if 0
/* XXX no capability */
- if ((ic->ic_caps & IEEE80211_C_DOTH) == 0)
- return EINVAL;
+ if ((vap->iv_caps & IEEE80211_C_DOTH) == 0)
+ return EOPNOTSUPP;
#endif
- ic->ic_flags |= IEEE80211_F_DOTH;
+ vap->iv_flags |= IEEE80211_F_DOTH;
} else
- ic->ic_flags &= ~IEEE80211_F_DOTH;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ vap->iv_flags &= ~IEEE80211_F_DOTH;
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_REGDOMAIN:
+ error = ieee80211_ioctl_setregdomain(vap, ireq);
+ break;
+ case IEEE80211_IOC_ROAM:
+ error = ieee80211_ioctl_setroam(vap, ireq);
+ break;
+ case IEEE80211_IOC_TXPARAMS:
+ error = ieee80211_ioctl_settxparams(vap, ireq);
break;
case IEEE80211_IOC_HTCOMPAT:
if (ireq->i_val) {
- if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
- return EINVAL;
- ic->ic_flags_ext |= IEEE80211_FEXT_HTCOMPAT;
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags_ext |= IEEE80211_FEXT_HTCOMPAT;
} else
- ic->ic_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT;
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT;
/* NB: reset only if we're operating on an 11n channel */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
- error = ENETRESET;
+ if (isvapht(vap))
+ error = ERESTART;
+ break;
+ case IEEE80211_IOC_DWDS:
+ if (ireq->i_val) {
+ /* NB: DWDS only makes sense for WDS-capable devices */
+ if ((ic->ic_caps & IEEE80211_C_WDS) == 0)
+ return EOPNOTSUPP;
+ /* NB: DWDS is used only with ap+sta vaps */
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_STA)
+ return EINVAL;
+ vap->iv_flags |= IEEE80211_F_DWDS;
+ } else
+ vap->iv_flags &= ~IEEE80211_F_DWDS;
break;
case IEEE80211_IOC_INACTIVITY:
if (ireq->i_val)
- ic->ic_flags_ext |= IEEE80211_FEXT_INACT;
+ vap->iv_flags_ext |= IEEE80211_FEXT_INACT;
+ else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_INACT;
+ break;
+ case IEEE80211_IOC_APPIE:
+ error = ieee80211_ioctl_setappie(vap, ireq);
+ break;
+ case IEEE80211_IOC_WPS:
+ if (ireq->i_val) {
+ if ((vap->iv_caps & IEEE80211_C_WPA) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags_ext |= IEEE80211_FEXT_WPS;
+ } else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_WPS;
+ break;
+ case IEEE80211_IOC_TSN:
+ if (ireq->i_val) {
+ if ((vap->iv_caps & IEEE80211_C_WPA) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags_ext |= IEEE80211_FEXT_TSN;
+ } else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_TSN;
+ break;
+ case IEEE80211_IOC_CHANSWITCH:
+ error = ieee80211_ioctl_chanswitch(vap, ireq);
+ break;
+ case IEEE80211_IOC_DFS:
+ if (ireq->i_val) {
+#if 0
+ /* XXX no capability */
+ if ((vap->iv_caps & IEEE80211_C_DFS) == 0)
+ return EOPNOTSUPP;
+#endif
+ /* NB: DFS requires 11h support */
+ if ((vap->iv_flags & IEEE80211_F_DOTH) == 0)
+ return EINVAL;
+ vap->iv_flags_ext |= IEEE80211_FEXT_DFS;
+ } else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_DFS;
+ break;
+ case IEEE80211_IOC_DOTD:
+ if (ireq->i_val)
+ vap->iv_flags_ext |= IEEE80211_FEXT_DOTD;
else
- ic->ic_flags_ext &= ~IEEE80211_FEXT_INACT;
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_DOTD;
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ error = ENETRESET;
break;
case IEEE80211_IOC_HTPROTMODE:
if (ireq->i_val > IEEE80211_PROT_RTSCTS)
@@ -2496,57 +3053,143 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
ic->ic_htprotmode = ireq->i_val ?
IEEE80211_PROT_RTSCTS : IEEE80211_PROT_NONE;
/* NB: if not operating in 11n this can wait */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+ if (isvapht(vap))
error = ERESTART;
break;
- case IEEE80211_IOC_HTCONF:
- if (ireq->i_val & 1)
- ic->ic_flags_ext |= IEEE80211_FEXT_HT;
- else
- ic->ic_flags_ext &= ~IEEE80211_FEXT_HT;
- if (ireq->i_val & 2)
- ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40;
- else
- ic->ic_flags_ext &= ~IEEE80211_FEXT_USEHT40;
- error = ENETRESET;
+ case IEEE80211_IOC_STA_VLAN:
+ error = ieee80211_ioctl_setstavlan(vap, ireq);
break;
default:
error = EINVAL;
break;
}
- if (error == ENETRESET)
- error = IS_UP_AUTO(ic) ? ieee80211_init(ic, 0) : 0;
+ /*
+ * The convention is that ENETRESET means an operation
+ * requires a complete re-initialization of the device (e.g.
+ * changing something that affects the association state).
+ * ERESTART means the request may be handled with only a
+ * reload of the hardware state. We hand ERESTART requests
+ * to the iv_reset callback so the driver can decide. If
+ * a device does not fillin iv_reset then it defaults to one
+ * that returns ENETRESET. Otherwise a driver may return
+ * ENETRESET (in which case a full reset will be done) or
+ * 0 to mean there's no need to do anything (e.g. when the
+ * change has no effect on the driver/device).
+ */
+ if (error == ERESTART)
+ error = IFNET_IS_UP_RUNNING(vap->iv_ifp) ?
+ vap->iv_reset(vap, ireq->i_type) : 0;
+ if (error == ENETRESET) {
+ /* XXX need to re-think AUTO handling */
+ if (IS_UP_AUTO(vap))
+ ieee80211_init(vap);
+ error = 0;
+ }
return error;
}
+/*
+ * Rebuild the parent's multicast address list after an add/del
+ * of a multicast address for a vap. We have no way to tell
+ * what happened above to optimize the work so we purge the entire
+ * list and rebuild from scratch. This is way expensive.
+ * Note also the half-baked workaround for if_addmulti calling
+ * back to the parent device; there's no way to insert mcast
+ * entries quietly and/or cheaply.
+ */
+static void
+ieee80211_ioctl_updatemulti(struct ieee80211com *ic)
+{
+ struct ifnet *parent = ic->ic_ifp;
+ struct ieee80211vap *vap;
+ void *ioctl;
+
+ IEEE80211_LOCK(ic);
+ if_purgemaddrs(parent);
+ ioctl = parent->if_ioctl; /* XXX WAR if_allmulti */
+ parent->if_ioctl = NULL;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ifmultiaddr *ifma;
+
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ (void) if_addmulti(parent, ifma->ifma_addr, NULL);
+ }
+ parent->if_ioctl = ioctl;
+
+ ic->ic_update_mcast(ic->ic_ifp);
+ IEEE80211_UNLOCK(ic);
+}
+
int
-ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data)
+ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap;
+ struct ieee80211com *ic;
int error = 0;
struct ifreq *ifr;
struct ifaddr *ifa; /* XXX */
+ vap = ifp->if_softc;
+ if (vap == NULL) {
+ /*
+ * During detach we clear the backpointer in the softc
+ * so any ioctl request through the ifnet that arrives
+ * before teardown is ignored/rejected. In particular
+ * this hack handles destroying a vap used by an app
+ * like wpa_supplicant that will respond to the vap
+ * being forced into INIT state by immediately trying
+ * to force it back up. We can yank this hack if/when
+ * we can destroy the ifnet before cleaning up vap state.
+ */
+ return ENXIO;
+ }
switch (cmd) {
+ case SIOCSIFFLAGS:
+ ic = vap->iv_ic;
+ IEEE80211_LOCK(ic);
+ ieee80211_syncifflag_locked(ic, IFF_PROMISC);
+ ieee80211_syncifflag_locked(ic, IFF_ALLMULTI);
+ if (ifp->if_flags & IFF_UP) {
+ /*
+ * Bring ourself up unless we're already operational.
+ * If we're the first vap and the parent is not up
+ * then it will automatically be brought up as a
+ * side-effect of bringing ourself up.
+ */
+ if (vap->iv_state == IEEE80211_S_INIT)
+ ieee80211_start_locked(vap);
+ } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ /*
+ * Stop ourself. If we are the last vap to be
+ * marked down the parent will also be taken down.
+ */
+ ieee80211_stop_locked(vap);
+ }
+ IEEE80211_UNLOCK(ic);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ ieee80211_ioctl_updatemulti(vap->iv_ic);
+ break;
case SIOCSIFMEDIA:
case SIOCGIFMEDIA:
- error = ifmedia_ioctl(ifp, (struct ifreq *) data,
- &ic->ic_media, cmd);
+ ifr = (struct ifreq *)data;
+ error = ifmedia_ioctl(ifp, ifr, &vap->iv_media, cmd);
break;
case SIOCG80211:
- error = ieee80211_ioctl_get80211(ic, cmd,
+ error = ieee80211_ioctl_get80211(vap, cmd,
(struct ieee80211req *) data);
break;
case SIOCS80211:
error = priv_check(curthread, PRIV_NET80211_MANAGE);
if (error == 0)
- error = ieee80211_ioctl_set80211(ic, cmd,
+ error = ieee80211_ioctl_set80211(vap, cmd,
(struct ieee80211req *) data);
break;
case SIOCG80211STATS:
ifr = (struct ifreq *)data;
- copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats));
+ copyout(&vap->iv_stats, ifr->ifr_data, sizeof (vap->iv_stats));
break;
case SIOCSIFMTU:
ifr = (struct ifreq *)data;
@@ -2601,6 +3244,14 @@ ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data)
break;
}
break;
+ /* Pass NDIS ioctls up to the driver */
+ case SIOCGDRVSPEC:
+ case SIOCSDRVSPEC:
+ case SIOCGPRIVATE_0: {
+ struct ifnet *parent = vap->iv_ic->ic_ifp;
+ error = parent->if_ioctl(parent, cmd, data);
+ break;
+ }
default:
error = ether_ioctl(ifp, cmd, data);
break;
diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h
index e96ab52..c40b929 100644
--- a/sys/net80211/ieee80211_ioctl.h
+++ b/sys/net80211/ieee80211_ioctl.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -196,6 +196,8 @@ struct ieee80211_stats {
uint32_t is_tx_badstate; /* tx discard state != RUN */
uint32_t is_tx_notassoc; /* tx failed, sta not assoc */
uint32_t is_tx_classify; /* tx classification failed */
+ uint32_t is_dwds_mcast; /* discard mcast over dwds */
+ uint32_t is_dwds_qdrop; /* dwds pending frame q full */
uint32_t is_ht_assoc_nohtcap; /* non-HT sta rejected */
uint32_t is_ht_assoc_downgrade; /* HT sta forced to legacy */
uint32_t is_ht_assoc_norate; /* HT assoc w/ rate mismatch */
@@ -208,7 +210,12 @@ struct ieee80211_stats {
uint32_t is_ampdu_stop; /* A-MPDU stream stopped */
uint32_t is_ampdu_stop_failed; /* A-MPDU stream not running */
uint32_t is_ampdu_rx_reorder; /* A-MPDU held for rx reorder */
- uint32_t is_spare[16];
+ uint32_t is_scan_bg; /* background scans started */
+ uint8_t is_rx_deauth_code; /* last rx'd deauth reason */
+ uint8_t is_rx_disassoc_code; /* last rx'd disassoc reason */
+ uint8_t is_rx_authfail_code; /* last rx'd auth fail reason */
+ uint32_t is_beacon_miss; /* beacon miss notification */
+ uint32_t is_spare[14];
};
/*
@@ -264,6 +271,7 @@ struct ieee80211req_mlme {
#define IEEE80211_MLME_DEAUTH 3 /* deauthenticate station */
#define IEEE80211_MLME_AUTHORIZE 4 /* authorize station */
#define IEEE80211_MLME_UNAUTHORIZE 5 /* unauthorize station */
+#define IEEE80211_MLME_AUTH 6 /* authenticate station */
uint8_t im_ssid_len; /* length of optional ssid */
uint16_t im_reason; /* 802.11 reason code */
uint8_t im_macaddr[IEEE80211_ADDR_LEN];
@@ -281,6 +289,7 @@ enum {
IEEE80211_MACCMD_DETACH = 4, /* detach ACL policy */
IEEE80211_MACCMD_POLICY = 5, /* get ACL policy */
IEEE80211_MACCMD_LIST = 6, /* get ACL database */
+ IEEE80211_MACCMD_POLICY_RADIUS = 7, /* set policy: RADIUS managed */
};
struct ieee80211req_maclist {
@@ -335,7 +344,7 @@ struct ieee80211req_sta_stats {
* to retrieve other data like stats, unicast key, etc.
*/
struct ieee80211req_sta_info {
- uint16_t isi_len; /* length (mult of 4) */
+ uint16_t isi_len; /* total length (mult of 4) */
uint16_t isi_ie_off; /* offset to IE data */
uint16_t isi_ie_len; /* IE length */
uint16_t isi_freq; /* MHz */
@@ -350,7 +359,7 @@ struct ieee80211req_sta_info {
uint8_t isi_nrates;
/* negotiated rates */
uint8_t isi_rates[IEEE80211_RATE_MAXSIZE];
- uint8_t isi_txrate; /* index to isi_rates[] */
+ uint8_t isi_txrate; /* legacy/IEEE rate or MCS */
uint16_t isi_associd; /* assoc response */
uint16_t isi_txpower; /* current tx power */
uint16_t isi_vlan; /* vlan tag */
@@ -358,6 +367,9 @@ struct ieee80211req_sta_info {
uint16_t isi_txseqs[IEEE80211_TID_SIZE];/* tx seq #/TID */
uint16_t isi_rxseqs[IEEE80211_TID_SIZE];/* rx seq#/TID */
uint16_t isi_inact; /* inactivity timer */
+ uint16_t isi_txmbps; /* current tx rate in .5 Mb/s */
+ uint32_t isi_jointime; /* time of assoc/join */
+ struct ieee80211_mimo_info isi_mimo; /* MIMO info for 11n sta's */
/* XXX frag state? */
/* variable length IE data */
};
@@ -384,15 +396,98 @@ struct ieee80211req_sta_txpow {
};
/*
- * WME parameters are set and return using i_val and i_len.
- * i_val holds the value itself. i_len specifies the AC
- * and, as appropriate, then high bit specifies whether the
- * operation is to be applied to the BSS or ourself.
+ * WME parameters manipulated with IEEE80211_IOC_WME_CWMIN
+ * through IEEE80211_IOC_WME_ACKPOLICY are set and return
+ * using i_val and i_len. i_val holds the value itself.
+ * i_len specifies the AC and, as appropriate, then high bit
+ * specifies whether the operation is to be applied to the
+ * BSS or ourself.
*/
#define IEEE80211_WMEPARAM_SELF 0x0000 /* parameter applies to self */
#define IEEE80211_WMEPARAM_BSS 0x8000 /* parameter applies to BSS */
#define IEEE80211_WMEPARAM_VAL 0x7fff /* parameter value */
+/*
+ * Application Information Elements can be appended to a variety
+ * of frames with the IEE80211_IOC_APPIE request. This request
+ * piggybacks on a normal ieee80211req; the frame type is passed
+ * in i_val as the 802.11 FC0 bytes and the length of the IE data
+ * is passed in i_len. The data is referenced in i_data. If i_len
+ * is zero then any previously configured IE data is removed. At
+ * most IEEE80211_MAX_APPIE data be appened. Note that multiple
+ * IE's can be supplied; the data is treated opaquely.
+ */
+#define IEEE80211_MAX_APPIE 1024 /* max app IE data */
+/*
+ * Hack: the WPA authenticator uses this mechanism to specify WPA
+ * ie's that are used instead of the ones normally constructed using
+ * the cipher state setup with separate ioctls. This avoids issues
+ * like the authenticator ordering ie data differently than the
+ * net80211 layer and needing to keep separate state for WPA and RSN.
+ */
+#define IEEE80211_APPIE_WPA \
+ (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON | \
+ IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+
+/*
+ * Station mode roaming parameters. These are maintained
+ * per band/mode and control the roaming algorithm.
+ */
+struct ieee80211_roamparams_req {
+ struct ieee80211_roamparam params[IEEE80211_MODE_MAX];
+};
+
+/*
+ * Transmit parameters. These can be used to set fixed transmit
+ * rate for each operating mode when operating as client or on a
+ * per-client basis according to the capabilities of the client
+ * (e.g. an 11b client associated to an 11g ap) when operating as
+ * an ap.
+ *
+ * MCS are distinguished from legacy rates by or'ing in 0x80.
+ */
+struct ieee80211_txparams_req {
+ struct ieee80211_txparam params[IEEE80211_MODE_MAX];
+};
+
+/*
+ * Set regulatory domain state with IEEE80211_IOC_REGDOMAIN.
+ * Note this is both the regulatory description and the channel
+ * list. The get request for IEEE80211_IOC_REGDOMAIN returns
+ * only the regdomain info; the channel list is obtained
+ * separately with IEEE80211_IOC_CHANINFO.
+ */
+struct ieee80211_regdomain_req {
+ struct ieee80211_regdomain rd;
+ struct ieee80211req_chaninfo chaninfo;
+};
+
+/*
+ * Get driver capabilities. Driver, hardware crypto, and
+ * HT/802.11n capabilities, and a table that describes what
+ * the radio can do.
+ */
+struct ieee80211_devcaps_req {
+ uint32_t dc_drivercaps; /* general driver caps */
+ uint32_t dc_cryptocaps; /* hardware crypto support */
+ uint32_t dc_htcaps; /* HT/802.11n support */
+ struct ieee80211req_chaninfo dc_chaninfo;
+};
+
+struct ieee80211_chanswitch_req {
+ struct ieee80211_channel csa_chan; /* new channel */
+ int csa_mode; /* CSA mode */
+ int csa_count; /* beacon count to switch */
+};
+
+/*
+ * Get/set per-station vlan tag.
+ */
+struct ieee80211req_sta_vlan {
+ uint8_t sv_macaddr[IEEE80211_ADDR_LEN];
+ uint16_t sv_vlan;
+};
+
#ifdef __FreeBSD__
/*
* FreeBSD-style ioctls.
@@ -443,8 +538,8 @@ struct ieee80211req {
#define IEEE80211_IOC_WPAKEY 19
#define IEEE80211_IOC_DELKEY 20
#define IEEE80211_IOC_MLME 21
-#define IEEE80211_IOC_OPTIE 22 /* optional info. element */
-#define IEEE80211_IOC_SCAN_REQ 23
+/* 22 was IEEE80211_IOC_OPTIE, replaced by IEEE80211_IOC_APPIE */
+/* 23 was IEEE80211_IOC_SCAN_REQ */
/* 24 was IEEE80211_IOC_SCAN_RESULTS */
#define IEEE80211_IOC_COUNTERMEASURES 25 /* WPA/TKIP countermeasures */
#define IEEE80211_IOC_WPA 26 /* WPA mode (0,1,2) */
@@ -452,14 +547,8 @@ struct ieee80211req {
#define IEEE80211_IOC_WME 28 /* WME mode (on, off) */
#define IEEE80211_IOC_HIDESSID 29 /* hide SSID mode (on, off) */
#define IEEE80211_IOC_APBRIDGE 30 /* AP inter-sta bridging */
-#define IEEE80211_IOC_MCASTCIPHER 31 /* multicast/default cipher */
-#define IEEE80211_IOC_MCASTKEYLEN 32 /* multicast key length */
-#define IEEE80211_IOC_UCASTCIPHERS 33 /* unicast cipher suites */
-#define IEEE80211_IOC_UCASTCIPHER 34 /* unicast cipher */
-#define IEEE80211_IOC_UCASTKEYLEN 35 /* unicast key length */
-#define IEEE80211_IOC_DRIVER_CAPS 36 /* driver capabilities */
-#define IEEE80211_IOC_KEYMGTALGS 37 /* key management algorithms */
-#define IEEE80211_IOC_RSNCAPS 38 /* RSN capabilities */
+/* 31-35,37-38 were for WPA authenticator settings */
+/* 36 was IEEE80211_IOC_DRIVER_CAPS */
#define IEEE80211_IOC_WPAIE 39 /* WPA information element */
#define IEEE80211_IOC_STA_STATS 40 /* per-station statistics */
#define IEEE80211_IOC_MACCMD 41 /* MAC ACL operation */
@@ -484,13 +573,7 @@ struct ieee80211req {
#define IEEE80211_IOC_BGSCAN_IDLE 60 /* bg scan idle threshold */
#define IEEE80211_IOC_BGSCAN_INTERVAL 61 /* bg scan interval */
#define IEEE80211_IOC_SCANVALID 65 /* scan cache valid threshold */
-#define IEEE80211_IOC_ROAM_RSSI_11A 66 /* rssi threshold in 11a */
-#define IEEE80211_IOC_ROAM_RSSI_11B 67 /* rssi threshold in 11b */
-#define IEEE80211_IOC_ROAM_RSSI_11G 68 /* rssi threshold in 11g */
-#define IEEE80211_IOC_ROAM_RATE_11A 69 /* tx rate threshold in 11a */
-#define IEEE80211_IOC_ROAM_RATE_11B 70 /* tx rate threshold in 11b */
-#define IEEE80211_IOC_ROAM_RATE_11G 71 /* tx rate threshold in 11g */
-#define IEEE80211_IOC_MCAST_RATE 72 /* tx rate for mcast frames */
+/* 66-72 were IEEE80211_IOC_ROAM_* and IEEE80211_IOC_MCAST_RATE */
#define IEEE80211_IOC_FRAGTHRESHOLD 73 /* tx fragmentation threshold */
#define IEEE80211_IOC_BURST 75 /* packet bursting */
#define IEEE80211_IOC_SCAN_RESULTS 76 /* get scan results */
@@ -506,13 +589,84 @@ struct ieee80211req {
#define IEEE80211_IOC_AMSDU_LIMIT 86 /* A-MSDU length limit */
#define IEEE80211_IOC_PUREN 87 /* pure 11n (no legacy sta's) */
#define IEEE80211_IOC_DOTH 88 /* 802.11h (on, off) */
-#define IEEE80211_IOC_REGDOMAIN 89 /* regulatory domain */
-#define IEEE80211_IOC_COUNTRYCODE 90 /* ISO country code */
-#define IEEE80211_IOC_LOCATION 91 /* indoor/outdoor/anywhere */
+/* 89-91 were regulatory items */
#define IEEE80211_IOC_HTCOMPAT 92 /* support pre-D1.10 HT ie's */
+#define IEEE80211_IOC_DWDS 93 /* DWDS/4-address handling */
#define IEEE80211_IOC_INACTIVITY 94 /* sta inactivity handling */
+#define IEEE80211_IOC_APPIE 95 /* application IE's */
+#define IEEE80211_IOC_WPS 96 /* WPS operation */
+#define IEEE80211_IOC_TSN 97 /* TSN operation */
+#define IEEE80211_IOC_DEVCAPS 98 /* driver+device capabilities */
+#define IEEE80211_IOC_CHANSWITCH 99 /* start 11h channel switch */
+#define IEEE80211_IOC_DFS 100 /* DFS (on, off) */
+#define IEEE80211_IOC_DOTD 101 /* 802.11d (on, off) */
#define IEEE80211_IOC_HTPROTMODE 102 /* HT protection (off, rts) */
+#define IEEE80211_IOC_SCAN_REQ 103 /* scan w/ specified params */
+#define IEEE80211_IOC_SCAN_CANCEL 104 /* cancel ongoing scan */
#define IEEE80211_IOC_HTCONF 105 /* HT config (off, HT20, HT40)*/
+#define IEEE80211_IOC_REGDOMAIN 106 /* regulatory domain info */
+#define IEEE80211_IOC_ROAM 107 /* roaming params en masse */
+#define IEEE80211_IOC_TXPARAMS 108 /* tx parameters */
+#define IEEE80211_IOC_STA_VLAN 109 /* per-station vlan tag */
+
+/*
+ * Parameters for controlling a scan requested with
+ * IEEE80211_IOC_SCAN_REQ.
+ *
+ * Active scans cause ProbeRequest frames to be issued for each
+ * specified ssid and, by default, a broadcast ProbeRequest frame.
+ * The set of ssid's is specified in the request.
+ *
+ * By default the scan will cause a BSS to be joined (in station/adhoc
+ * mode) or a channel to be selected for operation (hostap mode).
+ * To disable that specify IEEE80211_IOC_SCAN_NOPICK and if the
+ *
+ * If the station is currently associated to an AP then a scan request
+ * will cause the station to leave the current channel and potentially
+ * miss frames from the AP. Alternatively the station may notify the
+ * AP that it is going into power save mode before it leaves the channel.
+ * This ensures frames for the station are buffered by the AP. This is
+ * termed a ``bg scan'' and is requested with the IEEE80211_IOC_SCAN_BGSCAN
+ * flag. Background scans may take longer than foreground scans and may
+ * be preempted by traffic. If a station is not associated to an AP
+ * then a request for a background scan is automatically done in the
+ * foreground.
+ *
+ * The results of the scan request are cached by the system. This
+ * information is aged out and/or invalidated based on events like not
+ * being able to associated to an AP. To flush the current cache
+ * contents before doing a scan the IEEE80211_IOC_SCAN_FLUSH flag may
+ * be specified.
+ *
+ * By default the scan will be done until a suitable AP is located
+ * or a channel is found for use. A scan can also be constrained
+ * to be done once (IEEE80211_IOC_SCAN_ONCE) or to last for no more
+ * than a specified duration.
+ */
+struct ieee80211_scan_req {
+ int sr_flags;
+#define IEEE80211_IOC_SCAN_NOPICK 0x00001 /* scan only, no selection */
+#define IEEE80211_IOC_SCAN_ACTIVE 0x00002 /* active scan (probe req) */
+#define IEEE80211_IOC_SCAN_PICK1ST 0x00004 /* ``hey sailor'' mode */
+#define IEEE80211_IOC_SCAN_BGSCAN 0x00008 /* bg scan, exit ps at end */
+#define IEEE80211_IOC_SCAN_ONCE 0x00010 /* do one complete pass */
+#define IEEE80211_IOC_SCAN_NOBCAST 0x00020 /* don't send bcast probe req */
+#define IEEE80211_IOC_SCAN_NOJOIN 0x00040 /* no auto-sequencing */
+#define IEEE80211_IOC_SCAN_FLUSH 0x10000 /* flush scan cache first */
+#define IEEE80211_IOC_SCAN_CHECK 0x20000 /* check scan cache first */
+ u_int sr_duration; /* duration (ms) */
+#define IEEE80211_IOC_SCAN_DURATION_MIN 1
+#define IEEE80211_IOC_SCAN_DURATION_MAX 0x7fffffff
+#define IEEE80211_IOC_SCAN_FOREVER IEEE80211_IOC_SCAN_DURATION_MAX
+ u_int sr_mindwell; /* min channel dwelltime (ms) */
+ u_int sr_maxdwell; /* max channel dwelltime (ms) */
+ int sr_nssid;
+#define IEEE80211_IOC_SCAN_MAX_SSID 3
+ struct {
+ int len; /* length in bytes */
+ uint8_t ssid[IEEE80211_NWID_LEN]; /* ssid contents */
+ } sr_ssid[IEEE80211_IOC_SCAN_MAX_SSID];
+};
/*
* Scan result data returned for IEEE80211_IOC_SCAN_RESULTS.
@@ -523,8 +677,8 @@ struct ieee80211req {
* in isr_len. Result records are rounded to a multiple of 4 bytes.
*/
struct ieee80211req_scan_result {
- uint16_t isr_len; /* length (mult of 4) */
- uint16_t isr_ie_off; /* offset to IE data */
+ uint16_t isr_len; /* total length (mult of 4) */
+ uint16_t isr_ie_off; /* offset to SSID+IE data */
uint16_t isr_ie_len; /* IE length */
uint16_t isr_freq; /* MHz */
uint16_t isr_flags; /* channel flags */
@@ -540,10 +694,47 @@ struct ieee80211req_scan_result {
/* variable length SSID followed by IE data */
};
+/*
+ * Virtual AP cloning parameters. The parent device must
+ * be a vap-capable device. All parameters specified with
+ * the clone request are fixed for the lifetime of the vap.
+ *
+ * There are two flavors of WDS vaps: legacy and dynamic.
+ * Legacy WDS operation implements a static binding between
+ * two stations encapsulating traffic in 4-address frames.
+ * Dynamic WDS vaps are created when a station associates to
+ * an AP and sends a 4-address frame. If the AP vap is
+ * configured to support WDS then this will generate an
+ * event to user programs listening on the routing socket
+ * and a Dynamic WDS vap will be created to handle traffic
+ * to/from that station. In both cases the bssid of the
+ * peer must be specified when creating the vap.
+ *
+ * By default a vap will inherit the mac address/bssid of
+ * the underlying device. To request a unique address the
+ * IEEE80211_CLONE_BSSID flag should be supplied. This is
+ * meaningless for WDS vaps as they share the bssid of an
+ * AP vap that must otherwise exist. Note that some devices
+ * may not be able to support multiple addresses.
+ *
+ * Station mode vap's normally depend on the device to notice
+ * when the AP stops sending beacon frames. If IEEE80211_CLONE_NOBEACONS
+ * is specified the net80211 layer will do this in s/w. This
+ * is mostly useful when setting up a WDS repeater/extender where
+ * an AP vap is combined with a sta vap and the device isn't able
+ * to track beacon frames in hardware.
+ */
struct ieee80211_clone_params {
char icp_parent[IFNAMSIZ]; /* parent device */
- int icp_opmode; /* operating mode */
+ uint16_t icp_opmode; /* operating mode */
+ uint16_t icp_flags; /* see below */
+ uint8_t icp_bssid[IEEE80211_ADDR_LEN]; /* for WDS links */
+ uint8_t icp_macaddr[IEEE80211_ADDR_LEN];/* local address */
};
+#define IEEE80211_CLONE_BSSID 0x0001 /* allocate unique mac/bssid */
+#define IEEE80211_CLONE_NOBEACONS 0x0002 /* don't setup beacon timers */
+#define IEEE80211_CLONE_WDSLEGACY 0x0004 /* legacy WDS processing */
+#define IEEE80211_CLONE_MACADDR 0x0008 /* use specified mac addr */
#endif /* __FreeBSD__ */
#endif /* _NET80211_IEEE80211_IOCTL_H_ */
diff --git a/sys/net80211/ieee80211_monitor.c b/sys/net80211/ieee80211_monitor.c
new file mode 100644
index 0000000..b1e98eb
--- /dev/null
+++ b/sys/net80211/ieee80211_monitor.c
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 Monitor mode support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_monitor.h>
+
+static void monitor_vattach(struct ieee80211vap *);
+static int monitor_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int monitor_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp);
+
+void
+ieee80211_monitor_attach(struct ieee80211com *ic)
+{
+ ic->ic_vattach[IEEE80211_M_MONITOR] = monitor_vattach;
+}
+
+void
+ieee80211_monitor_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+monitor_vdetach(struct ieee80211vap *vap)
+{
+}
+
+static void
+monitor_vattach(struct ieee80211vap *vap)
+{
+ vap->iv_newstate = monitor_newstate;
+ vap->iv_input = monitor_input;
+ vap->iv_opdetach = monitor_vdetach;
+}
+
+/*
+ * IEEE80211_M_MONITOR vap state machine handler.
+ */
+static int
+monitor_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ enum ieee80211_state ostate;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate], arg);
+ vap->iv_state = nstate; /* state transition */
+ if (nstate == IEEE80211_S_RUN) {
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ ieee80211_create_ibss(vap, ic->ic_curchan);
+ break;
+ default:
+ break;
+ }
+ /*
+ * NB: this shouldn't be here but many people use
+ * monitor mode for raw packets; once we switch
+ * them over to adhoc demo mode remove this.
+ */
+ ieee80211_node_authorize(vap->iv_bss);
+ }
+ return 0;
+}
+
+/*
+ * Process a received frame in monitor mode.
+ */
+static int
+monitor_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ m_freem(m);
+ return -1;
+}
diff --git a/sys/net80211/ieee80211_monitor.h b/sys/net80211/ieee80211_monitor.h
new file mode 100644
index 0000000..d7dd8e9
--- /dev/null
+++ b/sys/net80211/ieee80211_monitor.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_MONITOR_H_
+#define _NET80211_IEEE80211_MONITOR_H_
+
+/*
+ * Monitor implementation definitions.
+ */
+void ieee80211_monitor_attach(struct ieee80211com *);
+void ieee80211_monitor_detach(struct ieee80211com *);
+#endif /* !_NET80211_IEEE80211_MONITOR_H_ */
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c
index 0ed8b79..58cb33f 100644
--- a/sys/net80211/ieee80211_node.c
+++ b/sys/net80211/ieee80211_node.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -27,6 +27,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -40,18 +42,22 @@ __FBSDID("$FreeBSD$");
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_input.h>
+#include <net80211/ieee80211_wds.h>
#include <net/bpf.h>
/*
* Association id's are managed with a bit vector.
*/
-#define IEEE80211_AID_SET(b, w) \
- ((w)[IEEE80211_AID(b) / 32] |= (1 << (IEEE80211_AID(b) % 32)))
-#define IEEE80211_AID_CLR(b, w) \
- ((w)[IEEE80211_AID(b) / 32] &= ~(1 << (IEEE80211_AID(b) % 32)))
-#define IEEE80211_AID_ISSET(b, w) \
- ((w)[IEEE80211_AID(b) / 32] & (1 << (IEEE80211_AID(b) % 32)))
+#define IEEE80211_AID_SET(_vap, b) \
+ ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] |= \
+ (1 << (IEEE80211_AID(b) % 32)))
+#define IEEE80211_AID_CLR(_vap, b) \
+ ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] &= \
+ ~(1 << (IEEE80211_AID(b) % 32)))
+#define IEEE80211_AID_ISSET(_vap, b) \
+ ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] & (1 << (IEEE80211_AID(b) % 32)))
#ifdef IEEE80211_DEBUG_REFCNT
#define REFCNT_LOC "%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line
@@ -64,117 +70,112 @@ static int ieee80211_sta_join1(struct ieee80211_node *);
static struct ieee80211_node *node_alloc(struct ieee80211_node_table *);
static void node_cleanup(struct ieee80211_node *);
static void node_free(struct ieee80211_node *);
+static void node_age(struct ieee80211_node *);
static int8_t node_getrssi(const struct ieee80211_node *);
static void node_getsignal(const struct ieee80211_node *, int8_t *, int8_t *);
+static void node_getmimoinfo(const struct ieee80211_node *,
+ struct ieee80211_mimo_info *);
-static void ieee80211_setup_node(struct ieee80211_node_table *,
- struct ieee80211_node *, const uint8_t *);
static void _ieee80211_free_node(struct ieee80211_node *);
static void ieee80211_node_table_init(struct ieee80211com *ic,
struct ieee80211_node_table *nt, const char *name,
int inact, int keymaxix);
-static void ieee80211_node_table_reset(struct ieee80211_node_table *);
+static void ieee80211_node_table_reset(struct ieee80211_node_table *,
+ struct ieee80211vap *);
+static void ieee80211_node_reclaim(struct ieee80211_node *);
static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt);
static void ieee80211_erp_timeout(struct ieee80211com *);
MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state");
+MALLOC_DEFINE(M_80211_NODE_IE, "80211nodeie", "802.11 node ie");
void
ieee80211_node_attach(struct ieee80211com *ic)
{
+ ieee80211_node_table_init(ic, &ic->ic_sta, "station",
+ IEEE80211_INACT_INIT, ic->ic_max_keyix);
+ callout_init(&ic->ic_inact, CALLOUT_MPSAFE);
+ callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
+ ieee80211_node_timeout, ic);
ic->ic_node_alloc = node_alloc;
ic->ic_node_free = node_free;
ic->ic_node_cleanup = node_cleanup;
+ ic->ic_node_age = node_age;
+ ic->ic_node_drain = node_age; /* NB: same as age */
ic->ic_node_getrssi = node_getrssi;
ic->ic_node_getsignal = node_getsignal;
+ ic->ic_node_getmimoinfo = node_getmimoinfo;
- /* default station inactivity timer setings */
- ic->ic_inact_init = IEEE80211_INACT_INIT;
- ic->ic_inact_auth = IEEE80211_INACT_AUTH;
- ic->ic_inact_run = IEEE80211_INACT_RUN;
- ic->ic_inact_probe = IEEE80211_INACT_PROBE;
-
- callout_init(&ic->ic_inact, CALLOUT_MPSAFE);
-
- /* NB: driver should override */
- ic->ic_max_aid = IEEE80211_AID_DEF;
-
+ /*
+ * Set flags to be propagated to all vap's;
+ * these define default behaviour/configuration.
+ */
ic->ic_flags_ext |= IEEE80211_FEXT_INACT; /* inactivity processing */
}
void
-ieee80211_node_lateattach(struct ieee80211com *ic)
+ieee80211_node_detach(struct ieee80211com *ic)
{
- struct ieee80211_rsnparms *rsn;
- if (ic->ic_max_aid > IEEE80211_AID_MAX)
- ic->ic_max_aid = IEEE80211_AID_MAX;
- MALLOC(ic->ic_aid_bitmap, uint32_t *,
- howmany(ic->ic_max_aid, 32) * sizeof(uint32_t),
- M_80211_NODE, M_NOWAIT | M_ZERO);
- if (ic->ic_aid_bitmap == NULL) {
- /* XXX no way to recover */
- printf("%s: no memory for AID bitmap!\n", __func__);
- ic->ic_max_aid = 0;
- }
+ callout_drain(&ic->ic_inact);
+ ieee80211_node_table_cleanup(&ic->ic_sta);
+}
- ieee80211_node_table_init(ic, &ic->ic_sta, "station",
- IEEE80211_INACT_INIT, ic->ic_crypto.cs_max_keyix);
+void
+ieee80211_node_vattach(struct ieee80211vap *vap)
+{
+ /* NB: driver can override */
+ vap->iv_max_aid = IEEE80211_AID_DEF;
- ieee80211_reset_bss(ic);
- /*
- * Setup "global settings" in the bss node so that
- * each new station automatically inherits them.
- */
- rsn = &ic->ic_bss->ni_rsn;
- /* WEP, TKIP, and AES-CCM are always supported */
- rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_WEP;
- rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_TKIP;
- rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_AES_CCM;
- if (ic->ic_caps & IEEE80211_C_AES)
- rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_AES_OCB;
- if (ic->ic_caps & IEEE80211_C_CKIP)
- rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_CKIP;
- /*
- * Default unicast cipher to WEP for 802.1x use. If
- * WPA is enabled the management code will set these
- * values to reflect.
- */
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_WEP;
- rsn->rsn_ucastkeylen = 104 / NBBY;
- /*
- * WPA says the multicast cipher is the lowest unicast
- * cipher supported. But we skip WEP which would
- * otherwise be used based on this criteria.
- */
- rsn->rsn_mcastcipher = IEEE80211_CIPHER_TKIP;
- rsn->rsn_mcastkeylen = 128 / NBBY;
+ /* default station inactivity timer setings */
+ vap->iv_inact_init = IEEE80211_INACT_INIT;
+ vap->iv_inact_auth = IEEE80211_INACT_AUTH;
+ vap->iv_inact_run = IEEE80211_INACT_RUN;
+ vap->iv_inact_probe = IEEE80211_INACT_PROBE;
+}
- /*
- * We support both WPA-PSK and 802.1x; the one used
- * is determined by the authentication mode and the
- * setting of the PSK state.
- */
- rsn->rsn_keymgmtset = WPA_ASE_8021X_UNSPEC | WPA_ASE_8021X_PSK;
- rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
+void
+ieee80211_node_latevattach(struct ieee80211vap *vap)
+{
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ /* XXX should we allow max aid to be zero? */
+ if (vap->iv_max_aid < IEEE80211_AID_MIN) {
+ vap->iv_max_aid = IEEE80211_AID_MIN;
+ if_printf(vap->iv_ifp,
+ "WARNING: max aid too small, changed to %d\n",
+ vap->iv_max_aid);
+ }
+ MALLOC(vap->iv_aid_bitmap, uint32_t *,
+ howmany(vap->iv_max_aid, 32) * sizeof(uint32_t),
+ M_80211_NODE, M_NOWAIT | M_ZERO);
+ if (vap->iv_aid_bitmap == NULL) {
+ /* XXX no way to recover */
+ printf("%s: no memory for AID bitmap, max aid %d!\n",
+ __func__, vap->iv_max_aid);
+ vap->iv_max_aid = 0;
+ }
+ }
+
+ ieee80211_reset_bss(vap);
- ic->ic_auth = ieee80211_authenticator_get(ic->ic_bss->ni_authmode);
+ vap->iv_auth = ieee80211_authenticator_get(vap->iv_bss->ni_authmode);
}
void
-ieee80211_node_detach(struct ieee80211com *ic)
+ieee80211_node_vdetach(struct ieee80211vap *vap)
{
+ struct ieee80211com *ic = vap->iv_ic;
- if (ic->ic_bss != NULL) {
- ieee80211_free_node(ic->ic_bss);
- ic->ic_bss = NULL;
+ ieee80211_node_table_reset(&ic->ic_sta, vap);
+ if (vap->iv_bss != NULL) {
+ ieee80211_free_node(vap->iv_bss);
+ vap->iv_bss = NULL;
}
- ieee80211_node_table_cleanup(&ic->ic_sta);
- if (ic->ic_aid_bitmap != NULL) {
- FREE(ic->ic_aid_bitmap, M_80211_NODE);
- ic->ic_aid_bitmap = NULL;
+ if (vap->iv_aid_bitmap != NULL) {
+ FREE(vap->iv_aid_bitmap, M_80211_NODE);
+ vap->iv_aid_bitmap = NULL;
}
}
@@ -185,20 +186,16 @@ ieee80211_node_detach(struct ieee80211com *ic)
void
ieee80211_node_authorize(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
-
ni->ni_flags |= IEEE80211_NODE_AUTH;
- ni->ni_inact_reload = ic->ic_inact_run;
+ ni->ni_inact_reload = ni->ni_vap->iv_inact_run;
ni->ni_inact = ni->ni_inact_reload;
}
void
ieee80211_node_unauthorize(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
-
ni->ni_flags &= ~IEEE80211_NODE_AUTH;
- ni->ni_inact_reload = ic->ic_inact_auth;
+ ni->ni_inact_reload = ni->ni_vap->iv_inact_auth;
if (ni->ni_inact > ni->ni_inact_reload)
ni->ni_inact = ni->ni_inact_reload;
}
@@ -206,18 +203,16 @@ ieee80211_node_unauthorize(struct ieee80211_node *ni)
/*
* Set/change the channel. The rate set is also updated as
* to insure a consistent view by drivers.
+ * XXX should be private but hostap needs it to deal with CSA
*/
-static void
-ieee80211_node_set_chan(struct ieee80211com *ic, struct ieee80211_node *ni)
+void
+ieee80211_node_set_chan(struct ieee80211_node *ni,
+ struct ieee80211_channel *chan)
{
- struct ieee80211_channel *chan = ic->ic_bsschan;
+ struct ieee80211com *ic = ni->ni_ic;
+
+ KASSERT(chan != IEEE80211_CHAN_ANYC, ("no channel"));
-#if 0
- KASSERT(chan != IEEE80211_CHAN_ANYC, ("bss channel not setup"));
-#else
- if (chan == IEEE80211_CHAN_ANYC) /* XXX while scanning */
- chan = ic->ic_curchan;
-#endif
ni->ni_chan = chan;
if (IEEE80211_IS_CHAN_HT(chan)) {
/*
@@ -232,31 +227,6 @@ ieee80211_node_set_chan(struct ieee80211com *ic, struct ieee80211_node *ni)
ni->ni_rates = *ieee80211_get_suprates(ic, chan);
}
-/*
- * Probe the curent channel, if allowed, while scanning.
- * If the channel is not marked passive-only then send
- * a probe request immediately. Otherwise mark state and
- * listen for beacons on the channel; if we receive something
- * then we'll transmit a probe request.
- */
-void
-ieee80211_probe_curchan(struct ieee80211com *ic, int force)
-{
- struct ifnet *ifp = ic->ic_ifp;
-
- if ((ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 || force) {
- /*
- * XXX send both broadcast+directed probe request
- */
- ieee80211_send_probereq(ic->ic_bss,
- ic->ic_myaddr, ifp->if_broadcastaddr,
- ifp->if_broadcastaddr,
- ic->ic_des_ssid[0].ssid, ic->ic_des_ssid[0].len,
- ic->ic_opt_ie, ic->ic_opt_ie_len);
- } else
- ic->ic_flags_ext |= IEEE80211_FEXT_PROBECHAN;
-}
-
static __inline void
copy_bss(struct ieee80211_node *nbss, const struct ieee80211_node *obss)
{
@@ -264,90 +234,87 @@ copy_bss(struct ieee80211_node *nbss, const struct ieee80211_node *obss)
nbss->ni_authmode = obss->ni_authmode;
nbss->ni_txpower = obss->ni_txpower;
nbss->ni_vlan = obss->ni_vlan;
- nbss->ni_rsn = obss->ni_rsn;
/* XXX statistics? */
+ /* XXX legacy WDS bssid? */
}
void
-ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan)
+ieee80211_create_ibss(struct ieee80211vap* vap, struct ieee80211_channel *chan)
{
- struct ieee80211_node_table *nt;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: creating ibss\n", __func__);
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: creating ibss on channel %u\n", __func__,
+ ieee80211_chan2ieee(ic, chan));
- /*
- * Create the station/neighbor table. Note that for adhoc
- * mode we make the initial inactivity timer longer since
- * we create nodes only through discovery and they typically
- * are long-lived associations.
- */
- nt = &ic->ic_sta;
- IEEE80211_NODE_LOCK(nt);
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- nt->nt_name = "station";
- nt->nt_inact_init = ic->ic_inact_init;
- } else {
- nt->nt_name = "neighbor";
- nt->nt_inact_init = ic->ic_inact_run;
- }
- IEEE80211_NODE_UNLOCK(nt);
-
- ni = ieee80211_alloc_node(&ic->ic_sta, ic->ic_myaddr);
+ ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr);
if (ni == NULL) {
/* XXX recovery? */
return;
}
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr);
- ni->ni_esslen = ic->ic_des_ssid[0].len;
- memcpy(ni->ni_essid, ic->ic_des_ssid[0].ssid, ni->ni_esslen);
- if (ic->ic_bss != NULL)
- copy_bss(ni, ic->ic_bss);
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr);
+ ni->ni_esslen = vap->iv_des_ssid[0].len;
+ memcpy(ni->ni_essid, vap->iv_des_ssid[0].ssid, ni->ni_esslen);
+ if (vap->iv_bss != NULL)
+ copy_bss(ni, vap->iv_bss);
ni->ni_intval = ic->ic_bintval;
- if (ic->ic_flags & IEEE80211_F_PRIVACY)
+ if (vap->iv_flags & IEEE80211_F_PRIVACY)
ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY;
if (ic->ic_phytype == IEEE80211_T_FH) {
ni->ni_fhdwell = 200; /* XXX */
ni->ni_fhindex = 1;
}
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- ic->ic_flags |= IEEE80211_F_SIBSS;
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
+ vap->iv_flags |= IEEE80211_F_SIBSS;
ni->ni_capinfo |= IEEE80211_CAPINFO_IBSS; /* XXX */
- if (ic->ic_flags & IEEE80211_F_DESBSSID)
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_des_bssid);
+ if (vap->iv_flags & IEEE80211_F_DESBSSID)
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_des_bssid);
else {
get_random_bytes(ni->ni_bssid, IEEE80211_ADDR_LEN);
/* clear group bit, add local bit */
ni->ni_bssid[0] = (ni->ni_bssid[0] &~ 0x01) | 0x02;
}
- } else if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
- if (ic->ic_flags & IEEE80211_F_DESBSSID)
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_des_bssid);
+ } else if (vap->iv_opmode == IEEE80211_M_AHDEMO) {
+ if (vap->iv_flags & IEEE80211_F_DESBSSID)
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_des_bssid);
else
memset(ni->ni_bssid, 0, IEEE80211_ADDR_LEN);
}
/*
* Fix the channel and related attributes.
*/
+ /* clear DFS CAC state on previous channel */
+ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+ ic->ic_bsschan->ic_freq != chan->ic_freq &&
+ IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan))
+ ieee80211_dfs_cac_clear(ic, ic->ic_bsschan);
ic->ic_bsschan = chan;
- ieee80211_node_set_chan(ic, ni);
+ ieee80211_node_set_chan(ni, chan);
ic->ic_curmode = ieee80211_chan2mode(chan);
/*
- * Do mode-specific rate setup.
+ * Do mode-specific setup.
*/
if (IEEE80211_IS_CHAN_FULL(chan)) {
if (IEEE80211_IS_CHAN_ANYG(chan)) {
/*
- * Use a mixed 11b/11g rate set.
+ * Use a mixed 11b/11g basic rate set.
*/
- ieee80211_set11gbasicrates(&ni->ni_rates,
- IEEE80211_MODE_11G);
+ ieee80211_setbasicrates(&ni->ni_rates,
+ IEEE80211_MODE_11G);
+ if (vap->iv_flags & IEEE80211_F_PUREG) {
+ /*
+ * Also mark OFDM rates basic so 11b
+ * stations do not join (WiFi compliance).
+ */
+ ieee80211_addbasicrates(&ni->ni_rates,
+ IEEE80211_MODE_11A);
+ }
} else if (IEEE80211_IS_CHAN_B(chan)) {
/*
* Force pure 11b rate set.
*/
- ieee80211_set11gbasicrates(&ni->ni_rates,
+ ieee80211_setbasicrates(&ni->ni_rates,
IEEE80211_MODE_11B);
}
}
@@ -362,23 +329,25 @@ ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan)
* etc. state).
*/
void
-ieee80211_reset_bss(struct ieee80211com *ic)
+ieee80211_reset_bss(struct ieee80211vap *vap)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni, *obss;
- callout_drain(&ic->ic_inact);
- ieee80211_node_table_reset(&ic->ic_sta);
+ ieee80211_node_table_reset(&ic->ic_sta, vap);
+ /* XXX multi-bss: wrong */
ieee80211_reset_erp(ic);
- ni = ieee80211_alloc_node(&ic->ic_sta, ic->ic_myaddr);
+ ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr);
KASSERT(ni != NULL, ("unable to setup inital BSS node"));
- obss = ic->ic_bss;
- ic->ic_bss = ieee80211_ref_node(ni);
+ obss = vap->iv_bss;
+ vap->iv_bss = ieee80211_ref_node(ni);
if (obss != NULL) {
copy_bss(ni, obss);
ni->ni_intval = ic->ic_bintval;
ieee80211_free_node(obss);
- }
+ } else
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr);
}
static int
@@ -399,20 +368,21 @@ match_ssid(const struct ieee80211_node *ni,
* Test a node for suitability/compatibility.
*/
static int
-check_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
+check_bss(struct ieee80211vap *vap, struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
uint8_t rate;
if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan)))
return 0;
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
return 0;
} else {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
return 0;
}
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ if (vap->iv_flags & IEEE80211_F_PRIVACY) {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
return 0;
} else {
@@ -424,11 +394,11 @@ check_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
if (rate & IEEE80211_RATE_BASIC)
return 0;
- if (ic->ic_des_nssid != 0 &&
- !match_ssid(ni, ic->ic_des_nssid, ic->ic_des_ssid))
+ if (vap->iv_des_nssid != 0 &&
+ !match_ssid(ni, vap->iv_des_nssid, vap->iv_des_ssid))
return 0;
- if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
- !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
+ if ((vap->iv_flags & IEEE80211_F_DESBSSID) &&
+ !IEEE80211_ADDR_EQ(vap->iv_des_bssid, ni->ni_bssid))
return 0;
return 1;
}
@@ -438,22 +408,23 @@ check_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
* Display node suitability/compatibility.
*/
static void
-check_bss_debug(struct ieee80211com *ic, struct ieee80211_node *ni)
+check_bss_debug(struct ieee80211vap *vap, struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
uint8_t rate;
int fail;
fail = 0;
if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan)))
fail |= 0x01;
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
fail |= 0x02;
} else {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
fail |= 0x02;
}
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ if (vap->iv_flags & IEEE80211_F_PRIVACY) {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
fail |= 0x04;
} else {
@@ -465,18 +436,17 @@ check_bss_debug(struct ieee80211com *ic, struct ieee80211_node *ni)
IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
if (rate & IEEE80211_RATE_BASIC)
fail |= 0x08;
- if (ic->ic_des_nssid != 0 &&
- !match_ssid(ni, ic->ic_des_nssid, ic->ic_des_ssid))
+ if (vap->iv_des_nssid != 0 &&
+ !match_ssid(ni, vap->iv_des_nssid, vap->iv_des_ssid))
fail |= 0x10;
- if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
- !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
+ if ((vap->iv_flags & IEEE80211_F_DESBSSID) &&
+ !IEEE80211_ADDR_EQ(vap->iv_des_bssid, ni->ni_bssid))
fail |= 0x20;
printf(" %c %s", fail ? '-' : '+', ether_sprintf(ni->ni_macaddr));
printf(" %s%c", ether_sprintf(ni->ni_bssid), fail & 0x20 ? '!' : ' ');
printf(" %3d%c",
ieee80211_chan2ieee(ic, ni->ni_chan), fail & 0x01 ? '!' : ' ');
- printf(" %+4d", ni->ni_rssi);
printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2,
fail & 0x08 ? '!' : ' ');
printf(" %4s%c",
@@ -507,25 +477,28 @@ check_bss_debug(struct ieee80211com *ic, struct ieee80211_node *ni)
int
ieee80211_ibss_merge(struct ieee80211_node *ni)
{
+ struct ieee80211vap *vap = ni->ni_vap;
+#ifdef IEEE80211_DEBUG
struct ieee80211com *ic = ni->ni_ic;
+#endif
- if (ni == ic->ic_bss ||
- IEEE80211_ADDR_EQ(ni->ni_bssid, ic->ic_bss->ni_bssid)) {
+ if (ni == vap->iv_bss ||
+ IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid)) {
/* unchanged, nothing to do */
return 0;
}
- if (!check_bss(ic, ni)) {
+ if (!check_bss(vap, ni)) {
/* capabilities mismatch */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
"%s: merge failed, capabilities mismatch\n", __func__);
#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_assoc(ic))
- check_bss_debug(ic, ni);
+ if (ieee80211_msg_assoc(vap))
+ check_bss_debug(vap, ni);
#endif
- ic->ic_stats.is_ibss_capmismatch++;
+ vap->iv_stats.is_ibss_capmismatch++;
return 0;
}
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
"%s: new bssid %s: %s preamble, %s slot time%s\n", __func__,
ether_sprintf(ni->ni_bssid),
ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
@@ -536,13 +509,72 @@ ieee80211_ibss_merge(struct ieee80211_node *ni)
}
/*
- * Change the bss channel.
+ * Calculate HT channel promotion flags for all vaps.
+ * This assumes ni_chan have been setup for each vap.
+ */
+static int
+gethtadjustflags(struct ieee80211com *ic)
+{
+ struct ieee80211vap *vap;
+ int flags;
+
+ flags = 0;
+ /* XXX locking */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap->iv_state < IEEE80211_S_RUN)
+ continue;
+ switch (vap->iv_opmode) {
+ case IEEE80211_M_WDS:
+ case IEEE80211_M_STA:
+ case IEEE80211_M_AHDEMO:
+ case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_IBSS:
+ flags |= ieee80211_htchanflags(vap->iv_bss->ni_chan);
+ break;
+ default:
+ break;
+ }
+ }
+ return flags;
+}
+
+/*
+ * Check if the current channel needs to change based on whether
+ * any vap's are using HT20/HT40. This is used sync the state of
+ * ic_curchan after a channel width change on a running vap.
*/
void
-ieee80211_setbsschan(struct ieee80211com *ic, struct ieee80211_channel *c)
+ieee80211_sync_curchan(struct ieee80211com *ic)
{
- ic->ic_bsschan = c;
- ic->ic_curchan = ic->ic_bsschan;
+ struct ieee80211_channel *c;
+
+ c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan, gethtadjustflags(ic));
+ if (c != ic->ic_curchan) {
+ ic->ic_curchan = c;
+ ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
+ ic->ic_set_channel(ic);
+ }
+}
+
+/*
+ * Change the current channel. The request channel may be
+ * promoted if other vap's are operating with HT20/HT40.
+ */
+void
+ieee80211_setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
+{
+ if (ic->ic_htcaps & IEEE80211_HTC_HT) {
+ int flags = gethtadjustflags(ic);
+ /*
+ * Check for channel promotion required to support the
+ * set of running vap's. This assumes we are called
+ * after ni_chan is setup for each vap.
+ */
+ /* NB: this assumes IEEE80211_FEXT_USEHT40 > IEEE80211_FEXT_HT */
+ if (flags > ieee80211_htchanflags(c))
+ c = ieee80211_ht_adjust_channel(ic, c, flags);
+ }
+ ic->ic_bsschan = ic->ic_curchan = c;
ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
ic->ic_set_channel(ic);
}
@@ -554,61 +586,49 @@ ieee80211_setbsschan(struct ieee80211com *ic, struct ieee80211_channel *c)
static int
ieee80211_sta_join1(struct ieee80211_node *selbs)
{
+ struct ieee80211vap *vap = selbs->ni_vap;
struct ieee80211com *ic = selbs->ni_ic;
struct ieee80211_node *obss;
int canreassoc;
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- struct ieee80211_node_table *nt;
- /*
- * Fillin the neighbor table; it will already
- * exist if we are simply switching mastership.
- * XXX ic_sta always setup so this is unnecessary?
- */
- nt = &ic->ic_sta;
- IEEE80211_NODE_LOCK(nt);
- nt->nt_name = "neighbor";
- nt->nt_inact_init = ic->ic_inact_run;
- IEEE80211_NODE_UNLOCK(nt);
- }
-
/*
* Committed to selbs, setup state.
*/
- obss = ic->ic_bss;
+ obss = vap->iv_bss;
/*
* Check if old+new node have the same address in which
* case we can reassociate when operating in sta mode.
*/
canreassoc = (obss != NULL &&
- ic->ic_state == IEEE80211_S_RUN &&
+ vap->iv_state == IEEE80211_S_RUN &&
IEEE80211_ADDR_EQ(obss->ni_macaddr, selbs->ni_macaddr));
- ic->ic_bss = selbs; /* NB: caller assumed to bump refcnt */
+ vap->iv_bss = selbs; /* NB: caller assumed to bump refcnt */
if (obss != NULL) {
copy_bss(selbs, obss);
- ieee80211_free_node(obss);
+ ieee80211_node_reclaim(obss);
+ obss = NULL; /* NB: guard against later use */
}
/*
* Delete unusable rates; we've already checked
* that the negotiated rate set is acceptable.
*/
- ieee80211_fix_rate(ic->ic_bss, &ic->ic_bss->ni_rates,
+ ieee80211_fix_rate(vap->iv_bss, &vap->iv_bss->ni_rates,
IEEE80211_F_DODEL | IEEE80211_F_JOIN);
- ieee80211_setbsschan(ic, selbs->ni_chan);
+ ieee80211_setcurchan(ic, selbs->ni_chan);
/*
* Set the erp state (mostly the slot time) to deal with
* the auto-select case; this should be redundant if the
* mode is locked.
*/
ieee80211_reset_erp(ic);
- ieee80211_wme_initparams(ic);
+ ieee80211_wme_initparams(vap);
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
if (canreassoc) {
/* Reassociate */
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1);
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1);
} else {
/*
* Act as if we received a DEAUTH frame in case we
@@ -616,21 +636,22 @@ ieee80211_sta_join1(struct ieee80211_node *selbs)
* us to try to re-authenticate if we are operating
* as a station.
*/
- ieee80211_new_state(ic, IEEE80211_S_AUTH,
+ ieee80211_new_state(vap, IEEE80211_S_AUTH,
IEEE80211_FC0_SUBTYPE_DEAUTH);
}
} else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ ieee80211_new_state(vap, IEEE80211_S_RUN, -1);
return 1;
}
int
-ieee80211_sta_join(struct ieee80211com *ic,
+ieee80211_sta_join(struct ieee80211vap *vap,
const struct ieee80211_scan_entry *se)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
- ni = ieee80211_alloc_node(&ic->ic_sta, se->se_macaddr);
+ ni = ieee80211_alloc_node(&ic->ic_sta, vap, se->se_macaddr);
if (ni == NULL) {
/* XXX msg */
return 0;
@@ -651,23 +672,21 @@ ieee80211_sta_join(struct ieee80211com *ic,
ni->ni_fhdwell = se->se_fhdwell;
ni->ni_fhindex = se->se_fhindex;
ni->ni_erp = se->se_erp;
- ni->ni_rssi = se->se_rssi;
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, se->se_rssi);
ni->ni_noise = se->se_noise;
- if (se->se_htcap_ie != NULL) {
- ieee80211_saveie(&ni->ni_htcap_ie, se->se_htcap_ie);
- ieee80211_parse_htcap(ni, ni->ni_htcap_ie);
+
+ if (ieee80211_ies_init(&ni->ni_ies, se->se_ies.data, se->se_ies.len)) {
+ ieee80211_ies_expand(&ni->ni_ies);
+ if (ni->ni_ies.ath_ie != NULL)
+ ieee80211_parse_ath(ni, ni->ni_ies.ath_ie);
+ if (ni->ni_ies.htcap_ie != NULL)
+ ieee80211_parse_htcap(ni, ni->ni_ies.htcap_ie);
+ if (ni->ni_ies.htinfo_ie != NULL)
+ ieee80211_parse_htinfo(ni, ni->ni_ies.htinfo_ie);
}
- if (se->se_wpa_ie != NULL)
- ieee80211_saveie(&ni->ni_wpa_ie, se->se_wpa_ie);
- if (se->se_rsn_ie != NULL)
- ieee80211_saveie(&ni->ni_rsn_ie, se->se_rsn_ie);
- if (se->se_wme_ie != NULL)
- ieee80211_saveie(&ni->ni_wme_ie, se->se_wme_ie);
- if (se->se_ath_ie != NULL)
- ieee80211_saveath(ni, se->se_ath_ie);
-
- ic->ic_dtim_period = se->se_dtimperiod;
- ic->ic_dtim_count = 0;
+
+ vap->iv_dtim_period = se->se_dtimperiod;
+ vap->iv_dtim_count = 0;
/* NB: must be after ni_chan is setup */
ieee80211_setup_rates(ni, se->se_rates, se->se_xrates,
@@ -681,10 +700,26 @@ ieee80211_sta_join(struct ieee80211com *ic,
* be passed in with a held reference.
*/
void
-ieee80211_sta_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_sta_leave(struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
+
ic->ic_node_cleanup(ni);
- ieee80211_notify_node_leave(ic, ni);
+ ieee80211_notify_node_leave(ni);
+}
+
+/*
+ * Send a deauthenticate frame and drop the station.
+ */
+void
+ieee80211_node_deauth(struct ieee80211_node *ni, int reason)
+{
+ /* NB: bump the refcnt to be sure temporay nodes are not reclaimed */
+ ieee80211_ref_node(ni);
+ if (ni->ni_associd != 0)
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
+ ieee80211_node_leave(ni);
+ ieee80211_free_node(ni);
}
static struct ieee80211_node *
@@ -698,6 +733,80 @@ node_alloc(struct ieee80211_node_table *nt)
}
/*
+ * Initialize an ie blob with the specified data. If previous
+ * data exists re-use the data block. As a side effect we clear
+ * all references to specific ie's; the caller is required to
+ * recalculate them.
+ */
+int
+ieee80211_ies_init(struct ieee80211_ies *ies, const uint8_t *data, int len)
+{
+ /* NB: assumes data+len are the last fields */
+ memset(ies, 0, offsetof(struct ieee80211_ies, data));
+ if (ies->data != NULL && ies->len != len) {
+ /* data size changed */
+ FREE(ies->data, M_80211_NODE_IE);
+ ies->data = NULL;
+ }
+ if (ies->data == NULL) {
+ MALLOC(ies->data, uint8_t *, len, M_80211_NODE_IE, M_NOWAIT);
+ if (ies->data == NULL) {
+ ies->len = 0;
+ /* NB: pointers have already been zero'd above */
+ return 0;
+ }
+ }
+ memcpy(ies->data, data, len);
+ ies->len = len;
+ return 1;
+}
+
+/*
+ * Reclaim storage for an ie blob.
+ */
+void
+ieee80211_ies_cleanup(struct ieee80211_ies *ies)
+{
+ if (ies->data != NULL)
+ FREE(ies->data, M_80211_NODE_IE);
+}
+
+/*
+ * Expand an ie blob data contents and to fillin individual
+ * ie pointers. The data blob is assumed to be well-formed;
+ * we don't do any validity checking of ie lengths.
+ */
+void
+ieee80211_ies_expand(struct ieee80211_ies *ies)
+{
+ uint8_t *ie;
+ int ielen;
+
+ ie = ies->data;
+ ielen = ies->len;
+ while (ielen > 0) {
+ switch (ie[0]) {
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswpaoui(ie))
+ ies->wpa_ie = ie;
+ else if (iswmeoui(ie))
+ ies->wme_ie = ie;
+ else if (isatherosoui(ie))
+ ies->ath_ie = ie;
+ break;
+ case IEEE80211_ELEMID_RSN:
+ ies->rsn_ie = ie;
+ break;
+ case IEEE80211_ELEMID_HTCAP:
+ ies->htcap_ie = ie;
+ break;
+ }
+ ielen -= 2 + ie[1];
+ ie += 2 + ie[1];
+ }
+}
+
+/*
* Reclaim any resources in a node and reset any critical
* state. Typically nodes are free'd immediately after,
* but in some cases the storage may be reused so we need
@@ -707,17 +816,16 @@ static void
node_cleanup(struct ieee80211_node *ni)
{
#define N(a) (sizeof(a)/sizeof(a[0]))
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
int i;
/* NB: preserve ni_table */
if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) {
- if (ic->ic_opmode != IEEE80211_M_STA)
- ic->ic_ps_sta--;
+ if (vap->iv_opmode != IEEE80211_M_STA)
+ vap->iv_ps_sta--;
ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
- "[%s] power save mode off, %u sta's in ps mode\n",
- ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "power save mode off, %u sta's in ps mode", vap->iv_ps_sta);
}
/*
* Cleanup any HT-related state.
@@ -735,8 +843,8 @@ node_cleanup(struct ieee80211_node *ni)
/*
* Drain power save queue and, if needed, clear TIM.
*/
- if (ieee80211_node_saveq_drain(ni) != 0 && ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0);
+ if (ieee80211_node_saveq_drain(ni) != 0 && vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 0);
ni->ni_associd = 0;
if (ni->ni_challenge != NULL) {
@@ -773,39 +881,80 @@ node_free(struct ieee80211_node *ni)
struct ieee80211com *ic = ni->ni_ic;
ic->ic_node_cleanup(ni);
- if (ni->ni_wpa_ie != NULL)
- FREE(ni->ni_wpa_ie, M_80211_NODE);
- if (ni->ni_rsn_ie != NULL)
- FREE(ni->ni_rsn_ie, M_80211_NODE);
- if (ni->ni_wme_ie != NULL)
- FREE(ni->ni_wme_ie, M_80211_NODE);
- if (ni->ni_ath_ie != NULL)
- FREE(ni->ni_ath_ie, M_80211_NODE);
+ ieee80211_ies_cleanup(&ni->ni_ies);
IEEE80211_NODE_SAVEQ_DESTROY(ni);
+ IEEE80211_NODE_WDSQ_DESTROY(ni);
FREE(ni, M_80211_NODE);
}
+static void
+node_age(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+#if 0
+ IEEE80211_NODE_LOCK_ASSERT(&ic->ic_sta);
+#endif
+ /*
+ * Age frames on the power save queue.
+ */
+ if (ieee80211_node_saveq_age(ni) != 0 &&
+ IEEE80211_NODE_SAVEQ_QLEN(ni) == 0 &&
+ vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 0);
+ /*
+ * Age frames on the wds pending queue.
+ */
+ if (IEEE80211_NODE_WDSQ_QLEN(ni) != 0)
+ ieee80211_node_wdsq_age(ni);
+ /*
+ * Age out HT resources (e.g. frames on the
+ * A-MPDU reorder queues).
+ */
+ if (ni->ni_associd != 0 && (ni->ni_flags & IEEE80211_NODE_HT))
+ ieee80211_ht_node_age(ni);
+}
+
static int8_t
node_getrssi(const struct ieee80211_node *ni)
{
- return ni->ni_rssi;
+ uint32_t avgrssi = ni->ni_avgrssi;
+ int32_t rssi;
+
+ if (avgrssi == IEEE80211_RSSI_DUMMY_MARKER)
+ return 0;
+ rssi = IEEE80211_RSSI_GET(avgrssi);
+ return rssi < 0 ? 0 : rssi > 127 ? 127 : rssi;
}
static void
node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise)
{
- *rssi = ni->ni_rssi;
+ *rssi = node_getrssi(ni);
*noise = ni->ni_noise;
}
static void
-ieee80211_setup_node(struct ieee80211_node_table *nt,
- struct ieee80211_node *ni, const uint8_t *macaddr)
+node_getmimoinfo(const struct ieee80211_node *ni,
+ struct ieee80211_mimo_info *info)
+{
+ /* XXX zero data? */
+}
+
+struct ieee80211_node *
+ieee80211_alloc_node(struct ieee80211_node_table *nt,
+ struct ieee80211vap *vap, const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
struct ieee80211com *ic = nt->nt_ic;
+ struct ieee80211_node *ni;
int hash;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ ni = ic->ic_node_alloc(nt);
+ if (ni == NULL) {
+ vap->iv_stats.is_rx_nodealloc++;
+ return NULL;
+ }
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s %p<%s> in %s table\n", __func__, ni,
ether_sprintf(macaddr), nt->nt_name);
@@ -815,31 +964,22 @@ ieee80211_setup_node(struct ieee80211_node_table *nt,
ni->ni_chan = IEEE80211_CHAN_ANYC;
ni->ni_authmode = IEEE80211_AUTH_OPEN;
ni->ni_txpower = ic->ic_txpowlimit; /* max power */
- ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE);
+ ieee80211_crypto_resetkey(vap, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE);
+ ni->ni_avgrssi = IEEE80211_RSSI_DUMMY_MARKER;
ni->ni_inact_reload = nt->nt_inact_init;
ni->ni_inact = ni->ni_inact_reload;
ni->ni_ath_defkeyix = 0x7fff;
IEEE80211_NODE_SAVEQ_INIT(ni, "unknown");
+ IEEE80211_NODE_WDSQ_INIT(ni, "unknown");
IEEE80211_NODE_LOCK(nt);
TAILQ_INSERT_TAIL(&nt->nt_node, ni, ni_list);
LIST_INSERT_HEAD(&nt->nt_hash[hash], ni, ni_hash);
ni->ni_table = nt;
+ ni->ni_vap = vap;
ni->ni_ic = ic;
IEEE80211_NODE_UNLOCK(nt);
-}
-
-struct ieee80211_node *
-ieee80211_alloc_node(struct ieee80211_node_table *nt, const uint8_t *macaddr)
-{
- struct ieee80211com *ic = nt->nt_ic;
- struct ieee80211_node *ni;
- ni = ic->ic_node_alloc(nt);
- if (ni != NULL)
- ieee80211_setup_node(nt, ni, macaddr);
- else
- ic->ic_stats.is_rx_nodealloc++;
return ni;
}
@@ -850,65 +990,129 @@ ieee80211_alloc_node(struct ieee80211_node_table *nt, const uint8_t *macaddr)
* once the send completes.
*/
struct ieee80211_node *
-ieee80211_tmp_node(struct ieee80211com *ic, const uint8_t *macaddr)
+ieee80211_tmp_node(struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
ni = ic->ic_node_alloc(&ic->ic_sta);
if (ni != NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s %p<%s>\n", __func__, ni, ether_sprintf(macaddr));
+ ni->ni_table = NULL; /* NB: pedantic */
+ ni->ni_ic = ic; /* NB: needed to set channel */
+ ni->ni_vap = vap;
+
IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr);
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_bss->ni_bssid);
ieee80211_node_initref(ni); /* mark referenced */
- ni->ni_txpower = ic->ic_bss->ni_txpower;
/* NB: required by ieee80211_fix_rate */
- ieee80211_node_set_chan(ic, ni);
- ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey,
+ ieee80211_node_set_chan(ni, vap->iv_bss->ni_chan);
+ ni->ni_txpower = vap->iv_bss->ni_txpower;
+ ieee80211_crypto_resetkey(vap, &ni->ni_ucastkey,
IEEE80211_KEYIX_NONE);
/* XXX optimize away */
IEEE80211_NODE_SAVEQ_INIT(ni, "unknown");
-
- ni->ni_table = NULL; /* NB: pedantic */
- ni->ni_ic = ic;
+ IEEE80211_NODE_WDSQ_INIT(ni, "unknown");
} else {
/* XXX msg */
- ic->ic_stats.is_rx_nodealloc++;
+ vap->iv_stats.is_rx_nodealloc++;
}
return ni;
}
struct ieee80211_node *
-ieee80211_dup_bss(struct ieee80211_node_table *nt, const uint8_t *macaddr)
+ieee80211_dup_bss(struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
- struct ieee80211com *ic = nt->nt_ic;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
- ni = ic->ic_node_alloc(nt);
+ ni = ieee80211_alloc_node(&ic->ic_sta, vap, macaddr);
if (ni != NULL) {
- ieee80211_setup_node(nt, ni, macaddr);
/*
- * Inherit from ic_bss.
+ * Inherit from iv_bss.
*/
- ni->ni_authmode = ic->ic_bss->ni_authmode;
- ni->ni_txpower = ic->ic_bss->ni_txpower;
- ni->ni_vlan = ic->ic_bss->ni_vlan; /* XXX?? */
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
- ieee80211_node_set_chan(ic, ni);
- ni->ni_rsn = ic->ic_bss->ni_rsn;
- } else
- ic->ic_stats.is_rx_nodealloc++;
+ ni->ni_authmode = vap->iv_bss->ni_authmode;
+ ni->ni_txpower = vap->iv_bss->ni_txpower;
+ ni->ni_vlan = vap->iv_bss->ni_vlan; /* XXX?? */
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_bss->ni_bssid);
+ ieee80211_node_set_chan(ni, vap->iv_bss->ni_chan);
+ }
return ni;
}
-static struct ieee80211_node *
+/*
+ * Create a bss node for a legacy WDS vap. The far end does
+ * not associate so we just create create a new node and
+ * simulate an association. The caller is responsible for
+ * installing the node as the bss node and handling any further
+ * setup work like authorizing the port.
+ */
+struct ieee80211_node *
+ieee80211_node_create_wds(struct ieee80211vap *vap,
+ const uint8_t bssid[IEEE80211_ADDR_LEN], struct ieee80211_channel *chan)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
+
+ /* XXX check if node already in sta table? */
+ ni = ieee80211_alloc_node(&ic->ic_sta, vap, bssid);
+ if (ni != NULL) {
+ ni->ni_wdsvap = vap;
+ IEEE80211_ADDR_COPY(ni->ni_bssid, bssid);
+ /*
+ * Inherit any manually configured settings.
+ */
+ ni->ni_authmode = vap->iv_bss->ni_authmode;
+ ni->ni_txpower = vap->iv_bss->ni_txpower;
+ ni->ni_vlan = vap->iv_bss->ni_vlan;
+ ieee80211_node_set_chan(ni, chan);
+ /* NB: propagate ssid so available to WPA supplicant */
+ ni->ni_esslen = vap->iv_des_ssid[0].len;
+ memcpy(ni->ni_essid, vap->iv_des_ssid[0].ssid, ni->ni_esslen);
+ /* NB: no associd for peer */
+ /*
+ * There are no management frames to use to
+ * discover neighbor capabilities, so blindly
+ * propagate the local configuration.
+ */
+ if (vap->iv_flags & IEEE80211_F_WME)
+ ni->ni_flags |= IEEE80211_NODE_QOS;
+ if (vap->iv_flags & IEEE80211_F_FF)
+ ni->ni_flags |= IEEE80211_NODE_FF;
+ if ((ic->ic_htcaps & IEEE80211_HTC_HT) &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_HT)) {
+ /*
+ * Device is HT-capable and HT is enabled for
+ * the vap; setup HT operation. On return
+ * ni_chan will be adjusted to an HT channel.
+ */
+ ieee80211_ht_wds_init(ni);
+ } else {
+ struct ieee80211_channel *c = ni->ni_chan;
+ /*
+ * Force a legacy channel to be used.
+ */
+ c = ieee80211_find_channel(ic,
+ c->ic_freq, c->ic_flags &~ IEEE80211_CHAN_HT);
+ KASSERT(c != NULL, ("no legacy channel, %u/%x",
+ ni->ni_chan->ic_freq, ni->ni_chan->ic_flags));
+ ni->ni_chan = c;
+ }
+ }
+ return ni;
+}
+
+struct ieee80211_node *
#ifdef IEEE80211_DEBUG_REFCNT
-_ieee80211_find_node_debug(struct ieee80211_node_table *nt,
- const uint8_t *macaddr, const char *func, int line)
+ieee80211_find_node_locked_debug(struct ieee80211_node_table *nt,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line)
#else
-_ieee80211_find_node(struct ieee80211_node_table *nt,
- const uint8_t *macaddr)
+ieee80211_find_node_locked(struct ieee80211_node_table *nt,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
#endif
{
struct ieee80211_node *ni;
@@ -921,7 +1125,7 @@ _ieee80211_find_node(struct ieee80211_node_table *nt,
if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
ieee80211_ref_node(ni); /* mark referenced */
#ifdef IEEE80211_DEBUG_REFCNT
- IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
"%s (%s:%u) %p<%s> refcnt %d\n", __func__,
func, line,
ni, ether_sprintf(ni->ni_macaddr),
@@ -932,23 +1136,73 @@ _ieee80211_find_node(struct ieee80211_node_table *nt,
}
return NULL;
}
+
+struct ieee80211_node *
#ifdef IEEE80211_DEBUG_REFCNT
-#define _ieee80211_find_node(nt, mac) \
- _ieee80211_find_node_debug(nt, mac, func, line)
+ieee80211_find_node_debug(struct ieee80211_node_table *nt,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line)
+#else
+ieee80211_find_node(struct ieee80211_node_table *nt,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
#endif
+{
+ struct ieee80211_node *ni;
+
+ IEEE80211_NODE_LOCK(nt);
+ ni = ieee80211_find_node_locked(nt, macaddr);
+ IEEE80211_NODE_UNLOCK(nt);
+ return ni;
+}
struct ieee80211_node *
#ifdef IEEE80211_DEBUG_REFCNT
-ieee80211_find_node_debug(struct ieee80211_node_table *nt,
- const uint8_t *macaddr, const char *func, int line)
+ieee80211_find_vap_node_locked_debug(struct ieee80211_node_table *nt,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line)
#else
-ieee80211_find_node(struct ieee80211_node_table *nt, const uint8_t *macaddr)
+ieee80211_find_vap_node_locked(struct ieee80211_node_table *nt,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
+#endif
+{
+ struct ieee80211_node *ni;
+ int hash;
+
+ IEEE80211_NODE_LOCK_ASSERT(nt);
+
+ hash = IEEE80211_NODE_HASH(macaddr);
+ LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
+ if (ni->ni_vap == vap &&
+ IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
+ ieee80211_ref_node(ni); /* mark referenced */
+#ifdef IEEE80211_DEBUG_REFCNT
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
+ "%s (%s:%u) %p<%s> refcnt %d\n", __func__,
+ func, line,
+ ni, ether_sprintf(ni->ni_macaddr),
+ ieee80211_node_refcnt(ni));
+#endif
+ return ni;
+ }
+ }
+ return NULL;
+}
+
+struct ieee80211_node *
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_vap_node_debug(struct ieee80211_node_table *nt,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line)
+#else
+ieee80211_find_vap_node(struct ieee80211_node_table *nt,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
#endif
{
struct ieee80211_node *ni;
IEEE80211_NODE_LOCK(nt);
- ni = _ieee80211_find_node(nt, macaddr);
+ ni = ieee80211_find_vap_node_locked(nt, vap, macaddr);
IEEE80211_NODE_UNLOCK(nt);
return ni;
}
@@ -960,21 +1214,20 @@ ieee80211_find_node(struct ieee80211_node_table *nt, const uint8_t *macaddr)
* it's private state.
*/
struct ieee80211_node *
-ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt,
+ieee80211_fakeup_adhoc_node(struct ieee80211vap *vap,
const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
- struct ieee80211com *ic = nt->nt_ic;
struct ieee80211_node *ni;
- IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s: mac<%s>\n", __func__, ether_sprintf(macaddr));
- ni = ieee80211_dup_bss(nt, macaddr);
+ ni = ieee80211_dup_bss(vap, macaddr);
if (ni != NULL) {
+ struct ieee80211com *ic = vap->iv_ic;
+
/* XXX no rate negotiation; just dup */
- ni->ni_rates = ic->ic_bss->ni_rates;
- if (ic->ic_newassoc != NULL)
- ic->ic_newassoc(ni, 1);
- if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
+ ni->ni_rates = vap->iv_bss->ni_rates;
+ if (vap->iv_opmode == IEEE80211_M_AHDEMO) {
/*
* In adhoc demo mode there are no management
* frames to use to discover neighbor capabilities,
@@ -982,11 +1235,13 @@ ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt,
* so we can do interesting things (e.g. use
* WME to disable ACK's).
*/
- if (ic->ic_flags & IEEE80211_F_WME)
+ if (vap->iv_flags & IEEE80211_F_WME)
ni->ni_flags |= IEEE80211_NODE_QOS;
- if (ic->ic_flags & IEEE80211_F_FF)
+ if (vap->iv_flags & IEEE80211_F_FF)
ni->ni_flags |= IEEE80211_NODE_FF;
}
+ if (ic->ic_newassoc != NULL)
+ ic->ic_newassoc(ni, 1);
/* XXX not right for 802.1x/WPA */
ieee80211_node_authorize(ni);
}
@@ -1009,14 +1264,12 @@ ieee80211_init_neighbor(struct ieee80211_node *ni,
ni->ni_fhindex = sp->fhindex;
ni->ni_erp = sp->erp;
ni->ni_timoff = sp->timoff;
- if (sp->wme != NULL)
- ieee80211_saveie(&ni->ni_wme_ie, sp->wme);
- if (sp->wpa != NULL)
- ieee80211_saveie(&ni->ni_wpa_ie, sp->wpa);
- if (sp->rsn != NULL)
- ieee80211_saveie(&ni->ni_rsn_ie, sp->rsn);
- if (sp->ath != NULL)
- ieee80211_saveath(ni, sp->ath);
+
+ if (ieee80211_ies_init(&ni->ni_ies, sp->ies, sp->ies_len)) {
+ ieee80211_ies_expand(&ni->ni_ies);
+ if (ni->ni_ies.ath_ie != NULL)
+ ieee80211_parse_ath(ni, ni->ni_ies.ath_ie);
+ }
/* NB: must be after ni_chan is setup */
ieee80211_setup_rates(ni, sp->rates, sp->xrates,
@@ -1031,16 +1284,18 @@ ieee80211_init_neighbor(struct ieee80211_node *ni,
* driver has an opportunity to setup it's private state.
*/
struct ieee80211_node *
-ieee80211_add_neighbor(struct ieee80211com *ic,
+ieee80211_add_neighbor(struct ieee80211vap *vap,
const struct ieee80211_frame *wh,
const struct ieee80211_scanparams *sp)
{
struct ieee80211_node *ni;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s: mac<%s>\n", __func__, ether_sprintf(wh->i_addr2));
- ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);/* XXX alloc_node? */
+ ni = ieee80211_dup_bss(vap, wh->i_addr2);/* XXX alloc_node? */
if (ni != NULL) {
+ struct ieee80211com *ic = vap->iv_ic;
+
ieee80211_init_neighbor(ni, wh, sp);
if (ic->ic_newassoc != NULL)
ic->ic_newassoc(ni, 1);
@@ -1077,16 +1332,13 @@ ieee80211_find_rxnode(struct ieee80211com *ic,
struct ieee80211_node_table *nt;
struct ieee80211_node *ni;
- /* XXX check ic_bss first in station mode */
/* XXX 4-address frames? */
nt = &ic->ic_sta;
IEEE80211_NODE_LOCK(nt);
if (IS_CTL(wh) && !IS_PSPOLL(wh) && !IS_BAR(wh) /*&& !IS_RTS(ah)*/)
- ni = _ieee80211_find_node(nt, wh->i_addr1);
+ ni = ieee80211_find_node_locked(nt, wh->i_addr1);
else
- ni = _ieee80211_find_node(nt, wh->i_addr2);
- if (ni == NULL)
- ni = ieee80211_ref_node(ic->ic_bss);
+ ni = ieee80211_find_node_locked(nt, wh->i_addr2);
IEEE80211_NODE_UNLOCK(nt);
return ni;
@@ -1121,12 +1373,10 @@ ieee80211_find_rxnode_withkey(struct ieee80211com *ic,
ni = NULL;
if (ni == NULL) {
if (IS_CTL(wh) && !IS_PSPOLL(wh) && !IS_BAR(wh) /*&& !IS_RTS(ah)*/)
- ni = _ieee80211_find_node(nt, wh->i_addr1);
+ ni = ieee80211_find_node_locked(nt, wh->i_addr1);
else
- ni = _ieee80211_find_node(nt, wh->i_addr2);
- if (ni == NULL)
- ni = ieee80211_ref_node(ic->ic_bss);
- if (nt->nt_keyixmap != NULL) {
+ ni = ieee80211_find_node_locked(nt, wh->i_addr2);
+ if (ni != NULL && nt->nt_keyixmap != NULL) {
/*
* If the station has a unicast key cache slot
* assigned update the key->node mapping table.
@@ -1135,7 +1385,8 @@ ieee80211_find_rxnode_withkey(struct ieee80211com *ic,
/* XXX can keyixmap[keyix] != NULL? */
if (keyix < nt->nt_keyixmax &&
nt->nt_keyixmap[keyix] == NULL) {
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap,
+ IEEE80211_MSG_NODE,
"%s: add key map entry %p<%s> refcnt %d\n",
__func__, ni, ether_sprintf(ni->ni_macaddr),
ieee80211_node_refcnt(ni)+1);
@@ -1158,13 +1409,15 @@ ieee80211_find_rxnode_withkey(struct ieee80211com *ic,
*/
struct ieee80211_node *
#ifdef IEEE80211_DEBUG_REFCNT
-ieee80211_find_txnode_debug(struct ieee80211com *ic, const uint8_t *macaddr,
+ieee80211_find_txnode_debug(struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN],
const char *func, int line)
#else
-ieee80211_find_txnode(struct ieee80211com *ic, const uint8_t *macaddr)
+ieee80211_find_txnode(struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
#endif
{
- struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211_node_table *nt = &vap->iv_ic->ic_sta;
struct ieee80211_node *ni;
/*
@@ -1175,11 +1428,13 @@ ieee80211_find_txnode(struct ieee80211com *ic, const uint8_t *macaddr)
*/
/* XXX can't hold lock across dup_bss 'cuz of recursive locking */
IEEE80211_NODE_LOCK(nt);
- if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr))
- ni = ieee80211_ref_node(ic->ic_bss);
+ if (vap->iv_opmode == IEEE80211_M_STA ||
+ vap->iv_opmode == IEEE80211_M_WDS ||
+ IEEE80211_IS_MULTICAST(macaddr))
+ ni = ieee80211_ref_node(vap->iv_bss);
else {
- ni = _ieee80211_find_node(nt, macaddr);
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ ni = ieee80211_find_node_locked(nt, macaddr);
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
(ni != NULL && ni->ni_associd == 0)) {
/*
* Station is not associated; don't permit the
@@ -1193,91 +1448,44 @@ ieee80211_find_txnode(struct ieee80211com *ic, const uint8_t *macaddr)
IEEE80211_NODE_UNLOCK(nt);
if (ni == NULL) {
- if (ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_AHDEMO) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS ||
+ vap->iv_opmode == IEEE80211_M_AHDEMO) {
/*
* In adhoc mode cons up a node for the destination.
* Note that we need an additional reference for the
- * caller to be consistent with _ieee80211_find_node.
+ * caller to be consistent with
+ * ieee80211_find_node_locked.
*/
- ni = ieee80211_fakeup_adhoc_node(nt, macaddr);
+ ni = ieee80211_fakeup_adhoc_node(vap, macaddr);
if (ni != NULL)
(void) ieee80211_ref_node(ni);
} else {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
- "[%s] no node, discard frame (%s)\n",
- ether_sprintf(macaddr), __func__);
- ic->ic_stats.is_tx_nonode++;
- }
- }
- return ni;
-}
-
-/*
- * Like find but search based on the ssid too.
- */
-struct ieee80211_node *
-#ifdef IEEE80211_DEBUG_REFCNT
-ieee80211_find_node_with_ssid_debug(struct ieee80211_node_table *nt,
- const uint8_t *macaddr, u_int ssidlen, const uint8_t *ssid,
- const char *func, int line)
-#else
-ieee80211_find_node_with_ssid(struct ieee80211_node_table *nt,
- const uint8_t *macaddr, u_int ssidlen, const uint8_t *ssid)
-#endif
-{
-#define MATCH_SSID(ni, ssid, ssidlen) \
- (ni->ni_esslen == ssidlen && memcmp(ni->ni_essid, ssid, ssidlen) == 0)
- static const uint8_t zeromac[IEEE80211_ADDR_LEN];
- struct ieee80211_node *ni;
- int hash;
-
- IEEE80211_NODE_LOCK(nt);
- /*
- * A mac address that is all zero means match only the ssid;
- * otherwise we must match both.
- */
- if (IEEE80211_ADDR_EQ(macaddr, zeromac)) {
- TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
- if (MATCH_SSID(ni, ssid, ssidlen))
- break;
- }
- } else {
- hash = IEEE80211_NODE_HASH(macaddr);
- LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
- if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) &&
- MATCH_SSID(ni, ssid, ssidlen))
- break;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, macaddr,
+ "no node, discard frame (%s)", __func__);
+ vap->iv_stats.is_tx_nonode++;
}
}
- if (ni != NULL) {
- ieee80211_ref_node(ni); /* mark referenced */
- IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
- REFCNT_LOC, ni, ether_sprintf(ni->ni_macaddr),
- ieee80211_node_refcnt(ni));
- }
- IEEE80211_NODE_UNLOCK(nt);
return ni;
-#undef MATCH_SSID
}
static void
_ieee80211_free_node(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_node_table *nt = ni->ni_table;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s %p<%s> in %s table\n", __func__, ni,
ether_sprintf(ni->ni_macaddr),
nt != NULL ? nt->nt_name : "<gone>");
- IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+ if (vap->iv_aid_bitmap != NULL)
+ IEEE80211_AID_CLR(vap, ni->ni_associd);
if (nt != NULL) {
TAILQ_REMOVE(&nt->nt_node, ni, ni_list);
LIST_REMOVE(ni, ni_hash);
}
- ic->ic_node_free(ni);
+ vap->iv_ic->ic_node_free(ni);
}
void
@@ -1290,7 +1498,7 @@ ieee80211_free_node(struct ieee80211_node *ni)
struct ieee80211_node_table *nt = ni->ni_table;
#ifdef IEEE80211_DEBUG_REFCNT
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
"%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line, ni,
ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)-1);
#endif
@@ -1310,7 +1518,8 @@ ieee80211_free_node(struct ieee80211_node *ni)
keyix = ni->ni_ucastkey.wk_rxkeyix;
if (keyix < nt->nt_keyixmax &&
nt->nt_keyixmap[keyix] == ni) {
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap,
+ IEEE80211_MSG_NODE,
"%s: %p<%s> clear key map entry", __func__,
ni, ether_sprintf(ni->ni_macaddr));
nt->nt_keyixmap[keyix] = NULL;
@@ -1331,8 +1540,9 @@ ieee80211_free_node(struct ieee80211_node *ni)
int
ieee80211_node_delucastkey(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
- struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211vap *vap = ni->ni_vap;
+ /* XXX is ni_table safe? */
+ struct ieee80211_node_table *nt = &ni->ni_ic->ic_sta;
struct ieee80211_node *nikey;
ieee80211_keyix keyix;
int isowned, status;
@@ -1353,19 +1563,19 @@ ieee80211_node_delucastkey(struct ieee80211_node *ni)
if (!isowned)
IEEE80211_NODE_LOCK(nt);
keyix = ni->ni_ucastkey.wk_rxkeyix;
- status = ieee80211_crypto_delkey(ic, &ni->ni_ucastkey);
+ status = ieee80211_crypto_delkey(vap, &ni->ni_ucastkey);
if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax) {
nikey = nt->nt_keyixmap[keyix];
nt->nt_keyixmap[keyix] = NULL;;
} else
nikey = NULL;
if (!isowned)
- IEEE80211_NODE_UNLOCK(&ic->ic_sta);
+ IEEE80211_NODE_UNLOCK(nt);
if (nikey != NULL) {
KASSERT(nikey == ni,
("key map out of sync, ni %p nikey %p", ni, nikey));
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s: delete key map entry %p<%s> refcnt %d\n",
__func__, ni, ether_sprintf(ni->ni_macaddr),
ieee80211_node_refcnt(ni)-1);
@@ -1386,7 +1596,7 @@ node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
IEEE80211_NODE_LOCK_ASSERT(nt);
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
"%s: remove %p<%s> from %s table, refcnt %d\n",
__func__, ni, ether_sprintf(ni->ni_macaddr),
nt->nt_name, ieee80211_node_refcnt(ni)-1);
@@ -1400,7 +1610,7 @@ node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
keyix = ni->ni_ucastkey.wk_rxkeyix;
if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax &&
nt->nt_keyixmap[keyix] == ni) {
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
"%s: %p<%s> clear key map entry\n",
__func__, ni, ether_sprintf(ni->ni_macaddr));
nt->nt_keyixmap[keyix] = NULL;
@@ -1420,23 +1630,151 @@ node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
_ieee80211_free_node(ni);
}
+/*
+ * Reclaim a (bss) node. Decrement the refcnt and reclaim
+ * the node if the only other reference to it is in the sta
+ * table. This is effectively ieee80211_free_node followed
+ * by node_reclaim when the refcnt is 1 (after the free).
+ */
static void
-ieee80211_free_allnodes_locked(struct ieee80211_node_table *nt)
+ieee80211_node_reclaim(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = nt->nt_ic;
- struct ieee80211_node *ni;
+ struct ieee80211_node_table *nt = ni->ni_table;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
- "%s: free all nodes in %s table\n", __func__, nt->nt_name);
+ KASSERT(nt != NULL, ("reclaim node not in table"));
+
+#ifdef IEEE80211_DEBUG_REFCNT
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
+ "%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line, ni,
+ ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)-1);
+#endif
+ IEEE80211_NODE_LOCK(nt);
+ if (ieee80211_node_dectestref(ni)) {
+ /*
+ * Last reference, reclaim state.
+ */
+ _ieee80211_free_node(ni);
+ nt = NULL;
+ } else if (ieee80211_node_refcnt(ni) == 1 &&
+ nt->nt_keyixmap != NULL) {
+ ieee80211_keyix keyix;
+ /*
+ * Check for a last reference in the key mapping table.
+ */
+ keyix = ni->ni_ucastkey.wk_rxkeyix;
+ if (keyix < nt->nt_keyixmax &&
+ nt->nt_keyixmap[keyix] == ni) {
+ IEEE80211_DPRINTF(ni->ni_vap,
+ IEEE80211_MSG_NODE,
+ "%s: %p<%s> clear key map entry", __func__,
+ ni, ether_sprintf(ni->ni_macaddr));
+ nt->nt_keyixmap[keyix] = NULL;
+ ieee80211_node_decref(ni); /* XXX needed? */
+ _ieee80211_free_node(ni);
+ nt = NULL;
+ }
+ }
+ if (nt != NULL && ieee80211_node_refcnt(ni) == 1) {
+ /*
+ * Last reference is in the sta table; complete
+ * the reclaim. This handles bss nodes being
+ * recycled: the node has two references, one for
+ * iv_bss and one for the table. After dropping
+ * the iv_bss ref above we need to reclaim the sta
+ * table reference.
+ */
+ ieee80211_node_decref(ni); /* NB: be pendantic */
+ _ieee80211_free_node(ni);
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+}
- while ((ni = TAILQ_FIRST(&nt->nt_node)) != NULL) {
+/*
+ * Node table support.
+ */
+
+static void
+ieee80211_node_table_init(struct ieee80211com *ic,
+ struct ieee80211_node_table *nt,
+ const char *name, int inact, int keyixmax)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+
+ nt->nt_ic = ic;
+ IEEE80211_NODE_LOCK_INIT(nt, ifp->if_xname);
+ IEEE80211_NODE_ITERATE_LOCK_INIT(nt, ifp->if_xname);
+ TAILQ_INIT(&nt->nt_node);
+ nt->nt_name = name;
+ nt->nt_scangen = 1;
+ nt->nt_inact_init = inact;
+ nt->nt_keyixmax = keyixmax;
+ if (nt->nt_keyixmax > 0) {
+ MALLOC(nt->nt_keyixmap, struct ieee80211_node **,
+ keyixmax * sizeof(struct ieee80211_node *),
+ M_80211_NODE, M_NOWAIT | M_ZERO);
+ if (nt->nt_keyixmap == NULL)
+ if_printf(ic->ic_ifp,
+ "Cannot allocate key index map with %u entries\n",
+ keyixmax);
+ } else
+ nt->nt_keyixmap = NULL;
+}
+
+static void
+ieee80211_node_table_reset(struct ieee80211_node_table *nt,
+ struct ieee80211vap *match)
+{
+ struct ieee80211_node *ni, *next;
+
+ IEEE80211_NODE_LOCK(nt);
+ TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, next) {
+ if (match != NULL && ni->ni_vap != match)
+ continue;
+ /* XXX can this happen? if so need's work */
if (ni->ni_associd != 0) {
- if (ic->ic_auth->ia_node_leave != NULL)
- ic->ic_auth->ia_node_leave(ic, ni);
- IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (vap->iv_auth->ia_node_leave != NULL)
+ vap->iv_auth->ia_node_leave(ni);
+ if (vap->iv_aid_bitmap != NULL)
+ IEEE80211_AID_CLR(vap, ni->ni_associd);
}
+ ni->ni_wdsvap = NULL; /* clear reference */
node_reclaim(nt, ni);
}
+ if (match != NULL && match->iv_opmode == IEEE80211_M_WDS) {
+ /*
+ * Make a separate pass to clear references to this vap
+ * held by DWDS entries. They will not be matched above
+ * because ni_vap will point to the ap vap but we still
+ * need to clear ni_wdsvap when the WDS vap is destroyed
+ * and/or reset.
+ */
+ TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, next)
+ if (ni->ni_wdsvap == match)
+ ni->ni_wdsvap = NULL;
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+}
+
+static void
+ieee80211_node_table_cleanup(struct ieee80211_node_table *nt)
+{
+ ieee80211_node_table_reset(nt, NULL);
+ if (nt->nt_keyixmap != NULL) {
+#ifdef DIAGNOSTIC
+ /* XXX verify all entries are NULL */
+ int i;
+ for (i = 0; i < nt->nt_keyixmax; i++)
+ if (nt->nt_keyixmap[i] != NULL)
+ printf("%s: %s[%u] still active\n", __func__,
+ nt->nt_name, i);
+#endif
+ FREE(nt->nt_keyixmap, M_80211_NODE);
+ nt->nt_keyixmap = NULL;
+ }
+ IEEE80211_NODE_ITERATE_LOCK_DESTROY(nt);
+ IEEE80211_NODE_LOCK_DESTROY(nt);
}
/*
@@ -1450,16 +1788,14 @@ ieee80211_free_allnodes_locked(struct ieee80211_node_table *nt)
* process each node only once.
*/
static void
-ieee80211_timeout_stations(struct ieee80211_node_table *nt)
+ieee80211_timeout_stations(struct ieee80211com *ic)
{
- struct ieee80211com *ic = nt->nt_ic;
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211vap *vap;
struct ieee80211_node *ni;
- u_int gen;
- int isadhoc;
+ int gen = 0;
- isadhoc = (ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_AHDEMO);
- IEEE80211_SCAN_LOCK(nt);
+ IEEE80211_NODE_ITERATE_LOCK(nt);
gen = ++nt->nt_scangen;
restart:
IEEE80211_NODE_LOCK(nt);
@@ -1473,14 +1809,25 @@ restart:
* will be reclaimed when the last reference to them
* goes away (when frame xmits complete).
*/
- if ((ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_STA) &&
+ vap = ni->ni_vap;
+ /*
+ * Only process stations when in RUN state. This
+ * insures, for example, that we don't timeout an
+ * inactive station during CAC. Note that CSA state
+ * is actually handled in ieee80211_node_timeout as
+ * it applies to more than timeout processing.
+ */
+ if (vap->iv_state != IEEE80211_S_RUN)
+ continue;
+ /* XXX can vap be NULL? */
+ if ((vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_STA) &&
(ni->ni_flags & IEEE80211_NODE_AREF) == 0)
continue;
/*
* Free fragment if not needed anymore
* (last fragment older than 1s).
- * XXX doesn't belong here
+ * XXX doesn't belong here, move to node_age
*/
if (ni->ni_rxfrag[0] != NULL &&
ticks > ni->ni_rxfragstamp + hz) {
@@ -1492,17 +1839,17 @@ restart:
/*
* Special case ourself; we may be idle for extended periods
* of time and regardless reclaiming our state is wrong.
+ * XXX run ic_node_age
*/
- if (ni == ic->ic_bss)
+ if (ni == vap->iv_bss)
continue;
- if (ni->ni_associd != 0 || isadhoc) {
+ if (ni->ni_associd != 0 ||
+ (vap->iv_opmode == IEEE80211_M_IBSS ||
+ vap->iv_opmode == IEEE80211_M_AHDEMO)) {
/*
- * Age frames on the power save queue.
+ * Age/drain resources held by the station.
*/
- if (ieee80211_node_saveq_age(ni) != 0 &&
- IEEE80211_NODE_SAVEQ_QLEN(ni) == 0 &&
- ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0);
+ ic->ic_node_age(ni);
/*
* Probe the station before time it out. We
* send a null data frame which may not be
@@ -1515,11 +1862,11 @@ restart:
* of); this will get fixed more properly
* soon with better handling of the rate set.
*/
- if ((ic->ic_flags_ext & IEEE80211_FEXT_INACT) &&
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_INACT) &&
(0 < ni->ni_inact &&
- ni->ni_inact <= ic->ic_inact_probe) &&
+ ni->ni_inact <= vap->iv_inact_probe) &&
ni->ni_rates.rs_nrates != 0) {
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_INACT | IEEE80211_MSG_NODE,
ni, "%s",
"probe station due to inactivity");
@@ -1537,9 +1884,9 @@ restart:
goto restart;
}
}
- if ((ic->ic_flags_ext & IEEE80211_FEXT_INACT) &&
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_INACT) &&
ni->ni_inact <= 0) {
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_INACT | IEEE80211_MSG_NODE, ni,
"station timed out due to inactivity "
"(refcnt %u)", ieee80211_node_refcnt(ni));
@@ -1561,45 +1908,109 @@ restart:
ieee80211_ref_node(ni);
IEEE80211_NODE_UNLOCK(nt);
if (ni->ni_associd != 0) {
- IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_SEND_MGMT(ni,
IEEE80211_FC0_SUBTYPE_DEAUTH,
IEEE80211_REASON_AUTH_EXPIRE);
}
- ieee80211_node_leave(ic, ni);
+ ieee80211_node_leave(ni);
ieee80211_free_node(ni);
- ic->ic_stats.is_node_timeout++;
+ vap->iv_stats.is_node_timeout++;
goto restart;
}
}
IEEE80211_NODE_UNLOCK(nt);
- IEEE80211_SCAN_UNLOCK(nt);
+ IEEE80211_NODE_ITERATE_UNLOCK(nt);
+}
+
+/*
+ * Aggressively reclaim resources. This should be used
+ * only in a critical situation to reclaim mbuf resources.
+ */
+void
+ieee80211_drain(struct ieee80211com *ic)
+{
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211vap *vap;
+ struct ieee80211_node *ni;
+
+ IEEE80211_NODE_LOCK(nt);
+ TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+ /*
+ * Ignore entries for which have yet to receive an
+ * authentication frame. These are transient and
+ * will be reclaimed when the last reference to them
+ * goes away (when frame xmits complete).
+ */
+ vap = ni->ni_vap;
+ /*
+ * Only process stations when in RUN state. This
+ * insures, for example, that we don't timeout an
+ * inactive station during CAC. Note that CSA state
+ * is actually handled in ieee80211_node_timeout as
+ * it applies to more than timeout processing.
+ */
+ if (vap->iv_state != IEEE80211_S_RUN)
+ continue;
+ /* XXX can vap be NULL? */
+ if ((vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_STA) &&
+ (ni->ni_flags & IEEE80211_NODE_AREF) == 0)
+ continue;
+ /*
+ * Free fragments.
+ * XXX doesn't belong here, move to node_drain
+ */
+ if (ni->ni_rxfrag[0] != NULL) {
+ m_freem(ni->ni_rxfrag[0]);
+ ni->ni_rxfrag[0] = NULL;
+ }
+ /*
+ * Drain resources held by the station.
+ */
+ ic->ic_node_drain(ni);
+ }
+ IEEE80211_NODE_UNLOCK(nt);
}
+/*
+ * Per-ieee80211com inactivity timer callback.
+ */
void
ieee80211_node_timeout(void *arg)
{
struct ieee80211com *ic = arg;
- ieee80211_scan_timeout(ic);
- ieee80211_timeout_stations(&ic->ic_sta);
-
- IEEE80211_LOCK(ic);
- ieee80211_erp_timeout(ic);
- ieee80211_ht_timeout(ic);
- IEEE80211_UNLOCK(ic);
+ /*
+ * Defer timeout processing if a channel switch is pending.
+ * We typically need to be mute so not doing things that
+ * might generate frames is good to handle in one place.
+ * Supressing the station timeout processing may extend the
+ * lifetime of inactive stations (by not decrementing their
+ * idle counters) but this should be ok unless the CSA is
+ * active for an unusually long time.
+ */
+ if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0) {
+ ieee80211_scan_timeout(ic);
+ ieee80211_timeout_stations(ic);
+ IEEE80211_LOCK(ic);
+ ieee80211_erp_timeout(ic);
+ ieee80211_ht_timeout(ic);
+ IEEE80211_UNLOCK(ic);
+ }
callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
ieee80211_node_timeout, ic);
}
void
-ieee80211_iterate_nodes(struct ieee80211_node_table *nt, ieee80211_iter_func *f, void *arg)
+ieee80211_iterate_nodes(struct ieee80211_node_table *nt,
+ ieee80211_iter_func *f, void *arg)
{
struct ieee80211_node *ni;
u_int gen;
- IEEE80211_SCAN_LOCK(nt);
+ IEEE80211_NODE_ITERATE_LOCK(nt);
gen = ++nt->nt_scangen;
restart:
IEEE80211_NODE_LOCK(nt);
@@ -1615,7 +2026,7 @@ restart:
}
IEEE80211_NODE_UNLOCK(nt);
- IEEE80211_SCAN_UNLOCK(nt);
+ IEEE80211_NODE_ITERATE_UNLOCK(nt);
}
void
@@ -1633,14 +2044,14 @@ ieee80211_dump_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
ni->ni_rxseqs[IEEE80211_NONQOS_TID] & IEEE80211_SEQ_FRAG_MASK,
ni->ni_rxfragstamp);
printf("\trstamp %u rssi %d noise %d intval %u capinfo 0x%x\n",
- ni->ni_rstamp, ni->ni_rssi, ni->ni_noise,
+ ni->ni_rstamp, node_getrssi(ni), ni->ni_noise,
ni->ni_intval, ni->ni_capinfo);
printf("\tbssid %s essid \"%.*s\" channel %u:0x%x\n",
ether_sprintf(ni->ni_bssid),
ni->ni_esslen, ni->ni_essid,
ni->ni_chan->ic_freq, ni->ni_chan->ic_flags);
- printf("\tfails %u inact %u txrate %u\n",
- ni->ni_fails, ni->ni_inact, ni->ni_txrate);
+ printf("\tinact %u txrate %u\n",
+ ni->ni_inact, ni->ni_txrate);
printf("\thtcap %x htparam %x htctlchan %u ht2ndchan %u\n",
ni->ni_htcap, ni->ni_htparam,
ni->ni_htctlchan, ni->ni_ht2ndchan);
@@ -1658,16 +2069,22 @@ ieee80211_dump_nodes(struct ieee80211_node_table *nt)
void
ieee80211_notify_erp(struct ieee80211com *ic)
{
- if (ic->ic_opmode == IEEE80211_M_HOSTAP)
- ieee80211_beacon_notify(ic, IEEE80211_BEACON_ERP);
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ ieee80211_beacon_notify(vap, IEEE80211_BEACON_ERP);
}
/*
* Handle a station joining an 11g network.
*/
static void
-ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_node_join_11g(struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
IEEE80211_LOCK_ASSERT(ic);
@@ -1680,7 +2097,7 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
*/
if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) {
ic->ic_longslotsta++;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"station needs long slot time, count %d",
ic->ic_longslotsta);
/* XXX vap's w/ conflicting needs won't work */
@@ -1698,9 +2115,9 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
* then bump the counter and enable protection
* if configured.
*/
- if (!ieee80211_iserp_rateset(ic, &ni->ni_rates)) {
+ if (!ieee80211_iserp_rateset(&ni->ni_rates)) {
ic->ic_nonerpsta++;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"station is !ERP, %d non-ERP stations associated",
ic->ic_nonerpsta);
/*
@@ -1708,18 +2125,19 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
* then we must enable use of Barker preamble.
*/
if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) == 0) {
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"%s", "station needs long preamble");
ic->ic_flags |= IEEE80211_F_USEBARKER;
ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
}
/*
- * If protection is configured, enable it.
+ * If protection is configured and this is the first
+ * indication we should use protection, enable it.
*/
if (ic->ic_protmode != IEEE80211_PROT_NONE &&
ic->ic_nonerpsta == 1 &&
(ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC,
"%s: enable use of protection\n", __func__);
ic->ic_flags |= IEEE80211_F_USEPROT;
ieee80211_notify_erp(ic);
@@ -1729,47 +2147,49 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
}
void
-ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp)
+ieee80211_node_join(struct ieee80211_node *ni, int resp)
{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
int newassoc;
if (ni->ni_associd == 0) {
uint16_t aid;
- IEEE80211_LOCK(ic);
+ KASSERT(vap->iv_aid_bitmap != NULL, ("no aid bitmap"));
/*
* It would be good to search the bitmap
* more efficiently, but this will do for now.
*/
- for (aid = 1; aid < ic->ic_max_aid; aid++) {
- if (!IEEE80211_AID_ISSET(aid,
- ic->ic_aid_bitmap))
+ for (aid = 1; aid < vap->iv_max_aid; aid++) {
+ if (!IEEE80211_AID_ISSET(vap, aid))
break;
}
- if (aid >= ic->ic_max_aid) {
- IEEE80211_UNLOCK(ic);
- IEEE80211_SEND_MGMT(ic, ni, resp,
+ if (aid >= vap->iv_max_aid) {
+ IEEE80211_SEND_MGMT(ni, resp,
IEEE80211_REASON_ASSOC_TOOMANY);
- ieee80211_node_leave(ic, ni);
+ ieee80211_node_leave(ni);
return;
}
ni->ni_associd = aid | 0xc000;
ni->ni_jointime = time_uptime;
- IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap);
+ IEEE80211_LOCK(ic);
+ IEEE80211_AID_SET(vap, ni->ni_associd);
+ vap->iv_sta_assoc++;
ic->ic_sta_assoc++;
if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
ieee80211_ht_node_join(ni);
if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
IEEE80211_IS_CHAN_FULL(ic->ic_bsschan))
- ieee80211_node_join_11g(ic, ni);
+ ieee80211_node_join_11g(ni);
IEEE80211_UNLOCK(ic);
newassoc = 1;
} else
newassoc = 0;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
"station associated at aid %d: %s preamble, %s slot time%s%s%s%s%s%s",
IEEE80211_NODE_AID(ni),
ic->ic_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long",
@@ -1779,20 +2199,20 @@ ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp
ni->ni_flags & IEEE80211_NODE_HT ?
(ni->ni_chw == 20 ? ", HT20" : ", HT40") : "",
ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ?
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ?
", fast-frames" : "",
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ?
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ?
", turbo" : ""
);
/* give driver a chance to setup state like ni_txrate */
if (ic->ic_newassoc != NULL)
ic->ic_newassoc(ni, newassoc);
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS);
+ IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_SUCCESS);
/* tell the authenticator about new station */
- if (ic->ic_auth->ia_node_join != NULL)
- ic->ic_auth->ia_node_join(ic, ni);
- ieee80211_notify_node_join(ic, ni,
+ if (vap->iv_auth->ia_node_join != NULL)
+ vap->iv_auth->ia_node_join(ni);
+ ieee80211_notify_node_join(ni,
resp == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
}
@@ -1817,14 +2237,15 @@ disable_protection(struct ieee80211com *ic)
* Handle a station leaving an 11g network.
*/
static void
-ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_node_leave_11g(struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
IEEE80211_LOCK_ASSERT(ic);
KASSERT(IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan),
- ("not in 11g, bss %u:0x%x, curmode %u", ic->ic_bsschan->ic_freq,
- ic->ic_bsschan->ic_flags, ic->ic_curmode));
+ ("not in 11g, bss %u:0x%x", ic->ic_bsschan->ic_freq,
+ ic->ic_bsschan->ic_flags));
/*
* If a long slot station do the slot time bookkeeping.
@@ -1833,7 +2254,7 @@ ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
KASSERT(ic->ic_longslotsta > 0,
("bogus long slot station count %d", ic->ic_longslotsta));
ic->ic_longslotsta--;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"long slot time station leaves, count now %d",
ic->ic_longslotsta);
if (ic->ic_longslotsta == 0) {
@@ -1843,7 +2264,8 @@ ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
*/
if ((ic->ic_caps & IEEE80211_C_SHSLOT) &&
ic->ic_opmode != IEEE80211_M_IBSS) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ IEEE80211_DPRINTF(ni->ni_vap,
+ IEEE80211_MSG_ASSOC,
"%s: re-enable use of short slot time\n",
__func__);
ieee80211_set_shortslottime(ic, 1);
@@ -1857,13 +2279,13 @@ ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
KASSERT(ic->ic_nonerpsta > 0,
("bogus non-ERP station count %d", ic->ic_nonerpsta));
ic->ic_nonerpsta--;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"non-ERP station leaves, count now %d%s", ic->ic_nonerpsta,
(ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) ?
" (non-ERP sta present)" : "");
if (ic->ic_nonerpsta == 0 &&
(ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC,
"%s: disable use of protection\n", __func__);
disable_protection(ic);
}
@@ -1886,8 +2308,10 @@ ieee80211_erp_timeout(struct ieee80211com *ic)
if ((ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) &&
time_after(ticks, ic->ic_lastnonerp + IEEE80211_NONERP_PRESENT_AGE)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "%s\n", "age out non-ERP sta present on channel");
+#if 0
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
+ "%s", "age out non-ERP sta present on channel");
+#endif
ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
if (ic->ic_nonerpsta == 0)
disable_protection(ic);
@@ -1899,15 +2323,17 @@ ieee80211_erp_timeout(struct ieee80211com *ic)
* when operating as an ap.
*/
void
-ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_node_leave(struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_node_table *nt = ni->ni_table;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
"station with aid %d leaves", IEEE80211_NODE_AID(ni));
- KASSERT(ic->ic_opmode != IEEE80211_M_STA,
- ("unexpected operating mode %u", ic->ic_opmode));
+ KASSERT(vap->iv_opmode != IEEE80211_M_STA,
+ ("unexpected operating mode %u", vap->iv_opmode));
/*
* If node wasn't previously associated all
* we need to do is reclaim the reference.
@@ -1921,19 +2347,20 @@ ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
* association id as the authenticator uses the
* associd to locate it's state block.
*/
- if (ic->ic_auth->ia_node_leave != NULL)
- ic->ic_auth->ia_node_leave(ic, ni);
+ if (vap->iv_auth->ia_node_leave != NULL)
+ vap->iv_auth->ia_node_leave(ni);
IEEE80211_LOCK(ic);
- IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+ IEEE80211_AID_CLR(vap, ni->ni_associd);
ni->ni_associd = 0;
+ vap->iv_sta_assoc--;
ic->ic_sta_assoc--;
if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
ieee80211_ht_node_leave(ni);
if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
IEEE80211_IS_CHAN_FULL(ic->ic_bsschan))
- ieee80211_node_leave_11g(ic, ni);
+ ieee80211_node_leave_11g(ni);
IEEE80211_UNLOCK(ic);
/*
* Cleanup station state. In particular clear various
@@ -1941,7 +2368,7 @@ ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
* is reused before the reference count goes to zero
* (and memory is reclaimed).
*/
- ieee80211_sta_leave(ic, ni);
+ ieee80211_sta_leave(ni);
done:
/*
* Remove the node from any table it's recorded in and
@@ -1957,121 +2384,89 @@ done:
ieee80211_free_node(ni);
}
+struct rssiinfo {
+ struct ieee80211vap *vap;
+ int rssi_samples;
+ uint32_t rssi_total;
+};
+
+static void
+get_hostap_rssi(void *arg, struct ieee80211_node *ni)
+{
+ struct rssiinfo *info = arg;
+ struct ieee80211vap *vap = ni->ni_vap;
+ int8_t rssi;
+
+ if (info->vap != vap)
+ return;
+ /* only associated stations */
+ if (ni->ni_associd == 0)
+ return;
+ rssi = vap->iv_ic->ic_node_getrssi(ni);
+ if (rssi != 0) {
+ info->rssi_samples++;
+ info->rssi_total += rssi;
+ }
+}
+
+static void
+get_adhoc_rssi(void *arg, struct ieee80211_node *ni)
+{
+ struct rssiinfo *info = arg;
+ struct ieee80211vap *vap = ni->ni_vap;
+ int8_t rssi;
+
+ if (info->vap != vap)
+ return;
+ /* only neighbors */
+ /* XXX check bssid */
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
+ return;
+ rssi = vap->iv_ic->ic_node_getrssi(ni);
+ if (rssi != 0) {
+ info->rssi_samples++;
+ info->rssi_total += rssi;
+ }
+}
+
int8_t
-ieee80211_getrssi(struct ieee80211com *ic)
+ieee80211_getrssi(struct ieee80211vap *vap)
{
#define NZ(x) ((x) == 0 ? 1 : (x))
- struct ieee80211_node_table *nt = &ic->ic_sta;
- int rssi_samples;
- int32_t rssi_total;
- struct ieee80211_node *ni;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct rssiinfo info;
- rssi_total = 0;
- rssi_samples = 0;
- switch (ic->ic_opmode) {
+ info.rssi_total = 0;
+ info.rssi_samples = 0;
+ info.vap = vap;
+ switch (vap->iv_opmode) {
case IEEE80211_M_IBSS: /* average of all ibss neighbors */
case IEEE80211_M_AHDEMO: /* average of all neighbors */
+ ieee80211_iterate_nodes(&ic->ic_sta, get_adhoc_rssi, &info);
+ break;
case IEEE80211_M_HOSTAP: /* average of all associated stations */
- /* XXX locking */
- TAILQ_FOREACH(ni, &nt->nt_node, ni_list)
- if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
- (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS)) {
- int8_t rssi = ic->ic_node_getrssi(ni);
- if (rssi != 0) {
- rssi_samples++;
- rssi_total += rssi;
- }
- }
+ ieee80211_iterate_nodes(&ic->ic_sta, get_hostap_rssi, &info);
break;
case IEEE80211_M_MONITOR: /* XXX */
case IEEE80211_M_STA: /* use stats from associated ap */
default:
- if (ic->ic_bss != NULL)
- rssi_total = ic->ic_node_getrssi(ic->ic_bss);
- rssi_samples = 1;
+ if (vap->iv_bss != NULL)
+ info.rssi_total = ic->ic_node_getrssi(vap->iv_bss);
+ info.rssi_samples = 1;
break;
}
- return rssi_total / NZ(rssi_samples);
+ return info.rssi_total / NZ(info.rssi_samples);
#undef NZ
}
void
-ieee80211_getsignal(struct ieee80211com *ic, int8_t *rssi, int8_t *noise)
+ieee80211_getsignal(struct ieee80211vap *vap, int8_t *rssi, int8_t *noise)
{
- if (ic->ic_bss == NULL) /* NB: shouldn't happen */
+ if (vap->iv_bss == NULL) /* NB: shouldn't happen */
return;
- ic->ic_node_getsignal(ic->ic_bss, rssi, noise);
+ vap->iv_ic->ic_node_getsignal(vap->iv_bss, rssi, noise);
/* for non-station mode return avg'd rssi accounting */
- if (ic->ic_opmode != IEEE80211_M_STA)
- *rssi = ieee80211_getrssi(ic);
-}
-
-/*
- * Node table support.
- */
-
-static void
-ieee80211_node_table_init(struct ieee80211com *ic,
- struct ieee80211_node_table *nt,
- const char *name, int inact, int keyixmax)
-{
-
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
- "%s %s table, inact %u\n", __func__, name, inact);
-
- nt->nt_ic = ic;
- /* XXX need unit */
- IEEE80211_NODE_LOCK_INIT(nt, ic->ic_ifp->if_xname);
- IEEE80211_SCAN_LOCK_INIT(nt, ic->ic_ifp->if_xname);
- TAILQ_INIT(&nt->nt_node);
- nt->nt_name = name;
- nt->nt_scangen = 1;
- nt->nt_inact_init = inact;
- nt->nt_keyixmax = keyixmax;
- if (nt->nt_keyixmax > 0) {
- MALLOC(nt->nt_keyixmap, struct ieee80211_node **,
- keyixmax * sizeof(struct ieee80211_node *),
- M_80211_NODE, M_NOWAIT | M_ZERO);
- if (nt->nt_keyixmap == NULL)
- if_printf(ic->ic_ifp,
- "Cannot allocate key index map with %u entries\n",
- keyixmax);
- } else
- nt->nt_keyixmap = NULL;
-}
-
-static void
-ieee80211_node_table_reset(struct ieee80211_node_table *nt)
-{
-
- IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
- "%s %s table\n", __func__, nt->nt_name);
-
- IEEE80211_NODE_LOCK(nt);
- ieee80211_free_allnodes_locked(nt);
- IEEE80211_NODE_UNLOCK(nt);
-}
-
-static void
-ieee80211_node_table_cleanup(struct ieee80211_node_table *nt)
-{
-
- IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
- "%s %s table\n", __func__, nt->nt_name);
-
- IEEE80211_NODE_LOCK(nt);
- ieee80211_free_allnodes_locked(nt);
- if (nt->nt_keyixmap != NULL) {
- /* XXX verify all entries are NULL */
- int i;
- for (i = 0; i < nt->nt_keyixmax; i++)
- if (nt->nt_keyixmap[i] != NULL)
- printf("%s: %s[%u] still active\n", __func__,
- nt->nt_name, i);
- FREE(nt->nt_keyixmap, M_80211_NODE);
- nt->nt_keyixmap = NULL;
- }
- IEEE80211_SCAN_LOCK_DESTROY(nt);
- IEEE80211_NODE_LOCK_DESTROY(nt);
+ if (vap->iv_opmode != IEEE80211_M_STA)
+ *rssi = ieee80211_getrssi(vap);
}
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
index 2799ed4..fb569a9 100644
--- a/sys/net80211/ieee80211_node.h
+++ b/sys/net80211/ieee80211_node.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -32,18 +32,16 @@
#include <net80211/ieee80211_ht.h> /* for aggregation state */
/*
- * Each ieee80211com instance has a single timer that fires once a
- * second. This is used to initiate various work depending on the
- * state of the instance: scanning (passive or active), ``transition''
- * (waiting for a response to a management frame when operating
- * as a station), and node inactivity processing (when operating
- * as an AP). For inactivity processing each node has a timeout
- * set in it's ni_inact field that is decremented on each timeout
- * and the node is reclaimed when the counter goes to zero. We
- * use different inactivity timeout values depending on whether
- * the node is associated and authorized (either by 802.1x or
- * open/shared key authentication) or associated but yet to be
- * authorized. The latter timeout is shorter to more aggressively
+ * Each ieee80211com instance has a single timer that fires every
+ * IEEE80211_INACT_WAIT seconds to handle "inactivity processing".
+ * This is used to do node inactivity processing when operating
+ * as an AP or in adhoc mode. For inactivity processing each node
+ * has a timeout set in it's ni_inact field that is decremented
+ * on each timeout and the node is reclaimed when the counter goes
+ * to zero. We use different inactivity timeout values depending
+ * on whether the node is associated and authorized (either by
+ * 802.1x or open/shared key authentication) or associated but yet
+ * to be authorized. The latter timeout is shorter to more aggressively
* reclaim nodes that leave part way through the 802.1x exchange.
*/
#define IEEE80211_INACT_WAIT 15 /* inactivity interval (secs) */
@@ -64,19 +62,28 @@
(((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \
IEEE80211_NODE_HASHSIZE)
-struct ieee80211_rsnparms {
- uint8_t rsn_mcastcipher; /* mcast/group cipher */
- uint8_t rsn_mcastkeylen; /* mcast key length */
- uint8_t rsn_ucastcipherset; /* unicast cipher set */
- uint8_t rsn_ucastcipher; /* selected unicast cipher */
- uint8_t rsn_ucastkeylen; /* unicast key length */
- uint8_t rsn_keymgmtset; /* key mangement algorithms */
- uint8_t rsn_keymgmt; /* selected key mgmt algo */
- uint16_t rsn_caps; /* capabilities */
-};
-
struct ieee80211_node_table;
struct ieee80211com;
+struct ieee80211vap;
+
+/*
+ * Information element ``blob''. We use this structure
+ * to capture management frame payloads that need to be
+ * retained. Information elemnts within the payload that
+ * we need to consult have references recorded.
+ */
+struct ieee80211_ies {
+ /* the following are either NULL or point within data */
+ uint8_t *wpa_ie; /* captured WPA ie */
+ uint8_t *rsn_ie; /* captured RSN ie */
+ uint8_t *wme_ie; /* captured WME ie */
+ uint8_t *ath_ie; /* captured Atheros ie */
+ uint8_t *htcap_ie; /* captured HTCAP ie */
+ uint8_t *htinfo_ie; /* captured HTINFO ie */
+ /* NB: these must be the last members of this structure */
+ uint8_t *data; /* frame data > 802.11 header */
+ int len; /* data size in bytes */
+};
/*
* Node specific information. Note that drivers are expected
@@ -85,11 +92,12 @@ struct ieee80211com;
* the ieee80211com structure.
*/
struct ieee80211_node {
- struct ieee80211com *ni_ic;
- struct ieee80211_node_table *ni_table;
- TAILQ_ENTRY(ieee80211_node) ni_list;
- LIST_ENTRY(ieee80211_node) ni_hash;
- u_int ni_refcnt;
+ struct ieee80211vap *ni_vap; /* associated vap */
+ struct ieee80211com *ni_ic; /* copy from vap to save deref*/
+ struct ieee80211_node_table *ni_table; /* NB: may be NULL */
+ TAILQ_ENTRY(ieee80211_node) ni_list; /* list of all nodes */
+ LIST_ENTRY(ieee80211_node) ni_hash; /* hash collision list */
+ u_int ni_refcnt; /* count of held references */
u_int ni_scangen; /* gen# for timeout scan */
uint8_t ni_authmode; /* authentication algorithm */
uint8_t ni_ath_flags; /* Atheros feature flags */
@@ -99,7 +107,7 @@ struct ieee80211_node {
#define IEEE80211_NODE_FF 0x0004 /* Fast Frame capable */
#define IEEE80211_NODE_XR 0x0008 /* Atheros WME enable */
#define IEEE80211_NODE_AR 0x0010 /* AR capable */
-#define IEEE80211_NODE_BOOST 0x0080
+#define IEEE80211_NODE_BOOST 0x0080
#define IEEE80211_NODE_PSUPDATE 0x0200 /* power save state changed */
#define IEEE80211_NODE_CHWUPDATE 0x0400 /* 11n channel width change */
uint16_t ni_flags; /* special-purpose state */
@@ -111,6 +119,8 @@ struct ieee80211_node {
#define IEEE80211_NODE_AREF 0x0020 /* authentication ref held */
#define IEEE80211_NODE_HT 0x0040 /* HT enabled */
#define IEEE80211_NODE_HTCOMPAT 0x0080 /* HT setup w/ vendor OUI's */
+#define IEEE80211_NODE_WPS 0x0100 /* WPS association */
+#define IEEE80211_NODE_TSN 0x0200 /* TSN association */
#define IEEE80211_NODE_AMPDU_RX 0x0400 /* AMPDU rx enabled */
#define IEEE80211_NODE_AMPDU_TX 0x0800 /* AMPDU tx enabled */
uint16_t ni_ath_defkeyix;/* Atheros def key index */
@@ -119,22 +129,18 @@ struct ieee80211_node {
uint16_t ni_vlan; /* vlan tag */
uint32_t ni_jointime; /* time of join (secs) */
uint32_t *ni_challenge; /* shared-key challenge */
- uint8_t *ni_wpa_ie; /* captured WPA ie */
- uint8_t *ni_rsn_ie; /* captured RSN ie */
- uint8_t *ni_wme_ie; /* captured WME ie */
- uint8_t *ni_ath_ie; /* captured Atheros ie */
+ struct ieee80211_ies ni_ies; /* captured ie's */
/* tx seq per-tid */
uint16_t ni_txseqs[IEEE80211_TID_SIZE];
/* rx seq previous per-tid*/
uint16_t ni_rxseqs[IEEE80211_TID_SIZE];
uint32_t ni_rxfragstamp; /* time stamp of last rx frag */
struct mbuf *ni_rxfrag[3]; /* rx frag reassembly */
- struct ieee80211_rsnparms ni_rsn; /* RSN/WPA parameters */
struct ieee80211_key ni_ucastkey; /* unicast key */
/* hardware */
uint32_t ni_rstamp; /* recv timestamp */
- int8_t ni_rssi; /* recv ssi */
+ uint32_t ni_avgrssi; /* recv ssi state */
int8_t ni_noise; /* noise floor */
/* header */
@@ -144,7 +150,7 @@ struct ieee80211_node {
/* beacon, probe response */
union {
uint8_t data[8];
- uint64_t tsf;
+ u_int64_t tsf;
} ni_tstamp; /* from last rcv'd beacon */
uint16_t ni_intval; /* beacon interval */
uint16_t ni_capinfo; /* capabilities */
@@ -154,13 +160,12 @@ struct ieee80211_node {
struct ieee80211_channel *ni_chan;
uint16_t ni_fhdwell; /* FH only */
uint8_t ni_fhindex; /* FH only */
- uint8_t ni_erp; /* ERP from beacon/probe resp */
+ uint16_t ni_erp; /* ERP from beacon/probe resp */
uint16_t ni_timoff; /* byte offset to TIM ie */
uint8_t ni_dtim_period; /* DTIM period */
uint8_t ni_dtim_count; /* DTIM count for last bcn */
/* 11n state */
- uint8_t *ni_htcap_ie; /* captured HTCAP ie */
uint16_t ni_htcap; /* HT capabilities */
uint8_t ni_htparam; /* HT params */
uint8_t ni_htctlchan; /* HT control channel */
@@ -174,14 +179,18 @@ struct ieee80211_node {
struct ieee80211_rx_ampdu ni_rx_ampdu[WME_NUM_TID];
/* others */
- int ni_fails; /* failure count to associate */
short ni_inact; /* inactivity mark count */
short ni_inact_reload;/* inactivity reload value */
- int ni_txrate; /* index to ni_rates[] */
- struct ifqueue ni_savedq; /* ps-poll queue */
+ int ni_txrate; /* legacy rate/MCS */
+ struct ifqueue ni_savedq; /* ps-poll queue */
struct ieee80211_nodestats ni_stats; /* per-node statistics */
+
+ struct ieee80211vap *ni_wdsvap; /* associated WDS vap */
+ /* XXX move to vap? */
+ struct ifqueue ni_wdsq; /* wds pending queue */
};
MALLOC_DECLARE(M_80211_NODE);
+MALLOC_DECLARE(M_80211_NODE_IE);
#define IEEE80211_NODE_ATH (IEEE80211_NODE_FF | IEEE80211_NODE_TURBOP)
#define IEEE80211_NODE_AMPDU \
@@ -193,6 +202,38 @@ MALLOC_DECLARE(M_80211_NODE);
#define IEEE80211_NODE_STAT_ADD(ni,stat,v) (ni->ni_stats.ns_##stat += v)
#define IEEE80211_NODE_STAT_SET(ni,stat,v) (ni->ni_stats.ns_##stat = v)
+/*
+ * Filtered rssi calculation support. The receive rssi is maintained
+ * as an average over the last 10 frames received using a low pass filter
+ * (all frames for now, possibly need to be more selective). Calculations
+ * are designed such that a good compiler can optimize them. The avg
+ * rssi state should be initialized to IEEE80211_RSSI_DUMMY_MARKER and
+ * each sample incorporated with IEEE80211_RSSI_LPF. Use IEEE80211_RSSI_GET
+ * to extract the current value.
+ *
+ * Note that we assume rssi data are in the range [-127..127] and we
+ * discard values <-20. This is consistent with assumptions throughout
+ * net80211 that signal strength data are in .5 dBm units relative to
+ * the current noise floor (linear, not log).
+ */
+#define IEEE80211_RSSI_LPF_LEN 10
+#define IEEE80211_RSSI_DUMMY_MARKER 127
+/* NB: pow2 to optimize out * and / */
+#define IEEE80211_RSSI_EP_MULTIPLIER (1<<7)
+#define IEEE80211_RSSI_IN(x) ((x) * IEEE80211_RSSI_EP_MULTIPLIER)
+#define _IEEE80211_RSSI_LPF(x, y, len) \
+ (((x) != IEEE80211_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y))
+#define IEEE80211_RSSI_LPF(x, y) do { \
+ if ((y) >= -20) { \
+ x = _IEEE80211_RSSI_LPF((x), IEEE80211_RSSI_IN((y)), \
+ IEEE80211_RSSI_LPF_LEN); \
+ } \
+} while (0)
+#define IEEE80211_RSSI_EP_RND(x, mul) \
+ ((((x) % (mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
+#define IEEE80211_RSSI_GET(x) \
+ IEEE80211_RSSI_EP_RND(x, IEEE80211_RSSI_EP_MULTIPLIER)
+
static __inline struct ieee80211_node *
ieee80211_ref_node(struct ieee80211_node *ni)
{
@@ -212,6 +253,9 @@ struct ieee80211com;
void ieee80211_node_attach(struct ieee80211com *);
void ieee80211_node_lateattach(struct ieee80211com *);
void ieee80211_node_detach(struct ieee80211com *);
+void ieee80211_node_vattach(struct ieee80211vap *);
+void ieee80211_node_latevattach(struct ieee80211vap *);
+void ieee80211_node_vdetach(struct ieee80211vap *);
static __inline int
ieee80211_node_is_authorized(const struct ieee80211_node *ni)
@@ -222,21 +266,32 @@ ieee80211_node_is_authorized(const struct ieee80211_node *ni)
void ieee80211_node_authorize(struct ieee80211_node *);
void ieee80211_node_unauthorize(struct ieee80211_node *);
-void ieee80211_probe_curchan(struct ieee80211com *, int);
-void ieee80211_create_ibss(struct ieee80211com*, struct ieee80211_channel *);
-void ieee80211_reset_bss(struct ieee80211com *);
-void ieee80211_setbsschan(struct ieee80211com *, struct ieee80211_channel *);
+void ieee80211_node_set_chan(struct ieee80211_node *,
+ struct ieee80211_channel *);
+void ieee80211_create_ibss(struct ieee80211vap*, struct ieee80211_channel *);
+void ieee80211_reset_bss(struct ieee80211vap *);
+void ieee80211_sync_curchan(struct ieee80211com *);
+void ieee80211_setcurchan(struct ieee80211com *, struct ieee80211_channel *);
int ieee80211_ibss_merge(struct ieee80211_node *);
struct ieee80211_scan_entry;
-int ieee80211_sta_join(struct ieee80211com *,
+int ieee80211_sta_join(struct ieee80211vap *,
const struct ieee80211_scan_entry *);
-void ieee80211_sta_leave(struct ieee80211com *, struct ieee80211_node *);
+void ieee80211_sta_leave(struct ieee80211_node *);
+void ieee80211_node_deauth(struct ieee80211_node *, int);
+
+int ieee80211_ies_init(struct ieee80211_ies *, const uint8_t *, int);
+void ieee80211_ies_cleanup(struct ieee80211_ies *);
+void ieee80211_ies_expand(struct ieee80211_ies *);
+#define ieee80211_ies_setie(_ies, _ie, _off) do { \
+ (_ies)._ie = (_ies).data + (_off); \
+} while (0)
/*
* Table of ieee80211_node instances. Each ieee80211com
- * has at least one for holding the scan candidates.
- * When operating as an access point or in ibss mode there
- * is a second table for associated stations or neighbors.
+ * has one that holds association stations (when operating
+ * as an ap) or neighbors (in ibss mode).
+ *
+ * XXX embed this in ieee80211com instead of indirect?
*/
struct ieee80211_node_table {
struct ieee80211com *nt_ic; /* back reference */
@@ -245,23 +300,41 @@ struct ieee80211_node_table {
LIST_HEAD(, ieee80211_node) nt_hash[IEEE80211_NODE_HASHSIZE];
struct ieee80211_node **nt_keyixmap; /* key ix -> node map */
int nt_keyixmax; /* keyixmap size */
- const char *nt_name; /* for debugging */
+ const char *nt_name; /* table name for debug msgs */
ieee80211_scan_lock_t nt_scanlock; /* on nt_scangen */
- u_int nt_scangen; /* gen# for timeout scan */
+ u_int nt_scangen; /* gen# for iterators */
int nt_inact_init; /* initial node inact setting */
};
-struct ieee80211_node *ieee80211_alloc_node(
- struct ieee80211_node_table *, const uint8_t *);
-struct ieee80211_node *ieee80211_tmp_node(struct ieee80211com *,
- const uint8_t *macaddr);
-struct ieee80211_node *ieee80211_dup_bss(struct ieee80211_node_table *,
- const uint8_t *);
+struct ieee80211_node *ieee80211_alloc_node(struct ieee80211_node_table *,
+ struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+struct ieee80211_node *ieee80211_tmp_node(struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+struct ieee80211_node *ieee80211_dup_bss(struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+struct ieee80211_node *ieee80211_node_create_wds(struct ieee80211vap *,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ struct ieee80211_channel *);
#ifdef IEEE80211_DEBUG_REFCNT
void ieee80211_free_node_debug(struct ieee80211_node *,
const char *func, int line);
+struct ieee80211_node *ieee80211_find_node_locked_debug(
+ struct ieee80211_node_table *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN],
+ const char *func, int line);
struct ieee80211_node *ieee80211_find_node_debug(struct ieee80211_node_table *,
- const uint8_t *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN],
+ const char *func, int line);
+struct ieee80211_node *ieee80211_find_vap_node_locked_debug(
+ struct ieee80211_node_table *,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN],
+ const char *func, int line);
+struct ieee80211_node *ieee80211_find_vap_node_debug(
+ struct ieee80211_node_table *,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN],
const char *func, int line);
struct ieee80211_node * ieee80211_find_rxnode_debug(struct ieee80211com *,
const struct ieee80211_frame_min *,
@@ -270,42 +343,43 @@ struct ieee80211_node * ieee80211_find_rxnode_withkey_debug(
struct ieee80211com *,
const struct ieee80211_frame_min *, uint16_t keyix,
const char *func, int line);
-struct ieee80211_node * ieee80211_find_rxnode_withkey_debug(
- struct ieee80211com *,
- const struct ieee80211_frame_min *, uint16_t keyix,
- const char *func, int line);
-struct ieee80211_node *ieee80211_find_txnode_debug(struct ieee80211com *,
+struct ieee80211_node *ieee80211_find_txnode_debug(struct ieee80211vap *,
const uint8_t *,
const char *func, int line);
-struct ieee80211_node *ieee80211_find_node_with_ssid_debug(
- struct ieee80211_node_table *, const uint8_t *macaddr,
- u_int ssidlen, const uint8_t *ssid,
- const char *func, int line);
#define ieee80211_free_node(ni) \
ieee80211_free_node_debug(ni, __func__, __LINE__)
+#define ieee80211_find_node_locked(nt, mac) \
+ ieee80211_find_node_locked_debug(nt, mac, __func__, __LINE__)
#define ieee80211_find_node(nt, mac) \
ieee80211_find_node_debug(nt, mac, __func__, __LINE__)
-#define ieee80211_find_rxnode(nt, wh) \
- ieee80211_find_rxnode_debug(nt, wh, __func__, __LINE__)
-#define ieee80211_find_rxnode_withkey(nt, wh, keyix) \
- ieee80211_find_rxnode_withkey_debug(nt, wh, keyix, __func__, __LINE__)
-#define ieee80211_find_txnode(nt, mac) \
- ieee80211_find_txnode_debug(nt, mac, __func__, __LINE__)
-#define ieee80211_find_node_with_ssid(nt, mac, sl, ss) \
- ieee80211_find_node_with_ssid_debug(nt, mac, sl, ss, __func__, __LINE__)
+#define ieee80211_find_vap_node_locked(nt, vap, mac) \
+ ieee80211_find_vap_node_locked_debug(nt, vap, mac, __func__, __LINE__)
+#define ieee80211_find_vap_node(nt, vap, mac) \
+ ieee80211_find_vap_node_debug(nt, vap, mac, __func__, __LINE__)
+#define ieee80211_find_rxnode(ic, wh) \
+ ieee80211_find_rxnode_debug(ic, wh, __func__, __LINE__)
+#define ieee80211_find_rxnode_withkey(ic, wh, keyix) \
+ ieee80211_find_rxnode_withkey_debug(ic, wh, keyix, __func__, __LINE__)
+#define ieee80211_find_txnode(vap, mac) \
+ ieee80211_find_txnode_debug(vap, mac, __func__, __LINE__)
#else
void ieee80211_free_node(struct ieee80211_node *);
+struct ieee80211_node *ieee80211_find_node_locked(struct ieee80211_node_table *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_node *ieee80211_find_node(struct ieee80211_node_table *,
- const uint8_t *);
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+struct ieee80211_node *ieee80211_find_vap_node_locked(
+ struct ieee80211_node_table *, const struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+struct ieee80211_node *ieee80211_find_vap_node(
+ struct ieee80211_node_table *, const struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_node * ieee80211_find_rxnode(struct ieee80211com *,
const struct ieee80211_frame_min *);
struct ieee80211_node * ieee80211_find_rxnode_withkey(struct ieee80211com *,
const struct ieee80211_frame_min *, uint16_t keyix);
-struct ieee80211_node *ieee80211_find_txnode(struct ieee80211com *,
- const uint8_t *);
-struct ieee80211_node *ieee80211_find_node_with_ssid(
- struct ieee80211_node_table *, const uint8_t *macaddr,
- u_int ssidlen, const uint8_t *ssid);
+struct ieee80211_node *ieee80211_find_txnode(struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
#endif
int ieee80211_node_delucastkey(struct ieee80211_node *);
void ieee80211_node_timeout(void *arg);
@@ -314,23 +388,22 @@ typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
void ieee80211_iterate_nodes(struct ieee80211_node_table *,
ieee80211_iter_func *, void *);
+void ieee80211_notify_erp(struct ieee80211com *);
void ieee80211_dump_node(struct ieee80211_node_table *,
struct ieee80211_node *);
void ieee80211_dump_nodes(struct ieee80211_node_table *);
-void ieee80211_notify_erp(struct ieee80211com *);
-
-struct ieee80211_node *ieee80211_fakeup_adhoc_node(
- struct ieee80211_node_table *, const uint8_t macaddr[]);
+struct ieee80211_node *ieee80211_fakeup_adhoc_node(struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_scanparams;
void ieee80211_init_neighbor(struct ieee80211_node *,
const struct ieee80211_frame *,
const struct ieee80211_scanparams *);
-struct ieee80211_node *ieee80211_add_neighbor(struct ieee80211com *,
+struct ieee80211_node *ieee80211_add_neighbor(struct ieee80211vap *,
const struct ieee80211_frame *,
const struct ieee80211_scanparams *);
-void ieee80211_node_join(struct ieee80211com *, struct ieee80211_node *,int);
-void ieee80211_node_leave(struct ieee80211com *, struct ieee80211_node *);
-int8_t ieee80211_getrssi(struct ieee80211com *);
-void ieee80211_getsignal(struct ieee80211com *, int8_t *, int8_t *);
+void ieee80211_node_join(struct ieee80211_node *,int);
+void ieee80211_node_leave(struct ieee80211_node *);
+int8_t ieee80211_getrssi(struct ieee80211vap *);
+void ieee80211_getsignal(struct ieee80211vap *, int8_t *, int8_t *);
#endif /* _NET80211_IEEE80211_NODE_H_ */
diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c
index 6e31d1d..cc2a911 100644
--- a/sys/net80211/ieee80211_output.c
+++ b/sys/net80211/ieee80211_output.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,6 +28,7 @@
__FBSDID("$FreeBSD$");
#include "opt_inet.h"
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -46,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_wds.h>
#ifdef INET
#include <netinet/in.h>
@@ -57,10 +59,10 @@ __FBSDID("$FreeBSD$");
#define ETHER_HEADER_COPY(dst, src) \
memcpy(dst, src, sizeof(struct ether_header))
-static struct mbuf *ieee80211_encap_fastframe(struct ieee80211com *ic,
+static struct mbuf *ieee80211_encap_fastframe(struct ieee80211vap *,
struct mbuf *m1, const struct ether_header *eh1,
struct mbuf *m2, const struct ether_header *eh2);
-static int ieee80211_fragment(struct ieee80211com *, struct mbuf *,
+static int ieee80211_fragment(struct ieee80211vap *, struct mbuf *,
u_int hdrsize, u_int ciphdrsize, u_int mtu);
static void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int);
@@ -72,24 +74,344 @@ static void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int);
* (e.g. beacons).
*/
static __inline int
-doprint(struct ieee80211com *ic, int subtype)
+doprint(struct ieee80211vap *vap, int subtype)
{
switch (subtype) {
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
- return (ic->ic_opmode == IEEE80211_M_IBSS);
+ return (vap->iv_opmode == IEEE80211_M_IBSS);
}
return 1;
}
#endif
/*
+ * Start method for vap's. All packets from the stack come
+ * through here. We handle common processing of the packets
+ * before dispatching them to the underlying device.
+ */
+void
+ieee80211_start(struct ifnet *ifp)
+{
+#define IS_DWDS(vap) \
+ (vap->iv_opmode == IEEE80211_M_WDS && \
+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0)
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *parent = ic->ic_ifp;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+ struct ether_header *eh;
+ int error;
+
+ /* NB: parent must be up and running */
+ if (!IFNET_IS_UP_RUNNING(parent)) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+ "%s: ignore queue, parent %s not up+running\n",
+ __func__, parent->if_xname);
+ /* XXX stat */
+ return;
+ }
+ if (vap->iv_state == IEEE80211_S_SLEEP) {
+ /*
+ * In power save, wakeup device for transmit.
+ */
+ ieee80211_new_state(vap, IEEE80211_S_RUN, 0);
+ return;
+ }
+ /*
+ * No data frames go out unless we're running.
+ * Note in particular this covers CAC and CSA
+ * states (though maybe we should check muting
+ * for CSA).
+ */
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ IEEE80211_LOCK(ic);
+ /* re-check under the com lock to avoid races */
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+ "%s: ignore queue, in %s state\n",
+ __func__, ieee80211_state_name[vap->iv_state]);
+ vap->iv_stats.is_tx_badstate++;
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ IEEE80211_UNLOCK(ic);
+ return;
+ }
+ IEEE80211_UNLOCK(ic);
+ }
+ for (;;) {
+ IFQ_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ /*
+ * Sanitize mbuf flags for net80211 use. We cannot
+ * clear M_PWR_SAV because this may be set for frames
+ * that are re-submitted from the power save queue.
+ *
+ * NB: This must be done before ieee80211_classify as
+ * it marks EAPOL in frames with M_EAPOL.
+ */
+ m->m_flags &= ~(M_80211_TX - M_PWR_SAV);
+ /*
+ * Cancel any background scan.
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN)
+ ieee80211_cancel_anyscan(vap);
+ /*
+ * Find the node for the destination so we can do
+ * things like power save and fast frames aggregation.
+ *
+ * NB: past this point various code assumes the first
+ * mbuf has the 802.3 header present (and contiguous).
+ */
+ ni = NULL;
+ if (m->m_len < sizeof(struct ether_header) &&
+ (m = m_pullup(m, sizeof(struct ether_header))) == NULL) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+ "discard frame, %s\n", "m_pullup failed");
+ vap->iv_stats.is_tx_nobuf++; /* XXX */
+ ifp->if_oerrors++;
+ continue;
+ }
+ eh = mtod(m, struct ether_header *);
+ if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
+ if (IS_DWDS(vap)) {
+ /*
+ * Only unicast frames from the above go out
+ * DWDS vaps; multicast frames are handled by
+ * dispatching the frame as it comes through
+ * the AP vap (see below).
+ */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_WDS,
+ eh->ether_dhost, "mcast", "%s", "on DWDS");
+ vap->iv_stats.is_dwds_mcast++;
+ m_freem(m);
+ continue;
+ }
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ /*
+ * Spam DWDS vap's w/ multicast traffic.
+ */
+ /* XXX only if dwds in use? */
+ ieee80211_dwds_mcast(vap, m);
+ }
+ }
+ ni = ieee80211_find_txnode(vap, eh->ether_dhost);
+ if (ni == NULL) {
+ /* NB: ieee80211_find_txnode does stat+msg */
+ ifp->if_oerrors++;
+ m_freem(m);
+ continue;
+ }
+ /* XXX AUTH'd */
+ if (ni->ni_associd == 0) {
+ /*
+ * Destination is not associated; must special
+ * case DWDS where we point iv_bss at the node
+ * for the associated station.
+ * XXX adhoc mode?
+ */
+ if (ni != vap->iv_bss || IS_DWDS(vap)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT,
+ eh->ether_dhost, NULL,
+ "sta not associated (type 0x%04x)",
+ htons(eh->ether_type));
+ vap->iv_stats.is_tx_notassoc++;
+ ifp->if_oerrors++;
+ m_freem(m);
+ ieee80211_free_node(ni);
+ continue;
+ }
+ }
+ if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
+ (m->m_flags & M_PWR_SAV) == 0) {
+ /*
+ * Station in power save mode; pass the frame
+ * to the 802.11 layer and continue. We'll get
+ * the frame back when the time is right.
+ * XXX lose WDS vap linkage?
+ */
+ ieee80211_pwrsave(ni, m);
+ ieee80211_free_node(ni);
+ continue;
+ }
+ /* calculate priority so drivers can find the tx queue */
+ if (ieee80211_classify(ni, m)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT,
+ eh->ether_dhost, NULL,
+ "%s", "classification failure");
+ vap->iv_stats.is_tx_classify++;
+ ifp->if_oerrors++;
+ m_freem(m);
+ ieee80211_free_node(ni);
+ continue;
+ }
+
+ BPF_MTAP(ifp, m); /* 802.11 tx path */
+
+ /*
+ * XXX When ni is associated with a WDS link then
+ * the vap will be the WDS vap but ni_vap will point
+ * to the ap vap the station associated to. Once
+ * we handoff the packet to the driver the callback
+ * to ieee80211_encap won't be able to tell if the
+ * packet should be encapsulated for WDS or not (e.g.
+ * multicast frames will not be handled correctly).
+ * We hack this by marking the mbuf so ieee80211_encap
+ * can do the right thing.
+ */
+ if (vap->iv_opmode == IEEE80211_M_WDS)
+ m->m_flags |= M_WDS;
+ else
+ m->m_flags &= ~M_WDS;
+
+ /*
+ * Stash the node pointer and hand the frame off to
+ * the underlying device. Note that we do this after
+ * any call to ieee80211_dwds_mcast because that code
+ * uses any existing value for rcvif.
+ */
+ m->m_pkthdr.rcvif = (void *)ni;
+
+ /* XXX defer if_start calls? */
+ IFQ_HANDOFF(parent, m, error);
+ if (error != 0) {
+ /* NB: IFQ_HANDOFF reclaims mbuf */
+ ieee80211_free_node(ni);
+ } else {
+ ifp->if_opackets++;
+ }
+ ic->ic_lastdata = ticks;
+ }
+#undef IS_DWDS
+}
+
+/*
+ * 802.11 output routine. This is (currently) used only to
+ * connect bpf write calls to the 802.11 layer for injecting
+ * raw 802.11 frames. Note we locate the ieee80211com from
+ * the ifnet using a spare field setup at attach time. This
+ * will go away when the virtual ap support comes in.
+ */
+int
+ieee80211_output(struct ifnet *ifp, struct mbuf *m,
+ struct sockaddr *dst, struct rtentry *rt0)
+{
+#define senderr(e) do { error = (e); goto bad;} while (0)
+ struct ieee80211_node *ni = NULL;
+ struct ieee80211vap *vap;
+ struct ieee80211_frame *wh;
+ int error;
+
+ if (ifp->if_drv_flags & IFF_DRV_OACTIVE) {
+ /*
+ * Short-circuit requests if the vap is marked OACTIVE
+ * as this is used when tearing down state to indicate
+ * the vap may be gone. This can also happen because a
+ * packet came down through ieee80211_start before the
+ * vap entered RUN state in which case it's also ok to
+ * just drop the frame. This should not be necessary
+ * but callers of if_output don't check OACTIVE.
+ */
+ senderr(ENETDOWN);
+ }
+ vap = ifp->if_softc;
+ /*
+ * Hand to the 802.3 code if not tagged as
+ * a raw 802.11 frame.
+ */
+ if (dst->sa_family != AF_IEEE80211)
+ return vap->iv_output(ifp, m, dst, rt0);
+#ifdef MAC
+ error = mac_check_ifnet_transmit(ifp, m);
+ if (error)
+ senderr(error);
+#endif
+ if (ifp->if_flags & IFF_MONITOR)
+ senderr(ENETDOWN);
+ if (!IFNET_IS_UP_RUNNING(ifp))
+ senderr(ENETDOWN);
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH,
+ "block %s frame in CAC state\n", "raw data");
+ vap->iv_stats.is_tx_badstate++;
+ senderr(EIO); /* XXX */
+ }
+ /* XXX bypass bridge, pfil, carp, etc. */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack))
+ senderr(EIO); /* XXX */
+ wh = mtod(m, struct ieee80211_frame *);
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0)
+ senderr(EIO); /* XXX */
+
+ /* locate destination node */
+ switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
+ case IEEE80211_FC1_DIR_NODS:
+ case IEEE80211_FC1_DIR_FROMDS:
+ ni = ieee80211_find_txnode(vap, wh->i_addr1);
+ break;
+ case IEEE80211_FC1_DIR_TODS:
+ case IEEE80211_FC1_DIR_DSTODS:
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame))
+ senderr(EIO); /* XXX */
+ ni = ieee80211_find_txnode(vap, wh->i_addr3);
+ break;
+ default:
+ senderr(EIO); /* XXX */
+ }
+ if (ni == NULL) {
+ /*
+ * Permit packets w/ bpf params through regardless
+ * (see below about sa_len).
+ */
+ if (dst->sa_len == 0)
+ senderr(EHOSTUNREACH);
+ ni = ieee80211_ref_node(vap->iv_bss);
+ }
+
+ /*
+ * Sanitize mbuf for net80211 flags leaked from above.
+ *
+ * NB: This must be done before ieee80211_classify as
+ * it marks EAPOL in frames with M_EAPOL.
+ */
+ m->m_flags &= ~M_80211_TX;
+
+ /* calculate priority so drivers can find the tx queue */
+ /* XXX assumes an 802.3 frame */
+ if (ieee80211_classify(ni, m))
+ senderr(EIO); /* XXX */
+
+ BPF_MTAP(ifp, m);
+
+ /*
+ * NB: DLT_IEEE802_11_RADIO identifies the parameters are
+ * present by setting the sa_len field of the sockaddr (yes,
+ * this is a hack).
+ * NB: we assume sa_data is suitably aligned to cast.
+ */
+ return vap->iv_ic->ic_raw_xmit(ni, m,
+ (const struct ieee80211_bpf_params *)(dst->sa_len ?
+ dst->sa_data : NULL));
+bad:
+ if (m != NULL)
+ m_freem(m);
+ if (ni != NULL)
+ ieee80211_free_node(ni);
+ return error;
+#undef senderr
+}
+
+/*
* Set the direction field and address fields of an outgoing
* non-QoS frame. Note this should be called early on in
* constructing a frame as it sets i_fc[1]; other bits can
* then be or'd in.
*/
static void
-ieee80211_send_setup(struct ieee80211com *ic,
+ieee80211_send_setup(
struct ieee80211_node *ni,
struct ieee80211_frame *wh,
int type,
@@ -101,7 +423,9 @@ ieee80211_send_setup(struct ieee80211com *ic,
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type;
if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) {
- switch (ic->ic_opmode) {
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ switch (vap->iv_opmode) {
case IEEE80211_M_STA:
wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
IEEE80211_ADDR_COPY(wh->i_addr1, bssid);
@@ -123,9 +447,8 @@ ieee80211_send_setup(struct ieee80211com *ic,
break;
case IEEE80211_M_WDS:
wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
- /* XXX cheat, bssid holds RA */
- IEEE80211_ADDR_COPY(wh->i_addr1, bssid);
- IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr1, da);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
IEEE80211_ADDR_COPY(wh->i_addr3, da);
IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa);
break;
@@ -139,6 +462,7 @@ ieee80211_send_setup(struct ieee80211com *ic,
IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
}
*(uint16_t *)&wh->i_dur[0] = 0;
+ /* XXX probe response use per-vap seq#? */
/* NB: use non-QoS tid */
*(uint16_t *)&wh->i_seq[0] =
htole16(ni->ni_txseqs[IEEE80211_NONQOS_TID] << IEEE80211_SEQ_SEQ_SHIFT);
@@ -151,55 +475,55 @@ ieee80211_send_setup(struct ieee80211com *ic,
* must have a reference as the pointer will be passed to the driver
* and potentially held for a long time. If the frame is successfully
* dispatched to the driver, then it is responsible for freeing the
- * reference (and potentially free'ing up any associated storage).
+ * reference (and potentially free'ing up any associated storage);
+ * otherwise deal with reclaiming any reference (on error).
*/
int
-ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni,
- struct mbuf *m, int type)
+ieee80211_mgmt_output(struct ieee80211_node *ni, struct mbuf *m, int type)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_frame *wh;
KASSERT(ni != NULL, ("null node"));
- /*
- * Yech, hack alert! We want to pass the node down to the
- * driver's start routine. If we don't do so then the start
- * routine must immediately look it up again and that can
- * cause a lock order reversal if, for example, this frame
- * is being sent because the station is being timedout and
- * the frame being sent is a DEAUTH message. We could stick
- * this in an m_tag and tack that on to the mbuf. However
- * that's rather expensive to do for every frame so instead
- * we stuff it in the rcvif field since outbound frames do
- * not (presently) use this.
- */
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH,
+ ni, "block %s frame in CAC state",
+ ieee80211_mgt_subtype_name[
+ (type & IEEE80211_FC0_SUBTYPE_MASK) >>
+ IEEE80211_FC0_SUBTYPE_SHIFT]);
+ vap->iv_stats.is_tx_badstate++;
+ ieee80211_free_node(ni);
+ m_freem(m);
+ return EIO; /* XXX */
+ }
+
M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
- if (m == NULL)
+ if (m == NULL) {
+ ieee80211_free_node(ni);
return ENOMEM;
- KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null"));
- m->m_pkthdr.rcvif = (void *)ni;
+ }
wh = mtod(m, struct ieee80211_frame *);
- ieee80211_send_setup(ic, ni, wh,
+ ieee80211_send_setup(ni, wh,
IEEE80211_FC0_TYPE_MGT | type,
- ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid);
+ vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid);
if ((m->m_flags & M_LINK0) != 0 && ni->ni_challenge != NULL) {
m->m_flags &= ~M_LINK0;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
- "[%s] encrypting frame (%s)\n",
- ether_sprintf(wh->i_addr1), __func__);
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr1,
+ "encrypting frame (%s)", __func__);
wh->i_fc[1] |= IEEE80211_FC1_WEP;
}
- if (ni->ni_flags & IEEE80211_NODE_QOS) {
- /* NB: force all management frames to the highest queue */
+ if (type != IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
+ /* NB: force non-ProbeResp frames to the highest queue */
M_WME_SETAC(m, WME_AC_VO);
} else
M_WME_SETAC(m, WME_AC_BE);
#ifdef IEEE80211_DEBUG
/* avoid printing too many frames */
- if ((ieee80211_msg_debug(ic) && doprint(ic, type)) ||
- ieee80211_msg_dumppkts(ic)) {
+ if ((ieee80211_msg_debug(vap) && doprint(vap, type)) ||
+ ieee80211_msg_dumppkts(vap)) {
printf("[%s] send %s on channel %u\n",
ether_sprintf(wh->i_addr1),
ieee80211_mgt_subtype_name[
@@ -209,135 +533,8 @@ ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni,
}
#endif
IEEE80211_NODE_STAT(ni, tx_mgmt);
- IF_ENQUEUE(&ic->ic_mgtq, m);
- if_start(ifp);
- ifp->if_opackets++;
-
- return 0;
-}
-
-/*
- * Raw packet transmit stub for legacy drivers.
- * Send the packet through the mgt q so we bypass
- * the normal encapsulation work.
- */
-int
-ieee80211_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
- const struct ieee80211_bpf_params *params)
-{
- struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
-
- m->m_pkthdr.rcvif = (void *) ni;
- IF_ENQUEUE(&ic->ic_mgtq, m);
- if_start(ifp);
- ifp->if_opackets++;
-
- return 0;
-}
-
-/*
- * 802.11 output routine. This is (currently) used only to
- * connect bpf write calls to the 802.11 layer for injecting
- * raw 802.11 frames. Note we locate the ieee80211com from
- * the ifnet using a spare field setup at attach time. This
- * will go away when the virtual ap support comes in.
- */
-int
-ieee80211_output(struct ifnet *ifp, struct mbuf *m,
- struct sockaddr *dst, struct rtentry *rt0)
-{
-#define senderr(e) do { error = (e); goto bad;} while (0)
- struct ieee80211com *ic = ifp->if_llsoftc; /* XXX */
- struct ieee80211_node *ni = NULL;
- struct ieee80211_frame *wh;
- int error;
-
- /*
- * Hand to the 802.3 code if not tagged as
- * a raw 802.11 frame.
- */
- if (dst->sa_family != AF_IEEE80211)
- return ether_output(ifp, m, dst, rt0);
-#ifdef MAC
- error = mac_check_ifnet_transmit(ifp, m);
- if (error)
- senderr(error);
-#endif
- if (ifp->if_flags & IFF_MONITOR)
- senderr(ENETDOWN);
- if ((ifp->if_flags & IFF_UP) == 0)
- senderr(ENETDOWN);
- /* XXX bypass bridge, pfil, carp, etc. */
-
- if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack))
- senderr(EIO); /* XXX */
- wh = mtod(m, struct ieee80211_frame *);
- if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
- IEEE80211_FC0_VERSION_0)
- senderr(EIO); /* XXX */
-
- /* locate destination node */
- switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
- case IEEE80211_FC1_DIR_NODS:
- case IEEE80211_FC1_DIR_FROMDS:
- ni = ieee80211_find_txnode(ic, wh->i_addr1);
- break;
- case IEEE80211_FC1_DIR_TODS:
- case IEEE80211_FC1_DIR_DSTODS:
- if (m->m_pkthdr.len < sizeof(struct ieee80211_frame))
- senderr(EIO); /* XXX */
- ni = ieee80211_find_txnode(ic, wh->i_addr3);
- break;
- default:
- senderr(EIO); /* XXX */
- }
- if (ni == NULL) {
- /*
- * Permit packets w/ bpf params through regardless
- * (see below about sa_len).
- */
- if (dst->sa_len == 0)
- senderr(EHOSTUNREACH);
- ni = ieee80211_ref_node(ic->ic_bss);
- }
-
- /* XXX ctrl frames should go through */
- if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
- (m->m_flags & M_PWR_SAV) == 0) {
- /*
- * Station in power save mode; pass the frame
- * to the 802.11 layer and continue. We'll get
- * the frame back when the time is right.
- */
- ieee80211_pwrsave(ni, m);
- error = 0;
- goto reclaim;
- }
-
- /* calculate priority so drivers can find the tx queue */
- /* XXX assumes an 802.3 frame */
- if (ieee80211_classify(ic, m, ni))
- senderr(EIO); /* XXX */
-
- BPF_MTAP(ifp, m);
- /*
- * NB: DLT_IEEE802_11_RADIO identifies the parameters are
- * present by setting the sa_len field of the sockaddr (yes,
- * this is a hack).
- * NB: we assume sa_data is suitably aligned to cast.
- */
- return ic->ic_raw_xmit(ni, m, (const struct ieee80211_bpf_params *)
- (dst->sa_len ? dst->sa_data : NULL));
-bad:
- if (m != NULL)
- m_freem(m);
-reclaim:
- if (ni != NULL)
- ieee80211_free_node(ni);
- return error;
-#undef senderr
+ return ic->ic_raw_xmit(ni, m, NULL);
}
/*
@@ -345,50 +542,61 @@ reclaim:
*
* NB: the caller is assumed to have setup a node reference
* for use; this is necessary to deal with a race condition
- * when probing for inactive stations.
+ * when probing for inactive stations. Like ieee80211_mgmt_output
+ * we must cleanup any node reference on error; however we
+ * can safely just unref it as we know it will never be the
+ * last reference to the node.
*/
int
ieee80211_send_nulldata(struct ieee80211_node *ni)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
struct mbuf *m;
struct ieee80211_frame *wh;
- MGETHDR(m, M_NOWAIT, MT_DATA);
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH,
+ ni, "block %s frame in CAC state", "null data");
+ ieee80211_unref_node(&ni);
+ vap->iv_stats.is_tx_badstate++;
+ return EIO; /* XXX */
+ }
+
+ m = m_gethdr(M_NOWAIT, MT_HEADER);
if (m == NULL) {
/* XXX debug msg */
ieee80211_unref_node(&ni);
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
return ENOMEM;
}
MH_ALIGN(m, sizeof(struct ieee80211_frame));
- m->m_pkthdr.rcvif = (void *) ni;
wh = mtod(m, struct ieee80211_frame *);
- ieee80211_send_setup(ic, ni, wh,
+ ieee80211_send_setup(ni, wh,
IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA,
- ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid);
- /* NB: power management bit is never sent by an AP */
- if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
- ic->ic_opmode != IEEE80211_M_HOSTAP &&
- ic->ic_opmode != IEEE80211_M_WDS)
- wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
- m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame);
+ vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid);
+ if (vap->iv_opmode != IEEE80211_M_WDS) {
+ /* NB: power management bit is never sent by an AP */
+ if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
+ vap->iv_opmode != IEEE80211_M_HOSTAP)
+ wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
+ m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame);
+ } else {
+ /* NB: 4-address frame */
+ m->m_len = m->m_pkthdr.len =
+ sizeof(struct ieee80211_frame_addr4);
+ }
M_WME_SETAC(m, WME_AC_BE);
IEEE80211_NODE_STAT(ni, tx_data);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
- "[%s] send null data frame on channel %u, pwr mgt %s\n",
- ether_sprintf(ni->ni_macaddr),
+ IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, ni,
+ "send null data frame on channel %u, pwr mgt %s",
ieee80211_chan2ieee(ic, ic->ic_curchan),
wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis");
- IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */
- if_start(ifp);
-
- return 0;
+ return ic->ic_raw_xmit(ni, m, NULL);
}
/*
@@ -398,13 +606,23 @@ ieee80211_send_nulldata(struct ieee80211_node *ni)
* applied.
*/
int
-ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni)
+ieee80211_classify(struct ieee80211_node *ni, struct mbuf *m)
{
+ const struct ether_header *eh = mtod(m, struct ether_header *);
int v_wme_ac, d_wme_ac, ac;
-#ifdef INET
- struct ether_header *eh;
-#endif
+ /*
+ * Always promote PAE/EAPOL frames to high priority.
+ */
+ if (eh->ether_type == htons(ETHERTYPE_PAE)) {
+ /* NB: mark so others don't need to check header */
+ m->m_flags |= M_EAPOL;
+ ac = WME_AC_VO;
+ goto done;
+ }
+ /*
+ * Non-qos traffic goes to BE.
+ */
if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) {
ac = WME_AC_BE;
goto done;
@@ -430,7 +648,6 @@ ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_nod
}
#ifdef INET
- eh = mtod(m, struct ether_header *);
if (eh->ether_type == htons(ETHERTYPE_IP)) {
uint8_t tos;
/*
@@ -459,13 +676,15 @@ ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_nod
/*
* Apply ACM policy.
*/
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (ni->ni_vap->iv_opmode == IEEE80211_M_STA) {
static const int acmap[4] = {
WME_AC_BK, /* WME_AC_BE */
WME_AC_BK, /* WME_AC_BK */
WME_AC_BE, /* WME_AC_VI */
WME_AC_VI, /* WME_AC_VO */
};
+ struct ieee80211com *ic = ni->ni_ic;
+
while (ac != WME_AC_BK &&
ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm)
ac = acmap[ac];
@@ -482,11 +701,11 @@ done:
* and fail rudely if they don't find the space they need.
*/
static struct mbuf *
-ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
+ieee80211_mbuf_adjust(struct ieee80211vap *vap, int hdrsize,
struct ieee80211_key *key, struct mbuf *m)
{
#define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc))
- int needed_space = ic->ic_headroom + hdrsize;
+ int needed_space = vap->iv_ic->ic_headroom + hdrsize;
if (key != NULL) {
/* XXX belongs in crypto code? */
@@ -501,9 +720,9 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
if (key->wk_flags & (IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC)) {
m = m_unshare(m, M_NOWAIT);
if (m == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
"%s: cannot get writable mbuf\n", __func__);
- ic->ic_stats.is_tx_nobuf++; /* XXX new stat */
+ vap->iv_stats.is_tx_nobuf++; /* XXX new stat */
return NULL;
}
}
@@ -520,9 +739,9 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
if (M_LEADINGSPACE(m) < needed_space - TO_BE_RECLAIMED) {
struct mbuf *n = m_gethdr(M_NOWAIT, m->m_type);
if (n == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
"%s: cannot expand storage\n", __func__);
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
m_freem(m);
return NULL;
}
@@ -564,13 +783,14 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
* we fall back to the default transmit key.
*/
static __inline struct ieee80211_key *
-ieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_crypto_getucastkey(struct ieee80211vap *vap,
+ struct ieee80211_node *ni)
{
if (IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) {
- if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
- IEEE80211_KEY_UNDEFINED(&ic->ic_nw_keys[ic->ic_def_txkey]))
+ if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE ||
+ IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey]))
return NULL;
- return &ic->ic_nw_keys[ic->ic_def_txkey];
+ return &vap->iv_nw_keys[vap->iv_def_txkey];
} else {
return &ni->ni_ucastkey;
}
@@ -582,12 +802,13 @@ ieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
* the default tx key.
*/
static __inline struct ieee80211_key *
-ieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_crypto_getmcastkey(struct ieee80211vap *vap,
+ struct ieee80211_node *ni)
{
- if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
- IEEE80211_KEY_UNDEFINED(&ic->ic_nw_keys[ic->ic_def_txkey]))
+ if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE ||
+ IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey]))
return NULL;
- return &ic->ic_nw_keys[ic->ic_def_txkey];
+ return &vap->iv_nw_keys[vap->iv_def_txkey];
}
/*
@@ -595,16 +816,21 @@ ieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
* If an error is encountered NULL is returned. The caller is required
* to provide a node reference and pullup the ethernet header in the
* first mbuf.
+ *
+ * NB: Packet is assumed to be processed by ieee80211_classify which
+ * marked EAPOL frames w/ M_EAPOL.
*/
struct mbuf *
-ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
- struct ieee80211_node *ni)
+ieee80211_encap(struct ieee80211_node *ni, struct mbuf *m)
{
+#define WH4(wh) ((struct ieee80211_frame_addr4 *)(wh))
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
struct ether_header eh;
struct ieee80211_frame *wh;
struct ieee80211_key *key;
struct llc *llc;
- int hdrsize, datalen, addqos, txfrag, isff;
+ int hdrsize, hdrspace, datalen, addqos, txfrag, isff, is4addr;
/*
* Copy existing Ethernet header to a safe place. The
@@ -612,7 +838,7 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
* reorganizing state for the final encapsulation.
*/
KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!"));
- memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
+ ETHER_HEADER_COPY(&eh, mtod(m, caddr_t));
/*
* Insure space for additional headers. First identify
@@ -626,23 +852,24 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
* buffer may not be expanded as needed by the cipher
* routines, but they will/should discard it.
*/
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
- if (ic->ic_opmode == IEEE80211_M_STA ||
- !IEEE80211_IS_MULTICAST(eh.ether_dhost))
- key = ieee80211_crypto_getucastkey(ic, ni);
+ if (vap->iv_flags & IEEE80211_F_PRIVACY) {
+ if (vap->iv_opmode == IEEE80211_M_STA ||
+ !IEEE80211_IS_MULTICAST(eh.ether_dhost) ||
+ (vap->iv_opmode == IEEE80211_M_WDS &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY)))
+ key = ieee80211_crypto_getucastkey(vap, ni);
else
- key = ieee80211_crypto_getmcastkey(ic, ni);
- if (key == NULL && eh.ether_type != htons(ETHERTYPE_PAE)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] no default transmit key (%s) deftxkey %u\n",
- ether_sprintf(eh.ether_dhost), __func__,
- ic->ic_def_txkey);
- ic->ic_stats.is_tx_nodefkey++;
+ key = ieee80211_crypto_getmcastkey(vap, ni);
+ if (key == NULL && (m->m_flags & M_EAPOL) == 0) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
+ eh.ether_dhost,
+ "no default transmit key (%s) deftxkey %u",
+ __func__, vap->iv_def_txkey);
+ vap->iv_stats.is_tx_nodefkey++;
goto bad;
}
} else
key = NULL;
- /* XXX 4-address format */
/*
* XXX Some ap's don't handle QoS-encapsulated EAPOL
* frames so suppress use. This may be an issue if other
@@ -651,13 +878,31 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
* configurable.
*/
addqos = (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) &&
- eh.ether_type != htons(ETHERTYPE_PAE);
+ (m->m_flags & M_EAPOL) == 0;
if (addqos)
hdrsize = sizeof(struct ieee80211_qosframe);
else
hdrsize = sizeof(struct ieee80211_frame);
+ /*
+ * 4-address frames need to be generated for:
+ * o packets sent through a WDS vap (M_WDS || IEEE80211_M_WDS)
+ * o packets relayed by a station operating with dynamic WDS
+ * (IEEE80211_M_STA+IEEE80211_F_DWDS and src address)
+ */
+ is4addr = (m->m_flags & M_WDS) ||
+ vap->iv_opmode == IEEE80211_M_WDS || /* XXX redundant? */
+ (vap->iv_opmode == IEEE80211_M_STA &&
+ (vap->iv_flags & IEEE80211_F_DWDS) &&
+ !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr));
+ if (is4addr)
+ hdrsize += IEEE80211_ADDR_LEN;
+ /*
+ * Honor driver DATAPAD requirement.
+ */
if (ic->ic_flags & IEEE80211_F_DATAPAD)
- hdrsize = roundup(hdrsize, sizeof(uint32_t));
+ hdrspace = roundup(hdrsize, sizeof(uint32_t));
+ else
+ hdrspace = hdrsize;
if ((isff = m->m_flags & M_FF) != 0) {
struct mbuf *m2;
@@ -671,7 +916,7 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
*/
m2 = m->m_nextpkt;
if (m2 == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: only one frame\n", __func__);
goto bad;
}
@@ -681,8 +926,8 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
* layout; this allocates space according to what
* ieee80211_encap_fastframe will do.
*/
- m = ieee80211_mbuf_adjust(ic,
- hdrsize + sizeof(struct llc) + sizeof(uint32_t) + 2 +
+ m = ieee80211_mbuf_adjust(vap,
+ hdrspace + sizeof(struct llc) + sizeof(uint32_t) + 2 +
sizeof(struct ether_header),
key, m);
if (m == NULL) {
@@ -697,22 +942,22 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
* at the end of first frame.
*/
KASSERT(m2->m_len >= sizeof(eh2), ("no ethernet header!"));
- memcpy(&eh2, mtod(m2, caddr_t), sizeof(struct ether_header));
- m2 = ieee80211_mbuf_adjust(ic,
+ ETHER_HEADER_COPY(&eh2, mtod(m2, caddr_t));
+ m2 = ieee80211_mbuf_adjust(vap,
ATH_FF_MAX_HDR_PAD + sizeof(struct ether_header),
NULL, m2);
if (m2 == NULL) {
/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
goto bad;
}
- m = ieee80211_encap_fastframe(ic, m, &eh, m2, &eh2);
+ m = ieee80211_encap_fastframe(vap, m, &eh, m2, &eh2);
if (m == NULL)
goto bad;
} else {
/*
* Normal frame.
*/
- m = ieee80211_mbuf_adjust(ic, hdrsize, key, m);
+ m = ieee80211_mbuf_adjust(vap, hdrspace, key, m);
if (m == NULL) {
/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
goto bad;
@@ -729,15 +974,21 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
}
datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */
- M_PREPEND(m, hdrsize, M_DONTWAIT);
+ M_PREPEND(m, hdrspace, M_DONTWAIT);
if (m == NULL) {
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
goto bad;
}
wh = mtod(m, struct ieee80211_frame *);
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
*(uint16_t *)wh->i_dur = 0;
- switch (ic->ic_opmode) {
+ if (is4addr) {
+ wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost);
+ IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost);
+ } else switch (vap->iv_opmode) {
case IEEE80211_M_STA:
wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid);
@@ -750,10 +1001,10 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
/*
- * NB: always use the bssid from ic_bss as the
+ * NB: always use the bssid from iv_bss as the
* neighbor's may be stale after an ibss merge
*/
- IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid);
+ IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_bss->ni_bssid);
break;
case IEEE80211_M_HOSTAP:
wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
@@ -762,42 +1013,47 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
break;
case IEEE80211_M_MONITOR:
- case IEEE80211_M_WDS:
+ case IEEE80211_M_WDS: /* NB: is4addr should always be true */
goto bad;
}
if (m->m_flags & M_MORE_DATA)
wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
if (addqos) {
- struct ieee80211_qosframe *qwh =
- (struct ieee80211_qosframe *) wh;
+ uint8_t *qos;
int ac, tid;
+ if (is4addr) {
+ qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos;
+ } else
+ qos = ((struct ieee80211_qosframe *) wh)->i_qos;
ac = M_WME_GETAC(m);
/* map from access class/queue to 11e header priorty value */
tid = WME_AC_TO_TID(ac);
- qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
+ qos[0] = tid & IEEE80211_QOS_TID;
/*
* Check if A-MPDU tx aggregation is setup or if we
* should try to enable it. The sta must be associated
- * with HT and A-MPDU enabled for use. On the first
- * frame that goes out We issue an ADDBA request and
- * wait for a reply. The frame being encapsulated
- * will go out w/o using A-MPDU, or possibly it might
- * be collected by the driver and held/retransmit.
- * ieee80211_ampdu_request handles staggering requests
- * in case the receiver NAK's us or we are otherwise
- * unable to establish a BA stream.
+ * with HT and A-MPDU enabled for use. When the policy
+ * routine decides we should enable A-MPDU we issue an
+ * ADDBA request and wait for a reply. The frame being
+ * encapsulated will go out w/o using A-MPDU, or possibly
+ * it might be collected by the driver and held/retransmit.
+ * The default ic_ampdu_enable routine handles staggering
+ * ADDBA requests in case the receiver NAK's us or we are
+ * otherwise unable to establish a BA stream.
*/
if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)) {
+ (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_TX)) {
struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac];
+ ieee80211_txampdu_count_packet(tap);
if (IEEE80211_AMPDU_RUNNING(tap)) {
/*
* Operational, mark frame for aggregation.
*/
- qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_BA;
- } else if (!IEEE80211_AMPDU_REQUESTED(tap)) {
+ qos[0] |= IEEE80211_QOS_ACKPOLICY_BA;
+ } else if (!IEEE80211_AMPDU_REQUESTED(tap) &&
+ ic->ic_ampdu_enable(ni, tap)) {
/*
* Not negotiated yet, request service.
*/
@@ -806,9 +1062,9 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
}
/* XXX works even when BA marked above */
if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy)
- qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK;
- qwh->i_qos[1] = 0;
- qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
+ qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK;
+ qos[1] = 0;
+ wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
*(uint16_t *)wh->i_seq =
htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
@@ -819,38 +1075,32 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
ni->ni_txseqs[IEEE80211_NONQOS_TID]++;
}
/* check if xmit fragmentation is required */
- txfrag = (m->m_pkthdr.len > ic->ic_fragthreshold &&
+ txfrag = (m->m_pkthdr.len > vap->iv_fragthreshold &&
!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
- (ic->ic_caps & IEEE80211_C_TXFRAG) &&
+ (vap->iv_caps & IEEE80211_C_TXFRAG) &&
!isff); /* NB: don't fragment ff's */
if (key != NULL) {
/*
* IEEE 802.1X: send EAPOL frames always in the clear.
* WPA/WPA2: encrypt EAPOL keys when pairwise keys are set.
*/
- if (eh.ether_type != htons(ETHERTYPE_PAE) ||
- ((ic->ic_flags & IEEE80211_F_WPA) &&
- (ic->ic_opmode == IEEE80211_M_STA ?
+ if ((m->m_flags & M_EAPOL) == 0 ||
+ ((vap->iv_flags & IEEE80211_F_WPA) &&
+ (vap->iv_opmode == IEEE80211_M_STA ?
!IEEE80211_KEY_UNDEFINED(key) :
!IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)))) {
wh->i_fc[1] |= IEEE80211_FC1_WEP;
- if (!ieee80211_crypto_enmic(ic, key, m, txfrag)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
- "[%s] enmic failed, discard frame\n",
- ether_sprintf(eh.ether_dhost));
- ic->ic_stats.is_crypto_enmicfail++;
+ if (!ieee80211_crypto_enmic(vap, key, m, txfrag)) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT,
+ eh.ether_dhost,
+ "%s", "enmic failed, discard frame");
+ vap->iv_stats.is_crypto_enmicfail++;
goto bad;
}
}
}
- /*
- * NB: frag flags may leak from above; they should only
- * be set on return to the caller if we fragment at
- * the 802.11 layer.
- */
- m->m_flags &= ~(M_FRAG | M_FIRSTFRAG);
- if (txfrag && !ieee80211_fragment(ic, m, hdrsize,
- key != NULL ? key->wk_cipher->ic_header : 0, ic->ic_fragthreshold))
+ if (txfrag && !ieee80211_fragment(vap, m, hdrsize,
+ key != NULL ? key->wk_cipher->ic_header : 0, vap->iv_fragthreshold))
goto bad;
IEEE80211_NODE_STAT(ni, tx_data);
@@ -860,11 +1110,16 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
IEEE80211_NODE_STAT(ni, tx_ucast);
IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen);
+ /* XXX fragmented frames not handled */
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+
return m;
bad:
if (m != NULL)
m_freem(m);
return NULL;
+#undef WH4
}
/*
@@ -875,7 +1130,7 @@ bad:
* type that specifies the payload size).
*/
static struct mbuf *
-ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m,
+ieee80211_encap1(struct ieee80211vap *vap, struct mbuf *m,
const struct ether_header *eh)
{
struct llc *llc;
@@ -894,9 +1149,9 @@ ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m,
M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
if (m == NULL) { /* XXX cannot happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no space for ether_header\n", __func__);
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
return NULL;
}
ETHER_HEADER_COPY(mtod(m, void *), eh);
@@ -914,7 +1169,7 @@ ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m,
* problem (should not happen).
*/
static struct mbuf *
-ieee80211_encap_fastframe(struct ieee80211com *ic,
+ieee80211_encap_fastframe(struct ieee80211vap *vap,
struct mbuf *m1, const struct ether_header *eh1,
struct mbuf *m2, const struct ether_header *eh2)
{
@@ -925,12 +1180,12 @@ ieee80211_encap_fastframe(struct ieee80211com *ic,
/*
* First, each frame gets a standard encapsulation.
*/
- m1 = ieee80211_encap1(ic, m1, eh1);
+ m1 = ieee80211_encap1(vap, m1, eh1);
if (m1 == NULL) {
m_freem(m2);
return NULL;
}
- m2 = ieee80211_encap1(ic, m2, eh2);
+ m2 = ieee80211_encap1(vap, m2, eh2);
if (m2 == NULL) {
m_freem(m1);
return NULL;
@@ -969,18 +1224,18 @@ ieee80211_encap_fastframe(struct ieee80211com *ic,
m1->m_pkthdr.len += m2->m_pkthdr.len;
M_PREPEND(m1, sizeof(uint32_t)+2, M_DONTWAIT);
if (m1 == NULL) { /* XXX cannot happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no space for tunnel header\n", __func__);
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
return NULL;
}
memset(mtod(m1, void *), 0, sizeof(uint32_t)+2);
M_PREPEND(m1, sizeof(struct llc), M_DONTWAIT);
if (m1 == NULL) { /* XXX cannot happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no space for llc header\n", __func__);
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
return NULL;
}
llc = mtod(m1, struct llc *);
@@ -991,7 +1246,7 @@ ieee80211_encap_fastframe(struct ieee80211com *ic,
llc->llc_snap.org_code[2] = ATH_FF_SNAP_ORGCODE_2;
llc->llc_snap.ether_type = htons(ATH_FF_ETH_TYPE);
- ic->ic_stats.is_ff_encap++;
+ vap->iv_stats.is_ff_encap++;
return m1;
}
@@ -1005,7 +1260,7 @@ ieee80211_encap_fastframe(struct ieee80211com *ic,
* packet's mbufs but that is significantly more complicated.
*/
static int
-ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0,
+ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0,
u_int hdrsize, u_int ciphdrsize, u_int mtu)
{
struct ieee80211_frame *wh, *whf;
@@ -1028,6 +1283,7 @@ ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0,
fragsize = totalhdrsize + remainder;
if (fragsize > mtu)
fragsize = mtu;
+ /* XXX fragsize can be >2048! */
KASSERT(fragsize < MCLBYTES,
("fragment size %u too big!", fragsize));
if (fragsize > MHLEN)
@@ -1073,8 +1329,8 @@ ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0,
m_adj(m0, -(m0->m_pkthdr.len - (mtu - ciphdrsize)));
m0->m_flags |= M_FIRSTFRAG | M_FRAG;
- ic->ic_stats.is_tx_fragframes++;
- ic->ic_stats.is_tx_frags += fragno-1;
+ vap->iv_stats.is_tx_fragframes++;
+ vap->iv_stats.is_tx_frags += fragno-1;
return 1;
bad:
@@ -1125,7 +1381,7 @@ ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs)
}
/*
- * Add an ssid elemet to a frame.
+ * Add an ssid element to a frame.
*/
static uint8_t *
ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len)
@@ -1157,188 +1413,39 @@ ieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic)
return frm;
}
+/*
+ * Add a CFParams element to a frame.
+ */
static uint8_t *
-ieee80211_setup_wpa_ie(struct ieee80211com *ic, uint8_t *ie)
+ieee80211_add_cfparms(uint8_t *frm, struct ieee80211com *ic)
{
-#define WPA_OUI_BYTES 0x00, 0x50, 0xf2
#define ADDSHORT(frm, v) do { \
frm[0] = (v) & 0xff; \
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
-#define ADDSELECTOR(frm, sel) do { \
- memcpy(frm, sel, 4); \
- frm += 4; \
-} while (0)
- static const uint8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE };
- static const uint8_t cipher_suite[][4] = {
- { WPA_OUI_BYTES, WPA_CSE_WEP40 }, /* NB: 40-bit */
- { WPA_OUI_BYTES, WPA_CSE_TKIP },
- { 0x00, 0x00, 0x00, 0x00 }, /* XXX WRAP */
- { WPA_OUI_BYTES, WPA_CSE_CCMP },
- { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */
- { WPA_OUI_BYTES, WPA_CSE_NULL },
- };
- static const uint8_t wep104_suite[4] =
- { WPA_OUI_BYTES, WPA_CSE_WEP104 };
- static const uint8_t key_mgt_unspec[4] =
- { WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC };
- static const uint8_t key_mgt_psk[4] =
- { WPA_OUI_BYTES, WPA_ASE_8021X_PSK };
- const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
- uint8_t *frm = ie;
- uint8_t *selcnt;
-
- *frm++ = IEEE80211_ELEMID_VENDOR;
- *frm++ = 0; /* length filled in below */
- memcpy(frm, oui, sizeof(oui)); /* WPA OUI */
- frm += sizeof(oui);
- ADDSHORT(frm, WPA_VERSION);
-
- /* XXX filter out CKIP */
-
- /* multicast cipher */
- if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
- rsn->rsn_mcastkeylen >= 13)
- ADDSELECTOR(frm, wep104_suite);
- else
- ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]);
-
- /* unicast cipher list */
- selcnt = frm;
- ADDSHORT(frm, 0); /* selector count */
- if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) {
- selcnt[0]++;
- ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
- }
- if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) {
- selcnt[0]++;
- ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]);
- }
-
- /* authenticator selector list */
- selcnt = frm;
- ADDSHORT(frm, 0); /* selector count */
- if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) {
- selcnt[0]++;
- ADDSELECTOR(frm, key_mgt_unspec);
- }
- if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) {
- selcnt[0]++;
- ADDSELECTOR(frm, key_mgt_psk);
- }
-
- /* optional capabilities */
- if (rsn->rsn_caps != 0 && rsn->rsn_caps != RSN_CAP_PREAUTH)
- ADDSHORT(frm, rsn->rsn_caps);
-
- /* calculate element length */
- ie[1] = frm - ie - 2;
- KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa),
- ("WPA IE too big, %u > %zu",
- ie[1]+2, sizeof(struct ieee80211_ie_wpa)));
+ *frm++ = IEEE80211_ELEMID_CFPARMS;
+ *frm++ = 6;
+ *frm++ = 0; /* CFP count */
+ *frm++ = 2; /* CFP period */
+ ADDSHORT(frm, 0); /* CFP MaxDuration (TU) */
+ ADDSHORT(frm, 0); /* CFP CurRemaining (TU) */
return frm;
#undef ADDSHORT
-#undef ADDSELECTOR
-#undef WPA_OUI_BYTES
}
-static uint8_t *
-ieee80211_setup_rsn_ie(struct ieee80211com *ic, uint8_t *ie)
+static __inline uint8_t *
+add_appie(uint8_t *frm, const struct ieee80211_appie *ie)
{
-#define RSN_OUI_BYTES 0x00, 0x0f, 0xac
-#define ADDSHORT(frm, v) do { \
- frm[0] = (v) & 0xff; \
- frm[1] = (v) >> 8; \
- frm += 2; \
-} while (0)
-#define ADDSELECTOR(frm, sel) do { \
- memcpy(frm, sel, 4); \
- frm += 4; \
-} while (0)
- static const uint8_t cipher_suite[][4] = {
- { RSN_OUI_BYTES, RSN_CSE_WEP40 }, /* NB: 40-bit */
- { RSN_OUI_BYTES, RSN_CSE_TKIP },
- { RSN_OUI_BYTES, RSN_CSE_WRAP },
- { RSN_OUI_BYTES, RSN_CSE_CCMP },
- { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */
- { RSN_OUI_BYTES, RSN_CSE_NULL },
- };
- static const uint8_t wep104_suite[4] =
- { RSN_OUI_BYTES, RSN_CSE_WEP104 };
- static const uint8_t key_mgt_unspec[4] =
- { RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC };
- static const uint8_t key_mgt_psk[4] =
- { RSN_OUI_BYTES, RSN_ASE_8021X_PSK };
- const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
- uint8_t *frm = ie;
- uint8_t *selcnt;
-
- *frm++ = IEEE80211_ELEMID_RSN;
- *frm++ = 0; /* length filled in below */
- ADDSHORT(frm, RSN_VERSION);
-
- /* XXX filter out CKIP */
-
- /* multicast cipher */
- if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
- rsn->rsn_mcastkeylen >= 13)
- ADDSELECTOR(frm, wep104_suite);
- else
- ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]);
-
- /* unicast cipher list */
- selcnt = frm;
- ADDSHORT(frm, 0); /* selector count */
- if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) {
- selcnt[0]++;
- ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
- }
- if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) {
- selcnt[0]++;
- ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]);
- }
-
- /* authenticator selector list */
- selcnt = frm;
- ADDSHORT(frm, 0); /* selector count */
- if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) {
- selcnt[0]++;
- ADDSELECTOR(frm, key_mgt_unspec);
- }
- if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) {
- selcnt[0]++;
- ADDSELECTOR(frm, key_mgt_psk);
- }
-
- /* optional capabilities */
- ADDSHORT(frm, rsn->rsn_caps);
- /* XXX PMKID */
-
- /* calculate element length */
- ie[1] = frm - ie - 2;
- KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa),
- ("RSN IE too big, %u > %zu",
- ie[1]+2, sizeof(struct ieee80211_ie_wpa)));
- return frm;
-#undef ADDSELECTOR
-#undef ADDSHORT
-#undef RSN_OUI_BYTES
+ memcpy(frm, ie->ie_data, ie->ie_len);
+ return frm + ie->ie_len;
}
-/*
- * Add a WPA/RSN element to a frame.
- */
-static uint8_t *
-ieee80211_add_wpa(uint8_t *frm, struct ieee80211com *ic)
+static __inline uint8_t *
+add_ie(uint8_t *frm, const uint8_t *ie)
{
-
- KASSERT(ic->ic_flags & IEEE80211_F_WPA, ("no WPA/RSN!"));
- if (ic->ic_flags & IEEE80211_F_WPA2)
- frm = ieee80211_setup_rsn_ie(ic, frm);
- if (ic->ic_flags & IEEE80211_F_WPA1)
- frm = ieee80211_setup_wpa_ie(ic, frm);
- return frm;
+ memcpy(frm, ie, 2 + ie[1]);
+ return frm + 2 + ie[1];
}
#define WME_OUI_BYTES 0x00, 0x50, 0xf2
@@ -1432,6 +1539,94 @@ ieee80211_add_ath(uint8_t *frm, uint8_t caps, uint16_t defkeyix)
#undef ATH_OUI_BYTES
/*
+ * Add an 11h Power Constraint element to a frame.
+ */
+static uint8_t *
+ieee80211_add_powerconstraint(uint8_t *frm, struct ieee80211vap *vap)
+{
+ const struct ieee80211_channel *c = vap->iv_bss->ni_chan;
+ /* XXX per-vap tx power limit? */
+ int8_t limit = vap->iv_ic->ic_txpowlimit / 2;
+
+ frm[0] = IEEE80211_ELEMID_PWRCNSTR;
+ frm[1] = 1;
+ frm[2] = c->ic_maxregpower > limit ? c->ic_maxregpower - limit : 0;
+ return frm + 3;
+}
+
+/*
+ * Add an 11h Power Capability element to a frame.
+ */
+static uint8_t *
+ieee80211_add_powercapability(uint8_t *frm, const struct ieee80211_channel *c)
+{
+ frm[0] = IEEE80211_ELEMID_PWRCAP;
+ frm[1] = 2;
+ frm[2] = c->ic_minpower;
+ frm[3] = c->ic_maxpower;
+ return frm + 4;
+}
+
+/*
+ * Add an 11h Supported Channels element to a frame.
+ */
+static uint8_t *
+ieee80211_add_supportedchannels(uint8_t *frm, struct ieee80211com *ic)
+{
+ static const int ielen = 26;
+
+ frm[0] = IEEE80211_ELEMID_SUPPCHAN;
+ frm[1] = ielen;
+ /* XXX not correct */
+ memcpy(frm+2, ic->ic_chan_avail, ielen);
+ return frm + 2 + ielen;
+}
+
+/*
+ * Add an 11h Channel Switch Announcement element to a frame.
+ * Note that we use the per-vap CSA count to adjust the global
+ * counter so we can use this routine to form probe response
+ * frames and get the current count.
+ */
+static uint8_t *
+ieee80211_add_csa(uint8_t *frm, struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_csa_ie *csa = (struct ieee80211_csa_ie *) frm;
+
+ csa->csa_ie = IEEE80211_ELEMID_CHANSWITCHANN;
+ csa->csa_len = 3;
+ csa->csa_mode = 1; /* XXX force quiet on channel */
+ csa->csa_newchan = ieee80211_chan2ieee(ic, ic->ic_csa_newchan);
+ csa->csa_count = ic->ic_csa_count - vap->iv_csa_count;
+ return frm + sizeof(*csa);
+}
+
+/*
+ * Add an 11h country information element to a frame.
+ */
+static uint8_t *
+ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic)
+{
+
+ if (ic->ic_countryie == NULL ||
+ ic->ic_countryie_chan != ic->ic_bsschan) {
+ /*
+ * Handle lazy construction of ie. This is done on
+ * first use and after a channel change that requires
+ * re-calculation.
+ */
+ if (ic->ic_countryie != NULL)
+ free(ic->ic_countryie, M_80211_NODE_IE);
+ ic->ic_countryie = ieee80211_alloc_countryie(ic);
+ if (ic->ic_countryie == NULL)
+ return frm;
+ ic->ic_countryie_chan = ic->ic_bsschan;
+ }
+ return add_appie(frm, ic->ic_countryie);
+}
+
+/*
* Send a probe request frame with the specified ssid
* and any optional information element data.
*/
@@ -1440,21 +1635,28 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
const uint8_t sa[IEEE80211_ADDR_LEN],
const uint8_t da[IEEE80211_ADDR_LEN],
const uint8_t bssid[IEEE80211_ADDR_LEN],
- const uint8_t *ssid, size_t ssidlen,
- const void *optie, size_t optielen)
+ const uint8_t *ssid, size_t ssidlen)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_frame *wh;
const struct ieee80211_rateset *rs;
struct mbuf *m;
uint8_t *frm;
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
+ "block %s frame in CAC state", "probe request");
+ vap->iv_stats.is_tx_badstate++;
+ return EIO; /* XXX */
+ }
+
/*
* Hold a reference on the node so it doesn't go away until after
* the xmit is complete all the way in the driver. On error we
* will remove our reference.
*/
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
__func__, __LINE__,
ni, ether_sprintf(ni->ni_macaddr),
@@ -1465,18 +1667,23 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
* prreq frame format
* [tlv] ssid
* [tlv] supported rates
+ * [tlv] RSN (optional)
* [tlv] extended supported rates
+ * [tlv] WPA (optional)
* [tlv] user-specified ie's
*/
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
- 2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_RATE_SIZE
+ + sizeof(struct ieee80211_ie_wpa)
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
- + (optie != NULL ? optielen : 0)
+ + sizeof(struct ieee80211_ie_wpa)
+ + (vap->iv_appie_probereq != NULL ?
+ vap->iv_appie_probereq->ie_len : 0)
);
if (m == NULL) {
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
ieee80211_free_node(ni);
return ENOMEM;
}
@@ -1484,62 +1691,70 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
frm = ieee80211_add_ssid(frm, ssid, ssidlen);
rs = ieee80211_get_suprates(ic, ic->ic_curchan);
frm = ieee80211_add_rates(frm, rs);
+ if (vap->iv_flags & IEEE80211_F_WPA2) {
+ if (vap->iv_rsn_ie != NULL)
+ frm = add_ie(frm, vap->iv_rsn_ie);
+ /* XXX else complain? */
+ }
frm = ieee80211_add_xrates(frm, rs);
-
- if (optie != NULL) {
- memcpy(frm, optie, optielen);
- frm += optielen;
+ if (vap->iv_flags & IEEE80211_F_WPA1) {
+ if (vap->iv_wpa_ie != NULL)
+ frm = add_ie(frm, vap->iv_wpa_ie);
+ /* XXX else complain? */
}
+ if (vap->iv_appie_probereq != NULL)
+ frm = add_appie(frm, vap->iv_appie_probereq);
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
if (m == NULL)
return ENOMEM;
- KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null"));
- m->m_pkthdr.rcvif = (void *)ni;
wh = mtod(m, struct ieee80211_frame *);
- ieee80211_send_setup(ic, ni, wh,
+ ieee80211_send_setup(ni, wh,
IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ,
sa, da, bssid);
/* XXX power management? */
+ M_WME_SETAC(m, WME_AC_BE);
+
IEEE80211_NODE_STAT(ni, tx_probereq);
IEEE80211_NODE_STAT(ni, tx_mgmt);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
- "[%s] send probe req on channel %u\n",
- ether_sprintf(wh->i_addr1),
- ieee80211_chan2ieee(ic, ic->ic_curchan));
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
+ "send probe req on channel %u bssid %s ssid \"%.*s\"\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(bssid),
+ ssidlen, ssid);
- IF_ENQUEUE(&ic->ic_mgtq, m);
- if_start(ic->ic_ifp);
- return 0;
+ return ic->ic_raw_xmit(ni, m, NULL);
}
/*
* Calculate capability information for mgt frames.
*/
static uint16_t
-getcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan)
+getcapinfo(struct ieee80211vap *vap, struct ieee80211_channel *chan)
{
+ struct ieee80211com *ic = vap->iv_ic;
uint16_t capinfo;
- KASSERT(ic->ic_opmode != IEEE80211_M_STA, ("station mode"));
+ KASSERT(vap->iv_opmode != IEEE80211_M_STA, ("station mode"));
- if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
capinfo = IEEE80211_CAPINFO_ESS;
- else if (ic->ic_opmode == IEEE80211_M_IBSS)
+ else if (vap->iv_opmode == IEEE80211_M_IBSS)
capinfo = IEEE80211_CAPINFO_IBSS;
else
capinfo = 0;
- if (ic->ic_flags & IEEE80211_F_PRIVACY)
+ if (vap->iv_flags & IEEE80211_F_PRIVACY)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
IEEE80211_IS_CHAN_2GHZ(chan))
capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
if (ic->ic_flags & IEEE80211_F_SHSLOT)
capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+ if (IEEE80211_IS_CHAN_5GHZ(chan) && (vap->iv_flags & IEEE80211_F_DOTH))
+ capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT;
return capinfo;
}
@@ -1549,12 +1764,13 @@ getcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan)
* count bumped to reflect our use for an indeterminant time.
*/
int
-ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
- int type, int arg)
+ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg)
{
#define HTFLAGS (IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT)
-#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
- const struct ieee80211_rateset *rs;
+#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_node *bss = vap->iv_bss;
struct mbuf *m;
uint8_t *frm;
uint16_t capinfo;
@@ -1567,7 +1783,7 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
* the xmit is complete all the way in the driver. On error we
* will remove our reference.
*/
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
__func__, __LINE__,
ni, ether_sprintf(ni->ni_macaddr),
@@ -1575,112 +1791,6 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
ieee80211_ref_node(ni);
switch (type) {
- case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
- /*
- * probe response frame format
- * [8] time stamp
- * [2] beacon interval
- * [2] cabability information
- * [tlv] ssid
- * [tlv] supported rates
- * [tlv] parameter set (FH/DS)
- * [tlv] parameter set (IBSS)
- * [tlv] extended rate phy (ERP)
- * [tlv] extended supported rates
- * [tlv] WPA
- * [tlv] WME (optional)
- * [tlv] HT capabilities
- * [tlv] HT information
- * [tlv] Vendor OUI HT capabilities (optional)
- * [tlv] Vendor OUI HT information (optional)
- * [tlv] Atheros capabilities
- */
- m = ieee80211_getmgtframe(&frm,
- ic->ic_headroom + sizeof(struct ieee80211_frame),
- 8
- + sizeof(uint16_t)
- + sizeof(uint16_t)
- + 2 + IEEE80211_NWID_LEN
- + 2 + IEEE80211_RATE_SIZE
- + 7 /* max(7,3) */
- + 6
- + 3
- + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
- /* XXX !WPA1+WPA2 fits w/o a cluster */
- + (ic->ic_flags & IEEE80211_F_WPA ?
- 2*sizeof(struct ieee80211_ie_wpa) : 0)
- + sizeof(struct ieee80211_wme_param)
- /* XXX check for cluster requirement */
- + 2*sizeof(struct ieee80211_ie_htcap) + 4
- + 2*sizeof(struct ieee80211_ie_htinfo) + 4
- + sizeof(struct ieee80211_ath_ie)
- );
- if (m == NULL)
- senderr(ENOMEM, is_tx_nobuf);
-
- memset(frm, 0, 8); /* timestamp should be filled later */
- frm += 8;
- *(uint16_t *)frm = htole16(ic->ic_bss->ni_intval);
- frm += 2;
- capinfo = getcapinfo(ic, ic->ic_curchan);
- *(uint16_t *)frm = htole16(capinfo);
- frm += 2;
-
- frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid,
- ic->ic_bss->ni_esslen);
- rs = ieee80211_get_suprates(ic, ic->ic_curchan);
- frm = ieee80211_add_rates(frm, rs);
-
- if (IEEE80211_IS_CHAN_FHSS(ic->ic_curchan)) {
- *frm++ = IEEE80211_ELEMID_FHPARMS;
- *frm++ = 5;
- *frm++ = ni->ni_fhdwell & 0x00ff;
- *frm++ = (ni->ni_fhdwell >> 8) & 0x00ff;
- *frm++ = IEEE80211_FH_CHANSET(
- ieee80211_chan2ieee(ic, ic->ic_curchan));
- *frm++ = IEEE80211_FH_CHANPAT(
- ieee80211_chan2ieee(ic, ic->ic_curchan));
- *frm++ = ni->ni_fhindex;
- } else {
- *frm++ = IEEE80211_ELEMID_DSPARMS;
- *frm++ = 1;
- *frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan);
- }
-
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- *frm++ = IEEE80211_ELEMID_IBSSPARMS;
- *frm++ = 2;
- *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */
- }
- if (ic->ic_flags & IEEE80211_F_WPA)
- frm = ieee80211_add_wpa(frm, ic);
- if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
- frm = ieee80211_add_erp(frm, ic);
- frm = ieee80211_add_xrates(frm, rs);
- /*
- * NB: legacy 11b clients do not get certain ie's.
- * The caller identifies such clients by passing
- * a token in arg to us. Could expand this to be
- * any legacy client for stuff like HT ie's.
- */
- if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
- arg != IEEE80211_SEND_LEGACY_11B) {
- frm = ieee80211_add_htcap(frm, ni);
- frm = ieee80211_add_htinfo(frm, ni);
- }
- if (ic->ic_flags & IEEE80211_F_WME)
- frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
- if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) &&
- arg != IEEE80211_SEND_LEGACY_11B) {
- frm = ieee80211_add_htcap_vendor(frm, ni);
- frm = ieee80211_add_htinfo_vendor(frm, ni);
- }
- if (ni->ni_ath_ie != NULL)
- frm = ieee80211_add_ath(frm, ni->ni_ath_flags,
- ni->ni_ath_defkeyix);
- m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
- break;
case IEEE80211_FC0_SUBTYPE_AUTH:
status = arg >> 16;
@@ -1699,10 +1809,10 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
is_shared_key = has_challenge ||
arg >= IEEE80211_AUTH_SHARED_RESPONSE ||
(arg == IEEE80211_AUTH_SHARED_REQUEST &&
- ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED);
+ bss->ni_authmode == IEEE80211_AUTH_SHARED);
m = ieee80211_getmgtframe(&frm,
- ic->ic_headroom + sizeof(struct ieee80211_frame),
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
3 * sizeof(uint16_t)
+ (has_challenge && status == IEEE80211_STATUS_SUCCESS ?
sizeof(uint16_t)+IEEE80211_CHALLENGE_LEN : 0)
@@ -1725,9 +1835,8 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
m->m_pkthdr.len = m->m_len =
4 * sizeof(uint16_t) + IEEE80211_CHALLENGE_LEN;
if (arg == IEEE80211_AUTH_SHARED_RESPONSE) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
- "[%s] request encrypt frame (%s)\n",
- ether_sprintf(ni->ni_macaddr), __func__);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
+ "request encrypt frame (%s)", __func__);
m->m_flags |= M_LINK0; /* WEP-encrypt, please */
}
} else
@@ -1739,15 +1848,14 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
else
IEEE80211_NODE_STAT(ni, tx_auth_fail);
- if (ic->ic_opmode == IEEE80211_M_STA)
+ if (vap->iv_opmode == IEEE80211_M_STA)
ieee80211_add_callback(m, ieee80211_tx_mgt_cb,
- (void *) ic->ic_state);
+ (void *) vap->iv_state);
break;
case IEEE80211_FC0_SUBTYPE_DEAUTH:
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
- "[%s] send station deauthenticate (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), arg);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
+ "send station deauthenticate (reason %d)", arg);
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
sizeof(uint16_t));
@@ -1772,11 +1880,13 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
* [tlv] ssid
* [tlv] supported rates
* [tlv] extended supported rates
- * [tlv] WME
+ * [4] power capability (optional)
+ * [28] supported channels (optional)
* [tlv] HT capabilities
+ * [tlv] WME (optional)
* [tlv] Vendor OUI HT capabilities (optional)
* [tlv] Atheros capabilities (if negotiated)
- * [tlv] user-specified ie's
+ * [tlv] AppIE's (optional)
*/
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
@@ -1786,18 +1896,24 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
+ 2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_RATE_SIZE
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ + 4
+ + 2 + 26
+ sizeof(struct ieee80211_wme_info)
- + 2*sizeof(struct ieee80211_ie_htcap) + 4
+ + sizeof(struct ieee80211_ie_htcap)
+ + 4 + sizeof(struct ieee80211_ie_htcap)
+ sizeof(struct ieee80211_ath_ie)
- + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0)
+ + (vap->iv_appie_wpa != NULL ?
+ vap->iv_appie_wpa->ie_len : 0)
+ + (vap->iv_appie_assocreq != NULL ?
+ vap->iv_appie_assocreq->ie_len : 0)
);
if (m == NULL)
senderr(ENOMEM, is_tx_nobuf);
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("wrong mode %u", ic->ic_opmode));
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA,
+ ("wrong mode %u", vap->iv_opmode));
capinfo = IEEE80211_CAPINFO_ESS;
- if (ic->ic_flags & IEEE80211_F_PRIVACY)
+ if (vap->iv_flags & IEEE80211_F_PRIVACY)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
/*
* NB: Some 11a AP's reject the request when
@@ -1810,50 +1926,63 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
(ic->ic_caps & IEEE80211_C_SHSLOT))
capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
if ((ni->ni_capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) &&
- (ic->ic_flags & IEEE80211_F_DOTH))
+ (vap->iv_flags & IEEE80211_F_DOTH))
capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT;
*(uint16_t *)frm = htole16(capinfo);
frm += 2;
- KASSERT(ic->ic_bss->ni_intval != 0,
- ("beacon interval is zero!"));
+ KASSERT(bss->ni_intval != 0, ("beacon interval is zero!"));
*(uint16_t *)frm = htole16(howmany(ic->ic_lintval,
- ic->ic_bss->ni_intval));
+ bss->ni_intval));
frm += 2;
if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
- IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid);
+ IEEE80211_ADDR_COPY(frm, bss->ni_bssid);
frm += IEEE80211_ADDR_LEN;
}
frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen);
frm = ieee80211_add_rates(frm, &ni->ni_rates);
+ if (vap->iv_flags & IEEE80211_F_WPA2) {
+ if (vap->iv_rsn_ie != NULL)
+ frm = add_ie(frm, vap->iv_rsn_ie);
+ /* XXX else complain? */
+ }
frm = ieee80211_add_xrates(frm, &ni->ni_rates);
- if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
- ni->ni_htcap_ie != NULL &&
- ni->ni_htcap_ie[0] == IEEE80211_ELEMID_HTCAP)
+ if (capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) {
+ frm = ieee80211_add_powercapability(frm,
+ ic->ic_curchan);
+ frm = ieee80211_add_supportedchannels(frm, ic);
+ }
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) &&
+ ni->ni_ies.htcap_ie != NULL &&
+ ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_HTCAP)
frm = ieee80211_add_htcap(frm, ni);
- if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
+ if (vap->iv_flags & IEEE80211_F_WPA1) {
+ if (vap->iv_wpa_ie != NULL)
+ frm = add_ie(frm, vap->iv_wpa_ie);
+ /* XXX else complain */
+ }
+ if ((ic->ic_flags & IEEE80211_F_WME) &&
+ ni->ni_ies.wme_ie != NULL)
frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
- if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
- ni->ni_htcap_ie != NULL &&
- ni->ni_htcap_ie[0] == IEEE80211_ELEMID_VENDOR)
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) &&
+ ni->ni_ies.htcap_ie != NULL &&
+ ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_VENDOR)
frm = ieee80211_add_htcap_vendor(frm, ni);
- if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS))
+ if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS))
frm = ieee80211_add_ath(frm,
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS),
- (ic->ic_flags & IEEE80211_F_WPA) == 0 &&
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS),
+ (vap->iv_flags & IEEE80211_F_WPA) == 0 &&
ni->ni_authmode != IEEE80211_AUTH_8021X &&
- ic->ic_def_txkey != IEEE80211_KEYIX_NONE ?
- ic->ic_def_txkey : 0x7fff);
- if (ic->ic_opt_ie != NULL) {
- memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len);
- frm += ic->ic_opt_ie_len;
- }
+ vap->iv_def_txkey != IEEE80211_KEYIX_NONE ?
+ vap->iv_def_txkey : 0x7fff);
+ if (vap->iv_appie_assocreq != NULL)
+ frm = add_appie(frm, vap->iv_appie_assocreq);
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
ieee80211_add_callback(m, ieee80211_tx_mgt_cb,
- (void *) ic->ic_state);
+ (void *) vap->iv_state);
break;
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
@@ -1865,10 +1994,13 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
* [2] association ID
* [tlv] supported rates
* [tlv] extended supported rates
- * [tlv] WME (if enabled and STA enabled)
- * [tlv] HT capabilities (standard or vendor OUI)
- * [tlv] HT information (standard or vendor OUI)
- * [tlv] Atheros capabilities (if enabled and STA enabled)
+ * [tlv] HT capabilities (standard, if STA enabled)
+ * [tlv] HT information (standard, if STA enabled)
+ * [tlv] WME (if configured and STA enabled)
+ * [tlv] HT capabilities (vendor OUI, if STA enabled)
+ * [tlv] HT information (vendor OUI, if STA enabled)
+ * [tlv] Atheros capabilities (if STA enabled)
+ * [tlv] AppIE's (optional)
*/
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
@@ -1877,15 +2009,17 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
+ sizeof(uint16_t)
+ 2 + IEEE80211_RATE_SIZE
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
- + sizeof(struct ieee80211_wme_param)
+ sizeof(struct ieee80211_ie_htcap) + 4
+ sizeof(struct ieee80211_ie_htinfo) + 4
+ + sizeof(struct ieee80211_wme_param)
+ sizeof(struct ieee80211_ath_ie)
+ + (vap->iv_appie_assocresp != NULL ?
+ vap->iv_appie_assocresp->ie_len : 0)
);
if (m == NULL)
senderr(ENOMEM, is_tx_nobuf);
- capinfo = getcapinfo(ic, ic->ic_curchan);
+ capinfo = getcapinfo(vap, bss->ni_chan);
*(uint16_t *)frm = htole16(capinfo);
frm += 2;
@@ -1906,23 +2040,25 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
frm = ieee80211_add_htcap(frm, ni);
frm = ieee80211_add_htinfo(frm, ni);
}
- if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
+ if ((vap->iv_flags & IEEE80211_F_WME) &&
+ ni->ni_ies.wme_ie != NULL)
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
if ((ni->ni_flags & HTFLAGS) == HTFLAGS) {
frm = ieee80211_add_htcap_vendor(frm, ni);
frm = ieee80211_add_htinfo_vendor(frm, ni);
}
- if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS))
+ if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS))
frm = ieee80211_add_ath(frm,
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS),
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS),
ni->ni_ath_defkeyix);
+ if (vap->iv_appie_assocresp != NULL)
+ frm = add_appie(frm, vap->iv_appie_assocresp);
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
break;
case IEEE80211_FC0_SUBTYPE_DISASSOC:
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] send station disassociate (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), arg);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
+ "send station disassociate (reason %d)", arg);
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
sizeof(uint16_t));
@@ -1936,17 +2072,13 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
break;
default:
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "[%s] invalid mgmt frame type %u\n",
- ether_sprintf(ni->ni_macaddr), type);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni,
+ "invalid mgmt frame type %u", type);
senderr(EINVAL, is_tx_unknownmgt);
/* NOTREACHED */
}
- ret = ieee80211_mgmt_output(ic, ni, m, type);
- if (ret != 0)
- goto bad;
- return 0;
+ return ieee80211_mgmt_output(ni, m, type);
bad:
ieee80211_free_node(ni);
return ret;
@@ -1954,19 +2086,283 @@ bad:
#undef HTFLAGS
}
+/*
+ * Return an mbuf with a probe response frame in it.
+ * Space is left to prepend and 802.11 header at the
+ * front but it's left to the caller to fill in.
+ */
+struct mbuf *
+ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy)
+{
+ struct ieee80211vap *vap = bss->ni_vap;
+ struct ieee80211com *ic = bss->ni_ic;
+ const struct ieee80211_rateset *rs;
+ struct mbuf *m;
+ uint16_t capinfo;
+ uint8_t *frm;
+
+ /*
+ * probe response frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] cabability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] parameter set (FH/DS)
+ * [tlv] parameter set (IBSS)
+ * [tlv] country (optional)
+ * [tlv] RSN (optional)
+ * [3] power control (optional)
+ * [5] channel switch announcement (CSA) (optional)
+ * [tlv] extended rate phy (ERP)
+ * [tlv] extended supported rates
+ * [tlv] HT capabilities
+ * [tlv] HT information
+ * [tlv] WPA (optional)
+ * [tlv] WME (optional)
+ * [tlv] Vendor OUI HT capabilities (optional)
+ * [tlv] Vendor OUI HT information (optional)
+ * [tlv] Atheros capabilities
+ * [tlv] AppIE's (optional)
+ */
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
+ 8
+ + sizeof(uint16_t)
+ + sizeof(uint16_t)
+ + 2 + IEEE80211_NWID_LEN
+ + 2 + IEEE80211_RATE_SIZE
+ + 7 /* max(7,3) */
+ + IEEE80211_COUNTRY_MAX_SIZE
+ + sizeof(struct ieee80211_ie_wpa)
+ + 3
+ + sizeof(struct ieee80211_csa_ie)
+ + 3
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ + sizeof(struct ieee80211_ie_htcap)
+ + sizeof(struct ieee80211_ie_htinfo)
+ + sizeof(struct ieee80211_ie_wpa)
+ + sizeof(struct ieee80211_wme_param)
+ + 4 + sizeof(struct ieee80211_ie_htcap)
+ + 4 + sizeof(struct ieee80211_ie_htinfo)
+ + sizeof(struct ieee80211_ath_ie)
+ + (vap->iv_appie_proberesp != NULL ?
+ vap->iv_appie_proberesp->ie_len : 0)
+ );
+ if (m == NULL) {
+ vap->iv_stats.is_tx_nobuf++;
+ return NULL;
+ }
+
+ memset(frm, 0, 8); /* timestamp should be filled later */
+ frm += 8;
+ *(uint16_t *)frm = htole16(bss->ni_intval);
+ frm += 2;
+ capinfo = getcapinfo(vap, bss->ni_chan);
+ *(uint16_t *)frm = htole16(capinfo);
+ frm += 2;
+
+ frm = ieee80211_add_ssid(frm, bss->ni_essid, bss->ni_esslen);
+ rs = ieee80211_get_suprates(ic, bss->ni_chan);
+ frm = ieee80211_add_rates(frm, rs);
+
+ if (IEEE80211_IS_CHAN_FHSS(bss->ni_chan)) {
+ *frm++ = IEEE80211_ELEMID_FHPARMS;
+ *frm++ = 5;
+ *frm++ = bss->ni_fhdwell & 0x00ff;
+ *frm++ = (bss->ni_fhdwell >> 8) & 0x00ff;
+ *frm++ = IEEE80211_FH_CHANSET(
+ ieee80211_chan2ieee(ic, bss->ni_chan));
+ *frm++ = IEEE80211_FH_CHANPAT(
+ ieee80211_chan2ieee(ic, bss->ni_chan));
+ *frm++ = bss->ni_fhindex;
+ } else {
+ *frm++ = IEEE80211_ELEMID_DSPARMS;
+ *frm++ = 1;
+ *frm++ = ieee80211_chan2ieee(ic, bss->ni_chan);
+ }
+
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
+ *frm++ = IEEE80211_ELEMID_IBSSPARMS;
+ *frm++ = 2;
+ *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */
+ }
+ if ((vap->iv_flags & IEEE80211_F_DOTH) ||
+ (vap->iv_flags_ext & IEEE80211_FEXT_DOTD))
+ frm = ieee80211_add_countryie(frm, ic);
+ if (vap->iv_flags & IEEE80211_F_WPA2) {
+ if (vap->iv_rsn_ie != NULL)
+ frm = add_ie(frm, vap->iv_rsn_ie);
+ /* XXX else complain? */
+ }
+ if (vap->iv_flags & IEEE80211_F_DOTH) {
+ if (IEEE80211_IS_CHAN_5GHZ(bss->ni_chan))
+ frm = ieee80211_add_powerconstraint(frm, vap);
+ if (ic->ic_flags & IEEE80211_F_CSAPENDING)
+ frm = ieee80211_add_csa(frm, vap);
+ }
+ if (IEEE80211_IS_CHAN_ANYG(bss->ni_chan))
+ frm = ieee80211_add_erp(frm, ic);
+ frm = ieee80211_add_xrates(frm, rs);
+ /*
+ * NB: legacy 11b clients do not get certain ie's.
+ * The caller identifies such clients by passing
+ * a token in legacy to us. Could expand this to be
+ * any legacy client for stuff like HT ie's.
+ */
+ if (IEEE80211_IS_CHAN_HT(bss->ni_chan) &&
+ legacy != IEEE80211_SEND_LEGACY_11B) {
+ frm = ieee80211_add_htcap(frm, bss);
+ frm = ieee80211_add_htinfo(frm, bss);
+ }
+ if (vap->iv_flags & IEEE80211_F_WPA1) {
+ if (vap->iv_wpa_ie != NULL)
+ frm = add_ie(frm, vap->iv_wpa_ie);
+ /* XXX else complain? */
+ }
+ if (vap->iv_flags & IEEE80211_F_WME)
+ frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
+ if (IEEE80211_IS_CHAN_HT(bss->ni_chan) &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) &&
+ legacy != IEEE80211_SEND_LEGACY_11B) {
+ frm = ieee80211_add_htcap_vendor(frm, bss);
+ frm = ieee80211_add_htinfo_vendor(frm, bss);
+ }
+ if (bss->ni_ies.ath_ie != NULL && legacy != IEEE80211_SEND_LEGACY_11B)
+ frm = ieee80211_add_ath(frm, bss->ni_ath_flags,
+ bss->ni_ath_defkeyix);
+ if (vap->iv_appie_proberesp != NULL)
+ frm = add_appie(frm, vap->iv_appie_proberesp);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+
+ return m;
+}
+
+/*
+ * Send a probe response frame to the specified mac address.
+ * This does not go through the normal mgt frame api so we
+ * can specify the destination address and re-use the bss node
+ * for the sta reference.
+ */
+int
+ieee80211_send_proberesp(struct ieee80211vap *vap,
+ const uint8_t da[IEEE80211_ADDR_LEN], int legacy)
+{
+ struct ieee80211_node *bss = vap->iv_bss;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_frame *wh;
+ struct mbuf *m;
+
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, bss,
+ "block %s frame in CAC state", "probe response");
+ vap->iv_stats.is_tx_badstate++;
+ return EIO; /* XXX */
+ }
+
+ /*
+ * Hold a reference on the node so it doesn't go away until after
+ * the xmit is complete all the way in the driver. On error we
+ * will remove our reference.
+ */
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+ "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
+ __func__, __LINE__, bss, ether_sprintf(bss->ni_macaddr),
+ ieee80211_node_refcnt(bss)+1);
+ ieee80211_ref_node(bss);
+
+ m = ieee80211_alloc_proberesp(bss, legacy);
+ if (m == NULL) {
+ ieee80211_free_node(bss);
+ return ENOMEM;
+ }
+
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+ KASSERT(m != NULL, ("no room for header"));
+
+ wh = mtod(m, struct ieee80211_frame *);
+ ieee80211_send_setup(bss, wh,
+ IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP,
+ vap->iv_myaddr, da, bss->ni_bssid);
+ /* XXX power management? */
+
+ M_WME_SETAC(m, WME_AC_BE);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
+ "send probe resp on channel %u to %s%s\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(da),
+ legacy ? " <legacy>" : "");
+ IEEE80211_NODE_STAT(bss, tx_mgmt);
+
+ return ic->ic_raw_xmit(bss, m, NULL);
+}
+
+/*
+ * Allocate and build a RTS (Request To Send) control frame.
+ */
+struct mbuf *
+ieee80211_alloc_rts(struct ieee80211com *ic,
+ const uint8_t ra[IEEE80211_ADDR_LEN],
+ const uint8_t ta[IEEE80211_ADDR_LEN],
+ uint16_t dur)
+{
+ struct ieee80211_frame_rts *rts;
+ struct mbuf *m;
+
+ /* XXX honor ic_headroom */
+ m = m_gethdr(M_DONTWAIT, MT_DATA);
+ if (m != NULL) {
+ rts = mtod(m, struct ieee80211_frame_rts *);
+ rts->i_fc[0] = IEEE80211_FC0_VERSION_0 |
+ IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_RTS;
+ rts->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+ *(u_int16_t *)rts->i_dur = htole16(dur);
+ IEEE80211_ADDR_COPY(rts->i_ra, ra);
+ IEEE80211_ADDR_COPY(rts->i_ta, ta);
+
+ m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_rts);
+ }
+ return m;
+}
+
+/*
+ * Allocate and build a CTS (Clear To Send) control frame.
+ */
+struct mbuf *
+ieee80211_alloc_cts(struct ieee80211com *ic,
+ const uint8_t ra[IEEE80211_ADDR_LEN], uint16_t dur)
+{
+ struct ieee80211_frame_cts *cts;
+ struct mbuf *m;
+
+ /* XXX honor ic_headroom */
+ m = m_gethdr(M_DONTWAIT, MT_DATA);
+ if (m != NULL) {
+ cts = mtod(m, struct ieee80211_frame_cts *);
+ cts->i_fc[0] = IEEE80211_FC0_VERSION_0 |
+ IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_CTS;
+ cts->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+ *(u_int16_t *)cts->i_dur = htole16(dur);
+ IEEE80211_ADDR_COPY(cts->i_ra, ra);
+
+ m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_cts);
+ }
+ return m;
+}
+
static void
ieee80211_tx_mgt_timeout(void *arg)
{
struct ieee80211_node *ni = arg;
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
- if (ic->ic_state != IEEE80211_S_INIT &&
- (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ if (vap->iv_state != IEEE80211_S_INIT &&
+ (vap->iv_ic->ic_flags & IEEE80211_F_SCAN) == 0) {
/*
* NB: it's safe to specify a timeout as the reason here;
* it'll only be used in the right state.
*/
- ieee80211_new_state(ic, IEEE80211_S_SCAN,
+ ieee80211_new_state(vap, IEEE80211_S_SCAN,
IEEE80211_SCAN_FAIL_TIMEOUT);
}
}
@@ -1974,7 +2370,7 @@ ieee80211_tx_mgt_timeout(void *arg)
static void
ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
enum ieee80211_state ostate = (enum ieee80211_state) arg;
/*
@@ -1988,27 +2384,20 @@ ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status)
*
* XXX what happens if !acked but response shows up before callback?
*/
- if (ic->ic_state == ostate)
- callout_reset(&ic->ic_mgtsend,
+ if (vap->iv_state == ostate)
+ callout_reset(&vap->iv_mgtsend,
status == 0 ? IEEE80211_TRANS_WAIT*hz : 0,
ieee80211_tx_mgt_timeout, ni);
}
-/*
- * Allocate a beacon frame and fillin the appropriate bits.
- */
-struct mbuf *
-ieee80211_beacon_alloc(struct ieee80211_node *ni,
- struct ieee80211_beacon_offsets *bo)
+static void
+ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm,
+ struct ieee80211_beacon_offsets *bo, struct ieee80211_node *ni)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
- struct ieee80211_frame *wh;
- struct mbuf *m;
- int pktlen;
- uint8_t *frm;
+ struct ieee80211_rateset *rs = &ni->ni_rates;
uint16_t capinfo;
- struct ieee80211_rateset *rs;
/*
* beacon frame format
@@ -2018,46 +2407,23 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
* [tlv] ssid
* [tlv] supported rates
* [3] parameter set (DS)
+ * [8] CF parameter set (optional)
* [tlv] parameter set (IBSS/TIM)
- * [tlv] country code
+ * [tlv] country (optional)
+ * [tlv] RSN parameters
+ * [3] power control (optional)
+ * [5] channel switch announcement (CSA) (optional)
* [tlv] extended rate phy (ERP)
* [tlv] extended supported rates
- * [tlv] WME parameters
- * [tlv] WPA/RSN parameters
* [tlv] HT capabilities
* [tlv] HT information
+ * XXX Vendor-specific OIDs (e.g. Atheros)
+ * [tlv] WPA parameters
+ * [tlv] WME parameters
* [tlv] Vendor OUI HT capabilities (optional)
* [tlv] Vendor OUI HT information (optional)
- * XXX Vendor-specific OIDs (e.g. Atheros)
- * NB: we allocate the max space required for the TIM bitmap.
+ * [tlv] application data (optional)
*/
- rs = &ni->ni_rates;
- pktlen = 8 /* time stamp */
- + sizeof(uint16_t) /* beacon interval */
- + sizeof(uint16_t) /* capabilities */
- + 2 + ni->ni_esslen /* ssid */
- + 2 + IEEE80211_RATE_SIZE /* supported rates */
- + 2 + 1 /* DS parameters */
- + 2 + 4 + ic->ic_tim_len /* DTIM/IBSSPARMS */
- + sizeof(struct ieee80211_country_ie) /* country code */
- + 2 + 1 /* ERP */
- + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
- + (ic->ic_caps & IEEE80211_C_WME ? /* WME */
- sizeof(struct ieee80211_wme_param) : 0)
- + (ic->ic_caps & IEEE80211_C_WPA ? /* WPA 1+2 */
- 2*sizeof(struct ieee80211_ie_wpa) : 0)
- /* XXX conditional? */
- + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */
- + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */
- ;
- m = ieee80211_getmgtframe(&frm,
- ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen);
- if (m == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "%s: cannot get buf; size %u\n", __func__, pktlen);
- ic->ic_stats.is_tx_nobuf++;
- return NULL;
- }
memset(bo, 0, sizeof(*bo));
@@ -2065,68 +2431,169 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
frm += 8;
*(uint16_t *)frm = htole16(ni->ni_intval);
frm += 2;
- capinfo = getcapinfo(ic, ni->ni_chan);
+ capinfo = getcapinfo(vap, ni->ni_chan);
bo->bo_caps = (uint16_t *)frm;
*(uint16_t *)frm = htole16(capinfo);
frm += 2;
*frm++ = IEEE80211_ELEMID_SSID;
- if ((ic->ic_flags & IEEE80211_F_HIDESSID) == 0) {
+ if ((vap->iv_flags & IEEE80211_F_HIDESSID) == 0) {
*frm++ = ni->ni_esslen;
memcpy(frm, ni->ni_essid, ni->ni_esslen);
frm += ni->ni_esslen;
} else
*frm++ = 0;
frm = ieee80211_add_rates(frm, rs);
- if (!IEEE80211_IS_CHAN_FHSS(ic->ic_bsschan)) {
+ if (!IEEE80211_IS_CHAN_FHSS(ni->ni_chan)) {
*frm++ = IEEE80211_ELEMID_DSPARMS;
*frm++ = 1;
- *frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan);
+ *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
+ }
+ if (ic->ic_flags & IEEE80211_F_PCF) {
+ bo->bo_cfp = frm;
+ frm = ieee80211_add_cfparms(frm, ic);
}
bo->bo_tim = frm;
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
*frm++ = IEEE80211_ELEMID_IBSSPARMS;
*frm++ = 2;
*frm++ = 0; *frm++ = 0; /* TODO: ATIM window */
bo->bo_tim_len = 0;
- } else if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ } else if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) frm;
tie->tim_ie = IEEE80211_ELEMID_TIM;
tie->tim_len = 4; /* length */
tie->tim_count = 0; /* DTIM count */
- tie->tim_period = ic->ic_dtim_period; /* DTIM period */
+ tie->tim_period = vap->iv_dtim_period; /* DTIM period */
tie->tim_bitctl = 0; /* bitmap control */
tie->tim_bitmap[0] = 0; /* Partial Virtual Bitmap */
frm += sizeof(struct ieee80211_tim_ie);
bo->bo_tim_len = 1;
}
bo->bo_tim_trailer = frm;
- if (ic->ic_flags & IEEE80211_F_DOTH)
- frm = ieee80211_add_countryie(frm, ic,
- ic->ic_countrycode, ic->ic_location);
- if (ic->ic_flags & IEEE80211_F_WPA)
- frm = ieee80211_add_wpa(frm, ic);
- if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) {
+ if ((vap->iv_flags & IEEE80211_F_DOTH) ||
+ (vap->iv_flags_ext & IEEE80211_FEXT_DOTD))
+ frm = ieee80211_add_countryie(frm, ic);
+ if (vap->iv_flags & IEEE80211_F_WPA2) {
+ if (vap->iv_rsn_ie != NULL)
+ frm = add_ie(frm, vap->iv_rsn_ie);
+ /* XXX else complain */
+ }
+ if (vap->iv_flags & IEEE80211_F_DOTH) {
+ if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan))
+ frm = ieee80211_add_powerconstraint(frm, vap);
+ bo->bo_csa = frm;
+ if (ic->ic_flags & IEEE80211_F_CSAPENDING)
+ frm = ieee80211_add_csa(frm, vap);
+ } else
+ bo->bo_csa = frm;
+ if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) {
bo->bo_erp = frm;
frm = ieee80211_add_erp(frm, ic);
}
frm = ieee80211_add_xrates(frm, rs);
- if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) {
+ if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
frm = ieee80211_add_htcap(frm, ni);
bo->bo_htinfo = frm;
frm = ieee80211_add_htinfo(frm, ni);
}
- if (ic->ic_flags & IEEE80211_F_WME) {
+ if (vap->iv_flags & IEEE80211_F_WPA1) {
+ if (vap->iv_wpa_ie != NULL)
+ frm = add_ie(frm, vap->iv_wpa_ie);
+ /* XXX else complain */
+ }
+ if (vap->iv_flags & IEEE80211_F_WME) {
bo->bo_wme = frm;
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
}
- if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT)) {
+ if (IEEE80211_IS_CHAN_HT(ni->ni_chan) &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT)) {
frm = ieee80211_add_htcap_vendor(frm, ni);
frm = ieee80211_add_htinfo_vendor(frm, ni);
}
+ if (vap->iv_appie_beacon != NULL) {
+ bo->bo_appie = frm;
+ bo->bo_appie_len = vap->iv_appie_beacon->ie_len;
+ frm = add_appie(frm, vap->iv_appie_beacon);
+ }
bo->bo_tim_trailer_len = frm - bo->bo_tim_trailer;
+ bo->bo_csa_trailer_len = frm - bo->bo_csa;
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+}
+
+/*
+ * Allocate a beacon frame and fillin the appropriate bits.
+ */
+struct mbuf *
+ieee80211_beacon_alloc(struct ieee80211_node *ni,
+ struct ieee80211_beacon_offsets *bo)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_frame *wh;
+ struct mbuf *m;
+ int pktlen;
+ uint8_t *frm;
+
+ /*
+ * beacon frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] cabability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [3] parameter set (DS)
+ * [8] CF parameter set (optional)
+ * [tlv] parameter set (IBSS/TIM)
+ * [tlv] country (optional)
+ * [3] power control (optional)
+ * [5] channel switch announcement (CSA) (optional)
+ * [tlv] extended rate phy (ERP)
+ * [tlv] extended supported rates
+ * [tlv] RSN parameters
+ * [tlv] HT capabilities
+ * [tlv] HT information
+ * [tlv] Vendor OUI HT capabilities (optional)
+ * [tlv] Vendor OUI HT information (optional)
+ * XXX Vendor-specific OIDs (e.g. Atheros)
+ * [tlv] WPA parameters
+ * [tlv] WME parameters
+ * [tlv] application data (optional)
+ * NB: we allocate the max space required for the TIM bitmap.
+ * XXX how big is this?
+ */
+ pktlen = 8 /* time stamp */
+ + sizeof(uint16_t) /* beacon interval */
+ + sizeof(uint16_t) /* capabilities */
+ + 2 + ni->ni_esslen /* ssid */
+ + 2 + IEEE80211_RATE_SIZE /* supported rates */
+ + 2 + 1 /* DS parameters */
+ + 2 + 6 /* CF parameters */
+ + 2 + 4 + vap->iv_tim_len /* DTIM/IBSSPARMS */
+ + IEEE80211_COUNTRY_MAX_SIZE /* country */
+ + 2 + 1 /* power control */
+ + sizeof(struct ieee80211_csa_ie) /* CSA */
+ + 2 + 1 /* ERP */
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ + (vap->iv_caps & IEEE80211_C_WPA ? /* WPA 1+2 */
+ 2*sizeof(struct ieee80211_ie_wpa) : 0)
+ /* XXX conditional? */
+ + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */
+ + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */
+ + (vap->iv_caps & IEEE80211_C_WME ? /* WME */
+ sizeof(struct ieee80211_wme_param) : 0)
+ + IEEE80211_MAX_APPIE
+ ;
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen);
+ if (m == NULL) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
+ "%s: cannot get buf; size %u\n", __func__, pktlen);
+ vap->iv_stats.is_tx_nobuf++;
+ return NULL;
+ }
+ ieee80211_beacon_construct(m, frm, bo, ni);
M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
KASSERT(m != NULL, ("no space for 802.11 header?"));
@@ -2136,7 +2603,7 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
*(uint16_t *)wh->i_dur = 0;
IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
- IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
*(uint16_t *)wh->i_seq = 0;
@@ -2150,16 +2617,46 @@ int
ieee80211_beacon_update(struct ieee80211_node *ni,
struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
int len_changed = 0;
uint16_t capinfo;
- IEEE80211_BEACON_LOCK(ic);
+ IEEE80211_LOCK(ic);
+ /*
+ * Handle 11h channel change when we've reached the count.
+ * We must recalculate the beacon frame contents to account
+ * for the new channel. Note we do this only for the first
+ * vap that reaches this point; subsequent vaps just update
+ * their beacon state to reflect the recalculated channel.
+ */
+ if (isset(bo->bo_flags, IEEE80211_BEACON_CSA) &&
+ vap->iv_csa_count == ic->ic_csa_count) {
+ vap->iv_csa_count = 0;
+ /*
+ * Effect channel change before reconstructing the beacon
+ * frame contents as many places reference ni_chan.
+ */
+ if (ic->ic_csa_newchan != NULL)
+ ieee80211_csa_completeswitch(ic);
+ /*
+ * NB: ieee80211_beacon_construct clears all pending
+ * updates in bo_flags so we don't need to explicitly
+ * clear IEEE80211_BEACON_CSA.
+ */
+ ieee80211_beacon_construct(m,
+ mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), bo, ni);
+
+ /* XXX do WME aggressive mode processing? */
+ IEEE80211_UNLOCK(ic);
+ return 1; /* just assume length changed */
+ }
+
/* XXX faster to recalculate entirely or just changes? */
- capinfo = getcapinfo(ic, ni->ni_chan);
+ capinfo = getcapinfo(vap, ni->ni_chan);
*bo->bo_caps = htole16(capinfo);
- if (ic->ic_flags & IEEE80211_F_WME) {
+ if (vap->iv_flags & IEEE80211_F_WME) {
struct ieee80211_wme_state *wme = &ic->ic_wme;
/*
@@ -2172,11 +2669,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
if (wme->wme_flags & WME_F_AGGRMODE) {
if (wme->wme_hipri_traffic >
wme->wme_hipri_switch_thresh) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: traffic %u, disable aggressive mode\n",
__func__, wme->wme_hipri_traffic);
wme->wme_flags &= ~WME_F_AGGRMODE;
- ieee80211_wme_updateparams_locked(ic);
+ ieee80211_wme_updateparams_locked(vap);
wme->wme_hipri_traffic =
wme->wme_hipri_switch_hysteresis;
} else
@@ -2184,11 +2681,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
} else {
if (wme->wme_hipri_traffic <=
wme->wme_hipri_switch_thresh) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: traffic %u, enable aggressive mode\n",
__func__, wme->wme_hipri_traffic);
wme->wme_flags |= WME_F_AGGRMODE;
- ieee80211_wme_updateparams_locked(ic);
+ ieee80211_wme_updateparams_locked(vap);
wme->wme_hipri_traffic = 0;
} else
wme->wme_hipri_traffic =
@@ -2200,12 +2697,12 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
}
}
- if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) {
- ieee80211_ht_update_beacon(ic, bo);
+ if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) {
+ ieee80211_ht_update_beacon(vap, bo);
clrbit(bo->bo_flags, IEEE80211_BEACON_HTINFO);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/
struct ieee80211_tim_ie *tie =
(struct ieee80211_tim_ie *) bo->bo_tim;
if (isset(bo->bo_flags, IEEE80211_BEACON_TIM)) {
@@ -2217,7 +2714,7 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
* data to make room. Note that we know there is
* contiguous space because ieee80211_beacon_allocate
* insures there is space in the mbuf to write a
- * maximal-size virtual bitmap (based on ic_max_aid).
+ * maximal-size virtual bitmap (based on iv_max_aid).
*/
/*
* Calculate the bitmap size and offset, copy any
@@ -2226,16 +2723,16 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
* Note that the tim bitmap must contain at least
* one byte and any offset must be even.
*/
- if (ic->ic_ps_pending != 0) {
+ if (vap->iv_ps_pending != 0) {
timoff = 128; /* impossibly large */
- for (i = 0; i < ic->ic_tim_len; i++)
- if (ic->ic_tim_bitmap[i]) {
+ for (i = 0; i < vap->iv_tim_len; i++)
+ if (vap->iv_tim_bitmap[i]) {
timoff = i &~ 1;
break;
}
KASSERT(timoff != 128, ("tim bitmap empty!"));
- for (i = ic->ic_tim_len-1; i >= timoff; i--)
- if (ic->ic_tim_bitmap[i])
+ for (i = vap->iv_tim_len-1; i >= timoff; i--)
+ if (vap->iv_tim_bitmap[i])
break;
timlen = 1 + (i - timoff);
} else {
@@ -2250,9 +2747,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
bo->bo_tim_trailer+adjust,
bo->bo_tim_trailer_len);
bo->bo_tim_trailer += adjust;
- bo->bo_wme += adjust;
bo->bo_erp += adjust;
bo->bo_htinfo += adjust;
+ bo->bo_appie += adjust;
+ bo->bo_wme += adjust;
+ bo->bo_csa += adjust;
bo->bo_tim_len = timlen;
/* update information element */
@@ -2260,14 +2759,14 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
tie->tim_bitctl = timoff;
len_changed = 1;
}
- memcpy(tie->tim_bitmap, ic->ic_tim_bitmap + timoff,
+ memcpy(tie->tim_bitmap, vap->iv_tim_bitmap + timoff,
bo->bo_tim_len);
clrbit(bo->bo_flags, IEEE80211_BEACON_TIM);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER,
"%s: TIM updated, pending %u, off %u, len %u\n",
- __func__, ic->ic_ps_pending, timoff, timlen);
+ __func__, vap->iv_ps_pending, timoff, timlen);
}
/* count down DTIM period */
if (tie->tim_count == 0)
@@ -2279,6 +2778,33 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
tie->tim_bitctl |= 1;
else
tie->tim_bitctl &= ~1;
+ if (isset(bo->bo_flags, IEEE80211_BEACON_CSA)) {
+ struct ieee80211_csa_ie *csa =
+ (struct ieee80211_csa_ie *) bo->bo_csa;
+
+ /*
+ * Insert or update CSA ie. If we're just starting
+ * to count down to the channel switch then we need
+ * to insert the CSA ie. Otherwise we just need to
+ * drop the count. The actual change happens above
+ * when the vap's count reaches the target count.
+ */
+ if (vap->iv_csa_count == 0) {
+ memmove(&csa[1], csa, bo->bo_csa_trailer_len);
+ bo->bo_erp += sizeof(*csa);
+ bo->bo_wme += sizeof(*csa);
+ bo->bo_appie += sizeof(*csa);
+ bo->bo_csa_trailer_len += sizeof(*csa);
+ bo->bo_tim_trailer_len += sizeof(*csa);
+ m->m_len += sizeof(*csa);
+ m->m_pkthdr.len += sizeof(*csa);
+
+ ieee80211_add_csa(bo->bo_csa, vap);
+ } else
+ csa->csa_count--;
+ vap->iv_csa_count++;
+ /* NB: don't clear IEEE80211_BEACON_CSA */
+ }
if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) {
/*
* ERP element needs updating.
@@ -2287,7 +2813,31 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
clrbit(bo->bo_flags, IEEE80211_BEACON_ERP);
}
}
- IEEE80211_BEACON_UNLOCK(ic);
+ if (isset(bo->bo_flags, IEEE80211_BEACON_APPIE)) {
+ const struct ieee80211_appie *aie = vap->iv_appie_beacon;
+ int aielen;
+ uint8_t *frm;
+
+ aielen = 0;
+ if (aie != NULL)
+ aielen += aie->ie_len;
+ if (aielen != bo->bo_appie_len) {
+ /* copy up/down trailer */
+ int adjust = aielen - bo->bo_appie_len;
+ ovbcopy(bo->bo_tim_trailer, bo->bo_tim_trailer+adjust,
+ bo->bo_tim_trailer_len);
+ bo->bo_tim_trailer += adjust;
+ bo->bo_appie += adjust;
+ bo->bo_appie_len = aielen;
+
+ len_changed = 1;
+ }
+ frm = bo->bo_appie;
+ if (aie != NULL)
+ frm = add_appie(frm, aie);
+ clrbit(bo->bo_flags, IEEE80211_BEACON_APPIE);
+ }
+ IEEE80211_UNLOCK(ic);
return len_changed;
}
diff --git a/sys/net80211/ieee80211_phy.c b/sys/net80211/ieee80211_phy.c
new file mode 100644
index 0000000..e1c847f
--- /dev/null
+++ b/sys/net80211/ieee80211_phy.c
@@ -0,0 +1,472 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * IEEE 802.11 PHY-related support.
+ */
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_phy.h>
+
+#ifdef notyet
+struct ieee80211_ds_plcp_hdr {
+ uint8_t i_signal;
+ uint8_t i_service;
+ uint16_t i_length;
+ uint16_t i_crc;
+} __packed;
+
+#endif /* notyet */
+
+/* shorthands to compact tables for readability */
+#define OFDM IEEE80211_T_OFDM
+#define CCK IEEE80211_T_CCK
+#define TURBO IEEE80211_T_TURBO
+#define PBCC (IEEE80211_T_HT+1) /* XXX */
+
+static struct ieee80211_rate_table ieee80211_11b_table = {
+ 4, /* number of rates, XXX no PBCC */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 1 Mb */ { CCK, 1000, 0x00, (0x80| 2), 0 },
+/* 2 Mb */ { CCK, 2000, 0x04, (0x80| 4), 1 },
+/* 5.5 Mb */ { CCK, 5500, 0x04, (0x80|11), 1 },
+/* 11 Mb */ { CCK, 11000, 0x04, (0x80|22), 1 },
+/* 22 Mb */ { PBCC, 22000, 0x04, 44, 3 }
+ },
+};
+
+
+static struct ieee80211_rate_table ieee80211_11g_table = {
+ 12, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 1 Mb */ { CCK, 1000, 0x00, (0x80| 2), 0 },
+/* 2 Mb */ { CCK, 2000, 0x04, (0x80| 4), 1 },
+/* 5.5 Mb */ { CCK, 5500, 0x04, (0x80|11), 2 },
+/* 11 Mb */ { CCK, 11000, 0x04, (0x80|22), 3 },
+/* 6 Mb */ { OFDM, 6000, 0x00, 12, 4 },
+/* 9 Mb */ { OFDM, 9000, 0x00, 18, 4 },
+/* 12 Mb */ { OFDM, 12000, 0x00, 24, 6 },
+/* 18 Mb */ { OFDM, 18000, 0x00, 36, 6 },
+/* 24 Mb */ { OFDM, 24000, 0x00, 48, 8 },
+/* 36 Mb */ { OFDM, 36000, 0x00, 72, 8 },
+/* 48 Mb */ { OFDM, 48000, 0x00, 96, 8 },
+/* 54 Mb */ { OFDM, 54000, 0x00, 108, 8 }
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_11a_table = {
+ 8, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 6 Mb */ { OFDM, 6000, 0x00, (0x80|12), 0 },
+/* 9 Mb */ { OFDM, 9000, 0x00, 18, 0 },
+/* 12 Mb */ { OFDM, 12000, 0x00, (0x80|24), 2 },
+/* 18 Mb */ { OFDM, 18000, 0x00, 36, 2 },
+/* 24 Mb */ { OFDM, 24000, 0x00, (0x80|48), 4 },
+/* 36 Mb */ { OFDM, 36000, 0x00, 72, 4 },
+/* 48 Mb */ { OFDM, 48000, 0x00, 96, 4 },
+/* 54 Mb */ { OFDM, 54000, 0x00, 108, 4 }
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_half_table = {
+ 8, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 6 Mb */ { OFDM, 3000, 0x00, (0x80| 6), 0 },
+/* 9 Mb */ { OFDM, 4500, 0x00, 9, 0 },
+/* 12 Mb */ { OFDM, 6000, 0x00, (0x80|12), 2 },
+/* 18 Mb */ { OFDM, 9000, 0x00, 18, 2 },
+/* 24 Mb */ { OFDM, 12000, 0x00, (0x80|24), 4 },
+/* 36 Mb */ { OFDM, 18000, 0x00, 36, 4 },
+/* 48 Mb */ { OFDM, 24000, 0x00, 48, 4 },
+/* 54 Mb */ { OFDM, 27000, 0x00, 54, 4 }
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_quarter_table = {
+ 8, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 6 Mb */ { OFDM, 1500, 0x00, (0x80| 3), 0 },
+/* 9 Mb */ { OFDM, 2250, 0x00, 4, 0 },
+/* 12 Mb */ { OFDM, 3000, 0x00, (0x80| 6), 2 },
+/* 18 Mb */ { OFDM, 4500, 0x00, 9, 2 },
+/* 24 Mb */ { OFDM, 6000, 0x00, (0x80|12), 4 },
+/* 36 Mb */ { OFDM, 9000, 0x00, 18, 4 },
+/* 48 Mb */ { OFDM, 12000, 0x00, 24, 4 },
+/* 54 Mb */ { OFDM, 13500, 0x00, 27, 4 }
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_turbog_table = {
+ 7, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 6 Mb */ { TURBO, 6000, 0x00, (0x80|12), 0 },
+/* 12 Mb */ { TURBO, 12000, 0x00, (0x80|24), 1 },
+/* 18 Mb */ { TURBO, 18000, 0x00, 36, 1 },
+/* 24 Mb */ { TURBO, 24000, 0x00, (0x80|48), 3 },
+/* 36 Mb */ { TURBO, 36000, 0x00, 72, 3 },
+/* 48 Mb */ { TURBO, 48000, 0x00, 96, 3 },
+/* 54 Mb */ { TURBO, 54000, 0x00, 108, 3 }
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_turboa_table = {
+ 8, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 6 Mb */ { TURBO, 6000, 0x00, (0x80|12), 0 },
+/* 9 Mb */ { TURBO, 9000, 0x00, 18, 0 },
+/* 12 Mb */ { TURBO, 12000, 0x00, (0x80|24), 2 },
+/* 18 Mb */ { TURBO, 18000, 0x00, 36, 2 },
+/* 24 Mb */ { TURBO, 24000, 0x00, (0x80|48), 4 },
+/* 36 Mb */ { TURBO, 36000, 0x00, 72, 4 },
+/* 48 Mb */ { TURBO, 48000, 0x00, 96, 4 },
+/* 54 Mb */ { TURBO, 54000, 0x00, 108, 4 }
+ },
+};
+
+#undef OFDM
+#undef CCK
+#undef TURBO
+#undef XR
+
+/*
+ * Setup a rate table's reverse lookup table and fill in
+ * ack durations. The reverse lookup tables are assumed
+ * to be initialized to zero (or at least the first entry).
+ * We use this as a key that indicates whether or not
+ * we've previously setup the reverse lookup table.
+ *
+ * XXX not reentrant, but shouldn't matter
+ */
+static void
+ieee80211_setup_ratetable(struct ieee80211_rate_table *rt)
+{
+#define N(a) (sizeof(a)/sizeof(a[0]))
+#define WLAN_CTRL_FRAME_SIZE \
+ (sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN)
+
+ int i;
+
+ for (i = 0; i < N(rt->rateCodeToIndex); i++)
+ rt->rateCodeToIndex[i] = (uint8_t) -1;
+ for (i = 0; i < rt->rateCount; i++) {
+ uint8_t code = rt->info[i].dot11Rate;
+ uint8_t cix = rt->info[i].ctlRateIndex;
+ uint8_t ctl_rate = rt->info[cix].dot11Rate;
+
+ rt->rateCodeToIndex[code] = i;
+ if (code & IEEE80211_RATE_BASIC) {
+ /*
+ * Map w/o basic rate bit too.
+ */
+ code &= IEEE80211_RATE_VAL;
+ rt->rateCodeToIndex[code] = i;
+ }
+
+ /*
+ * XXX for 11g the control rate to use for 5.5 and 11 Mb/s
+ * depends on whether they are marked as basic rates;
+ * the static tables are setup with an 11b-compatible
+ * 2Mb/s rate which will work but is suboptimal
+ *
+ * NB: Control rate is always less than or equal to the
+ * current rate, so control rate's reverse lookup entry
+ * has been installed and following call is safe.
+ */
+ rt->info[i].lpAckDuration = ieee80211_compute_duration(rt,
+ WLAN_CTRL_FRAME_SIZE, ctl_rate, 0);
+ rt->info[i].spAckDuration = ieee80211_compute_duration(rt,
+ WLAN_CTRL_FRAME_SIZE, ctl_rate, IEEE80211_F_SHPREAMBLE);
+ }
+
+#undef WLAN_CTRL_FRAME_SIZE
+#undef N
+}
+
+/* Setup all rate tables */
+static void
+ieee80211_phy_init(void)
+{
+#define N(arr) (int)(sizeof(arr) / sizeof(arr[0]))
+ static struct ieee80211_rate_table * const ratetables[] = {
+ &ieee80211_half_table,
+ &ieee80211_quarter_table,
+ &ieee80211_11a_table,
+ &ieee80211_11g_table,
+ &ieee80211_turbog_table,
+ &ieee80211_turboa_table,
+ &ieee80211_turboa_table,
+ &ieee80211_11a_table,
+ &ieee80211_11g_table,
+ &ieee80211_11b_table
+ };
+ int i;
+
+ for (i = 0; i < N(ratetables); ++i)
+ ieee80211_setup_ratetable(ratetables[i]);
+
+#undef N
+}
+SYSINIT(wlan_phy, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_phy_init, NULL);
+
+const struct ieee80211_rate_table *
+ieee80211_get_ratetable(struct ieee80211_channel *c)
+{
+ const struct ieee80211_rate_table *rt;
+
+ /* XXX HT */
+ if (IEEE80211_IS_CHAN_HALF(c))
+ rt = &ieee80211_half_table;
+ else if (IEEE80211_IS_CHAN_QUARTER(c))
+ rt = &ieee80211_quarter_table;
+ else if (IEEE80211_IS_CHAN_HTA(c))
+ rt = &ieee80211_11a_table; /* XXX */
+ else if (IEEE80211_IS_CHAN_HTG(c))
+ rt = &ieee80211_11g_table; /* XXX */
+ else if (IEEE80211_IS_CHAN_108G(c))
+ rt = &ieee80211_turbog_table;
+ else if (IEEE80211_IS_CHAN_ST(c))
+ rt = &ieee80211_turboa_table;
+ else if (IEEE80211_IS_CHAN_TURBO(c))
+ rt = &ieee80211_turboa_table;
+ else if (IEEE80211_IS_CHAN_A(c))
+ rt = &ieee80211_11a_table;
+ else if (IEEE80211_IS_CHAN_ANYG(c))
+ rt = &ieee80211_11g_table;
+ else if (IEEE80211_IS_CHAN_B(c))
+ rt = &ieee80211_11b_table;
+ else {
+ /* NB: should not get here */
+ panic("%s: no rate table for channel; freq %u flags 0x%x\n",
+ __func__, c->ic_freq, c->ic_flags);
+ }
+ return rt;
+}
+
+/*
+ * Convert PLCP signal/rate field to 802.11 rate (.5Mbits/s)
+ *
+ * Note we do no parameter checking; this routine is mainly
+ * used to derive an 802.11 rate for constructing radiotap
+ * header data for rx frames.
+ *
+ * XXX might be a candidate for inline
+ */
+uint8_t
+ieee80211_plcp2rate(uint8_t plcp, int ofdm)
+{
+ if (ofdm) {
+ static const uint8_t ofdm_plcp2rate[16] = {
+ [0xb] = 12,
+ [0xf] = 18,
+ [0xa] = 24,
+ [0xe] = 36,
+ [0x9] = 48,
+ [0xd] = 72,
+ [0x8] = 96,
+ [0xc] = 108
+ };
+ return ofdm_plcp2rate[plcp & 0xf];
+ } else {
+ static const uint8_t cck_plcp2rate[16] = {
+ [0xa] = 2, /* 0x0a */
+ [0x4] = 4, /* 0x14 */
+ [0x7] = 11, /* 0x37 */
+ [0xe] = 22, /* 0x6e */
+ [0xc] = 44, /* 0xdc , actually PBCC */
+ };
+ return cck_plcp2rate[plcp & 0xf];
+ }
+}
+
+/*
+ * Covert 802.11 rate to PLCP signal.
+ */
+uint8_t
+ieee80211_rate2plcp(int rate)
+{
+ switch (rate) {
+ /* CCK rates (returned values are device-dependent) */
+ case 2: return 0x0;
+ case 4: return 0x1;
+ case 11: return 0x2;
+ case 22: return 0x3;
+
+ /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
+ case 12: return 0xb;
+ case 18: return 0xf;
+ case 24: return 0xa;
+ case 36: return 0xe;
+ case 48: return 0x9;
+ case 72: return 0xd;
+ case 96: return 0x8;
+ case 108: return 0xc;
+ }
+ return 0xff; /* XXX unsupported/unknown rate */
+}
+/*
+ * Compute the time to transmit a frame of length frameLen bytes
+ * using the specified rate, phy, and short preamble setting.
+ * SIFS is included.
+ */
+uint16_t
+ieee80211_compute_duration(const struct ieee80211_rate_table *rt,
+ uint32_t frameLen, uint16_t rate, int isShortPreamble)
+{
+ uint8_t rix = rt->rateCodeToIndex[rate];
+ uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime;
+ uint32_t kbps;
+
+ KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
+ kbps = rt->info[rix].rateKbps;
+ if (kbps == 0) /* XXX bandaid for channel changes */
+ return 0;
+
+ switch (rt->info[rix].phy) {
+ case IEEE80211_T_CCK:
+#define CCK_SIFS_TIME 10
+#define CCK_PREAMBLE_BITS 144
+#define CCK_PLCP_BITS 48
+ phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
+ if (isShortPreamble && rt->info[rix].shortPreamble)
+ phyTime >>= 1;
+ numBits = frameLen << 3;
+ txTime = CCK_SIFS_TIME + phyTime
+ + ((numBits * 1000)/kbps);
+ break;
+#undef CCK_SIFS_TIME
+#undef CCK_PREAMBLE_BITS
+#undef CCK_PLCP_BITS
+
+ case IEEE80211_T_OFDM:
+#define OFDM_SIFS_TIME 16
+#define OFDM_PREAMBLE_TIME 20
+#define OFDM_PLCP_BITS 22
+#define OFDM_SYMBOL_TIME 4
+
+#define OFDM_SIFS_TIME_HALF 32
+#define OFDM_PREAMBLE_TIME_HALF 40
+#define OFDM_PLCP_BITS_HALF 22
+#define OFDM_SYMBOL_TIME_HALF 8
+
+#define OFDM_SIFS_TIME_QUARTER 64
+#define OFDM_PREAMBLE_TIME_QUARTER 80
+#define OFDM_PLCP_BITS_QUARTER 22
+#define OFDM_SYMBOL_TIME_QUARTER 16
+ if (rt == &ieee80211_half_table) {
+ bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_QUARTER) / 1000;
+ KASSERT(bitsPerSymbol != 0, ("1/2 rate bps"));
+
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = OFDM_SIFS_TIME_QUARTER
+ + OFDM_PREAMBLE_TIME_QUARTER
+ + (numSymbols * OFDM_SYMBOL_TIME_QUARTER);
+ } else if (rt == &ieee80211_quarter_table) {
+ bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_HALF) / 1000;
+ KASSERT(bitsPerSymbol != 0, ("1/4 rate bps"));
+
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = OFDM_SIFS_TIME_HALF
+ + OFDM_PREAMBLE_TIME_HALF
+ + (numSymbols * OFDM_SYMBOL_TIME_HALF);
+ } else { /* full rate channel */
+ bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000;
+ KASSERT(bitsPerSymbol != 0, ("full rate bps"));
+
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = OFDM_SIFS_TIME
+ + OFDM_PREAMBLE_TIME
+ + (numSymbols * OFDM_SYMBOL_TIME);
+ }
+ break;
+
+#undef OFDM_SIFS_TIME
+#undef OFDM_PREAMBLE_TIME
+#undef OFDM_PLCP_BITS
+#undef OFDM_SYMBOL_TIME
+
+ case IEEE80211_T_TURBO:
+#define TURBO_SIFS_TIME 8
+#define TURBO_PREAMBLE_TIME 14
+#define TURBO_PLCP_BITS 22
+#define TURBO_SYMBOL_TIME 4
+ /* we still save OFDM rates in kbps - so double them */
+ bitsPerSymbol = ((kbps << 1) * TURBO_SYMBOL_TIME) / 1000;
+ KASSERT(bitsPerSymbol != 0, ("turbo bps"));
+
+ numBits = TURBO_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = TURBO_SIFS_TIME + TURBO_PREAMBLE_TIME
+ + (numSymbols * TURBO_SYMBOL_TIME);
+ break;
+#undef TURBO_SIFS_TIME
+#undef TURBO_PREAMBLE_TIME
+#undef TURBO_PLCP_BITS
+#undef TURBO_SYMBOL_TIME
+
+ default:
+ panic("%s: unknown phy %u (rate %u)\n", __func__,
+ rt->info[rix].phy, rate);
+ break;
+ }
+ return txTime;
+}
diff --git a/sys/net80211/ieee80211_phy.h b/sys/net80211/ieee80211_phy.h
new file mode 100644
index 0000000..2b5adcf
--- /dev/null
+++ b/sys/net80211/ieee80211_phy.h
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NET80211_IEEE80211_PHY_H_
+#define _NET80211_IEEE80211_PHY_H_
+
+#ifdef _KERNEL
+/*
+ * IEEE 802.11 PHY-related definitions.
+ */
+
+/*
+ * Contention window (slots).
+ */
+#define IEEE80211_CW_MAX 1023 /* aCWmax */
+#define IEEE80211_CW_MIN_0 31 /* DS/CCK aCWmin, ERP aCWmin(0) */
+#define IEEE80211_CW_MIN_1 15 /* OFDM aCWmin, ERP aCWmin(1) */
+
+/*
+ * SIFS (microseconds).
+ */
+#define IEEE80211_DUR_SIFS 10 /* DS/CCK/ERP SIFS */
+#define IEEE80211_DUR_OFDM_SIFS 16 /* OFDM SIFS */
+
+/*
+ * Slot time (microseconds).
+ */
+#define IEEE80211_DUR_SLOT 20 /* DS/CCK slottime, ERP long slottime */
+#define IEEE80211_DUR_SHSLOT 9 /* ERP short slottime */
+#define IEEE80211_DUR_OFDM_SLOT 9 /* OFDM slottime */
+
+/*
+ * DIFS (microseconds).
+ */
+#define IEEE80211_DUR_DIFS(sifs, slot) ((sifs) + 2 * (slot))
+
+struct ieee80211_channel;
+
+struct ieee80211_rate_table {
+ int rateCount; /* NB: for proper padding */
+ uint8_t rateCodeToIndex[256]; /* back mapping */
+ struct {
+ uint8_t phy; /* CCK/OFDM/TURBO */
+ uint32_t rateKbps; /* transfer rate in kbs */
+ uint8_t shortPreamble; /* mask for enabling short
+ * preamble in CCK rate code */
+ uint8_t dot11Rate; /* value for supported rates
+ * info element of MLME */
+ uint8_t ctlRateIndex; /* index of next lower basic
+ * rate; used for dur. calcs */
+ uint16_t lpAckDuration; /* long preamble ACK dur. */
+ uint16_t spAckDuration; /* short preamble ACK dur. */
+ } info[32];
+};
+
+const struct ieee80211_rate_table *ieee80211_get_ratetable(
+ struct ieee80211_channel *);
+
+static __inline__ uint8_t
+ieee80211_ack_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
+{
+ uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex;
+ KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate));
+ return rt->info[cix].dot11Rate;
+}
+
+static __inline__ uint8_t
+ieee80211_ctl_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
+{
+ uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex;
+ KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate));
+ return rt->info[cix].dot11Rate;
+}
+
+static __inline__ enum ieee80211_phytype
+ieee80211_rate2phytype(const struct ieee80211_rate_table *rt, uint8_t rate)
+{
+ uint8_t rix = rt->rateCodeToIndex[rate];
+ KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
+ return rt->info[rix].phy;
+}
+
+/*
+ * Calculate ACK field for
+ * o non-fragment data frames
+ * o management frames
+ * sent using rate, phy and short preamble setting.
+ */
+static __inline__ uint16_t
+ieee80211_ack_duration(const struct ieee80211_rate_table *rt,
+ uint8_t rate, int isShortPreamble)
+{
+ uint8_t rix = rt->rateCodeToIndex[rate];
+
+ KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
+ if (isShortPreamble) {
+ KASSERT(rt->info[rix].spAckDuration != 0,
+ ("shpreamble ack dur is not computed!\n"));
+ return rt->info[rix].spAckDuration;
+ } else {
+ KASSERT(rt->info[rix].lpAckDuration != 0,
+ ("lgpreamble ack dur is not computed!\n"));
+ return rt->info[rix].lpAckDuration;
+ }
+}
+
+/*
+ * Compute the time to transmit a frame of length frameLen bytes
+ * using the specified 802.11 rate code, phy, and short preamble
+ * setting.
+ *
+ * NB: SIFS is included.
+ */
+uint16_t ieee80211_compute_duration(const struct ieee80211_rate_table *,
+ uint32_t frameLen, uint16_t rate, int isShortPreamble);
+/*
+ * Convert PLCP signal/rate field to 802.11 rate code (.5Mbits/s)
+ */
+uint8_t ieee80211_plcp2rate(uint8_t, int);
+/*
+ * Convert 802.11 rate code to PLCP signal.
+ */
+uint8_t ieee80211_rate2plcp(int);
+#endif /* _KERNEL */
+#endif /* !_NET80211_IEEE80211_PHY_H_ */
diff --git a/sys/net80211/ieee80211_power.c b/sys/net80211/ieee80211_power.c
index e764e1f..2a7dda8 100644
--- a/sys/net80211/ieee80211_power.c
+++ b/sys/net80211/ieee80211_power.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 power save support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
@@ -43,43 +45,57 @@ __FBSDID("$FreeBSD$");
#include <net/bpf.h>
-static void ieee80211_set_tim(struct ieee80211_node *ni, int set);
+static void ieee80211_update_ps(struct ieee80211vap *, int);
+static int ieee80211_set_tim(struct ieee80211_node *, int);
+
+MALLOC_DEFINE(M_80211_POWER, "80211power", "802.11 power save state");
void
ieee80211_power_attach(struct ieee80211com *ic)
{
- if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS) {
+}
+
+void
+ieee80211_power_detach(struct ieee80211com *ic)
+{
+}
+
+void
+ieee80211_power_vattach(struct ieee80211vap *vap)
+{
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS) {
/* NB: driver should override */
- ic->ic_set_tim = ieee80211_set_tim;
+ vap->iv_update_ps = ieee80211_update_ps;
+ vap->iv_set_tim = ieee80211_set_tim;
}
}
void
-ieee80211_power_lateattach(struct ieee80211com *ic)
+ieee80211_power_latevattach(struct ieee80211vap *vap)
{
/*
* Allocate these only if needed. Beware that we
* know adhoc mode doesn't support ATIM yet...
*/
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- ic->ic_tim_len = howmany(ic->ic_max_aid,8) * sizeof(uint8_t);
- MALLOC(ic->ic_tim_bitmap, uint8_t *, ic->ic_tim_len,
- M_DEVBUF, M_NOWAIT | M_ZERO);
- if (ic->ic_tim_bitmap == NULL) {
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ vap->iv_tim_len = howmany(vap->iv_max_aid,8) * sizeof(uint8_t);
+ MALLOC(vap->iv_tim_bitmap, uint8_t *, vap->iv_tim_len,
+ M_80211_POWER, M_NOWAIT | M_ZERO);
+ if (vap->iv_tim_bitmap == NULL) {
printf("%s: no memory for TIM bitmap!\n", __func__);
/* XXX good enough to keep from crashing? */
- ic->ic_tim_len = 0;
+ vap->iv_tim_len = 0;
}
}
}
void
-ieee80211_power_detach(struct ieee80211com *ic)
+ieee80211_power_vdetach(struct ieee80211vap *vap)
{
- if (ic->ic_tim_bitmap != NULL) {
- FREE(ic->ic_tim_bitmap, M_DEVBUF);
- ic->ic_tim_bitmap = NULL;
+ if (vap->iv_tim_bitmap != NULL) {
+ FREE(vap->iv_tim_bitmap, M_80211_POWER);
+ vap->iv_tim_bitmap = NULL;
}
}
@@ -116,12 +132,16 @@ ieee80211_node_saveq_age(struct ieee80211_node *ni)
int discard = 0;
if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) {
+#ifdef IEEE80211_DEBUG
+ struct ieee80211vap *vap = ni->ni_vap;
+#endif
struct mbuf *m;
IEEE80211_NODE_SAVEQ_LOCK(ni);
while (IF_POLL(&ni->ni_savedq, m) != NULL &&
M_AGE_GET(m) < IEEE80211_INACT_WAIT) {
-IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n", ether_sprintf(ni->ni_macaddr), M_AGE_GET(m));/*XXX*/
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "discard frame, age %u", M_AGE_GET(m));
_IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m);
m_freem(m);
discard++;
@@ -130,7 +150,7 @@ IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n"
M_AGE_SUB(m, IEEE80211_INACT_WAIT);
IEEE80211_NODE_SAVEQ_UNLOCK(ni);
- IEEE80211_NOTE(ni->ni_ic, IEEE80211_MSG_POWER, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"discard %u frames for age", discard);
IEEE80211_NODE_STAT_ADD(ni, ps_discard, discard);
}
@@ -138,35 +158,52 @@ IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n"
}
/*
- * Indicate whether there are frames queued for a station in power-save mode.
+ * Handle a change in the PS station occupancy.
*/
static void
+ieee80211_update_ps(struct ieee80211vap *vap, int nsta)
+{
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS,
+ ("operating mode %u", vap->iv_opmode));
+}
+
+/*
+ * Indicate whether there are frames queued for a station in power-save mode.
+ */
+static int
ieee80211_set_tim(struct ieee80211_node *ni, int set)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
uint16_t aid;
+ int changed;
- KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS,
- ("operating mode %u", ic->ic_opmode));
+ KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS,
+ ("operating mode %u", vap->iv_opmode));
aid = IEEE80211_AID(ni->ni_associd);
- KASSERT(aid < ic->ic_max_aid,
- ("bogus aid %u, max %u", aid, ic->ic_max_aid));
+ KASSERT(aid < vap->iv_max_aid,
+ ("bogus aid %u, max %u", aid, vap->iv_max_aid));
- IEEE80211_BEACON_LOCK(ic);
- if (set != (isset(ic->ic_tim_bitmap, aid) != 0)) {
+ IEEE80211_LOCK(ic);
+ changed = (set != (isset(vap->iv_tim_bitmap, aid) != 0));
+ if (changed) {
if (set) {
- setbit(ic->ic_tim_bitmap, aid);
- ic->ic_ps_pending++;
+ setbit(vap->iv_tim_bitmap, aid);
+ vap->iv_ps_pending++;
} else {
- clrbit(ic->ic_tim_bitmap, aid);
- ic->ic_ps_pending--;
+ clrbit(vap->iv_tim_bitmap, aid);
+ vap->iv_ps_pending--;
}
- /* NB: we know ic is in RUN state so no need to check */
- ic->ic_update_beacon(ic, IEEE80211_BEACON_TIM);
+ /* NB: we know vap is in RUN state so no need to check */
+ vap->iv_update_beacon(vap, IEEE80211_BEACON_TIM);
}
- IEEE80211_BEACON_UNLOCK(ic);
+ IEEE80211_UNLOCK(ic);
+
+ return changed;
}
/*
@@ -177,6 +214,7 @@ ieee80211_set_tim(struct ieee80211_node *ni, int set)
void
ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
int qlen, age;
@@ -184,13 +222,13 @@ ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m)
if (_IF_QFULL(&ni->ni_savedq)) {
_IF_DROP(&ni->ni_savedq);
IEEE80211_NODE_SAVEQ_UNLOCK(ni);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "[%s] pwr save q overflow, drops %d (size %d)\n",
- ether_sprintf(ni->ni_macaddr),
- ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni,
+ "pwr save q overflow, drops %d (size %d)",
+ ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE);
#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_dumppkts(ic))
- ieee80211_dump_pkt(ic, mtod(m, caddr_t), m->m_len, -1, -1);
+ if (ieee80211_msg_dumppkts(vap))
+ ieee80211_dump_pkt(ni->ni_ic, mtod(m, caddr_t),
+ m->m_len, -1, -1);
#endif
m_freem(m);
return;
@@ -207,57 +245,26 @@ ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m)
_IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age);
IEEE80211_NODE_SAVEQ_UNLOCK(ni);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
- "[%s] save frame with age %d, %u now queued\n",
- ether_sprintf(ni->ni_macaddr), age, qlen);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "save frame with age %d, %u now queued", age, qlen);
- if (qlen == 1 && ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 1);
+ if (qlen == 1 && vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 1);
}
/*
- * Handle station power-save state change.
+ * Unload the frames from the ps q but don't send them
+ * to the driver yet. We do this in two stages to minimize
+ * locking but also because there's no easy way to preserve
+ * ordering given the existing ifnet access mechanisms.
+ * XXX could be optimized
*/
-void
-ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
+static void
+pwrsave_flushq(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
struct mbuf *m, *mhead, *mtail;
int mcount;
- if (enable) {
- if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0)
- ic->ic_ps_sta++;
- ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
- IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
- "power save mode on, %u sta's in ps mode", ic->ic_ps_sta);
- return;
- }
-
- if (ni->ni_flags & IEEE80211_NODE_PWR_MGT)
- ic->ic_ps_sta--;
- ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
- IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
- "power save mode off, %u sta's in ps mode", ic->ic_ps_sta);
- /* XXX if no stations in ps mode, flush mc frames */
-
- /*
- * Flush queued unicast frames.
- */
- if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) {
- if (ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0); /* just in case */
- return;
- }
- IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
- "flush ps queue, %u packets queue", IEEE80211_NODE_SAVEQ_QLEN(ni));
- /*
- * Unload the frames from the ps q but don't send them
- * to the driver yet. We do this in two stages to minimize
- * locking but also because there's no easy way to preserve
- * ordering given the existing ifnet access mechanisms.
- * XXX could be optimized
- */
IEEE80211_NODE_SAVEQ_LOCK(ni);
mcount = IEEE80211_NODE_SAVEQ_QLEN(ni);
mhead = mtail = NULL;
@@ -275,26 +282,67 @@ ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
IEEE80211_NODE_SAVEQ_UNLOCK(ni);
if (mhead != NULL) {
/* XXX need different driver interface */
- /* XXX bypasses q max */
- IF_PREPEND_LIST(&ic->ic_ifp->if_snd, mhead, mtail, mcount);
+ /* XXX bypasses q max and OACTIVE */
+ struct ifnet *ifp = ni->ni_vap->iv_ifp;
+ IF_PREPEND_LIST(&ifp->if_snd, mhead, mtail, mcount);
+ if_start(ifp);
+ }
+}
+
+/*
+ * Handle station power-save state change.
+ */
+void
+ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ int update;
+
+ update = 0;
+ if (enable) {
+ if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
+ vap->iv_ps_sta++;
+ update = 1;
+ }
+ ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "power save mode on, %u sta's in ps mode", vap->iv_ps_sta);
+
+ if (update)
+ vap->iv_update_ps(vap, vap->iv_ps_sta);
+ } else {
+ if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) {
+ vap->iv_ps_sta--;
+ update = 1;
+ }
+ ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "power save mode off, %u sta's in ps mode", vap->iv_ps_sta);
+
+ /* NB: order here is intentional so TIM is clear before flush */
+ if (vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 0);
+ if (update) {
+ /* NB if no sta's in ps, driver should flush mc q */
+ vap->iv_update_ps(vap, vap->iv_ps_sta);
+ }
+ pwrsave_flushq(ni);
}
- if (ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0);
}
/*
* Handle power-save state change in station mode.
*/
void
-ieee80211_sta_pwrsave(struct ieee80211com *ic, int enable)
+ieee80211_sta_pwrsave(struct ieee80211vap *vap, int enable)
{
- struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211_node *ni = vap->iv_bss;
int qlen;
if (!((enable != 0) ^ ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) != 0)))
return;
- IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"sta power save mode %s", enable ? "on" : "off");
if (!enable) {
ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
@@ -307,20 +355,9 @@ ieee80211_sta_pwrsave(struct ieee80211com *ic, int enable)
*/
qlen = IEEE80211_NODE_SAVEQ_QLEN(ni);
if (qlen != 0) {
- IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"flush ps queue, %u packets queued", qlen);
- for (;;) {
- struct mbuf *m;
-
- IEEE80211_NODE_SAVEQ_LOCK(ni);
- _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m);
- IEEE80211_NODE_SAVEQ_UNLOCK(ni);
- if (m == NULL)
- break;
- /* XXX need different driver interface */
- /* XXX bypasses q max */
- IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
- }
+ pwrsave_flushq(ni);
}
} else {
ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
diff --git a/sys/net80211/ieee80211_power.h b/sys/net80211/ieee80211_power.h
index c8461f6..27f5d5a 100644
--- a/sys/net80211/ieee80211_power.h
+++ b/sys/net80211/ieee80211_power.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,14 +30,16 @@
struct ieee80211com;
void ieee80211_power_attach(struct ieee80211com *);
-void ieee80211_power_lateattach(struct ieee80211com *);
void ieee80211_power_detach(struct ieee80211com *);
+void ieee80211_power_vattach(struct ieee80211vap *);
+void ieee80211_power_vdetach(struct ieee80211vap *);
+void ieee80211_power_latevattach(struct ieee80211vap *);
int ieee80211_node_saveq_drain(struct ieee80211_node *);
int ieee80211_node_saveq_age(struct ieee80211_node *);
void ieee80211_pwrsave(struct ieee80211_node *, struct mbuf *);
void ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
-void ieee80211_sta_pwrsave(struct ieee80211com *, int enable);
+void ieee80211_sta_pwrsave(struct ieee80211vap *, int enable);
void ieee80211_power_poll(struct ieee80211com *);
#endif /* _NET80211_IEEE80211_POWER_H_ */
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c
index e67d40c..fe03b15 100644
--- a/sys/net80211/ieee80211_proto.c
+++ b/sys/net80211/ieee80211_proto.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -32,25 +32,32 @@ __FBSDID("$FreeBSD$");
*/
#include "opt_inet.h"
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
+#include <sys/taskqueue.h>
#include <sys/socket.h>
+#include <sys/sockio.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/ethernet.h> /* XXX for ether_sprintf */
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_adhoc.h>
+#include <net80211/ieee80211_sta.h>
+#include <net80211/ieee80211_hostap.h>
+#include <net80211/ieee80211_wds.h>
+#include <net80211/ieee80211_monitor.h>
+#include <net80211/ieee80211_input.h>
/* XXX tunables */
#define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */
#define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */
-#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2)
-
const char *ieee80211_mgt_subtype_name[] = {
"assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp",
"probe_req", "probe_resp", "reserved#6", "reserved#7",
@@ -66,11 +73,9 @@ const char *ieee80211_ctl_subtype_name[] = {
const char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = {
"IBSS", /* IEEE80211_M_IBSS */
"STA", /* IEEE80211_M_STA */
- "#2",
+ "WDS", /* IEEE80211_M_WDS */
"AHDEMO", /* IEEE80211_M_AHDEMO */
- "#4", "#5",
"HOSTAP", /* IEEE80211_M_HOSTAP */
- "#7",
"MONITOR" /* IEEE80211_M_MONITOR */
};
const char *ieee80211_state_name[IEEE80211_S_MAX] = {
@@ -91,11 +96,19 @@ const char *ieee80211_wme_acnames[] = {
"WME_UPSD",
};
-static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static void parent_updown(void *, int);
+static int ieee80211_new_state_locked(struct ieee80211vap *,
+ enum ieee80211_state, int);
-static void
-null_update_beacon(struct ieee80211com *ic, int item)
+static int
+null_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
{
+ struct ifnet *ifp = ni->ni_ic->ic_ifp;
+
+ if_printf(ifp, "missing ic_raw_xmit callback, drop frame\n");
+ m_freem(m);
+ return ENETDOWN;
}
void
@@ -103,54 +116,131 @@ ieee80211_proto_attach(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
- /* XXX room for crypto */
- ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4);
-
- ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT;
- ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT;
- ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
- ic->ic_bmiss_max = IEEE80211_BMISS_MAX;
- callout_init(&ic->ic_swbmiss, CALLOUT_MPSAFE);
- callout_init(&ic->ic_mgtsend, CALLOUT_MPSAFE);
- ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT;
+ /* override the 802.3 setting */
+ ifp->if_hdrlen = ic->ic_headroom
+ + sizeof(struct ieee80211_qosframe_addr4)
+ + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN
+ + IEEE80211_WEP_EXTIVLEN;
+ /* XXX no way to recalculate on ifdetach */
+ if (ALIGN(ifp->if_hdrlen) > max_linkhdr) {
+ /* XXX sanity check... */
+ max_linkhdr = ALIGN(ifp->if_hdrlen);
+ max_hdr = max_linkhdr + max_protohdr;
+ max_datalen = MHLEN - max_hdr;
+ }
ic->ic_protmode = IEEE80211_PROT_CTSONLY;
- ic->ic_roaming = IEEE80211_ROAMING_AUTO;
+
+ TASK_INIT(&ic->ic_parent_task, 0, parent_updown, ifp);
ic->ic_wme.wme_hipri_switch_hysteresis =
AGGRESSIVE_MODE_SWITCH_HYSTERESIS;
- mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF);
-
- /* protocol state change handler */
- ic->ic_newstate = ieee80211_newstate;
- ic->ic_update_beacon = null_update_beacon;
-
/* initialize management frame handlers */
- ic->ic_recv_mgmt = ieee80211_recv_mgmt;
ic->ic_send_mgmt = ieee80211_send_mgmt;
- ic->ic_raw_xmit = ieee80211_raw_xmit;
+ ic->ic_raw_xmit = null_raw_xmit;
+
+ ieee80211_adhoc_attach(ic);
+ ieee80211_sta_attach(ic);
+ ieee80211_wds_attach(ic);
+ ieee80211_hostap_attach(ic);
+ ieee80211_monitor_attach(ic);
}
void
ieee80211_proto_detach(struct ieee80211com *ic)
{
+ ieee80211_monitor_detach(ic);
+ ieee80211_hostap_detach(ic);
+ ieee80211_wds_detach(ic);
+ ieee80211_adhoc_detach(ic);
+ ieee80211_sta_detach(ic);
+}
+
+static void
+null_update_beacon(struct ieee80211vap *vap, int item)
+{
+}
+
+void
+ieee80211_proto_vattach(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ int i;
+
+ /* override the 802.3 setting */
+ ifp->if_hdrlen = ic->ic_ifp->if_hdrlen;
+
+ vap->iv_rtsthreshold = IEEE80211_RTS_DEFAULT;
+ vap->iv_fragthreshold = IEEE80211_FRAG_DEFAULT;
+ vap->iv_bmiss_max = IEEE80211_BMISS_MAX;
+ callout_init(&vap->iv_swbmiss, CALLOUT_MPSAFE);
+ callout_init(&vap->iv_mgtsend, CALLOUT_MPSAFE);
+ /*
+ * Install default tx rate handling: no fixed rate, lowest
+ * supported rate for mgmt and multicast frames. Default
+ * max retry count. These settings can be changed by the
+ * driver and/or user applications.
+ */
+ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_11NA; i++) {
+ const struct ieee80211_rateset *rs = &ic->ic_sup_rates[i];
+
+ vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE;
+ /* NB: we default to min supported rate for channel */
+ vap->iv_txparms[i].mgmtrate =
+ rs->rs_rates[0] & IEEE80211_RATE_VAL;
+ vap->iv_txparms[i].mcastrate =
+ rs->rs_rates[0] & IEEE80211_RATE_VAL;
+ vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT;
+ }
+ for (; i < IEEE80211_MODE_MAX; i++) {
+ vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE;
+ /* NB: default to MCS 0 */
+ vap->iv_txparms[i].mgmtrate = 0 | 0x80;
+ vap->iv_txparms[i].mcastrate = 0 | 0x80;
+ vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT;
+ }
+ vap->iv_roaming = IEEE80211_ROAMING_AUTO;
+
+ vap->iv_update_beacon = null_update_beacon;
+ vap->iv_deliver_data = ieee80211_deliver_data;
+
+ /* attach support for operating mode */
+ ic->ic_vattach[vap->iv_opmode](vap);
+}
+void
+ieee80211_proto_vdetach(struct ieee80211vap *vap)
+{
+#define FREEAPPIE(ie) do { \
+ if (ie != NULL) \
+ FREE(ie, M_80211_NODE_IE); \
+} while (0)
+ /*
+ * Detach operating mode module.
+ */
+ if (vap->iv_opdetach != NULL)
+ vap->iv_opdetach(vap);
/*
* This should not be needed as we detach when reseting
* the state but be conservative here since the
* authenticator may do things like spawn kernel threads.
*/
- if (ic->ic_auth->ia_detach)
- ic->ic_auth->ia_detach(ic);
-
- ieee80211_drain_ifq(&ic->ic_mgtq);
- mtx_destroy(&ic->ic_mgtq.ifq_mtx);
-
+ if (vap->iv_auth->ia_detach != NULL)
+ vap->iv_auth->ia_detach(vap);
/*
* Detach any ACL'ator.
*/
- if (ic->ic_acl != NULL)
- ic->ic_acl->iac_detach(ic);
+ if (vap->iv_acl != NULL)
+ vap->iv_acl->iac_detach(vap);
+
+ FREEAPPIE(vap->iv_appie_beacon);
+ FREEAPPIE(vap->iv_appie_probereq);
+ FREEAPPIE(vap->iv_appie_proberesp);
+ FREEAPPIE(vap->iv_appie_assocreq);
+ FREEAPPIE(vap->iv_appie_assocresp);
+ FREEAPPIE(vap->iv_appie_wpa);
+#undef FREEAPPIE
}
/*
@@ -363,16 +453,53 @@ ieee80211_fix_rate(struct ieee80211_node *ni,
struct ieee80211_rateset *nrs, int flags)
{
#define RV(v) ((v) & IEEE80211_RATE_VAL)
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
int i, j, rix, error;
- int okrate, badrate, fixedrate;
+ int okrate, badrate, fixedrate, ucastrate;
const struct ieee80211_rateset *srs;
uint8_t r;
error = 0;
okrate = badrate = 0;
+ ucastrate = vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)].ucastrate;
+ if (ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ /*
+ * Workaround awkwardness with fixed rate. We are called
+ * to check both the legacy rate set and the HT rate set
+ * but we must apply any legacy fixed rate check only to the
+ * legacy rate set and vice versa. We cannot tell what type
+ * of rate set we've been given (legacy or HT) but we can
+ * distinguish the fixed rate type (MCS have 0x80 set).
+ * So to deal with this the caller communicates whether to
+ * check MCS or legacy rate using the flags and we use the
+ * type of any fixed rate to avoid applying an MCS to a
+ * legacy rate and vice versa.
+ */
+ if (ucastrate & 0x80) {
+ if (flags & IEEE80211_F_DOFRATE)
+ flags &= ~IEEE80211_F_DOFRATE;
+ } else if ((ucastrate & 0x80) == 0) {
+ if (flags & IEEE80211_F_DOFMCS)
+ flags &= ~IEEE80211_F_DOFMCS;
+ }
+ /* NB: required to make MCS match below work */
+ ucastrate &= IEEE80211_RATE_VAL;
+ }
fixedrate = IEEE80211_FIXED_RATE_NONE;
- srs = ieee80211_get_suprates(ic, ni->ni_chan);
+ /*
+ * XXX we are called to process both MCS and legacy rates;
+ * we must use the appropriate basic rate set or chaos will
+ * ensue; for now callers that want MCS must supply
+ * IEEE80211_F_DOBRS; at some point we'll need to split this
+ * function so there are two variants, one for MCS and one
+ * for legacy rates.
+ */
+ if (flags & IEEE80211_F_DOBRS)
+ srs = (const struct ieee80211_rateset *)
+ ieee80211_get_suphtrates(ic, ni->ni_chan);
+ else
+ srs = ieee80211_get_suprates(ic, ni->ni_chan);
for (i = 0; i < nrs->rs_nrates; ) {
if (flags & IEEE80211_F_DOSORT) {
/*
@@ -391,7 +518,7 @@ ieee80211_fix_rate(struct ieee80211_node *ni,
/*
* Check for fixed rate.
*/
- if (r == ic->ic_fixed_rate)
+ if (r == ucastrate)
fixedrate = r;
/*
* Check against supported rates.
@@ -431,9 +558,13 @@ ieee80211_fix_rate(struct ieee80211_node *ni,
i++;
}
if (okrate == 0 || error != 0 ||
- ((flags & IEEE80211_F_DOFRATE) && fixedrate != ic->ic_fixed_rate))
+ ((flags & (IEEE80211_F_DOFRATE|IEEE80211_F_DOFMCS)) &&
+ fixedrate != ucastrate)) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
+ "%s: flags 0x%x okrate %d error %d fixedrate 0x%x "
+ "ucastrate %x\n", __func__, fixedrate, ucastrate, flags);
return badrate | IEEE80211_RATE_BASIC;
- else
+ } else
return RV(okrate);
#undef RV
}
@@ -491,7 +622,7 @@ ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff)
* NB: the rate set is assumed to be sorted.
*/
int
-ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs)
+ieee80211_iserp_rateset(const struct ieee80211_rateset *rs)
{
#define N(a) (sizeof(a) / sizeof(a[0]))
static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 };
@@ -516,14 +647,15 @@ ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs)
}
/*
- * Mark the basic rates for the 11g rate table based on the
+ * Mark the basic rates for the rate table based on the
* operating mode. For real 11g we mark all the 11b rates
* and 6, 12, and 24 OFDM. For 11b compatibility we mark only
* 11b rates. There's also a pseudo 11a-mode used to mark only
* the basic OFDM rates.
*/
-void
-ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode)
+static void
+setbasicrates(struct ieee80211_rateset *rs,
+ enum ieee80211_phymode mode, int add)
{
static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = {
{ .rs_nrates = 0 }, /* IEEE80211_MODE_AUTO */
@@ -531,16 +663,17 @@ ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode
{ 2, { 2, 4 } }, /* IEEE80211_MODE_11B */
{ 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */
{ .rs_nrates = 0 }, /* IEEE80211_MODE_FH */
- /* IEEE80211_MODE_PUREG (not yet) */
- { 7, { 2, 4, 11, 22, 12, 24, 48 } },
+ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_TURBO_A */
+ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_TURBO_G (mixed b/g) */
+ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_STURBO_A */
{ 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11NA */
- /* IEEE80211_MODE_11NG (mixed b/g) */
- { 7, { 2, 4, 11, 22, 12, 24, 48 } },
+ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11NG (mixed b/g) */
};
int i, j;
for (i = 0; i < rs->rs_nrates; i++) {
- rs->rs_rates[i] &= IEEE80211_RATE_VAL;
+ if (!add)
+ rs->rs_rates[i] &= IEEE80211_RATE_VAL;
for (j = 0; j < basic[mode].rs_nrates; j++)
if (basic[mode].rs_rates[j] == rs->rs_rates[i]) {
rs->rs_rates[i] |= IEEE80211_RATE_BASIC;
@@ -550,14 +683,40 @@ ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode
}
/*
- * WME protocol support. The following parameters come from the spec.
+ * Set the basic rates in a rate set.
+ */
+void
+ieee80211_setbasicrates(struct ieee80211_rateset *rs,
+ enum ieee80211_phymode mode)
+{
+ setbasicrates(rs, mode, 0);
+}
+
+/*
+ * Add basic rates to a rate set.
+ */
+void
+ieee80211_addbasicrates(struct ieee80211_rateset *rs,
+ enum ieee80211_phymode mode)
+{
+ setbasicrates(rs, mode, 1);
+}
+
+/*
+ * WME protocol support.
+ *
+ * The default 11a/b/g/n parameters come from the WiFi Alliance WMM
+ * System Interopability Test Plan (v1.4, Appendix F) and the 802.11n
+ * Draft 2.0 Test Plan (Appendix D).
+ *
+ * Static/Dynamic Turbo mode settings come from Atheros.
*/
typedef struct phyParamType {
- uint8_t aifsn;
- uint8_t logcwmin;
- uint8_t logcwmax;
- uint16_t txopLimit;
- uint8_t acm;
+ uint8_t aifsn;
+ uint8_t logcwmin;
+ uint8_t logcwmax;
+ uint16_t txopLimit;
+ uint8_t acm;
} paramType;
static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = {
@@ -646,15 +805,18 @@ static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = {
{ 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */
};
-void
-ieee80211_wme_initparams(struct ieee80211com *ic)
+static void
+ieee80211_wme_initparams_locked(struct ieee80211vap *vap)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
const paramType *pPhyParam, *pBssPhyParam;
struct wmeParams *wmep;
enum ieee80211_phymode mode;
int i;
+ IEEE80211_LOCK_ASSERT(ic);
+
if ((ic->ic_caps & IEEE80211_C_WME) == 0)
return;
@@ -704,7 +866,7 @@ ieee80211_wme_initparams(struct ieee80211com *ic)
wmep->wmep_txopLimit = pBssPhyParam->txopLimit;
}
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: %s chan [acm %u aifsn %u log2(cwmin) %u "
"log2(cwmax) %u txpoLimit %u]\n", __func__
, ieee80211_wme_acnames[i]
@@ -721,7 +883,7 @@ ieee80211_wme_initparams(struct ieee80211com *ic)
wmep->wmep_logcwmin = pBssPhyParam->logcwmin;
wmep->wmep_logcwmax = pBssPhyParam->logcwmax;
wmep->wmep_txopLimit = pBssPhyParam->txopLimit;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: %s bss [acm %u aifsn %u log2(cwmin) %u "
"log2(cwmax) %u txpoLimit %u]\n", __func__
, ieee80211_wme_acnames[i]
@@ -733,7 +895,7 @@ ieee80211_wme_initparams(struct ieee80211com *ic)
);
}
/* NB: check ic_bss to avoid NULL deref on initial attach */
- if (ic->ic_bss != NULL) {
+ if (vap->iv_bss != NULL) {
/*
* Calculate agressive mode switching threshold based
* on beacon interval. This doesn't need locking since
@@ -741,16 +903,26 @@ ieee80211_wme_initparams(struct ieee80211com *ic)
* which point we start sending beacon frames.
*/
wme->wme_hipri_switch_thresh =
- (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100;
- ieee80211_wme_updateparams(ic);
+ (HIGH_PRI_SWITCH_THRESH * vap->iv_bss->ni_intval) / 100;
+ ieee80211_wme_updateparams(vap);
}
}
+void
+ieee80211_wme_initparams(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK(ic);
+ ieee80211_wme_initparams_locked(vap);
+ IEEE80211_UNLOCK(ic);
+}
+
/*
* Update WME parameters for ourself and the BSS.
*/
void
-ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
+ieee80211_wme_updateparams_locked(struct ieee80211vap *vap)
{
static const paramType phyParam[IEEE80211_MODE_MAX] = {
{ 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_AUTO */
@@ -764,6 +936,7 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
{ 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NA */ /*XXXcheck*/
{ 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NG */ /*XXXcheck*/
};
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
const struct wmeParams *wmep;
struct wmeParams *chanp, *bssp;
@@ -806,11 +979,11 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
* BE uses agressive params to optimize performance of
* legacy/non-QoS traffic.
*/
- if ((ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ if ((vap->iv_opmode == IEEE80211_M_HOSTAP &&
(wme->wme_flags & WME_F_AGGRMODE) != 0) ||
- (ic->ic_opmode == IEEE80211_M_STA &&
- (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) ||
- (ic->ic_flags & IEEE80211_F_WME) == 0) {
+ (vap->iv_opmode == IEEE80211_M_STA &&
+ (vap->iv_bss->ni_flags & IEEE80211_NODE_QOS) == 0) ||
+ (vap->iv_flags & IEEE80211_F_WME) == 0) {
chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
@@ -820,9 +993,9 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
chanp->wmep_logcwmax = bssp->wmep_logcwmax =
phyParam[mode].logcwmax;
chanp->wmep_txopLimit = bssp->wmep_txopLimit =
- (ic->ic_flags & IEEE80211_F_BURST) ?
+ (vap->iv_flags & IEEE80211_F_BURST) ?
phyParam[mode].txopLimit : 0;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: %s [acm %u aifsn %u log2(cwmin) %u "
"log2(cwmax) %u txpoLimit %u]\n", __func__
, ieee80211_wme_acnames[WME_AC_BE]
@@ -834,7 +1007,8 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ /* XXX multi-bss */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) {
static const uint8_t logCwMin[IEEE80211_MODE_MAX] = {
3, /* IEEE80211_MODE_AUTO */
@@ -852,77 +1026,238 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode];
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: %s log2(cwmin) %u\n", __func__
, ieee80211_wme_acnames[WME_AC_BE]
, chanp->wmep_logcwmin
);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */
/*
* Arrange for a beacon update and bump the parameter
* set number so associated stations load the new values.
*/
wme->wme_bssChanParams.cap_info =
(wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT;
- ieee80211_beacon_notify(ic, IEEE80211_BEACON_WME);
+ ieee80211_beacon_notify(vap, IEEE80211_BEACON_WME);
}
wme->wme_update(ic);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: WME params updated, cap_info 0x%x\n", __func__,
- ic->ic_opmode == IEEE80211_M_STA ?
+ vap->iv_opmode == IEEE80211_M_STA ?
wme->wme_wmeChanParams.cap_info :
wme->wme_bssChanParams.cap_info);
}
void
-ieee80211_wme_updateparams(struct ieee80211com *ic)
+ieee80211_wme_updateparams(struct ieee80211vap *vap)
{
+ struct ieee80211com *ic = vap->iv_ic;
if (ic->ic_caps & IEEE80211_C_WME) {
- IEEE80211_BEACON_LOCK(ic);
- ieee80211_wme_updateparams_locked(ic);
- IEEE80211_BEACON_UNLOCK(ic);
+ IEEE80211_LOCK(ic);
+ ieee80211_wme_updateparams_locked(vap);
+ IEEE80211_UNLOCK(ic);
}
}
+static void
+parent_updown(void *arg, int npending)
+{
+ struct ifnet *parent = arg;
+
+ parent->if_ioctl(parent, SIOCSIFFLAGS, NULL);
+}
+
/*
- * Start a device. If this is the first vap running on the
- * underlying device then we first bring it up.
+ * Start a vap running. If this is the first vap to be
+ * set running on the underlying device then we
+ * automatically bring the device up.
*/
-int
-ieee80211_init(struct ieee80211com *ic, int forcescan)
+void
+ieee80211_start_locked(struct ieee80211vap *vap)
{
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *parent = ic->ic_ifp;
- IEEE80211_DPRINTF(ic,
+ IEEE80211_LOCK_ASSERT(ic);
+
+ IEEE80211_DPRINTF(vap,
IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
- "%s\n", "start running");
+ "start running, %d vaps running\n", ic->ic_nrunning);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ /*
+ * Mark us running. Note that it's ok to do this first;
+ * if we need to bring the parent device up we defer that
+ * to avoid dropping the com lock. We expect the device
+ * to respond to being marked up by calling back into us
+ * through ieee80211_start_all at which point we'll come
+ * back in here and complete the work.
+ */
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ /*
+ * We are not running; if this we are the first vap
+ * to be brought up auto-up the parent if necessary.
+ */
+ if (ic->ic_nrunning++ == 0 &&
+ (parent->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "%s: up parent %s\n", __func__, parent->if_xname);
+ parent->if_flags |= IFF_UP;
+ taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task);
+ return;
+ }
+ }
/*
- * Kick the 802.11 state machine as appropriate.
+ * If the parent is up and running, then kick the
+ * 802.11 state machine as appropriate.
*/
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL) {
- if (ic->ic_opmode == IEEE80211_M_STA) {
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ if ((parent->if_drv_flags & IFF_DRV_RUNNING) &&
+ vap->iv_roaming != IEEE80211_ROAMING_MANUAL) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+#if 0
+ /* XXX bypasses scan too easily; disable for now */
+ /*
+ * Try to be intelligent about clocking the state
+ * machine. If we're currently in RUN state then
+ * we should be able to apply any new state/parameters
+ * simply by re-associating. Otherwise we need to
+ * re-scan to select an appropriate ap.
+ */
+ if (vap->iv_state >= IEEE80211_S_RUN)
+ ieee80211_new_state_locked(vap,
+ IEEE80211_S_ASSOC, 1);
+ else
+#endif
+ ieee80211_new_state_locked(vap,
+ IEEE80211_S_SCAN, 0);
} else {
/*
- * For monitor+wds modes there's nothing to do but
- * start running. Otherwise, if this is the first
+ * For monitor+wds mode there's nothing to do but
+ * start running. Otherwise if this is the first
* vap to be brought up, start a scan which may be
* preempted if the station is locked to a particular
* channel.
*/
- if (ic->ic_opmode == IEEE80211_M_MONITOR ||
- ic->ic_opmode == IEEE80211_M_WDS) {
- ic->ic_state = IEEE80211_S_INIT; /* XXX*/
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ /* XXX needed? */
+ ieee80211_new_state_locked(vap, IEEE80211_S_INIT, 0);
+ if (vap->iv_opmode == IEEE80211_M_MONITOR ||
+ vap->iv_opmode == IEEE80211_M_WDS)
+ ieee80211_new_state_locked(vap,
+ IEEE80211_S_RUN, -1);
+ else
+ ieee80211_new_state_locked(vap,
+ IEEE80211_S_SCAN, 0);
}
}
- return 0;
+}
+
+/*
+ * Start a single vap.
+ */
+void
+ieee80211_init(void *arg)
+{
+ struct ieee80211vap *vap = arg;
+
+ /*
+ * This routine is publicly accessible through the vap's
+ * if_init method so guard against calls during detach.
+ * ieee80211_vap_detach null's the backpointer before
+ * tearing down state to signal any callback should be
+ * rejected/ignored.
+ */
+ if (vap != NULL) {
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "%s\n", __func__);
+
+ IEEE80211_LOCK(vap->iv_ic);
+ ieee80211_start_locked(vap);
+ IEEE80211_UNLOCK(vap->iv_ic);
+ }
+}
+
+/*
+ * Start all runnable vap's on a device.
+ */
+void
+ieee80211_start_all(struct ieee80211com *ic)
+{
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK(ic);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ struct ifnet *ifp = vap->iv_ifp;
+ if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */
+ ieee80211_start_locked(vap);
+ }
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
+ * Stop a vap. We force it down using the state machine
+ * then mark it's ifnet not running. If this is the last
+ * vap running on the underlying device then we close it
+ * too to insure it will be properly initialized when the
+ * next vap is brought up.
+ */
+void
+ieee80211_stop_locked(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ifnet *parent = ic->ic_ifp;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "stop running, %d vaps running\n", ic->ic_nrunning);
+
+ ieee80211_new_state_locked(vap, IEEE80211_S_INIT, -1);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; /* mark us stopped */
+ if (--ic->ic_nrunning == 0 &&
+ (parent->if_drv_flags & IFF_DRV_RUNNING)) {
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "down parent %s\n", parent->if_xname);
+ parent->if_flags &= ~IFF_UP;
+ taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task);
+ }
+ }
+}
+
+void
+ieee80211_stop(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK(ic);
+ ieee80211_stop_locked(vap);
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
+ * Stop all vap's running on a device.
+ */
+void
+ieee80211_stop_all(struct ieee80211com *ic)
+{
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK(ic);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ struct ifnet *ifp = vap->iv_ifp;
+ if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */
+ ieee80211_stop_locked(vap);
+ }
+ IEEE80211_UNLOCK(ic);
}
/*
@@ -932,19 +1267,20 @@ ieee80211_init(struct ieee80211com *ic, int forcescan)
* the driver to effect the change.
*/
void
-ieee80211_dturbo_switch(struct ieee80211com *ic, int newflags)
+ieee80211_dturbo_switch(struct ieee80211vap *vap, int newflags)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *chan;
chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags);
if (chan == NULL) { /* XXX should not happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no channel with freq %u flags 0x%x\n",
__func__, ic->ic_bsschan->ic_freq, newflags);
return;
}
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: %s -> %s (freq %u flags 0x%x)\n", __func__,
ieee80211_phymode_name[ieee80211_chan2mode(ic->ic_bsschan)],
ieee80211_phymode_name[ieee80211_chan2mode(chan)],
@@ -960,57 +1296,21 @@ ieee80211_dturbo_switch(struct ieee80211com *ic, int newflags)
void
ieee80211_beacon_miss(struct ieee80211com *ic)
{
+ struct ieee80211vap *vap;
- if (ic->ic_flags & IEEE80211_F_SCAN) {
- /* XXX check ic_curchan != ic_bsschan? */
+ if (ic->ic_flags & IEEE80211_F_SCAN)
return;
- }
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
- "%s\n", "beacon miss");
-
- /*
- * Our handling is only meaningful for stations that are
- * associated; any other conditions else will be handled
- * through different means (e.g. the tx timeout on mgt frames).
- */
- if (ic->ic_opmode != IEEE80211_M_STA || ic->ic_state != IEEE80211_S_RUN)
- return;
-
- if (++ic->ic_bmiss_count < ic->ic_bmiss_max) {
+ /* XXX locking */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
/*
- * Send a directed probe req before falling back to a scan;
- * if we receive a response ic_bmiss_count will be reset.
- * Some cards mistakenly report beacon miss so this avoids
- * the expensive scan if the ap is still there.
+ * We only pass events through for sta vap's in RUN state;
+ * may be too restrictive but for now this saves all the
+ * handlers duplicating these checks.
*/
- ieee80211_send_probereq(ic->ic_bss, ic->ic_myaddr,
- ic->ic_bss->ni_bssid, ic->ic_bss->ni_bssid,
- ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen,
- ic->ic_opt_ie, ic->ic_opt_ie_len);
- return;
- }
- ic->ic_bmiss_count = 0;
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
- /*
- * If we receive a beacon miss interrupt when using
- * dynamic turbo, attempt to switch modes before
- * reassociating.
- */
- if (IEEE80211_ATH_CAP(ic, ic->ic_bss, IEEE80211_NODE_TURBOP))
- ieee80211_dturbo_switch(ic,
- ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO);
- /*
- * Try to reassociate before scanning for a new ap.
- */
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1);
- } else {
- /*
- * Somebody else is controlling state changes (e.g.
- * a user-mode app) don't do anything that would
- * confuse them; just drop into scan mode so they'll
- * notified of the state change and given control.
- */
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ if (vap->iv_opmode == IEEE80211_M_STA &&
+ vap->iv_state == IEEE80211_S_RUN &&
+ vap->iv_bmiss != NULL)
+ vap->iv_bmiss(vap);
}
}
@@ -1019,377 +1319,378 @@ ieee80211_beacon_miss(struct ieee80211com *ic)
* were received in the last period. If not post a
* beacon miss; otherwise reset the counter.
*/
-static void
+void
ieee80211_swbmiss(void *arg)
{
- struct ieee80211com *ic = arg;
+ struct ieee80211vap *vap = arg;
- if (ic->ic_swbmiss_count == 0) {
- ieee80211_beacon_miss(ic);
- if (ic->ic_bmiss_count == 0) /* don't re-arm timer */
+ if (vap->iv_swbmiss_count == 0) {
+ if (vap->iv_bmiss != NULL)
+ vap->iv_bmiss(vap);
+ if (vap->iv_bmiss_count == 0) /* don't re-arm timer */
return;
} else
- ic->ic_swbmiss_count = 0;
- callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period,
- ieee80211_swbmiss, ic);
+ vap->iv_swbmiss_count = 0;
+ callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period,
+ ieee80211_swbmiss, vap);
+}
+
+/*
+ * Start an 802.11h channel switch. We record the parameters,
+ * mark the operation pending, notify each vap through the
+ * beacon update mechanism so it can update the beacon frame
+ * contents, and then switch vap's to CSA state to block outbound
+ * traffic. Devices that handle CSA directly can use the state
+ * switch to do the right thing so long as they call
+ * ieee80211_csa_completeswitch when it's time to complete the
+ * channel change. Devices that depend on the net80211 layer can
+ * use ieee80211_beacon_update to handle the countdown and the
+ * channel switch.
+ */
+void
+ieee80211_csa_startswitch(struct ieee80211com *ic,
+ struct ieee80211_channel *c, int mode, int count)
+{
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ic->ic_csa_newchan = c;
+ ic->ic_csa_count = count;
+ /* XXX record mode? */
+ ic->ic_flags |= IEEE80211_F_CSAPENDING;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS)
+ ieee80211_beacon_notify(vap, IEEE80211_BEACON_CSA);
+ /* switch to CSA state to block outbound traffic */
+ if (vap->iv_state == IEEE80211_S_RUN)
+ ieee80211_new_state_locked(vap, IEEE80211_S_CSA, 0);
+ }
+ ieee80211_notify_csa(ic, c, mode, count);
+}
+
+/*
+ * Complete an 802.11h channel switch started by ieee80211_csa_startswitch.
+ * We clear state and move all vap's in CSA state to RUN state
+ * so they can again transmit.
+ */
+void
+ieee80211_csa_completeswitch(struct ieee80211com *ic)
+{
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ KASSERT(ic->ic_flags & IEEE80211_F_CSAPENDING, ("csa not pending"));
+
+ ieee80211_setcurchan(ic, ic->ic_csa_newchan);
+ ic->ic_csa_newchan = NULL;
+ ic->ic_flags &= ~IEEE80211_F_CSAPENDING;
+
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_state == IEEE80211_S_CSA)
+ ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0);
+}
+
+/*
+ * Complete a DFS CAC started by ieee80211_dfs_cac_start.
+ * We clear state and move all vap's in CAC state to RUN state.
+ */
+void
+ieee80211_cac_completeswitch(struct ieee80211vap *vap0)
+{
+ struct ieee80211com *ic = vap0->iv_ic;
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK(ic);
+ /*
+ * Complete CAC state change for lead vap first; then
+ * clock all the other vap's waiting.
+ */
+ KASSERT(vap0->iv_state == IEEE80211_S_CAC,
+ ("wrong state %d", vap0->iv_state));
+ ieee80211_new_state_locked(vap0, IEEE80211_S_RUN, 0);
+
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_state == IEEE80211_S_CAC)
+ ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0);
+ IEEE80211_UNLOCK(ic);
}
+/*
+ * Force all vap's other than the specified vap to the INIT state
+ * and mark them as waiting for a scan to complete. These vaps
+ * will be brought up when the scan completes and the scanning vap
+ * reaches RUN state by wakeupwaiting.
+ * XXX if we do this in threads we can use sleep/wakeup.
+ */
static void
-sta_disassoc(void *arg, struct ieee80211_node *ni)
+markwaiting(struct ieee80211vap *vap0)
{
- struct ieee80211com *ic = arg;
+ struct ieee80211com *ic = vap0->iv_ic;
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
- if (ni->ni_associd != 0) {
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC,
- IEEE80211_REASON_ASSOC_LEAVE);
- ieee80211_node_leave(ic, ni);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap == vap0)
+ continue;
+ if (vap->iv_state != IEEE80211_S_INIT) {
+ vap->iv_newstate(vap, IEEE80211_S_INIT, 0);
+ vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
+ }
}
}
+/*
+ * Wakeup all vap's waiting for a scan to complete. This is the
+ * companion to markwaiting (above) and is used to coordinate
+ * multiple vaps scanning.
+ */
static void
-sta_deauth(void *arg, struct ieee80211_node *ni)
+wakeupwaiting(struct ieee80211vap *vap0)
{
- struct ieee80211com *ic = arg;
+ struct ieee80211com *ic = vap0->iv_ic;
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_ASSOC_LEAVE);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap == vap0)
+ continue;
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANWAIT) {
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT;
+ /* NB: sta's cannot go INIT->RUN */
+ vap->iv_newstate(vap,
+ vap->iv_opmode == IEEE80211_M_STA ?
+ IEEE80211_S_SCAN : IEEE80211_S_RUN, 0);
+ }
+ }
}
/*
- * Handle deauth with reason. We retry only for
- * the cases where we might succeed. Otherwise
- * we downgrade the ap and scan.
+ * Handle post state change work common to all operating modes.
*/
static void
-sta_authretry(struct ieee80211com *ic, struct ieee80211_node *ni, int reason)
+ieee80211_newstate_cb(struct ieee80211vap *vap,
+ enum ieee80211_state nstate, int arg)
{
- switch (reason) {
- case IEEE80211_STATUS_SUCCESS:
- case IEEE80211_STATUS_TIMEOUT:
- case IEEE80211_REASON_ASSOC_EXPIRE:
- case IEEE80211_REASON_NOT_AUTHED:
- case IEEE80211_REASON_NOT_ASSOCED:
- case IEEE80211_REASON_ASSOC_LEAVE:
- case IEEE80211_REASON_ASSOC_NOT_AUTHED:
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1);
- break;
- default:
- ieee80211_scan_assoc_fail(ic, ic->ic_bss->ni_macaddr, reason);
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
- ieee80211_check_scan(ic,
- IEEE80211_SCAN_ACTIVE,
- IEEE80211_SCAN_FOREVER,
- ic->ic_des_nssid, ic->ic_des_ssid);
- break;
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: %s arg %d\n", __func__, ieee80211_state_name[nstate], arg);
+
+ if (nstate == IEEE80211_S_RUN) {
+ /*
+ * OACTIVE may be set on the vap if the upper layer
+ * tried to transmit (e.g. IPv6 NDP) before we reach
+ * RUN state. Clear it and restart xmit.
+ *
+ * Note this can also happen as a result of SLEEP->RUN
+ * (i.e. coming out of power save mode).
+ */
+ vap->iv_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ if_start(vap->iv_ifp);
+
+ /* bring up any vaps waiting on us */
+ wakeupwaiting(vap);
+ } else if (nstate == IEEE80211_S_INIT) {
+ /*
+ * Flush the scan cache if we did the last scan (XXX?)
+ * and flush any frames on send queues from this vap.
+ * Note the mgt q is used only for legacy drivers and
+ * will go away shortly.
+ */
+ ieee80211_scan_flush(vap);
+
+ /* XXX NB: cast for altq */
+ ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap);
}
+ vap->iv_newstate_cb = NULL;
}
+/*
+ * Public interface for initiating a state machine change.
+ * This routine single-threads the request and coordinates
+ * the scheduling of multiple vaps for the purpose of selecting
+ * an operating channel. Specifically the following scenarios
+ * are handled:
+ * o only one vap can be selecting a channel so on transition to
+ * SCAN state if another vap is already scanning then
+ * mark the caller for later processing and return without
+ * doing anything (XXX? expectations by caller of synchronous operation)
+ * o only one vap can be doing CAC of a channel so on transition to
+ * CAC state if another vap is already scanning for radar then
+ * mark the caller for later processing and return without
+ * doing anything (XXX? expectations by caller of synchronous operation)
+ * o if another vap is already running when a request is made
+ * to SCAN then an operating channel has been chosen; bypass
+ * the scan and just join the channel
+ *
+ * Note that the state change call is done through the iv_newstate
+ * method pointer so any driver routine gets invoked. The driver
+ * will normally call back into operating mode-specific
+ * ieee80211_newstate routines (below) unless it needs to completely
+ * bypass the state machine (e.g. because the firmware has it's
+ * own idea how things should work). Bypassing the net80211 layer
+ * is usually a mistake and indicates lack of proper integration
+ * with the net80211 layer.
+ */
static int
-ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+ieee80211_new_state_locked(struct ieee80211vap *vap,
+ enum ieee80211_state nstate, int arg)
{
- struct ifnet *ifp = ic->ic_ifp;
- struct ieee80211_node *ni;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211vap *vp;
enum ieee80211_state ostate;
-
- ostate = ic->ic_state;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__,
- ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
- ic->ic_state = nstate; /* state transition */
- callout_stop(&ic->ic_mgtsend); /* XXX callout_drain */
- if (ostate != IEEE80211_S_SCAN)
- ieee80211_cancel_scan(ic); /* background scan */
- ni = ic->ic_bss; /* NB: no reference held */
- if (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS)
- callout_stop(&ic->ic_swbmiss);
- switch (nstate) {
- case IEEE80211_S_INIT:
- switch (ostate) {
- case IEEE80211_S_INIT:
- break;
- case IEEE80211_S_RUN:
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DISASSOC,
- IEEE80211_REASON_ASSOC_LEAVE);
- ieee80211_sta_leave(ic, ni);
- break;
- case IEEE80211_M_HOSTAP:
- ieee80211_iterate_nodes(&ic->ic_sta,
- sta_disassoc, ic);
- break;
- default:
- break;
- }
- break;
- case IEEE80211_S_ASSOC:
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_AUTH_LEAVE);
- break;
- case IEEE80211_M_HOSTAP:
- ieee80211_iterate_nodes(&ic->ic_sta,
- sta_deauth, ic);
- break;
- default:
- break;
- }
- break;
- case IEEE80211_S_SCAN:
- ieee80211_cancel_scan(ic);
- break;
- case IEEE80211_S_AUTH:
- break;
- default:
- break;
+ int nrunning, nscanning, rc;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ nrunning = nscanning = 0;
+ /* XXX can track this state instead of calculating */
+ TAILQ_FOREACH(vp, &ic->ic_vaps, iv_next) {
+ if (vp != vap) {
+ if (vp->iv_state >= IEEE80211_S_RUN)
+ nrunning++;
+ /* XXX doesn't handle bg scan */
+ /* NB: CAC+AUTH+ASSOC treated like SCAN */
+ else if (vp->iv_state > IEEE80211_S_INIT)
+ nscanning++;
}
- if (ostate != IEEE80211_S_INIT) {
- /* NB: optimize INIT -> INIT case */
- ieee80211_drain_ifq(&ic->ic_mgtq);
- ieee80211_reset_bss(ic);
- ieee80211_scan_flush(ic);
- }
- if (ic->ic_auth->ia_detach != NULL)
- ic->ic_auth->ia_detach(ic);
- break;
+ }
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: %s -> %s (nrunning %d nscanning %d)\n", __func__,
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate],
+ nrunning, nscanning);
+ switch (nstate) {
case IEEE80211_S_SCAN:
- switch (ostate) {
- case IEEE80211_S_INIT:
- createibss:
- if ((ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_AHDEMO) &&
- ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
- /*
- * Already have a channel; bypass the
- * scan and startup immediately. Because
- * of this explicitly sync the scanner state.
- */
- ieee80211_scan_update(ic);
- ieee80211_create_ibss(ic, ic->ic_des_chan);
- } else {
- ieee80211_check_scan(ic,
- IEEE80211_SCAN_ACTIVE |
- IEEE80211_SCAN_FLUSH,
- IEEE80211_SCAN_FOREVER,
- ic->ic_des_nssid, ic->ic_des_ssid);
- }
- break;
- case IEEE80211_S_SCAN:
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
+ if (ostate == IEEE80211_S_INIT) {
/*
- * These can happen either because of a timeout
- * on an assoc/auth response or because of a
- * change in state that requires a reset. For
- * the former we're called with a non-zero arg
- * that is the cause for the failure; pass this
- * to the scan code so it can update state.
- * Otherwise trigger a new scan unless we're in
- * manual roaming mode in which case an application
- * must issue an explicit scan request.
+ * INIT -> SCAN happens on initial bringup.
*/
- if (arg != 0)
- ieee80211_scan_assoc_fail(ic,
- ic->ic_bss->ni_macaddr, arg);
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
- ieee80211_check_scan(ic,
- IEEE80211_SCAN_ACTIVE,
- IEEE80211_SCAN_FOREVER,
- ic->ic_des_nssid, ic->ic_des_ssid);
- break;
- case IEEE80211_S_RUN: /* beacon miss */
- if (ic->ic_opmode == IEEE80211_M_STA) {
- ieee80211_sta_leave(ic, ni);
- ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
- ieee80211_check_scan(ic,
- IEEE80211_SCAN_ACTIVE,
- IEEE80211_SCAN_FOREVER,
- ic->ic_des_nssid,
- ic->ic_des_ssid);
- } else {
- ieee80211_iterate_nodes(&ic->ic_sta,
- sta_disassoc, ic);
- goto createibss;
- }
- break;
- default:
- break;
- }
- break;
- case IEEE80211_S_AUTH:
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("switch to %s state when operating in mode %u",
- ieee80211_state_name[nstate], ic->ic_opmode));
- switch (ostate) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 1);
- break;
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
- switch (arg & 0xff) {
- case IEEE80211_FC0_SUBTYPE_AUTH:
- /* ??? */
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 2);
- break;
- case IEEE80211_FC0_SUBTYPE_DEAUTH:
- sta_authretry(ic, ni, arg>>8);
- break;
- }
- break;
- case IEEE80211_S_RUN:
- switch (arg & 0xff) {
- case IEEE80211_FC0_SUBTYPE_AUTH:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 2);
- ic->ic_state = ostate; /* stay RUN */
- break;
- case IEEE80211_FC0_SUBTYPE_DEAUTH:
- ieee80211_sta_leave(ic, ni);
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
- /* try to reauth */
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 1);
- }
- break;
- }
- break;
- default:
- break;
- }
- break;
- case IEEE80211_S_ASSOC:
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("switch to %s state when operating in mode %u",
- ieee80211_state_name[nstate], ic->ic_opmode));
- switch (ostate) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "%s: invalid transition\n", __func__);
- break;
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
- break;
- case IEEE80211_S_RUN:
- ieee80211_sta_leave(ic, ni);
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
- IEEE80211_SEND_MGMT(ic, ni, arg ?
- IEEE80211_FC0_SUBTYPE_REASSOC_REQ :
- IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
- }
- break;
- default:
- break;
- }
- break;
- case IEEE80211_S_RUN:
- if (ic->ic_flags & IEEE80211_F_WPA) {
- /* XXX validate prerequisites */
- }
- switch (ostate) {
- case IEEE80211_S_INIT:
- if (ic->ic_opmode == IEEE80211_M_MONITOR ||
- ic->ic_opmode == IEEE80211_M_WDS ||
- ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ KASSERT(!(nscanning && nrunning),
+ ("%d scanning and %d running", nscanning, nrunning));
+ if (nscanning) {
/*
- * Already have a channel; bypass the
- * scan and startup immediately. Because
- * of this explicitly sync the scanner state.
+ * Someone is scanning, defer our state
+ * change until the work has completed.
*/
- ieee80211_scan_update(ic);
- ieee80211_create_ibss(ic,
- ieee80211_ht_adjust_channel(ic,
- ic->ic_curchan, ic->ic_flags_ext));
- break;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: defer %s -> %s\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate]);
+ vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
+ rc = 0;
+ goto done;
}
- /* fall thru... */
- case IEEE80211_S_AUTH:
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "%s: invalid transition\n", __func__);
- /* fall thru... */
- case IEEE80211_S_RUN:
- break;
- case IEEE80211_S_SCAN: /* adhoc/hostap mode */
- case IEEE80211_S_ASSOC: /* infra mode */
- KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates,
- ("%s: bogus xmit rate %u setup\n", __func__,
- ni->ni_txrate));
-#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_debug(ic)) {
- if (ic->ic_opmode == IEEE80211_M_STA)
- if_printf(ifp, "associated ");
+ if (nrunning) {
+ /*
+ * Someone is operating; just join the channel
+ * they have chosen.
+ */
+ /* XXX kill arg? */
+ /* XXX check each opmode, adhoc? */
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ nstate = IEEE80211_S_SCAN;
else
- if_printf(ifp, "synchronized ");
- printf("with %s ssid ",
- ether_sprintf(ni->ni_bssid));
- ieee80211_print_essid(ic->ic_bss->ni_essid,
- ni->ni_esslen);
- printf(" channel %d start %uMb\n",
- ieee80211_chan2ieee(ic, ic->ic_curchan),
- IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate]));
- }
+ nstate = IEEE80211_S_RUN;
+#ifdef IEEE80211_DEBUG
+ if (nstate != IEEE80211_S_SCAN) {
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE,
+ "%s: override, now %s -> %s\n",
+ __func__,
+ ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate]);
+ }
#endif
- if (ic->ic_opmode == IEEE80211_M_STA) {
- ieee80211_scan_assoc_success(ic,
- ni->ni_macaddr);
- ieee80211_notify_node_join(ic, ni,
- arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
}
- if_start(ifp); /* XXX not authorized yet */
- break;
- default:
- break;
+ } else {
+ /*
+ * SCAN was forced; e.g. on beacon miss. Force
+ * other running vap's to INIT state and mark
+ * them as waiting for the scan to complete. This
+ * insures they don't interfere with our scanning.
+ *
+ * XXX not always right, assumes ap follows sta
+ */
+ markwaiting(vap);
}
- if (ostate != IEEE80211_S_RUN &&
- ic->ic_opmode == IEEE80211_M_STA &&
- (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS)) {
+ break;
+ case IEEE80211_S_RUN:
+ if (vap->iv_opmode == IEEE80211_M_WDS &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) &&
+ nscanning) {
/*
- * Start s/w beacon miss timer for devices w/o
- * hardware support. We fudge a bit here since
- * we're doing this in software.
+ * Legacy WDS with someone else scanning; don't
+ * go online until that completes as we should
+ * follow the other vap to the channel they choose.
*/
- ic->ic_swbmiss_period = IEEE80211_TU_TO_TICKS(
- 2 * ic->ic_bmissthreshold * ni->ni_intval);
- ic->ic_swbmiss_count = 0;
- callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period,
- ieee80211_swbmiss, ic);
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: defer %s -> %s (legacy WDS)\n", __func__,
+ ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate]);
+ vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
+ rc = 0;
+ goto done;
}
- /*
- * Start/stop the authenticator when operating as an
- * AP. We delay until here to allow configuration to
- * happen out of order.
- */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */
- ic->ic_auth->ia_attach != NULL) {
- /* XXX check failure */
- ic->ic_auth->ia_attach(ic);
- } else if (ic->ic_auth->ia_detach != NULL) {
- ic->ic_auth->ia_detach(ic);
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
+ IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_DFS) &&
+ !IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) {
+ /*
+ * This is a DFS channel, transition to CAC state
+ * instead of RUN. This allows us to initiate
+ * Channel Availability Check (CAC) as specified
+ * by 11h/DFS.
+ */
+ nstate = IEEE80211_S_CAC;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: override %s -> %s (DFS)\n", __func__,
+ ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate]);
}
- /*
- * When 802.1x is not in use mark the port authorized
- * at this point so traffic can flow.
- */
- if (ni->ni_authmode != IEEE80211_AUTH_8021X)
- ieee80211_node_authorize(ni);
- /*
- * Enable inactivity processing.
- * XXX
- */
- callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
- ieee80211_node_timeout, ic);
break;
+ case IEEE80211_S_INIT:
+ if (ostate == IEEE80211_S_INIT ) {
+ /* XXX don't believe this */
+ /* INIT -> INIT. nothing to do */
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT;
+ }
+ /* fall thru... */
default:
break;
}
- return 0;
+ /* XXX on transition RUN->CAC do we need to set nstate = iv_state? */
+ if (ostate != nstate) {
+ /*
+ * Arrange for work to happen after state change completes.
+ * If this happens asynchronously the caller must arrange
+ * for the com lock to be held.
+ */
+ vap->iv_newstate_cb = ieee80211_newstate_cb;
+ }
+ rc = vap->iv_newstate(vap, nstate, arg);
+ if (rc == 0 && vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, nstate, arg);
+done:
+ return rc;
+}
+
+int
+ieee80211_new_state(struct ieee80211vap *vap,
+ enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ int rc;
+
+ IEEE80211_LOCK(ic);
+ rc = ieee80211_new_state_locked(vap, nstate, arg);
+ IEEE80211_UNLOCK(ic);
+ return rc;
}
diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h
index 9f94f1c..ec7061d 100644
--- a/sys/net80211/ieee80211_proto.h
+++ b/sys/net80211/ieee80211_proto.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -44,63 +44,65 @@ enum ieee80211_state {
};
#define IEEE80211_S_MAX (IEEE80211_S_SLEEP+1)
-#define IEEE80211_SEND_MGMT(_ic,_ni,_type,_arg) \
- ((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg))
-
-/*
- * The formation of some management frames requires guidance to
- * deal with legacy clients. When the client is identified as
- * "legacy 11b" this parameter can be passed in the arg param of a
- * IEEE80211_SEND_MGMT call.
- */
-#define IEEE80211_SEND_LEGACY_11B 0x1 /* legacy 11b client */
-#define IEEE80211_SEND_LEGACY_11 0x2 /* other legacy client */
-#define IEEE80211_SEND_LEGACY 0x3 /* any legacy client */
+#define IEEE80211_SEND_MGMT(_ni,_type,_arg) \
+ ((*(_ni)->ni_ic->ic_send_mgmt)(_ni, _type, _arg))
extern const char *ieee80211_mgt_subtype_name[];
extern const char *ieee80211_phymode_name[];
void ieee80211_proto_attach(struct ieee80211com *);
void ieee80211_proto_detach(struct ieee80211com *);
+void ieee80211_proto_vattach(struct ieee80211vap *);
+void ieee80211_proto_vdetach(struct ieee80211vap *);
+
+void ieee80211_syncifflag_locked(struct ieee80211com *, int flag);
+void ieee80211_syncflag(struct ieee80211vap *, int flag);
+void ieee80211_syncflag_ext(struct ieee80211vap *, int flag);
-struct ieee80211_node;
-int ieee80211_input(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *, int, int, uint32_t);
-void ieee80211_deliver_data(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *);
-struct mbuf *ieee80211_decap1(struct mbuf *, int *);
-int ieee80211_setup_rates(struct ieee80211_node *ni,
- const uint8_t *rates, const uint8_t *xrates, int flags);
-void ieee80211_saveie(uint8_t **, const uint8_t *);
-void ieee80211_saveath(struct ieee80211_node *, uint8_t *);
-void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *, int, int, int, uint32_t);
-int ieee80211_mgmt_output(struct ieee80211com *, struct ieee80211_node *,
- struct mbuf *, int type);
+#define ieee80211_input(ni, m, rssi, noise, rstamp) \
+ ((ni)->ni_vap->iv_input(ni, m, rssi, noise, rstamp))
+int ieee80211_input_all(struct ieee80211com *, struct mbuf *,
+ int, int, uint32_t);
+int ieee80211_mgmt_output(struct ieee80211_node *, struct mbuf *, int);
struct ieee80211_bpf_params;
int ieee80211_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
int ieee80211_output(struct ifnet *, struct mbuf *,
struct sockaddr *, struct rtentry *);
+void ieee80211_start(struct ifnet *);
int ieee80211_send_nulldata(struct ieee80211_node *);
-int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
- int, int);
+int ieee80211_classify(struct ieee80211_node *, struct mbuf *m);
+struct mbuf *ieee80211_encap(struct ieee80211_node *, struct mbuf *);
+int ieee80211_send_mgmt(struct ieee80211_node *, int, int);
+struct ieee80211_appie;
int ieee80211_send_probereq(struct ieee80211_node *ni,
const uint8_t sa[IEEE80211_ADDR_LEN],
const uint8_t da[IEEE80211_ADDR_LEN],
const uint8_t bssid[IEEE80211_ADDR_LEN],
- const uint8_t *ssid, size_t ssidlen,
- const void *optie, size_t optielen);
-int ieee80211_classify(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *);
-struct mbuf *ieee80211_encap(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *);
+ const uint8_t *ssid, size_t ssidlen);
+/*
+ * The formation of ProbeResponse frames requires guidance to
+ * deal with legacy clients. When the client is identified as
+ * "legacy 11b" ieee80211_send_proberesp is passed this token.
+ */
+#define IEEE80211_SEND_LEGACY_11B 0x1 /* legacy 11b client */
+#define IEEE80211_SEND_LEGACY_11 0x2 /* other legacy client */
+#define IEEE80211_SEND_LEGACY 0x3 /* any legacy client */
+struct mbuf *ieee80211_alloc_proberesp(struct ieee80211_node *, int);
+int ieee80211_send_proberesp(struct ieee80211vap *,
+ const uint8_t da[IEEE80211_ADDR_LEN], int);
+struct mbuf *ieee80211_alloc_rts(struct ieee80211com *ic,
+ const uint8_t [IEEE80211_ADDR_LEN],
+ const uint8_t [IEEE80211_ADDR_LEN], uint16_t);
+struct mbuf *ieee80211_alloc_cts(struct ieee80211com *,
+ const uint8_t [IEEE80211_ADDR_LEN], uint16_t);
void ieee80211_reset_erp(struct ieee80211com *);
void ieee80211_set_shortslottime(struct ieee80211com *, int onoff);
-int ieee80211_iserp_rateset(struct ieee80211com *,
- struct ieee80211_rateset *);
-void ieee80211_set11gbasicrates(struct ieee80211_rateset *,
+int ieee80211_iserp_rateset(const struct ieee80211_rateset *);
+void ieee80211_setbasicrates(struct ieee80211_rateset *,
+ enum ieee80211_phymode);
+void ieee80211_addbasicrates(struct ieee80211_rateset *,
enum ieee80211_phymode);
/*
@@ -146,16 +148,16 @@ ieee80211_anyhdrsize(const void *data)
/*
* Template for an in-kernel authenticator. Authenticators
* register with the protocol code and are typically loaded
- * as separate modules as needed.
+ * as separate modules as needed. One special authenticator
+ * is xauth; it intercepts requests so that protocols like
+ * WPA can be handled in user space.
*/
struct ieee80211_authenticator {
const char *ia_name; /* printable name */
- int (*ia_attach)(struct ieee80211com *);
- void (*ia_detach)(struct ieee80211com *);
- void (*ia_node_join)(struct ieee80211com *,
- struct ieee80211_node *);
- void (*ia_node_leave)(struct ieee80211com *,
- struct ieee80211_node *);
+ int (*ia_attach)(struct ieee80211vap *);
+ void (*ia_detach)(struct ieee80211vap *);
+ void (*ia_node_join)(struct ieee80211_node *);
+ void (*ia_node_leave)(struct ieee80211_node *);
};
void ieee80211_authenticator_register(int type,
const struct ieee80211_authenticator *);
@@ -166,23 +168,23 @@ struct ieee80211req;
/*
* Template for an MAC ACL policy module. Such modules
* register with the protocol code and are passed the sender's
- * address of each received frame for validation.
+ * address of each received auth frame for validation.
*/
struct ieee80211_aclator {
const char *iac_name; /* printable name */
- int (*iac_attach)(struct ieee80211com *);
- void (*iac_detach)(struct ieee80211com *);
- int (*iac_check)(struct ieee80211com *,
+ int (*iac_attach)(struct ieee80211vap *);
+ void (*iac_detach)(struct ieee80211vap *);
+ int (*iac_check)(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
- int (*iac_add)(struct ieee80211com *,
+ int (*iac_add)(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
- int (*iac_remove)(struct ieee80211com *,
+ int (*iac_remove)(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
- int (*iac_flush)(struct ieee80211com *);
- int (*iac_setpolicy)(struct ieee80211com *, int);
- int (*iac_getpolicy)(struct ieee80211com *);
- int (*iac_setioctl)(struct ieee80211com *, struct ieee80211req *);
- int (*iac_getioctl)(struct ieee80211com *, struct ieee80211req *);
+ int (*iac_flush)(struct ieee80211vap *);
+ int (*iac_setpolicy)(struct ieee80211vap *, int);
+ int (*iac_getpolicy)(struct ieee80211vap *);
+ int (*iac_setioctl)(struct ieee80211vap *, struct ieee80211req *);
+ int (*iac_getioctl)(struct ieee80211vap *, struct ieee80211req *);
};
void ieee80211_aclator_register(const struct ieee80211_aclator *);
void ieee80211_aclator_unregister(const struct ieee80211_aclator *);
@@ -190,11 +192,12 @@ const struct ieee80211_aclator *ieee80211_aclator_get(const char *name);
/* flags for ieee80211_fix_rate() */
#define IEEE80211_F_DOSORT 0x00000001 /* sort rate list */
-#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed rate */
+#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed legacy rate */
#define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */
#define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */
#define IEEE80211_F_DOBRS 0x00000010 /* check basic rate set */
#define IEEE80211_F_JOIN 0x00000020 /* sta joining our bss */
+#define IEEE80211_F_DOFMCS 0x00000040 /* use fixed HT rate */
int ieee80211_fix_rate(struct ieee80211_node *,
struct ieee80211_rateset *, int);
@@ -233,15 +236,38 @@ struct ieee80211_wme_state {
int (*wme_update)(struct ieee80211com *);
};
-void ieee80211_wme_initparams(struct ieee80211com *);
-void ieee80211_wme_updateparams(struct ieee80211com *);
-void ieee80211_wme_updateparams_locked(struct ieee80211com *);
+void ieee80211_wme_initparams(struct ieee80211vap *);
+void ieee80211_wme_updateparams(struct ieee80211vap *);
+void ieee80211_wme_updateparams_locked(struct ieee80211vap *);
+
+/*
+ * Return the WME TID from a QoS frame. If no TID
+ * is present return the index for the "non-QoS" entry.
+ */
+static __inline uint8_t
+ieee80211_gettid(const struct ieee80211_frame *wh)
+{
+ uint8_t tid;
+
+ if (IEEE80211_QOS_HAS_SEQ(wh)) {
+ tid = ((const struct ieee80211_qosframe *)wh)->
+ i_qos[0] & IEEE80211_QOS_TID;
+ tid++;
+ } else
+ tid = IEEE80211_NONQOS_TID;
+ return tid;
+}
-#define ieee80211_new_state(_ic, _nstate, _arg) \
- (((_ic)->ic_newstate)((_ic), (_nstate), (_arg)))
-int ieee80211_init(struct ieee80211com *, int forcescan);
-void ieee80211_dturbo_switch(struct ieee80211com *, int newflags);
+void ieee80211_start_locked(struct ieee80211vap *);
+void ieee80211_init(void *);
+void ieee80211_start_all(struct ieee80211com *);
+void ieee80211_stop_locked(struct ieee80211vap *);
+void ieee80211_stop(struct ieee80211vap *);
+void ieee80211_stop_all(struct ieee80211com *);
+void ieee80211_dturbo_switch(struct ieee80211vap *, int newflags);
+void ieee80211_swbmiss(void *arg);
void ieee80211_beacon_miss(struct ieee80211com *);
+int ieee80211_new_state(struct ieee80211vap *, enum ieee80211_state, int);
void ieee80211_print_essid(const uint8_t *, int);
void ieee80211_dump_pkt(struct ieee80211com *,
const uint8_t *, int, int, int);
@@ -275,7 +301,7 @@ struct mbuf *ieee80211_beacon_alloc(struct ieee80211_node *,
struct ieee80211_beacon_offsets *);
/*
- * Beacon frame updates are signaled through calls to ic_update_beacon
+ * Beacon frame updates are signaled through calls to iv_update_beacon
* with one of the IEEE80211_BEACON_* tokens defined below. For devices
* that construct beacon frames on the host this can trigger a rebuild
* or defer the processing. For devices that offload beacon frame
@@ -283,7 +309,7 @@ struct mbuf *ieee80211_beacon_alloc(struct ieee80211_node *,
* array in the ieee80211_beacon_offsets structure is intended to record
* deferred processing requirements; ieee80211_beacon_update uses the
* state to optimize work. Since this structure is owned by the driver
- * and not visible to the 802.11 layer drivers must supply an ic_update_beacon
+ * and not visible to the 802.11 layer drivers must supply an iv_update_beacon
* callback that marks the flag bits and schedules (as necessary) an update.
*/
enum {
@@ -299,14 +325,36 @@ enum {
int ieee80211_beacon_update(struct ieee80211_node *,
struct ieee80211_beacon_offsets *, struct mbuf *, int mcast);
+void ieee80211_csa_startswitch(struct ieee80211com *,
+ struct ieee80211_channel *, int mode, int count);
+void ieee80211_csa_completeswitch(struct ieee80211com *);
+void ieee80211_cac_completeswitch(struct ieee80211vap *);
+
/*
* Notification methods called from the 802.11 state machine.
* Note that while these are defined here, their implementation
* is OS-specific.
*/
-void ieee80211_notify_node_join(struct ieee80211com *,
- struct ieee80211_node *, int newassoc);
-void ieee80211_notify_node_leave(struct ieee80211com *,
- struct ieee80211_node *);
-void ieee80211_notify_scan_done(struct ieee80211com *);
+void ieee80211_notify_node_join(struct ieee80211_node *, int newassoc);
+void ieee80211_notify_node_leave(struct ieee80211_node *);
+void ieee80211_notify_scan_done(struct ieee80211vap *);
+void ieee80211_notify_wds_discover(struct ieee80211_node *);
+void ieee80211_notify_csa(struct ieee80211com *,
+ const struct ieee80211_channel *, int mode, int count);
+void ieee80211_notify_radar(struct ieee80211com *,
+ const struct ieee80211_channel *);
+enum ieee80211_notify_cac_event {
+ IEEE80211_NOTIFY_CAC_START = 0, /* CAC timer started */
+ IEEE80211_NOTIFY_CAC_STOP = 1, /* CAC intentionally stopped */
+ IEEE80211_NOTIFY_CAC_RADAR = 2, /* CAC stopped due to radar detectio */
+ IEEE80211_NOTIFY_CAC_EXPIRE = 3, /* CAC expired w/o radar */
+};
+void ieee80211_notify_cac(struct ieee80211com *,
+ const struct ieee80211_channel *,
+ enum ieee80211_notify_cac_event);
+void ieee80211_notify_node_deauth(struct ieee80211_node *);
+void ieee80211_notify_node_auth(struct ieee80211_node *);
+void ieee80211_notify_country(struct ieee80211vap *, const uint8_t [],
+ const uint8_t cc[2]);
+void ieee80211_notify_radio(struct ieee80211com *, int);
#endif /* _NET80211_IEEE80211_PROTO_H_ */
diff --git a/sys/net80211/ieee80211_regdomain.c b/sys/net80211/ieee80211_regdomain.c
index 7f1b3dc..4cf2dc0 100644
--- a/sys/net80211/ieee80211_regdomain.c
+++ b/sys/net80211/ieee80211_regdomain.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2005-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,7 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 regdomain support.
*/
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -37,26 +38,60 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <net/if.h>
-#include <net/if_arp.h>
-#include <net/if_dl.h>
#include <net/if_media.h>
-#include <net/if_types.h>
-#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_regdomain.h>
+static void
+null_getradiocaps(struct ieee80211com *ic, int *n, struct ieee80211_channel *c)
+{
+ /* just feed back the current channel list */
+ *n = ic->ic_nchans;
+ memcpy(c, ic->ic_channels,
+ ic->ic_nchans*sizeof(struct ieee80211_channel));
+}
+
+static int
+null_setregdomain(struct ieee80211com *ic,
+ struct ieee80211_regdomain *rd,
+ int nchans, struct ieee80211_channel chans[])
+{
+ return 0; /* accept anything */
+}
+
void
ieee80211_regdomain_attach(struct ieee80211com *ic)
{
- ic->ic_regdomain = 0; /* XXX */
- ic->ic_countrycode = CTRY_UNITED_STATES;/* XXX */
- ic->ic_location = 1+2; /* both */
+ if (ic->ic_regdomain.regdomain == 0 &&
+ ic->ic_regdomain.country == CTRY_DEFAULT) {
+ ic->ic_regdomain.country = CTRY_UNITED_STATES; /* XXX */
+ ic->ic_regdomain.location = ' '; /* both */
+ ic->ic_regdomain.isocc[0] = 'U'; /* XXX */
+ ic->ic_regdomain.isocc[1] = 'S'; /* XXX */
+ /* XXX? too late to setup default channel list */
+ }
+ ic->ic_getradiocaps = null_getradiocaps;
+ ic->ic_setregdomain = null_setregdomain;
}
void
ieee80211_regdomain_detach(struct ieee80211com *ic)
{
+ if (ic->ic_countryie != NULL) {
+ free(ic->ic_countryie, M_80211_NODE_IE);
+ ic->ic_countryie = NULL;
+ }
+}
+
+void
+ieee80211_regdomain_vattach(struct ieee80211vap *vap)
+{
+}
+
+void
+ieee80211_regdomain_vdetach(struct ieee80211vap *vap)
+{
}
static void
@@ -68,6 +103,7 @@ addchan(struct ieee80211com *ic, int ieee, int flags)
c->ic_freq = ieee80211_ieee2mhz(ieee, flags);
c->ic_ieee = ieee;
c->ic_flags = flags;
+ c->ic_extieee = 0;
}
/*
@@ -76,24 +112,27 @@ addchan(struct ieee80211com *ic, int ieee, int flags)
* when a driver does not obtain the channel list from another
* source (such as firmware).
*/
-void
+int
ieee80211_init_channels(struct ieee80211com *ic,
- int rd, enum ISOCountryCode cc, int bands, int outdoor, int ecm)
+ const struct ieee80211_regdomain *rd, const uint8_t bands[])
{
int i;
/* XXX just do something for now */
ic->ic_nchans = 0;
- if (isset(&bands, IEEE80211_MODE_11B) ||
- isset(&bands, IEEE80211_MODE_11G)) {
- for (i = 1; i <= (ecm ? 14 : 11); i++) {
- if (isset(&bands, IEEE80211_MODE_11B))
+ if (isset(bands, IEEE80211_MODE_11B) ||
+ isset(bands, IEEE80211_MODE_11G)) {
+ int maxchan = 11;
+ if (rd != NULL && rd->ecm)
+ maxchan = 14;
+ for (i = 1; i <= maxchan; i++) {
+ if (isset(bands, IEEE80211_MODE_11B))
addchan(ic, i, IEEE80211_CHAN_B);
- if (isset(&bands, IEEE80211_MODE_11G))
+ if (isset(bands, IEEE80211_MODE_11G))
addchan(ic, i, IEEE80211_CHAN_G);
}
}
- if (isset(&bands, IEEE80211_MODE_11A)) {
+ if (isset(bands, IEEE80211_MODE_11A)) {
for (i = 36; i <= 64; i += 4)
addchan(ic, i, IEEE80211_CHAN_A);
for (i = 100; i <= 140; i += 4)
@@ -101,17 +140,73 @@ ieee80211_init_channels(struct ieee80211com *ic,
for (i = 149; i <= 161; i += 4)
addchan(ic, i, IEEE80211_CHAN_A);
}
- ic->ic_regdomain = rd;
- ic->ic_countrycode = cc;
- ic->ic_location = outdoor;
+ if (rd != NULL)
+ ic->ic_regdomain = *rd;
+
+ return 0;
+}
+
+static __inline int
+chancompar(const void *a, const void *b)
+{
+ const struct ieee80211_channel *ca = a;
+ const struct ieee80211_channel *cb = b;
+
+ return (ca->ic_freq == cb->ic_freq) ?
+ (ca->ic_flags & IEEE80211_CHAN_ALL) -
+ (cb->ic_flags & IEEE80211_CHAN_ALL) :
+ ca->ic_freq - cb->ic_freq;
+}
+
+/*
+ * Insertion sort.
+ */
+#define swap(_a, _b, _size) { \
+ uint8_t *s = _b; \
+ int i = _size; \
+ do { \
+ uint8_t tmp = *_a; \
+ *_a++ = *s; \
+ *s++ = tmp; \
+ } while (--i); \
+ _a -= _size; \
+}
+
+static void
+sort_channels(void *a, size_t n, size_t size)
+{
+ uint8_t *aa = a;
+ uint8_t *ai, *t;
+
+ KASSERT(n > 0, ("no channels"));
+ for (ai = aa+size; --n >= 1; ai += size)
+ for (t = ai; t > aa; t -= size) {
+ uint8_t *u = t - size;
+ if (chancompar(u, t) <= 0)
+ break;
+ swap(u, t, size);
+ }
}
+#undef swap
/*
- * Add Country Information IE.
+ * Order channels w/ the same frequency so that
+ * b < g < htg and a < hta. This is used to optimize
+ * channel table lookups and some user applications
+ * may also depend on it (though they should not).
*/
-uint8_t *
-ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic,
- enum ISOCountryCode cc, int location)
+void
+ieee80211_sort_channels(struct ieee80211_channel chans[], int nchans)
+{
+ if (nchans > 0)
+ sort_channels(chans, nchans, sizeof(struct ieee80211_channel));
+}
+
+/*
+ * Allocate and construct a Country Information IE.
+ */
+struct ieee80211_appie *
+ieee80211_alloc_countryie(struct ieee80211com *ic)
{
#define CHAN_UNINTERESTING \
(IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO | \
@@ -131,35 +226,46 @@ ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic,
CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ, /* MODE_11NA */
CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ, /* MODE_11NG */
};
- struct ieee80211_country_ie *ie = (struct ieee80211_country_ie *)frm;
- const char *iso_name;
- uint8_t nextchan, chans[IEEE80211_CHAN_BYTES];
- int i, skip;
+ const struct ieee80211_regdomain *rd = &ic->ic_regdomain;
+ uint8_t nextchan, chans[IEEE80211_CHAN_BYTES], *frm;
+ struct ieee80211_appie *aie;
+ struct ieee80211_country_ie *ie;
+ int i, skip, nruns;
+ aie = malloc(IEEE80211_COUNTRY_MAX_SIZE, M_80211_NODE_IE,
+ M_NOWAIT | M_ZERO);
+ if (aie == NULL) {
+ if_printf(ic->ic_ifp,
+ "%s: unable to allocate memory for country ie\n", __func__);
+ /* XXX stat */
+ return NULL;
+ }
+ ie = (struct ieee80211_country_ie *) aie->ie_data;
ie->ie = IEEE80211_ELEMID_COUNTRY;
- iso_name = ieee80211_cctoiso(cc);
- if (iso_name == NULL) {
- if_printf(ic->ic_ifp, "bad country code %d ignored\n", cc);
- iso_name = " ";
+ if (rd->isocc[0] == '\0') {
+ if_printf(ic->ic_ifp, "no ISO country string for cc %d; "
+ "using blanks\n", rd->country);
+ ie->cc[0] = ie->cc[1] = ' ';
+ } else {
+ ie->cc[0] = rd->isocc[0];
+ ie->cc[1] = rd->isocc[1];
}
- ie->cc[0] = iso_name[0];
- ie->cc[1] = iso_name[1];
/*
- * Indoor/Outdoor portion of country string.
- * NB: this is not quite right, since we should have one of:
+ * Indoor/Outdoor portion of country string:
* 'I' indoor only
* 'O' outdoor only
* ' ' all enviroments
*/
- ie->cc[2] = ((location & 3) == 3 ? ' ' : location & 1 ? 'I' : 'O');
-
+ ie->cc[2] = (rd->location == 'I' ? 'I' :
+ rd->location == 'O' ? 'O' : ' ');
/*
* Run-length encoded channel+max tx power info.
*/
frm = (uint8_t *)&ie->band[0];
nextchan = 0; /* NB: impossible channel # */
+ nruns = 0;
memset(chans, 0, sizeof(chans));
- skip = skipflags[ic->ic_curmode];
+ skip = skipflags[ieee80211_chan2mode(ic->ic_bsschan)];
for (i = 0; i < ic->ic_nchans; i++) {
const struct ieee80211_channel *c = &ic->ic_channels[i];
@@ -170,12 +276,19 @@ ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic,
setbit(chans, c->ic_ieee);
if (c->ic_ieee != nextchan ||
c->ic_maxregpower != frm[-1]) { /* new run */
- /* XXX max of 83 runs */
+ if (nruns == IEEE80211_COUNTRY_MAX_BANDS) {
+ if_printf(ic->ic_ifp, "%s: country ie too big, "
+ "runs > max %d, truncating\n",
+ __func__, IEEE80211_COUNTRY_MAX_BANDS);
+ /* XXX stat? fail? */
+ break;
+ }
frm[0] = c->ic_ieee; /* starting channel # */
frm[1] = 1; /* # channels in run */
frm[2] = c->ic_maxregpower; /* tx power cap */
frm += 3;
nextchan = c->ic_ieee + 1; /* overflow? */
+ nruns++;
} else { /* extend run */
frm[-2]++;
nextchan++;
@@ -186,154 +299,114 @@ ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic,
ie->len++;
*frm++ = 0;
}
- return frm;
+ aie->ie_len = frm - aie->ie_data;
+
+ return aie;
#undef CHAN_UNINTERESTING
}
-/*
- * Country Code Table for code-to-string conversion.
- */
-static const struct {
- enum ISOCountryCode iso_code;
- const char* iso_name;
-} country_strings[] = {
- { CTRY_DEBUG, "DB" }, /* NB: nonstandard */
- { CTRY_DEFAULT, "NA" }, /* NB: nonstandard */
- { CTRY_ALBANIA, "AL" },
- { CTRY_ALGERIA, "DZ" },
- { CTRY_ARGENTINA, "AR" },
- { CTRY_ARMENIA, "AM" },
- { CTRY_AUSTRALIA, "AU" },
- { CTRY_AUSTRIA, "AT" },
- { CTRY_AZERBAIJAN, "AZ" },
- { CTRY_BAHRAIN, "BH" },
- { CTRY_BELARUS, "BY" },
- { CTRY_BELGIUM, "BE" },
- { CTRY_BELIZE, "BZ" },
- { CTRY_BOLIVIA, "BO" },
- { CTRY_BRAZIL, "BR" },
- { CTRY_BRUNEI_DARUSSALAM, "BN" },
- { CTRY_BULGARIA, "BG" },
- { CTRY_CANADA, "CA" },
- { CTRY_CHILE, "CL" },
- { CTRY_CHINA, "CN" },
- { CTRY_COLOMBIA, "CO" },
- { CTRY_COSTA_RICA, "CR" },
- { CTRY_CROATIA, "HR" },
- { CTRY_CYPRUS, "CY" },
- { CTRY_CZECH, "CZ" },
- { CTRY_DENMARK, "DK" },
- { CTRY_DOMINICAN_REPUBLIC, "DO" },
- { CTRY_ECUADOR, "EC" },
- { CTRY_EGYPT, "EG" },
- { CTRY_EL_SALVADOR, "SV" },
- { CTRY_ESTONIA, "EE" },
- { CTRY_FINLAND, "FI" },
- { CTRY_FRANCE, "FR" },
- { CTRY_FRANCE2, "F2" },
- { CTRY_GEORGIA, "GE" },
- { CTRY_GERMANY, "DE" },
- { CTRY_GREECE, "GR" },
- { CTRY_GUATEMALA, "GT" },
- { CTRY_HONDURAS, "HN" },
- { CTRY_HONG_KONG, "HK" },
- { CTRY_HUNGARY, "HU" },
- { CTRY_ICELAND, "IS" },
- { CTRY_INDIA, "IN" },
- { CTRY_INDONESIA, "ID" },
- { CTRY_IRAN, "IR" },
- { CTRY_IRELAND, "IE" },
- { CTRY_ISRAEL, "IL" },
- { CTRY_ITALY, "IT" },
- { CTRY_JAMAICA, "JM" },
- { CTRY_JAPAN, "JP" },
- { CTRY_JAPAN1, "J1" },
- { CTRY_JAPAN2, "J2" },
- { CTRY_JAPAN3, "J3" },
- { CTRY_JAPAN4, "J4" },
- { CTRY_JAPAN5, "J5" },
- { CTRY_JORDAN, "JO" },
- { CTRY_KAZAKHSTAN, "KZ" },
- { CTRY_KOREA_NORTH, "KP" },
- { CTRY_KOREA_ROC, "KR" },
- { CTRY_KOREA_ROC2, "K2" },
- { CTRY_KUWAIT, "KW" },
- { CTRY_LATVIA, "LV" },
- { CTRY_LEBANON, "LB" },
- { CTRY_LIECHTENSTEIN, "LI" },
- { CTRY_LITHUANIA, "LT" },
- { CTRY_LUXEMBOURG, "LU" },
- { CTRY_MACAU, "MO" },
- { CTRY_MACEDONIA, "MK" },
- { CTRY_MALAYSIA, "MY" },
- { CTRY_MEXICO, "MX" },
- { CTRY_MONACO, "MC" },
- { CTRY_MOROCCO, "MA" },
- { CTRY_NETHERLANDS, "NL" },
- { CTRY_NEW_ZEALAND, "NZ" },
- { CTRY_NORWAY, "NO" },
- { CTRY_OMAN, "OM" },
- { CTRY_PAKISTAN, "PK" },
- { CTRY_PANAMA, "PA" },
- { CTRY_PERU, "PE" },
- { CTRY_PHILIPPINES, "PH" },
- { CTRY_POLAND, "PL" },
- { CTRY_PORTUGAL, "PT" },
- { CTRY_PUERTO_RICO, "PR" },
- { CTRY_QATAR, "QA" },
- { CTRY_ROMANIA, "RO" },
- { CTRY_RUSSIA, "RU" },
- { CTRY_SAUDI_ARABIA, "SA" },
- { CTRY_SINGAPORE, "SG" },
- { CTRY_SLOVAKIA, "SK" },
- { CTRY_SLOVENIA, "SI" },
- { CTRY_SOUTH_AFRICA, "ZA" },
- { CTRY_SPAIN, "ES" },
- { CTRY_SWEDEN, "SE" },
- { CTRY_SWITZERLAND, "CH" },
- { CTRY_SYRIA, "SY" },
- { CTRY_TAIWAN, "TW" },
- { CTRY_THAILAND, "TH" },
- { CTRY_TRINIDAD_Y_TOBAGO, "TT" },
- { CTRY_TUNISIA, "TN" },
- { CTRY_TURKEY, "TR" },
- { CTRY_UKRAINE, "UA" },
- { CTRY_UAE, "AE" },
- { CTRY_UNITED_KINGDOM, "GB" },
- { CTRY_UNITED_STATES, "US" },
- { CTRY_URUGUAY, "UY" },
- { CTRY_UZBEKISTAN, "UZ" },
- { CTRY_VENEZUELA, "VE" },
- { CTRY_VIET_NAM, "VN" },
- { CTRY_YEMEN, "YE" },
- { CTRY_ZIMBABWE, "ZW" }
-};
-
-const char *
-ieee80211_cctoiso(enum ISOCountryCode cc)
+static int
+allvapsdown(struct ieee80211com *ic)
{
-#define N(a) (sizeof(a) / sizeof(a[0]))
- int i;
+ struct ieee80211vap *vap;
- for (i = 0; i < N(country_strings); i++) {
- if (country_strings[i].iso_code == cc)
- return country_strings[i].iso_name;
- }
- return NULL;
-#undef N
+ IEEE80211_LOCK_ASSERT(ic);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_state != IEEE80211_S_INIT)
+ return 0;
+ return 1;
}
int
-ieee80211_isotocc(const char iso[2])
+ieee80211_setregdomain(struct ieee80211vap *vap,
+ struct ieee80211_regdomain_req *reg)
{
-#define N(a) (sizeof(a) / sizeof(a[0]))
- int i;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_channel *c;
+ int desfreq = 0, desflags = 0; /* XXX silence gcc complaint */
+ int error, i;
- for (i = 0; i < N(country_strings); i++) {
- if (country_strings[i].iso_name[0] == iso[0] &&
- country_strings[i].iso_name[1] == iso[1])
- return country_strings[i].iso_code;
+ if (reg->rd.location != 'I' && reg->rd.location != 'O' &&
+ reg->rd.location != ' ')
+ return EINVAL;
+ if (reg->rd.isocc[0] == '\0' || reg->rd.isocc[1] == '\0')
+ return EINVAL;
+ if (reg->chaninfo.ic_nchans >= IEEE80211_CHAN_MAX)
+ return EINVAL;
+ /*
+ * Calculate freq<->IEEE mapping and default max tx power
+ * for channels not setup. The driver can override these
+ * setting to reflect device properties/requirements.
+ */
+ for (i = 0; i < reg->chaninfo.ic_nchans; i++) {
+ c = &reg->chaninfo.ic_chans[i];
+ if (c->ic_freq == 0 || c->ic_flags == 0)
+ return EINVAL;
+ if (c->ic_maxregpower == 0)
+ return EINVAL;
+ if (c->ic_ieee == 0)
+ c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags);
+ if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0)
+ c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq +
+ (IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20),
+ c->ic_flags);
+ if (c->ic_maxpower == 0)
+ c->ic_maxpower = 2*c->ic_maxregpower;
+ }
+ IEEE80211_LOCK(ic);
+ error = ic->ic_setregdomain(ic, &reg->rd,
+ reg->chaninfo.ic_nchans, reg->chaninfo.ic_chans);
+ if (error != 0) {
+ IEEE80211_UNLOCK(ic);
+ return error;
}
- return -1;
-#undef N
+ /* XXX bandaid; a running vap will likely crash */
+ if (!allvapsdown(ic)) {
+ IEEE80211_UNLOCK(ic);
+ return EBUSY;
+ }
+ /*
+ * Commit: copy in new channel table and reset media state.
+ * On return the state machines will be clocked so all vaps
+ * will reset their state.
+ *
+ * XXX ic_bsschan is marked undefined, must have vap's in
+ * INIT state or we blow up forcing stations off
+ */
+ /*
+ * Save any desired channel for restore below. Note this
+ * needs to be done for all vaps but for now we only do
+ * the one where the ioctl is issued.
+ */
+ if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
+ desfreq = vap->iv_des_chan->ic_freq;
+ desflags = vap->iv_des_chan->ic_flags;
+ }
+ /* regdomain parameters */
+ memcpy(&ic->ic_regdomain, &reg->rd, sizeof(reg->rd));
+ /* channel table */
+ memcpy(ic->ic_channels, reg->chaninfo.ic_chans,
+ reg->chaninfo.ic_nchans * sizeof(struct ieee80211_channel));
+ ic->ic_nchans = reg->chaninfo.ic_nchans;
+ memset(&ic->ic_channels[ic->ic_nchans], 0,
+ (IEEE80211_CHAN_MAX - ic->ic_nchans) *
+ sizeof(struct ieee80211_channel));
+ ieee80211_media_init(ic);
+
+ /*
+ * Invalidate channel-related state.
+ */
+ if (ic->ic_countryie != NULL) {
+ free(ic->ic_countryie, M_80211_NODE_IE);
+ ic->ic_countryie = NULL;
+ }
+ ieee80211_scan_flush(vap);
+ ieee80211_dfs_reset(ic);
+ if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
+ /* NB: may be NULL if not present in new channel list */
+ vap->iv_des_chan = ieee80211_find_channel(ic, desfreq, desflags);
+ }
+ IEEE80211_UNLOCK(ic);
+
+ return 0;
}
diff --git a/sys/net80211/ieee80211_regdomain.h b/sys/net80211/ieee80211_regdomain.h
index 9c1345e..c9c0823 100644
--- a/sys/net80211/ieee80211_regdomain.h
+++ b/sys/net80211/ieee80211_regdomain.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2005-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -43,42 +43,92 @@ enum ISOCountryCode {
CTRY_ANDORRA = 20,
CTRY_ANGOLA = 24,
CTRY_ANGUILLA = 660,
- /* XXX correct remainder */
+ CTRY_ANTARTICA = 10,
+ CTRY_ANTIGUA = 28, /* Antigua and Barbuda */
CTRY_ARGENTINA = 32, /* Argentina */
CTRY_ARMENIA = 51, /* Armenia */
+ CTRY_ARUBA = 533, /* Aruba */
CTRY_AUSTRALIA = 36, /* Australia */
CTRY_AUSTRIA = 40, /* Austria */
CTRY_AZERBAIJAN = 31, /* Azerbaijan */
+ CTRY_BAHAMAS = 44, /* Bahamas */
CTRY_BAHRAIN = 48, /* Bahrain */
+ CTRY_BANGLADESH = 50, /* Bangladesh */
+ CTRY_BARBADOS = 52,
CTRY_BELARUS = 112, /* Belarus */
CTRY_BELGIUM = 56, /* Belgium */
- CTRY_BELIZE = 84, /* Belize */
+ CTRY_BELIZE = 84,
+ CTRY_BENIN = 204,
+ CTRY_BERMUDA = 60,
+ CTRY_BHUTAN = 64,
CTRY_BOLIVIA = 68, /* Bolivia */
+ CTRY_BOSNIA_AND_HERZEGOWINA = 70,
+ CTRY_BOTSWANA = 72,
+ CTRY_BOUVET_ISLAND = 74,
CTRY_BRAZIL = 76, /* Brazil */
+ CTRY_BRITISH_INDIAN_OCEAN_TERRITORY = 86,
CTRY_BRUNEI_DARUSSALAM = 96, /* Brunei Darussalam */
CTRY_BULGARIA = 100, /* Bulgaria */
+ CTRY_BURKINA_FASO = 854,
+ CTRY_BURUNDI = 108,
+ CTRY_CAMBODIA = 116,
+ CTRY_CAMEROON = 120,
CTRY_CANADA = 124, /* Canada */
+ CTRY_CAPE_VERDE = 132,
+ CTRY_CAYMAN_ISLANDS = 136,
+ CTRY_CENTRAL_AFRICAN_REPUBLIC = 140,
+ CTRY_CHAD = 148,
CTRY_CHILE = 152, /* Chile */
CTRY_CHINA = 156, /* People's Republic of China */
+ CTRY_CHRISTMAS_ISLAND = 162,
+ CTRY_COCOS_ISLANDS = 166,
CTRY_COLOMBIA = 170, /* Colombia */
+ CTRY_COMOROS = 174,
+ CTRY_CONGO = 178,
+ CTRY_COOK_ISLANDS = 184,
CTRY_COSTA_RICA = 188, /* Costa Rica */
- CTRY_CROATIA = 191, /* Croatia */
+ CTRY_COTE_DIVOIRE = 384,
+ CTRY_CROATIA = 191, /* Croatia (local name: Hrvatska) */
CTRY_CYPRUS = 196, /* Cyprus */
CTRY_CZECH = 203, /* Czech Republic */
CTRY_DENMARK = 208, /* Denmark */
+ CTRY_DJIBOUTI = 262,
+ CTRY_DOMINICA = 212,
CTRY_DOMINICAN_REPUBLIC = 214, /* Dominican Republic */
+ CTRY_EAST_TIMOR = 626,
CTRY_ECUADOR = 218, /* Ecuador */
CTRY_EGYPT = 818, /* Egypt */
CTRY_EL_SALVADOR = 222, /* El Salvador */
+ CTRY_EQUATORIAL_GUINEA = 226,
+ CTRY_ERITREA = 232,
CTRY_ESTONIA = 233, /* Estonia */
+ CTRY_ETHIOPIA = 210,
+ CTRY_FALKLAND_ISLANDS = 238, /* (Malvinas) */
CTRY_FAEROE_ISLANDS = 234, /* Faeroe Islands */
+ CTRY_FIJI = 242,
CTRY_FINLAND = 246, /* Finland */
CTRY_FRANCE = 250, /* France */
- CTRY_FRANCE2 = 255, /* France2 */
+ CTRY_FRANCE2 = 255, /* France (Metropolitan) */
+ CTRY_FRENCH_GUIANA = 254,
+ CTRY_FRENCH_POLYNESIA = 258,
+ CTRY_FRENCH_SOUTHERN_TERRITORIES = 260,
+ CTRY_GABON = 266,
+ CTRY_GAMBIA = 270,
CTRY_GEORGIA = 268, /* Georgia */
CTRY_GERMANY = 276, /* Germany */
+ CTRY_GHANA = 288,
+ CTRY_GIBRALTAR = 292,
CTRY_GREECE = 300, /* Greece */
+ CTRY_GREENLAND = 304,
+ CTRY_GRENADA = 308,
+ CTRY_GUADELOUPE = 312,
+ CTRY_GUAM = 316,
CTRY_GUATEMALA = 320, /* Guatemala */
+ CTRY_GUINEA = 324,
+ CTRY_GUINEA_BISSAU = 624,
+ CTRY_GUYANA = 328,
+ /* XXX correct remainder */
+ CTRY_HAITI = 332,
CTRY_HONDURAS = 340, /* Honduras */
CTRY_HONG_KONG = 344, /* Hong Kong S.A.R., P.R.C. */
CTRY_HUNGARY = 348, /* Hungary */
@@ -113,9 +163,11 @@ enum ISOCountryCode {
CTRY_MACAU = 446, /* Macau */
CTRY_MACEDONIA = 807, /* the Former Yugoslav Republic of Macedonia */
CTRY_MALAYSIA = 458, /* Malaysia */
+ CTRY_MALTA = 470, /* Malta */
CTRY_MEXICO = 484, /* Mexico */
CTRY_MONACO = 492, /* Principality of Monaco */
CTRY_MOROCCO = 504, /* Morocco */
+ CTRY_NEPAL = 524, /* Nepal */
CTRY_NETHERLANDS = 528, /* Netherlands */
CTRY_NEW_ZEALAND = 554, /* New Zealand */
CTRY_NICARAGUA = 558, /* Nicaragua */
@@ -138,6 +190,7 @@ enum ISOCountryCode {
CTRY_SLOVENIA = 705, /* Slovenia */
CTRY_SOUTH_AFRICA = 710, /* South Africa */
CTRY_SPAIN = 724, /* Spain */
+ CTRY_SRILANKA = 144, /* Sri Lanka */
CTRY_SWEDEN = 752, /* Sweden */
CTRY_SWITZERLAND = 756, /* Switzerland */
CTRY_SYRIA = 760, /* Syria */
@@ -158,18 +211,39 @@ enum ISOCountryCode {
CTRY_ZIMBABWE = 716, /* Zimbabwe */
};
+enum RegdomainCode {
+ SKU_FCC = 0x10, /* FCC, aka United States */
+ SKU_CA = 0x20, /* North America, aka Canada */
+ SKU_ETSI = 0x30, /* Europe */
+ SKU_ETSI2 = 0x32, /* Europe w/o HT40 in 5GHz */
+ SKU_ETSI3 = 0x33, /* Europe - channel 36 */
+ SKU_FCC3 = 0x3a, /* FCC w/5470 band, 11h, DFS */
+ SKU_JAPAN = 0x40,
+ SKU_KOREA = 0x45,
+ SKU_APAC = 0x50, /* Asia Pacific */
+ SKU_APAC2 = 0x51, /* Asia Pacific w/ DFS on mid-band */
+ SKU_APAC3 = 0x5d, /* Asia Pacific w/o ISM band */
+ SKU_ROW = 0x81, /* China/Taiwan/Rest of World */
+ SKU_NONE = 0xf0, /* "Region Free" */
+ SKU_DEBUG = 0x1ff
+};
+
#if defined(__KERNEL__) || defined(_KERNEL)
#define CTRY_DEBUG 0x1ff /* debug */
#define CTRY_DEFAULT 0 /* default */
void ieee80211_regdomain_attach(struct ieee80211com *);
void ieee80211_regdomain_detach(struct ieee80211com *);
+void ieee80211_regdomain_vattach(struct ieee80211vap *);
+void ieee80211_regdomain_vdetach(struct ieee80211vap *);
-void ieee80211_init_channels(struct ieee80211com *ic,
- int rd, enum ISOCountryCode cc, int bands, int outdoor, int ecm);
-uint8_t *ieee80211_add_countryie(uint8_t *, struct ieee80211com *,
- enum ISOCountryCode cc, int location);
-const char *ieee80211_cctoiso(enum ISOCountryCode);
-int ieee80211_isotocc(const char iso[2]);
+int ieee80211_init_channels(struct ieee80211com *,
+ const struct ieee80211_regdomain *, const uint8_t bands[]);
+void ieee80211_sort_channels(struct ieee80211_channel chans[], int nchans);
+struct ieee80211_appie;
+struct ieee80211_appie *ieee80211_alloc_countryie(struct ieee80211com *);
+struct ieee80211_regdomain_req;
+int ieee80211_setregdomain(struct ieee80211vap *,
+ struct ieee80211_regdomain_req *);
#endif /* defined(__KERNEL__) || defined(_KERNEL) */
#endif /* _NET80211_IEEE80211_REGDOMAIN_H_ */
diff --git a/sys/net80211/ieee80211_rssadapt.c b/sys/net80211/ieee80211_rssadapt.c
new file mode 100644
index 0000000..f1fc409
--- /dev/null
+++ b/sys/net80211/ieee80211_rssadapt.c
@@ -0,0 +1,273 @@
+/* $FreeBSD$ */
+/* $NetBSD: ieee80211_rssadapt.c,v 1.9 2005/02/26 22:45:09 perry Exp $ */
+/*-
+ * Copyright (c) 2003, 2004 David Young. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. The name of David Young may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David
+ * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_rssadapt.h>
+
+struct rssadapt_expavgctl {
+ /* RSS threshold decay. */
+ u_int rc_decay_denom;
+ u_int rc_decay_old;
+ /* RSS threshold update. */
+ u_int rc_thresh_denom;
+ u_int rc_thresh_old;
+ /* RSS average update. */
+ u_int rc_avgrssi_denom;
+ u_int rc_avgrssi_old;
+};
+
+static struct rssadapt_expavgctl master_expavgctl = {
+ rc_decay_denom : 16,
+ rc_decay_old : 15,
+ rc_thresh_denom : 8,
+ rc_thresh_old : 4,
+ rc_avgrssi_denom : 8,
+ rc_avgrssi_old : 4
+};
+
+#ifdef interpolate
+#undef interpolate
+#endif
+#define interpolate(parm, old, new) ((parm##_old * (old) + \
+ (parm##_denom - parm##_old) * (new)) / \
+ parm##_denom)
+
+static void rssadapt_sysctlattach(struct ieee80211_rssadapt *rs,
+ struct sysctl_ctx_list *ctx, struct sysctl_oid *tree);
+
+/* number of references from net80211 layer */
+static int nrefs = 0;
+
+void
+ieee80211_rssadapt_setinterval(struct ieee80211_rssadapt *rs, int msecs)
+{
+ int t;
+
+ if (msecs < 100)
+ msecs = 100;
+ t = msecs_to_ticks(msecs);
+ rs->interval = (t < 1) ? 1 : t;
+}
+
+void
+ieee80211_rssadapt_init(struct ieee80211_rssadapt *rs, struct ieee80211vap *vap, int interval)
+{
+ rs->vap = vap;
+ ieee80211_rssadapt_setinterval(rs, interval);
+
+ rssadapt_sysctlattach(rs, vap->iv_sysctl, vap->iv_oid);
+}
+
+void
+ieee80211_rssadapt_cleanup(struct ieee80211_rssadapt *rs)
+{
+}
+
+static void
+rssadapt_updatestats(struct ieee80211_rssadapt_node *ra)
+{
+ long interval;
+
+ ra->ra_pktrate = (ra->ra_pktrate + 10*(ra->ra_nfail + ra->ra_nok))/2;
+ ra->ra_nfail = ra->ra_nok = 0;
+
+ /*
+ * A node is eligible for its rate to be raised every 1/10 to 10
+ * seconds, more eligible in proportion to recent packet rates.
+ */
+ interval = MAX(10*1000, 10*1000 / MAX(1, 10 * ra->ra_pktrate));
+ ra->ra_raise_interval = msecs_to_ticks(interval);
+}
+
+void
+ieee80211_rssadapt_node_init(struct ieee80211_rssadapt *rsa,
+ struct ieee80211_rssadapt_node *ra, struct ieee80211_node *ni)
+{
+ const struct ieee80211_rateset *rs = &ni->ni_rates;
+
+ ra->ra_rs = rsa;
+ ra->ra_rates = *rs;
+ rssadapt_updatestats(ra);
+
+ /* pick initial rate */
+ for (ra->ra_rix = rs->rs_nrates - 1;
+ ra->ra_rix > 0 && (rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL) > 72;
+ ra->ra_rix--)
+ ;
+ ni->ni_txrate = rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL;
+ ra->ra_ticks = ticks;
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "RSSADAPT initial rate %d", ni->ni_txrate);
+}
+
+static __inline int
+bucket(int pktlen)
+{
+ int i, top, thridx;
+
+ for (i = 0, top = IEEE80211_RSSADAPT_BKT0;
+ i < IEEE80211_RSSADAPT_BKTS;
+ i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) {
+ thridx = i;
+ if (pktlen <= top)
+ break;
+ }
+ return thridx;
+}
+
+int
+ieee80211_rssadapt_choose(struct ieee80211_node *ni,
+ struct ieee80211_rssadapt_node *ra, u_int pktlen)
+{
+ const struct ieee80211_rateset *rs = &ra->ra_rates;
+ uint16_t (*thrs)[IEEE80211_RATE_SIZE];
+ int rix, rssi;
+
+ if ((ticks - ra->ra_ticks) > ra->ra_rs->interval) {
+ rssadapt_updatestats(ra);
+ ra->ra_ticks = ticks;
+ }
+
+ thrs = &ra->ra_rate_thresh[bucket(pktlen)];
+
+ /* XXX this is average rssi, should be using last value */
+ rssi = ni->ni_ic->ic_node_getrssi(ni);
+ for (rix = rs->rs_nrates-1; rix >= 0; rix--)
+ if ((*thrs)[rix] < (rssi << 8))
+ break;
+ if (rix != ra->ra_rix) {
+ /* update public rate */
+ ni->ni_txrate = ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL;
+ ra->ra_rix = rix;
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "RSSADAPT new rate %d (pktlen %d rssi %d)",
+ ni->ni_txrate, pktlen, rssi);
+ }
+ return rix;
+}
+
+/*
+ * Adapt the data rate to suit the conditions. When a transmitted
+ * packet is dropped after RAL_RSSADAPT_RETRY_LIMIT retransmissions,
+ * raise the RSS threshold for transmitting packets of similar length at
+ * the same data rate.
+ */
+void
+ieee80211_rssadapt_lower_rate(struct ieee80211_rssadapt_node *ra,
+ int pktlen, int rssi)
+{
+ uint16_t last_thr;
+ uint16_t (*thrs)[IEEE80211_RATE_SIZE];
+ u_int rix;
+
+ thrs = &ra->ra_rate_thresh[bucket(pktlen)];
+
+ rix = ra->ra_rix;
+ last_thr = (*thrs)[rix];
+ (*thrs)[rix] = interpolate(master_expavgctl.rc_thresh,
+ last_thr, (rssi << 8));
+
+ IEEE80211_DPRINTF(ra->ra_rs->vap, IEEE80211_MSG_RATECTL,
+ "RSSADAPT lower threshold for rate %d (last_thr %d new thr %d rssi %d)\n",
+ ra->ra_rates.rs_rates[rix + 1] & IEEE80211_RATE_VAL,
+ last_thr, (*thrs)[rix], rssi);
+}
+
+void
+ieee80211_rssadapt_raise_rate(struct ieee80211_rssadapt_node *ra,
+ int pktlen, int rssi)
+{
+ uint16_t (*thrs)[IEEE80211_RATE_SIZE];
+ uint16_t newthr, oldthr;
+ int rix;
+
+ thrs = &ra->ra_rate_thresh[bucket(pktlen)];
+
+ rix = ra->ra_rix;
+ if ((*thrs)[rix + 1] > (*thrs)[rix]) {
+ oldthr = (*thrs)[rix + 1];
+ if ((*thrs)[rix] == 0)
+ newthr = (rssi << 8);
+ else
+ newthr = (*thrs)[rix];
+ (*thrs)[rix + 1] = interpolate(master_expavgctl.rc_decay,
+ oldthr, newthr);
+
+ IEEE80211_DPRINTF(ra->ra_rs->vap, IEEE80211_MSG_RATECTL,
+ "RSSADAPT raise threshold for rate %d (oldthr %d newthr %d rssi %d)\n",
+ ra->ra_rates.rs_rates[rix + 1] & IEEE80211_RATE_VAL,
+ oldthr, newthr, rssi);
+
+ ra->ra_last_raise = ticks;
+ }
+}
+
+static int
+rssadapt_sysctl_interval(SYSCTL_HANDLER_ARGS)
+{
+ struct ieee80211_rssadapt *rs = arg1;
+ int msecs = ticks_to_msecs(rs->interval);
+ int error;
+
+ error = sysctl_handle_int(oidp, &msecs, 0, req);
+ if (error || !req->newptr)
+ return error;
+ ieee80211_rssadapt_setinterval(rs, msecs);
+ return 0;
+}
+
+static void
+rssadapt_sysctlattach(struct ieee80211_rssadapt *rs,
+ struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
+{
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "rssadapt_rate_interval", CTLTYPE_INT | CTLFLAG_RW, rs,
+ 0, rssadapt_sysctl_interval, "I", "rssadapt operation interval (ms)");
+}
+
+/*
+ * Module glue.
+ */
+IEEE80211_RATE_MODULE(rssadapt, 1);
diff --git a/sys/dev/ral/if_ralrate.h b/sys/net80211/ieee80211_rssadapt.h
index 50eee44..b454f43 100644
--- a/sys/dev/ral/if_ralrate.h
+++ b/sys/net80211/ieee80211_rssadapt.h
@@ -29,6 +29,8 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
+#ifndef _NET80211_IEEE80211_RSSADAPT_H_
+#define _NET80211_IEEE80211_RSSADAPT_H_
/* Data-rate adaptation loosely based on "Link Adaptation Strategy
* for IEEE 802.11 WLAN via Received Signal Strength Measurement"
@@ -36,63 +38,64 @@
*/
/* Buckets for frames 0-128 bytes long, 129-1024, 1025-maximum. */
-#define RAL_RSSADAPT_BKTS 3
-#define RAL_RSSADAPT_BKT0 128
-#define RAL_RSSADAPT_BKTPOWER 3 /* 2**_BKTPOWER */
+#define IEEE80211_RSSADAPT_BKTS 3
+#define IEEE80211_RSSADAPT_BKT0 128
+#define IEEE80211_RSSADAPT_BKTPOWER 3 /* 2**_BKTPOWER */
-#define ral_rssadapt_thresh_new \
- (ral_rssadapt_thresh_denom - ral_rssadapt_thresh_old)
-#define ral_rssadapt_decay_new \
- (ral_rssadapt_decay_denom - ral_rssadapt_decay_old)
-#define ral_rssadapt_avgrssi_new \
- (ral_rssadapt_avgrssi_denom - ral_rssadapt_avgrssi_old)
-
-struct ral_rssadapt_expavgctl {
- /* RSS threshold decay. */
- u_int rc_decay_denom;
- u_int rc_decay_old;
- /* RSS threshold update. */
- u_int rc_thresh_denom;
- u_int rc_thresh_old;
- /* RSS average update. */
- u_int rc_avgrssi_denom;
- u_int rc_avgrssi_old;
+struct ieee80211_rssadapt {
+ struct ieee80211vap *vap;
+ int interval; /* update interval (ticks) */
};
-struct ral_rssadapt {
- /* exponential average RSSI << 8 */
- u_int16_t ra_avg_rssi;
+struct ieee80211_rssadapt_node {
+ struct ieee80211_rssadapt *ra_rs; /* backpointer */
+ struct ieee80211_rateset ra_rates; /* negotiated rates */
+ int ra_rix; /* current rate index */
+ int ra_ticks; /* time of last update */
+ int ra_last_raise; /* time of last rate raise */
+ int ra_raise_interval; /* rate raise time threshold */
+
/* Tx failures in this update interval */
- u_int32_t ra_nfail;
+ uint32_t ra_nfail;
/* Tx successes in this update interval */
- u_int32_t ra_nok;
+ uint32_t ra_nok;
/* exponential average packets/second */
- u_int32_t ra_pktrate;
+ uint32_t ra_pktrate;
/* RSSI threshold for each Tx rate */
- u_int16_t ra_rate_thresh[RAL_RSSADAPT_BKTS]
+ uint16_t ra_rate_thresh[IEEE80211_RSSADAPT_BKTS]
[IEEE80211_RATE_SIZE];
- struct timeval ra_last_raise;
- struct timeval ra_raise_interval;
};
-/* Properties of a Tx packet, for link adaptation. */
-struct ral_rssdesc {
- u_int id_len; /* Tx packet length */
- u_int id_rateidx; /* index into ni->ni_rates */
- struct ieee80211_node *id_node; /* destination STA MAC */
- u_int8_t id_rssi; /* destination STA avg RSS @
- * Tx time
- */
-};
+void ieee80211_rssadapt_init(struct ieee80211_rssadapt *,
+ struct ieee80211vap *, int);
+void ieee80211_rssadapt_cleanup(struct ieee80211_rssadapt *);
+void ieee80211_rssadapt_setinterval(struct ieee80211_rssadapt *, int);
+void ieee80211_rssadapt_node_init(struct ieee80211_rssadapt *,
+ struct ieee80211_rssadapt_node *, struct ieee80211_node *);
+int ieee80211_rssadapt_choose(struct ieee80211_node *,
+ struct ieee80211_rssadapt_node *, u_int);
+
+/* NB: these are public only for the inline below */
+void ieee80211_rssadapt_raise_rate(struct ieee80211_rssadapt_node *,
+ int pktlen, int rssi);
+void ieee80211_rssadapt_lower_rate(struct ieee80211_rssadapt_node *,
+ int pktlen, int rssi);
+
+#define IEEE80211_RSSADAPT_SUCCESS 1
+#define IEEE80211_RSSADAPT_FAILURE 0
-void ral_rssadapt_updatestats(struct ral_rssadapt *);
-void ral_rssadapt_input(struct ieee80211com *, struct ieee80211_node *,
- struct ral_rssadapt *, int);
-void ral_rssadapt_lower_rate(struct ieee80211com *,
- struct ieee80211_node *, struct ral_rssadapt *,
- struct ral_rssdesc *);
-void ral_rssadapt_raise_rate(struct ieee80211com *,
- struct ral_rssadapt *, struct ral_rssdesc *);
-int ral_rssadapt_choose(struct ral_rssadapt *,
- struct ieee80211_rateset *, struct ieee80211_frame *, u_int,
- const char *, int);
+static __inline void
+ieee80211_rssadapt_tx_complete(struct ieee80211_rssadapt_node *ra,
+ int success, int pktlen, int rssi)
+{
+ if (success) {
+ ra->ra_nok++;
+ if ((ra->ra_rix + 1) < ra->ra_rates.rs_nrates &&
+ (ticks - ra->ra_last_raise) >= ra->ra_raise_interval)
+ ieee80211_rssadapt_raise_rate(ra, pktlen, rssi);
+ } else {
+ ra->ra_nfail++;
+ ieee80211_rssadapt_lower_rate(ra, pktlen, rssi);
+ }
+}
+#endif /* _NET80211_IEEE80211_RSSADAPT_H_ */
diff --git a/sys/net80211/ieee80211_scan.c b/sys/net80211/ieee80211_scan.c
index 6f6b9a6..cec9673 100644
--- a/sys/net80211/ieee80211_scan.c
+++ b/sys/net80211/ieee80211_scan.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 scanning support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
@@ -72,7 +74,7 @@ struct scan_state {
/*
* Roaming-related defaults. RSSI thresholds are as returned by the
* driver (dBm). Transmit rate thresholds are IEEE rate codes (i.e
- * .5M units).
+ * .5M units) or MCS.
*/
#define ROAM_RSSI_11A_DEFAULT 14 /* rssi threshold for 11a bss */
#define ROAM_RSSI_11B_DEFAULT 14 /* rssi threshold for 11b bss */
@@ -80,10 +82,11 @@ struct scan_state {
#define ROAM_RATE_11A_DEFAULT 2*12 /* tx rate thresh for 11a bss */
#define ROAM_RATE_11B_DEFAULT 2*5 /* tx rate thresh for 11b bss */
#define ROAM_RATE_11BONLY_DEFAULT 2*1 /* tx rate thresh for 11b-only bss */
+#define ROAM_MCS_11N_DEFAULT 1 /* tx MCS thresh for 11n bss*/
static void scan_restart_pwrsav(void *);
-static void scan_curchan(struct ieee80211com *, unsigned long);
-static void scan_mindwell(struct ieee80211com *);
+static void scan_curchan(struct ieee80211_scan_state *, unsigned long);
+static void scan_mindwell(struct ieee80211_scan_state *);
static void scan_next(void *);
MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state");
@@ -93,29 +96,17 @@ ieee80211_scan_attach(struct ieee80211com *ic)
{
struct scan_state *ss;
- ic->ic_roaming = IEEE80211_ROAMING_AUTO;
-
MALLOC(ss, struct scan_state *, sizeof(struct scan_state),
M_80211_SCAN, M_NOWAIT | M_ZERO);
if (ss == NULL) {
ic->ic_scan = NULL;
return;
}
- callout_init(&ss->ss_scan_timer, CALLOUT_MPSAFE);
+ callout_init_mtx(&ss->ss_scan_timer, &ic->ic_comlock, 0);
ic->ic_scan = &ss->base;
ic->ic_scan_curchan = scan_curchan;
ic->ic_scan_mindwell = scan_mindwell;
-
- ic->ic_bgscanidle = (IEEE80211_BGSCAN_IDLE_DEFAULT*1000)/hz;
- ic->ic_bgscanintvl = IEEE80211_BGSCAN_INTVAL_DEFAULT*hz;
- ic->ic_scanvalid = IEEE80211_SCAN_VALID_DEFAULT*hz;
- ic->ic_roam.rssi11a = ROAM_RSSI_11A_DEFAULT;
- ic->ic_roam.rssi11b = ROAM_RSSI_11B_DEFAULT;
- ic->ic_roam.rssi11bOnly = ROAM_RSSI_11BONLY_DEFAULT;
- ic->ic_roam.rate11a = ROAM_RATE_11A_DEFAULT;
- ic->ic_roam.rate11b = ROAM_RATE_11B_DEFAULT;
- ic->ic_roam.rate11bOnly = ROAM_RATE_11BONLY_DEFAULT;
}
void
@@ -135,31 +126,94 @@ ieee80211_scan_detach(struct ieee80211com *ic)
}
}
+static __inline void
+setparams(struct ieee80211_roamparam *rp, int8_t rssi, uint8_t txrate)
+{
+ rp->rssi = rssi;
+ rp->rate = txrate;
+}
+
+void
+ieee80211_scan_vattach(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ vap->iv_bgscanidle = (IEEE80211_BGSCAN_IDLE_DEFAULT*1000)/hz;
+ vap->iv_bgscanintvl = IEEE80211_BGSCAN_INTVAL_DEFAULT*hz;
+ vap->iv_scanvalid = IEEE80211_SCAN_VALID_DEFAULT*hz;
+
+ vap->iv_roaming = IEEE80211_ROAMING_AUTO;
+
+ /* NB: only set supported modes so user apps can identify */
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11A))
+ setparams(&vap->iv_roamparms[IEEE80211_MODE_11A],
+ ROAM_RSSI_11A_DEFAULT, ROAM_RATE_11A_DEFAULT);
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11G))
+ setparams(&vap->iv_roamparms[IEEE80211_MODE_11G],
+ ROAM_RSSI_11B_DEFAULT, ROAM_RATE_11B_DEFAULT);
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11B))
+ setparams(&vap->iv_roamparms[IEEE80211_MODE_11B],
+ ROAM_RSSI_11BONLY_DEFAULT, ROAM_RATE_11BONLY_DEFAULT);
+ /* NB: default turbo controls to be the same as !turbo */
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_TURBO_A))
+ vap->iv_roamparms[IEEE80211_MODE_TURBO_A] =
+ vap->iv_roamparms[IEEE80211_MODE_11A];
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_TURBO_G))
+ vap->iv_roamparms[IEEE80211_MODE_TURBO_G] =
+ vap->iv_roamparms[IEEE80211_MODE_11G];
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_STURBO_A))
+ vap->iv_roamparms[IEEE80211_MODE_STURBO_A] =
+ vap->iv_roamparms[IEEE80211_MODE_11A];
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA))
+ setparams(&vap->iv_roamparms[IEEE80211_MODE_11NA],
+ ROAM_RSSI_11A_DEFAULT, ROAM_MCS_11N_DEFAULT | 0x80);
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG))
+ setparams(&vap->iv_roamparms[IEEE80211_MODE_11NG],
+ ROAM_RSSI_11B_DEFAULT, ROAM_MCS_11N_DEFAULT | 0x80);
+}
+
+void
+ieee80211_scan_vdetach(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss;
+
+ IEEE80211_LOCK(ic);
+ ss = ic->ic_scan;
+ if (ss != NULL && ss->ss_vap == vap) {
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ /* XXX callout_drain */
+ callout_stop(&SCAN_PRIVATE(ss)->ss_scan_timer);
+ ic->ic_flags &= ~IEEE80211_F_SCAN;
+ }
+ if (ss->ss_ops != NULL) {
+ ss->ss_ops->scan_detach(ss);
+ ss->ss_ops = NULL;
+ }
+ ss->ss_vap = NULL;
+ }
+ IEEE80211_UNLOCK(ic);
+}
+
/*
* Simple-minded scanner module support.
*/
-#define IEEE80211_SCANNER_MAX (IEEE80211_M_MONITOR+1)
-
-static const char *scan_modnames[IEEE80211_SCANNER_MAX] = {
+static const char *scan_modnames[IEEE80211_OPMODE_MAX] = {
"wlan_scan_sta", /* IEEE80211_M_IBSS */
"wlan_scan_sta", /* IEEE80211_M_STA */
"wlan_scan_wds", /* IEEE80211_M_WDS */
"wlan_scan_sta", /* IEEE80211_M_AHDEMO */
- "wlan_scan_4", /* n/a */
- "wlan_scan_5", /* n/a */
"wlan_scan_ap", /* IEEE80211_M_HOSTAP */
- "wlan_scan_7", /* n/a */
"wlan_scan_monitor", /* IEEE80211_M_MONITOR */
};
-static const struct ieee80211_scanner *scanners[IEEE80211_SCANNER_MAX];
+static const struct ieee80211_scanner *scanners[IEEE80211_OPMODE_MAX];
const struct ieee80211_scanner *
ieee80211_scanner_get(enum ieee80211_opmode mode)
{
- if (mode >= IEEE80211_SCANNER_MAX)
+ if (mode >= IEEE80211_OPMODE_MAX)
return NULL;
- /* NB: avoid monitor mode; there is no scan support */
- if (mode != IEEE80211_M_MONITOR && scanners[mode] == NULL)
+ if (scanners[mode] == NULL)
ieee80211_load_module(scan_modnames[mode]);
return scanners[mode];
}
@@ -168,7 +222,7 @@ void
ieee80211_scanner_register(enum ieee80211_opmode mode,
const struct ieee80211_scanner *scan)
{
- if (mode >= IEEE80211_SCANNER_MAX)
+ if (mode >= IEEE80211_OPMODE_MAX)
return;
scanners[mode] = scan;
}
@@ -177,7 +231,7 @@ void
ieee80211_scanner_unregister(enum ieee80211_opmode mode,
const struct ieee80211_scanner *scan)
{
- if (mode >= IEEE80211_SCANNER_MAX)
+ if (mode >= IEEE80211_OPMODE_MAX)
return;
if (scanners[mode] == scan)
scanners[mode] = NULL;
@@ -188,7 +242,7 @@ ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan)
{
int m;
- for (m = 0; m < IEEE80211_SCANNER_MAX; m++)
+ for (m = 0; m < IEEE80211_OPMODE_MAX; m++)
if (scanners[m] == scan)
scanners[m] = NULL;
}
@@ -201,35 +255,50 @@ ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan)
* ensure later callbacks find ss_ops set to properly
* reflect current operating mode.
*/
-int
-ieee80211_scan_update(struct ieee80211com *ic)
+static void
+scan_update_locked(struct ieee80211vap *vap,
+ const struct ieee80211_scanner *scan)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
- const struct ieee80211_scanner *scan;
- scan = ieee80211_scanner_get(ic->ic_opmode);
- IEEE80211_LOCK(ic);
- if (scan == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: no scanner support for mode %u\n",
- __func__, ic->ic_opmode);
- /* XXX stat */
+ IEEE80211_LOCK_ASSERT(ic);
+
+#ifdef IEEE80211_DEBUG
+ if (ss->ss_vap != vap || ss->ss_ops != scan) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: current scanner is <%s:%s>, switch to <%s:%s>\n",
+ __func__,
+ ss->ss_vap != NULL ?
+ ss->ss_vap->iv_ifp->if_xname : "none",
+ ss->ss_vap != NULL ?
+ ieee80211_opmode_name[ss->ss_vap->iv_opmode] : "none",
+ vap->iv_ifp->if_xname,
+ ieee80211_opmode_name[vap->iv_opmode]);
}
- ss->ss_ic = ic;
+#endif
+ ss->ss_vap = vap;
if (ss->ss_ops != scan) {
- /* switch scanners; detach old, attach new */
- if (ss->ss_ops != NULL)
- ss->ss_ops->scan_detach(ss);
- if (scan != NULL && !scan->scan_attach(ss)) {
- /* XXX attach failure */
- /* XXX stat+msg */
- ss->ss_ops = NULL;
- } else
- ss->ss_ops = scan;
+ /*
+ * Switch scanners; detach old, attach new. Special
+ * case where a single scan module implements multiple
+ * policies by using different scan ops but a common
+ * core. We assume if the old and new attach methods
+ * are identical then it's ok to just change ss_ops
+ * and not flush the internal state of the module.
+ */
+ if (scan == NULL || ss->ss_ops == NULL ||
+ ss->ss_ops->scan_attach != scan->scan_attach) {
+ if (ss->ss_ops != NULL)
+ ss->ss_ops->scan_detach(ss);
+ if (scan != NULL && !scan->scan_attach(ss)) {
+ /* XXX attach failure */
+ /* XXX stat+msg */
+ scan = NULL;
+ }
+ }
+ ss->ss_ops = scan;
}
- IEEE80211_UNLOCK(ic);
-
- return (scan != NULL);
}
static void
@@ -263,7 +332,7 @@ channel_type(const struct ieee80211_channel *c)
void
ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss)
{
- struct ieee80211com *ic = ss->ss_ic;
+ struct ieee80211com *ic = ss->ss_vap->iv_ic;
const char *sep;
int i;
@@ -277,6 +346,18 @@ ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss)
}
}
+#ifdef IEEE80211_DEBUG
+static void
+scan_dump(struct ieee80211_scan_state *ss)
+{
+ struct ieee80211vap *vap = ss->ss_vap;
+
+ if_printf(vap->iv_ifp, "scan set ");
+ ieee80211_scan_dump_channels(ss);
+ printf(" dwell min %lu max %lu\n", ss->ss_mindwell, ss->ss_maxdwell);
+}
+#endif /* IEEE80211_DEBUG */
+
/*
* Enable station power save mode and start/restart the scanning thread.
*/
@@ -284,23 +365,24 @@ static void
scan_restart_pwrsav(void *arg)
{
struct scan_state *ss = (struct scan_state *) arg;
- struct ieee80211com *ic = ss->base.ss_ic;
- int delay;
+ struct ieee80211vap *vap = ss->base.ss_vap;
+ struct ieee80211com *ic = vap->iv_ic;
+ int ticksdelay;
- ieee80211_sta_pwrsave(ic, 1);
+ ieee80211_sta_pwrsave(vap, 1);
/*
- * Use an initial 1ms delay to insure the null
+ * Use an initial 1ms delay so the null
* data frame has a chance to go out.
* XXX 1ms is a lot, better to trigger scan
* on tx complete.
*/
- delay = hz/1000;
- if (delay < 1)
- delay = 1;
+ ticksdelay = msecs_to_ticks(1);
+ if (ticksdelay < 1)
+ ticksdelay = 1;
ic->ic_scan_start(ic); /* notify driver */
- ss->ss_scanend = ticks + delay + ss->ss_duration;
+ ss->ss_scanend = ticks + ticksdelay + ss->ss_duration;
ss->ss_iflags |= ISCAN_START;
- callout_reset(&ss->ss_scan_timer, delay, scan_next, ss);
+ callout_reset(&ss->ss_scan_timer, ticksdelay, scan_next, ss);
}
/*
@@ -313,17 +395,18 @@ scan_restart_pwrsav(void *arg)
static int
scan_restart(struct scan_state *ss, u_int duration)
{
- struct ieee80211com *ic = ss->base.ss_ic;
+ struct ieee80211vap *vap = ss->base.ss_vap;
+ struct ieee80211com *ic = vap->iv_ic;
int defer = 0;
if (ss->base.ss_next == ss->base.ss_last) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no channels to scan\n", __func__);
return 0;
}
- if (ic->ic_opmode == IEEE80211_M_STA &&
- ic->ic_state == IEEE80211_S_RUN) {
- if ((ic->ic_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
+ if (vap->iv_opmode == IEEE80211_M_STA &&
+ vap->iv_state == IEEE80211_S_RUN) {
+ if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
/*
* Initiate power save before going off-channel.
* Note that we cannot do this directly because
@@ -346,12 +429,12 @@ scan_restart(struct scan_state *ss, u_int duration)
}
static void
-copy_ssid(struct ieee80211com *ic, struct ieee80211_scan_state *ss,
+copy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss,
int nssid, const struct ieee80211_scan_ssid ssids[])
{
if (nssid > IEEE80211_SCAN_MAX_SSID) {
/* XXX printf */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: too many ssid %d, ignoring all of them\n",
__func__, nssid);
return;
@@ -363,76 +446,97 @@ copy_ssid(struct ieee80211com *ic, struct ieee80211_scan_state *ss,
/*
* Start a scan unless one is already going.
*/
-int
-ieee80211_start_scan(struct ieee80211com *ic, int flags, u_int duration,
+static int
+start_scan_locked(const struct ieee80211_scanner *scan,
+ struct ieee80211vap *vap, int flags, u_int duration,
+ u_int mindwell, u_int maxdwell,
u_int nssid, const struct ieee80211_scan_ssid ssids[])
{
- const struct ieee80211_scanner *scan;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
- scan = ieee80211_scanner_get(ic->ic_opmode);
- if (scan == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: no scanner support for mode %u\n",
- __func__, ic->ic_opmode);
- /* XXX stat */
- return 0;
- }
+ IEEE80211_LOCK_ASSERT(ic);
- IEEE80211_LOCK(ic);
- if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: %s scan, duration %u, desired mode %s, %s%s%s%s\n"
+ if (ic->ic_flags & IEEE80211_F_CSAPENDING) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: scan inhibited by pending channel change\n", __func__);
+ } else if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: %s scan, duration %u mindwell %u maxdwell %u, desired mode %s, %s%s%s%s%s%s\n"
, __func__
, flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"
- , duration
- , ieee80211_phymode_name[ic->ic_des_mode]
+ , duration, mindwell, maxdwell
+ , ieee80211_phymode_name[vap->iv_des_mode]
, flags & IEEE80211_SCAN_FLUSH ? "flush" : "append"
, flags & IEEE80211_SCAN_NOPICK ? ", nopick" : ""
+ , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : ""
+ , flags & IEEE80211_SCAN_NOBCAST ? ", nobcast" : ""
, flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : ""
, flags & IEEE80211_SCAN_ONCE ? ", once" : ""
);
- ss->ss_ic = ic;
- if (ss->ss_ops != scan) {
- /* switch scanners; detach old, attach new */
- if (ss->ss_ops != NULL)
- ss->ss_ops->scan_detach(ss);
- if (!scan->scan_attach(ss)) {
- /* XXX attach failure */
- /* XXX stat+msg */
- ss->ss_ops = NULL;
- } else
- ss->ss_ops = scan;
- }
+ scan_update_locked(vap, scan);
if (ss->ss_ops != NULL) {
if ((flags & IEEE80211_SCAN_NOSSID) == 0)
- copy_ssid(ic, ss, nssid, ssids);
+ copy_ssid(vap, ss, nssid, ssids);
/* NB: top 4 bits for internal use */
ss->ss_flags = flags & 0xfff;
if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
- ic->ic_stats.is_scan_active++;
+ vap->iv_stats.is_scan_active++;
else
- ic->ic_stats.is_scan_passive++;
+ vap->iv_stats.is_scan_passive++;
if (flags & IEEE80211_SCAN_FLUSH)
ss->ss_ops->scan_flush(ss);
/* NB: flush frames rx'd before 1st channel change */
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
- ss->ss_ops->scan_start(ss, ic);
+ ss->ss_next = 0;
+ ss->ss_mindwell = mindwell;
+ ss->ss_maxdwell = maxdwell;
+ ss->ss_ops->scan_start(ss, vap);
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_scan(vap))
+ scan_dump(ss);
+#endif /* IEEE80211_DEBUG */
if (scan_restart(SCAN_PRIVATE(ss), duration))
ic->ic_flags |= IEEE80211_F_SCAN;
}
} else {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s scan already in progress\n", __func__,
ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
}
+ return (ic->ic_flags & IEEE80211_F_SCAN);
+}
+
+/*
+ * Start a scan unless one is already going.
+ */
+int
+ieee80211_start_scan(struct ieee80211vap *vap, int flags,
+ u_int duration, u_int mindwell, u_int maxdwell,
+ u_int nssid, const struct ieee80211_scan_ssid ssids[])
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ const struct ieee80211_scanner *scan;
+ int result;
+
+ scan = ieee80211_scanner_get(vap->iv_opmode);
+ if (scan == NULL) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: no scanner support for %s mode\n",
+ __func__, ieee80211_opmode_name[vap->iv_opmode]);
+ /* XXX stat */
+ return 0;
+ }
+
+ IEEE80211_LOCK(ic);
+ result = start_scan_locked(scan, vap, flags, duration,
+ mindwell, maxdwell, nssid, ssids);
IEEE80211_UNLOCK(ic);
- /* NB: racey, does it matter? */
- return (ic->ic_flags & IEEE80211_F_SCAN);
+ return result;
}
/*
@@ -440,11 +544,23 @@ ieee80211_start_scan(struct ieee80211com *ic, int flags, u_int duration,
* fails then kick off a new scan.
*/
int
-ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration,
+ieee80211_check_scan(struct ieee80211vap *vap, int flags,
+ u_int duration, u_int mindwell, u_int maxdwell,
u_int nssid, const struct ieee80211_scan_ssid ssids[])
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
- int checkscanlist = 0;
+ const struct ieee80211_scanner *scan;
+ int checkscanlist = 0, result;
+
+ scan = ieee80211_scanner_get(vap->iv_opmode);
+ if (scan == NULL) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: no scanner support for %s mode\n",
+ __func__, vap->iv_opmode);
+ /* XXX stat */
+ return 0;
+ }
/*
* Check if there's a list of scan candidates already.
@@ -452,30 +568,35 @@ ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration,
*/
IEEE80211_LOCK(ic);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: %s scan, duration %u, desired mode %s, %s%s%s%s\n"
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: %s scan, %s%s%s%s%s\n"
, __func__
, flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"
- , duration
- , ieee80211_phymode_name[ic->ic_des_mode]
, flags & IEEE80211_SCAN_FLUSH ? "flush" : "append"
, flags & IEEE80211_SCAN_NOPICK ? ", nopick" : ""
+ , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : ""
, flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : ""
, flags & IEEE80211_SCAN_ONCE ? ", once" : ""
);
+ if (ss->ss_ops != scan) {
+ /* XXX re-use cache contents? e.g. adhoc<->sta */
+ flags |= IEEE80211_SCAN_FLUSH;
+ }
+ scan_update_locked(vap, scan);
if (ss->ss_ops != NULL) {
- /* XXX verify ss_ops matches ic->ic_opmode */
+ /* XXX verify ss_ops matches vap->iv_opmode */
if ((flags & IEEE80211_SCAN_NOSSID) == 0) {
/*
* Update the ssid list and mark flags so if
* we call start_scan it doesn't duplicate work.
*/
- copy_ssid(ic, ss, nssid, ssids);
+ copy_ssid(vap, ss, nssid, ssids);
flags |= IEEE80211_SCAN_NOSSID;
}
if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 &&
- time_before(ticks, ic->ic_lastscan + ic->ic_scanvalid)) {
+ (flags & IEEE80211_SCAN_FLUSH) == 0 &&
+ time_before(ticks, ic->ic_lastscan + vap->iv_scanvalid)) {
/*
* We're not currently scanning and the cache is
* deemed hot enough to consult. Lock out others
@@ -486,30 +607,40 @@ ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration,
*/
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
ic->ic_flags |= IEEE80211_F_SCAN;
+ /* NB: need to use supplied flags in check below */
+ ss->ss_flags = flags & 0xff;
checkscanlist = 1;
}
}
- IEEE80211_UNLOCK(ic);
if (checkscanlist) {
- const struct ieee80211_scanner *scan;
-
- scan = ieee80211_scanner_get(ic->ic_opmode);
- if (scan == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: no scanner support for mode %u\n",
- __func__, ic->ic_opmode);
- /* XXX stat */
- return 0;
- }
- if (scan == ss->ss_ops && ss->ss_ops->scan_end(ss, ic)) {
+ if (ss->ss_ops->scan_end(ss, vap)) {
/* found an ap, just clear the flag */
ic->ic_flags &= ~IEEE80211_F_SCAN;
+ ieee80211_notify_scan_done(vap);
+ IEEE80211_UNLOCK(ic);
return 1;
}
/* no ap, clear the flag before starting a scan */
ic->ic_flags &= ~IEEE80211_F_SCAN;
}
- return ieee80211_start_scan(ic, flags, duration, nssid, ssids);
+ result = start_scan_locked(scan, vap, flags, duration,
+ mindwell, maxdwell, nssid, ssids);
+ IEEE80211_UNLOCK(ic);
+
+ return result;
+}
+
+/*
+ * Check the scan cache for an ap/channel to use; if that fails
+ * then kick off a scan using the current settings.
+ */
+int
+ieee80211_check_scan_current(struct ieee80211vap *vap)
+{
+ return ieee80211_check_scan(vap,
+ IEEE80211_SCAN_ACTIVE,
+ IEEE80211_SCAN_FOREVER, 0, 0,
+ vap->iv_des_nssid, vap->iv_des_ssid);
}
/*
@@ -517,9 +648,20 @@ ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration,
* then we start again using the existing channel list.
*/
int
-ieee80211_bg_scan(struct ieee80211com *ic)
+ieee80211_bg_scan(struct ieee80211vap *vap, int flags)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
+ const struct ieee80211_scanner *scan;
+
+ scan = ieee80211_scanner_get(vap->iv_opmode);
+ if (scan == NULL) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: no scanner support for %s mode\n",
+ __func__, vap->iv_opmode);
+ /* XXX stat */
+ return 0;
+ }
IEEE80211_LOCK(ic);
if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
@@ -532,13 +674,14 @@ ieee80211_bg_scan(struct ieee80211com *ic)
*/
duration = IEEE80211_SCAN_OFFCHANNEL;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s scan, ticks %u duration %lu\n", __func__,
ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive",
ticks, duration);
+ scan_update_locked(vap, scan);
if (ss->ss_ops != NULL) {
- ss->ss_ic = ic;
+ ss->ss_vap = vap;
/*
* A background scan does not select a new sta; it
* just refreshes the scan cache. Also, indicate
@@ -554,15 +697,30 @@ ieee80211_bg_scan(struct ieee80211com *ic)
* usual sta power save logic.
*/
ss->ss_flags |= IEEE80211_SCAN_NOPICK
- | IEEE80211_SCAN_BGSCAN;
+ | IEEE80211_SCAN_BGSCAN
+ | flags
+ ;
/* if previous scan completed, restart */
if (ss->ss_next >= ss->ss_last) {
- ss->ss_next = 0;
if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
- ic->ic_stats.is_scan_active++;
+ vap->iv_stats.is_scan_active++;
else
- ic->ic_stats.is_scan_passive++;
- ss->ss_ops->scan_restart(ss, ic);
+ vap->iv_stats.is_scan_passive++;
+ /*
+ * NB: beware of the scan cache being flushed;
+ * if the channel list is empty use the
+ * scan_start method to populate it.
+ */
+ ss->ss_next = 0;
+ if (ss->ss_last != 0)
+ ss->ss_ops->scan_restart(ss, vap);
+ else {
+ ss->ss_ops->scan_start(ss, vap);
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_scan(vap))
+ scan_dump(ss);
+#endif /* IEEE80211_DEBUG */
+ }
}
/* NB: flush frames rx'd before 1st channel change */
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
@@ -575,7 +733,7 @@ ieee80211_bg_scan(struct ieee80211com *ic)
/* XXX msg+stat */
}
} else {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s scan already in progress\n", __func__,
ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
}
@@ -586,17 +744,46 @@ ieee80211_bg_scan(struct ieee80211com *ic)
}
/*
+ * Cancel any scan currently going on for the specified vap.
+ */
+void
+ieee80211_cancel_scan(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ IEEE80211_LOCK(ic);
+ if ((ic->ic_flags & IEEE80211_F_SCAN) &&
+ ss->ss_vap == vap &&
+ (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: cancel %s scan\n", __func__,
+ ss->ss_flags & IEEE80211_SCAN_ACTIVE ?
+ "active" : "passive");
+
+ /* clear bg scan NOPICK and mark cancel request */
+ ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
+ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL;
+ /* force it to fire asap */
+ callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
+ 0, scan_next, ss);
+ }
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
* Cancel any scan currently going on.
*/
void
-ieee80211_cancel_scan(struct ieee80211com *ic)
+ieee80211_cancel_anyscan(struct ieee80211vap *vap)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
IEEE80211_LOCK(ic);
if ((ic->ic_flags & IEEE80211_F_SCAN) &&
(SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: cancel %s scan\n", __func__,
ss->ss_flags & IEEE80211_SCAN_ACTIVE ?
"active" : "passive");
@@ -616,15 +803,12 @@ ieee80211_cancel_scan(struct ieee80211com *ic)
* scanning themselves (e.g. for firmware-based devices).
*/
void
-ieee80211_scan_next(struct ieee80211com *ic)
+ieee80211_scan_next(struct ieee80211vap *vap)
{
- /*
- * XXX: We might need/want to decouple context here by either:
- * callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
- * or using a taskqueue. Let's see what kind of problems direct
- * dispatch has for now.
- */
- scan_next(ic->ic_scan);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
}
/*
@@ -632,12 +816,52 @@ ieee80211_scan_next(struct ieee80211com *ic)
* channels (e.g. for firmware-based devices).
*/
void
-ieee80211_scan_done(struct ieee80211com *ic)
+ieee80211_scan_done(struct ieee80211vap *vap)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss;
+ IEEE80211_LOCK(ic);
+ ss = ic->ic_scan;
ss->ss_next = ss->ss_last; /* all channels are complete */
scan_next(ss);
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
+ * Probe the curent channel, if allowed, while scanning.
+ * If the channel is not marked passive-only then send
+ * a probe request immediately. Otherwise mark state and
+ * listen for beacons on the channel; if we receive something
+ * then we'll transmit a probe request.
+ */
+void
+ieee80211_probe_curchan(struct ieee80211vap *vap, int force)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ifnet *ifp = vap->iv_ifp;
+ int i;
+
+ if ((ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) && !force) {
+ ic->ic_flags_ext |= IEEE80211_FEXT_PROBECHAN;
+ return;
+ }
+ /*
+ * Send directed probe requests followed by any
+ * broadcast probe request.
+ * XXX remove dependence on ic/vap->iv_bss
+ */
+ for (i = 0; i < ss->ss_nssid; i++)
+ ieee80211_send_probereq(vap->iv_bss,
+ vap->iv_myaddr, ifp->if_broadcastaddr,
+ ifp->if_broadcastaddr,
+ ss->ss_ssid[i].ssid, ss->ss_ssid[i].len);
+ if ((ss->ss_flags & IEEE80211_SCAN_NOBCAST) == 0)
+ ieee80211_send_probereq(vap->iv_bss,
+ vap->iv_myaddr, ifp->if_broadcastaddr,
+ ifp->if_broadcastaddr,
+ "", 0);
}
/*
@@ -646,37 +870,16 @@ ieee80211_scan_done(struct ieee80211com *ic)
* Arrange for the channel change after maxdwell ticks.
*/
static void
-scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
+scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211vap *vap = ss->ss_vap;
- if ((ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
- (ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) {
- struct ifnet *ifp = ic->ic_ifp;
- int i;
+ IEEE80211_LOCK_ASSERT(vap->iv_ic);
- /*
- * Send a broadcast probe request followed by
- * any specified directed probe requests.
- * XXX suppress broadcast probe req?
- * XXX remove dependence on ic/ic->ic_bss
- * XXX move to policy code?
- */
- ieee80211_send_probereq(ic->ic_bss,
- ic->ic_myaddr, ifp->if_broadcastaddr,
- ifp->if_broadcastaddr,
- "", 0,
- ic->ic_opt_ie, ic->ic_opt_ie_len);
- for (i = 0; i < ss->ss_nssid; i++)
- ieee80211_send_probereq(ic->ic_bss,
- ic->ic_myaddr, ifp->if_broadcastaddr,
- ifp->if_broadcastaddr,
- ss->ss_ssid[i].ssid,
- ss->ss_ssid[i].len,
- ic->ic_opt_ie, ic->ic_opt_ie_len);
- }
+ if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
+ ieee80211_probe_curchan(vap, 0);
callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
- maxdwell, scan_next, ss);
+ maxdwell, scan_next, ss);
}
/*
@@ -684,10 +887,8 @@ scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
* change to the next channel asap.
*/
static void
-scan_mindwell(struct ieee80211com *ic)
+scan_mindwell(struct ieee80211_scan_state *ss)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
-
callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
}
@@ -699,17 +900,16 @@ scan_next(void *arg)
{
#define ISCAN_REP (ISCAN_MINDWELL | ISCAN_START | ISCAN_DISCARD)
struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
- struct ieee80211com *ic = ss->ss_ic;
+ struct ieee80211vap *vap = ss->ss_vap;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *chan;
unsigned long maxdwell, scanend;
- int scanning, scandone;
+ int scandone;
- IEEE80211_LOCK(ic);
- scanning = (ic->ic_flags & IEEE80211_F_SCAN) != 0;
- IEEE80211_UNLOCK(ic);
- if (!scanning) /* canceled */
- return;
+ IEEE80211_LOCK_ASSERT(ic);
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0)
+ return;
again:
scandone = (ss->ss_next >= ss->ss_last) ||
(SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0;
@@ -728,7 +928,7 @@ again:
else
maxdwell = ss->ss_maxdwell;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: chan %3d%c -> %3d%c [%s, dwell min %lu max %lu]\n",
__func__,
ieee80211_chan2ieee(ic, ic->ic_curchan),
@@ -751,7 +951,7 @@ again:
* sending a probe request (as needed), and arming the
* timeout to switch channels after maxdwell ticks.
*/
- ic->ic_scan_curchan(ic, maxdwell);
+ ic->ic_scan_curchan(ss, maxdwell);
SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell;
/* clear mindwell lock and initial channel change flush */
@@ -768,7 +968,7 @@ again:
/* return to the bss channel */
if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
ic->ic_curchan != ic->ic_bsschan)
- change_channel(ic, ic->ic_bsschan);
+ ieee80211_setcurchan(ic, ic->ic_bsschan);
/* clear internal flags and any indication of a pick */
SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK;
@@ -781,20 +981,21 @@ again:
* rx frames alter the scan candidate list.
*/
if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 &&
- !ss->ss_ops->scan_end(ss, ic) &&
+ !ss->ss_ops->scan_end(ss, vap) &&
(ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 &&
time_before(ticks + ss->ss_mindwell, scanend)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: done, restart "
"[ticks %u, dwell min %lu scanend %lu]\n",
__func__,
ticks, ss->ss_mindwell, scanend);
ss->ss_next = 0; /* reset to begining */
if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
- ic->ic_stats.is_scan_active++;
+ vap->iv_stats.is_scan_active++;
else
- ic->ic_stats.is_scan_passive++;
+ vap->iv_stats.is_scan_passive++;
+ ss->ss_ops->scan_restart(ss, vap); /* XXX? */
ic->ic_scan_start(ic); /* notify driver */
goto again;
} else {
@@ -802,7 +1003,7 @@ again:
if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0)
scandone = 1;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s, "
"[ticks %u, dwell min %lu scanend %lu]\n",
__func__, scandone ? "done" : "stopped",
@@ -825,9 +1026,9 @@ again:
* waiting for us.
*/
if (scandone) {
- ieee80211_sta_pwrsave(ic, 0);
+ ieee80211_sta_pwrsave(vap, 0);
if (ss->ss_next >= ss->ss_last) {
- ieee80211_notify_scan_done(ic);
+ ieee80211_notify_scan_done(vap);
ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
}
}
@@ -841,32 +1042,48 @@ again:
#ifdef IEEE80211_DEBUG
static void
+dump_country(const uint8_t *ie)
+{
+ const struct ieee80211_country_ie *cie =
+ (const struct ieee80211_country_ie *) ie;
+ int i, nbands, schan, nchan;
+
+ if (cie->len < 3) {
+ printf(" <bogus country ie, len %d>", cie->len);
+ return;
+ }
+ printf(" country [%c%c%c", cie->cc[0], cie->cc[1], cie->cc[2]);
+ nbands = (cie->len - 3) / sizeof(cie->band[0]);
+ for (i = 0; i < nbands; i++) {
+ schan = cie->band[i].schan;
+ nchan = cie->band[i].nchan;
+ if (nchan != 1)
+ printf(" %u-%u,%u", schan, schan + nchan-1,
+ cie->band[i].maxtxpwr);
+ else
+ printf(" %u,%u", schan, cie->band[i].maxtxpwr);
+ }
+ printf("]");
+}
+
+static void
dump_probe_beacon(uint8_t subtype, int isnew,
const uint8_t mac[IEEE80211_ADDR_LEN],
- const struct ieee80211_scanparams *sp)
+ const struct ieee80211_scanparams *sp, int rssi)
{
printf("[%s] %s%s on chan %u (bss chan %u) ",
ether_sprintf(mac), isnew ? "new " : "",
ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT],
- IEEE80211_CHAN2IEEE(sp->curchan), sp->bchan);
+ sp->chan, sp->bchan);
ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]);
- printf("\n");
+ printf(" rssi %d\n", rssi);
if (isnew) {
printf("[%s] caps 0x%x bintval %u erp 0x%x",
ether_sprintf(mac), sp->capinfo, sp->bintval, sp->erp);
- if (sp->country != NULL) {
-#ifdef __FreeBSD__
- printf(" country info %*D",
- sp->country[1], sp->country+2, " ");
-#else
- int i;
- printf(" country info");
- for (i = 0; i < sp->country[1]; i++)
- printf(" %02x", sp->country[i+2]);
-#endif
- }
+ if (sp->country != NULL)
+ dump_country(sp->country);
printf("\n");
}
}
@@ -876,26 +1093,27 @@ dump_probe_beacon(uint8_t subtype, int isnew,
* Process a beacon or probe response frame.
*/
void
-ieee80211_add_scan(struct ieee80211com *ic,
+ieee80211_add_scan(struct ieee80211vap *vap,
const struct ieee80211_scanparams *sp,
const struct ieee80211_frame *wh,
int subtype, int rssi, int noise, int rstamp)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
+ /* XXX locking */
/*
* Frames received during startup are discarded to avoid
* using scan state setup on the initial entry to the timer
* callback. This can occur because the device may enable
* rx prior to our doing the initial channel change in the
- * timer routine (we defer the channel change to the timer
- * code to simplify locking on linux).
+ * timer routine.
*/
if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD)
return;
#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_scan(ic) && (ic->ic_flags & IEEE80211_F_SCAN))
- dump_probe_beacon(subtype, 1, wh->i_addr2, sp);
+ if (ieee80211_msg_scan(vap) && (ic->ic_flags & IEEE80211_F_SCAN))
+ dump_probe_beacon(subtype, 1, wh->i_addr2, sp, rssi);
#endif
if (ss->ss_ops != NULL &&
ss->ss_ops->scan_add(ss, sp, wh, subtype, rssi, noise, rstamp)) {
@@ -905,7 +1123,7 @@ ieee80211_add_scan(struct ieee80211com *ic,
*/
if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 &&
time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: chan %3d%c min dwell met (%u > %lu)\n",
__func__,
ieee80211_chan2ieee(ic, ic->ic_curchan),
@@ -914,9 +1132,9 @@ ieee80211_add_scan(struct ieee80211com *ic,
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL;
/*
* NB: trigger at next clock tick or wait for the
- * hardware
+ * hardware.
*/
- ic->ic_scan_mindwell(ic);
+ ic->ic_scan_mindwell(ss);
}
}
}
@@ -938,12 +1156,12 @@ ieee80211_scan_timeout(struct ieee80211com *ic)
* Mark a scan cache entry after a successful associate.
*/
void
-ieee80211_scan_assoc_success(struct ieee80211com *ic, const uint8_t mac[])
+ieee80211_scan_assoc_success(struct ieee80211vap *vap, const uint8_t mac[])
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
if (ss->ss_ops != NULL) {
- IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN,
mac, "%s", __func__);
ss->ss_ops->scan_assoc_success(ss, mac);
}
@@ -953,13 +1171,13 @@ ieee80211_scan_assoc_success(struct ieee80211com *ic, const uint8_t mac[])
* Demerit a scan cache entry after failing to associate.
*/
void
-ieee80211_scan_assoc_fail(struct ieee80211com *ic,
+ieee80211_scan_assoc_fail(struct ieee80211vap *vap,
const uint8_t mac[], int reason)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
if (ss->ss_ops != NULL) {
- IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN, mac,
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, mac,
"%s: reason %u", __func__, reason);
ss->ss_ops->scan_assoc_fail(ss, mac, reason);
}
@@ -969,10 +1187,10 @@ ieee80211_scan_assoc_fail(struct ieee80211com *ic,
* Iterate over the contents of the scan cache.
*/
void
-ieee80211_scan_iterate(struct ieee80211com *ic,
+ieee80211_scan_iterate(struct ieee80211vap *vap,
ieee80211_scan_iter_func *f, void *arg)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
if (ss->ss_ops != NULL)
ss->ss_ops->scan_iterate(ss, f, arg);
@@ -982,13 +1200,36 @@ ieee80211_scan_iterate(struct ieee80211com *ic,
* Flush the contents of the scan cache.
*/
void
-ieee80211_scan_flush(struct ieee80211com *ic)
+ieee80211_scan_flush(struct ieee80211vap *vap)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
- if (ss->ss_ops != NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s\n", __func__);
+ if (ss->ss_ops != NULL && ss->ss_vap == vap) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", __func__);
ss->ss_ops->scan_flush(ss);
}
}
+
+/*
+ * Check the scan cache for an ap/channel to use; if that
+ * fails then kick off a new scan.
+ */
+struct ieee80211_channel *
+ieee80211_scan_pickchannel(struct ieee80211com *ic, int flags)
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ if (ss == NULL || ss->ss_ops == NULL || ss->ss_vap == NULL) {
+ /* XXX printf? */
+ return NULL;
+ }
+ if (ss->ss_ops->scan_pickchan == NULL) {
+ IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
+ "%s: scan module does not support picking a channel, "
+ "opmode %s\n", __func__, ss->ss_vap->iv_opmode);
+ return NULL;
+ }
+ return ss->ss_ops->scan_pickchan(ss, flags);
+}
diff --git a/sys/net80211/ieee80211_scan.h b/sys/net80211/ieee80211_scan.h
index fed5e0b..df6ce1f7 100644
--- a/sys/net80211/ieee80211_scan.h
+++ b/sys/net80211/ieee80211_scan.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2005-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -27,18 +27,69 @@
#ifndef _NET80211_IEEE80211_SCAN_H_
#define _NET80211_IEEE80211_SCAN_H_
+/*
+ * 802.11 scanning support.
+ *
+ * Scanning is the procedure by which a station locates a bss to join
+ * (infrastructure/ibss mode), or a channel to use (when operating as
+ * an ap or ibss master). Scans are either "active" or "passive". An
+ * active scan causes one or more probe request frames to be sent on
+ * visiting each channel. A passive request causes each channel in the
+ * scan set to be visited but no frames to be transmitted; the station
+ * only listens for traffic. Note that active scanning may still need
+ * to listen for traffic before sending probe request frames depending
+ * on regulatory constraints; the 802.11 layer handles this by generating
+ * a callback when scanning on a ``passive channel'' when the
+ * IEEE80211_FEXT_PROBECHAN flag is set.
+ *
+ * A scan operation involves constructing a set of channels to inspec
+ * (the scan set), visiting each channel and collecting information
+ * (e.g. what bss are present), and then analyzing the results to make
+ * decisions like which bss to join. This process needs to be as fast
+ * as possible so we do things like intelligently construct scan sets
+ * and dwell on a channel only as long as necessary. The scan code also
+ * maintains a cache of recent scan results and uses it to bypass scanning
+ * whenever possible. The scan cache is also used to enable roaming
+ * between access points when operating in infrastructure mode.
+ *
+ * Scanning is handled with pluggable modules that implement "policy"
+ * per-operating mode. The core scanning support provides an
+ * instrastructure to support these modules and exports a common api
+ * to the rest of the 802.11 layer. Policy modules decide what
+ * channels to visit, what state to record to make decisions (e.g. ap
+ * mode scanning for auto channel selection keeps significantly less
+ * state than sta mode scanning for an ap to associate to), and selects
+ * the final station/channel to return as the result of a scan.
+ *
+ * Scanning is done synchronously when initially bringing a vap to an
+ * operational state and optionally in the background to maintain the
+ * scan cache for doing roaming and rogue ap monitoring. Scanning is
+ * not tied to the 802.11 state machine that governs vaps though there
+ * is linkage to the IEEE80211_SCAN state. Only one vap at a time may
+ * be scanning; this scheduling policy is handled in ieee80211_new_state
+ * and is invisible to the scanning code.
+*/
#define IEEE80211_SCAN_MAX IEEE80211_CHAN_MAX
-struct ieee80211_scanner;
+struct ieee80211_scanner; /* scan policy state */
struct ieee80211_scan_ssid {
- int len; /* length in bytes */
- uint8_t ssid[IEEE80211_NWID_LEN]; /* ssid contents */
+ int len; /* length in bytes */
+ uint8_t ssid[IEEE80211_NWID_LEN]; /* ssid contents */
};
-#define IEEE80211_SCAN_MAX_SSID 1
+#define IEEE80211_SCAN_MAX_SSID 1 /* max # ssid's to probe */
+/*
+ * Scan state visible to the 802.11 layer. Scan parameters and
+ * results are stored in this data structure. The ieee80211_scan_state
+ * structure is extended with space that is maintained private to
+ * the core scanning support. We allocate one instance and link it
+ * to the ieee80211com structure; then share it between all associated
+ * vaps. We could allocate multiple of these, e.g. to hold multiple
+ * scan results, but this is sufficient for current needs.
+ */
struct ieee80211_scan_state {
- struct ieee80211com *ss_ic;
+ struct ieee80211vap *ss_vap;
const struct ieee80211_scanner *ss_ops; /* policy hookup, see below */
void *ss_priv; /* scanner private state */
uint16_t ss_flags;
@@ -47,6 +98,8 @@ struct ieee80211_scan_state {
#define IEEE80211_SCAN_PICK1ST 0x0004 /* ``hey sailor'' mode */
#define IEEE80211_SCAN_BGSCAN 0x0008 /* bg scan, exit ps at end */
#define IEEE80211_SCAN_ONCE 0x0010 /* do one complete pass */
+#define IEEE80211_SCAN_NOBCAST 0x0020 /* no broadcast probe req */
+#define IEEE80211_SCAN_NOJOIN 0x0040 /* no auto-sequencing */
#define IEEE80211_SCAN_GOTPICK 0x1000 /* got candidate, can stop */
uint8_t ss_nssid; /* # ssid's to probe/match */
struct ieee80211_scan_ssid ss_ssid[IEEE80211_SCAN_MAX_SSID];
@@ -65,48 +118,64 @@ struct ieee80211_scan_state {
* ss_flags. It might be better to split this stuff out into
* a separate variable to avoid confusion.
*/
-#define IEEE80211_SCAN_FLUSH 0x10000 /* flush candidate table */
-#define IEEE80211_SCAN_NOSSID 0x20000 /* don't update ssid list */
+#define IEEE80211_SCAN_FLUSH 0x00010000 /* flush candidate table */
+#define IEEE80211_SCAN_NOSSID 0x80000000 /* don't update ssid list */
struct ieee80211com;
void ieee80211_scan_attach(struct ieee80211com *);
void ieee80211_scan_detach(struct ieee80211com *);
+void ieee80211_scan_vattach(struct ieee80211vap *);
+void ieee80211_scan_vdetach(struct ieee80211vap *);
void ieee80211_scan_dump_channels(const struct ieee80211_scan_state *);
-int ieee80211_scan_update(struct ieee80211com *);
#define IEEE80211_SCAN_FOREVER 0x7fffffff
-int ieee80211_start_scan(struct ieee80211com *, int flags, u_int duration,
+int ieee80211_start_scan(struct ieee80211vap *, int flags,
+ u_int duration, u_int mindwell, u_int maxdwell,
u_int nssid, const struct ieee80211_scan_ssid ssids[]);
-int ieee80211_check_scan(struct ieee80211com *, int flags, u_int duration,
+int ieee80211_check_scan(struct ieee80211vap *, int flags,
+ u_int duration, u_int mindwell, u_int maxdwell,
u_int nssid, const struct ieee80211_scan_ssid ssids[]);
-int ieee80211_bg_scan(struct ieee80211com *);
-void ieee80211_cancel_scan(struct ieee80211com *);
-void ieee80211_scan_next(struct ieee80211com *);
-void ieee80211_scan_done(struct ieee80211com *);
+int ieee80211_check_scan_current(struct ieee80211vap *);
+int ieee80211_bg_scan(struct ieee80211vap *, int);
+void ieee80211_cancel_scan(struct ieee80211vap *);
+void ieee80211_cancel_anyscan(struct ieee80211vap *);
+void ieee80211_scan_next(struct ieee80211vap *);
+void ieee80211_scan_done(struct ieee80211vap *);
+void ieee80211_probe_curchan(struct ieee80211vap *, int);
+struct ieee80211_channel *ieee80211_scan_pickchannel(struct ieee80211com *, int);
struct ieee80211_scanparams;
-void ieee80211_add_scan(struct ieee80211com *,
+void ieee80211_add_scan(struct ieee80211vap *,
const struct ieee80211_scanparams *,
const struct ieee80211_frame *,
int subtype, int rssi, int noise, int rstamp);
void ieee80211_scan_timeout(struct ieee80211com *);
-void ieee80211_scan_assoc_success(struct ieee80211com *,
+void ieee80211_scan_assoc_success(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
enum {
IEEE80211_SCAN_FAIL_TIMEOUT = 1, /* no response to mgmt frame */
IEEE80211_SCAN_FAIL_STATUS = 2 /* negative response to " " */
};
-void ieee80211_scan_assoc_fail(struct ieee80211com *,
+void ieee80211_scan_assoc_fail(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN], int reason);
-void ieee80211_scan_flush(struct ieee80211com *);
+void ieee80211_scan_flush(struct ieee80211vap *);
struct ieee80211_scan_entry;
typedef void ieee80211_scan_iter_func(void *,
const struct ieee80211_scan_entry *);
-void ieee80211_scan_iterate(struct ieee80211com *,
+void ieee80211_scan_iterate(struct ieee80211vap *,
ieee80211_scan_iter_func, void *);
+enum {
+ IEEE80211_BPARSE_BADIELEN = 0x01, /* ie len past end of frame */
+ IEEE80211_BPARSE_RATES_INVALID = 0x02, /* invalid RATES ie */
+ IEEE80211_BPARSE_XRATES_INVALID = 0x04, /* invalid XRATES ie */
+ IEEE80211_BPARSE_SSID_INVALID = 0x08, /* invalid SSID ie */
+ IEEE80211_BPARSE_CHAN_INVALID = 0x10, /* invalid FH/DSPARMS chan */
+ IEEE80211_BPARSE_OFFCHAN = 0x20, /* DSPARMS chan != curchan */
+ IEEE80211_BPARSE_BINTVAL_INVALID= 0x40, /* invalid beacon interval */
+};
/*
* Parameters supplied when adding/updating an entry in a
@@ -116,14 +185,17 @@ void ieee80211_scan_iterate(struct ieee80211com *,
* All multi-byte values must be in host byte order.
*/
struct ieee80211_scanparams {
- uint16_t capinfo; /* 802.11 capabilities */
- uint16_t fhdwell; /* FHSS dwell interval */
- struct ieee80211_channel *curchan;
- uint8_t bchan; /* chan# advertised inside beacon */
+ uint8_t status; /* bitmask of IEEE80211_BPARSE_* */
+ uint8_t chan; /* channel # from FH/DSPARMS */
+ uint8_t bchan; /* curchan's channel # */
uint8_t fhindex;
- uint8_t erp;
+ uint16_t fhdwell; /* FHSS dwell interval */
+ uint16_t capinfo; /* 802.11 capabilities */
+ uint16_t erp; /* NB: 0x100 indicates ie present */
uint16_t bintval;
uint8_t timoff;
+ uint8_t *ies; /* all captured ies */
+ size_t ies_len; /* length of all captured ies */
uint8_t *tim;
uint8_t *tstamp;
uint8_t *country;
@@ -146,13 +218,14 @@ struct ieee80211_scanparams {
struct ieee80211_scan_entry {
uint8_t se_macaddr[IEEE80211_ADDR_LEN];
uint8_t se_bssid[IEEE80211_ADDR_LEN];
+ /* XXX can point inside se_ies */
uint8_t se_ssid[2+IEEE80211_NWID_LEN];
uint8_t se_rates[2+IEEE80211_RATE_MAXSIZE];
uint8_t se_xrates[2+IEEE80211_RATE_MAXSIZE];
uint32_t se_rstamp; /* recv timestamp */
union {
uint8_t data[8];
- uint64_t tsf;
+ u_int64_t tsf;
} se_tstamp; /* from last rcv'd beacon */
uint16_t se_intval; /* beacon interval (host byte order) */
uint16_t se_capinfo; /* capabilities (host byte order) */
@@ -160,16 +233,12 @@ struct ieee80211_scan_entry {
uint16_t se_timoff; /* byte offset to TIM ie */
uint16_t se_fhdwell; /* FH only (host byte order) */
uint8_t se_fhindex; /* FH only */
- uint8_t se_erp; /* ERP from beacon/probe resp */
+ uint8_t se_dtimperiod; /* DTIM period */
+ uint16_t se_erp; /* ERP from beacon/probe resp */
int8_t se_rssi; /* avg'd recv ssi */
int8_t se_noise; /* noise floor */
- uint8_t se_dtimperiod; /* DTIM period */
- uint8_t *se_wpa_ie; /* captured WPA ie */
- uint8_t *se_rsn_ie; /* captured RSN ie */
- uint8_t *se_wme_ie; /* captured WME ie */
- uint8_t *se_htcap_ie; /* captured HTP cap ie */
- uint8_t *se_htinfo_ie; /* captured HTP info ie */
- uint8_t *se_ath_ie; /* captured Atheros ie */
+ uint8_t se_cc[2]; /* captured country code */
+ struct ieee80211_ies se_ies; /* captured ie's */
u_int se_age; /* age of entry (0 on create) */
};
MALLOC_DECLARE(M_80211_SCAN);
@@ -184,14 +253,16 @@ struct ieee80211_scanner {
int (*scan_attach)(struct ieee80211_scan_state *);
int (*scan_detach)(struct ieee80211_scan_state *);
int (*scan_start)(struct ieee80211_scan_state *,
- struct ieee80211com *);
+ struct ieee80211vap *);
int (*scan_restart)(struct ieee80211_scan_state *,
- struct ieee80211com *);
+ struct ieee80211vap *);
int (*scan_cancel)(struct ieee80211_scan_state *,
- struct ieee80211com *);
+ struct ieee80211vap *);
int (*scan_end)(struct ieee80211_scan_state *,
- struct ieee80211com *);
+ struct ieee80211vap *);
int (*scan_flush)(struct ieee80211_scan_state *);
+ struct ieee80211_channel *(*scan_pickchan)(
+ struct ieee80211_scan_state *, int);
/* add an entry to the cache */
int (*scan_add)(struct ieee80211_scan_state *,
const struct ieee80211_scanparams *,
diff --git a/sys/net80211/ieee80211_scan_ap.c b/sys/net80211/ieee80211_scan_ap.c
deleted file mode 100644
index 300f440..0000000
--- a/sys/net80211/ieee80211_scan_ap.c
+++ /dev/null
@@ -1,408 +0,0 @@
-/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*
- * IEEE 802.11 ap scanning support.
- */
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/module.h>
-
-#include <sys/socket.h>
-
-#include <net/if.h>
-#include <net/if_media.h>
-#include <net/ethernet.h>
-
-#include <net80211/ieee80211_var.h>
-
-#include <net/bpf.h>
-
-struct ap_state {
- int as_maxrssi[IEEE80211_CHAN_MAX];
-};
-
-static int ap_flush(struct ieee80211_scan_state *);
-
-/* number of references from net80211 layer */
-static int nrefs = 0;
-
-/*
- * Attach prior to any scanning work.
- */
-static int
-ap_attach(struct ieee80211_scan_state *ss)
-{
- struct ap_state *as;
-
- MALLOC(as, struct ap_state *, sizeof(struct ap_state),
- M_80211_SCAN, M_NOWAIT);
- ss->ss_priv = as;
- ap_flush(ss);
- nrefs++; /* NB: we assume caller locking */
- return 1;
-}
-
-/*
- * Cleanup any private state.
- */
-static int
-ap_detach(struct ieee80211_scan_state *ss)
-{
- struct ap_state *as = ss->ss_priv;
-
- if (as != NULL) {
- KASSERT(nrefs > 0, ("imbalanced attach/detach"));
- nrefs--; /* NB: we assume caller locking */
- FREE(as, M_80211_SCAN);
- }
- return 1;
-}
-
-/*
- * Flush all per-scan state.
- */
-static int
-ap_flush(struct ieee80211_scan_state *ss)
-{
- struct ap_state *as = ss->ss_priv;
-
- memset(as->as_maxrssi, 0, sizeof(as->as_maxrssi));
- ss->ss_last = 0; /* insure no channel will be picked */
- return 0;
-}
-
-static int
-find11gchannel(struct ieee80211com *ic, int i, int freq)
-{
- const struct ieee80211_channel *c;
- int j;
-
- /*
- * The normal ordering in the channel list is b channel
- * immediately followed by g so optimize the search for
- * this. We'll still do a full search just in case.
- */
- for (j = i+1; j < ic->ic_nchans; j++) {
- c = &ic->ic_channels[j];
- if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
- return 1;
- }
- for (j = 0; j < i; j++) {
- c = &ic->ic_channels[j];
- if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
- return 1;
- }
- return 0;
-}
-
-/*
- * Start an ap scan by populating the channel list.
- */
-static int
-ap_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
-{
- struct ieee80211_channel *c;
- int i;
-
- ss->ss_last = 0;
- if (ic->ic_des_mode == IEEE80211_MODE_AUTO) {
- for (i = 0; i < ic->ic_nchans; i++) {
- c = &ic->ic_channels[i];
- if (IEEE80211_IS_CHAN_TURBO(c)) {
-#ifdef IEEE80211_F_XR
- /* XR is not supported on turbo channels */
- if (ic->ic_flags & IEEE80211_F_XR)
- continue;
-#endif
- /* dynamic channels are scanned in base mode */
- if (!IEEE80211_IS_CHAN_ST(c))
- continue;
- } else if (IEEE80211_IS_CHAN_HT(c)) {
- /* HT channels are scanned in legacy */
- continue;
- } else {
- /*
- * Use any 11g channel instead of 11b one.
- */
- if (IEEE80211_IS_CHAN_B(c) &&
- find11gchannel(ic, i, c->ic_freq))
- continue;
- }
- if (ss->ss_last >= IEEE80211_SCAN_MAX)
- break;
- ss->ss_chans[ss->ss_last++] = c;
- }
- } else {
- static const u_int chanflags[IEEE80211_MODE_MAX] = {
- 0, /* IEEE80211_MODE_AUTO */
- IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */
- IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */
- IEEE80211_CHAN_G, /* IEEE80211_MODE_11G */
- IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */
- IEEE80211_CHAN_108A, /* IEEE80211_MODE_TURBO_A */
- IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */
- IEEE80211_CHAN_ST, /* IEEE80211_MODE_STURBO_A */
- IEEE80211_CHAN_A, /* IEEE80211_MODE_11NA */
- IEEE80211_CHAN_G, /* IEEE80211_MODE_11NG */
- };
- u_int modeflags;
-
- modeflags = chanflags[ic->ic_des_mode];
- if ((ic->ic_flags & IEEE80211_F_TURBOP) &&
- modeflags != IEEE80211_CHAN_ST) {
- if (ic->ic_des_mode == IEEE80211_MODE_11G)
- modeflags = IEEE80211_CHAN_108G;
- else
- modeflags = IEEE80211_CHAN_108A;
- }
- for (i = 0; i < ic->ic_nchans; i++) {
- c = &ic->ic_channels[i];
- if ((c->ic_flags & modeflags) != modeflags)
- continue;
-#ifdef IEEE80211_F_XR
- /* XR is not supported on turbo channels */
- if (IEEE80211_IS_CHAN_TURBO(c) &&
- (ic->ic_flags & IEEE80211_F_XR))
- continue;
-#endif
- if (ss->ss_last >= IEEE80211_SCAN_MAX)
- break;
- /*
- * Do not select static turbo channels if
- * the mode is not static turbo.
- */
- if (IEEE80211_IS_CHAN_STURBO(c) &&
- ic->ic_des_mode != IEEE80211_MODE_STURBO_A)
- continue;
- ss->ss_chans[ss->ss_last++] = c;
- }
- }
- ss->ss_next = 0;
- /* XXX tunables */
- ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */
- ss->ss_maxdwell = msecs_to_ticks(300); /* 300ms */
-
-#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_scan(ic)) {
- if_printf(ic->ic_ifp, "scan set ");
- ieee80211_scan_dump_channels(ss);
- printf(" dwell min %ld max %ld\n",
- ss->ss_mindwell, ss->ss_maxdwell);
- }
-#endif /* IEEE80211_DEBUG */
-
- return 0;
-}
-
-/*
- * Restart a bg scan.
- */
-static int
-ap_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
-{
- return 0;
-}
-
-/*
- * Cancel an ongoing scan.
- */
-static int
-ap_cancel(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
-{
- return 0;
-}
-
-/*
- * Record max rssi on channel.
- */
-static int
-ap_add(struct ieee80211_scan_state *ss,
- const struct ieee80211_scanparams *sp,
- const struct ieee80211_frame *wh,
- int subtype, int rssi, int noise, int rstamp)
-{
- struct ap_state *as = ss->ss_priv;
- struct ieee80211com *ic = ss->ss_ic;
- int chan;
-
- chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
- /* XXX better quantification of channel use? */
- /* XXX count bss's? */
- if (rssi > as->as_maxrssi[chan])
- as->as_maxrssi[chan] = rssi;
- /* XXX interference, turbo requirements */
- return 1;
-}
-
-/*
- * Pick a quiet channel to use for ap operation.
- */
-static int
-ap_end(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
-{
- struct ap_state *as = ss->ss_priv;
- int i, chan, bestchan, bestchanix;
-
- KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP,
- ("wrong opmode %u", ic->ic_opmode));
- /* XXX select channel more intelligently, e.g. channel spread, power */
- bestchan = -1;
- bestchanix = 0; /* NB: silence compiler */
- /* NB: use scan list order to preserve channel preference */
- for (i = 0; i < ss->ss_last; i++) {
- /*
- * If the channel is unoccupied the max rssi
- * should be zero; just take it. Otherwise
- * track the channel with the lowest rssi and
- * use that when all channels appear occupied.
- */
- /* XXX channel have interference? */
- chan = ieee80211_chan2ieee(ic, ss->ss_chans[i]);
-
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: channel %u rssi %d bestchan %d bestchan rssi %d\n",
- __func__, chan, as->as_maxrssi[chan],
- bestchan, bestchan != -1 ? as->as_maxrssi[bestchan] : 0);
-
- if (as->as_maxrssi[chan] == 0) {
- bestchan = chan;
- bestchanix = i;
- /* XXX use other considerations */
- break;
- }
- if (bestchan == -1 ||
- as->as_maxrssi[chan] < as->as_maxrssi[bestchan])
- bestchan = chan;
- }
- if (bestchan == -1) {
- /* no suitable channel, should not happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: no suitable channel! (should not happen)\n", __func__);
- /* XXX print something? */
- return 0; /* restart scan */
- } else {
- struct ieee80211_channel *c;
-
- /* XXX notify all vap's? */
- /*
- * If this is a dynamic turbo frequency,
- * start with normal mode first.
- */
- c = ss->ss_chans[bestchanix];
- if (IEEE80211_IS_CHAN_TURBO(c) &&
- !IEEE80211_IS_CHAN_STURBO(c)) {
- c = ieee80211_find_channel(ic, c->ic_freq,
- c->ic_flags & ~IEEE80211_CHAN_TURBO);
- if (c == NULL) {
- /* should never happen ?? */
- return 0;
- }
- }
- ieee80211_create_ibss(ic,
- ieee80211_ht_adjust_channel(ic, c, ic->ic_flags_ext));
- return 1;
- }
-}
-
-static void
-ap_age(struct ieee80211_scan_state *ss)
-{
- /* XXX is there anything meaningful to do? */
-}
-
-static void
-ap_iterate(struct ieee80211_scan_state *ss,
- ieee80211_scan_iter_func *f, void *arg)
-{
- /* NB: nothing meaningful we can do */
-}
-
-static void
-ap_assoc_success(struct ieee80211_scan_state *ss,
- const uint8_t macaddr[IEEE80211_ADDR_LEN])
-{
- /* should not be called */
-}
-
-static void
-ap_assoc_fail(struct ieee80211_scan_state *ss,
- const uint8_t macaddr[IEEE80211_ADDR_LEN], int reason)
-{
- /* should not be called */
-}
-
-static const struct ieee80211_scanner ap_default = {
- .scan_name = "default",
- .scan_attach = ap_attach,
- .scan_detach = ap_detach,
- .scan_start = ap_start,
- .scan_restart = ap_restart,
- .scan_cancel = ap_cancel,
- .scan_end = ap_end,
- .scan_flush = ap_flush,
- .scan_add = ap_add,
- .scan_age = ap_age,
- .scan_iterate = ap_iterate,
- .scan_assoc_success = ap_assoc_success,
- .scan_assoc_fail = ap_assoc_fail,
-};
-
-/*
- * Module glue.
- */
-static int
-wlan_modevent(module_t mod, int type, void *unused)
-{
- switch (type) {
- case MOD_LOAD:
- ieee80211_scanner_register(IEEE80211_M_HOSTAP, &ap_default);
- return 0;
- case MOD_UNLOAD:
- case MOD_QUIESCE:
- if (nrefs) {
- printf("wlan_scan_ap: still in use (%u dynamic refs)\n",
- nrefs);
- return EBUSY;
- }
- if (type == MOD_UNLOAD)
- ieee80211_scanner_unregister_all(&ap_default);
- return 0;
- }
- return EINVAL;
-}
-
-static moduledata_t wlan_mod = {
- "wlan_scan_ap",
- wlan_modevent,
- 0
-};
-DECLARE_MODULE(wlan_scan_ap, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_scan_ap, 1);
-MODULE_DEPEND(wlan_scan_ap, wlan, 1, 1, 1);
diff --git a/sys/net80211/ieee80211_scan_sta.c b/sys/net80211/ieee80211_scan_sta.c
index d152605..d1dc060 100644
--- a/sys/net80211/ieee80211_scan_sta.c
+++ b/sys/net80211/ieee80211_scan_sta.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 station scanning support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
@@ -41,6 +43,8 @@ __FBSDID("$FreeBSD$");
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_input.h>
+#include <net80211/ieee80211_regdomain.h>
#include <net/bpf.h>
@@ -61,20 +65,6 @@ __FBSDID("$FreeBSD$");
#define STA_RSSI_MIN 8 /* min acceptable rssi */
#define STA_RSSI_MAX 40 /* max rssi for comparison */
-#define RSSI_LPF_LEN 10
-#define RSSI_DUMMY_MARKER 0x127
-#define RSSI_EP_MULTIPLIER (1<<7) /* pow2 to optimize out * and / */
-#define RSSI_IN(x) ((x) * RSSI_EP_MULTIPLIER)
-#define LPF_RSSI(x, y, len) \
- ((x != RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y))
-#define RSSI_LPF(x, y) do { \
- if ((y) >= -20) \
- x = LPF_RSSI((x), RSSI_IN((y)), RSSI_LPF_LEN); \
-} while (0)
-#define EP_RND(x, mul) \
- ((((x)%(mul)) >= ((mul)/2)) ? howmany(x, mul) : (x)/(mul))
-#define RSSI_GET(x) EP_RND(x, RSSI_EP_MULTIPLIER)
-
struct sta_entry {
struct ieee80211_scan_entry base;
TAILQ_ENTRY(sta_entry) se_list;
@@ -83,11 +73,14 @@ struct sta_entry {
uint8_t se_seen; /* seen during current scan */
uint8_t se_notseen; /* not seen in previous scans */
uint8_t se_flags;
+#define STA_SSID_MATCH 0x01
+#define STA_BSSID_MATCH 0x02
uint32_t se_avgrssi; /* LPF rssi state */
unsigned long se_lastupdate; /* time of last update */
unsigned long se_lastfail; /* time of last failure */
unsigned long se_lastassoc; /* time of last association */
u_int se_scangen; /* iterator scan gen# */
+ u_int se_countrygen; /* gen# of last cc notify */
};
#define STA_HASHSIZE 32
@@ -99,9 +92,12 @@ struct sta_table {
struct mtx st_lock; /* on scan table */
TAILQ_HEAD(, sta_entry) st_entry; /* all entries */
LIST_HEAD(, sta_entry) st_hash[STA_HASHSIZE];
- struct mtx st_scanlock; /* on st_scangen */
- u_int st_scangen; /* gen# for iterator */
+ struct mtx st_scanlock; /* on st_scaniter */
+ u_int st_scaniter; /* gen# for iterator */
+ u_int st_scangen; /* scan generation # */
int st_newscan;
+ /* ap-related state */
+ int st_maxrssi[IEEE80211_CHAN_MAX];
};
static void sta_flush_table(struct sta_table *);
@@ -120,8 +116,16 @@ static void sta_flush_table(struct sta_table *);
#define MATCH_FAILS 0x040 /* too many failed auth attempts */
#define MATCH_NOTSEEN 0x080 /* not seen in recent scans */
#define MATCH_RSSI 0x100 /* rssi deemed too low to use */
-static int match_bss(struct ieee80211com *,
+#define MATCH_CC 0x200 /* country code mismatch */
+static int match_bss(struct ieee80211vap *,
const struct ieee80211_scan_state *, struct sta_entry *, int);
+static void adhoc_age(struct ieee80211_scan_state *);
+
+static __inline int
+isocmp(const uint8_t cc1[], const uint8_t cc2[])
+{
+ return (cc1[0] == cc2[0] && cc1[1] == cc2[1]);
+}
/* number of references from net80211 layer */
static int nrefs = 0;
@@ -191,18 +195,10 @@ sta_flush_table(struct sta_table *st)
TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) {
TAILQ_REMOVE(&st->st_entry, se, se_list);
LIST_REMOVE(se, se_hash);
+ ieee80211_ies_cleanup(&se->base.se_ies);
FREE(se, M_80211_SCAN);
}
-}
-
-static void
-saveie(uint8_t **iep, const uint8_t *ie)
-{
-
- if (ie == NULL)
- *iep = NULL;
- else
- ieee80211_saveie(iep, ie);
+ memset(st->st_maxrssi, 0, sizeof(st->st_maxrssi));
}
/*
@@ -221,10 +217,11 @@ sta_add(struct ieee80211_scan_state *ss,
IEEE80211_SCAN_PICK1ST)
struct sta_table *st = ss->ss_priv;
const uint8_t *macaddr = wh->i_addr2;
- struct ieee80211com *ic = ss->ss_ic;
+ struct ieee80211vap *vap = ss->ss_vap;
+ struct ieee80211com *ic = vap->iv_ic;
struct sta_entry *se;
struct ieee80211_scan_entry *ise;
- int hash, offchan;
+ int hash;
hash = STA_HASH(macaddr);
@@ -238,8 +235,8 @@ sta_add(struct ieee80211_scan_state *ss,
mtx_unlock(&st->st_lock);
return 0;
}
- se->se_scangen = st->st_scangen-1;
- se->se_avgrssi = RSSI_DUMMY_MARKER;
+ se->se_scangen = st->st_scaniter-1;
+ se->se_avgrssi = IEEE80211_RSSI_DUMMY_MARKER;
IEEE80211_ADDR_COPY(se->base.se_macaddr, macaddr);
TAILQ_INSERT_TAIL(&st->st_entry, se, se_list);
LIST_INSERT_HEAD(&st->st_hash[hash], se, se_hash);
@@ -254,23 +251,21 @@ found:
memcpy(ise->se_rates, sp->rates, 2+sp->rates[1]);
if (sp->xrates != NULL) {
/* XXX validate xrates[1] */
- KASSERT(sp->xrates[1] + sp->rates[1] <= IEEE80211_RATE_MAXSIZE,
+ KASSERT(sp->xrates[1] <= IEEE80211_RATE_MAXSIZE,
("xrate set too large: %u", sp->xrates[1]));
memcpy(ise->se_xrates, sp->xrates, 2+sp->xrates[1]);
} else
ise->se_xrates[1] = 0;
IEEE80211_ADDR_COPY(ise->se_bssid, wh->i_addr3);
- offchan = (IEEE80211_CHAN2IEEE(sp->curchan) != sp->bchan &&
- ic->ic_phytype != IEEE80211_T_FH);
- if (!offchan) {
+ if ((sp->status & IEEE80211_BPARSE_OFFCHAN) == 0) {
/*
* Record rssi data using extended precision LPF filter.
*
* NB: use only on-channel data to insure we get a good
* estimate of the signal we'll see when associated.
*/
- RSSI_LPF(se->se_avgrssi, rssi);
- ise->se_rssi = RSSI_GET(se->se_avgrssi);
+ IEEE80211_RSSI_LPF(se->se_avgrssi, rssi);
+ ise->se_rssi = IEEE80211_RSSI_GET(se->se_avgrssi);
ise->se_noise = noise;
}
ise->se_rstamp = rstamp;
@@ -280,24 +275,26 @@ found:
/*
* Beware of overriding se_chan for frames seen
* off-channel; this can cause us to attempt an
- * assocation on the wrong channel.
+ * association on the wrong channel.
*/
- if (offchan) {
+ if (sp->status & IEEE80211_BPARSE_OFFCHAN) {
struct ieee80211_channel *c;
/*
* Off-channel, locate the home/bss channel for the sta
- * using the value broadcast in the DSPARMS ie.
+ * using the value broadcast in the DSPARMS ie. We know
+ * sp->chan has this value because it's used to calculate
+ * IEEE80211_BPARSE_OFFCHAN.
*/
- c = ieee80211_find_channel_byieee(ic, sp->bchan,
- sp->curchan->ic_flags);
+ c = ieee80211_find_channel_byieee(ic, sp->chan,
+ ic->ic_curchan->ic_flags);
if (c != NULL) {
ise->se_chan = c;
} else if (ise->se_chan == NULL) {
/* should not happen, pick something */
- ise->se_chan = sp->curchan;
+ ise->se_chan = ic->ic_curchan;
}
} else
- ise->se_chan = sp->curchan;
+ ise->se_chan = ic->ic_curchan;
ise->se_fhdwell = sp->fhdwell;
ise->se_fhindex = sp->fhindex;
ise->se_erp = sp->erp;
@@ -307,17 +304,39 @@ found:
(const struct ieee80211_tim_ie *) sp->tim;
ise->se_dtimperiod = tim->tim_period;
}
- saveie(&ise->se_wme_ie, sp->wme);
- saveie(&ise->se_wpa_ie, sp->wpa);
- saveie(&ise->se_rsn_ie, sp->rsn);
- saveie(&ise->se_ath_ie, sp->ath);
- saveie(&ise->se_htcap_ie, sp->htcap);
- saveie(&ise->se_htinfo_ie, sp->htinfo);
+ if (sp->country != NULL) {
+ const struct ieee80211_country_ie *cie =
+ (const struct ieee80211_country_ie *) sp->country;
+ /*
+ * If 11d is enabled and we're attempting to join a bss
+ * that advertises it's country code then compare our
+ * current settings to what we fetched from the country ie.
+ * If our country code is unspecified or different then
+ * dispatch an event to user space that identifies the
+ * country code so our regdomain config can be changed.
+ */
+ /* XXX only for STA mode? */
+ if ((IEEE80211_IS_CHAN_11D(ise->se_chan) ||
+ (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) &&
+ (ic->ic_regdomain.country == CTRY_DEFAULT ||
+ !isocmp(cie->cc, ic->ic_regdomain.isocc))) {
+ /* only issue one notify event per scan */
+ if (se->se_countrygen != st->st_scangen) {
+ ieee80211_notify_country(vap, ise->se_bssid,
+ cie->cc);
+ se->se_countrygen = st->st_scangen;
+ }
+ }
+ ise->se_cc[0] = cie->cc[0];
+ ise->se_cc[1] = cie->cc[1];
+ }
+ /* NB: no need to setup ie ptrs; they are not (currently) used */
+ (void) ieee80211_ies_init(&ise->se_ies, sp->ies, sp->ies_len);
/* clear failure count after STA_FAIL_AGE passes */
if (se->se_fails && (ticks - se->se_lastfail) > STA_FAILS_AGE*hz) {
se->se_fails = 0;
- IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN, macaddr,
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, macaddr,
"%s: fails %u", __func__, se->se_fails);
}
@@ -325,13 +344,16 @@ found:
se->se_seen = 1;
se->se_notseen = 0;
+ if (rssi > st->st_maxrssi[sp->bchan])
+ st->st_maxrssi[sp->bchan] = rssi;
+
mtx_unlock(&st->st_lock);
/*
* If looking for a quick choice and nothing's
* been found check here.
*/
- if (PICK1ST(ss) && match_bss(ic, ss, se, IEEE80211_MSG_SCAN) == 0)
+ if (PICK1ST(ss) && match_bss(vap, ss, se, IEEE80211_MSG_SCAN) == 0)
ss->ss_flags |= IEEE80211_SCAN_GOTPICK;
return 1;
@@ -343,11 +365,11 @@ found:
* Check if a channel is excluded by user request.
*/
static int
-isexcluded(struct ieee80211com *ic, const struct ieee80211_channel *c)
+isexcluded(struct ieee80211vap *vap, const struct ieee80211_channel *c)
{
- return (isclr(ic->ic_chan_active, c->ic_ieee) ||
- (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
- c->ic_freq != ic->ic_des_chan->ic_freq));
+ return (isclr(vap->iv_ic->ic_chan_active, c->ic_ieee) ||
+ (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
+ c->ic_freq != vap->iv_des_chan->ic_freq));
}
static struct ieee80211_channel *
@@ -387,11 +409,12 @@ static const u_int chanflags[IEEE80211_MODE_MAX] = {
};
static void
-add_channels(struct ieee80211com *ic,
+add_channels(struct ieee80211vap *vap,
struct ieee80211_scan_state *ss,
enum ieee80211_phymode mode, const uint16_t freq[], int nfreq)
{
#define N(a) (sizeof(a) / sizeof(a[0]))
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *c, *cg;
u_int modeflags;
int i;
@@ -403,89 +426,28 @@ add_channels(struct ieee80211com *ic,
break;
c = ieee80211_find_channel(ic, freq[i], modeflags);
- if (c != NULL && isexcluded(ic, c))
+ if (c == NULL || isexcluded(vap, c))
continue;
if (mode == IEEE80211_MODE_AUTO) {
/*
* XXX special-case 11b/g channels so we select
- * the g channel if both are present or there
- * are only g channels.
+ * the g channel if both are present.
*/
- if (c == NULL || IEEE80211_IS_CHAN_B(c)) {
- cg = find11gchannel(ic, i, freq[i]);
- if (cg != NULL)
- c = cg;
- }
+ if (IEEE80211_IS_CHAN_B(c) &&
+ (cg = find11gchannel(ic, i, c->ic_freq)) != NULL)
+ c = cg;
}
- if (c == NULL)
- continue;
-
ss->ss_chans[ss->ss_last++] = c;
}
#undef N
}
-static const uint16_t rcl1[] = /* 8 FCC channel: 52, 56, 60, 64, 36, 40, 44, 48 */
-{ 5260, 5280, 5300, 5320, 5180, 5200, 5220, 5240 };
-static const uint16_t rcl2[] = /* 4 MKK channels: 34, 38, 42, 46 */
-{ 5170, 5190, 5210, 5230 };
-static const uint16_t rcl3[] = /* 2.4Ghz ch: 1,6,11,7,13 */
-{ 2412, 2437, 2462, 2442, 2472 };
-static const uint16_t rcl4[] = /* 5 FCC channel: 149, 153, 161, 165 */
-{ 5745, 5765, 5785, 5805, 5825 };
-static const uint16_t rcl7[] = /* 11 ETSI channel: 100,104,108,112,116,120,124,128,132,136,140 */
-{ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700 };
-static const uint16_t rcl8[] = /* 2.4Ghz ch: 2,3,4,5,8,9,10,12 */
-{ 2417, 2422, 2427, 2432, 2447, 2452, 2457, 2467 };
-static const uint16_t rcl9[] = /* 2.4Ghz ch: 14 */
-{ 2484 };
-static const uint16_t rcl10[] = /* Added Korean channels 2312-2372 */
-{ 2312, 2317, 2322, 2327, 2332, 2337, 2342, 2347, 2352, 2357, 2362, 2367, 2372 };
-static const uint16_t rcl11[] = /* Added Japan channels in 4.9/5.0 spectrum */
-{ 5040, 5060, 5080, 4920, 4940, 4960, 4980 };
-#ifdef ATH_TURBO_SCAN
-static const uint16_t rcl5[] = /* 3 static turbo channels */
-{ 5210, 5250, 5290 };
-static const uint16_t rcl6[] = /* 2 static turbo channels */
-{ 5760, 5800 };
-static const uint16_t rcl6x[] = /* 4 FCC3 turbo channels */
-{ 5540, 5580, 5620, 5660 };
-static const uint16_t rcl12[] = /* 2.4Ghz Turbo channel 6 */
-{ 2437 };
-static const uint16_t rcl13[] = /* dynamic Turbo channels */
-{ 5200, 5240, 5280, 5765, 5805 };
-#endif /* ATH_TURBO_SCAN */
-
struct scanlist {
uint16_t mode;
uint16_t count;
const uint16_t *list;
};
-#define X(a) .count = sizeof(a)/sizeof(a[0]), .list = a
-
-static const struct scanlist staScanTable[] = {
- { IEEE80211_MODE_11B, X(rcl3) },
- { IEEE80211_MODE_11A, X(rcl1) },
- { IEEE80211_MODE_11A, X(rcl2) },
- { IEEE80211_MODE_11B, X(rcl8) },
- { IEEE80211_MODE_11B, X(rcl9) },
- { IEEE80211_MODE_11A, X(rcl4) },
-#ifdef ATH_TURBO_SCAN
- { IEEE80211_MODE_STURBO_A, X(rcl5) },
- { IEEE80211_MODE_STURBO_A, X(rcl6) },
- { IEEE80211_MODE_TURBO_A, X(rcl6x) },
- { IEEE80211_MODE_TURBO_A, X(rcl13) },
-#endif /* ATH_TURBO_SCAN */
- { IEEE80211_MODE_11A, X(rcl7) },
- { IEEE80211_MODE_11B, X(rcl10) },
- { IEEE80211_MODE_11A, X(rcl11) },
-#ifdef ATH_TURBO_SCAN
- { IEEE80211_MODE_TURBO_G, X(rcl12) },
-#endif /* ATH_TURBO_SCAN */
- { .list = NULL }
-};
-
static int
checktable(const struct scanlist *scan, const struct ieee80211_channel *c)
{
@@ -500,16 +462,13 @@ checktable(const struct scanlist *scan, const struct ieee80211_channel *c)
}
static void
-sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211com *ic,
+sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211vap *vap,
const struct scanlist table[])
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *c;
int i;
- /*
- * Add the channels from the ic (from HAL) that are not present
- * in the staScanTable.
- */
for (i = 0; i < ic->ic_nchans; i++) {
if (ss->ss_last >= IEEE80211_SCAN_MAX)
break;
@@ -528,14 +487,14 @@ sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211com *ic,
* If a desired mode was specified, scan only
* channels that satisfy that constraint.
*/
- if (ic->ic_des_mode != IEEE80211_MODE_AUTO &&
- ic->ic_des_mode != ieee80211_chan2mode(c))
+ if (vap->iv_des_mode != IEEE80211_MODE_AUTO &&
+ vap->iv_des_mode != ieee80211_chan2mode(c))
continue;
/*
* Skip channels excluded by user request.
*/
- if (isexcluded(ic, c))
+ if (isexcluded(vap, c))
continue;
/*
@@ -552,14 +511,10 @@ sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211com *ic,
}
}
-/*
- * Start a station-mode scan by populating the channel list.
- */
-static int
-sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+static void
+makescanlist(struct ieee80211_scan_state *ss, struct ieee80211vap *vap,
+ const struct scanlist table[])
{
-#define N(a) (sizeof(a)/sizeof(a[0]))
- struct sta_table *st = ss->ss_priv;
const struct scanlist *scan;
enum ieee80211_phymode mode;
@@ -569,20 +524,20 @@ sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
* of channels for scanning. Any channels in the ordered
* list not in the master list will be discarded.
*/
- for (scan = staScanTable; scan->list != NULL; scan++) {
+ for (scan = table; scan->list != NULL; scan++) {
mode = scan->mode;
- if (ic->ic_des_mode != IEEE80211_MODE_AUTO) {
+ if (vap->iv_des_mode != IEEE80211_MODE_AUTO) {
/*
* If a desired mode was specified, scan only
* channels that satisfy that constraint.
*/
- if (ic->ic_des_mode != mode) {
+ if (vap->iv_des_mode != mode) {
/*
* The scan table marks 2.4Ghz channels as b
* so if the desired mode is 11g, then use
* the 11b channel list but upgrade the mode.
*/
- if (ic->ic_des_mode != IEEE80211_MODE_11G ||
+ if (vap->iv_des_mode != IEEE80211_MODE_11G ||
mode != IEEE80211_MODE_11B)
continue;
mode = IEEE80211_MODE_11G; /* upgrade */
@@ -597,7 +552,7 @@ sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
}
#ifdef IEEE80211_F_XR
/* XR does not operate on turbo channels */
- if ((ic->ic_flags & IEEE80211_F_XR) &&
+ if ((vap->iv_flags & IEEE80211_F_XR) &&
(mode == IEEE80211_MODE_TURBO_A ||
mode == IEEE80211_MODE_TURBO_G ||
mode == IEEE80211_MODE_STURBO_A))
@@ -607,40 +562,98 @@ sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
* Add the list of the channels; any that are not
* in the master channel list will be discarded.
*/
- add_channels(ic, ss, mode, scan->list, scan->count);
+ add_channels(vap, ss, mode, scan->list, scan->count);
}
/*
- * Add the channels from the ic (from HAL) that are not present
- * in the staScanTable.
+ * Add the channels from the ic that are not present
+ * in the table.
*/
- sweepchannels(ss, ic, staScanTable);
+ sweepchannels(ss, vap, table);
+}
- ss->ss_next = 0;
- /* XXX tunables */
- ss->ss_mindwell = msecs_to_ticks(20); /* 20ms */
- ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+static const uint16_t rcl1[] = /* 8 FCC channel: 52, 56, 60, 64, 36, 40, 44, 48 */
+{ 5260, 5280, 5300, 5320, 5180, 5200, 5220, 5240 };
+static const uint16_t rcl2[] = /* 4 MKK channels: 34, 38, 42, 46 */
+{ 5170, 5190, 5210, 5230 };
+static const uint16_t rcl3[] = /* 2.4Ghz ch: 1,6,11,7,13 */
+{ 2412, 2437, 2462, 2442, 2472 };
+static const uint16_t rcl4[] = /* 5 FCC channel: 149, 153, 161, 165 */
+{ 5745, 5765, 5785, 5805, 5825 };
+static const uint16_t rcl7[] = /* 11 ETSI channel: 100,104,108,112,116,120,124,128,132,136,140 */
+{ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700 };
+static const uint16_t rcl8[] = /* 2.4Ghz ch: 2,3,4,5,8,9,10,12 */
+{ 2417, 2422, 2427, 2432, 2447, 2452, 2457, 2467 };
+static const uint16_t rcl9[] = /* 2.4Ghz ch: 14 */
+{ 2484 };
+static const uint16_t rcl10[] = /* Added Korean channels 2312-2372 */
+{ 2312, 2317, 2322, 2327, 2332, 2337, 2342, 2347, 2352, 2357, 2362, 2367, 2372 };
+static const uint16_t rcl11[] = /* Added Japan channels in 4.9/5.0 spectrum */
+{ 5040, 5060, 5080, 4920, 4940, 4960, 4980 };
+#ifdef ATH_TURBO_SCAN
+static const uint16_t rcl5[] = /* 3 static turbo channels */
+{ 5210, 5250, 5290 };
+static const uint16_t rcl6[] = /* 2 static turbo channels */
+{ 5760, 5800 };
+static const uint16_t rcl6x[] = /* 4 FCC3 turbo channels */
+{ 5540, 5580, 5620, 5660 };
+static const uint16_t rcl12[] = /* 2.4Ghz Turbo channel 6 */
+{ 2437 };
+static const uint16_t rcl13[] = /* dynamic Turbo channels */
+{ 5200, 5240, 5280, 5765, 5805 };
+#endif /* ATH_TURBO_SCAN */
-#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_scan(ic)) {
- if_printf(ic->ic_ifp, "scan set ");
- ieee80211_scan_dump_channels(ss);
- printf(" dwell min %ld max %ld\n",
- ss->ss_mindwell, ss->ss_maxdwell);
- }
-#endif /* IEEE80211_DEBUG */
+#define X(a) .count = sizeof(a)/sizeof(a[0]), .list = a
+static const struct scanlist staScanTable[] = {
+ { IEEE80211_MODE_11B, X(rcl3) },
+ { IEEE80211_MODE_11A, X(rcl1) },
+ { IEEE80211_MODE_11A, X(rcl2) },
+ { IEEE80211_MODE_11B, X(rcl8) },
+ { IEEE80211_MODE_11B, X(rcl9) },
+ { IEEE80211_MODE_11A, X(rcl4) },
+#ifdef ATH_TURBO_SCAN
+ { IEEE80211_MODE_STURBO_A, X(rcl5) },
+ { IEEE80211_MODE_STURBO_A, X(rcl6) },
+ { IEEE80211_MODE_TURBO_A, X(rcl6x) },
+ { IEEE80211_MODE_TURBO_A, X(rcl13) },
+#endif /* ATH_TURBO_SCAN */
+ { IEEE80211_MODE_11A, X(rcl7) },
+ { IEEE80211_MODE_11B, X(rcl10) },
+ { IEEE80211_MODE_11A, X(rcl11) },
+#ifdef ATH_TURBO_SCAN
+ { IEEE80211_MODE_TURBO_G, X(rcl12) },
+#endif /* ATH_TURBO_SCAN */
+ { .list = NULL }
+};
+
+/*
+ * Start a station-mode scan by populating the channel list.
+ */
+static int
+sta_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
+{
+ struct sta_table *st = ss->ss_priv;
+
+ makescanlist(ss, vap, staScanTable);
+
+ if (ss->ss_mindwell == 0)
+ ss->ss_mindwell = msecs_to_ticks(20); /* 20ms */
+ if (ss->ss_maxdwell == 0)
+ ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+
+ st->st_scangen++;
st->st_newscan = 1;
return 0;
-#undef N
}
/*
- * Restart a bg scan.
+ * Restart a scan, typically a bg scan but can
+ * also be a fg scan that came up empty.
*/
static int
-sta_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+sta_restart(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct sta_table *st = ss->ss_priv;
@@ -652,18 +665,44 @@ sta_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
* Cancel an ongoing scan.
*/
static int
-sta_cancel(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+sta_cancel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
return 0;
}
-static uint8_t
+/* unalligned little endian access */
+#define LE_READ_2(p) \
+ ((uint16_t) \
+ ((((const uint8_t *)(p))[0] ) | \
+ (((const uint8_t *)(p))[1] << 8)))
+
+static int
maxrate(const struct ieee80211_scan_entry *se)
{
- uint8_t rmax, r;
- int i;
+ const struct ieee80211_ie_htcap *htcap =
+ (const struct ieee80211_ie_htcap *) se->se_ies.htcap_ie;
+ int rmax, r, i;
+ uint16_t caps;
rmax = 0;
+ if (htcap != NULL) {
+ /*
+ * HT station; inspect supported MCS and then adjust
+ * rate by channel width. Could also include short GI
+ * in this if we want to be extra accurate.
+ */
+ /* XXX assumes MCS15 is max */
+ for (i = 15; i >= 0 && isclr(htcap->hc_mcsset, i); i--)
+ ;
+ if (i >= 0) {
+ caps = LE_READ_2(&htcap->hc_cap);
+ /* XXX short/long GI */
+ if (caps & IEEE80211_HTCAP_CHWIDTH40)
+ rmax = ieee80211_htrates[i].ht40_rate_400ns;
+ else
+ rmax = ieee80211_htrates[i].ht40_rate_800ns;
+ }
+ }
for (i = 0; i < se->se_rates[1]; i++) {
r = se->se_rates[2+i] & IEEE80211_RATE_VAL;
if (r > rmax)
@@ -691,7 +730,7 @@ sta_compare(const struct sta_entry *a, const struct sta_entry *b)
if (((_a) ^ (_b)) & (_what)) \
return ((_a) & (_what)) ? 1 : -1; \
} while (0)
- uint8_t maxa, maxb;
+ int maxa, maxb;
int8_t rssia, rssib;
int weight;
@@ -722,12 +761,8 @@ sta_compare(const struct sta_entry *a, const struct sta_entry *b)
return maxa - maxb;
/* XXX use freq for channel preference */
/* for now just prefer 5Ghz band to all other bands */
- if (IEEE80211_IS_CHAN_5GHZ(a->base.se_chan) &&
- !IEEE80211_IS_CHAN_5GHZ(b->base.se_chan))
- return 1;
- if (!IEEE80211_IS_CHAN_5GHZ(a->base.se_chan) &&
- IEEE80211_IS_CHAN_5GHZ(b->base.se_chan))
- return -1;
+ PREFER(IEEE80211_IS_CHAN_5GHZ(a->base.se_chan),
+ IEEE80211_IS_CHAN_5GHZ(b->base.se_chan), 1);
}
/* all things being equal, use signal level */
return a->base.se_rssi - b->base.se_rssi;
@@ -736,20 +771,23 @@ sta_compare(const struct sta_entry *a, const struct sta_entry *b)
/*
* Check rate set suitability and return the best supported rate.
+ * XXX inspect MCS for HT
*/
static int
-check_rate(struct ieee80211com *ic, const struct ieee80211_scan_entry *se)
+check_rate(struct ieee80211vap *vap, const struct ieee80211_scan_entry *se)
{
#define RV(v) ((v) & IEEE80211_RATE_VAL)
const struct ieee80211_rateset *srs;
- int i, j, nrs, r, okrate, badrate, fixedrate;
+ int i, j, nrs, r, okrate, badrate, fixedrate, ucastrate;
const uint8_t *rs;
- okrate = badrate = fixedrate = 0;
+ okrate = badrate = 0;
- srs = ieee80211_get_suprates(ic, se->se_chan);
+ srs = ieee80211_get_suprates(vap->iv_ic, se->se_chan);
nrs = se->se_rates[1];
rs = se->se_rates+2;
+ /* XXX MCS */
+ ucastrate = vap->iv_txparms[ieee80211_chan2mode(se->se_chan)].ucastrate;
fixedrate = IEEE80211_FIXED_RATE_NONE;
again:
for (i = 0; i < nrs; i++) {
@@ -758,7 +796,7 @@ again:
/*
* Check any fixed rate is included.
*/
- if (r == ic->ic_fixed_rate)
+ if (r == ucastrate)
fixedrate = r;
/*
* Check against our supported rates.
@@ -787,7 +825,7 @@ again:
}
back:
- if (okrate == 0 || ic->ic_fixed_rate != fixedrate)
+ if (okrate == 0 || ucastrate != fixedrate)
return badrate | IEEE80211_RATE_BASIC;
else
return RV(okrate);
@@ -812,13 +850,14 @@ match_ssid(const uint8_t *ie,
* Test a scan candidate for suitability/compatibility.
*/
static int
-match_bss(struct ieee80211com *ic,
+match_bss(struct ieee80211vap *vap,
const struct ieee80211_scan_state *ss, struct sta_entry *se0,
int debug)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_entry *se = &se0->base;
- uint8_t rate;
- int fail;
+ uint8_t rate;
+ int fail;
fail = 0;
if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, se->se_chan)))
@@ -830,18 +869,33 @@ match_bss(struct ieee80211com *ic,
* list so we check the desired mode here to weed them
* out.
*/
- if (ic->ic_des_mode != IEEE80211_MODE_AUTO &&
+ if (vap->iv_des_mode != IEEE80211_MODE_AUTO &&
(se->se_chan->ic_flags & IEEE80211_CHAN_ALLTURBO) !=
- chanflags[ic->ic_des_mode])
+ chanflags[vap->iv_des_mode])
fail |= MATCH_CHANNEL;
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
if ((se->se_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
fail |= MATCH_CAPINFO;
} else {
if ((se->se_capinfo & IEEE80211_CAPINFO_ESS) == 0)
fail |= MATCH_CAPINFO;
+ /*
+ * If 11d is enabled and we're attempting to join a bss
+ * that advertises it's country code then compare our
+ * current settings to what we fetched from the country ie.
+ * If our country code is unspecified or different then do
+ * not attempt to join the bss. We should have already
+ * dispatched an event to user space that identifies the
+ * new country code so our regdomain config should match.
+ */
+ if ((IEEE80211_IS_CHAN_11D(se->se_chan) ||
+ (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) &&
+ se->se_cc[0] != 0 &&
+ (ic->ic_regdomain.country == CTRY_DEFAULT ||
+ !isocmp(se->se_cc, ic->ic_regdomain.isocc)))
+ fail |= MATCH_CC;
}
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ if (vap->iv_flags & IEEE80211_F_PRIVACY) {
if ((se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
fail |= MATCH_PRIVACY;
} else {
@@ -849,27 +903,27 @@ match_bss(struct ieee80211com *ic,
if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY)
fail |= MATCH_PRIVACY;
}
- rate = check_rate(ic, se);
+ rate = check_rate(vap, se);
if (rate & IEEE80211_RATE_BASIC)
fail |= MATCH_RATE;
if (ss->ss_nssid != 0 &&
!match_ssid(se->se_ssid, ss->ss_nssid, ss->ss_ssid))
fail |= MATCH_SSID;
- if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
- !IEEE80211_ADDR_EQ(ic->ic_des_bssid, se->se_bssid))
- fail |= MATCH_BSSID;
+ if ((vap->iv_flags & IEEE80211_F_DESBSSID) &&
+ !IEEE80211_ADDR_EQ(vap->iv_des_bssid, se->se_bssid))
+ fail |= MATCH_BSSID;
if (se0->se_fails >= STA_FAILS_MAX)
fail |= MATCH_FAILS;
- /* NB: entries may be present awaiting purge, skip */
if (se0->se_notseen >= STA_PURGE_SCANS)
fail |= MATCH_NOTSEEN;
if (se->se_rssi < STA_RSSI_MIN)
fail |= MATCH_RSSI;
#ifdef IEEE80211_DEBUG
- if (ieee80211_msg(ic, debug)) {
+ if (ieee80211_msg(vap, debug)) {
printf(" %c %s",
fail & MATCH_FAILS ? '=' :
fail & MATCH_NOTSEEN ? '^' :
+ fail & MATCH_CC ? '$' :
fail ? '-' : '+', ether_sprintf(se->se_macaddr));
printf(" %s%c", ether_sprintf(se->se_bssid),
fail & MATCH_BSSID ? '!' : ' ');
@@ -929,16 +983,17 @@ sta_dec_fails(struct sta_table *st)
}
static struct sta_entry *
-select_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic, int debug)
+select_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap, int debug)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *se, *selbs = NULL;
- IEEE80211_DPRINTF(ic, debug, " %s\n",
+ IEEE80211_DPRINTF(vap, debug, " %s\n",
"macaddr bssid chan rssi rate flag wep essid");
mtx_lock(&st->st_lock);
TAILQ_FOREACH(se, &st->st_entry, se_list) {
- if (match_bss(ic, ss, se, debug) == 0) {
+ if (match_bss(vap, ss, se, debug) == 0) {
+ ieee80211_ies_expand(&se->base.se_ies);
if (selbs == NULL)
selbs = se;
else if (sta_compare(se, selbs) > 0)
@@ -955,13 +1010,13 @@ select_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic, int debug)
* to use to start an ibss network.
*/
static int
-sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *selbs;
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("wrong mode %u", ic->ic_opmode));
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA,
+ ("wrong mode %u", vap->iv_opmode));
if (st->st_newscan) {
sta_update_notseen(st);
@@ -982,8 +1037,10 @@ sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
*/
/* NB: unlocked read should be ok */
if (TAILQ_FIRST(&st->st_entry) == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no scan candidate\n", __func__);
+ if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
+ return 0;
notfound:
/*
* If nothing suitable was found decrement
@@ -996,8 +1053,10 @@ notfound:
st->st_newscan = 1;
return 0; /* restart scan */
}
- selbs = select_bss(ss, ic, IEEE80211_MSG_SCAN);
- if (selbs == NULL || !ieee80211_sta_join(ic, &selbs->base))
+ selbs = select_bss(ss, vap, IEEE80211_MSG_SCAN);
+ if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
+ return (selbs != NULL);
+ if (selbs == NULL || !ieee80211_sta_join(vap, &selbs->base))
goto notfound;
return 1; /* terminate scan */
}
@@ -1024,12 +1083,14 @@ sta_lookup(struct sta_table *st, const uint8_t macaddr[IEEE80211_ADDR_LEN])
}
static void
-sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
- struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni = vap->iv_bss;
struct sta_table *st = ss->ss_priv;
+ enum ieee80211_phymode mode;
struct sta_entry *se, *selbs;
- uint8_t roamRate, curRate;
+ uint8_t roamRate, curRate, ucastRate;
int8_t roamRssi, curRssi;
se = sta_lookup(st, ni->ni_macaddr);
@@ -1038,27 +1099,21 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
return;
}
- /* XXX do we need 11g too? */
- if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) {
- roamRate = ic->ic_roam.rate11b;
- roamRssi = ic->ic_roam.rssi11b;
- } else if (IEEE80211_IS_CHAN_B(ic->ic_bsschan)) {
- roamRate = ic->ic_roam.rate11bOnly;
- roamRssi = ic->ic_roam.rssi11bOnly;
- } else {
- roamRate = ic->ic_roam.rate11a;
- roamRssi = ic->ic_roam.rssi11a;
- }
+ mode = ieee80211_chan2mode(ic->ic_bsschan);
+ roamRate = vap->iv_roamparms[mode].rate;
+ roamRssi = vap->iv_roamparms[mode].rssi;
+ ucastRate = vap->iv_txparms[mode].ucastrate;
/* NB: the most up to date rssi is in the node, not the scan cache */
curRssi = ic->ic_node_getrssi(ni);
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
- curRate = ni->ni_rates.rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ROAM,
+ if (ucastRate == IEEE80211_FIXED_RATE_NONE) {
+ curRate = ni->ni_txrate;
+ roamRate &= IEEE80211_RATE_VAL;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM,
"%s: currssi %d currate %u roamrssi %d roamrate %u\n",
__func__, curRssi, curRate, roamRssi, roamRate);
} else {
curRate = roamRate; /* NB: insure compare below fails */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ROAM,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM,
"%s: currssi %d roamrssi %d\n", __func__, curRssi, roamRssi);
}
/*
@@ -1066,7 +1121,7 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
* XXX deauth current ap
*/
if (curRate < roamRate || curRssi < roamRssi) {
- if (time_after(ticks, ic->ic_lastscan + ic->ic_scanvalid)) {
+ if (time_after(ticks, ic->ic_lastscan + vap->iv_scanvalid)) {
/*
* Scan cache contents are too old; force a scan now
* if possible so we have current state to make a
@@ -1076,19 +1131,19 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
* XXX force immediate switch on scan complete
*/
if (!IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
- time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle))
- ieee80211_bg_scan(ic);
+ time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle))
+ ieee80211_bg_scan(vap, 0);
return;
}
se->base.se_rssi = curRssi;
- selbs = select_bss(ss, ic, IEEE80211_MSG_ROAM);
+ selbs = select_bss(ss, vap, IEEE80211_MSG_ROAM);
if (selbs != NULL && selbs != se) {
- IEEE80211_DPRINTF(ic,
+ IEEE80211_DPRINTF(vap,
IEEE80211_MSG_ROAM | IEEE80211_MSG_DEBUG,
"%s: ROAM: curRate %u, roamRate %u, "
"curRssi %d, roamRssi %d\n", __func__,
curRate, roamRate, curRssi, roamRssi);
- ieee80211_sta_join(ic, &selbs->base);
+ ieee80211_sta_join(vap, &selbs->base);
}
}
}
@@ -1100,19 +1155,9 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
static void
sta_age(struct ieee80211_scan_state *ss)
{
- struct ieee80211com *ic = ss->ss_ic;
- struct sta_table *st = ss->ss_priv;
- struct sta_entry *se, *next;
+ struct ieee80211vap *vap = ss->ss_vap;
- mtx_lock(&st->st_lock);
- TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) {
- if (se->se_notseen > STA_PURGE_SCANS) {
- TAILQ_REMOVE(&st->st_entry, se, se_list);
- LIST_REMOVE(se, se_hash);
- FREE(se, M_80211_SCAN);
- }
- }
- mtx_unlock(&st->st_lock);
+ adhoc_age(ss);
/*
* If rate control is enabled check periodically to see if
* we should roam from our current connection to one that
@@ -1122,13 +1167,13 @@ sta_age(struct ieee80211_scan_state *ss)
* XXX repeater station
* XXX do when !bgscan?
*/
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("wrong mode %u", ic->ic_opmode));
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO &&
- (ic->ic_flags & IEEE80211_F_BGSCAN) &&
- ic->ic_state >= IEEE80211_S_RUN)
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA,
+ ("wrong mode %u", vap->iv_opmode));
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO &&
+ (vap->iv_ic->ic_flags & IEEE80211_F_BGSCAN) &&
+ vap->iv_state >= IEEE80211_S_RUN)
/* XXX vap is implicit */
- sta_roam_check(ss, ic);
+ sta_roam_check(ss, vap);
}
/*
@@ -1144,7 +1189,7 @@ sta_iterate(struct ieee80211_scan_state *ss,
u_int gen;
mtx_lock(&st->st_scanlock);
- gen = st->st_scangen++;
+ gen = st->st_scaniter++;
restart:
mtx_lock(&st->st_lock);
TAILQ_FOREACH(se, &st->st_entry, se_list) {
@@ -1173,7 +1218,7 @@ sta_assoc_fail(struct ieee80211_scan_state *ss,
if (se != NULL) {
se->se_fails++;
se->se_lastfail = ticks;
- IEEE80211_NOTE_MAC(ss->ss_ic, IEEE80211_MSG_SCAN,
+ IEEE80211_NOTE_MAC(ss->ss_vap, IEEE80211_MSG_SCAN,
macaddr, "%s: reason %u fails %u",
__func__, reason, se->se_fails);
}
@@ -1190,7 +1235,7 @@ sta_assoc_success(struct ieee80211_scan_state *ss,
if (se != NULL) {
#if 0
se->se_fails = 0;
- IEEE80211_NOTE_MAC(ss->ss_ic, IEEE80211_MSG_SCAN,
+ IEEE80211_NOTE_MAC(ss->ss_vap, IEEE80211_MSG_SCAN,
macaddr, "%s: fails %u",
__func__, se->se_fails);
#endif
@@ -1240,93 +1285,30 @@ static const struct scanlist adhocScanTable[] = {
* Start an adhoc-mode scan by populating the channel list.
*/
static int
-adhoc_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+adhoc_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
-#define N(a) (sizeof(a)/sizeof(a[0]))
struct sta_table *st = ss->ss_priv;
- const struct scanlist *scan;
- enum ieee80211_phymode mode;
- ss->ss_last = 0;
- /*
- * Use the table of ordered channels to construct the list
- * of channels for scanning. Any channels in the ordered
- * list not in the master list will be discarded.
- */
- for (scan = adhocScanTable; scan->list != NULL; scan++) {
- mode = scan->mode;
- if (ic->ic_des_mode != IEEE80211_MODE_AUTO) {
- /*
- * If a desired mode was specified, scan only
- * channels that satisfy that constraint.
- */
- if (ic->ic_des_mode != mode) {
- /*
- * The scan table marks 2.4Ghz channels as b
- * so if the desired mode is 11g, then use
- * the 11b channel list but upgrade the mode.
- */
- if (ic->ic_des_mode != IEEE80211_MODE_11G ||
- mode != IEEE80211_MODE_11B)
- continue;
- mode = IEEE80211_MODE_11G; /* upgrade */
- }
- } else {
- /*
- * This lets add_channels upgrade an 11b channel
- * to 11g if available.
- */
- if (mode == IEEE80211_MODE_11B)
- mode = IEEE80211_MODE_AUTO;
- }
-#ifdef IEEE80211_F_XR
- /* XR does not operate on turbo channels */
- if ((ic->ic_flags & IEEE80211_F_XR) &&
- (mode == IEEE80211_MODE_TURBO_A ||
- mode == IEEE80211_MODE_TURBO_G))
- continue;
-#endif
- /*
- * Add the list of the channels; any that are not
- * in the master channel list will be discarded.
- */
- add_channels(ic, ss, mode, scan->list, scan->count);
- }
-
- /*
- * Add the channels from the ic (from HAL) that are not present
- * in the staScanTable.
- */
- sweepchannels(ss, ic, adhocScanTable);
-
- ss->ss_next = 0;
- /* XXX tunables */
- ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */
- ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+ makescanlist(ss, vap, adhocScanTable);
-#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_scan(ic)) {
- if_printf(ic->ic_ifp, "scan set ");
- ieee80211_scan_dump_channels(ss);
- printf(" dwell min %ld max %ld\n",
- ss->ss_mindwell, ss->ss_maxdwell);
- }
-#endif /* IEEE80211_DEBUG */
+ if (ss->ss_mindwell == 0)
+ ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */
+ if (ss->ss_maxdwell == 0)
+ ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+ st->st_scangen++;
st->st_newscan = 1;
return 0;
-#undef N
}
/*
* Select a channel to start an adhoc network on.
* The channel list was populated with appropriate
* channels so select one that looks least occupied.
- * XXX need regulatory domain constraints
*/
static struct ieee80211_channel *
-adhoc_pick_channel(struct ieee80211_scan_state *ss)
+adhoc_pick_channel(struct ieee80211_scan_state *ss, int flags)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *se;
@@ -1339,7 +1321,14 @@ adhoc_pick_channel(struct ieee80211_scan_state *ss)
mtx_lock(&st->st_lock);
for (i = 0; i < ss->ss_last; i++) {
c = ss->ss_chans[i];
- if (!checktable(adhocScanTable, c))
+ /* never consider a channel with radar */
+ if (IEEE80211_IS_CHAN_RADAR(c))
+ continue;
+ /* skip channels disallowed by regulatory settings */
+ if (IEEE80211_IS_CHAN_NOADHOC(c))
+ continue;
+ /* check channel attributes for band compatibility */
+ if (flags != 0 && (c->ic_flags & flags) != flags)
continue;
maxrssi = 0;
TAILQ_FOREACH(se, &st->st_entry, se_list) {
@@ -1361,15 +1350,15 @@ adhoc_pick_channel(struct ieee80211_scan_state *ss)
* to use to start an ibss network.
*/
static int
-adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *selbs;
struct ieee80211_channel *chan;
- KASSERT(ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_AHDEMO,
- ("wrong opmode %u", ic->ic_opmode));
+ KASSERT(vap->iv_opmode == IEEE80211_M_IBSS ||
+ vap->iv_opmode == IEEE80211_M_AHDEMO,
+ ("wrong opmode %u", vap->iv_opmode));
if (st->st_newscan) {
sta_update_notseen(st);
@@ -1390,22 +1379,26 @@ adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
*/
/* NB: unlocked read should be ok */
if (TAILQ_FIRST(&st->st_entry) == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no scan candidate\n", __func__);
+ if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
+ return 0;
notfound:
- if (ic->ic_des_nssid) {
+ if (vap->iv_des_nssid) {
/*
* No existing adhoc network to join and we have
* an ssid; start one up. If no channel was
* specified, try to select a channel.
*/
- if (ic->ic_des_chan == IEEE80211_CHAN_ANYC)
- chan = ieee80211_ht_adjust_channel(ic,
- adhoc_pick_channel(ss), ic->ic_flags_ext);
- else
- chan = ic->ic_des_chan;
+ if (vap->iv_des_chan == IEEE80211_CHAN_ANYC ||
+ IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
+ chan = ieee80211_ht_adjust_channel(vap->iv_ic,
+ adhoc_pick_channel(ss, 0),
+ vap->iv_flags_ext);
+ } else
+ chan = vap->iv_des_chan;
if (chan != NULL) {
- ieee80211_create_ibss(ic, chan);
+ ieee80211_create_ibss(vap, chan);
return 1;
}
}
@@ -1420,8 +1413,10 @@ notfound:
st->st_newscan = 1;
return 0; /* restart scan */
}
- selbs = select_bss(ss, ic, IEEE80211_MSG_SCAN);
- if (selbs == NULL || !ieee80211_sta_join(ic, &selbs->base))
+ selbs = select_bss(ss, vap, IEEE80211_MSG_SCAN);
+ if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
+ return (selbs != NULL);
+ if (selbs == NULL || !ieee80211_sta_join(vap, &selbs->base))
goto notfound;
return 1; /* terminate scan */
}
@@ -1440,6 +1435,7 @@ adhoc_age(struct ieee80211_scan_state *ss)
if (se->se_notseen > STA_PURGE_SCANS) {
TAILQ_REMOVE(&st->st_entry, se, se_list);
LIST_REMOVE(se, se_hash);
+ ieee80211_ies_cleanup(&se->base.se_ies);
FREE(se, M_80211_SCAN);
}
}
@@ -1455,6 +1451,7 @@ static const struct ieee80211_scanner adhoc_default = {
.scan_cancel = sta_cancel,
.scan_end = adhoc_pick_bss,
.scan_flush = sta_flush,
+ .scan_pickchan = adhoc_pick_channel,
.scan_add = sta_add,
.scan_age = adhoc_age,
.scan_iterate = sta_iterate,
@@ -1462,39 +1459,161 @@ static const struct ieee80211_scanner adhoc_default = {
.scan_assoc_success = sta_assoc_success,
};
+static void
+ap_force_promisc(struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+
+ IEEE80211_LOCK(ic);
+ /* set interface into promiscuous mode */
+ ifp->if_flags |= IFF_PROMISC;
+ ic->ic_update_promisc(ifp);
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+ap_reset_promisc(struct ieee80211com *ic)
+{
+ IEEE80211_LOCK(ic);
+ ieee80211_syncifflag_locked(ic, IFF_PROMISC);
+ IEEE80211_UNLOCK(ic);
+}
+
+static int
+ap_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
+{
+ struct sta_table *st = ss->ss_priv;
+
+ makescanlist(ss, vap, staScanTable);
+
+ if (ss->ss_mindwell == 0)
+ ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */
+ if (ss->ss_maxdwell == 0)
+ ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+
+ st->st_scangen++;
+ st->st_newscan = 1;
+
+ ap_force_promisc(vap->iv_ic);
+ return 0;
+}
+
/*
- * Module glue.
+ * Cancel an ongoing scan.
*/
static int
-wlan_modevent(module_t mod, int type, void *unused)
+ap_cancel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
- switch (type) {
- case MOD_LOAD:
- ieee80211_scanner_register(IEEE80211_M_STA, &sta_default);
- ieee80211_scanner_register(IEEE80211_M_IBSS, &adhoc_default);
- ieee80211_scanner_register(IEEE80211_M_AHDEMO, &adhoc_default);
- return 0;
- case MOD_UNLOAD:
- case MOD_QUIESCE:
- if (nrefs) {
- printf("wlan_scan_sta: still in use (%u dynamic refs)\n",
- nrefs);
- return EBUSY;
+ ap_reset_promisc(vap->iv_ic);
+ return 0;
+}
+
+/*
+ * Pick a quiet channel to use for ap operation.
+ */
+static struct ieee80211_channel *
+ap_pick_channel(struct ieee80211_scan_state *ss, int flags)
+{
+ struct sta_table *st = ss->ss_priv;
+ struct ieee80211_channel *bestchan = NULL;
+ int i;
+
+ /* XXX select channel more intelligently, e.g. channel spread, power */
+ /* NB: use scan list order to preserve channel preference */
+ for (i = 0; i < ss->ss_last; i++) {
+ struct ieee80211_channel *chan = ss->ss_chans[i];
+ /*
+ * If the channel is unoccupied the max rssi
+ * should be zero; just take it. Otherwise
+ * track the channel with the lowest rssi and
+ * use that when all channels appear occupied.
+ */
+ if (IEEE80211_IS_CHAN_RADAR(chan))
+ continue;
+ if (IEEE80211_IS_CHAN_NOHOSTAP(chan))
+ continue;
+ /* check channel attributes for band compatibility */
+ if (flags != 0 && (chan->ic_flags & flags) != flags)
+ continue;
+ /* XXX channel have interference */
+ if (st->st_maxrssi[chan->ic_ieee] == 0) {
+ /* XXX use other considerations */
+ return chan;
}
- if (type == MOD_UNLOAD) {
- ieee80211_scanner_unregister_all(&sta_default);
- ieee80211_scanner_unregister_all(&adhoc_default);
+ if (bestchan == NULL ||
+ st->st_maxrssi[chan->ic_ieee] < st->st_maxrssi[bestchan->ic_ieee])
+ bestchan = chan;
+ }
+ return bestchan;
+}
+
+/*
+ * Pick a quiet channel to use for ap operation.
+ */
+static int
+ap_end(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_channel *bestchan;
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP,
+ ("wrong opmode %u", vap->iv_opmode));
+ bestchan = ap_pick_channel(ss, 0);
+ if (bestchan == NULL) {
+ /* no suitable channel, should not happen */
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: no suitable channel! (should not happen)\n", __func__);
+ /* XXX print something? */
+ return 0; /* restart scan */
+ }
+ /*
+ * If this is a dynamic turbo channel, start with the unboosted one.
+ */
+ if (IEEE80211_IS_CHAN_TURBO(bestchan)) {
+ bestchan = ieee80211_find_channel(ic, bestchan->ic_freq,
+ bestchan->ic_flags & ~IEEE80211_CHAN_TURBO);
+ if (bestchan == NULL) {
+ /* should never happen ?? */
+ return 0;
}
- return 0;
}
- return EINVAL;
+ ap_reset_promisc(ic);
+ if (ss->ss_flags & (IEEE80211_SCAN_NOPICK | IEEE80211_SCAN_NOJOIN)) {
+ /*
+ * Manual/background scan, don't select+join the
+ * bss, just return. The scanning framework will
+ * handle notification that this has completed.
+ */
+ ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
+ return 1;
+ }
+ ieee80211_create_ibss(vap,
+ ieee80211_ht_adjust_channel(ic, bestchan, vap->iv_flags_ext));
+ return 1;
}
-static moduledata_t wlan_mod = {
- "wlan_scan_sta",
- wlan_modevent,
- 0
+static const struct ieee80211_scanner ap_default = {
+ .scan_name = "default",
+ .scan_attach = sta_attach,
+ .scan_detach = sta_detach,
+ .scan_start = ap_start,
+ .scan_restart = sta_restart,
+ .scan_cancel = ap_cancel,
+ .scan_end = ap_end,
+ .scan_flush = sta_flush,
+ .scan_pickchan = ap_pick_channel,
+ .scan_add = sta_add,
+ .scan_age = adhoc_age,
+ .scan_iterate = sta_iterate,
+ .scan_assoc_success = sta_assoc_success,
+ .scan_assoc_fail = sta_assoc_fail,
};
-DECLARE_MODULE(wlan_scan_sta, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_scan_sta, 1);
-MODULE_DEPEND(wlan_scan_sta, wlan, 1, 1, 1);
+
+/*
+ * Module glue.
+ */
+IEEE80211_SCANNER_MODULE(sta, 1);
+IEEE80211_SCANNER_ALG(sta, IEEE80211_M_STA, sta_default);
+IEEE80211_SCANNER_ALG(ibss, IEEE80211_M_IBSS, adhoc_default);
+IEEE80211_SCANNER_ALG(ahdemo, IEEE80211_M_AHDEMO, adhoc_default);
+IEEE80211_SCANNER_ALG(ap, IEEE80211_M_HOSTAP, ap_default);
diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c
new file mode 100644
index 0000000..e3c57ad
--- /dev/null
+++ b/sys/net80211/ieee80211_sta.c
@@ -0,0 +1,1647 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 Station mode support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_sta.h>
+#include <net80211/ieee80211_input.h>
+
+#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2)
+
+static void sta_vattach(struct ieee80211vap *);
+static void sta_beacon_miss(struct ieee80211vap *);
+static int sta_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int sta_input(struct ieee80211_node *, struct mbuf *,
+ int rssi, int noise, uint32_t rstamp);
+static void sta_recv_mgmt(struct ieee80211_node *, struct mbuf *,
+ int subtype, int rssi, int noise, uint32_t rstamp);
+
+void
+ieee80211_sta_attach(struct ieee80211com *ic)
+{
+ ic->ic_vattach[IEEE80211_M_STA] = sta_vattach;
+}
+
+void
+ieee80211_sta_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+sta_vdetach(struct ieee80211vap *vap)
+{
+}
+
+static void
+sta_vattach(struct ieee80211vap *vap)
+{
+ vap->iv_newstate = sta_newstate;
+ vap->iv_input = sta_input;
+ vap->iv_recv_mgmt = sta_recv_mgmt;
+ vap->iv_opdetach = sta_vdetach;
+ vap->iv_bmiss = sta_beacon_miss;
+}
+
+/*
+ * Handle a beacon miss event. The common code filters out
+ * spurious events that can happen when scanning and/or before
+ * reaching RUN state.
+ */
+static void
+sta_beacon_miss(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning"));
+ KASSERT(vap->iv_state == IEEE80211_S_RUN,
+ ("wrong state %d", vap->iv_state));
+
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "beacon miss, mode %u state %s\n",
+ vap->iv_opmode, ieee80211_state_name[vap->iv_state]);
+
+ if (++vap->iv_bmiss_count < vap->iv_bmiss_max) {
+ /*
+ * Send a directed probe req before falling back to a
+ * scan; if we receive a response ic_bmiss_count will
+ * be reset. Some cards mistakenly report beacon miss
+ * so this avoids the expensive scan if the ap is
+ * still there.
+ */
+ ieee80211_send_probereq(vap->iv_bss, vap->iv_myaddr,
+ vap->iv_bss->ni_bssid, vap->iv_bss->ni_bssid,
+ vap->iv_bss->ni_essid, vap->iv_bss->ni_esslen);
+ return;
+ }
+ vap->iv_bmiss_count = 0;
+ vap->iv_stats.is_beacon_miss++;
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
+ /*
+ * If we receive a beacon miss interrupt when using
+ * dynamic turbo, attempt to switch modes before
+ * reassociating.
+ */
+ if (IEEE80211_ATH_CAP(vap, vap->iv_bss, IEEE80211_NODE_TURBOP))
+ ieee80211_dturbo_switch(vap,
+ ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO);
+ /*
+ * Try to reassociate before scanning for a new ap.
+ */
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1);
+ } else {
+ /*
+ * Somebody else is controlling state changes (e.g.
+ * a user-mode app) don't do anything that would
+ * confuse them; just drop into scan mode so they'll
+ * notified of the state change and given control.
+ */
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ }
+}
+
+/*
+ * Handle deauth with reason. We retry only for
+ * the cases where we might succeed. Otherwise
+ * we downgrade the ap and scan.
+ */
+static void
+sta_authretry(struct ieee80211vap *vap, struct ieee80211_node *ni, int reason)
+{
+ switch (reason) {
+ case IEEE80211_STATUS_SUCCESS: /* NB: MLME assoc */
+ case IEEE80211_STATUS_TIMEOUT:
+ case IEEE80211_REASON_ASSOC_EXPIRE:
+ case IEEE80211_REASON_NOT_AUTHED:
+ case IEEE80211_REASON_NOT_ASSOCED:
+ case IEEE80211_REASON_ASSOC_LEAVE:
+ case IEEE80211_REASON_ASSOC_NOT_AUTHED:
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, 1);
+ break;
+ default:
+ ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, reason);
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
+ ieee80211_check_scan_current(vap);
+ break;
+ }
+}
+
+/*
+ * IEEE80211_M_STA vap state machine handler.
+ * This routine handles the main states in the 802.11 protocol.
+ */
+static int
+sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
+ enum ieee80211_state ostate;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate], arg);
+ vap->iv_state = nstate; /* state transition */
+ callout_stop(&vap->iv_mgtsend); /* XXX callout_drain */
+ if (ostate != IEEE80211_S_SCAN)
+ ieee80211_cancel_scan(vap); /* background scan */
+ ni = vap->iv_bss; /* NB: no reference held */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
+ callout_stop(&vap->iv_swbmiss);
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ switch (ostate) {
+ case IEEE80211_S_SLEEP:
+ /* XXX wakeup */
+ case IEEE80211_S_RUN:
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_ASSOC_LEAVE);
+ ieee80211_sta_leave(ni);
+ break;
+ case IEEE80211_S_ASSOC:
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_LEAVE);
+ break;
+ case IEEE80211_S_SCAN:
+ ieee80211_cancel_scan(vap);
+ break;
+ default:
+ goto invalid;
+ }
+ if (ostate != IEEE80211_S_INIT) {
+ /* NB: optimize INIT -> INIT case */
+ ieee80211_reset_bss(vap);
+ }
+ if (vap->iv_auth->ia_detach != NULL)
+ vap->iv_auth->ia_detach(vap);
+ break;
+ case IEEE80211_S_SCAN:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ /*
+ * Initiate a scan. We can come here as a result
+ * of an IEEE80211_IOC_SCAN_REQ too in which case
+ * the vap will be marked with IEEE80211_FEXT_SCANREQ
+ * and the scan request parameters will be present
+ * in iv_scanreq. Otherwise we do the default.
+ */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
+ ieee80211_check_scan(vap,
+ vap->iv_scanreq_flags,
+ vap->iv_scanreq_duration,
+ vap->iv_scanreq_mindwell,
+ vap->iv_scanreq_maxdwell,
+ vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
+ } else
+ ieee80211_check_scan_current(vap);
+ break;
+ case IEEE80211_S_SCAN:
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+ /*
+ * These can happen either because of a timeout
+ * on an assoc/auth response or because of a
+ * change in state that requires a reset. For
+ * the former we're called with a non-zero arg
+ * that is the cause for the failure; pass this
+ * to the scan code so it can update state.
+ * Otherwise trigger a new scan unless we're in
+ * manual roaming mode in which case an application
+ * must issue an explicit scan request.
+ */
+ if (arg != 0)
+ ieee80211_scan_assoc_fail(vap,
+ vap->iv_bss->ni_macaddr, arg);
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
+ ieee80211_check_scan_current(vap);
+ break;
+ case IEEE80211_S_RUN: /* beacon miss */
+ /*
+ * Beacon miss. Notify user space and if not
+ * under control of a user application (roaming
+ * manual) kick off a scan to re-connect.
+ */
+ ieee80211_sta_leave(ni);
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
+ ieee80211_check_scan_current(vap);
+ break;
+ default:
+ goto invalid;
+ }
+ break;
+ case IEEE80211_S_AUTH:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 1);
+ break;
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+ switch (arg & 0xff) {
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ /* ??? */
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2);
+ break;
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ sta_authretry(vap, ni, arg>>8);
+ break;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ switch (arg & 0xff) {
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2);
+ vap->iv_state = ostate; /* stay RUN */
+ break;
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ ieee80211_sta_leave(ni);
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
+ /* try to reauth */
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 1);
+ }
+ break;
+ }
+ break;
+ default:
+ goto invalid;
+ }
+ break;
+ case IEEE80211_S_ASSOC:
+ switch (ostate) {
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
+ break;
+ case IEEE80211_S_SLEEP: /* cannot happen */
+ case IEEE80211_S_RUN:
+ ieee80211_sta_leave(ni);
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
+ IEEE80211_SEND_MGMT(ni, arg ?
+ IEEE80211_FC0_SUBTYPE_REASSOC_REQ :
+ IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
+ }
+ break;
+ default:
+ goto invalid;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ if (vap->iv_flags & IEEE80211_F_WPA) {
+ /* XXX validate prerequisites */
+ }
+ switch (ostate) {
+ case IEEE80211_S_RUN:
+ break;
+ case IEEE80211_S_AUTH: /* when join is done in fw */
+ case IEEE80211_S_ASSOC:
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_debug(vap)) {
+ ieee80211_note(vap, "%s with %s ssid ",
+ (vap->iv_opmode == IEEE80211_M_STA ?
+ "associated" : "synchronized"),
+ ether_sprintf(ni->ni_bssid));
+ ieee80211_print_essid(vap->iv_bss->ni_essid,
+ ni->ni_esslen);
+ /* XXX MCS/HT */
+ printf(" channel %d start %uMb\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ IEEE80211_RATE2MBS(ni->ni_txrate));
+ }
+#endif
+ ieee80211_scan_assoc_success(vap, ni->ni_macaddr);
+ ieee80211_notify_node_join(ni,
+ arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
+ break;
+ case IEEE80211_S_SLEEP:
+ ieee80211_sta_pwrsave(vap, 0);
+ break;
+ default:
+ goto invalid;
+ }
+ ieee80211_sync_curchan(ic);
+ if (ostate != IEEE80211_S_RUN &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)) {
+ /*
+ * Start s/w beacon miss timer for devices w/o
+ * hardware support. We fudge a bit here since
+ * we're doing this in software.
+ */
+ vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS(
+ 2 * vap->iv_bmissthreshold * ni->ni_intval);
+ vap->iv_swbmiss_count = 0;
+ callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period,
+ ieee80211_swbmiss, vap);
+ }
+ /*
+ * When 802.1x is not in use mark the port authorized
+ * at this point so traffic can flow.
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+ ieee80211_node_authorize(ni);
+ break;
+ case IEEE80211_S_SLEEP:
+ ieee80211_sta_pwrsave(vap, 0);
+ break;
+ default:
+ invalid:
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
+ "%s: invalid state transition %s -> %s\n", __func__,
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Return non-zero if the frame is an echo of a multicast
+ * frame sent by ourself. The dir is known to be DSTODS.
+ */
+static __inline int
+isdstods_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
+{
+#define QWH4(wh) ((const struct ieee80211_qosframe_addr4 *)wh)
+#define WH4(wh) ((const struct ieee80211_frame_addr4 *)wh)
+ const uint8_t *sa;
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode"));
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr3))
+ return 0;
+ sa = IEEE80211_QOS_HAS_SEQ(wh) ? QWH4(wh)->i_addr4 : WH4(wh)->i_addr4;
+ return IEEE80211_ADDR_EQ(sa, vap->iv_myaddr);
+#undef WH4
+#undef QWH4
+}
+
+/*
+ * Return non-zero if the frame is an echo of a multicast
+ * frame sent by ourself. The dir is known to be FROMDS.
+ */
+static __inline int
+isfromds_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
+{
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode"));
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
+ return 0;
+ return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr);
+}
+
+/*
+ * Decide if a received management frame should be
+ * printed when debugging is enabled. This filters some
+ * of the less interesting frames that come frequently
+ * (e.g. beacons).
+ */
+static __inline int
+doprint(struct ieee80211vap *vap, int subtype)
+{
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Process a received frame. The node associated with the sender
+ * should be supplied. If nothing was found in the node table then
+ * the caller is assumed to supply a reference to iv_bss instead.
+ * The RSSI and a timestamp are also supplied. The RSSI data is used
+ * during AP scanning to select a AP to associate with; it can have
+ * any units so long as values have consistent units and higher values
+ * mean ``better signal''. The receive timestamp is currently not used
+ * by the 802.11 layer.
+ */
+static int
+sta_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp)
+{
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define HAS_SEQ(type) ((type & 0x4) == 0)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
+ struct ether_header *eh;
+ int hdrspace, need_tap;
+ uint8_t dir, type, subtype, qos;
+ uint8_t *bssid;
+ uint16_t rxseq;
+
+ if (m->m_flags & M_AMPDU) {
+ /*
+ * Fastpath for A-MPDU reorder q resubmission. Frames
+ * w/ M_AMPDU marked have already passed through here
+ * but were received out of order and been held on the
+ * reorder queue. When resubmitted they are marked
+ * with the M_AMPDU flag and we can bypass most of the
+ * normal processing.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ type = IEEE80211_FC0_TYPE_DATA;
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ subtype = IEEE80211_FC0_SUBTYPE_QOS;
+ hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
+ goto resubmit_ampdu;
+ }
+
+ KASSERT(ni != NULL, ("null node"));
+ ni->ni_inact = ni->ni_inact_reload;
+
+ need_tap = 1; /* mbuf need to be tapped. */
+ type = -1; /* undefined */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "too short (1): len %u", m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ /*
+ * Bit of a cheat here, we use a pointer for a 3-address
+ * frame format but don't reference fields past outside
+ * ieee80211_frame_min w/o first validating the data is
+ * present.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
+ vap->iv_stats.is_rx_badversion++;
+ goto err;
+ }
+
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ bssid = wh->i_addr2;
+ if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
+ /* not interested in */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, NULL, "%s", "not to bss");
+ vap->iv_stats.is_rx_wrongbss++;
+ goto out;
+ }
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ if (HAS_SEQ(type)) {
+ uint8_t tid = ieee80211_gettid(wh);
+ if (IEEE80211_QOS_HAS_SEQ(wh) &&
+ TID_TO_WME_AC(tid) >= WME_AC_VI)
+ ic->ic_wme.wme_hipri_traffic++;
+ rxseq = le16toh(*(uint16_t *)wh->i_seq);
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
+ (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
+ /* duplicate, discard */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, "duplicate",
+ "seqno <%u,%u> fragno <%u,%u> tid %u",
+ rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[tid] >>
+ IEEE80211_SEQ_SEQ_SHIFT,
+ rxseq & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxseqs[tid] &
+ IEEE80211_SEQ_FRAG_MASK,
+ tid);
+ vap->iv_stats.is_rx_dup++;
+ IEEE80211_NODE_STAT(ni, rx_dup);
+ goto out;
+ }
+ ni->ni_rxseqs[tid] = rxseq;
+ }
+ }
+
+ switch (type) {
+ case IEEE80211_FC0_TYPE_DATA:
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ if (m->m_len < hdrspace &&
+ (m = m_pullup(m, hdrspace)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "data too short: expecting %u", hdrspace);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out; /* XXX */
+ }
+ /*
+ * Handle A-MPDU re-ordering. The station must be
+ * associated and negotiated HT. The frame must be
+ * a QoS frame (not QoS null data) and not previously
+ * processed for A-MPDU re-ordering. If the frame is
+ * to be processed directly then ieee80211_ampdu_reorder
+ * will return 0; otherwise it has consumed the mbuf
+ * and we should do nothing more with it.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+ (dir == IEEE80211_FC1_DIR_FROMDS ||
+ dir == IEEE80211_FC1_DIR_DSTODS) &&
+ ieee80211_ampdu_reorder(ni, m) != 0) {
+ m = NULL;
+ goto out;
+ }
+ resubmit_ampdu:
+ if (dir == IEEE80211_FC1_DIR_FROMDS) {
+ if ((ifp->if_flags & IFF_SIMPLEX) &&
+ isfromds_mcastecho(vap, wh)) {
+ /*
+ * In IEEE802.11 network, multicast
+ * packets sent from "me" are broadcast
+ * from the AP; silently discard for
+ * SIMPLEX interface.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "%s", "multicast echo");
+ vap->iv_stats.is_rx_mcastecho++;
+ goto out;
+ }
+ if ((vap->iv_flags & IEEE80211_F_DWDS) &&
+ IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ /*
+ * DWDS sta's must drop 3-address mcast frames
+ * as they will be sent separately as a 4-addr
+ * frame. Accepting the 3-addr frame will
+ * confuse the bridge into thinking the sending
+ * sta is located at the end of WDS link.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
+ "3-address data", "%s", "DWDS enabled");
+ vap->iv_stats.is_rx_mcastecho++;
+ goto out;
+ }
+ } else if (dir == IEEE80211_FC1_DIR_DSTODS) {
+ if ((vap->iv_flags & IEEE80211_F_DWDS) == 0) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_INPUT, wh, "4-address data",
+ "%s", "DWDS not enabled");
+ vap->iv_stats.is_rx_wrongdir++;
+ goto out;
+ }
+ if ((ifp->if_flags & IFF_SIMPLEX) &&
+ isdstods_mcastecho(vap, wh)) {
+ /*
+ * In IEEE802.11 network, multicast
+ * packets sent from "me" are broadcast
+ * from the AP; silently discard for
+ * SIMPLEX interface.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
+ "4-address data", "%s", "multicast echo");
+ vap->iv_stats.is_rx_mcastecho++;
+ goto out;
+ }
+ } else {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
+ "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto out;
+ }
+
+ /*
+ * Handle privacy requirements. Note that we
+ * must not be preempted from here until after
+ * we (potentially) call ieee80211_crypto_demic;
+ * otherwise we may violate assumptions in the
+ * crypto cipher modules used to do delayed update
+ * of replay sequence numbers.
+ */
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "WEP", "%s", "PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ IEEE80211_NODE_STAT(ni, rx_noprivacy);
+ goto out;
+ }
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ IEEE80211_NODE_STAT(ni, rx_wepfail);
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ } else {
+ /* XXX M_WEP and IEEE80211_F_PRIVACY */
+ key = NULL;
+ }
+
+ /*
+ * Save QoS bits for use below--before we strip the header.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+ qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+ ((struct ieee80211_qosframe *)wh)->i_qos[0];
+ } else
+ qos = 0;
+
+ /*
+ * Next up, any fragmentation.
+ */
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ m = ieee80211_defrag(ni, m, hdrspace);
+ if (m == NULL) {
+ /* Fragment dropped or frame not complete yet */
+ goto out;
+ }
+ }
+ wh = NULL; /* no longer valid, catch any uses */
+
+ /*
+ * Next strip any MSDU crypto bits.
+ */
+ if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "demic error");
+ vap->iv_stats.is_rx_demicfail++;
+ IEEE80211_NODE_STAT(ni, rx_demicfail);
+ goto out;
+ }
+
+ /* copy to listener after decrypt */
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ need_tap = 0;
+
+ /*
+ * Finally, strip the 802.11 header.
+ */
+ m = ieee80211_decap(vap, m, hdrspace);
+ if (m == NULL) {
+ /* XXX mask bit to check for both */
+ /* don't count Null data frames as errors */
+ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
+ subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
+ goto out;
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "decap error");
+ vap->iv_stats.is_rx_decap++;
+ IEEE80211_NODE_STAT(ni, rx_decap);
+ goto err;
+ }
+ eh = mtod(m, struct ether_header *);
+ if (!ieee80211_node_is_authorized(ni)) {
+ /*
+ * Deny any non-PAE frames received prior to
+ * authorization. For open/shared-key
+ * authentication the port is mark authorized
+ * after authentication completes. For 802.1x
+ * the port is not marked authorized by the
+ * authenticator until the handshake has completed.
+ */
+ if (eh->ether_type != htons(ETHERTYPE_PAE)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ eh->ether_shost, "data",
+ "unauthorized port: ether type 0x%x len %u",
+ eh->ether_type, m->m_pkthdr.len);
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(ni, rx_unauth);
+ goto err;
+ }
+ } else {
+ /*
+ * When denying unencrypted frames, discard
+ * any non-PAE frames received without encryption.
+ */
+ if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
+ (key == NULL && (m->m_flags & M_WEP) == 0) &&
+ eh->ether_type != htons(ETHERTYPE_PAE)) {
+ /*
+ * Drop unencrypted frames.
+ */
+ vap->iv_stats.is_rx_unencrypted++;
+ IEEE80211_NODE_STAT(ni, rx_unencrypted);
+ goto out;
+ }
+ }
+ /* XXX require HT? */
+ if (qos & IEEE80211_QOS_AMSDU) {
+ m = ieee80211_decap_amsdu(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
+#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
+ m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
+ struct llc *llc;
+
+ /*
+ * Check for fast-frame tunnel encapsulation.
+ */
+ if (m->m_len < FF_LLC_SIZE &&
+ (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame",
+ "%s", "m_pullup(llc) failed");
+ vap->iv_stats.is_rx_tooshort++;
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ llc = (struct llc *)(mtod(m, uint8_t *) +
+ sizeof(struct ether_header));
+ if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
+ m_adj(m, FF_LLC_SIZE);
+ m = ieee80211_decap_fastframe(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ }
+#undef FF_LLC_SIZE
+ ieee80211_deliver_data(vap, ni, m);
+ return IEEE80211_FC0_TYPE_DATA;
+
+ case IEEE80211_FC0_TYPE_MGT:
+ vap->iv_stats.is_rx_mgmt++;
+ IEEE80211_NODE_STAT(ni, rx_mgmt);
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto err;
+ }
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "mgt", "too short: len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+#ifdef IEEE80211_DEBUG
+ if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
+ ieee80211_msg_dumppkts(vap)) {
+ if_printf(ifp, "received %s from %s rssi %d\n",
+ ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(wh->i_addr2), rssi);
+ }
+#endif
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
+ /*
+ * Only shared key auth frames with a challenge
+ * should be encrypted, discard all others.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ "%s", "WEP set but not permitted");
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
+ goto out;
+ }
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "mgt", "%s", "WEP set but PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ goto out;
+ }
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ }
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
+ m_freem(m);
+ return IEEE80211_FC0_TYPE_MGT;
+
+ case IEEE80211_FC0_TYPE_CTL:
+ vap->iv_stats.is_rx_ctl++;
+ IEEE80211_NODE_STAT(ni, rx_ctrl);
+ goto out;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, NULL, "bad frame type 0x%x", type);
+ /* should not come here */
+ break;
+ }
+err:
+ ifp->if_ierrors++;
+out:
+ if (m != NULL) {
+ if (bpf_peers_present(vap->iv_rawbpf) && need_tap)
+ bpf_mtap(vap->iv_rawbpf, m);
+ m_freem(m);
+ }
+ return type;
+#undef SEQ_LEQ
+}
+
+static void
+sta_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh,
+ int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "open auth",
+ "bad sta auth mode %u", ni->ni_authmode);
+ vap->iv_stats.is_rx_bad_auth++; /* XXX */
+ return;
+ }
+ if (vap->iv_state != IEEE80211_S_AUTH ||
+ seq != IEEE80211_AUTH_OPEN_RESPONSE) {
+ vap->iv_stats.is_rx_bad_auth++;
+ return;
+ }
+ if (status != 0) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+ ni, "open auth failed (reason %d)", status);
+ vap->iv_stats.is_rx_auth_fail++;
+ vap->iv_stats.is_rx_authfail_code = status;
+ ieee80211_new_state(vap, IEEE80211_S_SCAN,
+ IEEE80211_SCAN_FAIL_STATUS);
+ } else
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
+}
+
+static void
+sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
+ uint8_t *frm, uint8_t *efrm, int rssi, int noise, uint32_t rstamp,
+ uint16_t seq, uint16_t status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ uint8_t *challenge;
+ int estatus;
+
+ /*
+ * NB: this can happen as we allow pre-shared key
+ * authentication to be enabled w/o wep being turned
+ * on so that configuration of these can be done
+ * in any order. It may be better to enforce the
+ * ordering in which case this check would just be
+ * for sanity/consistency.
+ */
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "%s", " PRIVACY is disabled");
+ estatus = IEEE80211_STATUS_ALG;
+ goto bad;
+ }
+ /*
+ * Pre-shared key authentication is evil; accept
+ * it only if explicitly configured (it is supported
+ * mainly for compatibility with clients like OS X).
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
+ ni->ni_authmode != IEEE80211_AUTH_SHARED) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "bad sta auth mode %u", ni->ni_authmode);
+ vap->iv_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */
+ estatus = IEEE80211_STATUS_ALG;
+ goto bad;
+ }
+
+ challenge = NULL;
+ if (frm + 1 < efrm) {
+ if ((frm[1] + 2) > (efrm - frm)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "ie %d/%d too long",
+ frm[0], (frm[1] + 2) - (efrm - frm));
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ if (*frm == IEEE80211_ELEMID_CHALLENGE)
+ challenge = frm;
+ frm += frm[1] + 2;
+ }
+ switch (seq) {
+ case IEEE80211_AUTH_SHARED_CHALLENGE:
+ case IEEE80211_AUTH_SHARED_RESPONSE:
+ if (challenge == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "%s", "no challenge");
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "bad challenge len %d", challenge[1]);
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ default:
+ break;
+ }
+ if (vap->iv_state != IEEE80211_S_AUTH)
+ return;
+ switch (seq) {
+ case IEEE80211_AUTH_SHARED_PASS:
+ if (ni->ni_challenge != NULL) {
+ FREE(ni->ni_challenge, M_80211_NODE);
+ ni->ni_challenge = NULL;
+ }
+ if (status != 0) {
+ IEEE80211_NOTE_FRAME(vap,
+ IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, wh,
+ "shared key auth failed (reason %d)", status);
+ vap->iv_stats.is_rx_auth_fail++;
+ vap->iv_stats.is_rx_authfail_code = status;
+ return;
+ }
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
+ break;
+ case IEEE80211_AUTH_SHARED_CHALLENGE:
+ if (!ieee80211_alloc_challenge(ni))
+ return;
+ /* XXX could optimize by passing recvd challenge */
+ memcpy(ni->ni_challenge, &challenge[2], challenge[1]);
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+ break;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_AUTH,
+ wh, "shared key auth", "bad seq %d", seq);
+ vap->iv_stats.is_rx_bad_auth++;
+ return;
+ }
+ return;
+bad:
+ /*
+ * Kick the state machine. This short-circuits
+ * using the mgt frame timeout to trigger the
+ * state transition.
+ */
+ if (vap->iv_state == IEEE80211_S_AUTH)
+ ieee80211_new_state(vap, IEEE80211_S_SCAN,
+ IEEE80211_SCAN_FAIL_STATUS);
+}
+
+static int
+ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm,
+ const struct ieee80211_frame *wh)
+{
+#define MS(_v, _f) (((_v) & _f) >> _f##_S)
+ struct ieee80211_wme_state *wme = &vap->iv_ic->ic_wme;
+ u_int len = frm[1], qosinfo;
+ int i;
+
+ if (len < sizeof(struct ieee80211_wme_param)-2) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME,
+ wh, "WME", "too short, len %u", len);
+ return -1;
+ }
+ qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)];
+ qosinfo &= WME_QOSINFO_COUNT;
+ /* XXX do proper check for wraparound */
+ if (qosinfo == wme->wme_wmeChanParams.cap_info)
+ return 0;
+ frm += __offsetof(struct ieee80211_wme_param, params_acParams);
+ for (i = 0; i < WME_NUM_AC; i++) {
+ struct wmeParams *wmep =
+ &wme->wme_wmeChanParams.cap_wmeParams[i];
+ /* NB: ACI not used */
+ wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM);
+ wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN);
+ wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN);
+ wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX);
+ wmep->wmep_txopLimit = LE_READ_2(frm+2);
+ frm += 4;
+ }
+ wme->wme_wmeChanParams.cap_info = qosinfo;
+ return 1;
+#undef MS
+}
+
+static int
+ieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm,
+ const struct ieee80211_frame *wh)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ const struct ieee80211_ath_ie *ath;
+ u_int len = frm[1];
+ int capschanged;
+ uint16_t defkeyix;
+
+ if (len < sizeof(struct ieee80211_ath_ie)-2) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_SUPERG,
+ wh, "Atheros", "too short, len %u", len);
+ return -1;
+ }
+ ath = (const struct ieee80211_ath_ie *)frm;
+ capschanged = (ni->ni_ath_flags != ath->ath_capability);
+ defkeyix = LE_READ_2(ath->ath_defkeyix);
+ if (capschanged || defkeyix != ni->ni_ath_defkeyix) {
+ ni->ni_ath_flags = ath->ath_capability;
+ ni->ni_ath_defkeyix = defkeyix;
+ IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni,
+ "ath ie change: new caps 0x%x defkeyix 0x%x",
+ ni->ni_ath_flags, ni->ni_ath_defkeyix);
+ }
+ if (IEEE80211_ATH_CAP(vap, ni, ATHEROS_CAP_TURBO_PRIME)) {
+ uint16_t curflags, newflags;
+
+ /*
+ * Check for turbo mode switch. Calculate flags
+ * for the new mode and effect the switch.
+ */
+ newflags = curflags = vap->iv_ic->ic_bsschan->ic_flags;
+ /* NB: BOOST is not in ic_flags, so get it from the ie */
+ if (ath->ath_capability & ATHEROS_CAP_BOOST)
+ newflags |= IEEE80211_CHAN_TURBO;
+ else
+ newflags &= ~IEEE80211_CHAN_TURBO;
+ if (newflags != curflags)
+ ieee80211_dturbo_switch(vap, newflags);
+ }
+ return capschanged;
+}
+
+/*
+ * Return non-zero if a background scan may be continued:
+ * o bg scan is active
+ * o no channel switch is pending
+ * o there has not been any traffic recently
+ *
+ * Note we do not check if there is an administrative enable;
+ * this is only done to start the scan. We assume that any
+ * change in state will be accompanied by a request to cancel
+ * active scans which will otherwise cause this test to fail.
+ */
+static __inline int
+contbgscan(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) &&
+ (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 &&
+ vap->iv_state == IEEE80211_S_RUN && /* XXX? */
+ time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle));
+}
+
+/*
+ * Return non-zero if a backgrond scan may be started:
+ * o bg scanning is administratively enabled
+ * o no channel switch is pending
+ * o we are not boosted on a dynamic turbo channel
+ * o there has not been a scan recently
+ * o there has not been any traffic recently
+ */
+static __inline int
+startbgscan(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ return ((vap->iv_flags & IEEE80211_F_BGSCAN) &&
+ (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 &&
+ !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
+ time_after(ticks, ic->ic_lastscan + vap->iv_bgscanintvl) &&
+ time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle));
+}
+
+static void
+sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
+ int subtype, int rssi, int noise, uint32_t rstamp)
+{
+#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+#define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame *wh;
+ uint8_t *frm, *efrm;
+ uint8_t *rates, *xrates, *wme, *htcap, *htinfo;
+ uint8_t rate;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (uint8_t *)&wh[1];
+ efrm = mtod(m0, uint8_t *) + m0->m_len;
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_BEACON: {
+ struct ieee80211_scanparams scan;
+ /*
+ * We process beacon/probe response frames:
+ * o when scanning, or
+ * o station mode when associated (to collect state
+ * updates such as 802.11g slot time), or
+ * Frames otherwise received are discarded.
+ */
+ if (!((ic->ic_flags & IEEE80211_F_SCAN) || ni->ni_associd)) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ /* XXX probe response in sta mode when !scanning? */
+ if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
+ return;
+ /*
+ * Count frame now that we know it's to be processed.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+ vap->iv_stats.is_rx_beacon++; /* XXX remove */
+ IEEE80211_NODE_STAT(ni, rx_beacons);
+ } else
+ IEEE80211_NODE_STAT(ni, rx_proberesp);
+ /*
+ * When operating in station mode, check for state updates.
+ * Be careful to ignore beacons received while doing a
+ * background scan. We consider only 11g/WMM stuff right now.
+ */
+ if (ni->ni_associd != 0 &&
+ ((ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
+ IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) {
+ /* record tsf of last beacon */
+ memcpy(ni->ni_tstamp.data, scan.tstamp,
+ sizeof(ni->ni_tstamp));
+ /* count beacon frame for s/w bmiss handling */
+ vap->iv_swbmiss_count++;
+ vap->iv_bmiss_count = 0;
+ if (ni->ni_erp != scan.erp) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
+ wh->i_addr2,
+ "erp change: was 0x%x, now 0x%x",
+ ni->ni_erp, scan.erp);
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
+ (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
+ ic->ic_flags |= IEEE80211_F_USEPROT;
+ else
+ ic->ic_flags &= ~IEEE80211_F_USEPROT;
+ ni->ni_erp = scan.erp;
+ /* XXX statistic */
+ /* XXX driver notification */
+ }
+ if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
+ wh->i_addr2,
+ "capabilities change: was 0x%x, now 0x%x",
+ ni->ni_capinfo, scan.capinfo);
+ /*
+ * NB: we assume short preamble doesn't
+ * change dynamically
+ */
+ ieee80211_set_shortslottime(ic,
+ IEEE80211_IS_CHAN_A(ic->ic_bsschan) ||
+ (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+ ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME)
+ | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME);
+ /* XXX statistic */
+ }
+ if (scan.wme != NULL &&
+ (ni->ni_flags & IEEE80211_NODE_QOS) &&
+ ieee80211_parse_wmeparams(vap, scan.wme, wh) > 0)
+ ieee80211_wme_updateparams(vap);
+ if (scan.ath != NULL)
+ ieee80211_parse_athparams(ni, scan.ath, wh);
+ if (scan.htcap != NULL && scan.htinfo != NULL) {
+ ieee80211_parse_htcap(ni, scan.htcap);
+ ieee80211_parse_htinfo(ni, scan.htinfo);
+ /* XXX state changes? */
+ }
+ if (scan.tim != NULL) {
+ struct ieee80211_tim_ie *tim =
+ (struct ieee80211_tim_ie *) scan.tim;
+#if 0
+ int aid = IEEE80211_AID(ni->ni_associd);
+ int ix = aid / NBBY;
+ int min = tim->tim_bitctl &~ 1;
+ int max = tim->tim_len + min - 4;
+ if ((tim->tim_bitctl&1) ||
+ (min <= ix && ix <= max &&
+ isset(tim->tim_bitmap - min, aid))) {
+ /*
+ * XXX Do not let bg scan kick off
+ * we are expecting data.
+ */
+ ic->ic_lastdata = ticks;
+ ieee80211_sta_pwrsave(vap, 0);
+ }
+#endif
+ ni->ni_dtim_count = tim->tim_count;
+ ni->ni_dtim_period = tim->tim_period;
+ }
+ /*
+ * If scanning, pass the info to the scan module.
+ * Otherwise, check if it's the right time to do
+ * a background scan. Background scanning must
+ * be enabled and we must not be operating in the
+ * turbo phase of dynamic turbo mode. Then,
+ * it's been a while since the last background
+ * scan and if no data frames have come through
+ * recently, kick off a scan. Note that this
+ * is the mechanism by which a background scan
+ * is started _and_ continued each time we
+ * return on-channel to receive a beacon from
+ * our ap.
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ ieee80211_add_scan(vap, &scan, wh,
+ subtype, rssi, noise, rstamp);
+ } else if (contbgscan(vap)) {
+ ieee80211_bg_scan(vap, 0);
+ } else if (startbgscan(vap)) {
+ vap->iv_stats.is_scan_bg++;
+#if 0
+ /* wakeup if we are sleeing */
+ ieee80211_set_pwrsave(vap, 0);
+#endif
+ ieee80211_bg_scan(vap, 0);
+ }
+ return;
+ }
+ /*
+ * If scanning, just pass information to the scan module.
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
+ /*
+ * Actively scanning a channel marked passive;
+ * send a probe request now that we know there
+ * is 802.11 traffic present.
+ *
+ * XXX check if the beacon we recv'd gives
+ * us what we need and suppress the probe req
+ */
+ ieee80211_probe_curchan(vap, 1);
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
+ }
+ ieee80211_add_scan(vap, &scan, wh,
+ subtype, rssi, noise, rstamp);
+ return;
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_AUTH: {
+ uint16_t algo, seq, status;
+ /*
+ * auth frame format
+ * [2] algorithm
+ * [2] sequence
+ * [2] status
+ * [tlv*] challenge
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
+ algo = le16toh(*(uint16_t *)frm);
+ seq = le16toh(*(uint16_t *)(frm + 2));
+ status = le16toh(*(uint16_t *)(frm + 4));
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2,
+ "recv auth frame with algorithm %d seq %d", algo, seq);
+
+ if (vap->iv_flags & IEEE80211_F_COUNTERM) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
+ wh, "auth", "%s", "TKIP countermeasures enabled");
+ vap->iv_stats.is_rx_auth_countermeasures++;
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ IEEE80211_REASON_MIC_FAILURE);
+ }
+ return;
+ }
+ if (algo == IEEE80211_AUTH_ALG_SHARED)
+ sta_auth_shared(ni, wh, frm + 6, efrm, rssi,
+ noise, rstamp, seq, status);
+ else if (algo == IEEE80211_AUTH_ALG_OPEN)
+ sta_auth_open(ni, wh, rssi, noise, rstamp,
+ seq, status);
+ else {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "auth", "unsupported alg %d", algo);
+ vap->iv_stats.is_rx_auth_unsupported++;
+ return;
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
+ uint16_t capinfo, associd;
+ uint16_t status;
+
+ if (vap->iv_state != IEEE80211_S_ASSOC) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+
+ /*
+ * asresp frame format
+ * [2] capability information
+ * [2] status
+ * [2] association ID
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ * [tlv] WME
+ * [tlv] HT capabilities
+ * [tlv] HT info
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
+ ni = vap->iv_bss;
+ capinfo = le16toh(*(uint16_t *)frm);
+ frm += 2;
+ status = le16toh(*(uint16_t *)frm);
+ frm += 2;
+ if (status != 0) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
+ wh->i_addr2, "%sassoc failed (reason %d)",
+ ISREASSOC(subtype) ? "re" : "", status);
+ vap->iv_stats.is_rx_auth_fail++; /* XXX */
+ return;
+ }
+ associd = le16toh(*(uint16_t *)frm);
+ frm += 2;
+
+ rates = xrates = wme = htcap = htinfo = NULL;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
+ switch (*frm) {
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ case IEEE80211_ELEMID_HTCAP:
+ htcap = frm;
+ break;
+ case IEEE80211_ELEMID_HTINFO:
+ htinfo = frm;
+ break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswmeoui(frm))
+ wme = frm;
+ else if (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
+ /*
+ * Accept pre-draft HT ie's if the
+ * standard ones have not been seen.
+ */
+ if (ishtcapoui(frm)) {
+ if (htcap == NULL)
+ htcap = frm;
+ } else if (ishtinfooui(frm)) {
+ if (htinfo == NULL)
+ htcap = frm;
+ }
+ }
+ /* XXX Atheros OUI support */
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
+ if (xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(xrates,
+ IEEE80211_RATE_MAXSIZE - rates[1], return);
+ rate = ieee80211_setup_rates(ni, rates, xrates,
+ IEEE80211_F_JOIN |
+ IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
+ IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (rate & IEEE80211_RATE_BASIC) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
+ wh->i_addr2,
+ "%sassoc failed (rate set mismatch)",
+ ISREASSOC(subtype) ? "re" : "");
+ vap->iv_stats.is_rx_assoc_norate++;
+ ieee80211_new_state(vap, IEEE80211_S_SCAN,
+ IEEE80211_SCAN_FAIL_STATUS);
+ return;
+ }
+
+ ni->ni_capinfo = capinfo;
+ ni->ni_associd = associd;
+ if (ni->ni_jointime == 0)
+ ni->ni_jointime = time_uptime;
+ if (wme != NULL &&
+ ieee80211_parse_wmeparams(vap, wme, wh) >= 0) {
+ ni->ni_flags |= IEEE80211_NODE_QOS;
+ ieee80211_wme_updateparams(vap);
+ } else
+ ni->ni_flags &= ~IEEE80211_NODE_QOS;
+ /*
+ * Setup HT state according to the negotiation.
+ *
+ * NB: shouldn't need to check if HT use is enabled but some
+ * ap's send back HT ie's even when we don't indicate we
+ * are HT capable in our AssocReq.
+ */
+ if (htcap != NULL && htinfo != NULL &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_HT)) {
+ ieee80211_ht_node_init(ni, htcap);
+ ieee80211_parse_htinfo(ni, htinfo);
+ ieee80211_setup_htrates(ni, htcap,
+ IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
+ ieee80211_setup_basic_htrates(ni, htinfo);
+ }
+ /*
+ * Configure state now that we are associated.
+ *
+ * XXX may need different/additional driver callbacks?
+ */
+ if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
+ (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
+ ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+ ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+ } else {
+ ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
+ ic->ic_flags |= IEEE80211_F_USEBARKER;
+ }
+ ieee80211_set_shortslottime(ic,
+ IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
+ (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+ /*
+ * Honor ERP protection.
+ *
+ * NB: ni_erp should zero for non-11g operation.
+ */
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
+ (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
+ ic->ic_flags |= IEEE80211_F_USEPROT;
+ else
+ ic->ic_flags &= ~IEEE80211_F_USEPROT;
+ IEEE80211_NOTE_MAC(vap,
+ IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, wh->i_addr2,
+ "%sassoc success at aid %d: %s preamble, %s slot time%s%s%s%s%s%s",
+ ISREASSOC(subtype) ? "re" : "",
+ IEEE80211_NODE_AID(ni),
+ ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
+ ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
+ ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
+ ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
+ ni->ni_flags & IEEE80211_NODE_HT ?
+ (ni->ni_chw == 20 ? ", HT20" : ", HT40") : "",
+ ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ?
+ ", fast-frames" : "",
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ?
+ ", turbo" : ""
+ );
+ ieee80211_new_state(vap, IEEE80211_S_RUN, subtype);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_DEAUTH: {
+ uint16_t reason;
+
+ if (vap->iv_state == IEEE80211_S_SCAN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
+ /* NB: can happen when in promiscuous mode */
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ }
+
+ /*
+ * deauth frame format
+ * [2] reason
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
+ reason = le16toh(*(uint16_t *)frm);
+
+ vap->iv_stats.is_rx_deauth++;
+ vap->iv_stats.is_rx_deauth_code = reason;
+ IEEE80211_NODE_STAT(ni, rx_deauth);
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
+ "recv deauthenticate (reason %d)", reason);
+ ieee80211_new_state(vap, IEEE80211_S_AUTH,
+ (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_DISASSOC: {
+ uint16_t reason;
+
+ if (vap->iv_state != IEEE80211_S_RUN &&
+ vap->iv_state != IEEE80211_S_ASSOC &&
+ vap->iv_state != IEEE80211_S_AUTH) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
+ /* NB: can happen when in promiscuous mode */
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ }
+
+ /*
+ * disassoc frame format
+ * [2] reason
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
+ reason = le16toh(*(uint16_t *)frm);
+
+ vap->iv_stats.is_rx_disassoc++;
+ vap->iv_stats.is_rx_disassoc_code = reason;
+ IEEE80211_NODE_STAT(ni, rx_disassoc);
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
+ "recv disassociate (reason %d)", reason);
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_ACTION:
+ if (vap->iv_state == IEEE80211_S_RUN) {
+ if (ieee80211_parse_action(ni, m0) == 0)
+ ic->ic_recv_action(ni, frm, efrm);
+ } else
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "mgt", "subtype 0x%x not handled", subtype);
+ vap->iv_stats.is_rx_badsubtype++;
+ break;
+ }
+#undef ISREASSOC
+#undef ISPROBE
+}
diff --git a/sys/net80211/ieee80211_sta.h b/sys/net80211/ieee80211_sta.h
new file mode 100644
index 0000000..1508a7c
--- /dev/null
+++ b/sys/net80211/ieee80211_sta.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_STA_H_
+#define _NET80211_IEEE80211_STA_H_
+
+/*
+ * Station-mode implementation definitions.
+ */
+void ieee80211_sta_attach(struct ieee80211com *);
+void ieee80211_sta_detach(struct ieee80211com *);
+void ieee80211_sta_vattach(struct ieee80211vap *);
+#endif /* !_NET80211_IEEE80211_STA_H_ */
diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h
index 3171cef..5ada0a2 100644
--- a/sys/net80211/ieee80211_var.h
+++ b/sys/net80211/ieee80211_var.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,9 +31,6 @@
/*
* Definitions for IEEE 802.11 drivers.
*/
-#define IEEE80211_DEBUG
-#undef IEEE80211_DEBUG_REFCNT /* node refcnt stuff */
-
/* NB: portability glue must go first */
#ifdef __NetBSD__
#include <net80211/ieee80211_netbsd.h>
@@ -48,6 +45,7 @@
#include <net80211/_ieee80211.h>
#include <net80211/ieee80211.h>
#include <net80211/ieee80211_crypto.h>
+#include <net80211/ieee80211_dfs.h>
#include <net80211/ieee80211_ioctl.h> /* for ieee80211_stats */
#include <net80211/ieee80211_node.h>
#include <net80211/ieee80211_power.h>
@@ -75,8 +73,8 @@
#define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */
#define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */
-#define IEEE80211_FIXED_RATE_NONE -1
-#define IEEE80211_MCAST_RATE_DEFAULT (2*1) /* default mcast rate (1M) */
+#define IEEE80211_FIXED_RATE_NONE 0xff
+#define IEEE80211_TXMAX_DEFAULT 6 /* default ucast max retries */
#define IEEE80211_RTS_DEFAULT IEEE80211_RTS_MAX
#define IEEE80211_FRAG_DEFAULT IEEE80211_FRAG_MAX
@@ -85,40 +83,57 @@
#define IEEE80211_TU_TO_MS(x) (((x) * 1024) / 1000)
#define IEEE80211_TU_TO_TICKS(x)(((x) * 1024 * hz) / (1000 * 1000))
-struct ieee80211_aclator;
-struct sysctl_ctx_list;
+/*
+ * 802.11 control state is split into a common portion that maps
+ * 1-1 to a physical device and one or more "Virtual AP's" (VAP)
+ * that are bound to an ieee80211com instance and share a single
+ * underlying device. Each VAP has a corresponding OS device
+ * entity through which traffic flows and that applications use
+ * for issuing ioctls, etc.
+ */
+
+/*
+ * Data common to one or more virtual AP's. State shared by
+ * the underlying device and the net80211 layer is exposed here;
+ * e.g. device-specific callbacks.
+ */
+struct ieee80211vap;
+typedef void (*ieee80211vap_attach)(struct ieee80211vap *);
+
+struct ieee80211_appie {
+ uint16_t ie_len; /* size of ie_data */
+ uint8_t ie_data[]; /* user-specified IE's */
+};
struct ieee80211com {
- SLIST_ENTRY(ieee80211com) ic_next;
struct ifnet *ic_ifp; /* associated device */
ieee80211_com_lock_t ic_comlock; /* state update lock */
- ieee80211_beacon_lock_t ic_beaconlock; /* beacon update lock */
+ TAILQ_HEAD(, ieee80211vap) ic_vaps; /* list of vap instances */
struct ieee80211_stats ic_stats; /* statistics */
- struct sysctl_ctx_list *ic_sysctl; /* dynamic sysctl context */
- uint32_t ic_debug; /* debug msg flags */
- int ic_vap; /* virtual AP index */
int ic_headroom; /* driver tx headroom needs */
enum ieee80211_phytype ic_phytype; /* XXX wrong for multi-mode */
enum ieee80211_opmode ic_opmode; /* operation mode */
struct ifmedia ic_media; /* interface media config */
uint8_t ic_myaddr[IEEE80211_ADDR_LEN];
+ struct callout ic_inact; /* inactivity processing */
+ struct task ic_parent_task; /* deferred parent processing */
uint32_t ic_flags; /* state flags */
uint32_t ic_flags_ext; /* extended state flags */
uint32_t ic_flags_ven; /* vendor state flags */
uint32_t ic_caps; /* capabilities */
uint32_t ic_htcaps; /* HT capabilities */
+ uint32_t ic_cryptocaps; /* crypto capabilities */
uint8_t ic_modecaps[2]; /* set of mode capabilities */
- uint16_t ic_curmode; /* current mode */
- struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
+ uint8_t ic_promisc; /* vap's needing promisc mode */
+ uint8_t ic_allmulti; /* vap's needing all multicast*/
+ uint8_t ic_nrunning; /* vap's marked running */
+ uint8_t ic_curmode; /* current mode */
uint16_t ic_bintval; /* beacon interval */
uint16_t ic_lintval; /* listen interval */
uint16_t ic_holdover; /* PM hold over duration */
uint16_t ic_txpowlimit; /* global tx power limit */
- int ic_ampdu_rxmax; /* A-MPDU rx limit (bytes) */
- int ic_ampdu_density;/* A-MPDU density */
- int ic_ampdu_limit; /* A-MPDU tx limit (bytes) */
- int ic_amsdu_limit; /* A-MSDU tx limit (bytes) */
+ struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
/*
* Channel state:
@@ -148,30 +163,27 @@ struct ieee80211com {
struct ieee80211_channel *ic_curchan; /* current channel */
struct ieee80211_channel *ic_bsschan; /* bss channel */
struct ieee80211_channel *ic_prevchan; /* previous channel */
- int ic_countrycode; /* ISO country code */
- uint16_t ic_regdomain; /* regulatory domain */
- uint8_t ic_location; /* unknown, indoor, outdoor */
+ struct ieee80211_regdomain ic_regdomain;/* regulatory data */
+ struct ieee80211_appie *ic_countryie; /* calculated country ie */
+ struct ieee80211_channel *ic_countryie_chan;
+
+ /* 802.11h/DFS state */
+ struct ieee80211_channel *ic_csa_newchan;/* channel for doing CSA */
+ int ic_csa_count; /* count for doing CSA */
+ struct ieee80211_dfs_state ic_dfs; /* DFS state */
struct ieee80211_scan_state *ic_scan; /* scan state */
- enum ieee80211_roamingmode ic_roaming; /* roaming mode */
int ic_lastdata; /* time of last data frame */
int ic_lastscan; /* time last scan completed */
- int ic_des_nssid; /* # desired ssids */
- struct ieee80211_scan_ssid ic_des_ssid[1];/* desired ssid table */
- uint8_t ic_des_bssid[IEEE80211_ADDR_LEN];
- struct ieee80211_channel *ic_des_chan; /* desired channel */
- int ic_des_mode; /* desired phymode */
- u_int ic_bgscanidle; /* bg scan idle threshold */
- u_int ic_bgscanintvl; /* bg scan min interval */
- u_int ic_scanvalid; /* scan cache valid threshold */
- struct ieee80211_roam ic_roam; /* sta-mode roaming state */
+ /* NB: this is the union of all vap stations/neighbors */
+ int ic_max_keyix; /* max h/w key index */
struct ieee80211_node_table ic_sta; /* stations/neighbors */
+ /* XXX multi-bss: split out common/vap parts */
struct ieee80211_wme_state ic_wme; /* WME/WMM state */
- const struct ieee80211_aclator *ic_acl; /* aclator glue */
- void *ic_as; /* private aclator state */
+ /* XXX multi-bss: can per-vap be done/make sense? */
enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */
uint16_t ic_nonerpsta; /* # non-ERP stations */
uint16_t ic_longslotsta; /* # long slot time stations */
@@ -183,91 +195,56 @@ struct ieee80211com {
int ic_lastnonerp; /* last time non-ERP sta noted*/
int ic_lastnonht; /* last time non-HT sta noted */
- struct ifqueue ic_mgtq;
- enum ieee80211_state ic_state; /* 802.11 state */
- struct callout ic_mgtsend; /* mgmt frame response timer */
- uint32_t *ic_aid_bitmap; /* association id map */
- uint16_t ic_max_aid;
- uint16_t ic_ps_sta; /* stations in power save */
- uint16_t ic_ps_pending; /* ps sta's w/ pending frames */
- uint8_t *ic_tim_bitmap; /* power-save stations w/ data*/
- uint16_t ic_tim_len; /* ic_tim_bitmap size (bytes) */
- uint8_t ic_dtim_period; /* DTIM period */
- uint8_t ic_dtim_count; /* DTIM count for last bcn */
- struct bpf_if *ic_rawbpf; /* packet filter structure */
- struct ieee80211_node *ic_bss; /* information for this node */
- int ic_fixed_rate; /* 802.11 rate or -1 */
- int ic_mcast_rate; /* rate for mcast frames */
- uint16_t ic_rtsthreshold;
- uint16_t ic_fragthreshold;
- uint8_t ic_bmissthreshold;
- uint8_t ic_bmiss_count; /* current beacon miss count */
- int ic_bmiss_max; /* max bmiss before scan */
- uint16_t ic_swbmiss_count;/* beacons in last period */
- uint16_t ic_swbmiss_period;/* s/w bmiss period */
- struct callout ic_swbmiss; /* s/w beacon miss timer */
-
- uint16_t ic_txmin; /* min tx retry count */
- uint16_t ic_txmax; /* max tx retry count */
- uint16_t ic_txlifetime; /* tx lifetime */
- struct callout ic_inact; /* inactivity timer wait */
- void *ic_opt_ie; /* user-specified IE's */
- uint16_t ic_opt_ie_len; /* length of ni_opt_ie */
- int ic_inact_init; /* initial setting */
- int ic_inact_auth; /* auth but not assoc setting */
- int ic_inact_run; /* authorized setting */
- int ic_inact_probe; /* inactive probe time */
-
- /*
- * Cipher state/configuration.
- */
- struct ieee80211_crypto_state ic_crypto;
-#define ic_nw_keys ic_crypto.cs_nw_keys /* XXX compatibility */
-#define ic_def_txkey ic_crypto.cs_def_txkey /* XXX compatibility */
- /*
- * 802.1x glue. When an authenticator attaches it
- * fills in this section. We assume that when ic_ec
- * is setup that the methods are safe to call.
- */
- const struct ieee80211_authenticator *ic_auth;
- struct eapolcom *ic_ec;
-
+ /* virtual ap create/delete */
+ struct ieee80211vap* (*ic_vap_create)(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit,
+ int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+ void (*ic_vap_delete)(struct ieee80211vap *);
+ /* operating mode attachment */
+ ieee80211vap_attach ic_vattach[IEEE80211_OPMODE_MAX];
+ /* return hardware/radio capabilities */
+ void (*ic_getradiocaps)(struct ieee80211com *,
+ int *, struct ieee80211_channel []);
+ /* check and/or prepare regdomain state change */
+ int (*ic_setregdomain)(struct ieee80211com *,
+ struct ieee80211_regdomain *,
+ int, struct ieee80211_channel []);
/* send/recv 802.11 management frame */
- int (*ic_send_mgmt)(struct ieee80211com *,
- struct ieee80211_node *, int, int);
- void (*ic_recv_mgmt)(struct ieee80211com *,
- struct mbuf *, struct ieee80211_node *,
- int, int, int, uint32_t);
+ int (*ic_send_mgmt)(struct ieee80211_node *,
+ int, int);
/* send raw 802.11 frame */
int (*ic_raw_xmit)(struct ieee80211_node *,
struct mbuf *,
const struct ieee80211_bpf_params *);
- /* reset device state after 802.11 parameter/state change */
- int (*ic_reset)(struct ifnet *);
- /* [schedule] beacon frame update */
- void (*ic_update_beacon)(struct ieee80211com *, int);
/* update device state for 802.11 slot time change */
void (*ic_updateslot)(struct ifnet *);
+ /* handle multicast state changes */
+ void (*ic_update_mcast)(struct ifnet *);
+ /* handle promiscuous mode changes */
+ void (*ic_update_promisc)(struct ifnet *);
/* new station association callback/notification */
void (*ic_newassoc)(struct ieee80211_node *, int);
/* node state management */
- struct ieee80211_node *(*ic_node_alloc)(struct ieee80211_node_table*);
+ struct ieee80211_node* (*ic_node_alloc)(struct ieee80211_node_table *);
void (*ic_node_free)(struct ieee80211_node *);
void (*ic_node_cleanup)(struct ieee80211_node *);
+ void (*ic_node_age)(struct ieee80211_node *);
+ void (*ic_node_drain)(struct ieee80211_node *);
int8_t (*ic_node_getrssi)(const struct ieee80211_node*);
void (*ic_node_getsignal)(const struct ieee80211_node*,
int8_t *, int8_t *);
+ void (*ic_node_getmimoinfo)(
+ const struct ieee80211_node*,
+ struct ieee80211_mimo_info *);
/* scanning support */
void (*ic_scan_start)(struct ieee80211com *);
void (*ic_scan_end)(struct ieee80211com *);
void (*ic_set_channel)(struct ieee80211com *);
- void (*ic_scan_curchan)(struct ieee80211com *,
+ void (*ic_scan_curchan)(struct ieee80211_scan_state *,
unsigned long);
- void (*ic_scan_mindwell)(struct ieee80211com *);
- /* per-vap eventually... */
- int (*ic_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
- void (*ic_set_tim)(struct ieee80211_node *, int);
+ void (*ic_scan_mindwell)(struct ieee80211_scan_state *);
/*
* 802.11n ADDBA support. A simple/generic implementation
@@ -282,6 +259,9 @@ struct ieee80211com {
int (*ic_send_action)(struct ieee80211_node *,
int category, int action,
uint16_t args[4]);
+ /* check if A-MPDU should be enabled this station+ac */
+ int (*ic_ampdu_enable)(struct ieee80211_node *,
+ struct ieee80211_tx_ampdu *);
/* start/stop doing A-MPDU tx aggregation for a station */
int (*ic_addba_request)(struct ieee80211_node *,
struct ieee80211_tx_ampdu *,
@@ -294,11 +274,154 @@ struct ieee80211com {
struct ieee80211_tx_ampdu *);
};
+struct ieee80211_aclator;
+
+struct ieee80211vap {
+ struct ifmedia iv_media; /* interface media config */
+ struct ifnet *iv_ifp; /* associated device */
+ struct bpf_if *iv_rawbpf; /* packet filter structure */
+ struct sysctl_ctx_list *iv_sysctl; /* dynamic sysctl context */
+ struct sysctl_oid *iv_oid; /* net.wlan.X sysctl oid */
+
+ TAILQ_ENTRY(ieee80211vap) iv_next; /* list of vap instances */
+ struct ieee80211com *iv_ic; /* back ptr to common state */
+ uint32_t iv_debug; /* debug msg flags */
+ struct ieee80211_stats iv_stats; /* statistics */
+
+ uint8_t iv_myaddr[IEEE80211_ADDR_LEN];
+ uint32_t iv_flags; /* state flags */
+ uint32_t iv_flags_ext; /* extended state flags */
+ uint32_t iv_flags_ven; /* vendor state flags */
+ uint32_t iv_caps; /* capabilities */
+ uint32_t iv_htcaps; /* HT capabilities */
+ enum ieee80211_opmode iv_opmode; /* operation mode */
+ enum ieee80211_state iv_state; /* state machine state */
+ void (*iv_newstate_cb)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ struct callout iv_mgtsend; /* mgmt frame response timer */
+ /* inactivity timer settings */
+ int iv_inact_init; /* setting for new station */
+ int iv_inact_auth; /* auth but not assoc setting */
+ int iv_inact_run; /* authorized setting */
+ int iv_inact_probe; /* inactive probe time */
+
+ int iv_des_nssid; /* # desired ssids */
+ struct ieee80211_scan_ssid iv_des_ssid[1];/* desired ssid table */
+ uint8_t iv_des_bssid[IEEE80211_ADDR_LEN];
+ struct ieee80211_channel *iv_des_chan; /* desired channel */
+ uint16_t iv_des_mode; /* desired mode */
+ int iv_nicknamelen; /* XXX junk */
+ uint8_t iv_nickname[IEEE80211_NWID_LEN];
+ u_int iv_bgscanidle; /* bg scan idle threshold */
+ u_int iv_bgscanintvl; /* bg scan min interval */
+ u_int iv_scanvalid; /* scan cache valid threshold */
+ u_int iv_scanreq_duration;
+ u_int iv_scanreq_mindwell;
+ u_int iv_scanreq_maxdwell;
+ uint16_t iv_scanreq_flags;/* held scan request params */
+ uint8_t iv_scanreq_nssid;
+ struct ieee80211_scan_ssid iv_scanreq_ssid[IEEE80211_SCAN_MAX_SSID];
+ /* sta-mode roaming state */
+ enum ieee80211_roamingmode iv_roaming; /* roaming mode */
+ struct ieee80211_roamparam iv_roamparms[IEEE80211_MODE_MAX];
+
+ uint8_t iv_bmissthreshold;
+ uint8_t iv_bmiss_count; /* current beacon miss count */
+ int iv_bmiss_max; /* max bmiss before scan */
+ uint16_t iv_swbmiss_count;/* beacons in last period */
+ uint16_t iv_swbmiss_period;/* s/w bmiss period */
+ struct callout iv_swbmiss; /* s/w beacon miss timer */
+
+ int iv_ampdu_rxmax; /* A-MPDU rx limit (bytes) */
+ int iv_ampdu_density;/* A-MPDU density */
+ int iv_ampdu_limit; /* A-MPDU tx limit (bytes) */
+ int iv_amsdu_limit; /* A-MSDU tx limit (bytes) */
+ u_int iv_ampdu_mintraffic[WME_NUM_AC];
+
+ uint32_t *iv_aid_bitmap; /* association id map */
+ uint16_t iv_max_aid;
+ uint16_t iv_sta_assoc; /* stations associated */
+ uint16_t iv_ps_sta; /* stations in power save */
+ uint16_t iv_ps_pending; /* ps sta's w/ pending frames */
+ uint16_t iv_txseq; /* mcast xmit seq# space */
+ uint16_t iv_tim_len; /* ic_tim_bitmap size (bytes) */
+ uint8_t *iv_tim_bitmap; /* power-save stations w/ data*/
+ uint8_t iv_dtim_period; /* DTIM period */
+ uint8_t iv_dtim_count; /* DTIM count from last bcn */
+ /* set/unset aid pwrsav state */
+ int iv_csa_count; /* count for doing CSA */
+
+ struct ieee80211_node *iv_bss; /* information for this node */
+ struct ieee80211_txparam iv_txparms[IEEE80211_MODE_MAX];
+ uint16_t iv_rtsthreshold;
+ uint16_t iv_fragthreshold;
+ int iv_inact_timer; /* inactivity timer wait */
+ /* application-specified IE's to attach to mgt frames */
+ struct ieee80211_appie *iv_appie_beacon;
+ struct ieee80211_appie *iv_appie_probereq;
+ struct ieee80211_appie *iv_appie_proberesp;
+ struct ieee80211_appie *iv_appie_assocreq;
+ struct ieee80211_appie *iv_appie_assocresp;
+ struct ieee80211_appie *iv_appie_wpa;
+ uint8_t *iv_wpa_ie;
+ uint8_t *iv_rsn_ie;
+ uint16_t iv_max_keyix; /* max h/w key index */
+ ieee80211_keyix iv_def_txkey; /* default/group tx key index */
+ struct ieee80211_key iv_nw_keys[IEEE80211_WEP_NKID];
+ int (*iv_key_alloc)(struct ieee80211vap *,
+ const struct ieee80211_key *,
+ ieee80211_keyix *, ieee80211_keyix *);
+ int (*iv_key_delete)(struct ieee80211vap *,
+ const struct ieee80211_key *);
+ int (*iv_key_set)(struct ieee80211vap *,
+ const struct ieee80211_key *,
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+ void (*iv_key_update_begin)(struct ieee80211vap *);
+ void (*iv_key_update_end)(struct ieee80211vap *);
+
+ const struct ieee80211_authenticator *iv_auth; /* authenticator glue */
+ void *iv_ec; /* private auth state */
+
+ const struct ieee80211_aclator *iv_acl; /* acl glue */
+ void *iv_as; /* private aclator state */
+
+ /* operate-mode detach hook */
+ void (*iv_opdetach)(struct ieee80211vap *);
+ /* receive processing */
+ int (*iv_input)(struct ieee80211_node *,
+ struct mbuf *, int rssi, int noise,
+ uint32_t rstamp);
+ void (*iv_recv_mgmt)(struct ieee80211_node *,
+ struct mbuf *, int, int, int, uint32_t);
+ void (*iv_deliver_data)(struct ieee80211vap *,
+ struct ieee80211_node *, struct mbuf *);
+#if 0
+ /* send processing */
+ int (*iv_send_mgmt)(struct ieee80211_node *,
+ int, int);
+#endif
+ /* beacon miss processing */
+ void (*iv_bmiss)(struct ieee80211vap *);
+ /* reset device state after 802.11 parameter/state change */
+ int (*iv_reset)(struct ieee80211vap *, u_long);
+ /* [schedule] beacon frame update */
+ void (*iv_update_beacon)(struct ieee80211vap *, int);
+ /* power save handling */
+ void (*iv_update_ps)(struct ieee80211vap *, int);
+ int (*iv_set_tim)(struct ieee80211_node *, int);
+ /* state machine processing */
+ int (*iv_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ /* 802.3 output method for raw frame xmit */
+ int (*iv_output)(struct ifnet *, struct mbuf *,
+ struct sockaddr *, struct rtentry *);
+};
+MALLOC_DECLARE(M_80211_VAP);
+
#define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0)
#define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN)
-/* ic_flags */
-/* NB: bits 0x4c available */
+/* ic_flags/iv_flags */
#define IEEE80211_F_TURBOP 0x00000001 /* CONF: ATH Turbo enabled*/
#define IEEE80211_F_COMP 0x00000002 /* CONF: ATH comp enabled */
#define IEEE80211_F_FF 0x00000004 /* CONF: ATH FF enabled */
@@ -322,6 +445,7 @@ struct ieee80211com {
#define IEEE80211_F_DATAPAD 0x00080000 /* CONF: do alignment pad */
#define IEEE80211_F_USEPROT 0x00100000 /* STATUS: protection enabled */
#define IEEE80211_F_USEBARKER 0x00200000 /* STATUS: use barker preamble*/
+#define IEEE80211_F_CSAPENDING 0x00400000 /* STATUS: chan switch pending*/
#define IEEE80211_F_WPA1 0x00800000 /* CONF: WPA enabled */
#define IEEE80211_F_WPA2 0x01000000 /* CONF: WPA2 enabled */
#define IEEE80211_F_WPA 0x01800000 /* CONF: WPA/WPA2 enabled */
@@ -329,22 +453,32 @@ struct ieee80211com {
#define IEEE80211_F_COUNTERM 0x04000000 /* CONF: TKIP countermeasures */
#define IEEE80211_F_HIDESSID 0x08000000 /* CONF: hide SSID in beacon */
#define IEEE80211_F_NOBRIDGE 0x10000000 /* CONF: dis. internal bridge */
+#define IEEE80211_F_PCF 0x20000000 /* CONF: PCF enabled */
#define IEEE80211_F_DOTH 0x40000000 /* CONF: 11h enabled */
+#define IEEE80211_F_DWDS 0x80000000 /* CONF: Dynamic WDS enabled */
/* Atheros protocol-specific flags */
#define IEEE80211_F_ATHEROS \
(IEEE80211_F_FF | IEEE80211_F_COMP | IEEE80211_F_TURBOP)
/* Check if an Atheros capability was negotiated for use */
-#define IEEE80211_ATH_CAP(ic, ni, bit) \
- ((ic)->ic_flags & (ni)->ni_ath_flags & (bit))
+#define IEEE80211_ATH_CAP(vap, ni, bit) \
+ ((vap)->iv_flags & (ni)->ni_ath_flags & (bit))
-/* ic_flags_ext */
+/* ic_flags_ext/iv_flags_ext */
#define IEEE80211_FEXT_NONHT_PR 0x00000001 /* STATUS: non-HT sta present */
#define IEEE80211_FEXT_INACT 0x00000002 /* CONF: sta inact handling */
+#define IEEE80211_FEXT_SCANWAIT 0x00000004 /* STATUS: awaiting scan */
/* 0x00000006 reserved */
#define IEEE80211_FEXT_BGSCAN 0x00000008 /* STATUS: complete bgscan */
+#define IEEE80211_FEXT_WPS 0x00000010 /* CONF: WPS enabled */
+#define IEEE80211_FEXT_TSN 0x00000020 /* CONF: TSN enabled */
+#define IEEE80211_FEXT_SCANREQ 0x00000040 /* STATUS: scan req params */
+#define IEEE80211_FEXT_DFS 0x00000800 /* CONF: DFS enabled */
#define IEEE80211_FEXT_NONERP_PR 0x00000200 /* STATUS: non-ERP sta present*/
#define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: do bmiss in s/w */
+#define IEEE80211_FEXT_DOTD 0x00001000 /* CONF: 11d enabled */
+/* NB: immutable: should be set only when creating a vap */
+#define IEEE80211_FEXT_WDSLEGACY 0x00010000 /* CONF: legacy WDS operation */
#define IEEE80211_FEXT_PROBECHAN 0x00020000 /* CONF: probe passive channel*/
#define IEEE80211_FEXT_HT 0x00080000 /* CONF: HT supported */
#define IEEE80211_FEXT_AMPDU_TX 0x00100000 /* CONF: A-MPDU tx supported */
@@ -357,12 +491,8 @@ struct ieee80211com {
#define IEEE80211_FEXT_SHORTGI40 0x08000000 /* CONF: short GI in HT40 */
#define IEEE80211_FEXT_HTCOMPAT 0x10000000 /* CONF: HT vendor OUI's */
-/* ic_caps */
-#define IEEE80211_C_WEP 0x00000001 /* CAPABILITY: WEP available */
-#define IEEE80211_C_TKIP 0x00000002 /* CAPABILITY: TKIP available */
-#define IEEE80211_C_AES 0x00000004 /* CAPABILITY: AES OCB avail */
-#define IEEE80211_C_AES_CCM 0x00000008 /* CAPABILITY: AES CCM avail */
-#define IEEE80211_C_CKIP 0x00000020 /* CAPABILITY: CKIP available */
+/* ic_caps/iv_caps: device driver capabilities */
+/* 0x2f available */
#define IEEE80211_C_FF 0x00000040 /* CAPABILITY: ATH FF avail */
#define IEEE80211_C_TURBOP 0x00000080 /* CAPABILITY: ATH Turbo avail*/
#define IEEE80211_C_IBSS 0x00000100 /* CAPABILITY: IBSS available */
@@ -374,7 +504,7 @@ struct ieee80211com {
#define IEEE80211_C_SHSLOT 0x00004000 /* CAPABILITY: short slottime */
#define IEEE80211_C_SHPREAMBLE 0x00008000 /* CAPABILITY: short preamble */
#define IEEE80211_C_MONITOR 0x00010000 /* CAPABILITY: monitor mode */
-#define IEEE80211_C_TKIPMIC 0x00020000 /* CAPABILITY: TKIP MIC avail */
+/* 0x20000 available */
#define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */
#define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */
#define IEEE80211_C_WPA 0x01800000 /* CAPABILITY: WPA1+WPA2 avail*/
@@ -386,10 +516,8 @@ struct ieee80211com {
#define IEEE80211_C_TXFRAG 0x40000000 /* CAPABILITY: tx fragments */
/* XXX protection/barker? */
-#define IEEE80211_C_CRYPTO 0x0000002f /* CAPABILITY: crypto alg's */
-
/*
- * ic_htcaps: HT-specific device/driver capabilities
+ * ic_htcaps/iv_htcaps: HT-specific device/driver capabilities
*
* NB: the low 16-bits are the 802.11 definitions, the upper
* 16-bits are used to define s/w/driver capabilities.
@@ -401,18 +529,23 @@ struct ieee80211com {
void ieee80211_ifattach(struct ieee80211com *);
void ieee80211_ifdetach(struct ieee80211com *);
+int ieee80211_vap_setup(struct ieee80211com *, struct ieee80211vap *,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+int ieee80211_vap_attach(struct ieee80211vap *,
+ ifm_change_cb_t, ifm_stat_cb_t);
+void ieee80211_vap_detach(struct ieee80211vap *);
const struct ieee80211_rateset *ieee80211_get_suprates(struct ieee80211com *ic,
const struct ieee80211_channel *);
void ieee80211_announce(struct ieee80211com *);
void ieee80211_announce_channels(struct ieee80211com *);
-void ieee80211_media_init(struct ieee80211com *,
- ifm_change_cb_t, ifm_stat_cb_t);
+void ieee80211_drain(struct ieee80211com *);
+void ieee80211_media_init(struct ieee80211com *);
struct ieee80211com *ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN]);
int ieee80211_media_change(struct ifnet *);
void ieee80211_media_status(struct ifnet *, struct ifmediareq *);
-int ieee80211_ioctl(struct ieee80211com *, u_long, caddr_t);
-int ieee80211_cfgget(struct ieee80211com *, u_long, caddr_t);
-int ieee80211_cfgset(struct ieee80211com *, u_long, caddr_t);
+int ieee80211_ioctl(struct ifnet *, u_long, caddr_t);
int ieee80211_rate2media(struct ieee80211com *, int,
enum ieee80211_phymode);
int ieee80211_media2rate(int);
@@ -431,14 +564,14 @@ enum ieee80211_phymode ieee80211_chan2mode(const struct ieee80211_channel *);
* Key update synchronization methods. XXX should not be visible.
*/
static __inline void
-ieee80211_key_update_begin(struct ieee80211com *ic)
+ieee80211_key_update_begin(struct ieee80211vap *vap)
{
- ic->ic_crypto.cs_key_update_begin(ic);
+ vap->iv_key_update_begin(vap);
}
static __inline void
-ieee80211_key_update_end(struct ieee80211com *ic)
+ieee80211_key_update_end(struct ieee80211vap *vap)
{
- ic->ic_crypto.cs_key_update_end(ic);
+ vap->iv_key_update_end(vap);
}
/*
@@ -472,13 +605,25 @@ ieee80211_anyhdrspace(struct ieee80211com *ic, const void *data)
}
/*
- * Notify a driver that beacon state has been updated.
+ * Notify a vap that beacon state has been updated.
*/
static __inline void
-ieee80211_beacon_notify(struct ieee80211com *ic, int what)
+ieee80211_beacon_notify(struct ieee80211vap *vap, int what)
+{
+ if (vap->iv_state == IEEE80211_S_RUN)
+ vap->iv_update_beacon(vap, what);
+}
+
+/*
+ * Calculate HT channel promotion flags for a channel.
+ * XXX belongs in ieee80211_ht.h but needs IEEE80211_FEXT_*
+ */
+static __inline int
+ieee80211_htchanflags(const struct ieee80211_channel *c)
{
- if (ic->ic_state == IEEE80211_S_RUN)
- ic->ic_update_beacon(ic, what);
+ return IEEE80211_IS_CHAN_HT40(c) ?
+ IEEE80211_FEXT_HT | IEEE80211_FEXT_USEHT40 :
+ IEEE80211_IS_CHAN_HT(c) ? IEEE80211_FEXT_HT : 0;
}
/*
@@ -525,44 +670,44 @@ ieee80211_beacon_notify(struct ieee80211com *ic, int what)
#define IEEE80211_MSG_ANY 0xffffffff /* anything */
#ifdef IEEE80211_DEBUG
-#define ieee80211_msg(_ic, _m) ((_ic)->ic_debug & (_m))
-#define IEEE80211_DPRINTF(_ic, _m, _fmt, ...) do { \
- if (ieee80211_msg(_ic, _m)) \
- ieee80211_note(_ic, _fmt, __VA_ARGS__); \
+#define ieee80211_msg(_vap, _m) ((_vap)->iv_debug & (_m))
+#define IEEE80211_DPRINTF(_vap, _m, _fmt, ...) do { \
+ if (ieee80211_msg(_vap, _m)) \
+ ieee80211_note(_vap, _fmt, __VA_ARGS__); \
} while (0)
-#define IEEE80211_NOTE(_ic, _m, _ni, _fmt, ...) do { \
- if (ieee80211_msg(_ic, _m)) \
- ieee80211_note_mac(_ic, (_ni)->ni_macaddr, _fmt, __VA_ARGS__);\
+#define IEEE80211_NOTE(_vap, _m, _ni, _fmt, ...) do { \
+ if (ieee80211_msg(_vap, _m)) \
+ ieee80211_note_mac(_vap, (_ni)->ni_macaddr, _fmt, __VA_ARGS__);\
} while (0)
-#define IEEE80211_NOTE_MAC(_ic, _m, _mac, _fmt, ...) do { \
- if (ieee80211_msg(_ic, _m)) \
- ieee80211_note_mac(_ic, _mac, _fmt, __VA_ARGS__); \
+#define IEEE80211_NOTE_MAC(_vap, _m, _mac, _fmt, ...) do { \
+ if (ieee80211_msg(_vap, _m)) \
+ ieee80211_note_mac(_vap, _mac, _fmt, __VA_ARGS__); \
} while (0)
-#define IEEE80211_NOTE_FRAME(_ic, _m, _wh, _fmt, ...) do { \
- if (ieee80211_msg(_ic, _m)) \
- ieee80211_note_frame(_ic, _wh, _fmt, __VA_ARGS__); \
+#define IEEE80211_NOTE_FRAME(_vap, _m, _wh, _fmt, ...) do { \
+ if (ieee80211_msg(_vap, _m)) \
+ ieee80211_note_frame(_vap, _wh, _fmt, __VA_ARGS__); \
} while (0)
-void ieee80211_note(struct ieee80211com *ic, const char *fmt, ...);
-void ieee80211_note_mac(struct ieee80211com *ic,
- const uint8_t mac[IEEE80211_ADDR_LEN], const char *fmt, ...);
-void ieee80211_note_frame(struct ieee80211com *ic,
- const struct ieee80211_frame *wh, const char *fmt, ...);
-#define ieee80211_msg_debug(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_DEBUG)
-#define ieee80211_msg_dumppkts(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_DUMPPKTS)
-#define ieee80211_msg_input(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_INPUT)
-#define ieee80211_msg_radius(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_RADIUS)
-#define ieee80211_msg_dumpradius(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_RADDUMP)
-#define ieee80211_msg_dumpradkeys(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_RADKEYS)
-#define ieee80211_msg_scan(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_SCAN)
-#define ieee80211_msg_assoc(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_ASSOC)
+void ieee80211_note(struct ieee80211vap *, const char *, ...);
+void ieee80211_note_mac(struct ieee80211vap *,
+ const uint8_t mac[IEEE80211_ADDR_LEN], const char *, ...);
+void ieee80211_note_frame(struct ieee80211vap *,
+ const struct ieee80211_frame *, const char *, ...);
+#define ieee80211_msg_debug(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_DEBUG)
+#define ieee80211_msg_dumppkts(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_DUMPPKTS)
+#define ieee80211_msg_input(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_INPUT)
+#define ieee80211_msg_radius(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_RADIUS)
+#define ieee80211_msg_dumpradius(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_RADDUMP)
+#define ieee80211_msg_dumpradkeys(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_RADKEYS)
+#define ieee80211_msg_scan(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_SCAN)
+#define ieee80211_msg_assoc(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_ASSOC)
/*
* Emit a debug message about discarding a frame or information
@@ -570,37 +715,37 @@ void ieee80211_note_frame(struct ieee80211com *ic,
* the frame header; the other is for when a header is not
* available or otherwise appropriate.
*/
-#define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...) do { \
- if ((_ic)->ic_debug & (_m)) \
- ieee80211_discard_frame(_ic, _wh, _type, _fmt, __VA_ARGS__);\
+#define IEEE80211_DISCARD(_vap, _m, _wh, _type, _fmt, ...) do { \
+ if ((_vap)->iv_debug & (_m)) \
+ ieee80211_discard_frame(_vap, _wh, _type, _fmt, __VA_ARGS__);\
} while (0)
-#define IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...) do { \
- if ((_ic)->ic_debug & (_m)) \
- ieee80211_discard_ie(_ic, _wh, _type, _fmt, __VA_ARGS__);\
+#define IEEE80211_DISCARD_IE(_vap, _m, _wh, _type, _fmt, ...) do { \
+ if ((_vap)->iv_debug & (_m)) \
+ ieee80211_discard_ie(_vap, _wh, _type, _fmt, __VA_ARGS__);\
} while (0)
-#define IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...) do { \
- if ((_ic)->ic_debug & (_m)) \
- ieee80211_discard_mac(_ic, _mac, _type, _fmt, __VA_ARGS__);\
+#define IEEE80211_DISCARD_MAC(_vap, _m, _mac, _type, _fmt, ...) do { \
+ if ((_vap)->iv_debug & (_m)) \
+ ieee80211_discard_mac(_vap, _mac, _type, _fmt, __VA_ARGS__);\
} while (0)
-void ieee80211_discard_frame(struct ieee80211com *,
+void ieee80211_discard_frame(struct ieee80211vap *,
const struct ieee80211_frame *, const char *type, const char *fmt, ...);
-void ieee80211_discard_ie(struct ieee80211com *,
+void ieee80211_discard_ie(struct ieee80211vap *,
const struct ieee80211_frame *, const char *type, const char *fmt, ...);
-void ieee80211_discard_mac(struct ieee80211com *,
+void ieee80211_discard_mac(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN], const char *type,
const char *fmt, ...);
#else
-#define IEEE80211_DPRINTF(_ic, _m, _fmt, ...)
-#define IEEE80211_NOTE(_ic, _m, _ni, _fmt, ...)
-#define IEEE80211_NOTE_FRAME(_ic, _m, _wh, _fmt, ...)
-#define IEEE80211_NOTE_MAC(_ic, _m, _mac, _fmt, ...)
-#define ieee80211_msg_dumppkts(_ic) 0
-#define ieee80211_msg(_ic, _m) 0
-
-#define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...)
-#define IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...)
-#define IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...)
+#define IEEE80211_DPRINTF(_vap, _m, _fmt, ...)
+#define IEEE80211_NOTE(_vap, _m, _ni, _fmt, ...)
+#define IEEE80211_NOTE_FRAME(_vap, _m, _wh, _fmt, ...)
+#define IEEE80211_NOTE_MAC(_vap, _m, _mac, _fmt, ...)
+#define ieee80211_msg_dumppkts(_vap) 0
+#define ieee80211_msg(_vap, _m) 0
+
+#define IEEE80211_DISCARD(_vap, _m, _wh, _type, _fmt, ...)
+#define IEEE80211_DISCARD_IE(_vap, _m, _wh, _type, _fmt, ...)
+#define IEEE80211_DISCARD_MAC(_vap, _m, _mac, _type, _fmt, ...)
#endif
#endif /* _NET80211_IEEE80211_VAR_H_ */
diff --git a/sys/net80211/ieee80211_wds.c b/sys/net80211/ieee80211_wds.c
new file mode 100644
index 0000000..f3d90f9
--- /dev/null
+++ b/sys/net80211/ieee80211_wds.c
@@ -0,0 +1,865 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 WDS mode support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_wds.h>
+#include <net80211/ieee80211_input.h>
+
+static void wds_vattach(struct ieee80211vap *);
+static int wds_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int wds_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp);
+static void wds_recv_mgmt(struct ieee80211_node *, struct mbuf *,
+ int subtype, int rssi, int noise, u_int32_t rstamp);
+
+void
+ieee80211_wds_attach(struct ieee80211com *ic)
+{
+ ic->ic_vattach[IEEE80211_M_WDS] = wds_vattach;
+}
+
+void
+ieee80211_wds_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+wds_vdetach(struct ieee80211vap *vap)
+{
+ if (vap->iv_bss != NULL) {
+ /* XXX locking? */
+ if (vap->iv_bss->ni_wdsvap == vap)
+ vap->iv_bss->ni_wdsvap = NULL;
+ }
+}
+
+static void
+wds_vattach(struct ieee80211vap *vap)
+{
+ vap->iv_newstate = wds_newstate;
+ vap->iv_input = wds_input;
+ vap->iv_recv_mgmt = wds_recv_mgmt;
+ vap->iv_opdetach = wds_vdetach;
+}
+
+static int
+ieee80211_create_wds(struct ieee80211vap *vap, struct ieee80211_channel *chan)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211_node *ni, *obss;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
+ "%s: creating link to %s on channel %u\n", __func__,
+ ether_sprintf(vap->iv_des_bssid), ieee80211_chan2ieee(ic, chan));
+
+ /* NB: vap create must specify the bssid for the link */
+ KASSERT(vap->iv_flags & IEEE80211_F_DESBSSID, ("no bssid"));
+ /* NB: we should only be called on RUN transition */
+ KASSERT(vap->iv_state == IEEE80211_S_RUN, ("!RUN state"));
+
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) {
+ /*
+ * Dynamic/non-legacy WDS. Reference the associated
+ * station specified by the desired bssid setup at vap
+ * create. Point ni_wdsvap at the WDS vap so 4-address
+ * frames received through the associated AP vap will
+ * be dispatched upward (e.g. to a bridge) as though
+ * they arrived on the WDS vap.
+ */
+ IEEE80211_NODE_LOCK(nt);
+ obss = NULL;
+ ni = ieee80211_find_node_locked(&ic->ic_sta, vap->iv_des_bssid);
+ if (ni == NULL) {
+ /*
+ * Node went away before we could hookup. This
+ * should be ok; no traffic will flow and a leave
+ * event will be dispatched that should cause
+ * the vap to be destroyed.
+ */
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
+ "%s: station %s went away\n",
+ __func__, ether_sprintf(vap->iv_des_bssid));
+ /* XXX stat? */
+ } else if (ni->ni_wdsvap != NULL) {
+ /*
+ * Node already setup with a WDS vap; we cannot
+ * allow multiple references so disallow. If
+ * ni_wdsvap points at us that's ok; we should
+ * do nothing anyway.
+ */
+ /* XXX printf instead? */
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
+ "%s: station %s in use with %s\n",
+ __func__, ether_sprintf(vap->iv_des_bssid),
+ ni->ni_wdsvap->iv_ifp->if_xname);
+ /* XXX stat? */
+ } else {
+ /*
+ * Committed to new node, setup state.
+ */
+ obss = vap->iv_bss;
+ vap->iv_bss = ni;
+ ni->ni_wdsvap = vap;
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+ if (obss != NULL) {
+ /* NB: deferred to avoid recursive lock */
+ ieee80211_free_node(obss);
+ }
+ } else {
+ /*
+ * Legacy WDS vap setup.
+ */
+ /*
+ * The far end does not associate so we just create
+ * create a new node and install it as the vap's
+ * bss node. We must simulate an association and
+ * authorize the port for traffic to flow.
+ * XXX check if node already in sta table?
+ */
+ ni = ieee80211_node_create_wds(vap, vap->iv_des_bssid, chan);
+ if (ni != NULL) {
+ obss = vap->iv_bss;
+ vap->iv_bss = ieee80211_ref_node(ni);
+ ni->ni_flags |= IEEE80211_NODE_AREF;
+ if (obss != NULL)
+ ieee80211_free_node(obss);
+ /* give driver a chance to setup state like ni_txrate */
+ if (ic->ic_newassoc != NULL)
+ ic->ic_newassoc(ni, 1);
+ /* tell the authenticator about new station */
+ if (vap->iv_auth->ia_node_join != NULL)
+ vap->iv_auth->ia_node_join(ni);
+ if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+ ieee80211_node_authorize(ni);
+
+ ieee80211_notify_node_join(ni, 1 /*newassoc*/);
+ /* XXX inject l2uf frame */
+ }
+ }
+
+ /*
+ * Flush pending frames now that were setup.
+ */
+ if (ni != NULL && IEEE80211_NODE_WDSQ_QLEN(ni) != 0) {
+ int8_t rssi, noise;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni,
+ "flush wds queue, %u packets queued",
+ IEEE80211_NODE_WDSQ_QLEN(ni));
+ ic->ic_node_getsignal(ni, &rssi, &noise);
+ for (;;) {
+ struct mbuf *m;
+
+ IEEE80211_NODE_WDSQ_LOCK(ni);
+ _IEEE80211_NODE_WDSQ_DEQUEUE_HEAD(ni, m);
+ IEEE80211_NODE_WDSQ_UNLOCK(ni);
+ if (m == NULL)
+ break;
+ /* XXX cheat and re-use last rstamp */
+ ieee80211_input(ni, m, rssi, noise, ni->ni_rstamp);
+ }
+ }
+ return (ni == NULL ? ENOENT : 0);
+}
+
+/*
+ * Propagate multicast frames of an ap vap to all DWDS links.
+ * The caller is assumed to have verified this frame is multicast.
+ */
+void
+ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m)
+{
+ struct ieee80211com *ic = vap0->iv_ic;
+ struct ifnet *parent = ic->ic_ifp;
+ const struct ether_header *eh = mtod(m, const struct ether_header *);
+ struct ieee80211_node *ni;
+ struct ieee80211vap *vap;
+ struct ifnet *ifp;
+ struct mbuf *mcopy;
+ int err;
+
+ KASSERT(ETHER_IS_MULTICAST(eh->ether_dhost),
+ ("%s not mcast", ether_sprintf(eh->ether_dhost)));
+
+ /* XXX locking */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ /* only DWDS vaps are interesting */
+ if (vap->iv_opmode != IEEE80211_M_WDS ||
+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY))
+ continue;
+ /* if it came in this interface, don't send it back out */
+ ifp = vap->iv_ifp;
+ if (ifp == m->m_pkthdr.rcvif)
+ continue;
+ /*
+ * Duplicate the frame and send it. We don't need
+ * to classify or lookup the tx node; this was already
+ * done by the caller so we can just re-use the info.
+ */
+ mcopy = m_copypacket(m, M_DONTWAIT);
+ if (mcopy == NULL) {
+ ifp->if_oerrors++;
+ /* XXX stat + msg */
+ continue;
+ }
+ ni = ieee80211_find_txnode(vap, eh->ether_dhost);
+ if (ni == NULL) {
+ /* NB: ieee80211_find_txnode does stat+msg */
+ ifp->if_oerrors++;
+ m_freem(mcopy);
+ continue;
+ }
+ if (ieee80211_classify(ni, mcopy)) {
+ IEEE80211_DISCARD_MAC(vap,
+ IEEE80211_MSG_OUTPUT | IEEE80211_MSG_WDS,
+ eh->ether_dhost, NULL,
+ "%s", "classification failure");
+ vap->iv_stats.is_tx_classify++;
+ ifp->if_oerrors++;
+ m_freem(mcopy);
+ ieee80211_free_node(ni);
+ continue;
+ }
+ mcopy->m_flags |= M_MCAST | M_WDS;
+ mcopy->m_pkthdr.rcvif = (void *) ni;
+
+ IFQ_HANDOFF(parent, mcopy, err);
+ if (err) {
+ /* NB: IFQ_HANDOFF reclaims mbuf */
+ ifp->if_oerrors++;
+ ieee80211_free_node(ni);
+ } else
+ ifp->if_opackets++;
+ }
+}
+
+/*
+ * Handle DWDS discovery on receipt of a 4-address frame in
+ * ap mode. Queue the frame and post an event for someone
+ * to plumb the necessary WDS vap for this station. Frames
+ * received prior to the vap set running will then be reprocessed
+ * as if they were just received.
+ */
+void
+ieee80211_dwds_discover(struct ieee80211_node *ni, struct mbuf *m)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ int qlen, age;
+
+ IEEE80211_NODE_WDSQ_LOCK(ni);
+ if (!_IF_QFULL(&ni->ni_wdsq)) {
+ /*
+ * Tag the frame with it's expiry time and insert
+ * it in the queue. The aging interval is 4 times
+ * the listen interval specified by the station.
+ * Frames that sit around too long are reclaimed
+ * using this information.
+ */
+ /* XXX handle overflow? */
+ /* XXX per/vap beacon interval? */
+ /* NB: TU -> secs */
+ age = ((ni->ni_intval * ic->ic_lintval) << 2) / 1024;
+ _IEEE80211_NODE_WDSQ_ENQUEUE(ni, m, qlen, age);
+ IEEE80211_NODE_WDSQ_UNLOCK(ni);
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni,
+ "save frame, %u now queued", qlen);
+ } else {
+ vap->iv_stats.is_dwds_qdrop++;
+ _IF_DROP(&ni->ni_wdsq);
+ IEEE80211_NODE_WDSQ_UNLOCK(ni);
+
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_WDS,
+ mtod(m, struct ieee80211_frame *), "wds data",
+ "pending q overflow, drops %d (len %d)",
+ ni->ni_wdsq.ifq_drops, ni->ni_wdsq.ifq_len);
+
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_dumppkts(vap))
+ ieee80211_dump_pkt(ic, mtod(m, caddr_t),
+ m->m_len, -1, -1);
+#endif
+ /* XXX tail drop? */
+ m_freem(m);
+ }
+ ieee80211_notify_wds_discover(ni);
+}
+
+/*
+ * Age frames on the WDS pending queue. The aging interval is
+ * 4 times the listen interval specified by the station. This
+ * number is factored into the age calculations when the frame
+ * is placed on the queue. We store ages as time differences
+ * so we can check and/or adjust only the head of the list.
+ * If a frame's age exceeds the threshold then discard it.
+ * The number of frames discarded is returned to the caller.
+ */
+int
+ieee80211_node_wdsq_age(struct ieee80211_node *ni)
+{
+#ifdef IEEE80211_DEBUG
+ struct ieee80211vap *vap = ni->ni_vap;
+#endif
+ struct mbuf *m;
+ int discard = 0;
+
+ IEEE80211_NODE_WDSQ_LOCK(ni);
+ while (_IF_POLL(&ni->ni_wdsq, m) != NULL &&
+ M_AGE_GET(m) < IEEE80211_INACT_WAIT) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni,
+ "discard frame, age %u", M_AGE_GET(m));
+
+ /* XXX could be optimized */
+ _IEEE80211_NODE_WDSQ_DEQUEUE_HEAD(ni, m);
+ m_freem(m);
+ discard++;
+ }
+ if (m != NULL)
+ M_AGE_SUB(m, IEEE80211_INACT_WAIT);
+ IEEE80211_NODE_WDSQ_UNLOCK(ni);
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni,
+ "discard %u frames for age", discard);
+#if 0
+ IEEE80211_NODE_STAT_ADD(ni, wds_discard, discard);
+#endif
+ return discard;
+}
+
+/*
+ * IEEE80211_M_WDS vap state machine handler.
+ */
+static int
+wds_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
+ enum ieee80211_state ostate;
+ int error;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__,
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
+ vap->iv_state = nstate; /* state transition */
+ callout_stop(&vap->iv_mgtsend); /* XXX callout_drain */
+ if (ostate != IEEE80211_S_SCAN)
+ ieee80211_cancel_scan(vap); /* background scan */
+ ni = vap->iv_bss; /* NB: no reference held */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
+ callout_stop(&vap->iv_swbmiss);
+ error = 0;
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ switch (ostate) {
+ case IEEE80211_S_SCAN:
+ ieee80211_cancel_scan(vap);
+ break;
+ default:
+ break;
+ }
+ if (ostate != IEEE80211_S_INIT) {
+ /* NB: optimize INIT -> INIT case */
+ ieee80211_reset_bss(vap);
+ }
+ break;
+ case IEEE80211_S_SCAN:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ ieee80211_check_scan_current(vap);
+ break;
+ default:
+ break;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ if (ostate == IEEE80211_S_INIT) {
+ /*
+ * Already have a channel; bypass the scan
+ * and startup immediately.
+ */
+ error = ieee80211_create_wds(vap, ic->ic_curchan);
+ }
+ break;
+ default:
+ break;
+ }
+ return error;
+}
+
+/*
+ * Process a received frame. The node associated with the sender
+ * should be supplied. If nothing was found in the node table then
+ * the caller is assumed to supply a reference to iv_bss instead.
+ * The RSSI and a timestamp are also supplied. The RSSI data is used
+ * during AP scanning to select a AP to associate with; it can have
+ * any units so long as values have consistent units and higher values
+ * mean ``better signal''. The receive timestamp is currently not used
+ * by the 802.11 layer.
+ */
+static int
+wds_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp)
+{
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define HAS_SEQ(type) ((type & 0x4) == 0)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
+ struct ether_header *eh;
+ int hdrspace, need_tap;
+ uint8_t dir, type, subtype, qos;
+ uint16_t rxseq;
+
+ if (m->m_flags & M_AMPDU) {
+ /*
+ * Fastpath for A-MPDU reorder q resubmission. Frames
+ * w/ M_AMPDU marked have already passed through here
+ * but were received out of order and been held on the
+ * reorder queue. When resubmitted they are marked
+ * with the M_AMPDU flag and we can bypass most of the
+ * normal processing.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ type = IEEE80211_FC0_TYPE_DATA;
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ subtype = IEEE80211_FC0_SUBTYPE_QOS;
+ hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
+ goto resubmit_ampdu;
+ }
+
+ KASSERT(ni != NULL, ("null node"));
+
+ need_tap = 1; /* mbuf need to be tapped. */
+ type = -1; /* undefined */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "too short (1): len %u", m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ /*
+ * Bit of a cheat here, we use a pointer for a 3-address
+ * frame format but don't reference fields past outside
+ * ieee80211_frame_min w/o first validating the data is
+ * present.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
+ vap->iv_stats.is_rx_badversion++;
+ goto err;
+ }
+
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+
+ /* NB: WDS vap's do not scan */
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_addr4)) {
+ IEEE80211_DISCARD_MAC(vap,
+ IEEE80211_MSG_ANY, ni->ni_macaddr, NULL,
+ "too short (3): len %u", m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ /* NB: the TA is implicitly verified by finding the wds peer node */
+ if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr) &&
+ !IEEE80211_ADDR_EQ(wh->i_addr1, ifp->if_broadcastaddr)) {
+ /* not interested in */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ wh->i_addr1, NULL, "%s", "not to bss");
+ vap->iv_stats.is_rx_wrongbss++;
+ goto out;
+ }
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ if (HAS_SEQ(type)) {
+ uint8_t tid = ieee80211_gettid(wh);
+ if (IEEE80211_QOS_HAS_SEQ(wh) &&
+ TID_TO_WME_AC(tid) >= WME_AC_VI)
+ ic->ic_wme.wme_hipri_traffic++;
+ rxseq = le16toh(*(uint16_t *)wh->i_seq);
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
+ (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
+ /* duplicate, discard */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ wh->i_addr1, "duplicate",
+ "seqno <%u,%u> fragno <%u,%u> tid %u",
+ rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[tid] >> IEEE80211_SEQ_SEQ_SHIFT,
+ rxseq & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxseqs[tid] & IEEE80211_SEQ_FRAG_MASK,
+ tid);
+ vap->iv_stats.is_rx_dup++;
+ IEEE80211_NODE_STAT(ni, rx_dup);
+ goto out;
+ }
+ ni->ni_rxseqs[tid] = rxseq;
+ }
+ switch (type) {
+ case IEEE80211_FC0_TYPE_DATA:
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ if (m->m_len < hdrspace &&
+ (m = m_pullup(m, hdrspace)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "data too short: expecting %u", hdrspace);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out; /* XXX */
+ }
+ if (dir != IEEE80211_FC1_DIR_DSTODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto out;
+ }
+ /*
+ * Only legacy WDS traffic should take this path.
+ */
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "%s", "not legacy wds");
+ vap->iv_stats.is_rx_wrongdir++;/*XXX*/
+ goto out;
+ }
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
+ ni->ni_inact = ni->ni_inact_reload;
+ /*
+ * Handle A-MPDU re-ordering. The station must be
+ * associated and negotiated HT. The frame must be
+ * a QoS frame (not QoS null data) and not previously
+ * processed for A-MPDU re-ordering. If the frame is
+ * to be processed directly then ieee80211_ampdu_reorder
+ * will return 0; otherwise it has consumed the mbuf
+ * and we should do nothing more with it.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+ ieee80211_ampdu_reorder(ni, m) != 0) {
+ m = NULL;
+ goto out;
+ }
+ resubmit_ampdu:
+
+ /*
+ * Handle privacy requirements. Note that we
+ * must not be preempted from here until after
+ * we (potentially) call ieee80211_crypto_demic;
+ * otherwise we may violate assumptions in the
+ * crypto cipher modules used to do delayed update
+ * of replay sequence numbers.
+ */
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "WEP", "%s", "PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ IEEE80211_NODE_STAT(ni, rx_noprivacy);
+ goto out;
+ }
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ IEEE80211_NODE_STAT(ni, rx_wepfail);
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ } else {
+ /* XXX M_WEP and IEEE80211_F_PRIVACY */
+ key = NULL;
+ }
+
+ /*
+ * Save QoS bits for use below--before we strip the header.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+ qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+ ((struct ieee80211_qosframe *)wh)->i_qos[0];
+ } else
+ qos = 0;
+
+ /*
+ * Next up, any fragmentation.
+ */
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ m = ieee80211_defrag(ni, m, hdrspace);
+ if (m == NULL) {
+ /* Fragment dropped or frame not complete yet */
+ goto out;
+ }
+ }
+ wh = NULL; /* no longer valid, catch any uses */
+
+ /*
+ * Next strip any MSDU crypto bits.
+ */
+ if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "demic error");
+ vap->iv_stats.is_rx_demicfail++;
+ IEEE80211_NODE_STAT(ni, rx_demicfail);
+ goto out;
+ }
+
+ /* copy to listener after decrypt */
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ need_tap = 0;
+
+ /*
+ * Finally, strip the 802.11 header.
+ */
+ m = ieee80211_decap(vap, m, hdrspace);
+ if (m == NULL) {
+ /* XXX mask bit to check for both */
+ /* don't count Null data frames as errors */
+ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
+ subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
+ goto out;
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "decap error");
+ vap->iv_stats.is_rx_decap++;
+ IEEE80211_NODE_STAT(ni, rx_decap);
+ goto err;
+ }
+ eh = mtod(m, struct ether_header *);
+ if (!ieee80211_node_is_authorized(ni)) {
+ /*
+ * Deny any non-PAE frames received prior to
+ * authorization. For open/shared-key
+ * authentication the port is mark authorized
+ * after authentication completes. For 802.1x
+ * the port is not marked authorized by the
+ * authenticator until the handshake has completed.
+ */
+ if (eh->ether_type != htons(ETHERTYPE_PAE)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ eh->ether_shost, "data",
+ "unauthorized port: ether type 0x%x len %u",
+ eh->ether_type, m->m_pkthdr.len);
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(ni, rx_unauth);
+ goto err;
+ }
+ } else {
+ /*
+ * When denying unencrypted frames, discard
+ * any non-PAE frames received without encryption.
+ */
+ if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
+ (key == NULL && (m->m_flags & M_WEP) == 0) &&
+ eh->ether_type != htons(ETHERTYPE_PAE)) {
+ /*
+ * Drop unencrypted frames.
+ */
+ vap->iv_stats.is_rx_unencrypted++;
+ IEEE80211_NODE_STAT(ni, rx_unencrypted);
+ goto out;
+ }
+ }
+ /* XXX require HT? */
+ if (qos & IEEE80211_QOS_AMSDU) {
+ m = ieee80211_decap_amsdu(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
+#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
+ m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
+ struct llc *llc;
+
+ /*
+ * Check for fast-frame tunnel encapsulation.
+ */
+ if (m->m_len < FF_LLC_SIZE &&
+ (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame",
+ "%s", "m_pullup(llc) failed");
+ vap->iv_stats.is_rx_tooshort++;
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ llc = (struct llc *)(mtod(m, uint8_t *) +
+ sizeof(struct ether_header));
+ if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
+ m_adj(m, FF_LLC_SIZE);
+ m = ieee80211_decap_fastframe(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ }
+#undef FF_LLC_SIZE
+ ieee80211_deliver_data(vap, ni, m);
+ return IEEE80211_FC0_TYPE_DATA;
+
+ case IEEE80211_FC0_TYPE_MGT:
+ vap->iv_stats.is_rx_mgmt++;
+ IEEE80211_NODE_STAT(ni, rx_mgmt);
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto err;
+ }
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "mgt", "too short: len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_debug(vap) || ieee80211_msg_dumppkts(vap)) {
+ if_printf(ifp, "received %s from %s rssi %d\n",
+ ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(wh->i_addr2), rssi);
+ }
+#endif
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL, "%s", "WEP set but not permitted");
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
+ goto out;
+ }
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
+ m_freem(m);
+ return IEEE80211_FC0_TYPE_MGT;
+
+ case IEEE80211_FC0_TYPE_CTL:
+ vap->iv_stats.is_rx_ctl++;
+ IEEE80211_NODE_STAT(ni, rx_ctrl);
+ goto out;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "bad", "frame type 0x%x", type);
+ /* should not come here */
+ break;
+ }
+err:
+ ifp->if_ierrors++;
+out:
+ if (m != NULL) {
+ if (bpf_peers_present(vap->iv_rawbpf) && need_tap)
+ bpf_mtap(vap->iv_rawbpf, m);
+ m_freem(m);
+ }
+ return type;
+#undef SEQ_LEQ
+}
+
+static void
+wds_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
+ int subtype, int rssi, int noise, u_int32_t rstamp)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame *wh;
+ u_int8_t *frm, *efrm;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (u_int8_t *)&wh[1];
+ efrm = mtod(m0, u_int8_t *) + m0->m_len;
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_DISASSOC:
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ case IEEE80211_FC0_SUBTYPE_ACTION:
+ if (vap->iv_state != IEEE80211_S_RUN ||
+ IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ }
+ ni->ni_inact = ni->ni_inact_reload;
+ if (ieee80211_parse_action(ni, m0) == 0)
+ ic->ic_recv_action(ni, frm, efrm);
+ break;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "mgt", "subtype 0x%x not handled", subtype);
+ vap->iv_stats.is_rx_badsubtype++;
+ break;
+ }
+}
diff --git a/sys/net80211/ieee80211_wds.h b/sys/net80211/ieee80211_wds.h
new file mode 100644
index 0000000..c34fb6e
--- /dev/null
+++ b/sys/net80211/ieee80211_wds.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_WDS_H_
+#define _NET80211_IEEE80211_WDS_H_
+
+/*
+ * WDS implementation definitions.
+ */
+void ieee80211_wds_attach(struct ieee80211com *);
+void ieee80211_wds_detach(struct ieee80211com *);
+
+void ieee80211_dwds_mcast(struct ieee80211vap *, struct mbuf *);
+void ieee80211_dwds_discover(struct ieee80211_node *, struct mbuf *);
+int ieee80211_node_wdsq_age(struct ieee80211_node *);
+#endif /* !_NET80211_IEEE80211_WDS_H_ */
diff --git a/sys/net80211/ieee80211_xauth.c b/sys/net80211/ieee80211_xauth.c
index c829b6e..2341ffb 100644
--- a/sys/net80211/ieee80211_xauth.c
+++ b/sys/net80211/ieee80211_xauth.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2004 Video54 Technologies, Inc.
- * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -39,6 +39,8 @@ __FBSDID("$FreeBSD$");
* of the available callbacks--the user mode authenticator process works
* entirely from messages about stations joining and leaving.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
@@ -54,6 +56,9 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
+/* XXX number of references from net80211 layer; needed for module code */
+static int nrefs = 0;
+
/*
* One module handles everything for now. May want
* to split things up for embedded applications.
@@ -66,30 +71,6 @@ static const struct ieee80211_authenticator xauth = {
.ia_node_leave = NULL,
};
-/*
- * Module glue.
- */
-static int
-wlan_xauth_modevent(module_t mod, int type, void *unused)
-{
- switch (type) {
- case MOD_LOAD:
- ieee80211_authenticator_register(IEEE80211_AUTH_8021X, &xauth);
- ieee80211_authenticator_register(IEEE80211_AUTH_WPA, &xauth);
- return 0;
- case MOD_UNLOAD:
- ieee80211_authenticator_unregister(IEEE80211_AUTH_8021X);
- ieee80211_authenticator_unregister(IEEE80211_AUTH_WPA);
- return 0;
- }
- return EINVAL;
-}
-
-static moduledata_t wlan_xauth_mod = {
- "wlan_xauth",
- wlan_xauth_modevent,
- 0
-};
-DECLARE_MODULE(wlan_xauth, wlan_xauth_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_xauth, 1);
-MODULE_DEPEND(wlan_xauth, wlan, 1, 1, 1);
+IEEE80211_AUTH_MODULE(xauth, 1);
+IEEE80211_AUTH_ALG(x8021x, IEEE80211_AUTH_8021X, xauth);
+IEEE80211_AUTH_ALG(wpa, IEEE80211_AUTH_WPA, xauth);
diff --git a/sys/pc98/conf/GENERIC b/sys/pc98/conf/GENERIC
index 91d582d8..80ac05e 100644
--- a/sys/pc98/conf/GENERIC
+++ b/sys/pc98/conf/GENERIC
@@ -215,8 +215,6 @@ device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
device wlan_amrr # AMRR transmit rate control algorithm
-device wlan_scan_ap # 802.11 AP mode scanning
-device wlan_scan_sta # 802.11 STA mode scanning
device an # Aironet 4500/4800 802.11 wireless NICs.
device ath # Atheros pci/cardbus NIC's
device ath_hal # Atheros HAL (Hardware Access Layer)
diff --git a/sys/sparc64/conf/GENERIC b/sys/sparc64/conf/GENERIC
index c5715af..f43731e 100644
--- a/sys/sparc64/conf/GENERIC
+++ b/sys/sparc64/conf/GENERIC
@@ -195,8 +195,6 @@ device wlan # 802.11 support
device wlan_wep # 802.11 WEP support
device wlan_ccmp # 802.11 CCMP support
device wlan_tkip # 802.11 TKIP support
-device wlan_scan_ap # 802.11 AP mode scanning
-device wlan_scan_sta # 802.11 STA mode scanning
device ath # Atheros pci/cardbus NIC's
device ath_hal # Atheros HAL (Hardware Access Layer)
device ath_rate_sample # SampleRate tx rate control for ath
OpenPOWER on IntegriCloud