From 5f1c0791b39b7de6321658ce707d1788eda0f27b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Wed, 15 Feb 2012 00:46:42 +0100 Subject: [PATCH] merged changes from FreeBSD 9.0 for most wlan drivers and net80211. * merged changes from FreeBSD 9.0 for most wlan drivers except atheroswifi and net80211. * added iwlwifi-6000g2b-ucode-18.168.6.1 --- .../iwlwifi-6000g2b-ucode-18.168.6.1.tgz | Bin 0 -> 330313 bytes .../network/wlan/aironetwifi/dev/an/if_an.c | 287 +- .../wlan/aironetwifi/dev/an/if_an_isa.c | 4 +- .../wlan/aironetwifi/dev/an/if_an_pccard.c | 4 +- .../wlan/aironetwifi/dev/an/if_an_pci.c | 34 +- .../wlan/aironetwifi/dev/an/if_anreg.h | 59 +- .../drivers/network/wlan/aironetwifi/glue.c | 1 + .../wlan/broadcom43xx/dev/bwi/if_bwi.c | 33 +- .../wlan/broadcom43xx/dev/bwi/if_bwivar.h | 1 + .../wlan/iprowifi2100/dev/ipw/if_ipw.c | 65 +- .../wlan/iprowifi2100/dev/ipw/if_ipwvar.h | 11 +- .../wlan/iprowifi2200/dev/iwi/if_iwi.c | 22 +- .../wlan/iprowifi3945/dev/wpi/if_wpi.c | 23 +- .../drivers/network/wlan/iprowifi4965/Jamfile | 4 +- .../wlan/iprowifi4965/dev/iwn/if_iwn.c | 3627 +++++++++-------- .../wlan/iprowifi4965/dev/iwn/if_iwnreg.h | 212 +- .../wlan/iprowifi4965/dev/iwn/if_iwnvar.h | 97 +- .../drivers/network/wlan/iprowifi4965/glue.c | 5 +- .../wlan/marvell88w8335/dev/malo/if_malo.c | 28 +- .../wlan/marvell88w8335/dev/malo/if_malo.h | 2 + .../marvell88w8335/dev/malo/if_malo_pci.c | 26 +- .../wlan/marvell88w8335/dev/malo/if_malohal.h | 2 +- .../wlan/marvell88w8363/dev/mwl/if_mwl.c | 36 +- .../wlan/marvell88w8363/dev/mwl/if_mwlvar.h | 2 + .../wlan/marvell88w8363/dev/mwl/mwlhal.c | 2 +- .../wlan/marvell88w8363/dev/mwl/mwlhal.h | 2 +- .../network/wlan/ralinkwifi/dev/ral/rt2560.c | 34 +- .../network/wlan/ralinkwifi/dev/ral/rt2661.c | 10 +- .../network/wlan/wavelanwifi/dev/wi/if_wi.c | 2 +- .../compat/freebsd_wlan/net80211/_ieee80211.h | 15 +- .../compat/freebsd_wlan/net80211/ieee80211.c | 87 +- .../compat/freebsd_wlan/net80211/ieee80211.h | 1 + .../freebsd_wlan/net80211/ieee80211_acl.c | 5 +- .../freebsd_wlan/net80211/ieee80211_adhoc.c | 124 +- .../freebsd_wlan/net80211/ieee80211_ageq.c | 4 +- .../freebsd_wlan/net80211/ieee80211_alq.c | 155 + .../freebsd_wlan/net80211/ieee80211_alq.h | 51 + .../freebsd_wlan/net80211/ieee80211_amrr.c | 4 +- .../freebsd_wlan/net80211/ieee80211_crypto.h | 4 +- .../net80211/ieee80211_crypto_ccmp.c | 3 +- .../net80211/ieee80211_crypto_tkip.c | 3 +- .../freebsd_wlan/net80211/ieee80211_dfs.c | 2 + .../freebsd_wlan/net80211/ieee80211_hostap.c | 36 +- .../freebsd_wlan/net80211/ieee80211_ht.c | 414 +- .../freebsd_wlan/net80211/ieee80211_ht.h | 5 +- .../freebsd_wlan/net80211/ieee80211_input.c | 48 +- .../freebsd_wlan/net80211/ieee80211_input.h | 98 + .../freebsd_wlan/net80211/ieee80211_ioctl.c | 178 +- .../freebsd_wlan/net80211/ieee80211_ioctl.h | 4 +- .../freebsd_wlan/net80211/ieee80211_mesh.c | 86 +- .../freebsd_wlan/net80211/ieee80211_mesh.h | 5 +- .../freebsd_wlan/net80211/ieee80211_node.c | 26 +- .../freebsd_wlan/net80211/ieee80211_node.h | 7 + .../freebsd_wlan/net80211/ieee80211_output.c | 131 +- .../freebsd_wlan/net80211/ieee80211_power.c | 2 +- .../freebsd_wlan/net80211/ieee80211_proto.c | 46 +- .../freebsd_wlan/net80211/ieee80211_proto.h | 27 + .../freebsd_wlan/net80211/ieee80211_scan.c | 5 +- .../net80211/ieee80211_scan_sta.c | 40 +- .../freebsd_wlan/net80211/ieee80211_sta.c | 45 +- .../freebsd_wlan/net80211/ieee80211_var.h | 22 +- .../freebsd_wlan/net80211/ieee80211_wds.c | 63 +- 62 files changed, 3890 insertions(+), 2491 deletions(-) create mode 100644 data/system/data/firmware/iprowifi4965/iwlwifi-6000g2b-ucode-18.168.6.1.tgz create mode 100644 src/libs/compat/freebsd_wlan/net80211/ieee80211_alq.c create mode 100644 src/libs/compat/freebsd_wlan/net80211/ieee80211_alq.h diff --git a/data/system/data/firmware/iprowifi4965/iwlwifi-6000g2b-ucode-18.168.6.1.tgz b/data/system/data/firmware/iprowifi4965/iwlwifi-6000g2b-ucode-18.168.6.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..3d1dc4414a37b220c7b73c176f902c569b8144e1 GIT binary patch literal 330313 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

w4p=*J8;G9DjyF$0)qsG+&fqjw#|+;W;W| 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¥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%0Askvb*EDxO7Fz5y*@%swA53r%^$45U5SN zxZsB(xT{_?w$q{O!_pUc&Gzc21%N0U%!7ygJz6e}2_mCe@8>>7&HV6F)9+M7x*9Ew zFm-@Sk$g#NDHaxG{5kqtuFZwiRoU+$TWZF+ed{IQqk~S8~_GVl%`v2Rh4<<%+7ykJG`t1h#Ydj>z0KL$>5n4pL2?xmlJVt&Dl9D zbrf$Et$xkgP@ka*;>LZ8#<>~5sxM1WT;Ea7mcz3%vkAaA^?jN{KU##hD8;{po0~A+ zDAvuXLIEl}jn6fgxa*%Z40*I?{}>W=#E4rBt6n#K3Nl=cfiMUPK19@(*7{Q5KGSNW zVli+%U-XeU9mtK;$X3z2*Kf*)J_F2wnB>JGBZ5B(zv8%qBH9&aVZ4FVAT{PK=D2zG z=A}(Wsz9_s4WA3XlSBIzJb5O_yQ>?`$!TvOM*&4Gh4{NW_N3^`_hem0*YB8rL3ho5dX4VW2R~8B>Fy0V^96Wvoq@z$bHp zfU~ZRzuL2C)#M<1!V&YN!`Q4trU-Q~O$5k0YkQ$=pik^3Z|Hclq_NW5n7uBx{nUVp3R@l>~(>KS1uxYW7j>oan^fSJiynpJaKKTN%$Z*(*iafH#GBd z-zRBf>fz(k-svpYM&G^*hZ!4mN4ocHwbX?!2NNyqxWNAFDem(bD%FR@z2V|Um>l5U z7!md#>Sm$aq1kWT$R5I|rW`1Q*tTaW%{3;W_P{E9+c^B1Ri{ns!fj7i}jzZh}eK}->(Zd16 zS&JwKzZn|K9$LhGg>al1@!}jWz@WtaSF;Q5>Laz0w3aRD-jdmmgs@m-+k}38#k(M) zToWJNVEa&*5(FpmXqqHc)v>#Kwn>v zsKg=f-tG-)b%plo!87O&SJ=RhC^ih{-kVU&xyo z7PlB9(}qI-@hckVsL?M)q*%XX(EW;|{22|Dezh*Tv&u=qI+^X)7FjtJJZiaVin@kQ z7MVxIG9TV+p}hNhW!f3UhC_jrf6+oYWX11~yYrb=K>BKvmG`hJCX#}Hrzg83`-W2< zpkQA~$34)z<|Oko!Uklr`u_4O8hAqf1-OU}8|(*i{x3JZ7CV@@UU z6V>2HP4($w4THZCD8P^h6yOQ+$u>IM((-*?Pynrr*WKS&wY)3{2i&0s`Ba4nsF3KW z=y<0C^D&6uzq7=5Pm{ere@tCo=)G_I03Q28`pXWFwLS#;%39~@p8F$2FyFsZt3GBh zoqWhXS}W3qU--GaU4&iq(>>XW7sUG^v~cA9Q;Zy(62j#!A`CxDT99mt!Ro6`+Ytc5yyVKs7OC*L_Zmk*4J{xTnCvZ zaI9;)E1szHQ&~Q;ug#h8XILbDeCi|$c)N1kh;FP+2qyCIaHCCSZ;q$azIm&K?n-8b z-X;`uW+C|hW1z~i8cvem5_=@H5!zp}%Yen&$BTHnTYWt}nx4)BW#=2 z``x;}%+2H!@U?~Uh3rS`WiZU>`>Wj;v7N@qB6~w%?~;4$PWNeWf@1fzBwd5|cX9WT zvltvrYDHb|6ej_z+(vgx30OQf*-P0*o7{Cgpl%04yLV;aMN)w14O!4e`yG9b)I!X@ zViBL1>rW(C*h!ogR;_}z=hFSEM5Aho_(c!&LxH_!@|LiVafhJbJaSG_L?LrNY>yG= zZ?B2(=&mDvk!MKy_+@(ZD*(E`vBZUw`-*pL^Z$ZTTV_8||w@~Wd9mpslAB=!*` zm!w|gzXp=Ll11&QlBh5QW(z+?+WR_W0iWEJF!2(yzn4+B;kz)tkVDuA9G{8$ppAx@ zOLR$6<={gp%~0H;PtBvv;+w7aaF+J+W(2!}V3r}NHuS(CQs|Y(eIkks&$3z_ko=Zf zkCR{L28a`ciU6iI)yHQOM!Zdep?zRqpuqgv-3|5p5(9tfa#w7M>Xx;gzZ zLa{?JNpVJz3v!!?;AJHGDeD3cP5ld<3GW{LcAR$}<|gddcLEhtZJK|Q`6#T7Tt?Oe zEo}*ds-*$4?|=MDer%&wpjM#q>Ae*RPX^DirZNWITw0ypaGF+EOph%#9y=z2mJW8D zzDtd*Zpy5nj58;$o)+4vNzDPB6w_{gID5lH!j3;`NL2xN;`Q`|q05AL9k;fP^p3uJ zlXxC#wkf)1qKsMTTK2wA2UBO99I$PEC@2)w0UDNjNPfY)Z^_kj^`WkQO)>FRNAxOE zGx1dL{V@z_UC(y3L%>S)EzoW_c(O8-DjP8wYauZU7=-G<1f)JGaE&HqS4ElW>cU_b z2o=qbr*Q_lu+}yZAkImch=;@E81xy-FF8BA#{`yj$OJpR7xfZ5jJh~)AqXxPt5IWk%2#Y>TqXqC|ts2VQf zXuit~q-aSuQ)blkd_(xAj$CgG^NlM5Nte3ZJp<2&%Nm7nhM=hcZ8oSE2uEoZh~9uv ze`1=+*hl||;MZA|aT*_E)p42m!kb35Wnk6<>U=!_XgQilAu)6j^f>HdgPlI)90zJy zY>M9iFXtlDB_f^`LfsC7AXw2q&cnqXdGNzUDFnk^1BbT|a;y{rLI0|Y;H?OiUKxfm z2dQ#w6r|(#6JC#;1FlxRk~IS)wz;VcB~Hk+R!p`-rShPlxk-CJlnwy)1*I6$_G$hl`4ffOC zwrY5CVY1>LxJ6H)RN*w`v$@Ac;auelj)Hg$I_S~Rfw!S|ClhHzsXydl6k(K7N`H)a9hLOMgp#F6;$*XnGIrFNoVPG{j}3?SLQfnDiU)yu)# zZ{Al=)w^n<6J>2bv$wJX*<(m`h9qdr`@y!C7k8j*^X=(f6=|%e z;SWA!kep$6>~kN0J-=#S|HaVDz))R;w!$q)1eKRFc4w)q5kEIGGv0Yk0DH^ujq~d zJrvmEB|5xHf4&&NwtdlC)$60;$8{--YR82v8Qp(C1tj((4Ni@oCXfw#JZ%KKDlN>A z*gy&^i+z43!m`YM0a?#taHjc?N->p~xRJKB->?@10&z5O!IBH8d}KfFcVSf!u5n7k`Hd>_NSVoQm-Rj z*V;vkPpWFjYj&4jbbnYK1T&?>5P~XKGHs^lvPaHKcc3e}dPi|qge`NW_^&TJzuve> zdhwT057=du>o6fxn0XgNU7dktOYr}GY^*s@u6Xk$1)79>DK40-nUgkNwSz>ab?~Y6 zg0y1;NLA1UbTt`{3NDjoi;`EiphrfR4;QX#9CBY$JlFwVuUXen#qP@uH9oj~v9q&R z@}SHn^T@lta2u4#yoKP6I^7D6BiJKGBfX7X;(HYkhKGC+U}fU?rv&;tj*PU4DX>;% zyoK-mDB_?O%OP!Y4-hI}BI@Ua*P5Ur709c0HhMYmC@JDirxfLh8 zaSHD9bJagcU;yz>xt9&U*nS?obb%spt6%%)j*te#E0{IXRgJkZ8jzvlp#I!N6w zZ*m(yEcHPH`qD^S@@4x^fheFdsQ>qMqj(X33%|8FZqDkH6!03>BUS7<$f|Y9k=4;s z#v6-c&;6=x zo;sr6O=I`($Nfb6{Xlt}(gxwgF-!InG{T2+gRwo{c2}%?@Z&7j_Rd2Y~{Sp?>TO2o(oSFA_4LPNn|?`g~gxokH|mQ_W_#8(k69_@rQg$WnqE&fHAUKiSX=a z@>D3v5K$6{648>;QnnH167tfv3G;Fib-6Hy@$*8Xn^+!LZTh*Fxx`Y+Nm0ziu_XFL`lNkICQ04cTVgOf5RXfh1Uc3O zq;n(oo?h~!a-I;eM#+iQG@}J?!_e9H{7k9~$rtZ->dauis$NU1Zp~0CGb|G(vn^9f z2+0@scH&HDzPsLE>}BmZX0~lsN+nDMMx{+fDk&@p zCaEn+3L^{y2BQr_YAb9DW~*&W$|TGL#-zX-QUAnGMmY+Za^^eiG>b7oGw4A)61 zeG09fy`NGF6ly)Y>{7r)>_mOlP^Q{1KL_1cvWJ??OooHw+el*gu|2*WCa!Lkx+pR$ zFw0IUGXKF_K{{=_F9P-fgMn+nqJh4F;DNP)uX=?wJ-_T2{^05&RktmE?M`I*=lYaA zcn9GK?TO~e<5}Daiw8p5kzrXlD?g6SmbqH zQuL?8ac=4&a>XFX(azw@O#w?rAj>I&8_Pp(mp6yf%h@3#$-=E+m#EdnO$!9pK@dM< zNdbp}K-7Rw&(R|?Tn#aQBz#nX-5xA6V|3Z@!n3)@wCs1q?VX}NUMSnc1;o}atCrh% zx~MZ#N+ue=3vh{Ni(^-Z$4jwX{nLV|Ko3PBp4=f(W`toCCNI=TA*$H zqPF8Bx4F+aj1|1BJ8 zr5pJ_92ZuJ7-~FW`n5d4zFhmI_~Ue2baQo2cIS30``vY?baM#TUEpojbu+vR0ktll z@a6?VEbpwXXjcqE9O-8qNS|GZo=dLZBHz1X-j#=)eGK9{SAw@{`!9!$1yi*MS(P|g z9bM8v!(P8k{Q8+m`ddYw?S_p2eT}>i|+?4X64DK^LOF99==SgIO?}eAMNWR?XZ_b-h34p&&&zyUKJO47KX-IT0AGjhm zUtUM>m_FcbtN~O9hZ4i3WJVs_lPn@Og08AhX?G_AAn(gnn@u54pSjFWUa9x|W3rTh zG2xd)PwBbuljr#&PESucPkzr~6nJDGP1oS8@s7Hjc6sHI2NP2D)@6&a$;#<@D=ujR znK#jX${`&Ju2PaUwwuA3$a=@Ee#&>uPb{M1ZEeP<`1_y~jhh`$EMSd)STm)S>4@xp z-(14>=SwvznnzA@bNyUZuFw$*fue!iy8XKEriW9)un%J9xR1l(+xr=c|I|kB2F}^x zXI;=AusQ5%d0TwSw?1?tVgbCh@wd&DFCGpbG%i+MtM>mLkZ9Lb$JI%yz3EfjlHZ^1 z3~g&dfB*F+l?1zMM3-H;Ex{%*DD<0xt%3G*(Xbq{O^=F^OtUD0dY>?RWKM8Qc>vnp z>3K~S*R7w3AO$bVoy`t}VBWrRJjhahC9MFriP$zSYjWtPCA5{ek zLGp4GG-?x2YaWQC2F&@42l_M#igH0+Rz%!LmjR^Cg5Qar0Ca1>TYU0hcpq@2p`-^|B6%ED@qR7NFE-7s~w?;-0b>b0u*FMbX`G-B9?s& zcdkYR&;93x>u^)YdDrG*@)MQS_~U0zx)UlEmU9*fQCTo2L}*t-ZFTc@Tu6e1cN5ZF z?lP0*{f-nFUx$-U;@1LwaC*6Xskjl*S%(@Zf&wAz`O$kO;*vD(0oBrm`Cj4R= zPa6B;S!4<>Ycio@0x#vfN{U2FynuD~UIWwBm+5kanGPoLU7Py9$!yxIUm?k8M?-iY zV$o~ zqzmq*7wP)}v!6R}Px>oABEg$sCo z*|{^vj{M*DsT#wDNtVY#HI4EhJ;XXRJ(Zz4T1jxW$g?J)J#0Dx>(Nya$M9C8(vt{^u#G(oJf0K{HWwV4>(%0IJW+J zot*}t*%nSO8j2Hd+cis;#5I3Ig^U?zjTMpAa;@hTQ2?Or)oz4u( zLAMf%#ZE@vhc5va)OloWrzawB!?hJIw41FvR}^SUBUz(4MBEH5^?MuV`8LeLp0?L^ zlt|~t#xt?n{M{eRTB;njK3Q%TKsyN^syCTa5+fSG_416e{KvDu{bBSYU9o`Y#r#f< znTSL8ZJCkg`i=Sfrf}}ne561EUV?w`Pe(-)l6y6Q8{)I>6}9V~4gu5$K=amYZ;Fy2 z!0YKqF@!#Ed1WiaDO~FHu{Q_M>9$EZosBlJGgZF)r)uMvcnSAKD+Ey6^=y^Es@%3! z>#!K4)O<7^;i;6}ezkRqEWDqc$N=zmlGV|kcNjgo$X`?OU40)j%q8=xed@j*hbjei zA9OI0bTGd)uQUigH{g^okd0>Y9XG979X&8vf6Q*?AExMjl0P_HVJJJcz8${pge%Fx z3$#mGWw2h_J>-ArHonn@ZWY##7d6DI8|$cDejLm{e?}aAc64{MY#DV3nlfZ!_u7+U%-;rqzyId;edd zH@+7ELoFLze$U5J(MV>{4(1a{R|fA_TlNpV592&d`S%5UTRUaUb#L1&U2}Jtv11xp`-3$^wD&3F|}<<2`TI!=Y$&+ z#-h>U^5XIm$M5l8vUvgPPVNu+Ld8roqdY7Xz0)<>5C0vCha(gGPfY&*W#FP#o~RAv zge z@p8G`h%iU$yZc*7h@G9C^h|fg=C~hgIPPMey1_M_G&9%N?L1ky$VhWa=#77KW`hhF zD%bq>a3iW1=ns*T`wNhaxonHGXNWB@Al7saD@~D1jQQ3zFY=TU2 zD%ycRbDHbi>TWeFCGC^R`DkEr^o4U-8M#sFBf}ooK;6?5!Dh6c5BF+Snz7hX^&K}m znO2r?JTBQMw)7=x*vSusf4>!HX{9NJwN9r#_g*tzBNF`hJ>y zOIworg*t-#NE0Skn_-ix*3Y^Q>8vgr4Y^hKluBvK_Fge+0CXYwEtm#y_ONa6pqSU6 z@19v`?R3>`mm10! z!Z=dNuvD3eSeMp8{3P4#(nO&*Xn5XSd2EFLN2Mb7)G`zi27{=RX$Nnx8O`o%9j3wJ z$Q5Dz`Lzdw#u_$XUxNyv*SQvZb7;^;=j6`U584J($aFP=mTNA@d+(+s2g5#v6|Pq_ zWOuHvWPYq$qdJSe?|VUfBWi_a+1%tKF_rU^d7_Qy%>wJ4k?(>d__^N>=N!FU3qAk* zsFU67;z-GiMi3LHqHD(vh@w$)Wx_IOieu&tX0k@1rXMA+XjOQZkO`~Ksq3Z4wYY4L zVo=*ZlPu@XH2mVS{aJ)q_Z2OJ{k9AfGLjimS>o64soU3P&XCfIX-963&QlbiQfc|o zLa07TXfWpRI>u`&s6nToqlitzn$Q~~-xA8yAicE=OV^M{Z7|0=n;8!*r2_s#ttma` zQpQ$&>Aue>`85eOnQ0xi$P%&rXoKKUI^E@^qF55G4EvJ(6V4MvBW3>$O9v~v`6y#m z&ASc*!SJ}$ToZ%6TqENok?6HhWzrp-78${#@Bial4U^3emq?_&)g_Ml@pk$`wXd^* zR5mehq4Ze5KRr+u7TIA(wznOcMT{yaYm%*@H9zam5vXYn8Anp~h$K zJ1v&mp<0)F83g7?_KMRIk2;Bu81Cm_^0jEtvV3@G6YOeg{8I)yB?+IrpHGQtTF=1( zRU)vR3sr)jwP#K8ee2X)}fRw->-){-h6Iu z-M0i>c><$iiQvg+X_o`oO>Nm=XoYNfD%LG;INVY^y~(cKvuLM6{)JDMj6TItbD@Lh zf?o7*NtPZ2xVtTI(}@-!vpgMmi#q$%Rj`2}@n}SwZY#oFcs8i+PwhKj6yj==_YhiC zsDgRH97_H@m_hR}B_}zjF1WG|cNJ3A%di~^Aixjj4*U(Z4#|Azzm;)?AFQwBDCf z*e@?KppDyoC)XD-v=o#qaUvC6y?zwN`w1a(ntb2@KcPW(kz1o~xQOg4{ODkX!;jdY zL2CRxCTj3XcmU%L-ieaAo$><+cYe8-;jsD1nWEIqks5t54Gi*;X_~_m(mY+{uX8rS z`m+}rv}C~Gtfo4ON{QO4qGn~U7YgVJG7*j=X6pSEM2wKE&<(vA_FRn1l_G*sv`N?2 z9k6?o@3M*{e&9ENWm^h?B-xA=#cL-Eue1#066z*8n!xcY`VBVzSDXapfKIRUXn{sr z3@wvRf1oD#b5O9^ADX1Q>+Ij{L5;&8Cs%UeCx>crFD#Q7k0{abKk*8^yd`v>?<65! zSt~BX8@>E?ZF#$i@e)Xbzlr5NP%Mi6@UdB58bGPG^Wh*nI>%g~FeclOS1 zmzwpZ<5<QF%OqKBySC_?l6mUBY+)IfFnJ_G$9 zRupPD14fW#CJ7*x-fINs)j6Qq);0Fx_;j1|8bKGKU@;7m@lCOy+hwBpK+D&JKek2) zomaO4dQ)0mczLsG&oxA84b-lR?4z`!CATp2gAHbuVY77ZU7|X|+?r##FCE2YV0iDV zIO!$ohzTj3sA+ZWgMAkFXh6#FCOs1cNsH-f^PQ8HR=xI}T9G5SR@c`#fNGAVP8O3R z66v2Un4PbI5=MG5R~Z4omQDO)Y@KpLFg6(2(VYfw2J#Nqrkf^adE|cR``dSQz83H$g`+$YjBn+%G1+!oK?Xp%YDFM$) zDKf>XF%V>R6yOcN#+n*H-t`g@24^ut$~!*dD#7`8&;ECo@Azs|AY|Jur9Rn%VW>lr zh)FS7g4~C6-_Bo>clX+^Fg&3KN*=>TDm@<7{Si3Y$j-mEThP}gID(Ib40P2J1Fp}w zK~hJ;?c=iHo^C3A1zs^=fUaq5XmbjBox$UwK8wB`yx3G%Pp%ZU;s+HvUSNoCOcXNN zTBZ!zIUe3`-rgJF5_Z>`PK5o6`)f;>2NLNmx$nHehF*_}k%urXTS`{c!S zh!42QV(Iv()pptc!+43_jkX@0&mNyX5c}7aUl*d#(1f0}BYHLIVZ$lpkv}B;!Yc`m%vMNATcB*v}wbkS2h{^7krYzZ|Ub}eI4%U9O<1` za^op@?Rc_jcwpG+EdQUzu7eb6(f`u?1;3t#V$pSdJ|8lnpR10)Wc?5CI88<@J^T8E za6Zm9r{2kSJpmtYN67aVe;=PNK!E4HB_k2g#~YsJ&?evmi(P4E2U_TVnXzxhvF!hs TdA%3|eX;#Ygz%*R@ymY!yY16x literal 0 HcmV?d00001 diff --git a/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_an.c b/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_an.c index 63cface801..0898961e95 100644 --- a/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_an.c +++ b/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_an.c @@ -102,7 +102,6 @@ __FBSDID("$FreeBSD$"); #include #include -#include #include #include #include @@ -140,9 +139,11 @@ static void an_reset(struct an_softc *); static int an_init_mpi350_desc(struct an_softc *); static int an_ioctl(struct ifnet *, u_long, caddr_t); static void an_init(void *); +static void an_init_locked(struct an_softc *); static int an_init_tx_ring(struct an_softc *); static void an_start(struct ifnet *); -static void an_watchdog(struct ifnet *); +static void an_start_locked(struct ifnet *); +static void an_watchdog(struct an_softc *); static void an_rxeof(struct an_softc *); static void an_txeof(struct an_softc *, int); @@ -314,7 +315,7 @@ an_pci_probe(device_t dev) struct an_softc *sc = device_get_softc(dev); mtx_init(&sc->an_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, - MTX_DEF | MTX_RECURSE); + MTX_DEF); return(0); } @@ -347,10 +348,6 @@ an_probe(device_t dev) * to be able to issue commands and call some of the * other routines. */ - sc->an_bhandle = rman_get_bushandle(sc->port_res); - sc->an_btag = rman_get_bustag(sc->port_res); - sc->an_unit = device_get_unit(dev); - ssid.an_len = sizeof(ssid); ssid.an_type = AN_RID_SSIDLIST; @@ -360,7 +357,7 @@ an_probe(device_t dev) CSR_WRITE_2(sc, AN_EVENT_ACK(sc->mpi350), 0xFFFF); mtx_init(&sc->an_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, - MTX_DEF | MTX_RECURSE); + MTX_DEF); AN_LOCK(sc); an_reset(sc); @@ -600,8 +597,7 @@ an_init_mpi350_desc(struct an_softc *sc) cmd_struct.an_parm1 = AN_RX_DESC_OFFSET; cmd_struct.an_parm2 = AN_MAX_RX_DESC; if (an_cmd_struct(sc, &cmd_struct, &reply)) { - printf("an%d: failed to allocate RX descriptor\n", - sc->an_unit); + if_printf(sc->an_ifp, "failed to allocate RX descriptor\n"); return(EIO); } @@ -629,8 +625,7 @@ an_init_mpi350_desc(struct an_softc *sc) cmd_struct.an_parm1 = AN_TX_DESC_OFFSET; cmd_struct.an_parm2 = AN_MAX_TX_DESC; if (an_cmd_struct(sc, &cmd_struct, &reply)) { - printf("an%d: failed to allocate TX descriptor\n", - sc->an_unit); + if_printf(sc->an_ifp, "failed to allocate TX descriptor\n"); return(EIO); } @@ -659,8 +654,7 @@ an_init_mpi350_desc(struct an_softc *sc) cmd_struct.an_parm1 = AN_HOST_DESC_OFFSET; cmd_struct.an_parm2 = 1; if (an_cmd_struct(sc, &cmd_struct, &reply)) { - printf("an%d: failed to allocate host descriptor\n", - sc->an_unit); + if_printf(sc->an_ifp, "failed to allocate host descriptor\n"); return(EIO); } @@ -678,7 +672,7 @@ an_init_mpi350_desc(struct an_softc *sc) } int -an_attach(struct an_softc *sc, int unit, int flags) +an_attach(struct an_softc *sc, int flags) { struct ifnet *ifp; int error = EIO; @@ -687,7 +681,7 @@ an_attach(struct an_softc *sc, int unit, int flags) ifp = sc->an_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { - printf("an%d: can not if_alloc()\n", sc->an_unit); + device_printf(sc->an_dev, "can not if_alloc()\n"); goto fail; } @@ -708,7 +702,7 @@ an_attach(struct an_softc *sc, int unit, int flags) /* Load factory config */ if (an_cmd(sc, AN_CMD_READCFG, 0)) { - printf("an%d: failed to load config data\n", sc->an_unit); + device_printf(sc->an_dev, "failed to load config data\n"); goto fail; } @@ -716,7 +710,7 @@ an_attach(struct an_softc *sc, int unit, int flags) sc->an_config.an_type = AN_RID_GENCONFIG; sc->an_config.an_len = sizeof(struct an_ltv_genconfig); if (an_read_record(sc, (struct an_ltv_gen *)&sc->an_config)) { - printf("an%d: read record failed\n", sc->an_unit); + device_printf(sc->an_dev, "read record failed\n"); goto fail; } @@ -724,7 +718,7 @@ an_attach(struct an_softc *sc, int unit, int flags) sc->an_caps.an_type = AN_RID_CAPABILITIES; sc->an_caps.an_len = sizeof(struct an_ltv_caps); if (an_read_record(sc, (struct an_ltv_gen *)&sc->an_caps)) { - printf("an%d: read record failed\n", sc->an_unit); + device_printf(sc->an_dev, "read record failed\n"); goto fail; } @@ -732,7 +726,7 @@ an_attach(struct an_softc *sc, int unit, int flags) sc->an_ssidlist.an_type = AN_RID_SSIDLIST; sc->an_ssidlist.an_len = sizeof(struct an_ltv_ssidlist_new); if (an_read_record(sc, (struct an_ltv_gen *)&sc->an_ssidlist)) { - printf("an%d: read record failed\n", sc->an_unit); + device_printf(sc->an_dev, "read record failed\n"); goto fail; } @@ -740,7 +734,7 @@ an_attach(struct an_softc *sc, int unit, int flags) sc->an_aplist.an_type = AN_RID_APLIST; sc->an_aplist.an_len = sizeof(struct an_ltv_aplist); if (an_read_record(sc, (struct an_ltv_gen *)&sc->an_aplist)) { - printf("an%d: read record failed\n", sc->an_unit); + device_printf(sc->an_dev, "read record failed\n"); goto fail; } @@ -751,26 +745,25 @@ an_attach(struct an_softc *sc, int unit, int flags) sc->an_rssimap.an_type = AN_RID_RSSI_MAP; sc->an_rssimap.an_len = sizeof(struct an_ltv_rssi_map); if (an_read_record(sc, (struct an_ltv_gen *)&sc->an_rssimap)) { - printf("an%d: unable to get RSSI <-> dBM map\n", sc->an_unit); + device_printf(sc->an_dev, + "unable to get RSSI <-> dBM map\n"); } else { - printf("an%d: got RSSI <-> dBM map\n", sc->an_unit); + device_printf(sc->an_dev, "got RSSI <-> dBM map\n"); sc->an_have_rssimap = 1; } } else { - printf("an%d: no RSSI <-> dBM map\n", sc->an_unit); + device_printf(sc->an_dev, "no RSSI <-> dBM map\n"); } #endif AN_UNLOCK(sc); ifp->if_softc = sc; - sc->an_unit = unit; if_initname(ifp, device_get_name(sc->an_dev), device_get_unit(sc->an_dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = an_ioctl; ifp->if_start = an_start; - ifp->if_watchdog = an_watchdog; ifp->if_init = an_init; ifp->if_baudrate = 10000000; IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); @@ -897,7 +890,7 @@ an_rxeof(struct an_softc *sc) */ if (!(sc->an_monitor & AN_MONITOR_INCLUDE_BEACON) && - (rx_frame.an_frame_ctl & + (rx_frame.an_frame_ctl & IEEE80211_FC0_SUBTYPE_BEACON)) { return; } @@ -907,9 +900,9 @@ an_rxeof(struct an_softc *sc) + sizeof(rx_frame); /* Check for insane frame length */ if (len > sizeof(sc->buf_802_11)) { - printf("an%d: oversized packet " + if_printf(ifp, "oversized packet " "received (%d, %d)\n", - sc->an_unit, len, MCLBYTES); + len, MCLBYTES); ifp->if_ierrors++; return; } @@ -933,9 +926,9 @@ an_rxeof(struct an_softc *sc) + ieee80211_header_len; /* Check for insane frame length */ if (len > sizeof(sc->buf_802_11)) { - printf("an%d: oversized packet " + if_printf(ifp, "oversized packet " "received (%d, %d)\n", - sc->an_unit, len, MCLBYTES); + len, MCLBYTES); ifp->if_ierrors++; return; } @@ -993,9 +986,9 @@ an_rxeof(struct an_softc *sc) len = rx_frame_802_3.an_rx_802_3_payload_len; if (len > sizeof(sc->buf_802_11)) { m_freem(m); - printf("an%d: oversized packet " + if_printf(ifp, "oversized packet " "received (%d, %d)\n", - sc->an_unit, len, MCLBYTES); + len, MCLBYTES); ifp->if_ierrors++; return; } @@ -1073,9 +1066,9 @@ an_rxeof(struct an_softc *sc) len = an_rx_desc.an_len + 12; if (len > MCLBYTES) { m_freem(m); - printf("an%d: oversized packet " + if_printf(ifp, "oversized packet " "received (%d, %d)\n", - sc->an_unit, len, MCLBYTES); + len, MCLBYTES); ifp->if_ierrors++; return; } @@ -1116,9 +1109,8 @@ an_rxeof(struct an_softc *sc) ((u_int32_t *)(void *)&an_rx_desc)[i]); } else { - printf("an%d: Didn't get valid RX packet " + if_printf(ifp, "Didn't get valid RX packet " "%x %x %d\n", - sc->an_unit, an_rx_desc.an_done, an_rx_desc.an_valid, an_rx_desc.an_len); } @@ -1135,7 +1127,7 @@ an_txeof(struct an_softc *sc, int status) AN_LOCK_ASSERT(sc); ifp = sc->an_ifp; - ifp->if_timer = 0; + sc->an_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; if (!sc->mpi350) { @@ -1188,6 +1180,8 @@ an_stats_update(void *xsc) sc = xsc; AN_LOCK_ASSERT(sc); ifp = sc->an_ifp; + if (sc->an_timer > 0 && --sc->an_timer == 0) + an_watchdog(sc); sc->an_status.an_type = AN_RID_STATUS; sc->an_status.an_len = sizeof(struct an_ltv_status); @@ -1233,10 +1227,8 @@ an_intr(void *xsc) ifp = sc->an_ifp; -#if !defined(__HAIKU__) /* Disable interrupts. */ CSR_WRITE_2(sc, AN_INT_EN(sc->mpi350), 0); -#endif status = CSR_READ_2(sc, AN_EVENT_STAT(sc->mpi350)); CSR_WRITE_2(sc, AN_EVENT_ACK(sc->mpi350), ~AN_INTRS(sc->mpi350)); @@ -1281,7 +1273,7 @@ an_intr(void *xsc) CSR_WRITE_2(sc, AN_INT_EN(sc->mpi350), AN_INTRS(sc->mpi350)); if ((ifp->if_flags & IFF_UP) && !IFQ_DRV_IS_EMPTY(&ifp->if_snd)) - an_start(ifp); + an_start_locked(ifp); AN_UNLOCK(sc); @@ -1395,7 +1387,7 @@ an_reset(struct an_softc *sc) an_cmd(sc, AN_CMD_NOOP2, 0); if (an_cmd(sc, AN_CMD_FORCE_SYNCLOSS, 0) == ETIMEDOUT) - printf("an%d: reset failed\n", sc->an_unit); + if_printf(sc->an_ifp, "reset failed\n"); an_cmd(sc, AN_CMD_DISABLE, 0); @@ -1412,6 +1404,7 @@ an_read_record(struct an_softc *sc, struct an_ltv_gen *ltv) struct an_card_rid_desc an_rid_desc; struct an_command cmd; struct an_reply reply; + struct ifnet *ifp; u_int16_t *ptr; u_int8_t *ptr2; int i, len; @@ -1420,16 +1413,17 @@ an_read_record(struct an_softc *sc, struct an_ltv_gen *ltv) if (ltv->an_len < 4 || ltv->an_type == 0) return(EINVAL); + ifp = sc->an_ifp; if (!sc->mpi350){ /* Tell the NIC to enter record read mode. */ if (an_cmd(sc, AN_CMD_ACCESS|AN_ACCESS_READ, ltv->an_type)) { - printf("an%d: RID access failed\n", sc->an_unit); + if_printf(ifp, "RID access failed\n"); return(EIO); } /* Seek to the record. */ if (an_seek(sc, ltv->an_type, 0, AN_BAP1)) { - printf("an%d: seek to record failed\n", sc->an_unit); + if_printf(ifp, "seek to record failed\n"); return(EIO); } @@ -1441,8 +1435,8 @@ an_read_record(struct an_softc *sc, struct an_ltv_gen *ltv) */ len = CSR_READ_2(sc, AN_DATA1); if (len > (ltv->an_len - 2)) { - printf("an%d: record length mismatch -- expected %d, " - "got %d for Rid %x\n", sc->an_unit, + if_printf(ifp, "record length mismatch -- expected %d, " + "got %d for Rid %x\n", ltv->an_len - 2, len, ltv->an_type); len = ltv->an_len - 2; } else { @@ -1478,8 +1472,8 @@ an_read_record(struct an_softc *sc, struct an_ltv_gen *ltv) if (an_cmd_struct(sc, &cmd, &reply) || reply.an_status & AN_CMD_QUAL_MASK) { - printf("an%d: failed to read RID %x %x %x %x %x, %d\n", - sc->an_unit, ltv->an_type, + if_printf(ifp, "failed to read RID %x %x %x %x %x, %d\n", + ltv->an_type, reply.an_status, reply.an_resp0, reply.an_resp1, @@ -1495,8 +1489,8 @@ an_read_record(struct an_softc *sc, struct an_ltv_gen *ltv) len = an_rid_desc.an_len; if (len > (ltv->an_len - 2)) { - printf("an%d: record length mismatch -- expected %d, " - "got %d for Rid %x\n", sc->an_unit, + if_printf(ifp, "record length mismatch -- expected %d, " + "got %d for Rid %x\n", ltv->an_len - 2, len, ltv->an_type); len = ltv->an_len - 2; } else { @@ -1588,8 +1582,9 @@ an_write_record(struct an_softc *sc, struct an_ltv_gen *ltv) DELAY(100000); if ((i = an_cmd_struct(sc, &cmd, &reply))) { - printf("an%d: failed to write RID 1 %x %x %x %x %x, %d\n", - sc->an_unit, ltv->an_type, + if_printf(sc->an_ifp, + "failed to write RID 1 %x %x %x %x %x, %d\n", + ltv->an_type, reply.an_status, reply.an_resp0, reply.an_resp1, @@ -1600,8 +1595,9 @@ an_write_record(struct an_softc *sc, struct an_ltv_gen *ltv) if (reply.an_status & AN_CMD_QUAL_MASK) { - printf("an%d: failed to write RID 2 %x %x %x %x %x, %d\n", - sc->an_unit, ltv->an_type, + if_printf(sc->an_ifp, + "failed to write RID 2 %x %x %x %x %x, %d\n", + ltv->an_type, reply.an_status, reply.an_resp0, reply.an_resp1, @@ -1625,11 +1621,11 @@ an_dump_record(struct an_softc *sc, struct an_ltv_gen *ltv, char *string) char buf[17], temp; len = ltv->an_len - 4; - printf("an%d: RID %4x, Length %4d, Mode %s\n", - sc->an_unit, ltv->an_type, ltv->an_len - 4, string); + if_printf(sc->an_ifp, "RID %4x, Length %4d, Mode %s\n", + ltv->an_type, ltv->an_len - 4, string); if (an_dump == 1 || (an_dump == ltv->an_type)) { - printf("an%d:\t", sc->an_unit); + if_printf(sc->an_ifp, "\t"); bzero(buf,sizeof(buf)); ptr2 = (u_int8_t *)<v->an_val; @@ -1644,7 +1640,7 @@ an_dump_record(struct an_softc *sc, struct an_ltv_gen *ltv, char *string) if (++count == 16) { count = 0; printf("%s\n",buf); - printf("an%d:\t", sc->an_unit); + if_printf(sc->an_ifp, "\t"); bzero(buf,sizeof(buf)); } } @@ -1671,7 +1667,7 @@ an_seek(struct an_softc *sc, int id, int off, int chan) offreg = AN_OFF1; break; default: - printf("an%d: invalid data path: %x\n", sc->an_unit, chan); + if_printf(sc->an_ifp, "invalid data path: %x\n", chan); return(EIO); } @@ -1745,8 +1741,8 @@ an_alloc_nicmem(struct an_softc *sc, int len, int *id) int i; if (an_cmd(sc, AN_CMD_ALLOC_MEM, len)) { - printf("an%d: failed to allocate %d bytes on NIC\n", - sc->an_unit, len); + if_printf(sc->an_ifp, "failed to allocate %d bytes on NIC\n", + len); return(ENOMEM); } @@ -1828,9 +1824,7 @@ an_setdef(struct an_softc *sc, struct an_req *areq) case AN_RID_WEP_PERM: case AN_RID_LEAPUSERNAME: case AN_RID_LEAPPASSWORD: - AN_UNLOCK(sc); - an_init(sc); - AN_LOCK(sc); + an_init_locked(sc); /* Disable the MAC. */ an_cmd(sc, AN_CMD_DISABLE, 0); @@ -1865,17 +1859,14 @@ an_setdef(struct an_softc *sc, struct an_req *areq) } break; default: - printf("an%d: unknown RID: %x\n", sc->an_unit, areq->an_type); + if_printf(ifp, "unknown RID: %x\n", areq->an_type); return; } /* Reinitialize the card. */ - if (ifp->if_flags) { - AN_UNLOCK(sc); - an_init(sc); - AN_LOCK(sc); - } + if (ifp->if_flags) + an_init_locked(sc); return; } @@ -1893,11 +1884,8 @@ an_promisc(struct an_softc *sc, int promisc) if (sc->mpi350) an_init_mpi350_desc(sc); } - if (sc->an_monitor || sc->an_was_monitor) { - AN_UNLOCK(sc); - an_init(sc); - AN_LOCK(sc); - } + if (sc->an_monitor || sc->an_was_monitor) + an_init_locked(sc); sc->an_was_monitor = sc->an_monitor; an_cmd(sc, AN_CMD_SET_MODE, promisc ? 0xffff : 0); @@ -1951,20 +1939,14 @@ an_ioctl(struct ifnet *ifp, u_long command, caddr_t data) !(ifp->if_flags & IFF_PROMISC) && sc->an_if_flags & IFF_PROMISC) { an_promisc(sc, 0); - } else { - AN_UNLOCK(sc); - an_init(sc); - AN_LOCK(sc); - } + } else + an_init_locked(sc); } else { - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - AN_UNLOCK(sc); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) an_stop(sc); - AN_LOCK(sc); - } } - AN_UNLOCK(sc); sc->an_if_flags = ifp->if_flags; + AN_UNLOCK(sc); error = 0; break; case SIOCSIFMEDIA: @@ -2488,7 +2470,6 @@ an_ioctl(struct ifnet *ifp, u_long command, caddr_t data) AN_UNLOCK(sc); break; } - AN_UNLOCK(sc); if (ireq->i_val == 4) { config->an_home_product |= AN_HOME_NETWORK; ireq->i_val = 0; @@ -2500,10 +2481,9 @@ an_ioctl(struct ifnet *ifp, u_long command, caddr_t data) = config->an_home_product; /* update configuration */ - an_init(sc); + an_init_locked(sc); bzero(&sc->areq, sizeof(struct an_ltv_key)); - AN_LOCK(sc); sc->areq.an_len = sizeof(struct an_ltv_key); sc->areq.an_type = AN_RID_WEP_PERM; key->kindex = 0xffff; @@ -2586,6 +2566,9 @@ an_ioctl(struct ifnet *ifp, u_long command, caddr_t data) an_setdef(sc, &sc->areq); AN_UNLOCK(sc); break; + default: + AN_UNLOCK(sc); + break; } /* @@ -2635,14 +2618,21 @@ static void an_init(void *xsc) { struct an_softc *sc = xsc; - struct ifnet *ifp = sc->an_ifp; AN_LOCK(sc); + an_init_locked(sc); + AN_UNLOCK(sc); +} - if (sc->an_gone) { - AN_UNLOCK(sc); +static void +an_init_locked(struct an_softc *sc) +{ + struct ifnet *ifp; + + AN_LOCK_ASSERT(sc); + ifp = sc->an_ifp; + if (sc->an_gone) return; - } if (ifp->if_drv_flags & IFF_DRV_RUNNING) an_stop(sc); @@ -2655,9 +2645,7 @@ an_init(void *xsc) if (sc->mpi350) an_init_mpi350_desc(sc); if (an_init_tx_ring(sc)) { - printf("an%d: tx buffer allocation " - "failed\n", sc->an_unit); - AN_UNLOCK(sc); + if_printf(ifp, "tx buffer allocation failed\n"); return; } } @@ -2697,8 +2685,7 @@ an_init(void *xsc) sc->an_ssidlist.an_type = AN_RID_SSIDLIST; sc->an_ssidlist.an_len = sizeof(struct an_ltv_ssidlist_new); if (an_write_record(sc, (struct an_ltv_gen *)&sc->an_ssidlist)) { - printf("an%d: failed to set ssid list\n", sc->an_unit); - AN_UNLOCK(sc); + if_printf(ifp, "failed to set ssid list\n"); return; } @@ -2706,8 +2693,7 @@ an_init(void *xsc) sc->an_aplist.an_type = AN_RID_APLIST; sc->an_aplist.an_len = sizeof(struct an_ltv_aplist); if (an_write_record(sc, (struct an_ltv_gen *)&sc->an_aplist)) { - printf("an%d: failed to set AP list\n", sc->an_unit); - AN_UNLOCK(sc); + if_printf(ifp, "failed to set AP list\n"); return; } @@ -2715,15 +2701,13 @@ an_init(void *xsc) sc->an_config.an_len = sizeof(struct an_ltv_genconfig); sc->an_config.an_type = AN_RID_GENCONFIG; if (an_write_record(sc, (struct an_ltv_gen *)&sc->an_config)) { - printf("an%d: failed to set configuration\n", sc->an_unit); - AN_UNLOCK(sc); + if_printf(ifp, "failed to set configuration\n"); return; } /* Enable the MAC */ if (an_cmd(sc, AN_CMD_ENABLE, 0)) { - printf("an%d: failed to enable MAC\n", sc->an_unit); - AN_UNLOCK(sc); + if_printf(ifp, "failed to enable MAC\n"); return; } @@ -2737,13 +2721,23 @@ an_init(void *xsc) ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; callout_reset(&sc->an_stat_ch, hz, an_stats_update, sc); - AN_UNLOCK(sc); return; } static void an_start(struct ifnet *ifp) +{ + struct an_softc *sc; + + sc = ifp->if_softc; + AN_LOCK(sc); + an_start_locked(ifp); + AN_UNLOCK(sc); +} + +static void +an_start_locked(struct ifnet *ifp) { struct an_softc *sc; struct mbuf *m0 = NULL; @@ -2756,6 +2750,7 @@ an_start(struct ifnet *ifp) sc = ifp->if_softc; + AN_LOCK_ASSERT(sc); if (sc->an_gone) return; @@ -2778,7 +2773,6 @@ an_start(struct ifnet *ifp) idx = sc->an_rdata.an_tx_prod; - AN_LOCK(sc); if (!sc->mpi350) { bzero((char *)&tx_frame_802_3, sizeof(tx_frame_802_3)); @@ -2805,7 +2799,7 @@ an_start(struct ifnet *ifp) tx_frame_802_3.an_tx_802_3_payload_len, (caddr_t)&sc->an_txbuf); - txcontrol = AN_TXCTL_8023; + txcontrol = AN_TXCTL_8023 | AN_TXCTL_HW(sc->mpi350); /* write the txcontrol only */ an_write_data(sc, id, 0x08, (caddr_t)&txcontrol, sizeof(txcontrol)); @@ -2829,14 +2823,14 @@ an_start(struct ifnet *ifp) sc->an_rdata.an_tx_ring[idx] = id; if (an_cmd(sc, AN_CMD_TX, id)) - printf("an%d: xmit failed\n", sc->an_unit); + if_printf(ifp, "xmit failed\n"); AN_INC(idx, AN_TX_RING_CNT); /* * Set a timeout in case the chip goes out to lunch. */ - ifp->if_timer = 5; + sc->an_timer = 5; } } else { /* MPI-350 */ /* Disable interrupts. */ @@ -2868,7 +2862,7 @@ an_start(struct ifnet *ifp) tx_frame_802_3.an_tx_802_3_payload_len, (caddr_t)&sc->an_txbuf); - txcontrol = AN_TXCTL_8023; + txcontrol = AN_TXCTL_8023 | AN_TXCTL_HW(sc->mpi350); /* write the txcontrol only */ bcopy((caddr_t)&txcontrol, &buf[0x08], sizeof(txcontrol)); @@ -2890,7 +2884,7 @@ an_start(struct ifnet *ifp) tx_frame_802_3.an_tx_802_3_payload_len; an_tx_desc.an_phys = sc->an_tx_buffer[idx].an_dma_paddr; - for (i = 0; i < sizeof(an_tx_desc) / 4 ; i++) { + for (i = sizeof(an_tx_desc) / 4 - 1; i >= 0; i--) { CSR_MEM_AUX_WRITE_4(sc, AN_TX_DESC_OFFSET /* zero for now */ + (0 * sizeof(an_tx_desc)) @@ -2913,13 +2907,12 @@ an_start(struct ifnet *ifp) /* * Set a timeout in case the chip goes out to lunch. */ - ifp->if_timer = 5; + sc->an_timer = 5; } /* Re-enable interrupts. */ CSR_WRITE_2(sc, AN_INT_EN(sc->mpi350), AN_INTRS(sc->mpi350)); } - AN_UNLOCK(sc); if (m0 != NULL) ifp->if_drv_flags |= IFF_DRV_OACTIVE; @@ -2935,12 +2928,10 @@ an_stop(struct an_softc *sc) struct ifnet *ifp; int i; - AN_LOCK(sc); + AN_LOCK_ASSERT(sc); - if (sc->an_gone) { - AN_UNLOCK(sc); + if (sc->an_gone) return; - } ifp = sc->an_ifp; @@ -2959,36 +2950,27 @@ an_stop(struct an_softc *sc) free(sc->an_flash_buffer, M_DEVBUF); sc->an_flash_buffer = NULL; } - - AN_UNLOCK(sc); - - return; } static void -an_watchdog(struct ifnet *ifp) +an_watchdog(struct an_softc *sc) { - struct an_softc *sc; + struct ifnet *ifp; - sc = ifp->if_softc; - AN_LOCK(sc); + AN_LOCK_ASSERT(sc); - if (sc->an_gone) { - AN_UNLOCK(sc); + if (sc->an_gone) return; - } - printf("an%d: device timeout\n", sc->an_unit); + ifp = sc->an_ifp; + if_printf(ifp, "device timeout\n"); an_reset(sc); if (sc->mpi350) an_init_mpi350_desc(sc); - AN_UNLOCK(sc); - an_init(sc); + an_init_locked(sc); ifp->if_oerrors++; - - return; } int @@ -2997,10 +2979,12 @@ an_shutdown(device_t dev) struct an_softc *sc; sc = device_get_softc(dev); + AN_LOCK(sc); an_stop(sc); sc->an_gone = 1; + AN_UNLOCK(sc); - return 0; + return (0); } void @@ -3018,7 +3002,7 @@ an_resume(device_t dev) an_reset(sc); if (sc->mpi350) an_init_mpi350_desc(sc); - an_init(sc); + an_init_locked(sc); /* Recovery temporary keys */ for (i = 0; i < 4; i++) { @@ -3030,7 +3014,7 @@ an_resume(device_t dev) } if (ifp->if_flags & IFF_UP) - an_start(ifp); + an_start_locked(ifp); AN_UNLOCK(sc); return; @@ -3129,7 +3113,7 @@ an_cache_store(struct an_softc *sc, struct ether_header *eh, struct mbuf *m, } #ifdef SIGDEBUG - printf("an: q value %x (MSB=0x%x, LSB=0x%x) \n", + if_printf(sc->an_ifp, "q value %x (MSB=0x%x, LSB=0x%x) \n", rx_rssi & 0xffff, rx_rssi >> 8, rx_rssi & 0xff); #endif @@ -3255,12 +3239,12 @@ an_media_change(struct ifnet *ifp) int otype = sc->an_config.an_opmode; int orate = sc->an_tx_rate; + AN_LOCK(sc); sc->an_tx_rate = ieee80211_media2rate( IFM_SUBTYPE(sc->an_ifmedia.ifm_cur->ifm_media)); if (sc->an_tx_rate < 0) sc->an_tx_rate = 0; - AN_LOCK(sc); if (orate != sc->an_tx_rate) { /* Read the current configuration */ sc->an_config.an_type = AN_RID_GENCONFIG; @@ -3281,11 +3265,11 @@ an_media_change(struct ifnet *ifp) sc->an_config.an_opmode &= ~AN_OPMODE_INFRASTRUCTURE_STATION; else sc->an_config.an_opmode |= AN_OPMODE_INFRASTRUCTURE_STATION; - AN_UNLOCK(sc); if (otype != sc->an_config.an_opmode || orate != sc->an_tx_rate) - an_init(sc); + an_init_locked(sc); + AN_UNLOCK(sc); return(0); } @@ -3306,7 +3290,6 @@ an_media_status(struct ifnet *ifp, struct ifmediareq *imr) imr->ifm_active = sc->an_ifmedia.ifm_cur->ifm_media; imr->ifm_status = IFM_AVALID|IFM_ACTIVE; } - AN_UNLOCK(sc); if (sc->an_tx_rate == 0) { imr->ifm_active = IFM_IEEE80211|IFM_AUTO; @@ -3319,6 +3302,7 @@ an_media_status(struct ifnet *ifp, struct ifmediareq *imr) imr->ifm_status = IFM_AVALID; if (status.an_opmode & AN_STATUS_OPMODE_ASSOCIATED) imr->ifm_status |= IFM_ACTIVE; + AN_UNLOCK(sc); } /********************** Cisco utility support routines *************/ @@ -3563,14 +3547,13 @@ cmdreset(struct ifnet *ifp) int status; struct an_softc *sc = ifp->if_softc; + AN_LOCK(sc); an_stop(sc); - AN_LOCK(sc); an_cmd(sc, AN_CMD_DISABLE, 0); if (!(status = WaitBusy(ifp, AN_TIMEOUT))) { - printf("an%d: Waitbusy hang b4 RESET =%d\n", - sc->an_unit, status); + if_printf(ifp, "Waitbusy hang b4 RESET =%d\n", status); AN_UNLOCK(sc); return -EBUSY; } @@ -3580,8 +3563,7 @@ cmdreset(struct ifnet *ifp) if (!(status = WaitBusy(ifp, 100))) { - printf("an%d: Waitbusy hang AFTER RESET =%d\n", - sc->an_unit, status); + if_printf(ifp, "Waitbusy hang AFTER RESET =%d\n", status); AN_UNLOCK(sc); return -EBUSY; } @@ -3689,8 +3671,7 @@ flashpchar(struct ifnet *ifp, int byte, int dwelltime) /* timeout for busy clear wait */ if (waittime <= 0) { - printf("an%d: flash putchar busywait timeout! \n", - sc->an_unit); + if_printf(ifp, "flash putchar busywait timeout!\n"); return -1; } /* @@ -3756,9 +3737,7 @@ flashrestart(struct ifnet *ifp) FLASH_DELAY(sc, 1024); /* Added 12/7/00 */ - AN_UNLOCK(sc); - an_init(sc); - AN_LOCK(sc); + an_init_locked(sc); FLASH_DELAY(sc, 1024); /* Added 12/7/00 */ return status; @@ -3776,8 +3755,7 @@ flashcard(struct ifnet *ifp, struct aironet_ioctl *l_ioctl) sc = ifp->if_softc; if (sc->mpi350) { - printf("an%d: flashing not supported on MPI 350 yet\n", - sc->an_unit); + if_printf(ifp, "flashing not supported on MPI 350 yet\n"); return(-1); } status = l_ioctl->command; @@ -3822,7 +3800,7 @@ flashcard(struct ifnet *ifp, struct aironet_ioctl *l_ioctl) break; case AIROFLPUTBUF: /* Send 32k to card */ if (l_ioctl->len > FLASH_SIZE) { - printf("an%d: Buffer to big, %x %x\n", sc->an_unit, + if_printf(ifp, "Buffer to big, %x %x\n", l_ioctl->len, FLASH_SIZE); return -EINVAL; } @@ -3839,8 +3817,7 @@ flashcard(struct ifnet *ifp, struct aironet_ioctl *l_ioctl) break; case AIRORESTART: if ((status = flashrestart(ifp)) != 0) { - printf("an%d: FLASHRESTART returned %d\n", - sc->an_unit, status); + if_printf(ifp, "FLASHRESTART returned %d\n", status); return -EIO; } else return 0; diff --git a/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_an_isa.c b/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_an_isa.c index 80933b18ad..19e5cd0755 100644 --- a/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_an_isa.c +++ b/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_an_isa.c @@ -111,11 +111,9 @@ an_attach_isa(device_t dev) an_alloc_port(dev, sc->port_rid, 1); an_alloc_irq(dev, sc->irq_rid, 0); - sc->an_bhandle = rman_get_bushandle(sc->port_res); - sc->an_btag = rman_get_bustag(sc->port_res); sc->an_dev = dev; - error = an_attach(sc, device_get_unit(dev), flags); + error = an_attach(sc, flags); if (error) { an_release_resources(dev); return (error); diff --git a/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_an_pccard.c b/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_an_pccard.c index b475265d84..bdd40f3dfe 100644 --- a/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_an_pccard.c +++ b/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_an_pccard.c @@ -141,11 +141,9 @@ an_pccard_attach(device_t dev) an_alloc_irq(dev, sc->irq_rid, 0); - sc->an_bhandle = rman_get_bushandle(sc->port_res); - sc->an_btag = rman_get_bustag(sc->port_res); sc->an_dev = dev; - error = an_attach(sc, device_get_unit(dev), flags); + error = an_attach(sc, flags); if (error) goto fail; diff --git a/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_an_pci.c b/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_an_pci.c index d13e3ab597..2d0d2b5a66 100644 --- a/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_an_pci.c +++ b/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_an_pci.c @@ -103,6 +103,7 @@ struct an_type { static struct an_type an_devs[] = { { AIRONET_VENDORID, AIRONET_DEVICEID_35x, "Cisco Aironet 350 Series" }, + { AIRONET_VENDORID, AIRONET_DEVICEID_MPI350, "Cisco Aironet MPI350" }, { AIRONET_VENDORID, AIRONET_DEVICEID_4500, "Aironet PCI4500" }, { AIRONET_VENDORID, AIRONET_DEVICEID_4800, "Aironet PCI4800" }, { AIRONET_VENDORID, AIRONET_DEVICEID_4xxx, "Aironet PCI4500/PCI4800" }, @@ -133,13 +134,6 @@ an_probe_pci(device_t dev) t++; } - if (pci_get_vendor(dev) == AIRONET_VENDORID && - pci_get_device(dev) == AIRONET_DEVICEID_MPI350) { - device_set_desc(dev, "Cisco Aironet MPI350"); - an_pci_probe(dev); - return(BUS_PROBE_DEFAULT); - } - return(ENXIO); } @@ -149,10 +143,9 @@ an_attach_pci(dev) { u_int32_t command; struct an_softc *sc; - int unit, flags, error = 0; + int flags, error = 0; sc = device_get_softc(dev); - unit = device_get_unit(dev); flags = device_get_flags(dev); if (pci_get_vendor(dev) == AIRONET_VENDORID && @@ -169,7 +162,7 @@ an_attach_pci(dev) command = pci_read_config(dev, PCIR_COMMAND, 4); if (!(command & PCIM_CMD_PORTEN)) { - printf("an%d: failed to enable I/O ports!\n", unit); + device_printf(dev, "failed to enable I/O ports!\n"); error = ENXIO; goto fail; } @@ -178,35 +171,28 @@ an_attach_pci(dev) error = an_alloc_port(dev, sc->port_rid, 1); if (error) { - printf("an%d: couldn't map ports\n", unit); + device_printf(dev, "couldn't map ports\n"); goto fail; } - sc->an_btag = rman_get_bustag(sc->port_res); - sc->an_bhandle = rman_get_bushandle(sc->port_res); - /* Allocate memory for MPI350 */ if (sc->mpi350) { /* Allocate memory */ sc->mem_rid = PCIR_BAR(1); error = an_alloc_memory(dev, sc->mem_rid, 1); if (error) { - printf("an%d: couldn't map memory\n", unit); + device_printf(dev, "couldn't map memory\n"); goto fail; } - sc->an_mem_btag = rman_get_bustag(sc->mem_res); - sc->an_mem_bhandle = rman_get_bushandle(sc->mem_res); /* Allocate aux. memory */ sc->mem_aux_rid = PCIR_BAR(2); error = an_alloc_aux_memory(dev, sc->mem_aux_rid, AN_AUX_MEM_SIZE); if (error) { - printf("an%d: couldn't map aux memory\n", unit); + device_printf(dev, "couldn't map aux memory\n"); goto fail; } - sc->an_mem_aux_btag = rman_get_bustag(sc->mem_aux_res); - sc->an_mem_aux_bhandle = rman_get_bushandle(sc->mem_aux_res); /* Allocate DMA region */ error = bus_dma_tag_create(NULL, /* parent */ @@ -222,7 +208,7 @@ an_attach_pci(dev) NULL, /* lockarg */ &sc->an_dtag); if (error) { - printf("an%d: couldn't get DMA region\n", unit); + device_printf(dev, "couldn't get DMA region\n"); goto fail; } } @@ -230,12 +216,14 @@ an_attach_pci(dev) /* Allocate interrupt */ error = an_alloc_irq(dev, 0, RF_SHAREABLE); if (error) { + device_printf(dev, "couldn't get interrupt\n"); goto fail; } sc->an_dev = dev; - error = an_attach(sc, device_get_unit(dev), flags); + error = an_attach(sc, flags); if (error) { + device_printf(dev, "couldn't attach\n"); goto fail; } @@ -244,6 +232,8 @@ an_attach_pci(dev) */ error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET, NULL, an_intr, sc, &sc->irq_handle); + if (error) + device_printf(dev, "couldn't setup interrupt\n"); fail: if (error) diff --git a/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_anreg.h b/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_anreg.h index ec0ad2bc4a..279e65b8e1 100644 --- a/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_anreg.h +++ b/src/add-ons/kernel/drivers/network/wlan/aironetwifi/dev/an/if_anreg.h @@ -45,47 +45,39 @@ /* * register space access macros */ -#define CSR_WRITE_2(sc, reg, val) \ - bus_space_write_2(sc->an_btag, sc->an_bhandle, reg, val) +#define CSR_WRITE_2(sc, reg, val) bus_write_2(sc->port_res, reg, val) -#define CSR_READ_2(sc, reg) \ - bus_space_read_2(sc->an_btag, sc->an_bhandle, reg) +#define CSR_READ_2(sc, reg) bus_read_2(sc->port_res, reg) -#define CSR_WRITE_1(sc, reg, val) \ - bus_space_write_1(sc->an_btag, sc->an_bhandle, reg, val) +#define CSR_WRITE_1(sc, reg, val) bus_write_1(sc->port_res, reg, val) -#define CSR_READ_1(sc, reg) \ - bus_space_read_1(sc->an_btag, sc->an_bhandle, reg) +#define CSR_READ_1(sc, reg) bus_read_1(sc->port_res, reg) /* * memory space access macros */ -#define CSR_MEM_WRITE_2(sc, reg, val) \ - bus_space_write_2(sc->an_mem_btag, sc->an_mem_bhandle, reg, val) +#define CSR_MEM_WRITE_2(sc, reg, val) bus_write_2(sc->mem_res, reg, val) -#define CSR_MEM_READ_2(sc, reg) \ - bus_space_read_2(sc->an_mem_btag, sc->an_mem_bhandle, reg) +#define CSR_MEM_READ_2(sc, reg) bus_read_2(sc->mem_res, reg) -#define CSR_MEM_WRITE_1(sc, reg, val) \ - bus_space_write_1(sc->an_mem_btag, sc->an_mem_bhandle, reg, val) +#define CSR_MEM_WRITE_1(sc, reg, val) bus_write_1(sc->mem_res, reg, val) -#define CSR_MEM_READ_1(sc, reg) \ - bus_space_read_1(sc->an_mem_btag, sc->an_mem_bhandle, reg) +#define CSR_MEM_READ_1(sc, reg) bus_read_1(sc->mem_res, reg) /* * aux. memory space access macros */ #define CSR_MEM_AUX_WRITE_4(sc, reg, val) \ - bus_space_write_4(sc->an_mem_aux_btag, sc->an_mem_aux_bhandle, reg, val) + bus_write_4(sc->mem_aux_res, reg, val) #define CSR_MEM_AUX_READ_4(sc, reg) \ - bus_space_read_4(sc->an_mem_aux_btag, sc->an_mem_aux_bhandle, reg) + bus_read_4(sc->mem_aux_res, reg) #define CSR_MEM_AUX_WRITE_1(sc, reg, val) \ - bus_space_write_1(sc->an_mem_aux_btag, sc->an_mem_aux_bhandle, reg, val) + bus_write_1(sc->mem_aux_res, reg, val) #define CSR_MEM_AUX_READ_1(sc, reg) \ - bus_space_read_1(sc->an_mem_aux_btag, sc->an_mem_aux_bhandle, reg) + bus_read_1(sc->mem_aux_res, reg) /* * Size of Aironet I/O space. @@ -394,13 +386,18 @@ struct an_txframe_802_3 { #define AN_PAYLOADTYPE_ETHER 0x0000 #define AN_PAYLOADTYPE_LLC 0x0010 -#define AN_TXCTL_80211 \ - (AN_TXCTL_TXOK_INTR|AN_TXCTL_TXERR_INTR|AN_HEADERTYPE_80211| \ - AN_PAYLOADTYPE_LLC|AN_TXCTL_NORELEASE) +#define AN_TXCTL_80211 (AN_HEADERTYPE_80211|AN_PAYLOADTYPE_LLC) -#define AN_TXCTL_8023 \ - (AN_TXCTL_TXOK_INTR|AN_TXCTL_TXERR_INTR|AN_HEADERTYPE_8023| \ - AN_PAYLOADTYPE_ETHER|AN_TXCTL_NORELEASE) +#define AN_TXCTL_8023 (AN_HEADERTYPE_8023|AN_PAYLOADTYPE_ETHER) + +/* + * Additions to transmit control bits for MPI350 + */ +#define AN_TXCTL_HW(x) \ + ( x ? (AN_TXCTL_NORELEASE) \ + : \ + (AN_TXCTL_TXOK_INTR|AN_TXCTL_TXERR_INTR|AN_TXCTL_NORELEASE) \ + ) #define AN_TXGAP_80211 0 #define AN_TXGAP_8023 0 @@ -442,8 +439,6 @@ struct an_tx_ring_data { struct an_softc { struct ifnet *an_ifp; - int an_unit; - int port_rid; /* resource id for port range */ struct resource* port_res; /* resource for port range */ int mem_rid; /* resource id for memory range */ @@ -456,11 +451,6 @@ struct an_softc { void* irq_handle; /* handle for irq handler */ struct resource* irq_res; /* resource for irq */ - bus_space_handle_t an_bhandle_p; - bus_space_handle_t an_bhandle; - bus_space_tag_t an_btag; - bus_space_handle_t an_mem_bhandle; - bus_space_tag_t an_mem_btag; bus_space_handle_t an_mem_aux_bhandle; bus_space_tag_t an_mem_aux_btag; bus_dma_tag_t an_dtag; @@ -491,6 +481,7 @@ struct an_softc { struct ifmedia an_ifmedia; int an_monitor; int an_was_monitor; + int an_timer; u_char buf_802_11[MCLBYTES]; struct an_req areq; unsigned short* an_flash_buffer; @@ -513,7 +504,7 @@ int an_pci_probe (device_t); int an_probe (device_t); int an_shutdown (device_t); void an_resume (device_t); -int an_attach (struct an_softc *, int, int); +int an_attach (struct an_softc *, int); int an_detach (device_t); void an_stop (struct an_softc *); diff --git a/src/add-ons/kernel/drivers/network/wlan/aironetwifi/glue.c b/src/add-ons/kernel/drivers/network/wlan/aironetwifi/glue.c index 1b6f594464..9acdafa949 100644 --- a/src/add-ons/kernel/drivers/network/wlan/aironetwifi/glue.c +++ b/src/add-ons/kernel/drivers/network/wlan/aironetwifi/glue.c @@ -6,6 +6,7 @@ #include #include +#include #include #include diff --git a/src/add-ons/kernel/drivers/network/wlan/broadcom43xx/dev/bwi/if_bwi.c b/src/add-ons/kernel/drivers/network/wlan/broadcom43xx/dev/bwi/if_bwi.c index 450605888f..f789ca0ba5 100644 --- a/src/add-ons/kernel/drivers/network/wlan/broadcom43xx/dev/bwi/if_bwi.c +++ b/src/add-ons/kernel/drivers/network/wlan/broadcom43xx/dev/bwi/if_bwi.c @@ -106,7 +106,7 @@ static void bwi_start(struct ifnet *); static void bwi_start_locked(struct ifnet *); static int bwi_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); -static void bwi_watchdog(struct ifnet *); +static void bwi_watchdog(void *); static void bwi_scan_start(struct ieee80211com *); static void bwi_set_channel(struct ieee80211com *); static void bwi_scan_end(struct ieee80211com *); @@ -461,10 +461,10 @@ bwi_attach(struct bwi_softc *sc) ifp->if_init = bwi_init; ifp->if_ioctl = bwi_ioctl; ifp->if_start = bwi_start; - ifp->if_watchdog = bwi_watchdog; IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; IFQ_SET_READY(&ifp->if_snd); + callout_init_mtx(&sc->sc_watchdog_timer, &sc->sc_mtx, 0); /* * Setup ratesets, phytype, channels and get MAC address @@ -537,11 +537,11 @@ bwi_attach(struct bwi_softc *sc) /* * Add sysctl nodes */ - SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), + SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "fw_version", CTLFLAG_RD, &sc->sc_fw_version, 0, "Firmware version"); - SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), + SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "led_idle", CTLFLAG_RW, &sc->sc_led_idle, 0, "# ticks before LED enters idle state"); @@ -577,6 +577,7 @@ bwi_detach(struct bwi_softc *sc) bwi_stop(sc, 1); callout_drain(&sc->sc_led_blink_ch); callout_drain(&sc->sc_calib_ch); + callout_drain(&sc->sc_watchdog_timer); ieee80211_ifdetach(ic); for (i = 0; i < sc->sc_nmac; ++i) @@ -1288,6 +1289,7 @@ bwi_init_statechg(struct bwi_softc *sc, int statechg) sc->sc_flags &= ~BWI_F_STOP; ifp->if_drv_flags |= IFF_DRV_RUNNING; + callout_reset(&sc->sc_watchdog_timer, hz, bwi_watchdog, sc); /* Enable intrs */ bwi_enable_intrs(sc, BWI_INIT_INTRS); @@ -1426,7 +1428,7 @@ bwi_start_locked(struct ifnet *ifp) tbd->tbd_idx = idx; if (trans) - ifp->if_timer = 5; + sc->sc_tx_timer = 5; } static int @@ -1467,7 +1469,7 @@ bwi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, if (++tbd->tbd_used + BWI_TX_NSPRDESC >= BWI_TX_NDESC) ifp->if_drv_flags |= IFF_DRV_OACTIVE; tbd->tbd_idx = (idx + 1) % BWI_TX_NDESC; - ifp->if_timer = 5; + sc->sc_tx_timer = 5; } else { /* NB: m is reclaimed on encap failure */ ieee80211_free_node(ni); @@ -1478,17 +1480,20 @@ bwi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, } static void -bwi_watchdog(struct ifnet *ifp) +bwi_watchdog(void *arg) { - struct bwi_softc *sc = ifp->if_softc; + struct bwi_softc *sc; + struct ifnet *ifp; - BWI_LOCK(sc); - if ((ifp->if_drv_flags & IFF_DRV_RUNNING)) { + sc = arg; + ifp = sc->sc_ifp; + BWI_ASSERT_LOCKED(sc); + if (sc->sc_tx_timer != 0 && --sc->sc_tx_timer == 0) { if_printf(ifp, "watchdog timeout\n"); ifp->if_oerrors++; taskqueue_enqueue(sc->sc_tq, &sc->sc_restart_task); } - BWI_UNLOCK(sc); + callout_reset(&sc->sc_watchdog_timer, hz, bwi_watchdog, sc); } static void @@ -1544,7 +1549,7 @@ bwi_stop_locked(struct bwi_softc *sc, int statechg) bwi_bbp_power_off(sc); sc->sc_tx_timer = 0; - ifp->if_timer = 0; + callout_stop(&sc->sc_watchdog_timer); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } @@ -3397,7 +3402,7 @@ _bwi_txeof(struct bwi_softc *sc, uint16_t tx_id, int acked, int data_txcnt) tb->tb_mbuf = NULL; if (tbd->tbd_used == 0) - ifp->if_timer = 0; + sc->sc_tx_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } @@ -3899,7 +3904,7 @@ bwi_led_attach(struct bwi_softc *sc) "%dth led, act %d, lowact %d\n", i, led->l_act, led->l_flags & BWI_LED_F_ACTLOW); } - callout_init(&sc->sc_led_blink_ch, CALLOUT_MPSAFE); + callout_init_mtx(&sc->sc_led_blink_ch, &sc->sc_mtx, 0); } static __inline uint16_t diff --git a/src/add-ons/kernel/drivers/network/wlan/broadcom43xx/dev/bwi/if_bwivar.h b/src/add-ons/kernel/drivers/network/wlan/broadcom43xx/dev/bwi/if_bwivar.h index e89ec1a020..bb35623a15 100644 --- a/src/add-ons/kernel/drivers/network/wlan/broadcom43xx/dev/bwi/if_bwivar.h +++ b/src/add-ons/kernel/drivers/network/wlan/broadcom43xx/dev/bwi/if_bwivar.h @@ -571,6 +571,7 @@ struct bwi_softc { bus_space_handle_t sc_mem_bh; struct callout sc_calib_ch; + struct callout sc_watchdog_timer; struct bwi_regwin *sc_cur_regwin; struct bwi_regwin sc_com_regwin; diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi2100/dev/ipw/if_ipw.c b/src/add-ons/kernel/drivers/network/wlan/iprowifi2100/dev/ipw/if_ipw.c index 60b440ace8..0a576ab064 100644 --- a/src/add-ons/kernel/drivers/network/wlan/iprowifi2100/dev/ipw/if_ipw.c +++ b/src/add-ons/kernel/drivers/network/wlan/iprowifi2100/dev/ipw/if_ipw.c @@ -199,6 +199,8 @@ static devclass_t ipw_devclass; DRIVER_MODULE(ipw, pci, ipw_driver, ipw_devclass, 0, 0); +MODULE_VERSION(ipw, 1); + static int ipw_probe(device_t dev) { @@ -289,8 +291,8 @@ ipw_attach(device_t dev) ifp->if_init = ipw_init; ifp->if_ioctl = ipw_ioctl; ifp->if_start = ipw_start; - IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); - ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); + ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; IFQ_SET_READY(&ifp->if_snd); ic->ic_ifp = ifp; @@ -888,10 +890,10 @@ ipw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) /* * XXX when joining an ibss network we are called * with a SCAN -> RUN transition on scan complete. - * Use that to call ipw_auth_and_assoc. On completing - * the join we are then called again with an - * AUTH -> RUN transition and we want to do nothing. - * This is all totally bogus and needs to be redone. + * Use that to call ipw_assoc. On completing the + * join we are then called again with an AUTH -> RUN + * transition and we want to do nothing. This is + * all totally bogus and needs to be redone. */ if (ostate == IEEE80211_S_SCAN) ipw_assoc(ic, vap); @@ -904,12 +906,19 @@ ipw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) break; case IEEE80211_S_AUTH: + /* + * Move to ASSOC state after the ipw_assoc() call. Firmware + * takes care of authentication, after the call we'll receive + * only an assoc response which would otherwise be discared + * if we are still in AUTH state. + */ + nstate = IEEE80211_S_ASSOC; ipw_assoc(ic, vap); break; case IEEE80211_S_ASSOC: /* - * If we are not transitioning from AUTH the resend the + * If we are not transitioning from AUTH then resend the * association request. */ if (ostate != IEEE80211_S_AUTH) @@ -1021,7 +1030,6 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) } sc->flags &= ~IPW_FLAG_ASSOCIATING; sc->flags |= IPW_FLAG_ASSOCIATED; - ieee80211_new_state(vap, IEEE80211_S_RUN, -1); break; case IPW_STATE_SCANNING: @@ -1034,8 +1042,10 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) * we checked the 802.11 layer state. */ if (sc->flags & IPW_FLAG_ASSOCIATED) { + IPW_UNLOCK(sc); /* XXX probably need to issue disassoc to fw */ ieee80211_beacon_miss(ic); + IPW_LOCK(sc); } break; @@ -1054,7 +1064,9 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) break; } if (sc->flags & IPW_FLAG_SCANNING) { + IPW_UNLOCK(sc); ieee80211_scan_done(vap); + IPW_LOCK(sc); sc->flags &= ~IPW_FLAG_SCANNING; sc->sc_scan_timer = 0; } @@ -1064,8 +1076,11 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) DPRINTFN(2, ("Association lost (%s flags 0x%x)\n", IEEESTATE(vap), sc->flags)); sc->flags &= ~(IPW_FLAG_ASSOCIATING | IPW_FLAG_ASSOCIATED); - if (vap->iv_state == IEEE80211_S_RUN) + if (vap->iv_state == IEEE80211_S_RUN) { + IPW_UNLOCK(sc); ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); + IPW_LOCK(sc); + } break; case IPW_STATE_DISABLED: @@ -1164,7 +1179,6 @@ ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status, bus_addr_t physaddr; int error; int8_t rssi, nf; - IPW_LOCK_DECL; DPRINTFN(5, ("received frame len=%u, rssi=%u\n", le32toh(status->len), status->rssi)); @@ -1234,10 +1248,10 @@ ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status, IPW_UNLOCK(sc); ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { - (void) ieee80211_input(ni, m, rssi, nf); + (void) ieee80211_input(ni, m, rssi - nf, nf); ieee80211_free_node(ni); } else - (void) ieee80211_input_all(ic, m, rssi, nf); + (void) ieee80211_input_all(ic, m, rssi - nf, nf); IPW_LOCK(sc); bus_dmamap_sync(sc->rbd_dmat, sc->rbd_map, BUS_DMASYNC_PREWRITE); @@ -1378,8 +1392,11 @@ ipw_fatal_error_intr(struct ipw_softc *sc) struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); device_printf(sc->sc_dev, "firmware error\n"); - if (vap != NULL) + if (vap != NULL) { + IPW_UNLOCK(sc); ieee80211_cancel_scan(vap); + IPW_LOCK(sc); + } ieee80211_runtask(ic, &sc->sc_init_task); } @@ -1388,7 +1405,6 @@ ipw_intr(void *arg) { struct ipw_softc *sc = arg; uint32_t r; - IPW_LOCK_DECL; IPW_LOCK(sc); @@ -1728,7 +1744,6 @@ static void ipw_start(struct ifnet *ifp) { struct ipw_softc *sc = ifp->if_softc; - IPW_LOCK_DECL; IPW_LOCK(sc); ipw_start_locked(ifp); @@ -1785,7 +1800,9 @@ ipw_watchdog(void *arg) DPRINTFN(3, ("Scan timeout\n")); /* End the scan */ if (sc->flags & IPW_FLAG_SCANNING) { + IPW_UNLOCK(sc); ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps)); + IPW_LOCK(sc); sc->flags &= ~IPW_FLAG_SCANNING; } } @@ -1801,7 +1818,6 @@ ipw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *) data; int error = 0, startall = 0; - IPW_LOCK_DECL; switch (cmd) { case SIOCSIFFLAGS: @@ -2211,7 +2227,6 @@ ipw_assoc(struct ieee80211com *ic, struct ieee80211vap *vap) struct ipw_security security; uint32_t data; int error; - IPW_LOCK_DECL; IPW_LOCK(sc); error = ipw_disable(sc); @@ -2270,8 +2285,8 @@ ipw_assoc(struct ieee80211com *ic, struct ieee80211vap *vap) if (error != 0) goto done; - if (vap->iv_appie_assocreq != NULL) { - struct ieee80211_appie *ie = vap->iv_appie_assocreq; + if (vap->iv_appie_wpa != NULL) { + struct ieee80211_appie *ie = vap->iv_appie_wpa; error = ipw_setwpaie(sc, ie->ie_data, ie->ie_len); if (error != 0) goto done; @@ -2301,7 +2316,6 @@ ipw_disassoc(struct ieee80211com *ic, struct ieee80211vap *vap) 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); DPRINTF(("Disassociate from %6D\n", ni->ni_bssid, ":")); @@ -2337,7 +2351,6 @@ 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); @@ -2504,19 +2517,19 @@ ipw_config(struct ipw_softc *sc) if (error != 0) return error; - data = htole32(0x3); /* 1, 2 */ + data = htole32(0xf); /* 1, 2, 5.5, 11 */ 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 */ + /* 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 */ + /* Use the same rate set */ 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) @@ -2544,7 +2557,6 @@ static void ipw_stop(void *priv) { struct ipw_softc *sc = priv; - IPW_LOCK_DECL; IPW_LOCK(sc); ipw_stop_locked(sc); @@ -2671,7 +2683,6 @@ ipw_scan_start(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct ipw_softc *sc = ifp->if_softc; - IPW_LOCK_DECL; IPW_LOCK(sc); ipw_scan(sc); @@ -2683,7 +2694,6 @@ ipw_set_channel(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct ipw_softc *sc = ifp->if_softc; - IPW_LOCK_DECL; IPW_LOCK(sc); if (ic->ic_opmode == IEEE80211_M_MONITOR) { @@ -2711,7 +2721,6 @@ ipw_scan_end(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct ipw_softc *sc = ifp->if_softc; - IPW_LOCK_DECL; IPW_LOCK(sc); sc->flags &= ~IPW_FLAG_SCANNING; diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi2100/dev/ipw/if_ipwvar.h b/src/add-ons/kernel/drivers/network/wlan/iprowifi2100/dev/ipw/if_ipwvar.h index 69c8de9346..6be3cbc1be 100644 --- a/src/add-ons/kernel/drivers/network/wlan/iprowifi2100/dev/ipw/if_ipwvar.h +++ b/src/add-ons/kernel/drivers/network/wlan/iprowifi2100/dev/ipw/if_ipwvar.h @@ -168,13 +168,6 @@ struct ipw_softc { * NB.: This models the only instance of async locking in ipw_init_locked * and must be kept in sync. */ -#define IPW_LOCK_DECL int __waslocked = 0 -#define IPW_LOCK(sc) do { \ - if (!(__waslocked = mtx_owned(&(sc)->sc_mtx))) \ - mtx_lock(&sc->sc_mtx); \ -} while (0) -#define IPW_UNLOCK(sc) do { \ - if (!__waslocked) \ - mtx_unlock(&sc->sc_mtx); \ -} while (0) +#define IPW_LOCK(sc) mtx_lock(&sc->sc_mtx); +#define IPW_UNLOCK(sc) mtx_unlock(&sc->sc_mtx); #define IPW_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi2200/dev/iwi/if_iwi.c b/src/add-ons/kernel/drivers/network/wlan/iprowifi2200/dev/iwi/if_iwi.c index f9ed03b481..a826750c2d 100644 --- a/src/add-ons/kernel/drivers/network/wlan/iprowifi2200/dev/iwi/if_iwi.c +++ b/src/add-ons/kernel/drivers/network/wlan/iprowifi2200/dev/iwi/if_iwi.c @@ -232,6 +232,8 @@ static devclass_t iwi_devclass; DRIVER_MODULE(iwi, pci, iwi_driver, iwi_devclass, 0, 0); +MODULE_VERSION(iwi, 1); + static __inline uint8_t MEM_READ_1(struct iwi_softc *sc, uint32_t addr) { @@ -397,11 +399,11 @@ iwi_attach(device_t dev) val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 2); macaddr[4] = val & 0xff; macaddr[5] = val >> 8; - + bands = 0; setbit(&bands, IEEE80211_MODE_11B); setbit(&bands, IEEE80211_MODE_11G); - if (pci_get_device(dev) >= 0x4223) + if (pci_get_device(dev) >= 0x4223) setbit(&bands, IEEE80211_MODE_11A); ieee80211_init_channels(ic, NULL, &bands); @@ -570,7 +572,7 @@ iwi_alloc_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring, int count) error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, - count * IWI_CMD_DESC_SIZE, 1, count * IWI_CMD_DESC_SIZE, 0, + count * IWI_CMD_DESC_SIZE, 1, count * IWI_CMD_DESC_SIZE, 0, NULL, NULL, &ring->desc_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create desc DMA tag\n"); @@ -615,7 +617,7 @@ iwi_free_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring) } if (ring->desc_dmat != NULL) - bus_dma_tag_destroy(ring->desc_dmat); + bus_dma_tag_destroy(ring->desc_dmat); } static int @@ -632,7 +634,7 @@ iwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring, int count, error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, - count * IWI_TX_DESC_SIZE, 1, count * IWI_TX_DESC_SIZE, 0, NULL, + count * IWI_TX_DESC_SIZE, 1, count * IWI_TX_DESC_SIZE, 0, NULL, NULL, &ring->desc_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create desc DMA tag\n"); @@ -911,7 +913,7 @@ iwi_node_free(struct ieee80211_node *ni) sc->sc_node_free(ni); } -/* +/* * Convert h/w rate code to IEEE rate code. */ static int @@ -2937,7 +2939,7 @@ iwi_auth_and_assoc(struct iwi_softc *sc, struct ieee80211vap *vap) * key setup. This typically is due to a user app bug * but if we blindly grab the key the firmware will * barf so avoid it for now. - */ + */ if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE) assoc->auth |= vap->iv_def_txkey << 4; @@ -3519,14 +3521,14 @@ iwi_ledattach(struct iwi_softc *sc) SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "softled", CTLTYPE_INT | CTLFLAG_RW, sc, 0, iwi_sysctl_softled, "I", "enable/disable software LED support"); - SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "ledpin", CTLFLAG_RW, &sc->sc_ledpin, 0, "pin setting to turn activity LED on"); - SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0, "idle time for inactivity LED (ticks)"); /* XXX for debugging */ - SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "nictype", CTLFLAG_RD, &sc->sc_nictype, 0, "NIC type from EEPROM"); diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/wpi/if_wpi.c b/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/wpi/if_wpi.c index 7d8721a92f..62f44d1362 100644 --- a/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/wpi/if_wpi.c +++ b/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/wpi/if_wpi.c @@ -29,7 +29,7 @@ __FBSDID("$FreeBSD$"); * state and told to load boot firmware. The boot firmware loads an init and a * main binary firmware image into SRAM on the card via DMA. * Once the firmware is loaded, the driver/hw then - * communicate by way of circular dma rings via the the SRAM to the firmware. + * communicate by way of circular dma rings via the SRAM to the firmware. * * There is 6 memory rings. 1 command ring, 1 rx data ring & 4 tx data rings. * The 4 tx data rings allow for prioritization QoS. @@ -181,7 +181,7 @@ static uint32_t wpi_mem_read(struct wpi_softc *, uint16_t); static void wpi_mem_write(struct wpi_softc *, uint16_t, uint32_t); static void wpi_mem_write_region_4(struct wpi_softc *, uint16_t, const uint32_t *, int); -static int wpi_read_prom_data(struct wpi_softc *, uint32_t, void *, int); +static uint16_t wpi_read_prom_data(struct wpi_softc *, uint32_t, void *, int); static int wpi_alloc_fwmem(struct wpi_softc *); static void wpi_free_fwmem(struct wpi_softc *); static int wpi_load_firmware(struct wpi_softc *); @@ -273,6 +273,8 @@ static devclass_t wpi_devclass; DRIVER_MODULE(wpi, pci, wpi_driver, wpi_devclass, 0, 0); +MODULE_VERSION(wpi, 1); + static const uint8_t wpi_ridx_to_plcp[] = { /* OFDM: IEEE Std 802.11a-1999, pp. 14 Table 80 */ /* R1-R4 (ral/ural is R4-R1) */ @@ -488,7 +490,7 @@ wpi_attach(device_t dev) struct wpi_softc *sc = device_get_softc(dev); struct ifnet *ifp; struct ieee80211com *ic; - int ac, error, result, supportsa = 1; + int ac, error, supportsa = 1; uint32_t tmp; const struct wpi_ident *ident; uint8_t macaddr[IEEE80211_ADDR_LEN]; @@ -545,9 +547,6 @@ wpi_attach(device_t dev) sc->sc_sh = rman_get_bushandle(sc->mem); sc->irq_rid = 0; - if ((result = pci_msi_count(dev)) == 1 && - pci_alloc_msi(dev, &result) == 0) - sc->irq_rid = 1; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, RF_ACTIVE | RF_SHAREABLE); if (sc->irq == NULL) { @@ -747,8 +746,6 @@ wpi_detach(device_t dev) 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->irq_rid == 1) - pci_release_msi(dev); } if (sc->mem != NULL) @@ -964,7 +961,7 @@ wpi_alloc_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) goto fail; } - error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MJUMPAGESIZE, 1, MJUMPAGESIZE, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); @@ -1009,7 +1006,7 @@ wpi_alloc_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) error = ENOMEM; /* XXX unique code */ goto fail; } - bus_dmamap_sync(ring->data_dmat, data->map, + bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); data->m = m; @@ -1364,7 +1361,7 @@ wpi_mem_write_region_4(struct wpi_softc *sc, uint16_t addr, * using the traditional bit-bang method. Data is read up until len bytes have * been obtained. */ -static int +static uint16_t wpi_read_prom_data(struct wpi_softc *sc, uint32_t addr, void *data, int len) { int ntries; @@ -2308,7 +2305,7 @@ wpi_mrr_setup(struct wpi_softc *sc) } /* setup MRR for control frames */ - mrr.which = htole32(WPI_MRR_CTL); + mrr.which = WPI_MRR_CTL; error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); if (error != 0) { device_printf(sc->sc_dev, @@ -2317,7 +2314,7 @@ wpi_mrr_setup(struct wpi_softc *sc) } /* setup MRR for data frames */ - mrr.which = htole32(WPI_MRR_DATA); + mrr.which = WPI_MRR_DATA; error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); if (error != 0) { device_printf(sc->sc_dev, diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/Jamfile b/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/Jamfile index 4ad32640c2..a7e3ffc364 100644 --- a/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/Jamfile +++ b/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/Jamfile @@ -28,11 +28,11 @@ HAIKU_WIFI_FIRMWARE_PACKAGES on iprowifi4965 = iwlwifi-1000-ucode-39.31.5.1 iwlwifi-4965-ucode-228.61.2.24 iwlwifi-5000-ucode-8.83.5.1 iwlwifi-5150-ucode-8.24.2.2 iwlwifi-6000-ucode-9.221.4.1 iwlwifi-6000g2a-ucode-17.168.5.3 - iwlwifi-6050-ucode-41.28.5.1 ; + iwlwifi-6000g2b-ucode-18.168.6.1 iwlwifi-6050-ucode-41.28.5.1 ; HAIKU_WIFI_FIRMWARE_ARCHIVES on iprowifi4965 = iwlwifi-1000-ucode-39.31.5.1.tgz iwlwifi-4965-ucode-228.61.2.24.tgz iwlwifi-5000-ucode-8.83.5.1.tgz iwlwifi-5150-ucode-8.24.2.2.tgz iwlwifi-6000-ucode-9.221.4.1.tgz iwlwifi-6000g2a-ucode-17.168.5.3.tgz - iwlwifi-6050-ucode-41.28.5.1.tgz ; + iwlwifi-6000g2b-ucode-18.168.6.1.tgz iwlwifi-6050-ucode-41.28.5.1.tgz ; HAIKU_WIFI_FIRMWARE_DO_EXTRACT on iprowifi4965 = true ; diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/dev/iwn/if_iwn.c b/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/dev/iwn/if_iwn.c index 33fde1592f..30f852d177 100644 --- a/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/dev/iwn/if_iwn.c +++ b/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/dev/iwn/if_iwn.c @@ -72,24 +72,65 @@ __FBSDID("$FreeBSD$"); #include #include +struct iwn_ident { + uint16_t vendor; + uint16_t device; + const char *name; +}; + +static const struct iwn_ident iwn_ident_table[] = { + { 0x8086, 0x0082, "Intel(R) Centrino(R) Advanced-N 6205" }, + { 0x8086, 0x0083, "Intel(R) Centrino(R) Wireless-N 1000" }, + { 0x8086, 0x0084, "Intel(R) Centrino(R) Wireless-N 1000" }, + { 0x8086, 0x0085, "Intel(R) Centrino(R) Advanced-N 6205" }, + { 0x8086, 0x0087, "Intel(R) Centrino(R) Advanced-N + WiMAX 6250" }, + { 0x8086, 0x0089, "Intel(R) Centrino(R) Advanced-N + WiMAX 6250" }, + { 0x8086, 0x008a, "Intel(R) Centrino(R) Wireless-N 1030" }, + { 0x8086, 0x008b, "Intel(R) Centrino(R) Wireless-N 1030" }, + { 0x8086, 0x0090, "Intel(R) Centrino(R) Advanced-N 6230" }, + { 0x8086, 0x0091, "Intel(R) Centrino(R) Advanced-N 6230" }, + { 0x8086, 0x0896, "Intel(R) Centrino(R) Wireless-N 130" }, + { 0x8086, 0x4229, "Intel(R) Wireless WiFi Link 4965" }, + { 0x8086, 0x422b, "Intel(R) Centrino(R) Ultimate-N 6300" }, + { 0x8086, 0x422c, "Intel(R) Centrino(R) Advanced-N 6200" }, + { 0x8086, 0x422d, "Intel(R) Wireless WiFi Link 4965" }, + { 0x8086, 0x4230, "Intel(R) Wireless WiFi Link 4965" }, + { 0x8086, 0x4232, "Intel(R) WiFi Link 5100" }, + { 0x8086, 0x4233, "Intel(R) Wireless WiFi Link 4965" }, + { 0x8086, 0x4235, "Intel(R) Ultimate N WiFi Link 5300" }, + { 0x8086, 0x4236, "Intel(R) Ultimate N WiFi Link 5300" }, + { 0x8086, 0x4237, "Intel(R) WiFi Link 5100" }, + { 0x8086, 0x4238, "Intel(R) Centrino(R) Ultimate-N 6300" }, + { 0x8086, 0x4239, "Intel(R) Centrino(R) Advanced-N 6200" }, + { 0x8086, 0x423a, "Intel(R) WiMAX/WiFi Link 5350" }, + { 0x8086, 0x423b, "Intel(R) WiMAX/WiFi Link 5350" }, + { 0x8086, 0x423c, "Intel(R) WiMAX/WiFi Link 5150" }, + { 0x8086, 0x423d, "Intel(R) WiMAX/WiFi Link 5150" }, + { 0, 0, NULL } +}; + static int iwn_probe(device_t); static int iwn_attach(device_t); -static const struct iwn_hal *iwn_hal_attach(struct iwn_softc *); +static int iwn4965_attach(struct iwn_softc *, uint16_t); +static int iwn5000_attach(struct iwn_softc *, uint16_t); static void iwn_radiotap_attach(struct iwn_softc *); +static void iwn_sysctlattach(struct iwn_softc *); static struct ieee80211vap *iwn_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 iwn_vap_delete(struct ieee80211vap *); -static int iwn_cleanup(device_t); static int iwn_detach(device_t); +static int iwn_shutdown(device_t); +static int iwn_suspend(device_t); +static int iwn_resume(device_t); static int iwn_nic_lock(struct iwn_softc *); static int iwn_eeprom_lock(struct iwn_softc *); static int iwn_init_otprom(struct iwn_softc *); static int iwn_read_prom_data(struct iwn_softc *, uint32_t, void *, int); static void iwn_dma_map_addr(void *, bus_dma_segment_t *, int, int); static int iwn_dma_contig_alloc(struct iwn_softc *, struct iwn_dma_info *, - void **, bus_size_t, bus_size_t, int); + void **, bus_size_t, bus_size_t); static void iwn_dma_contig_free(struct iwn_dma_info *); static int iwn_alloc_sched(struct iwn_softc *); static void iwn_free_sched(struct iwn_softc *); @@ -114,26 +155,28 @@ static void iwn4965_print_power_group(struct iwn_softc *, int); static void iwn5000_read_eeprom(struct iwn_softc *); static uint32_t iwn_eeprom_channel_flags(struct iwn_eeprom_chan *); static void iwn_read_eeprom_band(struct iwn_softc *, int); -#if 0 /* HT */ static void iwn_read_eeprom_ht40(struct iwn_softc *, int); -#endif -static void iwn_read_eeprom_channels(struct iwn_softc *, int, - uint32_t); +static void iwn_read_eeprom_channels(struct iwn_softc *, int, uint32_t); +static struct iwn_eeprom_chan *iwn_find_eeprom_channel(struct iwn_softc *, + struct ieee80211_channel *); +static int iwn_setregdomain(struct ieee80211com *, + struct ieee80211_regdomain *, int, + struct ieee80211_channel[]); static void iwn_read_eeprom_enhinfo(struct iwn_softc *); static struct ieee80211_node *iwn_node_alloc(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); +static void iwn_newassoc(struct ieee80211_node *, int); static int iwn_media_change(struct ifnet *); static int iwn_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static void iwn_calib_timeout(void *); static void iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); -static void iwn_timer_timeout(void *); -static void iwn_calib_reset(struct iwn_softc *); static void iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); -#if 0 /* HT */ static void iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); -#endif +static void iwn5000_rx_calib_results(struct iwn_softc *, + struct iwn_rx_desc *, struct iwn_rx_data *); static void iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); static void iwn4965_tx_done(struct iwn_softc *, struct iwn_rx_desc *, @@ -142,6 +185,7 @@ static void iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); static void iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int, uint8_t); +static void iwn_ampdu_tx_done(struct iwn_softc *, int, int, int, void *); static void iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *); static void iwn_notif_intr(struct iwn_softc *); static void iwn_wakeup_intr(struct iwn_softc *); @@ -155,23 +199,26 @@ static void iwn5000_update_sched(struct iwn_softc *, int, int, uint8_t, #ifdef notyet static void iwn5000_reset_sched(struct iwn_softc *, int, int); #endif -static uint8_t iwn_plcp_signal(int); static int iwn_tx_data(struct iwn_softc *, struct mbuf *, - struct ieee80211_node *, struct iwn_tx_ring *); + struct ieee80211_node *); +static int iwn_tx_data_raw(struct iwn_softc *, struct mbuf *, + struct ieee80211_node *, + const struct ieee80211_bpf_params *params); static int iwn_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void iwn_start(struct ifnet *); static void iwn_start_locked(struct ifnet *); -static void iwn_watchdog(struct iwn_softc *sc); +static void iwn_watchdog(void *); static int iwn_ioctl(struct ifnet *, u_long, caddr_t); static int iwn_cmd(struct iwn_softc *, int, const void *, int, int); static int iwn4965_add_node(struct iwn_softc *, struct iwn_node_info *, int); static int iwn5000_add_node(struct iwn_softc *, struct iwn_node_info *, int); -static int iwn_set_link_quality(struct iwn_softc *, uint8_t, int); +static int iwn_set_link_quality(struct iwn_softc *, + struct ieee80211_node *); static int iwn_add_broadcast_node(struct iwn_softc *, int); -static int iwn_wme_update(struct ieee80211com *); +static int iwn_updateedca(struct ieee80211com *); static void iwn_update_mcast(struct ifnet *); static void iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t); static int iwn_set_critical_temp(struct iwn_softc *); @@ -197,35 +244,39 @@ static void iwn_tune_sensitivity(struct iwn_softc *, const struct iwn_rx_stats *); static int iwn_send_sensitivity(struct iwn_softc *); static int iwn_set_pslevel(struct iwn_softc *, int, int, int); +static int iwn_send_btcoex(struct iwn_softc *); +static int iwn_send_advanced_btcoex(struct iwn_softc *); +static int iwn5000_runtime_calib(struct iwn_softc *); static int iwn_config(struct iwn_softc *); +static uint8_t *ieee80211_add_ssid(uint8_t *, const uint8_t *, u_int); static int iwn_scan(struct iwn_softc *); static int iwn_auth(struct iwn_softc *, struct ieee80211vap *vap); static int iwn_run(struct iwn_softc *, struct ieee80211vap *vap); -#if 0 /* HT */ -static int iwn_ampdu_rx_start(struct ieee80211com *, - struct ieee80211_node *, uint8_t); -static void iwn_ampdu_rx_stop(struct ieee80211com *, - struct ieee80211_node *, uint8_t); +static int iwn_ampdu_rx_start(struct ieee80211_node *, + struct ieee80211_rx_ampdu *, int, int, int); +static void iwn_ampdu_rx_stop(struct ieee80211_node *, + struct ieee80211_rx_ampdu *); +static int iwn_addba_request(struct ieee80211_node *, + struct ieee80211_tx_ampdu *, int, int, int); +static int iwn_addba_response(struct ieee80211_node *, + struct ieee80211_tx_ampdu *, int, int, int); static int iwn_ampdu_tx_start(struct ieee80211com *, struct ieee80211_node *, uint8_t); -static void iwn_ampdu_tx_stop(struct ieee80211com *, - struct ieee80211_node *, uint8_t); +static void iwn_ampdu_tx_stop(struct ieee80211_node *, + struct ieee80211_tx_ampdu *); static void iwn4965_ampdu_tx_start(struct iwn_softc *, - struct ieee80211_node *, uint8_t, uint16_t); -static void iwn4965_ampdu_tx_stop(struct iwn_softc *, uint8_t, uint16_t); + struct ieee80211_node *, int, uint8_t, uint16_t); +static void iwn4965_ampdu_tx_stop(struct iwn_softc *, int, + uint8_t, uint16_t); static void iwn5000_ampdu_tx_start(struct iwn_softc *, - struct ieee80211_node *, uint8_t, uint16_t); -static void iwn5000_ampdu_tx_stop(struct iwn_softc *, uint8_t, uint16_t); -#endif -static int iwn5000_send_calib_results(struct iwn_softc *); -static int iwn5000_save_calib_result(struct iwn_softc *, - struct iwn_phy_calib *, int, int); -static void iwn5000_free_calib_results(struct iwn_softc *); -static int iwn5000_chrystal_calib(struct iwn_softc *); -static int iwn5000_send_calib_query(struct iwn_softc *, uint32_t); -static int iwn5000_rx_calib_result(struct iwn_softc *, - struct iwn_rx_desc *, struct iwn_rx_data *); + struct ieee80211_node *, int, uint8_t, uint16_t); +static void iwn5000_ampdu_tx_stop(struct iwn_softc *, int, + uint8_t, uint16_t); +static int iwn5000_query_calibration(struct iwn_softc *); +static int iwn5000_send_calibration(struct iwn_softc *); static int iwn5000_send_wimax_coex(struct iwn_softc *); +static int iwn5000_crystal_calib(struct iwn_softc *); +static int iwn5000_temp_offset_calib(struct iwn_softc *); static int iwn4965_post_alive(struct iwn_softc *); static int iwn5000_post_alive(struct iwn_softc *); static int iwn4965_load_bootcode(struct iwn_softc *, const uint8_t *, @@ -248,27 +299,18 @@ static int iwn5000_nic_config(struct iwn_softc *); static int iwn_hw_prepare(struct iwn_softc *); static int iwn_hw_init(struct iwn_softc *); static void iwn_hw_stop(struct iwn_softc *); +static void iwn_radio_on(void *, int); +static void iwn_radio_off(void *, int); static void iwn_init_locked(struct iwn_softc *); static void iwn_init(void *); static void iwn_stop_locked(struct iwn_softc *); static void iwn_stop(struct iwn_softc *); -static void iwn_scan_start(struct ieee80211com *); -static void iwn_scan_end(struct ieee80211com *); -static void iwn_set_channel(struct ieee80211com *); -static void iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long); -static void iwn_scan_mindwell(struct ieee80211_scan_state *); -static struct iwn_eeprom_chan *iwn_find_eeprom_channel(struct iwn_softc *, - struct ieee80211_channel *); -static int iwn_setregdomain(struct ieee80211com *, - struct ieee80211_regdomain *, int, - struct ieee80211_channel []); +static void iwn_scan_start(struct ieee80211com *); +static void iwn_scan_end(struct ieee80211com *); +static void iwn_set_channel(struct ieee80211com *); +static void iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long); +static void iwn_scan_mindwell(struct ieee80211_scan_state *); static void iwn_hw_reset(void *, int); -static void iwn_radio_on(void *, int); -static void iwn_radio_off(void *, int); -static void iwn_sysctlattach(struct iwn_softc *); -static int iwn_shutdown(device_t); -static int iwn_suspend(device_t); -static int iwn_resume(device_t); #define IWN_DEBUG #ifdef IWN_DEBUG @@ -295,107 +337,77 @@ enum { printf(fmt, __VA_ARGS__); \ } while (0) -static const char *iwn_intr_str(uint8_t); +static const char * +iwn_intr_str(uint8_t cmd) +{ + switch (cmd) { + /* Notifications */ + case IWN_UC_READY: return "UC_READY"; + case IWN_ADD_NODE_DONE: return "ADD_NODE_DONE"; + case IWN_TX_DONE: return "TX_DONE"; + case IWN_START_SCAN: return "START_SCAN"; + case IWN_STOP_SCAN: return "STOP_SCAN"; + case IWN_RX_STATISTICS: return "RX_STATS"; + case IWN_BEACON_STATISTICS: return "BEACON_STATS"; + case IWN_STATE_CHANGED: return "STATE_CHANGED"; + case IWN_BEACON_MISSED: return "BEACON_MISSED"; + case IWN_RX_PHY: return "RX_PHY"; + case IWN_MPDU_RX_DONE: return "MPDU_RX_DONE"; + case IWN_RX_DONE: return "RX_DONE"; + + /* Command Notifications */ + case IWN_CMD_RXON: return "IWN_CMD_RXON"; + case IWN_CMD_RXON_ASSOC: return "IWN_CMD_RXON_ASSOC"; + case IWN_CMD_EDCA_PARAMS: return "IWN_CMD_EDCA_PARAMS"; + case IWN_CMD_TIMING: return "IWN_CMD_TIMING"; + case IWN_CMD_LINK_QUALITY: return "IWN_CMD_LINK_QUALITY"; + case IWN_CMD_SET_LED: return "IWN_CMD_SET_LED"; + case IWN5000_CMD_WIMAX_COEX: return "IWN5000_CMD_WIMAX_COEX"; + case IWN5000_CMD_CALIB_CONFIG: return "IWN5000_CMD_CALIB_CONFIG"; + case IWN5000_CMD_CALIB_RESULT: return "IWN5000_CMD_CALIB_RESULT"; + case IWN5000_CMD_CALIB_COMPLETE: return "IWN5000_CMD_CALIB_COMPLETE"; + case IWN_CMD_SET_POWER_MODE: return "IWN_CMD_SET_POWER_MODE"; + case IWN_CMD_SCAN: return "IWN_CMD_SCAN"; + case IWN_CMD_SCAN_RESULTS: return "IWN_CMD_SCAN_RESULTS"; + case IWN_CMD_TXPOWER: return "IWN_CMD_TXPOWER"; + case IWN_CMD_TXPOWER_DBM: return "IWN_CMD_TXPOWER_DBM"; + case IWN5000_CMD_TX_ANT_CONFIG: return "IWN5000_CMD_TX_ANT_CONFIG"; + case IWN_CMD_BT_COEX: return "IWN_CMD_BT_COEX"; + case IWN_CMD_SET_CRITICAL_TEMP: return "IWN_CMD_SET_CRITICAL_TEMP"; + case IWN_CMD_SET_SENSITIVITY: return "IWN_CMD_SET_SENSITIVITY"; + case IWN_CMD_PHY_CALIB: return "IWN_CMD_PHY_CALIB"; + } + return "UNKNOWN INTR NOTIF/CMD"; +} #else #define DPRINTF(sc, m, fmt, ...) do { (void) sc; } while (0) #endif -struct iwn_ident { - uint16_t vendor; - uint16_t device; - const char *name; +static device_method_t iwn_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, iwn_probe), + DEVMETHOD(device_attach, iwn_attach), + DEVMETHOD(device_detach, iwn_detach), + DEVMETHOD(device_shutdown, iwn_shutdown), + DEVMETHOD(device_suspend, iwn_suspend), + DEVMETHOD(device_resume, iwn_resume), + { 0, 0 } }; -static const struct iwn_ident iwn_ident_table [] = { - { 0x8086, 0x4229, "Intel(R) PRO/Wireless 4965BGN" }, - { 0x8086, 0x422D, "Intel(R) PRO/Wireless 4965BGN" }, - { 0x8086, 0x4230, "Intel(R) PRO/Wireless 4965BGN" }, - { 0x8086, 0x4233, "Intel(R) PRO/Wireless 4965BGN" }, - { 0x8086, 0x4232, "Intel(R) PRO/Wireless 5100" }, - { 0x8086, 0x4237, "Intel(R) PRO/Wireless 5100" }, - { 0x8086, 0x423C, "Intel(R) PRO/Wireless 5150" }, - { 0x8086, 0x423D, "Intel(R) PRO/Wireless 5150" }, - { 0x8086, 0x4235, "Intel(R) PRO/Wireless 5300" }, - { 0x8086, 0x4236, "Intel(R) PRO/Wireless 5300" }, - { 0x8086, 0x423A, "Intel(R) PRO/Wireless 5350" }, - { 0x8086, 0x423B, "Intel(R) PRO/Wireless 5350" }, - { 0x8086, 0x0083, "Intel(R) PRO/Wireless 1000" }, - { 0x8086, 0x0084, "Intel(R) PRO/Wireless 1000" }, - { 0x8086, 0x008D, "Intel(R) PRO/Wireless 6000" }, - { 0x8086, 0x008E, "Intel(R) PRO/Wireless 6000" }, - { 0x8086, 0x4238, "Intel(R) PRO/Wireless 6000" }, - { 0x8086, 0x4239, "Intel(R) PRO/Wireless 6000" }, - { 0x8086, 0x422B, "Intel(R) PRO/Wireless 6000" }, - { 0x8086, 0x422C, "Intel(R) PRO/Wireless 6000" }, - { 0x8086, 0x0087, "Intel(R) PRO/Wireless 6250" }, - { 0x8086, 0x0089, "Intel(R) PRO/Wireless 6250" }, - { 0x8086, 0x0082, "Intel(R) PRO/Wireless 6205a" }, - { 0x8086, 0x0085, "Intel(R) PRO/Wireless 6205a" }, -#ifdef notyet - { 0x8086, 0x008a, "Intel(R) PRO/Wireless 6205b" }, - { 0x8086, 0x008b, "Intel(R) PRO/Wireless 6205b" }, - { 0x8086, 0x008f, "Intel(R) PRO/Wireless 6205b" }, - { 0x8086, 0x0090, "Intel(R) PRO/Wireless 6205b" }, - { 0x8086, 0x0091, "Intel(R) PRO/Wireless 6205b" }, -#endif - { 0, 0, NULL } +static driver_t iwn_driver = { + "iwn", + iwn_methods, + sizeof(struct iwn_softc) }; +static devclass_t iwn_devclass; -static const struct iwn_hal iwn4965_hal = { - iwn4965_load_firmware, - iwn4965_read_eeprom, - iwn4965_post_alive, - iwn4965_nic_config, - iwn4965_update_sched, - iwn4965_get_temperature, - iwn4965_get_rssi, - iwn4965_set_txpower, - iwn4965_init_gains, - iwn4965_set_gains, - iwn4965_add_node, - iwn4965_tx_done, -#if 0 /* HT */ - iwn4965_ampdu_tx_start, - iwn4965_ampdu_tx_stop, -#endif - IWN4965_NTXQUEUES, - IWN4965_NDMACHNLS, - IWN4965_ID_BROADCAST, - IWN4965_RXONSZ, - IWN4965_SCHEDSZ, - IWN4965_FW_TEXT_MAXSZ, - IWN4965_FW_DATA_MAXSZ, - IWN4965_FWSZ, - IWN4965_SCHED_TXFACT -}; +DRIVER_MODULE(iwn, pci, iwn_driver, iwn_devclass, 0, 0); -static const struct iwn_hal iwn5000_hal = { - iwn5000_load_firmware, - iwn5000_read_eeprom, - iwn5000_post_alive, - iwn5000_nic_config, - iwn5000_update_sched, - iwn5000_get_temperature, - iwn5000_get_rssi, - iwn5000_set_txpower, - iwn5000_init_gains, - iwn5000_set_gains, - iwn5000_add_node, - iwn5000_tx_done, -#if 0 /* HT */ - iwn5000_ampdu_tx_start, - iwn5000_ampdu_tx_stop, -#endif - IWN5000_NTXQUEUES, - IWN5000_NDMACHNLS, - IWN5000_ID_BROADCAST, - IWN5000_RXONSZ, - IWN5000_SCHEDSZ, - IWN5000_FW_TEXT_MAXSZ, - IWN5000_FW_DATA_MAXSZ, - IWN5000_FWSZ, - IWN5000_SCHED_TXFACT -}; +MODULE_VERSION(iwn, 1); + +MODULE_DEPEND(iwn, firmware, 1, 1, 1); +MODULE_DEPEND(iwn, pci, 1, 1, 1); +MODULE_DEPEND(iwn, wlan, 1, 1, 1); static int iwn_probe(device_t dev) @@ -418,8 +430,7 @@ iwn_attach(device_t dev) struct iwn_softc *sc = (struct iwn_softc *)device_get_softc(dev); struct ieee80211com *ic; struct ifnet *ifp; - const struct iwn_hal *hal; - uint32_t tmp; + uint32_t reg; int i, error, result; uint8_t macaddr[IEEE80211_ADDR_LEN]; @@ -429,7 +440,7 @@ iwn_attach(device_t dev) * Get the offset of the PCI Express Capability Structure in PCI * Configuration Space. */ - error = pci_find_extcap(dev, PCIY_EXPRESS, &sc->sc_cap_off); + error = pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off); if (error != 0) { device_printf(dev, "PCIe capability structure not found!\n"); return error; @@ -439,12 +450,12 @@ iwn_attach(device_t dev) pci_write_config(dev, 0x41, 0, 1); /* Hardware bug workaround. */ - tmp = pci_read_config(dev, PCIR_COMMAND, 1); - if (tmp & PCIM_CMD_INTxDIS) { + reg = pci_read_config(dev, PCIR_COMMAND, 1); + if (reg & PCIM_CMD_INTxDIS) { DPRINTF(sc, IWN_DEBUG_RESET, "%s: PCIe INTx Disable set\n", __func__); - tmp &= ~PCIM_CMD_INTxDIS; - pci_write_config(dev, PCIR_COMMAND, tmp, 1); + reg &= ~PCIM_CMD_INTxDIS; + pci_write_config(dev, PCIR_COMMAND, reg, 1); } /* Enable bus-mastering. */ @@ -453,48 +464,48 @@ iwn_attach(device_t dev) sc->mem_rid = PCIR_BAR(0); sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); - if (sc->mem == NULL ) { - device_printf(dev, "could not allocate memory resources\n"); + if (sc->mem == NULL) { + device_printf(dev, "can't map mem space\n"); error = ENOMEM; return error; } - sc->sc_st = rman_get_bustag(sc->mem); sc->sc_sh = rman_get_bushandle(sc->mem); + sc->irq_rid = 0; if ((result = pci_msi_count(dev)) == 1 && pci_alloc_msi(dev, &result) == 0) sc->irq_rid = 1; + /* Install interrupt handler. */ sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, RF_ACTIVE | RF_SHAREABLE); if (sc->irq == NULL) { - device_printf(dev, "could not allocate interrupt resource\n"); + device_printf(dev, "can't map interrupt\n"); error = ENOMEM; goto fail; } IWN_LOCK_INIT(sc); - callout_init_mtx(&sc->sc_timer_to, &sc->sc_mtx, 0); - TASK_INIT(&sc->sc_reinit_task, 0, iwn_hw_reset, sc ); - TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc ); - TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc ); - /* Attach Hardware Abstraction Layer. */ - hal = iwn_hal_attach(sc); - if (hal == NULL) { - error = ENXIO; /* XXX: Wrong error code? */ + /* Read hardware revision and attach. */ + sc->hw_type = (IWN_READ(sc, IWN_HW_REV) >> 4) & 0xf; + if (sc->hw_type == IWN_HW_REV_TYPE_4965) + error = iwn4965_attach(sc, pci_get_device(dev)); + else + error = iwn5000_attach(sc, pci_get_device(dev)); + if (error != 0) { + device_printf(dev, "could not attach device, error %d\n", + error); goto fail; } - error = iwn_hw_prepare(sc); - if (error != 0) { + if ((error = iwn_hw_prepare(sc)) != 0) { device_printf(dev, "hardware not ready, error %d\n", error); goto fail; } /* Allocate DMA memory for firmware transfers. */ - error = iwn_alloc_fwmem(sc); - if (error != 0) { + if ((error = iwn_alloc_fwmem(sc)) != 0) { device_printf(dev, "could not allocate memory for firmware, error %d\n", error); @@ -502,53 +513,80 @@ iwn_attach(device_t dev) } /* Allocate "Keep Warm" page. */ - error = iwn_alloc_kw(sc); - if (error != 0) { + if ((error = iwn_alloc_kw(sc)) != 0) { device_printf(dev, - "could not allocate \"Keep Warm\" page, error %d\n", error); + "could not allocate keep warm page, error %d\n", error); goto fail; } /* Allocate ICT table for 5000 Series. */ if (sc->hw_type != IWN_HW_REV_TYPE_4965 && (error = iwn_alloc_ict(sc)) != 0) { - device_printf(dev, - "%s: could not allocate ICT table, error %d\n", - __func__, error); - goto fail; - } - - /* Allocate TX scheduler "rings". */ - error = iwn_alloc_sched(sc); - if (error != 0) { - device_printf(dev, - "could not allocate TX scheduler rings, error %d\n", + device_printf(dev, "could not allocate ICT table, error %d\n", error); goto fail; } - /* Allocate TX rings (16 on 4965AGN, 20 on 5000). */ - for (i = 0; i < hal->ntxqs; i++) { - error = iwn_alloc_tx_ring(sc, &sc->txq[i], i); - if (error != 0) { + /* Allocate TX scheduler "rings". */ + if ((error = iwn_alloc_sched(sc)) != 0) { + device_printf(dev, + "could not allocate TX scheduler rings, error %d\n", error); + goto fail; + } + + /* Allocate TX rings (16 on 4965AGN, 20 on >=5000). */ + for (i = 0; i < sc->ntxqs; i++) { + if ((error = iwn_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) { device_printf(dev, - "could not allocate Tx ring %d, error %d\n", - i, error); + "could not allocate TX ring %d, error %d\n", i, + error); goto fail; } } /* Allocate RX ring. */ - error = iwn_alloc_rx_ring(sc, &sc->rxq); - if (error != 0 ){ - device_printf(dev, - "could not allocate Rx ring, error %d\n", error); + if ((error = iwn_alloc_rx_ring(sc, &sc->rxq)) != 0) { + device_printf(dev, "could not allocate RX ring, error %d\n", + error); goto fail; } /* Clear pending interrupts. */ IWN_WRITE(sc, IWN_INT, 0xffffffff); + ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); + if (ifp == NULL) { + device_printf(dev, "can not allocate ifnet structure\n"); + 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 */ + + /* Set device capabilities. */ + ic->ic_caps = + IEEE80211_C_STA /* station mode supported */ + | IEEE80211_C_MONITOR /* monitor mode supported */ + | IEEE80211_C_BGSCAN /* background scanning */ + | IEEE80211_C_TXPMGT /* tx power management */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_WPA + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ +#if 0 + | IEEE80211_C_IBSS /* ibss/adhoc mode */ +#endif + | IEEE80211_C_WME /* WME */ + ; + + /* Read MAC address, channels, etc from EEPROM. */ + if ((error = iwn_read_eeprom(sc, macaddr)) != 0) { + device_printf(dev, "could not read EEPROM, error %d\n", + error); + goto fail; + } + /* Count the number of available chains. */ sc->ntxchains = ((sc->txchainmask >> 2) & 1) + @@ -558,84 +596,37 @@ iwn_attach(device_t dev) ((sc->rxchainmask >> 2) & 1) + ((sc->rxchainmask >> 1) & 1) + ((sc->rxchainmask >> 0) & 1); - - ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); - if (ifp == NULL) { - device_printf(dev, "can not allocate ifnet structure\n"); - goto fail; + if (bootverbose) { + device_printf(dev, "MIMO %dT%dR, %.4s, address %6D\n", + sc->ntxchains, sc->nrxchains, sc->eeprom_domain, + macaddr, ":"); } - 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 */ - - /* Set device capabilities. */ - ic->ic_caps = - IEEE80211_C_STA /* station mode supported */ - | IEEE80211_C_MONITOR /* monitor mode supported */ - | IEEE80211_C_TXPMGT /* tx power management */ - | IEEE80211_C_SHSLOT /* short slot time supported */ - | IEEE80211_C_WPA - | IEEE80211_C_SHPREAMBLE /* short preamble supported */ - | IEEE80211_C_BGSCAN /* background scanning */ -#if 0 - | IEEE80211_C_IBSS /* ibss/adhoc mode */ -#endif - | IEEE80211_C_WME /* WME */ - ; -#if 0 /* HT */ - /* XXX disable until HT channel setup works */ - ic->ic_htcaps = - IEEE80211_HTCAP_SMPS_ENA /* SM PS mode enabled */ - | IEEE80211_HTCAP_CHWIDTH40 /* 40MHz channel width */ - | IEEE80211_HTCAP_SHORTGI20 /* short GI in 20MHz */ - | IEEE80211_HTCAP_SHORTGI40 /* short GI in 40MHz */ - | IEEE80211_HTCAP_RXSTBC_2STREAM/* 1-2 spatial streams */ - | IEEE80211_HTCAP_MAXAMSDU_3839 /* max A-MSDU length */ - /* s/w capabilities */ - | IEEE80211_HTC_HT /* HT operation */ - | IEEE80211_HTC_AMPDU /* tx A-MPDU */ - | IEEE80211_HTC_AMSDU /* tx A-MSDU */ - ; - - /* Set HT capabilities. */ - ic->ic_htcaps = + if (sc->sc_flags & IWN_FLAG_HAS_11N) { + ic->ic_rxstream = sc->nrxchains; + ic->ic_txstream = sc->ntxchains; + ic->ic_htcaps = + IEEE80211_HTCAP_SMPS_OFF /* SMPS mode disabled */ + | IEEE80211_HTCAP_SHORTGI20 /* short GI in 20MHz */ + | IEEE80211_HTCAP_CHWIDTH40 /* 40MHz channel width*/ + | IEEE80211_HTCAP_SHORTGI40 /* short GI in 40MHz */ +#ifdef notyet + | IEEE80211_HTCAP_GREENFIELD #if IWN_RBUF_SIZE == 8192 - IEEE80211_HTCAP_AMSDU7935 | + | IEEE80211_HTCAP_MAXAMSDU_7935 /* max A-MSDU length */ +#else + | IEEE80211_HTCAP_MAXAMSDU_3839 /* max A-MSDU length */ #endif - IEEE80211_HTCAP_CBW20_40 | - IEEE80211_HTCAP_SGI20 | - IEEE80211_HTCAP_SGI40; - if (sc->hw_type != IWN_HW_REV_TYPE_4965) - ic->ic_htcaps |= IEEE80211_HTCAP_GF; - if (sc->hw_type == IWN_HW_REV_TYPE_6050) - ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DYN; - else - ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DIS; #endif - - /* Read MAC address, channels, etc from EEPROM. */ - error = iwn_read_eeprom(sc, macaddr); - if (error != 0) { - device_printf(dev, "could not read EEPROM, error %d\n", - error); - goto fail; + /* s/w capabilities */ + | IEEE80211_HTC_HT /* HT operation */ + | IEEE80211_HTC_AMPDU /* tx A-MPDU */ +#ifdef notyet + | IEEE80211_HTC_AMSDU /* tx A-MSDU */ +#endif + ; } - device_printf(sc->sc_dev, "MIMO %dT%dR, %.4s, address %6D\n", - sc->ntxchains, sc->nrxchains, sc->eeprom_domain, - macaddr, ":"); - -#if 0 /* HT */ - /* Set supported HT rates. */ - ic->ic_sup_mcs[0] = 0xff; - if (sc->nrxchains > 1) - ic->ic_sup_mcs[1] = 0xff; - if (sc->nrxchains > 2) - ic->ic_sup_mcs[2] = 0xff; -#endif - if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; @@ -651,7 +642,18 @@ iwn_attach(device_t dev) ic->ic_vap_delete = iwn_vap_delete; ic->ic_raw_xmit = iwn_raw_xmit; ic->ic_node_alloc = iwn_node_alloc; - ic->ic_wme.wme_update = iwn_wme_update; + sc->sc_ampdu_rx_start = ic->ic_ampdu_rx_start; + ic->ic_ampdu_rx_start = iwn_ampdu_rx_start; + sc->sc_ampdu_rx_stop = ic->ic_ampdu_rx_stop; + ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop; + sc->sc_addba_request = ic->ic_addba_request; + ic->ic_addba_request = iwn_addba_request; + sc->sc_addba_response = ic->ic_addba_response; + ic->ic_addba_response = iwn_addba_response; + sc->sc_addba_stop = ic->ic_addba_stop; + ic->ic_addba_stop = iwn_ampdu_tx_stop; + ic->ic_newassoc = iwn_newassoc; + ic->ic_wme.wme_update = iwn_updateedca; ic->ic_update_mcast = iwn_update_mcast; ic->ic_scan_start = iwn_scan_start; ic->ic_scan_end = iwn_scan_end; @@ -659,14 +661,15 @@ iwn_attach(device_t dev) ic->ic_scan_curchan = iwn_scan_curchan; ic->ic_scan_mindwell = iwn_scan_mindwell; ic->ic_setregdomain = iwn_setregdomain; -#if 0 /* HT */ - ic->ic_ampdu_rx_start = iwn_ampdu_rx_start; - ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop; - ic->ic_ampdu_tx_start = iwn_ampdu_tx_start; - ic->ic_ampdu_tx_stop = iwn_ampdu_tx_stop; -#endif iwn_radiotap_attach(sc); + + callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0); + callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0); + TASK_INIT(&sc->sc_reinit_task, 0, iwn_hw_reset, sc); + TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc); + TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc); + iwn_sysctlattach(sc); /* @@ -675,117 +678,141 @@ iwn_attach(device_t dev) error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, iwn_intr, sc, &sc->sc_ih); if (error != 0) { - device_printf(dev, "could not set up interrupt, error %d\n", + device_printf(dev, "can't establish interrupt, error %d\n", error); goto fail; } - ieee80211_announce(ic); + if (bootverbose) + ieee80211_announce(ic); return 0; fail: - iwn_cleanup(dev); + iwn_detach(dev); return error; } -static const struct iwn_hal * -iwn_hal_attach(struct iwn_softc *sc) +static int +iwn4965_attach(struct iwn_softc *sc, uint16_t pid) { - sc->hw_type = (IWN_READ(sc, IWN_HW_REV) >> 4) & 0xf; + struct iwn_ops *ops = &sc->ops; + + ops->load_firmware = iwn4965_load_firmware; + ops->read_eeprom = iwn4965_read_eeprom; + ops->post_alive = iwn4965_post_alive; + ops->nic_config = iwn4965_nic_config; + ops->update_sched = iwn4965_update_sched; + ops->get_temperature = iwn4965_get_temperature; + ops->get_rssi = iwn4965_get_rssi; + ops->set_txpower = iwn4965_set_txpower; + ops->init_gains = iwn4965_init_gains; + ops->set_gains = iwn4965_set_gains; + ops->add_node = iwn4965_add_node; + ops->tx_done = iwn4965_tx_done; + ops->ampdu_tx_start = iwn4965_ampdu_tx_start; + ops->ampdu_tx_stop = iwn4965_ampdu_tx_stop; + sc->ntxqs = IWN4965_NTXQUEUES; + sc->firstaggqueue = IWN4965_FIRSTAGGQUEUE; + sc->ndmachnls = IWN4965_NDMACHNLS; + sc->broadcast_id = IWN4965_ID_BROADCAST; + sc->rxonsz = IWN4965_RXONSZ; + sc->schedsz = IWN4965_SCHEDSZ; + sc->fw_text_maxsz = IWN4965_FW_TEXT_MAXSZ; + sc->fw_data_maxsz = IWN4965_FW_DATA_MAXSZ; + sc->fwsz = IWN4965_FWSZ; + sc->sched_txfact_addr = IWN4965_SCHED_TXFACT; + sc->limits = &iwn4965_sensitivity_limits; + sc->fwname = "iwn4965fw"; + /* Override chains masks, ROM is known to be broken. */ + sc->txchainmask = IWN_ANT_AB; + sc->rxchainmask = IWN_ANT_ABC; + + return 0; +} + +static int +iwn5000_attach(struct iwn_softc *sc, uint16_t pid) +{ + struct iwn_ops *ops = &sc->ops; + + ops->load_firmware = iwn5000_load_firmware; + ops->read_eeprom = iwn5000_read_eeprom; + ops->post_alive = iwn5000_post_alive; + ops->nic_config = iwn5000_nic_config; + ops->update_sched = iwn5000_update_sched; + ops->get_temperature = iwn5000_get_temperature; + ops->get_rssi = iwn5000_get_rssi; + ops->set_txpower = iwn5000_set_txpower; + ops->init_gains = iwn5000_init_gains; + ops->set_gains = iwn5000_set_gains; + ops->add_node = iwn5000_add_node; + ops->tx_done = iwn5000_tx_done; + ops->ampdu_tx_start = iwn5000_ampdu_tx_start; + ops->ampdu_tx_stop = iwn5000_ampdu_tx_stop; + sc->ntxqs = IWN5000_NTXQUEUES; + sc->firstaggqueue = IWN5000_FIRSTAGGQUEUE; + sc->ndmachnls = IWN5000_NDMACHNLS; + sc->broadcast_id = IWN5000_ID_BROADCAST; + sc->rxonsz = IWN5000_RXONSZ; + sc->schedsz = IWN5000_SCHEDSZ; + sc->fw_text_maxsz = IWN5000_FW_TEXT_MAXSZ; + sc->fw_data_maxsz = IWN5000_FW_DATA_MAXSZ; + sc->fwsz = IWN5000_FWSZ; + sc->sched_txfact_addr = IWN5000_SCHED_TXFACT; + sc->reset_noise_gain = IWN5000_PHY_CALIB_RESET_NOISE_GAIN; + sc->noise_gain = IWN5000_PHY_CALIB_NOISE_GAIN; switch (sc->hw_type) { - case IWN_HW_REV_TYPE_4965: - sc->sc_hal = &iwn4965_hal; - sc->limits = &iwn4965_sensitivity_limits; - sc->fwname = "iwn4965fw"; - sc->txchainmask = IWN_ANT_AB; - sc->rxchainmask = IWN_ANT_ABC; - break; case IWN_HW_REV_TYPE_5100: - sc->sc_hal = &iwn5000_hal; sc->limits = &iwn5000_sensitivity_limits; sc->fwname = "iwn5000fw"; + /* Override chains masks, ROM is known to be broken. */ sc->txchainmask = IWN_ANT_B; sc->rxchainmask = IWN_ANT_AB; - sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO | - IWN_CALIB_TX_IQ | IWN_CALIB_TX_IQ_PERIODIC | - IWN_CALIB_BASE_BAND; break; case IWN_HW_REV_TYPE_5150: - sc->sc_hal = &iwn5000_hal; sc->limits = &iwn5150_sensitivity_limits; sc->fwname = "iwn5150fw"; - sc->txchainmask = IWN_ANT_A; - sc->rxchainmask = IWN_ANT_AB; - sc->calib_init = IWN_CALIB_DC | IWN_CALIB_LO | - IWN_CALIB_TX_IQ | IWN_CALIB_BASE_BAND; break; case IWN_HW_REV_TYPE_5300: case IWN_HW_REV_TYPE_5350: - sc->sc_hal = &iwn5000_hal; sc->limits = &iwn5000_sensitivity_limits; sc->fwname = "iwn5000fw"; - sc->txchainmask = IWN_ANT_ABC; - sc->rxchainmask = IWN_ANT_ABC; - sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO | - IWN_CALIB_TX_IQ | IWN_CALIB_TX_IQ_PERIODIC | - IWN_CALIB_BASE_BAND; break; case IWN_HW_REV_TYPE_1000: - sc->sc_hal = &iwn5000_hal; sc->limits = &iwn1000_sensitivity_limits; sc->fwname = "iwn1000fw"; - sc->txchainmask = IWN_ANT_A; - sc->rxchainmask = IWN_ANT_AB; - sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO | - IWN_CALIB_TX_IQ | IWN_CALIB_TX_IQ_PERIODIC | - IWN_CALIB_BASE_BAND; break; case IWN_HW_REV_TYPE_6000: - sc->sc_hal = &iwn5000_hal; sc->limits = &iwn6000_sensitivity_limits; sc->fwname = "iwn6000fw"; - switch (pci_get_device(sc->sc_dev)) { - case 0x422C: - case 0x4239: + if (pid == 0x422c || pid == 0x4239) { sc->sc_flags |= IWN_FLAG_INTERNAL_PA; + /* Override chains masks, ROM is known to be broken. */ sc->txchainmask = IWN_ANT_BC; sc->rxchainmask = IWN_ANT_BC; - break; - default: - sc->txchainmask = IWN_ANT_ABC; - sc->rxchainmask = IWN_ANT_ABC; - sc->calib_runtime = IWN_CALIB_DC; - break; } - sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO | - IWN_CALIB_TX_IQ | IWN_CALIB_BASE_BAND; break; case IWN_HW_REV_TYPE_6050: - sc->sc_hal = &iwn5000_hal; sc->limits = &iwn6000_sensitivity_limits; sc->fwname = "iwn6050fw"; + /* Override chains masks, ROM is known to be broken. */ sc->txchainmask = IWN_ANT_AB; sc->rxchainmask = IWN_ANT_AB; - sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO | - IWN_CALIB_TX_IQ | IWN_CALIB_BASE_BAND; - sc->calib_runtime = IWN_CALIB_DC; break; case IWN_HW_REV_TYPE_6005: - sc->sc_hal = &iwn5000_hal; sc->limits = &iwn6000_sensitivity_limits; - sc->fwname = "iwn6005fw"; - sc->txchainmask = IWN_ANT_AB; - sc->rxchainmask = IWN_ANT_AB; - sc->calib_init = IWN_CALIB_XTAL | IWN_CALIB_LO | - IWN_CALIB_TX_IQ | IWN_CALIB_BASE_BAND; - sc->calib_runtime = IWN_CALIB_DC; + if (pid != 0x0082 && pid != 0x0085) { + sc->fwname = "iwn6000g2bfw"; + sc->sc_flags |= IWN_FLAG_ADV_BTCOEX; + } else + sc->fwname = "iwn6000g2afw"; break; default: device_printf(sc->sc_dev, "adapter type %d not supported\n", sc->hw_type); - return NULL; + return ENOTSUP; } - return sc->sc_hal; + return 0; } /* @@ -804,11 +831,24 @@ iwn_radiotap_attach(struct iwn_softc *sc) IWN_RX_RADIOTAP_PRESENT); } +static void +iwn_sysctlattach(struct iwn_softc *sc) +{ + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); + struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); + +#ifdef IWN_DEBUG + sc->sc_debug = 0; + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "debug", CTLFLAG_RW, &sc->sc_debug, 0, "control debugging printfs"); +#endif +} + static struct ieee80211vap * iwn_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]) + 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 iwn_vap *ivp; struct ieee80211vap *vap; @@ -844,12 +884,12 @@ iwn_vap_delete(struct ieee80211vap *vap) } static int -iwn_cleanup(device_t dev) +iwn_detach(device_t dev) { struct iwn_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic; - int i; + int qid; if (ifp != NULL) { ic = ifp->if_l2com; @@ -859,23 +899,12 @@ iwn_cleanup(device_t dev) ieee80211_draintask(ic, &sc->sc_radiooff_task); iwn_stop(sc); - callout_drain(&sc->sc_timer_to); + callout_drain(&sc->watchdog_to); + callout_drain(&sc->calib_to); ieee80211_ifdetach(ic); } - iwn5000_free_calib_results(sc); - - /* Free DMA resources. */ - iwn_free_rx_ring(sc, &sc->rxq); - if (sc->sc_hal != NULL) - for (i = 0; i < sc->sc_hal->ntxqs; i++) - iwn_free_tx_ring(sc, &sc->txq[i]); - iwn_free_sched(sc); - iwn_free_kw(sc); - if (sc->ict != NULL) - iwn_free_ict(sc); - iwn_free_fwmem(sc); - + /* Uninstall interrupt handler. */ 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); @@ -883,6 +912,16 @@ iwn_cleanup(device_t dev) pci_release_msi(dev); } + /* Free DMA resources. */ + iwn_free_rx_ring(sc, &sc->rxq); + for (qid = 0; qid < sc->ntxqs; qid++) + iwn_free_tx_ring(sc, &sc->txq[qid]); + iwn_free_sched(sc); + iwn_free_kw(sc); + if (sc->ict != NULL) + iwn_free_ict(sc); + iwn_free_fwmem(sc); + if (sc->mem != NULL) bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); @@ -894,9 +933,46 @@ iwn_cleanup(device_t dev) } static int -iwn_detach(device_t dev) +iwn_shutdown(device_t dev) { - iwn_cleanup(dev); + struct iwn_softc *sc = device_get_softc(dev); + + iwn_stop(sc); + return 0; +} + +static int +iwn_suspend(device_t dev) +{ + struct iwn_softc *sc = device_get_softc(dev); + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + iwn_stop(sc); + if (vap != NULL) + ieee80211_stop(vap); + return 0; +} + +static int +iwn_resume(device_t dev) +{ + struct iwn_softc *sc = device_get_softc(dev); + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + /* Clear device-specific "PCI retry timeout" register (41h). */ + pci_write_config(dev, 0x41, 0, 1); + + if (ifp->if_flags & IFF_UP) { + iwn_init(sc); + if (vap != NULL) + ieee80211_init(vap); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + iwn_start(ifp); + } return 0; } @@ -911,7 +987,7 @@ iwn_nic_lock(struct iwn_softc *sc) /* Spin until we actually get the lock. */ for (ntries = 0; ntries < 1000; ntries++) { if ((IWN_READ(sc, IWN_GP_CNTRL) & - (IWN_GP_CNTRL_MAC_ACCESS_ENA | IWN_GP_CNTRL_SLEEP)) == + (IWN_GP_CNTRL_MAC_ACCESS_ENA | IWN_GP_CNTRL_SLEEP)) == IWN_GP_CNTRL_MAC_ACCESS_ENA) return 0; DELAY(10); @@ -1044,12 +1120,10 @@ iwn_init_otprom(struct iwn_softc *sc) int count, error; /* Wait for clock stabilization before accessing prph. */ - error = iwn_clock_wait(sc); - if (error != 0) + if ((error = iwn_clock_wait(sc)) != 0) return error; - error = iwn_nic_lock(sc); - if (error != 0) + if ((error = iwn_nic_lock(sc)) != 0) return error; iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ); DELAY(5); @@ -1094,9 +1168,9 @@ iwn_init_otprom(struct iwn_softc *sc) static int iwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count) { + uint8_t *out = data; uint32_t val, tmp; int ntries; - uint8_t *out = data; addr += sc->prom_base; for (; count > 0; count -= 2, addr++) { @@ -1144,58 +1218,57 @@ iwn_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) static int iwn_dma_contig_alloc(struct iwn_softc *sc, struct iwn_dma_info *dma, - void **kvap, bus_size_t size, bus_size_t alignment, int flags) + void **kvap, bus_size_t size, bus_size_t alignment) { int error; - dma->size = size; dma->tag = NULL; + dma->size = size; error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, - 1, size, flags, NULL, NULL, &dma->tag); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: bus_dma_tag_create failed, error %d\n", - __func__, error); + 1, size, BUS_DMA_NOWAIT, NULL, NULL, &dma->tag); + if (error != 0) goto fail; - } + error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, - flags | BUS_DMA_ZERO, &dma->map); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: bus_dmamem_alloc failed, error %d\n", __func__, error); + BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map); + if (error != 0) goto fail; - } - error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, - size, iwn_dma_map_addr, &dma->paddr, flags); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: bus_dmamap_load failed, error %d\n", __func__, error); + + error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, + iwn_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT); + if (error != 0) goto fail; - } + + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); if (kvap != NULL) *kvap = dma->vaddr; + return 0; -fail: - iwn_dma_contig_free(dma); + +fail: iwn_dma_contig_free(dma); return error; } static void iwn_dma_contig_free(struct iwn_dma_info *dma) { - if (dma->tag != NULL) { - if (dma->map != NULL) { - if (dma->paddr == 0) { - bus_dmamap_sync(dma->tag, dma->map, - BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(dma->tag, dma->map); - } + if (dma->map != NULL) { + if (dma->vaddr != NULL) { + bus_dmamap_sync(dma->tag, dma->map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dma->tag, dma->map); bus_dmamem_free(dma->tag, &dma->vaddr, dma->map); + dma->vaddr = NULL; } + bus_dmamap_destroy(dma->tag, dma->map); + dma->map = NULL; + } + if (dma->tag != NULL) { bus_dma_tag_destroy(dma->tag); + dma->tag = NULL; } } @@ -1203,8 +1276,8 @@ static int iwn_alloc_sched(struct iwn_softc *sc) { /* TX scheduler rings must be aligned on a 1KB boundary. */ - return iwn_dma_contig_alloc(sc, &sc->sched_dma, - (void **)&sc->sched, sc->sc_hal->schedsz, 1024, BUS_DMA_NOWAIT); + return iwn_dma_contig_alloc(sc, &sc->sched_dma, (void **)&sc->sched, + sc->schedsz, 1024); } static void @@ -1217,8 +1290,7 @@ static int iwn_alloc_kw(struct iwn_softc *sc) { /* "Keep Warm" page must be aligned on a 4KB boundary. */ - return iwn_dma_contig_alloc(sc, &sc->kw_dma, NULL, 4096, 4096, - BUS_DMA_NOWAIT); + return iwn_dma_contig_alloc(sc, &sc->kw_dma, NULL, 4096, 4096); } static void @@ -1231,8 +1303,8 @@ static int iwn_alloc_ict(struct iwn_softc *sc) { /* ICT table must be aligned on a 4KB boundary. */ - return iwn_dma_contig_alloc(sc, &sc->ict_dma, - (void **)&sc->ict, IWN_ICT_SIZE, 4096, BUS_DMA_NOWAIT); + return iwn_dma_contig_alloc(sc, &sc->ict_dma, (void **)&sc->ict, + IWN_ICT_SIZE, 4096); } static void @@ -1245,8 +1317,7 @@ static int iwn_alloc_fwmem(struct iwn_softc *sc) { /* Must be aligned on a 16-byte boundary. */ - return iwn_dma_contig_alloc(sc, &sc->fw_dma, NULL, - sc->sc_hal->fwsz, 16, BUS_DMA_NOWAIT); + return iwn_dma_contig_alloc(sc, &sc->fw_dma, NULL, sc->fwsz, 16); } static void @@ -1265,33 +1336,33 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) /* Allocate RX descriptors (256-byte aligned). */ size = IWN_RX_RING_COUNT * sizeof (uint32_t); - error = iwn_dma_contig_alloc(sc, &ring->desc_dma, - (void **)&ring->desc, size, 256, BUS_DMA_NOWAIT); + error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, + size, 256); if (error != 0) { device_printf(sc->sc_dev, - "%s: could not allocate Rx ring DMA memory, error %d\n", - __func__, error); - goto fail; - } - - error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, - BUS_SPACE_MAXADDR_32BIT, - BUS_SPACE_MAXADDR, NULL, NULL, MJUMPAGESIZE, 1, - MJUMPAGESIZE, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: bus_dma_tag_create_failed, error %d\n", + "%s: could not allocate RX ring DMA memory, error %d\n", __func__, error); goto fail; } /* Allocate RX status area (16-byte aligned). */ - error = iwn_dma_contig_alloc(sc, &ring->stat_dma, - (void **)&ring->stat, sizeof (struct iwn_rx_status), - 16, BUS_DMA_NOWAIT); + error = iwn_dma_contig_alloc(sc, &ring->stat_dma, (void **)&ring->stat, + sizeof (struct iwn_rx_status), 16); if (error != 0) { device_printf(sc->sc_dev, - "%s: could not allocate Rx status DMA memory, error %d\n", + "%s: could not allocate RX status DMA memory, error %d\n", + __func__, error); + goto fail; + } + + /* Create RX buffer DMA tag. */ + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + IWN_RBUF_SIZE, 1, IWN_RBUF_SIZE, BUS_DMA_NOWAIT, NULL, NULL, + &ring->data_dmat); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not create RX buf DMA tag, error %d\n", __func__, error); goto fail; } @@ -1306,42 +1377,40 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, - "%s: bus_dmamap_create failed, error %d\n", + "%s: could not create RX buf DMA map, error %d\n", __func__, error); goto fail; } - data->m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); + data->m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, + IWN_RBUF_SIZE); if (data->m == NULL) { device_printf(sc->sc_dev, - "%s: could not allocate rx mbuf\n", __func__); - error = ENOMEM; + "%s: could not allocate RX mbuf\n", __func__); + error = ENOBUFS; goto fail; } - /* Map page. */ error = bus_dmamap_load(ring->data_dmat, data->map, - mtod(data->m, caddr_t), MJUMPAGESIZE, - iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); + mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr, + &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, - "%s: bus_dmamap_load failed, error %d\n", - __func__, error); - m_freem(data->m); - error = ENOMEM; /* XXX unique code */ + "%s: can't not map mbuf, error %d\n", __func__, + error); goto fail; } - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_PREWRITE); /* Set physical address of RX buffer (256-byte aligned). */ ring->desc[i] = htole32(paddr >> 8); } + bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); + return 0; -fail: - iwn_free_rx_ring(sc, ring); + +fail: iwn_free_rx_ring(sc, ring); return error; } @@ -1359,11 +1428,6 @@ iwn_reset_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) DELAY(10); } iwn_nic_unlock(sc); -#ifdef IWN_DEBUG - if (ntries == 1000) - DPRINTF(sc, IWN_DEBUG_ANY, "%s\n", - "timeout resetting Rx ring"); -#endif } ring->cur = 0; sc->last_rx_valid = 0; @@ -1385,27 +1449,32 @@ iwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) BUS_DMASYNC_POSTREAD); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); + data->m = NULL; } if (data->map != NULL) bus_dmamap_destroy(ring->data_dmat, data->map); } + if (ring->data_dmat != NULL) { + bus_dma_tag_destroy(ring->data_dmat); + ring->data_dmat = NULL; + } } static int iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid) { - bus_size_t size; bus_addr_t paddr; + bus_size_t size; int i, error; ring->qid = qid; ring->queued = 0; ring->cur = 0; - /* Allocate TX descriptors (256-byte aligned.) */ - size = IWN_TX_RING_COUNT * sizeof(struct iwn_tx_desc); - error = iwn_dma_contig_alloc(sc, &ring->desc_dma, - (void **)&ring->desc, size, 256, BUS_DMA_NOWAIT); + /* Allocate TX descriptors (256-byte aligned). */ + size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_desc); + error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, + size, 256); if (error != 0) { device_printf(sc->sc_dev, "%s: could not allocate TX ring DMA memory, error %d\n", @@ -1413,16 +1482,9 @@ iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid) goto fail; } - /* - * We only use rings 0 through 4 (4 EDCA + cmd) so there is no need - * to allocate commands space for other rings. - */ - if (qid > 4) - return 0; - - size = IWN_TX_RING_COUNT * sizeof(struct iwn_tx_cmd); - error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, - (void **)&ring->cmd, size, 4, BUS_DMA_NOWAIT); + size = IWN_TX_RING_COUNT * sizeof (struct iwn_tx_cmd); + error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd, + size, 4); if (error != 0) { device_printf(sc->sc_dev, "%s: could not allocate TX cmd DMA memory, error %d\n", @@ -1431,12 +1493,12 @@ iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid) } error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, - BUS_SPACE_MAXADDR_32BIT, - BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, IWN_MAX_SCATTER - 1, - MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, + IWN_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, + &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, - "%s: bus_dma_tag_create_failed, error %d\n", + "%s: could not create TX buf DMA tag, error %d\n", __func__, error); goto fail; } @@ -1452,16 +1514,14 @@ iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid) error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, - "%s: bus_dmamap_create failed, error %d\n", + "%s: could not create TX buf DMA map, error %d\n", __func__, error); goto fail; } - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_PREWRITE); } return 0; -fail: - iwn_free_tx_ring(sc, ring); + +fail: iwn_free_tx_ring(sc, ring); return error; } @@ -1474,6 +1534,8 @@ iwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) struct iwn_tx_data *data = &ring->data[i]; if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; @@ -1508,6 +1570,10 @@ iwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) if (data->map != NULL) bus_dmamap_destroy(ring->data_dmat, data->map); } + if (ring->data_dmat != NULL) { + bus_dma_tag_destroy(ring->data_dmat); + ring->data_dmat = NULL; + } } static void @@ -1520,7 +1586,7 @@ iwn5000_ict_reset(struct iwn_softc *sc) memset(sc->ict, 0, IWN_ICT_SIZE); sc->ict_cur = 0; - /* Set physical address of ICT table (4KB aligned.) */ + /* Set physical address of ICT table (4KB aligned). */ DPRINTF(sc, IWN_DEBUG_RESET, "%s: enabling ICT\n", __func__); IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE | IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12); @@ -1538,9 +1604,9 @@ iwn5000_ict_reset(struct iwn_softc *sc) static int iwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) { - const struct iwn_hal *hal = sc->sc_hal; - int error; + struct iwn_ops *ops = &sc->ops; uint16_t val; + int error; /* Check whether adapter has an EEPROM or an OTPROM. */ if (sc->hw_type >= IWN_HW_REV_TYPE_1000 && @@ -1550,11 +1616,10 @@ iwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) (sc->sc_flags & IWN_FLAG_HAS_OTPROM) ? "OTPROM" : "EEPROM"); /* Adapter has to be powered on for EEPROM access to work. */ - error = iwn_apm_init(sc); - if (error != 0) { + if ((error = iwn_apm_init(sc)) != 0) { device_printf(sc->sc_dev, - "%s: could not power ON adapter, error %d\n", - __func__, error); + "%s: could not power ON adapter, error %d\n", __func__, + error); return error; } @@ -1562,17 +1627,13 @@ iwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) device_printf(sc->sc_dev, "%s: bad ROM signature\n", __func__); return EIO; } - error = iwn_eeprom_lock(sc); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not lock ROM, error %d\n", + if ((error = iwn_eeprom_lock(sc)) != 0) { + device_printf(sc->sc_dev, "%s: could not lock ROM, error %d\n", __func__, error); return error; } - if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) { - error = iwn_init_otprom(sc); - if (error != 0) { + if ((error = iwn_init_otprom(sc)) != 0) { device_printf(sc->sc_dev, "%s: could not initialize OTPROM, error %d\n", __func__, error); @@ -1580,15 +1641,26 @@ iwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) } } + iwn_read_prom_data(sc, IWN_EEPROM_SKU_CAP, &val, 2); + DPRINTF(sc, IWN_DEBUG_RESET, "SKU capabilities=0x%04x\n", le16toh(val)); + /* Check if HT support is bonded out. */ + if (val & htole16(IWN_EEPROM_SKU_CAP_11N)) + sc->sc_flags |= IWN_FLAG_HAS_11N; + iwn_read_prom_data(sc, IWN_EEPROM_RFCFG, &val, 2); sc->rfcfg = le16toh(val); DPRINTF(sc, IWN_DEBUG_RESET, "radio config=0x%04x\n", sc->rfcfg); + /* Read Tx/Rx chains from ROM unless it's known to be broken. */ + if (sc->txchainmask == 0) + sc->txchainmask = IWN_RFCFG_TXANTMSK(sc->rfcfg); + if (sc->rxchainmask == 0) + sc->rxchainmask = IWN_RFCFG_RXANTMSK(sc->rfcfg); /* Read MAC address. */ iwn_read_prom_data(sc, IWN_EEPROM_MAC, macaddr, 6); /* Read adapter-specific information from EEPROM. */ - hal->read_eeprom(sc); + ops->read_eeprom(sc); iwn_apm_stop(sc); /* Power OFF adapter. */ @@ -1600,14 +1672,14 @@ static void iwn4965_read_eeprom(struct iwn_softc *sc) { uint32_t addr; - int i; uint16_t val; + int i; - /* Read regulatory domain (4 ASCII characters.) */ + /* Read regulatory domain (4 ASCII characters). */ iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4); - /* Read the list of authorized channels (20MHz ones only.) */ - for (i = 0; i < 5; i++) { + /* Read the list of authorized channels (20MHz ones only). */ + for (i = 0; i < 7; i++) { addr = iwn4965_regulatory_bands[i]; iwn_read_eeprom_channels(sc, i, addr); } @@ -1682,20 +1754,23 @@ static void iwn5000_read_eeprom(struct iwn_softc *sc) { struct iwn5000_eeprom_calib_hdr hdr; - int32_t temp, volt; - uint32_t addr, base; - int i; + int32_t volt; + uint32_t base, addr; uint16_t val; + int i; - /* Read regulatory domain (4 ASCII characters.) */ + /* Read regulatory domain (4 ASCII characters). */ iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2); base = le16toh(val); iwn_read_prom_data(sc, base + IWN5000_EEPROM_DOMAIN, sc->eeprom_domain, 4); - /* Read the list of authorized channels (20MHz ones only.) */ - for (i = 0; i < 5; i++) { - addr = base + iwn5000_regulatory_bands[i]; + /* Read the list of authorized channels (20MHz ones only). */ + for (i = 0; i < 7; i++) { + if (sc->hw_type >= IWN_HW_REV_TYPE_6000) + addr = base + iwn6000_regulatory_bands[i]; + else + addr = base + iwn5000_regulatory_bands[i]; iwn_read_eeprom_channels(sc, i, addr); } @@ -1707,19 +1782,25 @@ iwn5000_read_eeprom(struct iwn_softc *sc) base = le16toh(val); iwn_read_prom_data(sc, base, &hdr, sizeof hdr); DPRINTF(sc, IWN_DEBUG_CALIBRATE, - "%s: calib version=%u pa type=%u voltage=%u\n", - __func__, hdr.version, hdr.pa_type, le16toh(hdr.volt)); + "%s: calib version=%u pa type=%u voltage=%u\n", __func__, + hdr.version, hdr.pa_type, le16toh(hdr.volt)); sc->calib_ver = hdr.version; if (sc->hw_type == IWN_HW_REV_TYPE_5150) { /* Compute temperature offset. */ iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2); - temp = le16toh(val); + sc->eeprom_temp = le16toh(val); iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2); volt = le16toh(val); - sc->temp_off = temp - (volt / -5); + sc->temp_off = sc->eeprom_temp - (volt / -5); DPRINTF(sc, IWN_DEBUG_CALIBRATE, "temp=%d volt=%d offset=%dK\n", - temp, volt, sc->temp_off); + sc->eeprom_temp, volt, sc->temp_off); + } else { + /* Read crystal calibration. */ + iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL, + &sc->eeprom_crystal, sizeof (uint32_t)); + DPRINTF(sc, IWN_DEBUG_CALIBRATE, "crystal calibration 0x%08x\n", + le32toh(sc->eeprom_crystal)); } } @@ -1753,7 +1834,8 @@ iwn_read_eeprom_band(struct iwn_softc *sc, int n) struct iwn_eeprom_chan *channels = sc->eeprom_channels[n]; const struct iwn_chan_band *band = &iwn_bands[n]; struct ieee80211_channel *c; - int i, chan, nflags; + uint8_t chan; + int i, nflags; for (i = 0; i < band->nchan; i++) { if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) { @@ -1766,46 +1848,39 @@ iwn_read_eeprom_band(struct iwn_softc *sc, int n) chan = band->chan[i]; nflags = iwn_eeprom_channel_flags(&channels[i]); - DPRINTF(sc, IWN_DEBUG_RESET, - "add chan %d flags 0x%x maxpwr %d\n", - chan, channels[i].flags, channels[i].maxpwr); - c = &ic->ic_channels[ic->ic_nchans++]; c->ic_ieee = chan; c->ic_maxregpower = channels[i].maxpwr; c->ic_maxpower = 2*c->ic_maxregpower; - /* Save maximum allowed TX power for this channel. */ - sc->maxpwr[chan] = channels[i].maxpwr; - if (n == 0) { /* 2GHz band */ - c->ic_freq = ieee80211_ieee2mhz(chan, - IEEE80211_CHAN_G); - + c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_G); /* G =>'s B is supported */ c->ic_flags = IEEE80211_CHAN_B | nflags; - c = &ic->ic_channels[ic->ic_nchans++]; c[0] = c[-1]; c->ic_flags = IEEE80211_CHAN_G | nflags; } else { /* 5GHz band */ - c->ic_freq = ieee80211_ieee2mhz(chan, - IEEE80211_CHAN_A); + c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_A); c->ic_flags = IEEE80211_CHAN_A | nflags; - sc->sc_flags |= IWN_FLAG_HAS_5GHZ; } -#if 0 /* HT */ - /* XXX no constraints on using HT20 */ - /* add HT20, HT40 added separately */ - c = &ic->ic_channels[ic->ic_nchans++]; - c[0] = c[-1]; - c->ic_flags |= IEEE80211_CHAN_HT20; - /* XXX NARROW =>'s 1/2 and 1/4 width? */ -#endif + + /* Save maximum allowed TX power for this channel. */ + sc->maxpwr[chan] = channels[i].maxpwr; + + DPRINTF(sc, IWN_DEBUG_RESET, + "add chan %d flags 0x%x maxpwr %d\n", chan, + channels[i].flags, channels[i].maxpwr); + + if (sc->sc_flags & IWN_FLAG_HAS_11N) { + /* add HT20, HT40 added separately */ + c = &ic->ic_channels[ic->ic_nchans++]; + c[0] = c[-1]; + c->ic_flags |= IEEE80211_CHAN_HT20; + } } } -#if 0 /* HT */ static void iwn_read_eeprom_ht40(struct iwn_softc *sc, int n) { @@ -1814,55 +1889,59 @@ iwn_read_eeprom_ht40(struct iwn_softc *sc, int n) struct iwn_eeprom_chan *channels = sc->eeprom_channels[n]; const struct iwn_chan_band *band = &iwn_bands[n]; struct ieee80211_channel *c, *cent, *extc; - int i; + uint8_t chan; + int i, nflags; + + if (!(sc->sc_flags & IWN_FLAG_HAS_11N)) + return; for (i = 0; i < band->nchan; i++) { - if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID) || - !(channels[i].flags & IWN_EEPROM_CHAN_WIDE)) { + if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) { DPRINTF(sc, IWN_DEBUG_RESET, "skip chan %d flags 0x%x maxpwr %d\n", band->chan[i], channels[i].flags, channels[i].maxpwr); continue; } + chan = band->chan[i]; + nflags = iwn_eeprom_channel_flags(&channels[i]); + /* * Each entry defines an HT40 channel pair; find the * center channel, then the extension channel above. */ - cent = ieee80211_find_channel_byieee(ic, band->chan[i], - band->flags & ~IEEE80211_CHAN_HT); + cent = ieee80211_find_channel_byieee(ic, chan, + (n == 5 ? IEEE80211_CHAN_G : IEEE80211_CHAN_A)); if (cent == NULL) { /* XXX shouldn't happen */ device_printf(sc->sc_dev, - "%s: no entry for channel %d\n", - __func__, band->chan[i]); + "%s: no entry for channel %d\n", __func__, chan); continue; } extc = ieee80211_find_channel(ic, cent->ic_freq+20, - band->flags & ~IEEE80211_CHAN_HT); + (n == 5 ? IEEE80211_CHAN_G : IEEE80211_CHAN_A)); if (extc == NULL) { DPRINTF(sc, IWN_DEBUG_RESET, - "skip chan %d, extension channel not found\n", - band->chan[i]); + "%s: skip chan %d, extension channel not found\n", + __func__, chan); continue; } DPRINTF(sc, IWN_DEBUG_RESET, "add ht40 chan %d flags 0x%x maxpwr %d\n", - band->chan[i], channels[i].flags, channels[i].maxpwr); + chan, channels[i].flags, channels[i].maxpwr); c = &ic->ic_channels[ic->ic_nchans++]; c[0] = cent[0]; c->ic_extieee = extc->ic_ieee; c->ic_flags &= ~IEEE80211_CHAN_HT; - c->ic_flags |= IEEE80211_CHAN_HT40U; + c->ic_flags |= IEEE80211_CHAN_HT40U | nflags; c = &ic->ic_channels[ic->ic_nchans++]; c[0] = extc[0]; c->ic_extieee = cent->ic_ieee; c->ic_flags &= ~IEEE80211_CHAN_HT; - c->ic_flags |= IEEE80211_CHAN_HT40D; + c->ic_flags |= IEEE80211_CHAN_HT40D | nflags; } } -#endif static void iwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr) @@ -1875,31 +1954,86 @@ iwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr) if (n < 5) iwn_read_eeprom_band(sc, n); -#if 0 /* HT */ else iwn_read_eeprom_ht40(sc, n); -#endif ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); } +static struct iwn_eeprom_chan * +iwn_find_eeprom_channel(struct iwn_softc *sc, struct ieee80211_channel *c) +{ + int band, chan, i, j; + + if (IEEE80211_IS_CHAN_HT40(c)) { + band = IEEE80211_IS_CHAN_5GHZ(c) ? 6 : 5; + if (IEEE80211_IS_CHAN_HT40D(c)) + chan = c->ic_extieee; + else + chan = c->ic_ieee; + for (i = 0; i < iwn_bands[band].nchan; i++) { + if (iwn_bands[band].chan[i] == chan) + return &sc->eeprom_channels[band][i]; + } + } else { + for (j = 0; j < 5; j++) { + for (i = 0; i < iwn_bands[j].nchan; i++) { + if (iwn_bands[j].chan[i] == c->ic_ieee) + return &sc->eeprom_channels[j][i]; + } + } + } + return NULL; +} + +/* + * Enforce flags read from EEPROM. + */ +static int +iwn_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd, + int nchan, struct ieee80211_channel chans[]) +{ + struct iwn_softc *sc = ic->ic_ifp->if_softc; + int i; + + for (i = 0; i < nchan; i++) { + struct ieee80211_channel *c = &chans[i]; + struct iwn_eeprom_chan *channel; + + channel = iwn_find_eeprom_channel(sc, c); + if (channel == NULL) { + if_printf(ic->ic_ifp, + "%s: invalid channel %u freq %u/0x%x\n", + __func__, c->ic_ieee, c->ic_freq, c->ic_flags); + return EINVAL; + } + c->ic_flags |= iwn_eeprom_channel_flags(channel); + } + + return 0; +} + #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) static void iwn_read_eeprom_enhinfo(struct iwn_softc *sc) { struct iwn_eeprom_enhinfo enhinfo[35]; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211_channel *c; uint16_t val, base; int8_t maxpwr; - int i; + uint8_t flags; + int i, j; iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2); base = le16toh(val); iwn_read_prom_data(sc, base + IWN6000_EEPROM_ENHINFO, enhinfo, sizeof enhinfo); - memset(sc->enh_maxpwr, 0, sizeof sc->enh_maxpwr); for (i = 0; i < nitems(enhinfo); i++) { - if (enhinfo[i].chan == 0 || enhinfo[i].reserved != 0) + flags = enhinfo[i].flags; + if (!(flags & IWN_ENHINFO_VALID)) continue; /* Skip invalid entries. */ maxpwr = 0; @@ -1913,11 +2047,34 @@ iwn_read_eeprom_enhinfo(struct iwn_softc *sc) maxpwr = MAX(maxpwr, enhinfo[i].mimo2); else if (sc->ntxchains == 3) maxpwr = MAX(maxpwr, enhinfo[i].mimo3); - maxpwr /= 2; /* Convert half-dBm to dBm. */ - DPRINTF(sc, IWN_DEBUG_RESET, "enhinfo %d, maxpwr=%d\n", i, - maxpwr); - sc->enh_maxpwr[i] = maxpwr; + for (j = 0; j < ic->ic_nchans; j++) { + c = &ic->ic_channels[j]; + if ((flags & IWN_ENHINFO_5GHZ)) { + if (!IEEE80211_IS_CHAN_A(c)) + continue; + } else if ((flags & IWN_ENHINFO_OFDM)) { + if (!IEEE80211_IS_CHAN_G(c)) + continue; + } else if (!IEEE80211_IS_CHAN_B(c)) + continue; + if ((flags & IWN_ENHINFO_HT40)) { + if (!IEEE80211_IS_CHAN_HT40(c)) + continue; + } else { + if (IEEE80211_IS_CHAN_HT40(c)) + continue; + } + if (enhinfo[i].chan != 0 && + enhinfo[i].chan != c->ic_ieee) + continue; + + DPRINTF(sc, IWN_DEBUG_RESET, + "channel %d(%x), maxpwr %d\n", c->ic_ieee, + c->ic_flags, maxpwr / 2); + c->ic_maxregpower = maxpwr / 2; + c->ic_maxpower = maxpwr; + } } } @@ -1927,10 +2084,82 @@ iwn_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) return malloc(sizeof (struct iwn_node), M_80211_NODE,M_NOWAIT | M_ZERO); } +static __inline int +rate2plcp(int rate) +{ + switch (rate & 0xff) { + case 12: return 0xd; + case 18: return 0xf; + case 24: return 0x5; + case 36: return 0x7; + case 48: return 0x9; + case 72: return 0xb; + case 96: return 0x1; + case 108: return 0x3; + case 2: return 10; + case 4: return 20; + case 11: return 55; + case 22: return 110; + } + return 0; +} + +static void +iwn_newassoc(struct ieee80211_node *ni, int isnew) +{ +#define RV(v) ((v) & IEEE80211_RATE_VAL) + struct ieee80211com *ic = ni->ni_ic; + struct iwn_softc *sc = ic->ic_ifp->if_softc; + struct iwn_node *wn = (void *)ni; + uint8_t txant1, txant2; + int i, plcp, rate, ridx; + + /* Use the first valid TX antenna. */ + txant1 = IWN_LSB(sc->txchainmask); + txant2 = IWN_LSB(sc->txchainmask & ~txant1); + + if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { + ridx = ni->ni_rates.rs_nrates - 1; + for (i = ni->ni_htrates.rs_nrates - 1; i >= 0; i--) { + plcp = RV(ni->ni_htrates.rs_rates[i]) | IWN_RFLAG_MCS; + if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) { + plcp |= IWN_RFLAG_HT40; + if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40) + plcp |= IWN_RFLAG_SGI; + } else if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) + plcp |= IWN_RFLAG_SGI; + if (i > 7) + plcp |= IWN_RFLAG_ANT(txant1 | txant2); + else + plcp |= IWN_RFLAG_ANT(txant1); + if (ridx >= 0) { + rate = RV(ni->ni_rates.rs_rates[ridx]); + wn->ridx[rate] = plcp; + } + wn->ridx[IEEE80211_RATE_MCS | i] = plcp; + ridx--; + } + } else { + for (i = 0; i < ni->ni_rates.rs_nrates; i++) { + rate = RV(ni->ni_rates.rs_rates[i]); + plcp = rate2plcp(rate); + ridx = ic->ic_rt->rateCodeToIndex[rate]; + if (ridx < IWN_RIDX_OFDM6 && + IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + plcp |= IWN_RFLAG_CCK; + plcp |= IWN_RFLAG_ANT(txant1); + wn->ridx[rate] = htole32(plcp); + } + } +#undef RV +} + static int iwn_media_change(struct ifnet *ifp) { - int error = ieee80211_media_change(ifp); + int error; + + error = ieee80211_media_change(ifp); /* NB: only the fixed rate can change and that doesn't need a reset */ return (error == ENETRESET ? 0 : error); } @@ -1941,15 +2170,14 @@ iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) struct iwn_vap *ivp = IWN_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct iwn_softc *sc = ic->ic_ifp->if_softc; - int error; + int error = 0; DPRINTF(sc, IWN_DEBUG_STATE, "%s: %s -> %s\n", __func__, - ieee80211_state_name[vap->iv_state], - ieee80211_state_name[nstate]); + ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); IWN_LOCK(sc); - callout_stop(&sc->sc_timer_to); + callout_stop(&sc->calib_to); switch (nstate) { case IEEE80211_S_ASSOC: @@ -1966,17 +2194,20 @@ iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) */ sc->rxon.associd = 0; sc->rxon.filter &= ~htole32(IWN_FILTER_BSS); - iwn_calib_reset(sc); - error = iwn_auth(sc, vap); + sc->calib.state = IWN_CALIB_STATE_INIT; + + if ((error = iwn_auth(sc, vap)) != 0) { + device_printf(sc->sc_dev, + "%s: could not move to auth state\n", __func__); + } break; case IEEE80211_S_RUN: /* * RUN -> RUN transition; Just restart the timers. */ - if (vap->iv_state == IEEE80211_S_RUN && - vap->iv_opmode != IEEE80211_M_MONITOR) { - iwn_calib_reset(sc); + if (vap->iv_state == IEEE80211_S_RUN) { + sc->calib_cnt = 0; break; } @@ -1985,7 +2216,14 @@ iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) * which is done with a firmware cmd. We also defer * starting the timers until that work is done. */ - error = iwn_run(sc, vap); + if ((error = iwn_run(sc, vap)) != 0) { + device_printf(sc->sc_dev, + "%s: could not move to run state\n", __func__); + } + break; + + case IEEE80211_S_INIT: + sc->calib.state = IWN_CALIB_STATE_INIT; break; default: @@ -1993,9 +2231,32 @@ iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) } IWN_UNLOCK(sc); IEEE80211_LOCK(ic); + if (error != 0) + return error; return ivp->iv_newstate(vap, nstate, arg); } +static void +iwn_calib_timeout(void *arg) +{ + struct iwn_softc *sc = arg; + + IWN_LOCK_ASSERT(sc); + + /* Force automatic TX power calibration every 60 secs. */ + if (++sc->calib_cnt >= 120) { + uint32_t flags = 0; + + DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s\n", + "sending request for statistics"); + (void)iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, + sizeof flags, 1); + sc->calib_cnt = 0; + } + callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout, + sc); +} + /* * Process an RX_PHY firmware notification. This is usually immediately * followed by an MPDU_RX_DONE notification. @@ -2014,32 +2275,6 @@ iwn_rx_phy(struct iwn_softc *sc, struct iwn_rx_desc *desc, sc->last_rx_valid = 1; } -static void -iwn_timer_timeout(void *arg) -{ - struct iwn_softc *sc = arg; - uint32_t flags = 0; - - IWN_LOCK_ASSERT(sc); - - if (sc->calib_cnt && --sc->calib_cnt == 0) { - DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s\n", - "send statistics request"); - (void) iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, - sizeof flags, 1); - sc->calib_cnt = 60; /* do calibration every 60s */ - } - iwn_watchdog(sc); /* NB: piggyback tx watchdog */ - callout_reset(&sc->sc_timer_to, hz, iwn_timer_timeout, sc); -} - -static void -iwn_calib_reset(struct iwn_softc *sc) -{ - callout_reset(&sc->sc_timer_to, hz, iwn_timer_timeout, sc); - sc->calib_cnt = 60; /* do calibration every 60s */ -} - /* * Process an RX_DONE (4965AGN only) or MPDU_RX_DONE firmware notification. * Each MPDU_RX_DONE notification must be preceded by an RX_PHY one. @@ -2048,7 +2283,7 @@ static void iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, struct iwn_rx_data *data) { - const struct iwn_hal *hal = sc->sc_hal; + struct iwn_ops *ops = &sc->ops; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct iwn_rx_ring *ring = &sc->rxq; @@ -2066,10 +2301,8 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, if (!sc->last_rx_valid) { DPRINTF(sc, IWN_DEBUG_ANY, "%s: missing RX_PHY\n", __func__); - ifp->if_ierrors++; return; } - sc->last_rx_valid = 0; stat = &sc->last_rx_stat; } else stat = (struct iwn_rx_stat *)(desc + 1); @@ -2078,9 +2311,8 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, if (stat->cfg_phy_len > IWN_STAT_MAXLEN) { device_printf(sc->sc_dev, - "%s: invalid rx statistic header, len %d\n", - __func__, stat->cfg_phy_len); - ifp->if_ierrors++; + "%s: invalid RX statistic header, len %d\n", __func__, + stat->cfg_phy_len); return; } if (desc->type == IWN_MPDU_RX_DONE) { @@ -2096,7 +2328,7 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, /* Discard frames with a bad FCS early. */ if ((flags & IWN_RX_NOERROR) != IWN_RX_NOERROR) { - DPRINTF(sc, IWN_DEBUG_RECV, "%s: rx flags error %x\n", + DPRINTF(sc, IWN_DEBUG_RECV, "%s: RX flags error %x\n", __func__, flags); ifp->if_ierrors++; return; @@ -2109,8 +2341,7 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, return; } - /* XXX don't need mbuf, just dma buffer */ - m1 = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); + m1 = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, IWN_RBUF_SIZE); if (m1 == NULL) { DPRINTF(sc, IWN_DEBUG_ANY, "%s: no mbuf to restock ring\n", __func__); @@ -2119,13 +2350,24 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, } bus_dmamap_unload(ring->data_dmat, data->map); - error = bus_dmamap_load(ring->data_dmat, data->map, - mtod(m1, caddr_t), MJUMPAGESIZE, - iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); + error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, void *), + IWN_RBUF_SIZE, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "%s: bus_dmamap_load failed, error %d\n", __func__, error); m_freem(m1); + + /* Try to reload the old mbuf. */ + error = bus_dmamap_load(ring->data_dmat, data->map, + mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr, + &paddr, BUS_DMA_NOWAIT); + if (error != 0 && error != EFBIG) { + panic("%s: could not load old RX mbuf", __func__); + } + /* Physical address may have changed. */ + ring->desc[ring->cur] = htole32(paddr >> 8); + bus_dmamap_sync(ring->data_dmat, ring->desc_dma.map, + BUS_DMASYNC_PREWRITE); ifp->if_ierrors++; return; } @@ -2142,21 +2384,23 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, m->m_data = head; m->m_pkthdr.len = m->m_len = len; - rssi = hal->get_rssi(sc, stat); - /* Grab a reference to the source node. */ wh = mtod(m, struct ieee80211_frame *); ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); nf = (ni != NULL && ni->ni_vap->iv_state == IEEE80211_S_RUN && (ic->ic_flags & IEEE80211_F_SCAN) == 0) ? sc->noise : -95; + rssi = ops->get_rssi(sc, stat); + if (ieee80211_radiotap_active(ic)) { struct iwn_rx_radiotap_header *tap = &sc->sc_rxtap; - tap->wr_tsft = htole64(stat->tstamp); tap->wr_flags = 0; if (stat->flags & htole16(IWN_STAT_FLAG_SHPREAMBLE)) tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + tap->wr_dbm_antsignal = (int8_t)rssi; + tap->wr_dbm_antnoise = (int8_t)nf; + tap->wr_tsft = stat->tstamp; switch (stat->rate) { /* CCK rates. */ case 10: tap->wr_rate = 2; break; @@ -2175,36 +2419,130 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, /* Unknown rate: should not happen. */ default: tap->wr_rate = 0; } - tap->wr_dbm_antsignal = rssi; - tap->wr_dbm_antnoise = nf; } IWN_UNLOCK(sc); /* Send the frame to the 802.11 layer. */ if (ni != NULL) { - (void) ieee80211_input(ni, m, rssi - nf, nf); + if (ni->ni_flags & IEEE80211_NODE_HT) + m->m_flags |= M_AMPDU; + (void)ieee80211_input(ni, m, rssi - nf, nf); /* Node is no longer needed. */ ieee80211_free_node(ni); } else - (void) ieee80211_input_all(ic, m, rssi - nf, nf); + (void)ieee80211_input_all(ic, m, rssi - nf, nf); IWN_LOCK(sc); } -#if 0 /* HT */ /* Process an incoming Compressed BlockAck. */ static void iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc, struct iwn_rx_data *data) { + struct ifnet *ifp = sc->sc_ifp; + struct iwn_node *wn; + struct ieee80211_node *ni; struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1); struct iwn_tx_ring *txq; + struct ieee80211_tx_ampdu *tap; + uint64_t bitmap; + uint8_t tid; + int ackfailcnt = 0, i, shift; - txq = &sc->txq[letoh16(ba->qid)]; - /* XXX TBD */ + bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); + + txq = &sc->txq[le16toh(ba->qid)]; + tap = sc->qid2tap[le16toh(ba->qid)]; + tid = WME_AC_TO_TID(tap->txa_ac); + ni = tap->txa_ni; + wn = (void *)ni; + + if (wn->agg[tid].bitmap == 0) + return; + + shift = wn->agg[tid].startidx - ((le16toh(ba->seq) >> 4) & 0xff); + if (shift < 0) + shift += 0x100; + + if (wn->agg[tid].nframes > (64 - shift)) + return; + + bitmap = (le64toh(ba->bitmap) >> shift) & wn->agg[tid].bitmap; + for (i = 0; bitmap; i++) { + if ((bitmap & 1) == 0) { + ifp->if_oerrors++; + ieee80211_ratectl_tx_complete(ni->ni_vap, ni, + IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL); + } else { + ifp->if_opackets++; + ieee80211_ratectl_tx_complete(ni->ni_vap, ni, + IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL); + } + bitmap >>= 1; + } +} + +/* + * Process a CALIBRATION_RESULT notification sent by the initialization + * firmware on response to a CMD_CALIB_CONFIG command (5000 only). + */ +static void +iwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc, + struct iwn_rx_data *data) +{ + struct iwn_phy_calib *calib = (struct iwn_phy_calib *)(desc + 1); + int len, idx = -1; + + /* Runtime firmware should not send such a notification. */ + if (sc->sc_flags & IWN_FLAG_CALIB_DONE) + return; + + len = (le32toh(desc->len) & 0x3fff) - 4; + bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); + + switch (calib->code) { + case IWN5000_PHY_CALIB_DC: + if ((sc->sc_flags & IWN_FLAG_INTERNAL_PA) == 0 && + (sc->hw_type == IWN_HW_REV_TYPE_5150 || + sc->hw_type >= IWN_HW_REV_TYPE_6000) && + sc->hw_type != IWN_HW_REV_TYPE_6050) + idx = 0; + break; + case IWN5000_PHY_CALIB_LO: + idx = 1; + break; + case IWN5000_PHY_CALIB_TX_IQ: + idx = 2; + break; + case IWN5000_PHY_CALIB_TX_IQ_PERIODIC: + if (sc->hw_type < IWN_HW_REV_TYPE_6000 && + sc->hw_type != IWN_HW_REV_TYPE_5150) + idx = 3; + break; + case IWN5000_PHY_CALIB_BASE_BAND: + idx = 4; + break; + } + if (idx == -1) /* Ignore other results. */ + return; + + /* Save calibration result. */ + if (sc->calibcmd[idx].buf != NULL) + free(sc->calibcmd[idx].buf, M_DEVBUF); + sc->calibcmd[idx].buf = malloc(len, M_DEVBUF, M_NOWAIT); + if (sc->calibcmd[idx].buf == NULL) { + DPRINTF(sc, IWN_DEBUG_CALIBRATE, + "not enough memory for calibration result %d\n", + calib->code); + return; + } + DPRINTF(sc, IWN_DEBUG_CALIBRATE, + "saving calibration result code=%d len=%d\n", calib->code, len); + sc->calibcmd[idx].len = len; + memcpy(sc->calibcmd[idx].buf, calib, len); } -#endif /* * Process an RX_STATISTICS or BEACON_STATISTICS firmware notification. @@ -2214,7 +2552,7 @@ static void iwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc, struct iwn_rx_data *data) { - const struct iwn_hal *hal = sc->sc_hal; + struct iwn_ops *ops = &sc->ops; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); @@ -2222,24 +2560,26 @@ iwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc, struct iwn_stats *stats = (struct iwn_stats *)(desc + 1); int temp; - /* Beacon stats are meaningful only when associated and not scanning. */ + /* Ignore statistics received during a scan. */ if (vap->iv_state != IEEE80211_S_RUN || (ic->ic_flags & IEEE80211_F_SCAN)) return; bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); - DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: cmd %d\n", __func__, desc->type); - iwn_calib_reset(sc); /* Reset TX power calibration timeout. */ + + DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received statistics, cmd %d\n", + __func__, desc->type); + sc->calib_cnt = 0; /* Reset TX power calibration timeout. */ /* Test if temperature has changed. */ if (stats->general.temp != sc->rawtemp) { /* Convert "raw" temperature to degC. */ sc->rawtemp = stats->general.temp; - temp = hal->get_temperature(sc); + temp = ops->get_temperature(sc); DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d\n", __func__, temp); - /* Update TX power if need be (4965AGN only.) */ + /* Update TX power if need be (4965AGN only). */ if (sc->hw_type == IWN_HW_REV_TYPE_4965) iwn4965_power_calibration(sc, temp); } @@ -2272,7 +2612,11 @@ iwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, struct iwn_rx_data *data) { struct iwn4965_tx_stat *stat = (struct iwn4965_tx_stat *)(desc + 1); - struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf]; + struct iwn_tx_ring *ring; + int qid; + + qid = desc->qid & 0xf; + ring = &sc->txq[qid]; DPRINTF(sc, IWN_DEBUG_XMIT, "%s: " "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n", @@ -2281,7 +2625,13 @@ iwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, le32toh(stat->status)); bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); - iwn_tx_done(sc, desc, stat->ackfailcnt, le32toh(stat->status) & 0xff); + if (qid >= sc->firstaggqueue) { + iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes, + &stat->status); + } else { + iwn_tx_done(sc, desc, stat->ackfailcnt, + le32toh(stat->status) & 0xff); + } } static void @@ -2289,7 +2639,11 @@ iwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, struct iwn_rx_data *data) { struct iwn5000_tx_stat *stat = (struct iwn5000_tx_stat *)(desc + 1); - struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf]; + struct iwn_tx_ring *ring; + int qid; + + qid = desc->qid & 0xf; + ring = &sc->txq[qid]; DPRINTF(sc, IWN_DEBUG_XMIT, "%s: " "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n", @@ -2303,7 +2657,13 @@ iwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, #endif bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); - iwn_tx_done(sc, desc, stat->ackfailcnt, le16toh(stat->status) & 0xff); + if (qid >= sc->firstaggqueue) { + iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes, + &stat->status); + } else { + iwn_tx_done(sc, desc, stat->ackfailcnt, + le16toh(stat->status) & 0xff); + } } /* @@ -2355,11 +2715,12 @@ iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt, /* * Update rate control statistics for the node. */ - if (status & 0x80) { + if (status & IWN_TX_FAIL) { ifp->if_oerrors++; ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL); } else { + ifp->if_opackets++; ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL); } @@ -2394,6 +2755,8 @@ iwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc) /* If the command was mapped in an mbuf, free it. */ if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; @@ -2401,12 +2764,103 @@ iwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc) wakeup(&ring->desc[desc->idx]); } +static void +iwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes, + void *stat) +{ + struct ifnet *ifp = sc->sc_ifp; + struct iwn_tx_ring *ring = &sc->txq[qid]; + struct iwn_tx_data *data; + struct mbuf *m; + struct iwn_node *wn; + struct ieee80211_node *ni; + struct ieee80211vap *vap; + struct ieee80211_tx_ampdu *tap; + uint64_t bitmap; + uint32_t *status = stat; + uint16_t *aggstatus = stat; + uint8_t tid; + int bit, i, lastidx, seqno, shift, start; + +#ifdef NOT_YET + if (nframes == 1) { + if ((*status & 0xff) != 1 && (*status & 0xff) != 2) + printf("ieee80211_send_bar()\n"); + } +#endif + + bitmap = 0; + start = idx; + for (i = 0; i < nframes; i++) { + if (le16toh(aggstatus[i * 2]) & 0xc) + continue; + + idx = le16toh(aggstatus[2*i + 1]) & 0xff; + bit = idx - start; + shift = 0; + if (bit >= 64) { + shift = 0x100 - idx + start; + bit = 0; + start = idx; + } else if (bit <= -64) + bit = 0x100 - start + idx; + else if (bit < 0) { + shift = start - idx; + start = idx; + bit = 0; + } + bitmap = bitmap << shift; + bitmap |= 1ULL << bit; + } + tap = sc->qid2tap[qid]; + tid = WME_AC_TO_TID(tap->txa_ac); + wn = (void *)tap->txa_ni; + wn->agg[tid].bitmap = bitmap; + wn->agg[tid].startidx = start; + wn->agg[tid].nframes = nframes; + + seqno = le32toh(*(status + nframes)) & 0xfff; + for (lastidx = (seqno & 0xff); ring->read != lastidx;) { + data = &ring->data[ring->read]; + + KASSERT(data->ni != NULL, ("no node")); + + /* Unmap and free mbuf. */ + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dmat, data->map); + m = data->m, data->m = NULL; + ni = data->ni, data->ni = NULL; + vap = ni->ni_vap; + + if (m->m_flags & M_TXCB) + ieee80211_process_callback(ni, m, 1); + + m_freem(m); + ieee80211_free_node(ni); + + ring->queued--; + ring->read = (ring->read + 1) % IWN_TX_RING_COUNT; + } + + sc->sc_tx_timer = 0; + if (ring->queued < IWN_TX_RING_LOMARK) { + sc->qfullmsk &= ~(1 << ring->qid); + if (sc->qfullmsk == 0 && + (ifp->if_drv_flags & IFF_DRV_OACTIVE)) { + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + iwn_start_locked(ifp); + } + } +} + /* * Process an INT_FH_RX or INT_SW_RX interrupt. */ static void iwn_notif_intr(struct iwn_softc *sc) { + struct iwn_ops *ops = &sc->ops; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); @@ -2444,16 +2898,14 @@ iwn_notif_intr(struct iwn_softc *sc) iwn_rx_done(sc, desc, data); break; -#if 0 /* HT */ case IWN_RX_COMPRESSED_BA: /* A Compressed BlockAck has been received. */ iwn_rx_compressed_ba(sc, desc, data); break; -#endif case IWN_TX_DONE: /* An 802.11 frame has been transmitted. */ - sc->sc_hal->tx_done(sc, desc, data); + ops->tx_done(sc, desc, data); break; case IWN_RX_STATISTICS: @@ -2471,23 +2923,22 @@ iwn_notif_intr(struct iwn_softc *sc) BUS_DMASYNC_POSTREAD); misses = le32toh(miss->consecutive); - /* XXX not sure why we're notified w/ zero */ - if (misses == 0) - break; DPRINTF(sc, IWN_DEBUG_STATE, "%s: beacons missed %d/%d\n", __func__, misses, le32toh(miss->total)); - /* * If more than 5 consecutive beacons are missed, * reinitialize the sensitivity state machine. */ - if (vap->iv_state == IEEE80211_S_RUN && misses > 5) - (void) iwn_init_sensitivity(sc); - if (misses >= vap->iv_bmissthreshold) { - IWN_UNLOCK(sc); - ieee80211_beacon_miss(ic); - IWN_LOCK(sc); + if (vap->iv_state == IEEE80211_S_RUN && + (ic->ic_flags & IEEE80211_F_SCAN) == 0) { + if (misses > 5) + (void)iwn_init_sensitivity(sc); + if (misses >= vap->iv_bmissthreshold) { + IWN_UNLOCK(sc); + ieee80211_beacon_miss(ic); + IWN_LOCK(sc); + } } break; } @@ -2561,7 +3012,7 @@ iwn_notif_intr(struct iwn_softc *sc) break; } case IWN5000_CALIBRATION_RESULT: - iwn5000_rx_calib_result(sc, desc, data); + iwn5000_rx_calib_results(sc, desc, data); break; case IWN5000_CALIBRATION_DONE: @@ -2592,7 +3043,7 @@ iwn_wakeup_intr(struct iwn_softc *sc) /* Wakeup RX and TX rings. */ IWN_WRITE(sc, IWN_FH_RX_WPTR, sc->rxq.cur & ~7); - for (qid = 0; qid < sc->sc_hal->ntxqs; qid++) { + for (qid = 0; qid < sc->ntxqs; qid++) { struct iwn_tx_ring *ring = &sc->txq[qid]; IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | ring->cur); } @@ -2623,7 +3074,6 @@ iwn_rftoggle_intr(struct iwn_softc *sc) static void iwn_fatal_intr(struct iwn_softc *sc) { - const struct iwn_hal *hal = sc->sc_hal; struct iwn_fw_dump dump; int i; @@ -2635,14 +3085,13 @@ iwn_fatal_intr(struct iwn_softc *sc) /* Check that the error log address is valid. */ if (sc->errptr < IWN_FW_DATA_BASE || sc->errptr + sizeof (dump) > - IWN_FW_DATA_BASE + hal->fw_data_maxsz) { - printf("%s: bad firmware error log address 0x%08x\n", - __func__, sc->errptr); + IWN_FW_DATA_BASE + sc->fw_data_maxsz) { + printf("%s: bad firmware error log address 0x%08x\n", __func__, + sc->errptr); return; } if (iwn_nic_lock(sc) != 0) { - printf("%s: could not read firmware error log\n", - __func__); + printf("%s: could not read firmware error log\n", __func__); return; } /* Read firmware error log from SRAM. */ @@ -2651,8 +3100,7 @@ iwn_fatal_intr(struct iwn_softc *sc) iwn_nic_unlock(sc); if (dump.valid == 0) { - printf("%s: firmware error log is empty\n", - __func__); + printf("%s: firmware error log is empty\n", __func__); return; } printf("firmware error log:\n"); @@ -2672,7 +3120,7 @@ iwn_fatal_intr(struct iwn_softc *sc) /* Dump driver status (TX and RX rings) while we're here. */ printf("driver status:\n"); - for (i = 0; i < hal->ntxqs; i++) { + for (i = 0; i < sc->ntxqs; i++) { struct iwn_tx_ring *ring = &sc->txq[i]; printf(" tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n", i, ring->qid, ring->cur, ring->queued); @@ -2738,6 +3186,9 @@ iwn_intr(void *arg) __func__); } if (r1 & (IWN_INT_SW_ERR | IWN_INT_HW_ERR)) { + device_printf(sc->sc_dev, "%s: fatal firmware error\n", + __func__); + /* Dump firmware error log and stop. */ iwn_fatal_intr(sc); ifp->if_flags &= ~IFF_UP; iwn_stop_locked(sc); @@ -2781,7 +3232,7 @@ done: /* * Update TX scheduler ring when transmitting an 802.11 frame (4965AGN and - * 5000 adapters use a slightly different format.) + * 5000 adapters use a slightly different format). */ static void iwn4965_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id, @@ -2806,7 +3257,6 @@ iwn5000_update_sched(struct iwn_softc *sc, int qid, int idx, uint8_t id, uint16_t *w = &sc->sched[qid * IWN5000_SCHED_COUNT + idx]; *w = htole16(id << 12 | (len + 8)); - bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, BUS_DMASYNC_PREWRITE); if (idx < IWN_SCHED_WINSZ) { @@ -2833,40 +3283,28 @@ iwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx) } #endif -static uint8_t -iwn_plcp_signal(int rate) { - int i; - - for (i = 0; i < IWN_RIDX_MAX + 1; i++) { - if (rate == iwn_rates[i].rate) - return i; - } - - return 0; -} - static int -iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, - struct iwn_tx_ring *ring) +iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { - const struct iwn_hal *hal = sc->sc_hal; + struct iwn_ops *ops = &sc->ops; const struct ieee80211_txparam *tp; - const struct iwn_rate *rinfo; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct iwn_node *wn = (void *)ni; + struct iwn_tx_ring *ring; struct iwn_tx_desc *desc; struct iwn_tx_data *data; struct iwn_tx_cmd *cmd; struct iwn_cmd_data *tx; struct ieee80211_frame *wh; struct ieee80211_key *k = NULL; - struct mbuf *mnew; - bus_dma_segment_t segs[IWN_MAX_SCATTER]; + struct mbuf *m1; uint32_t flags; + uint16_t qos; u_int hdrlen; - int totlen, error, pad, nsegs = 0, i, rate; - uint8_t ridx, type, txant; + bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER]; + uint8_t tid, ridx, txant, type; + int ac, i, totlen, error, pad, nsegs = 0, rate; IWN_LOCK_ASSERT(sc); @@ -2874,6 +3312,27 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, hdrlen = ieee80211_anyhdrsize(wh); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + /* Select EDCA Access Category and TX ring for this frame. */ + if (IEEE80211_QOS_HAS_SEQ(wh)) { + qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; + tid = qos & IEEE80211_QOS_TID; + } else { + qos = 0; + tid = 0; + } + ac = M_WME_GETAC(m); + + if (IEEE80211_QOS_HAS_SEQ(wh) && + IEEE80211_AMPDU_RUNNING(&ni->ni_tx_ampdu[ac])) { + struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac]; + + ring = &sc->txq[*(int *)tap->txa_private]; + *(uint16_t *)wh->i_seq = + htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT); + ni->ni_txseqs[tid]++; + } else { + ring = &sc->txq[ac]; + } desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; @@ -2890,17 +3349,17 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, (void) ieee80211_ratectl_rate(ni, NULL, 0); rate = ni->ni_txrate; } - ridx = iwn_plcp_signal(rate); - rinfo = &iwn_rates[ridx]; + ridx = ic->ic_rt->rateCodeToIndex[rate]; /* Encrypt the frame if need be. */ if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + /* Retrieve key for TX. */ k = ieee80211_crypto_encap(ni, m); if (k == NULL) { m_freem(m); return ENOBUFS; } - /* Packet header may have moved, reset our local pointer. */ + /* 802.11 header may have moved. */ wh = mtod(m, struct ieee80211_frame *); } totlen = m->m_pkthdr.len; @@ -2909,7 +3368,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, struct iwn_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; - tap->wt_rate = rinfo->rate; + tap->wt_rate = rate; if (k != NULL) tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; @@ -2928,8 +3387,12 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, tx->scratch = 0; /* clear "scratch" area */ flags = 0; - if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) - flags |= IWN_TX_NEED_ACK; + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + /* Unicast frame, check if an ACK is expected. */ + if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != + IEEE80211_QOS_ACKPOLICY_NOACK) + flags |= IWN_TX_NEED_ACK; + } if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR)) @@ -2962,7 +3425,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, if (IEEE80211_IS_MULTICAST(wh->i_addr1) || type != IEEE80211_FC0_TYPE_DATA) - tx->id = hal->broadcast_id; + tx->id = sc->broadcast_id; else tx->id = wn->id; @@ -2972,7 +3435,6 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, /* Tell HW to set timestamp in probe responses. */ if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) flags |= IWN_TX_INSERT_TSTAMP; - if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) tx->timeout = htole16(3); @@ -2989,23 +3451,21 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, pad = 0; tx->len = htole16(totlen); - tx->tid = 0; + tx->tid = tid; tx->rts_ntries = 60; tx->data_ntries = 15; tx->lifetime = htole32(IWN_LIFETIME_INFINITE); - tx->plcp = rinfo->plcp; - tx->rflags = rinfo->flags; - if (tx->id == hal->broadcast_id) { + tx->rate = wn->ridx[rate]; + if (tx->id == sc->broadcast_id) { /* Group or management frame. */ tx->linkq = 0; /* XXX Alternate between antenna A and B? */ txant = IWN_LSB(sc->txchainmask); - tx->rflags |= IWN_RFLAG_ANT(txant); + tx->rate |= htole32(IWN_RFLAG_ANT(txant)); } else { - tx->linkq = IWN_RIDX_OFDM54 - ridx; + tx->linkq = ni->ni_rates.rs_nrates - ridx - 1; flags |= IWN_TX_LINKQ; /* enable MRR */ } - /* Set physical address of "scratch area". */ tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr)); tx->hiaddr = IWN_HIADDR(data->scratch_paddr); @@ -3018,26 +3478,30 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, tx->security = 0; tx->flags = htole32(flags); - if (m->m_len > 0) { - error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, - m, segs, &nsegs, BUS_DMA_NOWAIT); - if (error == EFBIG) { - /* too many fragments, linearize */ - mnew = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER); - if (mnew == NULL) { - device_printf(sc->sc_dev, - "%s: could not defrag mbuf\n", __func__); - m_freem(m); - return ENOBUFS; - } - m = mnew; - error = bus_dmamap_load_mbuf_sg(ring->data_dmat, - data->map, m, segs, &nsegs, BUS_DMA_NOWAIT); + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs, + &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + if (error != EFBIG) { + device_printf(sc->sc_dev, + "%s: can't map mbuf (error %d)\n", __func__, error); + m_freem(m); + return error; } + /* Too many DMA segments, linearize mbuf. */ + m1 = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER); + if (m1 == NULL) { + device_printf(sc->sc_dev, + "%s: could not defrag mbuf\n", __func__); + m_freem(m); + return ENOBUFS; + } + m = m1; + + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, + segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, - "%s: bus_dmamap_load_mbuf_sg failed, error %d\n", - __func__, error); + "%s: can't map mbuf (error %d)\n", __func__, error); m_freem(m); return error; } @@ -3050,16 +3514,20 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs); /* Fill TX descriptor. */ - desc->nsegs = 1 + nsegs; + desc->nsegs = 1; + if (m->m_len != 0) + desc->nsegs += nsegs; /* First DMA segment is used by the TX command. */ desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr)); desc->segs[0].len = htole16(IWN_HIADDR(data->cmd_paddr) | (4 + sizeof (*tx) + hdrlen + pad) << 4); /* Other DMA segments are for data payload. */ + seg = &segs[0]; for (i = 1; i <= nsegs; i++) { - desc->segs[i].addr = htole32(IWN_LOADDR(segs[i - 1].ds_addr)); - desc->segs[i].len = htole16(IWN_HIADDR(segs[i - 1].ds_addr) | - segs[i - 1].ds_len << 4); + desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr)); + desc->segs[i].len = htole16(IWN_HIADDR(seg->ds_addr) | + seg->ds_len << 4); + seg++; } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); @@ -3068,10 +3536,9 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); -#ifdef notyet /* Update TX scheduler. */ - hal->update_sched(sc, ring->qid, ring->cur, tx->id, totlen); -#endif + if (ring->qid >= sc->firstaggqueue) + ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen); /* Kick TX ring. */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; @@ -3086,25 +3553,23 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, static int iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, - struct ieee80211_node *ni, struct iwn_tx_ring *ring, - const struct ieee80211_bpf_params *params) + struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { - const struct iwn_hal *hal = sc->sc_hal; - const struct iwn_rate *rinfo; + struct iwn_ops *ops = &sc->ops; struct ifnet *ifp = sc->sc_ifp; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ifp->if_l2com; struct iwn_tx_cmd *cmd; struct iwn_cmd_data *tx; struct ieee80211_frame *wh; + struct iwn_tx_ring *ring; struct iwn_tx_desc *desc; struct iwn_tx_data *data; - struct mbuf *mnew; - bus_addr_t paddr; - bus_dma_segment_t segs[IWN_MAX_SCATTER]; + struct mbuf *m1; + bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER]; uint32_t flags; u_int hdrlen; - int totlen, error, pad, nsegs = 0, i, rate; + int ac, totlen, error, pad, nsegs = 0, i, rate; uint8_t ridx, type, txant; IWN_LOCK_ASSERT(sc); @@ -3113,18 +3578,20 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, hdrlen = ieee80211_anyhdrsize(wh); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + ac = params->ibp_pri & 3; + + ring = &sc->txq[ac]; desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; /* Choose a TX rate index. */ rate = params->ibp_rate0; - if (!ieee80211_isratevalid(ic->ic_rt, rate)) { + ridx = ic->ic_rt->rateCodeToIndex[rate]; + if (ridx == (uint8_t)-1) { /* XXX fall back to mcast/mgmt rate? */ m_freem(m); return EINVAL; } - ridx = iwn_plcp_signal(rate); - rinfo = &iwn_rates[ridx]; totlen = m->m_pkthdr.len; @@ -3161,6 +3628,7 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, if (type == IEEE80211_FC0_TYPE_MGT) { uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + /* Tell HW to set timestamp in probe responses. */ if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) flags |= IWN_TX_INSERT_TSTAMP; @@ -3190,20 +3658,21 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, tx->len = htole16(totlen); tx->tid = 0; - tx->id = hal->broadcast_id; + tx->id = sc->broadcast_id; tx->rts_ntries = params->ibp_try1; tx->data_ntries = params->ibp_try0; tx->lifetime = htole32(IWN_LIFETIME_INFINITE); - tx->plcp = rinfo->plcp; - tx->rflags = rinfo->flags; + tx->rate = htole32(rate2plcp(rate)); + if (ridx < IWN_RIDX_OFDM6 && + IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + tx->rate |= htole32(IWN_RFLAG_CCK); /* Group or management frame. */ tx->linkq = 0; txant = IWN_LSB(sc->txchainmask); - tx->rflags |= IWN_RFLAG_ANT(txant); + tx->rate |= htole32(IWN_RFLAG_ANT(txant)); /* Set physical address of "scratch area". */ - paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd); - tx->loaddr = htole32(IWN_LOADDR(paddr)); - tx->hiaddr = IWN_HIADDR(paddr); + tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr)); + tx->hiaddr = IWN_HIADDR(data->scratch_paddr); /* Copy 802.11 header in TX command. */ memcpy((uint8_t *)(tx + 1), wh, hdrlen); @@ -3213,26 +3682,30 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, tx->security = 0; tx->flags = htole32(flags); - if (m->m_len > 0) { - error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, - m, segs, &nsegs, BUS_DMA_NOWAIT); - if (error == EFBIG) { - /* Too many fragments, linearize. */ - mnew = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER); - if (mnew == NULL) { - device_printf(sc->sc_dev, - "%s: could not defrag mbuf\n", __func__); - m_freem(m); - return ENOBUFS; - } - m = mnew; - error = bus_dmamap_load_mbuf_sg(ring->data_dmat, - data->map, m, segs, &nsegs, BUS_DMA_NOWAIT); + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs, + &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + if (error != EFBIG) { + device_printf(sc->sc_dev, + "%s: can't map mbuf (error %d)\n", __func__, error); + m_freem(m); + return error; } + /* Too many DMA segments, linearize mbuf. */ + m1 = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER); + if (m1 == NULL) { + device_printf(sc->sc_dev, + "%s: could not defrag mbuf\n", __func__); + m_freem(m); + return ENOBUFS; + } + m = m1; + + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, + segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, - "%s: bus_dmamap_load_mbuf_sg failed, error %d\n", - __func__, error); + "%s: can't map mbuf (error %d)\n", __func__, error); m_freem(m); return error; } @@ -3245,16 +3718,20 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs); /* Fill TX descriptor. */ - desc->nsegs = 1 + nsegs; + desc->nsegs = 1; + if (m->m_len != 0) + desc->nsegs += nsegs; /* First DMA segment is used by the TX command. */ desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr)); desc->segs[0].len = htole16(IWN_HIADDR(data->cmd_paddr) | (4 + sizeof (*tx) + hdrlen + pad) << 4); /* Other DMA segments are for data payload. */ + seg = &segs[0]; for (i = 1; i <= nsegs; i++) { - desc->segs[i].addr = htole32(IWN_LOADDR(segs[i - 1].ds_addr)); - desc->segs[i].len = htole16(IWN_HIADDR(segs[i - 1].ds_addr) | - segs[i - 1].ds_len << 4); + desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr)); + desc->segs[i].len = htole16(IWN_HIADDR(seg->ds_addr) | + seg->ds_len << 4); + seg++; } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); @@ -3263,10 +3740,9 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); -#ifdef notyet /* Update TX scheduler. */ - hal->update_sched(sc, ring->qid, ring->cur, tx->id, totlen); -#endif + if (ring->qid >= sc->firstaggqueue) + ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen); /* Kick TX ring. */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; @@ -3281,12 +3757,11 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, static int iwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, - const struct ieee80211_bpf_params *params) + const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct iwn_softc *sc = ifp->if_softc; - struct iwn_tx_ring *txq; int error = 0; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { @@ -3296,29 +3771,26 @@ iwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, } IWN_LOCK(sc); - if (params == NULL) - txq = &sc->txq[M_WME_GETAC(m)]; - else - txq = &sc->txq[params->ibp_pri & 3]; - if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ - error = iwn_tx_data(sc, m, ni, txq); + error = iwn_tx_data(sc, m, ni); } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ - error = iwn_tx_data_raw(sc, m, ni, txq, params); + error = iwn_tx_data_raw(sc, m, ni, params); } if (error != 0) { /* NB: m is reclaimed on tx failure */ ieee80211_free_node(ni); ifp->if_oerrors++; } + sc->sc_tx_timer = 5; + IWN_UNLOCK(sc); return error; } @@ -3338,12 +3810,14 @@ iwn_start_locked(struct ifnet *ifp) { struct iwn_softc *sc = ifp->if_softc; struct ieee80211_node *ni; - struct iwn_tx_ring *txq; struct mbuf *m; - int pri; IWN_LOCK_ASSERT(sc); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || + (ifp->if_drv_flags & IFF_DRV_OACTIVE)) + return; + for (;;) { if (sc->qfullmsk != 0) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; @@ -3353,27 +3827,34 @@ iwn_start_locked(struct ifnet *ifp) if (m == NULL) break; ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; - pri = M_WME_GETAC(m); - txq = &sc->txq[pri]; - if (iwn_tx_data(sc, m, ni, txq) != 0) { - ifp->if_oerrors++; + if (iwn_tx_data(sc, m, ni) != 0) { ieee80211_free_node(ni); - break; + ifp->if_oerrors++; + continue; } sc->sc_tx_timer = 5; } } static void -iwn_watchdog(struct iwn_softc *sc) +iwn_watchdog(void *arg) { - if (sc->sc_tx_timer > 0 && --sc->sc_tx_timer == 0) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct iwn_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; - if_printf(ifp, "device timeout\n"); - ieee80211_runtask(ic, &sc->sc_reinit_task); + IWN_LOCK_ASSERT(sc); + + KASSERT(ifp->if_drv_flags & IFF_DRV_RUNNING, ("not running")); + + if (sc->sc_tx_timer > 0) { + if (--sc->sc_tx_timer == 0) { + if_printf(ifp, "device timeout\n"); + ieee80211_runtask(ic, &sc->sc_reinit_task); + return; + } } + callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc); } static int @@ -3386,6 +3867,9 @@ iwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) int error = 0, startall = 0, stop = 0; switch (cmd) { + case SIOCGIFADDR: + error = ether_ioctl(ifp, cmd, data); + break; case SIOCSIFFLAGS: IWN_LOCK(sc); if (ifp->if_flags & IFF_UP) { @@ -3409,9 +3893,6 @@ iwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; - case SIOCGIFADDR: - error = ether_ioctl(ifp, cmd, data); - break; default: error = EINVAL; break; @@ -3433,7 +3914,8 @@ iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async) bus_addr_t paddr; int totlen, error; - IWN_LOCK_ASSERT(sc); + if (async == 0) + IWN_LOCK_ASSERT(sc); desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; @@ -3483,11 +3965,6 @@ iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async) bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); -#ifdef notyet - /* Update TX scheduler. */ - sc->sc_hal->update_sched(sc, ring->qid, ring->cur, 0, 0); -#endif - /* Kick command ring. */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); @@ -3521,98 +3998,49 @@ iwn5000_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async) return iwn_cmd(sc, IWN_CMD_ADD_NODE, node, sizeof (*node), async); } -#if 0 /* HT */ -static const uint8_t iwn_ridx_to_plcp[] = { - 10, 20, 55, 110, /* CCK */ - 0xd, 0xf, 0x5, 0x7, 0x9, 0xb, 0x1, 0x3, 0x3 /* OFDM R1-R4 */ -}; -static const uint8_t iwn_siso_mcs_to_plcp[] = { - 0, 0, 0, 0, /* CCK */ - 0, 0, 1, 2, 3, 4, 5, 6, 7 /* HT */ -}; -static const uint8_t iwn_mimo_mcs_to_plcp[] = { - 0, 0, 0, 0, /* CCK */ - 8, 8, 9, 10, 11, 12, 13, 14, 15 /* HT */ -}; -#endif -static const uint8_t iwn_prev_ridx[] = { - /* NB: allow fallback from CCK11 to OFDM9 and from OFDM6 to CCK5 */ - 0, 0, 1, 5, /* CCK */ - 2, 4, 3, 6, 7, 8, 9, 10, 10 /* OFDM */ -}; - -/* - * Configure hardware link parameters for the specified - * node operating on the specified channel. - */ static int -iwn_set_link_quality(struct iwn_softc *sc, uint8_t id, int async) +iwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; +#define RV(v) ((v) & IEEE80211_RATE_VAL) + struct iwn_node *wn = (void *)ni; + struct ieee80211_rateset *rs = &ni->ni_rates; struct iwn_cmd_link_quality linkq; - const struct iwn_rate *rinfo; - int i; - uint8_t txant, ridx; + uint8_t txant; + int i, rate, txrate; /* Use the first valid TX antenna. */ txant = IWN_LSB(sc->txchainmask); memset(&linkq, 0, sizeof linkq); - linkq.id = id; + linkq.id = wn->id; linkq.antmsk_1stream = txant; linkq.antmsk_2stream = IWN_ANT_AB; - linkq.ampdu_max = 31; + linkq.ampdu_max = 64; linkq.ampdu_threshold = 3; linkq.ampdu_limit = htole16(4000); /* 4ms */ -#if 0 /* HT */ - if (IEEE80211_IS_CHAN_HT(c)) - linkq.mimo = 1; -#endif - - if (id == IWN_ID_BSS) - ridx = IWN_RIDX_OFDM54; - else if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) - ridx = IWN_RIDX_OFDM6; + /* Start at highest available bit-rate. */ + if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) + txrate = ni->ni_htrates.rs_nrates - 1; else - ridx = IWN_RIDX_CCK1; - + txrate = rs->rs_nrates - 1; for (i = 0; i < IWN_MAX_TX_RETRIES; i++) { - rinfo = &iwn_rates[ridx]; -#if 0 /* HT */ - if (IEEE80211_IS_CHAN_HT40(c)) { - linkq.retry[i].plcp = iwn_mimo_mcs_to_plcp[ridx] - | IWN_RIDX_MCS; - linkq.retry[i].rflags = IWN_RFLAG_HT - | IWN_RFLAG_HT40; - /* XXX shortGI */ - } else if (IEEE80211_IS_CHAN_HT(c)) { - linkq.retry[i].plcp = iwn_siso_mcs_to_plcp[ridx] - | IWN_RIDX_MCS; - linkq.retry[i].rflags = IWN_RFLAG_HT; - /* XXX shortGI */ - } else -#endif - { - linkq.retry[i].plcp = rinfo->plcp; - linkq.retry[i].rflags = rinfo->flags; - } - linkq.retry[i].rflags |= IWN_RFLAG_ANT(txant); - ridx = iwn_prev_ridx[ridx]; + if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) + rate = IEEE80211_RATE_MCS | txrate; + else + rate = RV(rs->rs_rates[txrate]); + linkq.retry[i] = wn->ridx[rate]; + + if ((le32toh(wn->ridx[rate]) & IWN_RFLAG_MCS) && + RV(le32toh(wn->ridx[rate])) > 7) + linkq.mimo = i + 1; + + /* Next retry at immediate lower bit-rate. */ + if (txrate > 0) + txrate--; } -#ifdef IWN_DEBUG - if (sc->sc_debug & IWN_DEBUG_STATE) { - printf("%s: set link quality for node %d, mimo %d ssmask %d\n", - __func__, id, linkq.mimo, linkq.antmsk_1stream); - printf("%s:", __func__); - for (i = 0; i < IWN_MAX_TX_RETRIES; i++) - printf(" %d:%x", linkq.retry[i].plcp, - linkq.retry[i].rflags); - printf("\n"); - } -#endif - return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, async); + return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, 1); +#undef RV } /* @@ -3621,50 +4049,70 @@ iwn_set_link_quality(struct iwn_softc *sc, uint8_t id, int async) static int iwn_add_broadcast_node(struct iwn_softc *sc, int async) { - const struct iwn_hal *hal = sc->sc_hal; + struct iwn_ops *ops = &sc->ops; struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; struct iwn_node_info node; - int error; + struct iwn_cmd_link_quality linkq; + uint8_t txant; + int i, error; memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.macaddr, ifp->if_broadcastaddr); - node.id = hal->broadcast_id; + node.id = sc->broadcast_id; DPRINTF(sc, IWN_DEBUG_RESET, "%s: adding broadcast node\n", __func__); - error = hal->add_node(sc, &node, async); - if (error != 0) + if ((error = ops->add_node(sc, &node, async)) != 0) return error; - error = iwn_set_link_quality(sc, hal->broadcast_id, async); - return error; + /* Use the first valid TX antenna. */ + txant = IWN_LSB(sc->txchainmask); + + memset(&linkq, 0, sizeof linkq); + linkq.id = sc->broadcast_id; + linkq.antmsk_1stream = txant; + linkq.antmsk_2stream = IWN_ANT_AB; + linkq.ampdu_max = 64; + linkq.ampdu_threshold = 3; + linkq.ampdu_limit = htole16(4000); /* 4ms */ + + /* Use lowest mandatory bit-rate. */ + if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) + linkq.retry[0] = htole32(0xd); + else + linkq.retry[0] = htole32(10 | IWN_RFLAG_CCK); + linkq.retry[0] |= htole32(IWN_RFLAG_ANT(txant)); + /* Use same bit-rate for all TX retries. */ + for (i = 1; i < IWN_MAX_TX_RETRIES; i++) { + linkq.retry[i] = linkq.retry[0]; + } + return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, async); } static int -iwn_wme_update(struct ieee80211com *ic) +iwn_updateedca(struct ieee80211com *ic) { #define IWN_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */ -#define IWN_TXOP_TO_US(v) (v<<5) struct iwn_softc *sc = ic->ic_ifp->if_softc; struct iwn_edca_params cmd; - int i; + int aci; memset(&cmd, 0, sizeof cmd); cmd.flags = htole32(IWN_EDCA_UPDATE); - for (i = 0; i < WME_NUM_AC; i++) { - const struct wmeParams *wmep = - &ic->ic_wme.wme_chanParams.cap_wmeParams[i]; - cmd.ac[i].aifsn = wmep->wmep_aifsn; - cmd.ac[i].cwmin = htole16(IWN_EXP2(wmep->wmep_logcwmin)); - cmd.ac[i].cwmax = htole16(IWN_EXP2(wmep->wmep_logcwmax)); - cmd.ac[i].txoplimit = - htole16(IWN_TXOP_TO_US(wmep->wmep_txopLimit)); + for (aci = 0; aci < WME_NUM_AC; aci++) { + const struct wmeParams *ac = + &ic->ic_wme.wme_chanParams.cap_wmeParams[aci]; + cmd.ac[aci].aifsn = ac->wmep_aifsn; + cmd.ac[aci].cwmin = htole16(IWN_EXP2(ac->wmep_logcwmin)); + cmd.ac[aci].cwmax = htole16(IWN_EXP2(ac->wmep_logcwmax)); + cmd.ac[aci].txoplimit = + htole16(IEEE80211_TXOP_TO_US(ac->wmep_txopLimit)); } IEEE80211_UNLOCK(ic); IWN_LOCK(sc); - (void) iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1 /*async*/); + (void)iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1); IWN_UNLOCK(sc); IEEE80211_LOCK(ic); return 0; -#undef IWN_TXOP_TO_US #undef IWN_EXP2 } @@ -3709,8 +4157,7 @@ iwn_set_critical_temp(struct iwn_softc *sc) temp = 110; memset(&crit, 0, sizeof crit); crit.tempR = htole32(temp); - DPRINTF(sc, IWN_DEBUG_RESET, "setting critical temp to %d\n", - temp); + DPRINTF(sc, IWN_DEBUG_RESET, "setting critical temp to %d\n", temp); return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0); } @@ -3726,7 +4173,7 @@ iwn_set_timing(struct iwn_softc *sc, struct ieee80211_node *ni) cmd.lintval = htole16(10); /* Compute remaining time until next beacon. */ - val = (uint64_t)ni->ni_intval * 1024; /* msecs -> usecs */ + val = (uint64_t)ni->ni_intval * IEEE80211_DUR_TU; mod = le64toh(cmd.tstamp) % val; cmd.binitval = htole32((uint32_t)(val - mod)); @@ -3742,7 +4189,7 @@ iwn4965_power_calibration(struct iwn_softc *sc, int temp) struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; - /* Adjust TX power if need be (delta >= 3 degC.) */ + /* Adjust TX power if need be (delta >= 3 degC). */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d->%d\n", __func__, sc->temp, temp); if (abs(temp - sc->temp) >= 3) { @@ -3769,18 +4216,16 @@ iwn4965_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, ((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 }; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; struct iwn_ucode_info *uc = &sc->ucode_info; struct iwn4965_cmd_txpower cmd; struct iwn4965_eeprom_chan_samples *chans; + const uint8_t *rf_gain, *dsp_gain; int32_t vdiff, tdiff; int i, c, grp, maxpwr; - const uint8_t *rf_gain, *dsp_gain; uint8_t chan; - /* Retrieve channel number. */ - chan = ieee80211_chan2ieee(ic, ch); + /* Retrieve current channel from last RXON. */ + chan = sc->rxon.chan; DPRINTF(sc, IWN_DEBUG_RESET, "setting TX power for channel %d\n", chan); @@ -3941,22 +4386,16 @@ iwn4965_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat) agc = (le16toh(phy->agc) >> 7) & 0x7f; rssi = 0; -#if 0 - if (mask & IWN_ANT_A) /* Ant A */ - rssi = max(rssi, phy->rssi[0]); - if (mask & IWN_ATH_B) /* Ant B */ - rssi = max(rssi, phy->rssi[2]); - if (mask & IWN_ANT_C) /* Ant C */ - rssi = max(rssi, phy->rssi[4]); -#else - rssi = max(rssi, phy->rssi[0]); - rssi = max(rssi, phy->rssi[2]); - rssi = max(rssi, phy->rssi[4]); -#endif + if (mask & IWN_ANT_A) + rssi = MAX(rssi, phy->rssi[0]); + if (mask & IWN_ANT_B) + rssi = MAX(rssi, phy->rssi[2]); + if (mask & IWN_ANT_C) + rssi = MAX(rssi, phy->rssi[4]); - DPRINTF(sc, IWN_DEBUG_RECV, "%s: agc %d mask 0x%x rssi %d %d %d " - "result %d\n", __func__, agc, mask, - phy->rssi[0], phy->rssi[2], phy->rssi[4], + DPRINTF(sc, IWN_DEBUG_RECV, + "%s: agc %d mask 0x%x rssi %d %d %d result %d\n", __func__, agc, + mask, phy->rssi[0], phy->rssi[2], phy->rssi[4], rssi - agc - IWN_RSSI_TO_DBM); return rssi - agc - IWN_RSSI_TO_DBM; } @@ -3965,8 +4404,8 @@ static int iwn5000_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat) { struct iwn5000_rx_phystat *phy = (void *)stat->phybuf; - int rssi; uint8_t agc; + int rssi; agc = (le32toh(phy->agc) >> 9) & 0x7f; @@ -3974,8 +4413,8 @@ iwn5000_get_rssi(struct iwn_softc *sc, struct iwn_rx_stat *stat) le16toh(phy->rssi[1]) & 0xff); rssi = MAX(le16toh(phy->rssi[2]) & 0xff, rssi); - DPRINTF(sc, IWN_DEBUG_RECV, "%s: agc %d rssi %d %d %d " - "result %d\n", __func__, agc, + DPRINTF(sc, IWN_DEBUG_RECV, + "%s: agc %d rssi %d %d %d result %d\n", __func__, agc, phy->rssi[0], phy->rssi[1], phy->rssi[2], rssi - agc - IWN_RSSI_TO_DBM); return rssi - agc - IWN_RSSI_TO_DBM; @@ -4014,11 +4453,11 @@ iwn4965_get_temperature(struct iwn_softc *sc) r3 = le32toh(uc->temp[2].chan20MHz); r4 = le32toh(sc->rawtemp); - if (r1 == r3) /* Prevents division by 0 (should not happen.) */ + if (r1 == r3) /* Prevents division by 0 (should not happen). */ return 0; /* Sign-extend 23-bit R4 value to 32-bit. */ - r4 = (r4 << 8) >> 8; + r4 = ((r4 & 0xffffff) ^ 0x800000) - 0x800000; /* Compute temperature in Kelvin. */ temp = (259 * (r4 - r2)) / (r3 - r1); temp = (temp * 97) / 100 + 8; @@ -4035,8 +4474,7 @@ iwn5000_get_temperature(struct iwn_softc *sc) /* * Temperature is not used by the driver for 5000 Series because - * TX power calibration is handled by firmware. We export it to - * users through the sensor framework though. + * TX power calibration is handled by firmware. */ temp = le32toh(sc->rawtemp); if (sc->hw_type == IWN_HW_REV_TYPE_5150) { @@ -4052,7 +4490,7 @@ iwn5000_get_temperature(struct iwn_softc *sc) static int iwn_init_sensitivity(struct iwn_softc *sc) { - const struct iwn_hal *hal = sc->sc_hal; + struct iwn_ops *ops = &sc->ops; struct iwn_calib_state *calib = &sc->calib; uint32_t flags; int error; @@ -4071,18 +4509,17 @@ iwn_init_sensitivity(struct iwn_softc *sc) calib->energy_cck = sc->limits->energy_cck; /* Write initial sensitivity. */ - error = iwn_send_sensitivity(sc); - if (error != 0) + if ((error = iwn_send_sensitivity(sc)) != 0) return error; /* Write initial gains. */ - error = hal->init_gains(sc); - if (error != 0) + if ((error = ops->init_gains(sc)) != 0) return error; /* Request statistics at each beacon interval. */ flags = 0; - DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: calibrate phy\n", __func__); + DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending request for statistics\n", + __func__); return iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, sizeof flags, 1); } @@ -4095,7 +4532,7 @@ static void iwn_collect_noise(struct iwn_softc *sc, const struct iwn_rx_general_stats *stats) { - const struct iwn_hal *hal = sc->sc_hal; + struct iwn_ops *ops = &sc->ops; struct iwn_calib_state *calib = &sc->calib; uint32_t val; int i; @@ -4126,13 +4563,13 @@ iwn_collect_noise(struct iwn_softc *sc, if ((sc->chainmask & sc->txchainmask) == 0) sc->chainmask |= IWN_LSB(sc->txchainmask); - (void)hal->set_gains(sc); + (void)ops->set_gains(sc); calib->state = IWN_CALIB_STATE_RUN; #ifdef notyet /* XXX Disable RX chains with no antennas connected. */ sc->rxon.rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask)); - (void)iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 1); + (void)iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); #endif #if 0 @@ -4162,7 +4599,7 @@ iwn5000_init_gains(struct iwn_softc *sc) struct iwn_phy_calib cmd; memset(&cmd, 0, sizeof cmd); - cmd.code = IWN5000_PHY_CALIB_RESET_NOISE_GAIN; + cmd.code = sc->reset_noise_gain; cmd.ngroups = 1; cmd.isvalid = 1; DPRINTF(sc, IWN_DEBUG_CALIBRATE, @@ -4208,13 +4645,13 @@ iwn5000_set_gains(struct iwn_softc *sc) { struct iwn_calib_state *calib = &sc->calib; struct iwn_phy_calib_gain cmd; - int i, ant, delta, div; + int i, ant, div, delta; /* We collected 20 beacons and !=6050 need a 1.5 factor. */ div = (sc->hw_type == IWN_HW_REV_TYPE_6050) ? 20 : 30; memset(&cmd, 0, sizeof cmd); - cmd.code = IWN5000_PHY_CALIB_NOISE_GAIN; + cmd.code = sc->noise_gain; cmd.ngroups = 1; cmd.isvalid = 1; /* Get first available RX antenna as referential. */ @@ -4269,14 +4706,13 @@ iwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats) int i, needs_update = 0; /* Check that we've been enabled long enough. */ - rxena = le32toh(stats->general.load); - if (rxena == 0) + if ((rxena = le32toh(stats->general.load)) == 0) return; /* Compute number of false alarms since last call for OFDM. */ fa = le32toh(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm; fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm; - fa *= 200 * 1024; /* 200TU */ + fa *= 200 * IEEE80211_DUR_TU; /* 200TU */ /* Save counters values for next call. */ calib->bad_plcp_ofdm = le32toh(stats->ofdm.bad_plcp); @@ -4333,7 +4769,7 @@ iwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats) /* Compute number of false alarms since last call for CCK. */ fa = le32toh(stats->cck.bad_plcp) - calib->bad_plcp_cck; fa += le32toh(stats->cck.fa) - calib->fa_cck; - fa *= 200 * 1024; /* 200TU */ + fa *= 200 * IEEE80211_DUR_TU; /* 200TU */ /* Save counters values for next call. */ calib->bad_plcp_cck = le32toh(stats->cck.bad_plcp); @@ -4368,7 +4804,7 @@ iwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats) if (calib->cck_state != IWN_CCK_STATE_INIT && (((int32_t)calib->noise_ref - (int32_t)noise_ref) > 2 || - calib->low_fa > 100)) { + calib->low_fa > 100)) { inc(calib->energy_cck, 2, limits->min_energy_cck); dec(calib->cck_x4, 3, limits->min_cck_x4); dec(calib->cck_mrc_x4, 3, limits->min_cck_mrc_x4); @@ -4397,31 +4833,47 @@ static int iwn_send_sensitivity(struct iwn_softc *sc) { struct iwn_calib_state *calib = &sc->calib; - struct iwn_sensitivity_cmd cmd; + struct iwn_enhanced_sensitivity_cmd cmd; + int len; memset(&cmd, 0, sizeof cmd); + len = sizeof (struct iwn_sensitivity_cmd); cmd.which = IWN_SENSITIVITY_WORKTBL; /* OFDM modulation. */ - cmd.corr_ofdm_x1 = htole16(calib->ofdm_x1); - cmd.corr_ofdm_mrc_x1 = htole16(calib->ofdm_mrc_x1); - cmd.corr_ofdm_x4 = htole16(calib->ofdm_x4); - cmd.corr_ofdm_mrc_x4 = htole16(calib->ofdm_mrc_x4); - cmd.energy_ofdm = htole16(sc->limits->energy_ofdm); - cmd.energy_ofdm_th = htole16(62); + cmd.corr_ofdm_x1 = htole16(calib->ofdm_x1); + cmd.corr_ofdm_mrc_x1 = htole16(calib->ofdm_mrc_x1); + cmd.corr_ofdm_x4 = htole16(calib->ofdm_x4); + cmd.corr_ofdm_mrc_x4 = htole16(calib->ofdm_mrc_x4); + cmd.energy_ofdm = htole16(sc->limits->energy_ofdm); + cmd.energy_ofdm_th = htole16(62); /* CCK modulation. */ - cmd.corr_cck_x4 = htole16(calib->cck_x4); - cmd.corr_cck_mrc_x4 = htole16(calib->cck_mrc_x4); - cmd.energy_cck = htole16(calib->energy_cck); + cmd.corr_cck_x4 = htole16(calib->cck_x4); + cmd.corr_cck_mrc_x4 = htole16(calib->cck_mrc_x4); + cmd.energy_cck = htole16(calib->energy_cck); /* Barker modulation: use default values. */ - cmd.corr_barker = htole16(190); - cmd.corr_barker_mrc = htole16(390); + cmd.corr_barker = htole16(190); + cmd.corr_barker_mrc = htole16(390); DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: set sensitivity %d/%d/%d/%d/%d/%d/%d\n", __func__, calib->ofdm_x1, calib->ofdm_mrc_x1, calib->ofdm_x4, calib->ofdm_mrc_x4, calib->cck_x4, calib->cck_mrc_x4, calib->energy_cck); - return iwn_cmd(sc, IWN_CMD_SET_SENSITIVITY, &cmd, sizeof cmd, 1); + + if (!(sc->sc_flags & IWN_FLAG_ENH_SENS)) + goto send; + /* Enhanced sensitivity settings. */ + len = sizeof (struct iwn_enhanced_sensitivity_cmd); + cmd.ofdm_det_slope_mrc = htole16(668); + cmd.ofdm_det_icept_mrc = htole16(4); + cmd.ofdm_det_slope = htole16(486); + cmd.ofdm_det_icept = htole16(37); + cmd.cck_det_slope_mrc = htole16(853); + cmd.cck_det_icept_mrc = htole16(4); + cmd.cck_det_slope = htole16(476); + cmd.cck_det_icept = htole16(99); +send: + return iwn_cmd(sc, IWN_CMD_SET_SENSITIVITY, &cmd, len, 1); } /* @@ -4431,10 +4883,10 @@ iwn_send_sensitivity(struct iwn_softc *sc) static int iwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async) { - const struct iwn_pmgt *pmgt; struct iwn_pmgt_cmd cmd; + const struct iwn_pmgt *pmgt; uint32_t max, skip_dtim; - uint32_t tmp; + uint32_t reg; int i; /* Select which PS parameters to use. */ @@ -4451,8 +4903,8 @@ iwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async) if (level == 5) cmd.flags |= htole16(IWN_PS_FAST_PD); /* Retrieve PCIe Active State Power Management (ASPM). */ - tmp = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); - if (!(tmp & 0x1)) /* L0s Entry disabled. */ + reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); + if (!(reg & 0x1)) /* L0s Entry disabled. */ cmd.flags |= htole16(IWN_PS_PCI_PMGT); cmd.rxtimeout = htole32(pmgt->rxtimeout * 1024); cmd.txtimeout = htole32(pmgt->txtimeout * 1024); @@ -4479,18 +4931,122 @@ iwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async) return iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async); } +static int +iwn_send_btcoex(struct iwn_softc *sc) +{ + struct iwn_bluetooth cmd; + + memset(&cmd, 0, sizeof cmd); + cmd.flags = IWN_BT_COEX_CHAN_ANN | IWN_BT_COEX_BT_PRIO; + cmd.lead_time = IWN_BT_LEAD_TIME_DEF; + cmd.max_kill = IWN_BT_MAX_KILL_DEF; + DPRINTF(sc, IWN_DEBUG_RESET, "%s: configuring bluetooth coexistence\n", + __func__); + return iwn_cmd(sc, IWN_CMD_BT_COEX, &cmd, sizeof(cmd), 0); +} + +static int +iwn_send_advanced_btcoex(struct iwn_softc *sc) +{ + static const uint32_t btcoex_3wire[12] = { + 0xaaaaaaaa, 0xaaaaaaaa, 0xaeaaaaaa, 0xaaaaaaaa, + 0xcc00ff28, 0x0000aaaa, 0xcc00aaaa, 0x0000aaaa, + 0xc0004000, 0x00004000, 0xf0005000, 0xf0005000, + }; + struct iwn6000_btcoex_config btconfig; + struct iwn_btcoex_priotable btprio; + struct iwn_btcoex_prot btprot; + int error, i; + + memset(&btconfig, 0, sizeof btconfig); + btconfig.flags = 145; + btconfig.max_kill = 5; + btconfig.bt3_t7_timer = 1; + btconfig.kill_ack = htole32(0xffff0000); + btconfig.kill_cts = htole32(0xffff0000); + btconfig.sample_time = 2; + btconfig.bt3_t2_timer = 0xc; + for (i = 0; i < 12; i++) + btconfig.lookup_table[i] = htole32(btcoex_3wire[i]); + btconfig.valid = htole16(0xff); + btconfig.prio_boost = 0xf0; + DPRINTF(sc, IWN_DEBUG_RESET, + "%s: configuring advanced bluetooth coexistence\n", __func__); + error = iwn_cmd(sc, IWN_CMD_BT_COEX, &btconfig, sizeof(btconfig), 1); + if (error != 0) + return error; + + memset(&btprio, 0, sizeof btprio); + btprio.calib_init1 = 0x6; + btprio.calib_init2 = 0x7; + btprio.calib_periodic_low1 = 0x2; + btprio.calib_periodic_low2 = 0x3; + btprio.calib_periodic_high1 = 0x4; + btprio.calib_periodic_high2 = 0x5; + btprio.dtim = 0x6; + btprio.scan52 = 0x8; + btprio.scan24 = 0xa; + error = iwn_cmd(sc, IWN_CMD_BT_COEX_PRIOTABLE, &btprio, sizeof(btprio), + 1); + if (error != 0) + return error; + + /* Force BT state machine change. */ + memset(&btprot, 0, sizeof btprio); + btprot.open = 1; + btprot.type = 1; + error = iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1); + if (error != 0) + return error; + btprot.open = 0; + return iwn_cmd(sc, IWN_CMD_BT_COEX_PROT, &btprot, sizeof(btprot), 1); +} + +static int +iwn5000_runtime_calib(struct iwn_softc *sc) +{ + struct iwn5000_calib_config cmd; + + memset(&cmd, 0, sizeof cmd); + cmd.ucode.once.enable = 0xffffffff; + cmd.ucode.once.start = IWN5000_CALIB_DC; + DPRINTF(sc, IWN_DEBUG_CALIBRATE, + "%s: configuring runtime calibration\n", __func__); + return iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof(cmd), 0); +} + static int iwn_config(struct iwn_softc *sc) { - const struct iwn_hal *hal = sc->sc_hal; + struct iwn_ops *ops = &sc->ops; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; - struct iwn_bluetooth bluetooth; uint32_t txmask; - int error; uint16_t rxchain; + int error; - /* Configure valid TX chains for 5000 Series. */ + if (sc->hw_type == IWN_HW_REV_TYPE_6005) { + /* Set radio temperature sensor offset. */ + error = iwn5000_temp_offset_calib(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not set temperature offset\n", __func__); + return error; + } + } + + if (sc->hw_type == IWN_HW_REV_TYPE_6050) { + /* Configure runtime DC calibration. */ + error = iwn5000_runtime_calib(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not configure runtime calibration\n", + __func__); + return error; + } + } + + /* Configure valid TX chains for >=5000 Series. */ if (sc->hw_type != IWN_HW_REV_TYPE_4965) { txmask = htole32(sc->txchainmask); DPRINTF(sc, IWN_DEBUG_RESET, @@ -4506,13 +5062,10 @@ iwn_config(struct iwn_softc *sc) } /* Configure bluetooth coexistence. */ - memset(&bluetooth, 0, sizeof bluetooth); - bluetooth.flags = IWN_BT_COEX_CHAN_ANN | IWN_BT_COEX_BT_PRIO; - bluetooth.lead_time = IWN_BT_LEAD_TIME_DEF; - bluetooth.max_kill = IWN_BT_MAX_KILL_DEF; - DPRINTF(sc, IWN_DEBUG_RESET, "%s: config bluetooth coexistence\n", - __func__); - error = iwn_cmd(sc, IWN_CMD_BT_COEX, &bluetooth, sizeof bluetooth, 0); + if (sc->sc_flags & IWN_FLAG_ADV_BTCOEX) + error = iwn_send_advanced_btcoex(sc); + else + error = iwn_send_btcoex(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: could not configure bluetooth coexistence, error %d\n", @@ -4553,38 +5106,34 @@ iwn_config(struct iwn_softc *sc) IWN_RXCHAIN_IDLE_COUNT(2); sc->rxon.rxchain = htole16(rxchain); DPRINTF(sc, IWN_DEBUG_RESET, "%s: setting configuration\n", __func__); - error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 0); + error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 0); if (error != 0) { - device_printf(sc->sc_dev, - "%s: RXON command failed\n", __func__); + device_printf(sc->sc_dev, "%s: RXON command failed\n", + __func__); return error; } - error = iwn_add_broadcast_node(sc, 0); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not add broadcast node\n", __func__); + if ((error = iwn_add_broadcast_node(sc, 0)) != 0) { + device_printf(sc->sc_dev, "%s: could not add broadcast node\n", + __func__); return error; } /* Configuration has changed, set TX power accordingly. */ - error = hal->set_txpower(sc, ic->ic_curchan, 0); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not set TX power\n", __func__); + if ((error = ops->set_txpower(sc, ic->ic_curchan, 0)) != 0) { + device_printf(sc->sc_dev, "%s: could not set TX power\n", + __func__); return error; } - error = iwn_set_critical_temp(sc); - if (error != 0) { + if ((error = iwn_set_critical_temp(sc)) != 0) { device_printf(sc->sc_dev, - "%s: ccould not set critical temperature\n", __func__); + "%s: could not set critical temperature\n", __func__); return error; } /* Set power saving level to CAM during initialization. */ - error = iwn_set_pslevel(sc, 0, 0, 0); - if (error != 0) { + if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) { device_printf(sc->sc_dev, "%s: could not set power saving level\n", __func__); return error; @@ -4592,12 +5141,25 @@ iwn_config(struct iwn_softc *sc) return 0; } +/* + * Add an ssid element to a frame. + */ +static uint8_t * +ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) +{ + *frm++ = IEEE80211_ELEMID_SSID; + *frm++ = len; + memcpy(frm, ssid, len); + return frm + len; +} + static int iwn_scan(struct iwn_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_scan_state *ss = ic->ic_scan; /*XXX*/ + struct ieee80211_node *ni = ss->ss_vap->iv_bss; struct iwn_scan_hdr *hdr; struct iwn_cmd_data *tx; struct iwn_scan_essid *essid; @@ -4605,9 +5167,10 @@ iwn_scan(struct iwn_softc *sc) struct ieee80211_frame *wh; struct ieee80211_rateset *rs; struct ieee80211_channel *c; - int buflen, error, nrates; + uint8_t *buf, *frm; uint16_t rxchain; - uint8_t *buf, *frm, txant; + uint8_t txant; + int buflen, error; buf = malloc(IWN_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO); if (buf == NULL) { @@ -4617,7 +5180,6 @@ iwn_scan(struct iwn_softc *sc) return ENOMEM; } hdr = (struct iwn_scan_hdr *)buf; - /* * Move to the next channel if no frames are received within 10ms * after sending the probe request. @@ -4633,7 +5195,7 @@ iwn_scan(struct iwn_softc *sc) if (IEEE80211_IS_CHAN_A(ic->ic_curchan) && sc->hw_type == IWN_HW_REV_TYPE_4965) { /* Ant A must be avoided in 5GHz because of an HW bug. */ - rxchain |= IWN_RXCHAIN_FORCE_SEL(IWN_ANT_BC); + rxchain |= IWN_RXCHAIN_FORCE_SEL(IWN_ANT_B); } else /* Use all available RX antennas. */ rxchain |= IWN_RXCHAIN_FORCE_SEL(sc->rxchainmask); hdr->rxchain = htole16(rxchain); @@ -4641,23 +5203,27 @@ iwn_scan(struct iwn_softc *sc) tx = (struct iwn_cmd_data *)(hdr + 1); tx->flags = htole32(IWN_TX_AUTO_SEQ); - tx->id = sc->sc_hal->broadcast_id; + tx->id = sc->broadcast_id; tx->lifetime = htole32(IWN_LIFETIME_INFINITE); - if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) { + if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) { /* Send probe requests at 6Mbps. */ - tx->plcp = iwn_rates[IWN_RIDX_OFDM6].plcp; + tx->rate = htole32(0xd); rs = &ic->ic_sup_rates[IEEE80211_MODE_11A]; } else { hdr->flags = htole32(IWN_RXON_24GHZ | IWN_RXON_AUTO); - /* Send probe requests at 1Mbps. */ - tx->plcp = iwn_rates[IWN_RIDX_CCK1].plcp; - tx->rflags = IWN_RFLAG_CCK; + if (sc->hw_type == IWN_HW_REV_TYPE_4965 && + sc->rxon.associd && sc->rxon.chan > 14) + tx->rate = htole32(0xd); + else { + /* Send probe requests at 1Mbps. */ + tx->rate = htole32(10 | IWN_RFLAG_CCK); + } rs = &ic->ic_sup_rates[IEEE80211_MODE_11G]; } /* Use the first valid TX antenna. */ txant = IWN_LSB(sc->txchainmask); - tx->rflags |= IWN_RFLAG_ANT(txant); + tx->rate |= htole32(IWN_RFLAG_ANT(txant)); essid = (struct iwn_scan_essid *)(tx + 1); if (ss->ss_ssid[0].len != 0) { @@ -4665,7 +5231,6 @@ iwn_scan(struct iwn_softc *sc) essid[0].len = ss->ss_ssid[0].len; memcpy(essid[0].data, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len); } - /* * Build a probe request frame. Most of the following code is a * copy & paste of what is done in net80211. @@ -4681,30 +5246,12 @@ iwn_scan(struct iwn_softc *sc) *(uint16_t *)&wh->i_seq[0] = 0; /* filled by HW */ frm = (uint8_t *)(wh + 1); - - /* Add SSID IE. */ - *frm++ = IEEE80211_ELEMID_SSID; - *frm++ = ss->ss_ssid[0].len; - memcpy(frm, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len); - frm += ss->ss_ssid[0].len; - - /* Add supported rates IE. */ - *frm++ = IEEE80211_ELEMID_RATES; - nrates = rs->rs_nrates; - if (nrates > IEEE80211_RATE_SIZE) - nrates = IEEE80211_RATE_SIZE; - *frm++ = nrates; - memcpy(frm, rs->rs_rates, nrates); - frm += nrates; - - /* Add supported xrates IE. */ - if (rs->rs_nrates > IEEE80211_RATE_SIZE) { - nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; - *frm++ = IEEE80211_ELEMID_XRATES; - *frm++ = (uint8_t)nrates; - memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); - frm += nrates; - } + frm = ieee80211_add_ssid(frm, NULL, 0); + frm = ieee80211_add_rates(frm, rs); + if (rs->rs_nrates > IEEE80211_RATE_SIZE) + frm = ieee80211_add_xrates(frm, rs); + if (ic->ic_htcaps & IEEE80211_HTC_HT) + frm = ieee80211_add_htcap(frm, ni); /* Set length of probe request. */ tx->len = htole16(frm - (uint8_t *)wh); @@ -4766,17 +5313,15 @@ iwn_scan(struct iwn_softc *sc) static int iwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap) { - const struct iwn_hal *hal = sc->sc_hal; + struct iwn_ops *ops = &sc->ops; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_node *ni = vap->iv_bss; int error; - sc->calib.state = IWN_CALIB_STATE_INIT; - /* Update adapter configuration. */ IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); - sc->rxon.chan = htole16(ieee80211_chan2ieee(ic, ni->ni_chan)); + sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan); sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); @@ -4791,70 +5336,56 @@ iwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap) sc->rxon.cck_mask = 0x03; sc->rxon.ofdm_mask = 0; } else { - /* XXX assume 802.11b/g */ + /* Assume 802.11b/g. */ sc->rxon.cck_mask = 0x0f; sc->rxon.ofdm_mask = 0x15; } - DPRINTF(sc, IWN_DEBUG_STATE, - "%s: config chan %d mode %d flags 0x%x cck 0x%x ofdm 0x%x " - "ht_single 0x%x ht_dual 0x%x rxchain 0x%x " - "myaddr %6D wlap %6D bssid %6D associd %d filter 0x%x\n", - __func__, - le16toh(sc->rxon.chan), sc->rxon.mode, le32toh(sc->rxon.flags), - sc->rxon.cck_mask, sc->rxon.ofdm_mask, - sc->rxon.ht_single_mask, sc->rxon.ht_dual_mask, - le16toh(sc->rxon.rxchain), - sc->rxon.myaddr, ":", sc->rxon.wlap, ":", sc->rxon.bssid, ":", - le16toh(sc->rxon.associd), le32toh(sc->rxon.filter)); - error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 1); + DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n", + sc->rxon.chan, sc->rxon.flags, sc->rxon.cck_mask, + sc->rxon.ofdm_mask); + error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); if (error != 0) { - device_printf(sc->sc_dev, - "%s: RXON command failed, error %d\n", __func__, error); + device_printf(sc->sc_dev, "%s: RXON command failed, error %d\n", + __func__, error); return error; } /* Configuration has changed, set TX power accordingly. */ - error = hal->set_txpower(sc, ni->ni_chan, 1); - if (error != 0) { + if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) { device_printf(sc->sc_dev, - "%s: could not set Tx power, error %d\n", __func__, error); + "%s: could not set TX power, error %d\n", __func__, error); return error; } /* * Reconfiguring RXON clears the firmware nodes table so we must * add the broadcast node again. */ - error = iwn_add_broadcast_node(sc, 1); - if (error != 0) { + if ((error = iwn_add_broadcast_node(sc, 1)) != 0) { device_printf(sc->sc_dev, - "%s: could not add broadcast node, error %d\n", - __func__, error); + "%s: could not add broadcast node, error %d\n", __func__, + error); return error; } return 0; } -/* - * Configure the adapter for associated state. - */ static int iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) { -#define MS(v,x) (((v) & x) >> x##_S) - const struct iwn_hal *hal = sc->sc_hal; + struct iwn_ops *ops = &sc->ops; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_node *ni = vap->iv_bss; struct iwn_node_info node; + uint32_t htflags = 0; int error; if (ic->ic_opmode == IEEE80211_M_MONITOR) { /* Link LED blinks while monitoring. */ - iwn_set_led(sc, IWN_LED_LINK, 20, 20); + iwn_set_led(sc, IWN_LED_LINK, 5, 5); return 0; } - error = iwn_set_timing(sc, ni); - if (error != 0) { + if ((error = iwn_set_timing(sc, ni)) != 0) { device_printf(sc->sc_dev, "%s: could not set timing, error %d\n", __func__, error); return error; @@ -4862,15 +5393,11 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) /* Update adapter configuration. */ IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); - sc->rxon.chan = htole16(ieee80211_chan2ieee(ic, ni->ni_chan)); sc->rxon.associd = htole16(IEEE80211_AID(ni->ni_associd)); - /* Short preamble and slot time are negotiated when associating. */ - sc->rxon.flags &= ~htole32(IWN_RXON_SHPREAMBLE | IWN_RXON_SHSLOT); - sc->rxon.flags |= htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); + sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan); + sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); - else - sc->rxon.flags &= ~htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ); if (ic->ic_flags & IEEE80211_F_SHSLOT) sc->rxon.flags |= htole32(IWN_RXON_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) @@ -4882,139 +5409,153 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) sc->rxon.cck_mask = 0x03; sc->rxon.ofdm_mask = 0; } else { - /* XXX assume 802.11b/g */ + /* Assume 802.11b/g. */ sc->rxon.cck_mask = 0x0f; sc->rxon.ofdm_mask = 0x15; } -#if 0 /* HT */ if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { - sc->rxon.flags &= ~htole32(IWN_RXON_HT); - if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) - sc->rxon.flags |= htole32(IWN_RXON_HT40U); - else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) - sc->rxon.flags |= htole32(IWN_RXON_HT40D); - else - sc->rxon.flags |= htole32(IWN_RXON_HT20); - sc->rxon.rxchain = htole16( - IWN_RXCHAIN_VALID(3) - | IWN_RXCHAIN_MIMO_COUNT(3) - | IWN_RXCHAIN_IDLE_COUNT(1) - | IWN_RXCHAIN_MIMO_FORCE); - - maxrxampdu = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); - ampdudensity = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); - } else - maxrxampdu = ampdudensity = 0; -#endif + htflags |= IWN_RXON_HT_PROTMODE(ic->ic_curhtprotmode); + if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) { + switch (ic->ic_curhtprotmode) { + case IEEE80211_HTINFO_OPMODE_HT20PR: + htflags |= IWN_RXON_HT_MODEPURE40; + break; + default: + htflags |= IWN_RXON_HT_MODEMIXED; + break; + } + } + if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) + htflags |= IWN_RXON_HT_HT40MINUS; + } + sc->rxon.flags |= htole32(htflags); sc->rxon.filter |= htole32(IWN_FILTER_BSS); - - DPRINTF(sc, IWN_DEBUG_STATE, - "%s: config chan %d mode %d flags 0x%x cck 0x%x ofdm 0x%x " - "ht_single 0x%x ht_dual 0x%x rxchain 0x%x " - "myaddr %6D wlap %6D bssid %6D associd %d filter 0x%x\n", - __func__, - le16toh(sc->rxon.chan), sc->rxon.mode, le32toh(sc->rxon.flags), - sc->rxon.cck_mask, sc->rxon.ofdm_mask, - sc->rxon.ht_single_mask, sc->rxon.ht_dual_mask, - le16toh(sc->rxon.rxchain), - sc->rxon.myaddr, ":", sc->rxon.wlap, ":", sc->rxon.bssid, ":", - le16toh(sc->rxon.associd), le32toh(sc->rxon.filter)); - error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 1); + DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x\n", + sc->rxon.chan, sc->rxon.flags); + error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); if (error != 0) { device_printf(sc->sc_dev, - "%s: could not update configuration, error %d\n", - __func__, error); + "%s: could not update configuration, error %d\n", __func__, + error); return error; } /* Configuration has changed, set TX power accordingly. */ - error = hal->set_txpower(sc, ni->ni_chan, 1); - if (error != 0) { + if ((error = ops->set_txpower(sc, ni->ni_chan, 1)) != 0) { device_printf(sc->sc_dev, - "%s: could not set Tx power, error %d\n", __func__, error); + "%s: could not set TX power, error %d\n", __func__, error); return error; } + /* Fake a join to initialize the TX rate. */ + ((struct iwn_node *)ni)->id = IWN_ID_BSS; + iwn_newassoc(ni, 1); + /* Add BSS node. */ memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); node.id = IWN_ID_BSS; -#ifdef notyet - node.htflags = htole32(IWN_AMDPU_SIZE_FACTOR(3) | - IWN_AMDPU_DENSITY(5)); /* 2us */ -#endif - DPRINTF(sc, IWN_DEBUG_STATE, "%s: add BSS node, id %d htflags 0x%x\n", - __func__, node.id, le32toh(node.htflags)); - error = hal->add_node(sc, &node, 1); - if (error != 0) { - device_printf(sc->sc_dev, "could not add BSS node\n"); - return error; + if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { + switch (ni->ni_htcap & IEEE80211_HTCAP_SMPS) { + case IEEE80211_HTCAP_SMPS_ENA: + node.htflags |= htole32(IWN_SMPS_MIMO_DIS); + break; + case IEEE80211_HTCAP_SMPS_DYNAMIC: + node.htflags |= htole32(IWN_SMPS_MIMO_PROT); + break; + } + node.htflags |= htole32(IWN_AMDPU_SIZE_FACTOR(3) | + IWN_AMDPU_DENSITY(5)); /* 4us */ + if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) + node.htflags |= htole32(IWN_NODE_HT40); } - DPRINTF(sc, IWN_DEBUG_STATE, "setting link quality for node %d\n", - node.id); - error = iwn_set_link_quality(sc, node.id, 1); + DPRINTF(sc, IWN_DEBUG_STATE, "%s: adding BSS node\n", __func__); + error = ops->add_node(sc, &node, 1); if (error != 0) { device_printf(sc->sc_dev, - "%s: could not setup MRR for node %d, error %d\n", + "%s: could not add BSS node, error %d\n", __func__, error); + return error; + } + DPRINTF(sc, IWN_DEBUG_STATE, "%s: setting link quality for node %d\n", + __func__, node.id); + if ((error = iwn_set_link_quality(sc, ni)) != 0) { + device_printf(sc->sc_dev, + "%s: could not setup link quality for node %d, error %d\n", __func__, node.id, error); return error; } - error = iwn_init_sensitivity(sc); - if (error != 0) { + if ((error = iwn_init_sensitivity(sc)) != 0) { device_printf(sc->sc_dev, - "%s: could not set sensitivity, error %d\n", - __func__, error); + "%s: could not set sensitivity, error %d\n", __func__, + error); return error; } - /* Start periodic calibration timer. */ sc->calib.state = IWN_CALIB_STATE_ASSOC; - iwn_calib_reset(sc); + sc->calib_cnt = 0; + callout_reset(&sc->calib_to, msecs_to_ticks(500), iwn_calib_timeout, + sc); /* Link LED always on while associated. */ iwn_set_led(sc, IWN_LED_LINK, 0, 1); - return 0; -#undef MS } -#if 0 /* HT */ /* * This function is called by upper layer when an ADDBA request is received * from another STA and before the ADDBA response is sent. */ static int -iwn_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni, - uint8_t tid) +iwn_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap, + int baparamset, int batimeout, int baseqctl) { - struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid]; - struct iwn_softc *sc = ic->ic_softc; +#define MS(_v, _f) (((_v) & _f) >> _f##_S) + struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc; + struct iwn_ops *ops = &sc->ops; struct iwn_node *wn = (void *)ni; struct iwn_node_info node; + uint16_t ssn; + uint8_t tid; + int error; + + tid = MS(le16toh(baparamset), IEEE80211_BAPS_TID); + ssn = MS(le16toh(baseqctl), IEEE80211_BASEQ_START); memset(&node, 0, sizeof node); node.id = wn->id; node.control = IWN_NODE_UPDATE; node.flags = IWN_FLAG_SET_ADDBA; node.addba_tid = tid; - node.addba_ssn = htole16(ba->ba_winstart); + node.addba_ssn = htole16(ssn); DPRINTF(sc, IWN_DEBUG_RECV, "ADDBA RA=%d TID=%d SSN=%d\n", - wn->id, tid, ba->ba_winstart)); - return sc->sc_hal->add_node(sc, &node, 1); + wn->id, tid, ssn); + error = ops->add_node(sc, &node, 1); + if (error != 0) + return error; + return sc->sc_ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl); +#undef MS } /* * This function is called by upper layer on teardown of an HT-immediate - * Block Ack agreement (eg. uppon receipt of a DELBA frame.) + * Block Ack agreement (eg. uppon receipt of a DELBA frame). */ static void -iwn_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni, - uint8_t tid) +iwn_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) { - struct iwn_softc *sc = ic->ic_softc; + struct ieee80211com *ic = ni->ni_ic; + struct iwn_softc *sc = ic->ic_ifp->if_softc; + struct iwn_ops *ops = &sc->ops; struct iwn_node *wn = (void *)ni; struct iwn_node_info node; + uint8_t tid; + + /* XXX: tid as an argument */ + for (tid = 0; tid < WME_NUM_TID; tid++) { + if (&ni->ni_rx_ampdu[tid] == rap) + break; + } memset(&node, 0, sizeof node); node.id = wn->id; @@ -5022,7 +5563,58 @@ iwn_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni, node.flags = IWN_FLAG_SET_DELBA; node.delba_tid = tid; DPRINTF(sc, IWN_DEBUG_RECV, "DELBA RA=%d TID=%d\n", wn->id, tid); - (void)sc->sc_hal->add_node(sc, &node, 1); + (void)ops->add_node(sc, &node, 1); + sc->sc_ampdu_rx_stop(ni, rap); +} + +static int +iwn_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, + int dialogtoken, int baparamset, int batimeout) +{ + struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc; + int qid; + + for (qid = sc->firstaggqueue; qid < sc->ntxqs; qid++) { + if (sc->qid2tap[qid] == NULL) + break; + } + if (qid == sc->ntxqs) { + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: not free aggregation queue\n", + __func__); + return 0; + } + tap->txa_private = malloc(sizeof(int), M_DEVBUF, M_NOWAIT); + if (tap->txa_private == NULL) { + device_printf(sc->sc_dev, + "%s: failed to alloc TX aggregation structure\n", __func__); + return 0; + } + sc->qid2tap[qid] = tap; + *(int *)tap->txa_private = qid; + return sc->sc_addba_request(ni, tap, dialogtoken, baparamset, + batimeout); +} + +static int +iwn_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, + int code, int baparamset, int batimeout) +{ + struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc; + int qid = *(int *)tap->txa_private; + uint8_t tid = WME_AC_TO_TID(tap->txa_ac); + int ret; + + if (code == IEEE80211_STATUS_SUCCESS) { + ni->ni_txseqs[tid] = tap->txa_start & 0xfff; + ret = iwn_ampdu_tx_start(ni->ni_ic, ni, tid); + if (ret != 1) + return ret; + } else { + sc->qid2tap[qid] = NULL; + free(tap->txa_private, M_DEVBUF); + tap->txa_private = NULL; + } + return sc->sc_addba_response(ni, tap, code, baparamset, batimeout); } /* @@ -5033,12 +5625,12 @@ static int iwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni, uint8_t tid) { - struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid]; - struct iwn_softc *sc = ic->ic_softc; - const struct iwn_hal *hal = sc->sc_hal; + struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[TID_TO_WME_AC(tid)]; + struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc; + struct iwn_ops *ops = &sc->ops; struct iwn_node *wn = (void *)ni; struct iwn_node_info node; - int error; + int error, qid; /* Enable TX for the specified RA/TID. */ wn->disable_tid &= ~(1 << tid); @@ -5047,38 +5639,47 @@ iwn_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni, node.control = IWN_NODE_UPDATE; node.flags = IWN_FLAG_SET_DISABLE_TID; node.disable_tid = htole16(wn->disable_tid); - error = hal->add_node(sc, &node, 1); + error = ops->add_node(sc, &node, 1); if (error != 0) - return error; + return 0; if ((error = iwn_nic_lock(sc)) != 0) - return error; - hal->ampdu_tx_start(sc, ni, tid, ba->ba_winstart); + return 0; + qid = *(int *)tap->txa_private; + ops->ampdu_tx_start(sc, ni, qid, tid, tap->txa_start & 0xfff); iwn_nic_unlock(sc); - return 0; + + iwn_set_link_quality(sc, ni); + return 1; } static void -iwn_ampdu_tx_stop(struct ieee80211com *ic, struct ieee80211_node *ni, - uint8_t tid) +iwn_ampdu_tx_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { - struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid]; - struct iwn_softc *sc = ic->ic_softc; - int error; + struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc; + struct iwn_ops *ops = &sc->ops; + uint8_t tid = WME_AC_TO_TID(tap->txa_ac); + int qid; - error = iwn_nic_lock(sc); - if (error != 0) + if (tap->txa_private == NULL) return; - sc->sc_hal->ampdu_tx_stop(sc, tid, ba->ba_winstart); + + qid = *(int *)tap->txa_private; + if (iwn_nic_lock(sc) != 0) + return; + ops->ampdu_tx_stop(sc, qid, tid, tap->txa_start & 0xfff); iwn_nic_unlock(sc); + sc->qid2tap[qid] = NULL; + free(tap->txa_private, M_DEVBUF); + tap->txa_private = NULL; + sc->sc_addba_stop(ni, tap); } static void iwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni, - uint8_t tid, uint16_t ssn) + int qid, uint8_t tid, uint16_t ssn) { struct iwn_node *wn = (void *)ni; - int qid = 7 + tid; /* Stop TX scheduler while we're changing its configuration. */ iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), @@ -5092,6 +5693,7 @@ iwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni, iwn_prph_setbits(sc, IWN4965_SCHED_QCHAIN_SEL, 1 << qid); /* Set starting sequence number from the ADDBA request. */ + sc->txq[qid].cur = sc->txq[qid].read = (ssn & 0xff); IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); iwn_prph_write(sc, IWN4965_SCHED_QUEUE_RDPTR(qid), ssn); @@ -5112,10 +5714,8 @@ iwn4965_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni, } static void -iwn4965_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn) +iwn4965_ampdu_tx_stop(struct iwn_softc *sc, int qid, uint8_t tid, uint16_t ssn) { - int qid = 7 + tid; - /* Stop TX scheduler while we're changing its configuration. */ iwn_prph_write(sc, IWN4965_SCHED_QUEUE_STATUS(qid), IWN4965_TXQ_STATUS_CHGACT); @@ -5134,10 +5734,9 @@ iwn4965_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn) static void iwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni, - uint8_t tid, uint16_t ssn) + int qid, uint8_t tid, uint16_t ssn) { struct iwn_node *wn = (void *)ni; - int qid = 10 + tid; /* Stop TX scheduler while we're changing its configuration. */ iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), @@ -5154,6 +5753,7 @@ iwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni, iwn_prph_setbits(sc, IWN5000_SCHED_AGGR_SEL, 1 << qid); /* Set starting sequence number from the ADDBA request. */ + sc->txq[qid].cur = sc->txq[qid].read = (ssn & 0xff); IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | (ssn & 0xff)); iwn_prph_write(sc, IWN5000_SCHED_QUEUE_RDPTR(qid), ssn); @@ -5170,10 +5770,8 @@ iwn5000_ampdu_tx_start(struct iwn_softc *sc, struct ieee80211_node *ni, } static void -iwn5000_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn) +iwn5000_ampdu_tx_stop(struct iwn_softc *sc, int qid, uint8_t tid, uint16_t ssn) { - int qid = 10 + tid; - /* Stop TX scheduler while we're changing its configuration. */ iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), IWN5000_TXQ_STATUS_CHGACT); @@ -5192,203 +5790,59 @@ iwn5000_ampdu_tx_stop(struct iwn_softc *sc, uint8_t tid, uint16_t ssn) iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), IWN5000_TXQ_STATUS_INACTIVE | iwn_tid2fifo[tid]); } -#endif /* - * Send calibration results to the runtime firmware. These results were - * obtained on first boot from the initialization firmware, or by reading - * the EEPROM for crystal calibration. + * Query calibration tables from the initialization firmware. We do this + * only once at first boot. Called from a process context. */ static int -iwn5000_send_calib_results(struct iwn_softc *sc) +iwn5000_query_calibration(struct iwn_softc *sc) { - struct iwn_calib_info *calib_result; - int idx, error; - - for (idx = 0; idx < IWN_CALIB_NUM; idx++) { - calib_result = &sc->calib_results[idx]; - - /* No support for this type of calibration. */ - if ((sc->calib_init & (1 << idx)) == 0) - continue; - - /* No calibration result available. */ - if (calib_result->buf == NULL) - continue; - - DPRINTF(sc, IWN_DEBUG_CALIBRATE, - "%s: send calibration result idx=%d, len=%d\n", - __func__, idx, calib_result->len); - - error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, calib_result->buf, - calib_result->len, 0); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not send calibration result " - "idx=%d, error=%d\n", - __func__, idx, error); - return error; - } - } - return 0; -} - -/* - * Save calibration result at the given index. The index determines - * in which order the results are sent to the runtime firmware. - */ -static int -iwn5000_save_calib_result(struct iwn_softc *sc, struct iwn_phy_calib *calib, - int len, int idx) -{ - struct iwn_calib_info *calib_result = &sc->calib_results[idx]; - - DPRINTF(sc, IWN_DEBUG_CALIBRATE, - "%s: saving calibration result code=%d, idx=%d, len=%d\n", - __func__, calib->code, idx, len); - - if (calib_result->buf != NULL) - free(calib_result->buf, M_DEVBUF); - - calib_result->buf = malloc(len, M_DEVBUF, M_NOWAIT); - if (calib_result->buf == NULL) { - device_printf(sc->sc_dev, - "%s: not enough memory for calibration result " - "code=%d, len=%d\n", __func__, calib->code, len); - return ENOMEM; - } - - calib_result->len = len; - memcpy(calib_result->buf, calib, len); - return 0; -} - -static void -iwn5000_free_calib_results(struct iwn_softc *sc) -{ - struct iwn_calib_info *calib_result; - int idx; - - for (idx = 0; idx < IWN_CALIB_NUM; idx++) { - calib_result = &sc->calib_results[idx]; - - if (calib_result->buf != NULL) - free(calib_result->buf, M_DEVBUF); - - calib_result->buf = NULL; - calib_result->len = 0; - } -} - -/* - * Obtain the crystal calibration result from the EEPROM. - */ -static int -iwn5000_chrystal_calib(struct iwn_softc *sc) -{ - struct iwn5000_phy_calib_crystal cmd; - uint32_t base, crystal; - uint16_t val; - - /* Read crystal calibration. */ - iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2); - base = le16toh(val); - iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL, &crystal, - sizeof(uint32_t)); - DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: crystal calibration=0x%08x\n", - __func__, le32toh(crystal)); - - memset(&cmd, 0, sizeof cmd); - cmd.code = IWN5000_PHY_CALIB_CRYSTAL; - cmd.ngroups = 1; - cmd.isvalid = 1; - cmd.cap_pin[0] = le32toh(crystal) & 0xff; - cmd.cap_pin[1] = (le32toh(crystal) >> 16) & 0xff; - - return iwn5000_save_calib_result(sc, (struct iwn_phy_calib *)&cmd, - sizeof cmd, IWN_CALIB_IDX_XTAL); -} - -/* - * Query calibration results from the initialization firmware. We do this - * only once at first boot. - */ -static int -iwn5000_send_calib_query(struct iwn_softc *sc, uint32_t cfg) -{ -#define CALIB_INIT_CFG 0xffffffff; struct iwn5000_calib_config cmd; int error; memset(&cmd, 0, sizeof cmd); - cmd.ucode.once.enable = CALIB_INIT_CFG; - if (cfg == 0) { - cmd.ucode.once.start = CALIB_INIT_CFG; - cmd.ucode.once.send = CALIB_INIT_CFG; - cmd.ucode.flags = CALIB_INIT_CFG; - } else - cmd.ucode.once.start = cfg; - - DPRINTF(sc, IWN_DEBUG_CALIBRATE, - "%s: query calibration results, cfg %x\n", __func__, cfg); - + cmd.ucode.once.enable = 0xffffffff; + cmd.ucode.once.start = 0xffffffff; + cmd.ucode.once.send = 0xffffffff; + cmd.ucode.flags = 0xffffffff; + DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending calibration query\n", + __func__); error = iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof cmd, 0); if (error != 0) return error; /* Wait at most two seconds for calibration to complete. */ if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) - error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", 2 * hz); - + error = msleep(sc, &sc->sc_mtx, PCATCH, "iwncal", 2 * hz); return error; -#undef CALIB_INIT_CFG } /* - * Process a CALIBRATION_RESULT notification sent by the initialization - * firmware on response to a CMD_CALIB_CONFIG command. + * Send calibration results to the runtime firmware. These results were + * obtained on first boot from the initialization firmware. */ static int -iwn5000_rx_calib_result(struct iwn_softc *sc, struct iwn_rx_desc *desc, - struct iwn_rx_data *data) +iwn5000_send_calibration(struct iwn_softc *sc) { -#define FRAME_SIZE_MASK 0x3fff - struct iwn_phy_calib *calib = (struct iwn_phy_calib *)(desc + 1); - int len, idx; + int idx, error; - bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); - len = (le32toh(desc->len) & FRAME_SIZE_MASK); - - /* Remove length field itself. */ - len -= 4; - - /* - * Determine the order in which the results will be send to the - * runtime firmware. - */ - switch (calib->code) { - case IWN5000_PHY_CALIB_DC: - idx = IWN_CALIB_IDX_DC; - break; - case IWN5000_PHY_CALIB_LO: - idx = IWN_CALIB_IDX_LO; - break; - case IWN5000_PHY_CALIB_TX_IQ: - idx = IWN_CALIB_IDX_TX_IQ; - break; - case IWN5000_PHY_CALIB_TX_IQ_PERIODIC: - idx = IWN_CALIB_IDX_TX_IQ_PERIODIC; - break; - case IWN5000_PHY_CALIB_BASE_BAND: - idx = IWN_CALIB_IDX_BASE_BAND; - break; - default: + for (idx = 0; idx < 5; idx++) { + if (sc->calibcmd[idx].buf == NULL) + continue; /* No results available. */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, - "%s: unknown calibration code=%d\n", __func__, calib->code); - return EINVAL; + "send calibration result idx=%d len=%d\n", idx, + sc->calibcmd[idx].len); + error = iwn_cmd(sc, IWN_CMD_PHY_CALIB, sc->calibcmd[idx].buf, + sc->calibcmd[idx].len, 0); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not send calibration result, error %d\n", + __func__, error); + return error; + } } - return iwn5000_save_calib_result(sc, calib, len, idx); -#undef FRAME_SIZE_MASK + return 0; } static int @@ -5418,9 +5872,43 @@ iwn5000_send_wimax_coex(struct iwn_softc *sc) return iwn_cmd(sc, IWN5000_CMD_WIMAX_COEX, &wimax, sizeof wimax, 0); } +static int +iwn5000_crystal_calib(struct iwn_softc *sc) +{ + struct iwn5000_phy_calib_crystal cmd; + + memset(&cmd, 0, sizeof cmd); + cmd.code = IWN5000_PHY_CALIB_CRYSTAL; + cmd.ngroups = 1; + cmd.isvalid = 1; + cmd.cap_pin[0] = le32toh(sc->eeprom_crystal) & 0xff; + cmd.cap_pin[1] = (le32toh(sc->eeprom_crystal) >> 16) & 0xff; + DPRINTF(sc, IWN_DEBUG_CALIBRATE, "sending crystal calibration %d, %d\n", + cmd.cap_pin[0], cmd.cap_pin[1]); + return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0); +} + +static int +iwn5000_temp_offset_calib(struct iwn_softc *sc) +{ + struct iwn5000_phy_calib_temp_offset cmd; + + memset(&cmd, 0, sizeof cmd); + cmd.code = IWN5000_PHY_CALIB_TEMP_OFFSET; + cmd.ngroups = 1; + cmd.isvalid = 1; + if (sc->eeprom_temp != 0) + cmd.offset = htole16(sc->eeprom_temp); + else + cmd.offset = htole16(IWN_DEFAULT_TEMP_OFFSET); + DPRINTF(sc, IWN_DEBUG_CALIBRATE, "setting radio sensor offset to %d\n", + le16toh(cmd.offset)); + return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0); +} + /* * This function is called after the runtime firmware notifies us of its - * readiness (called in a process context.) + * readiness (called in a process context). */ static int iwn4965_post_alive(struct iwn_softc *sc) @@ -5435,7 +5923,7 @@ iwn4965_post_alive(struct iwn_softc *sc) iwn_mem_set_region_4(sc, sc->sched_base + IWN4965_SCHED_CTX_OFF, 0, IWN4965_SCHED_CTX_LEN / sizeof (uint32_t)); - /* Set physical address of TX scheduler rings (1KB aligned.) */ + /* Set physical address of TX scheduler rings (1KB aligned). */ iwn_prph_write(sc, IWN4965_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10); IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY); @@ -5473,7 +5961,7 @@ iwn4965_post_alive(struct iwn_softc *sc) /* * This function is called after the initialization or runtime firmware - * notifies us of its readiness (called in a process context.) + * notifies us of its readiness (called in a process context). */ static int iwn5000_post_alive(struct iwn_softc *sc) @@ -5485,8 +5973,7 @@ iwn5000_post_alive(struct iwn_softc *sc) iwn5000_ict_reset(sc); #endif - error = iwn_nic_lock(sc); - if (error != 0) + if ((error = iwn_nic_lock(sc)) != 0) return error; /* Clear TX scheduler state in SRAM. */ @@ -5494,7 +5981,7 @@ iwn5000_post_alive(struct iwn_softc *sc) iwn_mem_set_region_4(sc, sc->sched_base + IWN5000_SCHED_CTX_OFF, 0, IWN5000_SCHED_CTX_LEN / sizeof (uint32_t)); - /* Set physical address of TX scheduler rings (1KB aligned.) */ + /* Set physical address of TX scheduler rings (1KB aligned). */ iwn_prph_write(sc, IWN5000_SCHED_DRAM_ADDR, sc->sched_dma.paddr >> 10); IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY); @@ -5536,40 +6023,24 @@ iwn5000_post_alive(struct iwn_softc *sc) __func__, error); return error; } - - if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) { - /* - * Start calibration by setting and sending the chrystal - * calibration first, this must be done before we are able - * to query the other calibration results. - */ - error = iwn5000_chrystal_calib(sc); + if (sc->hw_type != IWN_HW_REV_TYPE_5150) { + /* Perform crystal calibration. */ + error = iwn5000_crystal_calib(sc); if (error != 0) { device_printf(sc->sc_dev, - "%s: could not set chrystal calibration, " - "error=%d\n", __func__, error); - return error; - } - error = iwn5000_send_calib_results(sc); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not send chrystal calibration, " - "error=%d\n", __func__, error); - return error; - } - - /* - * Query other calibration results from the initialization - * firmware. - */ - error = iwn5000_send_calib_query(sc, 0); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not query calibration, error=%d\n", + "%s: crystal calibration failed, error %d\n", + __func__, error); + return error; + } + } + if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) { + /* Query calibration from the initialization firmware. */ + if ((error = iwn5000_query_calibration(sc)) != 0) { + device_printf(sc->sc_dev, + "%s: could not query calibration, error %d\n", __func__, error); return error; } - /* * We have the calibration results now, reboot with the * runtime firmware (call ourselves recursively!) @@ -5577,31 +6048,15 @@ iwn5000_post_alive(struct iwn_softc *sc) iwn_hw_stop(sc); error = iwn_hw_init(sc); } else { - /* - * Send calibration results obtained from the initialization - * firmware to the runtime firmware. - */ - error = iwn5000_send_calib_results(sc); - - /* - * Tell the runtime firmware to do certain calibration types. - */ - if (sc->calib_runtime != 0) { - error = iwn5000_send_calib_query(sc, sc->calib_runtime); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not send query calibration, " - "error=%d, cfg=%x\n", __func__, error, - sc->calib_runtime); - } - } + /* Send calibration results to runtime firmware. */ + error = iwn5000_send_calibration(sc); } return error; } /* * The firmware boot code is small and is intended to be copied directly into - * the NIC internal memory (no DMA transfer.) + * the NIC internal memory (no DMA transfer). */ static int iwn4965_load_bootcode(struct iwn_softc *sc, const uint8_t *ucode, int size) @@ -5610,8 +6065,7 @@ iwn4965_load_bootcode(struct iwn_softc *sc, const uint8_t *ucode, int size) size /= sizeof (uint32_t); - error = iwn_nic_lock(sc); - if (error != 0) + if ((error = iwn_nic_lock(sc)) != 0) return error; /* Copy microcode image into NIC memory. */ @@ -5655,14 +6109,13 @@ iwn4965_load_firmware(struct iwn_softc *sc) /* Copy initialization sections into pre-allocated DMA-safe memory. */ memcpy(dma->vaddr, fw->init.data, fw->init.datasz); - bus_dmamap_sync(sc->fw_dma.tag, dma->map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ, fw->init.text, fw->init.textsz); - bus_dmamap_sync(sc->fw_dma.tag, dma->map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); /* Tell adapter where to find initialization sections. */ - error = iwn_nic_lock(sc); - if (error != 0) + if ((error = iwn_nic_lock(sc)) != 0) return error; iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4); iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->init.datasz); @@ -5682,8 +6135,7 @@ iwn4965_load_firmware(struct iwn_softc *sc) IWN_WRITE(sc, IWN_RESET, 0); /* Wait at most one second for first alive notification. */ - error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz); - if (error) { + if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) { device_printf(sc->sc_dev, "%s: timeout waiting for adapter to initialize, error %d\n", __func__, error); @@ -5696,16 +6148,14 @@ iwn4965_load_firmware(struct iwn_softc *sc) /* Copy runtime sections into pre-allocated DMA-safe memory. */ memcpy(dma->vaddr, fw->main.data, fw->main.datasz); - bus_dmamap_sync(sc->fw_dma.tag, dma->map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); memcpy(dma->vaddr + IWN4965_FW_DATA_MAXSZ, fw->main.text, fw->main.textsz); - bus_dmamap_sync(sc->fw_dma.tag, dma->map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); /* Tell adapter where to find runtime sections. */ - error = iwn_nic_lock(sc); - if (error != 0) + if ((error = iwn_nic_lock(sc)) != 0) return error; - iwn_prph_write(sc, IWN_BSM_DRAM_DATA_ADDR, dma->paddr >> 4); iwn_prph_write(sc, IWN_BSM_DRAM_DATA_SIZE, fw->main.datasz); iwn_prph_write(sc, IWN_BSM_DRAM_TEXT_ADDR, @@ -5726,10 +6176,9 @@ iwn5000_load_firmware_section(struct iwn_softc *sc, uint32_t dst, /* Copy firmware section into pre-allocated DMA-safe memory. */ memcpy(dma->vaddr, section, size); - bus_dmamap_sync(sc->fw_dma.tag, dma->map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); - error = iwn_nic_lock(sc); - if (error != 0) + if ((error = iwn_nic_lock(sc)) != 0) return error; IWN_WRITE(sc, IWN_FH_TX_CONFIG(IWN_SRVC_DMACHNL), @@ -5752,7 +6201,7 @@ iwn5000_load_firmware_section(struct iwn_softc *sc, uint32_t dst, iwn_nic_unlock(sc); /* Wait at most five seconds for FH DMA transfer to complete. */ - return msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz); + return msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", 5 * hz); } static int @@ -5797,7 +6246,7 @@ iwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw) size_t hdrlen = 24; uint32_t rev; - ptr = (const uint32_t *)sc->fw_fp->data; + ptr = (const uint32_t *)fw->data; rev = le32toh(*ptr++); /* Check firmware API version. */ @@ -5812,8 +6261,7 @@ iwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw) ptr++; } if (fw->size < hdrlen) { - device_printf(sc->sc_dev, - "%s: firmware file too short: %zu bytes\n", + device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n", __func__, fw->size); return EINVAL; } @@ -5826,8 +6274,7 @@ iwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw) /* Check that all firmware sections fit. */ if (fw->size < hdrlen + fw->main.textsz + fw->main.datasz + fw->init.textsz + fw->init.datasz + fw->boot.textsz) { - device_printf(sc->sc_dev, - "%s: firmware file too short: %zu bytes\n", + device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n", __func__, fw->size); return EINVAL; } @@ -5838,14 +6285,13 @@ iwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw) fw->init.text = fw->main.data + fw->main.datasz; fw->init.data = fw->init.text + fw->init.textsz; fw->boot.text = fw->init.data + fw->init.datasz; - return 0; } /* * Extract text and data sections from a TLV firmware image. */ -int +static int iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw, uint16_t alt) { @@ -5853,21 +6299,21 @@ iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw, const struct iwn_fw_tlv *tlv; const uint8_t *ptr, *end; uint64_t altmask; - uint32_t len; + uint32_t len, tmp; if (fw->size < sizeof (*hdr)) { - device_printf(sc->sc_dev, - "%s: firmware file too short: %zu bytes\n", + device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n", __func__, fw->size); return EINVAL; } hdr = (const struct iwn_fw_tlv_hdr *)fw->data; if (hdr->signature != htole32(IWN_FW_SIGNATURE)) { - device_printf(sc->sc_dev, - "%s: bad firmware file signature 0x%08x\n", + device_printf(sc->sc_dev, "%s: bad firmware signature 0x%08x\n", __func__, le32toh(hdr->signature)); return EINVAL; } + DPRINTF(sc, IWN_DEBUG_RESET, "FW: \"%.64s\", build 0x%x\n", hdr->descr, + le32toh(hdr->build)); /* * Select the closest supported alternative that is less than @@ -5876,6 +6322,7 @@ iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw, altmask = le64toh(hdr->altmask); while (alt > 0 && !(altmask & (1ULL << alt))) alt--; /* Downgrade. */ + DPRINTF(sc, IWN_DEBUG_RESET, "using alternative %d\n", alt); ptr = (const uint8_t *)(hdr + 1); end = (const uint8_t *)(fw->data + fw->size); @@ -5888,8 +6335,8 @@ iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw, ptr += sizeof (*tlv); if (ptr + len > end) { device_printf(sc->sc_dev, - "%s: firmware file too short: %zu bytes\n", - __func__, fw->size); + "%s: firmware too short: %zu bytes\n", __func__, + fw->size); return EINVAL; } /* Skip other alternatives. */ @@ -5917,13 +6364,23 @@ iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw, fw->boot.text = ptr; fw->boot.textsz = len; break; + case IWN_FW_TLV_ENH_SENS: + if (!len) + sc->sc_flags |= IWN_FLAG_ENH_SENS; + break; + case IWN_FW_TLV_PHY_CALIB: + tmp = htole32(*ptr); + if (tmp < 253) { + sc->reset_noise_gain = tmp; + sc->noise_gain = tmp + 1; + } + break; default: DPRINTF(sc, IWN_DEBUG_RESET, - "%s: TLV type %d not handled\n", - __func__, le16toh(tlv->type)); + "TLV type %d not handled\n", le16toh(tlv->type)); break; } -next: /* TLV fields are 32-bit aligned. */ + next: /* TLV fields are 32-bit aligned. */ ptr += (len + 3) & ~3; } return 0; @@ -5932,7 +6389,6 @@ next: /* TLV fields are 32-bit aligned. */ static int iwn_read_firmware(struct iwn_softc *sc) { - const struct iwn_hal *hal = sc->sc_hal; struct iwn_fw_info *fw = &sc->fw; int error; @@ -5943,9 +6399,8 @@ iwn_read_firmware(struct iwn_softc *sc) /* Read firmware image from filesystem. */ sc->fw_fp = firmware_get(sc->fwname); if (sc->fw_fp == NULL) { - device_printf(sc->sc_dev, - "%s: could not load firmare image \"%s\"\n", __func__, - sc->fwname); + device_printf(sc->sc_dev, "%s: could not read firmware %s\n", + __func__, sc->fwname); IWN_LOCK(sc); return EINVAL; } @@ -5954,9 +6409,10 @@ iwn_read_firmware(struct iwn_softc *sc) fw->size = sc->fw_fp->datasize; fw->data = (const uint8_t *)sc->fw_fp->data; if (fw->size < sizeof (uint32_t)) { - device_printf(sc->sc_dev, - "%s: firmware file too short: %zu bytes\n", + device_printf(sc->sc_dev, "%s: firmware too short: %zu bytes\n", __func__, fw->size); + firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); + sc->fw_fp = NULL; return EINVAL; } @@ -5967,19 +6423,24 @@ iwn_read_firmware(struct iwn_softc *sc) error = iwn_read_firmware_tlv(sc, fw, 1); if (error != 0) { device_printf(sc->sc_dev, - "%s: could not read firmware sections\n", __func__); + "%s: could not read firmware sections, error %d\n", + __func__, error); + firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); + sc->fw_fp = NULL; return error; } /* Make sure text and data sections fit in hardware memory. */ - if (fw->main.textsz > hal->fw_text_maxsz || - fw->main.datasz > hal->fw_data_maxsz || - fw->init.textsz > hal->fw_text_maxsz || - fw->init.datasz > hal->fw_data_maxsz || + if (fw->main.textsz > sc->fw_text_maxsz || + fw->main.datasz > sc->fw_data_maxsz || + fw->init.textsz > sc->fw_text_maxsz || + fw->init.datasz > sc->fw_data_maxsz || fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ || (fw->boot.textsz & 3) != 0) { - device_printf(sc->sc_dev, - "%s: firmware sections too large\n", __func__); + device_printf(sc->sc_dev, "%s: firmware sections too large\n", + __func__); + firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); + sc->fw_fp = NULL; return EINVAL; } @@ -6009,24 +6470,24 @@ iwn_clock_wait(struct iwn_softc *sc) static int iwn_apm_init(struct iwn_softc *sc) { - uint32_t tmp; + uint32_t reg; int error; - /* Disable L0s exit timer (NMI bug workaround.) */ + /* Disable L0s exit timer (NMI bug workaround). */ IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER); - /* Don't wait for ICH L0s (ICH bug workaround.) */ + /* Don't wait for ICH L0s (ICH bug workaround). */ IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX); - /* Set FH wait threshold to max (HW bug under stress workaround.) */ + /* Set FH wait threshold to max (HW bug under stress workaround). */ IWN_SETBITS(sc, IWN_DBG_HPET_MEM, 0xffff0000); /* Enable HAP INTA to move adapter from L1a to L0s. */ IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_HAP_WAKE_L1A); /* Retrieve PCIe Active State Power Management (ASPM). */ - tmp = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); + reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */ - if (tmp & 0x02) /* L1 Entry enabled. */ + if (reg & 0x02) /* L1 Entry enabled. */ IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); else IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); @@ -6036,16 +6497,13 @@ iwn_apm_init(struct iwn_softc *sc) IWN_SETBITS(sc, IWN_ANA_PLL, IWN_ANA_PLL_INIT); /* Wait for clock stabilization before accessing prph. */ - error = iwn_clock_wait(sc); - if (error != 0) + if ((error = iwn_clock_wait(sc)) != 0) return error; - error = iwn_nic_lock(sc); - if (error != 0) + if ((error = iwn_nic_lock(sc)) != 0) return error; - if (sc->hw_type == IWN_HW_REV_TYPE_4965) { - /* Enable DMA and BSM (Bootstrap State Machine.) */ + /* Enable DMA and BSM (Bootstrap State Machine). */ iwn_prph_write(sc, IWN_APMG_CLK_EN, IWN_APMG_CLK_CTRL_DMA_CLK_RQT | IWN_APMG_CLK_CTRL_BSM_CLK_RQT); @@ -6055,7 +6513,6 @@ iwn_apm_init(struct iwn_softc *sc) IWN_APMG_CLK_CTRL_DMA_CLK_RQT); } DELAY(20); - /* Disable L1-Active. */ iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS); iwn_nic_unlock(sc); @@ -6075,8 +6532,7 @@ iwn_apm_stop_master(struct iwn_softc *sc) return; DELAY(10); } - device_printf(sc->sc_dev, "%s: timeout waiting for master\n", - __func__); + device_printf(sc->sc_dev, "%s: timeout waiting for master\n", __func__); } static void @@ -6125,8 +6581,7 @@ iwn5000_nic_config(struct iwn_softc *sc) IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_RADIO_SI | IWN_HW_IF_CONFIG_MAC_SI); - error = iwn_nic_lock(sc); - if (error != 0) + if ((error = iwn_nic_lock(sc)) != 0) return error; iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_EARLY_PWROFF_DIS); @@ -6147,10 +6602,13 @@ iwn5000_nic_config(struct iwn_softc *sc) /* Use internal power amplifier only. */ IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_IPA); } - if (sc->hw_type == IWN_HW_REV_TYPE_6050 && sc->calib_ver >= 6) { + if ((sc->hw_type == IWN_HW_REV_TYPE_6050 || + sc->hw_type == IWN_HW_REV_TYPE_6005) && sc->calib_ver >= 6) { /* Indicate that ROM calibration version is >=6. */ IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_CALIB_VER6); } + if (sc->hw_type == IWN_HW_REV_TYPE_6005) + IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_6050_1X2); return 0; } @@ -6196,41 +6654,37 @@ iwn_hw_prepare(struct iwn_softc *sc) static int iwn_hw_init(struct iwn_softc *sc) { - const struct iwn_hal *hal = sc->sc_hal; + struct iwn_ops *ops = &sc->ops; int error, chnl, qid; /* Clear pending interrupts. */ IWN_WRITE(sc, IWN_INT, 0xffffffff); - error = iwn_apm_init(sc); - if (error != 0) { + if ((error = iwn_apm_init(sc)) != 0) { device_printf(sc->sc_dev, - "%s: could not power ON adapter, error %d\n", - __func__, error); + "%s: could not power ON adapter, error %d\n", __func__, + error); return error; } /* Select VMAIN power source. */ - error = iwn_nic_lock(sc); - if (error != 0) + if ((error = iwn_nic_lock(sc)) != 0) return error; iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_PWR_SRC_MASK); iwn_nic_unlock(sc); /* Perform adapter-specific initialization. */ - error = hal->nic_config(sc); - if (error != 0) + if ((error = ops->nic_config(sc)) != 0) return error; /* Initialize RX ring. */ - error = iwn_nic_lock(sc); - if (error != 0) + if ((error = iwn_nic_lock(sc)) != 0) return error; IWN_WRITE(sc, IWN_FH_RX_CONFIG, 0); IWN_WRITE(sc, IWN_FH_RX_WPTR, 0); - /* Set physical address of RX ring (256-byte aligned.) */ + /* Set physical address of RX ring (256-byte aligned). */ IWN_WRITE(sc, IWN_FH_RX_BASE, sc->rxq.desc_dma.paddr >> 8); - /* Set physical address of RX status (16-byte aligned.) */ + /* Set physical address of RX status (16-byte aligned). */ IWN_WRITE(sc, IWN_FH_STATUS_WPTR, sc->rxq.stat_dma.paddr >> 4); /* Enable RX. */ IWN_WRITE(sc, IWN_FH_RX_CONFIG, @@ -6243,28 +6697,27 @@ iwn_hw_init(struct iwn_softc *sc) iwn_nic_unlock(sc); IWN_WRITE(sc, IWN_FH_RX_WPTR, (IWN_RX_RING_COUNT - 1) & ~7); - error = iwn_nic_lock(sc); - if (error != 0) + if ((error = iwn_nic_lock(sc)) != 0) return error; /* Initialize TX scheduler. */ - iwn_prph_write(sc, hal->sched_txfact_addr, 0); + iwn_prph_write(sc, sc->sched_txfact_addr, 0); - /* Set physical address of "keep warm" page (16-byte aligned.) */ + /* Set physical address of "keep warm" page (16-byte aligned). */ IWN_WRITE(sc, IWN_FH_KW_ADDR, sc->kw_dma.paddr >> 4); /* Initialize TX rings. */ - for (qid = 0; qid < hal->ntxqs; qid++) { + for (qid = 0; qid < sc->ntxqs; qid++) { struct iwn_tx_ring *txq = &sc->txq[qid]; - /* Set physical address of TX ring (256-byte aligned.) */ + /* Set physical address of TX ring (256-byte aligned). */ IWN_WRITE(sc, IWN_FH_CBBC_QUEUE(qid), txq->desc_dma.paddr >> 8); } iwn_nic_unlock(sc); /* Enable DMA channels. */ - for (chnl = 0; chnl < hal->ndmachnls; chnl++) { + for (chnl = 0; chnl < sc->ndmachnls; chnl++) { IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), IWN_FH_TX_CONFIG_DMA_ENA | IWN_FH_TX_CONFIG_DMA_CREDIT_ENA); @@ -6285,30 +6738,30 @@ iwn_hw_init(struct iwn_softc *sc) IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); - error = hal->load_firmware(sc); - if (error != 0) { + /* Enable shadow registers. */ + if (sc->hw_type >= IWN_HW_REV_TYPE_6000) + IWN_SETBITS(sc, IWN_SHADOW_REG_CTRL, 0x800fffff); + + if ((error = ops->load_firmware(sc)) != 0) { device_printf(sc->sc_dev, - "%s: could not load firmware, error %d\n", - __func__, error); + "%s: could not load firmware, error %d\n", __func__, + error); return error; } /* Wait at most one second for firmware alive notification. */ - error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz); - if (error != 0) { + if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz)) != 0) { device_printf(sc->sc_dev, "%s: timeout waiting for adapter to initialize, error %d\n", __func__, error); return error; } /* Do post-firmware initialization. */ - return hal->post_alive(sc); + return ops->post_alive(sc); } static void iwn_hw_stop(struct iwn_softc *sc) { - const struct iwn_hal *hal = sc->sc_hal; - uint32_t tmp; int chnl, qid, ntries; IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO); @@ -6323,15 +6776,14 @@ iwn_hw_stop(struct iwn_softc *sc) iwn_nic_unlock(sc); /* Stop TX scheduler. */ - iwn_prph_write(sc, hal->sched_txfact_addr, 0); + iwn_prph_write(sc, sc->sched_txfact_addr, 0); /* Stop all DMA channels. */ if (iwn_nic_lock(sc) == 0) { - for (chnl = 0; chnl < hal->ndmachnls; chnl++) { + for (chnl = 0; chnl < sc->ndmachnls; chnl++) { IWN_WRITE(sc, IWN_FH_TX_CONFIG(chnl), 0); for (ntries = 0; ntries < 200; ntries++) { - tmp = IWN_READ(sc, IWN_FH_TX_STATUS); - if ((tmp & IWN_FH_TX_STATUS_IDLE(chnl)) == + if (IWN_READ(sc, IWN_FH_TX_STATUS) & IWN_FH_TX_STATUS_IDLE(chnl)) break; DELAY(10); @@ -6344,7 +6796,7 @@ iwn_hw_stop(struct iwn_softc *sc) iwn_reset_rx_ring(sc, &sc->rxq); /* Reset all TX rings. */ - for (qid = 0; qid < hal->ntxqs; qid++) + for (qid = 0; qid < sc->ntxqs; qid++) iwn_reset_tx_ring(sc, &sc->txq[qid]); if (iwn_nic_lock(sc) == 0) { @@ -6353,11 +6805,43 @@ iwn_hw_stop(struct iwn_softc *sc) iwn_nic_unlock(sc); } DELAY(5); - /* Power OFF adapter. */ iwn_apm_stop(sc); } +static void +iwn_radio_on(void *arg0, int pending) +{ + struct iwn_softc *sc = arg0; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + if (vap != NULL) { + iwn_init(sc); + ieee80211_init(vap); + } +} + +static void +iwn_radio_off(void *arg0, int pending) +{ + struct iwn_softc *sc = arg0; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + iwn_stop(sc); + if (vap != NULL) + ieee80211_stop(vap); + + /* Enable interrupts to get RF toggle notification. */ + IWN_LOCK(sc); + IWN_WRITE(sc, IWN_INT, 0xffffffff); + IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); + IWN_UNLOCK(sc); +} + static void iwn_init_locked(struct iwn_softc *sc) { @@ -6366,9 +6850,8 @@ iwn_init_locked(struct iwn_softc *sc) IWN_LOCK_ASSERT(sc); - error = iwn_hw_prepare(sc); - if (error != 0) { - device_printf(sc->sc_dev, "%s: hardware not ready, eror %d\n", + if ((error = iwn_hw_prepare(sc)) != 0) { + device_printf(sc->sc_dev, "%s: hardware not ready, error %d\n", __func__, error); goto fail; } @@ -6381,7 +6864,6 @@ iwn_init_locked(struct iwn_softc *sc) if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) { device_printf(sc->sc_dev, "radio is disabled by hardware switch\n"); - /* Enable interrupts to get RF toggle notifications. */ IWN_WRITE(sc, IWN_INT, 0xffffffff); IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); @@ -6389,11 +6871,10 @@ iwn_init_locked(struct iwn_softc *sc) } /* Read firmware images from the filesystem. */ - error = iwn_read_firmware(sc); - if (error != 0) { + if ((error = iwn_read_firmware(sc)) != 0) { device_printf(sc->sc_dev, - "%s: could not read firmware, error %d\n", - __func__, error); + "%s: could not read firmware, error %d\n", __func__, + error); goto fail; } @@ -6403,27 +6884,26 @@ iwn_init_locked(struct iwn_softc *sc) sc->fw_fp = NULL; if (error != 0) { device_printf(sc->sc_dev, - "%s: could not initialize hardware, error %d\n", - __func__, error); + "%s: could not initialize hardware, error %d\n", __func__, + error); goto fail; } /* Configure adapter now that it is ready. */ - error = iwn_config(sc); - if (error != 0) { + if ((error = iwn_config(sc)) != 0) { device_printf(sc->sc_dev, - "%s: could not configure device, error %d\n", - __func__, error); + "%s: could not configure device, error %d\n", __func__, + error); goto fail; } ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; + callout_reset(&sc->watchdog_to, hz, iwn_watchdog, sc); return; -fail: - iwn_stop_locked(sc); +fail: iwn_stop_locked(sc); } static void @@ -6449,7 +6929,8 @@ iwn_stop_locked(struct iwn_softc *sc) IWN_LOCK_ASSERT(sc); sc->sc_tx_timer = 0; - callout_stop(&sc->sc_timer_to); + callout_stop(&sc->watchdog_to); + callout_stop(&sc->calib_to); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); /* Power OFF hardware. */ @@ -6506,12 +6987,24 @@ iwn_set_channel(struct ieee80211com *ic) const struct ieee80211_channel *c = ic->ic_curchan; struct ifnet *ifp = ic->ic_ifp; struct iwn_softc *sc = ifp->if_softc; + int error; IWN_LOCK(sc); sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq); sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags); sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq); sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags); + + /* + * Only need to set the channel in Monitor mode. AP scanning and auth + * are already taken care of by their respective firmware commands. + */ + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + error = iwn_config(sc); + if (error != 0) + device_printf(sc->sc_dev, + "%s: error %d settting channel\n", __func__, error); + } IWN_UNLOCK(sc); } @@ -6543,48 +7036,6 @@ iwn_scan_mindwell(struct ieee80211_scan_state *ss) /* NB: don't try to abort scan; wait for firmware to finish */ } -static struct iwn_eeprom_chan * -iwn_find_eeprom_channel(struct iwn_softc *sc, struct ieee80211_channel *c) -{ - int i, j; - - for (j = 0; j < 7; j++) { - for (i = 0; i < iwn_bands[j].nchan; i++) { - if (iwn_bands[j].chan[i] == c->ic_ieee) - return &sc->eeprom_channels[j][i]; - } - } - - return NULL; -} - -/* - * Enforce flags read from EEPROM. - */ -static int -iwn_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd, - int nchan, struct ieee80211_channel chans[]) -{ - struct iwn_softc *sc = ic->ic_ifp->if_softc; - int i; - - for (i = 0; i < nchan; i++) { - struct ieee80211_channel *c = &chans[i]; - struct iwn_eeprom_chan *channel; - - channel = iwn_find_eeprom_channel(sc, c); - if (channel == NULL) { - if_printf(ic->ic_ifp, - "%s: invalid channel %u freq %u/0x%x\n", - __func__, c->ic_ieee, c->ic_freq, c->ic_flags); - return EINVAL; - } - c->ic_flags |= iwn_eeprom_channel_flags(channel); - } - - return 0; -} - static void iwn_hw_reset(void *arg0, int pending) { @@ -6596,161 +7047,3 @@ iwn_hw_reset(void *arg0, int pending) iwn_init(sc); ieee80211_notify_radio(ic, 1); } - -static void -iwn_radio_on(void *arg0, int pending) -{ - struct iwn_softc *sc = arg0; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - if (vap != NULL) { - iwn_init(sc); - ieee80211_init(vap); - } -} - -static void -iwn_radio_off(void *arg0, int pending) -{ - struct iwn_softc *sc = arg0; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - iwn_stop(sc); - if (vap != NULL) - ieee80211_stop(vap); - - /* Enable interrupts to get RF toggle notification. */ - IWN_LOCK(sc); - IWN_WRITE(sc, IWN_INT, 0xffffffff); - IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); - IWN_UNLOCK(sc); -} - -static void -iwn_sysctlattach(struct iwn_softc *sc) -{ - struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); - struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); - -#ifdef IWN_DEBUG - sc->sc_debug = 0; - SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, - "debug", CTLFLAG_RW, &sc->sc_debug, 0, "control debugging printfs"); -#endif -} - -static int -iwn_shutdown(device_t dev) -{ - struct iwn_softc *sc = device_get_softc(dev); - - iwn_stop(sc); - return 0; -} - -static int -iwn_suspend(device_t dev) -{ - struct iwn_softc *sc = device_get_softc(dev); - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - iwn_stop(sc); - if (vap != NULL) - ieee80211_stop(vap); - return 0; -} - -static int -iwn_resume(device_t dev) -{ - struct iwn_softc *sc = device_get_softc(dev); - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - /* Clear device-specific "PCI retry timeout" register (41h). */ - pci_write_config(dev, 0x41, 0, 1); - - if (ifp->if_flags & IFF_UP) { - iwn_init(sc); - if (vap != NULL) - ieee80211_init(vap); - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - iwn_start(ifp); - } - return 0; -} - -#ifdef IWN_DEBUG -static const char * -iwn_intr_str(uint8_t cmd) -{ - switch (cmd) { - /* Notifications */ - case IWN_UC_READY: return "UC_READY"; - case IWN_ADD_NODE_DONE: return "ADD_NODE_DONE"; - case IWN_TX_DONE: return "TX_DONE"; - case IWN_START_SCAN: return "START_SCAN"; - case IWN_STOP_SCAN: return "STOP_SCAN"; - case IWN_RX_STATISTICS: return "RX_STATS"; - case IWN_BEACON_STATISTICS: return "BEACON_STATS"; - case IWN_STATE_CHANGED: return "STATE_CHANGED"; - case IWN_BEACON_MISSED: return "BEACON_MISSED"; - case IWN_RX_PHY: return "RX_PHY"; - case IWN_MPDU_RX_DONE: return "MPDU_RX_DONE"; - case IWN_RX_DONE: return "RX_DONE"; - - /* Command Notifications */ - case IWN_CMD_RXON: return "IWN_CMD_RXON"; - case IWN_CMD_RXON_ASSOC: return "IWN_CMD_RXON_ASSOC"; - case IWN_CMD_EDCA_PARAMS: return "IWN_CMD_EDCA_PARAMS"; - case IWN_CMD_TIMING: return "IWN_CMD_TIMING"; - case IWN_CMD_LINK_QUALITY: return "IWN_CMD_LINK_QUALITY"; - case IWN_CMD_SET_LED: return "IWN_CMD_SET_LED"; - case IWN5000_CMD_WIMAX_COEX: return "IWN5000_CMD_WIMAX_COEX"; - case IWN5000_CMD_CALIB_CONFIG: return "IWN5000_CMD_CALIB_CONFIG"; - case IWN5000_CMD_CALIB_RESULT: return "IWN5000_CMD_CALIB_RESULT"; - case IWN5000_CMD_CALIB_COMPLETE: return "IWN5000_CMD_CALIB_COMPLETE"; - case IWN_CMD_SET_POWER_MODE: return "IWN_CMD_SET_POWER_MODE"; - case IWN_CMD_SCAN: return "IWN_CMD_SCAN"; - case IWN_CMD_SCAN_RESULTS: return "IWN_CMD_SCAN_RESULTS"; - case IWN_CMD_TXPOWER: return "IWN_CMD_TXPOWER"; - case IWN_CMD_TXPOWER_DBM: return "IWN_CMD_TXPOWER_DBM"; - case IWN5000_CMD_TX_ANT_CONFIG: return "IWN5000_CMD_TX_ANT_CONFIG"; - case IWN_CMD_BT_COEX: return "IWN_CMD_BT_COEX"; - case IWN_CMD_SET_CRITICAL_TEMP: return "IWN_CMD_SET_CRITICAL_TEMP"; - case IWN_CMD_SET_SENSITIVITY: return "IWN_CMD_SET_SENSITIVITY"; - case IWN_CMD_PHY_CALIB: return "IWN_CMD_PHY_CALIB"; - } - return "UNKNOWN INTR NOTIF/CMD"; -} -#endif /* IWN_DEBUG */ - -static device_method_t iwn_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, iwn_probe), - DEVMETHOD(device_attach, iwn_attach), - DEVMETHOD(device_detach, iwn_detach), - DEVMETHOD(device_shutdown, iwn_shutdown), - DEVMETHOD(device_suspend, iwn_suspend), - DEVMETHOD(device_resume, iwn_resume), - { 0, 0 } -}; - -static driver_t iwn_driver = { - "iwn", - iwn_methods, - sizeof (struct iwn_softc) -}; -static devclass_t iwn_devclass; - -DRIVER_MODULE(iwn, pci, iwn_driver, iwn_devclass, 0, 0); -MODULE_DEPEND(iwn, pci, 1, 1, 1); -MODULE_DEPEND(iwn, firmware, 1, 1, 1); -MODULE_DEPEND(iwn, wlan, 1, 1, 1); diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/dev/iwn/if_iwnreg.h b/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/dev/iwn/if_iwnreg.h index c0230091ef..062125fb3a 100644 --- a/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/dev/iwn/if_iwnreg.h +++ b/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/dev/iwn/if_iwnreg.h @@ -27,6 +27,9 @@ #define IWN4965_NTXQUEUES 16 #define IWN5000_NTXQUEUES 20 +#define IWN4965_FIRSTAGGQUEUE 7 +#define IWN5000_FIRSTAGGQUEUE 10 + #define IWN4965_NDMACHNLS 7 #define IWN5000_NDMACHNLS 8 @@ -50,9 +53,6 @@ #define IWN_HIADDR(paddr) (0) #endif -/* Base Address Register. */ -#define IWN_PCI_BAR0 PCI_MAPREG_START - /* * Control and status registers. */ @@ -73,6 +73,7 @@ #define IWN_UCODE_GP1_CLR 0x05c #define IWN_LED 0x094 #define IWN_DRAM_INT_TBL 0x0a0 +#define IWN_SHADOW_REG_CTRL 0x0a8 #define IWN_GIO_CHICKEN 0x100 #define IWN_ANA_PLL 0x20c #define IWN_HW_REV_WA 0x22c @@ -82,10 +83,10 @@ #define IWN_MEM_WADDR 0x410 #define IWN_MEM_WDATA 0x418 #define IWN_MEM_RDATA 0x41c -#define IWN_PRPH_WADDR 0x444 -#define IWN_PRPH_RADDR 0x448 -#define IWN_PRPH_WDATA 0x44c -#define IWN_PRPH_RDATA 0x450 +#define IWN_PRPH_WADDR 0x444 +#define IWN_PRPH_RADDR 0x448 +#define IWN_PRPH_WDATA 0x44c +#define IWN_PRPH_RDATA 0x450 #define IWN_HBUS_TARG_WRPTR 0x460 /* @@ -218,6 +219,7 @@ #define IWN_GP_DRIVER_RADIO_2X2_HYB (1 << 0) #define IWN_GP_DRIVER_RADIO_2X2_IPA (2 << 0) #define IWN_GP_DRIVER_CALIB_VER6 (1 << 2) +#define IWN_GP_DRIVER_6050_1X2 (1 << 3) /* Possible flags for register IWN_UCODE_GP1_CLR. */ #define IWN_UCODE_GP1_RFKILL (1 << 1) @@ -286,8 +288,7 @@ #define IWN_FH_TX_CHICKEN_SCHED_RETRY (1 << 1) /* Possible flags for register IWN_FH_TX_STATUS. */ -#define IWN_FH_TX_STATUS_IDLE(chnl) \ - (1 << ((chnl) + 24) | 1 << ((chnl) + 16)) +#define IWN_FH_TX_STATUS_IDLE(chnl) (1 << ((chnl) + 16)) /* Possible flags for register IWN_FH_RX_CONFIG. */ #define IWN_FH_RX_CONFIG_ENA (1 << 31) @@ -436,6 +437,8 @@ struct iwn_tx_cmd { #define IWN_CMD_SET_CRITICAL_TEMP 164 #define IWN_CMD_SET_SENSITIVITY 168 #define IWN_CMD_PHY_CALIB 176 +#define IWN_CMD_BT_COEX_PRIOTABLE 204 +#define IWN_CMD_BT_COEX_PROT 205 uint8_t flags; uint8_t idx; @@ -489,6 +492,10 @@ struct iwn_rxon { #define IWN_RXON_ANTENNA_A (1 << 8) #define IWN_RXON_ANTENNA_B (1 << 9) #define IWN_RXON_TSF (1 << 15) +#define IWN_RXON_HT_HT40MINUS (1 << 22) +#define IWN_RXON_HT_PROTMODE(x) (x << 23) +#define IWN_RXON_HT_MODEPURE40 (1 << 25) +#define IWN_RXON_HT_MODEMIXED (2 << 25) #define IWN_RXON_CTS_TO_SELF (1 << 30) uint32_t filter; @@ -588,7 +595,10 @@ struct iwn_node_info { uint8_t txmic[8]; uint32_t htflags; +#define IWN_SMPS_MIMO_PROT (1 << 17) #define IWN_AMDPU_SIZE_FACTOR(x) ((x) << 19) +#define IWN_NODE_HT40 (1 << 21) +#define IWN_SMPS_MIMO_DIS (1 << 22) #define IWN_AMDPU_DENSITY(x) ((x) << 23) uint32_t mask; @@ -625,8 +635,13 @@ struct iwn4965_node_info { uint32_t reserved7; } __packed; -#define IWN_RFLAG_CCK (1 << 1) -#define IWN_RFLAG_ANT(x) ((x) << 6) +#define IWN_RFLAG_MCS (1 << 8) +#define IWN_RFLAG_CCK (1 << 9) +#define IWN_RFLAG_GREENFIELD (1 << 10) +#define IWN_RFLAG_HT40 (1 << 11) +#define IWN_RFLAG_DUPLICATE (1 << 12) +#define IWN_RFLAG_SGI (1 << 13) +#define IWN_RFLAG_ANT(x) ((x) << 14) /* Structure for command IWN_CMD_TX_DATA. */ struct iwn_cmd_data { @@ -647,9 +662,7 @@ struct iwn_cmd_data { #define IWN_TX_NEED_PADDING (1 << 20) uint32_t scratch; - uint8_t plcp; - uint8_t rflags; - uint16_t xrflags; + uint32_t rate; uint8_t id; uint8_t security; @@ -690,11 +703,7 @@ struct iwn_cmd_link_quality { uint8_t ampdu_threshold; uint8_t ampdu_max; uint32_t reserved2; - struct { - uint8_t plcp; - uint8_t rflags; - uint16_t xrflags; - } __packed retry[IWN_MAX_TX_RETRIES]; + uint32_t retry[IWN_MAX_TX_RETRIES]; uint32_t reserved3; } __packed; @@ -730,6 +739,8 @@ struct iwn5000_wimax_coex { struct iwn5000_calib_elem { uint32_t enable; uint32_t start; +#define IWN5000_CALIB_DC (1 << 1) + uint32_t send; uint32_t apply; uint32_t reserved; @@ -831,7 +842,7 @@ struct iwn5000_cmd_txpower { uint8_t reserved; } __packed; -/* Structure for command IWN_CMD_BLUETOOTH. */ +/* Structures for command IWN_CMD_BLUETOOTH. */ struct iwn_bluetooth { uint8_t flags; #define IWN_BT_COEX_CHAN_ANN (1 << 0) @@ -849,6 +860,43 @@ struct iwn_bluetooth { uint32_t kill_cts; } __packed; +struct iwn6000_btcoex_config { + uint8_t flags; + uint8_t lead_time; + uint8_t max_kill; + uint8_t bt3_t7_timer; + uint32_t kill_ack; + uint32_t kill_cts; + uint8_t sample_time; + uint8_t bt3_t2_timer; + uint16_t bt4_reaction; + uint32_t lookup_table[12]; + uint16_t bt4_decision; + uint16_t valid; + uint8_t prio_boost; + uint8_t tx_prio_boost; + uint16_t rx_prio_boost; +} __packed; + +struct iwn_btcoex_priotable { + uint8_t calib_init1; + uint8_t calib_init2; + uint8_t calib_periodic_low1; + uint8_t calib_periodic_low2; + uint8_t calib_periodic_high1; + uint8_t calib_periodic_high2; + uint8_t dtim; + uint8_t scan52; + uint8_t scan24; + uint8_t reserved[7]; +} __packed; + +struct iwn_btcoex_prot { + uint8_t open; + uint8_t type; + uint8_t reserved[2]; +} __packed; + /* Structure for command IWN_CMD_SET_CRITICAL_TEMP. */ struct iwn_critical_temp { uint32_t reserved; @@ -860,7 +908,7 @@ struct iwn_critical_temp { #define IWN_CTOMUK(c) (((c) * 1000000) + 273150000) } __packed; -/* Structure for command IWN_CMD_SET_SENSITIVITY. */ +/* Structures for command IWN_CMD_SET_SENSITIVITY. */ struct iwn_sensitivity_cmd { uint16_t which; #define IWN_SENSITIVITY_DEFAULTTBL 0 @@ -879,6 +927,34 @@ struct iwn_sensitivity_cmd { uint16_t energy_ofdm_th; } __packed; +struct iwn_enhanced_sensitivity_cmd { + uint16_t which; + uint16_t energy_cck; + uint16_t energy_ofdm; + uint16_t corr_ofdm_x1; + uint16_t corr_ofdm_mrc_x1; + uint16_t corr_cck_mrc_x4; + uint16_t corr_ofdm_x4; + uint16_t corr_ofdm_mrc_x4; + uint16_t corr_barker; + uint16_t corr_barker_mrc; + uint16_t corr_cck_x4; + uint16_t energy_ofdm_th; + /* "Enhanced" part. */ + uint16_t ina_det_ofdm; + uint16_t ina_det_cck; + uint16_t corr_11_9_en; + uint16_t ofdm_det_slope_mrc; + uint16_t ofdm_det_icept_mrc; + uint16_t ofdm_det_slope; + uint16_t ofdm_det_icept; + uint16_t cck_det_slope_mrc; + uint16_t cck_det_icept_mrc; + uint16_t cck_det_slope; + uint16_t cck_det_icept; + uint16_t reserved; +} __packed; + /* Structures for command IWN_CMD_PHY_CALIB. */ struct iwn_phy_calib { uint8_t code; @@ -889,6 +965,8 @@ struct iwn_phy_calib { #define IWN5000_PHY_CALIB_CRYSTAL 15 #define IWN5000_PHY_CALIB_BASE_BAND 16 #define IWN5000_PHY_CALIB_TX_IQ_PERIODIC 17 +#define IWN5000_PHY_CALIB_TEMP_OFFSET 18 + #define IWN5000_PHY_CALIB_RESET_NOISE_GAIN 18 #define IWN5000_PHY_CALIB_NOISE_GAIN 19 @@ -907,6 +985,17 @@ struct iwn5000_phy_calib_crystal { uint8_t reserved[2]; } __packed; +struct iwn5000_phy_calib_temp_offset { + uint8_t code; + uint8_t group; + uint8_t ngroups; + uint8_t isvalid; + int16_t offset; +#define IWN_DEFAULT_TEMP_OFFSET 2700 + + uint16_t reserved; +} __packed; + struct iwn_phy_calib_gain { uint8_t code; uint8_t group; @@ -987,9 +1076,7 @@ struct iwn4965_tx_stat { uint8_t btkillcnt; uint8_t rtsfailcnt; uint8_t ackfailcnt; - uint8_t rate; - uint8_t rflags; - uint16_t xrflags; + uint32_t rate; uint16_t duration; uint16_t reserved; uint32_t power[2]; @@ -1001,9 +1088,7 @@ struct iwn5000_tx_stat { uint8_t btkillcnt; uint8_t rtsfailcnt; uint8_t ackfailcnt; - uint8_t rate; - uint8_t rflags; - uint16_t xrflags; + uint32_t rate; uint16_t duration; uint16_t reserved; uint32_t power[2]; @@ -1058,9 +1143,7 @@ struct iwn_rx_stat { uint16_t chan; uint8_t phybuf[32]; - uint8_t rate; - uint8_t rflags; - uint16_t xrflags; + uint32_t rate; uint16_t len; uint16_t reserve3; } __packed; @@ -1283,6 +1366,8 @@ struct iwn_fw_tlv { #define IWN_FW_TLV_INIT_DATA 4 #define IWN_FW_TLV_BOOT_TEXT 5 #define IWN_FW_TLV_PBREQ_MAXLEN 6 +#define IWN_FW_TLV_ENH_SENS 14 +#define IWN_FW_TLV_PHY_CALIB 15 uint16_t alt; uint32_t len; @@ -1300,6 +1385,7 @@ struct iwn_fw_tlv { * Offsets into EEPROM. */ #define IWN_EEPROM_MAC 0x015 +#define IWN_EEPROM_SKU_CAP 0x045 #define IWN_EEPROM_RFCFG 0x048 #define IWN4965_EEPROM_DOMAIN 0x060 #define IWN4965_EEPROM_BAND1 0x063 @@ -1322,12 +1408,18 @@ struct iwn_fw_tlv { #define IWN5000_EEPROM_BAND4 0x02e #define IWN5000_EEPROM_BAND5 0x03a #define IWN5000_EEPROM_BAND6 0x041 +#define IWN6000_EEPROM_BAND6 0x040 #define IWN5000_EEPROM_BAND7 0x049 #define IWN6000_EEPROM_ENHINFO 0x054 #define IWN5000_EEPROM_CRYSTAL 0x128 #define IWN5000_EEPROM_TEMP 0x12a #define IWN5000_EEPROM_VOLT 0x12b +/* Possible flags for IWN_EEPROM_SKU_CAP. */ +#define IWN_EEPROM_SKU_CAP_11N (1 << 6) +#define IWN_EEPROM_SKU_CAP_AMT (1 << 7) +#define IWN_EEPROM_SKU_CAP_IPAN (1 << 8) + /* Possible flags for IWN_EEPROM_RFCFG. */ #define IWN_RFCFG_TYPE(x) (((x) >> 0) & 0x3) #define IWN_RFCFG_STEP(x) (((x) >> 2) & 0x3) @@ -1346,7 +1438,17 @@ struct iwn_eeprom_chan { } __packed; struct iwn_eeprom_enhinfo { - uint16_t chan; + uint8_t flags; +#define IWN_ENHINFO_VALID 0x01 +#define IWN_ENHINFO_5GHZ 0x02 +#define IWN_ENHINFO_OFDM 0x04 +#define IWN_ENHINFO_HT40 0x08 +#define IWN_ENHINFO_HTAP 0x10 +#define IWN_ENHINFO_RES1 0x20 +#define IWN_ENHINFO_RES2 0x40 +#define IWN_ENHINFO_COMMON 0x80 + + uint8_t chan; int8_t chain[3]; /* max power in half-dBm */ uint8_t reserved; int8_t mimo2; /* max power in half-dBm */ @@ -1400,6 +1502,16 @@ static const uint32_t iwn5000_regulatory_bands[IWN_NBANDS] = { IWN5000_EEPROM_BAND7 }; +static const uint32_t iwn6000_regulatory_bands[IWN_NBANDS] = { + IWN5000_EEPROM_BAND1, + IWN5000_EEPROM_BAND2, + IWN5000_EEPROM_BAND3, + IWN5000_EEPROM_BAND4, + IWN5000_EEPROM_BAND5, + IWN6000_EEPROM_BAND6, + IWN5000_EEPROM_BAND7 +}; + #define IWN_CHAN_BANDS_COUNT 7 #define IWN_MAX_CHAN_PER_BAND 14 static const struct iwn_chan_band { @@ -1419,35 +1531,13 @@ static const struct iwn_chan_band { { 11, { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 } } }; -#define IWN1000_OTP_NBLOCKS 3 -#define IWN6000_OTP_NBLOCKS 4 +#define IWN1000_OTP_NBLOCKS 3 +#define IWN6000_OTP_NBLOCKS 4 #define IWN6050_OTP_NBLOCKS 7 /* HW rate indices. */ -#define IWN_RIDX_CCK1 0 -#define IWN_RIDX_CCK11 3 -#define IWN_RIDX_OFDM6 4 -#define IWN_RIDX_OFDM54 11 - -static const struct iwn_rate { - uint8_t rate; - uint8_t plcp; - uint8_t flags; -} iwn_rates[IWN_RIDX_MAX + 1] = { - { 2, 10, IWN_RFLAG_CCK }, - { 4, 20, IWN_RFLAG_CCK }, - { 11, 55, IWN_RFLAG_CCK }, - { 22, 110, IWN_RFLAG_CCK }, - { 12, 0xd, 0 }, - { 18, 0xf, 0 }, - { 24, 0x5, 0 }, - { 36, 0x7, 0 }, - { 48, 0x9, 0 }, - { 72, 0xb, 0 }, - { 96, 0x1, 0 }, - { 108, 0x3, 0 }, - { 120, 0x3, 0 } -}; +#define IWN_RIDX_CCK1 0 +#define IWN_RIDX_OFDM6 4 #define IWN4965_MAX_PWR_INDEX 107 @@ -1611,13 +1701,13 @@ static const struct iwn_sensitivity_limits iwn5150_sensitivity_limits = { static const struct iwn_sensitivity_limits iwn1000_sensitivity_limits = { 120, 155, 240, 290, - 90, 120, + 90, 120, 170, 210, 125, 200, 170, 400, - 95, - 95, - 95 + 95, + 95, + 95 }; static const struct iwn_sensitivity_limits iwn6000_sensitivity_limits = { diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/dev/iwn/if_iwnvar.h b/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/dev/iwn/if_iwnvar.h index ff1e068b90..5288781307 100644 --- a/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/dev/iwn/if_iwnvar.h +++ b/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/dev/iwn/if_iwnvar.h @@ -78,6 +78,7 @@ struct iwn_tx_ring { int qid; int queued; int cur; + int read; }; struct iwn_softc; @@ -101,7 +102,12 @@ struct iwn_node { struct ieee80211_node ni; /* must be the first */ uint16_t disable_tid; uint8_t id; - uint8_t ridx[IEEE80211_RATE_MAXSIZE]; + uint32_t ridx[256]; + struct { + uint64_t bitmap; + int startidx; + int nframes; + } agg[IEEE80211_TID_SIZE]; }; struct iwn_calib_state { @@ -157,7 +163,7 @@ struct iwn_fw_info { struct iwn_fw_part boot; }; -struct iwn_hal { +struct iwn_ops { int (*load_firmware)(struct iwn_softc *); void (*read_eeprom)(struct iwn_softc *); int (*post_alive)(struct iwn_softc *); @@ -174,21 +180,10 @@ struct iwn_hal { int); void (*tx_done)(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); -#if 0 /* HT */ void (*ampdu_tx_start)(struct iwn_softc *, - struct ieee80211_node *, uint8_t, uint16_t); - void (*ampdu_tx_stop)(struct iwn_softc *, uint8_t, + struct ieee80211_node *, int, uint8_t, uint16_t); + void (*ampdu_tx_stop)(struct iwn_softc *, int, uint8_t, uint16_t); -#endif - int ntxqs; - int ndmachnls; - uint8_t broadcast_id; - int rxonsz; - int schedsz; - uint32_t fw_text_maxsz; - uint32_t fw_data_maxsz; - uint32_t fwsz; - bus_size_t sched_txfact_addr; }; struct iwn_vap { @@ -201,31 +196,40 @@ struct iwn_vap { #define IWN_VAP(_vap) ((struct iwn_vap *)(_vap)) struct iwn_softc { + device_t sc_dev; + struct ifnet *sc_ifp; int sc_debug; - /* Locks */ struct mtx sc_mtx; - /* Bus */ - device_t sc_dev; - int mem_rid; - int irq_rid; - struct resource *mem; - struct resource *irq; - u_int sc_flags; -#define IWN_FLAG_HAS_5GHZ (1 << 0) #define IWN_FLAG_HAS_OTPROM (1 << 1) #define IWN_FLAG_CALIB_DONE (1 << 2) #define IWN_FLAG_USE_ICT (1 << 3) #define IWN_FLAG_INTERNAL_PA (1 << 4) +#define IWN_FLAG_HAS_11N (1 << 6) +#define IWN_FLAG_ENH_SENS (1 << 7) +#define IWN_FLAG_ADV_BTCOEX (1 << 8) uint8_t hw_type; - const struct iwn_hal *sc_hal; + + struct iwn_ops ops; const char *fwname; const struct iwn_sensitivity_limits *limits; + int ntxqs; + int firstaggqueue; + int ndmachnls; + uint8_t broadcast_id; + int rxonsz; + int schedsz; + uint32_t fw_text_maxsz; + uint32_t fw_data_maxsz; + uint32_t fwsz; + bus_size_t sched_txfact_addr; + uint32_t reset_noise_gain; + uint32_t noise_gain; /* TX scheduler rings. */ struct iwn_dma_info sched_dma; @@ -250,37 +254,28 @@ struct iwn_softc { struct iwn_tx_ring txq[IWN5000_NTXQUEUES]; struct iwn_rx_ring rxq; + int mem_rid; + struct resource *mem; bus_space_tag_t sc_st; bus_space_handle_t sc_sh; + int irq_rid; + struct resource *irq; void *sc_ih; bus_size_t sc_sz; int sc_cap_off; /* PCIe Capabilities. */ /* Tasks used by the driver */ - struct task sc_reinit_task; + struct task sc_reinit_task; struct task sc_radioon_task; struct task sc_radiooff_task; + struct callout calib_to; int calib_cnt; struct iwn_calib_state calib; - u_int calib_init; - u_int calib_runtime; -#define IWN_CALIB_XTAL (1 << IWN_CALIB_IDX_XTAL) -#define IWN_CALIB_DC (1 << IWN_CALIB_IDX_DC) -#define IWN_CALIB_LO (1 << IWN_CALIB_IDX_LO) -#define IWN_CALIB_TX_IQ (1 << IWN_CALIB_IDX_TX_IQ) -#define IWN_CALIB_TX_IQ_PERIODIC (1 << IWN_CALIB_IDX_TX_IQ_PERIODIC) -#define IWN_CALIB_BASE_BAND (1 << IWN_CALIB_IDX_BASE_BAND) -#define IWN_CALIB_NUM 6 - struct iwn_calib_info calib_results[IWN_CALIB_NUM]; -#define IWN_CALIB_IDX_XTAL 0 -#define IWN_CALIB_IDX_DC 1 -#define IWN_CALIB_IDX_LO 2 -#define IWN_CALIB_IDX_TX_IQ 3 -#define IWN_CALIB_IDX_TX_IQ_PERIODIC 4 -#define IWN_CALIB_IDX_BASE_BAND 5 + struct callout watchdog_to; struct iwn_fw_info fw; + struct iwn_calib_info calibcmd[5]; uint32_t errptr; struct iwn_rx_stat last_rx_stat; @@ -299,11 +294,12 @@ struct iwn_softc { uint16_t rfcfg; uint8_t calib_ver; char eeprom_domain[4]; + uint32_t eeprom_crystal; + int16_t eeprom_temp; int16_t eeprom_voltage; int8_t maxpwr2GHz; int8_t maxpwr5GHz; int8_t maxpwr[IEEE80211_CHAN_MAX]; - int8_t enh_maxpwr[35]; int32_t temp_off; uint32_t int_mask; @@ -313,9 +309,22 @@ struct iwn_softc { uint8_t rxchainmask; uint8_t chainmask; - struct callout sc_timer_to; int sc_tx_timer; + struct ieee80211_tx_ampdu *qid2tap[IWN5000_NTXQUEUES]; + + int (*sc_ampdu_rx_start)(struct ieee80211_node *, + struct ieee80211_rx_ampdu *, int, int, int); + void (*sc_ampdu_rx_stop)(struct ieee80211_node *, + struct ieee80211_rx_ampdu *); + int (*sc_addba_request)(struct ieee80211_node *, + struct ieee80211_tx_ampdu *, int, int, int); + int (*sc_addba_response)(struct ieee80211_node *, + struct ieee80211_tx_ampdu *, int, int, int); + void (*sc_addba_stop)(struct ieee80211_node *, + struct ieee80211_tx_ampdu *); + + struct iwn_rx_radiotap_header sc_rxtap; struct iwn_tx_radiotap_header sc_txtap; diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/glue.c b/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/glue.c index 3e87128923..26292bb5d1 100644 --- a/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/glue.c +++ b/src/add-ons/kernel/drivers/network/wlan/iprowifi4965/glue.c @@ -24,13 +24,14 @@ NO_HAIKU_FBSD_MII_DRIVER(); NO_HAIKU_REENABLE_INTERRUPTS(); HAIKU_DRIVER_REQUIREMENTS(FBSD_TASKQUEUES | FBSD_WLAN); HAIKU_FIRMWARE_VERSION(44417); -HAIKU_FIRMWARE_NAME_MAP(7) = { +HAIKU_FIRMWARE_NAME_MAP(8) = { {"iwn1000fw", "iwlwifi-1000-5.ucode"}, {"iwn4965fw", "iwlwifi-4965-2.ucode"}, {"iwn5000fw", "iwlwifi-5000-5.ucode"}, {"iwn5150fw", "iwlwifi-5150-2.ucode"}, {"iwn6000fw", "iwlwifi-6000-4.ucode"}, - {"iwn6005fw", "iwlwifi-6000g2a-5.ucode"}, + {"iwn6000g2afw", "iwlwifi-6000g2a-5.ucode"}, + {"iwn6000g2bfw", "iwlwifi-6000g2b-6.ucode"}, {"iwn6050fw", "iwlwifi-6050-5.ucode"} }; diff --git a/src/add-ons/kernel/drivers/network/wlan/marvell88w8335/dev/malo/if_malo.c b/src/add-ons/kernel/drivers/network/wlan/marvell88w8335/dev/malo/if_malo.c index d547cfce12..ae6ef88265 100644 --- a/src/add-ons/kernel/drivers/network/wlan/marvell88w8335/dev/malo/if_malo.c +++ b/src/add-ons/kernel/drivers/network/wlan/marvell88w8335/dev/malo/if_malo.c @@ -135,7 +135,7 @@ static int malo_setup_hwdma(struct malo_softc *); static void malo_txq_init(struct malo_softc *, struct malo_txq *, int); static void malo_tx_cleanupq(struct malo_softc *, struct malo_txq *); static void malo_start(struct ifnet *); -static void malo_watchdog(struct ifnet *); +static void malo_watchdog(void *); static int malo_ioctl(struct ifnet *, u_long, caddr_t); static void malo_updateslot(struct ifnet *); static int malo_newstate(struct ieee80211vap *, enum ieee80211_state, int); @@ -168,8 +168,8 @@ 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%zx val 0x%x\n", - __func__, off, val); + DPRINTF(sc, MALO_DEBUG_FW, "%s: off 0x%jx val 0x%x\n", + __func__, (intmax_t)off, val); bus_space_write_4(sc->malo_io0t, sc->malo_io0h, off, val); } @@ -191,6 +191,7 @@ malo_attach(uint16_t devid, struct malo_softc *sc) ic = ifp->if_l2com; MALO_LOCK_INIT(sc); + callout_init_mtx(&sc->malo_watchdog_timer, &sc->malo_mtx, 0); /* set these up early for if_printf use */ if_initname(ifp, device_get_name(sc->malo_dev), @@ -272,7 +273,6 @@ malo_attach(uint16_t devid, struct malo_softc *sc) ifp->if_softc = sc; ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; ifp->if_start = malo_start; - ifp->if_watchdog = malo_watchdog; ifp->if_ioctl = malo_ioctl; ifp->if_init = malo_init; IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); @@ -1076,7 +1076,7 @@ malo_tx_proc(void *arg, int npending) if (nreaped != 0) { ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - ifp->if_timer = 0; + sc->malo_timer = 0; malo_start(ifp); } } @@ -1260,7 +1260,7 @@ malo_tx_start(struct malo_softc *sc, struct ieee80211_node *ni, MALO_TXDESC_SYNC(txq, ds, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); ifp->if_opackets++; - ifp->if_timer = 5; + sc->malo_timer = 5; MALO_TXQ_UNLOCK(txq); return 0; #undef IEEE80211_DIR_DSTODS @@ -1339,10 +1339,17 @@ malo_start(struct ifnet *ifp) } static void -malo_watchdog(struct ifnet *ifp) +malo_watchdog(void *arg) { - struct malo_softc *sc = ifp->if_softc; + struct malo_softc *sc; + struct ifnet *ifp; + sc = arg; + callout_reset(&sc->malo_watchdog_timer, hz, malo_watchdog, sc); + if (sc->malo_timer == 0 || --sc->malo_timer > 0) + return; + + ifp = sc->malo_ifp; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && !sc->malo_invalid) { if_printf(ifp, "watchdog timeout\n"); @@ -1536,6 +1543,7 @@ malo_init_locked(struct malo_softc *sc) ifp->if_drv_flags |= IFF_DRV_RUNNING; malo_hal_intrset(mh, sc->malo_imask); + callout_reset(&sc->malo_watchdog_timer, hz, malo_watchdog, sc); } static void @@ -1699,7 +1707,8 @@ malo_stop_locked(struct ifnet *ifp, int disable) * is gone (invalid). */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - ifp->if_timer = 0; + callout_stop(&sc->malo_watchdog_timer); + sc->malo_timer = 0; /* diable interrupt. */ malo_hal_intrset(mh, 0); /* turn off the radio. */ @@ -2241,6 +2250,7 @@ malo_detach(struct malo_softc *sc) * Other than that, it's straightforward... */ ieee80211_ifdetach(ic); + callout_drain(&sc->malo_watchdog_timer); malo_dma_cleanup(sc); malo_tx_cleanup(sc); malo_hal_detach(sc->malo_mh); diff --git a/src/add-ons/kernel/drivers/network/wlan/marvell88w8335/dev/malo/if_malo.h b/src/add-ons/kernel/drivers/network/wlan/marvell88w8335/dev/malo/if_malo.h index 7e3a7e89e4..bac290c633 100644 --- a/src/add-ons/kernel/drivers/network/wlan/marvell88w8335/dev/malo/if_malo.h +++ b/src/add-ons/kernel/drivers/network/wlan/marvell88w8335/dev/malo/if_malo.h @@ -550,6 +550,8 @@ struct malo_softc { struct malo_txq malo_txq[MALO_NUM_TX_QUEUES]; struct task malo_txtask; /* tx int processing */ + struct callout malo_watchdog_timer; + int malo_timer; struct malo_tx_radiotap_header malo_tx_th; struct malo_rx_radiotap_header malo_rx_th; diff --git a/src/add-ons/kernel/drivers/network/wlan/marvell88w8335/dev/malo/if_malo_pci.c b/src/add-ons/kernel/drivers/network/wlan/marvell88w8335/dev/malo/if_malo_pci.c index 65aac7ca63..9c2027b924 100644 --- a/src/add-ons/kernel/drivers/network/wlan/marvell88w8335/dev/malo/if_malo_pci.c +++ b/src/add-ons/kernel/drivers/network/wlan/marvell88w8335/dev/malo/if_malo_pci.c @@ -149,21 +149,6 @@ malo_pci_probe(device_t dev) #undef N } -static int -malo_pci_setup(device_t dev) -{ - - /* - * Enable memory mapping and bus mastering. - */ - if (pci_enable_busmaster(dev) != 0) - return -1; - if (pci_enable_io(dev, SYS_RES_MEMORY) != 0) - return -1; - - return 0; -} - static int malo_pci_attach(device_t dev) { @@ -173,11 +158,7 @@ malo_pci_attach(device_t dev) sc->malo_dev = dev; - /* - * Enable memory mapping and bus mastering. - */ - if (malo_pci_setup(dev)) - return (ENXIO); + pci_enable_busmaster(dev); /* * Setup memory-mapping of PCI registers. @@ -194,7 +175,7 @@ malo_pci_attach(device_t dev) */ sc->malo_invalid = 1; - if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { + if (pci_find_cap(dev, PCIY_EXPRESS, ®) == 0) { msic = pci_msi_count(dev); if (bootverbose) device_printf(dev, "MSI count : %d\n", msic); @@ -342,9 +323,6 @@ malo_pci_resume(device_t dev) { struct malo_pci_softc *psc = device_get_softc(dev); - if (!malo_pci_setup(dev)) - return ENXIO; - malo_resume(&psc->malo_sc); return (0); diff --git a/src/add-ons/kernel/drivers/network/wlan/marvell88w8335/dev/malo/if_malohal.h b/src/add-ons/kernel/drivers/network/wlan/marvell88w8335/dev/malo/if_malohal.h index 80c27ace43..fb962cc8dd 100644 --- a/src/add-ons/kernel/drivers/network/wlan/marvell88w8335/dev/malo/if_malohal.h +++ b/src/add-ons/kernel/drivers/network/wlan/marvell88w8335/dev/malo/if_malohal.h @@ -133,7 +133,7 @@ struct malo_hal_hwstats { /* * Set Antenna Configuration (legacy operation). * - * The RX antenna can be selected using the the bitmask + * The RX antenna can be selected using the bitmask * ant (bit 0 = antenna 1, bit 1 = antenna 2, etc.) * (diversity?XXX) */ diff --git a/src/add-ons/kernel/drivers/network/wlan/marvell88w8363/dev/mwl/if_mwl.c b/src/add-ons/kernel/drivers/network/wlan/marvell88w8363/dev/mwl/if_mwl.c index 0c030746c4..0d47661c4e 100644 --- a/src/add-ons/kernel/drivers/network/wlan/marvell88w8363/dev/mwl/if_mwl.c +++ b/src/add-ons/kernel/drivers/network/wlan/marvell88w8363/dev/mwl/if_mwl.c @@ -98,7 +98,7 @@ static void mwl_start(struct ifnet *); static int mwl_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static int mwl_media_change(struct ifnet *); -static void mwl_watchdog(struct ifnet *); +static void mwl_watchdog(void *); static int mwl_ioctl(struct ifnet *, u_long, caddr_t); static void mwl_radar_proc(void *, int); static void mwl_chanswitch_proc(void *, int); @@ -304,7 +304,7 @@ mwl_attach(uint16_t devid, struct mwl_softc *sc) ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { - device_printf(sc->sc_dev, "can not if_alloc()\n"); + device_printf(sc->sc_dev, "cannot if_alloc()\n"); return ENOSPC; } ic = ifp->if_l2com; @@ -360,6 +360,7 @@ mwl_attach(uint16_t devid, struct mwl_softc *sc) goto bad1; callout_init(&sc->sc_timer, CALLOUT_MPSAFE); + callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); sc->sc_tq = taskqueue_create("mwl_taskq", M_NOWAIT, taskqueue_thread_enqueue, &sc->sc_tq); @@ -401,7 +402,6 @@ mwl_attach(uint16_t devid, struct mwl_softc *sc) ifp->if_softc = sc; ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; ifp->if_start = mwl_start; - ifp->if_watchdog = mwl_watchdog; ifp->if_ioctl = mwl_ioctl; ifp->if_init = mwl_init; IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); @@ -558,6 +558,7 @@ mwl_detach(struct mwl_softc *sc) * Other than that, it's straightforward... */ ieee80211_ifdetach(ic); + callout_drain(&sc->sc_watchdog); mwl_dma_cleanup(sc); mwl_tx_cleanup(sc); mwl_hal_detach(sc->sc_mh); @@ -1218,6 +1219,7 @@ mwl_init_locked(struct mwl_softc *sc) ifp->if_drv_flags |= IFF_DRV_RUNNING; mwl_hal_intrset(mh, sc->sc_imask); + callout_reset(&sc->sc_watchdog, hz, mwl_watchdog, sc); return 0; } @@ -1255,7 +1257,8 @@ mwl_stop_locked(struct ifnet *ifp, int disable) * Shutdown the hardware and driver. */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - ifp->if_timer = 0; + callout_stop(&sc->sc_watchdog); + sc->sc_tx_timer = 0; mwl_draintxq(sc); } } @@ -1742,6 +1745,10 @@ mwl_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k, * WEP keys when the sta reaches AUTH state. */ macaddr = vap->iv_bss->ni_bssid; + if ((k->wk_flags & IEEE80211_KEY_GROUP) == 0) { + /* XXX plumb to local sta db too for static key wep */ + mwl_hal_keyset(hvap, &hk, vap->iv_myaddr); + } } else if (vap->iv_opmode == IEEE80211_M_WDS && vap->iv_state != IEEE80211_S_RUN) { /* @@ -2303,8 +2310,10 @@ mwl_dma_setup(struct mwl_softc *sc) int error, i; error = mwl_rxdma_setup(sc); - if (error != 0) + if (error != 0) { + mwl_rxdma_cleanup(sc); return error; + } for (i = 0; i < MWL_NUM_TX_QUEUES; i++) { error = mwl_txdma_setup(sc, &sc->sc_txq[i]); @@ -3409,7 +3418,7 @@ mwl_tx_start(struct mwl_softc *sc, struct ieee80211_node *ni, struct mwl_txbuf * MWL_TXDESC_SYNC(txq, ds, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); ifp->if_opackets++; - ifp->if_timer = 5; + sc->sc_tx_timer = 5; MWL_TXQ_UNLOCK(txq); return 0; @@ -3556,7 +3565,7 @@ mwl_tx_proc(void *arg, int npending) if (nreaped != 0) { ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - ifp->if_timer = 0; + sc->sc_tx_timer = 0; if (!IFQ_IS_EMPTY(&ifp->if_snd)) { /* NB: kick fw; the tx thread may have been preempted */ mwl_hal_txstart(sc->sc_mh, 0); @@ -3622,7 +3631,7 @@ mwl_draintxq(struct mwl_softc *sc) for (i = 0; i < MWL_NUM_TX_QUEUES; i++) mwl_tx_draintxq(sc, &sc->sc_txq[i]); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - ifp->if_timer = 0; + sc->sc_tx_timer = 0; } #ifdef MWL_DIAGAPI @@ -4768,10 +4777,17 @@ mwl_txq_dump(struct mwl_txq *txq) #endif static void -mwl_watchdog(struct ifnet *ifp) +mwl_watchdog(void *arg) { - struct mwl_softc *sc = ifp->if_softc; + struct mwl_softc *sc; + struct ifnet *ifp; + sc = arg; + callout_reset(&sc->sc_watchdog, hz, mwl_watchdog, sc); + if (sc->sc_tx_timer == 0 || --sc->sc_tx_timer > 0) + return; + + ifp = sc->sc_ifp; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && !sc->sc_invalid) { if (mwl_hal_setkeepalive(sc->sc_mh)) if_printf(ifp, "transmit timeout (firmware hung?)\n"); diff --git a/src/add-ons/kernel/drivers/network/wlan/marvell88w8363/dev/mwl/if_mwlvar.h b/src/add-ons/kernel/drivers/network/wlan/marvell88w8363/dev/mwl/if_mwlvar.h index ada1e8a402..0c43434739 100644 --- a/src/add-ons/kernel/drivers/network/wlan/marvell88w8363/dev/mwl/if_mwlvar.h +++ b/src/add-ons/kernel/drivers/network/wlan/marvell88w8363/dev/mwl/if_mwlvar.h @@ -255,6 +255,8 @@ struct mwl_softc { bus_space_tag_t sc_io1t; struct mtx sc_mtx; /* master lock (recursive) */ struct taskqueue *sc_tq; /* private task queue */ + struct callout sc_watchdog; + int sc_tx_timer; unsigned int sc_invalid : 1, /* disable hardware accesses */ sc_recvsetup:1, /* recv setup */ sc_csapending:1,/* 11h channel switch pending */ diff --git a/src/add-ons/kernel/drivers/network/wlan/marvell88w8363/dev/mwl/mwlhal.c b/src/add-ons/kernel/drivers/network/wlan/marvell88w8363/dev/mwl/mwlhal.c index 78b05ee717..18992c2f68 100644 --- a/src/add-ons/kernel/drivers/network/wlan/marvell88w8363/dev/mwl/mwlhal.c +++ b/src/add-ons/kernel/drivers/network/wlan/marvell88w8363/dev/mwl/mwlhal.c @@ -279,7 +279,7 @@ mwl_hal_attach(device_t dev, uint16_t devid, hvap->vap_type = MWL_HAL_STA; hvap->bss_type = htole16(WL_MAC_TYPE_PRIMARY_CLIENT); hvap->macid = i; - for (i++; i < MWL_MBSS_STA_MAX; i++) { + for (i++; i < MWL_MBSS_MAX; i++) { hvap = &mh->mh_vaps[i]; hvap->vap_type = MWL_HAL_STA; hvap->bss_type = htole16(WL_MAC_TYPE_SECONDARY_CLIENT); diff --git a/src/add-ons/kernel/drivers/network/wlan/marvell88w8363/dev/mwl/mwlhal.h b/src/add-ons/kernel/drivers/network/wlan/marvell88w8363/dev/mwl/mwlhal.h index ab6b7d0176..25b8b07c48 100644 --- a/src/add-ons/kernel/drivers/network/wlan/marvell88w8363/dev/mwl/mwlhal.h +++ b/src/add-ons/kernel/drivers/network/wlan/marvell88w8363/dev/mwl/mwlhal.h @@ -291,7 +291,7 @@ int mwl_hal_setradio(struct mwl_hal *mh, int onoff, MWL_HAL_PREAMBLE preamble); /* * Set Antenna Configuration (legacy operation). * - * The RX antenna can be selected using the the bitmask + * The RX antenna can be selected using the bitmask * ant (bit 0 = antenna 1, bit 1 = antenna 2, etc.) * (diversity?XXX) */ diff --git a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/ral/rt2560.c b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/ral/rt2560.c index 7f8502d427..33667becf5 100644 --- a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/ral/rt2560.c +++ b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/ral/rt2560.c @@ -144,7 +144,8 @@ static void rt2560_enable_tsf_sync(struct rt2560_softc *); static void rt2560_enable_tsf(struct rt2560_softc *); static void rt2560_update_plcp(struct rt2560_softc *); static void rt2560_update_slot(struct ifnet *); -static void rt2560_set_basicrates(struct rt2560_softc *); +static void rt2560_set_basicrates(struct rt2560_softc *, + const struct ieee80211_rateset *); 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 *); @@ -779,7 +780,7 @@ rt2560_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) if (vap->iv_opmode != IEEE80211_M_MONITOR) { rt2560_update_plcp(sc); - rt2560_set_basicrates(sc); + rt2560_set_basicrates(sc, &ni->ni_rates); rt2560_set_bssid(sc, ni->ni_bssid); } @@ -2355,22 +2356,29 @@ rt2560_update_slot(struct ifnet *ifp) } static void -rt2560_set_basicrates(struct rt2560_softc *sc) +rt2560_set_basicrates(struct rt2560_softc *sc, + const struct ieee80211_rateset *rs) { +#define RV(r) ((r) & IEEE80211_RATE_VAL) struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; + uint32_t mask = 0; + uint8_t rate; + int i; - /* update basic rate set */ - if (ic->ic_curmode == IEEE80211_MODE_11B) { - /* 11b basic rates: 1, 2Mbps */ - RAL_WRITE(sc, RT2560_ARSP_PLCP_1, 0x3); - } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) { - /* 11a basic rates: 6, 12, 24Mbps */ - RAL_WRITE(sc, RT2560_ARSP_PLCP_1, 0x150); - } else { - /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ - RAL_WRITE(sc, RT2560_ARSP_PLCP_1, 0x15f); + for (i = 0; i < rs->rs_nrates; i++) { + rate = rs->rs_rates[i]; + + if (!(rate & IEEE80211_RATE_BASIC)) + continue; + + mask |= 1 << ic->ic_rt->rateCodeToIndex[RV(rate)]; } + + RAL_WRITE(sc, RT2560_ARSP_PLCP_1, mask); + + DPRINTF(sc, "Setting basic rate mask to 0x%x\n", mask); +#undef RV } static void diff --git a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/ral/rt2661.c b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/ral/rt2661.c index d785c1c80b..0adc8990d4 100644 --- a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/ral/rt2661.c +++ b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/ral/rt2661.c @@ -1917,7 +1917,7 @@ rt2661_set_basicrates(struct rt2661_softc *sc, struct ieee80211com *ic = ifp->if_l2com; uint32_t mask = 0; uint8_t rate; - int i, j; + int i; for (i = 0; i < rs->rs_nrates; i++) { rate = rs->rs_rates[i]; @@ -1925,13 +1925,7 @@ rt2661_set_basicrates(struct rt2661_softc *sc, if (!(rate & IEEE80211_RATE_BASIC)) continue; - /* - * Find h/w rate index. We know it exists because the rate - * set has already been negotiated. - */ - for (j = 0; ic->ic_sup_rates[IEEE80211_MODE_11G].rs_rates[j] != RV(rate); j++); - - mask |= 1 << j; + mask |= 1 << ic->ic_rt->rateCodeToIndex[RV(rate)]; } RAL_WRITE(sc, RT2661_TXRX_CSR5, mask); diff --git a/src/add-ons/kernel/drivers/network/wlan/wavelanwifi/dev/wi/if_wi.c b/src/add-ons/kernel/drivers/network/wlan/wavelanwifi/dev/wi/if_wi.c index f29c30d381..e46b74866b 100644 --- a/src/add-ons/kernel/drivers/network/wlan/wavelanwifi/dev/wi/if_wi.c +++ b/src/add-ons/kernel/drivers/network/wlan/wavelanwifi/dev/wi/if_wi.c @@ -295,7 +295,7 @@ wi_attach(device_t dev) SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "pri_version", CTLFLAG_RD, &sc->sc_pri_firmware_ver, 0, "Primary Firmware version"); - SYSCTL_ADD_XINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "nic_id", + SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "nic_id", CTLFLAG_RD, &sc->sc_nic_id, 0, "NIC id"); SYSCTL_ADD_STRING(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "nic_name", CTLFLAG_RD, sc->sc_nic_name, 0, "NIC name"); diff --git a/src/libs/compat/freebsd_wlan/net80211/_ieee80211.h b/src/libs/compat/freebsd_wlan/net80211/_ieee80211.h index 0e78e10959..7cbc684fef 100644 --- a/src/libs/compat/freebsd_wlan/net80211/_ieee80211.h +++ b/src/libs/compat/freebsd_wlan/net80211/_ieee80211.h @@ -133,7 +133,7 @@ enum ieee80211_roamingmode { */ struct ieee80211_channel { uint32_t ic_flags; /* see below */ - uint16_t ic_freq; /* setting in Mhz */ + uint16_t ic_freq; /* setting in MHz */ uint8_t ic_ieee; /* IEEE channel number */ int8_t ic_maxregpower; /* maximum regulatory tx power in dBm */ int8_t ic_maxpower; /* maximum tx power in .5 dBm */ @@ -335,7 +335,7 @@ struct ieee80211_rateset { * the structure such that it can be used interchangeably * with an ieee80211_rateset (modulo structure size). */ -#define IEEE80211_HTRATE_MAXSIZE 127 +#define IEEE80211_HTRATE_MAXSIZE 77 struct ieee80211_htrateset { uint8_t rs_nrates; @@ -387,9 +387,16 @@ struct ieee80211_regdomain { /* * MIMO antenna/radio state. */ + +#define IEEE80211_MAX_CHAINS 3 +#define IEEE80211_MAX_EVM_PILOTS 6 + +/* + * XXX This doesn't yet export both ctl/ext chain details + */ struct ieee80211_mimo_info { - int8_t rssi[3]; /* per-antenna rssi */ - int8_t noise[3]; /* per-antenna noise floor */ + int8_t rssi[IEEE80211_MAX_CHAINS]; /* per-antenna rssi */ + int8_t noise[IEEE80211_MAX_CHAINS]; /* per-antenna noise floor */ uint8_t pad[2]; uint32_t evm[3]; /* EVM data */ }; diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211.c index fff964f61f..a03904176c 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211.c @@ -203,6 +203,15 @@ ieee80211_chan_init(struct ieee80211com *ic) DEFAULTRATES(IEEE80211_MODE_11NA, ieee80211_rateset_11a); DEFAULTRATES(IEEE80211_MODE_11NG, ieee80211_rateset_11g); + /* + * Setup required information to fill the mcsset field, if driver did + * not. Assume a 2T2R setup for historic reasons. + */ + if (ic->ic_rxstream == 0) + ic->ic_rxstream = 2; + if (ic->ic_txstream == 0) + ic->ic_txstream = 2; + /* * Set auto mode to reset active channel state and any desired channel. */ @@ -413,6 +422,7 @@ ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, 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_htextcaps = ic->ic_htextcaps; vap->iv_opmode = opmode; vap->iv_caps |= ieee80211_opcap[opmode]; switch (opmode) { @@ -1073,10 +1083,18 @@ ieee80211_media_setup(struct ieee80211com *ic, isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { addmedia(media, caps, addsta, IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); - /* XXX could walk htrates */ - /* XXX known array size */ - if (ieee80211_htrates[15].ht40_rate_400ns > maxrate) - maxrate = ieee80211_htrates[15].ht40_rate_400ns; + i = ic->ic_txstream * 8 - 1; + if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) && + (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)) + rate = ieee80211_htrates[i].ht40_rate_400ns; + else if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40)) + rate = ieee80211_htrates[i].ht40_rate_800ns; + else if ((ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20)) + rate = ieee80211_htrates[i].ht20_rate_400ns; + else + rate = ieee80211_htrates[i].ht20_rate_800ns; + if (rate > maxrate) + maxrate = rate; } return maxrate; } @@ -1519,6 +1537,67 @@ ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode m { 13, IFM_IEEE80211_MCS }, { 14, IFM_IEEE80211_MCS }, { 15, IFM_IEEE80211_MCS }, + { 16, IFM_IEEE80211_MCS }, + { 17, IFM_IEEE80211_MCS }, + { 18, IFM_IEEE80211_MCS }, + { 19, IFM_IEEE80211_MCS }, + { 20, IFM_IEEE80211_MCS }, + { 21, IFM_IEEE80211_MCS }, + { 22, IFM_IEEE80211_MCS }, + { 23, IFM_IEEE80211_MCS }, + { 24, IFM_IEEE80211_MCS }, + { 25, IFM_IEEE80211_MCS }, + { 26, IFM_IEEE80211_MCS }, + { 27, IFM_IEEE80211_MCS }, + { 28, IFM_IEEE80211_MCS }, + { 29, IFM_IEEE80211_MCS }, + { 30, IFM_IEEE80211_MCS }, + { 31, IFM_IEEE80211_MCS }, + { 32, IFM_IEEE80211_MCS }, + { 33, IFM_IEEE80211_MCS }, + { 34, IFM_IEEE80211_MCS }, + { 35, IFM_IEEE80211_MCS }, + { 36, IFM_IEEE80211_MCS }, + { 37, IFM_IEEE80211_MCS }, + { 38, IFM_IEEE80211_MCS }, + { 39, IFM_IEEE80211_MCS }, + { 40, IFM_IEEE80211_MCS }, + { 41, IFM_IEEE80211_MCS }, + { 42, IFM_IEEE80211_MCS }, + { 43, IFM_IEEE80211_MCS }, + { 44, IFM_IEEE80211_MCS }, + { 45, IFM_IEEE80211_MCS }, + { 46, IFM_IEEE80211_MCS }, + { 47, IFM_IEEE80211_MCS }, + { 48, IFM_IEEE80211_MCS }, + { 49, IFM_IEEE80211_MCS }, + { 50, IFM_IEEE80211_MCS }, + { 51, IFM_IEEE80211_MCS }, + { 52, IFM_IEEE80211_MCS }, + { 53, IFM_IEEE80211_MCS }, + { 54, IFM_IEEE80211_MCS }, + { 55, IFM_IEEE80211_MCS }, + { 56, IFM_IEEE80211_MCS }, + { 57, IFM_IEEE80211_MCS }, + { 58, IFM_IEEE80211_MCS }, + { 59, IFM_IEEE80211_MCS }, + { 60, IFM_IEEE80211_MCS }, + { 61, IFM_IEEE80211_MCS }, + { 62, IFM_IEEE80211_MCS }, + { 63, IFM_IEEE80211_MCS }, + { 64, IFM_IEEE80211_MCS }, + { 65, IFM_IEEE80211_MCS }, + { 66, IFM_IEEE80211_MCS }, + { 67, IFM_IEEE80211_MCS }, + { 68, IFM_IEEE80211_MCS }, + { 69, IFM_IEEE80211_MCS }, + { 70, IFM_IEEE80211_MCS }, + { 71, IFM_IEEE80211_MCS }, + { 72, IFM_IEEE80211_MCS }, + { 73, IFM_IEEE80211_MCS }, + { 74, IFM_IEEE80211_MCS }, + { 75, IFM_IEEE80211_MCS }, + { 76, IFM_IEEE80211_MCS }, }; int m; diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211.h b/src/libs/compat/freebsd_wlan/net80211/ieee80211.h index c1bce85328..49716b3d80 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211.h +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211.h @@ -131,6 +131,7 @@ struct ieee80211_qosframe_addr4 { #define IEEE80211_FC0_SUBTYPE_AUTH 0xb0 #define IEEE80211_FC0_SUBTYPE_DEAUTH 0xc0 #define IEEE80211_FC0_SUBTYPE_ACTION 0xd0 +#define IEEE80211_FC0_SUBTYPE_ACTION_NOACK 0xe0 /* for TYPE_CTL */ #define IEEE80211_FC0_SUBTYPE_BAR 0x80 #define IEEE80211_FC0_SUBTYPE_BA 0x90 diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_acl.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_acl.c index cb20b87e73..da505e3b9d 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_acl.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_acl.c @@ -77,7 +77,7 @@ struct acl { struct aclstate { acl_lock_t as_lock; int as_policy; - int as_nacls; + uint32_t as_nacls; TAILQ_HEAD(, acl) as_list; /* list of all ACL's */ LIST_HEAD(, acl) as_hash[ACL_HASHSIZE]; struct ieee80211vap *as_vap; @@ -289,7 +289,8 @@ acl_getioctl(struct ieee80211vap *vap, struct ieee80211req *ireq) struct aclstate *as = vap->iv_as; struct acl *acl; struct ieee80211req_maclist *ap; - int error, space, i; + int error; + uint32_t i, space; switch (ireq->i_val) { case IEEE80211_MACCMD_POLICY: diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_adhoc.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_adhoc.c index d3b43bd572..8d9bcce589 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_adhoc.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_adhoc.c @@ -285,7 +285,6 @@ doprint(struct ieee80211vap *vap, int subtype) static int adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) { -#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; @@ -412,9 +411,7 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) 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])) { + if (! ieee80211_check_rxseq(ni, wh)) { /* duplicate, discard */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, bssid, "duplicate", @@ -660,7 +657,6 @@ out: m_freem(m); } return type; -#undef SEQ_LEQ } static int @@ -823,80 +819,44 @@ adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 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) { + case IEEE80211_FC0_SUBTYPE_ACTION: + case IEEE80211_FC0_SUBTYPE_ACTION_NOACK: + if (ni == vap->iv_bss) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "unknown node"); + vap->iv_stats.is_rx_mgtdiscard++; + } else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) && + !IEEE80211_IS_MULTICAST(wh->i_addr1)) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "not for us"); + vap->iv_stats.is_rx_mgtdiscard++; + } else if (vap->iv_state != IEEE80211_S_RUN) { IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "wrong state %s", ieee80211_state_name[vap->iv_state]); vap->iv_stats.is_rx_mgtdiscard++; - return; + } else { + if (ieee80211_parse_action(ni, m0) == 0) + (void)ic->ic_recv_action(ni, wh, frm, efrm); } - /* - * 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, wh, 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_REQ: case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: - case IEEE80211_FC0_SUBTYPE_DEAUTH: + case IEEE80211_FC0_SUBTYPE_ATIM: case IEEE80211_FC0_SUBTYPE_DISASSOC: + case IEEE80211_FC0_SUBTYPE_AUTH: + case IEEE80211_FC0_SUBTYPE_DEAUTH: IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, - wh, NULL, "%s", "not handled"); + wh, NULL, "%s", "not handled"); vap->iv_stats.is_rx_mgtdiscard++; - return; + break; default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, - wh, "mgt", "subtype 0x%x not handled", subtype); + wh, "mgt", "subtype 0x%x not handled", subtype); vap->iv_stats.is_rx_badsubtype++; break; } @@ -910,6 +870,7 @@ ahdemo_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_frame *wh; /* * Process management frames when scanning; useful for doing @@ -917,11 +878,42 @@ ahdemo_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, */ if (ic->ic_flags & IEEE80211_F_SCAN) adhoc_recv_mgmt(ni, m0, subtype, rssi, nf); - else - vap->iv_stats.is_rx_mgtdiscard++; + else { + wh = mtod(m0, struct ieee80211_frame *); + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + case IEEE80211_FC0_SUBTYPE_BEACON: + case IEEE80211_FC0_SUBTYPE_ATIM: + case IEEE80211_FC0_SUBTYPE_DISASSOC: + case IEEE80211_FC0_SUBTYPE_AUTH: + case IEEE80211_FC0_SUBTYPE_DEAUTH: + case IEEE80211_FC0_SUBTYPE_ACTION: + case IEEE80211_FC0_SUBTYPE_ACTION_NOACK: + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "not handled"); + vap->iv_stats.is_rx_mgtdiscard++; + break; + default: + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, "mgt", "subtype 0x%x not handled", subtype); + vap->iv_stats.is_rx_badsubtype++; + break; + } + } } static void -adhoc_recv_ctl(struct ieee80211_node *ni, struct mbuf *m0, int subtype) +adhoc_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype) { + + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_BAR: + ieee80211_recv_bar(ni, m); + break; + } } diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_ageq.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_ageq.c index 21745c8661..018ddc2711 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_ageq.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_ageq.c @@ -49,7 +49,7 @@ __FBSDID("$FreeBSD$"); void ieee80211_ageq_init(struct ieee80211_ageq *aq, int maxlen, const char *name) { - memset(aq, 0, sizeof(aq)); + memset(aq, 0, sizeof(*aq)); aq->aq_maxlen = maxlen; IEEE80211_AGEQ_INIT(aq, name); /* OS-dependent setup */ } @@ -154,7 +154,7 @@ ieee80211_ageq_drain_node(struct ieee80211_ageq *aq, * deltas (in seconds) relative to the head so we can check * and/or adjust only the head of the list. If a frame's age * exceeds the time quanta then remove it. The list of removed - * frames is is returned to the caller joined by m_nextpkt. + * frames is returned to the caller joined by m_nextpkt. */ struct mbuf * ieee80211_ageq_age(struct ieee80211_ageq *aq, int quanta) diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_alq.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_alq.c new file mode 100644 index 0000000000..0d06d4b9ef --- /dev/null +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_alq.c @@ -0,0 +1,155 @@ +/*- + * Copyright (c) 2011 Adrian Chadd, Xenion Lty Ltd + * 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 +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * net80211 fast-logging support, primarily for debugging. + * + * This implements a single debugging queue which includes + * per-device enumeration where needed. + */ + +#include "opt_wlan.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static struct alq *ieee80211_alq; +static int ieee80211_alq_lost; +static int ieee80211_alq_logged; +static char ieee80211_alq_logfile[MAXPATHLEN] = "/tmp/net80211.log"; +static unsigned int ieee80211_alq_qsize = 64*1024; + +static int +ieee80211_alq_setlogging(int enable) +{ + int error; + + if (enable) { + if (ieee80211_alq) + alq_close(ieee80211_alq); + + error = alq_open(&ieee80211_alq, + ieee80211_alq_logfile, + curthread->td_ucred, + ALQ_DEFAULT_CMODE, + sizeof (struct ieee80211_alq_rec), + ieee80211_alq_qsize); + ieee80211_alq_lost = 0; + ieee80211_alq_logged = 0; + printf("net80211: logging to %s enabled; struct size %d bytes\n", + ieee80211_alq_logfile, sizeof(struct ieee80211_alq_rec)); + } else { + if (ieee80211_alq) + alq_close(ieee80211_alq); + ieee80211_alq = NULL; + printf("net80211: logging disabled\n"); + error = 0; + } + return (error); +} + +static int +sysctl_ieee80211_alq_log(SYSCTL_HANDLER_ARGS) +{ + int error, enable; + + enable = (ieee80211_alq != NULL); + error = sysctl_handle_int(oidp, &enable, 0, req); + if (error || !req->newptr) + return (error); + else + return (ieee80211_alq_setlogging(enable)); +} + +SYSCTL_PROC(_net_wlan, OID_AUTO, alq, CTLTYPE_INT|CTLFLAG_RW, + 0, 0, sysctl_ieee80211_alq_log, "I", "Enable net80211 alq logging"); +SYSCTL_INT(_net_wlan, OID_AUTO, alq_size, CTLFLAG_RW, + &ieee80211_alq_qsize, 0, "In-memory log size (#records)"); +SYSCTL_INT(_net_wlan, OID_AUTO, alq_lost, CTLFLAG_RW, + &ieee80211_alq_lost, 0, "Debugging operations not logged"); +SYSCTL_INT(_net_wlan, OID_AUTO, alq_logged, CTLFLAG_RW, + &ieee80211_alq_logged, 0, "Debugging operations logged"); + +static struct ale * +ieee80211_alq_get(void) +{ + struct ale *ale; + + ale = alq_get(ieee80211_alq, ALQ_NOWAIT); + if (!ale) + ieee80211_alq_lost++; + else + ieee80211_alq_logged++; + return ale; +} + +void +ieee80211_alq_log(struct ieee80211vap *vap, uint8_t op, u_char *p, int l) +{ + struct ale *ale; + struct ieee80211_alq_rec *r; + + if (ieee80211_alq == NULL) + return; + + ale = ieee80211_alq_get(); + if (! ale) + return; + + r = (struct ieee80211_alq_rec *) ale->ae_data; + r->r_timestamp = htonl(ticks); + r->r_version = 1; + r->r_wlan = htons(vap->iv_ifp->if_dunit); + r->r_op = op; + memcpy(&r->r_payload, p, MIN(l, sizeof(r->r_payload))); + alq_post(ieee80211_alq, ale); +} diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_alq.h b/src/libs/compat/freebsd_wlan/net80211/ieee80211_alq.h new file mode 100644 index 0000000000..648eba2ba0 --- /dev/null +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_alq.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 2011 Adrian Chadd, Xenion Lty Ltd + * 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 __IEEE80211_ALQ_H__ +#define __IEEE80211_ALQ_H__ + +#define IEEE80211_ALQ_PAYLOAD_SIZE 24 + +/* + * timestamp + * wlan interface + * operation + * sub-operation + * rest of structure - operation specific + */ +struct ieee80211_alq_rec { + uint32_t r_timestamp; /* XXX may wrap! */ + uint16_t r_wlan; /* wlan interface number */ + uint8_t r_version; /* version */ + uint8_t r_op; /* top-level operation id */ + u_char r_payload[IEEE80211_ALQ_PAYLOAD_SIZE]; + /* operation-specific payload */ +}; + +/* General logging function */ +extern void ieee80211_alq_log(struct ieee80211vap *vap, uint8_t op, u_char *p, int l); + +#endif /* __IEEE80211_ALQ_H__ */ diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_amrr.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_amrr.c index 7902545c28..73dd901a35 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_amrr.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_amrr.c @@ -308,10 +308,10 @@ amrr_sysctlattach(struct ieee80211vap *vap, "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, vap, 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)"); /* XXX bounds check values */ - SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + SYSCTL_ADD_UINT(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, + SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "amrr_min_sucess_threshold", CTLFLAG_RW, &amrr->amrr_min_success_threshold, 0, ""); } diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_crypto.h b/src/libs/compat/freebsd_wlan/net80211/ieee80211_crypto.h index 5a429fa5b5..8294546c68 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_crypto.h +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_crypto.h @@ -78,6 +78,7 @@ struct ieee80211_key { #define IEEE80211_KEY_XMIT 0x0001 /* key used for xmit */ #define IEEE80211_KEY_RECV 0x0002 /* key used for recv */ #define IEEE80211_KEY_GROUP 0x0004 /* key used for WPA group operation */ +#define IEEE80211_KEY_NOREPLAY 0x0008 /* ignore replay failures */ #define IEEE80211_KEY_SWENCRYPT 0x0010 /* host-based encrypt */ #define IEEE80211_KEY_SWDECRYPT 0x0020 /* host-based decrypt */ #define IEEE80211_KEY_SWENMIC 0x0040 /* host-based enmic */ @@ -98,7 +99,8 @@ struct ieee80211_key { uint8_t wk_macaddr[IEEE80211_ADDR_LEN]; }; #define IEEE80211_KEY_COMMON /* common flags passed in by apps */\ - (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV | IEEE80211_KEY_GROUP) + (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV | IEEE80211_KEY_GROUP | \ + IEEE80211_KEY_NOREPLAY) #define IEEE80211_KEY_DEVICE /* flags owned by device driver */\ (IEEE80211_KEY_DEVKEY|IEEE80211_KEY_CIPHER0|IEEE80211_KEY_CIPHER1) diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_crypto_ccmp.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_crypto_ccmp.c index 79df266cef..cb9ed0af01 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_crypto_ccmp.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_crypto_ccmp.c @@ -226,7 +226,8 @@ ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) } tid = ieee80211_gettid(wh); pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]); - if (pn <= k->wk_keyrsc[tid]) { + if (pn <= k->wk_keyrsc[tid] && + (k->wk_flags & IEEE80211_KEY_NOREPLAY) == 0) { /* * Replay violation. */ diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_crypto_tkip.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_crypto_tkip.c index 6e1fda1fc9..05d9c74bdf 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_crypto_tkip.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_crypto_tkip.c @@ -281,7 +281,8 @@ tkip_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) 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[tid]) { + if (ctx->rx_rsc <= k->wk_keyrsc[tid] && + (k->wk_flags & IEEE80211_KEY_NOREPLAY) == 0) { /* * Replay violation; notify upper layer. */ diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_dfs.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_dfs.c index 72a21abc78..4ea1ac0ecb 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_dfs.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_dfs.c @@ -318,6 +318,8 @@ ieee80211_dfs_notify_radar(struct ieee80211com *ic, struct ieee80211_channel *ch * on the NOL to expire. */ /*XXX*/ + if_printf(ic->ic_ifp, "%s: No free channels; waiting for entry " + "on NOL to expire\n", __func__); } } else { /* diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_hostap.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_hostap.c index 63bcd3c9a1..e190450037 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_hostap.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_hostap.c @@ -472,7 +472,6 @@ doprint(struct ieee80211vap *vap, int subtype) static int hostap_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) { -#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; @@ -572,9 +571,7 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) 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])) { + if (! ieee80211_check_rxseq(ni, wh)) { /* duplicate, discard */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, bssid, "duplicate", @@ -914,7 +911,6 @@ out: m_freem(m); } return type; -#undef SEQ_LEQ } static void @@ -2194,18 +2190,38 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, } case IEEE80211_FC0_SUBTYPE_ACTION: - if (vap->iv_state == IEEE80211_S_RUN) { - if (ieee80211_parse_action(ni, m0) == 0) - ic->ic_recv_action(ni, wh, frm, efrm); - } else + case IEEE80211_FC0_SUBTYPE_ACTION_NOACK: + if (ni == vap->iv_bss) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "unknown node"); vap->iv_stats.is_rx_mgtdiscard++; + } else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) && + !IEEE80211_IS_MULTICAST(wh->i_addr1)) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "not for us"); + vap->iv_stats.is_rx_mgtdiscard++; + } else if (vap->iv_state != IEEE80211_S_RUN) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "wrong state %s", + ieee80211_state_name[vap->iv_state]); + vap->iv_stats.is_rx_mgtdiscard++; + } else { + if (ieee80211_parse_action(ni, m0) == 0) + (void)ic->ic_recv_action(ni, wh, frm, efrm); + } break; case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_ATIM: + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "not handled"); + vap->iv_stats.is_rx_mgtdiscard++; + break; + default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, - wh, "mgt", "subtype 0x%x not handled", subtype); + wh, "mgt", "subtype 0x%x not handled", subtype); vap->iv_stats.is_rx_badsubtype++; break; } diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_ht.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_ht.c index c823fa7a6b..aef705957b 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_ht.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_ht.c @@ -54,31 +54,86 @@ __FBSDID("$FreeBSD$"); #define MS(_v, _f) (((_v) & _f) >> _f##_S) #define SM(_v, _f) (((_v) << _f##_S) & _f) -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 */ +const struct ieee80211_mcs_rates ieee80211_htrates[IEEE80211_HTRATE_MAXSIZE] = { + { 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 */ + { 39, 43, 81, 90 }, /* MCS 16 */ + { 78, 87, 162, 180 }, /* MCS 17 */ + { 117, 130, 243, 270 }, /* MCS 18 */ + { 156, 173, 324, 360 }, /* MCS 19 */ + { 234, 260, 486, 540 }, /* MCS 20 */ + { 312, 347, 648, 720 }, /* MCS 21 */ + { 351, 390, 729, 810 }, /* MCS 22 */ + { 390, 433, 810, 900 }, /* MCS 23 */ + { 52, 58, 108, 120 }, /* MCS 24 */ + { 104, 116, 216, 240 }, /* MCS 25 */ + { 156, 173, 324, 360 }, /* MCS 26 */ + { 208, 231, 432, 480 }, /* MCS 27 */ + { 312, 347, 648, 720 }, /* MCS 28 */ + { 416, 462, 864, 960 }, /* MCS 29 */ + { 468, 520, 972, 1080 }, /* MCS 30 */ + { 520, 578, 1080, 1200 }, /* MCS 31 */ + { 0, 0, 12, 13 }, /* MCS 32 */ + { 78, 87, 162, 180 }, /* MCS 33 */ + { 104, 116, 216, 240 }, /* MCS 34 */ + { 130, 144, 270, 300 }, /* MCS 35 */ + { 117, 130, 243, 270 }, /* MCS 36 */ + { 156, 173, 324, 360 }, /* MCS 37 */ + { 195, 217, 405, 450 }, /* MCS 38 */ + { 104, 116, 216, 240 }, /* MCS 39 */ + { 130, 144, 270, 300 }, /* MCS 40 */ + { 130, 144, 270, 300 }, /* MCS 41 */ + { 156, 173, 324, 360 }, /* MCS 42 */ + { 182, 202, 378, 420 }, /* MCS 43 */ + { 182, 202, 378, 420 }, /* MCS 44 */ + { 208, 231, 432, 480 }, /* MCS 45 */ + { 156, 173, 324, 360 }, /* MCS 46 */ + { 195, 217, 405, 450 }, /* MCS 47 */ + { 195, 217, 405, 450 }, /* MCS 48 */ + { 234, 260, 486, 540 }, /* MCS 49 */ + { 273, 303, 567, 630 }, /* MCS 50 */ + { 273, 303, 567, 630 }, /* MCS 51 */ + { 312, 347, 648, 720 }, /* MCS 52 */ + { 130, 144, 270, 300 }, /* MCS 53 */ + { 156, 173, 324, 360 }, /* MCS 54 */ + { 182, 202, 378, 420 }, /* MCS 55 */ + { 156, 173, 324, 360 }, /* MCS 56 */ + { 182, 202, 378, 420 }, /* MCS 57 */ + { 208, 231, 432, 480 }, /* MCS 58 */ + { 234, 260, 486, 540 }, /* MCS 59 */ + { 208, 231, 432, 480 }, /* MCS 60 */ + { 234, 260, 486, 540 }, /* MCS 61 */ + { 260, 289, 540, 600 }, /* MCS 62 */ + { 260, 289, 540, 600 }, /* MCS 63 */ + { 286, 318, 594, 660 }, /* MCS 64 */ + { 195, 217, 405, 450 }, /* MCS 65 */ + { 234, 260, 486, 540 }, /* MCS 66 */ + { 273, 303, 567, 630 }, /* MCS 67 */ + { 234, 260, 486, 540 }, /* MCS 68 */ + { 273, 303, 567, 630 }, /* MCS 69 */ + { 312, 347, 648, 720 }, /* MCS 70 */ + { 351, 390, 729, 810 }, /* MCS 71 */ + { 312, 347, 648, 720 }, /* MCS 72 */ + { 351, 390, 729, 810 }, /* MCS 73 */ + { 390, 433, 810, 900 }, /* MCS 74 */ + { 390, 433, 810, 900 }, /* MCS 75 */ + { 429, 477, 891, 990 }, /* MCS 76 */ }; -static const struct ieee80211_htrateset ieee80211_rateset_11n = - { 16, { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15 } - }; - #ifdef IEEE80211_AMPDU_AGE static int ieee80211_ampdu_age = -1; /* threshold for ampdu reorder q (ms) */ SYSCTL_PROC(_net_wlan, OID_AUTO, ampdu_age, CTLTYPE_INT | CTLFLAG_RW, @@ -162,6 +217,9 @@ static int ieee80211_addba_response(struct ieee80211_node *ni, int code, int baparamset, int batimeout); static void ieee80211_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap); +static void null_addba_response_timeout(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap); + static void ieee80211_bar_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int status); static void ampdu_tx_stop(struct ieee80211_tx_ampdu *tap); @@ -179,6 +237,7 @@ ieee80211_ht_attach(struct ieee80211com *ic) ic->ic_ampdu_enable = ieee80211_ampdu_enable; ic->ic_addba_request = ieee80211_addba_request; ic->ic_addba_response = ieee80211_addba_response; + ic->ic_addba_response_timeout = null_addba_response_timeout; ic->ic_addba_stop = ieee80211_addba_stop; ic->ic_bar_response = ieee80211_bar_response; ic->ic_ampdu_rx_start = ampdu_rx_start; @@ -247,40 +306,149 @@ ieee80211_ht_vdetach(struct ieee80211vap *vap) { } +static int +ht_getrate(struct ieee80211com *ic, int index, int mode, int ratetype) +{ + int mword, rate; + + mword = ieee80211_rate2media(ic, index | IEEE80211_RATE_MCS, mode); + if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS) + return (0); + switch (ratetype) { + case 0: + rate = ieee80211_htrates[index].ht20_rate_800ns; + break; + case 1: + rate = ieee80211_htrates[index].ht20_rate_400ns; + break; + case 2: + rate = ieee80211_htrates[index].ht40_rate_800ns; + break; + default: + rate = ieee80211_htrates[index].ht40_rate_400ns; + break; + } + return (rate); +} + +static struct printranges { + int minmcs; + int maxmcs; + int txstream; + int ratetype; + int htcapflags; +} ranges[] = { + { 0, 7, 1, 0, 0 }, + { 8, 15, 2, 0, 0 }, + { 16, 23, 3, 0, 0 }, + { 24, 31, 4, 0, 0 }, + { 32, 0, 1, 2, IEEE80211_HTC_TXMCS32 }, + { 33, 38, 2, 0, IEEE80211_HTC_TXUNEQUAL }, + { 39, 52, 3, 0, IEEE80211_HTC_TXUNEQUAL }, + { 53, 76, 4, 0, IEEE80211_HTC_TXUNEQUAL }, + { 0, 0, 0, 0, 0 }, +}; + static void -ht_announce(struct ieee80211com *ic, int mode, - const struct ieee80211_htrateset *rs) +ht_rateprint(struct ieee80211com *ic, int mode, int ratetype) { struct ifnet *ifp = ic->ic_ifp; - int i, rate, mword; + int minrate, maxrate; + struct printranges *range; - if_printf(ifp, "%s MCS: ", ieee80211_phymode_name[mode]); - for (i = 0; i < rs->rs_nrates; i++) { - mword = ieee80211_rate2media(ic, - rs->rs_rates[i] | IEEE80211_RATE_MCS, mode); - if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS) + for (range = ranges; range->txstream != 0; range++) { + if (ic->ic_txstream < range->txstream) continue; - rate = ieee80211_htrates[rs->rs_rates[i]].ht40_rate_400ns; - printf("%s%d%sMbps", (i != 0 ? " " : ""), - rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); + if (range->htcapflags && + (ic->ic_htcaps & range->htcapflags) == 0) + continue; + if (ratetype < range->ratetype) + continue; + minrate = ht_getrate(ic, range->minmcs, mode, ratetype); + maxrate = ht_getrate(ic, range->maxmcs, mode, ratetype); + if (range->maxmcs) { + if_printf(ifp, "MCS %d-%d: %d%sMbps - %d%sMbps\n", + range->minmcs, range->maxmcs, + minrate/2, ((minrate & 0x1) != 0 ? ".5" : ""), + maxrate/2, ((maxrate & 0x1) != 0 ? ".5" : "")); + } else { + if_printf(ifp, "MCS %d: %d%sMbps\n", range->minmcs, + minrate/2, ((minrate & 0x1) != 0 ? ".5" : "")); + } + } +} + +static void +ht_announce(struct ieee80211com *ic, int mode) +{ + struct ifnet *ifp = ic->ic_ifp; + const char *modestr = ieee80211_phymode_name[mode]; + + if_printf(ifp, "%s MCS 20MHz\n", modestr); + ht_rateprint(ic, mode, 0); + if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20) { + if_printf(ifp, "%s MCS 20MHz SGI\n", modestr); + ht_rateprint(ic, mode, 1); + } + if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) { + if_printf(ifp, "%s MCS 40MHz:\n", modestr); + ht_rateprint(ic, mode, 2); + } + if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) && + (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)) { + if_printf(ifp, "%s MCS 40MHz SGI:\n", modestr); + ht_rateprint(ic, mode, 3); } - printf("\n"); } void ieee80211_ht_announce(struct ieee80211com *ic) { + struct ifnet *ifp = ic->ic_ifp; + + if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || + isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) + if_printf(ifp, "%dT%dR\n", ic->ic_txstream, ic->ic_rxstream); if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA)) - ht_announce(ic, IEEE80211_MODE_11NA, &ieee80211_rateset_11n); + ht_announce(ic, IEEE80211_MODE_11NA); if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) - ht_announce(ic, IEEE80211_MODE_11NG, &ieee80211_rateset_11n); + ht_announce(ic, IEEE80211_MODE_11NG); } +static struct ieee80211_htrateset htrateset; + const struct ieee80211_htrateset * ieee80211_get_suphtrates(struct ieee80211com *ic, - const struct ieee80211_channel *c) + const struct ieee80211_channel *c) { - return &ieee80211_rateset_11n; +#define ADDRATE(x) do { \ + htrateset.rs_rates[htrateset.rs_nrates] = x; \ + htrateset.rs_nrates++; \ +} while (0) + int i; + + memset(&htrateset, 0, sizeof(struct ieee80211_htrateset)); + for (i = 0; i < ic->ic_txstream * 8; i++) + ADDRATE(i); + if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) && + (ic->ic_htcaps & IEEE80211_HTC_TXMCS32)) + ADDRATE(i); + if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) { + if (ic->ic_txstream >= 2) { + for (i = 33; i <= 38; i++) + ADDRATE(i); + } + if (ic->ic_txstream >= 3) { + for (i = 39; i <= 52; i++) + ADDRATE(i); + } + if (ic->ic_txstream == 4) { + for (i = 53; i <= 76; i++) + ADDRATE(i); + } + } + return &htrateset; +#undef ADDRATE } /* @@ -396,6 +564,7 @@ ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap, static void ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) { + ampdu_rx_purge(rap); rap->rxa_flags &= ~(IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND); } @@ -656,7 +825,7 @@ again: if (off < rap->rxa_wnd) { /* * Common case (hopefully): in the BA window. - * Sec 9.10.7.6 a) (D2.04 p.118 line 47) + * Sec 9.10.7.6.2 a) (p.137) */ #ifdef IEEE80211_AMPDU_AGE /* @@ -721,7 +890,7 @@ again: /* * Outside the BA window, but within range; * flush the reorder q and move the window. - * Sec 9.10.7.6 b) (D2.04 p.118 line 60) + * Sec 9.10.7.6.2 b) (p.138) */ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, "move BA win <%u:%u> (%u frames) rxseq %u tid %u", @@ -745,7 +914,7 @@ again: } else { /* * Outside the BA window and out of range; toss. - * Sec 9.10.7.6 c) (D2.04 p.119 line 16) + * Sec 9.10.7.6.2 c) (p.138) */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, @@ -809,7 +978,7 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) if (off < IEEE80211_SEQ_BA_RANGE) { /* * Flush the reorder q up to rxseq and move the window. - * Sec 9.10.7.6 a) (D2.04 p.119 line 22) + * Sec 9.10.7.6.3 a) (p.138) */ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, "BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u", @@ -830,7 +999,7 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) } else { /* * Out of range; toss. - * Sec 9.10.7.6 b) (D2.04 p.119 line 41) + * Sec 9.10.7.6.3 b) (p.138) */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, @@ -1398,7 +1567,7 @@ ieee80211_ht_updatehtcap(struct ieee80211_node *ni, const uint8_t *htcapie) htcap_update_shortgi(ni); /* NB: honor operating mode constraint */ - /* XXX 40 MHZ intolerant */ + /* XXX 40 MHz intolerant */ htflags = (vap->iv_flags_ht & IEEE80211_FHT_HT) ? IEEE80211_CHAN_HT20 : 0; if ((ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) && @@ -1417,10 +1586,22 @@ ieee80211_ht_updatehtcap(struct ieee80211_node *ni, const uint8_t *htcapie) 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; + int i, maxequalmcs, maxunequalmcs; + + maxequalmcs = ic->ic_txstream * 8 - 1; + if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) { + if (ic->ic_txstream >= 2) + maxunequalmcs = 38; + if (ic->ic_txstream >= 3) + maxunequalmcs = 52; + if (ic->ic_txstream >= 4) + maxunequalmcs = 76; + } else + maxunequalmcs = 0; rs = &ni->ni_htrates; memset(rs, 0, sizeof(*rs)); @@ -1439,6 +1620,13 @@ ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags) vap->iv_stats.is_rx_rstoobig++; break; } + if (i <= 31 && i > maxequalmcs) + continue; + if (i == 32 && + (ic->ic_htcaps & IEEE80211_HTC_TXMCS32) == 0) + continue; + if (i > 32 && i > maxunequalmcs) + continue; rs->rs_rates[rs->rs_nrates++] = i; } } @@ -1507,14 +1695,23 @@ ampdu_tx_stop(struct ieee80211_tx_ampdu *tap) tap->txa_flags &= ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK); } +/* + * ADDBA response timeout. + * + * If software aggregation and per-TID queue management was done here, + * that queue would be unpaused after the ADDBA timeout occurs. + */ static void addba_timeout(void *arg) { struct ieee80211_tx_ampdu *tap = arg; + struct ieee80211_node *ni = tap->txa_ni; + struct ieee80211com *ic = ni->ni_ic; /* XXX ? */ tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; tap->txa_attempts++; + ic->ic_addba_response_timeout(ni, tap); } static void @@ -1537,6 +1734,12 @@ addba_stop_timeout(struct ieee80211_tx_ampdu *tap) } } +static void +null_addba_response_timeout(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap) +{ +} + /* * Default method for requesting A-MPDU tx aggregation. * We setup the specified state block and start a timer @@ -1621,7 +1824,7 @@ ht_recv_action_ba_addba_request(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap; uint8_t dialogtoken; uint16_t baparamset, batimeout, baseqctl; - uint16_t args[4]; + uint16_t args[5]; int tid; dialogtoken = frm[2]; @@ -1671,6 +1874,7 @@ ht_recv_action_ba_addba_request(struct ieee80211_node *ni, | SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ) ; args[3] = 0; + args[4] = 0; ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, IEEE80211_ACTION_BA_ADDBA_RESPONSE, args); return 0; @@ -1874,7 +2078,7 @@ ieee80211_ampdu_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { struct ieee80211com *ic = ni->ni_ic; - uint16_t args[4]; + uint16_t args[5]; int tid, dialogtoken; static int tokens = 0; /* XXX */ @@ -1891,13 +2095,14 @@ ieee80211_ampdu_request(struct ieee80211_node *ni, tap->txa_start = ni->ni_txseqs[tid]; args[0] = dialogtoken; - args[1] = IEEE80211_BAPS_POLICY_IMMEDIATE + args[1] = 0; /* NB: status code not used */ + args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE | SM(tid, IEEE80211_BAPS_TID) | SM(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ) ; - args[2] = 0; /* batimeout */ + args[3] = 0; /* batimeout */ /* NB: do first so there's no race against reply */ - if (!ic->ic_addba_request(ni, tap, dialogtoken, args[1], args[2])) { + if (!ic->ic_addba_request(ni, tap, dialogtoken, args[2], args[3])) { /* unable to setup state, don't make request */ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: could not setup BA stream for AC %d", @@ -1911,7 +2116,7 @@ ieee80211_ampdu_request(struct ieee80211_node *ni, } tokens = dialogtoken; /* allocate token */ /* NB: after calling ic_addba_request so driver can set txa_start */ - args[3] = SM(tap->txa_start, IEEE80211_BASEQ_START) + args[4] = SM(tap->txa_start, IEEE80211_BASEQ_START) | SM(0, IEEE80211_BASEQ_FRAG) ; return ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, @@ -2002,7 +2207,7 @@ bar_tx_complete(struct ieee80211_node *ni, void *arg, int status) callout_pending(&tap->txa_timer)) { struct ieee80211com *ic = ni->ni_ic; - if (status) /* ACK'd */ + if (status == 0) /* ACK'd */ bar_stop_timer(tap); ic->ic_bar_response(ni, tap, status); /* NB: just let timer expire so we pace requests */ @@ -2014,7 +2219,7 @@ ieee80211_bar_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int status) { - if (status != 0) { /* got ACK */ + if (status == 0) { /* got ACK */ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "BAR moves BA win <%u:%u> (%u frames) txseq %u tid %u", tap->txa_start, @@ -2104,11 +2309,15 @@ ieee80211_send_bar(struct ieee80211_node *ni, ni, "send BAR: tid %u ctl 0x%x start %u (attempt %d)", tid, barctl, seq, tap->txa_attempts); + /* + * ic_raw_xmit will free the node reference + * regardless of queue/TX success or failure. + */ ret = ic->ic_raw_xmit(ni, m, NULL); if (ret != 0) { /* xmit failed, clear state flag */ tap->txa_flags &= ~IEEE80211_AGGR_BARPEND; - goto bad; + return ret; } /* XXX hack against tx complete happening before timer is started */ if (tap->txa_flags & IEEE80211_AGGR_BARPEND) @@ -2157,12 +2366,12 @@ ht_send_action_ba_addba(struct ieee80211_node *ni, uint8_t *frm; IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, - "send ADDBA %s: dialogtoken %d " + "send ADDBA %s: dialogtoken %d status %d " "baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x", (action == IEEE80211_ACTION_BA_ADDBA_REQUEST) ? "request" : "response", - args[0], args[1], MS(args[1], IEEE80211_BAPS_TID), - args[2], args[3]); + args[0], args[1], args[2], MS(args[2], IEEE80211_BAPS_TID), + args[3], args[4]); IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, @@ -2179,10 +2388,12 @@ ht_send_action_ba_addba(struct ieee80211_node *ni, *frm++ = category; *frm++ = action; *frm++ = args[0]; /* dialog token */ - ADDSHORT(frm, args[1]); /* baparamset */ - ADDSHORT(frm, args[2]); /* batimeout */ + if (action == IEEE80211_ACTION_BA_ADDBA_RESPONSE) + ADDSHORT(frm, args[1]); /* status code */ + ADDSHORT(frm, args[2]); /* baparamset */ + ADDSHORT(frm, args[3]); /* batimeout */ if (action == IEEE80211_ACTION_BA_ADDBA_REQUEST) - ADDSHORT(frm, args[3]); /* baseqctl */ + ADDSHORT(frm, args[4]); /* baseqctl */ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); return ht_action_output(ni, m); } else { @@ -2276,21 +2487,49 @@ ht_send_action_ht_txchwidth(struct ieee80211_node *ni, #undef ADDSHORT /* - * Construct the MCS bit mask for inclusion - * in an HT information element. + * Construct the MCS bit mask for inclusion in an HT capabilities + * information element. */ -static void -ieee80211_set_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs) +static void +ieee80211_set_mcsset(struct ieee80211com *ic, uint8_t *frm) { int i; + uint8_t txparams; - for (i = 0; i < rs->rs_nrates; i++) { - int r = rs->rs_rates[i] & IEEE80211_RATE_VAL; - if (r < IEEE80211_HTRATE_MAXSIZE) { /* XXX? */ - /* NB: this assumes a particular implementation */ - setbit(frm, r); + KASSERT((ic->ic_rxstream > 0 && ic->ic_rxstream <= 4), + ("ic_rxstream %d out of range", ic->ic_rxstream)); + KASSERT((ic->ic_txstream > 0 && ic->ic_txstream <= 4), + ("ic_txstream %d out of range", ic->ic_txstream)); + + for (i = 0; i < ic->ic_rxstream * 8; i++) + setbit(frm, i); + if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) && + (ic->ic_htcaps & IEEE80211_HTC_RXMCS32)) + setbit(frm, 32); + if (ic->ic_htcaps & IEEE80211_HTC_RXUNEQUAL) { + if (ic->ic_rxstream >= 2) { + for (i = 33; i <= 38; i++) + setbit(frm, i); + } + if (ic->ic_rxstream >= 3) { + for (i = 39; i <= 52; i++) + setbit(frm, i); + } + if (ic->ic_txstream >= 4) { + for (i = 53; i <= 76; i++) + setbit(frm, i); } } + + if (ic->ic_rxstream != ic->ic_txstream) { + txparams = 0x1; /* TX MCS set defined */ + txparams |= 0x2; /* TX RX MCS not equal */ + txparams |= (ic->ic_txstream - 1) << 2; /* num TX streams */ + if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) + txparams |= 0x16; /* TX unequal modulation sup */ + } else + txparams = 0; + frm[12] = txparams; } /* @@ -2304,8 +2543,9 @@ 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; + uint16_t caps, extcaps; int rxmax, density; /* HT capabilities */ @@ -2327,6 +2567,17 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) /* use advertised setting (XXX locally constraint) */ rxmax = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); density = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); + + /* + * NB: Hardware might support HT40 on some but not all + * channels. We can't determine this earlier because only + * after association the channel is upgraded to HT based + * on the negotiated capabilities. + */ + if (ni->ni_chan != IEEE80211_CHAN_ANYC && + findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT40U) == NULL && + findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT40D) == NULL) + caps &= ~IEEE80211_HTCAP_CHWIDTH40; } else { /* override 20/40 use based on current channel */ if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) @@ -2356,15 +2607,24 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) /* supported MCS set */ /* - * XXX it would better to get the rate set from ni_htrates - * so we can restrict it but for sta mode ni_htrates isn't - * setup when we're called to form an AssocReq frame so for - * now we're restricted to the default HT rate set. + * XXX: For sta mode the rate set should be restricted based + * on the AP's capabilities, but ni_htrates isn't setup when + * we're called to form an AssocReq frame so for now we're + * restricted to the device capabilities. */ - ieee80211_set_htrates(frm, &ieee80211_rateset_11n); + ieee80211_set_mcsset(ni->ni_ic, frm); + + frm += __offsetof(struct ieee80211_ie_htcap, hc_extcap) - + __offsetof(struct ieee80211_ie_htcap, hc_mcsset); + + /* HT extended capabilities */ + extcaps = vap->iv_htextcaps & 0xffff; + + ADDSHORT(frm, extcaps); frm += sizeof(struct ieee80211_ie_htcap) - - __offsetof(struct ieee80211_ie_htcap, hc_mcsset); + __offsetof(struct ieee80211_ie_htcap, hc_txbf); + return frm; #undef ADDSHORT } diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_ht.h b/src/libs/compat/freebsd_wlan/net80211/ieee80211_ht.h index 4d81893918..d345fe9fb3 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_ht.h +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_ht.h @@ -144,7 +144,8 @@ struct ieee80211_rx_ampdu { int rxa_age; /* age of oldest frame in window */ int rxa_nframes; /* frames since ADDBA */ struct mbuf *rxa_m[IEEE80211_AGGR_BAWMAX]; - uint64_t rxa_pad[4]; + void *rxa_private; + uint64_t rxa_pad[3]; }; void ieee80211_ht_attach(struct ieee80211com *); @@ -160,7 +161,7 @@ struct ieee80211_mcs_rates { uint16_t ht40_rate_800ns; uint16_t ht40_rate_400ns; }; -extern const struct ieee80211_mcs_rates ieee80211_htrates[16]; +extern const struct ieee80211_mcs_rates ieee80211_htrates[]; const struct ieee80211_htrateset *ieee80211_get_suphtrates( struct ieee80211com *, const struct ieee80211_channel *); diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_input.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_input.c index 28a58fe06b..a18da4afe2 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_input.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_input.c @@ -57,8 +57,53 @@ __FBSDID("$FreeBSD$"); #include #endif +static void +ieee80211_process_mimo(struct ieee80211_node *ni, struct ieee80211_rx_stats *rx) +{ + int i; + + /* Verify the required MIMO bits are set */ + if ((rx->r_flags & (IEEE80211_R_C_CHAIN | IEEE80211_R_C_NF | IEEE80211_R_C_RSSI)) != + (IEEE80211_R_C_CHAIN | IEEE80211_R_C_NF | IEEE80211_R_C_RSSI)) + return; + + /* XXX This assumes the MIMO radios have both ctl and ext chains */ + for (i = 0; i < MIN(rx->c_chain, IEEE80211_MAX_CHAINS); i++) { + IEEE80211_RSSI_LPF(ni->ni_mimo_rssi_ctl[i], rx->c_rssi_ctl[i]); + IEEE80211_RSSI_LPF(ni->ni_mimo_rssi_ext[i], rx->c_rssi_ext[i]); + } + + /* XXX This also assumes the MIMO radios have both ctl and ext chains */ + for(i = 0; i < MIN(rx->c_chain, IEEE80211_MAX_CHAINS); i++) { + ni->ni_mimo_noise_ctl[i] = rx->c_nf_ctl[i]; + ni->ni_mimo_noise_ext[i] = rx->c_nf_ext[i]; + } + ni->ni_mimo_chains = rx->c_chain; +} + +int +ieee80211_input_mimo(struct ieee80211_node *ni, struct mbuf *m, + struct ieee80211_rx_stats *rx) +{ + /* XXX should assert IEEE80211_R_NF and IEEE80211_R_RSSI are set */ + ieee80211_process_mimo(ni, rx); + return ieee80211_input(ni, m, rx->rssi, rx->nf); +} + int ieee80211_input_all(struct ieee80211com *ic, struct mbuf *m, int rssi, int nf) +{ + struct ieee80211_rx_stats rx; + + rx.r_flags = IEEE80211_R_NF | IEEE80211_R_RSSI; + rx.nf = nf; + rx.rssi = rssi; + return ieee80211_input_mimo_all(ic, m, &rx); +} + +int +ieee80211_input_mimo_all(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_rx_stats *rx) { struct ieee80211vap *vap; int type = -1; @@ -96,7 +141,7 @@ ieee80211_input_all(struct ieee80211com *ic, struct mbuf *m, int rssi, int nf) m = NULL; } ni = ieee80211_ref_node(vap->iv_bss); - type = ieee80211_input(ni, mcopy, rssi, nf); + type = ieee80211_input_mimo(ni, mcopy, rx); ieee80211_free_node(ni); } if (m != NULL) /* no vaps, reclaim mbuf */ @@ -677,7 +722,6 @@ ieee80211_parse_action(struct ieee80211_node *ni, struct mbuf *m) 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) { diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_input.h b/src/libs/compat/freebsd_wlan/net80211/ieee80211_input.h index 921d07c238..c5638a9579 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_input.h +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_input.h @@ -142,6 +142,104 @@ ishtinfooui(const uint8_t *frm) return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI); } +#include /* For le16toh() */ + +/* + * Check the current frame sequence number against the current TID + * state and return whether it's in sequence or should be dropped. + * + * Since out of order packet and duplicate packet eliminations should + * be done by the AMPDU RX code, this routine blindly accepts all + * frames from a HT station w/ a TID that is currently doing AMPDU-RX. + * HT stations without WME or where the TID is not doing AMPDU-RX + * are checked like non-HT stations. + * + * The routine only eliminates packets whose sequence/fragment + * match or are less than the last seen sequence/fragment number + * AND are retransmits It doesn't try to eliminate out of order packets. + * + * Since all frames after sequence number 4095 will be less than 4095 + * (as the seqnum wraps), handle that special case so packets aren't + * incorrectly dropped - ie, if the next packet is sequence number 0 + * but a retransmit since the initial packet didn't make it. + */ +static __inline int +ieee80211_check_rxseq(struct ieee80211_node *ni, struct ieee80211_frame *wh) +{ +#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) +#define SEQ_EQ(a,b) ((int)((a)-(b)) == 0) +#define HAS_SEQ(type) ((type & 0x4) == 0) +#define SEQNO(a) ((a) >> IEEE80211_SEQ_SEQ_SHIFT) +#define FRAGNO(a) ((a) & IEEE80211_SEQ_FRAG_MASK) + uint16_t rxseq; + uint8_t type; + uint8_t tid; + struct ieee80211_rx_ampdu *rap; + + rxseq = le16toh(*(uint16_t *)wh->i_seq); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + /* Types with no sequence number are always treated valid */ + if (! HAS_SEQ(type)) + return 1; + + tid = ieee80211_gettid(wh); + + /* + * Only do the HT AMPDU check for WME stations; non-WME HT stations + * shouldn't exist outside of debugging. We should at least + * handle that. + */ + if (tid < WME_NUM_TID) { + rap = &ni->ni_rx_ampdu[tid]; + /* HT nodes currently doing RX AMPDU are always valid */ + if ((ni->ni_flags & IEEE80211_NODE_HT) && + (rap->rxa_flags & IEEE80211_AGGR_RUNNING)) + return 1; + } + + /* + * Otherwise, retries for packets below or equal to the last + * seen sequence number should be dropped. + */ + + /* + * Treat frame seqnum 4095 as special due to boundary + * wrapping conditions. + */ + if (SEQNO(ni->ni_rxseqs[tid]) == 4095) { + /* + * Drop retransmits on seqnum 4095/current fragment for itself. + */ + if (SEQ_EQ(rxseq, ni->ni_rxseqs[tid]) && + (wh->i_fc[1] & IEEE80211_FC1_RETRY)) + return 0; + /* + * Treat any subsequent frame as fine if the last seen frame + * is 4095 and it's not a retransmit for the same sequence + * number. However, this doesn't capture incorrectly ordered + * fragments w/ sequence number 4095. It shouldn't be seen + * in practice, but see the comment above for further info. + */ + return 1; + } + + /* + * At this point we assume that retransmitted seq/frag numbers below + * the current can simply be eliminated. + */ + if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && + SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) + return 0; + + return 1; +#undef SEQ_LEQ +#undef SEQ_EQ +#undef HAS_SEQ +#undef SEQNO +#undef FRAGNO +} + void ieee80211_deliver_data(struct ieee80211vap *, struct ieee80211_node *, struct mbuf *); struct mbuf *ieee80211_defrag(struct ieee80211_node *, diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_ioctl.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_ioctl.c index fb1a2427e1..15c6c17a7f 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_ioctl.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_ioctl.c @@ -70,6 +70,8 @@ __FBSDID("$FreeBSD$"); static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; static struct ieee80211_channel *findchannel(struct ieee80211com *, int ieee, int mode); +static int ieee80211_scanreq(struct ieee80211vap *, + struct ieee80211_scan_req *); static __noinline int ieee80211_ioctl_getkey(struct ieee80211vap *vap, struct ieee80211req *ireq) @@ -141,7 +143,7 @@ static __noinline int ieee80211_ioctl_getchaninfo(struct ieee80211vap *vap, struct ieee80211req *ireq) { struct ieee80211com *ic = vap->iv_ic; - int space; + uint32_t space; space = __offsetof(struct ieee80211req_chaninfo, ic_chans[ic->ic_nchans]); @@ -205,7 +207,7 @@ ieee80211_ioctl_getstastats(struct ieee80211vap *vap, struct ieee80211req *ireq) { struct ieee80211_node *ni; uint8_t macaddr[IEEE80211_ADDR_LEN]; - const int off = __offsetof(struct ieee80211req_sta_stats, is_stats); + const size_t off = __offsetof(struct ieee80211req_sta_stats, is_stats); int error; if (ireq->i_len < off) @@ -325,7 +327,7 @@ ieee80211_ioctl_getscanresults(struct ieee80211vap *vap, if (req.space > ireq->i_len) req.space = ireq->i_len; if (req.space > 0) { - size_t space; + uint32_t space; void *p; space = req.space; @@ -464,7 +466,7 @@ get_sta_info(void *arg, struct ieee80211_node *ni) static __noinline int getstainfo_common(struct ieee80211vap *vap, struct ieee80211req *ireq, - struct ieee80211_node *ni, int off) + struct ieee80211_node *ni, size_t off) { struct ieee80211com *ic = vap->iv_ic; struct stainforeq req; @@ -509,7 +511,7 @@ 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); + const size_t off = __offsetof(struct ieee80211req_sta_req, info); struct ieee80211_node *ni; int error; @@ -1479,14 +1481,15 @@ mlmelookup(void *arg, const struct ieee80211_scan_entry *se) } 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]) +setmlme_assoc_sta(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; + KASSERT(vap->iv_opmode == IEEE80211_M_STA, + ("expected opmode STA not %s", + ieee80211_opmode_name[vap->iv_opmode])); /* NB: this is racey if roaming is !manual */ lookup.se = NULL; @@ -1502,6 +1505,37 @@ setmlme_assoc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN], return 0; } +static __noinline int +setmlme_assoc_adhoc(struct ieee80211vap *vap, + const uint8_t mac[IEEE80211_ADDR_LEN], int ssid_len, + const uint8_t ssid[IEEE80211_NWID_LEN]) +{ + struct ieee80211_scan_req sr; + + KASSERT(vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_opmode == IEEE80211_M_AHDEMO, + ("expected opmode IBSS or AHDEMO not %s", + ieee80211_opmode_name[vap->iv_opmode])); + + if (ssid_len == 0) + return EINVAL; + + /* NB: IEEE80211_IOC_SSID call missing for ap_scan=2. */ + memset(vap->iv_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN); + vap->iv_des_ssid[0].len = ssid_len; + memcpy(vap->iv_des_ssid[0].ssid, ssid, ssid_len); + vap->iv_des_nssid = 1; + + memset(&sr, 0, sizeof(sr)); + sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE; + sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER; + memcpy(sr.sr_ssid[0].ssid, ssid, ssid_len); + sr.sr_ssid[0].len = ssid_len; + sr.sr_nssid = 1; + + return ieee80211_scanreq(vap, &sr); +} + static __noinline int ieee80211_ioctl_setmlme(struct ieee80211vap *vap, struct ieee80211req *ireq) { @@ -1513,9 +1547,13 @@ ieee80211_ioctl_setmlme(struct ieee80211vap *vap, struct ieee80211req *ireq) error = copyin(ireq->i_data, &mlme, sizeof(mlme)); if (error) return error; - if (mlme.im_op == IEEE80211_MLME_ASSOC) - return setmlme_assoc(vap, mlme.im_macaddr, + if (vap->iv_opmode == IEEE80211_M_STA && + mlme.im_op == IEEE80211_MLME_ASSOC) + return setmlme_assoc_sta(vap, mlme.im_macaddr, vap->iv_des_ssid[0].len, vap->iv_des_ssid[0].ssid); + else if (mlme.im_op == IEEE80211_MLME_ASSOC) + return setmlme_assoc_adhoc(vap, mlme.im_macaddr, + mlme.im_ssid_len, mlme.im_ssid); else return setmlme_common(vap, mlme.im_op, mlme.im_macaddr, mlme.im_reason); @@ -2344,8 +2382,8 @@ ieee80211_ioctl_chanswitch(struct ieee80211vap *vap, struct ieee80211req *ireq) return error; } -static __noinline int -ieee80211_ioctl_scanreq(struct ieee80211vap *vap, struct ieee80211req *ireq) +static int +ieee80211_scanreq(struct ieee80211vap *vap, struct ieee80211_scan_req *sr) { #define IEEE80211_IOC_SCAN_FLAGS \ (IEEE80211_IOC_SCAN_NOPICK | IEEE80211_IOC_SCAN_ACTIVE | \ @@ -2354,48 +2392,38 @@ ieee80211_ioctl_scanreq(struct ieee80211vap *vap, struct ieee80211req *ireq) 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; + 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) + 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; + 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_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; + 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) + 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; + 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 @@ -2403,13 +2431,13 @@ ieee80211_ioctl_scanreq(struct ieee80211vap *vap, struct ieee80211req *ireq) * 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; + 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, + __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); + 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 @@ -2424,13 +2452,13 @@ ieee80211_ioctl_scanreq(struct ieee80211vap *vap, struct ieee80211req *ireq) 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_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); @@ -2438,25 +2466,45 @@ ieee80211_ioctl_scanreq(struct ieee80211vap *vap, struct ieee80211req *ireq) } 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, + if (sr->sr_flags & IEEE80211_IOC_SCAN_CHECK) { + error = 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]); + (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, + error = 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]); + (const struct ieee80211_scan_ssid *) &sr->sr_ssid[0]); } + if (error == 0) + return EINPROGRESS; } - return error; + return 0; #undef IEEE80211_IOC_SCAN_FLAGS } +static __noinline int +ieee80211_ioctl_scanreq(struct ieee80211vap *vap, struct ieee80211req *ireq) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_req sr; /* XXX off stack? */ + int error; + + /* 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; + return ieee80211_scanreq(vap, &sr); +} + static __noinline int ieee80211_ioctl_setstavlan(struct ieee80211vap *vap, struct ieee80211req *ireq) { diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_ioctl.h b/src/libs/compat/freebsd_wlan/net80211/ieee80211_ioctl.h index b4dc8675a1..f1a1dcd69a 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_ioctl.h +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_ioctl.h @@ -582,7 +582,7 @@ struct ieee80211req { char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */ uint16_t i_type; /* req type */ int16_t i_val; /* Index or simple value */ - int16_t i_len; /* Index or simple value */ + uint16_t i_len; /* Index or simple value */ void *i_data; /* Extra data */ }; #define SIOCS80211 _IOW('i', 234, struct ieee80211req) @@ -798,7 +798,7 @@ struct ieee80211req_scan_result { #endif int8_t isr_noise; int8_t isr_rssi; - uint8_t isr_intval; /* beacon interval */ + uint16_t isr_intval; /* beacon interval */ uint8_t isr_capinfo; /* capabilities */ uint8_t isr_erp; /* ERP element */ uint8_t isr_bssid[IEEE80211_ADDR_LEN]; diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_mesh.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_mesh.c index d42d55db72..571a733539 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_mesh.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_mesh.c @@ -82,6 +82,7 @@ static void mesh_forward(struct ieee80211vap *, struct mbuf *, static int mesh_input(struct ieee80211_node *, struct mbuf *, int, int); static void mesh_recv_mgmt(struct ieee80211_node *, struct mbuf *, int, int, int); +static void mesh_recv_ctl(struct ieee80211_node *, struct mbuf *, int); static void mesh_peer_timeout_setup(struct ieee80211_node *); static void mesh_peer_timeout_backoff(struct ieee80211_node *); static void mesh_peer_timeout_cb(void *); @@ -520,6 +521,7 @@ mesh_vattach(struct ieee80211vap *vap) vap->iv_input = mesh_input; vap->iv_opdetach = mesh_vdetach; vap->iv_recv_mgmt = mesh_recv_mgmt; + vap->iv_recv_ctl = mesh_recv_ctl; ms = malloc(sizeof(struct ieee80211_mesh_state), M_80211_VAP, M_NOWAIT | M_ZERO); if (ms == NULL) { @@ -1038,7 +1040,6 @@ mesh_isucastforme(struct ieee80211vap *vap, const struct ieee80211_frame *wh, static int mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) { -#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; @@ -1092,9 +1093,7 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) 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])) { + if (! ieee80211_check_rxseq(ni, wh)) { /* duplicate, discard */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr1, "duplicate", @@ -1461,18 +1460,19 @@ mesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, meshid = frm; break; } - frm += frm[2] + 2; + frm += frm[1] + 2; } IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return); IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return); if (xrates != NULL) IEEE80211_VERIFY_ELEMENT(xrates, IEEE80211_RATE_MAXSIZE - rates[1], return); - if (meshid != NULL) + if (meshid != NULL) { IEEE80211_VERIFY_ELEMENT(meshid, IEEE80211_MESHID_LEN, return); - /* NB: meshid, not ssid */ - IEEE80211_VERIFY_SSID(vap->iv_bss, meshid, return); + /* NB: meshid, not ssid */ + IEEE80211_VERIFY_SSID(vap->iv_bss, meshid, return); + } /* XXX find a better class or define it's own */ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2, @@ -1486,46 +1486,42 @@ mesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, ieee80211_send_proberesp(vap, wh->i_addr2, 0); break; } + case IEEE80211_FC0_SUBTYPE_ACTION: - if (vap->iv_state != IEEE80211_S_RUN) { - vap->iv_stats.is_rx_mgtdiscard++; - break; - } - /* - * We received an action for an unknown neighbor. - * XXX: wait for it to beacon or create ieee80211_node? - */ + case IEEE80211_FC0_SUBTYPE_ACTION_NOACK: if (ni == vap->iv_bss) { - IEEE80211_DISCARD(vap, IEEE80211_MSG_MESH, + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "%s", "unknown node"); vap->iv_stats.is_rx_mgtdiscard++; - break; - } - /* - * Discard if not for us. - */ - if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) && + } else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) && !IEEE80211_IS_MULTICAST(wh->i_addr1)) { - IEEE80211_DISCARD(vap, IEEE80211_MSG_MESH, - wh, NULL, "%s", "not for me"); + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "not for us"); vap->iv_stats.is_rx_mgtdiscard++; - break; + } else if (vap->iv_state != IEEE80211_S_RUN) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "wrong state %s", + ieee80211_state_name[vap->iv_state]); + vap->iv_stats.is_rx_mgtdiscard++; + } else { + if (ieee80211_parse_action(ni, m0) == 0) + (void)ic->ic_recv_action(ni, wh, frm, efrm); } - /* XXX parse_action is a bit useless now */ - if (ieee80211_parse_action(ni, m0) == 0) - ic->ic_recv_action(ni, wh, 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_REQ: case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: - case IEEE80211_FC0_SUBTYPE_DEAUTH: + case IEEE80211_FC0_SUBTYPE_ATIM: case IEEE80211_FC0_SUBTYPE_DISASSOC: + case IEEE80211_FC0_SUBTYPE_AUTH: + case IEEE80211_FC0_SUBTYPE_DEAUTH: IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, NULL, "%s", "not handled"); vap->iv_stats.is_rx_mgtdiscard++; - return; + break; + default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, wh, "mgt", "subtype 0x%x not handled", subtype); @@ -1534,6 +1530,17 @@ mesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, } } +static void +mesh_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype) +{ + + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_BAR: + ieee80211_recv_bar(ni, m); + break; + } +} + /* * Parse meshpeering action ie's for open+confirm frames; the * important bits are returned in the supplied structure. @@ -2283,6 +2290,7 @@ mesh_verify_meshconf(struct ieee80211vap *vap, const uint8_t *ie) const struct ieee80211_meshconf_ie *meshconf = (const struct ieee80211_meshconf_ie *) ie; const struct ieee80211_mesh_state *ms = vap->iv_mesh; + uint16_t cap; if (meshconf == NULL) return 1; @@ -2316,8 +2324,10 @@ mesh_verify_meshconf(struct ieee80211vap *vap, const uint8_t *ie) meshconf->conf_pselid); return 1; } + /* NB: conf_cap is only read correctly here */ + cap = LE_READ_2(&meshconf->conf_cap); /* Not accepting peers */ - if (!(meshconf->conf_cap & IEEE80211_MESHCONF_CAP_AP)) { + if (!(cap & IEEE80211_MESHCONF_CAP_AP)) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_MESH, "not accepting peers: 0x%x\n", meshconf->conf_cap); return 1; @@ -2381,6 +2391,7 @@ uint8_t * ieee80211_add_meshconf(uint8_t *frm, struct ieee80211vap *vap) { const struct ieee80211_mesh_state *ms = vap->iv_mesh; + uint16_t caps; KASSERT(vap->iv_opmode == IEEE80211_M_MBSS, ("not a MBSS vap")); @@ -2396,11 +2407,12 @@ ieee80211_add_meshconf(uint8_t *frm, struct ieee80211vap *vap) if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL) *frm |= IEEE80211_MESHCONF_FORM_MP; frm += 1; + caps = 0; if (ms->ms_flags & IEEE80211_MESHFLAGS_AP) - *frm |= IEEE80211_MESHCONF_CAP_AP; + caps |= IEEE80211_MESHCONF_CAP_AP; if (ms->ms_flags & IEEE80211_MESHFLAGS_FWD) - *frm |= IEEE80211_MESHCONF_CAP_FWRD; - frm += 1; + caps |= IEEE80211_MESHCONF_CAP_FWRD; + ADDSHORT(frm, caps); return frm; } diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_mesh.h b/src/libs/compat/freebsd_wlan/net80211/ieee80211_mesh.h index 5a9d148977..a748be32f8 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_mesh.h +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_mesh.h @@ -49,7 +49,7 @@ struct ieee80211_meshconf_ie { uint8_t conf_syncid; /* Sync. Protocol ID */ uint8_t conf_authid; /* Auth. Protocol ID */ uint8_t conf_form; /* Formation Information */ - uint8_t conf_cap; + uint16_t conf_cap; } __packed; /* Hybrid Wireless Mesh Protocol */ @@ -72,7 +72,8 @@ struct ieee80211_meshconf_ie { #define IEEE80211_MESHCONF_CAP_FWRD 0x08 /* forwarding enabled */ #define IEEE80211_MESHCONF_CAP_BTR 0x10 /* Beacon Timing Report Enab */ #define IEEE80211_MESHCONF_CAP_TBTTA 0x20 /* TBTT Adj. Enabled */ -#define IEEE80211_MESHCONF_CAP_PSL 0x40 /* Power Save Level */ +#define IEEE80211_MESHCONF_CAP_TBTT 0x40 /* TBTT Adjusting */ +#define IEEE80211_MESHCONF_CAP_PSL 0x80 /* Power Save Level */ /* Mesh Identifier */ struct ieee80211_meshid_ie { diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_node.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_node.c index 514e666529..1cbff9618b 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_node.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_node.c @@ -282,10 +282,7 @@ ieee80211_node_set_chan(struct ieee80211_node *ni, mode = ieee80211_chan2mode(chan); if (IEEE80211_IS_CHAN_HT(chan)) { /* - * XXX Gotta be careful here; the rate set returned by - * ieee80211_get_suprates is actually any HT rate - * set so blindly copying it will be bad. We must - * install the legacy rate est in ni_rates and the + * We must install the legacy rate est in ni_rates and the * HT rate set in ni_htrates. */ ni->ni_htrates = *ieee80211_get_suphtrates(ic, chan); @@ -1085,7 +1082,26 @@ static void node_getmimoinfo(const struct ieee80211_node *ni, struct ieee80211_mimo_info *info) { - /* XXX zero data? */ + int i; + uint32_t avgrssi; + int32_t rssi; + + bzero(info, sizeof(*info)); + + for (i = 0; i < ni->ni_mimo_chains; i++) { + avgrssi = ni->ni_mimo_rssi_ctl[i]; + if (avgrssi == IEEE80211_RSSI_DUMMY_MARKER) { + info->rssi[i] = 0; + } else { + rssi = IEEE80211_RSSI_GET(avgrssi); + info->rssi[i] = rssi < 0 ? 0 : rssi > 127 ? 127 : rssi; + } + info->noise[i] = ni->ni_mimo_noise_ctl[i]; + } + + /* XXX ext radios? */ + + /* XXX EVM? */ } struct ieee80211_node * diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_node.h b/src/libs/compat/freebsd_wlan/net80211/ieee80211_node.h index 8fae8d6104..68167a0e05 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_node.h +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_node.h @@ -166,6 +166,13 @@ struct ieee80211_node { uint32_t ni_avgrssi; /* recv ssi state */ int8_t ni_noise; /* noise floor */ + /* mimo statistics */ + uint32_t ni_mimo_rssi_ctl[IEEE80211_MAX_CHAINS]; + uint32_t ni_mimo_rssi_ext[IEEE80211_MAX_CHAINS]; + uint8_t ni_mimo_noise_ctl[IEEE80211_MAX_CHAINS]; + uint8_t ni_mimo_noise_ext[IEEE80211_MAX_CHAINS]; + uint8_t ni_mimo_chains; + /* header */ uint8_t ni_macaddr[IEEE80211_ADDR_LEN]; uint8_t ni_bssid[IEEE80211_ADDR_LEN]; diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_output.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_output.c index 874ceb8bb8..bcd3c2bdac 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_output.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_output.c @@ -57,8 +57,11 @@ __FBSDID("$FreeBSD$"); #include #include -#ifdef INET +#if defined(INET) || defined(INET6) #include +#endif + +#ifdef INET #include #include #include @@ -67,6 +70,8 @@ __FBSDID("$FreeBSD$"); #include #endif +#include + #define ETHER_HEADER_COPY(dst, src) \ memcpy(dst, src, sizeof(struct ether_header)) @@ -396,14 +401,101 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m, 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, ro); +#ifdef MAC + error = mac_ifnet_check_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 */ + } else if (vap->iv_state == IEEE80211_S_SCAN) + senderr(EIO); + /* XXX bypass bridge, pfil, carp, etc. */ - return vap->iv_output(ifp, m, dst, ro); + 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 */ + + ifp->if_opackets++; + IEEE80211_NODE_STAT(ni, tx_data); + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { + IEEE80211_NODE_STAT(ni, tx_mcast); + m->m_flags |= M_MCAST; + } else + IEEE80211_NODE_STAT(ni, tx_ucast); + /* NB: ieee80211_encap does not include 802.11 header */ + IEEE80211_NODE_STAT_ADD(ni, tx_bytes, m->m_pkthdr.len); + + /* + * 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); ifp->if_oerrors++; - return error; #undef senderr } @@ -424,6 +516,7 @@ ieee80211_send_setup( { #define WH4(wh) ((struct ieee80211_frame_addr4 *)wh) struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_tx_ampdu *tap; struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); ieee80211_seq seqno; @@ -491,9 +584,15 @@ ieee80211_send_setup( } *(uint16_t *)&wh->i_dur[0] = 0; - seqno = ni->ni_txseqs[tid]++; - *(uint16_t *)&wh->i_seq[0] = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); - M_SEQNO_SET(m, seqno); + tap = &ni->ni_tx_ampdu[TID_TO_WME_AC(tid)]; + if (tid != IEEE80211_NONQOS_TID && IEEE80211_AMPDU_RUNNING(tap)) + m->m_flags |= M_AMPDU_MPDU; + else { + seqno = ni->ni_txseqs[tid]++; + *(uint16_t *)&wh->i_seq[0] = + htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); + M_SEQNO_SET(m, seqno); + } if (IEEE80211_IS_MULTICAST(wh->i_addr1)) m->m_flags |= M_MCAST; @@ -2693,6 +2792,8 @@ ieee80211_beacon_update(struct ieee80211_node *ni, struct ieee80211com *ic = ni->ni_ic; int len_changed = 0; uint16_t capinfo; + struct ieee80211_frame *wh; + ieee80211_seq seqno; IEEE80211_LOCK(ic); /* @@ -2724,6 +2825,12 @@ ieee80211_beacon_update(struct ieee80211_node *ni, return 1; /* just assume length changed */ } + wh = mtod(m, struct ieee80211_frame *); + seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++; + *(uint16_t *)&wh->i_seq[0] = + htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); + M_SEQNO_SET(m, seqno); + /* XXX faster to recalculate entirely or just changes? */ capinfo = ieee80211_getcapinfo(vap, ni->ni_chan); *bo->bo_caps = htole16(capinfo); @@ -2834,13 +2941,13 @@ ieee80211_beacon_update(struct ieee80211_node *ni, bo->bo_tim_trailer += adjust; bo->bo_erp += adjust; bo->bo_htinfo += adjust; -#ifdef IEEE80211_SUPERG_SUPPORT +#ifdef IEEE80211_SUPPORT_SUPERG bo->bo_ath += adjust; #endif -#ifdef IEEE80211_TDMA_SUPPORT +#ifdef IEEE80211_SUPPORT_TDMA bo->bo_tdma += adjust; #endif -#ifdef IEEE80211_MESH_SUPPORT +#ifdef IEEE80211_SUPPORT_MESH bo->bo_meshconf += adjust; #endif bo->bo_appie += adjust; @@ -2888,13 +2995,13 @@ ieee80211_beacon_update(struct ieee80211_node *ni, bo->bo_erp += sizeof(*csa); bo->bo_htinfo += sizeof(*csa); bo->bo_wme += sizeof(*csa); -#ifdef IEEE80211_SUPERG_SUPPORT +#ifdef IEEE80211_SUPPORT_SUPERG bo->bo_ath += sizeof(*csa); #endif -#ifdef IEEE80211_TDMA_SUPPORT +#ifdef IEEE80211_SUPPORT_TDMA bo->bo_tdma += sizeof(*csa); #endif -#ifdef IEEE80211_MESH_SUPPORT +#ifdef IEEE80211_SUPPORT_MESH bo->bo_meshconf += sizeof(*csa); #endif bo->bo_appie += sizeof(*csa); diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_power.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_power.c index 89f93cf0ca..2b68f9022f 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_power.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_power.c @@ -100,7 +100,7 @@ ieee80211_power_vdetach(struct ieee80211vap *vap) void ieee80211_psq_init(struct ieee80211_psq *psq, const char *name) { - memset(psq, 0, sizeof(psq)); + memset(psq, 0, sizeof(*psq)); psq->psq_maxlen = IEEE80211_PS_MAX_QUEUE; IEEE80211_PSQ_INIT(psq, name); /* OS-dependent setup */ } diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_proto.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_proto.c index fa6b1684fe..f44c594111 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_proto.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_proto.c @@ -64,7 +64,7 @@ const char *ieee80211_mgt_subtype_name[] = { "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", "probe_req", "probe_resp", "reserved#6", "reserved#7", "beacon", "atim", "disassoc", "auth", - "deauth", "action", "reserved#14", "reserved#15" + "deauth", "action", "action_noack", "reserved#15" }; const char *ieee80211_ctl_subtype_name[] = { "reserved#0", "reserved#1", "reserved#2", "reserved#3", @@ -207,6 +207,21 @@ ieee80211_proto_vattach(struct ieee80211vap *vap) const struct ieee80211_rateset *rs = &ic->ic_sup_rates[i]; vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE; + + /* + * Setting the management rate to MCS 0 assumes that the + * BSS Basic rate set is empty and the BSS Basic MCS set + * is not. + * + * Since we're not checking this, default to the lowest + * defined rate for this mode. + * + * At least one 11n AP (DLINK DIR-825) is reported to drop + * some MCS management traffic (eg BA response frames.) + * + * See also: 9.6.0 of the 802.11n-2009 specification. + */ +#ifdef NOTYET if (i == IEEE80211_MODE_11NA || i == IEEE80211_MODE_11NG) { vap->iv_txparms[i].mgmtrate = 0 | IEEE80211_RATE_MCS; vap->iv_txparms[i].mcastrate = 0 | IEEE80211_RATE_MCS; @@ -216,6 +231,9 @@ ieee80211_proto_vattach(struct ieee80211vap *vap) vap->iv_txparms[i].mcastrate = rs->rs_rates[0] & IEEE80211_RATE_VAL; } +#endif + 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; } vap->iv_roaming = IEEE80211_ROAMING_AUTO; @@ -877,6 +895,15 @@ ieee80211_wme_initparams_locked(struct ieee80211vap *vap) if ((ic->ic_caps & IEEE80211_C_WME) == 0 || ic->ic_nrunning > 1) return; + /* + * Clear the wme cap_info field so a qoscount from a previous + * vap doesn't confuse later code which only parses the beacon + * field and updates hardware when said field changes. + * Otherwise the hardware is programmed with defaults, not what + * the beacon actually announces. + */ + wme->wme_wmeChanParams.cap_info = 0; + /* * Select mode; we can be called early in which case we * always use auto mode. We know we'll be called when @@ -1474,6 +1501,11 @@ ieee80211_csa_startswitch(struct ieee80211com *ic, ieee80211_notify_csa(ic, c, mode, count); } +/* + * Complete the channel switch by transitioning all CSA VAPs to RUN. + * This is called by both the completion and cancellation functions + * so each VAP is placed back in the RUN state and can thus transmit. + */ static void csa_completeswitch(struct ieee80211com *ic) { @@ -1491,15 +1523,27 @@ csa_completeswitch(struct ieee80211com *ic) * 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. + * + * Although this may not be completely correct, update the BSS channel + * for each VAP to the newly configured channel. The setcurchan sets + * the current operating channel for the interface (so the radio does + * switch over) but the VAP BSS isn't updated, leading to incorrectly + * reported information via ioctl. */ 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); + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if (vap->iv_state == IEEE80211_S_CSA) + vap->iv_bss->ni_chan = ic->ic_curchan; + csa_completeswitch(ic); } diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_proto.h b/src/libs/compat/freebsd_wlan/net80211/ieee80211_proto.h index 56544f98d5..2b325576ef 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_proto.h +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_proto.h @@ -63,9 +63,36 @@ void ieee80211_syncflag(struct ieee80211vap *, int flag); void ieee80211_syncflag_ht(struct ieee80211vap *, int flag); void ieee80211_syncflag_ext(struct ieee80211vap *, int flag); +#define IEEE80211_R_NF 0x0000001 /* global NF value valid */ +#define IEEE80211_R_RSSI 0x0000002 /* global RSSI value valid */ +#define IEEE80211_R_C_CHAIN 0x0000004 /* RX chain count valid */ +#define IEEE80211_R_C_NF 0x0000008 /* per-chain NF value valid */ +#define IEEE80211_R_C_RSSI 0x0000010 /* per-chain RSSI value valid */ +#define IEEE80211_R_C_EVM 0x0000020 /* per-chain EVM valid */ +#define IEEE80211_R_C_HT40 0x0000040 /* RX'ed packet is 40mhz, pilots 4,5 valid */ + +struct ieee80211_rx_stats { + uint32_t r_flags; /* IEEE80211_R_* flags */ + uint8_t c_chain; /* number of RX chains involved */ + int16_t c_nf_ctl[IEEE80211_MAX_CHAINS]; /* per-chain NF */ + int16_t c_nf_ext[IEEE80211_MAX_CHAINS]; /* per-chain NF */ + int16_t c_rssi_ctl[IEEE80211_MAX_CHAINS]; /* per-chain RSSI */ + int16_t c_rssi_ext[IEEE80211_MAX_CHAINS]; /* per-chain RSSI */ + uint8_t nf; /* global NF */ + uint8_t rssi; /* global RSSI */ + uint8_t evm[IEEE80211_MAX_CHAINS][IEEE80211_MAX_EVM_PILOTS]; + /* per-chain, per-pilot EVM values */ +}; + #define ieee80211_input(ni, m, rssi, nf) \ ((ni)->ni_vap->iv_input(ni, m, rssi, nf)) int ieee80211_input_all(struct ieee80211com *, struct mbuf *, int, int); + +int ieee80211_input_mimo(struct ieee80211_node *, struct mbuf *, + struct ieee80211_rx_stats *); +int ieee80211_input_mimo_all(struct ieee80211com *, struct mbuf *, + struct ieee80211_rx_stats *); + struct ieee80211_bpf_params; int ieee80211_mgmt_output(struct ieee80211_node *, struct mbuf *, int, struct ieee80211_bpf_params *); diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_scan.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_scan.c index db2802a399..68a0ea4542 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_scan.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_scan.c @@ -424,6 +424,8 @@ start_scan_locked(const struct ieee80211_scanner *scan, vap->iv_stats.is_scan_passive++; if (flags & IEEE80211_SCAN_FLUSH) ss->ss_ops->scan_flush(ss); + if (flags & IEEE80211_SCAN_BGSCAN) + ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN; /* NB: flush frames rx'd before 1st channel change */ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; @@ -440,12 +442,13 @@ start_scan_locked(const struct ieee80211_scanner *scan, ic->ic_flags |= IEEE80211_F_SCAN; ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); } + return 1; } else { 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); + return 0; } /* diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_scan_sta.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_scan_sta.c index 3c1ed811d6..11bd3f56c0 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_scan_sta.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_scan_sta.c @@ -238,6 +238,7 @@ sta_add(struct ieee80211_scan_state *ss, const uint8_t *macaddr = wh->i_addr2; struct ieee80211vap *vap = ss->ss_vap; struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_channel *c; struct sta_entry *se; struct ieee80211_scan_entry *ise; int hash; @@ -300,7 +301,6 @@ found: * association on the wrong channel. */ 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. We know @@ -317,6 +317,14 @@ found: } } else ise->se_chan = ic->ic_curchan; + if (IEEE80211_IS_CHAN_HT(ise->se_chan) && sp->htcap == NULL) { + /* Demote legacy networks to a non-HT channel. */ + c = ieee80211_find_channel(ic, ise->se_chan->ic_freq, + ise->se_chan->ic_flags & ~IEEE80211_CHAN_HT); + KASSERT(c != NULL, + ("no legacy channel %u", ise->se_chan->ic_ieee)); + ise->se_chan = c; + } ise->se_fhdwell = sp->fhdwell; ise->se_fhindex = sp->fhindex; ise->se_erp = sp->erp; @@ -756,26 +764,38 @@ maxrate(const struct ieee80211_scan_entry *se) { const struct ieee80211_ie_htcap *htcap = (const struct ieee80211_ie_htcap *) se->se_ies.htcap_ie; - int rmax, r, i; + int rmax, r, i, txstream; uint16_t caps; + uint8_t txparams; 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. + * rate by channel width. */ - /* XXX assumes MCS15 is max */ - for (i = 15; i >= 0 && isclr(htcap->hc_mcsset, i); i--) - ; + txparams = htcap->hc_mcsset[12]; + if (txparams & 0x3) { + /* + * TX MCS parameters defined and not equal to RX, + * extract the number of spartial streams and + * map it to the highest MCS rate. + */ + txstream = ((txparams & 0xc) >> 2) + 1; + i = txstream * 8 - 1; + } else + for (i = 31; 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) + if ((caps & IEEE80211_HTCAP_CHWIDTH40) && + (caps & IEEE80211_HTCAP_SHORTGI40)) rmax = ieee80211_htrates[i].ht40_rate_400ns; - else + else if (caps & IEEE80211_HTCAP_CHWIDTH40) rmax = ieee80211_htrates[i].ht40_rate_800ns; + else if (caps & IEEE80211_HTCAP_SHORTGI20) + rmax = ieee80211_htrates[i].ht20_rate_400ns; + else + rmax = ieee80211_htrates[i].ht20_rate_800ns; } } for (i = 0; i < se->se_rates[1]; i++) { diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_sta.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_sta.c index f377d86e6d..5444459a15 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_sta.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_sta.c @@ -434,7 +434,7 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) goto invalid; break; case IEEE80211_S_SLEEP: - ieee80211_sta_pwrsave(vap, 0); + ieee80211_sta_pwrsave(vap, 1); break; default: invalid: @@ -512,7 +512,6 @@ doprint(struct ieee80211vap *vap, int subtype) static int sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) { -#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; @@ -591,9 +590,7 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) 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])) { + if (! ieee80211_check_rxseq(ni, wh)) { /* duplicate, discard */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, bssid, "duplicate", @@ -910,7 +907,6 @@ out: m_freem(m); } return type; -#undef SEQ_LEQ } static void @@ -1544,7 +1540,7 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, htcap = frm; } else if (ishtinfooui(frm)) { if (htinfo == NULL) - htcap = frm; + htinfo = frm; } } /* XXX Atheros OUI support */ @@ -1718,21 +1714,35 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, } case IEEE80211_FC0_SUBTYPE_ACTION: - if (vap->iv_state == IEEE80211_S_RUN) { - if (ieee80211_parse_action(ni, m0) == 0) - ic->ic_recv_action(ni, wh, frm, efrm); - } else + case IEEE80211_FC0_SUBTYPE_ACTION_NOACK: + if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1) && + !IEEE80211_IS_MULTICAST(wh->i_addr1)) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "not for us"); vap->iv_stats.is_rx_mgtdiscard++; + } else if (vap->iv_state != IEEE80211_S_RUN) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "wrong state %s", + ieee80211_state_name[vap->iv_state]); + vap->iv_stats.is_rx_mgtdiscard++; + } else { + if (ieee80211_parse_action(ni, m0) == 0) + (void)ic->ic_recv_action(ni, wh, frm, efrm); + } break; - case IEEE80211_FC0_SUBTYPE_PROBE_REQ: case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + case IEEE80211_FC0_SUBTYPE_ATIM: + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "not handled"); vap->iv_stats.is_rx_mgtdiscard++; - return; + break; + default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, - wh, "mgt", "subtype 0x%x not handled", subtype); + wh, "mgt", "subtype 0x%x not handled", subtype); vap->iv_stats.is_rx_badsubtype++; break; } @@ -1741,6 +1751,11 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, } static void -sta_recv_ctl(struct ieee80211_node *ni, struct mbuf *m0, int subtype) +sta_recv_ctl(struct ieee80211_node *ni, struct mbuf *m, int subtype) { + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_BAR: + ieee80211_recv_bar(ni, m); + break; + } } diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_var.h b/src/libs/compat/freebsd_wlan/net80211/ieee80211_var.h index 6848e0524b..93db5b313e 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_var.h +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_var.h @@ -140,6 +140,7 @@ struct ieee80211com { uint32_t ic_flags_ven; /* vendor state flags */ uint32_t ic_caps; /* capabilities */ uint32_t ic_htcaps; /* HT capabilities */ + uint32_t ic_htextcaps; /* HT extended capabilities */ uint32_t ic_cryptocaps; /* crypto capabilities */ uint8_t ic_modecaps[2]; /* set of mode capabilities */ uint8_t ic_promisc; /* vap's needing promisc mode */ @@ -192,8 +193,8 @@ struct ieee80211com { struct ieee80211_dfs_state ic_dfs; /* DFS state */ struct ieee80211_scan_state *ic_scan; /* scan state */ - long long ic_lastdata; /* time of last data frame */ - long long ic_lastscan; /* time last scan completed */ + int ic_lastdata; /* time of last data frame */ + int ic_lastscan; /* time last scan completed */ /* NB: this is the union of all vap stations/neighbors */ int ic_max_keyix; /* max h/w key index */ @@ -213,8 +214,10 @@ struct ieee80211com { uint16_t ic_ht40_sta_assoc;/* HT40 stations associated */ uint8_t ic_curhtprotmode;/* HTINFO bss state */ enum ieee80211_protmode ic_htprotmode; /* HT protection mode */ - long long ic_lastnonerp; /* last time non-ERP sta noted*/ - long long ic_lastnonht; /* last time non-HT sta noted */ + int ic_lastnonerp; /* last time non-ERP sta noted*/ + int ic_lastnonht; /* last time non-HT sta noted */ + uint8_t ic_rxstream; /* # RX streams */ + uint8_t ic_txstream; /* # TX streams */ /* optional state for Atheros SuperG protocol extensions */ struct ieee80211_superg *ic_superg; @@ -307,6 +310,8 @@ struct ieee80211com { int status, int baparamset, int batimeout); void (*ic_addba_stop)(struct ieee80211_node *, struct ieee80211_tx_ampdu *); + void (*ic_addba_response_timeout)(struct ieee80211_node *, + struct ieee80211_tx_ampdu *); /* BAR response received */ void (*ic_bar_response)(struct ieee80211_node *, struct ieee80211_tx_ampdu *, int status); @@ -316,7 +321,7 @@ struct ieee80211com { int batimeout, int baseqctl); void (*ic_ampdu_rx_stop)(struct ieee80211_node *, struct ieee80211_rx_ampdu *); - uint64_t ic_spare[8]; + uint64_t ic_spare[7]; }; struct ieee80211_aclator; @@ -343,6 +348,7 @@ struct ieee80211vap { uint32_t iv_flags_ven; /* vendor state flags */ uint32_t iv_caps; /* capabilities */ uint32_t iv_htcaps; /* HT capabilities */ + uint32_t iv_htextcaps; /* HT extended capabilities */ enum ieee80211_opmode iv_opmode; /* operation mode */ enum ieee80211_state iv_state; /* state machine state */ enum ieee80211_state iv_nstate; /* pending state */ @@ -576,7 +582,7 @@ struct ieee80211vap { #define IEEE80211_FHT_BITS \ "\20\1NONHT_PR" \ - "\23GF\24HT\25AMDPU_TX\26AMPDU_TX" \ + "\23GF\24HT\25AMPDU_TX\26AMPDU_TX" \ "\27AMSDU_TX\30AMSDU_RX\31USEHT40\32PUREN\33SHORTGI20\34SHORTGI40" \ "\35HTCOMPAT\36RIFS\37STBC_TX\40STBC_RX" @@ -635,6 +641,10 @@ struct ieee80211vap { #define IEEE80211_HTC_HT 0x00040000 /* CAPABILITY: HT operation */ #define IEEE80211_HTC_SMPS 0x00080000 /* CAPABILITY: MIMO power save*/ #define IEEE80211_HTC_RIFS 0x00100000 /* CAPABILITY: RIFS support */ +#define IEEE80211_HTC_RXUNEQUAL 0x00200000 /* CAPABILITY: RX unequal MCS */ +#define IEEE80211_HTC_RXMCS32 0x00400000 /* CAPABILITY: MCS32 support */ +#define IEEE80211_HTC_TXUNEQUAL 0x00800000 /* CAPABILITY: TX unequal MCS */ +#define IEEE80211_HTC_TXMCS32 0x01000000 /* CAPABILITY: MCS32 suport */ #define IEEE80211_C_HTCAP_BITS \ "\20\1LDPC\2CHWIDTH40\5GREENFIELD\6SHORTGI20\7SHORTGI40\10TXSTBC" \ diff --git a/src/libs/compat/freebsd_wlan/net80211/ieee80211_wds.c b/src/libs/compat/freebsd_wlan/net80211/ieee80211_wds.c index 15518d8181..ed82fac1c0 100644 --- a/src/libs/compat/freebsd_wlan/net80211/ieee80211_wds.c +++ b/src/libs/compat/freebsd_wlan/net80211/ieee80211_wds.c @@ -406,7 +406,6 @@ wds_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) static int wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) { -#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; @@ -454,6 +453,9 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) */ wh = mtod(m, struct ieee80211_frame *); + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) + ni->ni_inact = ni->ni_inact_reload; + if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != IEEE80211_FC0_VERSION_0) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, @@ -492,9 +494,7 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) 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])) { + if (! ieee80211_check_rxseq(ni, wh)) { /* duplicate, discard */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr1, "duplicate", @@ -536,8 +536,6 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) 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. If the frame is to be * processed directly then ieee80211_ampdu_reorder @@ -740,7 +738,6 @@ out: m_freem(m); } return type; -#undef SEQ_LEQ } static void @@ -756,31 +753,47 @@ wds_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 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_ACTION: + case IEEE80211_FC0_SUBTYPE_ACTION_NOACK: + if (ni == vap->iv_bss) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "unknown node"); + vap->iv_stats.is_rx_mgtdiscard++; + } else if (!IEEE80211_ADDR_EQ(vap->iv_myaddr, wh->i_addr1)) { + /* NB: not interested in multicast frames. */ + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "not for us"); + vap->iv_stats.is_rx_mgtdiscard++; + } else if (vap->iv_state != IEEE80211_S_RUN) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "wrong state %s", + ieee80211_state_name[vap->iv_state]); + vap->iv_stats.is_rx_mgtdiscard++; + } else { + if (ieee80211_parse_action(ni, m0) == 0) + (void)ic->ic_recv_action(ni, wh, frm, efrm); + } + break; + + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: 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_ATIM: case IEEE80211_FC0_SUBTYPE_DISASSOC: + case IEEE80211_FC0_SUBTYPE_AUTH: + case IEEE80211_FC0_SUBTYPE_DEAUTH: + IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, + wh, NULL, "%s", "not handled"); 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, wh, frm, efrm); - break; + default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, - wh, "mgt", "subtype 0x%x not handled", subtype); + wh, "mgt", "subtype 0x%x not handled", subtype); vap->iv_stats.is_rx_badsubtype++; break; }