From 51c27792ea7eaab9c337a05af527ef0d93e7a464 Mon Sep 17 00:00:00 2001 From: David Meyer Date: Thu, 28 Dec 2023 16:15:30 -0500 Subject: [PATCH] add ability to update nScope from driver --- .gitignore | 1 + .run/Update Firmware.run.xml | 21 +++++++++++++++ Cargo.toml | 1 + examples/list_all_nscopes.rs | 8 ++++-- examples/update_firmware.rs | 34 +++++++++++++++++++++++ src/firmware.rs | 11 ++++++++ src/firmware/v2.0 | Bin 0 -> 38676 bytes src/lab_bench.rs | 15 +++++++++-- src/lib.rs | 3 +++ src/scope_dfu.rs | 51 +++++++++++++++++++++++++++++++++++ 10 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 .run/Update Firmware.run.xml create mode 100644 examples/update_firmware.rs create mode 100644 src/firmware.rs create mode 100755 src/firmware/v2.0 create mode 100644 src/scope_dfu.rs diff --git a/.gitignore b/.gitignore index b471067..f5fba27 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target +.DS_Store Cargo.lock .idea diff --git a/.run/Update Firmware.run.xml b/.run/Update Firmware.run.xml new file mode 100644 index 0000000..93e16f0 --- /dev/null +++ b/.run/Update Firmware.run.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 73f3f1f..89cdab6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ hidapi = "2.3.3" log = "~0.4" regex = "~1" rusb = "0.9.3" +dfu-libusb = "0.5.1" [dev-dependencies] env_logger = "0.10.0" diff --git a/examples/list_all_nscopes.rs b/examples/list_all_nscopes.rs index af87619..5f1bf4e 100644 --- a/examples/list_all_nscopes.rs +++ b/examples/list_all_nscopes.rs @@ -18,11 +18,15 @@ fn main() -> Result<(), Box> { let bench = LabBench::new()?; // Print the bench to show a list of detected nScopes - println!("{:?}", bench); + println!("Lab Bench: \n{:?}", bench); + println!("\nManual list of all detected nScopes:"); // Or loop over all nScope links in the list and print them for nscope_link in bench.list() { - println!("{:?}", nscope_link) + println!(" {:?}", nscope_link) + } + for nscope_dfu in bench.scopes_in_dfu() { + println!(" {:?}", nscope_dfu) } Ok(()) } diff --git a/examples/update_firmware.rs b/examples/update_firmware.rs new file mode 100644 index 0000000..2a4a9ac --- /dev/null +++ b/examples/update_firmware.rs @@ -0,0 +1,34 @@ +/*************************************************************************************************** + * + * nLabs, LLC + * https://nscope.org + * Copyright(c) 2020. All Rights Reserved + * + * This file is part of the nScope API + * + **************************************************************************************************/ + +use nscope::{LabBench, NscopeDFU}; +use std::error::Error; + +fn main() -> Result<(), Box> { + env_logger::init(); + + // Create a LabBench + let bench = LabBench::new()?; + + // Print the bench to show a list of detected nScopes + println!("{:?}", bench); + + // Get a list of all the nScopes that are detected in DFU mode + let scopes_in_dfu: Vec = bench.scopes_in_dfu().collect(); + + // If we have a scope in DFU mode, update the first one we found + if let Some(dfu) = scopes_in_dfu.first() { + dfu.update()?; + } else { + println!("Cannot find any nScopes in DFU mode"); + } + + Ok(()) +} diff --git a/src/firmware.rs b/src/firmware.rs new file mode 100644 index 0000000..b854995 --- /dev/null +++ b/src/firmware.rs @@ -0,0 +1,11 @@ +/*************************************************************************************************** + * + * nLabs, LLC + * https://nscope.org + * Copyright(c) 2020. All Rights Reserved + * + * This file is part of the nScope API + * + **************************************************************************************************/ + +pub(crate) static FIRMWARE: &[u8] = include_bytes!("firmware/v2.0"); \ No newline at end of file diff --git a/src/firmware/v2.0 b/src/firmware/v2.0 new file mode 100755 index 0000000000000000000000000000000000000000..51508ce076e54954d75ad8801947cc99ba641c82 GIT binary patch literal 38676 zcmeFadw5jUy+6G6WipdwNG1tlG6}F}CTPf@4hbrP%48OhjQNTyP^nVS1&Hs@9zpd+=aQ=Uj)~TT5w*QxC`6g}Oj8n0HGwt7ve?xWuZPaj7 zQ(~#gKE@X(HS-0Bdx?j!SlosaZk`CVP*X3_>5g*6sm)v=r{?QU(Da9qPxCx7v9q1T zTO*Sk2bu0&5zOIs1iAR?pm|Mo(6VNguQZrge!4rU;%)EgW{5q=5qEwev3C=~ z%**i7a{oBpG~^jM*rU_8bn96%q?cdu5|mitNOm6g`s~S0cm58U#MgkTD6a>!GFi<0 zqk&TJNqJ26QZ7wCN-6K7kUFnUbm!LvnfNp>5#0HSa&;;1x04L79kN2ahpfNdOZo|D zp*nq2erKuSJTt_OROAx;zSASH$012!h`sFWW=U9@?tB*M6pH3A4wewpSNXwD+?0lp zJ}u~5GR>P7^e^2J)W_Gut@RmmJ)pqOCR6@Q(|n+{!Wmp_a0c&6*fY$`)d}pyc zpGud?gPHfePZE1l;y>|1?$E?LJ%r7nRQFI0D5ooevx&Wjkfp&A4qU2SP@Mw4g!S5q zp^wBf^9FtPpq|ttol(;ZNdiruKj_N|>Y1kl`UtnlUdnHb&jzSu?`-_G`@uO0*G}oa z){Ea#owyxgxYEh6BYiyMcd+q(T9)${LrOYPH&M<$VJ+d1UA2pElXIo-#cqzJk}hOR zgLdIo$l0^h8B9oU1`SD6#!outdY2PN&rjIWKB9lpk?vh?q?{e;zAHZ6yRZ8$$nSo{ zwyUx2h&_v#Uyb<@`-G%2REKG4o&M3x1y>dye$w$(YjfvQtx%0rXMMXiZVmjqvi-$` zY;}-Pb>B}8Qz=#Vxex2a9|dTbhr|~2l_36uz*lnr(2uaAy6^tO(1NqN@Akth0>lgZ zygdqSq*O+Gi1-T?{uHDIiCx{k6MRh5lHgK(vX{2Td%Z2)9I5sG;o7<0wZTu_=(*6l zsO@(R>t7MH&(ip5ukd$-b7p<&`<}wbqOGIF>BKTI6o@|>8u%bYG77wg9ugmaVxgk@ zM`)lgr1vrc5x%GBhKV$F0-HGLs zI!c4Y;)Fa5usq8UE7KlW?K!|pVo#ZBhZ;X}?Kn(oM-^E&JD$Tt*=i)Jc z7yqp}KSoZJh3OHPZ!1Tv@B2t5@8xYqG4YL-p^cYEzhLu7_i0!TrG%F`&AaGhOM~xe zCd+(0rA$Ah=`Rmz*feiNP{Wnev5Y?uAkFUl8@l9D=fa;i+08RWUgYKB!PQkaR$5``yca+%8#3YDO^0HyZQsKVN&B(_UV!7zHn@!>#cQQ)F4IynbyExgUZ94IV z{B)`O_`RqjdhOKiDoaj>c6!^j3UlxoCA#%7>0^a7&428aVrY7!hr}e)-XZ3A4-=mw zYIYdJOdnSt8rU(yH8ORi?mqD@Q4opTFs8qi%sS?0(*F{8wd&PlM80N}k+n)wpu6gv zHsx%i_SLqTkGCn;*xHl?f!vntWlW4fqA@1=Mte#6nyKF^Y$H8GY` z&>KnTER`Z_E|u%-r(iSuPFBtxG>d?_i>9%1+fbIk$Z?~L`Kz=)4t*7#F%;Eg0Dk%C zFPi3po~Zp%a`h0gVDxdLhHPNX=IdGcwqe%72nLE}_NlHY=2TaF7MISOqxPTbnl$HB zS3*`2p!N^zn%DnkEWnBJ*KyaIL^6YlCGD6ez z2)Re-@2ewa1!cvW@={qB+uB9uvzARR)=Vs|T~Xg}McZa&b|@^NWhKjs4RxTBpmw-4 zxCFRKL|-?pE~##hODNk@Rw}R1*)Iof7Q}bE$lT9eocyH{E)Twz84d{IDaBsLp&f=( z1(B1F42Hw{y0|($AUOG!P&nm2(O8#OS1P+=N@Wt8OiOnqzskw((QrOfCy3x-klCGG zqT4>loDA*j6Hax-!5QEZ;U>dHCrsHMB`*hDX7RfU@1fN337@<6y7rb?Kx0FMMq2wR z#RmGKucEIaLSIasPNhKieNO&7VmBn6%m95l&=*ZCi3L;Yb!G!}Y1o3=^H6&pC#OWH zjVaJ0kCPKdcd1rM46L2-qyVev0tM}f+)wPjX!v?!e?12N9L7#y;bbYKZAq^~SSA}@ z2-^T~8{wwGnc)NrCl8E8iD9|?vfzSp?~9B)ZE%ixwx~+wK}ZEo2WNmwgq!?e%rB?> zGD`llvaR4lMdw;!`LK&{&|dap@xw0u!4H*W*N2MU#>p{X>2Rt3z+zEf&&gF0yI2f; zosQ(|>Zpup$~_VJOsV6+^J4Mq5~&*r(;4P&T3vcwvI>(N;VKQ>)Q#~GZWcqw#}t!j zgMOprDy6KTyqI$_@`i|xSW9yKGz&4O)-Nx3O<5^07TQYE%*GwlcTB5iZk{%A6s zVGAd-Uz5$%nom|?lL2Rhn+9iwqnaNY9oKw@IBg*#o0U@8;`JHIw>d{yJil#ya78+Lt6h!^upfZCVzl7aP^y?LCy+4~b-m6_#$4+BQ*& z%Ab}AjA$cz)X2|`U0ZZ*VR8YHSB=aQ?U(;)D(xF`6Zx;BlMA8?iF`syDzJ)KBE}y1 zwjpAovs_-z8Y1#%7-Om+E-8q>WgNS4Q*Y_5<}k8#px(tQ<%6TEe%PiM9I z^wyhw2J2@MC#Cw%tu&mdkX8exypqB&^TQYt#z@9reRK_SF2Gx(Q7#GPu0gr-N=~}y zZ$LR(rU7Lr{&D}D8ZWOL_l+vtXA<@0qom(+4=>uKHpOSAl(#8ovo z`wS9`4Xu_g9UyF*6mO$s|~57~Z&Y^@L`^ITe9% zT_{K6#B0Pfk!0lfn2bC9meTZsnc~>MLd+Pyr;y2W%NX0(z`QY<_VgGdrWK40EC^}j z__5HyLPaB1mEC!Sv6RX`y=(q@K@1JdP;|!-yPIHz5*o-*$T5@C$TahjOFs^?rAC zk6$OU@=GJeqITsEiDY{_+TRjIr?;R#`ye!>0P~-I&v$xc9z0WX? z&!OFKaOi(h^4Ep8WKwc9bEfVB6PyhwcQg`z$4O9W{1LZ6``AzPM z?uRCk&jxSnYYhpI6Vt;mPKCco=gm_Dp;_hYuU#yop*)m^2DYBCiBF*BI@J7jKuvub zST-;>F!fbF_J$H4x#5!kCxt&)ZSjv&UNvAreOwl50Y||T@Mu>acFuN4Rovhjqf{TE;rE4D{GQKkP8Hv`{0` z)N`1Vk&K5RS7I6)_+X4f>`_14!0h6)b{nu;6VLT995lXyYTUH=h$@+N0cT-8I5nu8 zjtx72nH$ZCa5^?}HK&*(M0jl&BaPCVl+TW_SHHpM%~$ccpwDr5oYVFZ%IT@$FsGYT zPCr&To$q>8d+}?Wo)26jb7a1&-G4d~s_t8H_}&29$UJMD3R$cpmglt8dP{@*2;Mix}aWM7S=c_Fy7muCH98HcaiA*q_bAo>(qzlklB{?6JR#a}gp} z=aJTrobR%XPIU>4ZhC?4C}S?Uyg0kH>vaolO?$44tS6Q|J&JzU)e=UVDgi~|x{?Zr zDKtK(Q5P>O9uA+Ni ziaM^%C^lBT(rm0^Gc`VA6`$#ExzV43(1pnb6(6F9=gmgU zur7@l3sZ|zOLQ*AoLXRX*-ec6t0BEfkXZS_kRV-K#G#aNC8aQJ*dk3XN=2N`OX5o6 z3y9fnGGdS8sUbnq1L7D;P?`;(c^_z|w5jxhZc1|>;xZiNgRa?d@F@^D_fbcIV-eP9 zIgDI3#ANW|9N&V4M1E$(s_wRM9d{HPB_{K`zS|ehaos2}H;TS_I89@$nLZ2lsEE&f z;?@#a!U)}+S&%W%LF7kA<`fGqqeL>X+iN1G6jz?#Ryeyj8Zx#O&|f0eR-CJFpU9So z_4j@vyGG0lEyWqGG}qgKnFUyv+{MU7%$`ZcNB-*;rWddlyU7A>rwnDN+&V&G&D}VB zJV4}}k!i)(l%%TkZuAn%b#ANcwE!{4LaJfT`UWXy&-p_G*M}MclZxVs6F|kJB1#K1 zBZR4eMHlEGpD!V&&WOCI9KV43JKWXsRM)+LrE*OvN~&7j%*i2!otf#sw(#0wos=S7 zTTJzyDs8IxV+CfrmL%x>%i+Yrq~au1=SdNr9~=zpe9f33#b2d!^N>Z-Pw0Hy-ma$M zztbsQGnj&(uG{Spf}QUD-W1Us{A~R$hvD`8-efVZq`|S@YZNCzN0Um7u1Uov_^DDl zXib-PSHu>7T9FF<^8V=JRHP-Zgv}jS_j)5XL+xpt4k{mNQ?bRyA2?)&H9lzk!A7hx z8J6rI4Gj-DWY3a@2O1yZa4y5tGmR{Er*3`&?G$?l?xUPkRJ65;q}Mx6VU3D?uw0F( z+sXUHqIyS;FQ&BZNp^^-{bB#Yg_exrV_-u|`vIn+NhWGtHbzp8)NsQXyB_FL83 z`Qckp`vSH0&_Ly2GiI6(4*mn{EV@>uYb+|exmZW(2y5R3KMg)PdIxN$-m#@A(|4rD zQ<~zN2N~Y(yA8jh`g^Z$F2Wh=?=}8gslFghrHjf32S>QBim8T*9)I&B9^VNuPE%j>at_%Z}+u8K6m>T6*o8}-$MNI>hEK| zNlmsBT>t&fr~~KL6c#vS9u@|hgU{{YRGqUCn`5ze3QJnTVm%*aiL&qwG4({gX^dd! z5w+0W-iO&4F_zm3V(Oz{lTmp@7RLS*;CehxDr59;4}aZ;Nj*CQm=1(@o&m37j2fdf zWfXrM7LreAEP$VYW>n~ZjfOd3vibC{mw*;$uvcMcRrrr1@QaXR4A|59czD+_J=HGm97OD(k8lYhsh=!hjwsg*!k3Q^(z0U%mqK4q`^WyGy61>J ze=GWsF{FLd(SB;&5jo=+gPo0Qupdv`3k}C3Oxp}SH;4uf-BG@PmG2*(MtaB_g>SoisBu#-CJmvNtD*wPIb8e~rldh=3XY&l$m;zqCrQ z4*2V9d263PsWhqhg0i@-*8F?)pc>2k^?ET%OuQ8u)M*uVn^s{;=8C!=MyE3u;_S&a z_PZWUytXG<`!9cYEmt&Cv|)WG=W}x9Tf|!e8VJLacF1K?!4u-7g1ExCB4S}X&))m) zVm|xbPV((U zpgD7o#$%+4J;s3ZNrk0-kFD2;EyKONI;m;6S0?hW6*}wxmp}f7f3&kWJ)nD7L|&|L z#ez5$r$In0=;6}g3`aTGolS`sLIY<)hGr&R5HVxJy7oUuXoxG~3zIOe$L1zc?R(FH zH(DNZLTNlc7wsT4a3xfcrV&{m58BdlLj(T|*;Ds8hR%fR(-wXxact?85aIedD_DCH z?#Os6oP8wwk1AejS82uPrIWp^g&aNEqm6s3hsWxK*Jt3gj*mOu6BVD@!Of@V`+D3P zG6(f+I2<=(!VQhxxQvq%WEuT=(&Ms6Go-)F{y~dJAxX3^C*~wE5YQB(J&hZKL>@Or7-Y0;M4i&GO> zzge-g60_E15F5%2$5YDO`P8Z?zS?F;^-&%>%OQ4tGxTm^%%*jgGV{7*NYt?|G%z&W z8&ILW3S?)0H0pUK<3D_p!~~Hsm#<^Z*GuMgcf7ernkQXQe66j@tkw(4NAPW}7{iua zP~Js&PAh-oJXeWNcVoVf-01LSig!w4ANkImE+RjU6Syd`V%?obES2=c$x>z$l@pyR zzHJeFwPAdiuUt?(6Zmcbz6{@WM~E5N-e3_w&ORAZNqOedpa=n0MX5c%YobeD$%_RcNA_e z=0}r6;AT@Ko}NK2uCtqhgh_FwpjO=FxoirutPwT7KlZ(2dn6)1IAoTRBnwVll3cjs zwvP4%ok+hgoGxFT9_}X%M>nscyEcO%3XeN+uxZ^<&noC7q(fgx;}m9GZWOlvDp*c3 zO51tb^RsRDt=_>WpGBOgVM5N*wT^k6xf1=xU36?Qmo2L>N@mDd5RERnS|Re3W4xJN z$5vv501U&jVHoI@a&{_Yk=g>pOZS1Ck3KCiRwBReVJyyd{7t1Y%SDNk#K|JJ?hzk{ zb(4V)Rx+77|L)b9tELNk>@TYFig7kn9H?wxtu-?<*&%*pRpo#9*qKwjIJfU%Ooh=2 z&df)dOw^Crp{EKEmvuwD;y{ZMAm6n?olX~Ib*moD4>DSvH^0yG2$Pv9(GYor&CKYt z7i{nq0OHs7{Gd*I0UQEP&R)A2_tcE8wk|F)S+ZXl`{B!WA*j*pV3;9Z&TzinyNO}@ z+~vux7^nS8{5qQ2IwM7T(XJWd`%FQePjjXGfa;s1$2b|2@j*eHz3$<4>MoP%CCm!n z8`X-lnkRRL23kj3Q<;q0+q0$Rt5f+aXBh!!zoXgGs#V67ama5{^Jl5~#CqjC%|EE- z-?y@1&4E>iB_6WFYTdTSPU$8rWQMs<*QyYtIG%EDWhqu$xWiMt%MsU+Efw^Ug3SSz zjvzJJm7Lir>$vPPgP67MV1Nfqt+$}hQ;;pKhyIAPC7QhcE7a*D2|CX4XAECLOif z>y#OYX?(;&X>cEB(MPk$nPDgC?^f{A`)g)MGh{|H!{zIm{NDQt-#WF~*EMAg4T)=_ zMSH&OXlutBXP~91$!oH&&Jw$wK>NH8lqQXRs-n4q62^lM|zTy!o99#zf4F zut}L=*rcRWs-OS2?Sp3d>+278Pn&PQ+Y#QLmK zV-$DRs?uw9ajVz6{@Cn3o}ACBP}cy8jZXyB?x-jgDu%P7A6Bh#)p{2(LFNWx{oGHv z)2U?!eP-=ITc(t=+APPhnb-?rhqzvnzQOelwGunE5;F6W&wX5%Pxq4b(i2T&7Lhm6 zn@{=+O6%SVYN4@P#MmLO|C%7LTkGpkc(+#O*t@1NGuL%+nRdC!!w%^$`<)tTrai`a zZJ&Js>sszRr?>>#<9-OAhFOH^O2>k-Y>&sm_It88++^A=y+-1pbeqNmRsSi^BKUgDR5Wp=a#UO##>Sb>?XIGt!onRAdQ{CAd$%{! zj$V`9>iSSR#yShf;~%;TC413Z+}+-p$Y+?lT#3>kH-92O#>HgM8R9PMdszKue|T@Y zM(R-XIPvCClYWd+NNsesv&OX+VV}Zx-Wb%x{l1r6^PD{|7^lf|y(ejcOk9VeTdWz< z_tP{s^F0@pQgz>&!++J0?kA!+SsQorD)NgC#jv(yd?h|DLoj+&v8Ah`VYJMRKL&ly6V5~ItX9KA9QJ?`$-JSlm^e}XE;gs zHoXS7OthL$6ruAq5)G~(Pt8Y)u=qV`Mv&LM=gNc6vJTfq$&8U}Gjuc?I!g9w@tX)q zU-7}JG@2eyo~GY(GpvelQ*^B_0T-`OtI`Ga`g%V}e>H&7EsKO4PR$(YG?^^VO-x(gJUqkT3}i!))5vfmsUSfU(I z`+s^rj#t7t#Ebn=Awe>{zESC9o0Yq5OnPIB#rcl%g<~&bG{tbdvBim55{=_agx7B? zl8z-wTwiO&k$%tG(~8F?_$=s=4aeU>*qx8B1*&zvB*}2x0vPnw6-1!xoUfO;!AE;> ztd%0AC%0k3K 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 impl Iterator + '_ { + self.rusb_devices + .iter() + .filter_map(|d| { + NscopeDFU::new(d) + }) + } + /// Returns a vector containing all nScopes that are available pub fn open_all_available(&self) -> Vec { self.list().filter_map(|nsl| nsl.open(false).ok()).collect() @@ -139,8 +148,10 @@ impl fmt::Debug for LabBench { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "LabBench: {:#?}", - self.list().collect::>() + "Connected nScopes: {:#?} \n\ + nScopes in DFU mode: {:#?}", + self.list().collect::>(), + self.scopes_in_dfu().collect::>() ) } } diff --git a/src/lib.rs b/src/lib.rs index 52bac92..57bf39d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,10 +53,13 @@ mod lab_bench; mod scope; +mod scope_dfu; mod version; +mod firmware; pub use lab_bench::LabBench; pub use lab_bench::NscopeLink; +pub use scope_dfu::NscopeDFU; pub use scope::Nscope; pub use scope::power::*; pub use scope::pulse_output::*; diff --git a/src/scope_dfu.rs b/src/scope_dfu.rs new file mode 100644 index 0000000..1fb1a18 --- /dev/null +++ b/src/scope_dfu.rs @@ -0,0 +1,51 @@ +/*************************************************************************************************** + * + * nLabs, LLC + * https://nscope.org + * Copyright(c) 2020. All Rights Reserved + * + * This file is part of the nScope API + * + **************************************************************************************************/ + +use std::error::Error; +use std::fmt; + +use crate::firmware::FIRMWARE; + +pub struct NscopeDFU { + device: rusb::Device, +} + + +impl NscopeDFU { + pub(crate) fn new(device: &rusb::Device) -> Option { + if let Ok(device_desc) = device.device_descriptor() { + if device_desc.vendor_id() == 0x0483 && device_desc.product_id() == 0xA4AB { + return Some(NscopeDFU { device: device.clone() }); + } + } + None + } + + pub fn update(&self) -> Result<(), Box> { + let mut dfu = dfu_libusb::DfuLibusb::from_usb_device( + self.device.clone(), + self.device.open()?, + 0, 0)?; + + dfu.override_address(0x08008000); + dfu.download_from_slice(FIRMWARE)?; + dfu.detach()?; + println!("Resetting device"); + dfu.usb_reset()?; + + Ok(()) + } +} + +impl fmt::Debug for NscopeDFU { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "nScope in DFU mode: {:?}", &self.device) + } +} \ No newline at end of file