From 54ff9bfa4414a9766ed571d7fcd4bb748c7e686e Mon Sep 17 00:00:00 2001 From: kai-morich Date: Wed, 23 Aug 2023 07:48:11 +0200 Subject: [PATCH] composite CDC devices: get correct ACM data interface from IAD (#499) --- .idea/misc.xml | 1 - test/pi_pico/README.md | 6 + test/pi_pico/tinyusb_dev_cdc_dual_ports.uf2 | Bin 0 -> 73216 bytes .../usbserial/driver/CdcAcmSerialDriver.java | 105 ++++--- .../hoho/android/usbserial/util/UsbUtils.java | 30 ++ .../driver/CdcAcmSerialDriverTest.java | 272 ++++++++++++++++-- 6 files changed, 364 insertions(+), 50 deletions(-) create mode 100644 test/pi_pico/README.md create mode 100644 test/pi_pico/tinyusb_dev_cdc_dual_ports.uf2 create mode 100644 usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/UsbUtils.java diff --git a/.idea/misc.xml b/.idea/misc.xml index 367b49b..9e36b0b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/test/pi_pico/README.md b/test/pi_pico/README.md new file mode 100644 index 0000000..b0e5d4d --- /dev/null +++ b/test/pi_pico/README.md @@ -0,0 +1,6 @@ + +# `tinyusb_dev_cdc_dual_ports.uf2` + +compiled from `C:/Program Files/Raspberry Pi/Pico SDK v1.5.1/pico-sdk/lib/tinyusb/examples/device/cdc_dual_ports` +to `C:/Users/` _user_`/Documents/Pico-v1.5.1/pico-examples/build/usb/device/tinyusb_device_examples/cdc_dual_ports/tinyusb_dev_cdc_dual_ports.uf2` + diff --git a/test/pi_pico/tinyusb_dev_cdc_dual_ports.uf2 b/test/pi_pico/tinyusb_dev_cdc_dual_ports.uf2 new file mode 100644 index 0000000000000000000000000000000000000000..9e45a1ceba4ea3216a5d99c2671cd9a5bdb2f3de GIT binary patch literal 73216 zcmd?S3v^UPwl}^{zq`{Rod={l-6Z`8kfZ~I1dxYHryEa_Zj?9j(3$WM8(%asj*U1& z0Cn(j=R$%GN$>^ujf&1-AdZeiGdeTM#hH6L0VgEm*a&)N##fK1ofqlvSLYNdJz*vc#k3skt=Z zurIIP7gRWQ`Sw+JRGU{XUp*;6j{0haW5sID5p&ira93HXImAq}Q*3Ux!nxaW0urbqqcXEfSFL=n=7MgQu0q*df1AqAdHVzo|GOi~7Dcmpy?K2i{&m89 zm-#M53z5Qu&oUEfiIhJihm+_VTs!Ub48Gf>`+}lKzx4Z2(+&4sx)^~cB^zHcEKSBA zb-?gvByb6TyUHF%{5uGLK=|GNLwGkS?F7zGBK+z9=Rj{>{`=~Jy2dnv-u(KrU7POT zNv1;xQx#;|eK1EPdmd;>^WJvQlBr^BB?YNqawPT82!#F8Ck?|Nb-}<_B;h~meJWH4 z6wB!D5zSQ=KiMhD{cex9KayRwt70h7n-@jy1ow7>x34`^!Rob@$i6#;>_7TB$|RMY z1?k*`a~ALmRp={~vCbZ^hQ?q8CD@<-}L>(ZvLQqwLzu)3qu8^ta z86&;~zsfuCHUU#Jxs`&lO3qz+=PV(0Y-c|=^|&{!jVApw!gA^cb8_x~ zM(m}EkSN+`DMIH(mRH}eJQ71)Z`nbnlIA@}D4&aRQ_nYX#iKjyNRRYU!FPhkxq)X_ zoqcLh<_4bZiTJCDBAjtJdnNISn0}RrO-n7pf@8C*2)aq&^rB7L@tO>P{y^n1>sl$JETZc6tP7hNf=KP+g70<1qZyGXCl$ z{OeY$9f|l82@`rx%78Vf07AapedVo9sQXrp$JAWo>QbA73Ux-RGR3O08ue9=c$ERa*Lg=G?nB=+41bM`za|O)M7%XY#fmOFF5KG$+IYDSsI8p_uF3)9lDk~H5 zUni7DsO3G-fjyn<+CXE_R8m8h1*~iqkk?_U-XT!{+Gq6D^FbS?W_^w>&j7w(Jy-AF zZ(+YKi$XGXadMNVt>Q%*UI>#iY~SPE}yT4)u7sc*r7%Z77n29V~GTH zClVplTft!cF^Q@4n}V061M;0kf2&X>CcZt3OJ5!rcLy@X?JU7JPiz zF#L5g{<nvf%^K%{Wp@N}LU#rlf&t!C)=2Hk_wl)8&6(_;aV8kp zcirTO`d7CG%4)V!Bi~`Eq1AzXyl1YWWNlRgNw|(~`gRl7sIB|q+_$J= z?x;VHdY5zAe9!QU_;S9IPsAyqKN36s2H(WLz(2#sT9yTt`Tycr2Kw7@aamxo|8I`P zIA43Q)#sUObSTiaP+>-db3Nrd%9jz1@7(gDH~oypv0@n&8oT_o2r?>{t_?Jr|0Pxg zeExqre7N6tF_&o4EJ1zP=g+CxK5J!-#&uztqK@3zopUPHha35QHCGh=dKrH-&6oIJ z(a^Bz!%bX+wzg!f1N`;wz>%8YY}!g)mPRU?pD?fDwI{Xj9uULS3be-uTFt7G(y>Q( zoiW%7wUJfo6{8hXs%W7(5r1*uwCE1F{jWIkQTOVL>R*kO=tNP+=p@^}n^%x`uCjKw z%?w(p8wk}X8%$$YfExY{&&&<11Z^zJSyaB*XHAvp2k)e4+|S>7iH6dVC$nbz?8P-Q zHQgw-@{IqxFrIvdwKNqQ*KH&dR|y>m`aAdI{EEWg zAmeXH!oM1I^Wuv{vF1PX6@1O$ND$I;*WIr!OOlpLAQ7fQopO-=vV2kCX|GoJwU;9M zqTDYQ1o|Ky^k=;MOez5dWeg~fv>tz*KLWmH*J}p0^W

TLxG=4=v4hD&&O6yy@U* zZvTECIS&R8dou(keW)n4YcVyN8_7vC=bZ;XPj}1+%=9Ow%mh`f?03>(@3M$y+$}fK z#2H<;(<(~?O$OgL1P@eqZuxkV#@Vp!z^2A6tD4B2);Y2AnHrX-k;ePm%|-t7aQ&vy zp(>ueP3gdMqJ8WYg@1~Se@YVma_!{#8_hXm8!vJ7s?mzF6%tp!b@4-SY#s+f4BJI~U&-Z*yb> zbU;vt5_LtLntb8x;%fs>%S zf)3ErPI9{i(ClM(zgp95xg;8P^ zu~FidzE+;$&Ez)2l+l0yOpSxscjKJNaE3p_U4?jZssf6rs!H(=SE?=QmBt@*9{o%F z6w=7xVPYBp55^?W6M?sz`Z=k$h>onwwTZo>iD{6K^4ws`IVpu)Bbfk(_5V_3{8N+g zUprVkOlz}xE~`gYTbJvS>EP-I>+B2k;6u%7jLXr3#8^vTWykT6i;4Js{j%1jhJw|_ z_XsYl#u~o;op zdENqej!?yGpaE!Z3!Q>8Xx&=x&F#pR$AR3=AbVS5unN3xky0DHqdRs6(<&d}*UZh} z8qB8P;-G>!`=eg2W0XMlR*VyM0loifhaOb;YA+izUX3v$s$#_chT%U-#(xyPcu4=t z#g6nI@P5_t1!Qs}kw^>O4PDKT$YjHj)qSp@M9o_}0<_AkoUIBTg4T};Edmvd74~=? zIF1ukTMu}h9jf30sxd2P*v;ERmM|6OMRK5rHwDuDK}S03{Qh2czH!PZod38tEilR- za-`rq)SDVe@o#dl*5S5ZHns~&W4m>|<)NI=hj_2Oco*Y6WIBSQc{7>*j`z#Qu>>Im zJy9xU5fx_PU9yCl;O5}I3M$YDPK+2?JHJ0sK-y>#Ii#z{6L#nZOcZL-QNK81p_e{v z82)K8{!BeGgg?`oK_6Jl)fo=k!YPOI!c;sS9JO-mUauDYGjdxZR6IK(>xZb=DC8hT z-;onsOIC|=cDuO|GccbCoa-OrwShUnZVqtN^s@6?rE}_42KR5J;w~XWx;L$34P{uA zvn;{Kz^ehXIj6HBv=WrGX{%GBfc1&c<_Eka zJT4aegc?RN1q{O(PU6t|9;PdjIkc{)Am~5~@bJLLVlt0bf-lYBS0+P}Wr`Ws{ovhX{mi3B^t;KW ztJ^`%fe)5nMT?@x0>}Jw9LMn7wLR?oYU%vu9*h|xRImZo1RiLpt{Zs9Yl##EZx3o} zwRMMs`}og-4ZM#hH{?LES(q)+OjgI*+}>dOO}aX(!t9%|n(U*a2LcEDKX*Kh^2>Uj zy|VOw=`#N5N%%kQuXXH4T}Pv$d4JLIv#MLn6S>#H z`M1n12yO`0iLDdnOZBYzHU@^k^Am0a;WxjQ-QFB=lO@p>^k@YC*EDLou=6|TOTLwy_ zO9Ko2&pK`Zwe0TZoQu4R0-uW41$b~SkGO}rX%e_~;<3nV$mInBYfY|-a1JW;M>@As z(e;rA{8vinDxGcA#QFr6{kuqj$yFHF$Y=iur^FZzZE#gYvTC8 z1pgIR?g^DltM zXCQ6{;?^W+^879zzmm!R2QJHg?H+P>OVU5DOnw7Z9l4Zsin;*L)h*_!u9Rkz2fF37 zi^|-lh=R)bD!DBimsOc)Tf^oA42F-ZX5Z{6uF>R6upX|UubmO}QmmwGM_0`2Y=a;FMoP=cZ32lSm; zd2M#^IKO`GtKlmO|1mQDW0LTvNgoKD{gjv)$bw|g0*22I6gx5lnb7!U;_BG}c3$o< z;k`;tNIhv;Ad zu0zcaVC-qc+$_`J=0+0nBLn18)NyrA*bNjxIhTmH3}|v)AKKqa#19Ni!S#lOEwJh~ zHpa{G*WUJ`x43P;cO1T8d~^)r>oGFTLi`S|wyhpB$DBPU3d>}KF;UK*1-mbHuf8Ki zx>t*PuPFSpW&C01cS-)UI?|zjGzcoxkpf4|-rH42LM@!VPu#?GhA_4WHOtnu;7@4T zbCPC;IA+s-Zv^> z32CDSzjACN)i1AKfQg zT~YX(W&B|ve+hrsW8AjW8;4b|Tu-t+Mk3yozz7PYPzasgit)!wesh^i**MZrDOBP==(I(hwAigz`9oXqj5iEGd zK>Yp$=U~TRf^!}8r28|g4^cXF$BZiE*#$QK47D79)gU<{3pnQikD)3hT<1#(siB2V z#Gl3*0#-sr;=IUcrVz9~qZ+t_Rv6u|z6?R$s9;mZ)#j3-j>!_`gFoQZS_9x;~5WlQ z56M_oum3l7{uJBMKcw@g*!}%OI)94&xPM6JPq9${kj|fCxAiCM{Il-8tn(+F|KHO2 z;~QPR&0N{e&C0D;6#iBje`^x{?`-B=imeUatTW^`3W}|D=pC)3HjMf?Nk7k(bu}#x zeIfPngHj*g+)qV%p%&T!F1VN)kn<5UxfWxthqVPksV&g-v+?^7erT&Vd#iU=C*p5Q zHh`>+(dAMER^^3L1sCR*rVP$6IVFw#N@!?e!bGGrpjXeVL6ox{c$>9zZw_M(S%?b8 zA#ES#oP1=xl*UrzS>KHb4caZ%D*Ojpu_@BoEs1wUHvbpI{2#?$?PK$Q!7FY4&nDw< zOTwRGNBS<~_F`W$Zt`3&Mhk_Lg{jg!;1EuVOE^u6GyttB5la|0T?r-Z?F_;{q1&4j zc~R2vvGduHmo|UCncUZ4CTYIl#thB84tH=h87<1IZ5DE^4TW091;+blIK~5;U>`dV zO6O~EZuie}*b#qIpDQrdKih$|Td`$*Oh;9iq@ya7sC7x`9dS%x4DKF-T)sYYz~uKz zW0!0C$ekh-fxdNvR8u6+4vk;|FiifBmGNgrPebiLVRr1OsK3M+g?*YK&WO2St6}^x znLbCzLP(~KAg*^KFr6IZX9HcgwGbS4OQL#n0cKZO92MRw__jWZHl7O8CHfgP$mfcn zVBQI>gE2z(Wl;_*5I%(FU}3Z{PzdY_f!!y)Oc&trfS1z5dd>nG5_@A}V)97MFhGfG1cDest$Z7#EiE162X4l6(arA?G)u%JH2@g?ho@)W9~UdalpAyxfCzA8#)TRbWjRT|k*s z)Bo1{=v!XK>)IYo?gU{>;7RXzp~l&!939B1mP2DOo$7tPnB`?iwI$f{F0GL$3u{iQbpiKUrR(sz63|?YGeh+k>6uCoMae@kgjTFL16_3|tqV|7 z8rAZKD{KDWF5_=c!oSYDpgm{Q9zIurx^R_H70<=$`fB()XdTuBlmOFpbJqrUda13t z+MU7{D4hmRPW6Ex=X%4F>FpIxN_0S;eCPn|7(eVqn2JyWn-?P_ zKzh%>l{NqCknwjU;jivxYh}r$iq_La|D?)Y3%D}9yZcO0pxD21jS?$w-54u3+)6P; zPho&VjZX-yx`T7~MJ5DT{ksBGsYjS9DU7Q%Y(&1`c!hn0_R6VZ8CggizbAYQc ziI;&f!A*!m;sJ{q~d zVe;Q80@MG2n=au$)IJ`9Ts$9!uLOi@g!>WRLx7|v zrH}Ksufck~8ns6gcu|u2lbxN_OmCA@t&rx^`#O>CQFy2t;Ycqrj}fNkM7&SxOYAse zWLih8^40~YC`I6E8BZ%nQ>lo3-NS*C#e^#GFH!0L7x+1b=h7C~is2Y)W^0Oas@;%B zIn~8jg;Er#L;s*?9v9WUzptVYl-3I;0FMv2V8I~CqhOb>i|dVy3wR*6R^FO(Yv+2` zJbly^aQS}*>6D271~#Ar@!R@!Qz4f|@NXFY`7-|b_~s%0$81;|f>sy@#q=;2@KUXc!(P1v;&4KuZ?t%EU{(<=GiL5~sG9?t)d=2Y!z4fQQ z0Y+B?+pZ?o_X=YoVHX1CdO3=5o)FGUfbZ`S79g;6whjQVCFNs&MbeJ36~sEwM`@1= zR*70c{Q|egjJKK9qeSckiE8F0j^O@Qgir;vdIbt01vcUPDoiaPbGtUW6Z)Z5w7{mT zHicWwB{K}c%y7xl_z$g<-Fc*r&m$wOJ5u+L zf_+C?W206nyvC=IhMZBZ_8`9tD{5cowR8+~t4nPGtk$#JU#sAz7SXrjH8h{UiRT&{ z`B!+Je~rJ2CXqJEUA@${m^a_-!^*HD6-7kF-$tg8m*d<=io4Imwc6ABy=t5*J4z;uRP5Pz4a0xDjQ@C~4dKsf z=36nY8GZU*wfYnlD}}w@d||)WDWo-+irmuw68u*bO%w8p6&)qcBHWQztnTph(EAA! zrEBFAn6=+akNfyFu(?^BP+wXBxF(k?)(B}&n;I|Lh1$ z_eXI3{^`8p|L9mQU7ry1ir?+1E@jVpN4#6BJnfd^|3fU*zIWO#o&O#bae1A09oBh2 z!s}}Fb+*H%lgu_L_en=1Y*>qx5tGZyH~4n*@MJ+epX>4RIaqB-#ZG}(RgBo*F#IRT z_%os(!hck>r0h)7y*9()`lU=~PO%RXFL}qb=S|M)xRz9s9-U(ECr(0U{sU_~Yc=ZA z<&G2P6HSJhCz?JMS3A1J3FPB7b@oPeU0$QAPWkvM9@Ov)!(pn^H|iP|N-MtYiOlcu zdNOv8AJOgB?=jS-)a5>vx8E*VmN_AVTu+XNJSxLv4KOTQ1Pp5>3~%J~Jd1##MPO+Y z1)7kBujp*rYJ*%j+_}`xyJ6|?z_-Jio)IaXTQUhjRz{Ni8 zfs#k~B+BfF{}Pbn{vyS7NpbCialet`BCr<0ih-v}NdF|e`z0y%!>DebzMk2kGcM0< z$nm7s;=h-RXk4~B%|2~Cvt~1(HBhqUSaLYe=8eGh`pa#kd|W_Uj(_*@*3x za=+qlLXNRL6KbsJlkS41B4F5;QqRR&yUDo^B_Hi(CCPEG zB5p6@UPy|&qVO-0@kdYWlKg*3stcLto|MiEbU#*yXdj2qMpr!_;MbP@(Vg3&8lT%}RgXXIhQv=3*7?^3 z7*BJtH@c}{3s!Tl9=v`I*P8@eW8gZ?hkkxvjxX0%r;Mt{e}vL(b%V0Oms^XLAEm@$$z(uKe*-+{!e+=wLOicHcVm~ATcw&@CG>vz=J;UDdx(KG0a_(F2Yi?nEWq`vgVzUeU5RUQ$>|MpI{Grs##L%M z^=g#zmq(qDPwXp3>TekTPn7YWhz}m>|2x37N*7o6JhbcqekHH1*Pt)=b>FD7taezc z3aJOamwK)*5mmXIkQmPT6t78WgibFF{u`b3$~}&H#U7^JyIj`$eSeSc4SXRgonMMh zvk%18wt z-!%;XNizPElJF)?=cW3h}TIpN7_Qa&ZLXhL|gfrl5*B>}nS+mZG`1S@Xgl#OMXoV(Ktp#+YKi?^}PVM0S0=PmLANQ|SwF1j9cZ z|H(4`lauhDN=k5YriXLepx@DS_}Cg>!FYJ0RD(k@fZNN)dY5_iMW#;4s!3*$Gm@r&MkiCr0A&cM)(fWV3k&N+IZM4J?lMOTeW|{ zwOzVy6(B*=eD*O- zSjwk2n!^}jM#PWDVTIcLXqjfw3jX5LF{Opb|x`kQolnKGT#@ z_~VX8=?_&WaR>9x#+9bmejq9a&my8~@a%n2H+c50Xc|0wTTFjG5g*@c*c}mVjX7g& zwPtu{S9KY7GlbIX{wS5>^BU^D!%SWqW%*Xx(rZ_SGY8*Hb*88YmMM6f-$*5Ky*v1( z@0GLzG{8#wrAYQ}5k7-4idbk%-?Qk-vj3VQ<3A+{|2v8}!A9mNEGh`7O-bLA5v_3z z)lN`K)gx6-Qz2~?%LjBBS1D=S?9QgeMRcb4<2riC-wyQIjG>(43#q2%b5RU~_m&sT z5H=#LK`^=Y`wZjZi)H-sf-+%4fl^q9w#Mk(>szblPIEYJC@4Z)fpAbwr=O0V!~fT! zYc+K0KccpV^s%-*>A?gnKGb^+=v6F+UP|!#XzTkgn8LtJ-U5 zP}VNu6}2}7v06;hAqj`>)?36u-RPJZu{S{E#qXVFzO4^Ldm+oQ9{)c8aGC_jlb(WQ9$Oc($jslYY^cvC}tz4x1r zY4ijB4c@f(io$=IjQ_MG{D!iw=nwSznHEvwE-2CAZNgtnX&6^kvzxzk7(g zi#{3Zn~$^0y>r$(&ASwPId1ePhCl;v`s78v;yOh;{6pjtFlQ7=cA;VheSfa;1hac- ze(;E5w{j2Xpt?-*dlfGzUm%!_P%R`x_P21OX=A8D)m-DyYu?UY=wb7L@A;Im$9mzl zsU_B&b=T6nZrnI#VZfM&t3O!oW2n{qVB!C z*FRMIYhb6jm%sK>{v}fWSxNclA^-I6+f3+ovTac#rrnS*mx@sYfkVEJoXZ+ z^VY#SXLG4cKZ{iG9+Wc)wA#X3(M)3jYEu+yj-}6^iPBg}CC608ts7@4&3kzomo z+^ggbEM|;rmGy6)k34T#*756oJFFpIHBD2-(HYP4o>mLBb5jm@HEr0niAm={8p*J7 zB^TQNCF&<^vkrLH@duh%iPXl;bQ~6$SQd}ICU^XKOHBsX+8fP=8B38wiQDr_xHMSI7abwbO1=`t7;htyYGb{QqfOpRk^=W*hJI{Zy4msj$lqkb^&At4uL0Kio4>TPHjcf*$)Rzy@2s@XoLarj~3*|j+;aqaWT8z@2 z_ww6Pe(gzEq9WzrY|{!^^MA5_rX+>fcU-;k@usJmZUIGmtgn=hHs%`H`E=D}9v%nGaj ze%OHNT2vpUTnm4%$FkvbLyn=3Mk8h~4`1=qj>bE0v_e*N72vN;Z3yEd)bGL)ZZ}vzgos0 zR{59szaLm9T2jz1Dt(#0Z-o=SDsuv|JE27;tl#+0L`^v{yMCOXNN{I>LJNn$u3!Hz z*RM!SpK}SSO(0T1ZsKWVv z95Lc2`~8>`MEDVcwz-;TbA9wCx65Eqlzddw8#&RmL#Jw2?DFg)6?_~7R+`y{^8epMD7_l%_$&)1%^@**GeH+C{j4N26B zoq^VQ7nCn7U&CLYS`bN1#h#M|c|yL$Q(jfh%}Pz}5!R8Wos;^8RLn&@IuCKPQcmr) zokuS)L!CaiOKX`MYdNE}2P{DTj2522F#h`xo=CbPXzAEg$cX(7!yoM^!=D+w4dMSs ztw;AKZGo>!N0ZJ9XT+-~6?WRM^+=cybirSHvY?@V!(;QcXTK!A#M>w9I_=X{bj*`V z&7N-0%<=-?bK=#Ljh*&69x0yP)$LLFbo7eH==)vvKSjx|D}S#D!;5W$7iV*A1XO{1N2=+%st4)M0sTXW~AyR72(b2vGdRmyR%XWiUi9DAVYiKZX?cA*-16WT%wwW1&2Q=e}LcB)X=oRXEd(y<{y-ky`o%md{+}xsf4+(uxW-gk>RV^^`OfD&-tII@QSi?-IJ3$&Sa=+!CPGSm;7Pgv02+Np^9>r6QP z;mR!PZ^et?)d4MD!b#2cQdk*w>j)zXT&IjSoH8-3Uu;TuRu(svI>~V|J8PaDqX(vS zTr+p-EaNF-k+xGsdpx^6i@f&IQyvHU_Z_}7_-`-Oo-*=#rTFx6!DC09j!tCTOO>Zs zUQ;6J=@fGEnumWf` zFDzd@m)*z47-Pdl7%42r3LtJ`y1-4;e|Rt#t2HrZ@Lw{cC%i(x_vJ zlcQY3KGdCu@dCyqUR&gs@E?=2Yb?&kM8+bPou$JMY0j>_NNMn#lr4-%C6K)Bbh4-|G6^$7)o8jpN-_Q{F*?{n$Fd~ffw*h_{+KW zDUBiDPoO^*D4*=cs1kmq3TR)&#OUmwn4$`Jgqw}r)yx&*i2V&~|F4trzYbqK#Q)sX zW8SPzD!%Zc+rrJZhEr#+^e!&%YAU+fP!z)~*_V-Lyke&#r@+kW>tpeVSJ^pb@QNmB zK7852UE?0SYxX_eE%`rXK0DN;ginbvf-ztVs-uOZI$?)B(a}Wy1}fGI%-=+hz_dQ_ z)+ZeFUWmXB+vToi{v#fPFQ2^#vahg|V2xWXID!G2t9qSz+$Oeb8`DXBE1H@w#`SE~ zU~WoRY9%=`%mM6LlhOr0|6K<7he+!)wdmoWehzjCxfp+QfUO?U1-=rQ&yqRVU+!Z3 z*nr17qQ(Dk`!8O`A8dUIf98vW`Rz6WH|Dn+Q+nG>%?i}uo4qFB!P193FYG+R_S)MG zJNrcZt;BZzb{l4Ai**QIC(Hldba@})M7%lijra%16h%d(0$`x(6RP3f!6%_L|lpdClfSL5wVjK`v)ZAD-%7K@vVp4k^Q--Uf@n}dlK=E zgz|{)xax?xLf(H6RwVgpQAP`?pat~x3dTh0HZ*GbPo;!2tGNon30=4%MRLE+z3wUG(l_72lSMsfx zD)F>YU^}`laMi6%r>RpG-#)`B`}#|PuRlES=@ENi@kQ4&LR1Hvc}e7n;DV63`yF*COrmtbz2gz>B11?&`0ql0VN|CfU=9}2#dua9hn*o4@z)06@zr5MSsO2YDJmRx zoZWVj?Z3s!D?vMgKBChUZJ>;*1R+pRG|~J!*-3`QN06lBfT1) zX(PVsQGs`=yIVRbbucX^a#$V6&F&(DpY2ZOt9GE=Cla5FEKV~R_l@`f+>ec5_=mUu z0U7^568^-Q7Sx~~ur>Sw9YUztD0yAp3?I7=s8myXLa_aWEpUW+>+FkXW4E2t4kbMO zSCJCGxl^m zTa`N=yLZ0aJ?46&>+r1!=jbZ=nI4pv2^sU#esX%i`HA?g33kSwu_mF2nYuZbdmUCD zsXtq{+okvaa{(J~{3rIOdB~xK_v*wHE#CDF_%kO;U$9z9c8SIXZY0A$9RGPT{>DvdqTjN1i&c^0Tcuu+`f<2c zTbuR_JpbDh9A?`4CAvsVVRZ2z>YA1PqrtLaKOAcTHJIRQl#SJ~2MIiLC9q!)yOYtA zs!E0XHVodk4)+-pM3Q`H)=@yCj-1qUZUa0~s{;+|*?wTm|1deY*y|{lb6yY$G-Q(U zQpAMb=N{}P_73C+2T$X_#F&x!B``H(1)Iv~&#aaLV}%j>8z%p+m+`+IUp$0=Ly*-+ zov$Zin71EX#`xxBAM@M_54+&FKlJHcYj1^CB}yKGPe*D+06n0kW&A4XS$6)E+}V-; zwS!#ec5r0}e>`rrow(R~tQ}VgdjD=$jpJcO1RTl5;M=!A4X-h-dqXW;?0W3g#A4wv zLc)%XK`7rCWxIEGk{SKJMl-pKgjGSNx#6asZ^Ae-e9&uZ&-61dq6fYI=s1_tg*^bx z5w>SZ8;!!+|4&30yagKPEgt-ub$wBr$(ewE`5*S#DYL{bov(n;^W#Rc(+K_z!~X^u z{~PelL-=E{hExaZwnJZ+#@hWv9R4<9T^CK*!-4tNA4Bg2DfZ#TbHP$#ed~SLmyOM} z$-ehmn|B<+y1kk2HTA{cf-h-`JQnqZ8+Zj|$ivY;Y`HC+n;odi@nms^uxY|?eNODa z^m4k}vx;kLvgaRw%~487L;C4E!oLsaE3$kl%m0E$n$6)9_?+iljr``Y%6&)VptZ57 z9=5>aq8H%VuPpqX`D<|_ZL{>zG=Fw5VRlE|@KX8PU>WqhQzPwsX>?Mho4BWELD!I3 z!o}|HVY@vRgL)2Q6*}3k!D@vsnhJ0|g5e)7|L4p2&ribtu=hecc0n%sn{e2By5lp6 zo2Ey8NPV!ga?VefY4Q}o&tt{#zBe#J<96!`&YszR-AC=K;0OMX7(dlrcjC;^aHF}a zDWeKr0mF$xYb34er1`@TSK8L}6?T!Ac4UvzTbK71R2+#8di{)HZiw;zw+Kvr|4ZcT zUx|zk8E-y#Gwgbj=#yb3)8xtUu5g-rK}Q2ks(NJkFZjOrMd$)ETre&JX};4x7dwfN z+bck7y`{+Y2;0N&gjuU4ryuQ~8dZS4;Qbfc>yi5##{Uas{8^`Wi2tX`bp+qc=(X7k zqJ8ndq9)_wo^X3ojWzUJqpmHhx9AlXUtjz`dlb1jf-)@E=41V-*i(i1e~@b%JG~61 zZ0kpi9n!+y4{Uo8pl`Fon$-Rc!q;L4I-be3uLvv;`*Qb{w>E_;7!4=_=CDWGn#THv z>{^NI#EhXFt^6Gr_pvqz{%R5$`14@{SuPjf1!;3!X*4Lvj!f?W#jsBDbd?$6fA3X_zh zCCO=O&yd>P!`^=mN{T-Ww!3L)cRz&>7N!-lg;*cLa*_T2xStz;RO07Dz|UhM`vPD# z7e`j@jJFw|v$jXZ(n$Ki*zlv_kQ1I81x7oXKz$gQW2ez`hzpG367iWAzZO}3Mibr5 zf95Orn&$1i5vxygNymJ?La(Q9z>bCC^JZ?^uf16xu0@}AOyq1B_G)Rbcrzmos)Wt) zyC{>r$4LDR!+(*C{~~K-B69ZDCDO!Wx#@7fltzMPnM9M1IiI_{Kyce#Kq&=f2d41^)X@6))i`dvcSGd}l>8Zvl@dwX(+-ZU{ z+@@(8#T8NHL{x$QX=kYjs|)Q4>=nv(k$9K2?vYJr&5#}gy_l;&-<8R_FGV{>kw_D6 zKfCtI>i;c~@n4dJzurw}Hk@swZ#f4jLW{5qt)T6o=`5w7#5w>L_C zLlWklT|p<&J3NN|9`muppLI%7`S1O|6Xld&`O||Z%rT7ZDE4mevtcH)H}^f~&kQRZ zDcDJtv%e%h8(0;-jn|aoYOvQ9?B(pwi2p0xh?K6Tqgc_u4`)onN0Txmj|YAblKUGo zknb^(m3g{v_xv})`y^i#tNVTY8=-%QQ>1q}0;%vxWQ!=b7q?vek$50{4*i)->|w>) zl{>`kcpDww230X)f5YVeQW^iH_~Ie?pE+2osi@V}|D~2zh+(Og9tCD{-S_rz_8Y{z z13BS^`dS_5O7SQh22Z9>Lb66Nh@=u_%@$=^YKh(oS~?S64{lTo>(P&>_ZGCD$T`u( zW@nE?1GEaXw0e-1UX04LZB7gzB0FTc|fypu|l z%UK{%-Wl^{>zf^ zueG$@Kc|vuW$-_|!<*5bk<->hMWZ9js0kXCSvS0jo}dXnVXNT_R_3k?eeZuNr%tWZ z1Yb3ODbo1Qfe`1;5R@#h#ACnxl=0}l4KOTBmuTZ2Ao232zYKpI&QQ$qL96V}!5ks> zh900vQ!YK>?LH>+5B8Y-Q(f&hb!j`&n7rQesf^}TDC1G7^jQPv&_{XjQ?ln$b8fjg&=9&Ab`%cEnzCjC_S&ww`&DLr(J!!_ zzbmV`7nnY;tb5IdgBw^3o1@u@oxrJ}Q{WD-3hUe-bu`lTQd`3v#%L~H(HDSZI3{v;cmqXiTGz1sh~N+-jMAed0VeN zT!Z8K-oqky*dp=!`}zemIbbzgEhS|g*w6fVUq`4TtenPek02e+J+^hqhU7rb=FZL4oe^Ve-Zu;m=)moj~N z_z6KzZBmBd!AQL}G3Y<$drxF3EBgN>vL}-L3+a7L%~{X@DexUZXt3q?fM1){OoznY ziTSZn&C@yAPFFYMOGYsK!}Y%_W&Bqr;eW9D-1dXpO)ac$m~PcZC-f5;SLz=Hr%>^N zh#Q;0qX|%fD*>}mC{TA2MvvLkh`>9d4YZK+c^`hcwW^&eA2}OP1dTD z54W2en@(`vv}c_3Ug_De$HnUAmA=nJxt8{!5B7-&TTo)VsCB?T zq!lKj3Rs9xtWV;tR`eA@%@0t%`5f8I=Sh40I!kSn75#Zr*>VfT77So<7EpUV!FB|1 z<>mZp#5{tTA{Bf?NuxW^P$K>?Vz>zB!c2hcg7^}4pFJ-H`_%lvd9!M&9cZKo2i~ z&weQm;mD9bF9rKly@XP26RYJE_yuRj;6=uFOe@cLcZXyP%I>{vPkhK(ON9O22Vh4+ z`FwQ3{O4#QaB*AyMsHdYuFad6|1hWj>l)5ky8Y{q&jHS z4K)^Q&f^$}|EkX|p#twcv46Wb(efGD%Ni|X$1+*_;6+IT7klQST=w7249Nu99yhQ> z;a(Pb6#s9GP(D|j*>Mnd09{fjzWy_FBXmIuf!!Mue@SyJ2`Ox|pvO3YCeINb4bKi4 z!$ymKoHFVNUvGK2X`Oj(IN!}h9`}BCMveKtwU|p*MAmLGV2nBg6nW=BUi6B>{|7Su zKS;vgL9E~QHg@x|VH|`ts{}`WlH)l~sxZ0KJu}d0(?iQaP-U26X=@=89AlVVk z_C3Y+Xv7RtRcWI1U(g{q-BSd8huxhnIN)oND|88*{bNX-sOWO5aqSnBh1Vml192aS zT%i)zI$R5)rc?|4QzAb1B4F;2=^g`2Wcd^{$tG_z|YK%BiwGR2dJY7 z+~l;5<8&T90T$+9?K;1A4o(0fyE z=W})~z-m%&e+k~oEUevh+LE6?($0sq|J5@7)%eh%_J7TmwU+m= z0-(YBZG_d-LG%G)k=0vfNPP2|=t8}z1eWpyQk3W&B;pG%z9-7*anXi!1*9qCGA2|1 zo%PXxf-P#N{1S?-$6eM4VLYEBG-_B%TJ+g4hdpLabz={X2sciPYc_slabI_%9LN+l zhx1#^;c$>C9AwI1+mi&4sd*qCXY;q$9^Jv&zeEf+5yA=- z&i;4As|4)j?KL3p*}=T@zuUVS=%}hYf8WgH3xb3XF&_|KG6XU}$R|osama)LGN>T| zi$r~y%uJYp$;@OvLPD)3e6$Io3#cm+?cztfdcZB4s%_7%E2nm?t?g-2M#K zdTis?8j=9BzyG`QCNB)KJ(aHKO!wxz-1qL>d*8eN-~aya_x~@V^%2kF;w=H4NiKP1 zZ>BJq-jRC_kiR=#SxuNlYDm&JN^V28$v%9fEliRniGUFGIRi>SufgO0; zIwuWMcoXZ+>c-x#XEPt|{Un@&U!JNQJ7aMEuB%p8n_h==;g^PsF-OmL{_x)2qp9hG zliO}@E7Hs{?Q5H-D=@v?mXo~5RNkN~y_re5uVa4h_x;-P43=pmtDSUeh)$VlaI);u zaO#NAQ{M1NSlM;B$~=fOTuBTTMPE#7+ub#Y@1LDBGwb=@KXg5)dmuf9Z8yCR?J&>x zJ_r4ui=9WzhpHI$6Aak5G|}>)ie8C!8;8G*<8M>pzrQl+q~EmCTw}g8eCqsAcsb+u zKR<79E@L%Sn-Tuw^S=r2uS`a~gT3@?WccyOKv)Ys?}tqXUJgKE1KJP40gU>uFN=H4jwgi7O=p+Qy zyHcJw{Oufny9)nRu(kP8?+4*;Wqk*|DWxv6Cl;bDUxRrYWj6)YV!qpZE@Lk02~g|6 zhTb02clY!*b_-9igJoFKrT)RoI3a57?(RL(MY;UwQbVVR6p;HvkB#y1f)BmCS?uxD zw86Zr1{$0uo$M4I6<)kFtT|8m zUdJteD{>&U?O4~l;5oG%n0h5SPzX5y`x9IaD6~pMKMsEf$KRpCzZVu0j-aP?K$ZjR z%|`RZ;rq|o^#wjOfe;_$y?zy5)!9H3t#!#yfFz(WpBkONDuDhJk^)0~h5 zFH)V)#;H=s`tq9G6S5>IR3t$uBteNH2}lM!3mLE;@4h$kbhuEK0ef(oS9{?4Tv!(9 zZyr2>IcSADFN{`nKi}H|Ui@SGKw5VHce?gL0$9N%nB6{8pWV}&h`1|dio?H&Clu-|hN#<#nfNO)jthqntl>)$2B= zZ#8Lqo-#jHWrN+NbXfh^S2^XR4fL$f+JS!C)PWs22YYEe@YY2gtbz1}>9-xM2WtTz zh11zv;bQh?xCdc}(R0uoW(Suu?UOVUF+Fm;YdJ=ik>R5kOFD;uCCQDp(Pbxb zon2Z;`^xTvy^mI=V1>8`Ej)DSp2?g|{YQGPmNEWg7~S#i`tB3irpit7er|QjWH}`h zAF4FUzm2E+B)n68gR#4#cNZj0j?5vcXHI4xLjLqrgSChr^g3xDIUn_@TO-|QKEBO{ zZ{#h}tc$!Q+KIL(GOW2c-V*(Mghno;wI*?09R5y@zf*-jwEhoI!yB7gc#M__o6-iJm*`Pr8o;-ap_2xkZ(YmpI#6< zg#`*n9U582&J(rqwtvLiFhXSNcfBy8uCLFGOytLQ`B#zyL5wPt(Y`u;*M#()BOeLp z+8@lzG8#+ktr}KUtCDUpSO+)w2C>W2>8SWE;k3~^+*`(kS0 z+P<4CXT3M!^e8Sj`OqkmHx<9`zNnG?V;9SC#vnC+vvDqFhf8o8Q!9r*a&d`8lYe&0 z17*6rCuCl!u~)~qlVu!&UkIOc3^l+X*D%eW$IxseDhSq_c=AP7^l+ixyEsWq? z!?yTu;OAHqmjmMDKWISff7?{}XS|XUrIfaxBb|ZO_)NK0lkOg(QN}AH!($f&%(c>Q z&ql`NIlf&fllrtwn}>PB^2wN$&xXaek^anVoaSa(&h+Xj>-J{uHRV@M?Z4ZZR;?@N zPpNoHiamXdeSbnZr5x*jHypWi=~4&W`BzSug_kvJcAJHNZCK4-$G|{TKfj-L^=B)# z9jN2~SeW?yRaJh8i~belznkOlR^dkVd4gtr+s5x zC5!rEm7~>V!$7mNth{0=42~(`rYaLl)4>jyz%+l*2*yi4(HNN^XtdzVG|j_IC$NsP z)X7Yv(I5a31>ES8puF6iW14@bor!KhdKy`9acM`jRh*kT7$#`a#T0x;)$=JIEm`w z@b_~3y(qAPKPNNc0vB?5g1(?%3K}Mo%^((u>R%P&gQ8LD6X63Ct<+EuaP4q-n+gL# zf5;XTuN>E4z&Dh%mD;Fo7cO*p0zt!y72pWMBG1ugbNGU&)&&3f1EP9D&Eg{Qt_>?I z*R7JwYisK^NNa1?BTbXl9iVwlS3nAY^T0y%V!w5}0(~Lp4Vl0E2Bzdn?@Lq{hrf^G z??ZtV{7Duxc|#sMe4?1uZkK|PoKn!stCZUn2-0sJ)X1RJ=(l=oP6TK=XQo zB3j9{cWk(^Lb`qBrfY^lqcz|t#yi|0yF+gMi-YYxM_>s}O)>fi2^HD9-9zHZF11?S zE_V));7uGwL1d%7ImD z0y7{kI@)fur*upGaUL$_-72bn$n!Z-5j|)+JOLTV!f|vLi+%?>bjZPg_JG<7%8j_p z<7iXfVRgG3tq?x+4w)~a+~|>fG`?^I0+c{*T702Kx66hs3LzVut}V{RN=Ak#@5Fx% z6>Ks1j`|&WoT=m*}1fHP3J@$V2+bB5t$&AI~ zfF)#pKwPk+j4#O)`gS~1*M@U!G!S7f`Ylmi9R4AWKXtMd{CT&I69n`iQpyiREDi?x zhG=BvjseoGiH_<-`A4z9jvGG)TbDjSIUiypLgnFzwkm(*D`4Jqj-3?x(`6#lBg~Y|5lEFD+(OLKibVB z8u-x{Ko$Bt*r@y2sul%Grd7;U$c1tAb%vr90T%?ihnA(U9*;LZ%;VTD1wxHd5JP@y zhPXIazFw~DyqS^5DdS=QpAZXgm!}Enjfv~x@ZZky-;N?H_=6@_l8Z4pfQdR1q%Aa| zLOxOJ8rNML$(BGd_o(*Dchs9`a&5ufBWtzEV^R4V3TxJy?v!fQt&{G8fBV`yBy;Vm z^^#@HW^tj2{|4$_#uA{Dd0B#$nVZj$>Zen%7M zQ-VB(e=%NbVE7ohGecM}7MnsI8_g{(7wI{XU0nTd=lD}!N~!;RtYY}1Vv3lsfi0=I*ECWZ_*ZPvME`;_%2sfoW*cxnvy^2l$5k8kKS3Bhf7Vs<8Nc0!@JE)y2EZ41y;=@> zB(RIa|0^8-uc+{E1r3M}pBIO%!1K?^gW@Guunh9)z3TUWRvr|-NyR4*AU(ZH9iQ&U zp&Yu6-_J90|8DLZ|Bm}zZ*YI>dG52sbaD8@9v`*;_oKK<{il+5>|U~x{J!Zn+28#i zPsqOGXFprHkgxkJlY-(xgWuaMVfxgH(V%x*$X7_ue0|M^VRqqyx&@2H1?B|?Ls6Ll z-LVR4uTOsZSEc@^Gs4ciF27jr?5V(!TefP{}iQz1!KhqciqseWN`Wx=;o zxf9jJ;SW2Eg#TAjU#yR+-TBSen`c5(Q_)(+wS0LrZ3Pqa8W6skM~TBzqml;s~H&lGgBcs;^I zE@o21q7`E0op)}iTeVK6Yu(GAJX46gf89B2$k5||ZO6aeeRAEZDwc-4kzBJ0zop`S zIv!NZ$f7}>{%s63HDS64>&}=dDrqd0UC2QFOaT5D$_lru5wk%uYsh;|h$CntK(XH% z-Jare(FPP^sf2ZL_`{|O;r}2?tl(c!Qf#-`CBCHwa?`n;OL%Wn6Ob;v3?TyzxUkJj zs;a47c{^VbWNf3dZ9~Lbyjap!&^C$rokVgDBE7(2_XVXk0F8IYp1nQ?tv}N*N7OPx;)s6hMfVTlAiMNhJx6ez!)v+OAepJr3qsV zH7w&z*t@5FBk{xtRT+0>z5`Me86FiiwWfgrX|6w0mhx;m+v+KVV9DXs)sOW4B+`nm(s zR=nr4`V>r59Bn)+R366tu8fIJ7jx0@m9i7A;CO^e+0i!O%N_kL;@cA4#o@n;d%#GLGPS4A%$fe|EwD1U@>b8Ae?9;Bw;9GUkM?;eGyBHBPpW>l)VOAHEAV zzy_Ehqi<3J6LXxhzF}Ap-3NWczSVvC)03yS)%Iq$2;vY<3qnUV?80+fAv#zJIb_yC zZ?V|-MV9}s{q8vYcXRxAtMDf~siYnEN%Qv|R=b5p1X% zCvo0~CYPy$lVD|i53H=Sp)D6IW_`=V*h0*b2hNhTz@8JXV?G9ZvO95V`+7DTHl!oN zkKlx1O25!k->cvTJ)!cc|AkNuMCXE^fsMHHIA|~xT4!=_YG_LC^np>*9;ioSk;jJ8 zF62XHu-XbdrTSXr{A*;3luiXfZ@hd`C-29x*YBi(h+f;GjqyV#>YDL&ocIAfUzX|lKI}I>hP-|n z#hK20rs9mxS)i1zS#lUYG{Gp@-WJ;Cxf!x;Xs5#_|7}3jcw< zhs^2F-6*`khMpKp(Qh@^wQMt2o3s5Uoa`6m@b#lazvCl62qzuqc6p$i2j{{V&V-U3 z)uC7R9N$`fNPlV*EU;=8v)RTumDPP)EQhyl!P!qXkIhdTbXcCuyrq1PEf_Aa>(T4j4OZ-yQ+&7zO{H$&6-^76TDtoXyeFXR3egtJAz>R-X2?SDyX ze;xD$Ln~Q-ZT0?gZd=k~dCF~1vQJS;QZ~j=k{K=9^12qyW4siKn+wq|;<(+Lh{X2_v74ICY zH20In2GCiUZPMS#^fxy|hR5s&M@Hr%kLlWM$C!IS`^!^o=-tsX;ST)( z(vGM2(Wow#;_D+bEE)OPj<*mihmeeY+!^iPmv8m_jB}YIGqCzqXkdvOSbfEO|G8q?8#1&t!%%!h@FN^C;NX23h-xJO`pE5U8K_jJ$4b2?YyPkrz&-qs3j5((FWIYkPoHtJ|NUNYK$vil58%OfYuKLr20 z)xk1;1Aha2()J+JAC@`3AF%<6dZsz0e`psh`@nK@o`{~>OVAF@^t)Pj?R;aW7TWsJ zZ!l>3C{>n`L3iLZQ(aQ}sh!Rl&ibUu?@YE#HqK~>gvbAO$+qWrwUO3`fwUf`tv0-Sp$24L3|783^6%#VLd)Fd~>9u;BN~5y&V6&D*T&;BvTgZx+Z@b%Yx=T zP5*PK@yW2hkPMBg^~tmPnI83BkNU2Eb}u9CjnM9Toh8>u*4E2Sjm{Yj3(<=s48I9` z>w^BnupmFBN0LlQh<8LkfOx-)o+Xb&BHzaKpM=An2Ut-u&gXk0RJSMh6Yq^9m<9w% zURR7z-aosPlnZ@6upG?_eix>Y!;#-2Pr~&v;7T@Yl9&k^o>~9hgUP2gix$Ba5$k(< zkj>jQNEXfC9Q2W0gOoL=Jg{n|%Ok5+%@&X%VO^a3e~9D%5K63!|3BN74{6Y0LrEi- z+wv(KypOE(hTrVbXixyX(wMhxY6$8(i4PL{#a&Bw4G{=w;l1s?^A!+#&g zf1e6}Za;k}YR9!mw(;{{ef6--ajyUU@I26~KIPk|@?$kd>0x)CUj+MoGg^}LbuBqp z!HFzFv7yE|oc4sTyGkm~C#&O9ZH?IiB0E)N0|-T7cFb#2Uqr2=K&WOJ>gO++CO-Gy zz@I67qPjTzALjT!i~=k5U(vDg{i=!dd2-&wXNP{`a~gC>spt>9k^8r)uo%fwh+1=d|~b*@G{ zxIe*vdJVs4fae6-*&pIsgCTncOQ$Gtjw8f_p9W8)-H7W}meGv*_de!#o7YD3i%a?c L0oR%Wg#Z5ld)F}( literal 0 HcmV?d00001 diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java index 91243a7..65a8095 100644 --- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java +++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java @@ -12,6 +12,9 @@ import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbInterface; import android.util.Log; +import com.hoho.android.usbserial.util.HexDump; +import com.hoho.android.usbserial.util.UsbUtils; + import java.io.IOException; import java.util.ArrayList; import java.util.EnumSet; @@ -107,6 +110,10 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { @Override protected void openInt() throws IOException { + Log.d(TAG, "interfaces:"); + for (int i = 0; i < mDevice.getInterfaceCount(); i++) { + Log.d(TAG, mDevice.getInterface(i).toString()); + } if (mPortNumber == -1) { Log.d(TAG,"device might be castrated ACM device, trying single interface logic"); openSingleInterface(); @@ -142,51 +149,57 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { } private void openInterface() throws IOException { - Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount()); - int rndisControlInterfaceCount = 0; - int controlInterfaceCount = 0; - int dataInterfaceCount = 0; mControlInterface = null; mDataInterface = null; - for (int i = 0; i < mDevice.getInterfaceCount(); i++) { - UsbInterface usbInterface = mDevice.getInterface(i); - if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM && - usbInterface.getInterfaceSubclass() == USB_SUBCLASS_ACM) { - if(controlInterfaceCount == mPortNumber) { - mControlIndex = usbInterface.getId(); - mControlInterface = usbInterface; + int j = getInterfaceIdFromDescriptors(); + Log.d(TAG, "interface count=" + mDevice.getInterfaceCount() + ", IAD=" + j); + if (j >= 0) { + for (int i = 0; i < mDevice.getInterfaceCount(); i++) { + UsbInterface usbInterface = mDevice.getInterface(i); + if (usbInterface.getId() == j || usbInterface.getId() == j+1) { + if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM && + usbInterface.getInterfaceSubclass() == USB_SUBCLASS_ACM) { + mControlIndex = usbInterface.getId(); + mControlInterface = usbInterface; + } + if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) { + mDataInterface = usbInterface; + } } - controlInterfaceCount++; } - if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) { - if(dataInterfaceCount == mPortNumber + rndisControlInterfaceCount) { - mDataInterface = usbInterface; + } + if (mControlInterface == null || mDataInterface == null) { + Log.d(TAG, "no IAD fallback"); + int controlInterfaceCount = 0; + int dataInterfaceCount = 0; + for (int i = 0; i < mDevice.getInterfaceCount(); i++) { + UsbInterface usbInterface = mDevice.getInterface(i); + if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM && + usbInterface.getInterfaceSubclass() == USB_SUBCLASS_ACM) { + if (controlInterfaceCount == mPortNumber) { + mControlIndex = usbInterface.getId(); + mControlInterface = usbInterface; + } + controlInterfaceCount++; + } + if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) { + if (dataInterfaceCount == mPortNumber) { + mDataInterface = usbInterface; + } + dataInterfaceCount++; } - dataInterfaceCount++; - } - if (mDataInterface == null && - usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_WIRELESS_CONTROLLER && - usbInterface.getInterfaceSubclass() == 1 && - usbInterface.getInterfaceProtocol() == 3) { - /* - * RNDIS is a MSFT variant of CDC-ACM states the Linux kernel in rndis_host.c - * The devices provide IAD descriptors to indicate consecutive interfaces belonging - * together, but this is not exposed to Java. So simply skip related data interfaces. - */ - rndisControlInterfaceCount++; } } if(mControlInterface == null) { throw new IOException("No control interface"); } - Log.d(TAG, "Control iface=" + mControlInterface); + Log.d(TAG, "Control interface id " + mControlInterface.getId()); if (!mConnection.claimInterface(mControlInterface, true)) { throw new IOException("Could not claim control interface"); } - mControlEndpoint = mControlInterface.getEndpoint(0); if (mControlEndpoint.getDirection() != UsbConstants.USB_DIR_IN || mControlEndpoint.getType() != UsbConstants.USB_ENDPOINT_XFER_INT) { throw new IOException("Invalid control endpoint"); @@ -195,12 +208,10 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { if(mDataInterface == null) { throw new IOException("No data interface"); } - Log.d(TAG, "data iface=" + mDataInterface); - + Log.d(TAG, "data interface id " + mDataInterface.getId()); if (!mConnection.claimInterface(mDataInterface, true)) { throw new IOException("Could not claim data interface"); } - for (int i = 0; i < mDataInterface.getEndpointCount(); i++) { UsbEndpoint ep = mDataInterface.getEndpoint(i); if (ep.getDirection() == UsbConstants.USB_DIR_IN && ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) @@ -210,6 +221,36 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { } } + private int getInterfaceIdFromDescriptors() { + ArrayList descriptors = UsbUtils.getDescriptors(mConnection); + Log.d(TAG, "USB descriptor:"); + for(byte[] descriptor : descriptors) + Log.d(TAG, HexDump.toHexString(descriptor)); + + if (descriptors.size() > 0 && + descriptors.get(0).length == 18 && + descriptors.get(0)[1] == 1 && // bDescriptorType + descriptors.get(0)[4] == (byte)(UsbConstants.USB_CLASS_MISC) && //bDeviceClass + descriptors.get(0)[5] == 2 && // bDeviceSubClass + descriptors.get(0)[6] == 1) { // bDeviceProtocol + // is IAD device, see https://www.usb.org/sites/default/files/iadclasscode_r10.pdf + int port = -1; + for (int d = 1; d < descriptors.size(); d++) { + if (descriptors.get(d).length == 8 && + descriptors.get(d)[1] == 0x0b && // bDescriptorType == IAD + descriptors.get(d)[4] == UsbConstants.USB_CLASS_COMM && // bFunctionClass == CDC + descriptors.get(d)[5] == USB_SUBCLASS_ACM) { // bFunctionSubClass == ACM + port++; + if (port == mPortNumber && + descriptors.get(d)[3] == 2) { // bInterfaceCount + return descriptors.get(d)[2]; // bFirstInterface + } + } + } + } + return -1; + } + private int sendAcmControlMessage(int request, int value, byte[] buf) throws IOException { int len = mConnection.controlTransfer( USB_RT_ACM, request, value, mControlIndex, buf, buf != null ? buf.length : 0, 5000); diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/UsbUtils.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/UsbUtils.java new file mode 100644 index 0000000..5802b9e --- /dev/null +++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/UsbUtils.java @@ -0,0 +1,30 @@ +package com.hoho.android.usbserial.util; + +import android.hardware.usb.UsbDeviceConnection; + +import java.util.ArrayList; + +public class UsbUtils { + + public static ArrayList getDescriptors(UsbDeviceConnection connection) { + ArrayList descriptors = new ArrayList<>(); + byte[] rawDescriptors = connection.getRawDescriptors(); + if (rawDescriptors != null) { + int pos = 0; + while (pos < rawDescriptors.length) { + int len = rawDescriptors[pos] & 0xFF; + if (len == 0) + break; + if (pos + len > rawDescriptors.length) + len = rawDescriptors.length - pos; + byte[] descriptor = new byte[len]; + System.arraycopy(rawDescriptors, pos, descriptor, 0, len); + descriptors.add(descriptor); + pos += len; + } + } + return descriptors; + } + + +} diff --git a/usbSerialForAndroid/src/test/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriverTest.java b/usbSerialForAndroid/src/test/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriverTest.java index 1f7e18b..f5b7fbd 100644 --- a/usbSerialForAndroid/src/test/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriverTest.java +++ b/usbSerialForAndroid/src/test/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriverTest.java @@ -4,7 +4,10 @@ import static com.hoho.android.usbserial.driver.CdcAcmSerialDriver.USB_SUBCLASS_ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.hardware.usb.UsbConstants; @@ -13,6 +16,8 @@ import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbInterface; +import com.hoho.android.usbserial.util.HexDump; + import org.junit.Test; import java.io.IOException; @@ -29,15 +34,37 @@ public class CdcAcmSerialDriverTest { UsbEndpoint readEndpoint = mock(UsbEndpoint.class); UsbEndpoint writeEndpoint = mock(UsbEndpoint.class); + /* + * digispark - no IAD + * UsbInterface[mId=0,mAlternateSetting=0,mName=null,mClass=2,mSubclass=2,mProtocol=1,mEndpoints=[ + * UsbEndpoint[mAddress=131,mAttributes=3,mMaxPacketSize=8,mInterval=255]] + * UsbInterface[mId=1,mAlternateSetting=0,mName=null,mClass=10,mSubclass=0,mProtocol=0,mEndpoints=[ + * UsbEndpoint[mAddress=1,mAttributes=2,mMaxPacketSize=8,mInterval=0] + * UsbEndpoint[mAddress=129,mAttributes=2,mMaxPacketSize=8,mInterval=0]] + */ + when(usbDeviceConnection.getRawDescriptors()).thenReturn(HexDump.hexStringToByteArray( + "12 01 10 01 02 00 00 08 D0 16 7E 08 00 01 01 02 00 01\n" + + "09 02 43 00 02 01 00 80 32\n" + + "09 04 00 00 01 02 02 01 00\n" + + "05 24 00 10 01\n" + + "04 24 02 02\n" + + "05 24 06 00 01\n" + + "05 24 01 03 01\n" + + "07 05 83 03 08 00 FF\n" + + "09 04 01 00 02 0A 00 00 00\n" + + "07 05 01 02 08 00 00\n" + + "07 05 81 02 08 00 00")); when(usbDeviceConnection.claimInterface(controlInterface,true)).thenReturn(true); when(usbDeviceConnection.claimInterface(dataInterface,true)).thenReturn(true); when(usbDevice.getInterfaceCount()).thenReturn(2); when(usbDevice.getInterface(0)).thenReturn(controlInterface); when(usbDevice.getInterface(1)).thenReturn(dataInterface); + when(controlInterface.getId()).thenReturn(0); when(controlInterface.getInterfaceClass()).thenReturn(UsbConstants.USB_CLASS_COMM); when(controlInterface.getInterfaceSubclass()).thenReturn(USB_SUBCLASS_ACM); when(controlInterface.getEndpointCount()).thenReturn(1); when(controlInterface.getEndpoint(0)).thenReturn(controlEndpoint); + when(dataInterface.getId()).thenReturn(1); when(dataInterface.getInterfaceClass()).thenReturn(UsbConstants.USB_CLASS_CDC_DATA); when(dataInterface.getEndpointCount()).thenReturn(2); when(dataInterface.getEndpoint(0)).thenReturn(writeEndpoint); @@ -98,7 +125,7 @@ public class CdcAcmSerialDriverTest { @Test public void multiPortDevice() throws Exception { - int n = 4; + int n = 2; UsbDeviceConnection usbDeviceConnection = mock(UsbDeviceConnection.class); UsbDevice usbDevice = mock(UsbDevice.class); UsbInterface[] controlInterfaces = new UsbInterface[n]; @@ -107,6 +134,42 @@ public class CdcAcmSerialDriverTest { UsbEndpoint[] readEndpoints = new UsbEndpoint[n]; UsbEndpoint[] writeEndpoints = new UsbEndpoint[n]; + /* + * pi zero - dual port + * UsbInterface[mId=0,mAlternateSetting=0,mName=TinyUSB CDC,mClass=2,mSubclass=2,mProtocol=0,mEndpoints=[ + * UsbEndpoint[mAddress=129,mAttributes=3,mMaxPacketSize=8,mInterval=16]] + * UsbInterface[mId=1,mAlternateSetting=0,mName=null,mClass=10,mSubclass=0,mProtocol=0,mEndpoints=[ + * UsbEndpoint[mAddress=2,mAttributes=2,mMaxPacketSize=64,mInterval=0] + * UsbEndpoint[mAddress=130,mAttributes=2,mMaxPacketSize=64,mInterval=0]] + * UsbInterface[mId=2,mAlternateSetting=0,mName=TinyUSB CDC,mClass=2,mSubclass=2,mProtocol=0,mEndpoints=[ + * UsbEndpoint[mAddress=131,mAttributes=3,mMaxPacketSize=8,mInterval=16]] + * UsbInterface[mId=3,mAlternateSetting=0,mName=null,mClass=10,mSubclass=0,mProtocol=0,mEndpoints=[ + * UsbEndpoint[mAddress=4,mAttributes=2,mMaxPacketSize=64,mInterval=0] + * UsbEndpoint[mAddress=132,mAttributes=2,mMaxPacketSize=64,mInterval=0]] + */ + when(usbDeviceConnection.getRawDescriptors()).thenReturn(HexDump.hexStringToByteArray( + "12 01 00 02 EF 02 01 40 FE CA 02 40 00 01 01 02 03 01\n" + + "09 02 8D 00 04 01 00 80 32\n" + + "08 0B 00 02 02 02 00 00\n" + + "09 04 00 00 01 02 02 00 04\n" + + "05 24 00 20 01\n" + + "05 24 01 00 01\n" + + "04 24 02 02\n" + + "05 24 06 00 01\n" + + "07 05 81 03 08 00 10\n" + + "09 04 01 00 02 0A 00 00 00\n" + + "07 05 02 02 40 00 00\n" + + "07 05 82 02 40 00 00\n" + + "08 0B 02 02 02 02 00 00\n" + + "09 04 02 00 01 02 02 00 04\n" + + "05 24 00 20 01\n" + + "05 24 01 00 03\n" + + "04 24 02 02\n" + + "05 24 06 02 03\n" + + "07 05 83 03 08 00 10\n" + + "09 04 03 00 02 0A 00 00 00\n" + + "07 05 04 02 40 00 00\n" + + "07 05 84 02 40 00 00\n")); when(usbDevice.getInterfaceCount()).thenReturn(2*n); for(int i=0; i probeDriver = probeTable.findDriver(usbDevice); + assertEquals(driver.getClass(), probeDriver); + } + + @Test + public void compositeRndisDevice() throws Exception { + UsbDeviceConnection usbDeviceConnection = mock(UsbDeviceConnection.class); + UsbDevice usbDevice = mock(UsbDevice.class); + UsbInterface rndisControlInterface = mock(UsbInterface.class); + UsbInterface rndisDataInterface = mock(UsbInterface.class); + UsbInterface controlInterface = mock(UsbInterface.class); + UsbInterface dataInterface = mock(UsbInterface.class); + UsbEndpoint controlEndpoint = mock(UsbEndpoint.class); + UsbEndpoint readEndpoint = mock(UsbEndpoint.class); + UsbEndpoint writeEndpoint = mock(UsbEndpoint.class); + + // has multiple USB_CLASS_CDC_DATA interfaces => get correct with IAD + when(usbDeviceConnection.getRawDescriptors()).thenReturn(HexDump.hexStringToByteArray( + "12 01 00 02 EF 02 01 40 FE CA 02 40 00 01 01 02 03 01\n" + + "09 02 8D 00 04 01 00 80 32\n" + + "08 0B 00 02 E0 01 03 00\n" + + "09 04 00 00 01 E0 01 03 04\n" + + "05 24 00 10 01\n" + + "05 24 01 00 01\n" + + "04 24 02 00\n" + + "05 24 06 00 01\n" + + "07 05 81 03 08 00 01\n" + + "09 04 01 00 02 0A 00 00 00\n" + + "07 05 82 02 40 00 00\n" + + "07 05 02 02 40 00 00\n" + + "08 0B 02 02 02 02 00 00\n" + + "09 04 02 00 01 02 02 00 04\n" + + "05 24 00 20 01\n" + + "05 24 01 00 03\n" + + "04 24 02 02\n" + + "05 24 06 02 03\n" + + "07 05 83 03 08 00 10\n" + + "09 04 03 00 02 0A 00 00 00\n" + + "07 05 04 02 40 00 00\n" + + "07 05 84 02 40 00 00")); + when(usbDeviceConnection.claimInterface(controlInterface,true)).thenReturn(true); + when(usbDeviceConnection.claimInterface(dataInterface,true)).thenReturn(true); + when(usbDevice.getInterfaceCount()).thenReturn(4); + when(usbDevice.getInterface(0)).thenReturn(rndisControlInterface); + when(usbDevice.getInterface(1)).thenReturn(rndisDataInterface); + when(usbDevice.getInterface(2)).thenReturn(controlInterface); + when(usbDevice.getInterface(3)).thenReturn(dataInterface); + when(rndisControlInterface.getId()).thenReturn(0); + when(rndisControlInterface.getInterfaceClass()).thenReturn(UsbConstants.USB_CLASS_WIRELESS_CONTROLLER); + when(rndisControlInterface.getInterfaceSubclass()).thenReturn(1); + when(rndisControlInterface.getInterfaceProtocol()).thenReturn(3); + when(rndisDataInterface.getId()).thenReturn(1); + when(rndisDataInterface.getInterfaceClass()).thenReturn(UsbConstants.USB_CLASS_CDC_DATA); + when(controlInterface.getId()).thenReturn(2); + when(controlInterface.getInterfaceClass()).thenReturn(UsbConstants.USB_CLASS_COMM); + when(controlInterface.getInterfaceSubclass()).thenReturn(USB_SUBCLASS_ACM); + when(dataInterface.getId()).thenReturn(3); + when(dataInterface.getInterfaceClass()).thenReturn(UsbConstants.USB_CLASS_CDC_DATA); + + when(controlInterface.getEndpointCount()).thenReturn(1); + when(controlInterface.getEndpoint(0)).thenReturn(controlEndpoint); + when(dataInterface.getEndpointCount()).thenReturn(2); + when(dataInterface.getEndpoint(0)).thenReturn(writeEndpoint); + when(dataInterface.getEndpoint(1)).thenReturn(readEndpoint); + when(controlEndpoint.getDirection()).thenReturn(UsbConstants.USB_DIR_IN); + when(controlEndpoint.getType()).thenReturn(UsbConstants.USB_ENDPOINT_XFER_INT); + when(readEndpoint.getDirection()).thenReturn(UsbConstants.USB_DIR_IN); + when(readEndpoint.getType()).thenReturn(UsbConstants.USB_ENDPOINT_XFER_BULK); + when(writeEndpoint.getDirection()).thenReturn(UsbConstants.USB_DIR_OUT); + when(writeEndpoint.getType()).thenReturn(UsbConstants.USB_ENDPOINT_XFER_BULK); + + CdcAcmSerialDriver driver = new CdcAcmSerialDriver(usbDevice); + CdcAcmSerialDriver.CdcAcmSerialPort port = (CdcAcmSerialDriver.CdcAcmSerialPort) driver.getPorts().get(0); + port.mConnection = usbDeviceConnection; + port.openInt(); + assertEquals(readEndpoint, port.mReadEndpoint); + assertEquals(writeEndpoint, port.mWriteEndpoint); } - @Test - public void compositeRndisDevice() throws Exception { + public void compositeAlternateSettingDevice() throws Exception { UsbDeviceConnection usbDeviceConnection = mock(UsbDeviceConnection.class); UsbDevice usbDevice = mock(UsbDevice.class); - UsbInterface rndisControlInterface = mock(UsbInterface.class); - UsbInterface rndisDataInterface = mock(UsbInterface.class); + UsbInterface ethernetControlInterface = mock(UsbInterface.class); + UsbInterface ethernetDummyInterface = mock(UsbInterface.class); + UsbInterface ethernetDataInterface = mock(UsbInterface.class); UsbInterface controlInterface = mock(UsbInterface.class); UsbInterface dataInterface = mock(UsbInterface.class); UsbEndpoint controlEndpoint = mock(UsbEndpoint.class); UsbEndpoint readEndpoint = mock(UsbEndpoint.class); UsbEndpoint writeEndpoint = mock(UsbEndpoint.class); + // has multiple USB_CLASS_CDC_DATA interfaces => get correct with IAD + when(usbDeviceConnection.getRawDescriptors()).thenReturn(HexDump.hexStringToByteArray( + "12 01 00 02 EF 02 01 40 FE CA 02 40 00 01 01 02 03 01\n" + + "09 02 9A 00 04 01 00 80 32\n" + + "08 0B 00 02 02 06 00 00\n" + + "09 04 00 00 01 02 06 00 04\n" + + "05 24 00 20 01\n" + + "05 24 06 00 01\n" + + "0D 24 0F 04 00 00 00 00 DC 05 00 00 00\n" + + "07 05 81 03 08 00 01\n" + + "09 04 01 00 00 0A 00 00 00\n" + + "09 04 01 01 02 0A 00 00 00\n" + + "07 05 82 02 40 00 00\n" + + "07 05 02 02 40 00 00\n" + + "08 0B 02 02 02 02 00 00\n" + + "09 04 02 00 01 02 02 00 04\n" + + "05 24 00 20 01\n" + + "05 24 01 00 03\n" + + "04 24 02 02\n" + + "05 24 06 02 03\n" + + "07 05 83 03 08 00 10\n" + + "09 04 03 00 02 0A 00 00 00\n" + + "07 05 04 02 40 00 00\n" + + "07 05 84 02 40 00 00")); when(usbDeviceConnection.claimInterface(controlInterface,true)).thenReturn(true); when(usbDeviceConnection.claimInterface(dataInterface,true)).thenReturn(true); - when(usbDevice.getInterfaceCount()).thenReturn(4); - when(usbDevice.getInterface(0)).thenReturn(rndisControlInterface); - when(usbDevice.getInterface(1)).thenReturn(rndisDataInterface); - when(usbDevice.getInterface(2)).thenReturn(controlInterface); - when(usbDevice.getInterface(3)).thenReturn(dataInterface); - when(rndisControlInterface.getInterfaceClass()).thenReturn(UsbConstants.USB_CLASS_WIRELESS_CONTROLLER); - when(rndisControlInterface.getInterfaceSubclass()).thenReturn(1); - when(rndisControlInterface.getInterfaceProtocol()).thenReturn(3); - when(rndisDataInterface.getInterfaceClass()).thenReturn(UsbConstants.USB_CLASS_CDC_DATA); + when(usbDevice.getInterfaceCount()).thenReturn(5); + when(usbDevice.getInterface(0)).thenReturn(ethernetControlInterface); + when(usbDevice.getInterface(1)).thenReturn(ethernetDummyInterface); + when(usbDevice.getInterface(2)).thenReturn(ethernetDataInterface); + when(usbDevice.getInterface(3)).thenReturn(controlInterface); + when(usbDevice.getInterface(4)).thenReturn(dataInterface); + when(ethernetControlInterface.getId()).thenReturn(0); + when(ethernetControlInterface.getInterfaceClass()).thenReturn(UsbConstants.USB_CLASS_COMM); + when(ethernetControlInterface.getInterfaceSubclass()).thenReturn(6); + when(ethernetDummyInterface.getId()).thenReturn(1); + when(ethernetDummyInterface.getAlternateSetting()).thenReturn(0); + when(ethernetDummyInterface.getInterfaceClass()).thenReturn(UsbConstants.USB_CLASS_CDC_DATA); + when(ethernetDataInterface.getId()).thenReturn(1); + when(ethernetDataInterface.getAlternateSetting()).thenReturn(1); + when(ethernetDataInterface.getInterfaceClass()).thenReturn(UsbConstants.USB_CLASS_CDC_DATA); + when(controlInterface.getId()).thenReturn(2); when(controlInterface.getInterfaceClass()).thenReturn(UsbConstants.USB_CLASS_COMM); when(controlInterface.getInterfaceSubclass()).thenReturn(USB_SUBCLASS_ACM); + when(dataInterface.getId()).thenReturn(3); when(dataInterface.getInterfaceClass()).thenReturn(UsbConstants.USB_CLASS_CDC_DATA); when(controlInterface.getEndpointCount()).thenReturn(1);