From 5cbe5a3ce8d4ba9dfaac94d86ab2e3193885ae2e Mon Sep 17 00:00:00 2001 From: Aji Gerdiman Date: Thu, 9 Jan 2025 08:50:52 +1100 Subject: [PATCH] version 2.13.0.4 --- Firmware/pmemdata_wlan_bt_9117 | Bin 315008 -> 323200 bytes LICENSE | 0 Makefile | 6 +- README.md | 83 +++- apps/Makefile | 3 + apps/ble_transmit.c | 11 +- apps/bt_util.c | 48 +- apps/cmfg/cmfg.h | 109 +++++ apps/cmfg/cmfg_main.c | 65 +++ apps/cmfg/cmfg_netlink.c | 112 +++++ apps/cmfg/cmfg_routine.c | 129 ++++++ apps/cmfg/cmfg_socket.c | 280 ++++++++++++ apps/matlab_utils.c | 22 +- apps/onebox_util.c | 130 +++++- apps/per_util.h | 32 +- apps/transmit.c | 67 ++- apps/update_bt_ble_gain_table.c | 11 +- apps/update_wlan_gain_table.c | 4 - docs/release-notes/index.md | 695 +++++++++++++++++++---------- {rsi => release}/Automation_README | 20 +- release/osd_common_insert.sh | 39 +- rsi/README | 286 ------------ rsi/rsi_91x_debugfs.c | 5 +- rsi/rsi_91x_hal.c | 22 +- rsi/rsi_91x_hci.c | 48 +- rsi/rsi_91x_he.c | 1 + rsi/rsi_91x_mac80211.c | 23 +- rsi/rsi_91x_main.c | 33 +- rsi/rsi_91x_mgmt.c | 322 +++++++++++-- rsi/rsi_91x_nlsock.c | 254 ++++++++++- rsi/rsi_91x_per.c | 8 +- rsi/rsi_91x_ps.c | 7 + rsi/rsi_91x_sdio.c | 9 + rsi/rsi_common.h | 2 + rsi/rsi_debugfs.h | 2 + rsi/rsi_hci.h | 6 + rsi/rsi_main.h | 93 +++- rsi/rsi_mgmt.h | 11 +- rsi/rsi_ps.h | 1 + rsi/rsi_rrm.h | 2 + rsi/rsi_usb.h | 6 + rsi/rsi_zigb.h | 6 + 42 files changed, 2353 insertions(+), 660 deletions(-) mode change 100644 => 100755 LICENSE create mode 100755 apps/cmfg/cmfg.h create mode 100755 apps/cmfg/cmfg_main.c create mode 100755 apps/cmfg/cmfg_netlink.c create mode 100755 apps/cmfg/cmfg_routine.c create mode 100755 apps/cmfg/cmfg_socket.c mode change 100644 => 100755 docs/release-notes/index.md rename {rsi => release}/Automation_README (54%) delete mode 100755 rsi/README diff --git a/Firmware/pmemdata_wlan_bt_9117 b/Firmware/pmemdata_wlan_bt_9117 index b374fb235f0a52dd32137c76ecd815d2fbfa8da9..ec25410c71914bbcfb5357f466719d9e463854e6 100755 GIT binary patch delta 79313 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

p#DY^<) zUidnwX`TIY>N?>8|Aq@ZSor}#M?ELJ%U|X6Th!fRCI*|{wk3+6{_@E-QQ9Yw4*N@}P!akFt{ zF44wGW42c{S5W_xT*N1zR!J?f9No1I7Va$^ZMxC) z_W@;_*b2z*t{S9OAZ@Yf^rg&!o{Jzz#u+?6EsHEF02D|i>b~7n{!!oO{zKk~I{PP6 zKP{?QTvv2)>L&>B6^_>GE=K(Q)54y7`$cRJtdl`qhjFc~zDPH`Ep&4X5l`)J3s`=} ziY^_8%BG5q55&+W9gup7V}|n??CQBDU7>O}O?^kGYl019T0iq$Ai%W?OBO)ojo)4q z*Kmj+9+U^yLwqeocB7X#i+NDAJ;O2B2qGY=%(d&d{`{c@hrFLP!N3>Sc5&xtcF*&R zY0iMQ?$VCWisziIU0ZXm);<@$GLU%a5yv4q_p5NaaE3fY?1M(Ppdr_bV}s}}2ZNh4umxF0Daq6_5=4YpV+0w+>FrWXn=ZkxU zQ~X2(9J&NJqh5O^v_Z5GnT9z?9CJ0)xz_VP7x z`r0z64q_I^!}lncZWg9TpO2v_*k&?%ZX(b-B?GSG^X11k3+dyRQq5H(20{kSNr}Wu zjivVd$f2b)#}yG>)$_?_;aQ#po|ZfO!c=mrQ&#-KhKQf10?y@gUWHu|mx=bQxnmi2APn!T>S}Dey(RN# zV^7l-;SYrO$?jI+t~hKv4f~&mZ3i{wAPeIQ2+}(Fc&lKKHl=O3Tgo~!9%$%rJN90P zXp{$Ag~V}dq-;}K0OXMH;-Vx+-nqQuo4>eu(cMQk%C@b-INmBtTZKuh*0twJ+?j-R z8i{4KaU?722nVF*L_~{|x=%#}5_IYoJ5s92a=9Wolg6f)54fm1)ttpk%VCl0WZ=*N zThsfcf|3^g467upymi#gT7RU9x$+xZg}4YrrGj8qv*DuZ4*7Dc@HhUr{9&7LkaS&^ zw{8wO+aD0xSdeM# zcHw98_y>i_V0=e*FVJ^J!2sQI95C0fA9Z0d>Y`2VyQHrgPj#^@xR=I1NrsZwZu;V0)5blU%^L^rm69K_=ve2=c?X@$ zG~af2W~DMN(nsf=h+}WJ%R=pVW**Hw5!YUf_fI?=MsvDJUzI(-JPBl5ylJEC*e8s) zT5A$%>fQuKdrNcoC73p@^{(Ay-Y9$b39i(fCkduc8_i9LH>#Vpuv>>csh*A)#*nLw zd2Mc&ul`Q?#eKp=W9mpWrd7VUPss4+rY0$4GJHDEv_gt}@VR`^oSZ~K8m;JkIaiZ| zJ9)xB=1dcm?d10jG`BTsG%yrFRj9cRtQfnBADVg;TXAEj>^RKGb~>*$S}B#{fNh5- zGM}Tl0|~67V490sN6t15Rcb7k{O20wow5)Uxj&{M#z}gG%WlL7MEz&{l@&~eD&B3>X5ghH6XvkvBRMq{Mjm{1Ppt14a@Z%pa4D>EaCVU;la%XxNlSG{`oW|R7h?$i6Iup+T2 zfPKGuRU{HHNtoX1?^AR%rclE&#BsP>_%k6HF!?-l2G+fCa^ug0d}4a6r~79@IG-8{ z>_WlGZ^w!yI9rAlG5gI}wDeT!)*z<7ES+)8T>80SV+17nUkDTZLIlt*O{{$)i$|2xm5C*20nEH*bLjEh3^W zkFC?}%Xg%r7id2nLxoG=E&}({WHsXk^j$5xatM$W_Ir9de2Xb&ZrH;j`l#@}nwk!> z-p|yd1-H=TuOQBKRTytUG+n{bin@^}aF%kkLUi80UUd1{dl!pJLU8vJPJ1VOO6~;i zG>aOrx@WldA zNTU&st+r}kb^98JT7go&eGR%Q!`P|MB%pxm!A-^Jhq@&8hQU@sXxns7UcFyP^v7;U zpzedQChUaM%1MT8vql5mz(kV{AMhD5sseHof)ClUl5#NCVK90RLwutpZ}O>~9Z{xQ zDf<@&&=3In42*?482hn$A4?yHbh|)fUrJ;y@GOHe#gwK@3gs0OJuk6iMi|v#9&#e; zJ62A3Oc;NgdqaW}f#oMc#|^Du^qlC3rs-JIc zSCyi1NUtdc# z2NQfb;5Tw3YS09NmqZf+`rzhF%;IV4x0=3r<-Ezum|t1&TIfqMvZG6WMBJ33vu?No zy^ZD~Sl4@@Z17r)rC-tR(~ZJng1^7gbLw&7Rs^S-5=vA?QdfOU$b!KJ#FO$~UPAhB=7nd}E9F|B8DTxTwnffBf8Ln1O*A z7;frO2Y~?;W4H`>nQ{;W%u!KLtUa3p3>OU*0T(N`85D1YWkQ~6tuW0?k!eu0`*H%gYze%yXXSInVX;e4fwc{YeF2 zS&d+QiBjw8ve0sgcN}ELq{Ek|{{b73`n zKr%QAK$DAi(zf`XDG;2>6Q*}0-z&j41%#^5*IK6TcR2CP`S1p>f-*>P!*$do*9=rk zR?@C;>Nd!C%61)f)Sg*2Q0|S6!iYOxBBxT`JiTKS9+?XxoqpQX2DQhw^W?eKF)&BS z45cyQljPDXcpq$_c{U%@L>a{o55YthMY2YU5YEa11^dW9!TeCrz*VpxO{FqeB9=kd3vE-C`mf1vxP0LAj%QfMct5^!0=%p9aUJI%arFrRA6qJ(euj+;x$iq!=nUXW z&fD^x_}w$C`A2C7z7wZD%T91RrH5Rt4$qa+j*oQCLY-iNL9Q{I77X)Qp&hIScS)@) zKMkk^j1sm87pJS?O_&;}fz+npL^ynoLkn25Gq@V-0)F-|Q!i`@B76^s6vAhyLG>^* za^GRho2;u1bHQCBnAAW8ySPP;I27FJ$ax^9gk9G5DHKxd-kWZ zuszJkJEvuRGAO4j(?R3P+QE*Wl4<7TTGjylaW+ru5n}Gl*cL#=hgKVdT zC2wgDCaZzB*UCtc`WE<4DS8saLHI1^{+5i-8Z=4!P>*kx?PIyj}A< zl1<3j3iT?M5i$x!(ui+8q{?@YGefTO>r&pn|7DU_4-H|p=zflkh`#ze`E6yq9o#Od zCci2Q_{wIn<2iN={e*bxIX0Cxi{C!S#?oU&{s_BlTH9;n9c!>9*1t*$FWi6vO{DOH zdw4_bCAkGr2RWc8$egQ?b(%wtC%QY1u!T&x@0`f2(w}-#$TIFZ=oX z4^VkGe7~ITd#BiQ42E`@KZqY5V{<3m`-88;Q27i?wYnbYGn2_6>Ssd5EM~vJ=0tii z$1o(sGW+Z_QTr`~Qbp_F|<|a-Ia# zV|Y!;Q>6j?_i?43t;iF8Qps}(Z5959+$Z<4ktMqx!7tHhEZIk~XG-x!E~%L2=@Z#SmZgW%S7N4?CxNxKa&hBnC3bW3w6 zL76Jfgp^T+E^2fpI3WqmI84L`Q{a_=b2uE>6}x0CQGzH6Kf2j`D$;6f>>nUvYm2U_ zawJ!dP*df4A;j&CAW{Gf}yo;Cb;!W-E5pSH7`Oq6gNlA4M@5}Q=$;-ZKn6?MHggY(J5y!68dY?%VA9{to zjVbICFTVoC2|hKivPm!z)5GB0sq-AI1t^h9XvS5}g5$0j!t2&%_4X8UX+!WtXJ1?h zIZT+pB2Fq|VF!8TM5VrF< zh~qhreATIu_PO>E3C_n51w_VVD3#LDFhK zJ(5dxJcL^r3GvbtapP<3teZdSSP#BeKA)8)3j1(+-=|tjRC;sqz6vbz{#2#hB6T3f=*dsJE>{&LMVZ?&h*;e-Z07m@ab+&@t2G^UsH&~0xr2z>L?|FkgX_gEFSl0@W zwPesa_J!F}xq$0w7)c|1ju*elZlj|ce5X7*pHYSJ+8Ue=LR0gSIPeGdqo8PrL{#L~ z;SCo*IK!SHUJM;?v14!Yv2eF%`Am;Tv3crzs5d#vduH*7S+ZOv)K>-pYRGx#<)0HL zzRj*P{Z-~gpozD9CV#{Am`b3*i(5Psy=gi|?0OqK>ech&Ki-CQCO+?dhfRd(h308K zMimFiKu$}dlu=g;P-Puq!{C;AkPpR^6TGSUpq%Fr{KOCPsYuhQFY;@nDSf~14w49> z&`byo_?%i#v&^+zmW(<8igFwQX2x4kPTyiny9)aV@J%3M2VJ=N!;%c~v3PsG4j#O- z`Pw&o__Z|US;$*1`)rC^BWynZJL8#@%sN3}&V&6OL-vb`gG2RUrq4&?S$5`^E&EvyLX{`$sAWux}W%*kV1PLiH zF~G(f)(aUg1qrk2skCZ>?qN&o$QCkA1r5B(+jF-9sVPo}uuIl4M zOLLfDJQ3A9Syvh0j0nif_8z!htT+!7lbx;IkDq53aB?V!uij_JF=3%%&>z{G*e+O8 z*#5{KX1qTgb-!+(oT5?kbsk8&PO)F5Ys3F|-M)SOvs$jL30db_WuXz?F z-Bz;=L4y79EkTlpZ>DHzpKaQe zVF>&->7Zr}0L~6gxmWRUzlkNGY#Z}PDB2x7io{OfrY;Yvf&8)({2M#p{fF>0%@Cqx zeZ;I;US--lOtb!>^LhjH4zY+TpjhGND za8I+O&c+I0w!utZ#ceMU+;Km^zbwd+Hn(LhM2VUQ*Ghp9|H^XDtU18qU39{CeRkYc80iZt5snj=NvO|%KE)YaEJLVFn%|v|(O};~K!oFLaIz5hEzfaTX15FxXgKI!zIcAS8QYm@2B5#wN zSIEGtJNXwFl&C^#DN-Nz)sT-GvgI0tI5Y=b33O`{W03NkTdp(ZiFeoUhqYv+PmbUSt!~BJ_IkxXc{S6 zg`%Y>ir0p3my348QX(5AcjzQtkTcC?GhU_C_JIT}aGW3+h~ss-9E1 z#)ZiPMpJSVsG?8U!;AOy!S9ePvbF$X0#M`NCwZAQ4#A9q$iBH|VKt887g)-6|GZG! zl$K>=pZrPQCqV!x;4>&^+A1L*T6pDf)E(G+#*yD1AA36BJ zg~}el*b;t6O)mSmQR|?V7^on7*2|m}$NU+Byjb!JR!r48Mk!*Z1}Y_1$iPM{L%QxH zB^2X3j;a0o$O zr8M!-2kfl5aORCfJR|Sb1D14F&5`Vps#a!;ouG$Dsvs+DN7%)pNI2v|nhhEjsp_P* z$WFYGDq$()AMI@!sMIW4K4cqAj^*SO7mQm$iUcP)xP;h)YUwrtLI*fRt$5-?wzOF5 za0)pmAZJhzykJ}YOOn;>bme(ZHPfS}{Q8V1pQOlK?Kb%!FlwbmI!{aP-0Dl^ZGqm~ zL%Z*~$R^SxXvn^g*qSsfi{55kLtRBZrE7rUOM|X~6dy6;hV*l53?D;lsZtx9S1_q! z_FpjSIL4R%g^i9x`2m#QRZhy+mHWzzJO0AvEnZ`}ITL>Wf*irNP1IDW!0|3(9T(~DhG1Bh)XKn(^F-6fY<3Aa%fxzx zA8&N_R_HeD+MwT1&kFGXTU3Ox>%d>D5NX&9ss9G(lQ)pN67k1=D}oI0ei`M@454gC zL(3|HD@c4o#XTRhOT;<-?452%1_*M%T34N5?+?YTQ(^xd{!3i~OlfUp05q3~@~mZ@ zL$0JFqhpaR4O*{B^WunpObuA1%bN*K33M5GE$eFJT)iiB;Y4M1c^z-SwM@RI^?u^7 zV>cjaxhzd&Ee6hs+f`;il%UYM*tux$z1alGG8*BggDSDdM)!pv7A_4DU+rgC!2YK@ z=@a$>?bg+*A@XC1X)wkRFo(p96|kBlJSYmUmj^=KA4afyYvUs0tV11M0D@I1~O zm`vDGq2+=NLvFC=41X3Y^}_`CG7sb?uWxh~zHIvyEaOOwIAG>Vc94dQnrT@v=({_c zL!&isBJezr?ZH8T18}+T*$teLTW$=%;|71CAn&rWo5(iv-Em7 zzK0?&2vRDMysoH~O13#3Bo%(9eam)9>`FO7wKJX{Kh0uTr(K>&MWXMUUx zCOM1?*(3*}4G1ZsF(L)Y>-7zeX(<^c_LOCdf?mF^mlESXW9N@m-t6m0@mRnsQS#ux zV(b3T*u{ptv5cG+ZcDM#Xp#8kXYBG|SP$JLX>3M2g$9VXea`M0kBkV=DVy#1rtI%v zJDX(C;HO2+U!k#jBSTF3D`Y?`(&^qgpsMkwgFA!xP1~uq+XZWXNN<&GUC(&zMWFqq ztFWx}1M6E=LWG1su{jw&iv}KqG(|-b<_N?!%Zt~7xLYqFVo!kHHa!@%_6P5a72+=i ze+-yoG8%Xi67LZJ*$bg^bbk(PJ;e|H%Epcd5z}+tN^*zif)VrA5DSq$;8&w837hK6vdRLWTO$aVQXf5980jA~_!0?7~2v}F8f&fj%!ie;4oQ)u+VoX%FcjY)nP`QG8)wXbD^h++Yi|J@ z`2+h4*gi?>l2JoKl)l7TUx;`yL=kqp5+X}{tV=>#t%#A#6M&a96u}2HicMd#nYWD8 zEU7<@A%hDLC^QlH)4|wVq`Cgy5&KhR`KOF@LHzrd>|{O3MR;M$7@WGzw}>}=#YVf+ z0r49boM*D7Dm`h9$NQ6kze>F&zrQn{bRXO>^Z}j#d90VBKXy`z7&Z<-Pe`oKq_IsE zWM~W^Nf>Q7(aMWrFkGPBA!^r~RAf>BEUO1$-JxPP5{)jz&(%l#V+ zFM&a7m9wo#h)4~#%?vgO@t|C3WU)hKQReTRr-Hz4)i@9f{afF-%aqUmMnY%7jdAT0zbRObA*2Q%E=Y^k%A<@%iEFpcOhe-)xrA z!hMePFdjG95h$Yv%<+3Ty)(LF97~3>cdirGfoXWy6((#J4S!>!0ygZb*j1_DAl~#h zHd-Z1|HOHJV`qtd1FS~e^*7e&-jHXt*&$k>YGw( zRW;~#sGL%P>0Q`Ft=iVH?u_sC&7o#$s%~%`PKnS9F_&{|bO25@i-tjVl38qW)RX4i zuxTS>CoUUg$J4y{t3fv1DD+Cyov`3WZ8a^_$_FE)gn#*B$I& zvtSaJ6xSGIS?_Dp*=B%dyX1%h(NsWb+jvQnTu#OYYi>URrbWgC;(#g+gD}l;CrOty zH6zaB64H*Z<*bbMDk#4ofCxsE1`HHLx ztVx)zlF-=R)x15qj?Xrj%+8QnDacVKsmOV#HicNE$u06iM_2^->|<9BApA+7va+Nx zbKP;>F+i!t-cNwdIgUuol>>p0Nes&_I3XSf=V)UhsUZY0DwUguai-yfV(E3EkGlXx zl?Fb20v~FW=|~@k2q|)H#bC6t-w-w^5Gw#9TSKS~#Sv;lP+CEPaXh(kHsieL<-l&3 zaoS}b3xNqEbXy*oRtTAdq>AI>qu|ND77_6nik*Cwyg%@ODvjw zlaw^K8r_*JTl8{K*$5*VESN^{!6!(`LK-$VlqHheAh6iorVhJc#O{v4?q;#O4avuc zFfM{|Gb0#x;O}AlJR2OxxDBtkdC&MM7KT*+CEDT`|(e{T_ak zA;HR$*hYdXHx}aQaoPa^^H3@EkTf}YrMw5Hc?i`{++z3B2}QXE331tX?B78MhQUWaB-P>a z!{&xznxy8?+UF3$H;Tkq!8{pZDNPH;6=a#$ zz1JnEzoQ3$;4XxZzO9iHflA{@;_=b4CD%Cyev=1kQv|%Fr1m+(Y-Dr zZ+kHW>;{~KB&+w4#8inV8>nq2Zr6&}NoJHsULI}(Dzix1URF&Nak}3CDRBZhMogBE zQ*Es7J00%MwM$lLHZBkdb^`YTD%K-(*FIF8C!){rOO%Gv&gI z`G(!}hT$VyW^Y-E)xl;k!9t3Y9RP(TD7p%gy!gHrg=4-eMX)FO6( z+O&$K01i;yUV{qF;9u-w!}qL6!X*X&0Q{RaUVQ!sb}ZW;#E55pfK~PTn?(IJHZ8w) z7yYYiONTj*!m+WXljzm8Kvlxh25dIw9eRjB*&Qhzh*tzLoS2UV$tfDdJCxZI#5#O2 zn%hph5xv!Kk)dl5sLzACDUfp)u~GNPpv4gN&xRaIE*^y7#ROp!zhV4DFo6jq4$6r;QU9LKcGyC2wKnQreb*jb^))y=iuqhp2FIMELxrv^604ZE#K42mJw! zS5k6;5XS%nliw?vx?~T)c3q)TeBFXp2*j@W^mi04eO2QY zBa72DkvG6ZE)>>~Fx2uDEPzV*duXjfUFjh%gmomKR=5vApzF7;VO@H$gXT)yIS*=! zqj~(_6~yOk)*?1^&VAZqJ)h&zwk@ou`J5ND#W8$Nm$ul#=N!_u&1nTb*$E7#>3P;>tOeEshx;3` z>)@TkdcJgccvGe=wL`Y}X0j*44G(~wfCxzwQWL|^22m>@d?xSJv}T+ z#JVM;7i}k_$D1}LKow5%r?ulINcHpZ#70$ksj{9;hLs@>RBSLz4oQYzV~dun3W=V| z5ZokU9`B8wD$;@6`e?LT>86zJtyZ4HaI<=<_)s9{G$}?pL@tOv5$tNUldvHkx}HWZ zIHZRLapuXso>RV_%W^ap@bpdU2LPQ|QO`=#Z0zH6y!}(f(jYFJ!DnL-H!b9d+B3+> z&z6v9uLN->^6bMP?oHckq{b)y!R;_$^R=;R845$uAoWy4DGKp1C%E$f9aZc3(d7LtP}*bzl}_y2~5|AOqa ztDK3-69t0LQbk6``wuAnry02u?rHCl-!SL9K(smDjl?uQ-4jxqM-wX$+&T{>GkKLE zwSS28_9Qdlel0D3BAZdK`$9=^f=BkH2&!1Xk0O@`;5H*^=}KBHZ+T=S?c-oM-%);+ zWzR_Bppv)~rm`bx`$FW5J-lWANLr_oRtUVXKZ#F7aj~j)-s1g}cy}acW_tc421elX z#Gk}B$?wtH@yGGIml{ED6<$vAB zGrm5amHRkVoEXK0{+B!IYn+|8bo{t&&KtDNbAz_klC~*JfkJi+nA#cy~<|--_nk zOv-!Wf-ziHAX!<6je#*09~{FaGI{dzC1S`}{J3$ZSaHu-ZV|IGS^Rn|H_09Ccl%H{ z42i`1qHwrj2as50(lUZm{=Lip+m_KxSn1zG)DXEV89@+yP9r!rz!-G=IvGwmQly%I z)@U{%px1Sh##;*!#aOM9_fYg1h`E(6Z9Dab^8&El!gnlLbXS_m<~EYDk`X! z8;pviX}Sl9?DlH4(zRNpYgSjDKgzjyk>{e5&*@;GbsmA0EiD>b`O#ZpuIgt&WUO3r z43nchkOZ8cFrLd2Qzmc;;)d~Dto$u{HwhJOf9z2{8{E9}k8F{KH{J0KwNCtCJhzi+ z)rhqdxM=aE37qJzm2fD7!wCh4lk1=K`#lmAMbs690n_u@;CSK)+Pex4T~nH*IO6Dt zIDW%CjfNlwEVvxlpZU2s5ZH+~;NW!FJ&G-?1GQe+6jyhv-cirM(hkWAdqpAHk`8us zB*lOf!e*3%V>W`Gj#NOSNqjuI>EH|BI>Rr5_}PAj1vgy&#E)|tkQ1n{KhF6+drrld zx09Uie^DvMZV|@L{zo<);*Dp%q2NUs4&Y^ek%k(WEiHyucn+BuG)ZYZc~L5FeTj^G zc^u%8`SRzrZJf_&kfM5cIsh?8cvFX5Lca}E8r{)^XX`ukiBRx9W#T3?8Sja2o4AS0 z*d+056IW;+o5a|ram;{A*)s4ykdIw`hb>cHlc6|>zDg1wkKx9No*2#;_^Op@g-X?X zF_R?DXPQ@wmt(kmg4JW4#Ql@}PK)I>FwQRVXe>9De4dHrOtYrRBu3l9Tc#`DYARtx zqoN>U(8m*alrSmId&f;op6rW)3%Qpfn9Jw!zsegoU7Qlf#ohXkX^77&8%a_>IX+i{ zM2#u~3CVr@F@veHQksGr*iUnBOco!Fq8{?;lz^;;&sqSb zkxxKd%AY%>Kzxc*;<=~P2FXs^_THEmNoQtQdkQ`y;Rj+d9R26EJDB@u~z4 zM!L1#?tLxUx&__6rxZI>Jhg{36A)q6bjWocAK^kvZ?nDF`y2O(9kkfu_Z^wf|X}iPBhL zV(-Ep07D6y9wJ+SO3+4X_9=<>#a^eJ&Fv%}si!(G3wpA7`RON-`wGbWPyx zy7!1bfwmCTG7iqqYTHB;V@m*Q2n)-N^pOrh&(dG%h|)FK(mIv&s;aDO zB5w~?YB(Cu-gmX+XEhvEYS5zwly#$uWFqPfF5I(1h0B?gb8r)>B;JsY8Fgj`PJotK%%G1966YEg4lwmQqQ;gH}{xxL!$f(a$Qm_^%o=NvY%X z4(oW{9(uiwxCK9|!%YVD$_*=N4I!hkwZ%G#w}ih@}J&j z#rlc3p8tH_&+=P-(p2|X$!KHYo@GiKw{9h`jJ#gOiQJ!6VVA2I{3m&3{uYuy>U#d% zoS)?{RPuj|fTT*kS|Yuit>kl$TH8*gZKKvUJPH9)#DQU=4v@{`U;l%F*He_q8$?83 zgaNBTK+Tc7%Cw|w$ll7IGWv-!%B^aVa!ZDQ$)g`B13Ji&LZ=f)KT(FRh$T<9CX9Zf zj7TSuImykGUuZ@Si_-UJTaIk|qT{PWU;`{lC!f_F(R`8oRiflJO3BdbYShq5bgYLS z@jqa{MF>Q03{3oz)+NA21yBLRB02Vc%38>qGW=YVT|c}jgSR@#;cRJ^g*<Na zfsJ9bkOu*Zc^WK_MG^9Bo&&*;VR#lF>i(Xb@OEhOvsr3i2B~UiGg-}ip`8|rmV!?J zm@69zk<SF(V~&$Z$L5rVfcrTkOG+RcmE|g}JXsS}E!O#?npucuS{43r6i?=F zup5yve$JHfCX2t9g=pm>G}5Yu{s2w1jB26*O*AfL1>>mZC8K$eA)(as6j?A+G1DN{1+MK$z zq#I~}0S!Tj8NoEFAs8#S(h?&1^vxx>L5?Cwnp@=gR#PFm`ddd9VCbkm+c7oSn3@c} z5Dj01UR#ZfZw17S$~Q8Fa%24Ki^AlwOWhEmQkcb2te zM^yWhKKi@Z$^E>v8*9^m*2C^rnX?|>oT0jZF=rlm%C3)2(R4pY&?9pMMw-}f6Aoa{ zk2r)7+@W_vpanfc3-}6+G!72LZl|{kP+|}mBHn1~2^Qi$6g-m8hyH%H?~<)@f&Hun zlYxu3(fc2Yh|sS&2+@IMgrHi;>S4P}h5T=WhMHP_?NbX8y?)l;0@P46S{XIXcZN`H zsX_vxOWBL3k`YbV;kFw5fxqBm@Dw4kL5DqMAr`G-(=yf9{LLh0R_Ln->7WYa z*t}pneA#8Pq{BT5Pvh(60yYM|??6)0_#1_LD-2ZNmgXCdg<+)!OV4YC<(N5W@)MsS zuRq7mxwopcOY5KCYB{x@kp~iXG02PMdr?lstiv#cAVchzfBX49AaWt9OAqU--^V-F z9+vA5oa4SXp$sqZRdrNOuGjjjx~%-x{J8MThe+Xi#@9Gs*>C04>&?iJ%l`XjP>0<9 zszx`%{*$UU$iMwnVVPmEiF+j#LgfSqS>Fa-fDso=buA=LtdMn*6&8L3NAVwmWVU-O z*zW8Eds`$F!I?n4TSW}2Cs2rcUxxda`?fchNWmSYC3|byz*eQto9)Vj<*P<8z#S{* z6X^R$zy_d4<-ElTOJl~sD&27bQKEbnpLx1ya$_jc5CV02e&2hq4Yzv0Sg*}Tc#8iV z=IzkBf!YfJSo2gL_wn$#Q~pC5w+OI(kf#(u%j0J+dSp%z+Lw`(vvSI_$wv~uX!&ZJ zam)257nLU+M-F|V`$|*ayj!bWTYMI-UrElaNkiErjA)9?JqDPrs)(COaI%q(nk|K? zER8XOICBPOw0GMPZj&2wk}#WDr4sB6PubN@FD_#8^^3WH6C4*V z-<8ZS^%rMD#$T3s0e~*5vZx#)@GptQcbACoe$_~52T_THw)iz6LZ_PA>=atXBa^wY zfO_Ih+f|a>1#;C??sgYgGE?H)GjT!39rCX;xp&k`AH~;foGlXVLhz_K=uDWXNg zx)4kBC!sfUD}RZ6*DDg`igt&N8f}(neN9jC;9K5ECQy98TfGM+~}$p=;>%XRpOKoB)a%l@=^Ux$bawGfiM~X)#kOiozy=>I>r2y8IKHr^TuE`1la!*dLWWB(b>1dn@n|uM1ECRrPK*wCUP3#?K6_oEsOh*HKuM6$@+*d>-s9WL1CkGlh@^O zver>mAhWH*d?-q%68BZUuxkZ*Q771b&d)Ob`%-HOVsIh1k`zl1`Y#ugU*_+XMsDgk zAo-8HbVP;;WGN^}<)-?_#G*9yb-Ag0w!Liy>FFSjyZ>Tw!o(Fu%T0#&>z$L4;`ol~U3fn9|Ae?#wN|61}oj8%=F0&FUFA zrI>flU(pF0uNX?w%#RVT6>#I}#UekK%i{0*1_BRIVA^}+8`@^gCH{+c+j7!@baBmG zj#u3rkXIPDINcEL2NnH2n9&KGr${=(-^;AL)Y->l1*v)2GZR3 zN1EGl7qlX3w?y!KNDI|!@cky-cy?3Xj7RBS?WLyTKwF%FKnOy&nf~piroO8aC|M^m z^9)Sj5%2@OQ9IEe3$umr#%2e(izLcq1!t|tr?L0<_9E{=XPZdpOxdJ!AT@4Z@8f*E z$0(rp>Kv$^ji8% zluIP92?Gzy0?CH&c1%__`r(@ZeJz>-ToW|P*A&!zO{rOF8gO$U%|>Z5Ri?Mnd3Q_* zrHAs6zhk5?AN<=2Oc&Gn|7IC^%6>k*9~B4KvfKa{brKA1bWJftd{4~>9Izltu8Kg+xUlQOPPlym9k!HJh ze-@M@BSg|qBYPC;z#ql7JBDp{okn7JgGO#7G|~c!;G>bIw##HM$y?Q=hJ~@7l4g=E z3>pcVz?;;J8>B%ds34NJ`HAGkk@hJ>^1nqRPk=%Zy|GLm3Cx(>8UOhHltQ`{qb5tZ zk7!cGWk^K-+j}0 z1nZUu^~Q7)c3sM`bl*%q6*=W&2~z=LElmMc@l#DQC2nHuMd;t#KO&PRkj>FM!_?L< z&m@^W%gIze6{QHVy+g@`@*n)$@|G+AqKumyAU@jslb8OBQ<0F(1W-*op_=vAsitz& z5c*e$$y|g9v0yTBEaQN(V|Nj6iGq+q(9!cgI*Juyc4M_GOT=75giJUzQ*pY#v+fCA9!P&2x zdzabXpIV&k{l^sXq50e-9=Ve4>dz@gtWvnb#(TfXBcV~E@y#(V^)eQc4&d2T^cWx0)vQzq>CD=7h?KVS;ZZ$#s;+~ z_&#CLaaLIL7P~zO*7tD8ry2-vTbV~}YM$7_BWeeM0R0dU=zEsJWo2PgfONBvqY4(% z2~cnwt~zmjL?QqP$dzP{zfR^YZYAk*%}^bbF|v{D8#UbxwppGiwcQqb@3jjLWO*8$ zQZNM1Ye=CBi^bL6-zP9x#;7h~$!GLXS5(H~ipF}Jm5T$Z!}sKuwPlN|Az}4&8HJ^H z0W9P$ji;;e2-Lv=E>mOCg$5ySjE-8JwOUwC2MoIP;!RRaKsGXGHtB@CCJnWEXcfRd z)ULwD&I=iW86-Wvdn%-W>iQ5NZ@W%dx?SVh)wlzQEozscG5ta@TncoaxmokkW3|gZ z)RwLjk3j!o>WUD|r2v;H)p+I(IdE6N)5g{dT|y+S^K^B0z$RbqcA18X)3v0<0ZpXE zE70NRARRiL z-4=P$-F>I9{J4sAq?7Wc29f^b>{;E2XM0sHci~W15l#9TihgFH!X}l=GSmRkhK#ub z6HZkBQGk&59(w`30#HOiSn1G8V9}^ZFqo*e!)pi18tcubsIK5l!8{#f_+0?CQds(s z&a=9EB`|7KE<+ZcWa9~~Ax|E~lSVv=!4r2Q9?iugt^8;&9%bPXt;({%=2PP`buSh2 z7GcmsHJ&;l@-SeThgJZEhHEUu{?(wVzY_9p!}Brn^J944jpxU??m~ViWTa2#Yo)o; zc0{CkvWkK~qjsp2@8RF$k7F4gjeC!GAH$dS+%A4EF+eB{?B07J<3wsnk!cms53DCQ zT!`_&J(F)WQe9fxbe#!S_*BXBo^_3NM#h%ZbIq1U(!QlHquhNal&!UgQfGHB ze&e1CFGypa%4mPO@vxAxmm4ZX{Tf$c){7S~L69A5E*x!N=gwLIK|F^cvPx5hNypf% z5}Vde;V~m=)ut-S8c;=BxdXGJ77ol-n=X{1|JA67%Q_Z?;ARQVzP1QT+(nh0F6}GR zQKw70CSGzD*IjaUA!JPP;;qi`yo1i-lGDz%o6iO1m-eM;sQl6{bwFwH%7oIk^dfi2 z*3!NgG!*`SSW9*4A!Yl$mO5D06_!xe_mqx;1U=wVsT_Nztc_2Pt}5%>sHdvRy6&kT z6HwkaDToRvhY@mFU!9h^RMr+$6rNw+7Zyz6e|r#BRo=H4XSzpfmS7LuYLaZiCUDI2 zG*_hCHC;pPQM=tm6XAWg5mqoW3oc8y7VI4Iq|=fhY(~nIg3qKG1+XC&cMtNCLuiKE z;V%-eN|O@{h7dfLRECs!iKr|Qm6hyn9UBGSM=@E^7EtrM3xtK)+_^B46ZOq2K+Xc> zH2#yk6J=*6y7AIPytL?h@=_#CNyJNu;d1&v$tz*3@{4M`vKp_vu)8(9q*lt=>}lKv zQ7YHUmF$#?H@9aUf;gW$N0-Rr(xEEs7xo;ZJnKG)&B?m#TEu)S?4&T3Oy^bD$x&;RT-}9 zFMx59VjeE;)OL94hAPlQM(L9YecFaTZNsoa!{EzDf(FlViaR}PyOH-e$%_sm@04xm z;5LkI71b38SGgefiL}NF>`TUFXaBHe9558|(K&=CeAni(>pCxN!qcA8q1g$kQ! z98gQt?E&_NpbLn{BNq3kH3WRJd}ua^5bY`)dJ#uZV9DaQs-yO{XLL^@m}En#Akgp` z{WE)E9jIcNusjAivt9r}*OsJxnd=)HT~u9~+Z~nWL6iucHtQ)orpaUIuw;L^Ni`)e zJkWpv4E>v-H(3fUNfGO$*ybc0Lssv=e5NFA4JRdG9#`60VHeUmj_OM~H+*4%EYA!U zACQXksMC9k)np2C)i(D2Nbd{3p-YN8R+i{xSZ>0;M3^eKgtTB-m&A}CxM?3r8w7$?q5Q1~-#qfS zPGXKBzUJg^9g9mlq=+W(U$oIA-VcBkXSugbac%#0#2*u~wd zy?YmMK6%5-m7>$djnDA?+=d{F@T`dD>2ok5-D1M(p`=*c&hWeykp&4b9a&{^ig?V$ zS;s0EpICl)fPZPY#`-G$5I@^!pg4a~6e80+mxU>dPx_fl`I6_x-VfWW%$z(_LcCb-l_AqL-c+2Fw7GQx45 z1Vwo#ff7>ksG;^OSE(rCfR&~Gl^h=$<^*RdxG4d5EMX8rgBI1p zY!Q24CoKxY>@4}q6T|GiB3H>Rlz%O)$76$Pl zR3`(RKsfRF)67p0JMKV=n0t++-2!zNG^jR<4(Bcjfg3Ko zyE)$3pBDc8QjP4cM3A_KrQX90cdBB=gnGt|98SwKO>Z@Ju5;jtHiH)_jL8eEbZl$wp%&YM-P?mj zMN5-CciPHLl&QqS_RqEkdE4A*+sMGT*HQ1J>tOtjm#cdaqtWGwC4=c%Gd`nmANm!3 zb=YW>5EQZ4D2)xu1q0@8Pq{$@a>vi0%tl(eJt$nVAkP{p*^}gNbXVi35>S$nc@b=L z#?R=lwgT3GdKLyviE{q~36vy?FO&dy(7(7#b&mEJB{Rk;Tp1r}a!`S3t!Itr?Q}-I zx1-_bXuchTjoXNAtBE@0mOA%$^Jn1?-_y2VDuF?j?*3T0*3}P`+vcvs;uwQPl2JwB z6peLKHtO+sTNM_NI%6UP_=3$kKEaleg{PZgo@bl$_)OdME5pDu0F)WZrTNR5*4vgm z9^bxB(%CHI6X12I3QatX*lms80>*1s`S;;P~l@_-Y)RYP3CpckO3FL z&md~D)YLz%El?Wcsh+fkPxnx594O9HSkVMUFW#GLO!Wixbtg7(9789IQ-8%>V{%Ko zr#5o&ba&Z3+|ycS!KH5PX6}8ONvjY)-opKbIT|gV-pUEgr?-jm+qjMTowqYXlx?gh zurWqj)a?;p+Q!8*$+wGt-o|YU`}8)(NXtl?P=DjC$+vemZs&R#l3`v8H-Twe(S2tN z7e}it78uv97a!iid6=5p#i$3kN%-uZ`2bhIkT9Z~cXBmMgCGv>BNEUWD*f!#YmOv8>O^#hWv&$Fm~~B82F@bGg=`=+hznqDkIJ}ODsl5;Al&34_HlQ(Zc~zbg z^H{~hwENqKxZ4;euU0H-!r%0*$5QvkdY++Z1v7!56qu zsi;4`IJ%YP3x}`qi>_Vsto(Qq+HUfAV0Pr$ls2$xvuQJh+`KMV*DhJHbk&=m#KQhe zO&M8fkuChnu%h^`7FWy>C|_BnEw4JY|f$Wbb>*K;i$3F%)bVR0-LAxuWM0ZaH({E%BpAxL3o>n;0or zYpOG40U(oRg6)Iiu}8TmranV_^HJ_4(_kXuZ&s)S?0u8MsU%!fiOc+Hpb&WYEG5-1d)fCcFM4Hw}~+Zj3cT?AXD`0`<@SbKl&7i%#x zsyAHynSBHBmfL9{aqDP6W77w5?re@}{o(U)o8>foVLTOIEPB8(H}ox?5K+nyB<8Hy zxVZskt%CWy;h?jP1AC{cU?4tXccf|yh4>}R=e>OS5M_r`Df4 zJvhl?d+2_x@Z=>%;B0g^!bKgX$$*f^kx~+Grm0G&Y1L4L?V+pa(pJhA(=u&HymJrN z9Qo;5B845JwtN3_j3&2%d0G-fUG+Hkd*(0a#Wx=3x|qciME4Wi+sr@4iVL5_e#Pgb zPjX&nL8e&J&K)720ekTqq1{jJUjTOpPo=I$m%K0M5s z-1~*irL?Y0i`ZQ!-61QJ8tQ;)hf5(fH6wXNU1iFOPn}a&Bs-Jm3u#46U8Rt=lo4J8 zj#32f0037|QGBKfk>YH|mT$XF1a?G-Uq!1gN>#QGDYhtZ;P>Vp+cum((}3K1H-R1# zB3977@wV9>+T3%FGRqG$x6+>N&=%$pQX-ioTe9x{TC=J5bzD9s&ZmV4vHck?Wl4lB z#Y3xI=o1}PZnl33X7Rb&9fkMTzFm6+xGgK_fkeUwWg%c>6V-oD_zR@EDaE$5t+#ke zA6!TJHsmCDT2nK22P--TFw~}JxrsMt>PrFL^K<|RCVnFk2V#Bl8#yii5;E{!?$YjN)pZY5a_!OwHIGK=pNZ+o80qPK_-Jx1m>0oDIGd6(yz7e?NRnYSTV-zxmZEE1+8Sg; zNY}UkM8X10m)c$wOsaz4UZB!gN2&)Q*M!((Y;OUc)Zc$RxmC!B<&an1J4epF z3O6~8f*~7Oo9sOEoVVGP`bI;Qvn^a)e-uZ6+0)&Al*^%Y1>v}vA5P7)drfzUUmxS_ zA)iLkmDHEf=Sb|O$SCpV7r3)yF$XOV!}Bwu@0*LogiciF7$+|5 z%>`IT;*8JIU4r;i<4^8%5BC55{DN%U1GeNbK+}V+#~vED_}m}uz3fTq{=dk$``q* zL55zyI<&*fFs@R3;ziCny)gNruFp{hXfM6@_J>nz)(hbu(YM>gKX+q8;dAcG+){jY?|qs3BB*ocgB;!6a)PsJ-0k%=2rEVY_H0RD{NMeb zd!iGVJ>&5=NqM$s!jJRpdG-72rKhhBX2G0|}yI$UWgXdb5LmmEi{Nz)7?Je&3{{%+gs0{!B delta 70548 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\n"); + usage(); } } break; @@ -62,7 +64,7 @@ int main(int argc, char *argv[]) return ONEBOX_STATUS_FAILURE; } } else { - printf("Usage: ./bt_util bt_e2e_periodic_stats < filename > < duration >\n"); + usage(); } } break; case RSI_GET_BT_STATS: { @@ -72,7 +74,7 @@ int main(int argc, char *argv[]) return ONEBOX_STATUS_FAILURE; } } else { - printf("Usage: ./bt_util bt_stats < filename > \n"); + usage(); } } break; @@ -87,14 +89,43 @@ int main(int argc, char *argv[]) bb_rf_params.Data[0] = bb_addr; bb_rf_params.Data[1] = bb_val; } else { - printf("Usage: bb_write addr \n"); + usage(); return ONEBOX_STATUS_FAILURE; } - if (send_bb_write_frame_to_drv(bb_rf_params, sfd) < 0) { + if (send_bb_read_write_frame_to_drv(bb_rf_params, sfd) < 0) { printf("Unable to perform BB_WRITE\n"); } else printf("SUCCESS Writing to BB: \n"); break; + case RSI_SET_BB_READ: + len = sizeof(bb_rf_params); + if (argc == 3) { + bb_addr = strtol(argv[2], NULL, 16); + ONEBOX_PRINT("BB addr: 0x%x \n", bb_addr); + bb_rf_params.value = 0; //BB_READ_TYPE + bb_rf_params.no_of_values = 1; + bb_rf_params.soft_reset = 0; + bb_rf_params.Data[0] = bb_addr; + } else { + usage(); + return ONEBOX_STATUS_FAILURE; + } + if (send_bb_read_write_frame_to_drv(bb_rf_params, sfd) < 0) { + printf("Unable to perform BB_READ\n"); + } else + printf("SUCCESS in Reading BB: \n"); + nlh = common_recv_mesg_wrapper(sfd, len); + if (nlh == NULL) { + printf("Error receving from bb\n"); + break; + } else { + memcpy(&bb_rf_params, NLMSG_DATA(nlh), len); + for (ii = 0; ii < bb_rf_params.no_of_values; ii++) { + printf("BB_read value is 0x%x\n", bb_rf_params.Data[ii]); + } + } + free(nlh); + break; default: break; } @@ -180,7 +211,7 @@ int send_bt_stat_frame_to_drv(struct bb_rf_param_bt_t bb_rf_params, int sfd) return ret; } -int send_bb_write_frame_to_drv(struct bb_rf_param_bt_t bb_rf_params, int sfd) +int send_bb_read_write_frame_to_drv(struct bb_rf_param_bt_t bb_rf_params, int sfd) { struct sockaddr_nl dest_addr; struct nlmsghdr *nlh = NULL; @@ -586,6 +617,8 @@ int get_bt_cmdnumber(char *command) return RSI_GET_BT_STATS; else if (!strcmp(command, "bb_write")) return RSI_SET_BB_WRITE; + else if (!strcmp(command, "bb_read")) + return RSI_SET_BB_READ; else usage(); return ONEBOX_STATUS_SUCCESS; @@ -601,5 +634,6 @@ void usage() ONEBOX_PRINT("Usage: ./bt_util bt_e2e_periodic_stats < filename > < duration >\n"); ONEBOX_PRINT("Usage: ./bt_util bt_stats < filename > \n"); ONEBOX_PRINT("Usage: ./bt_util bb_write < value> \n"); + ONEBOX_PRINT("Usage: ./bt_util bb_read \n"); return; } diff --git a/apps/cmfg/cmfg.h b/apps/cmfg/cmfg.h new file mode 100755 index 0000000..4f8e2e7 --- /dev/null +++ b/apps/cmfg/cmfg.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2020-2023 Silicon Labs, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define SOCKET_PROTOCOL 31 + +// Minimun TX packet length +#define TX_CMD_DESC 14 +// Minimun RX packet length +#define RX_CMD_DESC 10 +// Maximum TX packet buffer +#define MAX_TX_BUFF_SUPPORTED (4096 + TX_CMD_DESC) +// Maximum RX packet buffer +#define MAX_RX_BUFF_SUPPORTED (4096 + RX_CMD_DESC) + +// MCU commands +#define RESET_INTERFACE_COMMAND 0x0000 +#define INIT_INTERFACE_COMMAND 0x0001 +#define RESET_TARGET_COMMAND 0x0002 +#define POWER_TARGET_COMMAND 0x0003 +#define READ_MEMORY_COMMAND 0x0004 +#define WRITE_MEMORY_COMMAND 0x0005 + +// Error Marcos +#define MEMORY_ALLOCATION_FAILURE 0x0 +#define TX_PACKET_CRC_CHECK_ERROR 0xE0 +#define RX_PACKET_CRC_CHECK_ERROR 0xE1 +#define INVALID_COMMAND 0xE2 +#define UNSUPPORTED_COMMAND 0xE3 +#define SOFT_RESET_TARGET 0x4 + +#define CMFG_MAX_COMMAND_LENGTH MAX_TX_BUFF_SUPPORTED +#define CMFG_MIN_COMMAND_LENGTH TX_CMD_DESC + +// Transfer packet Structure +typedef struct tx_packet_command_desc_s { + uint8_t start_tag; + uint8_t sequence_number; + uint16_t packet_length; + uint16_t MCU_command; + uint16_t length; + uint32_t address; + uint8_t content[0]; +} tx_packet_command_desc_t; + +// Receive Packet Structure +typedef struct rx_packet_command_desc_s { + uint8_t start_tag; + uint8_t sequence_number; + uint16_t packet_length; + uint32_t status; + uint8_t content[0]; +} rx_packet_command_desc_t; + +// TCP socket related +#define SOURCE_PORT 51917 +int tcp_server(void); +int server_response(rx_packet_command_desc_t *, uint16_t, int); +int process_request(tx_packet_command_desc_t *, uint16_t, int); +int allocate_tx_buffer(tx_packet_command_desc_t **); +void allocate_rx_buffer(rx_packet_command_desc_t **); +void sig_handler(int); +void interrupt_checker(void); + +// Netlink socket related +#define WLAN_PACKET 1 +#define MANUFACTURING 16 +#define MFG_READ 'R' +#define MFG_WRITE 'W' +#define MFG_RESET 'X' +// Master structure for read and write +typedef struct mfg_rw_s { + uint32_t address; + uint16_t length; + uint8_t data[MAX_TX_BUFF_SUPPORTED]; + uint32_t reset_value; + char read_write; +} mfg_rw_t; +//Command Type +struct rsi_nl_desc { + uint16_t desc_word[0]; +}; + +extern int netlink_sfd; +int netlink_socket_creation(void); +int send_tx_packet(mfg_rw_t *, int, int); +int receive_rx_packet(mfg_rw_t *, int, int); + +// Device R/W Routines +void reset_routine(rx_packet_command_desc_t *, int); +void read_routine(rx_packet_command_desc_t *, tx_packet_command_desc_t *, int); +void write_routine(rx_packet_command_desc_t *, tx_packet_command_desc_t *, int); + +// CRC related +uint16_t crc16_computation(uint8_t *, uint16_t); +void rx_crc_check(rx_packet_command_desc_t *, uint32_t content_len); diff --git a/apps/cmfg/cmfg_main.c b/apps/cmfg/cmfg_main.c new file mode 100755 index 0000000..5b8a154 --- /dev/null +++ b/apps/cmfg/cmfg_main.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2020-2023 Silicon Labs, Inc. + */ +#ifndef CMFG_H +#define CMFG_H + +#include "cmfg.h" + +#endif + +int netlink_sfd; + +// Function to provide user the access to terminate the application +void sig_handler(int signal) +{ + if (signal == SIGINT) { + printf("\nCtrl + C sent by the user\n"); + printf("Closing the CMFG Application\n"); + exit(0); + } +} + +// Function to check for signal sent by the user +void interrupt_checker(void) +{ + struct sigaction action; + bzero(&action, sizeof(action)); + action.sa_handler = &sig_handler; + sigaction(SIGINT, &action, NULL); +} + +//Calculate crc of the packet +uint16_t crc16_computation(uint8_t *data_p, uint16_t length) +{ + uint16_t x; + uint16_t crc = 0xFFFF; + while (length--) { + x = crc >> 8 ^ *data_p++; + x ^= x >> 4; + crc = (crc << 8) ^ ((x << 12)) ^ ((x << 5)) ^ (x); + } + return crc; +} + +int main(int argc, char **argv) +{ + int server_status = 0; + + netlink_sfd = netlink_socket_creation(); + if (netlink_sfd < 0) + printf("Unable to start netlink\n"); + + interrupt_checker(); + + while (1) { + sleep(0.5); + if (server_status == 0) { + server_status = tcp_server(); + if (server_status < 0) + printf("Unable to start server\n"); + } + } + return 0; +} diff --git a/apps/cmfg/cmfg_netlink.c b/apps/cmfg/cmfg_netlink.c new file mode 100755 index 0000000..1c3a16e --- /dev/null +++ b/apps/cmfg/cmfg_netlink.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2020-2023 Silicon Labs, Inc. + */ +#ifndef CMFG_H +#define CMFG_H + +#include "cmfg.h" + +#endif + +int netlink_socket_creation() +{ + int nl_sfd; + struct sockaddr_nl src_addr; + nl_sfd = socket(PF_NETLINK, SOCK_RAW, SOCKET_PROTOCOL); + + if (nl_sfd < 0) { + printf("User space netlink socket could not be created\n"); + return -1; + } + + memset(&src_addr, 0, sizeof(src_addr)); + src_addr.nl_family = AF_NETLINK; + src_addr.nl_pid = getpid(); + + if (bind(nl_sfd, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0) { + printf("Unable to bind the socket\n"); + return -1; + } + return nl_sfd; +} + +int send_tx_packet(mfg_rw_t *mfg_rw, int sfd, int size) +{ + printf("send_tx_packet entry\n"); + struct msghdr msg = { 0 }; + struct nlmsghdr *nlh = NULL; + struct iovec iov = { 0 }; + struct sockaddr_nl dest_addr; + struct rsi_nl_desc *nlh_desc = NULL; + + memset(&dest_addr, 0, sizeof(dest_addr)); + dest_addr.nl_family = AF_NETLINK; + dest_addr.nl_pid = 0; + dest_addr.nl_groups = 0; + nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(size + 16)); + memset(nlh, 0, NLMSG_SPACE(size + 16)); + + nlh->nlmsg_len = NLMSG_SPACE(size + 16); + nlh->nlmsg_type = (unsigned short)WLAN_PACKET; + nlh_desc = (struct rsi_nl_desc *)NLMSG_DATA(nlh); + nlh_desc->desc_word[0] = MANUFACTURING; + nlh->nlmsg_pid = getpid(); + + iov.iov_base = (void *)nlh; + iov.iov_len = nlh->nlmsg_len; + msg.msg_name = (void *)&dest_addr; + msg.msg_namelen = sizeof(dest_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + memcpy(NLMSG_DATA(nlh) + 16, mfg_rw, size); + + if (sendmsg(sfd, &msg, 0) < 0) { + printf("Unable to send commander tx packet to driver\n"); + close(sfd); + free(nlh); + return -1; + } + free(nlh); + return 0; +} + +int receive_rx_packet(mfg_rw_t *mfg_rw, int sfd, int size) +{ + struct msghdr msg = { 0 }; + struct nlmsghdr *nlh = NULL; + struct iovec iov = { 0 }; + struct sockaddr_nl dest_addr; + struct rsi_nl_desc *nlh_desc = NULL; + + memset(&dest_addr, 0, sizeof(dest_addr)); + dest_addr.nl_family = AF_NETLINK; + dest_addr.nl_pid = 0; + dest_addr.nl_groups = 0; + nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_RX_BUFF_SUPPORTED)); + memset(nlh, 0, NLMSG_SPACE(MAX_RX_BUFF_SUPPORTED)); + nlh->nlmsg_len = NLMSG_SPACE(MAX_RX_BUFF_SUPPORTED); + nlh->nlmsg_type = (unsigned short)WLAN_PACKET; + nlh_desc = (struct rsi_nl_desc *)NLMSG_DATA(nlh); + nlh_desc->desc_word[0] = MANUFACTURING; + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = 0; + iov.iov_base = (void *)nlh; + iov.iov_len = nlh->nlmsg_len; + msg.msg_name = (void *)&dest_addr; + msg.msg_namelen = sizeof(dest_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + if (recvmsg(sfd, &msg, 0) < 0) { + printf("Unable to receive rx packet from driver\n"); + close(sfd); + free(nlh); + return -1; + } + + memcpy(mfg_rw->data, NLMSG_DATA(nlh), MAX_RX_BUFF_SUPPORTED); + free(nlh); + return 0; +} diff --git a/apps/cmfg/cmfg_routine.c b/apps/cmfg/cmfg_routine.c new file mode 100755 index 0000000..8f03050 --- /dev/null +++ b/apps/cmfg/cmfg_routine.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2020-2023 Silicon Labs, Inc. + */ +#ifndef CMFG_H +#define CMFG_H + +#include "cmfg.h" + +#endif + +// This function resets the device +void reset_routine(rx_packet_command_desc_t *rx_packet, int sfd) +{ + close(sfd); + system("rmmod rsi_sdio rsi_91x"); + system("insmod rsi_91x.ko dev_oper_mode=1 rsi_zone_enabled=0x601 skip_fw_load=1"); + system("insmod rsi_sdio.ko"); + sleep(1); + netlink_sfd = netlink_socket_creation(); + + rx_packet->content[0] = SOFT_RESET_TARGET; + rx_packet->content[1] = 0; //reserved + rx_packet->content[2] = 0; //reserved + rx_packet->content[3] = 0; //reserved +} + +// This function reads data from the address and size specified by the commander +void read_routine(rx_packet_command_desc_t *rx_packet, tx_packet_command_desc_t *tx_packet, int sfd) +{ + mfg_rw_t mfg_rw, *ptr; + int size = 0; + uint16_t remaining_length = 0; + uint16_t aligned_length = 0; + + // Check if length is aligned or unaligned + remaining_length = (tx_packet->length & 3); + // Get the aligned length + aligned_length = tx_packet->length - remaining_length; + + if (remaining_length == 0) { + mfg_rw.address = tx_packet->address; + mfg_rw.length = tx_packet->length; + mfg_rw.read_write = MFG_READ; + size = sizeof(mfg_rw); +#ifdef DEBUG + printf("Read routine rem_len = 0 mfg_rw packet size = %d\n", size); +#endif + ptr = &mfg_rw; + send_tx_packet(ptr, sfd, size); + rx_packet->status = receive_rx_packet(ptr, sfd, size); + memcpy(rx_packet->content, ptr->data, tx_packet->length); + } else { + + mfg_rw.address = tx_packet->address; + mfg_rw.length = aligned_length; + mfg_rw.read_write = MFG_READ; + size = sizeof(mfg_rw); +#ifdef DEBUG + printf("1. Read routine rem_len!= 0 mfg_rw packet size = %d\n", size); +#endif + ptr = &mfg_rw; + send_tx_packet(ptr, sfd, size); + rx_packet->status = receive_rx_packet(ptr, sfd, size); + memcpy(rx_packet->content, ptr->data, tx_packet->length); + + mfg_rw.address = tx_packet->address + aligned_length; + mfg_rw.length = remaining_length; + mfg_rw.read_write = MFG_READ; + size = sizeof(mfg_rw); +#ifdef DEBUG + printf("2. Read routine rem_len!= 0 mfg_rw packet size = %d\n", size); +#endif + ptr = &mfg_rw; + send_tx_packet(ptr, sfd, size); + rx_packet->status = receive_rx_packet(ptr, sfd, size); + memcpy(rx_packet->content + aligned_length, ptr->data, tx_packet->length); + } + rx_packet->packet_length += tx_packet->packet_length; +} + +// This function reads data from the address and size specified by the commander +void write_routine(rx_packet_command_desc_t *rx_packet, tx_packet_command_desc_t *tx_packet, int sfd) +{ + mfg_rw_t mfg_rw, *ptr; + int size = 0; + uint16_t remaining_length = 0; + uint16_t aligned_length = 0; + + // Check if length is aligned or unaligned + remaining_length = (tx_packet->length & 3); + // Get the aligned length + aligned_length = tx_packet->length - remaining_length; + + if (remaining_length == 0) { + mfg_rw.address = tx_packet->address; + mfg_rw.length = aligned_length; + mfg_rw.read_write = MFG_WRITE; + memcpy(&mfg_rw.data, tx_packet->content, aligned_length); + size = sizeof(mfg_rw); +#ifdef DEBUG + printf("Write routine rem_len=0 mfg_rw packet size = %d\n", size); +#endif + ptr = &mfg_rw; + rx_packet->status = send_tx_packet(ptr, sfd, size); + } else { + mfg_rw.address = tx_packet->address; + mfg_rw.length = aligned_length; + mfg_rw.read_write = MFG_WRITE; + memcpy(&mfg_rw.data, tx_packet->content, aligned_length); + size = sizeof(mfg_rw); +#ifdef DEBUG + printf("1. Write routine rem_len!= 0 mfg_rw packet size = %d\n", size); +#endif + ptr = &mfg_rw; + rx_packet->status = send_tx_packet(ptr, sfd, size); + + mfg_rw.address = tx_packet->address + aligned_length; + mfg_rw.length = remaining_length; + mfg_rw.read_write = MFG_WRITE; + memcpy(&mfg_rw.data + aligned_length, tx_packet->content + aligned_length, remaining_length); + size = sizeof(mfg_rw); +#ifdef DEBUG + printf("2. Write routine rem_len!= 0 mfg_rw packet size = %d\n", size); +#endif + ptr = &mfg_rw; + rx_packet->status = send_tx_packet(ptr, sfd, size); + } +} diff --git a/apps/cmfg/cmfg_socket.c b/apps/cmfg/cmfg_socket.c new file mode 100755 index 0000000..7f8dc6d --- /dev/null +++ b/apps/cmfg/cmfg_socket.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2020-2023 Silicon Labs, Inc. + */ +#ifndef CMFG_H +#define CMFG_H + +#include "cmfg.h" + +#endif + +int server_fd; +int client_fd; + +int tcp_server(void) +{ + + int pktlen = 0; +#ifdef DEBUG + int count = 0; +#endif + unsigned int client_len = 0; + uint8_t packet[CMFG_MAX_COMMAND_LENGTH]; + tx_packet_command_desc_t *tx_pkt; + struct sockaddr_in server_addr; + struct sockaddr_in client_addr; + + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(SOURCE_PORT); + + server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (server_fd < 0) { + printf("TCP Socket creation failed\n"); + } + + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &(int){ 1 }, sizeof(int)) < 0) { + printf("Enabling port reuse using SO_REUSEPORT via setsockopt failed\n"); + return -1; + } + + if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { + printf("TCP server bind of CMFG Application failed\n"); + return -1; + } + + if (listen(server_fd, 1) == -1) { + printf("Error : Server is not listening on specified port\n"); + return -1; + } + + printf("********** Server is up and listening on TCP PORT NUMBER : %d **********\n", SOURCE_PORT); + printf("********** Please press Ctrl + C to terminate the application **********\n"); + client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len); + if (client_fd < 0) { + printf("Server could not establish connection with commander\n"); + return -1; + } + printf("Connection with commander established successfully!\n"); + printf("Waiting for input from USER\n"); + while (1) { + allocate_tx_buffer(&tx_pkt); + memset(packet, 0, CMFG_MAX_COMMAND_LENGTH); + pktlen = recv(client_fd, packet, CMFG_MAX_COMMAND_LENGTH, 0); + if (pktlen == 0) + break; + + if ((allocate_tx_buffer(&tx_pkt) < 0)) { + printf("Tx buffer allocation failed\n"); + break; + } + memcpy(tx_pkt, packet, pktlen); + if (pktlen > 8) { +#ifdef DEBUG + printf("\n/*****TX PACKET START*****/\n"); + printf("Packet of size %d received\n", pktlen); + printf("Start tag -> %x\n", tx_pkt->start_tag); + printf("sequence number -> %d\n", tx_pkt->sequence_number); + printf("Packet_length (from start tag via commander)-> %d\n", tx_pkt->packet_length); + printf("MCU_command -> %x\n", tx_pkt->MCU_command); + printf("data size -> %d\n", tx_pkt->length); + printf("address -> %02x\n", tx_pkt->address); + printf("Data section of TX packet\n"); + for (int i = 0; i < tx_pkt->packet_length; i++) { + printf("%02x ", tx_pkt->content[i]); + count++; + if (count == 16) { + printf("\n"); + count = 0; + } + } + printf("\nEnd of data section\n"); + printf("/*****TX PACKET ENDING*****/\n"); + printf("\n"); +#endif + if (process_request(tx_pkt, pktlen, netlink_sfd) < 0) { + printf("Unable to process request\n"); + free(tx_pkt); + break; + } + free(tx_pkt); + } + } + + printf("Closing socket connection\n"); + close(server_fd); + close(client_fd); + + return 0; +} + +int process_request(tx_packet_command_desc_t *tx_packet, uint16_t length, int netlink_sfd) +{ + int status = 0; + uint16_t tx_packet_crc = 0; + uint16_t tx_packet_crc_calculate = 0; + + rx_packet_command_desc_t *rx_packet; + allocate_rx_buffer(&rx_packet); + memset(rx_packet, 0, MAX_RX_BUFF_SUPPORTED); + rx_packet->start_tag = 0xaa; + rx_packet->sequence_number = tx_packet->sequence_number; + rx_packet->packet_length = 10; + + // TX Packet CRC Check + tx_packet_crc = *(uint16_t *)((uint8_t *)tx_packet + tx_packet->packet_length - 2); + tx_packet_crc_calculate = crc16_computation((uint8_t *)tx_packet, tx_packet->packet_length - 2); + if (tx_packet_crc != tx_packet_crc_calculate) { + printf("TX Packet CRC Error\n"); + rx_packet->status = TX_PACKET_CRC_CHECK_ERROR; + rx_packet->packet_length = 0; + if (server_response(rx_packet, rx_packet->packet_length, client_fd)) { + free(rx_packet); + return -1; + } + } + + switch (tx_packet->MCU_command) { + case RESET_INTERFACE_COMMAND: + printf("Reset Interface Case Entry\n"); + rx_packet->status = UNSUPPORTED_COMMAND; + rx_crc_check(rx_packet, 0); + if (server_response(rx_packet, rx_packet->packet_length, client_fd)) + status = -1; + free(rx_packet); + break; + + case INIT_INTERFACE_COMMAND: + printf("Init Interface Case Entry\n"); + rx_packet->status = 0; + rx_packet->content[0] = 1; + rx_packet->content[1] = 0; //reserved + *(uint16_t *)&rx_packet->content[2] = MAX_TX_BUFF_SUPPORTED; +#ifdef DEBUG + printf("1.Content[0]-> %d\n", rx_packet->content[0]); + printf("1.Content[1]-> %d\n", rx_packet->content[1]); + printf("1.Content[2]-> %d\n", rx_packet->content[2]); +#endif + rx_crc_check(rx_packet, 4); + if (server_response(rx_packet, rx_packet->packet_length, client_fd)) + status = -1; + free(rx_packet); + break; + + case RESET_TARGET_COMMAND: + printf("Reset Target Case Entry\n"); + reset_routine(rx_packet, netlink_sfd); + rx_crc_check(rx_packet, 4); + if (server_response(rx_packet, rx_packet->packet_length, client_fd)) + status = -1; + free(rx_packet); + break; + + case POWER_TARGET_COMMAND: + printf("Power Target Case Entry\n"); + rx_packet->status = UNSUPPORTED_COMMAND; + rx_crc_check(rx_packet, 0); + printf("Unsupported command as of now\n"); + if (server_response(rx_packet, rx_packet->packet_length, client_fd)) + status = -1; + free(rx_packet); + break; + + case READ_MEMORY_COMMAND: + printf("Read Case Entry\n"); + read_routine(rx_packet, tx_packet, netlink_sfd); + rx_crc_check(rx_packet, tx_packet->length); + if (server_response(rx_packet, rx_packet->packet_length, client_fd)) + status = -1; + free(rx_packet); + printf("Read Case exit\n\n"); + break; + + case WRITE_MEMORY_COMMAND: + printf("Write Case Entry\n"); + write_routine(rx_packet, tx_packet, netlink_sfd); + rx_crc_check(rx_packet, 0); + if (server_response(rx_packet, rx_packet->packet_length, client_fd)) + status = -1; + free(rx_packet); + printf("Write Case Exit\n\n"); + break; + + default: + rx_packet->status = INVALID_COMMAND; + rx_crc_check(rx_packet, 0); + if (server_response(rx_packet, rx_packet->packet_length, client_fd)) + status = -1; + free(rx_packet); + break; + } + return status; +} + +int server_response(rx_packet_command_desc_t *resp_packet, uint16_t length, int client_fd) +{ + int resp_pktlen = 0; +#ifdef DEBUG + int count = 0; + printf("\n/*****RX PACKET START*****/\n"); + printf("Start tag -> %x\n", resp_packet->start_tag); + printf("sequence number -> %d\n", resp_packet->sequence_number); + printf("Packet_length (from start tag via commander)-> %d\n", resp_packet->packet_length); + printf("Status-> %x\n", resp_packet->status); + printf("Data section of RX packet\n"); + for (int i = 0; i < resp_packet->packet_length; i++) { + printf("%02x ", resp_packet->content[i]); + count++; + if (count == 16) { + printf("\n"); + count = 0; + } + } + printf("\nEnd of data section\n"); + printf("/*****RX PACKET ENDING*****/\n"); +#endif + resp_pktlen = write(client_fd, resp_packet, length); + if (resp_pktlen < 0) { + printf("Failed to send response back to commander\n"); + return 1; + } else + printf("Response of size %d sent to commander\n", resp_pktlen); + return 0; +} + +void rx_crc_check(rx_packet_command_desc_t *resp_packet, uint32_t content_len) +{ + uint16_t rx_packet_crc = 0; + uint16_t rx_packet_crc_calculate = 0; +#ifdef DEBUG + printf("RX CRC Check function entry\n"); +#endif + resp_packet->packet_length = 10 + content_len; + rx_packet_crc = crc16_computation((uint8_t *)resp_packet, resp_packet->packet_length - 2); + *(uint16_t *)((uint8_t *)resp_packet + resp_packet->packet_length - 2) = rx_packet_crc; + rx_packet_crc_calculate = *(uint16_t *)((uint8_t *)resp_packet + resp_packet->packet_length - 2); + if (rx_packet_crc_calculate != rx_packet_crc) { + printf("RX Packet CRC Error\n"); + resp_packet->status = RX_PACKET_CRC_CHECK_ERROR; + resp_packet->packet_length = 0; + } +} + +int allocate_tx_buffer(tx_packet_command_desc_t **tx_packet_command) +{ + *tx_packet_command = malloc(MAX_TX_BUFF_SUPPORTED); + if (*tx_packet_command == NULL) { + printf("Tx Packet malloc failed\n"); + return -1; + } + return 0; +} + +void allocate_rx_buffer(rx_packet_command_desc_t **rx_packet_command) +{ + *rx_packet_command = malloc(MAX_RX_BUFF_SUPPORTED); + if (*rx_packet_command == NULL) { + printf("Rx Packet malloc failed\n"); + } +} diff --git a/apps/matlab_utils.c b/apps/matlab_utils.c index bd47b18..d74a623 100755 --- a/apps/matlab_utils.c +++ b/apps/matlab_utils.c @@ -77,7 +77,7 @@ ONEBOX_STATUS rsi_process_packet(uint_8 *packet, uint_32 packet_len, struct sock { uint_16 type, ii, jj, i, j, flags; - uint_32 bb_addr, bb_len, soft_reset = 0, vals_per_reg, num_of_regs, no_of_addr; + uint_32 bb_addr, bb_len, soft_reset = 0, vals_per_reg, num_of_regs, no_of_addr = 0; uint_32 tx_count; uint_32 bb_val; uint_32 RCV_BUFFER[5]; @@ -311,9 +311,9 @@ int_32 bb_read(uint_32 bb_addr, uint_32 bb_len, uint_32 soft_rst) { int_32 count, cc = 1, m; uint_32 addr, ref, swap, len; - struct bb_rf_param_t bb_rf_params, bb_print_params; - count = bb_len; - addr = bb_addr; + struct bb_rf_param_t bb_rf_params = {}, bb_print_params; + count = bb_len; + addr = bb_addr; printf(" Final read format for base band is 0x%x length is 0x%x :\n", addr, count); while (count) { ref = addr; @@ -357,9 +357,9 @@ int_32 bb_read(uint_32 bb_addr, uint_32 bb_len, uint_32 soft_rst) int_32 bb_read_multiple(uint_32 bb_len, uint_32 soft_rst, uint_32 *BUFFER) { int_32 count, cc = 1, m; - uint_32 addr, i, len; - struct bb_rf_param_t bb_rf_params, bb_print_params; - count = bb_len; + uint_32 addr = 0, i, len; + struct bb_rf_param_t bb_rf_params = {}, bb_print_params; + count = bb_len; if (bb_len > 6) { printf("Maximum 6 Values allowed"); @@ -442,7 +442,7 @@ int_32 buffer_read(uint_32 *BUFFER, uint_32 no_of_addr, uint_32 soft_rst, uint_8 { uint_16 val, i, ii, jj, j = 3, index = 0, k = 0, buf_len; uint_8 blocks, count, count1, write_block = 27; - struct bb_rf_param_t bb_rf_params, bb_rf_print; + struct bb_rf_param_t bb_rf_params = {}, bb_rf_print; uint_32 swap, len; val = (uint_16)no_of_addr; printf("Total no regs are = %d :\n", val); @@ -556,7 +556,7 @@ int_32 buffer_write(uint_32 *BUFFER, uint_32 no_of_addr, uint_32 soft_rst, uint_ { uint_16 val, i, ii, j = 3, index = 0, k = 0, buf_len; uint_8 blocks, count, count1, write_block = 27; - struct bb_rf_param_t bb_rf_params; + struct bb_rf_param_t bb_rf_params = {}; val = (uint_16)no_of_addr; if (write_type == INDIRECT_ADDR) { @@ -650,7 +650,7 @@ int_32 tx_packet(uint_32 *buf, uint_32 tx_cnt) int chan_number, cmd; int ii = 0, i; unsigned short tmp_rate; - per_params_t per_params; + per_params_t per_params = {}; unsigned char rate_flags = 0; if (tx_cnt == 1) { @@ -1550,7 +1550,7 @@ int tcp_server(void) struct sockaddr_in server_addr; struct sockaddr_in client_addr; uint_8 packet[ONEBOX_MAX_PKT_LEN]; - uint_8 packet1[ONEBOX_MAX_PKT_LEN]; + uint_8 packet1[ONEBOX_MAX_PKT_LEN] = {}; uint_32 read_len, client_len, i = 0, burst = 0, read_type; ONEBOX_STATUS status; uint_8 flag = 0; diff --git a/apps/onebox_util.c b/apps/onebox_util.c index 021231d..4fbf803 100755 --- a/apps/onebox_util.c +++ b/apps/onebox_util.c @@ -18,6 +18,7 @@ //__9117_CODE_END #include "per_util.h" #include +#include /****************************************************************************** * main() @@ -32,12 +33,12 @@ int main(int argc, char *argv[]) unsigned char *pkt1; unsigned int pkt, i; unsigned int bb_addr = 0, bb_val = 0, len = 0; - struct bb_rf_param_t bb_rf_params; - struct bb_rf_param_t bb_rf_read; + struct bb_rf_param_t bb_rf_params = {}; + struct bb_rf_param_t bb_rf_read = {}; w_9116_features_t w_9116_features; struct master_params_s master; struct efuse_content_t *efuse_content; - struct fltr_bcast bcast; + struct fltr_bcast bcast = {}; int length; int multiple_bb_read = 0; //__9117_CODE_START @@ -49,10 +50,12 @@ int main(int argc, char *argv[]) rsi_twt_user_params twt_params; rsi_twt_status_resp *twt_resp = malloc(sizeof(rsi_twt_status_resp)); wifi_reschedule_twt_config_t reschedule_twt_config; + twt_selection_t twt_sel_req; #endif unsigned short int bmiss_threshold_value, prev_bmiss_threshold; unsigned short int keep_alive_period; //__9117_CODE_END + short int gaintable_status; short int received_rssi; //__9117_CODE_START int diag_cmd = 0; @@ -102,7 +105,23 @@ int main(int argc, char *argv[]) switch (cmdNo) { case UPDATE_WLAN_GAIN_TABLE: { ret = update_wlan_gain_table(argc, argv, ifName, sfd); - } break; + if (ret < 0) { + ONEBOX_PRINT("Error while issuing get_rssi ioctl\n"); + ret = ONEBOX_STATUS_FAILURE; + break; + } else { + nlh = common_recv_mesg_wrapper(sfd, 2); + memcpy(&gaintable_status, (short int *)NLMSG_DATA(nlh), 2); + } + if (gaintable_status == -EINVAL) { + ONEBOX_PRINT( + "\n************** ERROR : Update WLAN Gain table not supported for ACx modules ************* \n\n"); + } else { + ONEBOX_PRINT("\n************** Successfully completed programming gain tables ************* \n\n"); + } + close(sfd); + break; + } case RSI_SET_BB_WRITE: if (argc > 3) { bb_addr = strtol(argv[3], NULL, 16); @@ -203,6 +222,61 @@ int main(int argc, char *argv[]) free(nlh); } break; + case SOC_REG_WRITE: + if (argc != 5) { + ONEBOX_PRINT("Usage: ./onebox_util rpine0 soc_reg_write $32bit_address $data\n"); + close(sfd); + return ONEBOX_STATUS_FAILURE; + } + bb_rf_params.value = SOC_REG_WRITE; + bb_rf_params.no_of_values = 4; + bb_addr = strtol(argv[3], NULL, 16); + bb_val = strtol(argv[4], NULL, 16); + bb_rf_params.Data[1] = (uint_16)(bb_addr); + bb_rf_params.Data[2] = (uint_16)(bb_addr >> 16); + bb_rf_params.Data[3] = (uint_16)(bb_val); + bb_rf_params.Data[4] = (uint_16)(bb_val >> 16); + ONEBOX_PRINT("SOC addr: 0x%x value 0x%x\n", bb_addr, bb_val); + ret = bb_read_write_wrapper(bb_rf_params, sfd); + if (ret < 0) { + printf("SOC_REG_WRITE Failed\n"); + } else { + printf("SOC_REG_WRITE SUCCESS\n"); + } + break; + case SOC_REG_READ: + len = sizeof(bb_rf_params); + if (argc != 4) { + ONEBOX_PRINT("Usage: ./onebox_util rpine0 soc_reg_read $32bit_address \n"); + close(sfd); + return ONEBOX_STATUS_FAILURE; + } + bb_rf_params.value = SOC_REG_READ; + bb_rf_params.no_of_values = 4; + bb_addr = strtol(argv[3], NULL, 16); + bb_rf_params.Data[1] = (uint_16)(bb_addr); + bb_rf_params.Data[2] = (uint_16)(bb_addr >> 16); + ONEBOX_PRINT("SOC addr: 0x%x \n", bb_addr); + ONEBOX_PRINT("SOC addr:Data[1] 0x%x Date[2] 0x%x\n", bb_rf_params.Data[1], bb_rf_params.Data[2]); + + ret = bb_read_write_wrapper(bb_rf_params, sfd); + if (ret < 0) { + printf("SOC_REG_READ Failed\n"); + } + nlh = common_recv_mesg_wrapper(sfd, len); + if (nlh == NULL) { + printf("SOC_REG_READ Receive failed\n"); + break; + } else { + memcpy(&bb_rf_params, NLMSG_DATA(nlh), len); + for (ii = 0; ii < bb_rf_params.no_of_values; ii++) { + printf("SOC addr : 0x%x \n", bb_rf_params.Data[ii]); + if (!multiple_bb_read) + break; + } + } + free(nlh); + break; //__9117_CODE_START case REAL_TIME_STATS: if (argc > 5 || argc < 5) { @@ -585,6 +659,45 @@ int main(int argc, char *argv[]) (void *)&reschedule_twt_config, sizeof(wifi_reschedule_twt_config_t)); + break; + case TWT_AUTO_CONFIG: + if (argc != 7) { + usage(); + return ONEBOX_STATUS_FAILURE; + } + memset(&twt_sel_req, 0, sizeof(twt_selection_t)); + twt_sel_req.twt_enable = atoi(argv[3]); + + if ((twt_sel_req.twt_enable != 0) && twt_sel_req.twt_enable != 1) + return ONEBOX_STATUS_FAILURE; + twt_sel_req.rx_latency = atoi(argv[4]); + twt_sel_req.tx_latency = atoi(argv[5]); + if (twt_sel_req.twt_enable + && ((twt_sel_req.rx_latency > MAX_TX_AND_RX_LATENCY_LIMIT) + || (twt_sel_req.tx_latency > MAX_TX_AND_RX_LATENCY_LIMIT))) { + return ONEBOX_STATUS_FAILURE; + } + twt_sel_req.rx_latency = (twt_sel_req.rx_latency == 0) ? 2000 : twt_sel_req.rx_latency; + twt_sel_req.avg_tx_throughput = atoi(argv[6]); + if ((twt_sel_req.rx_latency < 2000) || (twt_sel_req.avg_tx_throughput > (DEVICE_AVG_THROUGHPUT / 2))) { + return ONEBOX_STATUS_FAILURE; + } + if ((twt_sel_req.tx_latency < 200) && (twt_sel_req.tx_latency != 0)) { + return ONEBOX_STATUS_FAILURE; + } + if (twt_sel_req.twt_enable == 1) { + twt_sel_req.device_avg_throughput = DEVICE_AVG_THROUGHPUT; + twt_sel_req.estimated_extra_wake_duration_percent = ESTIMATE_EXTRA_WAKE_DURATION_PERCENT; + twt_sel_req.twt_tolerable_deviation = TWT_TOLERABLE_DEVIATION; + twt_sel_req.default_wake_interval_ms = TWT_DEFAULT_WAKE_INTERVAL_MS; + twt_sel_req.default_minimum_wake_duration_ms = TWT_DEFAULT_WAKE_DURATION_MS; + twt_sel_req.beacon_wake_up_count_after_sp = MAX_BEACON_WAKE_UP_AFTER_SP; + } else { + twt_sel_req.avg_tx_throughput = 0; + twt_sel_req.rx_latency = 0; + twt_sel_req.tx_latency = 0; + } + prepare_and_send_nl_cmd(sfd, TWT_AUTO_CONFIG, 0, WLAN_PACKET, (void *)&twt_sel_req, sizeof(twt_selection_t)); break; #endif case SET_HW_BMISS_THRESHOLD: @@ -807,6 +920,10 @@ int getcmdnumber(char *command, char *ifName) return RSI_MULTIPLE_BB_READ; } else if (!strcmp(command, "bb_read") && !strncmp(ifName, "rpine", 5)) { return RSI_SET_BB_READ; + } else if (!strcmp(command, "soc_reg_read") && !strncmp(ifName, "rpine", 5)) { + return SOC_REG_READ; + } else if (!strcmp(command, "soc_reg_write") && !strncmp(ifName, "rpine", 5)) { + return SOC_REG_WRITE; //__9117_CODE_START } else if (!strcmp(command, "real_time_stats") && !strncmp(ifName, "rpine", 5)) { return REAL_TIME_STATS; @@ -824,6 +941,8 @@ int getcmdnumber(char *command, char *ifName) return TWT_STATUS; } else if (!strcmp(command, "reschedule_twt") && !strncmp(ifName, "rpine0", 5)) { return RESCHEDULE_TWT; + } else if (!strcmp(command, "twt_auto_config") && !strncmp(ifName, "rpine0", 5)) { + return TWT_AUTO_CONFIG; } #endif else if (!strcmp(command, "hw_bmiss_threshold") && !strncmp(ifName, "rpine0", 5)) { @@ -862,6 +981,7 @@ void usage() "twt_retry_limit twt_retry_interval twt_req_type twt_enable wake_duration_unit\n"); ONEBOX_PRINT("Usage:./onebox_util rpine0 twt_status\n"); ONEBOX_PRINT("Usage:./onebox_util rpine0 reschedule_twt twt_flow_id twt_action duration\n"); + ONEBOX_PRINT("Usage:./onebox_util rpine0 twt_auto_config twt_enable rx_latency  tx_latency avg_tx_throughput\n"); #endif ONEBOX_PRINT("Usage:./onebox_util rpine0 hw_bmiss_threshold value\n"); ONEBOX_PRINT("Usage:./onebox_util rpine0 keep_alive value\n"); @@ -876,6 +996,8 @@ void usage() "dpd SIFSTransmitenable pwrsave_options\n"); ONEBOX_PRINT("Usage: onebox_util base_interface master_read $32bit_address $no_bytes_to_read )\n"); ONEBOX_PRINT("Usage: onebox_util base_interface master_write $32bit_address $no_bytes_to_write $data)\n"); + ONEBOX_PRINT("Usage: ./onebox_util rpine0 soc_reg_write $32bit_address $data\n"); + ONEBOX_PRINT("Usage: ./onebox_util rpine0 soc_reg_read $32bit_address\n"); //__9117_CODE_END return; } diff --git a/apps/per_util.h b/apps/per_util.h index f3143be..5666f30 100755 --- a/apps/per_util.h +++ b/apps/per_util.h @@ -333,6 +333,9 @@ typedef struct wlan_9116_features_s { #define RSI_MULTIPLE_BB_READ 0x03 #define RSI_SET_BB_RF 5 #define MASTER_OPS 0xA1 +#define SOC_REG_WRITE 18 +#define SOC_REG_READ 19 + struct bb_rf_param_t { unsigned short Data[1024]; unsigned short no_of_fields; @@ -477,6 +480,7 @@ unsigned int validate_11ax_lsig_len(per_params_t *per_params); #define TWT_CONFIG 11 #define TWT_STATUS 13 #define RESCHEDULE_TWT 17 +#define TWT_AUTO_CONFIG 23 #define MAX_TWT_SUSPEND_DURATION 0x5265c00 //TWT STATUS CODES @@ -499,6 +503,7 @@ unsigned int validate_11ax_lsig_len(per_params_t *per_params); #define TWT_INACTIVE_NO_AP_SUPPORT 16 #define TWT_RESCHEDULE_SUCC 17 #define TWT_RESCHEDULE_FAIL 18 +#define TWT_AUTO_CONFIG_FAIL 19 // TWT request structure to configure a session typedef struct rsi_twt_user_params_s { @@ -572,6 +577,31 @@ typedef struct { uint64_t suspend_duration; } wifi_reschedule_twt_config_t; +// Use case based twt selection +typedef struct twt_selection_s { + uint8_t twt_enable; + uint16_t avg_tx_throughput; + uint32_t tx_latency; + uint32_t rx_latency; + uint16_t device_avg_throughput; + uint8_t estimated_extra_wake_duration_percent; + uint8_t twt_tolerable_deviation; + uint32_t default_wake_interval_ms; + uint32_t default_minimum_wake_duration_ms; + uint8_t beacon_wake_up_count_after_sp; +} twt_selection_t; + +#define DEVICE_AVG_THROUGHPUT 20000 +#define ESTIMATE_EXTRA_WAKE_DURATION_PERCENT 0 +#define TWT_TOLERABLE_DEVIATION 10 +#define TWT_DEFAULT_WAKE_INTERVAL_MS 1024 // in milli seconds +#define TWT_DEFAULT_WAKE_DURATION_MS 8 // in milli seconds +#define MAX_TX_AND_RX_LATENCY_LIMIT 22118400 // 6hrs in milli seconds +#define MAX_BEACON_WAKE_UP_AFTER_SP \ + 2 // The number of beacons after the service period completion for which the module wakes up and listens for any pending RX. +#define MIN_OF_2(X, Y) (X > Y) ? Y : X +#define MIN_OF_3(A, B, C) (A > B) ? ((B > C) ? C : B) : ((A > C) ? C : A) + int check_twt_status(rsi_twt_status_resp *twt_resp, int sfd, struct nlmsghdr *nlh); int twt_status_wrapper(int sock_fd, int len); int twt_config_wrapper(rsi_twt_user_params twt_params, int sock_fd); @@ -621,7 +651,7 @@ int per_transmit_packet_wrapper(per_packet_t per_packet, int cmd, int sock_fd); int per_transmit_wrapper(per_params_t per_params, int cmd, int sock_fd); int send_get_rssi_frame_to_drv(int sock_fd); int send_filter_broadcast_frame_to_drv(struct fltr_bcast bcast, int sock_fd); -int send_bb_write_frame_to_drv(struct bb_rf_param_bt_t bb_rf_params, int sfd); +int send_bb_read_write_frame_to_drv(struct bb_rf_param_bt_t bb_rf_params, int sfd); #define ONEBOX_STATUS_FAILURE -1 #define ONEBOX_STATUS_SUCCESS 0 #define ONEBOX_STATUS int_32 diff --git a/apps/transmit.c b/apps/transmit.c index 6e5dc5b..f4e6bac 100755 --- a/apps/transmit.c +++ b/apps/transmit.c @@ -14,6 +14,13 @@ #include #include "per_util.h" +#define FCC 0x00 +#define ETSI 0x01 +#define TELEC 0x02 +#define WORLD 0x03 +#define WORLD_SAFE 0x05 +#define SRRC 0x06 + int is11brate(unsigned int rate) { switch (rate) { @@ -93,10 +100,10 @@ int main(int argc, char *argv[]) tx_pwr += 32; } #endif - if ((tx_pwr >= 0 && tx_pwr <= 30) || tx_pwr == 127) { + if (tx_pwr >= -15 && tx_pwr <= 127) { per_params.power = tx_pwr; } else { - printf("Invalid tx_pwr is given by user . Please enter tx_pwr between 0 to 30 (or) tx_pwr == 127 \n"); + printf("Invalid tx_pwr is given by user . Please enter tx_pwr between -15 to 127 \n"); exit(0); } @@ -179,13 +186,53 @@ int main(int argc, char *argv[]) if ((per_params.pkt_length - (per_params.aggr_count * PER_AGGR_LIMIT_PER_PKT)) > 0) { per_params.aggr_count++; } - if (per_params.aggr_count == 1) { - per_params.aggr_enable = 0; - per_params.aggr_count = 0; - } per_params.no_of_pkts = atoi(argv[10]); per_params.delay = atoi(argv[11]); per_params.ctry_region = atoi(argv[12]); + + if (per_params.ctry_region == 255) { + /*** Remove Me When Updated in Doc and More regions are added*/ + per_params.ctry_region = 3; /* changing ctry_region to 3 from 127 to make sure same value in PER and End-to-End*/ + } else if (((per_params.ctry_region < 0) || (per_params.ctry_region > 2)) && (per_params.ctry_region != 5) + && (per_params.ctry_region != 6)) { + printf("Invalid Country region \n"); + printf("Valid country regions are : 0- FCC(US), 1- ETSI(Europe), 2-JP (japan), 5-WORLD_SAFE, 6-SRRC (china), " + "255-World\n"); + return -1; + } + + if (per_params.ctry_region == FCC) { + if ((chan_number < 1) || (chan_number > 11)) { + printf("Invalid Channel (%d) selected in FCC Region\n", chan_number); + return -1; + } + } else if (per_params.ctry_region == ETSI) { + if ((chan_number < 1) || (chan_number > 13)) { + printf("Invalid Channel (%d) selected in ETSI Region\n", chan_number); + return -1; + } + } else if (per_params.ctry_region == TELEC) { + if ((chan_number < 1) || (chan_number > 14)) { + printf("Invalid Channel (%d) selected in JP Region\n", chan_number); + return -1; + } + } else if (per_params.ctry_region == WORLD_SAFE) { + if ((chan_number < 1) || (chan_number > 14)) { + printf("Invalid Channel (%d) selected in WORLD_SAFE Region\n", chan_number); + return -1; + } + } else if (per_params.ctry_region == SRRC) { + if ((chan_number < 1) || (chan_number > 13)) { + printf("Invalid Channel (%d) selected in SRRC Region\n", chan_number); + return -1; + } + } else if (per_params.ctry_region == WORLD) { + if ((chan_number < 1) || (chan_number > 13)) { + printf("Invalid Channel (%d) selected in WORLD Region\n", chan_number); + return -1; + } + } + #if 1 if (tx_pktlen > 1536 && per_params.aggr_enable == 0) { printf("Invalid length,Give the length <= 1536 \n"); @@ -317,14 +364,6 @@ int main(int argc, char *argv[]) printf("DELAY : %d\n", per_params.delay); printf("CTRY_REGION : %d\n", per_params.ctry_region); - if (per_params.ctry_region == 255) { - /*** Remove Me When Updated in Doc and More regions are added*/ - per_params.ctry_region = 3; /* changing ctry_region to 3 from 127 to make sure same value in PER and End-to-End*/ - } else if ((per_params.ctry_region < 0) || (per_params.ctry_region > 2)) { - printf("Invalid Country region \n"); - printf("Valid country regions are : 0- FCC(US), 1- ETSI(Europe), 2-JP (japan), 255-World\n"); - return -1; - } //__9117_CODE_START memset(&per_params.heconf, 0, sizeof(per_params.heconf)); diff --git a/apps/update_bt_ble_gain_table.c b/apps/update_bt_ble_gain_table.c index cf4c5a6..3b8a427 100755 --- a/apps/update_bt_ble_gain_table.c +++ b/apps/update_bt_ble_gain_table.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -210,10 +211,10 @@ int main(int argc, char *argv[]) int_32 sfd; update_bt_ble_gain_table_t table_info; uint_16 max_struct_length; - uint_16 status; + int_16 status; FILE *fp; - uint_32 string_matched, i, max_check, str_indx; + uint_32 string_matched, i = 0, max_check, str_indx; uint_32 ret = ONEBOX_STATUS_SUCCESS, count, comment_decoded = 0; int_32 no_of_structs = 0; uint_8 str[MAX_STRUCT_SIZE], *str_tmp, ch; @@ -382,7 +383,11 @@ int main(int argc, char *argv[]) if (status == 0) { printf("Gain table updated successfully \n\n"); } else { - printf("Gain table update failed with status : %x \n\n", status); + if (status == -EINVAL) { + printf("ERROR : Update BLE Gain table not supported for ACx modules \n\n", status); + } else { + printf("Gain table update failed with status : %x \n\n", status); + } goto file_failure; } } diff --git a/apps/update_wlan_gain_table.c b/apps/update_wlan_gain_table.c index 4ce4938..44101bd 100755 --- a/apps/update_wlan_gain_table.c +++ b/apps/update_wlan_gain_table.c @@ -326,10 +326,6 @@ int update_wlan_gain_table(int argc, char *argv[], char *ifName, int sfd) ret = ONEBOX_STATUS_FAILURE; end_of_file: fclose(fp); - if (ret == ONEBOX_STATUS_SUCCESS) { - close(sfd); - printf("\n**************Successfully completed programming %d gain tables ************* \n\n", (no_of_structs - 1)); - } #ifdef DEBUG printf("return %d\n", ret); #endif diff --git a/docs/release-notes/index.md b/docs/release-notes/index.md old mode 100644 new mode 100755 index bd7143f..78a6388 --- a/docs/release-notes/index.md +++ b/docs/release-notes/index.md @@ -1,229 +1,466 @@ -# **SiWT917 2.10.0.5 RCP Release Notes** -## **Release Details** - -|**Item**|**Details**| -| :- | :- | -|Release date|28th February, 2024| -|Firmware Version|1711.2.10.1.0.0.5| -|Package Name|SiWT917.2.10.0.5| -|Linux Kernel Version support|From v3.18 to v5.19| -|Host interfaces supported|SDIO| -|Operating Modes Supported|Wi-Fi STA, Wi-Fi AP, Wi-Fi STA+BLE, Wi-Fi STA+AP, BLE| -|Build Quality|GA| - -- SiWT917 release consists of the following components - - Firmware - SiWx917 Firmware Binary - - rsi - SiWT917 RCP driver source code - - apps - contains driver tools source code - - release - contains kernel modules and script files -- This software is meant only for use with designs based on SiWx917 Silicon -## **Supported hardware/device setups** -- **SiWx917-EB4346A** - SiWx917 Wi-Fi 6 and Bluetooth LE Co-Processor EXP Expansion Kit -- **SiWx917-EB4346B** - SiWx917 Wi-Fi 6 and Bluetooth LE Co-Processor Raspberry Pi Expansion Kit -## **Features Supported** -### **System** -- #### **Operating Modes :** - - Wi-Fi STA (802.11ax, 802.11n), - - Wi-Fi 802.11n AP, - - Wi-Fi STA (802.11ax, 802.11n) + 802.11n AP, - - Wi-Fi STA (802.11ax, 802.11n) + BLE, - - BLE -- #### **Host Interface :** - - SDIO 2.0 -- #### **Power save** - - Deep Sleep - - Connected Sleep - - Host-based wake-up -### **Wi-Fi** -- #### **Wi-Fi protocols** - - IEEE 802.11 b/g/n/ax (2.4GHz) -- #### **Wi-Fi Station mode** - - #### **Scan** - - Selective Scan - - Active/Passive Scanning - - #### **Wi-Fi Security** - - Open Mode - - WPA2 Personal, WPA2 Enhancements - - WPA3 Personal - - Mixed Mode (WPA/WPA2) - - WPA3 Personal Transition Mode (WPA2/WPA3) - - #### **Wi-Fi STA Rejoin** - - #### **Wi-Fi STA Roaming** - - BG Scan - - OKC (Opportunistic Key caching) - - PMK (Pairwise Master Key) caching - - Pre-Authentication - - #### **Wi-Fi Protocol Power save** - - Deep sleep (unconnected state) - - Max PSP - - Enhanced Max PSP - - Fast PSP - - TWT - - #### **QoS** - - WMM-QoS - - #### **Wi-Fi 6 Feature** - - MUMIMO (DL) - - OFDMA (UL/DL) - - iTWT,** TWT I-Frame & TWT Enhancements - - BSS coloring - - MBSSID -- #### **Access Point (AP) mode** - - 8 Client Support - - **Wi-Fi Security** - - Open Mode - - WPA2 Personal - - WPA3 Personal (H2E method only) - - WPA Mixed mode (WPA/WPA2) - - Hidden SSID Mode - - Auto Channel Selection -- #### **WPA2 Enterprise security** - - Method - - PEAP/TTLS/TLS/FAST -- #### **Wi-Fi Concurrency** - - AP+STA (Same channel) - - Scan in AP mode -- #### **Wi-Fi Band/Channels** - - 2.4GHz CH1-11 - - 2.4GHz CH1-13 - - 2.4GHz CH1-14 (Japan) -- #### **Known Security vulnerabilities handled** - - WPA2 KRACK Attacks - - Fragment and Forge Vulnerability -### **BLE** -- Security -- Accept list -- LE PHY(1Mbps, 2Mbps) & Coded PHY(125Kbps, 500kbps) -- Simultaneous scanning on 1M and Coded PHY -- LE dual role topology -- LE data packet length extensions( DLE) -- Asymmetric PHYs -- LE channel selection algorithm 2 (CSA#2) -- Bluetooth 5.4 Qualified -### **Multi-protocol Mode** -- Wi-Fi STA + BLE -### **PTA Coexistence** -- 3 wire coex acting as Wi-Fi Station with external Bluetooth -- 3 wire coex acting as Wi-Fi Station with external Zigbee/OT -## **Changes and Fixes** -### **Wi-Fi** -- #### **Enhancements** - - WPA2/WPA3 Personal, WPA2-EAP Support for Station Mode. - - Wi-Fi STA(802.11n/802.11ax) + 802.11n AP Support - - HE (802.11ax) Support - - Individual TWT Feature Support - - Multiple BSSID feature support as an STA - - Encap offload support - - Added changes to support EDCA suspension. - - Added user configurable option for 11ax rate, beacon miss threshold, keep alive period and get rssi. - - Added ER_SU support in 11ax mode. - - Added GI_LTF reconfigurability option from the user. - - Added support for WPA3 personal security (with H2E method only) in AP mode. -- #### **Fixed Issues** - - None -### **BLE** -- #### **Enhancements** - - None -- #### **Fixed issues** - - Resolved issue where DUT continues transmitting the BLE scan response packets with random address even after changing from random static address to public address. - - The issue with BLE advertise reports not appearing in the scan results of the remote device (mobile) when the address was changed from random to public is resolved. -### **Multi-protocol** -- #### **Enhancements** - - Increased priority of Wi-Fi handling of data transmission in coex mode. -- #### **Fixed issues** - - Handling of PLL value in coex mode. - - Resolved DUT hang issue when performing WLAN ping traffic, BLE advertising enabled and disabled continuously with two peripheral connections. -## **Recommendations** -### **System** -- Set the recommended Power Save Profile (PSP) type to Enhanced Max PSP. -### **Wi-Fi** -- Disable power save for higher throughput values or use FAST PSP power save mode as per application requirement. -### **BLE** -- In BLE, the recommended range of Connection Interval in - - Power Save (BLE Only) - 100 ms to 1.28 s. -- In BLE, during Connection, the configuration of Scan Interval and Scan Window with the same value is not recommended. The suggested ratio of Scan Window to Scan Interval is 3:4. -- In BLE, if a device is acting as Central, the scan window must be less than the existing Connection Interval. The suggested ratio of Scan Window to Connection Interval is 2:3. -- In BLE mode, if scanning and advertising are in progress on the SiWx91x module and it subsequently gets connected and moves to the central role, scanning stops else if it moves to the peripheral role, advertising stops. To further establish a connection to another peripheral device or to a central device, the application should give a command for starting advertising and scanning again. -- Recommend using XTAL clock for BLE instead of RC clock. -### **Multi-protocol** -- For concurrent Wi-Fi + BLE, and while a Wi-Fi connection is active, we recommend setting the ratio of the BLE scan window to BLE scan interval to 1:3 or 1:4. -- Wi-Fi + BLE Advertising - - All standard advertising intervals are supported. As Wi-Fi throughput is increased, a slight difference in on-air advertisements compared to configured intervals may be seen. - - BLE advertising is skipped if the advertising interval collides with Wi-Fi activity. -- Wi-Fi + BLE scanning - - All standard scan intervals are supported. For better scan results, we recommend setting the ratio of the BLE scan window to BLE scan interval to 1:3 or 1:4. - - BLE scanning will be stopped for intervals that collide with Wi-Fi activity. -- Wi-Fi + BLE Central/Peripheral Connections - - All standard connection intervals are supported. - - For a stable connection, use optimal connection intervals and max supervision timeout in the presence of Wi-Fi activity. -- Wi-Fi + BLE Central/Peripheral Data Transfer - - To achieve higher throughput for both Wi-Fi and BLE, use medium connection intervals, such as 45 to 80 ms with maximum supervision timeout. - - Ensure Wi-Fi activity consumes lower intervals. -## **Known Issues** -### **Wi-Fi** -- The set rate cannot be used for setting non-MCS and non-basic rates in kernels above 4.13.16. -- Enterprise security with WPA3 is not supported. -- Tx and Rx Rate information is not being updated in WLAN interface stats reported by the iwconfig tool. -- Auto Channel Selection in AP with WORLD region will not work. -- In Concurrent Mode, Wi-Fi performance may be reduced if more than four clients are connected to DUT-AP. -- Deep sleep is disabled by default. Users can enable it using the default_deep_sleep_enable module_param mentioned in the TRM. -- Observed inconsistency with MQTT keep alive when TWT is enabled -- TWT device (STA) may not go to sleep in between TWT intervals in case of burst data traffic where data transfer happens over multiple TWT service periods. -- Observed interop issue (connectivity failure) with few APs (EERO 6+, EERO PRO 6E, Cisco Catalyst 9120AXID) when device reset/reboot happens on active connection. -- Disconnections were observed with Netgear RAX120 AP in WPA3 security at expected intervals with power save enabled. -- WoWLAN feature is not supported. -- In AP mode, performance is expected to degrade with the addition of the number of clients. -- WFA cases- a few of PMF/AMB cases not passing. -- Hardware Beacon Miss event observed using MBSSID mode. -- Throughput inconsistency(TPUT in Kbps) seen when > 4 clients are connected to DUT as AP mode. -- Around 38% of packet loss observed in RF chamber while running UDP-bidirectional traffic. -- Wi-Fi Client received de-auth from AP when enabled trigger TWT with announced. -- Wi-Fi Client is failed to connect with EAP TLS1.0 version. -- Monitor/sniffer mode - observed 70% packet loss while capturing the on-air data. -- AP Mode - WPA3-PSK - Data transfers failed between the Wi-Fi Clients connected to 917 AP, i.e. ping. -### **BLE** -- The maximum power that BLE can support is 16 dBm. It is not permitted for users to program more than 16dBm. 127 power_index is not supported in this release. -- BLE disconnections are expected with the RC clock in power save. -### **Multi-protocol** -- MU retries in DL MUMIMO are more in CoEx -- Observed Wi-Fi throughput inconsistency (TPUT in Kbps) for STA(Wi-Fi Tx)+BLE Mode while doing BLE scan. -- BLE disconnections observed after two hours, when STA + BLE running simultaneous traffic. -### **System** -- None -## **Limitations and Unsupported Features** -### **System** -- Current software is not validated on [SiWT917M100XGT](https://www.silabs.com/wireless/wi-fi/siwx917-wireless-socs/device.siwt917m100xgt?tab=specs). -### **Wi-Fi** -- Broadcast TWT is not supported. -- AMSDU TX is not supported. -- Fragmentation is not supported. -- AMSDUs within AMPDU is not supported. -- 802.11k is not supported -- 40 MHz bandwidth for the 2.4 GHz band is not supported. -- Low Power mode is not supported. -- In iTWT mode, downlink data traffic should be limited. Disconnections may be observed if AP fails to send all the buffered data in the next TWT Service Period. -- The number of Non-Transmitting BSSIDs processed is limited by the beacon length that can be processed by the stack (which is 1024 bytes). Beacons greater than 1024 Bytes in length will not be processed. -- UL MUMIMO is not supported. -- WPA3 AP supports only the H2E algorithm. -- PMKSA caching is not supported in WPA3 AP mode. -- Wi-Fi performance may be reduced in dense environments. -- 802.11j is not supported. -- Background scan(Scan after DUT-STA connection) and power-save features are not supported for the station mode vap in concurrent mode. -- In Wi-Fi concurrent mode, Both DUT AP and Testbed AP should be in the same channel. The Channel in the hostapd configuration file needs to be updated manually to the same channel as the Testbed AP. -- In Wi-Fi concurrent Mode, DUT-STA doesn't reconnect to testbed AP if the testbed AP changes the channel(Only if DUT-AP is up), also DUT-AP stays in the same channel and continues to beacon. -- WPS mode is not supported. -- GPIO-based power save is not supported. -### **BLE** -- For BLE, if the connection is established with a small connection interval (less than 15 ms), simultaneous roles (i.e., Central + Scanning and Peripheral + Advertising) are not supported. -- BLE maximum two concurrent connections are supported(1 Central connection and 1 Peripheral connection) or (2 Central connections) or (2 Peripheral connections). -- BLE Slave latency value is valid up to 32 only. -- The advertising Extension feature is not supported. -- Isochronous channels feature is not supported. -- The connection subrating feature is not supported. -- LE power controller feature is not supported. -- The EATT feature is not supported. -- Periodic Advertising with response(PAwR) feature is not supported. -- BLE Audio is not supported. -- The feature of dynamically changing the TX power when extended advertising is active is not supported. -### **Multi-protocol** -- BLE may disconnect with Wi-Fi + BLE configuration and Wi-Fi continuous data transfer when the low BLE supervision timeout is configured. When the supervision timeout is configured with the value of 16 seconds, no disconnections are observed. -- In Wi-Fi +BLE configuration with Wi-Fi disconnects, a BLE reconnection issue is observed (Refer the section '[Recommendations](#recommendations)' for stable connections). +# **SiWT917 2.13.0.4 RCP Release Notes** +## **Release Details** + +|**Item**|**Details**| +| :- | :- | +|Release date|8th January, 2025| +|Firmware Version|1711.2.13.1.0.0.4| +|Package Name|SiWT917.2.13.0.4| +|Linux Kernel Version support|From v3.18 to v6.1| +|Host interfaces supported|SDIO| +|Operating Modes Supported|Wi-Fi STA, Wi-Fi AP, Wi-Fi STA+BLE, Wi-Fi STA+AP, BLE| + + +- SiWT917 release consists of the following components + - Firmware - SiWx917 Firmware Binary + - rsi - SiWT917 RCP driver source code + - apps - contains driver tools source code + - release - contains kernel modules and script files + - docs - contains release notes +- This software is meant only for use with designs based on SiWx917 Silicon +## **Supported Hardware OPNs** +|**Hardware**|**OPN (Ordering Part Number)**| +| :- | :- | +|IC OPN|QFN OPN: SIWT917M100XGTBA | +|Expansion kits:|

SiWx917-EB4346A (based on Radio board SiWx917-4346A + [8045A Co-Processor Adapter board] or [8045B Co-Processor Raspberry Pi Adapter board])| + + + +## **Features Supported** +### **System** +- #### **Operating Modes :** + - Wi-Fi STA (802.11ax, 802.11n), + - Wi-Fi 802.11n AP, + - Wi-Fi STA (802.11ax, 802.11n) + 802.11n AP, + - Wi-Fi STA (802.11ax, 802.11n) + BLE, + - BLE +- #### **Host Interface :** + - SDIO 2.0 +- #### **Power save** + - Deep Sleep + - Connected Sleep + - Host-based wake-up +### **Wi-Fi** +- #### **Wi-Fi protocols** + - IEEE 802.11 b/g/n/ax (2.4GHz) +- #### **Wi-Fi Station mode** + - #### **Scan** + - Selective Scan + - Active/Passive Scanning + - #### **Wi-Fi Security** + - Open Mode + - WPA2 Personal, WPA2 Enhancements + - WPA2 Enterprise + - WPA3 Personal + - Mixed Mode (WPA/WPA2) + - WPA3 Personal Transition Mode (WPA2/WPA3) + - #### **Wi-Fi STA Rejoin** + - #### **Wi-Fi STA Roaming** + - BG Scan + - OKC (Opportunistic Key caching) + - PMK (Pairwise Master Key) caching + - Pre-Authentication + - #### **Wi-Fi Protocol Power save** + - Deep sleep (unconnected state) + - Max PSP + - Enhanced Max PSP + - Fast PSP + - TWT + - #### **QoS** + - WMM-QoS + - #### **Wi-Fi 6 Feature** + - MU-MIMO (DL) + - OFDMA (UL/DL) + - iTWT,TWT I-Frame & TWT Enhancements + - BSS coloring + - MBSSID +- #### **Access Point (AP) mode** + - 8 Client Support + - **Wi-Fi Security** + - Open Mode + - WPA2 Personal + - WPA2 Enterprise + - WPA3 Personal (H2E method only) + - WPA Mixed mode (WPA/WPA2) + - Hidden SSID Mode + - Auto Channel Selection +- #### **WPA2 Enterprise security** + - Method + - PEAP/TTLS/TLS/FAST +- #### **Wi-Fi Concurrency** + - AP+STA (Same channel) + - Scan in AP mode +- #### **Wi-Fi Band/Channels** + - 2.4GHz CH1-11 + - 2.4GHz CH1-13 + - 2.4GHz CH1-14 (Japan) +- #### **Known Security vulnerabilities handled** + - WPA2 KRACK Attacks + - Fragment and Forge Vulnerability +### **BLE** +- Security +- Accept list +- LE PHY(1Mbps, 2Mbps) & Coded PHY(125Kbps, 500kbps) +- Simultaneous scanning on 1M and Coded PHY +- LE dual role topology +- LE data packet length extensions( DLE) +- Asymmetric PHYs +- LE channel selection algorithm 2 (CSA#2) +- Bluetooth 5.4 Qualified +### **Multi-protocol Mode** +- Wi-Fi STA + BLE +### **PTA Coexistence** +- 3 wire coex acting as Wi-Fi Station with external Bluetooth +- 3 wire coex acting as Wi-Fi Station with external Zigbee/OT +## **Changes and Fixes** +### **System** +- #### **Enhancements** + - Updated Linux kernel version support upto version 6.1. +- #### **Fixed Issues** + - None +### **Wi-Fi** +- #### **Enhancements** + - Added support for Customer MFG tool. + - Enhancement on WLAN keepalive functionality +- #### **Fixed Issues** + - None +### **BLE** +- #### **Enhancements** + - Added support for reading BLE BBP register in PER mode. +- #### **Fixed issues** + - Resolved memory corruption issue with BLE two connections. + - Resolved BLE pairing failure issue. +### **Multi-protocol** +- #### **Enhancements** + - None +- #### **Fixed issues** + - Fixed Packet loss issue in dense environments. +## **Recommendations** +### **System** +- Set the recommended Power Save Profile (PSP) type to Enhanced Max PSP. +### **Wi-Fi** +- Disable power save for higher throughput values or use FAST PSP power save mode as per application requirement. +### **BLE** +- In BLE, the recommended range of Connection Interval in + - Power Save (BLE Only) - 100 ms to 1.28 s. +- In BLE, during Connection, the configuration of Scan Interval and Scan Window with the same value is not recommended. The suggested ratio of Scan Window to Scan Interval is 3:4. +- In BLE, if a device is acting as Central, the scan window must be less than the existing Connection Interval. The suggested ratio of Scan Window to Connection Interval is 2:3. +- In BLE mode, if scanning and advertising are in progress on the SiWx91x module and it subsequently gets connected and moves to the central role, scanning stops else if it moves to the peripheral role, advertising stops. To further establish a connection to another peripheral device or to a central device, the application should give a command for starting advertising and scanning again. +- Recommend using XTAL clock for BLE instead of RC clock. +### **Multi-protocol** +- For concurrent Wi-Fi + BLE, and while a Wi-Fi connection is active, we recommend setting the ratio of the BLE scan window to BLE scan interval to 1:3 or 1:4. +- Wi-Fi + BLE Advertising + - All standard advertising intervals are supported. As Wi-Fi throughput is increased, a slight difference in on-air advertisements compared to configured intervals may be seen. + - BLE advertising is skipped if the advertising interval collides with Wi-Fi activity. +- Wi-Fi + BLE scanning + - All standard scan intervals are supported. For better scan results, we recommend setting the ratio of the BLE scan window to BLE scan interval to 1:3 or 1:4. + - BLE scanning will be stopped for intervals that collide with Wi-Fi activity. +- Wi-Fi + BLE Central/Peripheral Connections + - All standard connection intervals are supported. + - For a stable connection, use optimal connection intervals and max supervision timeout in the presence of Wi-Fi activity. +- Wi-Fi + BLE Central/Peripheral Data Transfer + - To achieve higher throughput for both Wi-Fi and BLE, use medium connection intervals, such as 45 to 80 ms with maximum supervision timeout. + - Ensure Wi-Fi activity consumes lower intervals. +## **Known Issues** +### **Wi-Fi** +- The set rate cannot be used for setting non-MCS and non-basic rates in kernels above 4.13.16. +- Enterprise security with WPA3 is not supported. +- Tx and Rx Rate information is not being updated in WLAN interface stats reported by the iwconfig tool. +- Auto Channel Selection in AP with WORLD region will not work. +- In Concurrent Mode, Wi-Fi performance may be reduced if more than four clients are connected to DUT-AP. +- Deep sleep is disabled by default. Users can enable it using the default_deep_sleep_enable module_param mentioned in the TRM. +- Observed inconsistency with MQTT keep alive when TWT is enabled +- TWT device (STA) may not go to sleep in between TWT intervals in case of burst data traffic where data transfer happens over multiple TWT service periods. +- Observed interop issue (connectivity failure) with few APs (EERO 6+, EERO PRO 6E, Cisco Catalyst 9120AXID) when device reset/reboot happens on active connection. +- Disconnections were observed with Netgear RAX120 AP in WPA3 security at expected intervals with power save enabled. +- WoWLAN feature is not supported. +- In AP mode, performance is expected to degrade with the addition of the number of clients. +- WFA cases- a few of PMF/AMB cases not passing. +- Hardware Beacon Miss event observed using MBSSID mode. +- Throughput inconsistency(TPUT in Kbps) seen when > 4 clients are connected to DUT as AP mode. +- Around 38% of packet loss observed in RF chamber while running UDP-bidirectional traffic. +- Wi-Fi Client received de-auth from AP when enabled trigger TWT with announced. +- Wi-Fi Client is failed to connect with EAP TLS1.0 version. +- Monitor/sniffer mode - observed 70% packet loss while capturing the on-air data. +- AP Mode - WPA3-PSK - Data transfers failed between the Wi-Fi Clients connected to 917 AP, i.e. ping. +- It is recommended to compile the driver locally to support Wi-Fi STA + AP mode. +### **BLE** +- The maximum power that BLE can support is 16 dBm. It is not permitted for users to program more than 16dBm. 127 power_index is not supported in this release. +- BLE disconnections are expected with the RC clock in power save. +### **Multi-protocol** +- MU retries in DL MU-MIMO are more in CoEx +- Observed Wi-Fi throughput inconsistency (TPUT in Kbps) for STA(Wi-Fi Tx)+BLE Mode while doing BLE scan. +- BLE disconnections observed after two hours, when STA + BLE running simultaneous traffic. +### **System** +- None +## **Limitations and Unsupported Features** +### **System** +- None +### **Wi-Fi** +- Broadcast TWT is not supported. +- AMSDU TX is not supported. +- Fragmentation is not supported. +- AMSDUs within AMPDU is not supported. +- 802.11k is not supported +- 40 MHz bandwidth is not supported. +- Low Power mode is not supported. +- In iTWT mode, downlink data traffic should be limited. Disconnections may be observed if AP fails to send all the buffered data in the next TWT Service Period. +- The number of Non-Transmitting BSSIDs processed is limited by the beacon length that can be processed by the stack (which is 1024 bytes). Beacons greater than 1024 Bytes in length will not be processed. +- UL MU-MIMO is not supported. +- WPA3 AP supports only the H2E algorithm. +- PMKSA caching is not supported in WPA3 AP mode. +- Wi-Fi performance may be reduced in dense environments. +- 802.11j is not supported. +- Background scan(Scan after DUT-STA connection) and power-save features are not supported for the station mode vap in concurrent mode. +- In Wi-Fi concurrent mode, Both DUT AP and Testbed AP should be in the same channel. The Channel in the hostapd configuration file needs to be updated manually to the same channel as the Testbed AP. +- In Wi-Fi concurrent Mode, DUT-STA doesn't reconnect to testbed AP if the testbed AP changes the channel(Only if DUT-AP is up), also DUT-AP stays in the same channel and continues to beacon. +- WPS mode is not supported. +- GPIO-based power save is not supported. + +### **BLE** +- For BLE, if the connection is established with a small connection interval (less than 15 ms), simultaneous roles (i.e., Central + Scanning and Peripheral + Advertising) are not supported. +- BLE maximum two concurrent connections are supported(1 Central connection and 1 Peripheral connection) or (2 Central connections) or (2 Peripheral connections). +- The advertising Extension feature is not supported. +- Isochronous channels feature is not supported. +- The connection subrating feature is not supported. +- LE power controller feature is not supported. +- The EATT feature is not supported. +- Periodic Advertising with response(PAwR) feature is not supported. +- BLE Audio is not supported. +- The dynamic adjustment of TX power while extended advertising is active is not supported. +- Peripheral Latency feature is not supported. + +### **Multi-protocol** +- BLE may disconnect with Wi-Fi + BLE configuration and Wi-Fi continuous data transfer when the low BLE supervision timeout is configured. When the supervision timeout is configured with the value of 16 seconds, no disconnections are observed. +- In Wi-Fi +BLE configuration with Wi-Fi disconnects, a BLE reconnection issue is observed (Refer the section '[Recommendations](#recommendations)' for stable connections). + +# **SiWT917 2.10.0.5 RCP Release Notes** +## **Release Details** + +|**Item**|**Details**| +| :- | :- | +|Release date|28th February, 2024| +|Firmware Version|1711.2.10.1.0.0.5| +|Package Name|SiWT917.2.10.0.5| +|Linux Kernel Version support|From v3.18 to v5.19| +|Host interfaces supported|SDIO| +|Operating Modes Supported|Wi-Fi STA, Wi-Fi AP, Wi-Fi STA+BLE, Wi-Fi STA+AP, BLE| +|Build Quality|GA| + +- SiWT917 release consists of the following components + - Firmware - SiWx917 Firmware Binary + - rsi - SiWT917 RCP driver source code + - apps - contains driver tools source code + - release - contains kernel modules and script files +- This software is meant only for use with designs based on SiWx917 Silicon +## **Supported hardware/device setups** +- **SiWx917-EB4346A** - SiWx917 Wi-Fi 6 and Bluetooth LE Co-Processor EXP Expansion Kit +- **SiWx917-EB4346B** - SiWx917 Wi-Fi 6 and Bluetooth LE Co-Processor Raspberry Pi Expansion Kit +## **Features Supported** +### **System** +- #### **Operating Modes :** + - Wi-Fi STA (802.11ax, 802.11n), + - Wi-Fi 802.11n AP, + - Wi-Fi STA (802.11ax, 802.11n) + 802.11n AP, + - Wi-Fi STA (802.11ax, 802.11n) + BLE, + - BLE +- #### **Host Interface :** + - SDIO 2.0 +- #### **Power save** + - Deep Sleep + - Connected Sleep + - Host-based wake-up +### **Wi-Fi** +- #### **Wi-Fi protocols** + - IEEE 802.11 b/g/n/ax (2.4GHz) +- #### **Wi-Fi Station mode** + - #### **Scan** + - Selective Scan + - Active/Passive Scanning + - #### **Wi-Fi Security** + - Open Mode + - WPA2 Personal, WPA2 Enhancements + - WPA3 Personal + - Mixed Mode (WPA/WPA2) + - WPA3 Personal Transition Mode (WPA2/WPA3) + - #### **Wi-Fi STA Rejoin** + - #### **Wi-Fi STA Roaming** + - BG Scan + - OKC (Opportunistic Key caching) + - PMK (Pairwise Master Key) caching + - Pre-Authentication + - #### **Wi-Fi Protocol Power save** + - Deep sleep (unconnected state) + - Max PSP + - Enhanced Max PSP + - Fast PSP + - TWT + - #### **QoS** + - WMM-QoS + - #### **Wi-Fi 6 Feature** + - MUMIMO (DL) + - OFDMA (UL/DL) + - iTWT,** TWT I-Frame & TWT Enhancements + - BSS coloring + - MBSSID +- #### **Access Point (AP) mode** + - 8 Client Support + - **Wi-Fi Security** + - Open Mode + - WPA2 Personal + - WPA3 Personal (H2E method only) + - WPA Mixed mode (WPA/WPA2) + - Hidden SSID Mode + - Auto Channel Selection +- #### **WPA2 Enterprise security** + - Method + - PEAP/TTLS/TLS/FAST +- #### **Wi-Fi Concurrency** + - AP+STA (Same channel) + - Scan in AP mode +- #### **Wi-Fi Band/Channels** + - 2.4GHz CH1-11 + - 2.4GHz CH1-13 + - 2.4GHz CH1-14 (Japan) +- #### **Known Security vulnerabilities handled** + - WPA2 KRACK Attacks + - Fragment and Forge Vulnerability +### **BLE** +- Security +- Accept list +- LE PHY(1Mbps, 2Mbps) & Coded PHY(125Kbps, 500kbps) +- Simultaneous scanning on 1M and Coded PHY +- LE dual role topology +- LE data packet length extensions( DLE) +- Asymmetric PHYs +- LE channel selection algorithm 2 (CSA#2) +- Bluetooth 5.4 Qualified +### **Multi-protocol Mode** +- Wi-Fi STA + BLE +### **PTA Coexistence** +- 3 wire coex acting as Wi-Fi Station with external Bluetooth +- 3 wire coex acting as Wi-Fi Station with external Zigbee/OT +## **Changes and Fixes** +### **Wi-Fi** +- #### **Enhancements** + - WPA2/WPA3 Personal, WPA2-EAP Support for Station Mode. + - Wi-Fi STA(802.11n/802.11ax) + 802.11n AP Support + - HE (802.11ax) Support + - Individual TWT Feature Support + - Multiple BSSID feature support as an STA + - Encap offload support + - Added changes to support EDCA suspension. + - Added user configurable option for 11ax rate, beacon miss threshold, keep alive period and get rssi. + - Added ER_SU support in 11ax mode. + - Added GI_LTF reconfigurability option from the user. + - Added support for WPA3 personal security (with H2E method only) in AP mode. +- #### **Fixed Issues** + - None +### **BLE** +- #### **Enhancements** + - None +- #### **Fixed issues** + - Resolved issue where DUT continues transmitting the BLE scan response packets with random address even after changing from random static address to public address. + - The issue with BLE advertise reports not appearing in the scan results of the remote device (mobile) when the address was changed from random to public is resolved. +### **Multi-protocol** +- #### **Enhancements** + - Increased priority of Wi-Fi handling of data transmission in coex mode. +- #### **Fixed issues** + - Handling of PLL value in coex mode. + - Resolved DUT hang issue when performing WLAN ping traffic, BLE advertising enabled and disabled continuously with two peripheral connections. +## **Recommendations** +### **System** +- Set the recommended Power Save Profile (PSP) type to Enhanced Max PSP. +### **Wi-Fi** +- Disable power save for higher throughput values or use FAST PSP power save mode as per application requirement. +### **BLE** +- In BLE, the recommended range of Connection Interval in + - Power Save (BLE Only) - 100 ms to 1.28 s. +- In BLE, during Connection, the configuration of Scan Interval and Scan Window with the same value is not recommended. The suggested ratio of Scan Window to Scan Interval is 3:4. +- In BLE, if a device is acting as Central, the scan window must be less than the existing Connection Interval. The suggested ratio of Scan Window to Connection Interval is 2:3. +- In BLE mode, if scanning and advertising are in progress on the SiWx91x module and it subsequently gets connected and moves to the central role, scanning stops else if it moves to the peripheral role, advertising stops. To further establish a connection to another peripheral device or to a central device, the application should give a command for starting advertising and scanning again. +- Recommend using XTAL clock for BLE instead of RC clock. +### **Multi-protocol** +- For concurrent Wi-Fi + BLE, and while a Wi-Fi connection is active, we recommend setting the ratio of the BLE scan window to BLE scan interval to 1:3 or 1:4. +- Wi-Fi + BLE Advertising + - All standard advertising intervals are supported. As Wi-Fi throughput is increased, a slight difference in on-air advertisements compared to configured intervals may be seen. + - BLE advertising is skipped if the advertising interval collides with Wi-Fi activity. +- Wi-Fi + BLE scanning + - All standard scan intervals are supported. For better scan results, we recommend setting the ratio of the BLE scan window to BLE scan interval to 1:3 or 1:4. + - BLE scanning will be stopped for intervals that collide with Wi-Fi activity. +- Wi-Fi + BLE Central/Peripheral Connections + - All standard connection intervals are supported. + - For a stable connection, use optimal connection intervals and max supervision timeout in the presence of Wi-Fi activity. +- Wi-Fi + BLE Central/Peripheral Data Transfer + - To achieve higher throughput for both Wi-Fi and BLE, use medium connection intervals, such as 45 to 80 ms with maximum supervision timeout. + - Ensure Wi-Fi activity consumes lower intervals. +## **Known Issues** +### **Wi-Fi** +- The set rate cannot be used for setting non-MCS and non-basic rates in kernels above 4.13.16. +- Enterprise security with WPA3 is not supported. +- Tx and Rx Rate information is not being updated in WLAN interface stats reported by the iwconfig tool. +- Auto Channel Selection in AP with WORLD region will not work. +- In Concurrent Mode, Wi-Fi performance may be reduced if more than four clients are connected to DUT-AP. +- Deep sleep is disabled by default. Users can enable it using the default_deep_sleep_enable module_param mentioned in the TRM. +- Observed inconsistency with MQTT keep alive when TWT is enabled +- TWT device (STA) may not go to sleep in between TWT intervals in case of burst data traffic where data transfer happens over multiple TWT service periods. +- Observed interop issue (connectivity failure) with few APs (EERO 6+, EERO PRO 6E, Cisco Catalyst 9120AXID) when device reset/reboot happens on active connection. +- Disconnections were observed with Netgear RAX120 AP in WPA3 security at expected intervals with power save enabled. +- WoWLAN feature is not supported. +- In AP mode, performance is expected to degrade with the addition of the number of clients. +- WFA cases- a few of PMF/AMB cases not passing. +- Hardware Beacon Miss event observed using MBSSID mode. +- Throughput inconsistency(TPUT in Kbps) seen when > 4 clients are connected to DUT as AP mode. +- Around 38% of packet loss observed in RF chamber while running UDP-bidirectional traffic. +- Wi-Fi Client received de-auth from AP when enabled trigger TWT with announced. +- Wi-Fi Client is failed to connect with EAP TLS1.0 version. +- Monitor/sniffer mode - observed 70% packet loss while capturing the on-air data. +- AP Mode - WPA3-PSK - Data transfers failed between the Wi-Fi Clients connected to 917 AP, i.e. ping. +### **BLE** +- The maximum power that BLE can support is 16 dBm. It is not permitted for users to program more than 16dBm. 127 power_index is not supported in this release. +- BLE disconnections are expected with the RC clock in power save. +### **Multi-protocol** +- MU retries in DL MUMIMO are more in CoEx +- Observed Wi-Fi throughput inconsistency (TPUT in Kbps) for STA(Wi-Fi Tx)+BLE Mode while doing BLE scan. +- BLE disconnections observed after two hours, when STA + BLE running simultaneous traffic. +### **System** +- None +## **Limitations and Unsupported Features** +### **System** +- Current software is not validated on [SiWT917M100XGT](https://www.silabs.com/wireless/wi-fi/siwx917-wireless-socs/device.siwt917m100xgt?tab=specs). +### **Wi-Fi** +- Broadcast TWT is not supported. +- AMSDU TX is not supported. +- Fragmentation is not supported. +- AMSDUs within AMPDU is not supported. +- 802.11k is not supported +- 40 MHz bandwidth for the 2.4 GHz band is not supported. +- Low Power mode is not supported. +- In iTWT mode, downlink data traffic should be limited. Disconnections may be observed if AP fails to send all the buffered data in the next TWT Service Period. +- The number of Non-Transmitting BSSIDs processed is limited by the beacon length that can be processed by the stack (which is 1024 bytes). Beacons greater than 1024 Bytes in length will not be processed. +- UL MUMIMO is not supported. +- WPA3 AP supports only the H2E algorithm. +- PMKSA caching is not supported in WPA3 AP mode. +- Wi-Fi performance may be reduced in dense environments. +- 802.11j is not supported. +- Background scan(Scan after DUT-STA connection) and power-save features are not supported for the station mode vap in concurrent mode. +- In Wi-Fi concurrent mode, Both DUT AP and Testbed AP should be in the same channel. The Channel in the hostapd configuration file needs to be updated manually to the same channel as the Testbed AP. +- In Wi-Fi concurrent Mode, DUT-STA doesn't reconnect to testbed AP if the testbed AP changes the channel(Only if DUT-AP is up), also DUT-AP stays in the same channel and continues to beacon. +- WPS mode is not supported. +- GPIO-based power save is not supported. +### **BLE** +- For BLE, if the connection is established with a small connection interval (less than 15 ms), simultaneous roles (i.e., Central + Scanning and Peripheral + Advertising) are not supported. +- BLE maximum two concurrent connections are supported(1 Central connection and 1 Peripheral connection) or (2 Central connections) or (2 Peripheral connections). +- BLE Slave latency value is valid up to 32 only. +- The advertising Extension feature is not supported. +- Isochronous channels feature is not supported. +- The connection subrating feature is not supported. +- LE power controller feature is not supported. +- The EATT feature is not supported. +- Periodic Advertising with response(PAwR) feature is not supported. +- BLE Audio is not supported. +- The feature of dynamically changing the TX power when extended advertising is active is not supported. +### **Multi-protocol** +- BLE may disconnect with Wi-Fi + BLE configuration and Wi-Fi continuous data transfer when the low BLE supervision timeout is configured. When the supervision timeout is configured with the value of 16 seconds, no disconnections are observed. +- In Wi-Fi +BLE configuration with Wi-Fi disconnects, a BLE reconnection issue is observed (Refer the section '[Recommendations](#recommendations)' for stable connections). + diff --git a/rsi/Automation_README b/release/Automation_README similarity index 54% rename from rsi/Automation_README rename to release/Automation_README index 8ce421f..22bdbc7 100755 --- a/rsi/Automation_README +++ b/release/Automation_README @@ -1,25 +1,19 @@ Note : 1. osd_common_insert.sh file is used to enable different configuration option used by the driver. - 2. start_rs9116.sh file is used to load the driver along with different modes. - 3. To use these scripts just connect our RS9116 Module to the platform and run start_rs9116.sh file.Command to execute the file : ./start_rs9116.sh - 4. Update sta_settings.conf file present in scripts folder as per use case. - 5. Update ap configuration files (i.e. ap_open.conf,ap_wpa.conf etc) present in scripts folder as per use case. + 2. start_SiWT917.sh file is used to load the driver along with different modes. + 3. To use these scripts just connect our SiWT917 Module to the platform and run start_SiWT917.sh file.Command to execute the file : ./start_SiWT917.sh + 4. Update sta_settings.conf file present in release folder as per use case. + 5. Update ap configuration files (i.e. ap_open.conf,ap_wpa.conf etc) present in release folder as per use case. Diffrent modes : • AP • STA • AP_STA(Here STA will be initiated first then AP. Configure the ap configuration files according before running the script). -• BT • BLE -• STA_BT -• AP_BT • STA_BLE -• AP_BLE -• STA_BT_BLE -• AP_BT_BLE -Command to execute the file : ./start_rs9116.sh +Command to execute the file : ./start_SiWT917.sh Note : 1. For IMX6 Wand Borad do the following updation before running the script : - a. Change "killall" commands with "pkill" in the script(start_rs9116.sh file). - b. Disable "rfkill unblock all " command from the script(start_rs9116.sh file). + a. Change "killall" commands with "pkill" in the script(start_SiWT917.sh file). + b. Disable "rfkill unblock all " command from the script(start_SiWT917.sh file). 2. For Linux PCs and RPI, no need to update anything. diff --git a/release/osd_common_insert.sh b/release/osd_common_insert.sh index 30378e0..c5ac6af 100755 --- a/release/osd_common_insert.sh +++ b/release/osd_common_insert.sh @@ -63,6 +63,7 @@ BLE_POWER_SAVE_OPTIONS=2 #BIT[7:4] - Number of Peripheral Roles Allowed BLE_ROLES=19 +BLE_VENDOR_FEATURE_BITMAP=0x00 #BIT(0) SCAN_RESP_DISABLE BT_RF_TX_POWER_MODE=0 @@ -130,7 +131,7 @@ SLEEP_CLK_SOURCE_SEL=0 #0 - UULP_GPIO_3 #1 - UULP_GPIO_0 #Default value is 0. -SLEEP_IND_GPIO_SEL=0 +SLEEP_IND_GPIO_SEL=1 #Host Interface on Demand Feature has the following possible values. #0 - Disable Host Interface on Demand Feature @@ -138,6 +139,20 @@ SLEEP_IND_GPIO_SEL=0 #Default value for Host Interface on Demand Feature Options is 0, which indicates that Host Interface on Demand Feature is disabled. HOST_INTF_ON_DEMAND=0 +#COUNTRY Selection +#0 - World Domain +#840 - US Domain Maps to US Region +#276 - Germany Maps to EU Region +#392 - Japan Maps to Japan Region +COUNTRY_CODE=0 + +#PTA Configuration +#0 - PTA_Disabled +#1 - PTA_CONFIG_1 +#2 - PTA_CONFIG_2 +#3 - PTA_CONFIG_3 +PTA_CONFIG=0 + #Extended Options. #ext_opt value should be 0,1,2 or 3 as per front end switch selection. #0 - Reserved @@ -175,19 +190,20 @@ VALUE_OF_NG_CB=0 #'BIT(1)' - Trigger Response For BK #'BIT(2)' - Trigger Response For VI #'BIT(3)' - Trigger Response For VO -TRIGGER_RESP_IND=0 +TRIGGER_RESP_IND=0xF #Gaurd interval -#'0' for 8us -#'1' for 16us -#'2' for 32us -GUARD_INTERVAL=0 +#'0' - 1x(3.2) HE_LTF + 0.8 GI +#'1' - 2x(6.4) HE_LTF + 0.8 GI +#'2' - 2x(6.4) HE_LTF + 1.6 GI +#'3' - 4x(12.8) HE_LTF + 3.2 GI +GUARD_INTERVAL=3 #Nominal Packet extention #'0' for 0us #'1' for 8us #'2' for 16us -NOMINAL_PE=0 +NOMINAL_PE=2 #DCM Enable #'0' for Disabling DCM @@ -219,19 +235,24 @@ PARAMS=$PARAMS" rsi_zone_enabled=$RSI_ZONE_ENABLED" #OSD Module Params #PARAMS=$PARAMS" host_intf_on_demand=$HOST_INTF_ON_DEMAND" +#PARAMS=$PARAMS" ext_opt=$EXT_OPT" +#PARAMS=$PARAMS" country_code=$COUNTRY_CODE" +#PARAMS=$PARAMS" pta_config=$PTA_CONFIG" +#PARAMS=$PARAMS" sleep_clk_source_sel=$SLEEP_CLK_SOURCE_SEL" #PARAMS=$PARAMS" sleep_ind_gpio_sel=$SLEEP_IND_GPIO_SEL" #PARAMS=$PARAMS" anchor_point_gap=$ANCHOR_POINT_GAP" #PARAMS=$PARAMS" lmac_bcon_drop=$LMAC_BCON_DROP" #PARAMS=$PARAMS" feature_bitmap_9116=$FEATURE_BITMAP_9116" #PARAMS=$PARAMS" standby_assoc_chain_sel=$STANDBY_ASSOC_CHAIN_SEL" -#PARAMS=$PARAMS" power_save_opt=$POWER_SAVE_OPT" +#PARAMS=$PARAMS" pwr_save_opt=$POWER_SAVE_OPT" #PARAMS=$PARAMS" ulp_gpio_write=$ULP_GPIO_WRITE" #PARAMS=$PARAMS" ulp_gpio_read=$ULP_GPIO_READ" #PARAMS=$PARAMS" bt_feature_bitmap=$BT_FEATURE_BITMAP" +#PARAMS=$PARAMS" ble_vendor_feature_bitmap=$BLE_VENDOR_FEATURE_BITMAP" #PARAMS=$PARAMS" bt_rf_rx_power_mode=$BT_RF_RX_POWER_MODE" #PARAMS=$PARAMS" bt_rf_tx_power_mode=$BT_RF_TX_POWER_MODE" #PARAMS=$PARAMS" ble_roles=$BLE_ROLES" -#PARAMS=$PARAMS" ble_power_save_options=$BLE_POWER_SAVE_OPTIONS" +#PARAMS=$PARAMS" ble_pwr_save_options=$BLE_POWER_SAVE_OPTIONS" #PARAMS=$PARAMS" ble_tx_pwr_inx=$BLE_TX_PWR_INX" #PARAMS=$PARAMS" bt_rf_type=$BT_RF_TYPE" #PARAMS=$PARAMS" lp_handshake_mode=$LP_HANDSHAKE_MODE" diff --git a/rsi/README b/rsi/README deleted file mode 100755 index e735bd8..0000000 --- a/rsi/README +++ /dev/null @@ -1,286 +0,0 @@ -Description: -============ - - This package contains Redpine open source driver (rsi) package for both RS9113 and RS9116 chip sets. - This file gives the instruction to build and install the driver. For instructions on complete usage of - the driver and module, please refer RSI_OpenSource_Driver_TRM.pdf in documents folder. - -Features Supported: -=================== - * Wi-Fi Station Mode - * 802.11 Legacy Power save - * 802.11 UAPSD - * Hardware connection monitor - * Back ground scan and roaming - * 802.11d Regulatory - * Wi-Fi AP Mode - * WoWLAN - * Wi-Fi Direct mode - * BT-EDR mode - * BT-LE mode - * Wi-Fi BT Coex mode - * ZIGB end device mode - * Wi-Fi ZIGB end device coex mode - * ZIGB coordinator mode - * ZIGB router mode - -Package: -======== - Driver : rsi [Source code] - Firmware : RS9113_WLAN_QSPI.rps [For Wi-Fi only mode] - RS9113_WLAN_BT_DUAL_MODE.rps [Wi-Fi Station + BT Classic + BT LE or BT alone mode] - RS9113_AP_BT_DUAL_MODE.rps [Wi-Fi AP + BT Classic + BT LE] - RS9113_WLAN_ZIGBEE.rps [Wi-Fi Station + ZigB End device mode] - RS9113_ZIGBEE_COORDINATOR.rps [ZigB Cordinator] - RS9113_ZIGBEE_ROUTER.rps [ZigB Router] - RS9116_NLINK_WLAN_IMAGE.rps [Wi-Fi STA + Wi-Fi AP for RS9116 - Flash mode] - RS9116_NLINK_WLAN_BT_IMAGE.rps [WLAN (STA+AP) + BT(DUAL mode) for RS9116 - Flash mode] - RS9116_NLINK_WLAN_ZB_IMAGE.rps [WLAN sta + ZigB for RS9116 - Flash mode] - RS9116_NLINK_ZB_COORDINATOR.rps [ZigB Router for RS9116-Flash mode] - pmemdata [Wi-Fi station + AP for RS9116-RAM mode] - pmemdata_wlan_bt_classic [Wi-fi(STA + AP) + BT(DUAL) for RS9116-RAM mode] - pmemdata_wlan_zigb [Wi-fi(STA + AP) + ZigB for RS9116-RAM mode] - pmemdata_zigb_coordinator [ZigB Co-ordinator for RS9116-RAM mode] - pmemdata_zigb_router [ZigB Router for RS9116-RAM mode] - Release Notes : Change log of the releases - README : Build, install and usage guide - -Build: -====== - * Copy relevant firmware file [as mentioned above] to /lib/firmware. - * To build driver from kernel source: - -> copy the driver to /drivers/net/wireless - -> Move Makefile to Makefile_local - -> Move Makefile_ker to Makefile - -> Give 'make menuconfig' from kernel source - - $ make menuconfig - - -> Go to 'Device Drivers -> Network device support -> Wireless LAN' - -> Select 'Redpine Signals Inc devices' - -> Select required interface (SDIO/USB). - -> Select CONFIG_RSI_HCI option for BT (classic/LE) alone mode. - -> Select CONFIG_RSI_COEX_MODE option for Wi-Fi + BT Coex mode. - -> Select CONFIG_RSI_ZIGB option for Zigbee Mode and WLAN + Zigb Coex modes. - -> Build driver using below commands - - $ make SUBDIRS=drivers/net/wireless/rsi - - * To build driver locally: - -> Edit Makefile and configure option as below: - - -> Uncomment 'CONFIG_HW_SCAN_OFFLOAD=y' to use Hardware scan offload feature. - By default this flag is enabled. It is recommened not to comment this flag - [Particularly if you want to use back ground scanning and roaming feature]. - - -> Uncomment 'CONFIG_CARACALLA_BOARD=y' if you are working on Dell Caracalla - board. By default 'EXTRA_CFLAGS += -DPLATFORM_X86' is enabled. For embedded - platforms please comment this flag. - - -> Uncomment 'CONFIG_RSI_WOW=y' if you want to use Wake-on-WLAN feature. - - -> Uncomment 'CONFIG_RSI_P2P=y' if you want to use Wi-Fi direct (p2p) mode. - - -> Uncomment 'CONFIG_RSI_HCI=y' in Makefile to run BT alone (classic/LE) mode. - - -> Uncomment 'CONFIG_RSI_MULTI_MODE=y' in Makefile to enable multiple opermodes support - in driver. - - -> Uncomment 'CONFIG_RSI_COEX_MODE=y' in Makefile to run Wi-Fi + BT Coex mode or - Wi-Fi + ZigB end device coex mode. - - -> Uncomment 'CONFIG_RSI_NO_SDIO_MULTIBLOCK=y' if your platform's SDIO host controller - does not support multiblock mode. Do not uncomment this if you use USB interface. - - -> Build driver using 'make' - $ make - - * Binaries prepared: - rsi_91x.ko, rsi_usb.ko and rsi_sdio.ko - -Install: -======== - * insmod rsi_91x.ko rsi_zone_enabled=0x1f dev_oper_mode=1 - where - bit 0 - error zone - bit 1 - info zone (generic debug messages) - bit 2 - init zone - bit 3 - mgmt tx zone - bit 4 - mgmt rx zone - bit 5 - data tx zone - bit 6 - data rx zone - bit 7 - fsm zone - bit 8 - isr zone - - To enable error and info zone use below command: - $ insmod rsi_91x.ko rsi_zone_enabled=0x3 dev_oper_mode= - or - To use multiple modules with multiple opermodes, use below command: - $ insmod rsi_91x.ko rsi_zone_enabled=0x3 dev_oper_mode=,,,, # Max support for mulltiple dev_oper_modes for multiple modules are 5. - - where val: - 1 - Wi-Fi alone mode - 4 - BT alone mode - 8 - BT LE alone mode - 12 - BT classic + BT LE - 5 - Wi-Fi station + BT classic mode - 9 - Wi-Fi station + BT LE mode - 13 - Wi-Fi station + BT dual mode - 6 - Wi-Fi AP + BT classic mode - 14 - Wi-Fi AP + BT dual mode - 16 - ZigB alone mode - 17 - Wi-Fi station + zigb station mode - 32 - Wi-Fi station + zigb coordinator mode - 48 - Wi-Fi station + zigb router mode - - * device operating mode value can be found in the below 'sysfs' entries: - - $ cat /sys/module/rsi_91x/parameters/dev_oper_mode - 13 - - * If any invalid mode is given, the default mode 1 (Wi-Fi alone) is used. - - * insmod rsi_usb.ko - or - * insmod rsi_sdio.ko - - While using SDIO interface, To change SDIO clock, User can give their board specific SDIO clock using below command - $ insmod rsi_91x.ko rsi_zone_enabled=0x3 dev_oper_mode=13 - $ insmod rsi_sdio.ko sdio_clock=50 # sdio_clock is in the range from 1 to 50. - -Remove: -======= - * rmmod rsi_usb - or - * rmmod rsi_sdio - * rmmod rsi_91x - -Station Mode -============ - * Use wpa_supplicant version 2.6 or above to verify the station connnectivity - * To use stand-alone station mode, add below line in wpa_supplicant configuration - file - p2p_disabled=1 - -AP Mode -======= - * Run start_ap.sh script in scripts folder to start AP - -Concurrent Mode -=============== - * Concurrent mode is supported only in dev_oper_mode = 1[Wi-Fi alone mode]. - * You can create AP or Station on default interface. - * To create a new interface use below command. - - * For AP - $> iw dev interface add type __ap - Example: iw dev wlan0 interface add wlan1 type __ap - - * For Station - $> iw dev interface add type managed - -Configure: -========== - * Use iwconfig or iw to configure different parameters in device. - * Check below sections for individual commands usage - -Powersave configuration -======================= -* Powersave can be enabled or disable from command line using iwconfig / iw. - -* Use below commad to enable power save - $> iwconfig power on - -* Use below command to disable power save - $> iwconfig power off - - -Background scanning: -==================== -Background scanning is not directly supported in mac80211. Hence this feature -is supported in rsi driver through debugfs. -Firmware bgcan alone does not work for roaming. Hence it should be enable with -supplicant bgscan. - - * To verify the bgscan status and parameters - - $> cat /sys/kernel/debug/phy/bgscan - - * To enable back ground scan and configure its parameters from debugfs: - - $> echo 1 10 10 20 20 100 1 3 1 6 11 > /sys/kernel/debug/phy/bgscan - - 1 - Enable background scan. - 10 - BGscan threshold (in dBM). This is the upper RSSI threshold. - If the signal strength of connected beacon is less than this - threshold at bgscan interval bgscan will be performed otherwise - not performed. To always do bgscan this value can be given 0. - 10 - RSSI tolerence threshold used in roaming. If the difference between - the current RSSI value of the connected Access Point and the RSSI - value of the Access Point from the previous background scan is greater - than the RSSI Tolerance Threshold, then the module performs a - background scan. Assigning a large value to this field will eliminate - this method of triggering background scans. - 20 - Periodicity (in seconds). This is the back ground scan interval. - Setting this value to 0 will disable the background scan - 20 - active scan duraiton (in msecs). Active scan duration in each channel. - Max value is 255ms. - 100 - passive scan duration (in msecs). Passive scan duration in DFS channels. - Max value is 255ms. - 1 - two probe enable (directed and undirected probe alternates). 0 indicates - sending only broadcast probe. - 3 - number of background scan channels. Max channels supported are 24. - 1,6,11 - list of channels - - * To enable all valid channels in the current regulatory domain, configure 0 in num of channel field - $> echo 1 10 10 20 20 100 0 > /sys/kernel/debug/phy/bgscan - - * To disable back ground scan from debugs: - - $> echo 0 > /sys/kernel/debug/phy/bgscan - - * For checking the list of bgscan channels configured to device use below command - - $> cat /sys/kernel/debug/phy/bgscan - - This will display the list of bgscan channels configured to device with DFS indication also - - -Antenna Selection -================= -To select external antenna use the below command - - $> iw phy set antenna 1 0 - -To move back to internal antenna - - $> iw phy set antenna 0 0 - -Note: Make sure you put interface down before setting antenna and interface up after setting the antenna. - -Country Setting -=============== -To set a country use below command - - $> iw reg set - - Eg: iw reg set IN (For India) - -To check the current regulatory domain - - $> iw reg get - -Software RF-Kill verification -============================= -* rfkill package needs to be installed to verify software rfkill - -* Use below command to list the wireless interfaces in the system - $> rfkill list - -* Use below command to block an interface - $> rfkill block - -* Use below command to unblock an interface - $> rfkill unblock - - diff --git a/rsi/rsi_91x_debugfs.c b/rsi/rsi_91x_debugfs.c index 0233568..c4cada9 100755 --- a/rsi/rsi_91x_debugfs.c +++ b/rsi/rsi_91x_debugfs.c @@ -67,6 +67,7 @@ static int rsi_version_read(struct seq_file *seq, void *data) struct rsi_common *common = seq->private; struct rsi_hw *adapter = common->priv; + //__9117_CODE_START if (adapter->device_model >= RSI_DEV_9117) { seq_printf(seq, "Driver : %s\nLMAC : %04x.%d.%d.%d.%d.%d.%d\n", @@ -78,7 +79,9 @@ static int rsi_version_read(struct seq_file *seq, void *data) common->lmac_ver.patch_id, common->lmac_ver.customer_id, common->lmac_ver.build_id); - } else if (adapter->device_model == RSI_DEV_9116) { + } else + //__9117_CODE_END + if (adapter->device_model == RSI_DEV_9116) { seq_printf(seq, "Driver : %s\nLMAC : %04x.%d.%d.%d.%d.%d\n", common->driver_ver, diff --git a/rsi/rsi_91x_hal.c b/rsi/rsi_91x_hal.c index 99a5fe2..fbd38fc 100755 --- a/rsi/rsi_91x_hal.c +++ b/rsi/rsi_91x_hal.c @@ -779,6 +779,8 @@ int rsi_prepare_beacon(struct rsi_common *common, struct sk_buff *skb, struct ie u8 vap_id = 0; int status = 0; u16 tim_offset = 0; + u8 index = 0; + u8 ie_id, ie_len; #ifndef CONFIG_STA_PLUS_AP mac_bcn = ieee80211_beacon_get_tim(adapter->hw, @@ -835,6 +837,24 @@ int rsi_prepare_beacon(struct rsi_common *common, struct sk_buff *skb, struct ie if (mac_bcn->data[tim_offset + 2] == 0) bcn_frm->desc_word[3] |= cpu_to_le16(DTIM_BEACON); + if (common->acx_module == true) { + + index = 36; //mac header(24) + fixed params(12) + + while (index < (mac_bcn->len)) { + ie_id = mac_bcn->data[index]; + ie_len = mac_bcn->data[index + 1]; + if (ie_id == BEACON_COUNTRY_IE) { + rsi_dbg(INFO_ZONE, "Country IE is enabled \n"); + memmove(mac_bcn->data + index, mac_bcn->data + index + ie_len + 2, mac_bcn->len - (index + ie_len + 2)); + mac_bcn->len = mac_bcn->len - ie_len - 2; + bcn_frm->desc_word[0] = cpu_to_le16(mac_bcn->len | (RSI_WIFI_DATA_Q << 12)); + continue; + } + index += (2 + ie_len); + } + } + memcpy(&skb->data[FRAME_DESC_SZ], mac_bcn->data, mac_bcn->len); skb_put(skb, mac_bcn->len + FRAME_DESC_SZ); @@ -1434,9 +1454,7 @@ static int rsi_load_9116_firmware(struct rsi_hw *adapter) u32 base_address; u32 block_size; struct lmac_version_info *version_info; -#ifndef _9117_MACRO_DEF u32 chip_rev = 0; -#endif rsi_dbg(INIT_ZONE, "***** Load 9116 TA Instructions *****\n"); diff --git a/rsi/rsi_91x_hci.c b/rsi/rsi_91x_hci.c index 4e0f787..caa9e35 100755 --- a/rsi/rsi_91x_hci.c +++ b/rsi/rsi_91x_hci.c @@ -316,6 +316,28 @@ int rsi_bt_per_stats(struct rsi_hw *adapter, struct nlmsghdr *nlh, int payload_l return 0; } +int rsi_send_bb_read_data_to_app(struct rsi_hw *adapter, bb_rf_params_bt_t bb_rf_params_bt) +{ + struct sk_buff *skb_out = { 0 }; + struct nlmsghdr *nlh; + int msg_size, res; + msg_size = sizeof(bb_rf_params_bt); + skb_out = nlmsg_new(msg_size, 0); + if (!skb_out) { + rsi_dbg(ERR_ZONE, "%s: Failed to allocate new skb\n", __func__); + return 0; + } + + nlh = nlmsg_put(skb_out, adapter->bt_nl_pid, 0, NLMSG_DONE, msg_size, 0); + memcpy(nlmsg_data(nlh), &bb_rf_params_bt, msg_size); + res = nlmsg_unicast(adapter->nl_sk, skb_out, adapter->bt_nl_pid); + if (res < 0) { + rsi_dbg(ERR_ZONE, "%s: Failed to send stats to App\n", __func__); + return -1; + } + return 0; +} + int rsi_process_rx_bt_per_stats(struct rsi_common *common, bt_stats_t bt_stats) { struct sk_buff *skb_out = NULL; @@ -350,6 +372,10 @@ int rsi_bt_ble_update_gain_table(struct rsi_hw *adapter, struct nlmsghdr *nlh, i return -ENODEV; } rsi_dbg(MGMT_TX_ZONE, "%s: <==== Sending BT_BLE_GAIN_TABLE frame ====>\n", __func__); + if (common->acx_module == true) { + rsi_dbg(ERR_ZONE, " ERROR : Update BLE Gain table not supported for ACx module \n"); + return -EINVAL; + } skb = dev_alloc_skb(payload_len); if (!skb) @@ -400,6 +426,7 @@ int rsi_hci_recv_pkt(struct rsi_common *common, u8 *pkt) int pkt_len = rsi_get_length(pkt, 0); u8 queue_no = rsi_get_queueno(pkt, 0); bt_stats_t bt_stats; + bb_rf_params_bt_t bb_rf_params_bt; char status; if ((common->bt_fsm_state == BT_DEVICE_NOT_READY) && (pkt[14] == BT_CARD_READY_IND)) { @@ -446,11 +473,18 @@ int rsi_hci_recv_pkt(struct rsi_common *common, u8 *pkt) rsi_process_rx_bt_e2e_stats(common, bt_stats); return 0; case BT_PER: - rsi_dbg(MGMT_RX_ZONE, " Received BT PER STATS confirm from LMAC\n"); - memcpy(&bt_stats, pkt + FRAME_DESC_SZ, sizeof(bt_stats_t)); - rsi_hex_dump(MGMT_RX_ZONE, "BT PER STATS From LMAC", (char *)&bt_stats, sizeof(bt_stats_t)); - rsi_set_event(&common->rsi_bt_per_event); - rsi_process_rx_bt_per_stats(common, bt_stats); + if (common->priv->bb_read_cmd) { + rsi_dbg(MGMT_RX_ZONE, " Received BB_READ confirm from LMAC\n"); + memcpy(&bb_rf_params_bt.Data[0], pkt + FRAME_DESC_SZ, pkt_len); + bb_rf_params_bt.no_of_values = pkt_len / 2; + rsi_send_bb_read_data_to_app(common->priv, bb_rf_params_bt); + } else { + rsi_dbg(MGMT_RX_ZONE, " Received BT PER STATS confirm from LMAC\n"); + memcpy(&bt_stats, pkt + FRAME_DESC_SZ, sizeof(bt_stats_t)); + rsi_hex_dump(MGMT_RX_ZONE, "BT PER STATS From LMAC", (char *)&bt_stats, sizeof(bt_stats_t)); + rsi_set_event(&common->rsi_bt_per_event); + rsi_process_rx_bt_per_stats(common, bt_stats); + } return 0; case BT_BLE_GAIN_TABLE: rsi_dbg(MGMT_RX_ZONE, " Received BT/BLE GAIN TABLE UPDATE confirm from LMAC\n"); @@ -553,9 +587,9 @@ int rsi_hci_attach(struct rsi_common *common) hdev->bus = HCI_USB; hci_set_drvdata(hdev, h_adapter); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) && LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)) hdev->dev_type = HCI_PRIMARY; -#else +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) hdev->dev_type = HCI_BREDR; #endif SET_HCIDEV_DEV(hdev, common->priv->device); diff --git a/rsi/rsi_91x_he.c b/rsi/rsi_91x_he.c index 2044520..bc38d6f 100755 --- a/rsi/rsi_91x_he.c +++ b/rsi/rsi_91x_he.c @@ -28,6 +28,7 @@ static struct ieee80211_sband_iftype_data rsi_he_cap = { IEEE80211_HE_MAC_CAP2_ALL_ACK | IEEE80211_HE_MAC_CAP2_BSR, .mac_cap_info[3] = + IEEE80211_HE_MAC_CAP3_RX_CTRL_FRAME_TO_MULTIBSS| // IEEE80211_HE_MAC_CAP3_OMI_CONTROL | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0) IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_2, diff --git a/rsi/rsi_91x_mac80211.c b/rsi/rsi_91x_mac80211.c index 950b4f8..df781ea 100755 --- a/rsi/rsi_91x_mac80211.c +++ b/rsi/rsi_91x_mac80211.c @@ -1353,6 +1353,10 @@ static int rsi_channel_change(struct ieee80211_hw *hw) #else status = rsi_set_channel(adapter->priv, curchan); #endif + if (status) { + rsi_dbg(ERR_ZONE, "Failed to set the channel\n"); + return -EINVAL; + } if (BSS_ASSOC) { if (common->hw_data_qs_blocked && (rsi_get_connected_channel(adapter) == channel)) { @@ -1443,13 +1447,24 @@ static int rsi_mac80211_config(struct ieee80211_hw *hw, u32 changed) /* channel */ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - if (!BSS_ASSOC && (adapter->ps_state == PS_ENABLED)) + if (!BSS_ASSOC && (adapter->ps_state == PS_ENABLED)) { rsi_disable_ps(adapter); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + adapter->user_ps_en = 1; +#endif + } #ifdef CONFIG_STA_PLUS_AP status = rsi_channel_change(hw, vif); #else status = rsi_channel_change(hw); #endif + if (status) { + rsi_dbg(ERR_ZONE, " This channel is not supported for ACx modules\n"); + common->acx_stop_beacon = true; + mutex_unlock(&common->mutex); + return -EOPNOTSUPP; + } + common->acx_stop_beacon = false; } /* listen interval */ @@ -1473,7 +1488,11 @@ static int rsi_mac80211_config(struct ieee80211_hw *hw, u32 changed) unsigned long flags; spin_lock_irqsave(&adapter->ps_lock, flags); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + if (adapter->user_ps_en == 0) { +#else if ((conf->flags & IEEE80211_CONF_PS) && (adapter->user_ps_en == 0)) { +#endif rsi_enable_ps(adapter); adapter->user_ps_en = 1; } else if (adapter->ps_state == PS_ENABLED) { @@ -1481,7 +1500,9 @@ static int rsi_mac80211_config(struct ieee80211_hw *hw, u32 changed) adapter->user_ps_en = 0; } else if (adapter->ps_state == PS_NONE) { rsi_dbg(INFO_ZONE, "Device already Power Save Disabled State\n"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 0, 0) adapter->user_ps_en = 0; +#endif } spin_unlock_irqrestore(&adapter->ps_lock, flags); } diff --git a/rsi/rsi_91x_main.c b/rsi/rsi_91x_main.c index 2ae7a76..01815d7 100755 --- a/rsi/rsi_91x_main.c +++ b/rsi/rsi_91x_main.c @@ -372,6 +372,11 @@ module_param(enable_encap_offload, bool, S_IRUGO); MODULE_PARM_DESC(enable_encap_offload, "\nenable_encap_offload\n\ '0' - Disable \n'1' - Enable\n"); +/* Skipping firmware loading is disabled by default */ +u16 skip_fw_load = 0; +module_param(skip_fw_load, ushort, 0); +MODULE_PARM_DESC(skip_fw_load, " 1 to Skip fw loading else 0"); + /** * rsi_dbg() - This function outputs informational messages. * @zone: Zone of interest for output message. @@ -457,6 +462,17 @@ static char *opmode_str(int oper_mode) void rsi_print_version(struct rsi_common *common) { +#ifdef NO_FIRMWARE_LOAD_SUPPORT + memcpy(common->driver_ver, DRV_VER, ARRAY_SIZE(DRV_VER)); + common->driver_ver[ARRAY_SIZE(DRV_VER)] = '\0'; + if (skip_fw_load != 0) { + rsi_dbg(ERR_ZONE, "================================================\n"); + rsi_dbg(ERR_ZONE, "=========== FIRMWARE LOADING SKIPPED ===========\n"); + rsi_dbg(ERR_ZONE, "================================================\n"); + rsi_dbg(ERR_ZONE, "Driver Version\t: %s", common->driver_ver); + rsi_dbg(ERR_ZONE, "Operating mode\t: %d [%s]", common->oper_mode, opmode_str(common->oper_mode)); + } +#else struct rsi_hw *adapter = common->priv; memcpy(common->driver_ver, DRV_VER, ARRAY_SIZE(DRV_VER)); common->driver_ver[ARRAY_SIZE(DRV_VER)] = '\0'; @@ -464,6 +480,7 @@ void rsi_print_version(struct rsi_common *common) rsi_dbg(ERR_ZONE, "================================================\n"); rsi_dbg(ERR_ZONE, "================ RSI Version Info ==============\n"); rsi_dbg(ERR_ZONE, "================================================\n"); + //__9117_CODE_START if (adapter->device_model >= RSI_DEV_9117) { rsi_dbg(ERR_ZONE, "FW Version\t: %04x.%d.%d.%d.%d.%d.%d\n", @@ -474,7 +491,9 @@ void rsi_print_version(struct rsi_common *common) common->lmac_ver.patch_id, common->lmac_ver.customer_id, common->lmac_ver.build_id); - } else if (adapter->device_model == RSI_DEV_9116) { + } else + //__9117_CODE_END + if (adapter->device_model == RSI_DEV_9116) { rsi_dbg(ERR_ZONE, "FW Version\t: %04x.%d.%d.%d.%d.%d\n", common->lmac_ver.chip_id, @@ -494,6 +513,7 @@ void rsi_print_version(struct rsi_common *common) rsi_dbg(ERR_ZONE, "Operating mode\t: %d [%s]", common->oper_mode, opmode_str(common->oper_mode)); rsi_dbg(ERR_ZONE, "Firmware file\t: %s", common->priv->fw_file_name); rsi_dbg(ERR_ZONE, "================================================\n"); +#endif } /** @@ -702,7 +722,11 @@ void rsi_sdio_intr_poll_scheduler_thread(struct rsi_common *common) msleep(20); } while (atomic_read(&common->sdio_intr_poll_thread.thread_done) == 0); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 17, 0) complete_and_exit(&common->sdio_intr_poll_thread.completion, 0); +#else + kthread_complete_and_exit(&common->sdio_intr_poll_thread.completion, 0); +#endif } void init_sdio_intr_status_poll_thread(struct rsi_common *common) @@ -815,6 +839,13 @@ struct rsi_hw *rsi_91x_init(void) common = adapter->priv; common->priv = adapter; +#ifdef NO_FIRMWARE_LOAD_SUPPORT + if (skip_fw_load != 0) { + common->skip_fw_load = 1; + rsi_print_version(common); + } +#endif + for (ii = 0; ii < NUM_SOFT_QUEUES; ii++) skb_queue_head_init(&common->tx_queue[ii]); diff --git a/rsi/rsi_91x_mgmt.c b/rsi/rsi_91x_mgmt.c index c1268bb..376990f 100755 --- a/rsi/rsi_91x_mgmt.c +++ b/rsi/rsi_91x_mgmt.c @@ -1887,6 +1887,14 @@ int rsi_set_channel(struct rsi_common *common, struct ieee80211_channel *channel dev_kfree_skb(skb); return 0; } + + if ((common->driver_mode == E2E_MODE) && (common->acx_module == true)) { + if (channel->hw_value > 11) { + rsi_dbg(ERR_ZONE, "%s : Channel = %d not Supported for ACx Modules\n ", __func__, channel->hw_value); + return -EOPNOTSUPP; + } + } + memset(skb->data, 0, FRAME_DESC_SZ); mgmt_frame = (struct rsi_mac_frame *)skb->data; @@ -2512,6 +2520,12 @@ void rsi_validate_bgscan_channels(struct rsi_hw *adapter, struct bgscan_config_p rsi_dbg(INFO_ZONE, "Final bgscan channels:\n"); for (cnt = 0; cnt < params->num_user_channels; cnt++) { ch_num = params->user_channels[cnt]; + if (common->acx_module == true) { + if (ch_num > 11) { + rsi_dbg(ERR_ZONE, "Don't include channel = %d for ACx module ", ch_num); + continue; + } + } if ((ch_num < 1) || ((ch_num > 14) && (ch_num < 36)) || ((ch_num > 64) && (ch_num < 100)) || ((ch_num > 140) && (ch_num < 149)) || (ch_num > 165)) @@ -3387,6 +3401,10 @@ int rsi_update_wlan_gain_table(struct rsi_hw *adapter, struct nlmsghdr *nlh, int struct rsi_mac_frame *gain_table = NULL; rsi_dbg(INT_MGMT_ZONE, "<=== Update WLAN Gain table ===>\n"); + if (common->acx_module == true) { + rsi_dbg(ERR_ZONE, "ERROR : Update WLAN Gain table not supported for ACx module \n"); + return -EINVAL; + } skb = dev_alloc_skb(FRAME_DESC_SZ + frame_len); if (!skb) @@ -3630,10 +3648,15 @@ void rsi_scan_start(struct work_struct *work) continue; #ifndef CONFIG_STA_PLUS_AP - if (rsi_set_channel(common, cur_chan)) { + status = rsi_set_channel(common, cur_chan); #else - if (rsi_set_channel(common, cur_chan, vif)) { + status = rsi_set_channel(common, cur_chan, vif); #endif + if (status == -EOPNOTSUPP) { + rsi_dbg(ERR_ZONE, "Failed to set the channel for ACx\n"); + init_channel_timer(common->priv, ACTIVE_SCAN_DURATION); + goto SKIP_PROBE; + } else if (status) { rsi_dbg(ERR_ZONE, "Failed to set the channel\n"); break; } @@ -3673,6 +3696,7 @@ void rsi_scan_start(struct work_struct *work) init_channel_timer(common->priv, ACTIVE_SCAN_DURATION); } } +SKIP_PROBE: if (!common->scan_in_prog) break; if (common->iface_down) @@ -3966,6 +3990,21 @@ static int rsi_handle_ta_confirm(struct rsi_common *common, u8 *msg) } break; + case SOC_REG_OPS: + rsi_dbg(FSM_ZONE, "SOC_REG_OPS: SOC REG Read confirm Received: msg_len: %d \n", msg_len); + if (msg_len > 0) { + rsi_hex_dump(ERR_ZONE, "Message", &msg[FRAME_DESC_SZ], msg_len); + memcpy((&adapter->bb_rf_read.Data[0]), &msg[FRAME_DESC_SZ], msg_len); + adapter->bb_rf_read.no_of_values = msg_len / 2; + rsi_dbg(FSM_ZONE, + "SOC_REG_OPS: msg_len is : %d no_of_vals is %d \n", + msg_len, + adapter->bb_rf_read.no_of_values); + if ((rsi_bb_prog_data_to_app(adapter)) < 0) + return -1; + rsi_dbg(INFO_ZONE, "%s : Success in Performing operation\n", __func__); + } + break; case BB_PROG_VALUES_REQUEST: rsi_dbg(FSM_ZONE, "BBP PROG STATS: Utils BB confirm Received: msg_len: %d \n", msg_len); if (msg_len > 0) { @@ -3993,8 +4032,12 @@ static int rsi_handle_ta_confirm(struct rsi_common *common, u8 *msg) ieee80211_wake_queues(adapter->hw); complete(&common->wlan_init_completion); common->reinit_hw = false; - } else - return rsi_mac80211_attach(common); + } else { + if (common->driver_mode != RF_EVAL_MODE_ON) + return rsi_mac80211_attach(common); + else + return 0; + } } } else { rsi_dbg(INFO_ZONE, "%s: Received bb_rf cfm in %d state\n", __func__, common->fsm_state); @@ -4019,6 +4062,7 @@ static int rsi_handle_ta_confirm(struct rsi_common *common, u8 *msg) case SCAN_REQUEST: rsi_dbg(INFO_ZONE, "Scan confirm.\n"); + rsi_hex_dump(INFO_ZONE, "Scan Confirm", &msg[0], 16); #ifdef CONFIG_STA_PLUS_AP if (!vif) { /* FIXME: vif can be destroyed just before getting confirm from LMAC */ @@ -4157,6 +4201,15 @@ int rsi_handle_card_ready(struct rsi_common *common, u8 *msg) switch (common->fsm_state) { case FSM_CARD_NOT_READY: + if (msg[15] & BIT(0)) { + rsi_dbg(ERR_ZONE, " %s : ACx Module Detected \n", __func__); + common->acx_module = true; + if (msg[15] & BIT(1)) { + rsi_dbg(ERR_ZONE, "[%s] ERROR : Voltage Out of Range \n", __func__); + common->common_hal_tx_access = true; + return -EINVAL; + } + } rsi_dbg(INIT_ZONE, "Card ready indication from Common HAL\n"); common->common_hal_tx_access = true; rsi_set_default_parameters(common); @@ -4332,6 +4385,10 @@ int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg) common->eapol4_confirm = 1; if (!rsi_send_block_unblock_frame(common, false)) common->hw_data_qs_blocked = false; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + if (adapter->user_ps_en) + rsi_enable_ps(adapter); +#endif //__9117_CODE_START #ifdef CONFIG_TWT_SUPPORT if (adapter->ax_params._11ax_enabled && adapter->ap_support_twt) { @@ -4439,6 +4496,9 @@ int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg) return -1; if (!common->beacon_enabled) return -1; + if (common->acx_stop_beacon == true) { + return -EINVAL; + } rsi_dbg(MGMT_DEBUG_ZONE, "<==== Beacon Interrupt Received ====>\n"); #ifndef CONFIG_STA_PLUS_AP rsi_send_beacon(common); @@ -4563,6 +4623,27 @@ int rsi_bb_prog_data_to_app(struct rsi_hw *adapter) } return 0; } +int send_gaintable_status_to_app(struct rsi_hw *adapter) +{ + struct sk_buff *skb_out = { 0 }; + struct nlmsghdr *nlh; + int res; + skb_out = nlmsg_new(2, 0); + if (!skb_out) { + rsi_dbg(ERR_ZONE, "%s: Failed to allocate new skb\n", __func__); + return 0; + } + + nlh = nlmsg_put(skb_out, adapter->wlan_nl_pid, 0, NLMSG_DONE, 2, 0); + /* NETLINK_CB(skb_out).dst_group = 0; */ + memcpy(nlmsg_data(nlh), &adapter->gaintable_status, 2); + res = nlmsg_unicast(adapter->nl_sk, skb_out, adapter->wlan_nl_pid); + if (res < 0) { + rsi_dbg(ERR_ZONE, "%s: Failed to send RSSI to App\n", __func__); + return -1; + } + return 0; +} int send_rssi_to_app(struct rsi_hw *adapter) { @@ -4861,6 +4942,44 @@ int rsi_mgmt_send_bb_prog_frames(struct rsi_hw *adapter, unsigned short *bb_prog return 0; } +int rsi_mgmt_soc_reg_ops_req(struct rsi_hw *adapter, unsigned short *bb_prog_vals, unsigned short type) +{ + struct rsi_bb_prog_params *bb_prog_req; + struct sk_buff *skb; + unsigned short frame_len; + + skb = dev_alloc_skb(sizeof(struct rsi_bb_prog_params)); + if (!skb) + return -1; + + bb_prog_req = (struct rsi_bb_prog_params *)skb->data; + memset(skb->data, 0, sizeof(struct rsi_bb_prog_params)); + + rsi_dbg(INT_MGMT_ZONE, "====> Sending SOC REG OPS Request Packet <====\n"); + frame_len = ((4) * 2); /* each 2 bytes */ + + /* Preparing the Frame descriptor */ + + bb_prog_req->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); + bb_prog_req->desc_word[1] = cpu_to_le16(SOC_REG_OPS); + bb_prog_req->desc_word[3] = cpu_to_le16(bb_prog_vals[1]); + bb_prog_req->desc_word[4] = cpu_to_le16(bb_prog_vals[2]); + if (type == SOC_REG_WRITE) { + bb_prog_req->desc_word[5] = cpu_to_le16(bb_prog_vals[3]); + bb_prog_req->desc_word[6] = cpu_to_le16(bb_prog_vals[4]); + bb_prog_req->desc_word[7] = cpu_to_le16(0); //WRITE INDICATION + } else { + bb_prog_req->desc_word[7] = cpu_to_le16(1); //READ INDICATION + } + + skb_put(skb, (frame_len + FRAME_DESC_SZ)); + rsi_hex_dump(INT_MGMT_ZONE, "SOC_REG_OPS", skb->data, skb->len); + + rsi_send_internal_mgmt_frame(adapter->priv, skb); + + return 0; +} + int send_get_rssi_frame_to_fw(struct rsi_hw *adapter) { struct sk_buff *skb = NULL; @@ -4975,6 +5094,12 @@ int rsi_set_bb_rf_values(struct rsi_hw *adapter) } else { printk("Invalid R/W type\n"); } + + } else if (type == SOC_REG_WRITE || type == SOC_REG_READ) { + rsi_dbg(INFO_ZONE, "READ_BUF_VALUES : SOC_REG_READ/WRITE\n"); + if (rsi_mgmt_soc_reg_ops_req(adapter, adapter->bb_rf_params.Data, type) != 0) { + return -1; + } } else { rsi_dbg(INFO_ZONE, "Invalid R/W type\n"); return -1; @@ -5363,6 +5488,7 @@ int send_twt_session_details_to_lmac(struct rsi_hw *adapter, struct sk_buff *skb = NULL; twt_session_config_t *twt_config = NULL; u64 wake_interval; + u32 rx_latency = 0; struct rsi_common *common = adapter->priv; skb = dev_alloc_skb(sizeof(twt_session_config_t)); if (!skb) { @@ -5375,6 +5501,18 @@ int send_twt_session_details_to_lmac(struct rsi_hw *adapter, twt_config->frame_desc[0] = cpu_to_le16((sizeof(twt_session_config_t) - FRAME_DESC_SZ) | (RSI_WIFI_MGMT_Q << 12)); twt_config->frame_desc[1] = cpu_to_le16(TWT_CONFIG); twt_config->frame_desc[2] = cpu_to_le16(twt_user_config->restrict_tx_outside_tsp << 8); + if (setup) { + wake_interval = twt_setup_resp->twt_element.wake_interval_mantisa; + wake_interval = wake_interval * (1 << twt_setup_resp->twt_element.req_type_twt_wi_exp); + } + + if (adapter->twt_auto_config_enable && (setup == 1)) { + rx_latency = (u32)(div64_u64(((u64)twt_user_config->rx_latency - (div64_u64(wake_interval, 1024))), 100)); + twt_config->frame_desc[4] = twt_user_config->beacon_wake_up_count_after_sp & 0xFF; + twt_config->frame_desc[3] = ((rx_latency & 0xFF) | (((rx_latency >> 8) & 0xFF) << 8)); + twt_config->frame_desc[5] = (((rx_latency >> 16) & 0xFF) | (((rx_latency >> 24) & 0xFF) << 8)); + } + if (setup) { adapter->twt_session_active = 1; adapter->twt_active_session_flow_id = twt_setup_resp->twt_element.req_type_twt_flow_id; @@ -5396,8 +5534,6 @@ int send_twt_session_details_to_lmac(struct rsi_hw *adapter, } else { twt_config->twt_nom_wake_dur = (twt_setup_resp->twt_element.nom_min_wake_duration * 256); } - wake_interval = twt_setup_resp->twt_element.wake_interval_mantisa; - wake_interval = wake_interval * (1 << twt_setup_resp->twt_element.req_type_twt_wi_exp); twt_config->twt_wake_interval[0] = wake_interval & 0xFFFFFFFF; twt_config->twt_wake_interval[1] = (wake_interval >> 32) & 0xFFFFFFFF; } else { @@ -5446,12 +5582,12 @@ int validate_unsupported_twt_resp_params(struct rsi_hw *adapter, twt_setup_frame && (twt_setup_resp->twt_element.req_type_twt_setup_command < 3)) { rsi_dbg(ERR_ZONE, "twt setup requests processing not supported\n"); return 1; + } else if ((adapter->twt_auto_config_enable == 1) && (twt_setup_resp->twt_element.req_type_flow_type != 0)) { + rsi_dbg(ERR_ZONE, "only announced TWT setup is supported when auto config enabled\n"); + return 1; } else if (twt_setup_resp->dialog_token != 1) { rsi_dbg(ERR_ZONE, "twt setup response dialogue token does not match\n"); return 1; - } else if (twt_setup_resp->twt_element.req_type_twt_flow_id != (adapter->rsi_twt_config.twt_flow_id)) { - rsi_dbg(ERR_ZONE, "twt setup response flow id does not match\n"); - return 1; } else if (twt_setup_resp->twt_element.control_negotiation_type) { rsi_dbg(ERR_ZONE, "only individual twt setup is supported \n"); return 1; @@ -5468,6 +5604,54 @@ int validate_unsupported_twt_resp_params(struct rsi_hw *adapter, twt_setup_frame return 0; } +static u8 does_twt_response_match_user_requirement(struct rsi_hw *adapter, twt_setup_frame_t *twt_setup_resp) +{ + twt_selection_t *user_config = &adapter->user_twt_auto_config; + rsi_twt_config_t *twt_config = &adapter->rsi_twt_config; + uint64_t resp_twt_wi, req_twt_wi, max_twt_wi, max_twt_wi_tol, total_resp_wake_duration_ms, + min_wake_duration_required_ms; + uint32_t resp_wake_duration, req_wake_duration, num_twt_service_periods; + if (twt_setup_resp->twt_element.req_type_flow_type != 0) { + return 0; + } + resp_twt_wi = ((uint64_t)twt_setup_resp->twt_element.wake_interval_mantisa + * ((uint64_t)0x1 << twt_setup_resp->twt_element.req_type_twt_wi_exp)) + >> 10; + resp_wake_duration = (((uint32_t)twt_setup_resp->twt_element.nom_min_wake_duration + << (twt_setup_resp->twt_element.control_wake_duration_unit ? 10 : 8))) + >> 10; + req_twt_wi = ((uint64_t)twt_config->wake_int_mantissa * ((u64)0x1 << twt_config->wake_int_exp)) >> 10; + req_wake_duration = (((uint32_t)twt_config->wake_duration << (twt_config->twt_wake_duration_unit ? 10 : 8))) >> 10; + max_twt_wi = 0, max_twt_wi_tol = 0; + if (user_config->tx_latency != 0) { + max_twt_wi = MIN_OF_3(user_config->rx_latency, user_config->tx_latency, user_config->default_wake_interval_ms); + } else { + max_twt_wi = MIN_OF_2(user_config->rx_latency, user_config->default_wake_interval_ms); + } + max_twt_wi_tol = div64_u64((max_twt_wi * (u64)(100 + user_config->twt_tolerable_deviation)), 100); + // ensuring that AP given TWT Wake interval does not exceed max allowed TWT Wake interval beyond tolerated value. + if ((resp_twt_wi > max_twt_wi_tol) || (resp_wake_duration > (resp_twt_wi >> 1))) { + return 0; + } else { + //Calculating num of AP given TWT service periods possible within the Max allowed TWT Wake Interval + num_twt_service_periods = (uint32_t)(div64_u64(max_twt_wi, resp_twt_wi)); + // If calculated number of service periods is zero, assign 1 + num_twt_service_periods = num_twt_service_periods ? num_twt_service_periods : 1; + // Calculate total AP wake duration for all the number of TWT service periods + total_resp_wake_duration_ms = (uint64_t)num_twt_service_periods * (uint64_t)resp_wake_duration; + // min wake dur required by STA + min_wake_duration_required_ms = (uint64_t)req_wake_duration * (uint64_t)(div64_u64(max_twt_wi, req_twt_wi)); + if ((total_resp_wake_duration_ms + < (div64_u64((min_wake_duration_required_ms * (u64)(100 - user_config->twt_tolerable_deviation)), 100))) + || (total_resp_wake_duration_ms + > (div64_u64((min_wake_duration_required_ms * (u64)(100 + user_config->twt_tolerable_deviation)), 100)))) { + // indicate session setup failure if total AP wake duration is not within the tolerable limits. + return 0; + } + } + return 1; +} + int rsi_mgmt_process_twt_setup_resp(struct rsi_hw *adapter, struct sk_buff *skb) { twt_setup_frame_t *twt_setup_frame = (twt_setup_frame_t *)&skb->data[MIN_802_11_HDR_LEN]; @@ -5526,27 +5710,30 @@ int rsi_mgmt_process_twt_setup_resp(struct rsi_hw *adapter, struct sk_buff *skb) rsi_dbg(MGMT_DEBUG_ZONE, "Received response = %x for TWT-SETUP-SUGGEST Frame \n", twt_setup_frame->twt_element.req_type_twt_setup_command); - if ((twt_setup_frame->twt_element.nom_min_wake_duration - < ((user_twt_config->wake_duration > user_twt_config->wake_duration_tol) - ? (user_twt_config->wake_duration - user_twt_config->wake_duration_tol) - : 0)) - || (twt_setup_frame->twt_element.nom_min_wake_duration - > (user_twt_config->wake_duration + user_twt_config->wake_duration_tol)) - || (twt_setup_frame->twt_element.req_type_twt_wi_exp - < ((user_twt_config->wake_int_exp > user_twt_config->wake_int_exp_tol) - ? (user_twt_config->wake_int_exp - user_twt_config->wake_int_exp_tol) - : 0)) - || (twt_setup_frame->twt_element.req_type_twt_wi_exp - > (user_twt_config->wake_int_exp + user_twt_config->wake_int_exp_tol)) - || (twt_setup_frame->twt_element.wake_interval_mantisa - < ((user_twt_config->wake_int_mantissa > user_twt_config->wake_int_mantissa_tol) - ? (user_twt_config->wake_int_mantissa - user_twt_config->wake_int_mantissa_tol) - : 0)) - || (twt_setup_frame->twt_element.wake_interval_mantisa - > (user_twt_config->wake_int_mantissa + user_twt_config->wake_int_mantissa_tol)) - || (twt_setup_frame->twt_element.req_type_trigger != (user_twt_config->triggered_twt & 1)) + if ((twt_setup_frame->twt_element.req_type_trigger != (user_twt_config->triggered_twt & 1)) || (twt_setup_frame->twt_element.req_type_implicit_twt != (user_twt_config->implicit_twt & 1)) - || (twt_setup_frame->twt_element.req_type_flow_type != (user_twt_config->un_announced_twt & 1))) { + || (twt_setup_frame->twt_element.req_type_flow_type != (user_twt_config->un_announced_twt & 1)) + || (((adapter->twt_auto_config_enable == 0) + && ((twt_setup_frame->twt_element.nom_min_wake_duration + < ((user_twt_config->wake_duration > user_twt_config->wake_duration_tol) + ? (user_twt_config->wake_duration - user_twt_config->wake_duration_tol) + : 0)) + || (twt_setup_frame->twt_element.nom_min_wake_duration + > (user_twt_config->wake_duration + user_twt_config->wake_duration_tol)) + || (twt_setup_frame->twt_element.req_type_twt_wi_exp + < ((user_twt_config->wake_int_exp > user_twt_config->wake_int_exp_tol) + ? (user_twt_config->wake_int_exp - user_twt_config->wake_int_exp_tol) + : 0)) + || (twt_setup_frame->twt_element.req_type_twt_wi_exp + > (user_twt_config->wake_int_exp + user_twt_config->wake_int_exp_tol)) + || (twt_setup_frame->twt_element.wake_interval_mantisa + < ((user_twt_config->wake_int_mantissa > user_twt_config->wake_int_mantissa_tol) + ? (user_twt_config->wake_int_mantissa - user_twt_config->wake_int_mantissa_tol) + : 0)) + || (twt_setup_frame->twt_element.wake_interval_mantisa + > (user_twt_config->wake_int_mantissa + user_twt_config->wake_int_mantissa_tol)))) + || ((adapter->twt_auto_config_enable == 1) + && (does_twt_response_match_user_requirement(adapter, twt_setup_frame) == 0)))) { rsi_dbg(ERR_ZONE, "AP TWT parameters not the in tolerance limit provided \n"); adapter->twt_current_status = TWT_SETUP_RSP_OUTOF_TOL; if (twt_setup_frame->twt_element.req_type_twt_setup_command == TWT_SETUP_CMD_ACCEPT) { @@ -5741,6 +5928,83 @@ int send_twt_information_frame(struct rsi_hw *adapter, wifi_reschedule_twt_confi return 0; } +static u64 calculate_min_required_wake_dur(twt_selection_t *user_config, u32 max_allowed_wake_interval_ms) +{ + //minimum wake duration required within the max allowable wake interval to achieve expected tx throughput given the fixed device average throughput. + u64 min_wake_duration_required_ms = + div64_u64(((u64)user_config->expected_tx_throughput * (u64)max_allowed_wake_interval_ms), + (u64)user_config->device_avg_throughput); + min_wake_duration_required_ms *= + (100 + + user_config + ->estimated_extra_wake_duration_percent); //Overestimating the time required by estimated_extra_wake_duration_percent + min_wake_duration_required_ms = (div64_u64(min_wake_duration_required_ms, 100)); + //min_wake_duration_required_ms should be not be less than default minimum wake duration. + if (min_wake_duration_required_ms < user_config->default_min_wake_duration_ms) { + min_wake_duration_required_ms = user_config->default_min_wake_duration_ms; + } + return min_wake_duration_required_ms; +} + +int use_case_based_twt_params_configuration(struct rsi_hw *adapter, twt_selection_t *user_config) +{ + rsi_twt_config_t *twt_config = &adapter->rsi_twt_config; + uint64_t wake_interval, min_wake_duration_required; + uint32_t max_allowed_wake_interval, num_twt_service_periods, wake_duration_per_sp, remainder; + wake_interval = min_wake_duration_required = 0; + max_allowed_wake_interval = num_twt_service_periods = wake_duration_per_sp = 0; + //Taking minimum of rx_latency/tx_latency/Default_wake_interval as Maximum allowable Wake interval. + if (user_config->tx_latency != 0) { + max_allowed_wake_interval = + MIN_OF_3(user_config->rx_latency, user_config->tx_latency, user_config->default_wake_interval_ms); + } else { + max_allowed_wake_interval = MIN_OF_2(user_config->rx_latency, user_config->default_wake_interval_ms); + } + min_wake_duration_required = calculate_min_required_wake_dur(user_config, max_allowed_wake_interval); + if (min_wake_duration_required > (max_allowed_wake_interval >> 1)) { + rsi_dbg(ERR_ZONE, "Invalid User Configuration\n"); + return -1; + } + /*wake duration unit 1 is allowed only if ratio of expected_tx_throughput to device_avg_throughput greater than 1/4 + when it is the initial negotiation ie., when re-negotiating always use wake duration = 0 */ + if ((user_config->device_avg_throughput > (user_config->expected_tx_throughput << 2)) + || (twt_config->twt_wake_duration_unit == 1)) { + wake_duration_per_sp = (min_wake_duration_required < 64) ? min_wake_duration_required : 63; + twt_config->twt_wake_duration_unit = 0; + } else { + wake_duration_per_sp = (min_wake_duration_required < 256) ? min_wake_duration_required : 255; + twt_config->twt_wake_duration_unit = (min_wake_duration_required < 64) ? 0 : 1; + } + div_u64_rem(min_wake_duration_required, wake_duration_per_sp, &remainder); + num_twt_service_periods = div64_u64(min_wake_duration_required, wake_duration_per_sp) + ((remainder != 0) ? 1 : 0); + num_twt_service_periods = (num_twt_service_periods != 0) ? num_twt_service_periods : 1; + wake_duration_per_sp = div64_u64((min_wake_duration_required << 10), num_twt_service_periods); + twt_config->wake_duration = + twt_config->twt_wake_duration_unit + ? (wake_duration_per_sp >> 10) + : (wake_duration_per_sp + >> 8); //divide by 256 to convert from 1uS units to 256uS units and 1024 to convert to 1024us + twt_config->wake_int_mantissa = (uint16_t)((uint64_t)max_allowed_wake_interval / (uint64_t)num_twt_service_periods); + twt_config->wake_int_exp = 0xA; + twt_config->wake_int_exp_tol = 0xFF; + twt_config->wake_int_mantissa_tol = 0xFFFF; + twt_config->implicit_twt = 1; + twt_config->un_announced_twt = 0; //only announced TWT for use case based TWT + twt_config->triggered_twt = 0; + twt_config->twt_channel = 0; //twt_channel must be zero + twt_config->twt_protection = 0; //twt_protection must be zero + twt_config->restrict_tx_outside_tsp = 1; + twt_config->twt_retry_limit = 3; + twt_config->twt_retry_interval = 10; + twt_config->req_type = 1; //0 - Request TWT; 1 - Suggest TWT; 2 - Demand TWT + twt_config->negotiation_type = 0; + twt_config->twt_flow_id = 0; + twt_config->twt_enable = 1; + twt_config->rx_latency = user_config->rx_latency; + twt_config->beacon_wake_up_count_after_sp = user_config->beacon_wake_up_count_after_sp; + return 0; +} + #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)) diff --git a/rsi/rsi_91x_nlsock.c b/rsi/rsi_91x_nlsock.c index 5dbca3d..b68dd16 100755 --- a/rsi/rsi_91x_nlsock.c +++ b/rsi/rsi_91x_nlsock.c @@ -14,6 +14,12 @@ struct rsi_hw *adapter_g; +#ifdef NO_FIRMWARE_LOAD_SUPPORT +#define SD_REQUEST_MASTER 0x10000 +#define READ_BLOCK 16 +#define WRITE_BLOCK 256 +#endif + int rsi_response(struct rsi_hw *adapter, struct nlmsghdr *nlh, int status) { struct sk_buff *skb_out = NULL; @@ -36,6 +42,165 @@ int rsi_response(struct rsi_hw *adapter, struct nlmsghdr *nlh, int status) return 0; } +#ifdef NO_FIRMWARE_LOAD_SUPPORT +/* Send response packet with data regarding mfg application */ +int rsi_mfg_response(struct rsi_hw *adapter, struct nlmsghdr *nlh, int size) +{ + struct sk_buff *skb_out = NULL; + struct rsi_hw *adapter_n = adapter; + int res; + skb_out = nlmsg_new(size, 0); + if (!skb_out) { + rsi_dbg(ERR_ZONE, "%s: Failed to allocate skb\n", __func__); + return -1; + } + nlh = nlmsg_put(skb_out, adapter_n->wlan_nl_pid, 0, NLMSG_DONE, size, 0); + memcpy(nlmsg_data(nlh), adapter_n->mfg_rw.data, size); + rsi_dbg(MGMT_RX_ZONE, "<==== Sending Response to cmfg Application ====>\n"); + res = nlmsg_unicast(adapter_n->nl_sk, skb_out, adapter_n->wlan_nl_pid); + if (res < 0) { + rsi_dbg(ERR_ZONE, "%s: Failed to send response to cmfg Application\n", __func__); + return -1; + } + return 0; +} + +/* Read data from the specified memory region */ +int rsi_sdio_read_data_region(struct rsi_hw *adapter, + u32 base_address, + u32 instructions_sz, + u16 block_size, + u8 *read_buffer) +{ + u32 num_blocks; + u16 msb_address; + u32 offset, ii; + u8 *temp_buf; + u16 lsb_address; + struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops; + + temp_buf = kzalloc(block_size, GFP_KERNEL); + if (!temp_buf) + return -ENOMEM; + + num_blocks = instructions_sz / block_size; + msb_address = base_address >> 16; + + rsi_dbg(INFO_ZONE, "ins_size: %d\n", instructions_sz); + rsi_dbg(INFO_ZONE, "num_blocks: %d\n", num_blocks); + + /* Loading DM ms word in the sdio slave */ + if (hif_ops->master_access_msword(adapter, msb_address)) { + rsi_dbg(ERR_ZONE, "%s: Unable to set ms word reg\n", __func__); + goto err; + } + + for (offset = 0, ii = 0; ii < num_blocks; ii++, offset += block_size) { + memset(temp_buf, 0, block_size); + lsb_address = (u16)base_address; + if (hif_ops->read_reg_multiple(adapter, lsb_address | SD_REQUEST_MASTER, temp_buf, block_size)) { + rsi_dbg(ERR_ZONE, "%s: failed to write\n", __func__); + goto err; + } + rsi_dbg(INFO_ZONE, "%s: loading block: %d\n", __func__, ii); + memcpy(read_buffer + offset, temp_buf, block_size); + base_address += block_size; + + if ((base_address >> 16) != msb_address) { + msb_address += 1; + + /* Loading DM ms word in the sdio slave */ + if (hif_ops->master_access_msword(adapter, msb_address)) { + rsi_dbg(ERR_ZONE, "%s: Unable to set ms word reg\n", __func__); + goto err; + } + } + } + + if (instructions_sz % block_size) { + memset(temp_buf, 0, block_size); + lsb_address = (u16)base_address; + if (hif_ops->read_reg_multiple(adapter, lsb_address | SD_REQUEST_MASTER, temp_buf, instructions_sz % block_size)) { + goto err; + } + rsi_dbg(INFO_ZONE, "Written Last Block in Address 0x%x Successfully\n", offset | SD_REQUEST_MASTER); + memcpy(read_buffer + offset, temp_buf, block_size); + } + kfree(temp_buf); + return 0; + +err: + kfree(temp_buf); + return -EIO; +} + +/* Write data to specified memory region */ +int rsi_sdio_write_data_region(struct rsi_hw *adapter, u32 base_address, u32 instructions_sz, u16 block_size, u8 *data) +{ + u32 num_blocks; + u16 msb_address; + u32 offset, ii; + u8 *temp_buf; + u16 lsb_address; + struct rsi_host_intf_ops *hif_ops = adapter->host_intf_ops; + + temp_buf = kzalloc(block_size, GFP_KERNEL); + if (!temp_buf) + return -ENOMEM; + + num_blocks = instructions_sz / block_size; + msb_address = base_address >> 16; + + rsi_dbg(INFO_ZONE, "ins_size: %d\n", instructions_sz); + rsi_dbg(INFO_ZONE, "num_blocks: %d\n", num_blocks); + + /* Loading DM ms word in the sdio slave */ + if (hif_ops->master_access_msword(adapter, msb_address)) { + rsi_dbg(ERR_ZONE, "%s: Unable to set ms word reg\n", __func__); + goto err; + } + + for (offset = 0, ii = 0; ii < num_blocks; ii++, offset += block_size) { + memset(temp_buf, 0, block_size); + memcpy(temp_buf, data + offset, block_size); + lsb_address = (u16)base_address; + if (hif_ops->write_reg_multiple(adapter, lsb_address | SD_REQUEST_MASTER, temp_buf, block_size)) { + rsi_dbg(ERR_ZONE, "%s: failed to write\n", __func__); + goto err; + } + rsi_dbg(INFO_ZONE, "%s: loading block: %d\n", __func__, ii); + base_address += block_size; + + if ((base_address >> 16) != msb_address) { + msb_address += 1; + + /* Loading DM ms word in the sdio slave */ + if (hif_ops->master_access_msword(adapter, msb_address)) { + rsi_dbg(ERR_ZONE, "%s: Unable to set ms word reg\n", __func__); + goto err; + } + } + } + + if (instructions_sz % block_size) { + memset(temp_buf, 0, block_size); + memcpy(temp_buf, data + offset, instructions_sz % block_size); + lsb_address = (u16)base_address; + if (hif_ops->write_reg_multiple(adapter, lsb_address | SD_REQUEST_MASTER, temp_buf, instructions_sz % block_size)) { + goto err; + } + rsi_dbg(INFO_ZONE, "Written Last Block in Address 0x%x Successfully\n", offset | SD_REQUEST_MASTER); + } + kfree(temp_buf); + return 0; + +err: + kfree(temp_buf); + return -EIO; +} + +#endif + static void rsi_nl_recv_msg(struct sk_buff *skb) { //__9117_CODE_START @@ -52,10 +217,12 @@ static void rsi_nl_recv_msg(struct sk_buff *skb) struct rsi_common *common = adapter->priv; bb_rf_params_bt_t bb_rf_params_bt; #endif + //__9117_CODE_START u16 prev_bmiss_threshold_value; #ifdef CONFIG_TWT_SUPPORT wifi_reschedule_twt_config_t reschedule_twt_config; + twt_selection_t user_config; #endif //__9117_CODE_END struct rsi_nl_desc *nl_desc = NULL; @@ -88,9 +255,11 @@ static void rsi_nl_recv_msg(struct sk_buff *skb) } break; case UPDATE_WLAN_GAIN_TABLE: + adapter->wlan_nl_pid = pid; if (adapter->priv->fsm_state == FSM_MAC_INIT_DONE) { - payload_len = nl_desc->desc_word[1]; - rsi_update_wlan_gain_table(adapter, nlh, payload_len); + payload_len = nl_desc->desc_word[1]; + adapter->gaintable_status = rsi_update_wlan_gain_table(adapter, nlh, payload_len); + send_gaintable_status_to_app(adapter); } else { rsi_dbg(INFO_ZONE, "%s: uninitialized fsm state\n", __func__); return; @@ -144,6 +313,48 @@ static void rsi_nl_recv_msg(struct sk_buff *skb) rsi_dbg(ERR_ZONE, "Driver not installed before issuing command\n"); } break; +#ifdef NO_FIRMWARE_LOAD_SUPPORT + case MANUFACTURING: + rsi_dbg(ERR_ZONE, "Manufacturing mode operations in progress\n"); + adapter->wlan_nl_pid = pid; + payload_len = sizeof(adapter->mfg_rw); + memcpy((&adapter->mfg_rw), nlmsg_data(nlh) + FRAME_DESC_SZ, payload_len); + rsi_dbg(ERR_ZONE, "Mode of Read(R)/Write(W) operation = %c\n", adapter->mfg_rw.read_write); + if (adapter->mfg_rw.read_write == 'W') { + rsi_dbg(ERR_ZONE, "Performing write to device\n"); + rsi_dbg(INFO_ZONE, "mfg_rw.address = %x\n", adapter->mfg_rw.address); + rsi_dbg(INFO_ZONE, "mfg_rw.length = %x\n", adapter->mfg_rw.length); + rsi_hex_dump(INFO_ZONE, "Write data", adapter->mfg_rw.data, adapter->mfg_rw.length); + rsi_sdio_write_data_region(adapter, + adapter->mfg_rw.address, + adapter->mfg_rw.length, + WRITE_BLOCK, + adapter->mfg_rw.data); + } else if (adapter->mfg_rw.read_write == 'R') { + rsi_dbg(ERR_ZONE, "Performing read from device\n"); + rsi_dbg(INFO_ZONE, "mfg_rw.address = %x\n", adapter->mfg_rw.address); + rsi_dbg(INFO_ZONE, "mfg_rw.length = %x\n", adapter->mfg_rw.length); + rsi_sdio_read_data_region(adapter, + adapter->mfg_rw.address, + adapter->mfg_rw.length, + READ_BLOCK, + adapter->mfg_rw.data); + rsi_hex_dump(INFO_ZONE, "Read data", adapter->mfg_rw.data, adapter->mfg_rw.length); + rsi_mfg_response(adapter, nlh, adapter->mfg_rw.length); + } else if (adapter->mfg_rw.read_write == 'X') { + rsi_dbg(ERR_ZONE, "Performing Reset of the device using the below values\n"); + rsi_dbg(ERR_ZONE, "mfg_rw.address = %x\n", adapter->mfg_rw.address); + rsi_dbg(ERR_ZONE, "mfg_rw.length = %x\n", adapter->mfg_rw.length); + rsi_dbg(ERR_ZONE, "mfg_rw.reset_value = %x\n", adapter->mfg_rw.reset_value); + rsi_sdio_write_data_region(adapter, + adapter->mfg_rw.address, + adapter->mfg_rw.length, + WRITE_BLOCK, + adapter->mfg_rw.data); + } + rsi_dbg(ERR_ZONE, "End of Manufacturing mode operations\n\n"); + break; +#endif #ifdef CONFIG_TWT_SUPPORT case TWT_CONFIG_CMD: adapter->wlan_nl_pid = pid; @@ -209,6 +420,34 @@ static void rsi_nl_recv_msg(struct sk_buff *skb) } break; + case TWT_AUTO_CONFIG: + payload_len = sizeof(twt_selection_t); + memset(&user_config, 0, sizeof(twt_selection_t)); + memcpy(&user_config, nlmsg_data(nlh) + FRAME_DESC_SZ, payload_len); + if (use_case_based_twt_params_configuration(adapter, &user_config) == -1) { + rsi_dbg(ERR_ZONE, "%s: TWT_AUTO_CONFIG Failed\n", __func__); + adapter->twt_current_status = TWT_AUTO_CONFIG_FAIL; + } + memcpy(&adapter->user_twt_auto_config, &user_config, sizeof(twt_selection_t)); + if (adapter->ax_params._11ax_enabled) { + if (user_config.twt_enable) { + adapter->twt_auto_config_enable = 1; + adapter->twt_retry_count_limit = 0; + send_twt_setup_frame(adapter, 0, NULL); + } else { + send_twt_teardown_frame(adapter, + adapter->rsi_twt_config.twt_flow_id, + adapter->rsi_twt_config.negotiation_type); + send_twt_session_details_to_lmac(adapter, + 0, + adapter->rsi_twt_config.twt_flow_id, + NULL, + &adapter->rsi_twt_config); + adapter->twt_current_status = TWT_TEARDOWN_SUCC; + } + } else + rsi_dbg(ERR_ZONE, "STA is not yet connected to AP, TWT Setup/Teardown will trigger after connection\n"); + break; #endif case SET_BMISS_THRESHOLD: if (adapter->device_model >= RSI_DEV_9117) { @@ -267,13 +506,16 @@ static void rsi_nl_recv_msg(struct sk_buff *skb) break; //__9117_CODE_END case WLAN_9116_FEATURE: - memcpy(&adapter->priv->w9116_features, nlmsg_data(nlh) + FRAME_DESC_SZ, sizeof(struct rsi_wlan_9116_features)); + memcpy(&adapter->priv->w9116_features, nlmsg_data(nlh) + FRAME_DESC_SZ, sizeof(struct rsi_9116_features)); rsi_send_w9116_features(adapter->priv); break; default: rsi_dbg(ERR_ZONE, "Invalid Message Type\n"); } } else if (pkt_type == BT_PACKET || pkt_type == BLE_PACKET) { + adapter->read_cmd = 0; + adapter->bb_read_cmd = 0; + switch (cmd) { #if defined(CONFIG_RSI_COEX_MODE) || defined(CONFIG_RSI_BT_ALONE) case BT_PER: @@ -307,6 +549,8 @@ static void rsi_nl_recv_msg(struct sk_buff *skb) case PER_BR_EDR_TRANSMIT: case PER_BLE_TRANSMIT: adapter->tx_running = bb_rf_params_bt.Data[0]; + case BB_READ: + adapter->bb_read_cmd = 1; } rsi_dbg(INFO_ZONE, "BT PER STATS Request from Application\n"); adapter->bt_nl_pid = pid; @@ -346,8 +590,10 @@ static void rsi_nl_recv_msg(struct sk_buff *skb) payload_len = nl_desc->desc_word[1]; rsi_hex_dump(DATA_TX_ZONE, "TX BT pkt", skb->data, skb->len); status = rsi_bt_ble_update_gain_table(adapter, nlh, payload_len, cmd); - if (status < 0) + if (status < 0) { + rsi_process_rx_bt_ble_gain_table_update(common, status); rsi_dbg(ERR_ZONE, " Failed in BT/BLE GAIN TABLE UPDATE\n"); + } break; #endif default: diff --git a/rsi/rsi_91x_per.c b/rsi/rsi_91x_per.c index 49cac50..b8f1484 100755 --- a/rsi/rsi_91x_per.c +++ b/rsi/rsi_91x_per.c @@ -36,7 +36,13 @@ int rsi_stats_frame(struct rsi_hw *adapter) mgmt_frame->desc_word[1] = cpu_to_le16(STATS_REQUEST); mgmt_frame->desc_word[3] = cpu_to_le16(adapter->ch_util_start_flag); mgmt_frame->desc_word[4] = cpu_to_le16(adapter->stats_interval); - mgmt_frame->desc_word[5] = cpu_to_le16(adapter->false_cca_rssi_threshold); + + //__9117_CODE_START + if (adapter->device_model >= RSI_DEV_9117) + mgmt_frame->desc_word[5] = cpu_to_le16(adapter->false_cca_rssi_threshold | PER_RATE_STATS_ENABLE_917); + else + //__9117_CODE_END + mgmt_frame->desc_word[5] = cpu_to_le16(adapter->false_cca_rssi_threshold | PER_RATE_STATS_ENABLE); /* Indication to PPE to request statistics */ mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); diff --git a/rsi/rsi_91x_ps.c b/rsi/rsi_91x_ps.c index bb2d36a..0eafb9c 100755 --- a/rsi/rsi_91x_ps.c +++ b/rsi/rsi_91x_ps.c @@ -95,6 +95,9 @@ void rsi_enable_ps(struct rsi_hw *adapter) return; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + adapter->user_ps_en = 1; +#endif rsi_modify_ps_state(adapter, PS_ENABLE_REQ_SENT); } @@ -112,6 +115,10 @@ void rsi_disable_ps(struct rsi_hw *adapter) return; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + if ((adapter->ps_info.monitor_interval == 0 && adapter->priv->disable_ps_from_lmac == false)) + adapter->user_ps_en = 0; +#endif rsi_modify_ps_state(adapter, PS_DISABLE_REQ_SENT); } diff --git a/rsi/rsi_91x_sdio.c b/rsi/rsi_91x_sdio.c index 50518d7..4fe36ee 100755 --- a/rsi/rsi_91x_sdio.c +++ b/rsi/rsi_91x_sdio.c @@ -1147,6 +1147,7 @@ static int rsi_probe(struct sdio_func *pfunction, const struct sdio_device_id *i adapter->num_debugfs_entries = MAX_DEBUGFS_ENTRIES; #endif +#ifndef NO_FIRMWARE_LOAD_SUPPORT if (rsi_hal_device_init(adapter)) { rsi_dbg(ERR_ZONE, "%s: Failed in device init\n", __func__); goto fail_dev_init; @@ -1158,18 +1159,26 @@ static int rsi_probe(struct sdio_func *pfunction, const struct sdio_device_id *i goto fail_dev_init; } rsi_dbg(INIT_ZONE, "%s: Setting ms word to 0x41050000\n", __func__); +#endif adapter->priv->hibernate_resume = false; +#ifdef NO_FIRMWARE_LOAD_SUPPORT + adapter->common_hal_fsm = COMMON_HAL_TX_ACCESS; + common->common_hal_tx_access = true; +#endif + #if defined(CONFIG_ARCH_HAVE_CUSTOM_GPIO_H) if (common->ulp_ps_handshake_mode == GPIO_HAND_SHAKE) gpio_init(common); #endif return 0; +#ifndef NO_FIRMWARE_LOAD_SUPPORT fail_dev_init: sdio_claim_host(pfunction); sdio_release_irq(pfunction); sdio_release_host(pfunction); +#endif fail_claim_irq: rsi_kill_thread(&sdev->rx_thread); kfree(sdev->temp_rcv_buf); diff --git a/rsi/rsi_common.h b/rsi/rsi_common.h index 532a033..8b717d1 100755 --- a/rsi/rsi_common.h +++ b/rsi/rsi_common.h @@ -118,8 +118,10 @@ struct rsi_sta *rsi_find_sta(struct rsi_common *common, u8 *mac_addr); void rsi_init_bcn_timer(struct rsi_common *common); void rsi_del_bcn_timer(struct rsi_common *common); void rsi_bcn_scheduler_thread(struct rsi_common *common); +int rsi_response(struct rsi_hw *adapter, struct nlmsghdr *nlh, int status); #ifdef CONFIG_SDIO_INTR_POLL void init_sdio_intr_status_poll_thread(struct rsi_common *common); +void rsi_sdio_intr_poll_scheduler_thread(struct rsi_common *common); #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) void rsi_roc_timeout(unsigned long data); diff --git a/rsi/rsi_debugfs.h b/rsi/rsi_debugfs.h index cf8378b..90fe9c3 100755 --- a/rsi/rsi_debugfs.h +++ b/rsi/rsi_debugfs.h @@ -19,6 +19,7 @@ static inline void rsi_remove_dbgfs(struct rsi_hw *adapter) { return; } +int rsi_validate_ps_params(struct rsi_common *common, int *ps_params_vals); //__9117_CODE_START static inline int rsi_init_917_dbgfs(struct rsi_hw *adapter) { @@ -54,5 +55,6 @@ void rsi_remove_917_dbgfs(struct rsi_hw *adapter); //__9117_CODE_END int rsi_init_dbgfs(struct rsi_hw *adapter); void rsi_remove_dbgfs(struct rsi_hw *adapter); +int rsi_validate_ps_params(struct rsi_common *common, int *ps_params_vals); #endif #endif diff --git a/rsi/rsi_hci.h b/rsi/rsi_hci.h index 4716d1d..4c35c8f 100755 --- a/rsi/rsi_hci.h +++ b/rsi/rsi_hci.h @@ -136,6 +136,12 @@ int rsi_hci_attach(struct rsi_common *common); void rsi_hci_detach(struct rsi_common *common); int rsi_hci_recv_pkt(struct rsi_common *common, u8 *pkt); +int rsi_send_rfmode_frame(struct rsi_common *common); +int rsi_process_rx_bt_e2e_stats(struct rsi_common *common, bt_stats_t bt_stats); +int rsi_send_bb_read_data_to_app(struct rsi_hw *adapter, bb_rf_params_bt_t bb_rf_params_bt); +int rsi_process_rx_bt_per_stats(struct rsi_common *common, bt_stats_t bt_stats); +int rsi_process_rx_bt_ble_gain_table_update(struct rsi_common *common, unsigned short status); + #ifdef CONFIG_RSI_BT_ANDROID int rsi_bdroid_init(struct rsi_common *common); void rsi_bdroid_deinit(struct rsi_common *common); diff --git a/rsi/rsi_main.h b/rsi/rsi_main.h index 5692602..f68dbc6 100755 --- a/rsi/rsi_main.h +++ b/rsi/rsi_main.h @@ -17,7 +17,7 @@ struct rsi_hw; #include "rsi_ps.h" -#define DRV_VER "SiWT917.2.10.0.5" +#define DRV_VER "SiWT917.2.13.0.4" #define ERR_ZONE BIT(0) /* Error Msgs */ #define INFO_ZONE BIT(1) /* Generic Debug Msgs */ #define INIT_ZONE BIT(2) /* Driver Init Msgs */ @@ -128,6 +128,11 @@ void rsi_hex_dump(u32 zone, char *msg_str, const u8 *msg, u32 len); #define DEV_MODEL_9116 (adapter->device_model >= RSI_DEV_9116) #define COMMON_DEV_MODEL_9116 (common->priv->device_model >= RSI_DEV_9116) +//__9117_CODE_START +#define PER_RATE_STATS_ENABLE_917 (BIT(9)) +//__9117_CODE_END +#define PER_RATE_STATS_ENABLE 0 + //__9117_CODE_START #if defined(CONFIG_RSI_11K) && defined(RSI_DEBUG_RRM) #define MAX_DEBUGFS_ENTRIES_917 13 @@ -614,6 +619,8 @@ struct rsi_common { struct cfg80211_scan_request *scan_request; struct ieee80211_vif *scan_vif; bool scan_in_prog; + bool acx_module; + bool acx_stop_beacon; struct workqueue_struct *scan_workqueue; struct work_struct scan_work; struct rsi_event chan_set_event; @@ -836,13 +843,33 @@ typedef struct { unsigned int rssi; } __attribute__((packed)) real_time_rx_stats; +#ifdef NO_FIRMWARE_LOAD_SUPPORT + +#define MFG_READ 'R' +#define MFG_WRITE 'W' +#define MFG_RESET 'X' +typedef struct mfg_rw_s { + uint32_t address; + uint16_t length; + uint8_t data[4110]; + uint32_t reset_value; + char read_write; +} mfg_rw_t; + +#endif + #define SET_BMISS_THRESHOLD 14 #define KEEP_ALIVE_PERIOD 15 -#define REAL_TIME_STATS 10 -#define TX_STAT_LENGTH (sizeof(real_time_tx_stats)) -#define RX_STAT_LENGTH (sizeof(real_time_rx_stats)) -#define TX_STATS 1 -#define RX_STATS 2 + +#ifdef NO_FIRMWARE_LOAD_SUPPORT +#define MANUFACTURING 16 +#endif + +#define REAL_TIME_STATS 10 +#define TX_STAT_LENGTH (sizeof(real_time_tx_stats)) +#define RX_STAT_LENGTH (sizeof(real_time_rx_stats)) +#define TX_STATS 1 +#define RX_STATS 2 //__9117_CODE_END typedef struct { @@ -1103,6 +1130,9 @@ struct rsi_ax_params { #define TWT_CONFIG 75 #define NEXT_TWT_SUBFIELD_SIZE 3 //Next twt subfield size value of 3 indicates the size of next twt to be 64 bits #define TWT_INFO_FRAME_CONFIRM 0x4 +#define TWT_AUTO_CONFIG 23 +#define MIN_OF_2(X, Y) (X > Y) ? Y : X +#define MIN_OF_3(A, B, C) (A > B) ? ((B > C) ? C : B) : ((A > C) ? C : A) #if (LINUX_VERSION_CODE <= KERNEL_VERSION(5, 14, 21)) enum ieee80211_twt_setup_cmd { @@ -1137,6 +1167,7 @@ enum ieee80211_twt_setup_cmd { #define TWT_INACTIVE_NO_AP_SUPPORT 16 #define TWT_RESCHEDULE_SUCC 17 #define TWT_RESCHEDULE_FAIL 18 +#define TWT_AUTO_CONFIG_FAIL 19 typedef struct rsi_twt_config_s { u8 wake_duration; @@ -1158,6 +1189,8 @@ typedef struct rsi_twt_config_s { u8 req_type; u8 twt_enable; u8 twt_wake_duration_unit; + u32 rx_latency; + u8 beacon_wake_up_count_after_sp; } rsi_twt_config_t; typedef struct twt_element_s { @@ -1243,6 +1276,19 @@ typedef struct { uint64_t suspend_duration; } wifi_reschedule_twt_config_t; +typedef struct twt_selection_s { + uint8_t twt_enable; + uint16_t expected_tx_throughput; + uint32_t tx_latency; + uint32_t rx_latency; + uint16_t device_avg_throughput; + uint8_t estimated_extra_wake_duration_percent; + uint8_t twt_tolerable_deviation; + uint32_t default_wake_interval_ms; + uint32_t default_min_wake_duration_ms; + uint8_t beacon_wake_up_count_after_sp; +} twt_selection_t; + typedef struct twt_info_frame_s { uint8_t action_category; uint8_t action_type; @@ -1288,6 +1334,8 @@ struct rsi_per_params { #define BUF_READ_REQ 0x6 #define BUF_WRITE_REQ 0x7 #define WLAN_9116_FEATURE 68 +#define SOC_REG_WRITE 18 +#define SOC_REG_READ 19 typedef struct bb_rf_params_s { unsigned short Data[1024]; @@ -1396,6 +1444,7 @@ struct rsi_hw { u8 disable_programming; int wlan_nl_pid; int bt_nl_pid; + u8 bb_read_cmd; struct sock *nl_sk; per_stats sta_info; //__9117_CODE_START @@ -1410,6 +1459,7 @@ struct rsi_hw { unsigned char bb_rf_rw; unsigned char soft_reset; u8 read_cmd; + s16 gaintable_status; s16 rx_rssi; unsigned long prev_rssi_fetch_time; u8 chip_rev; @@ -1431,7 +1481,14 @@ struct rsi_hw { wifi_reschedule_twt_config_t reschedule_twt_config; u8 ap_twt_info_frame_support; bool twt_rescheduling_in_progress; + twt_selection_t user_twt_auto_config; + u8 twt_auto_config_enable; +#endif + +#ifdef NO_FIRMWARE_LOAD_SUPPORT + struct mfg_rw_s mfg_rw; #endif + //__9117_CODE_END struct master_params_s master_ops; }; @@ -1506,8 +1563,31 @@ int rsi_mgmt_send_bb_prog_frames(struct rsi_hw *adapter, unsigned short *bb_prog int rsi_do_master_ops(struct rsi_hw *w_adapter, u16 type); int rsi_bb_prog_data_to_app(struct rsi_hw *adapter); int send_rssi_to_app(struct rsi_hw *adapter); +int send_gaintable_status_to_app(struct rsi_hw *adapter); int send_filter_broadcast_frame_to_fw(struct rsi_hw *adapter, struct nlmsghdr *nlh, int payload_len); int send_get_rssi_frame_to_fw(struct rsi_hw *adapter); + +int rsi_stats_frame(struct rsi_hw *adapter); +int rsi_mgmt_send_rf_reset_req(struct rsi_hw *adapter, u16 *bb_prog_vals); +int rsi_send_bb_reset_req(struct rsi_hw *adapter); +int set_per_configurations(struct rsi_hw *adapter); +int rsi_send_he_tb_mu_params(struct rsi_hw *adapter); +int prepare_per_pkt(struct rsi_hw *adapter, struct sk_buff *skb); +int do_continuous_send(struct rsi_hw *adapter); +int send_per_frame(struct rsi_hw *adapter, unsigned char mode); +int send_per_ampdu_indication_frame(struct rsi_common *common); +int start_per_tx(struct rsi_hw *adapter); +int rsi_start_ap(struct ieee80211_hw *hw, + struct ieee80211_vif *vif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + , + struct ieee80211_bss_conf *link_conf +#endif +); +int rsi_validate_pn(struct rsi_hw *adapter, struct ieee80211_hdr *hdr); +char *rsi_vif_type_to_name(enum nl80211_iftype vif_type); +void obm_configure_region(struct rsi_hw *adapter, u16 country_code); + //__9117_CODE_START #define DIAG_TOOL_UPDATE 86 int diag_tool_update_dpd_fw(struct rsi_hw *adapter, @@ -1531,6 +1611,7 @@ int rsi_twt_status_resp_to_app(struct rsi_hw *adapter); int rsi_mgmt_process_twt_setup_resp(struct rsi_hw *adapter, struct sk_buff *skb); int validate_unsupported_twt_resp_params(struct rsi_hw *adapter, twt_setup_frame_t *twt_setup_frame); int send_twt_information_frame(struct rsi_hw *adapter, wifi_reschedule_twt_config_t reschedule_twt_config); +int use_case_based_twt_params_configuration(struct rsi_hw *adapter, twt_selection_t *user_config); #endif int process_rx_mgmt_beacon(struct rsi_hw *adapter, struct sk_buff *skb); int rsi_process_mbssid_parameters(struct rsi_hw *adapter, struct ieee80211_mgmt *mgmt, u8 *ie_start, int ie_len); diff --git a/rsi/rsi_mgmt.h b/rsi/rsi_mgmt.h index e9f0043..d027b62 100755 --- a/rsi/rsi_mgmt.h +++ b/rsi/rsi_mgmt.h @@ -257,6 +257,8 @@ enum rx_cmd_type { #define RSI_TXPOWER_MIN -127 +#define BEACON_COUNTRY_IE 7 + #define ENABLE_MAC_INFO BIT(0) #define BROADCAST_IND BIT(9) #define CONTINUOUS_MODE BIT(10) @@ -356,7 +358,7 @@ enum cmd_frame_type { RF_LOOPBACK_REQ, /* 0x1C */ RF_LPBK_M3, /* 0x1D */ RF_RESET_FRAME, /* 0x1E */ - LMAC_REG_OPS, /* 0x1F */ + SOC_REG_OPS, /* 0x1F */ ANT_SEL_FRAME, /* 0x20 */ CONFIRM, /* 0x21 */ WLAN_DE_REGISTER, /* 0x22 */ @@ -816,6 +818,13 @@ int rsi_validate_debugfs_bgscan_channels(struct rsi_common *common); int rsi_start_per_burst(struct rsi_hw *adapter); void init_traffic_timer(struct rsi_hw *adapter, unsigned long timeout); void check_traffic_pwr_save(struct rsi_hw *adapter); + +int ieee80211_chan_to_bw(struct rsi_hw *adapter, u8 op_class, u8 channel_num); +int is_dfs_channel(struct rsi_hw *adapter, int channel); +int rsi_send_common_dev_params(struct rsi_common *common); +int rsi_bb_buffer_request_direct(struct rsi_hw *w_adapter, u16 *bb_buf_vals, u16 num_of_vals); +int rsi_mgmt_soc_reg_ops_req(struct rsi_hw *adapter, unsigned short *bb_prog_vals, unsigned short type); + #ifdef CONFIG_RSI_WOW int rsi_send_wowlan_request(struct rsi_common *common, u16 flags, u16 sleep_status); #endif diff --git a/rsi/rsi_ps.h b/rsi/rsi_ps.h index 62a6f00..f9bcfd6 100755 --- a/rsi/rsi_ps.h +++ b/rsi/rsi_ps.h @@ -44,5 +44,6 @@ int rsi_handle_ps_confirm(struct rsi_hw *adapter, u8 *msg); void rsi_default_ps_params(struct rsi_hw *hw); int rsi_send_ps_request(struct rsi_hw *adapter, bool enable); void rsi_conf_uapsd(struct rsi_hw *adapter); +void check_data_load(struct rsi_hw *adapter); #endif diff --git a/rsi/rsi_rrm.h b/rsi/rsi_rrm.h index 4160962..aa2954c 100755 --- a/rsi/rsi_rrm.h +++ b/rsi/rsi_rrm.h @@ -187,4 +187,6 @@ void rsi_rrm_recv_cmd_frame(struct rsi_common *common, u8 *msg, int len); int rsi_prepare_channel_load_rpt(struct rsi_common *common, u8 *msg, int len); int rsi_prepare_frame_rpt(struct rsi_common *common, u8 *msg, int len); int rsi_prepare_beacon_rpt(struct rsi_common *common, u8 *msg, int len); +int rsi_rrm_parse_frame_req(struct rsi_common *common, struct sk_buff *skb, struct rsi_frame_meas_params *params); +int rsi_rrm_parse_beacon_req(struct rsi_common *common, struct sk_buff *skb, struct rsi_beacon_meas_params *params); #endif diff --git a/rsi/rsi_usb.h b/rsi/rsi_usb.h index bd775e5..dbb726e 100755 --- a/rsi/rsi_usb.h +++ b/rsi/rsi_usb.h @@ -99,4 +99,10 @@ int rsi_usb_load_data_master_write(struct rsi_hw *adapter, u8 *ta_firmware); int rsi_usb_check_queue_status(struct rsi_hw *adapter, u8 q_num); int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num); +int usb_bulk_msg_rsi(struct usb_device *usb_dev, + unsigned int pipe, + void *data, + int len, + int *actual_length, + int timeout); #endif diff --git a/rsi/rsi_zigb.h b/rsi/rsi_zigb.h index 0cc3798..923b196 100755 --- a/rsi/rsi_zigb.h +++ b/rsi/rsi_zigb.h @@ -130,4 +130,10 @@ int zigb_genl_recv(struct sk_buff *skb, struct genl_info *info); int rsi_zigb_recv_pkt(void *priv, u8 *pkt); int rsi_zigb_send_pkt(struct rsi_common *common, struct sk_buff *skb); int zigb_genl_send(struct genl_cb *gcb, struct sk_buff *skb); +void rsi_zb_dbg(u32 zone, const char *fmt, ...); +void unregister_dev(struct net_device *dev); +int rsi_zigb_open(struct net_device *dev); +int rsi_zigb_close(struct net_device *dev); +int rsi_zigb_xmit(struct sk_buff *skb, struct net_device *dev); +int rsi_zigb_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); #endif