diff options
author | Sam Leffler <sam@FreeBSD.org> | 2008-04-20 20:35:46 +0000 |
---|---|---|
committer | Sam Leffler <sam@FreeBSD.org> | 2008-04-20 20:35:46 +0000 |
commit | b032f27c365b992e9d8e42214183b39acfb8c6ac (patch) | |
tree | bc7985c57e7ecfa1ac03e48c406a25430dba634b /sys | |
parent | f44636071190e39b601efef820f32bf0fb0bb02f (diff) |
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)
Notes
Notes:
svn path=/head/; revision=178354
Diffstat (limited to 'sys')
133 files changed, 26321 insertions, 21596 deletions
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC index 8a3566aa489d..4d0a1d7b9d4e 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 53b2681eadd3..c436eabd4911 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 fc0529c3968b..225a12b9c7cd 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 60e3da14f694..54f631d2e415 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 34564a50e84d..5aef1fda665f 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 125d2baccb8b..fd41e73fcfb6 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 09acf41c1616..eec0e9da5c3c 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 000000000000..5acf6dbed08c --- /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 000000000000..8560f4267381 --- /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 000000000000..9e511d4c1d67 --- /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 000000000000..43d4353623fc --- /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 000000000000..ab022674065d --- /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 556ac5f03b23..db09b02ca7b7 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 000000000000..14b3f7b6fdb7 --- /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 dcc57650752e..249c260fd51b 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 a82bd020cfb5..2e2a561de3b8 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 00ae5688a386..c230bdf2aadd 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 cb5d135975bf..c97a00712c03 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 eb5759e98b45..25bac0926a5e 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 4eef8620268d..27bbe94920ff 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 180ef82f851b..60a81ed37996 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 1e9377e23b0f..1ea96c497e6c 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 fd02770071d0..0d5ef914326c 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, ®domain); - 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 d775966adf8d..ed35447c4cc3 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 ccd7220bf19f..1c737a4e75f4 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 a6aaf6f3ce62..3ad287af5aa3 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 af1045d10857..257c6eedb5ec 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 c44ed53b6169..896ddb348125 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 cdbf543ec052..681470993fe6 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 8a69a405627e..f21bc5be798e 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 f6ec999a5291..de35019661e8 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 97dad68bae78..1ddb59b19a6f 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 fca0b7aaaf7c..abc6f9c27dd1 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 a1a628529d13..8e70f4059904 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 0cb5bc658bdf..070649acefa3 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 2ceafe4a97ae..a36218bd71c8 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 b8922ba0da04..000000000000 --- 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 63f7de05f9cc..1536a3594ed5 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 bab455d440b3..8e501b4c2231 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 1f68311dc69d..08414443e233 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 025646d6f44a..c94dbe734e76 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 9f12a15fca61..0c15840c8392 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 7657d10d4101..ec46c84756f9 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 e7d6d15cd6c3..59980c0e102e 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 c2b063ff683d..40a225b8c4c5 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 665b05bd4070..39aef9e885ac 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 509d33c39e76..bf511770ed60 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 fc736ae8885c..4d9c8fdfd36d 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 0a04bed31745..0061e638bd0d 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 9ff16a42159f..d750ab14e3f2 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 53f067b97ba1..ad1b5082680f 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 fa67a6bf9938..cfbe4dc90802 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 88238a346534..a3e9e66292ee 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 ce053fa1930c..000000000000 --- 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 7df317bac1bc..f80149340f1e 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 39020df1deb4..1436592851ea 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 36d68d169a95..202769293857 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 8b63260f114a..73af1ee8596e 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 b5de9b8e0c54..46b5eb07d137 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 5523815cd96f..0405da2f4685 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 c111e2644da3..2edd73f138dc 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 fc91e2b8562e..4a13f23e484d 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 c0e88c5c022b..78861027dfa4 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 2ec55bff722e..6a3b269f5bb3 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 000000000000..3db53add36cb --- /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 000000000000..2dc6b47a1043 --- /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 000000000000..71044a60927a --- /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 000000000000..40c6bc1cc94c --- /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 000000000000..b372786c1a74 --- /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 57122180a39a..b6f10e308818 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 aa71de2eebf8..59228599996e 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 ac7b5ce25517..481d124d280e 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 06bafcec6eed..bbf8f6207fa4 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 000000000000..2285afc34dd6 --- /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 117a6bca3d87..000000000000 --- 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 8d96875d5fdf..000000000000 --- 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 1449b78479e0..80c19f85fa05 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 88ad322bb5e7..3a5c6dcb9792 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 fba6f8aabb46..1877fd18eb44 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 9f50e3c4d21a..d41659ff30e6 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 dbeb7a3d6ea8..952f420a8209 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 97f9365f7f3e..3e910686c85f 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 c5305d055e93..13407a4a1b39 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 000000000000..ce9c4cb46d6e --- /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 000000000000..d8e19e5e81d5 --- /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 5b34768b4e6b..a759e64b8125 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 947d6179be7f..c03f69978c2b 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 83d7c3f968c1..d644d0c8b044 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 ee99caa19000..999f139b7a91 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 525fe9adcaa0..d3b1af5b8631 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 7fbb53d9dee3..b1ffbb48aa48 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 327306a298c1..398246f23890 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 81d15cc20326..df988a6ef13e 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 000000000000..5b782831e794 --- /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 000000000000..0351cb81b776 --- /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 000000000000..36d14aa93ab8 --- /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 1cb78137e972..d0b1b69fd561 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, &du_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 0052dfe79915..bccc0cf08c6e 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 000000000000..3c15505b56e7 --- /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 000000000000..87f858d19609 --- /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 63fb39a110fd..d8b2d3d83d23 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 d1d6699e5a3d..80052cfb02aa 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 9238bdfa2a3e..9ae653f71191 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 000000000000..dd41d7cbe99c --- /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 83627e83bd44..3b088b1c8ee1 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 e96ab521f3b6..c40b92923a6e 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 000000000000..b1e98ebc1d72 --- /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 000000000000..d7dd8e98c51d --- /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 0ed8b7934681..58cb33fbc63e 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 2799ed4c814c..fb569a9e236c 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 6e31d1d169a0..cc2a911dff84 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 000000000000..e1c847fb415a --- /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 000000000000..2b5adcf4a5ff --- /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 e764e1f291df..2a7dda88246a 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 c8461f6040f5..27f5d5a3873a 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 e67d40ca7da5..fe03b15f8d8e 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 9f94f1cfc77b..ec7061da332f 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 7f1b3dc8484f..4cf2dc030cd2 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 = ®->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, ®->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, ®->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 9c1345e0e7ca..c9c08236a5fb 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 000000000000..f1fc409edca1 --- /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 50eee4429bc3..b454f43cadcb 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 6f6b9a684d84..cec9673f11e1 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 fed5e0b71566..df6ce1f7b746 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 300f4407b407..000000000000 --- 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 d1526052a697..d1dc060e58f5 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 000000000000..e3c57ad0ee5e --- /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 000000000000..1508a7c7bbc4 --- /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 3171ceff4794..5ada0a21152b 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 000000000000..f3d90f9db2b5 --- /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 000000000000..c34fb6ee8375 --- /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 c829b6ea2a57..2341ffb162e9 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 91d582d863ed..80ac05ede4b3 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 c5715af89b04..f43731ebfeae 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 |