From 25e89472821701624def0553b4cb4de171a67ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 15 Feb 2023 19:05:08 +0900 Subject: [PATCH 01/27] Dolphin: drop holiday animation (#2398) --- .../L1_Happy_holidays_128x64/frame_0.png | Bin 1787 -> 0 bytes .../L1_Happy_holidays_128x64/frame_1.png | Bin 1795 -> 0 bytes .../L1_Happy_holidays_128x64/frame_10.png | Bin 1791 -> 0 bytes .../L1_Happy_holidays_128x64/frame_11.png | Bin 1814 -> 0 bytes .../L1_Happy_holidays_128x64/frame_12.png | Bin 1779 -> 0 bytes .../L1_Happy_holidays_128x64/frame_2.png | Bin 1774 -> 0 bytes .../L1_Happy_holidays_128x64/frame_3.png | Bin 1801 -> 0 bytes .../L1_Happy_holidays_128x64/frame_4.png | Bin 1799 -> 0 bytes .../L1_Happy_holidays_128x64/frame_5.png | Bin 1797 -> 0 bytes .../L1_Happy_holidays_128x64/frame_6.png | Bin 1789 -> 0 bytes .../L1_Happy_holidays_128x64/frame_7.png | Bin 1804 -> 0 bytes .../L1_Happy_holidays_128x64/frame_8.png | Bin 1809 -> 0 bytes .../L1_Happy_holidays_128x64/frame_9.png | Bin 1816 -> 0 bytes .../L1_Happy_holidays_128x64/meta.txt | 23 ------------------ assets/dolphin/external/manifest.txt | 7 ------ 15 files changed, 30 deletions(-) delete mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png delete mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png delete mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png delete mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png delete mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png delete mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png delete mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png delete mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png delete mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png delete mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png delete mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png delete mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png delete mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png delete mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png deleted file mode 100644 index 909cc330392b999e38b0326b5381344c952380b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1787 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 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png deleted file mode 100644 index 3a43c6b844541f9be3cadc2aa78864e9147919c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1791 zcmVnvyf3eBg-+&c%%cGpOsw&ZJ*b5*$z;l?5Yi0jy7XFZ9XH9ar)E7M=0e0a+Ci( z*H%Q2(@-5?W+Nw@07V4tJf0gn{`g#T0?YV)Y^APuj|?se z%wo3_XlwL;BVyKdU19uBz)K3K-u#Yqz>+ZSnT!yhp3gOSd--#KNP{F|$U6{2D@90T zb^8>D(e!+21hHjLCR{RkI08$a|KbQkCoyWq)1?u(OR=_A+r`sG&SQhH~mDq%3O>Yy?=Ilw#UdV2kEcJ z|1^18l+nhqE#sYidTXyypvp&LSs93c8Z^vU`HW33+N~ewYdpf5TLtFEvk_5(A@*Ax zU_8V~)uEMY?0V=K(+ynN%V6bMPFsO;2k?kg#tA&e?aA;C%St~hk#!~+rDh$W+ySiH zYE=)O6KKYX8jLwCuVs9<&k3p=A@pfrl$6Nv5u(+J%or`?YfqjRDY7JM7L}<)b~B?$ zeHJ9wP4(ivowkk6{ul2ybEWQm(Or>;k2p2`8(5al&q zytyY}m73^%IlsCRkUB)(mtfk{BVHna)kHx+<38a7k_;lwi!K9dUyt)MH=B)0bQLg` zaoT%*(X1jTfeaL#!yxl{N{01^8bNILE7L8zqZ4GKlSUrLTWVDvRdxGJ`8Vp=Uq41+ z+5(<12}`l46L=jtZ;n?9>~Kir5S5NFBkN%u#F%%ydYkQ4drt6)oWaZQ5>Y`OJ`Mn=e{w+ykX)}gf)|3=W?M5VVMTKHz1U>5SSNYaeXh&e;lruO8> z=c8_E2VfQV=CjZ?V9BvI>!mD|v$e6Bj;LCaGFU`pF-}nC02N@!COiK0SjGV~c?eAU z_2y_}`?HsC1@C|t$}BD+390pgk2TifDM+q2R@(#8Ym~#nBj6sa>Z#8^@`Vt2h+em> zk74AW9y_9HUr2srUG40*{y+pGji|xl9g;V;-(1PMQD{YLL=Yu+Rbchq{tF>qpP)7F zv*HjVa-%#ZvsTp}Wo1JBT>#tYVaG8F&eXP++>RA;vz=2-hQ>WsaWt0$j7p)- zpj3WFX|{gW=2>9s=#1P5G$+u~*NmeIijt zavZC>sNqDDO@q{vasVcj&xCt#E;7Enxd>zAis&|j*;JSXb7rlzc0C8pR@J1IBHEhr zJx%FM5b(1ifW2JSLpy=7s*me8ViMIt;}IDYy9Zk$huWcpK9WX%1ZTX z;c|jB{%oZ1Zi6I56$Z4{V-MT96fALLQ`GKM@#X-Uf!W&~!OGHyG4Kq5Wudj7EnQBL z#3KfsGTv0Ro-b4V~63n$)d(UL0@Fx)i~~L9UKs%_C951OJANj` zX^kRFmgYPwdM)05-u^BCEk2_NLItn51ES8XMTtoH>|x{~*7-iP2?)ru**u4{!{KzqK5I3i?oWoFz;UKB+%t#*AMY#DSS0<6A? zOam&Sd!zwX|23?V1N7HYc9otUqD~w!tn95s)`_fzWdBtGmREX)l8cPv zR&oHOr}r$P6JqKXSi{JQDiRr@%`R)s%MV0U8mb~cqEgc13Dq9FFv!TPESbD9cQJg&%8T&wSwN;2(4m!TUKxmF hpSXqJX8hJ#`vpJ+v^_(~quc-h002ovPDHLkV1n0aQh)#e diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png deleted file mode 100644 index 20ea12eb06d4e6d10d9dcea9f1d2c69277765e1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1814 zcmV+x2kH2UP)F$kQo{QqB`eacqE2AWGUy4zKj8gn6mK!ct= z=W!g@@y~Ew_Lk4{+$72Uc-$?(Zd}*(uN3UzJpPdfm~Z}ho~9?*X(XOU>cYI|vA-uX zkQMNbn`J50Q?l&C^tWWN#%Jg50#g2QI0jG^c;$wXu~n3Z#xwIVMt`^REwplgPu=7y zba;=up&CGTGtvd`fVPfC_%vnxRdT4g|KlJzKos-A@31*0d|A~mV}`;=^?&7o+EgB`89yZh9k=Sgo9)aF>9pX zVleO`9HJ1!=FQPf%Ujj~8Sk~W(Jt3UGO_z&Gk~gbFh=PWv@`;E90?Mbjg@0u=WRhe zBSP?yK`?DQ5VzC%6O}}z4_2Qch0!k)!!a6GFXOe zfsuWy0n7?O!Z8&+?*P0o!;nO2oU1qhJ9pG*BltCdt_dx7DsMA;yCRHx%)27n_=F0f zG}5X}?HoWOAzJppq@Ck=eyz1>pR;R7*}9FX3mL+P5Hf)1VisAOf!gKaqt{w~n@Lr7 zD!DrquJDw{m?VPeVx4IpyPkaZvWai0=4r?p!5az8RKi$f*(?i`DOgo7OFm|;IbUB@ z@yoJ)b{xzCsz$(A<4%v@fhi`ff~T;kLK(6=)}jkftY>kaOPr6BaaYSS1qE@Bs-~kLObpTj=yRPeE z+_<+ntu$-(`72vk>H2Y-U5> zsALN)$@7X=-7>KAzx^U;Oy~{-xpoA9EgV=i2n~_5tAvFp8=etV>JGd6A_HJc?Y5u zMkFDX&t6{{gPD1~zGx4~qs)&?K_}8~0eQ1qeh?xL(IZl$4>SMWeP?JjkuhgSK}l=( zLe#)k4`9VctH9wMl-GAI|7tOvkv#$?<=t75){y#Da0Omv@i@4zkLNOU=OJe1^*Dis zNLZ-sQS-Lfb#Wnpy*1g}HNzrMvY%Yjn-|Gx^<(DuhiBGgFn<&(4?`m`=;n7C0Yc@FDz`C<$a(Bc#VHb~ zvGxk7Vt|aGa_cS3u|G0zl!JT%@tZ;akti$wt=x2Wtd`cWeIxUFDd|{St1d>@-Ff4s zoKHpoEACf~fN_n86v~6IDxK-ON>Sb6Ml)(;tWVh~GO285Zxiw)=nyoav$rmb&f<+W z_exlDKP_cw?GlmCxYK05mJ_c7qX8SDlwxV}){Lri3!5yP^KW5X5-r*Odc9?tbU(=y2s0 zBB|u+eS6u;n*mrJz^Vjh1)JAEuhIMH{7BwPDD%iN!&6og%Q63~FHM#Qa#IMM9AFkw zYmdfQ2}VX)++s0v6HPwvhF6Tw3L{#!q-Fu5qsQF4J-{sF>S`i`4x>h4)5?0}xNJSf z0@B+o(?d2`i=)>as488%$-7xM%AT~xal9(t^Q@JNXN9DAHZYBGB5X1Dya~Z-=lcSuA?DySWym8!P?~1d2T=P8;!m}nhrs@GKcm`v&q4C~2S{{wS?p0<~k#mZiCzS?vD+n1x zWe(ZqrJ+_^cApXBVO%r%obd*ox^g{07noZku|k$;_{g~Cxj$10^u~6b0}7wZJOEle z^4SXFXQi!3o{lrAdK>v#o0I8-u*CyZvw)T8;0fKxcqxYeAiVvNsEn&-1Vm$5QiaB3 zz^wCW`TT0Ud>7zt-1;0<4ZSqbnmg;#D!5bmcU4M%0Cc+*>zlWu!2kdN07*qoM6N<$ Ef-Tf;h5!Hn diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png deleted file mode 100644 index a05abd80f1bbcf8c4c31985df242787246776d05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1779 zcmV0r&z%C3U6&)&Q402Ruv+OXzmGsctVBk~ZZKW%&qr8NNGen&b$i!kXm9wELxpG)xe@^*j-K@u_K6AD8uMQ~(w z`xb|h^t=&4Xxozsmq;FtK$FL706JcC#tfH$b^uxD@SG8qsp)wmf+7lLbEQ-hMA!D@ zY587rTFD_iW3Pnfyxv#|sJ^Kpu*zty>9-JB3oSGy<0ChHSGvkv22WR1_n+Ol%d5z( zjr87cUy?j2%1Gl-%6M1jGP_Pz#*udeJtyE}sllcBk`o{|nvRnk&C_ibD^g2T-ZCCQ z0^M+=>yTPCbUo`egbiH9WoQvkL#9qcQ0@R;A(e3gui~C9h@!@rMaGe=E=rPzo^81U zXt!1C9y~=Q<3tUHL=TTLu5ylS6tP;^?Af*!bbx3W=HsnG3-xfRde`K6UBYfssxpO0 zyhjG9A~3qBR8*}55+QaJc{?!ML3}vC3Obn1S?LV)FzELctsGBv-kx@mNl}O?09Do_ zUc9*{prs7!IiLSTLuPSvfK1Edktp99S}jDJz@yHq+Q2MD4;O(_QAM9=N`8YAX!=EJ zi0qotf_k8WIqaQ$C{#17#=4>7>0(bp_5h{g(Q3ss8JRcJBt0?&(Jkd&*L6wk7+)Yt zz#uH!Z-I420}>Hd0Vy)0Z+i9K+e0+HN<4a%%e0<<8%{ela;BCIO&-AWY|cg@ z2K+`NTX;fxn>o|)CxYG+CqvrlL#sw44Xi>m4cv;rC}#6n^yYXQ^2YMqhsgED@^RK# zaCS0!*(9Lczh$ebH-oBLk;obr&FhV|YL$#!1e(Dk9fS2O`^M6HzwLht*p-{nTPAfO z<9B|mk+GT%mRxTv)!AETC#;%{N3kOx2;quYksuN>pD>ord1ArK_1gN8Fe?{nH=qBk z$+gCgc6NMuW4$#_0!=^YxUCVOHH;@fl(%wr1u}Uac~(_7MdeUED0>P0T>wh-pyMn^ zxxE5b1z#Z-rN(Dc12tR~+!UY{Sk|~%<4J`bI!^;n0abFNb7+)9qg`t`$3dc`nF9D> zX=I0%w^#54*&K~Jl~s#Gp`Agdtz1=u^vjTp^v&AyF=mz((+C{8uGRKP@Wy(qUepOI zoGF5cLqSEL70%EFrMa0_4AE=0fb-JaSle?>KDMGpiU6s5AO}EhYejKmJ?G#Dk9vJ1 z@1k-M@RYVX>`Q4!bat8oR<%?dmS^&3(hbW*9!V}b-%CMUR}IniGC(hrngX+$EBgJg}b$+bY+FHgTLP7zJw$HJ}fQPH1!1>`tv-hF8EqlmDK)|y<;mGf8O z!vQoekAyk|)eM;?YefMY1&tu9jc#2~>GdNKfF3?V&P3O%$hB2YPK(qi`7^t=_4@!+ z1JI(WDgs!!|5RS3Iw3kBtf)74W%x(-@bRHgWK*l0fWD6CWXVMJo7s=l&1ATN6X{^! zXAB(QwOmzQ15hQc-z#KB##il)UYICL>dB3y5pRv3Arx6y`nduptKdp#T!k0qP=oSf z($fJ+Gar8&=o!Oe(@-bLKJ#>{0xHkrG*Ds)Bo}K zv5Udv$mlwtkxSC$sxZDb|HT(T3o|;;>Kd%YpNiEEWf@c|AZIRu=RE2sg190cKOnun zqtfNbtc4nU6%8z*au1ecywEo2LBG^ zCriHd9ChMIAzHr>)FmU?^ z5tW9L9AZWWf6Z${t=DE#64KrwYe+3MGPlZBy>~wXRKfG`N<+=)p^cB|fjVo?8It25(No$3 zV$Y9%5|s(n9=uzC$gIkF$e1S(SCs{)h1+`p87^ReGgn?Gz&Ez?7NU5H@dtK9z<;d< Vp()s7p}_zE002ovPDHLkV1hccKn4H+ diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png deleted file mode 100644 index fc7e0364afbfa0c0750a59c1d546dc9211c42161..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1774 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 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png deleted file mode 100644 index bcc7f28a9eb40eda4f9d472409a93fab7f0d98ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1799 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 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png deleted file mode 100644 index c0b1a4fe24af639bddda8ca7f69d2bee8aed8689..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1789 zcmVgl@z3GiqHRtsLHW&}w z$K$%L*Y(Trdf_EM9*<3yoX5{;19s!}di^Q~M|fPnCh;4e0%<>@HZ&zpfwvW({7sU@O>WoM*ky$YX;3wDS>KIlx^P zxe6WG<22j=s4hmQ;2rSvc!bY$*8fTlz4U)wBnQYsZv2ju;RW66^)iFNbN-myY46=L zgBbuF2Ja36OV9Wk5wl_I3g>?Vmi7U5Tn8)(v&M9W`1bqULbR5D0gzd6#+i56NH!3w zLHaELV;+P{HGIlE|m$~vI$dv9!vYu%Pj?7G+rU?w=&fQ%Yi9s#_r0tu|%mE*n6 z(_-|tR@u7@`cz`i(JB91`l0F&M==j*xADc%8jKr(wg=1We)+PtI; zM9`;pjA?kO+fDt!$)p!L!f1Q)dEumY`;ANHu~yl9cI^&0Gn)QFWr%r9@?J zC&8TQ16Y1|N?{!ad4^?!05MwT#G?KHl2Nhk1enGcgJ;x!WfgR!q&sqk2$hn!yAOc6 zuaQpJ2#TVtauUAL$q0A2-ezq0_=%t)FnrOdwNr|3kVZQZ^&OCt*mgMj9*gza)A4o{ zUc~^=3M;6VHCk`9SmYceH=1kd^qdvd{A>_tRLXJ~0J~Fk6a+JUhda#G?{yv+ritXu zA9J?<51jNN+9Y{liRgMpRJj{HD~-bA!9nR@rc+&hw5 zZ$GWQ-Jzd&Jy}{l*tuvBq*@@<{b5Wlpsz{Iy#B(x0dl?3RjB-SRydL?i z!EC^F&DHCTGfPvME2;f+U8mE9C6pS|-l&YvY{hyY7lES{vtyKL&h2MI*wi~d|i z^u8u6GtHeb-y%R6=nQ1pCJkX)m*}v;;zOA}g7t5;4aYKo)`Q68Z2IfIgR^AW%Md$YU4PQX zAAJfVw%> z_YZpL3GZG=2hoW>-vOsfKeL3ruJ~sHbqREG5lDYEOR?8m<~h>8q6x%wZ6(H>k1GHX zFnt|Fcb`6u*0thYKa3s^m5t4E0FR?Ea>hHiX#~j=BiVj*@=-6myL4QMQ`ZL52qNYlB>4-2RpLIZ{Qet5>g0gF=l+D8@Pp?L`0z!}JlkSaI>)>d^XAwDU z#^x*{#*S=^o}*_UB(MWI16T%1!?gAO?GHq(Dod)e&AJs_3+z~RogL*{HI?hRUA_u4 zKo^)(qp`x4?DestX1Cy-((bIOqnfmwyPBhEjejXrNIC}1-DpUp{*(>it f52u`evP=30f`%HTZ&_~;00000NkvXXu0mjfZU|{H diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png deleted file mode 100644 index 19c8181d0369a8edf52ef2124d403f5472c8e280..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1804 zcmV+n2lM!eP)>9Y%tkPU1zRDvW!+_xJJ^ zMU*>nvyf3eBTE{F9_fJAXL(ma+h_0Hc7O_HS8dpGv>D@R>luEG)1NjzLTL>kH~FvU z+KT9L8ma@#Y~+L!popNovyAnxIxKS3w-itn`p3gueLG~{AD?SZU>X0Wtke^T(S}8&z ztJ}9YjHc&f5X81UnQ+PE;Rq~#vIZFGEoaQk64(wP>l~glvOFz4AA_KXg4tZD)dW%5 z9zQGJTTZJvglFuPu$ATWZ<}!G?vbz85&Rt$b zjyBSJfBe$;X;DU_V@JljI+s~FSsq6|4fLEq(vAk#)@x3{+~|=`b2Lx4S*%DcQGUzj z02;;(N4gHJRb%C$XUsHk)hxq`cos5s8iR5N@CvDn6L=N(Y(W$?#w;?9gt{n=AA7dt z4q)9@t9$Si)r=E07!y6bk#UuCWTOabVY6r3TF?QaVOY|mLW}iqt$Mfkd0oQpp;ToG z(dHf*q>8|(P_3w12_{19DDrk-wu5+cfEDB5am`9+7!PCoeMKuLQ=PY`UBjd()D*xf z>kVGKwI^Vu4D?>E|3pJ(@o<1l%i|4EzBRNWM4Z4IomaJi*%3Wm1ZqXq_|8)D8=Sz> zFH%EfWkw6?8x@no-iI%RT86czZY=#M?30i^K&^O0t(c^fKWFvL@{sFZH!~@S8LmX4 zLDUJniJZ5_>jX~LAQd%)G-h@`FKgjAgZDlt@y4r?Cwl*FU=Ds&1Z4PnkqOQLypScQ zQH%k<(Z~p=)7$KMhQAT?yHJ_!hXj9EO)v{4zemps6^dx8>D3FP?a~gQF^(eQ1n9#L zW{hm*OYcJ!M62oOMN3Oh3n)cfw4TF^XEOC+P*p1uk%5XkkJh^-QN9eK?VcukRSVdf z_*U=^c$=|m30aV$512Gst4=|DJz8xKT12TZSftLfpk06?UkH(hh$2Dc27LnUxTf+_ zF*-02RqD0%D`6-{%KU8IDvK|oy>IUQ%cJ#VoWMA2@(@W;TO&YgoCilXxvHC(8Cs!t zGZV;c3P96K>hA@xgC3R+VT=-$#`P-rS-$LOJkt$Sr;`zG1Q-|k* zv1gsb8sLm_Bf}S@t9+jw>S)mr$*u&O-a>7lr8F`O!!y&0F?!7wa9&G#%eM6Q>H2K7l9>Wf^@>8aG!`44r97=?mGs(kgiPfvY2_d$+HoLsM-iDaYBD|JkNRupCEuUKn*(SHv$s2fstK6}#=x0KlHL1kOB|J>!Fqlq z0*r@07G)JxeT1Hw2h8enubC;3BZaGVfh|xqz*sa@c{0lVRmEsj&rv6)^Qf3(pmnSa zwBEzpmqNV|QW?%r!O7@^c(VFs_JbTj?nz6XOvGDxz9U{Ep{Gz~1hAB>+IW`U%cEL- zVL8IMPLsE~D`48)x4##Fm`TP}sHiJ;z`7{%ma|U1s{?>e-P-d%jPQEq)5fyes@a?= zk(q_u>oT=MCA?Y7{j9bBy8zU)YMyNM=N6rmF}{~SqyC@W@D5m&xpw<^>K9EbKXTES z>%A~$r2?6IP7gbxh*s+w{dhLJoFF5^{6>%rW8Qo~d%mi0M99cu4-U~W)6JC^BBI;< z?7FV@V%s3*rd!Wf_p({6Xvff+s@kBcrR=@0`Cr3i1kefh#Jdly|nf) zTI-;5W7duModjBuz%!H-GSaOKc}P$1Swtr^$g#kl6GLz5936I9YaahVM5UoB<_6Yb zdGEYuvngrjoFa~4t*Nw?8v&}o+!~49#S)@lUBpJZtQi?Hk}Zn>n()ZG6$Kz_TP>Vy zVXc`0y9XxPY>5D$+6CM)X6uAiKtnnCT-yU<&$oXhDif+bcoi|jv%(mRoSicZq{A*e uz7LR@1>CSg6>0+!?-RH1uNl9!*8T%3!qMV0ZN~}#0000)bcKF-n7eO>&inGYaBK-D=SXJlo1dcF`r83n7k(y9rv zYe(|DeD67}>N-^!w|p8HIe{K44Q|c1oPfE}a-8L8k#4Klky^6y zw($TK!D{18@P+h@FJdvN}a}_-2tLPs^SDu#l2b(WsR|liX)*e%96*P zZMy?_x7F(&B1J9ZWDUkdkBBnva*k>gK`m_cY_BWn0NF6C$D=}v^>C|t_vA%g!f8^f zGlf{ZM+K!KFuSN#RP6*4Ax;!|C$QQhMETaxijZ-Fh&u0T1FIB0T?AT1m3-$Z`3+9s z>6fV?s%vHo>WL2Ka18OKP|vU)>&A|ki+vKR2WS%TP=MU)YXb))9|z#Y&>UQ(d+0}!cZ>K`PsWwo?J9mR?kt(irV74AfpJ&2(szoXau+} zjwfK0kBFXCtdLgdU02<-Qa{MlMEza>OZ2eg5HxGn1ysRz$n{v!j^ah?z+~x2DAoD0s62!vct>UJ9vU>j%J+-)goDVXHaP? zchw-{2_!Rpv-f_EK}8B%furby94+H4)?Pd0%JFx#+7?o6APQ&fg4WzhE5_)xTEInV zZmu0Uryko;BW1wUJ(L45x3#0VLGLy6!LweU$-Asv20Eqf4*OQxF`ZqefL$$>2J%dc zNz>yhk;jtD&W}=%*40CHy$aaN5PsrB4Be;3{y!1MA9s!fO7v`c~$Df=)ka|(cGQkpV`CP zmqM9M?Q#P7oY4u%Wc6FwkJZgoxPd#;!NkuTIREx?Rd)>_l|(;x$jprI+MB#ES(Y@C zn@JG?{ouv9M(y0rqyol3a zi6NFh*>O>RQvgGB=yVXZSUR*?X~j!FNWR7?22-P=>x4%xS(mHB_}={AUk#HRVG1)j z4|NS_@lVBWhe8Im3dog<5IK+gjUcT^#1B}{PgJ@Zm9ouL=IA^_7*vpOO9=soMinL@OSOUV@N^WXa2Ll3k_ z7`95)b9UAMNYCh5Mkj0U9g#_&PsPbs_Av4CA4GH-%5sPm8Twh&hN5SyDT!(Cm^G}H znwi^Wt1(6#0lE-*c&DLO^zg=KbYY?&y8XK%r?u9$2w*LqdAFkg^!A9>s-8)dE;L7C z>0&uTW)F<+Mh^x+m z6XE5vfC?8dp_!|w6VMymc?(&5it#5-M8N+5*h|0~>x0|N00000NkvXXu0mjf5$bp8 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png deleted file mode 100644 index 929a207ef58a63e946d60e4413e5ad96a75d7c0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1816 zcmV+z2j}>SP)fcfU1=V^L^okrq$q%O>R9{YPT z16cv@xLKA`JtfONOn*xTYkYR@E+FL}hhqR$fmd!A8CykpXgo6?WAt|`-$E+~_|#3V zLWlRb8>#_hHzQr}4ruFWgilk}UnPf{`#%nn14J<&{EnNU8QpbV&H>04*8V+c=`X^PI)Dc$9Ae?JuZ*{v+nW!~bLWhkUH$1HBZFng z78u#L8o;aoBpg%G^A5lZGYm*!ARZFPr$5YMzFy5xkMWOeKs(md&z2nSxaXv*csen)CHl z6~8R&XUD-TplSq+HSY8X9++a%DtHQuDwHA1GagJ41ufw9Lw!F1l>$o!)@5KSep#~D zUI>!u+i07AXT8DQuoQ&eR}H&DQX?{i3PBo4id4xeAz?SNO4O>99Xx%CDJnrPb%#ID>}>*x=e)Jj(o&DdH)0SXcaiTgYx>$o*Ya@V0pjMUO7(-t^v$KS(QPp zMxKGRQct#dT(z&hR2(4Ca?Y51-BVb%3B8Bqct4e43u2NKYxY3Ln8S7JaicBio+1rFX2|5H#X!h251rZjV z#T#wzm9XSKujz@EetE2z^#b~_J?aurViZW`j3P2# zo3rvPwU5f!q|riW{SC zJQHK1huA>~E%`hn%$}haMzm~6ljGMPhoXa*M6BClwc=6~Rbtr9Ydiy!=`JJxID7U| zNIB5Bhb5$39KG&9mqBF|s*TAOwd8y2Qq5k%^Q@JNXBp#LA&5zrG>c`8B>Rj(CPY~- z907&PsB*N-$}5OfA@}tb0xSY1!+{mU<#R2pXyJ!hN5c+g9d*SsEQQYAAWFJ3MXFZ_ zkVb&gG^;*<+E=DptFYR5SX2xwg4c&ZQkl{vc@;v`-kmyn$gaa0roCFh>c@7GttJVkicB*#=efCbNBtTr^>TZgUiY3pip&s-~VPLcDZ(!g#7 zA%m#QA=|t()N0G_Gh#e!CD7tkU7^W>>jApJ+!~1$vP8p2#x>9VnM$BHw(A^F_*~`z z(BhG2D~O+!wjy~t&ZO#XSL4@b0p7-~=csDvrGeJmS(jG9oyxzfQu+f7>@m9|qN_##0000 Date: Wed, 15 Feb 2023 19:56:25 +0300 Subject: [PATCH 02/27] [FL-3098] Up toolchain to version 20 (#2397) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Up toolchain to 20 * Python reformat, add version info into fbtenv Co-authored-by: あく --- scripts/flipper/assets/dolphin.py | 2 -- scripts/flipper/utils/cdc.py | 1 + scripts/flipper/utils/templite.py | 2 ++ scripts/toolchain/fbtenv.cmd | 5 ++++- scripts/toolchain/fbtenv.sh | 11 ++++++++++- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/scripts/flipper/assets/dolphin.py b/scripts/flipper/assets/dolphin.py index 096d3162..cbd1320b 100644 --- a/scripts/flipper/assets/dolphin.py +++ b/scripts/flipper/assets/dolphin.py @@ -22,7 +22,6 @@ def _convert_image(source_filename: str): class DolphinBubbleAnimation: - FILE_TYPE = "Flipper Animation" FILE_VERSION = 1 @@ -243,7 +242,6 @@ class DolphinBubbleAnimation: class DolphinManifest: - FILE_TYPE = "Flipper Animation Manifest" FILE_VERSION = 1 diff --git a/scripts/flipper/utils/cdc.py b/scripts/flipper/utils/cdc.py index 7047db2a..7c735167 100644 --- a/scripts/flipper/utils/cdc.py +++ b/scripts/flipper/utils/cdc.py @@ -1,5 +1,6 @@ import serial.tools.list_ports as list_ports + # Returns a valid port or None, if it cannot be found def resolve_port(logger, portname: str = "auto"): if portname != "auto": diff --git a/scripts/flipper/utils/templite.py b/scripts/flipper/utils/templite.py index 8af57d7f..2d958bd7 100644 --- a/scripts/flipper/utils/templite.py +++ b/scripts/flipper/utils/templite.py @@ -173,12 +173,14 @@ class Templite: """Renders the template according to the given namespace.""" stack = [] namespace["__file__"] = self.file + # add write method def write(*args): for value in args: stack.append(str(value)) namespace["write"] = write + # add include method def include(file): if not os.path.isabs(file): diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 9fbd8fd9..5dff4b25 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,7 +13,7 @@ if not ["%FBT_NOENV%"] == [""] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=19" +set "FLIPPER_TOOLCHAIN_VERSION=20" if ["%FBT_TOOLCHAIN_ROOT%"] == [""] ( set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows" @@ -31,9 +31,12 @@ if not exist "%FBT_TOOLCHAIN_VERSION_FILE%" ( set /p REAL_TOOLCHAIN_VERSION=<"%FBT_TOOLCHAIN_VERSION_FILE%" if not "%REAL_TOOLCHAIN_VERSION%" == "%FLIPPER_TOOLCHAIN_VERSION%" ( + echo FBT: starting toolchain upgrade process.. powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" %flipper_toolchain_version% "%FBT_TOOLCHAIN_ROOT%" + set /p REAL_TOOLCHAIN_VERSION=<"%FBT_TOOLCHAIN_VERSION_FILE%" ) +echo FBT: using toolchain version %REAL_TOOLCHAIN_VERSION% set "HOME=%USERPROFILE%" set "PYTHONHOME=%FBT_TOOLCHAIN_ROOT%\python" set "PYTHONPATH=" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index dd5484aa..bedb3450 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -5,7 +5,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"19"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"20"}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; fbtenv_show_usage() @@ -35,6 +35,7 @@ fbtenv_restore_env() PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/bin://g")"; PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/protobuf\/bin://g")"; PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/openocd\/bin://g")"; + PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/openssl\/bin://g")"; if [ -n "${PS1:-""}" ]; then PS1="$(echo "$PS1" | sed 's/\[fbt\]//g')"; elif [ -n "${PROMPT:-""}" ]; then @@ -248,6 +249,7 @@ fbtenv_check_download_toolchain() elif [ ! -f "$TOOLCHAIN_ARCH_DIR/VERSION" ]; then fbtenv_download_toolchain || return 1; elif [ "$(cat "$TOOLCHAIN_ARCH_DIR/VERSION")" -ne "$FBT_TOOLCHAIN_VERSION" ]; then + echo "FBT: starting toolchain upgrade process.." fbtenv_download_toolchain || return 1; fi return 0; @@ -269,6 +271,11 @@ fbtenv_download_toolchain() return 0; } +fbtenv_print_version() +{ + echo "FBT: using toolchain version $(cat "$TOOLCHAIN_ARCH_DIR/VERSION")"; +} + fbtenv_main() { fbtenv_check_sourced || return 1; @@ -281,10 +288,12 @@ fbtenv_main() fbtenv_check_script_path || return 1; fbtenv_check_download_toolchain || return 1; fbtenv_set_shell_prompt; + fbtenv_print_version; PATH="$TOOLCHAIN_ARCH_DIR/python/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/protobuf/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/openocd/bin:$PATH"; + PATH="$TOOLCHAIN_ARCH_DIR/openssl/bin:$PATH"; SAVED_PYTHONNOUSERSITE="${PYTHONNOUSERSITE:-""}"; SAVED_PYTHONPATH="${PYTHONPATH:-""}"; From 1e983612997af682e935ebad3bf0a7e9eb5820dc Mon Sep 17 00:00:00 2001 From: Ari Bytyqi <101530102+Z3BRO@users.noreply.github.com> Date: Fri, 17 Feb 2023 02:17:57 -0600 Subject: [PATCH 03/27] Fixed first start animation typo (#2384) * fixed first start animation typo * updated pixels. (bottom left corner was off by two pixels) Co-authored-by: jbohack --- assets/slideshow/first_start/frame_02.png | Bin 562 -> 656 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/slideshow/first_start/frame_02.png b/assets/slideshow/first_start/frame_02.png index dc1080abaab43b40f9214c53e444839f9f3c9ffb..adff6af6672bc6f13ad0be1c0f7d19db4955f589 100644 GIT binary patch delta 643 zcmV-}0(||l1ds)g7=H)@0002x2#-ww0004VQb$4nuFf3k00006P)t-s|Ns900033O z(|!N|0v<_3K~!jg?Usvjgdhw>$^ZX!J1dVoZW2)3PG`upu7Yw8m|Q94#EBCpPMkRL zUxPM8HfpH9WfE3Bx_$wG#06fF2*6vzLUVv@bXUo3_W+915q}RJtu^i%0G}Cu{yD(a zRwp*joUy}v!<}7?F1h+w6Ng%xIJiS6xc~`VfZ8jxue5LhxB!hol-Lsh@c;mbrSKA{ z96$gn2V4MN3sC5u_w+6loi|c zh+yjh?R@sAKbpSdNL~|U0K;)Qz`_c6*1rHCx0^Hi+SzXacsa0I1@?BEMa~L53h`|> z9oqcmT{{87?DtwXI^m@LYXGzQCJDC01Ar|Azr*|h{u~6R)=RWQ0000EWmrjOO-%qQ d00008000000002eQcz2XilNogTwuu7~B5w5vn;e3F20e`W(DTKpH#XNu(EBFSM zQ(E~93pSQ1E!4gLH#-~GJi|?z>@VMb^XHoh{x4aotd<4hvLLj=4+hxc2hagBpg=5O z(fjT}Swz-D!g9hg*%AI8B|kPF2<_@ceLDKQpRe<4mreQ}lKuRN&{qkP8nnrfGC@~* zjM(FzkTJohH-Dd_mBMBc4INbjdvHs%qefDOLb3>%K#C;Y319~SjEBBL34L()FxmKB zFf_)kh}%x{_37>IIU$;L72za*3NR?r_(dvo4bF9*p?}@oPc}Sk1HBn{v3`yO<@gq~ zLJgeIAJ-f*hr!cmKnm3mlR8S()jG$HFIhfWY-x>MNGyj{1sW)uR=FJ*r(a)QP}+KndE%yd>|C%9KqbsgZ!@ n!-X*daUS@46g7nZOX>arU#^{;{__u)00000NkvXXu0mjfq&fEl From 32b74b968e8bb5730d95d0b3fd18b9cf90dca5b6 Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Fri, 17 Feb 2023 12:16:53 +0300 Subject: [PATCH 04/27] Fix openssl cert path in fbtenv (#2408) * Temp fix openssl cert path * Moving fix from CI to fbtenv --- scripts/toolchain/fbtenv.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index bedb3450..2d4d1724 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -289,6 +289,9 @@ fbtenv_main() fbtenv_check_download_toolchain || return 1; fbtenv_set_shell_prompt; fbtenv_print_version; + if [ "$SYS_TYPE" = "Linux" ]; then + SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt + fi PATH="$TOOLCHAIN_ARCH_DIR/python/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/protobuf/bin:$PATH"; From 487d03eca4093d6e699c07f192531dd8689b0bf7 Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Fri, 17 Feb 2023 12:59:08 +0300 Subject: [PATCH 05/27] Fix openssl path again (#2409) * Temp fix openssl cert path * Moving fix from CI to fbtenv * Fix openssl again --- scripts/toolchain/fbtenv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index 2d4d1724..8933bc48 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -290,7 +290,7 @@ fbtenv_main() fbtenv_set_shell_prompt; fbtenv_print_version; if [ "$SYS_TYPE" = "Linux" ]; then - SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt + export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt fi PATH="$TOOLCHAIN_ARCH_DIR/python/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/bin:$PATH"; From 009c9b1b71bd8e7bd68f5ad9b955ff924dd5a913 Mon Sep 17 00:00:00 2001 From: Mathie <62908057+MathieDev@users.noreply.github.com> Date: Fri, 17 Feb 2023 06:43:06 -0500 Subject: [PATCH 06/27] Update nfc_cli.c (#2402) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modified the f to a capital F, looks cleaner. Co-authored-by: あく --- applications/main/nfc/nfc_cli.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index a6475ca6..23335e29 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -32,7 +32,7 @@ static void nfc_cli_detect(Cli* cli, FuriString* args) { while(!cmd_exit) { cmd_exit |= cli_cmd_interrupt_received(cli); if(furi_hal_nfc_detect(&dev_data, 400)) { - printf("found: %s ", nfc_get_dev_type(dev_data.type)); + printf("Found: %s ", nfc_get_dev_type(dev_data.type)); printf("UID length: %d, UID:", dev_data.uid_len); for(size_t i = 0; i < dev_data.uid_len; i++) { printf("%02X", dev_data.uid[i]); From 335f8b9aff670b033072e78ca477d4057ae54284 Mon Sep 17 00:00:00 2001 From: hedger Date: Fri, 17 Feb 2023 16:22:08 +0400 Subject: [PATCH 07/27] fbt: FBT_QUIET option; docs on environment (#2403) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fbt: added FBT_QUIET to suppress toolchain version output; docs: added info on fbt environment * docs: typo fixes * fbt: fix for FBT_QUIET handling on *nix * Add FBT_VERBOSE flag * Add export * Fix export * docs: updates for FBT_VERBOSE Co-authored-by: DrunkBatya Co-authored-by: あく --- .vscode/example/launch.json | 5 +++-- documentation/fbt.md | 28 ++++++++++++++++++++++------ fbt | 11 ++++++++--- fbt.cmd | 7 ++++++- scripts/toolchain/fbtenv.cmd | 5 ++++- scripts/toolchain/fbtenv.sh | 28 ++++++++++++++++------------ 6 files changed, 59 insertions(+), 25 deletions(-) diff --git a/.vscode/example/launch.json b/.vscode/example/launch.json index 5c46d397..f7a9f826 100644 --- a/.vscode/example/launch.json +++ b/.vscode/example/launch.json @@ -11,9 +11,10 @@ "args": { "useSingleResult": true, "env": { - "PATH": "${workspaceFolder};${env:PATH}" + "PATH": "${workspaceFolder};${env:PATH}", + "FBT_QUIET": 1 }, - "command": "./fbt get_blackmagic", + "command": "fbt get_blackmagic", "description": "Get Blackmagic device", } } diff --git a/documentation/fbt.md b/documentation/fbt.md index 5166d0ab..65c3ee68 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -3,17 +3,27 @@ FBT is the entry point for firmware-related commands and utilities. It is invoked by `./fbt` in the firmware project root directory. Internally, it is a wrapper around [scons](https://scons.org/) build system. -## Requirements +## Environment -Install Python packages required by assets build scripts: `pip3 install -r scripts/requirements.txt` +To use `fbt`, you only need `git` installed in your system. -## NB +`fbt` by default downloads and unpacks a pre-built toolchain, and then modifies environment variables for itself to use it. It does not contaminate your global system's path with the toolchain. + > However, if you wish to use tools supplied with the toolchain outside `fbt`, you can open an *fbt shell*, with properly configured environment. + > - On Windows, simply run `scripts/toochain/fbtenv.cmd`. + > - On Linux & MacOS, run `source scripts/toochain/fbtenv.sh` in a new shell. + + If your system is not supported by pre-built toolchain variants or you want to use custom versions of dependencies, you can `set FBT_NOENV=1`. `fbt` will skip toolchain & environment configuration and will expect all tools to be available on your system's `PATH`. *(this option is not available on Windows)* + + If `FBT_TOOLCHAIN_PATH` variable is set, `fbt` will use that directory to unpack toolchain into. By default, it downloads toolchain into `toolchain` subdirectory repo's root. -- `fbt` constructs all referenced environments and their targets' dependency trees on startup. So, to keep startup time as low as possible, we're hiding the construction of certain targets behind command-line options. -- `fbt` always performs `git submodule update --init` on start, unless you set `FBT_NO_SYNC=1` in the environment: +If you want to enable extra debug output for `fbt` and toolchain management scripts, you can `set FBT_VERBOSE=1`. + +`fbt` always performs `git submodule update --init` on start, unless you set `FBT_NO_SYNC=1` in the environment: - On Windows, it's `set "FBT_NO_SYNC=1"` in the shell you're running `fbt` from - On \*nix, it's `$ FBT_NO_SYNC=1 ./fbt ...` -- `fbt` builds updater & firmware in separate subdirectories in `build`, and their names depend on optimization settings (`COMPACT` & `DEBUG` options). However, for ease of integration with IDEs, the latest built variant's directory is always linked as `built/latest`. Additionally, `compile_commands.json` is generated in that folder (used for code completion support in IDE). + + > There are more variables controlling basic `fbt` behavior. See `fbt` & `fbtenv` scripts' sources for details. + ## Invoking FBT @@ -23,6 +33,12 @@ To build with FBT, call it and specify configuration options & targets to build. To run cleanup (think of `make clean`) for specified targets, add the `-c` option. +## Build directories + +`fbt` builds updater & firmware in separate subdirectories in `build`, and their names depend on optimization settings (`COMPACT` & `DEBUG` options). However, for ease of integration with IDEs, the latest built variant's directory is always linked as `built/latest`. Additionally, `compile_commands.json` is generated in that folder (it is used for code completion support in IDEs). + +`build/latest` symlink & compilation database are only updated upon *firmware build targets* - that is, when you're re-building the firmware itself. Running other tasks, like firmware flashing or building update bundles *for a different debug/release configuration or hardware target*, does not update `built/latest` dir to point to that configuration. + ## VSCode integration `fbt` includes basic development environment configuration for VS Code. Run `./fbt vscode_dist` to deploy it. That will copy the initial environment configuration to the `.vscode` folder. After that, you can use that configuration by starting VS Code and choosing the firmware root folder in the "File > Open Folder" menu. diff --git a/fbt b/fbt index e576f37a..f80e802b 100755 --- a/fbt +++ b/fbt @@ -6,16 +6,21 @@ set -eu; # private variables SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd -P)"; -SCONS_DEFAULT_FLAGS="-Q --warn=target-not-built"; -SCONS_EP="python3 -m SCons" +SCONS_DEFAULT_FLAGS="--warn=target-not-built"; +SCONS_EP="python3 -m SCons"; # public variables FBT_NOENV="${FBT_NOENV:-""}"; FBT_NO_SYNC="${FBT_NO_SYNC:-""}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; +FBT_VERBOSE="${FBT_VERBOSE:-""}"; if [ -z "$FBT_NOENV" ]; then - . "$SCRIPT_PATH/scripts/toolchain/fbtenv.sh"; + FBT_VERBOSE="$FBT_VERBOSE" . "$SCRIPT_PATH/scripts/toolchain/fbtenv.sh"; +fi + +if [ -z "$FBT_VERBOSE" ]; then + SCONS_DEFAULT_FLAGS="$SCONS_DEFAULT_FLAGS -Q"; fi if [ -z "$FBT_NO_SYNC" ]; then diff --git a/fbt.cmd b/fbt.cmd index 197f2359..92c73486 100644 --- a/fbt.cmd +++ b/fbt.cmd @@ -12,5 +12,10 @@ if [%FBT_NO_SYNC%] == [] ( ) ) -set "SCONS_DEFAULT_FLAGS=-Q --warn=target-not-built" +set "SCONS_DEFAULT_FLAGS=--warn=target-not-built" + +if not defined FBT_VERBOSE ( + set "SCONS_DEFAULT_FLAGS=%SCONS_DEFAULT_FLAGS% -Q" +) + %SCONS_EP% %SCONS_DEFAULT_FLAGS% %* diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 5dff4b25..3f69de00 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -36,7 +36,10 @@ if not "%REAL_TOOLCHAIN_VERSION%" == "%FLIPPER_TOOLCHAIN_VERSION%" ( set /p REAL_TOOLCHAIN_VERSION=<"%FBT_TOOLCHAIN_VERSION_FILE%" ) -echo FBT: using toolchain version %REAL_TOOLCHAIN_VERSION% +if defined FBT_VERBOSE ( + echo FBT: using toolchain version %REAL_TOOLCHAIN_VERSION% +) + set "HOME=%USERPROFILE%" set "PYTHONHOME=%FBT_TOOLCHAIN_ROOT%\python" set "PYTHONPATH=" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index 8933bc48..ddd27c37 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -7,13 +7,14 @@ DEFAULT_SCRIPT_PATH="$(pwd -P)"; SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}"; FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"20"}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; +FBT_VERBOSE="${FBT_VERBOSE:-""}"; fbtenv_show_usage() { echo "Running this script manually is wrong, please source it"; echo "Example:"; printf "\tsource scripts/toolchain/fbtenv.sh\n"; - echo "To restore your environment source fbtenv.sh with '--restore'." + echo "To restore your environment, source fbtenv.sh with '--restore'." echo "Example:"; printf "\tsource scripts/toolchain/fbtenv.sh --restore\n"; } @@ -42,9 +43,9 @@ fbtenv_restore_env() PROMPT="$(echo "$PROMPT" | sed 's/\[fbt\]//g')"; fi - PYTHONNOUSERSITE="$SAVED_PYTHONNOUSERSITE"; - PYTHONPATH="$SAVED_PYTHONPATH"; - PYTHONHOME="$SAVED_PYTHONHOME"; + export PYTHONNOUSERSITE="$SAVED_PYTHONNOUSERSITE"; + export PYTHONPATH="$SAVED_PYTHONPATH"; + export PYTHONHOME="$SAVED_PYTHONHOME"; unset SAVED_PYTHONNOUSERSITE; unset SAVED_PYTHONPATH; @@ -122,7 +123,7 @@ fbtenv_get_kernel_type() TOOLCHAIN_ARCH_DIR="$FBT_TOOLCHAIN_PATH/toolchain/x86_64-linux"; TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-linux-flipper-$FBT_TOOLCHAIN_VERSION.tar.gz"; elif echo "$SYS_TYPE" | grep -q "MINGW"; then - echo "In MinGW shell use \"[u]fbt.cmd\" instead of \"[u]fbt\""; + echo "In MinGW shell, use \"[u]fbt.cmd\" instead of \"[u]fbt\""; return 1; else echo "Your system configuration is not supported. Sorry.. Please report us your configuration."; @@ -273,7 +274,9 @@ fbtenv_download_toolchain() fbtenv_print_version() { - echo "FBT: using toolchain version $(cat "$TOOLCHAIN_ARCH_DIR/VERSION")"; + if [ -n "$FBT_VERBOSE" ]; then + echo "FBT: using toolchain version $(cat "$TOOLCHAIN_ARCH_DIR/VERSION")"; + fi } fbtenv_main() @@ -297,14 +300,15 @@ fbtenv_main() PATH="$TOOLCHAIN_ARCH_DIR/protobuf/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/openocd/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/openssl/bin:$PATH"; + export PATH; - SAVED_PYTHONNOUSERSITE="${PYTHONNOUSERSITE:-""}"; - SAVED_PYTHONPATH="${PYTHONPATH:-""}"; - SAVED_PYTHONHOME="${PYTHONHOME:-""}"; + export SAVED_PYTHONNOUSERSITE="${PYTHONNOUSERSITE:-""}"; + export SAVED_PYTHONPATH="${PYTHONPATH:-""}"; + export SAVED_PYTHONHOME="${PYTHONHOME:-""}"; - PYTHONNOUSERSITE=1; - PYTHONPATH=; - PYTHONHOME=; + export PYTHONNOUSERSITE=1; + export PYTHONPATH=; + export PYTHONHOME=; } fbtenv_main "${1:-""}"; From c7fbc8323b93337c94dc1e69a3ce755224e2621c Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Fri, 17 Feb 2023 19:06:48 +0300 Subject: [PATCH 08/27] Toolchain 20 rollback (#2410) * Toolchain rollback * Remove extra code --- scripts/toolchain/fbtenv.cmd | 2 +- scripts/toolchain/fbtenv.sh | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 3f69de00..dce5f37c 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,7 +13,7 @@ if not ["%FBT_NOENV%"] == [""] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=20" +set "FLIPPER_TOOLCHAIN_VERSION=19" if ["%FBT_TOOLCHAIN_ROOT%"] == [""] ( set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index ddd27c37..c68673b7 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -5,7 +5,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"20"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"19"}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; FBT_VERBOSE="${FBT_VERBOSE:-""}"; @@ -292,9 +292,6 @@ fbtenv_main() fbtenv_check_download_toolchain || return 1; fbtenv_set_shell_prompt; fbtenv_print_version; - if [ "$SYS_TYPE" = "Linux" ]; then - export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt - fi PATH="$TOOLCHAIN_ARCH_DIR/python/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/protobuf/bin:$PATH"; From 78afaab7e8b7beea2b3789c175d613bd0089ce4d Mon Sep 17 00:00:00 2001 From: ComputerCarsten <50232606+ComputerCarsten@users.noreply.github.com> Date: Mon, 20 Feb 2023 10:24:51 +0100 Subject: [PATCH 09/27] IR Universal Audio Remote: Add Grundig CMS 5000 (#2414) Add Grundig CMS 5000 to Infrared Universal Audio Remote. The 'Play' button doubles as 'Pause' button. The 'Pause' button is unused. Issue: 'Prev' button rewinds to start of title, to skip to previous title two consecutive button presses in a short time are required, however the timing is not satisfied. --- assets/resources/infrared/assets/audio.ir | 43 +++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index bcf035df..825d1bc3 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -285,3 +285,46 @@ type: parsed protocol: NECext address: 10 E7 00 00 command: 41 BE 00 00 +# +# Model: Grundig CMS 5000 +name: Power +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 10 EF 00 00 +# Also Pause +name: Play +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 02 FD 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 0D F2 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 17 E8 00 00 +# +name: Next +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 13 EC 00 00 +# +name: Prev +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 11 EE 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 0C F3 00 00 From 3de6ae07b7bfdc6d75f82d4c688eac00db313318 Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Mon, 20 Feb 2023 15:21:29 +0300 Subject: [PATCH 10/27] [FL-2974] Up toolchain version to 21 (#2416) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- scripts/toolchain/fbtenv.cmd | 2 +- scripts/toolchain/fbtenv.sh | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index dce5f37c..8587f6d0 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,7 +13,7 @@ if not ["%FBT_NOENV%"] == [""] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=19" +set "FLIPPER_TOOLCHAIN_VERSION=21" if ["%FBT_TOOLCHAIN_ROOT%"] == [""] ( set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index c68673b7..8f05c23c 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -5,7 +5,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"19"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"21"}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; FBT_VERBOSE="${FBT_VERBOSE:-""}"; @@ -43,10 +43,19 @@ fbtenv_restore_env() PROMPT="$(echo "$PROMPT" | sed 's/\[fbt\]//g')"; fi + if [ -n "$SAVED_SSL_CERT_FILE" ]; then + export SSL_CERT_FILE="$SAVED_SSL_CERT_FILE"; + export REQUESTS_CA_BUNDLE="$SAVED_REQUESTS_CA_BUNDLE"; + else + unset SSL_CERT_FILE; + unset REQUESTS_CA_BUNDLE; + fi export PYTHONNOUSERSITE="$SAVED_PYTHONNOUSERSITE"; export PYTHONPATH="$SAVED_PYTHONPATH"; export PYTHONHOME="$SAVED_PYTHONHOME"; + unset SAVED_SSL_CERT_FILE; + unset SAVED_REQUESTS_CA_BUNDLE; unset SAVED_PYTHONNOUSERSITE; unset SAVED_PYTHONPATH; unset SAVED_PYTHONHOME; @@ -299,10 +308,14 @@ fbtenv_main() PATH="$TOOLCHAIN_ARCH_DIR/openssl/bin:$PATH"; export PATH; + export SAVED_SSL_CERT_FILE="${SSL_CERT_FILE:-""}"; + export SAVED_REQUESTS_CA_BUNDLE="${REQUESTS_CA_BUNDLE:-""}"; export SAVED_PYTHONNOUSERSITE="${PYTHONNOUSERSITE:-""}"; export SAVED_PYTHONPATH="${PYTHONPATH:-""}"; export SAVED_PYTHONHOME="${PYTHONHOME:-""}"; + export SSL_CERT_FILE="$TOOLCHAIN_ARCH_DIR/python/lib/python3.11/site-packages/certifi/cacert.pem"; + export REQUESTS_CA_BUNDLE="$SSL_CERT_FILE"; export PYTHONNOUSERSITE=1; export PYTHONPATH=; export PYTHONHOME=; From 738e0df4f460733ec9ae46c3d90a56372fb81249 Mon Sep 17 00:00:00 2001 From: Igor Danilov <59930161+polarikus@users.noreply.github.com> Date: Mon, 20 Feb 2023 15:52:15 +0300 Subject: [PATCH 11/27] Delete rwfiletest.bin on exit SDcard benchmark (#2415) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update storage_settings_scene_benchmark.c: delete rwfiletest.bin on exit SDcard brencmark * Settings: cleanup SD Benchmark temp file only if test successful Co-authored-by: あく --- .../storage_settings/scenes/storage_settings_scene_benchmark.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c index 71a3df78..8359c00b 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c @@ -103,6 +103,9 @@ static void storage_settings_scene_benchmark(StorageSettings* app) { break; furi_string_cat_printf(app->text_string, "R %luK", bench_r_speed[i]); + + storage_common_remove(app->fs_api, BENCH_FILE); + dialog_ex_set_text( dialog_ex, furi_string_get_cstr(app->text_string), 0, 32, AlignLeft, AlignCenter); } From b15c4afea11cf02068216057f6fa1f15ded7fc2d Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Mon, 20 Feb 2023 16:44:03 +0200 Subject: [PATCH 12/27] [FL-3122] Re-init NFC when starting the worker (#2399) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Re-init NFC when starting the worker * FuriHal: cleanup nfc init/deinit sequence * FuriHal: a little bit more defensive nfc init Co-authored-by: あく --- firmware/targets/f7/api_symbols.csv | 3 ++- firmware/targets/f7/furi_hal/furi_hal_nfc.c | 20 ++++++++++++++++++-- firmware/targets/f7/furi_hal/furi_hal_nfc.h | 4 ++++ lib/nfc/nfc_worker.c | 2 ++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 33c443ae..e320fc92 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,14.0,, +Version,+,14.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1171,6 +1171,7 @@ Function,+,furi_hal_mpu_protect_disable,void,FuriHalMpuRegion Function,+,furi_hal_mpu_protect_no_access,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" Function,+,furi_hal_mpu_protect_read_only,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" Function,+,furi_hal_nfc_activate_nfca,_Bool,"uint32_t, uint32_t*" +Function,-,furi_hal_nfc_deinit,void, Function,+,furi_hal_nfc_detect,_Bool,"FuriHalNfcDevData*, uint32_t" Function,+,furi_hal_nfc_emulate_nfca,_Bool,"uint8_t*, uint8_t, uint8_t*, uint8_t, FuriHalNfcEmulateCallback, void*, uint32_t" Function,+,furi_hal_nfc_exit_sleep,void, diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index ce81fd05..8910d887 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -24,13 +24,29 @@ FuriEventFlag* event = NULL; #define FURI_HAL_NFC_UID_INCOMPLETE (0x04) void furi_hal_nfc_init() { + furi_assert(!event); + event = furi_event_flag_alloc(); + ReturnCode ret = rfalNfcInitialize(); if(ret == ERR_NONE) { furi_hal_nfc_start_sleep(); - event = furi_event_flag_alloc(); FURI_LOG_I(TAG, "Init OK"); } else { - FURI_LOG_W(TAG, "Initialization failed, RFAL returned: %d", ret); + FURI_LOG_W(TAG, "Init Failed, RFAL returned: %d", ret); + } +} + +void furi_hal_nfc_deinit() { + ReturnCode ret = rfalDeinitialize(); + if(ret == ERR_NONE) { + FURI_LOG_I(TAG, "Deinit OK"); + } else { + FURI_LOG_W(TAG, "Deinit Failed, RFAL returned: %d", ret); + } + + if(event) { + furi_event_flag_free(event); + event = NULL; } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.h b/firmware/targets/f7/furi_hal/furi_hal_nfc.h index d3f6de60..dc3f873f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.h +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.h @@ -101,6 +101,10 @@ typedef struct { */ void furi_hal_nfc_init(); +/** Deinit nfc + */ +void furi_hal_nfc_deinit(); + /** Check if nfc worker is busy * * @return true if busy diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index a652e088..54bdbb24 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -56,6 +56,8 @@ void nfc_worker_start( while(furi_hal_nfc_is_busy()) { furi_delay_ms(10); } + furi_hal_nfc_deinit(); + furi_hal_nfc_init(); nfc_worker->callback = callback; nfc_worker->context = context; From 0a3ff7f85ae653629957dfdd970a6fd3310ad14e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 20 Feb 2023 18:19:53 +0300 Subject: [PATCH 13/27] Show RSSI in Weather Station app (#2395) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Show RSSI in weather station app: copy changes from main SubGHz app * WeatherStation: remove dead code * WeatherStation: sync naming schema with current code. Co-authored-by: あく --- .../scenes/weather_station_receiver.c | 4 +++ .../views/weather_station_receiver.c | 32 +++++++++++++++++-- .../views/weather_station_receiver.h | 2 ++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/applications/plugins/weather_station/scenes/weather_station_receiver.c b/applications/plugins/weather_station/scenes/weather_station_receiver.c index 670c8c38..e7681043 100644 --- a/applications/plugins/weather_station/scenes/weather_station_receiver.c +++ b/applications/plugins/weather_station/scenes/weather_station_receiver.c @@ -195,6 +195,10 @@ bool weather_station_scene_receiver_on_event(void* context, SceneManagerEvent ev ws_hopper_update(app); weather_station_scene_receiver_update_statusbar(app); } + // Get current RSSI + float rssi = furi_hal_subghz_get_rssi(); + ws_view_receiver_set_rssi(app->ws_receiver, rssi); + if(app->txrx->txrx_state == WSTxRxStateRx) { notification_message(app->notifications, &sequence_blink_cyan_10); } diff --git a/applications/plugins/weather_station/views/weather_station_receiver.c b/applications/plugins/weather_station/views/weather_station_receiver.c index de5d7b1a..f8e2e328 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver.c +++ b/applications/plugins/weather_station/views/weather_station_receiver.c @@ -12,6 +12,7 @@ #define MENU_ITEMS 4u #define UNLOCK_CNT 3 +#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f typedef struct { FuriString* item_str; uint8_t type; @@ -59,8 +60,24 @@ typedef struct { uint16_t list_offset; uint16_t history_item; WSReceiverBarShow bar_show; + uint8_t u_rssi; } WSReceiverModel; +void ws_view_receiver_set_rssi(WSReceiver* instance, float rssi) { + furi_assert(instance); + with_view_model( + instance->view, + WSReceiverModel * model, + { + if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { + model->u_rssi = 0; + } else { + model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); + } + }, + true); +} + void ws_view_receiver_set_lock(WSReceiver* ws_receiver, WSLock lock) { furi_assert(ws_receiver); ws_receiver->lock_count = 0; @@ -164,13 +181,22 @@ static void ws_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrol canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11); } +static void ws_view_rssi_draw(Canvas* canvas, WSReceiverModel* model) { + for(uint8_t i = 1; i < model->u_rssi; i++) { + if(i % 5) { + canvas_draw_dot(canvas, 46 + i, 50); + canvas_draw_dot(canvas, 47 + i, 51); + canvas_draw_dot(canvas, 46 + i, 52); + } + } +} + void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) { canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); elements_button_left(canvas, "Config"); - canvas_draw_line(canvas, 46, 51, 125, 51); bool scrollbar = model->history_item > 4; FuriString* str_buff; @@ -203,10 +229,12 @@ void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) { canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 63, 46, "Scanning..."); - canvas_draw_line(canvas, 46, 51, 125, 51); canvas_set_font(canvas, FontSecondary); } + // Draw RSSI + ws_view_rssi_draw(canvas, model); + switch(model->bar_show) { case WSReceiverBarShowLock: canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8); diff --git a/applications/plugins/weather_station/views/weather_station_receiver.h b/applications/plugins/weather_station/views/weather_station_receiver.h index 30c6516d..f81aa1f5 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver.h +++ b/applications/plugins/weather_station/views/weather_station_receiver.h @@ -8,6 +8,8 @@ typedef struct WSReceiver WSReceiver; typedef void (*WSReceiverCallback)(WSCustomEvent event, void* context); +void ws_view_receiver_set_rssi(WSReceiver* instance, float rssi); + void ws_view_receiver_set_lock(WSReceiver* ws_receiver, WSLock keyboard); void ws_view_receiver_set_callback( From 663eb6cd6d4a14d00caf93d402b71eadb9a4d63f Mon Sep 17 00:00:00 2001 From: Liam Hays Date: Tue, 21 Feb 2023 00:15:48 -0700 Subject: [PATCH 14/27] Display Mifare Classic data in NFC app (#2389) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add data display for Mifare Classic cards. * Clean up log statements and data display. Co-authored-by: あく --- .../main/nfc/scenes/nfc_scene_config.h | 1 + .../nfc/scenes/nfc_scene_mf_classic_data.c | 106 ++++++++++++++++++ .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 6 +- .../main/nfc/scenes/nfc_scene_saved_menu.c | 2 + 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_data.c diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index f81fe178..a9da07df 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -29,6 +29,7 @@ ADD_SCENE(nfc, mf_desfire_menu, MfDesfireMenu) ADD_SCENE(nfc, mf_desfire_data, MfDesfireData) ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp) ADD_SCENE(nfc, mf_classic_read_success, MfClassicReadSuccess) +ADD_SCENE(nfc, mf_classic_data, MfClassicData) ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu) ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate) ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_data.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_data.c new file mode 100644 index 00000000..dcb02d36 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_data.c @@ -0,0 +1,106 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_data_on_enter(void* context) { + Nfc* nfc = context; + MfClassicType type = nfc->dev->dev_data.mf_classic_data.type; + MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; + TextBox* text_box = nfc->text_box; + + text_box_set_font(text_box, TextBoxFontHex); + + int card_blocks = 0; + if(type == MfClassicType1k) { + card_blocks = MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4; + } else if(type == MfClassicType4k) { + // 16 sectors of 4 blocks each plus 8 sectors of 16 blocks each + card_blocks = MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4 + 8 * 16; + } else if(type == MfClassicTypeMini) { + card_blocks = MF_MINI_TOTAL_SECTORS_NUM * 4; + } + + int bytes_written = 0; + for(int block_num = 0; block_num < card_blocks; block_num++) { + bool is_sec_trailer = mf_classic_is_sector_trailer(block_num); + if(is_sec_trailer) { + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, sector_num); + // Key A + for(size_t i = 0; i < sizeof(sec_tr->key_a); i += 2) { + if((bytes_written % 8 == 0) && (bytes_written != 0)) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + if(mf_classic_is_key_found(data, sector_num, MfClassicKeyA)) { + furi_string_cat_printf( + nfc->text_box_store, "%02X%02X ", sec_tr->key_a[i], sec_tr->key_a[i + 1]); + } else { + furi_string_cat_printf(nfc->text_box_store, "???? "); + } + bytes_written += 2; + } + // Access bytes + for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i += 2) { + if((bytes_written % 8 == 0) && (bytes_written != 0)) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + if(mf_classic_is_block_read(data, block_num)) { + furi_string_cat_printf( + nfc->text_box_store, + "%02X%02X ", + sec_tr->access_bits[i], + sec_tr->access_bits[i + 1]); + } else { + furi_string_cat_printf(nfc->text_box_store, "???? "); + } + bytes_written += 2; + } + // Key B + for(size_t i = 0; i < sizeof(sec_tr->key_b); i += 2) { + if((bytes_written % 8 == 0) && (bytes_written != 0)) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + if(mf_classic_is_key_found(data, sector_num, MfClassicKeyB)) { + furi_string_cat_printf( + nfc->text_box_store, "%02X%02X ", sec_tr->key_b[i], sec_tr->key_b[i + 1]); + } else { + furi_string_cat_printf(nfc->text_box_store, "???? "); + } + bytes_written += 2; + } + } else { + // Write data block + for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i += 2) { + if((bytes_written % 8 == 0) && (bytes_written != 0)) { + furi_string_push_back(nfc->text_box_store, '\n'); + } + if(mf_classic_is_block_read(data, block_num)) { + furi_string_cat_printf( + nfc->text_box_store, + "%02X%02X ", + data->block[block_num].value[i], + data->block[block_num].value[i + 1]); + } else { + furi_string_cat_printf(nfc->text_box_store, "???? "); + } + bytes_written += 2; + } + } + } + text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); +} + +bool nfc_scene_mf_classic_data_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void nfc_scene_mf_classic_data_on_exit(void* context) { + Nfc* nfc = context; + + // Clean view + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index b44ab782..92ad7b56 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -14,7 +14,8 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { NfcDeviceData* dev_data = &nfc->dev->dev_data; NfcProtocol protocol = dev_data->protocol; uint8_t text_scroll_height = 0; - if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl)) { + if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) || + (protocol == NfcDeviceProtocolMifareClassic)) { widget_add_button_element( widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc); text_scroll_height = 52; @@ -136,6 +137,9 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { } else if(protocol == NfcDeviceProtocolMifareUl) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData); consumed = true; + } else if(protocol == NfcDeviceProtocolMifareClassic) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData); + consumed = true; } } } diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index 04c686fb..f42e0fe9 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -150,6 +150,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { application_info_present = nfc_supported_card_verify_and_parse(dev_data); } + FURI_LOG_I("nfc", "application_info_present: %d", application_info_present); + if(application_info_present) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); } else { From 82ad44a8631bd9fa9c9876d519500f8e331446aa Mon Sep 17 00:00:00 2001 From: Konstantin Volkov <72250702+doomwastaken@users.noreply.github.com> Date: Sat, 25 Feb 2023 15:05:02 +0300 Subject: [PATCH 15/27] changed updater and unit benches (#2427) * changed updater and unit benches * switched flipper name from macos style to searching Co-authored-by: Konstantin Volkov --- .github/workflows/unit_tests.yml | 2 +- .github/workflows/updater_test.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 7e625229..527e9a71 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -10,7 +10,7 @@ env: jobs: run_units_on_bench: - runs-on: [self-hosted, FlipperZeroTest] + runs-on: [self-hosted, FlipperZeroUnitTest] steps: - name: 'Decontaminate previous build leftovers' run: | diff --git a/.github/workflows/updater_test.yml b/.github/workflows/updater_test.yml index 0b02920f..300440aa 100644 --- a/.github/workflows/updater_test.yml +++ b/.github/workflows/updater_test.yml @@ -10,7 +10,7 @@ env: jobs: test_updater_on_bench: - runs-on: [self-hosted, FlipperZeroTestMac1] + runs-on: [self-hosted, FlipperZeroUpdaterTest] steps: - name: 'Decontaminate previous build leftovers' run: | @@ -27,7 +27,7 @@ jobs: - name: 'Get flipper from device manager (mock)' id: device run: | - echo "flipper=/dev/tty.usbmodemflip_Rekigyn1" >> $GITHUB_OUTPUT + echo "flipper=Rekigyn" >> $GITHUB_OUTPUT echo "stlink=0F020D026415303030303032" >> $GITHUB_OUTPUT - name: 'Flashing target firmware' From 203adabc46ff70f7179de849c165001dd6c1abaa Mon Sep 17 00:00:00 2001 From: Mathie <62908057+MathieDev@users.noreply.github.com> Date: Sat, 25 Feb 2023 08:41:49 -0500 Subject: [PATCH 16/27] Update update.py (#2426) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed grammar mistake Co-authored-by: あく --- scripts/update.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update.py b/scripts/update.py index 3259c5b0..2b015726 100755 --- a/scripts/update.py +++ b/scripts/update.py @@ -199,7 +199,7 @@ class Main(App): def disclaimer(self): self.logger.error( - "You might brick you device into a state in which you'd need an SWD programmer to fix it." + "You might brick your device into a state in which you'd need an SWD programmer to fix it." ) self.logger.error( "Please confirm that you REALLY want to do that with --I-understand-what-I-am-doing=yes" From e999c3574977db5243cf011d21673b4d1a929819 Mon Sep 17 00:00:00 2001 From: Logandev_ Date: Sat, 25 Feb 2023 05:49:53 -0800 Subject: [PATCH 17/27] Grammar fix in CLI (#2390) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fixed grammar * Update cli_commands.c: made it a little nicer Co-authored-by: あく --- applications/services/cli/cli_commands.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index 4414d365..b0f1bdbd 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -31,7 +31,7 @@ void cli_command_device_info(Cli* cli, FuriString* args, void* context) { void cli_command_help(Cli* cli, FuriString* args, void* context) { UNUSED(args); UNUSED(context); - printf("Commands we have:"); + printf("Commands available:"); // Command count const size_t commands_count = CliCommandTree_size(cli->commands); @@ -61,9 +61,9 @@ void cli_command_help(Cli* cli, FuriString* args, void* context) { if(furi_string_size(args) > 0) { cli_nl(); - printf("Also I have no clue what '"); + printf("`"); printf("%s", furi_string_get_cstr(args)); - printf("' is."); + printf("` command not found"); } } From eaf965c66f7e57855f2cd9be8247882070bf6453 Mon Sep 17 00:00:00 2001 From: n30f0x <87524177+n30f0x@users.noreply.github.com> Date: Sat, 25 Feb 2023 20:34:48 +0300 Subject: [PATCH 18/27] BadUsb: STRINGDELAY feature, worker signal handling refactoring (#2269) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * BadUsb: Added stringdelay feature * BadUsb: added stringdelay feature, fixed delay * BadUsb: fix cursed delay structure * BadUsb: long delay check added * BadUsb: long delay distribution * furi_delay_ms(0) edgecase fix, add documentation entry * additional documentation entry * BadUsb: get rid of bad logic, fixed documentation * BadUSB script: fix events handling * Delay value fix * Script execution fix Co-authored-by: あく Co-authored-by: nminaylov --- applications/main/bad_usb/bad_usb_script.c | 81 ++++++++++++++----- .../scenes/bad_usb_scene_file_select.c | 2 - .../file_formats/BadUsbScriptFormat.md | 8 ++ 3 files changed, 69 insertions(+), 22 deletions(-) diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index 34dfec2c..d66ce8a9 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -32,6 +32,7 @@ struct BadUsbScript { FuriString* file_path; uint32_t defdelay; uint16_t layout[128]; + uint32_t stringdelay; FuriThread* thread; uint8_t file_buf[FILE_BUFFER_LEN + 1]; uint8_t buf_start; @@ -113,6 +114,8 @@ static const char ducky_cmd_delay[] = {"DELAY "}; static const char ducky_cmd_string[] = {"STRING "}; static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY "}; static const char ducky_cmd_defdelay_2[] = {"DEFAULTDELAY "}; +static const char ducky_cmd_stringdelay_1[] = {"STRINGDELAY "}; +static const char ducky_cmd_stringdelay_2[] = {"STRING_DELAY "}; static const char ducky_cmd_repeat[] = {"REPEAT "}; static const char ducky_cmd_sysrq[] = {"SYSRQ "}; @@ -211,14 +214,19 @@ static bool ducky_altstring(const char* param) { static bool ducky_string(BadUsbScript* bad_usb, const char* param) { uint32_t i = 0; + while(param[i] != '\0') { uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, param[i]); if(keycode != HID_KEYBOARD_NONE) { furi_hal_hid_kb_press(keycode); furi_hal_hid_kb_release(keycode); + if(bad_usb->stringdelay > 0) { + furi_delay_ms(bad_usb->stringdelay); + } } i++; } + bad_usb->stringdelay = 0; return true; } @@ -277,6 +285,20 @@ static int32_t snprintf(error, error_len, "Invalid number %s", line_tmp); } return (state) ? (0) : SCRIPT_STATE_ERROR; + } else if( + (strncmp(line_tmp, ducky_cmd_stringdelay_1, strlen(ducky_cmd_stringdelay_1)) == 0) || + (strncmp(line_tmp, ducky_cmd_stringdelay_2, strlen(ducky_cmd_stringdelay_2)) == 0)) { + //STRINGDELAY, finally it's here + line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; + state = ducky_get_number(line_tmp, &bad_usb->stringdelay); + if((state) && (bad_usb->stringdelay > 0)) { + return state; + } + if(error != NULL) { + snprintf(error, error_len, "Invalid number %s", line_tmp); + } + return SCRIPT_STATE_ERROR; + } else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) { // STRING line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; @@ -484,6 +506,19 @@ static void bad_usb_hid_state_callback(bool state, void* context) { furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtDisconnect); } +static uint32_t bad_usb_flags_get(uint32_t flags_mask, uint32_t timeout) { + uint32_t flags = furi_thread_flags_get(); + furi_check((flags & FuriFlagError) == 0); + if(flags == 0) { + flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny, timeout); + furi_check(((flags & FuriFlagError) == 0) || (flags == FuriFlagErrorTimeout)); + } else { + uint32_t state = furi_thread_flags_clear(flags); + furi_check((state & FuriFlagError) == 0); + } + return flags; +} + static int32_t bad_usb_worker(void* context) { BadUsbScript* bad_usb = context; @@ -520,11 +555,9 @@ static int32_t bad_usb_worker(void* context) { bad_usb->st.state = worker_state; } else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, - FuriFlagWaitAny, - FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever); + if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtConnect) { @@ -535,11 +568,9 @@ static int32_t bad_usb_worker(void* context) { bad_usb->st.state = worker_state; } else if(worker_state == BadUsbStateIdle) { // State: ready to start - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, - FuriFlagWaitAny, - FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriWaitForever); + if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtToggle) { // Start executing script @@ -548,6 +579,7 @@ static int32_t bad_usb_worker(void* context) { bad_usb->buf_len = 0; bad_usb->st.line_cur = 0; bad_usb->defdelay = 0; + bad_usb->stringdelay = 0; bad_usb->repeat_cnt = 0; bad_usb->file_end = false; storage_file_seek(script_file, 0, true); @@ -558,11 +590,9 @@ static int32_t bad_usb_worker(void* context) { bad_usb->st.state = worker_state; } else if(worker_state == BadUsbStateWillRun) { // State: start on connection - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, - FuriFlagWaitAny, - FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever); + if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtConnect) { // Start executing script @@ -571,12 +601,22 @@ static int32_t bad_usb_worker(void* context) { bad_usb->buf_len = 0; bad_usb->st.line_cur = 0; bad_usb->defdelay = 0; + bad_usb->stringdelay = 0; bad_usb->repeat_cnt = 0; bad_usb->file_end = false; storage_file_seek(script_file, 0, true); // extra time for PC to recognize Flipper as keyboard - furi_thread_flags_wait(0, FuriFlagWaitAny, 1500); - worker_state = BadUsbStateRunning; + flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtToggle, + FuriFlagWaitAny | FuriFlagNoClear, + 1500); + if(flags == FuriFlagErrorTimeout) { + // If nothing happened - start script execution + worker_state = BadUsbStateRunning; + } else if(flags & WorkerEvtToggle) { + worker_state = BadUsbStateIdle; + furi_thread_flags_clear(WorkerEvtToggle); + } } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution worker_state = BadUsbStateNotConnected; } @@ -586,6 +626,7 @@ static int32_t bad_usb_worker(void* context) { uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); uint32_t flags = furi_thread_flags_wait( WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur); + delay_val -= delay_cur; if(!(flags & FuriFlagError)) { if(flags & WorkerEvtEnd) { @@ -629,9 +670,9 @@ static int32_t bad_usb_worker(void* context) { } else if( (worker_state == BadUsbStateFileError) || (worker_state == BadUsbStateScriptError)) { // State: error - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd, FuriFlagWaitAny, FuriWaitForever); // Waiting for exit command - furi_check((flags & FuriFlagError) == 0); + uint32_t flags = + bad_usb_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command + if(flags & WorkerEvtEnd) { break; } diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c index b0466925..d6f05a1e 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c @@ -22,7 +22,6 @@ static bool bad_usb_file_select(BadUsbApp* bad_usb) { void bad_usb_scene_file_select_on_enter(void* context) { BadUsbApp* bad_usb = context; - furi_hal_usb_disable(); if(bad_usb->bad_usb_script) { bad_usb_script_close(bad_usb->bad_usb_script); bad_usb->bad_usb_script = NULL; @@ -34,7 +33,6 @@ void bad_usb_scene_file_select_on_enter(void* context) { scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork); } else { - furi_hal_usb_enable(); view_dispatcher_stop(bad_usb->view_dispatcher); } } diff --git a/documentation/file_formats/BadUsbScriptFormat.md b/documentation/file_formats/BadUsbScriptFormat.md index 2ef1d313..94dee594 100644 --- a/documentation/file_formats/BadUsbScriptFormat.md +++ b/documentation/file_formats/BadUsbScriptFormat.md @@ -75,6 +75,14 @@ Can be combined with a special key command or a single character. | ------- | ----------- | ----------------- | | STRING | Text string | Print text string | +## String delay + +Delay between keypresses. +|Command|Parameters|Notes| +|-|-|-| +|STRING_DELAY|Delay value in ms|Applied once to next appearing string| +|STRINGDELAY|Delay value in ms|Same as STRING_DELAY| + ## Repeat | Command | Parameters | Notes | From 03f889962bc6fec3ed19ef558ef65e9683f6fd86 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sat, 25 Feb 2023 23:59:50 -0800 Subject: [PATCH 19/27] Picopass: factory key support, app rename and move to NFC category, minor code cleanup (#2417) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * message on successful card write * auth using factory key * auth using factory default * factory default screen * write standard iclass key * pass block explicitly * Fix array indexing, add empty detection * PicoPass: rename app and move to NFC group, minor code cleanup Co-authored-by: あく --- applications/plugins/picopass/application.fam | 4 +- applications/plugins/picopass/picopass.c | 10 ++ .../plugins/picopass/picopass_device.c | 2 +- .../plugins/picopass/picopass_device.h | 1 + applications/plugins/picopass/picopass_i.h | 12 ++ .../plugins/picopass/picopass_worker.c | 165 +++++++++++++++++- .../plugins/picopass/picopass_worker.h | 1 + .../plugins/picopass/picopass_worker_i.h | 1 + .../picopass/scenes/picopass_scene_config.h | 2 + .../scenes/picopass_scene_read_card.c | 11 +- .../scenes/picopass_scene_read_card_success.c | 41 +++-- .../picopass_scene_read_factory_success.c | 78 +++++++++ .../picopass_scene_write_card_success.c | 13 ++ .../scenes/picopass_scene_write_key.c | 53 ++++++ 14 files changed, 367 insertions(+), 27 deletions(-) create mode 100644 applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c create mode 100644 applications/plugins/picopass/scenes/picopass_scene_write_key.c diff --git a/applications/plugins/picopass/application.fam b/applications/plugins/picopass/application.fam index f2da6a9f..c5087b80 100644 --- a/applications/plugins/picopass/application.fam +++ b/applications/plugins/picopass/application.fam @@ -1,6 +1,6 @@ App( appid="picopass", - name="PicoPass Reader", + name="PicoPass", apptype=FlipperAppType.EXTERNAL, targets=["f7"], entry_point="picopass_app", @@ -11,7 +11,7 @@ App( stack_size=4 * 1024, order=30, fap_icon="125_10px.png", - fap_category="Tools", + fap_category="NFC", fap_libs=["mbedtls"], fap_private_libs=[ Lib( diff --git a/applications/plugins/picopass/picopass.c b/applications/plugins/picopass/picopass.c index 217f963d..96ea82c3 100644 --- a/applications/plugins/picopass/picopass.c +++ b/applications/plugins/picopass/picopass.c @@ -171,6 +171,16 @@ void picopass_show_loading_popup(void* context, bool show) { } } +bool picopass_is_memset(const uint8_t* data, const uint8_t pattern, size_t size) { + bool result = size > 0; + while(size > 0) { + result &= (*data == pattern); + data++; + size--; + } + return result; +} + int32_t picopass_app(void* p) { UNUSED(p); Picopass* picopass = picopass_alloc(); diff --git a/applications/plugins/picopass/picopass_device.c b/applications/plugins/picopass/picopass_device.c index fd8ddbfb..e3940698 100644 --- a/applications/plugins/picopass/picopass_device.c +++ b/applications/plugins/picopass/picopass_device.c @@ -368,7 +368,7 @@ ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* r record->CardNumber = (bot >> 1) & 0xFFFF; record->FacilityCode = (bot >> 17) & 0xFF; - FURI_LOG_D(TAG, "FC:%u CN: %u\n", record->FacilityCode, record->CardNumber); + FURI_LOG_D(TAG, "FC: %u CN: %u", record->FacilityCode, record->CardNumber); record->valid = true; } else { record->CardNumber = 0; diff --git a/applications/plugins/picopass/picopass_device.h b/applications/plugins/picopass/picopass_device.h index 150b095a..99f1ceea 100644 --- a/applications/plugins/picopass/picopass_device.h +++ b/applications/plugins/picopass/picopass_device.h @@ -22,6 +22,7 @@ #define PICOPASS_KD_BLOCK_INDEX 3 #define PICOPASS_KC_BLOCK_INDEX 4 #define PICOPASS_AIA_BLOCK_INDEX 5 +#define PICOPASS_PACS_CFG_BLOCK_INDEX 6 #define PICOPASS_APP_FOLDER ANY_PATH("picopass") #define PICOPASS_APP_EXTENSION ".picopass" diff --git a/applications/plugins/picopass/picopass_i.h b/applications/plugins/picopass/picopass_i.h index 469a672b..54533e82 100644 --- a/applications/plugins/picopass/picopass_i.h +++ b/applications/plugins/picopass/picopass_i.h @@ -81,3 +81,15 @@ void picopass_blink_start(Picopass* picopass); void picopass_blink_stop(Picopass* picopass); void picopass_show_loading_popup(void* context, bool show); + +/** Check if memory is set to pattern + * + * @warning zero size will return false + * + * @param[in] data Pointer to the byte array + * @param[in] pattern The pattern + * @param[in] size The byte array size + * + * @return True if memory is set to pattern, false otherwise + */ +bool picopass_is_memset(const uint8_t* data, const uint8_t pattern, size_t size); diff --git a/applications/plugins/picopass/picopass_worker.c b/applications/plugins/picopass/picopass_worker.c index 1ee814aa..6d904478 100644 --- a/applications/plugins/picopass/picopass_worker.c +++ b/applications/plugins/picopass/picopass_worker.c @@ -5,7 +5,8 @@ #define TAG "PicopassWorker" const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; -const uint8_t picopass_factory_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00}; +const uint8_t picopass_factory_credit_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00}; +const uint8_t picopass_factory_debit_key[] = {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87}; static void picopass_worker_enable_field() { furi_hal_nfc_ll_txrx_on(); @@ -197,6 +198,28 @@ static ReturnCode picopass_auth_standard(uint8_t* csn, uint8_t* div_key) { return rfalPicoPassPollerCheck(mac, &chkRes); } +static ReturnCode picopass_auth_factory(uint8_t* csn, uint8_t* div_key) { + rfalPicoPassReadCheckRes rcRes; + rfalPicoPassCheckRes chkRes; + + ReturnCode err; + + uint8_t mac[4] = {0}; + uint8_t ccnr[12] = {0}; + + err = rfalPicoPassPollerReadCheck(&rcRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); + return err; + } + memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 + + loclass_diversifyKey(csn, picopass_factory_debit_key, div_key); + loclass_opt_doReaderMAC(ccnr, div_key, mac); + + return rfalPicoPassPollerCheck(mac, &chkRes); +} + static ReturnCode picopass_auth_dict( uint8_t* csn, PicopassPacs* pacs, @@ -264,14 +287,23 @@ static ReturnCode picopass_auth_dict( ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) { ReturnCode err; - FURI_LOG_E(TAG, "Trying standard legacy key"); + FURI_LOG_I(TAG, "Trying standard legacy key"); err = picopass_auth_standard( AA1[PICOPASS_CSN_BLOCK_INDEX].data, AA1[PICOPASS_KD_BLOCK_INDEX].data); if(err == ERR_NONE) { + memcpy(pacs->key, picopass_iclass_key, PICOPASS_BLOCK_LEN); return ERR_NONE; } - FURI_LOG_E(TAG, "Starting user dictionary attack"); + FURI_LOG_I(TAG, "Trying factory default key"); + err = picopass_auth_factory( + AA1[PICOPASS_CSN_BLOCK_INDEX].data, AA1[PICOPASS_KD_BLOCK_INDEX].data); + if(err == ERR_NONE) { + memcpy(pacs->key, picopass_factory_debit_key, PICOPASS_BLOCK_LEN); + return ERR_NONE; + } + + FURI_LOG_I(TAG, "Starting user dictionary attack"); err = picopass_auth_dict( AA1[PICOPASS_CSN_BLOCK_INDEX].data, pacs, @@ -281,7 +313,7 @@ ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) { return ERR_NONE; } - FURI_LOG_E(TAG, "Starting in-built dictionary attack"); + FURI_LOG_I(TAG, "Starting system dictionary attack"); err = picopass_auth_dict( AA1[PICOPASS_CSN_BLOCK_INDEX].data, pacs, @@ -406,6 +438,84 @@ ReturnCode picopass_write_card(PicopassBlock* AA1) { return ERR_NONE; } +ReturnCode picopass_write_block(PicopassPacs* pacs, uint8_t blockNo, uint8_t* newBlock) { + rfalPicoPassIdentifyRes idRes; + rfalPicoPassSelectRes selRes; + rfalPicoPassReadCheckRes rcRes; + rfalPicoPassCheckRes chkRes; + + ReturnCode err; + + uint8_t div_key[8] = {0}; + uint8_t mac[4] = {0}; + uint8_t ccnr[12] = {0}; + + err = rfalPicoPassPollerIdentify(&idRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err); + return err; + } + + err = rfalPicoPassPollerSelect(idRes.CSN, &selRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err); + return err; + } + + err = rfalPicoPassPollerReadCheck(&rcRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); + return err; + } + memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 + + loclass_diversifyKey(selRes.CSN, pacs->key, div_key); + loclass_opt_doReaderMAC(ccnr, div_key, mac); + + err = rfalPicoPassPollerCheck(mac, &chkRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); + return err; + } + + FURI_LOG_D(TAG, "rfalPicoPassPollerWriteBlock %d", blockNo); + uint8_t data[9] = { + blockNo, + newBlock[0], + newBlock[1], + newBlock[2], + newBlock[3], + newBlock[4], + newBlock[5], + newBlock[6], + newBlock[7]}; + loclass_doMAC_N(data, sizeof(data), div_key, mac); + FURI_LOG_D( + TAG, + "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x", + blockNo, + data[1], + data[2], + data[3], + data[4], + data[5], + data[6], + data[7], + data[8], + mac[0], + mac[1], + mac[2], + mac[3]); + + err = rfalPicoPassPollerWriteBlock(data[0], data + 1, mac); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerWriteBlock error %d", err); + return err; + } + + return ERR_NONE; +} + int32_t picopass_worker_task(void* context) { PicopassWorker* picopass_worker = context; @@ -414,6 +524,8 @@ int32_t picopass_worker_task(void* context) { picopass_worker_detect(picopass_worker); } else if(picopass_worker->state == PicopassWorkerStateWrite) { picopass_worker_write(picopass_worker); + } else if(picopass_worker->state == PicopassWorkerStateWriteStandardKey) { + picopass_worker_write_standard_key(picopass_worker); } picopass_worker_disable_field(ERR_NONE); @@ -448,7 +560,7 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) { } // Thank you proxmark! - pacs->legacy = (memcmp(AA1[5].data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); + pacs->legacy = picopass_is_memset(AA1[5].data, 0xFF, 8); pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); if(pacs->se_enabled) { FURI_LOG_D(TAG, "SE enabled"); @@ -520,3 +632,46 @@ void picopass_worker_write(PicopassWorker* picopass_worker) { furi_delay_ms(100); } } + +void picopass_worker_write_standard_key(PicopassWorker* picopass_worker) { + PicopassDeviceData* dev_data = picopass_worker->dev_data; + PicopassBlock* AA1 = dev_data->AA1; + PicopassPacs* pacs = &dev_data->pacs; + ReturnCode err; + PicopassWorkerEvent nextState = PicopassWorkerEventSuccess; + + uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data; + uint8_t* configBlock = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data; + uint8_t fuses = configBlock[7]; + uint8_t* oldKey = AA1[PICOPASS_KD_BLOCK_INDEX].data; + + uint8_t newKey[PICOPASS_BLOCK_LEN] = {0}; + loclass_diversifyKey(csn, picopass_iclass_key, newKey); + + if((fuses & 0x80) == 0x80) { + FURI_LOG_D(TAG, "Plain write for personalized mode key change"); + } else { + FURI_LOG_D(TAG, "XOR write for application mode key change"); + // XOR when in application mode + for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + newKey[i] ^= oldKey[i]; + } + } + + while(picopass_worker->state == PicopassWorkerStateWriteStandardKey) { + if(picopass_detect_card(1000) == ERR_NONE) { + err = picopass_write_block(pacs, PICOPASS_KD_BLOCK_INDEX, newKey); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_write_block error %d", err); + nextState = PicopassWorkerEventFail; + } + + // Notify caller and exit + if(picopass_worker->callback) { + picopass_worker->callback(nextState, picopass_worker->context); + } + break; + } + furi_delay_ms(100); + } +} diff --git a/applications/plugins/picopass/picopass_worker.h b/applications/plugins/picopass/picopass_worker.h index 29a890a1..775212c6 100644 --- a/applications/plugins/picopass/picopass_worker.h +++ b/applications/plugins/picopass/picopass_worker.h @@ -12,6 +12,7 @@ typedef enum { // Main worker states PicopassWorkerStateDetect, PicopassWorkerStateWrite, + PicopassWorkerStateWriteStandardKey, // Transition PicopassWorkerStateStop, } PicopassWorkerState; diff --git a/applications/plugins/picopass/picopass_worker_i.h b/applications/plugins/picopass/picopass_worker_i.h index ded40e6c..cf55fbdf 100644 --- a/applications/plugins/picopass/picopass_worker_i.h +++ b/applications/plugins/picopass/picopass_worker_i.h @@ -31,3 +31,4 @@ int32_t picopass_worker_task(void* context); void picopass_worker_detect(PicopassWorker* picopass_worker); void picopass_worker_write(PicopassWorker* picopass_worker); +void picopass_worker_write_standard_key(PicopassWorker* picopass_worker); diff --git a/applications/plugins/picopass/scenes/picopass_scene_config.h b/applications/plugins/picopass/scenes/picopass_scene_config.h index 27d6bbcd..95700787 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_config.h +++ b/applications/plugins/picopass/scenes/picopass_scene_config.h @@ -11,3 +11,5 @@ ADD_SCENE(picopass, delete, Delete) ADD_SCENE(picopass, delete_success, DeleteSuccess) ADD_SCENE(picopass, write_card, WriteCard) ADD_SCENE(picopass, write_card_success, WriteCardSuccess) +ADD_SCENE(picopass, read_factory_success, ReadFactorySuccess) +ADD_SCENE(picopass, write_key, WriteKey) diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card.c b/applications/plugins/picopass/scenes/picopass_scene_read_card.c index 8188207a..90422a2e 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_read_card.c +++ b/applications/plugins/picopass/scenes/picopass_scene_read_card.c @@ -1,6 +1,8 @@ #include "../picopass_i.h" #include +const uint8_t picopass_factory_key_check[] = {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87}; + void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context) { UNUSED(event); Picopass* picopass = context; @@ -34,7 +36,14 @@ bool picopass_scene_read_card_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == PicopassCustomEventWorkerExit) { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); + if(memcmp( + picopass->dev->dev_data.pacs.key, + picopass_factory_key_check, + PICOPASS_BLOCK_LEN) == 0) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadFactorySuccess); + } else { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); + } consumed = true; } } diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c index d89a5d89..f078d460 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c @@ -15,6 +15,7 @@ void picopass_scene_read_card_success_widget_callback( void picopass_scene_read_card_success_on_enter(void* context) { Picopass* picopass = context; + FuriString* csn_str = furi_string_alloc_set("CSN:"); FuriString* credential_str = furi_string_alloc(); FuriString* wiegand_str = furi_string_alloc(); @@ -30,27 +31,31 @@ void picopass_scene_read_card_success_on_enter(void* context) { PicopassPacs* pacs = &picopass->dev->dev_data.pacs; Widget* widget = picopass->widget; - uint8_t csn[PICOPASS_BLOCK_LEN]; - memcpy(csn, &AA1->data[PICOPASS_CSN_BLOCK_INDEX], PICOPASS_BLOCK_LEN); + uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; + memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { furi_string_cat_printf(csn_str, "%02X ", csn[i]); } - // Neither of these are valid. Indicates the block was all 0x00 or all 0xff - if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { + bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN); + bool empty = + picopass_is_memset(AA1[PICOPASS_PACS_CFG_BLOCK_INDEX].data, 0xFF, PICOPASS_BLOCK_LEN); + + if(no_key) { furi_string_cat_printf(wiegand_str, "Read Failed"); if(pacs->se_enabled) { furi_string_cat_printf(credential_str, "SE enabled"); } + } else if(empty) { + furi_string_cat_printf(wiegand_str, "Empty"); + } else if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { + // Neither of these are valid. Indicates the block was all 0x00 or all 0xff + furi_string_cat_printf(wiegand_str, "Invalid PACS"); - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Retry", - picopass_scene_read_card_success_widget_callback, - picopass); - + if(pacs->se_enabled) { + furi_string_cat_printf(credential_str, "SE enabled"); + } } else { size_t bytesLength = 1 + pacs->record.bitLength / 8; furi_string_set(credential_str, ""); @@ -82,13 +87,6 @@ void picopass_scene_read_card_success_on_enter(void* context) { } } - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Retry", - picopass_scene_read_card_success_widget_callback, - picopass); - widget_add_button_element( widget, GuiButtonTypeRight, @@ -97,6 +95,13 @@ void picopass_scene_read_card_success_on_enter(void* context) { picopass); } + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + picopass_scene_read_card_success_widget_callback, + picopass); + widget_add_string_element( widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str)); widget_add_string_element( diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c b/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c new file mode 100644 index 00000000..8e32d21f --- /dev/null +++ b/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c @@ -0,0 +1,78 @@ +#include "../picopass_i.h" +#include + +void picopass_scene_read_factory_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + furi_assert(context); + Picopass* picopass = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(picopass->view_dispatcher, result); + } +} + +void picopass_scene_read_factory_success_on_enter(void* context) { + Picopass* picopass = context; + FuriString* title = furi_string_alloc_set("Factory Default"); + FuriString* subtitle = furi_string_alloc_set(""); + + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + // Send notification + notification_message(picopass->notifications, &sequence_success); + + // Setup view + Widget* widget = picopass->widget; + //PicopassPacs* pacs = &picopass->dev->dev_data.pacs; + PicopassBlock* AA1 = picopass->dev->dev_data.AA1; + + uint8_t* configBlock = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data; + uint8_t fuses = configBlock[7]; + + if((fuses & 0x80) == 0x80) { + furi_string_cat_printf(subtitle, "Personalization mode"); + } else { + furi_string_cat_printf(subtitle, "Application mode"); + } + + widget_add_button_element( + widget, + GuiButtonTypeCenter, + "Write Standard iClass Key", + picopass_scene_read_factory_success_widget_callback, + picopass); + + widget_add_string_element( + widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(title)); + widget_add_string_element( + widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(subtitle)); + + furi_string_free(title); + furi_string_free(subtitle); + + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); +} + +bool picopass_scene_read_factory_success_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(picopass->scene_manager); + } else if(event.event == GuiButtonTypeCenter) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); + consumed = true; + } + } + return consumed; +} + +void picopass_scene_read_factory_success_on_exit(void* context) { + Picopass* picopass = context; + + // Clear view + widget_reset(picopass->widget); +} diff --git a/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c b/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c index 108e7d1c..4bbca816 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c +++ b/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c @@ -16,6 +16,7 @@ void picopass_scene_write_card_success_widget_callback( void picopass_scene_write_card_success_on_enter(void* context) { Picopass* picopass = context; Widget* widget = picopass->widget; + FuriString* str = furi_string_alloc_set("Write Success!"); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); @@ -29,6 +30,18 @@ void picopass_scene_write_card_success_on_enter(void* context) { picopass_scene_write_card_success_widget_callback, picopass); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "Menu", + picopass_scene_write_card_success_widget_callback, + picopass); + + widget_add_string_element( + widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(str)); + + furi_string_free(str); + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); } diff --git a/applications/plugins/picopass/scenes/picopass_scene_write_key.c b/applications/plugins/picopass/scenes/picopass_scene_write_key.c new file mode 100644 index 00000000..83d594ca --- /dev/null +++ b/applications/plugins/picopass/scenes/picopass_scene_write_key.c @@ -0,0 +1,53 @@ +#include "../picopass_i.h" +#include + +void picopass_write_key_worker_callback(PicopassWorkerEvent event, void* context) { + UNUSED(event); + Picopass* picopass = context; + view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventWorkerExit); +} + +void picopass_scene_write_key_on_enter(void* context) { + Picopass* picopass = context; + DOLPHIN_DEED(DolphinDeedNfcSave); + + // Setup view + Popup* popup = picopass->popup; + popup_set_header(popup, "Writing\niClass\nkey", 68, 30, AlignLeft, AlignTop); + popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); + + // Start worker + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup); + picopass_worker_start( + picopass->worker, + PicopassWorkerStateWriteStandardKey, + &picopass->dev->dev_data, + picopass_write_key_worker_callback, + picopass); + + picopass_blink_start(picopass); +} + +bool picopass_scene_write_key_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == PicopassCustomEventWorkerExit) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCardSuccess); + consumed = true; + } + } + return consumed; +} + +void picopass_scene_write_key_on_exit(void* context) { + Picopass* picopass = context; + + // Stop worker + picopass_worker_stop(picopass->worker); + // Clear view + popup_reset(picopass->popup); + + picopass_blink_stop(picopass); +} From 12c1ec37a20a2b4f3a4ed5359bb708fcab79ca6c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 26 Feb 2023 11:08:05 +0300 Subject: [PATCH 20/27] Fix PVS warnings (#2430) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/bad_usb/bad_usb_script.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index d66ce8a9..beb35b89 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -511,7 +511,7 @@ static uint32_t bad_usb_flags_get(uint32_t flags_mask, uint32_t timeout) { furi_check((flags & FuriFlagError) == 0); if(flags == 0) { flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny, timeout); - furi_check(((flags & FuriFlagError) == 0) || (flags == FuriFlagErrorTimeout)); + furi_check(((flags & FuriFlagError) == 0) || (flags == (unsigned)FuriFlagErrorTimeout)); } else { uint32_t state = furi_thread_flags_clear(flags); furi_check((state & FuriFlagError) == 0); @@ -610,7 +610,7 @@ static int32_t bad_usb_worker(void* context) { WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtToggle, FuriFlagWaitAny | FuriFlagNoClear, 1500); - if(flags == FuriFlagErrorTimeout) { + if(flags == (unsigned)FuriFlagErrorTimeout) { // If nothing happened - start script execution worker_state = BadUsbStateRunning; } else if(flags & WorkerEvtToggle) { From 9bda3e62eec4d81cc59352431ee468024c5921ed Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Sun, 26 Feb 2023 14:28:51 +0300 Subject: [PATCH 21/27] SD Cache: moved to diskio layer, invalidation in case of error (#2428) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- firmware/targets/f7/fatfs/sd_spi_io.c | 38 +------------------------ firmware/targets/f7/fatfs/sd_spi_io.h | 1 + firmware/targets/f7/fatfs/user_diskio.c | 37 ++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/firmware/targets/f7/fatfs/sd_spi_io.c b/firmware/targets/f7/fatfs/sd_spi_io.c index 93b837e8..68903acf 100644 --- a/firmware/targets/f7/fatfs/sd_spi_io.c +++ b/firmware/targets/f7/fatfs/sd_spi_io.c @@ -17,7 +17,6 @@ #define SD_DUMMY_BYTE 0xFF #define SD_ANSWER_RETRY_COUNT 8 #define SD_IDLE_RETRY_COUNT 100 -#define SD_BLOCK_SIZE 512 #define FLAG_SET(x, y) (((x) & (y)) == (y)) @@ -598,23 +597,6 @@ static SdSpiStatus sd_spi_get_cid(SD_CID* Cid) { return ret; } -static inline bool sd_cache_get(uint32_t address, uint32_t* data) { - uint8_t* cached_data = sector_cache_get(address); - if(cached_data) { - memcpy(data, cached_data, SD_BLOCK_SIZE); - return true; - } - return false; -} - -static inline void sd_cache_put(uint32_t address, uint32_t* data) { - sector_cache_put(address, (uint8_t*)data); -} - -static inline void sd_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector) { - sector_cache_invalidate_range(start_sector, end_sector); -} - static SdSpiStatus sd_spi_cmd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { uint32_t block_address = address; @@ -833,30 +815,12 @@ SdSpiStatus sd_get_card_info(SD_CardInfo* card_info) { SdSpiStatus sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { - SdSpiStatus status = SdSpiStatusError; - - bool single_sector_read = (blocks == 1); - - if(single_sector_read) { - if(sd_cache_get(address, data)) { - return SdSpiStatusOK; - } - - status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); - - if(status == SdSpiStatusOK) { - sd_cache_put(address, data); - } - } else { - status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); - } - + SdSpiStatus status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); return status; } SdSpiStatus sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { - sd_cache_invalidate_range(address, address + blocks); SdSpiStatus status = sd_spi_cmd_write_blocks(data, address, blocks, timeout_ms); return status; } diff --git a/firmware/targets/f7/fatfs/sd_spi_io.h b/firmware/targets/f7/fatfs/sd_spi_io.h index 8850eceb..954c78c4 100644 --- a/firmware/targets/f7/fatfs/sd_spi_io.h +++ b/firmware/targets/f7/fatfs/sd_spi_io.h @@ -5,6 +5,7 @@ #define __IO volatile #define SD_TIMEOUT_MS (1000) +#define SD_BLOCK_SIZE 512 typedef enum { SdSpiStatusOK, diff --git a/firmware/targets/f7/fatfs/user_diskio.c b/firmware/targets/f7/fatfs/user_diskio.c index 16ac78e4..d7be09c5 100644 --- a/firmware/targets/f7/fatfs/user_diskio.c +++ b/firmware/targets/f7/fatfs/user_diskio.c @@ -36,6 +36,7 @@ /* Includes ------------------------------------------------------------------*/ #include "user_diskio.h" #include +#include "sector_cache.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ @@ -79,6 +80,26 @@ Diskio_drvTypeDef USER_Driver = { }; /* Private functions ---------------------------------------------------------*/ +static inline bool sd_cache_get(uint32_t address, uint32_t* data) { + uint8_t* cached_data = sector_cache_get(address); + if(cached_data) { + memcpy(data, cached_data, SD_BLOCK_SIZE); + return true; + } + return false; +} + +static inline void sd_cache_put(uint32_t address, uint32_t* data) { + sector_cache_put(address, (uint8_t*)data); +} + +static inline void sd_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector) { + sector_cache_invalidate_range(start_sector, end_sector); +} + +static inline void sd_cache_invalidate_all() { + sector_cache_init(); +} /** * @brief Initializes a Drive @@ -125,6 +146,14 @@ DRESULT USER_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { UNUSED(pdrv); DRESULT res = RES_ERROR; + bool single_sector = count == 1; + + if(single_sector) { + if(sd_cache_get(sector, (uint32_t*)buff)) { + return RES_OK; + } + } + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; @@ -145,6 +174,10 @@ DRESULT USER_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { furi_hal_sd_spi_handle = NULL; furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); + if(single_sector && res == RES_OK) { + sd_cache_put(sector, (uint32_t*)buff); + } + return res; /* USER CODE END READ */ } @@ -164,6 +197,8 @@ DRESULT USER_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { UNUSED(pdrv); DRESULT res = RES_ERROR; + sd_cache_invalidate_range(sector, sector + count); + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; @@ -175,6 +210,8 @@ DRESULT USER_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { res = RES_OK; while(sd_get_card_state() != SdSpiStatusOK) { if(furi_hal_cortex_timer_is_expired(timer)) { + sd_cache_invalidate_all(); + res = RES_ERROR; break; } From 0c06e54831f85e0b4d11efda860e44a6db2a4a8e Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Sun, 26 Feb 2023 14:28:52 +0200 Subject: [PATCH 22/27] [FL-3105] Unify power info, power debug, and device_info into one info command (#2393) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Unify power info, power debug, and device_info into one info command * Fix the storage script * Cli: return device_info command for compatibility, rollback storage script * Cli: remove unused context in info_get calls * Cli: cleanup device info callbacks, switch to new separator Co-authored-by: あく --- applications/services/cli/cli_commands.c | 41 ++++++++++++++++++------ applications/services/power/power_cli.c | 30 ----------------- 2 files changed, 32 insertions(+), 39 deletions(-) diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index b0f1bdbd..ca9d8b98 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -12,20 +12,42 @@ // Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'` #define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d" -void cli_command_device_info_callback(const char* key, const char* value, bool last, void* context) { - UNUSED(context); +void cli_command_info_callback(const char* key, const char* value, bool last, void* context) { UNUSED(last); + UNUSED(context); printf("%-30s: %s\r\n", key, value); } -/* - * Device Info Command +/** Info Command + * * This command is intended to be used by humans + * + * Arguments: + * - device - print device info + * - power - print power info + * - power_debug - print power debug info + * + * @param cli The cli instance + * @param args The arguments + * @param context The context */ -void cli_command_device_info(Cli* cli, FuriString* args, void* context) { +void cli_command_info(Cli* cli, FuriString* args, void* context) { UNUSED(cli); - UNUSED(args); - furi_hal_info_get(cli_command_device_info_callback, '_', context); + + if(context) { + furi_hal_info_get(cli_command_info_callback, '_', NULL); + return; + } + + if(!furi_string_cmp(args, "device")) { + furi_hal_info_get(cli_command_info_callback, '.', NULL); + } else if(!furi_string_cmp(args, "power")) { + furi_hal_power_info_get(cli_command_info_callback, '.', NULL); + } else if(!furi_string_cmp(args, "power_debug")) { + furi_hal_power_debug_get(cli_command_info_callback, NULL); + } else { + cli_print_usage("info", "", furi_string_get_cstr(args)); + } } void cli_command_help(Cli* cli, FuriString* args, void* context) { @@ -410,8 +432,9 @@ void cli_command_i2c(Cli* cli, FuriString* args, void* context) { } void cli_commands_init(Cli* cli) { - cli_add_command(cli, "!", CliCommandFlagParallelSafe, cli_command_device_info, NULL); - cli_add_command(cli, "device_info", CliCommandFlagParallelSafe, cli_command_device_info, NULL); + cli_add_command(cli, "!", CliCommandFlagParallelSafe, cli_command_info, (void*)true); + cli_add_command(cli, "info", CliCommandFlagParallelSafe, cli_command_info, NULL); + cli_add_command(cli, "device_info", CliCommandFlagParallelSafe, cli_command_info, (void*)true); cli_add_command(cli, "?", CliCommandFlagParallelSafe, cli_command_help, NULL); cli_add_command(cli, "help", CliCommandFlagParallelSafe, cli_command_help, NULL); diff --git a/applications/services/power/power_cli.c b/applications/services/power/power_cli.c index f4a10f0a..021ce355 100644 --- a/applications/services/power/power_cli.c +++ b/applications/services/power/power_cli.c @@ -26,24 +26,6 @@ void power_cli_reboot2dfu(Cli* cli, FuriString* args) { power_reboot(PowerBootModeDfu); } -static void power_cli_callback(const char* key, const char* value, bool last, void* context) { - UNUSED(last); - UNUSED(context); - printf("%-24s: %s\r\n", key, value); -} - -void power_cli_info(Cli* cli, FuriString* args) { - UNUSED(cli); - UNUSED(args); - furi_hal_power_info_get(power_cli_callback, '_', NULL); -} - -void power_cli_debug(Cli* cli, FuriString* args) { - UNUSED(cli); - UNUSED(args); - furi_hal_power_debug_get(power_cli_callback, NULL); -} - void power_cli_5v(Cli* cli, FuriString* args) { UNUSED(cli); if(!furi_string_cmp(args, "0")) { @@ -74,8 +56,6 @@ static void power_cli_command_print_usage() { printf("\toff\t - shutdown power\r\n"); printf("\treboot\t - reboot\r\n"); printf("\treboot2dfu\t - reboot to dfu bootloader\r\n"); - printf("\tinfo\t - show power info\r\n"); - printf("\tdebug\t - show debug information\r\n"); printf("\t5v <0 or 1>\t - enable or disable 5v ext\r\n"); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { printf("\t3v3 <0 or 1>\t - enable or disable 3v3 ext\r\n"); @@ -108,16 +88,6 @@ void power_cli(Cli* cli, FuriString* args, void* context) { break; } - if(furi_string_cmp_str(cmd, "info") == 0) { - power_cli_info(cli, args); - break; - } - - if(furi_string_cmp_str(cmd, "debug") == 0) { - power_cli_debug(cli, args); - break; - } - if(furi_string_cmp_str(cmd, "5v") == 0) { power_cli_5v(cli, args); break; From 3efb7d4050ef3183a43e59fd65dc2ef24fd4f604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Mon, 27 Feb 2023 00:15:26 +0900 Subject: [PATCH 23/27] Updater: handle storage errors when removing files, fix folder remove routine, prevent unused services from starting (#2432) * Updater: handle storage errors when removing files * Updater: properly handle folder removal in post update cleanup stage. Prevent power, desktop and dolphin services from starting on update. * Desktop, Dolphin, Power: proper handling and message for special boot mode. * Desktop, Power: add missing TAG * Updater: unify start skip message and fix double delete in backup worker * Cli: unify special boot mode message --- applications/services/bt/bt_service/bt.c | 2 +- applications/services/cli/cli.c | 2 +- applications/services/desktop/desktop.c | 8 +++++ applications/services/dolphin/dolphin.c | 6 ++++ .../services/power/power_service/power.c | 7 ++++ .../updater/util/update_task_worker_backup.c | 33 ++++++++++--------- furi/core/thread.c | 4 +-- furi/flipper.c | 7 ++-- 8 files changed, 46 insertions(+), 23 deletions(-) diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 9e578269..16b60231 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -373,7 +373,7 @@ int32_t bt_srv(void* p) { Bt* bt = bt_alloc(); if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { - FURI_LOG_W(TAG, "Skipped BT init: device in special startup mode"); + FURI_LOG_W(TAG, "Skipping start in special boot mode"); ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT); furi_record_create(RECORD_BT, bt); return 0; diff --git a/applications/services/cli/cli.c b/applications/services/cli/cli.c index 384d1780..b68505c5 100644 --- a/applications/services/cli/cli.c +++ b/applications/services/cli/cli.c @@ -461,7 +461,7 @@ int32_t cli_srv(void* p) { if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) { cli_session_open(cli, &cli_vcp); } else { - FURI_LOG_W(TAG, "Skipped CLI session open: device in special startup mode"); + FURI_LOG_W(TAG, "Skipping start in special boot mode"); } while(1) { diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 848f5cb6..f8716e6c 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -17,6 +17,8 @@ #include "helpers/pin_lock.h" #include "helpers/slideshow_filename.h" +#define TAG "Desktop" + static void desktop_auto_lock_arm(Desktop*); static void desktop_auto_lock_inhibit(Desktop*); static void desktop_start_auto_lock_timer(Desktop*); @@ -321,6 +323,12 @@ static bool desktop_check_file_flag(const char* flag_path) { int32_t desktop_srv(void* p) { UNUSED(p); + + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + FURI_LOG_W(TAG, "Skipping start in special boot mode"); + return 0; + } + Desktop* desktop = desktop_alloc(); bool loaded = DESKTOP_SETTINGS_LOAD(&desktop->settings); diff --git a/applications/services/dolphin/dolphin.c b/applications/services/dolphin/dolphin.c index 41eeef3b..dd8b7105 100644 --- a/applications/services/dolphin/dolphin.c +++ b/applications/services/dolphin/dolphin.c @@ -154,6 +154,12 @@ static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin) { int32_t dolphin_srv(void* p) { UNUSED(p); + + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + FURI_LOG_W(TAG, "Skipping start in special boot mode"); + return 0; + } + Dolphin* dolphin = dolphin_alloc(); furi_record_create(RECORD_DOLPHIN, dolphin); diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 5df611a7..d9319d3d 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -4,6 +4,7 @@ #include #define POWER_OFF_TIMEOUT 90 +#define TAG "Power" void power_draw_battery_callback(Canvas* canvas, void* context) { furi_assert(context); @@ -217,6 +218,12 @@ static void power_check_battery_level_change(Power* power) { int32_t power_srv(void* p) { UNUSED(p); + + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + FURI_LOG_W(TAG, "Skipping start in special boot mode"); + return 0; + } + Power* power = power_alloc(); power_update_info(power); furi_record_create(RECORD_POWER, power); diff --git a/applications/system/updater/util/update_task_worker_backup.c b/applications/system/updater/util/update_task_worker_backup.c index ed53c353..f2c33c2e 100644 --- a/applications/system/updater/util/update_task_worker_backup.c +++ b/applications/system/updater/util/update_task_worker_backup.c @@ -97,7 +97,16 @@ static void update_task_cleanup_resources(UpdateTask* update_task, const uint32_ path_concat( STORAGE_EXT_PATH_PREFIX, furi_string_get_cstr(entry_ptr->name), file_path); FURI_LOG_D(TAG, "Removing %s", furi_string_get_cstr(file_path)); - storage_simply_remove(update_task->storage, furi_string_get_cstr(file_path)); + + FS_Error result = + storage_common_remove(update_task->storage, furi_string_get_cstr(file_path)); + if(result != FSE_OK && result != FSE_EXIST) { + FURI_LOG_E( + TAG, + "%s remove failed, cause %s", + furi_string_get_cstr(file_path), + storage_error_get_desc(result)); + } furi_string_free(file_path); } else if(entry_ptr->type == ResourceManifestEntryTypeDirectory) { n_dir_entries++; @@ -116,7 +125,6 @@ static void update_task_cleanup_resources(UpdateTask* update_task, const uint32_ n_dir_entries); FuriString* folder_path = furi_string_alloc(); - File* folder_file = storage_file_alloc(update_task->storage); do { path_concat( @@ -125,24 +133,17 @@ static void update_task_cleanup_resources(UpdateTask* update_task, const uint32_ folder_path); FURI_LOG_D(TAG, "Removing folder %s", furi_string_get_cstr(folder_path)); - if(!storage_dir_open(folder_file, furi_string_get_cstr(folder_path))) { - FURI_LOG_W( + FS_Error result = storage_common_remove( + update_task->storage, furi_string_get_cstr(folder_path)); + if(result != FSE_OK && result != FSE_EXIST) { + FURI_LOG_E( TAG, - "%s can't be opened, skipping", - furi_string_get_cstr(folder_path)); - break; + "%s remove failed, cause %s", + furi_string_get_cstr(folder_path), + storage_error_get_desc(result)); } - - if(storage_dir_read(folder_file, NULL, NULL, 0)) { - FURI_LOG_I( - TAG, "%s is not empty, skipping", furi_string_get_cstr(folder_path)); - break; - } - - storage_simply_remove(update_task->storage, furi_string_get_cstr(folder_path)); } while(false); - storage_file_free(folder_file); furi_string_free(folder_path); } } diff --git a/furi/core/thread.c b/furi/core/thread.c index 9a112d9a..ea9f45e8 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -96,9 +96,9 @@ static void furi_thread_body(void* context) { furi_assert(thread->state == FuriThreadStateRunning); if(thread->is_service) { - FURI_LOG_E( + FURI_LOG_W( TAG, - "%s service thread exited. Thread memory cannot be reclaimed.", + "%s service thread TCB memory will not be reclaimed", thread->name ? thread->name : ""); } diff --git a/furi/flipper.c b/furi/flipper.c index 73899e58..d16a84a1 100644 --- a/furi/flipper.c +++ b/furi/flipper.c @@ -3,6 +3,7 @@ #include #include #include +#include #define TAG "Flipper" @@ -29,10 +30,10 @@ static void flipper_print_version(const char* target, const Version* version) { void flipper_init() { flipper_print_version("Firmware", furi_hal_version_get_firmware_version()); - FURI_LOG_I(TAG, "starting services"); + FURI_LOG_I(TAG, "Boot mode %d, starting services", furi_hal_rtc_get_boot_mode()); for(size_t i = 0; i < FLIPPER_SERVICES_COUNT; i++) { - FURI_LOG_I(TAG, "starting service %s", FLIPPER_SERVICES[i].name); + FURI_LOG_I(TAG, "Starting service %s", FLIPPER_SERVICES[i].name); FuriThread* thread = furi_thread_alloc_ex( FLIPPER_SERVICES[i].name, @@ -44,7 +45,7 @@ void flipper_init() { furi_thread_start(thread); } - FURI_LOG_I(TAG, "services startup complete"); + FURI_LOG_I(TAG, "Startup complete"); } void vApplicationGetIdleTaskMemory( From 1d55aee39cbb329758a50f0110f6d69bc0d00274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Mon, 27 Feb 2023 01:29:42 +0900 Subject: [PATCH 24/27] Fix incorrect type choise condition in image compressor (#2434) --- scripts/flipper/assets/icon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/flipper/assets/icon.py b/scripts/flipper/assets/icon.py index ed85b024..f0dae25b 100644 --- a/scripts/flipper/assets/icon.py +++ b/scripts/flipper/assets/icon.py @@ -105,7 +105,7 @@ def file2image(file): data_enc = bytearray([len(data_enc) & 0xFF, len(data_enc) >> 8]) + data_enc # Use encoded data only if its length less than original, including header - if len(data_enc) < len(data_bin) + 1: + if len(data_enc) + 2 < len(data_bin) + 1: data = b"\x01\x00" + data_enc else: data = b"\x00" + data_bin From 09edf66a2a00c20bce65203c84c6081c9d129aa7 Mon Sep 17 00:00:00 2001 From: Shane Synan Date: Sun, 26 Feb 2023 12:23:39 -0500 Subject: [PATCH 25/27] FuriHal, Power, UnitTests: fix, rename battery charging voltage limit API (#2228) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FuriHal, Power, UnitTests: rename battery charge voltage limit API * FuriHal: bump API, power info major versions * Power: fix battery charge voltage limit for > 7.935v Co-authored-by: あく --- .../debug/unit_tests/power/power_test.c | 61 +++++++++++-------- .../services/power/power_service/power.c | 6 +- .../services/power/power_service/power.h | 2 +- .../power_settings_scene_battery_info.c | 2 +- .../power_settings_app/views/battery_info.c | 6 +- .../power_settings_app/views/battery_info.h | 2 +- firmware/targets/f7/api_symbols.csv | 4 +- firmware/targets/f7/furi_hal/furi_hal_power.c | 12 ++-- .../targets/furi_hal_include/furi_hal_power.h | 10 +-- lib/drivers/bq25896.c | 17 +++--- lib/drivers/bq25896.h | 4 +- 11 files changed, 69 insertions(+), 57 deletions(-) diff --git a/applications/debug/unit_tests/power/power_test.c b/applications/debug/unit_tests/power/power_test.c index ce2c7aad..a9b66b22 100644 --- a/applications/debug/unit_tests/power/power_test.c +++ b/applications/debug/unit_tests/power/power_test.c @@ -3,56 +3,63 @@ #include "../minunit.h" static void power_test_deinit(void) { - // Try to reset to default charging voltage - furi_hal_power_set_battery_charging_voltage(4.208f); + // Try to reset to default charge voltage limit + furi_hal_power_set_battery_charge_voltage_limit(4.208f); } -MU_TEST(test_power_charge_voltage_exact) { - // Power of 16mV charge voltages get applied exactly +MU_TEST(test_power_charge_voltage_limit_exact) { + // Power of 16mV charge voltage limits get applied exactly // (bq25896 charge controller works in 16mV increments) // // This test may need adapted if other charge controllers are used in the future. for(uint16_t charge_mv = 3840; charge_mv <= 4208; charge_mv += 16) { float charge_volt = (float)charge_mv / 1000.0f; - furi_hal_power_set_battery_charging_voltage(charge_volt); - mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charging_voltage()); + furi_hal_power_set_battery_charge_voltage_limit(charge_volt); + mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charge_voltage_limit()); } } -MU_TEST(test_power_charge_voltage_floating_imprecision) { +MU_TEST(test_power_charge_voltage_limit_floating_imprecision) { // 4.016f should act as 4.016 V, even with floating point imprecision - furi_hal_power_set_battery_charging_voltage(4.016f); - mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charging_voltage()); + furi_hal_power_set_battery_charge_voltage_limit(4.016f); + mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charge_voltage_limit()); } -MU_TEST(test_power_charge_voltage_inexact) { - // Charge voltages that are not power of 16mV get truncated down - furi_hal_power_set_battery_charging_voltage(3.841f); - mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage()); +MU_TEST(test_power_charge_voltage_limit_inexact) { + // Charge voltage limits that are not power of 16mV get truncated down + furi_hal_power_set_battery_charge_voltage_limit(3.841f); + mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit()); - furi_hal_power_set_battery_charging_voltage(3.900f); - mu_assert_double_eq(3.888, furi_hal_power_get_battery_charging_voltage()); + furi_hal_power_set_battery_charge_voltage_limit(3.900f); + mu_assert_double_eq(3.888, furi_hal_power_get_battery_charge_voltage_limit()); - furi_hal_power_set_battery_charging_voltage(4.200f); - mu_assert_double_eq(4.192, furi_hal_power_get_battery_charging_voltage()); + furi_hal_power_set_battery_charge_voltage_limit(4.200f); + mu_assert_double_eq(4.192, furi_hal_power_get_battery_charge_voltage_limit()); } -MU_TEST(test_power_charge_voltage_invalid_clamped) { - // Out-of-range charge voltages get clamped to 3.840 V and 4.208 V - furi_hal_power_set_battery_charging_voltage(3.808f); - mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage()); +MU_TEST(test_power_charge_voltage_limit_invalid_clamped) { + // Out-of-range charge voltage limits get clamped to 3.840 V and 4.208 V + furi_hal_power_set_battery_charge_voltage_limit(3.808f); + mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit()); + furi_hal_power_set_battery_charge_voltage_limit(1.0f); + mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit()); // NOTE: Intentionally picking a small increment above 4.208 V to reduce the risk of an // unhappy battery if this fails. - furi_hal_power_set_battery_charging_voltage(4.240f); - mu_assert_double_eq(4.208, furi_hal_power_get_battery_charging_voltage()); + furi_hal_power_set_battery_charge_voltage_limit(4.240f); + mu_assert_double_eq(4.208, furi_hal_power_get_battery_charge_voltage_limit()); + // Likewise, picking a number that the uint8_t wraparound in the driver would result in a + // VREG value under 23 if this test fails. + // E.g. (uint8_t)((8105-3840)/16) -> 10 + furi_hal_power_set_battery_charge_voltage_limit(8.105f); + mu_assert_double_eq(4.208, furi_hal_power_get_battery_charge_voltage_limit()); } MU_TEST_SUITE(test_power_suite) { - MU_RUN_TEST(test_power_charge_voltage_exact); - MU_RUN_TEST(test_power_charge_voltage_floating_imprecision); - MU_RUN_TEST(test_power_charge_voltage_inexact); - MU_RUN_TEST(test_power_charge_voltage_invalid_clamped); + MU_RUN_TEST(test_power_charge_voltage_limit_exact); + MU_RUN_TEST(test_power_charge_voltage_limit_floating_imprecision); + MU_RUN_TEST(test_power_charge_voltage_limit_inexact); + MU_RUN_TEST(test_power_charge_voltage_limit_invalid_clamped); power_test_deinit(); } diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index d9319d3d..56dbd0f8 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -13,8 +13,8 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { if(power->info.gauge_is_ok) { canvas_draw_box(canvas, 2, 2, (power->info.charge + 4) / 5, 4); - if(power->info.voltage_battery_charging < 4.2) { - // Battery charging voltage is modified, indicate with cross pattern + if(power->info.voltage_battery_charge_limit < 4.2) { + // Battery charge voltage limit is modified, indicate with cross pattern canvas_invert_color(canvas); uint8_t battery_bar_width = (power->info.charge + 4) / 5; bool cross_odd = false; @@ -147,7 +147,7 @@ static bool power_update_info(Power* power) { info.capacity_full = furi_hal_power_get_battery_full_capacity(); info.current_charger = furi_hal_power_get_battery_current(FuriHalPowerICCharger); info.current_gauge = furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge); - info.voltage_battery_charging = furi_hal_power_get_battery_charging_voltage(); + info.voltage_battery_charge_limit = furi_hal_power_get_battery_charge_voltage_limit(); info.voltage_charger = furi_hal_power_get_battery_voltage(FuriHalPowerICCharger); info.voltage_gauge = furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge); info.voltage_vbus = furi_hal_power_get_usb_voltage(); diff --git a/applications/services/power/power_service/power.h b/applications/services/power/power_service/power.h index 8b9019c4..c7f5d7e3 100644 --- a/applications/services/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -41,7 +41,7 @@ typedef struct { float current_charger; float current_gauge; - float voltage_battery_charging; + float voltage_battery_charge_limit; float voltage_charger; float voltage_gauge; float voltage_vbus; diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c b/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c index 5fa38df7..5181c93f 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c @@ -7,7 +7,7 @@ static void power_settings_scene_battery_info_update_model(PowerSettingsApp* app .gauge_voltage = app->info.voltage_gauge, .gauge_current = app->info.current_gauge, .gauge_temperature = app->info.temperature_gauge, - .charging_voltage = app->info.voltage_battery_charging, + .charge_voltage_limit = app->info.voltage_battery_charge_limit, .charge = app->info.charge, .health = app->info.health, }; diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index d29769d2..7394fd3c 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -69,7 +69,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); } else if(drain_current != 0) { snprintf(header, 20, "..."); - } else if(data->charging_voltage < 4.2) { + } else if(data->charge_voltage_limit < 4.2) { // Non-default battery charging limit, mention it snprintf(emote, sizeof(emote), "Charged!"); snprintf(header, sizeof(header), "Limited to"); @@ -77,8 +77,8 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { value, sizeof(value), "%lu.%luV", - (uint32_t)(data->charging_voltage), - (uint32_t)(data->charging_voltage * 10) % 10); + (uint32_t)(data->charge_voltage_limit), + (uint32_t)(data->charge_voltage_limit * 10) % 10); } else { snprintf(header, sizeof(header), "Charged!"); } diff --git a/applications/settings/power_settings_app/views/battery_info.h b/applications/settings/power_settings_app/views/battery_info.h index 7bfacf69..e52d1844 100644 --- a/applications/settings/power_settings_app/views/battery_info.h +++ b/applications/settings/power_settings_app/views/battery_info.h @@ -9,7 +9,7 @@ typedef struct { float gauge_voltage; float gauge_current; float gauge_temperature; - float charging_voltage; + float charge_voltage_limit; uint8_t charge; uint8_t health; } BatteryInfoModel; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index e320fc92..8a76f8c9 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1211,7 +1211,7 @@ Function,+,furi_hal_power_enable_external_3_3v,void, Function,+,furi_hal_power_enable_otg,void, Function,+,furi_hal_power_gauge_is_ok,_Bool, Function,+,furi_hal_power_get_bat_health_pct,uint8_t, -Function,+,furi_hal_power_get_battery_charging_voltage,float, +Function,+,furi_hal_power_get_battery_charge_voltage_limit,float, Function,+,furi_hal_power_get_battery_current,float,FuriHalPowerIC Function,+,furi_hal_power_get_battery_design_capacity,uint32_t, Function,+,furi_hal_power_get_battery_full_capacity,uint32_t, @@ -1230,7 +1230,7 @@ Function,+,furi_hal_power_is_charging_done,_Bool, Function,+,furi_hal_power_is_otg_enabled,_Bool, Function,+,furi_hal_power_off,void, Function,+,furi_hal_power_reset,void, -Function,+,furi_hal_power_set_battery_charging_voltage,void,float +Function,+,furi_hal_power_set_battery_charge_voltage_limit,void,float Function,+,furi_hal_power_shutdown,void, Function,+,furi_hal_power_sleep,void, Function,+,furi_hal_power_sleep_available,_Bool, diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index 2d709620..dd7c34ae 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -341,14 +341,14 @@ bool furi_hal_power_is_otg_enabled() { return ret; } -float furi_hal_power_get_battery_charging_voltage() { +float furi_hal_power_get_battery_charge_voltage_limit() { furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); float ret = (float)bq25896_get_vreg_voltage(&furi_hal_i2c_handle_power) / 1000.0f; furi_hal_i2c_release(&furi_hal_i2c_handle_power); return ret; } -void furi_hal_power_set_battery_charging_voltage(float voltage) { +void furi_hal_power_set_battery_charge_voltage_limit(float voltage) { furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); // Adding 0.0005 is necessary because 4.016f is 4.015999794000, which gets truncated bq25896_set_vreg_voltage(&furi_hal_i2c_handle_power, (uint16_t)(voltage * 1000.0f + 0.0005f)); @@ -486,7 +486,7 @@ void furi_hal_power_info_get(PropertyValueCallback out, char sep, void* context) property_value_out(&property_context, NULL, 2, "format", "major", "2"); property_value_out(&property_context, NULL, 2, "format", "minor", "1"); } else { - property_value_out(&property_context, NULL, 3, "power", "info", "major", "1"); + property_value_out(&property_context, NULL, 3, "power", "info", "major", "2"); property_value_out(&property_context, NULL, 3, "power", "info", "minor", "1"); } @@ -505,8 +505,10 @@ void furi_hal_power_info_get(PropertyValueCallback out, char sep, void* context) } property_value_out(&property_context, NULL, 2, "charge", "state", charge_state); - uint16_t charge_voltage = (uint16_t)(furi_hal_power_get_battery_charging_voltage() * 1000.f); - property_value_out(&property_context, "%u", 2, "charge", "voltage", charge_voltage); + uint16_t charge_voltage_limit = + (uint16_t)(furi_hal_power_get_battery_charge_voltage_limit() * 1000.f); + property_value_out( + &property_context, "%u", 3, "charge", "voltage", "limit", charge_voltage_limit); uint16_t voltage = (uint16_t)(furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge) * 1000.f); property_value_out(&property_context, "%u", 2, "battery", "voltage", voltage); diff --git a/firmware/targets/furi_hal_include/furi_hal_power.h b/firmware/targets/furi_hal_include/furi_hal_power.h index 39a11e99..462e20e4 100644 --- a/firmware/targets/furi_hal_include/furi_hal_power.h +++ b/firmware/targets/furi_hal_include/furi_hal_power.h @@ -121,21 +121,21 @@ void furi_hal_power_check_otg_status(); */ bool furi_hal_power_is_otg_enabled(); -/** Get battery charging voltage in V +/** Get battery charge voltage limit in V * * @return voltage in V */ -float furi_hal_power_get_battery_charging_voltage(); +float furi_hal_power_get_battery_charge_voltage_limit(); -/** Set battery charging voltage in V +/** Set battery charge voltage limit in V * - * Invalid values will be clamped to the nearest valid value. + * Invalid values will be clamped downward to the nearest valid value. * * @param voltage[in] voltage in V * * @return voltage in V */ -void furi_hal_power_set_battery_charging_voltage(float voltage); +void furi_hal_power_set_battery_charge_voltage_limit(float voltage); /** Get remaining battery battery capacity in mAh * diff --git a/lib/drivers/bq25896.c b/lib/drivers/bq25896.c index 7e3008d6..99534fb1 100644 --- a/lib/drivers/bq25896.c +++ b/lib/drivers/bq25896.c @@ -140,15 +140,18 @@ uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle) { void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage) { if(vreg_voltage < 3840) { - // Minimum value is 3840 mV - bq25896_regs.r06.VREG = 0; - } else { - // Find the nearest voltage value (subtract offset, divide into sections) - // Values are truncated downward as needed (e.g. 4200mV -> 4192 mV) - bq25896_regs.r06.VREG = (uint8_t)((vreg_voltage - 3840) / 16); + // Minimum valid value is 3840 mV + vreg_voltage = 3840; + } else if(vreg_voltage > 4208) { + // Maximum safe value is 4208 mV + vreg_voltage = 4208; } - // Do not allow values above 23 (0x17, 4208mV) + // Find the nearest voltage value (subtract offset, divide into sections) + // Values are truncated downward as needed (e.g. 4200mV -> 4192 mV) + bq25896_regs.r06.VREG = (uint8_t)((vreg_voltage - 3840) / 16); + + // Double check: do not allow values above 23 (0x17, 4208mV) // Exceeding 4.2v will overcharge the battery! if(bq25896_regs.r06.VREG > 23) { bq25896_regs.r06.VREG = 23; diff --git a/lib/drivers/bq25896.h b/lib/drivers/bq25896.h index c8a8526a..f3d1d0e0 100644 --- a/lib/drivers/bq25896.h +++ b/lib/drivers/bq25896.h @@ -36,10 +36,10 @@ void bq25896_disable_otg(FuriHalI2cBusHandle* handle); /** Is otg enabled */ bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle); -/** Get VREG (charging) voltage in mV */ +/** Get VREG (charging limit) voltage in mV */ uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle); -/** Set VREG (charging) voltage in mV +/** Set VREG (charging limit) voltage in mV * * Valid range: 3840mV - 4208mV, in steps of 16mV */ From b054912167764e37e968a8996fc50047392c6be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Mon, 27 Feb 2023 02:39:26 +0900 Subject: [PATCH 26/27] F8, F18: bump API symbols version (#2435) --- firmware/targets/f18/api_symbols.csv | 6 +++--- firmware/targets/f7/api_symbols.csv | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 462fbf73..549d3812 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,14.0,, +Version,+,15.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -979,7 +979,7 @@ Function,+,furi_hal_power_enable_external_3_3v,void, Function,+,furi_hal_power_enable_otg,void, Function,+,furi_hal_power_gauge_is_ok,_Bool, Function,+,furi_hal_power_get_bat_health_pct,uint8_t, -Function,+,furi_hal_power_get_battery_charging_voltage,float, +Function,+,furi_hal_power_get_battery_charge_voltage_limit,float, Function,+,furi_hal_power_get_battery_current,float,FuriHalPowerIC Function,+,furi_hal_power_get_battery_design_capacity,uint32_t, Function,+,furi_hal_power_get_battery_full_capacity,uint32_t, @@ -998,7 +998,7 @@ Function,+,furi_hal_power_is_charging_done,_Bool, Function,+,furi_hal_power_is_otg_enabled,_Bool, Function,+,furi_hal_power_off,void, Function,+,furi_hal_power_reset,void, -Function,+,furi_hal_power_set_battery_charging_voltage,void,float +Function,+,furi_hal_power_set_battery_charge_voltage_limit,void,float Function,+,furi_hal_power_shutdown,void, Function,+,furi_hal_power_sleep,void, Function,+,furi_hal_power_sleep_available,_Bool, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 8a76f8c9..8152095d 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,14.1,, +Version,+,15.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, From 478390de191f876ef12ed9a54301390a01e8ce40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Mon, 27 Feb 2023 03:06:19 +0900 Subject: [PATCH 27/27] Drivers: remove excessive check in bq25896 and make PVS happy (#2436) --- lib/drivers/bq25896.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/drivers/bq25896.c b/lib/drivers/bq25896.c index 99534fb1..4c1d687c 100644 --- a/lib/drivers/bq25896.c +++ b/lib/drivers/bq25896.c @@ -151,12 +151,6 @@ void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage // Values are truncated downward as needed (e.g. 4200mV -> 4192 mV) bq25896_regs.r06.VREG = (uint8_t)((vreg_voltage - 3840) / 16); - // Double check: do not allow values above 23 (0x17, 4208mV) - // Exceeding 4.2v will overcharge the battery! - if(bq25896_regs.r06.VREG > 23) { - bq25896_regs.r06.VREG = 23; - } - // Apply changes furi_hal_i2c_write_reg_8( handle, BQ25896_ADDRESS, 0x06, *(uint8_t*)&bq25896_regs.r06, BQ25896_I2C_TIMEOUT);