From 7b32e5f01f58bce2ada968ad44d7687429c43840 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 15 Jul 2019 19:28:15 +0100 Subject: [PATCH 001/158] trivial: post release version bump --- RELEASE | 4 ++-- meson.build | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/RELEASE b/RELEASE index a5559f993..b425e191b 100644 --- a/RELEASE +++ b/RELEASE @@ -2,7 +2,7 @@ fwupd Release Notes Write release entries: -git log --format="%s" --cherry-pick --right-only 1.2.9... | grep -i -v trivial | grep -v Merge | sort | uniq +git log --format="%s" --cherry-pick --right-only 1.2.10... | grep -i -v trivial | grep -v Merge | sort | uniq Add any user visible changes into ../data/org.freedesktop.fwupd.metainfo.xml appstream-util appdata-to-news ../data/org.freedesktop.fwupd.metainfo.xml > NEWS @@ -17,7 +17,7 @@ git add ../po/*.po 2. Commit changes to git: # MAKE SURE THIS IS CORRECT -export release_ver="1.2.10" +export release_ver="1.2.11" git commit -a -m "Release fwupd ${release_ver}" git tag -s -f -m "Release fwupd ${release_ver}" "${release_ver}" diff --git a/meson.build b/meson.build index 1364e3b0b..1c177e0b3 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('fwupd', 'c', - version : '1.2.10', + version : '1.2.11', license : 'LGPL-2.1+', meson_version : '>=0.47.0', default_options : ['warning_level=2', 'c_std=c99'], From aaf0ce788f7ef1dc449c52b129a81615663cece0 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 16 Jul 2019 08:43:57 +0100 Subject: [PATCH 002/158] Allow specifying GUID to check any version exists If the version is not specified then treat it as just 'exists'. --- src/fu-engine.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fu-engine.c b/src/fu-engine.c index ced430f42..88b6d96ef 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -1096,7 +1096,8 @@ fu_engine_check_requirement_firmware (FuEngine *self, XbNode *req, /* get the version of the other device */ version = fu_device_get_version (device2); - if (!fu_engine_require_vercmp (req, version, &error_local)) { + if (version != NULL && + !fu_engine_require_vercmp (req, version, &error_local)) { if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) { g_set_error (error, FWUPD_ERROR, From 09700bbce8b2016056079cf73b0408e9afbc5301 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 22 Jul 2019 15:23:56 -0500 Subject: [PATCH 003/158] trivial: debian: For signed package fill out Built-Using (Closes: #932757) --- contrib/debian/signing-template/control | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contrib/debian/signing-template/control b/contrib/debian/signing-template/control index 70282f73c..3006d94d1 100644 --- a/contrib/debian/signing-template/control +++ b/contrib/debian/signing-template/control @@ -14,9 +14,10 @@ Section: admin Architecture: SIGNARCH Provides: fwupd-signed Depends: ${shlibs:Depends}, ${misc:Depends}, fwupd (= SIGNVERSION) +Built-Using: fwupd (= SIGNVERSION) Description: Tools to manage UEFI firmware updates (signed) - fwupd provides functionality to update system firmware. It has been - initially designed to update firmware using UEFI capsule updates, but + fwupd provides functionality to update system firmware. It has been + initially designed to update firmware using UEFI capsule updates, but it is designed to be extensible to other firmware update standards. . This package contains just the signed version of the fwupd binary, From f5c15354e584e813d6b1cbee8911a1f96854d1c4 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 30 Jul 2019 09:58:04 +0100 Subject: [PATCH 004/158] trivial: Allow a device to *clear* a firmware version This can be used, for instance, when doing klass_usb_device->probe() to prevent the bcdVersion from overwriting a previously set runtime version. --- src/fu-device.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/fu-device.c b/src/fu-device.c index 285e5e357..2248e68ab 100644 --- a/src/fu-device.c +++ b/src/fu-device.c @@ -1218,7 +1218,7 @@ fu_device_set_id (FuDevice *self, const gchar *id) /** * fu_device_set_version: * @self: A #FuDevice - * @version: a string, e.g. `1.2.3` + * @version: (allow-none): a string, e.g. `1.2.3` * @fmt: a #FwupdVersionFormat, e.g. %FWUPD_VERSION_FORMAT_TRIPLET * * Sets the device version, sanitizing the string if required. @@ -1232,7 +1232,6 @@ fu_device_set_version (FuDevice *self, const gchar *version, FwupdVersionFormat g_autoptr(GError) error = NULL; g_return_if_fail (FU_IS_DEVICE (self)); - g_return_if_fail (version != NULL); /* sanitize if required */ if (fu_device_has_flag (self, FWUPD_DEVICE_FLAG_ENSURE_SEMVER)) { From d8f78d0b93f32194b3d94136b8954e85d1e10bbf Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 29 Jul 2019 12:18:12 +0100 Subject: [PATCH 005/158] Add support for the SoloKey Secure --- contrib/fwupd.spec.in | 1 + plugins/meson.build | 1 + plugins/solokey/README.md | 28 + .../solokey/data/bootloader-version-hid.tdc | Bin 0 -> 2312 bytes plugins/solokey/data/bootloader-version.tdc | Bin 0 -> 2099 bytes plugins/solokey/data/firmware-update.tdc | Bin 0 -> 158011 bytes plugins/solokey/data/firmware-version.tdc | Bin 0 -> 2804 bytes plugins/solokey/data/lsusb.txt | 137 +++++ plugins/solokey/fu-plugin-solokey.c | 52 ++ plugins/solokey/fu-solokey-device.c | 547 ++++++++++++++++++ plugins/solokey/fu-solokey-device.h | 18 + plugins/solokey/meson.build | 32 + plugins/solokey/solokey.quirk | 4 + 13 files changed, 820 insertions(+) create mode 100644 plugins/solokey/README.md create mode 100644 plugins/solokey/data/bootloader-version-hid.tdc create mode 100644 plugins/solokey/data/bootloader-version.tdc create mode 100644 plugins/solokey/data/firmware-update.tdc create mode 100644 plugins/solokey/data/firmware-version.tdc create mode 100644 plugins/solokey/data/lsusb.txt create mode 100644 plugins/solokey/fu-plugin-solokey.c create mode 100644 plugins/solokey/fu-solokey-device.c create mode 100644 plugins/solokey/fu-solokey-device.h create mode 100644 plugins/solokey/meson.build create mode 100644 plugins/solokey/solokey.quirk diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index ec48ddfb3..edf90f99f 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -311,6 +311,7 @@ rm ${RPM_BUILD_ROOT}%{_sbindir}/flashrom %endif %{_libdir}/fwupd-plugins-3/libfu_plugin_rts54hid.so %{_libdir}/fwupd-plugins-3/libfu_plugin_rts54hub.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_solokey.so %{_libdir}/fwupd-plugins-3/libfu_plugin_steelseries.so %{_libdir}/fwupd-plugins-3/libfu_plugin_superio.so %if 0%{?have_dell} diff --git a/plugins/meson.build b/plugins/meson.build index b45621f1f..68daba8c6 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -8,6 +8,7 @@ subdir('dell-dock') subdir('nitrokey') subdir('rts54hid') subdir('rts54hub') +subdir('solokey') subdir('synaptics-prometheus') subdir('test') subdir('udev') diff --git a/plugins/solokey/README.md b/plugins/solokey/README.md new file mode 100644 index 000000000..5ce036642 --- /dev/null +++ b/plugins/solokey/README.md @@ -0,0 +1,28 @@ +SoloKey Support +=============== + +Introduction +------------ + +The SoloKey Secure and Hacker is an affordable open source FIDO2 security key. + +All hardware supports the U2F HID flashing protocol. The Hacker version is not +supported and the existing DFU update procedure should be used. + +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmware blob in +a JSON wrapped, base-64, Intel hex file. + +This plugin supports the following protocol ID: + + * com.solokeys + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_0483&PID_A2CA&REV_0001` + * `USB\VID_0483&PID_A2CA` diff --git a/plugins/solokey/data/bootloader-version-hid.tdc b/plugins/solokey/data/bootloader-version-hid.tdc new file mode 100644 index 0000000000000000000000000000000000000000..e00aa7cf42cf4a73b14f106463f7437e08c7009f GIT binary patch literal 2312 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 Fz%S##=1l+q literal 0 HcmV?d00001 diff --git a/plugins/solokey/data/firmware-update.tdc b/plugins/solokey/data/firmware-update.tdc new file mode 100644 index 0000000000000000000000000000000000000000..943a29d5408d1d9450a3bb76acca31dbc93eef6d GIT binary patch literal 158011 zcmeFacU)A*7xzDN@AjoE(m~|zE`32!ELezl0au!_5MxgkQ3Ek1s4>=r1v^UA*fmCs zvBem%U?d3vV~HsyV2PS&Tx?k5q9Ci3`<%J6OZfg?zyF_qp1fXbLAdk2pL6ES%sDfY z*M=o0A@&+Z@Shx|%D%okaDp2CM!ujyGd`A)e?abtg{k+lP*n`Ra-3TY%~-y$Ohq1?L5Vb8-XT(CM2H`2 zRG6o-=)~~TyauDe99xyZDa?aqjL-wCS$EEzM|>P}9xdcv{*msaCV2Lpb%-`MLscZHn;Go zK*l26!=vCVX6c(G8;gFt1Ajg9#Imf`_bd}Bjn2+YsS4#r6A`UFzc1xa#Fdqcg%tSt%jCZxn zXX*og&pHcG$k!_2ai%9gEd^n2$uhIfqDpNbmt>7bcyn(M#g7}nCt6P8Z##*q5F&pr z5ga*DR3smy{EzA^3gEiHzYSv0zrRu`x=VWkRv_1(h!L!+$toi4b|&pE#?bCEc&)pt zJxQ5tH3~@Pl4L8xS3gG@qg94DKdUFg`wNUkD2UTr@kx9Sl@xN`H}T3vTM^FwGfBpy zyUZTc#jH6&B>L!oB|^cR5sX!YnQy5?y`=Hknc$!n=w9f<9Ywc-GZ`&HwTy)Ev|pf# z=m^=oCqTB4Dzf7&BSDNY(;9{F`9e@c&-JUtXL0Sn?Sh05L9&uXLFWB`1PLXASOzgZ zdoN~XpnwNHZ!vwPinrg27;Od$CT35usm+3Rb$-CUg~Ce$EHTW9Yk!)jGLtmN9-s>d z-BF~W25o4R7aNaI1Q!GkOJKggy2`4WTG)&PZ5itH09{9s=3Z5Os&+o~O=XqRE0Xk7 z6vg$0@14aY?KYhpVRflR2F?e*5QE43W!Z^udRT*MQ8e)u=KPfuc$!sRi(rlts6ai2%*4%B(jyAsVl$tl_3MW_dtpfi;uFtWlT)9Bmov5(X{ zP#o9Q3VmeiTnm3k{o=mc5NWkS#iL=9UxZ9nR3Ywu9#Zt3ZdJmdNxVak%a10T z=`p|0A+B)(bBe5_JDL}$7pDW;6R)t`R>j_7U(k+|9E*Ds`wwF7bzcDn34G+UgE=ib zn^lDRa0$dmi>4Qw6Y6w}LKIep$N%P7c<6|AZi@h>o@ zKs3Ax8m{GvP(RKwa`0|A{y~A7yxJw%6);^o*N?p1TM?U~HIzZR@VQ{IHN?Kv zRL@GL)*;DVH+e0d2~pF%ip7)1RjF}y0?0aq(?V!Lu}&+#>QuCY712=f1v9SDMz8k1 z28|BmVAMauEb#f1yy945!CKe$SCpO@WtGagjkPGBQ<;&K)$<+Ir9Kor{yCyqZ z%oJOM7rpKKpQNvulMpHygw1DYo7ZJlXmcdj7h#UIEM!(owv%^#of`lRGtZhoGxH7V z01#IxOTUs+^^D>22KN~6`WRYHJ8xeXn`Fc6kDQ$IcF{0AlYYUz4plvaVc(re^UUG3 z`0H*!Hp(g~M6Z_=)`k?J(Ilu9$f^<>d;{9$A0z7U{?P*BS8Q$9+g(iWqOn}xTAa!p ze7Ykmbr8BKAKnML^C&cqizJ#TAAnb{Pb|A5+Y5Az+|#_{O`PthU7IxxRc7U9y@wTS zRWM>XcbVR1WBrmee0N@te?0~~ldQ8V`2avzq|}6>&PGhKB&-UqqW-Xp2u*~>J!-(Po}|~X=Oc;MBh!d zELLyhv;C5Ov@g?Z@vgaml0~GMK&6@JBO{b|aZ;3c0)O~!*=fAF!BMMXc(O$~)SY`vBTq;b(_g|V5NWXuZ z^p(J)0J55b$W=UEi?=eRA-hmz@K5cB&XilPf6-!4M6fPAt_>RetCcfr1uC({O2?rscYG# zOM;PPO0Z00MOG#)Q}4^-!@Vetq!euGDIbWw;AuOfG zH=tfVH6kDgmqr=PM5+chk(N(M1rq2Ew5Vz~t5E2P7AT6Q@_ zTu9`zj0O3Q-_m-!y0bQ`U1(Y<=u=qReNM@479vc>70ghZMeol}5_V1vSu35nGkfP$ zy;-gfNFSS{vsovOHIMb4VnB z>30Itclh@r6kksw;kp#+TN>>sj1sk@IJuM`k!V9Bc(uBdOD9QUL;u7?TRy|Pt8@+d zOk#tg(^zu=r&cZ)k=TGn1eiDHC3&OeTqV(}X|Sb`-*ij9sX^n0iku?|b+{=;SRkMk zBG>?2A=;Et%3VOMm2}XWG_aDYRZP_SvNx!;7u53d5Sq%{gr+P(k&bZH;e~cRimxcT zoH34#4a16jLDd^pux{ObnTXUCZuQ10Y0sy zF!4(Qx2_%TiNlTn(I4JnJ@g?mx0rOD0(!{30B{3cy0~;cO#n9%Kp(PTx&wgs-HfcK z&@@216!i!%MtpcN^Fx~_kYt6$F|L9Kr$leVCm^hkgf|OK-)f6d4#zSYLA}t39>Bvl z5eJ(GODk}Dv{`g8634H0bg(U=YIDT(R1xDX_k*9?N>y8}JWHTH6;*?^`(yP@GQN0) ztU+A(9_HINMbstFadKHT|8$RsET!U}hDWyA3Qa}EOcsSSpychRjxd_Ci0Ev5aCg!K zw2fFXwL~0lKND$SxP9zDowJH?34(a z3V0Oqjtq`Df^f(}W@}rhwm2i3!3K$T4fBnME_$-$$5wD4y(ikBcSpS;+we-;qLY%u zHbGE;Jw7Mm6p>{XjQ%^FIOvZKE``5_z`Szz9GT4@ zL%V6@8gL!JY;>q&e40{rjAJs|R%=(G;o51qvMpn{{Pnh-Ii5BbcT<0Kp7Ta$V_qbm zl)cvGmcrPYLM~xD0ra6Bioopug~A+Ym+4SPn6E{cvkPlzEC`iHz$ED)b)w!sUZ5U`l`a3(h1@Wo>?(eDHpjwFQ(%k5a)zDH$Q|+Cap0y`l0lmc>LolN_$?V_#r}-ptt#6!Yb-u^iJ^i?2xVF7{*-Y~* z@>oDCr)cueu2Qsb30n4g3vbsQ^a{wr`#96N`|PX(cF8biJ!3Py)W1@$w6J>f&K!M1 zN!-}sN*uP+?DC~quhH@brAzN~_ubyote^J?BKvnlubnFdReMGlKtIm+gInLIX| z_tl4b?limU7`<%YP}^$$+*%2)^*tcC-B?5=BQ@DN)~U z1Hfo!Dbcu`o0RBhLbUx&3y;3KuQhrGDMp^VuX>%aWxmko#3kv?-leu43%RBJ3>b6T z*^*?T=~(MnL#HFH+8nH3`*P>g!!HY;ZsFOUAAn515Sb8DB1WlSom8l#!}JROMxzw( zcZ)k!>tDnq$a%mz$3f1IVtgT=( zTlcBJoL5D2_N6R;iE6&zhpeVPBe2IkhG%LdNEKHIwz^bkGPhd8_sLMmx%)`ZhVPLwJ#$S`eb_+6b00vG z>)s@g<{wT;W-p^C&>azGF}=8s|yNHfZ#*EZ%EHY5=~cxZkZty$nN=LPZ!R`f+{rvEBFVY+a&?! zsfZLDY^UPGpx^*s_PI595EL|mc1m3vgR)sw7sH#vM(I**#CR$XdO7F&S{0sIEoX&6qP_;SV8iVk~ zti&(TJuT4qSZtDWo10`-O1dM z-<#A6J*0EE-NggU33qf==n(X<8SoNK| zFZYa^%ru~xQHwFF3F+hJ(o?ohxz25!EJLoB^|XO~9+RLnX_2Myc1SPuuV@~a!QJt# zYKM9v_N^}x5@<-eFDH2c8+Di`^9Rz7Ur&ho%>+rlxaA6ozgsGAgko19cWplYs%^+* zsfNuqYf5_svay)gL?w-oVnb^W&;GL{{;e@-7h*Q6t=WN+K$j$2Wp;T8a6RRciFn&) zSj52eOe7=8lg;pGP>t>61nf7%Or}uB4Lk*WEVlt*BZ)+3n67UpniFojm1t8RD6-IV zLYbZgl+_TN9=bh};-m%~ccJe=w~t$drrxax77dyYgr=pOE9z!;g&dhHvcM+-{=6D~ zp9>!??}|UW4v0-ey06V5V0^KeW^2!O?DQ`z$t8oB-~5v(S^YqgICx2=^Bp~irZAcb zRR5s>brzt)4BB7sDhx8_aP@4VX@+(J3NTKPA1<_WNN9Q+3Pznu+>s7`p9min{8`Vr z%iV-^hlV!1X#uDf0`=LeRJGQ2)vDUDq@C22MB(+H8KLQ&_)Mhgdqc9Mt-cQB z3tq7bN&47a6jiwjYGnv>`5aSn65ky_=@@<>CuZmb-)1o8TyfRM!V(!bG}27$3waA* zNw@^~xrNN&p6MhyNyWeNc%{L@zfk`HOs{MH~vnOB-u zx_2syaOvjmuQ93L=4@8;!oJo9$#K7$ zmFa#H$L7L89WKsSKMcIsF9;xcwzqPZw2u;TY zb*?%qEmzkdOw`%rOj=fYZqkD)Hp#}Y>AYIiV2Ml5lBNgbF5!$t+;60O`anjxJlZ{N z(_Nqcl17~fS!|U^#!6!qWLf0tARFG6Nyxk$WZN3S$x7~O4M?8Y#0KP!%{9&{d=cjU z<}K}ZnM5$_@zFa?u_Jf$@uV15nujEKl!B0d;U`{-E-JAd=XOAa%|8~JWveB(86bp&-@z~`i z;O(M)K&7~-&&7IuR(7sFZU@Q^SsYiKwL%ed|C!O-V^>U3u3vrN+GU03KT62F}Nc*)Efseqdy%b zQX|)%^I^IjbtPv*RhA-hszfS@=d>SENeY9(B;qS<|H(lUbI!|KE`6SZKbe@*$u`BEsm7ezwxAgw2ncRW1bc@#bevL2JuuV> z*XY}|z0ZabG|QX`A{y(B{9yIVgkxAgong)&dy|4ikO1ucwS{j{-+_#XQeBw%xZ2x} z=QA8D-;&r=BZ<3N_j;vkP{i6wCLKMrARhURkE%mR_r1GGQ&#m%&I?U8fiazUZ!nNW zk`O%S0L6;p5~&}e9UT`!1_HKjikX7>qs{<^q8%2!`Nc$Y0{{GO7Xwr@sryBIYT+O0 zMlmkWvZ>vkl_~6=in@f^kZdDSpVU#ymQKoB_+CF$0TaG!o$BdUK)~^zZ zoE4aBse>{~o<4}Fd>?aqX$rQ*-TV}0uyNSR!VtEc;`Mau*W!Dp-K@O`-1mu7D4Z6B z5cPUH;Kt?WP&g=vig1TM5Mv&g*30dCR5>RJacsIIZQR?Pa>jC~nSr_8MK{FH!H83o zxuqjrK$dI&0tUx<_Uxm6AgCdET@^4kV#BKd7f;}jcS+&>5F_gAWJARrZQGB~RmbL1 zx=_CeLf79x*Zl5ON;kkk_xcoZzVGTTy{bz0%h4R-53{xv`GV%98XMS}ihBx3_*|-p zp1fnN!~%#5V0_4fPkf(&k{QQ@FsS2V*en5(l~1Ni-B+Ag9cTRm3DoT_?@*{nMKpop z9Z;?l-ZdvQ=|DyY)bQ7w1DKbl=_f&!?q{BOOgbd`kRfMhB2#VFu9xPtMVsR*PUJ)W z4M&|ohr@=u2u%kC6u2z$Lz5HVDr$K;4*9rdMU}^4q@BD7i_xTtf^qqo9j48 zrX-9*if$Ym7LIfB!orh0l}viz9Cf+SRI&2y-yqVH2}vRT*+{0pB`C#7_sI9Z`dli| zVV7n)gmw4pNpwqdu=VZxJ{iK&9b~g(fvl8{5z17;;hZsKcgMl? zRYJzy7s*hsNFGAlPz<|4QkmrN*@SBt32YyCu|Q?j4Z0@dS#fXlS|;NMq+Kq()Qa@_ zy!c8+cvP1t7TS% zrRQa;QeQNHsPvjcrP1E=2+c@`N}s`=EeK`7UKNTmUWc^G6t(TaOL>aW^jJUq}D^lX@9zi)Fu?310uY*cWk$INpzPa&@%bXhM~8 zT3qR(gswjV)EEcU3id++HI_m#|GZpaCJ7$Z&tBfKhL_d6zCg=B$~3n^l7skb1Cz9h ze=FH^l`g={ru{m0q_nacfG%>ZD!z%Ll+c<#lTwoCr#1$dZjDOemRMqHQlC3{3`Rf zfbgxdeQo+2++QDc&IiZ+S%(bP8mzxw=b7^t;tpv{75CzMELLv(;Y!M{2sR2#cCaN= zwkiLAY;O^^MXMIl2$rhJAiCu^*p8fAOxbcBY^u{1{)uiHwNyp zvA#oyHhs8+5)JViMu?_4i0*#3j1s-;AkrSO@aX6R)o(BmA^*{vz4@2h>@Z!e-^Gqj z9Bq`APOwXwQKfNaY9A)GJU?=R?E6+(O0`a__e`BGYp(c+6GGNP1MfMa)}q5AT;u^* zd6I?kzK;EIIzjtt>T-%UJpMI;Hp78-W#0;lHq(LDc?V5@1vDLQHD<9mrJ*jd*_iLz zrz~NJA!lr7iAh$C*2a4>Yu`CEdY8w(7g)c5Lu-YmO|24Lu4&OaV^Pl_vK+`Sp@PkF z3I?9c9j?;a9~92ubr>9{#&Ot z=}bEjTaU8Y#(8mpIiovwslkWvC)fLbp14g@c5bR@MOzneI4;N4W0dcWnp5tDxPblXE_&fY)fk>0UOniSG z9VelnEcVU~Qz2!o7PSA8aWcqQfJW=?Gr1YO=t8uQ}y#{X$RN=Tq#VFMrgj~2F zrl|G9uwF`ID+-a+qddvVdHv8r2ic3aMY67a!P`iQue9Sxw3rCkqF+zJvd|;~w!|Ue zz=<2Fw=H#mtsP_G@9T`xE;`gWk58WNYk}0pf>vod-V~6tIuX^h)%lr!tV7nYfs%Wy z;w*k&uE ziERtakeLGYa?SBpT~0vymPEFuYqCCP)8Mnc)~Fg3#zD8L7etfaAexYx8<*&FVdPqQZzz3l zYFk!re9r|%)K^0q5H=}nZS4`V#y_GBa}ul2^jGVWM0_4`c@;uaVH=9by#S4T;sAWw zlLD@72RzX+rmO=%9RDvJr9n45u3$bm#OIzDXSdY>f6w^^jZ1??rW< zwN%3O5>P_?PN||yXd2S`##IkZ*^Cg*gnQnG#msLl3!?2c+zbYxZux?Tnb}AJL!ooL z+T=7b+S3tgGl9yuR-k6)U)69e%ULioZ0$pS{7kHw&V9)zpe>L~#_Y%21@8oASHBss zu_5HVWiVoH2xCqq6!EBgZ>UF;K68T6R36GYd*3Y|d#D8^@sv*;FEr~I$i4WMDpL#F zufd5tK0_x%3x$4y?=NVZL%wTeAfNgyBzobCpAikS&Wd|#7r>rc_!-|{+Ff(Il^w*Q zQ+bC;y&{uc&aEV3mXO9Q3t43S`Y2>Ab^kCsVAP7Q;&IR2Fdv6a739h0O8B*lrlJSz zkQ>@?bjGlxlPaY_afNNP#Lb994T2dxJQ;Eywe{O$MZtK<=gI$|&qc(Oc;haDSOkd5 zIVF!sk3Hkr!!eI;zE969-h>8?<+}H+q}zpxioKZ%zc|tTcDXOZ${4>RmzwH zjcD33({~=w%pQl5U#_K&xR*L2DI$tV-wq1yBbbjej#JDvXdS`)(t*jX_|}p2gX3429`qUAM8xQB1e2Ui8zdO)Wo|>d=45xpCeLdwLtRq_ z#!K)cXkD&+re z(L6#V^osm};%!Es61<}h35UK_MJ-+Cz}xzUg|FB3mavGkS4^0Rn)~ERt>B)w12`6z z3z{;Q43~7Rw%D6}Sm2?yePdUp2jrkA<#i}JcF&H~Zp6h<55mX9xZO5^u58*%*Dz1e zc((iCaY7d1dxDZ}LnVZ)+(DK)@+9dTCFolR*@g@l=ydS&a)}oA)0zX!+W9-y;A^;G zg>fUol>&R%tX8DRZ2K!<^&iL?1`c5*rhC5t&37Wri*)L4j+*uVrrs(D%K+&q%CZA} zL0Eops+4qw8nM#Ba-g5MaE8F=EyX4oENzPIvNTCO&zgH?+9iRB94p(I_@YKr;ia7o z6(W{VnQ))QjLcTPV};ojN%IBWMpQ)&h%_bQ^z($%dZ1EDWxGm00vR6mOkCZtyU<<& zcG3a%X7`^-JU->Xs)!J$D$%3Dn;t1fu`*kAMPi*iwrowQ#N=uU3qsn#<^Bcso`-OB z=W0Giaustb+Y7-pgA0u^X-#~PEY1G5iPtww?~P77;5ZQupTQhSghabE#l+~*|kwjua8f+iNRYkVyHQ}qd0oO;SV=HUJXw%(Yr<3KxX$2%hM+7+%s z)31UD%%QkYoN>4AWS}e$%EGHlHQGav;?*)gZaxJg>@P6MLp`Zj?Ir9M-}b{TufWc5 z%OE`G4@z?Yl@hJaiQ*(M`&|DZK)*Y*YLhrOB4LGaQjUh`eomq5##zT9DS3SaBj+^}y%1c7xRrgRQKf5QorF0&(79FxM71 zT0$0n7aSPa#*N5vCw*NN zWs(%7ZFGy$Hok@F2leHPjySOGsYUdoOHMyR$2$Zn^e6G~lKtnYhnH!N0~d3d&dTrI zzex8ViM8oIwc?aW0*NZKB7BA9&YMVaFX|^BMVTyJmdfe;Oe)(GL7FB_*O)n2D(lfB zE?9z=u?kK2PcXw|@r^-{R=!H+y)^~*Zn?N1ie2GGpqwfJKD!!H}V*a{( znMNkDOda}%xKLo#@-jugp)6YKX-W-M%2H5{()Nd-DTn1xJva@6jw$!{jq&nSqdQ-;)!UKY3%l?OAoknK~)n8hjnF(p-CU27h)_+~XugY86jHYCJOYg z69~ejXQb0~=m|lRjHsnZKP!GCNHzzOY4t4;3D=8Lgy~DcZl_?|@*}rVN_U~@L0g2$ zWk`2z7HkwyA}%srQrm{KI_+$Q&=ep@>!G7_p3EMB;{s(cW*dd3KlE{tnb>d(+IUL5 z>f#a#_g?|KykqU8fx!9kw<+8?MKytIbifVx=q}akxkImIvqUp`XgJR@X$D#c%?Xn9 z&Sp3-)lI^bYARj}**fNR&?V@3kB#h}=6~x3SE4Q25fn`>qLC%~j*d%ov3i|jr(@4`WF=5a0#(Eu?){XEe=36XZ;XX+ zRS$y!S>n|+yjiMU&b->ZIBPi$v>KO7d|MFtXU4t>bj@`x> zv3Jlt0_jON>_6^gCy-tQl2?f?2C6CHjH0dJ`@FJcr6C^%+uYe=JBmemFH^A{#}?a} zSfR-xNKGM!{153&IhtgZZB|>hzv4@#r)P3>a!v`P+r3HH*r}v!K2GU6Os>eO*FkvK zk8~k^A3Y*eP{T%;TWnd#JVDI_%pbs*DEmt@+1lC3w}&dnRC}1UbJ46l_#L6?7a=LI zQ(i?-6uu%fU8;nPz+j|Pn>?aU;NL65x=V(xFZD9TJY#L{-9LiI2N0;@CJGfOLcu1y zjs+%CqQ>Q=1!7kFalDN{L5Z5!^TxleG^yQ%h!t|z0=xC0egkp^`^kl-*M#6`naS@O z2gLw21dqJ7VtaI6@}cAP$lXs`t=_MLHQ6SAm2E&g_ zh898y*q#d%EYt}GKE{67?qgzox^^X|XpIqe4t_A4s>b}vn#JRlVtsnZkIpiVYk)nP zlRaed@ybM-hDm$|f6&ek|3*1Y*k+H8{Yw^fDJcfA5X2(enEjlN-8YE~l14&prE?5C zU93)*o}gIm)#(Obw{|E}o5qdvBHo43EdcV}>HOd0!Zfz$`18op>RpLTZCHNn@BMY!^RhIg z_snC{U|#qqDV%pDid{cQ(Ykd|487G3C)TBvxpHw=I%+i*utr4c(!|dq_9!V?4?;Hc zGjXt#bx!kyO+l{R29gOV^nDg4%0klDB?A-L@RQw^^1d#4Wj?R^r1zW?xU8u3 zK;&Dj*LRnX7>g2Tkx239HpRr-hzrAnxs#+Adu zBMAzqyQ3assnygCj~@#>3BZG*O2ly5UVcU!q1xFFBo?3t#c3#z5|fQLP=6*sv!{2W zKt=*|2#zd6eGmtK7oLxw)o5pbvDCQGxXLTOOfe)nXFQa*T3$#RH@v?nQF=uceh}>p z`A+fdMFbS1(dA*ll1Nw_+l7;yPK3VfxVR82&SSKml*Kht11wN)CM?t5^rkE(!gBI; zQ5?igk+Z>N{^>DCX3osB)A(t1rG4 z4LliyCxINp=Cz33F8CTbg;@zXL9Y+sG_iAH<0o`|!u5h{F34G|S2?(L16I%ltp#d` zV7|Yw-Z&aVZgnWhtqwRS=2lDk&EP7k&IlePOF4}2pJfuQky%>7O_0?f4?#sTs!&bl z@xIFtH6{r|*P#&v#skE0CQdU6K^DEQV(x9#KmVIJeN9Z9zE~4Q6Q?mAu{d2tO3Oqn zU3E0c#$=+`NgFvoi3uw)XPQHDrpOfjSQ%Tk9C99<&ehash*YbxrMGfDzVi-gQ>Cq) zbL8d1XXBI-o4W*?*5)M(`GQ%8MgbuH^$gh|@DCCX6=#u@DeRU}6#<*iz;-i^A!c;7 z#85MK_vi^`g!(ce3;CfNHRCu!Hm^)fMBaA$2kPw>Nw4ypUO%}b*h4gciMsg5__bQ7z1{j_nD z@5FYhY(mv*d>qxPuRl5FFo{B~-_nahO(sx3ud(o`yis)-#uv<(lJnKof&%suJ*#8% zE@@-mgk7gU!8uKqd%$@hWr+u*<~$e)R~!BUMwQC2kYBN75CBafKrs}Eo;M~X(}NwC zVmSmUHLW*AO7JH~9H5#^%r|aXJVkn2)G1$_wkK#1o4gct(~g2o1uPm5nZ3NS1C5pm zOTxOjp*aH7Qp5a|0r<;U6Vv~N#_y2n`C<=l&P?kzy98KTxWHv>92~YC;nTyjV5Uc0z_Mbymsz^sGkLX^wKJVzFA4)YZei)M z63qh~T#!n;;D6_n7kBin1%zlblSqXcf`^e#2z6v4)FnQdB7H=VRPo^N$Lmz&pco@h zx1PklLenTg8o`>lIm()$4VomGj!zYuF0^Xb;|a1}_Zegu6Y{f*h9nZpDw@zr1yucB-rmgAwR_Xq=hrnoy z2+o#Jiqn1+x#OaK3Bft>4_(!a#G{EsP%Etv}QSrC*sNjmU2>AJmrP>Qum3PB7MaQj8*}DjmHf+( zR==dqGyS^Vjr)6!FE@uZd~#S)Eirgho1PbHzo^UXvh}dE34lH(KtEs#w8{yDKJK`7 zUrmtiJ1zm5fX#peWnhBj@nR@J`h+08W+OXTSum7B?QV#ubl170hDl{f=b^Sbxpm}q z=AV{gxU<5G3&LJHOCTdjeG>C`#!@sQI$zQ!68}gRQ)>y$H%};6d+@PM+(xy(Y=ZOW|i&ycU~Ogyau=i z$K1CkSxhMMgKGI4Ib5GnZJ1syxBbX6hM_g_rtA^Gw23hNcAYZOGsY0RVHSdRwfA@^ z@eX1=HfD1CYh(^S8Rq~N6m^M4eJ+hOi|eRQ30C26V(>=0f}ymqxuy3IUz^5VX1ZiY ztD{X_s_{c5d!*6wp6u$-poV{p@vu?Jo+LEsD+&^oS6u=W4-t+^(V0?S{0(H=2-%%q zDOvk@!vU^@ud`EM;e+6)DKq=w^8F0Jlv$r+Fy z_@Fu3ya~1mVQs-IkHjC!VNJ|c7bI$jDzCZ*M462F9z&Al50wzO-fRvvhvo#PZs-JP zp931|T_R4%cRKBeiaUChSm^CLa}141(<1Y>^&&h$ZV?QdmY4uhR9Lw+IUlS-H!&J8edzi zyJmn9xb_e(73HD_kI6XvKSyf!5yggNji-uD@B4nXH!18AtTo%;pji6})^{63Q~Hl| z)6`%nj(pa_Ko)3NizZ>I((poPGQyFbe4$`DPOo^4#XIXbSG4d<5>zTzmn!yJCY6X~ zT-1fa9B3EmTj%f#jRNu4j^Q_J=$lljS$GZ!O$RB`hs9YGsgxl7y2!aohfJt#g?*bn zB$lnL`Zp|)CfCCuMfyqmirc^`j$}nW`PCOLM}PP zBlnDhB{^>W`NM&lcvzr6Y(}>0c8RI2X`&$Iq?$JcK2RAUyNYb87vxkXqle?Vu#07v zfac~4it+UL@jNr-YA@wNZ_NGg43AjM-R7S|$qMkIEU}hL$QnnzP01<=S)ZZpDQ&Na zC)aUEQyoIG^Z?W59FK;oH=AqxX0FZo8%`)x=eSfIhTK^tA_v>bT=b#%kudHSb6;Te z{#g19>{vwv+u}Hqb)wzRK6gwINHG~?h0Be^Y2G3ER7$rPF9W)hR5yuf8l^i$=+fXo z7Twe9>AfXu%S25DlAsht8RTLSZ{tE1Gk*&RDsES@ro+`piVE#2PVW|9=8`TkZGm$Y z_|jk9Za|Z#3D^P0X)Jmm89c0GD(EM$BtGppjkO%F1fa7Ns7KE{GV=UPaPr_J7R8bH zeyHB&sb`%t`bsou9>g+yX1;av5>>{r6`-atYM zxcGwO^i_KW7wzs?@%oLhT)jmH=3=}WSfGMSO!q=Nld$|wSWN9luav%_$zLT(YG7a4 ziCNl>%nm`KEyhplPRvJ^toe9@V3is0q}|Z^AEfntjuTe&gfT>=j>&4Va{IW3w!H?g zgSMe?OWN+I`GB;2fwVnA4sr4~n%QLhpu#^(A~Y2V&*B~y%x9&he9q)6vj{l|+1f%5 zqWO~!vU3D3&$y3yUoe~HxVIEoE)te1$EhmuEHNpqgBK?aXdmrJ!4_l3sjBsO6V4a~ zXNjHp-@a{g2+N^U&svM#s6}fPV+0PC zE3XS?nAK($+yjcMgrd5E_9a@Og>)|}?Kr0uE41EU&!yhFIr7tYM!`8^QIzqI=F^#g zSfbr?%jq6WfKZ|x$qW;$oD>bw7T2;XpdicJs^AI@qZRpvS7hy!`Db8Gm-7_*bQiHO z>l`lH>#`jk)Jj54X#+;WbZU5 z4}Qjm7Vu=2l-`5Y_dTH^_gL@Ze*lLQBBTf3cKWv7UgnZl(FS zE_ykh*=Edz)&FqqdXx`s4ar*1NDXY(dMR%Rtd?p=mdh@;xn}TMmeb15x8YEi^oI(F z#!m^4newz3WrhFAb~Vs!7c=*@WfbWEF6|4w0_w6*V;Kk+9^Cw-h*CW#RGK1j80-5z z3}YxbvEG=EMt%MS4NZKa-Hy_=+j(C}B-w*7jQAg|94ajzVbcHV zRl~Mi`a8$$j#EBK%0w=NOhU-W-p{};$W@mY3S$?B{;6b;LH>7othA@`?DKdH;MXZ^JjhChoZirWuDN{!uf@CzTjc@c@ z?E6GmHZ?k8B@`UVDL{>W#l)v|oW8lvxB@mUa8MROhJH1{X^g&WzA!!tc@S_&=EE#8 zz*?&*LY)XW32o?qZZY#D$Rk(Ly8_v(u=x+%#ETLiKli2Rg`~0y74B2cEoV}!$${HY z-a8;+LsF5X+b&2VV$6B6HV<<(?9buc(X*kaEm{_bjYC{0^2!&A-bX%!2nyTe#w%YZ zOvm8wM^JJ`jU@ofO=LV+sxl~SN!0_45J0v9?ua!;%u7yWkh^xzxzp4G?}W=C4qs!ks_ zI%$}syDCMUp6ItF@%aT+nipe5`b4{=zXCSFzqp{Xy=Rx2w}JW`aN#+Q10h-`7<5LL z)^;=;f?eZZqLRe6fT6ov8w>iarvc-#HMP2{jpVubBV)z?|k6{ zlROVLqh6VUIL=><+~Cg8WwA*Oc`Qt;kxROqE|TF-jIm$fDpJ2y_AnYgoD|GE}9r?H!?O(7^t;p*_c; zzjzPaK<<#()MLMt`xe2CrWu<%kxkGN(~?ryfnUAa9@&$f5CvdS1gv<0MYN?s1d{<+ z$FLSnz{-0$t`K~TZ2%TS^^&|_PN}*Qs$p>a1GW?U$lzddV7bK}5rS>uFGEeNexzNa zUZl+pLbj+j_jB)>+|L+t;>?nIiDJ_|H`oM*Y_71$iI=}p#KPXVZiMLwoc=(|jN=~2 zr1v#O6zjAmT>shOp3Lrq?VKKN5+p-ZJ#GND9+Zun_&sHdBWw%CIFAcsd5CsYu+F46 zN$NFH9HuZWv`1*JF2UX1BNy5u%?s@Ybn?ER8}ggP)^n__2I+%Mh4zRZIL_Trc*PY& z>q!7_I1YmJYPV)bu-*i$)&p)5WCG)uo5+)%1bLH3;z^K3Y{w=8xj-3V&I7hk_zkP1 zwDE-Yoxv8k&1g`=P1z@~e)d+_BWD!a4Ksec|MBb}?feX;o?~n^x3KF;rM$5f>lfKW z@{+nKZ{=bGQ^T32lC(!(!uW^d{6{}(SaDY0YfO7z=ns&%QJ`y#7q|t)8$iYLenq@o zFoD2K?IlhWWlCgQ)vSNJU|cY=&2Hv$ux*hYaWe6=KjvQ6t+q$>#0Cv)mStV4u^#Fm zF#PM;x9!}+e8GPtdW8`E-jll8K&L%BT+4%a!lQ)a9&jxr+Y0|<2pHOp6li?3c$r`# z0s2Pov?Wr7Hmi=cB5sjALIP)SqhqvlnGhUhoXe`Ku}(V|h3H-3YV;3b>c7K^!jBEoK{4(@{aOjJZ+Aa1C`y;o5H)l!T4nNCQmX)p=im z{|q4%m@4$DQz5WRyFNn+#h~K_V$V}DEw&!1lOz&+fE&HZfF~AY#p%GBjytW# z5TI%6;JQEplddU$UUn-=?&+yC!sK`UmQIWNs>Y($q%yM zofKw3vBnQw>x5WvELKDYYlZ21ro-uNG5Z#dm^H z>V8R=bC~m1>ERkfa&;1C`kZrBV%sY(kd`xjk^z<@WmtUqIJI1R9kwG?HW6w=-eqx? zY&^vHGo69UQHeeL^;OC>nQ%#WJEfAsHLLLg7cyJ2TS$^9YVll@WSnc9%?C1@gr!y{ z5TneJK+HS`1X6g#IlCxAd*!twf>?L`@(p?=AQQxJ0N|**9{Kbd1$dhPc(1eYsQ8I? z8tRlaRZ=9Jk%VO}mGW@cwS+C0iuqWJJ5pGYJG0JijykQB)*$U7yV+}1EnrL~7<1uR zQS<+y>&@e1yxRExbM9rf?E6aYduEUgk)TvlGnp|#s3jCdI}uA#JYs93t!5&Y*oxYz zirT3XODjbaqNS>BVri+C#5NMuPKah?o8NWrGnr0*-|zPiPrW?a^Uih7eU|HUE!uC9 zOKNp@@k}R{_-2r^6rjjx0mCV=)-1b2F=h}9;{qu@1qBSrWZrKSV)tU@i~DHhxrU6r z{F@G*kGO6uWpoJ!aU(eT<;EWvO>;015HS_+&8K-UR857i$UR$q79qIuaur>)9hhh! zaEhr_xpygnB&NPfGKFY%U0^+$%%dXl-TbMD%a|%h5PDn2G1v4v@ol?tw|}w&%Q{}y zWF4~`1+~q>_;ZaQ#(XGgsCWnD2a%7+C8DCmHNmdBZ0KFW^Dz1z<%wb9fd^z%I3r1z z;p52#hB9fjJd zHJZtSlC-KL8{xb3sGrS8uCwj1_}P3hTiB^mRm-xh<%oT#CfPGO0X=1OzQ(FT1q zriH{V|MaFzF5T39>z8y>^)pK$tR?ujIpl+odU)6tydjZPeZYJ^|He{Kc$#uuvyq0_CX0f_REB(hg*-n9t}i9 zLPcVcG*M0k74E3+p26i{Biyr^9*Mn~SAfVVp{7LDkt0!(P767Y72- zM}$a0iChvY6s>hlnk1kOq}e}AGNS_^r+_-jtDXRTOlIr^*LIWf47YjPe|s3kC$6k!w&^#YUC(3|9~XD>*X*zOZ+sVqUQ31!r9->)Quo;*dMSRB=KL^v#t1_@ z<XXmscgn`Q8|7-U*6-?*&A>;Iu{h+MJb| z;cIhu3=+MR{bDY>*d*DRB3`$<`LD4S(NhTl_H~H z+Vpa_U}T%;2!l&<-}>Y)poXlyqoAr~7>ywJ<{qbp9J3Gg-Dr;%*r&{ZY>Y3$Jbq5N z_CY4leQgms)MhXDIEpXGB0HDKQd5m&Mg!GhN>#msr&N+=nzXs?G-HLwrr?+;g>def zl9@HJX{wm*>lg(;JHjzS_scLDr5nSH13IU0s$8w0bdqp7Wl7tnz=+I< z^V``3b(~4N2(-Net=g?hR-LsLZmx-Tu^W%83?{>pvuqiwwrBK43-cVFrBp4ww3rgP z)Kd4&=_Em^?W=S;D%tVO97dOnN{F9wtK_FVK>d_o6YE`M4ywb=??a9{>j&-7%zs;w zcxZ-(i2SL;0pV~(T3}Z_ai*yU9w%i<2mD)>6nufFiH+V11EMxJB>e#n zPv1PyMG2hbsayk|%KPkX*CdvMz_zbIkNf$v zO+1Um=p=Y1`C|3V^RUjoBQ~53=@2#AJanOL`J=DgZz~rG-7=aG!OUkYK=(bR`|K|@ zaqR{X*%LzJ9!TyZNK5DNmYjAuiDy)SYoJOcyrQ*Xrcu*bc}s_q;os{#(410-?2&)3 zGi(!P;oani)bMniov&F0L_Yu#35p|b$;)j(aCHHhuTB2>ku0=NKh@ARpU*4=qMsT>f2T4~<(;q6`h`M%m5nUlfM^|*DM2nb0Ai7G4>Mli7 zA`nDDjz4FCoDu4oSGu`1<8)x2$-|w31m3On*?OyL-A&rrU%m6>=R=A?a7Z>&GG{ zW32xDU^WXO?S_}etrQIPY2O-PBs{ImG z5fpHqLwY~-q;o%NstM0uuXm+9YnXMwbC>eS)_0>k_Xy88NOXW=_i)lg$%d%1K5qQ1 zesI?D#)b8Ko#PcKgQH+O4zc-oCpL1=B0Vcl*I^&o4MW!VS(;jg9cTzJ7|a;bwQQ>( z!V=_!`-HA^Iej`kXfu?1DC0u{#`H_1VC$Jp0A_VPRYn(dr)-Z1+miPzI<`!K&~y0+ z0K{^IIKwkQH=&}!j*37Jcu~^hfglfW_^NS9!;snmN?D`wMIMT+RVw*LdBcEOxm>?h zCN~G)SW=M-Ct(eNtDQ&JR;>glgNMrrce_0%c)>|MDBfn~6TthE+HKU#mnhp4!d8$4 z^LQS%IpR!KWQp&d%|ne4u+T}I=lONxvc^?eLuv=AW~{1cEQk_YdY1qyh%KZC;tQ(&j?2INQzPK!uU^Rs^ct!g!xkK(g2oKHp*WZYLzTI0e4?)O?^8; z1;rE^4IKn|ogt zI3laF3{h4N${KlHwmQX7%dtvxfJ|25KXA1N$?|F;WF073t3(FN#(hmY2-)Pvlx#oq zdB!q~IMS-*ofre50ufY)uL>3&S_4&cqdZgC3g)?G!fL2QbP1X@=w=5PSo89%@(CV= z1rwBMicKl<3if$X`X-^1AJmG;+?0YVJw9MF4cP|Bf&e0h5IL24&`YSy20{DgTwQO0 zg}=ZFavjM6dG98LI>a0SbAfz%4+k+fLrjE9MyR^_Nbhfj7iJ9^Snxg5J)k|Liy+99 zEPiguOMpP76Bhmkj_Fh38#B8C2~gi5gzF3Xvqg~0!K3Y!Uo1ZkBg)Bw`q-N;C`nDt zH~zPwo|0JUqYKn)Ud$W^AQfFroA?0)$OC|w=H~uz2T;R9Ug7=R^48Nz_U#~zUz8=C zoxr|r%sQJb+AK50`iFXbEiVRTYwA(Dp_WfVBh4C}l36u)pQbZbgaVK!vC&&}*1Xzm zge>k=;k*gaXD9L{*W(H1BoH~J)8C$@QCF-lF;enl=~V?=5bwi3FB-W|J3oSdZkePnc?Kn3zBqQ0C5)vwVHFNlDp-L&Xz9(M3tu)bWpLEXSX z{pUD?`nfoRi8H)6;Thtqxt)xE22@cTZkJ0$?mJEIEBAiLykl*f%U>0PS6@)PLmf4Hr(E0D;@Tma+AnBfU zpHTp(?&);oBds%my^}x_*k$8JQ;P4I%Rm9zCwWiy-0}vch#_X!47oXQODD%f;NmzL zc-G*f(P)CinK~tU^`1`<{81(>5BMXd{?26}kOa==)s%n=obWxndxPVN3Bm`CrGx*- zRKVa)eKV?XJRQ6X8GI3B;lT462U+sR7=rj=@a6hlho2t$wK4ttu0RU&`HXX!i!{I{ z&cl|Z%plctSUKH9TMqVo<6SKkHXnUR*2Kdo@bI@yi2EKaDVkk+y-8^*nQK7fR5bg% zW2Q7ogl7A6X~$!N4Sp##e3GFS8-``ffW1Q;P#FJ`3_mX#R+s{HlH`_*!W3DhJ}aXb zchkpD=k?|*S&ojdf{2WXoNZW~D&~LB14+b8>6k%Tt}{1)1td!{EfXeYnv-#VB%r&p zVThTP0?`+;wIoYahc80r4P55KtH?$ZVw|*K(wK7%DB!b(>D8#Jb2a9)8T(NH1S+Lx z>^DOkdH#Mpvka!HH{ppdk{sq#u>^Ou`^ic4O6K*ziInFi^E>c31EVqB93B(B5=*Kc&L!_q?+Bv zaz}hk;_~j10iL-DcCEmgF6OH1|4dOspw#4W!01n`GPFp#&`5fuBz`S$e|SoYA zK|PKp**geoU7|q<;ZssTLx7GGo?h>(a#80GDt{^*615z#1{3Q{bmrB#6iPUQ{62M^ zL6Szf$C+7E!~6+cpizqYq5`H=HH&~r5@ir0`Gb(hlm=MF9^JztbKg-YI=q8|nwZ2X z$G{miG}ic;9ZmGg)h}URmnp96_9?P29!p0td0tIHPuiaa}riZ{xi?-k7AgPu#aX4l(P~JvB+J zRv1+ZjN=I7ZfClUOUZOUrQRfjcQ@ox!sm<~2%So%?b>_vlQWYg_8R2cfSZGj8qRrW z3plNyuCJQ192!IF_JlB6K`G8ekaK`6<}hwSJ+zX1huWNVrOoROS(jh z-EUf%1mvHE?`J?iQYg{!$`$4X(+n_9`I0N2Phlkaa?00|^z12E#}1yT>{q)``?J-* z(_T;Y&M$Xf`(WpzL{nHcC& z30+SvMbo|DL>A#v&HhkoMx|(B5R3GvP16i1wwR^(xrrN~sY+lGlC;ea_DT5$aOd(C zhYs~`k|o9(2QTdaZ7w?Usu8N(!MXlNQD;M?elwoIrIs-2q4AwcpkZSPvUDoJYMpsA zo*4pgQwiLiy+sRjVwJ4?iSONi#ZHvx{v@4F7~5y$w|N~g8_MdE(f`2f$b<#71H=sK zb(}$Vu#3zc!^h@iNDYR{n03mYM9l=RLm${e3Lbl@1YHfAm*-$lIUSALNyOw#g7U@7 zV5c@e<18WNUq2&+L-GhG`gyXtPMCF-YD2RncjE@gIUw%FFg6n0jdSQOdNFA|bvMo> zqsK$m!99=k$KEO_Aksn{j!S;Pugf0F--q^t@1wH#wOA`yM5JE49>4LWAsxL#kXFSO zEf9O&6VlPUghxLC+#K~GgIphAE?5R1aON4bV+B}H^XT|x;wG|7y+=lm2!rM1kq0^4 z(9iclHO@v}ogSbLV)l{;Z2zYgF0}@$4e`q#A>2QL(J_HS0InrPG~Oo&A3AdlJ|F;O z1Fs3mbB#ZrjQ@=@*Fa>oF#d-^+fDrkpREKTk05lFOP?2qMV--V8i1xh^c+^eqv7|w zI^i3YFqVak^|7pI0bGjE!>DYi6->s1d${{KzJQE(rIfy)Ind>HmI|ytapgkp&HDp$J#tTJw?$Ymdc^e9By7`GNo39`+ zjazq6n9ghhz^tS&J%8J2mRzGB5txr5jo>e}z;J?kQj5W3aS{^5sYunM9I0Q*=Bf-D z8+4geUuG<<0D+_kp1$HDER5(f3f1O0VlY}oV4hb{nAH>pZV@#qJviN?@?Ah{2$1)( zT@!0$Jz8n0Znx0+pvEaFOe4tvLnVvUu$xFNT6 zav=$uo8XeUjtqbI8#?^@He0x_{taYcb?#m|a1XW@47`yJ9RKBhI`Aek@JEm%K;}p` z)2~`tfW~#&j}kOI(qx7~h;xUBhOqJ^ymBVke=`}g?sGckmbNk715~$?F?&uvK*#LO zz5-)@LdVS7^*J4L8yWL5g#W{XgWM$L#)4`-`@IaYpui`#l4Sx6f&c{ov?uO3IxUdt z*l>d|VGWG_DU6P~7D@M|?QMg*hq)Dz!3T~wLrDqUj{ zYgashyaq-0ApEJ-IP*R;@1A!Iy!m7?_=FBd+(|}sh9~W68`1sz+)YM&Gvx>!QO_D- z#65Jx$(EyZ#J$pw3z7U#2?Z(dIAYEB*?HwLNSBwS3oDP&g&|#e44z9O1olZkA-O02 z88r&+ZyOO7xLbnk0W#*SgkyBfer$gj^B^5_;r~w1F+V3`-kl^JlSl>wES z2p#t0aVP1p#bnrj(&=jQ(9g&(;0o$3>3?M`!R#^plKb*i3~0)l4)>V8xcCu_dX$Vh zhK_oyZB%#Z@i-ZEt7i$^3xe&zPnf~%P#E_F9e2;bQ*_)f$+*fC*NXD&w0+@Rrofi4 zV>+@-T)W_fxF%k&lUq9J*C3s^MyJ66AHnEf!RUCJo-XH;ZNs~l-YJ3M@!>|gp$=n5 zz}S*>w~YC!*EeKCJw--tpFn?NuUfpdJVK>Q|HJB6V$l@Y2W70{j@U;h7&Lw)fJnbx z=i^cS%$qIp|An+lI8DaShZRkqt&(;py#eMw3BK#vKfWOh<&P-CNOm+Zl+vHSqT^Y@ zP(~Qy+fxSbYV;OsL^-MY>?j6&h&YKcj#c2t26lM(W+=!Gh42c84rjXAt@aZhoT-2A zN7<@upZCkmV2`teXH`3D4@s=cCt{uZgQT3m{Q3)p8N-eP7^hBG6L_A$d`n=Ap3=P^ zDlbMs@ft7Fn92<)%SwyGkr-^+pYi3SatUhXCBHJ&vClvAElu@?x`C4geyZ2Znl zo{h`qkWI%o;n#aKN@#)xWav~`TIo)^Y~oHkGx6=Eu}hOo zj@iIMq&a-<1!Zw*b7+AG)_L3LH8C{P$KId!e#Q7KIOb0Ei^X z{b;2iE=dmkr%$OQ$p!4VB-PhqCb3yS;*{iGxpSF*IoHT^Z6MhmYEnns|2XFUvTjIO zF>$AIe@04pGHVpZXecM-GW-H1IUeuZMa zE@LUT6rMkN{fydSdOyqCxzV|AoDz3HB=QbM^(oYx#J_P>Y&otkCm+>4*-eN7VG@u_ zk#i57`5oqo=`+8AB28y=0LiJyy>aRaMY>Ip_Caz7ted3OWZeX+>mtjT>(=Bh{Ee9k z8+mqhTgp7P2K1i^0W$gGLwE)Gz^`XTVc5@d;RLoA`n(G51&J(&Jc^|KRZ``o5B)#Q z*Ca`9$keM8XeK)cfSi(C&i?BZ=pF$&T_o*v@WMbrgHH@fQ%$wX(`G6SwfUM;kQRzd z)lJ~VY*Ol%hKlwC^Fz|WvDfPZYm~MVzQ*wiPg9rkdBk%{#5fd1nOs`jfAaty5+h}f z`GqpgWpjZE)Hvb@S+Vg3WqL%Iu6!h|p&k+b!KT!32ovnyv*ecCSejrDO27geWjo_v z$k~~Ae-&qkdQ-1Hu@0$!Z}yR<7T*l;I99x<$F35H1hM=%BsmJwn3GIjO!?s0x~u3Cbu`s)Nfk zn>7|{{dBxmS%Z2e8GgzdoXusa-cB-jXL}_XKFW?Wz#oDKKd(H`k?Z)wUu&^ ztO`nxF>862wmxT~p*Bbe3g9@6;plVp76_@8%U7@&SU^=>SD`avl5F7G=?2@i(O@!3 zu3HSL9VBSLCD*yXWF@g~yXFqHSU$TDm_V#UR5tiFqnct#dY#W0X~j=_0B?k-Fbnij zFMt#XT>9Db#fbr=*}5jc?49;o*12bl`dqs@ISy#Y#YC5KqAU(NJteV?_}Pk5_GP5L zHThFLRqpWY(G-qKckpn530>IVHK9bjV~+XVrFe_jLcjy{jx4m=A@?bpB;KW_N^G4c z!t(bGH>}k~V1D2&=&EW+QfSs1cXPgs4n5IeR4uT95PDc)&*6|wQ36pG0lp`$LPWaG zkM%TlZw0UxvYj6^(S3_5cd*jX;`_n>hT}`x-34bZ$5M6$(e8Mn-QicQ}s%)Q?m1kuO%=tvgBuL;5=DsQ`uqm za3q(XuiRFWlbVwnJ1{*05duK} zVey0)KqZNGzpClXxkNjdIkyV`N^;))eTzb^Vb=kaQ?~08{)9jg*$(GGLI<2N>-Ayn zWJxR}Ll15VEp8B_`nNRPZboW9Q>f;eI2Y=pt6q%OAhiXo!>}(l30?>ndEw^q;F6dx zNp|?EFU9d{GZJbmzaZ7OlQcW_62)21ZUP*qW~Wxz2o6!~m>kF7X6L@Zw71_7$D{lm z;}y@L-|~vtNX-=apeKS4`W${OfZsoWe_n=P1!f-vIX970sZe*dm988~s3XZF|E=gj zggPAJOytCH z2%wxw-TeE{rFc;&9Z1sI;UAJ@cMSlm{Hy-=Rn z3#(+}a__``rP!8-S@_V{$1Aq#saA*L=tl0!XGB}vH_LY9yYIX19}2XC-335Sp>F#X zJCzGM_pV;2W#-9+jCb!H5y{&;j-j03%C@> zzQUh`P%#1v#UUIG8#1OEA8;!YJxZ94GnCM1FBL^MR@kkUAT(CzBZRQMPHd@wb(27- zPWPr?S2t39Audl8tDsF?z~1(5H&JHeS8EjiYrFlU0C|GCj7 zWc#NPYu4E<{^#w}5|7&#C!V+GBw}A%6bpSEEBX{OalRGUX2EX_kbmf4t71&T$vlUy zBkDnzzV8GZv`101FAHU1VH(LL&fNj`5`mIeQ>gFR%K+7jLd8V|6R6$<>O%;ZcZ#iy zSilFofX+Z?#Ib@08`B8JP|621KC_ZWRed|)Qx)Pie>N_%vZ5wEaczpn&8^{EQp)VG z3&Hc@Wx`Y)MD5h44ai+ndxZc+{7iv!l!b~3@B_*52 zl6tmAf)rgOU5xs*A;J1)Pq~(o31}F@!AQEkud_D*Czaw9)U-F#6nU8gPlHDrT$}q7 zwm+(*b*>l(*ASjhf>P39+O5z^V1Q$tJvR~ex+<&-u3Tu(1p}66A8UI0oM{T`F{xxr zit0v>>%4kxitI|P#=uP9{W35PAdEV?Q4MTkM9bXw$uz<^YI782yvhC!jDuX?;2*z= zX34WTn6O3vEO|Du%-(4mKRnKK+j>pAk-L=@SG;Z_bkTWfHoGa+BI`oKzpzEm9 zI2z8jCYdZEB#R6?Zy%b0)WQ_B38>PErOeb)Bibz07?SHk8cC4y=60Y+)$DyhdfjEG zl4G67bb@;k-EUV!L_EkFgiw0ev80(q3hic}uUfC!S10byK;q;H3+;1;$Ns4B&oH02 zf8ehvHpF4Uv?7g#r2RPpAD;)AN4@c^KY?sCu~+T^YOgWWUa*t8UnSolY-=;4Dcb|~ z5wMM=<~qJ3mYQoEL2_(r3rfLCuG40IMdGb&w%Fh^@+=o%V$@}w4#~=DEFvVA zqUotykvM)UT$rYOgV0MSJaSo^nHAzl6QsZjU7UXfiqqLXvMAGXm2v3lGRvKC7+?xP zTQ-MIQdS$9d+Ew-0`_y~ZqzJwY(4y2Z&R~)PD`RtQwh}MOzB<(J^`8MqX64I)GWRX zM-0o%M*#xQ{)Dz7lZK5kZOs~x8Vc`Bs0N%?7_P!E%VKAMKF`>=V9o#D$#cv+kERi- z?1|Z*i3OnL^<(Zubf;9$SvyckYThzNc%vs-(z=b-c3)C0@BLwI$4ivg{|tjr zg}pE_B1`zdt4@YDrJk|N1Qa`@;&CC0GsIqnqWzyZAY1tZQ#zYivh{7DgB)#2Wi*EM zn_)kfgumNEDVe1CkrVH~_y5_1twH$`WqZLk0~@G(h(0Wb^zTiLHkTM}cVDU2rl)sK zjrG>s4c1#n`M6Z|-yM2$au1(?5dAi0Y}T&{)m&lX)~u^$Idd!PXZYWU>>tc>96wGE z`Hf^*JM+UN%`YD=2JQv8CCSeNnqxrhkD`S|9MNl?qMyF!E97i>kC1HdEIH&^pRw5& zGDgX^W|v#p4-82!@QOspF&sN}Qz}#+#!V$3Tk4yb?zg^A;!{8{|7K6bY2X+DVkKF&KX!{b7JOXkwqHP^9zNSOkRji|-hr_ta zZ*Aol*5!qUk{=3_N(L9^b!KnG>wBz4?8Lo(uR98?HAgM>cl;diWv!8QIV1ZN?^`B@3Oq1y3jfdVpvUvji7W;C5|`6Rw$ z#hOOz(%c?{aZF5(HDiev6Q`(Pl*XkQ zqCM`3-Af2h>bZOmc;qrl&J%c?qT%gUBjH&}cmn^iKoMOU+amWr49TV%y?J~${S?o? zTNtPZJM-`l)@~)t{8X$r>w>r`R(%#0^SmY={vK+jT3cdrGt6TIFpnf1zWbc6WS4aK z-^w$RbU5}%KS}Ioyf|M#a!QBG{z|1~&?M=w>VXtM3`f5r;Gy+WA&&jB(zrAP#T|u$ z%~2pI&PBo1*X zFrfi|kSNY(;{r$O`jEqkOK@V>YLt%`->`%zJq{yHoi$9T`9_zD z){%APM5Hn!d~xVg_sv`q8VgpWNsw$17s^5TQBWG4l&dcdrj(M{`1Gu_z@U75P;%Pe z+$cE%yEUpfSn^U_CYJl&h?|Gwyly;UpUEE=MwIxL2v9u;sxuMk5sjplqRJy+{5Pp@ zt23!@<9n}BbA@q{U@oWpcd#EJAK|Lj4c@`V=HbIdO#0{hGtzS*|gR=>*}8Mu&#Cztoe`xLgYM`a*2xE zrJUVl-!6T91jQ0LEnqpd%O~qzhakjMBp_QmJK9Uw;#Rb6Q#jPFUyhXGa;Ca+Y(fq9 zsB*B(7F!~F()`4lCi@jahpU^n{=odv{M+On3=p*+ZfG1OL)xT%NK59oCf73RYPYJwo&k!@m_b`>9Iqgd$Y z4)xOcCbkD}^oz9RvF$43%#1q5q^cSyE3S-_4Jt)-ffzk6`Z~`svJ4#}P`l<)D3|sL zhaCF1SBa#4S`*)(STS5YU^&%K^~`aUOHx1omnFSfxbT@!eniYR&Q*D~eqp_0H;O2> zC}X7(GSGPbdC}b4R$V)fBh89pthr^4HveXQsV|e0mK!@bW~ro#;>sx$$R&%CH_N~6 zW{#8DYZAs&pw3(Z06AsRz_;F_K$0w)4Jja?JS!p$IhS&m=dzD`WOJ5*^E~G1lw5)3 z(!AwnC0A|%$N0%gw6J4`8!~BO{ID$CX?;iW7&Ua0{zR%TeVpns&Z+ z{8ms&!kJjpU)DDwuN?hx1e0{@=!y|ft^S%{Z|avx+9}c$NekR1oVtsG-&FDakTV&i z2iFUrKs9ylNJINfA_t5loE}VU^S*hu^RS@)IjM8N-^OVy9_XOpp@OnIgSh@4rUbPZ z`$YAV0}B71`4hg3RaI;R{;VI6oQ36syeSD4O<`wpe}k8kgYG3~_D65-6+m%nrl~Pm zgyUQCfqf>l-P%z5jH#%s8cjuu#i>y_CprvVam2$U|D37wmHB23%YwpB-zpN1ro@{X zD*bbnu{rGrv>$}jWBUWlcLau~FfOgs9Y)eh)0|l!$(#;goLXr{$W#JDv{HQ9yA38J zYNb6?xcJ^m=yVX-ALVLu1>;IPkD#(1BSWF^lJYeBDdgzlyp?tZS}O1=r7!zAJ|vie zrhElJKM)|N#^e%A-GP22)_Lm!y_V=XBLF$&(s%g(5g?+GGMAgDn@JiPyda|l=ql>@ zh3v6kFywjSV0+-SjyaxRbTdK(nmsTwBPUoE%hfU5Zg~Vg`kT<2tqo9 z&-8_}M7{ZFx?yQA~?65c)2Rfc~za! zcGAw}7O_zXETy)Xzu$!va9lcbiy`KZB{6?@+#Q8~NX|>gO9j+>8`mzA+G(mTpu8Qlk3?OsP!#C}(%f&HY?h;R?8mWZ7WO|99rW!)uK z4CuI+ZdsGKESTAQ6tnxpLb_$$Cv*$O0$mZM!zfyZRNnlu+;B65idEt6smU`{Eq3d` zLVwj8=6b8(CvW%MIw;p8-bXB8Ih)K|hsawHNB(3Oj~)=Z2XD~XeMrEZPbs(59xLJM z6u6XfWpmSj>k;M38NQry)ex>yh{LBILu#R8X{QnlJ+z%{EF_jjdI@RBLj$^kz{rt6oXS~AaSm}j^uJZ|Gc>!EWd`0U)8 z=>19H`am+R6xcAG%K^A26z(bo({yFVHDTxzf0Q2$!gc)tfcl$2c~U5w3#zT1iX`;Z0S_Kx zqZ`LeZVuo)r8pD2tRWl6GXis}g9H=KR!ul>AB}xMJJCVE8&;hU&aRXuod$k48@(dF z&&boNbsezZ`2j0Ew2`y9fqZ}PL-m;lF!cn+SzP{k8w?zR8&$3q^$&r`bO!3r<#GY0 zfx;9-t|KsZ0#g~#mOjC9&>wPGfDDzww~Wy11+HnyUY}yfBgptUwYCmwzoG@$)$gJjunylxeGdGht!~Q^VgD*a>Px zeUxl=`s}r{ug>OT*m<%_thqY->ia0}!%8f)tKwrKD`ex^tw>QsLC-lW9CvZu(rB_R`uZJAPy$d}G*3PP&6`)@)F5#dRPr|vxQcXn28Af|;c0lnt zyDH(16>G1}`rV4+eyd=GAa1SY7hNtmR1`uC=|Z>RkD>+e3824Zdq_HrlJdc3|DpR0 zT}8gQ;~Tb-;5P`0?|GkD#1#Ueht!ddDZRGcEIEBW31NrJ(v|@Js8EGf7IWz)in~~8 zoL4YU9>YH7G+U1f6~9CKYpyatoXueEI%s^$)+xCaDG`-{pon)chOUReW;uD4dV%q9 z;SVH)raFqW@^o%i?#H$dVY|4nh+1wbw*t6)spS;qpHVJ9!u2K%*7sHh+L>vAc04HX zu}te%s&;tk*3G&$D`>W&bi`~~mA`(TxE!Jcg)laOty77sRO>6T(a+8fLaSc|CV#?o zwuEk<0c}Lia91)i{GCAOg#JEjC&gOHtp=nT*2@m%PP8GkpQoCaY zTH6sb%s9V}JR9(s*JNx&38~v5<7{KcVO*i1c!SmABTJwCrIk+zoE(QjfQLE1cQ-8) z7uq&~;QxUX$+li=Va^w+&Ai7f)rwogtplKN3KX(^9|ejaK;t(^D-43L;MvKBn$}uV zp8GBZ8OY02cm#emnMT@LLW+l-2p7=7vE~N>31*8~Qtt__olFX&!nTt`U*M zQf2F@rP{TD!FHvzlI@C&4z_CVv=ujGKeg3*ZWEwINq6MFPyuJa%7c_nAan;8TA*MX ze4H}RhI|KiXli^1|46CfudjKSSz}`A6wE)BieA48Vpv+viO4MdQ%1%;w+!Do*1r$} z07KWLFn>S^fTBT21U}?Lw=lJYCzD)?$-b`LR6`b5X!7TjY%})>kbwY8t`Xzj|AJz5 zAXtZI0oEsQK0~Nsuoo8OwpSdn;G(MvmD{#Cc8-N6p6LUUJ*L4P3qe0n7o@&a=b<8f z=b<-tf*=NII&-hKs(*b4?ADQR`BN@QqjjFo;Z-iKXu=gAcbIZ*p7gMfS z!c}etuHC=|pCjXK>Xzr|Z~z_X8{xr2<TIEd zN^TqBIMQgb!Q-rZ91EEyq`3nTX;CLiCTX-zGPlFIGa=K39i?PDxLrW@uS7e0^l=K; zmB5t^k&=R8>9ao2YF+)pZ%ZLsy>;FpJ15AXkL5}z2Cv_yVEVI1pmSW)@Z|ZPXtl!u zb#7A^m`)xE)&9t>HhKNDAIOMM+x7}`>9lC$pTVPCHX}MMGeUj>Hn9l!b7nWU57c7^h-8b>sNJYsOIMog-x_d|V4L+k-vgq4 znf=`7faVlzk0gCfjwVU4ZH|?`&9ITYAMVnjf)U4hEVV$-H6s$58?HA&8C0D(U7=B7 zvE#m#_DvcM&TcCHQ$~mk zWY$6;eFdDxn?{8Nh$a;@^))+i!oZ5=hT*N4Pm%Y*242|ew#R8p|tfrOMFe27&N1S#$m+M^c_hAESD*^_svg{UqIk_;+wzc@&`?B%WBQJ^zi82~wT*`VJp z($7uOWwRg$1{8KSIKiv3!wVJW>-5Oc5bg*qj){v1+;wy^L}^|(Hd@(uT%m}!SFX3~ zEGO#{e4YuNv0g58<7C}TGf2b+(Pfz*DlVGKN&K2inRUOJO3G~2((ma>c$Pa4Q2#2k zBQO3yxg<@t5K>_v&H{FI9MY?ExhIB}$2Ap56K>L*dUXtwcXBA=yF&qK^$V0RAl-9F zGxv4yy5@!-TLdvdh+-bJU{i0r>nebaCiC|vq{)!LNT)0d<~rj3*-5%={kxZ`xxVEt z0+~~n)qnXD#gcT{iHBfYzYJwd`Ot>y7EUyEMSqA>QlZjri_Qz*ZZRe4lz2v~{#_7p zHn-|Jalz{x4eNHD!E2`g3Xx>-1<0NuC@xJFopql%NtIPh{+XhD&s_!-rz&gNRY_4K zRrWaK&%mV9@-kuL_ISxwwE7P^VBKbrGjZQn3_Po7ZuqPjMSogxF7}oR`N$wt<6P^) zqgY#_@HMtR98&+2KhcRN02GPXAg{lFwc5k(OcTit9y#I)W%`k;04A4GJLltTlxi|8 zaOSI}*UUsrANUNWjtMt5i6hzHjW_MWQr>21ZF-1iH{m`VS3l-_KXy3O2@S~pQnDi0 z6b0cNS;bh~&CP2ROu-PX+)a{dk+YJtOQ}UWBrr7D>7>b~P8ud5<$8ux_N9`$26Rrr zHrw(mWs?Nk$w|Y7o(Z`gS@BoD4!>tG^6WMjm4gdSvekS2KGtV$@(L8@HrIuC4 z8hG&eoj%*HdzjZZ%7IYkL=&pzn%F^e4MBLC3}VbB*9aJm>b2mgaqnd#>9xj$8`Nai zxf=lG)N3dI?>7pSLw1DXNo@~#MP%K|(Tkwsiz?xnzwexcGdM{Z_pB1)X%peaZ0vT{|#(kLa_EZlYeTsKLN|B*tQ1N5G+Zt9pedD&h8Fa zgrFzCJ&j0n&y0KAw5sP%cxPCoI9Du)T>a2mp5=RDYzdp4R$|C1gFFE?*YFOzbnMrL z0_bnu`CXW`Wd!OB;W62NNeN2<&2UYOAf+t}JYv~8qe`5=|6VwxF zRfl&%;spE`UGC3K#QoV`t|>Q{XQ4RHKTur|R~1@br(=*Pw+#TfA0o@+i@iVin)tFd zK?m%WcKOMLkP^L~@X26VK^+(0bf`^B=$y_^w=3^PFc8x}wUvH(O5PKuag#*G7A!&< z-N)5xDbrJjPkaC~dFH-&8=4%CR-;~j1j}u`TS_$;9F20!z*Sd~aLDc}u!)D6H4Y}R zRC&P>3T{V7Lt<$(uNZvCKF$&}_$g^(xrMNWk-)&)P@fjS$PHekQX;-Dx^w~^LV!#lJoHnL&M+N<9xg? z^pjJrAU+hh_EN6nLzI*Y)K(pSYLd9bfgFp~r!@EZ^$@hxcj8)Rt|JV>1dRD>wHrMQ z%sf8p(DVH9i(~UUc4pqZOVT6v6YDr7_X83NoN{0tx2lf^!8&-8gKkt|d?Y}8P9bc` zo@7TjL=bxPph37?Z098z( ztZ_aB>L`I~r?JrP%n-$a`RE>3*-u-!c;im?NUI3J^@{P1q$+RI1M8>VQ@uh(+{D@} zqnD&0+m`$hQ}I8Q<6h#vZe4HN4m;;D!emiXrsHi)V5mNeTsy)E!c^u=k`Q?DR1O&=N zgNyQOLV4|bi{z{7z()h+DQY=yMG!$e4Ty}<5op#yF%TYN$2=SfnlP`7R-`>sl^uy} z3-cEY(0F+Y5S-h@Hsp;ylcK6HczHBb2FUp9j)pm{z9oh}%!j5EV7D`bXs6JDE;s{^9ca;ErsfHKin*BQ{NG&HIO&~Zx}e7c_oUzQ1N!`)~L-X z<(1iK*#nQ+ktHB2YY0xt?DuL`fLUkjo4@a|j0v37zfWv}k*nje@VngRXu4oWS4maR z3xurxH%fM~jm$mT?0drHyDoxqb>q7O*CopJ)|GaY>j%Ph01_$CE?R&!N@WSC8rPq- zyp{H5|8hGj8=clWHq87+T6Ae|s7*X7EfDUX-NMY(?c}yc(3-~4klHFY>H0=+a@%`N z``(>^E&~`*zyH=wM?bc4fyLZ9L;Xa^v#LHWIU6TmaYJ+KMRS#)^~w<`T5tXpK)XiK#(trpXx9nap54+G zCikq!kN2s-V(JoZE%ZxYBTiwyuvD3OkJa%{D>s~C#g5iH_Sm2N)xAwRliXI`q=VMV zJqBE~A0z7gFN8=*!{dK#BXW;kzCm~fy{V-<$-EADBq0}COn=gm^87}4zFsRmW|Uss z&_X-X?XxR7pJF0gbunppc&mP+|GpMdjk!x&1TLlsSfyWs=Zv-TebP@I5?OYZ^sqPS}+I)!$ zd8@5a+nbsjezngvch5;MRa-SfHo}rW2`#U2g-m?%aEL7oE9Lu^K(B6WyVBInBtH<^ zKjJlPt8M-95`Ymcw=H$2TCx;uR%n~M6n>YW&3QSN+_8d~etdrbbBefIa^ua(AbM|n zzlRQO?F(W1Z!zdTA$&Na?W2_Yj8-$N_27$L6Z&D(YE4p(*K&xgplXaKdGOU-q^!&?bnVRUCYl>_)efalb5 zODA`wiA^;G>G^mI97Nz+XMG!(R&^a?`P?cL&RsHc6$*!dPpH)nzHgQ@i%1XzIV9<@ zP~eVLp)P&v^A6v!|88;QIgXE|GVW*!9#u)dMnnD3Y4K zvbT0k%$I9oj1U#xsS>my(m$2)pu5>Za^pFdHc@w^#;ups#p zT0CDRKnwvI%So3{S!GmEPOANcpq4>+zB)nOO>SG)$eAo98|;g7_&O7Qxt&Q?XF_t@ z(ng_Vy?wFnUc|VRaeBdIKZCSQmvaD%NV@nNmSQ;-U06a#*|Mi?LW3e0f#J8lm;Vp4gWVrW2yC{TvF0`wKaMeP)23oRd6G9;h4Z2so z`YR{PWx5oxuvyoC| z^V5LJso@^HZlYhFq~X?qwhUihc<6wvgyx1Fjm-^bUkIYtV~oI?{z3r`{yzlh@_In$ z_fnXLvbo{b3vph52x0hM2+;+xz6Z{b;x$NOpr)mZVak>3j|V zIn~>}-wvQal6qTv8a}_zVbAmkYi_`ezG~n7TwTA80iIEfaudYq*uJ!c9)N-w-~>JQ z!X#d^9}#!3hhKyqNSp_?oF2XCZ4wXRJ--802%&Q3h*A|7PC%e?OI8abR72+uq*OEc zIY8xC>#*PP>l~dsBTDc;LR_}{1jJSXy)nS zyep%NM6^W^S+1`5Tfm4U7|!E_YPNuZw=0FfI+=sTI1z7mq_krBzs1$s6RL@sgDKTq zJ{PD!y(LR(^Nt~uN|J7Wg=8XB3inQR#}7K7h6yavTW;ZX6*1rJSZ zmfn1L4TzNED~YhSZnO#J0^s zi2v|P^*muew@+s4&=NNgTD3i03aZ13Run%p&!L}_vI zQ@7mRa@?gG$PfNmh7kUj=0uYK1vTKM76&wV$sD>7xRlz~kXx=5F6p#IonNOo`TRn_ z`B$g?YwBo9BGbOhtYCmx%qizYwSb}~Km&hiz)|Uhg5HwMY%;8UDs;8SZylhPC zCnZ6mpow69Y4#L?cyRB}oVmdSwQvd2#t9V3CDFQ9&5{J#;}K&h(jvYPkU*X#3#oa` zSjr=5v#F3*1h1^8-Uad~Yu>INonFK#1XoiI6|4dFVCt%$qYiAC6#9q{dm?>@FDk2 z0kPn%G>|oLktx17E8}8|_eWBj>?Lm!g%&au%3x1fAeD=R@X=g>{B5Y;boguPDeXtN zEv3NS7CUMpo5ug?D!qtOC0A+Id$ThJv2h}gs=Y4iwZOZdKLW9%{iUm*uepFlpYO>v zCqqvfqR8qn18!Pcvyt|wDI#7|RH5mI8(IwgnA}Qcr-7N&{Qr@4-T_UW`~QE=$VtKs zOGHG@A(3H3hEgXr0gMb4XVqFOQBiSJ)LI9%1aXflj#`UWtcy{r)KRHg_iAe;wN|ZF zp^iAJ1_cvlexK(&3C;a||Hy57?=^Y#^X$+2Gx{hK(eHc!dX^DDPct$_0raSs?X=#nf{A{osZG>Q^hC&{zi##rauHpPa%s0FTuSK$AHWtg@2!X^ijs6_ za!FO<+u&%-^Eu>5U@`T5$KXBp|1TtS4CcAYFD5)EC8vSsgI1nLA1)z0V=+$;NRmL? z;5rWrmSH5iS3)MYta)HXmX43eq0^cqowuSlypQbDX0}|R;&l);h4KazaM4Fr9-OLU z`$TbW$qJ##FagLu#AJmd$f6zRAakBp8JMivV<{m!BRLObdNOPg1D6x5@fd4fl0nlD zGM{5D@m2`+>YU4|ty6UhK^j?4O|;&(Y1%0Attj00R96Ual0$|mI#t~HBpZFC;2_s+ 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 literal 0 HcmV?d00001 diff --git a/plugins/solokey/data/firmware-version.tdc b/plugins/solokey/data/firmware-version.tdc new file mode 100644 index 0000000000000000000000000000000000000000..859795eb18ca0ed046b2e88acf8259d7603c8b99 GIT binary patch literal 2804 zcmeHJ`)?H275?s>nVns)vDcWz2Ago!Hm?|iW1gXGxVwIU3k1mK0i}dlY$sTDaSDcz zxR}}HVQ1B-MxiaBHd-enRLu)DX+kTJm^#3!X$wKALQ_I#F{M>$3M~++N>ld?gsA^P zf9XhPq`7nNobP)G#EdJaU z6k1zU8+5BUT?GtOX&vwUL^`6%K4`b$uzJ`V_^wuGA_AzdMZ8vP_42^UqKy;i@3q$iAnG~0GJBePd+Dt-NNffvVB3>i=vK45+1>9*LABf_6 zk<*UnxFN(;k*ldm!qn7OH5{pnq|`LVGmcxrc-!t3MKj9s()C#*75}*&s5cjxDxBOt z5SG9Cq&`xs;pNao2Vp6p+Y1djwZPDFX&mxMBZM;1?OD+yzxuBYRydoNK|wYrqwaNt z68o-keLqkQb|-ZweehcM2u{4MmCyDnqsx@JK}^~ z^p|hCm{JhgoRs>|#q?^HG4-kj$MbF|MJlVOB3-f%V%=(|65{QTBPooec?<*v1Ie50 z4h3Q;q)LTE9b@R`FAZt2FIfyTi>5A6bzq~LKBX-+mos??x-0B3UhGfdf*P4c5E!Vb%Zvz=BKQ#Ry}p~%sd_|Yi_*Tdk@y(2cEl;JT0gNe-e2l1jvKJq|K z!`cUH);BcPEn8c&wpu3I)_)`3RJEz4sbphIr?$LzOH;`Q>FD-{HszfxeO5l9A6&Pw z&ELb^7CU+pdFk84M!FdFw&!EVzaRJ9WOh4i_Fq+DEZxBkQ?DV?yZI)o+KTD-PhxIL zs4^>3^5GMYTVX7sVAgD$cqr63cudW}nTA6sr73LfZt?C>u4Jcb$a+%cF}IFXQ>+v> z)@^?5M)PCs@y_e5P3^5M8#oH`x5d9$bpCdFQ32b{ z-rC$1UxJc9kFJrcioPCCVFJy+AXdm{pWDXHD@#pY?MNkXfZ2FjH4OQJOvsL4*03;E zoix5Zsqa$+9HUr7BGl@5Al?BZm_$K(vR{bi{oD#nz+}3I_4<}0qDTzo1TSf=E(}gJ zk0(!Y_clhd?Y{1aF;pDX)|i%Sz&pP%;L!6y@!8*|c-VoL%vLH<2#rN0nOULBSNd$L z>tfk?Om!K`@3qyXvR1hR%ax6s+LgIy_r1NR>BBvX*RSgOy+td7Sk?*e={G@pUjwZT zfhI2i?IS-{Qwc%b8D*qvv~o(s3z{h6Y@a!j+;c{E*IkGn{Ac#r!M8U3akt@5g!(jwuN)_8zr|ws_%H9A%Fq-1i-L z)EvmR-yWPzP=)+Q#Xg-;Ue3V>Ck6ZzMy`a%>bX8 zuPV}CVMkj<_g>TIV*@%!6D_E|LYM{&t5kixs%UzhmO?B(tpbjG(}K<``EWWVe)$}E ziAY*yG{f>+DFuJugNGJP*m||7 zzEA=Gl9iRgG!hKeFb$zRbcf?R4iTM>7=3_t(y*A$>Q7)3jXE+>UGC*&8?$0!-D}6*icGX1uD+1iFKjR= zIHbq>u})gEkB0ksA?l~TD4pxPnAmV?3M#3q)%UN8ac17P) jgFN=8Q@(56dY=3)!5QCw-wNhhrRPM}c$sq)Xngw*mv5=B literal 0 HcmV?d00001 diff --git a/plugins/solokey/data/lsusb.txt b/plugins/solokey/data/lsusb.txt new file mode 100644 index 000000000..bce01b7fe --- /dev/null +++ b/plugins/solokey/data/lsusb.txt @@ -0,0 +1,137 @@ +Bus 001 Device 011: ID 0483:a2ca STMicroelectronics Solo 2.3.0 +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 STMicroelectronics + idProduct 0xa2ca + bcdDevice 1.00 + iManufacturer 1 SoloKeys + iProduct 2 Solo 2.3.0 + iSerial 3 2060329D304B + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 0x0029 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 2 Solo 2.3.0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.11 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 34 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 5 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 5 +Device Status: 0x0000 + (Bus Powered) + +Bus 001 Device 013: ID 0483:a2ca STMicroelectronics Solo Bootloader 2.3.0 +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 STMicroelectronics + idProduct 0xa2ca + bcdDevice 1.00 + iManufacturer 1 SoloKeys + iProduct 2 Solo Bootloader 2.3.0 + iSerial 3 2060329D304B + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 0x0029 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 3 Human Interface Device + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 2 Solo Bootloader 2.3.0 + HID Device Descriptor: + bLength 9 + bDescriptorType 33 + bcdHID 1.11 + bCountryCode 0 Not supported + bNumDescriptors 1 + bDescriptorType 34 Report + wDescriptorLength 34 + Report Descriptors: + ** UNAVAILABLE ** + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 5 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 5 +Device Status: 0x0000 + (Bus Powered) diff --git a/plugins/solokey/fu-plugin-solokey.c b/plugins/solokey/fu-plugin-solokey.c new file mode 100644 index 000000000..f896df482 --- /dev/null +++ b/plugins/solokey/fu-plugin-solokey.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" + +#include "fu-solokey-device.h" + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.solokeys"); +} + +gboolean +fu_plugin_update (FuPlugin *plugin, + FuDevice *device, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + + /* write firmware */ + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + return fu_device_write_firmware (device, blob_fw, flags, error); +} + +gboolean +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuSolokeyDevice) dev = NULL; + + /* open the device */ + dev = fu_solokey_device_new (device); + locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + + /* insert to hash */ + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + return TRUE; +} diff --git a/plugins/solokey/fu-solokey-device.c b/plugins/solokey/fu-solokey-device.c new file mode 100644 index 000000000..599c13758 --- /dev/null +++ b/plugins/solokey/fu-solokey-device.c @@ -0,0 +1,547 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "dfu-firmware.h" +#include "fu-chunk.h" +#include "fu-solokey-device.h" + +struct _FuSolokeyDevice { + FuUsbDevice parent_instance; + guint32 cid; +}; + +G_DEFINE_TYPE (FuSolokeyDevice, fu_solokey_device, FU_TYPE_USB_DEVICE) + +#define SOLO_EXTENSION_VERSION 0x14 +#define SOLO_BOOTLOADER_WRITE 0x40 +#define SOLO_BOOTLOADER_DONE 0x41 +#define SOLO_BOOTLOADER_VERSION 0x44 +#define SOLO_BOOTLOADER_HID_CMD_BOOT 0x50 + +#define SOLO_USB_TIMEOUT 5000 /* ms */ +#define SOLO_USB_HID_EP 0x0001 +#define SOLO_USB_HID_EP_IN (SOLO_USB_HID_EP | 0x80) +#define SOLO_USB_HID_EP_OUT (SOLO_USB_HID_EP | 0x00) +#define SOLO_USB_HID_EP_SIZE 64 + +static void +fu_solokey_device_exchange (GByteArray *req, guint8 cmd, guint32 addr, GByteArray *ibuf) +{ + guint8 buf_addr[4] = { 0x00 }; + guint8 buf_len[2] = { 0x00 }; + + /* command */ + g_byte_array_append (req, &cmd, sizeof(cmd)); + + /* first *3* bytes of the LE address */ + fu_common_write_uint32 (buf_addr, addr, G_LITTLE_ENDIAN); + g_byte_array_append (req, buf_addr, 3); + + /* "random" number :/ */ + g_byte_array_append (req, (const guint8 *) "\x8C\x27\x90\xf6", 4); + + /* uint16 length then optional (or dummy) data */ + if (ibuf != NULL) { + fu_common_write_uint16 (buf_len, ibuf->len, G_BIG_ENDIAN); + g_byte_array_append (req, buf_len, sizeof(buf_len)); + g_byte_array_append (req, ibuf->data, ibuf->len); + return; + } + + /* dummy data */ + fu_common_write_uint16 (buf_len, 16, G_BIG_ENDIAN); + g_byte_array_append (req, buf_len, sizeof(buf_len)); + for (guint i = 0; i < 16; i++) { + guint8 tmp = 'A'; + g_byte_array_append (req, &tmp, sizeof(tmp)); + } +} + +static gboolean +fu_solokey_device_probe (FuUsbDevice *device, GError **error) +{ + /* always disregard the bcdVersion */ + fu_device_set_version (FU_DEVICE (device), NULL, FWUPD_VERSION_FORMAT_UNKNOWN); + return TRUE; +} + +static gboolean +fu_solokey_device_open (FuUsbDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + g_autofree gchar *product = NULL; + g_auto(GStrv) split = NULL; + + /* got the version using the HID API */ + if (!g_usb_device_set_configuration (usb_device, 0x0001, error)) + return FALSE; + if (!g_usb_device_claim_interface (usb_device, 0x0000, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + return FALSE; + } + + /* parse product ID */ + product = g_usb_device_get_string_descriptor (usb_device, + g_usb_device_get_product_index (usb_device), + error); + if (product == NULL) + return FALSE; + split = g_strsplit (product, " ", -1); + if (g_strv_length (split) < 2) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "product not parsable, got '%s'", + product); + return FALSE; + } + if (g_strcmp0 (split[0], "Solo") != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "product not expected format, got '%s'", + product); + return FALSE; + } + if (g_strcmp0 (split[1], "Hacker") == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Only Solo Secure supported"); + return FALSE; + } + if (g_strcmp0 (split[1], "Bootloader") == 0) { + fu_device_set_version_bootloader (FU_DEVICE (device), split[2]); + fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + fu_device_remove_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER); + } else { + fu_device_set_version (FU_DEVICE (device), split[1], FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_remove_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + fu_device_add_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_solokey_device_packet_tx (FuSolokeyDevice *self, GByteArray *req, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + gsize actual_length = 0; + + /* round up to the endpoint size */ + for (guint i = req->len; i < SOLO_USB_HID_EP_SIZE; i++) { + const guint8 tmp = 0x0; + g_byte_array_append (req, &tmp, 1); + } + + /* request */ + if (g_getenv ("FWUPD_SOLOKEY_VERBOSE") != NULL) { + fu_common_dump_full (G_LOG_DOMAIN, "REQ", req->data, req->len, + 16, FU_DUMP_FLAGS_SHOW_ADDRESSES); + } + + /* do not hit hardware */ + if (g_getenv ("FWUPD_SOLOKEY_EMULATE") != NULL) + return TRUE; + + if (!g_usb_device_interrupt_transfer (usb_device, + SOLO_USB_HID_EP_OUT, + req->data, + req->len, + &actual_length, + SOLO_USB_TIMEOUT, + NULL, /* cancellable */ + error)) { + g_prefix_error (error, "failed to send request: "); + return FALSE; + } + if (actual_length != req->len) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "request not all sent, got %" G_GSIZE_FORMAT, + actual_length); + return FALSE; + } + + /* success */ + return TRUE; +} + +static GByteArray * +fu_solokey_device_packet_rx (FuSolokeyDevice *self, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + gsize actual_length = 0; + g_autoptr(GByteArray) res = g_byte_array_new (); + guint8 buf[SOLO_USB_HID_EP_SIZE] = { 0x00 }; + + /* return anything */ + if (g_getenv ("FWUPD_SOLOKEY_EMULATE") != NULL) + return g_steal_pointer (&res); + + /* read reply */ + if (!g_usb_device_interrupt_transfer (usb_device, + SOLO_USB_HID_EP_IN, + buf, sizeof(buf), + &actual_length, + SOLO_USB_TIMEOUT, + NULL, /* cancellable */ + error)) { + g_prefix_error (error, "failed to get reply: "); + return FALSE; + } + if (g_getenv ("FWUPD_SOLOKEY_VERBOSE") != NULL) + fu_common_dump_raw (G_LOG_DOMAIN, "RES", buf, actual_length); + + /* copy back optional buf */ + g_byte_array_append (res, buf, actual_length); + return g_steal_pointer (&res); +} + +static GByteArray * +fu_solokey_device_packet (FuSolokeyDevice *self, guint8 cmd, + GByteArray *payload, GError **error) +{ + guint8 buf_cid[4] = { 0x00 }; + guint8 buf_len[2] = { 0x00 }; + guint8 cmd_id = cmd | 0x80; + guint16 first_chunk_size; + g_autoptr(GByteArray) req = g_byte_array_new (); + g_autoptr(GByteArray) res = NULL; + + /* U2F header */ + fu_common_write_uint32 (buf_cid, self->cid, G_LITTLE_ENDIAN); + g_byte_array_append (req, buf_cid, sizeof(buf_cid)); + g_byte_array_append (req, &cmd_id, sizeof(cmd_id)); + + /* no payload */ + if (payload == NULL) { + if (!fu_solokey_device_packet_tx (self, req, error)) + return NULL; + return fu_solokey_device_packet_rx (self, error); + } + + /* sanity check */ + if (payload->len > 7609) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "payload impossible size, got %x", + payload->len); + return NULL; + } + + /* initialization packet */ + first_chunk_size = payload->len; + if (payload->len > SOLO_USB_HID_EP_SIZE - 7) + first_chunk_size = SOLO_USB_HID_EP_SIZE - 7; + fu_common_write_uint16 (buf_len, payload->len, G_BIG_ENDIAN); + g_byte_array_append (req, buf_len, sizeof(buf_len)); + g_byte_array_append (req, payload->data, first_chunk_size); + if (!fu_solokey_device_packet_tx (self, req, error)) + return NULL; + + /* continuation packets */ + if (payload->len > first_chunk_size) { + g_autoptr(GPtrArray) chunks = NULL; + chunks = fu_chunk_array_new (payload->data + first_chunk_size, + payload->len - first_chunk_size, + 0x00, /* addr start */ + 0x00, /* page_sz */ + SOLO_USB_HID_EP_SIZE - 5); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + guint8 seq = chk->idx; + g_autoptr(GByteArray) req2 = g_byte_array_new (); + g_byte_array_append (req2, buf_cid, sizeof(buf_cid)); + g_byte_array_append (req2, &seq, sizeof(seq)); + g_byte_array_append (req2, chk->data, chk->data_sz); + if (!fu_solokey_device_packet_tx (self, req2, error)) + return NULL; + } + } + + /* do not hit hardware */ + if (g_getenv ("FWUPD_SOLOKEY_EMULATE") != NULL) + return g_byte_array_new (); + + /* success */ + res = fu_solokey_device_packet_rx (self, error); + if (res == NULL) + return NULL; + if (res->len != SOLO_USB_HID_EP_SIZE) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "result invalid size, got %x", res->len); + return NULL; + } + if (memcmp (res->data + 0, buf_cid, sizeof(buf_cid)) != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "CID invalid, got %x", + fu_common_read_uint32 (res->data + 0, G_BIG_ENDIAN)); + return NULL; + } + if (res->data[4] != cmd_id) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "commmand ID invalid, got %x", res->data[4]); + return NULL; + } + return g_steal_pointer (&res); +} + +static gboolean +fu_solokey_device_setup_cid (FuSolokeyDevice *self, GError **error) +{ + g_autoptr(GByteArray) nonce = g_byte_array_new (); + g_autoptr(GByteArray) res = NULL; + + /* do not hit hardware */ + if (g_getenv ("FWUPD_SOLOKEY_EMULATE") != NULL) + return TRUE; + + /* get a channel ID */ + for (guint i = 0; i < 8; i++) { + guint8 tmp = g_random_int_range (0x00, 0xff); + g_byte_array_append (nonce, &tmp, 1); + } + res = fu_solokey_device_packet (self, 0x06, nonce, error); + if (res == NULL) + return FALSE; + if (fu_common_read_uint16 (res->data + 5, G_LITTLE_ENDIAN) < 0x11) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "INIT length invalid"); + return FALSE; + } + if (memcmp (res->data + 7, nonce->data, 8) != 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "nonce invalid"); + return FALSE; + } + self->cid = fu_common_read_uint32 (res->data + 7 + 8, G_LITTLE_ENDIAN); + g_debug ("CID to use for device: %04x", self->cid); + return TRUE; +} + +static gboolean +fu_solokey_device_get_version_bl (FuSolokeyDevice *self, GError **error) +{ + g_autofree gchar *version = NULL; + g_autoptr(GByteArray) req = g_byte_array_new (); + g_autoptr(GByteArray) res = NULL; + + /* pass thru data */ + fu_solokey_device_exchange (req, SOLO_BOOTLOADER_VERSION, 0x00, NULL); + res = fu_solokey_device_packet (self, SOLO_BOOTLOADER_HID_CMD_BOOT, req, error); + if (res == NULL) + return FALSE; + version = g_strdup_printf ("%u.%u.%u", res->data[8], res->data[9], res->data[10]); + fu_device_set_version_bootloader (FU_DEVICE (self), version); + return TRUE; +} + +static gboolean +fu_solokey_device_setup (FuDevice *device, GError **error) +{ + FuSolokeyDevice *self = FU_SOLOKEY_DEVICE (device); + + /* get channel ID */ + if (!fu_solokey_device_setup_cid (self, error)) + return FALSE; + + /* verify version */ + if (fu_device_has_flag (FU_DEVICE (device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + if (!fu_solokey_device_get_version_bl (self, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static GByteArray * +_g_base64_decode_to_byte_array (const gchar *text) +{ + gsize out_len = 0; + guchar *out = g_base64_decode (text, &out_len); + return g_byte_array_new_take ((guint8 *) out, out_len); +} + +static GBytes * +_g_base64_decode_to_bytes (const gchar *text) +{ + gsize out_len = 0; + guchar *out = g_base64_decode (text, &out_len); + return g_bytes_new_take ((guint8 *) out, out_len); +} + +static gboolean +fu_solokey_device_verify (FuSolokeyDevice *self, const gchar *base64, GError **error) +{ + g_autoptr(GByteArray) req = g_byte_array_new (); + g_autoptr(GByteArray) res = NULL; + g_autoptr(GByteArray) sig = _g_base64_decode_to_byte_array (base64); + + fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_VERIFY); + fu_solokey_device_exchange (req, SOLO_BOOTLOADER_DONE, 0x00, sig); + res = fu_solokey_device_packet (self, SOLO_BOOTLOADER_HID_CMD_BOOT, req, error); + if (res == NULL) + return FALSE; + return TRUE; +} + +static gboolean +fu_solokey_device_write_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuSolokeyDevice *self = FU_SOLOKEY_DEVICE (device); + DfuElement *element; + DfuImage *image; + JsonNode *json_root; + JsonObject *json_obj; + const gchar *base64; + g_autoptr(DfuFirmware) firmware = dfu_firmware_new (); + g_autoptr(GBytes) fw_ihex = NULL; + g_autoptr(GPtrArray) chunks = NULL; + g_autoptr(GString) base64_websafe = NULL; + g_autoptr(JsonParser) parser = json_parser_new (); + + /* parse JSON */ + fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); + if (!json_parser_load_from_data (parser, + (const gchar *) g_bytes_get_data (fw, NULL), + (gssize) g_bytes_get_size (fw), + error)) { + g_prefix_error (error, "firmware not in JSON format: "); + return FALSE; + } + json_root = json_parser_get_root (parser); + json_obj = json_node_get_object (json_root); + if (!json_object_has_member (json_obj, "firmware")) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "JSON invalid as has no 'firmware'"); + return FALSE; + } + if (!json_object_has_member (json_obj, "signature")) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "JSON invalid as has no 'signature'"); + return FALSE; + } + + /* decode */ + base64 = json_object_get_string_member (json_obj, "firmware"); + fw_ihex = _g_base64_decode_to_bytes (base64); + if (!dfu_firmware_parse_data (firmware, fw_ihex, DFU_FIRMWARE_PARSE_FLAG_NONE, error)) + return FALSE; + image = dfu_firmware_get_image_default (firmware); + if (image == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "No image data"); + return FALSE; + } + element = dfu_image_get_element_default (image); + if (element == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "No element data"); + return FALSE; + } + + /* build packets */ + chunks = fu_chunk_array_new_from_bytes (dfu_element_get_contents (element), + dfu_element_get_address (element), + 0x00, /* page_sz */ + 2048); + + /* write each block */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + g_autoptr(GByteArray) buf = g_byte_array_new (); + g_autoptr(GByteArray) req = g_byte_array_new (); + g_autoptr(GByteArray) res = NULL; + g_autoptr(GError) error_local = NULL; + + g_byte_array_append (buf, chk->data, chk->data_sz); + fu_solokey_device_exchange (req, SOLO_BOOTLOADER_WRITE, chk->address, buf); + res = fu_solokey_device_packet (self, SOLO_BOOTLOADER_HID_CMD_BOOT, req, &error_local); + if (res == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to write: %s", + error_local->message); + return FALSE; + } + + /* update progress */ + fu_device_set_progress_full (device, (gsize) i, (gsize) chunks->len); + } + + /* verify the signature and reboot back to runtime */ + base64_websafe = g_string_new (json_object_get_string_member (json_obj, "signature")); + fu_common_string_replace (base64_websafe, "-", "+"); + fu_common_string_replace (base64_websafe, "_", "/"); + g_string_append (base64_websafe, "=="); + return fu_solokey_device_verify (self, base64_websafe->str, error); +} + +static void +fu_solokey_device_init (FuSolokeyDevice *self) +{ + self->cid = 0xffffffff; + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_USER_REPLUG); + fu_device_set_name (FU_DEVICE (self), "Solo Secure"); + fu_device_set_summary (FU_DEVICE (self), "An open source FIDO2 security key"); + fu_device_add_icon (FU_DEVICE (self), "applications-internet"); +} + +static void +fu_solokey_device_class_init (FuSolokeyDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); + klass_device->write_firmware = fu_solokey_device_write_firmware; + klass_device->setup = fu_solokey_device_setup; + klass_usb_device->open = fu_solokey_device_open; + klass_usb_device->probe = fu_solokey_device_probe; +} + +FuSolokeyDevice * +fu_solokey_device_new (FuUsbDevice *device) +{ + FuSolokeyDevice *self = NULL; + self = g_object_new (FU_TYPE_SOLOKEY_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; +} diff --git a/plugins/solokey/fu-solokey-device.h b/plugins/solokey/fu-solokey-device.h new file mode 100644 index 000000000..e82225b31 --- /dev/null +++ b/plugins/solokey/fu-solokey-device.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define FU_TYPE_SOLOKEY_DEVICE (fu_solokey_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuSolokeyDevice, fu_solokey_device, FU, SOLOKEY_DEVICE, FuUsbDevice) + +FuSolokeyDevice *fu_solokey_device_new (FuUsbDevice *device); + +G_END_DECLS diff --git a/plugins/solokey/meson.build b/plugins/solokey/meson.build new file mode 100644 index 000000000..868efa368 --- /dev/null +++ b/plugins/solokey/meson.build @@ -0,0 +1,32 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginSoloKey"'] + +install_data([ + 'solokey.quirk', + ], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_solokey', + fu_hash, + sources : [ + 'fu-solokey-device.c', + 'fu-plugin-solokey.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../dfu'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + install : true, + install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + dfu, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + libjsonglib, + ], +) diff --git a/plugins/solokey/solokey.quirk b/plugins/solokey/solokey.quirk new file mode 100644 index 000000000..aee996234 --- /dev/null +++ b/plugins/solokey/solokey.quirk @@ -0,0 +1,4 @@ +# SoloKey1 +[DeviceInstanceId=USB\VID_0483&PID_A2CA] +Plugin = solokey +InstallDuration = 20 From ffe7fe24bc1cff9994b567ae7c417e3d23887ce1 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 30 Jul 2019 12:49:17 -0500 Subject: [PATCH 006/158] trivial: Update icons for Thunderbolt and MST devices --- plugins/dell-dock/dell-dock.quirk | 2 ++ plugins/thunderbolt/fu-plugin-thunderbolt.c | 7 ++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/dell-dock/dell-dock.quirk b/plugins/dell-dock/dell-dock.quirk index 2f683c386..6329a34c4 100644 --- a/plugins/dell-dock/dell-dock.quirk +++ b/plugins/dell-dock/dell-dock.quirk @@ -97,6 +97,7 @@ DellDockInstallDurationI2C=360 DellDockBlobMajorOffset = 0x18400 DellDockBlobMinorOffset = 0x18401 DellDockBlobBuildOffset = 0x18402 +Icon = video-display # Thunderbolt controller [Guid=TBT-00d4b070] @@ -107,6 +108,7 @@ ParentGuid = USB\VID_413C&PID_B06E&hub&embedded FirmwareSizeMin=0x40000 FirmwareSizeMax=0x80000 Flags = require-ac +Icon = thunderbolt InstallDuration = 22 DellDockInstallDurationI2C = 181 DellDockUnlockTarget = 10 diff --git a/plugins/thunderbolt/fu-plugin-thunderbolt.c b/plugins/thunderbolt/fu-plugin-thunderbolt.c index c318e1e1f..dc5dbbecc 100644 --- a/plugins/thunderbolt/fu-plugin-thunderbolt.c +++ b/plugins/thunderbolt/fu-plugin-thunderbolt.c @@ -356,12 +356,9 @@ fu_plugin_thunderbolt_add (FuPlugin *plugin, GUdevDevice *device) fu_device_set_name (dev, name); } } - if (is_host) { + if (is_host) fu_device_set_summary (dev, "Unmatched performance for high-speed I/O"); - fu_device_add_icon (dev, "computer"); - } else { - fu_device_add_icon (dev, "audio-card"); - } + fu_device_add_icon (dev, "thunderbolt"); fu_device_set_quirks (dev, fu_plugin_get_quirks (plugin)); vendor = g_udev_device_get_sysfs_attr (device, "vendor_name"); From 596d593cdd1dde02595ca95c003e78f6fb340638 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 31 Jul 2019 10:21:11 +0100 Subject: [PATCH 007/158] Do not segfault when trying to quit the downgrade selection --- src/fu-util.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/fu-util.c b/src/fu-util.c index 722730633..d4563c86f 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -1340,6 +1340,8 @@ fu_util_prompt_for_release (FuUtilPrivate *priv, GPtrArray *rels, GError **error /* TRANSLATORS: get interactive prompt */ g_print ("%s\n", _("Choose a release:")); + /* TRANSLATORS: this is to abort the interactive prompt */ + g_print ("0.\t%s\n", _("Cancel")); for (guint i = 0; i < rels->len; i++) { rel = g_ptr_array_index (rels, i); g_print ("%u.\t%s (%s)\n", @@ -1348,6 +1350,13 @@ fu_util_prompt_for_release (FuUtilPrivate *priv, GPtrArray *rels, GError **error fwupd_release_get_description (rel)); } idx = fu_util_prompt_for_number (rels->len); + if (idx == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "Request canceled"); + return NULL; + } rel = g_ptr_array_index (rels, idx - 1); return g_object_ref (rel); } From 7565baf3c0e7968aede695a89faaf3a6343378df Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 31 Jul 2019 10:22:53 +0100 Subject: [PATCH 008/158] Never show AppStream markup on the console --- src/fu-util.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/fu-util.c b/src/fu-util.c index d4563c86f..517e5798b 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -1343,11 +1343,23 @@ fu_util_prompt_for_release (FuUtilPrivate *priv, GPtrArray *rels, GError **error /* TRANSLATORS: this is to abort the interactive prompt */ g_print ("0.\t%s\n", _("Cancel")); for (guint i = 0; i < rels->len; i++) { + const gchar *desc_tmp; + g_autofree gchar *desc = NULL; + rel = g_ptr_array_index (rels, i); - g_print ("%u.\t%s (%s)\n", - i + 1, - fwupd_release_get_version (rel), - fwupd_release_get_description (rel)); + + /* no description provided */ + desc_tmp = fwupd_release_get_description (rel); + if (desc_tmp == NULL) { + g_print ("%u.\t%s\n", i + 1, fwupd_release_get_version (rel)); + continue; + } + + /* remove markup, and fall back if we fail */ + desc = fu_util_convert_appstream_description (desc_tmp, NULL); + if (desc == NULL) + desc = g_strdup (desc_tmp); + g_print ("%u.\t%s (%s)\n", i + 1, fwupd_release_get_version (rel), desc); } idx = fu_util_prompt_for_number (rels->len); if (idx == 0) { From 00aabbf78bae4d3fec90a7364370650d04bd1bf4 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 31 Jul 2019 21:13:15 -0500 Subject: [PATCH 009/158] trivial: add cache directory into systemd unit rw paths --- data/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/data/meson.build b/data/meson.build index 61664cd68..c40d56836 100644 --- a/data/meson.build +++ b/data/meson.build @@ -41,6 +41,7 @@ if get_option('systemd') rw_directories = [] rw_directories += join_paths (localstatedir, 'lib', 'fwupd') + rw_directories += join_paths (localstatedir, 'cache', 'fwupd') rw_directories += join_paths (sysconfdir, 'fwupd') rw_directories += join_paths (sysconfdir, 'fwupd', 'remotes.d') if get_option('plugin_uefi') From b4627629cdaadd1a75d7c650b7c5973fcd18dfb0 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 1 Aug 2019 09:45:25 +0100 Subject: [PATCH 010/158] Relax the certificate time checks in the self tests for the legacy certificate One test verifies a firmware with a signature from the old LVFS which was hosted on secure-lvfs.rhcloud.com and used the original PKCS-7 key. This key had a two year validity (expiring today, ohh the naivety...) rather than the newer fwupd.org key which expires in the year 2058. For this specific test only, disable the certificate time checks to fix CI. Fixes https://github.com/hughsie/fwupd/issues/1264 --- src/fu-keyring-pkcs7.c | 10 +++++++++- src/fu-keyring.h | 6 ++++-- src/fu-self-test.c | 3 ++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/fu-keyring-pkcs7.c b/src/fu-keyring-pkcs7.c index 6dc944edd..a42feaa7e 100644 --- a/src/fu-keyring-pkcs7.c +++ b/src/fu-keyring-pkcs7.c @@ -642,6 +642,14 @@ fu_keyring_pkcs7_verify_data (FuKeyring *keyring, for (gint i = 0; i < count; i++) { gnutls_pkcs7_signature_info_st info; gint64 signing_time = 0; + gnutls_certificate_verify_flags verify_flags = 0; + + /* use with care */ + if (flags & FU_KEYRING_VERIFY_FLAG_DISABLE_TIME_CHECKS) { + g_debug ("WARNING: disabling time checks"); + verify_flags |= GNUTLS_VERIFY_DISABLE_TIME_CHECKS; + verify_flags |= GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS; + } /* verify the data against the detached signature */ if (flags & FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT) { @@ -652,7 +660,7 @@ fu_keyring_pkcs7_verify_data (FuKeyring *keyring, 0, /* vdata_size */ i, /* index */ &datum, /* data */ - 0); /* flags */ + verify_flags); } if (rc < 0) { g_set_error (error, diff --git a/src/fu-keyring.h b/src/fu-keyring.h index 39819ca47..2f20e35e1 100644 --- a/src/fu-keyring.h +++ b/src/fu-keyring.h @@ -20,12 +20,14 @@ G_DECLARE_DERIVABLE_TYPE (FuKeyring, fu_keyring, FU, KEYRING, GObject) * FuKeyringVerifyFlags: * @FU_KEYRING_VERIFY_FLAG_NONE: No flags set * @FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT: Use client certificate to verify + * @FU_KEYRING_VERIFY_FLAG_DISABLE_TIME_CHECKS: Disable checking of validity periods * * The flags to use when interacting with a keyring **/ typedef enum { - FU_KEYRING_VERIFY_FLAG_NONE = 0, - FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT = 1 << 1, + FU_KEYRING_VERIFY_FLAG_NONE = 0, + FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT = 1 << 1, + FU_KEYRING_VERIFY_FLAG_DISABLE_TIME_CHECKS = 1 << 2, /*< private >*/ FU_KEYRING_VERIFY_FLAG_LAST } FuKeyringVerifyFlags; diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 363f644ef..24b121107 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -2628,7 +2628,8 @@ fu_keyring_pkcs7_func (void) g_assert_no_error (error); g_assert_nonnull (blob_sig); result_pass = fu_keyring_verify_data (keyring, blob_pass, blob_sig, - FU_KEYRING_VERIFY_FLAG_NONE, &error); + FU_KEYRING_VERIFY_FLAG_DISABLE_TIME_CHECKS, + &error); g_assert_no_error (error); g_assert_nonnull (result_pass); g_assert_cmpint (fu_keyring_result_get_timestamp (result_pass), >= , 1502871248); From dfaca2de2e0c125a71c83c9342e653fbc0f9f4b7 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 1 Aug 2019 08:08:03 +0100 Subject: [PATCH 011/158] Fix a crash when stopping the fwupd service This difficult to debug bug only showed up when the fwupd service was stopped, which the user never noticed, but services like abrt were still keen to report. The root issue was that the call to fu_plugin_get_smbios_data() in fu-plugin-uefi.c:fu_plugin_startup() was freeing the returned const GBytes, which rippled down all the way to a double-free deep in libxmlb. It's somewhat unusual to have a const GBytes, so just change the plugin helper to returned a ref'd copy, on the logic a potential 16 byte memory leak is better than a double-free when the next plugin gets the logic the wrong way around. Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1734746 --- plugins/dell/fu-plugin-dell.c | 8 ++++---- plugins/redfish/fu-plugin-redfish.c | 5 ++++- src/fu-hwids.c | 4 ++-- src/fu-plugin.c | 2 +- src/fu-smbios.c | 4 ++-- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/plugins/dell/fu-plugin-dell.c b/plugins/dell/fu-plugin-dell.c index d1a007bd5..27c8a2363 100644 --- a/plugins/dell/fu-plugin-dell.c +++ b/plugins/dell/fu-plugin-dell.c @@ -169,9 +169,9 @@ fu_dell_host_mst_supported (FuPlugin *plugin) static gboolean fu_dell_supported (FuPlugin *plugin) { - GBytes *de_table = NULL; - GBytes *da_table = NULL; - GBytes *enclosure = NULL; + g_autoptr(GBytes) de_table = NULL; + g_autoptr(GBytes) da_table = NULL; + g_autoptr(GBytes) enclosure = NULL; const guint8 *value; const struct da_structure *da_values; gsize len; @@ -482,7 +482,7 @@ fu_plugin_usb_device_added (FuPlugin *plugin, gboolean fu_plugin_get_results (FuPlugin *plugin, FuDevice *device, GError **error) { - GBytes *de_table = NULL; + g_autoptr(GBytes) de_table = NULL; const gchar *tmp = NULL; const guint16 *completion_code; gsize len; diff --git a/plugins/redfish/fu-plugin-redfish.c b/plugins/redfish/fu-plugin-redfish.c index 60ca60ece..48ec3f768 100644 --- a/plugins/redfish/fu-plugin-redfish.c +++ b/plugins/redfish/fu-plugin-redfish.c @@ -48,9 +48,12 @@ gboolean fu_plugin_startup (FuPlugin *plugin, GError **error) { FuPluginData *data = fu_plugin_get_data (plugin); - GBytes *smbios_data = fu_plugin_get_smbios_data (plugin, REDFISH_SMBIOS_TABLE_TYPE); g_autofree gchar *redfish_uri = NULL; g_autofree gchar *ca_check = NULL; + g_autoptr(GBytes) smbios_data = NULL; + + /* optional */ + smbios_data = fu_plugin_get_smbios_data (plugin, REDFISH_SMBIOS_TABLE_TYPE); /* read the conf file */ redfish_uri = fu_plugin_get_config_value (plugin, "Uri"); diff --git a/src/fu-hwids.c b/src/fu-hwids.c index 4c74658b7..a64c6a2af 100644 --- a/src/fu-hwids.c +++ b/src/fu-hwids.c @@ -268,7 +268,7 @@ fu_hwids_convert_padded_integer_cb (FuSmbios *smbios, guint8 type, guint8 offset, GError **error) { - GBytes *data; + g_autoptr(GBytes) data = NULL; const guint8 *data_raw; gsize data_sz = 0; data = fu_smbios_get_data (smbios, type, error); @@ -290,7 +290,7 @@ fu_hwids_convert_integer_cb (FuSmbios *smbios, guint8 type, guint8 offset, GError **error) { - GBytes *data; + g_autoptr(GBytes) data = NULL; const guint8 *data_raw; gsize data_sz = 0; data = fu_smbios_get_data (smbios, type, error); diff --git a/src/fu-plugin.c b/src/fu-plugin.c index 836e72fb1..501db42f7 100644 --- a/src/fu-plugin.c +++ b/src/fu-plugin.c @@ -600,7 +600,7 @@ fu_plugin_get_smbios_string (FuPlugin *self, guint8 structure_type, guint8 offse * * Gets a hardware SMBIOS data. * - * Returns: (transfer none): A #GBytes, or %NULL + * Returns: (transfer full): A #GBytes, or %NULL * * Since: 0.9.8 **/ diff --git a/src/fu-smbios.c b/src/fu-smbios.c index a65068249..9d725bac2 100644 --- a/src/fu-smbios.c +++ b/src/fu-smbios.c @@ -370,7 +370,7 @@ fu_smbios_get_item_for_type (FuSmbios *self, guint8 type) * * Reads a SMBIOS data blob, which includes the SMBIOS section header. * - * Returns: a #GBytes, or %NULL if invalid or not found + * Returns: (transfer full): a #GBytes, or %NULL if invalid or not found **/ GBytes * fu_smbios_get_data (FuSmbios *self, guint8 type, GError **error) @@ -385,7 +385,7 @@ fu_smbios_get_data (FuSmbios *self, guint8 type, GError **error) "no structure with type %02x", type); return NULL; } - return item->data; + return g_bytes_ref (item->data); } /** From 4836cb360e4cb31296806cadb8831055de8a19b9 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 15 Jul 2019 21:14:03 +0100 Subject: [PATCH 012/158] Verify we don't break the ABI This adds a script which can check for ABI breaks between two Git revisions: $ ./contrib/ci/check-abi.sh The CI is set up to run it automatically between the tip of the branch being tested and the last release tag. Based on the work by Mathieu Bridon , many thanks. --- .circleci/config.yml | 13 ++++ contrib/ci/abidiff.suppr | 3 + contrib/ci/check-abi | 114 ++++++++++++++++++++++++++++++++++++ contrib/ci/dependencies.xml | 5 ++ 4 files changed, 135 insertions(+) create mode 100644 contrib/ci/abidiff.suppr create mode 100755 contrib/ci/check-abi diff --git a/.circleci/config.yml b/.circleci/config.yml index 4e7603bef..32805d1c2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,5 +1,17 @@ version: 2 jobs: + check-abi: + machine: + image: circleci/classic:latest + steps: + - checkout + - run: + name: "Build container" + command: OS=debian-x86_64 ./contrib/ci/generate_docker.py + - run: + name: "Run build script" + command: docker run -t -v `pwd`:/build fwupd-debian-x86_64 ./contrib/ci/check-abi $(git describe --abbrev=0 --tags) $(git rev-parse HEAD) + build-s390x: machine: image: circleci/classic:latest @@ -60,6 +72,7 @@ workflows: jobs: - build-s390x - build-snap + - check-abi - publish-edge: requires: - build-snap diff --git a/contrib/ci/abidiff.suppr b/contrib/ci/abidiff.suppr new file mode 100644 index 000000000..3010c19f4 --- /dev/null +++ b/contrib/ci/abidiff.suppr @@ -0,0 +1,3 @@ +[suppress_type] + type_kind = enum + changed_enumerators = FWUPD_STATUS_LAST diff --git a/contrib/ci/check-abi b/contrib/ci/check-abi new file mode 100755 index 000000000..8621b1c5d --- /dev/null +++ b/contrib/ci/check-abi @@ -0,0 +1,114 @@ +#!/usr/bin/python3 + + +import argparse +import contextlib +import os +import shutil +import subprocess +import sys + + +def format_title(title): + box = { + 'tl': '╔', 'tr': '╗', 'bl': '╚', 'br': '╝', 'h': '═', 'v': '║', + } + hline = box['h'] * (len(title) + 2) + + return '\n'.join([ + f"{box['tl']}{hline}{box['tr']}", + f"{box['v']} {title} {box['v']}", + f"{box['bl']}{hline}{box['br']}", + ]) + + +def rm_rf(path): + try: + shutil.rmtree(path) + except FileNotFoundError: + pass + + +def sanitize_path(name): + return name.replace('/', '-') + + +def get_current_revision(): + revision = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], + encoding='utf-8').strip() + + if revision == 'HEAD': + # This is a detached HEAD, get the commit hash + revision = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip().decode('utf-8') + + return revision + + +@contextlib.contextmanager +def checkout_git_revision(revision): + current_revision = get_current_revision() + subprocess.check_call(['git', 'checkout', '-q', revision]) + + try: + yield + finally: + subprocess.check_call(['git', 'checkout', '-q', current_revision]) + + +def build_install(revision): + build_dir = '_build' + dest_dir = os.path.abspath(sanitize_path(revision)) + print(format_title(f'# Building and installing {revision} in {dest_dir}'), + end='\n\n', flush=True) + + with checkout_git_revision(revision): + rm_rf(build_dir) + rm_rf(revision) + + subprocess.check_call(['meson', build_dir, + '--prefix=/usr', '--libdir=lib', + '-Db_coverage=false', '-Dgtkdoc=false', '-Dtests=false']) + subprocess.check_call(['ninja', '-v', '-C', build_dir]) + subprocess.check_call(['ninja', '-v', '-C', build_dir, 'install'], + env={'DESTDIR': dest_dir}) + + return dest_dir + + +def compare(old_tree, new_tree): + print(format_title(f'# Comparing the two ABIs'), end='\n\n', flush=True) + + old_headers = os.path.join(old_tree, 'usr', 'include') + old_lib = os.path.join(old_tree, 'usr', 'lib', 'libfwupd.so') + + new_headers = os.path.join(new_tree, 'usr', 'include') + new_lib = os.path.join(new_tree, 'usr', 'lib', 'libfwupd.so') + + subprocess.check_call([ + 'abidiff', '--headers-dir1', old_headers, '--headers-dir2', new_headers, + '--drop-private-types', '--suppressions', 'contrib/ci/abidiff.suppr', + '--fail-no-debug-info', '--no-added-syms', old_lib, new_lib]) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + + parser.add_argument('old', help='the previous revision, considered the reference') + parser.add_argument('new', help='the new revision, to compare to the reference') + + args = parser.parse_args() + + if args.old == args.new: + print("Let's not waste time comparing something to itself") + sys.exit(0) + + old_tree = build_install(args.old) + new_tree = build_install(args.new) + + try: + compare(old_tree, new_tree) + + except subprocess.CalledProcessError: + sys.exit(1) + + print(f'Hurray! {args.old} and {args.new} are ABI-compatible!') diff --git a/contrib/ci/dependencies.xml b/contrib/ci/dependencies.xml index 047f51810..0edd6679f 100644 --- a/contrib/ci/dependencies.xml +++ b/contrib/ci/dependencies.xml @@ -10,6 +10,11 @@ + + + + + From 90da72ad765cb13aa6a998ea49858ab1add6334c Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 1 Aug 2019 17:17:13 +0100 Subject: [PATCH 013/158] trivial: Fix overzealous search-replace action in user string --- plugins/uefi/fu-uefi-tool.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/uefi/fu-uefi-tool.c b/plugins/uefi/fu-uefi-tool.c index 0e404e630..fbf2e0519 100644 --- a/plugins/uefi/fu-uefi-tool.c +++ b/plugins/uefi/fu-uefi-tool.c @@ -232,8 +232,8 @@ main (int argc, char *argv[]) if (action_list) { for (guint i = 0; i < devices->len; i++) { FuUefiDevice *dev = g_ptr_array_index (devices, i); - g_print ("%s type, {%s} action_version %" G_GUINT32_FORMAT " can be updated " - "to any action_version above %" G_GUINT32_FORMAT "\n", + g_print ("%s type, {%s} version %" G_GUINT32_FORMAT " can be updated " + "to any version above %" G_GUINT32_FORMAT "\n", fu_uefi_device_kind_to_string (fu_uefi_device_get_kind (dev)), fu_uefi_device_get_guid (dev), fu_uefi_device_get_version (dev), From c5eb83cb876b9a2e9e4c4b45e80e385fa8cb05dd Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 1 Aug 2019 17:17:47 +0100 Subject: [PATCH 014/158] trivial: Do not show a critical warning if no firmware is specified --- plugins/uefi/fu-uefi-tool.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/uefi/fu-uefi-tool.c b/plugins/uefi/fu-uefi-tool.c index fbf2e0519..cdfd869a7 100644 --- a/plugins/uefi/fu-uefi-tool.c +++ b/plugins/uefi/fu-uefi-tool.c @@ -318,7 +318,13 @@ main (int argc, char *argv[]) if (apply != NULL) { g_autoptr(FuUefiDevice) dev = fu_uefi_device_new_from_guid (apply); g_autoptr(GError) error_local = NULL; - g_autoptr(GBytes) fw = fu_common_get_contents_bytes (argv[1], &error_local); + g_autoptr(GBytes) fw = NULL; + + if (argv[1] == NULL) { + g_printerr ("capsule filename required\n"); + return EXIT_FAILURE; + } + fw = fu_common_get_contents_bytes (argv[1], &error_local); if (fw == NULL) { g_printerr ("failed: %s\n", error_local->message); return EXIT_FAILURE; From fad78a758e169585a38f27844d106b9e01c7bbd6 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 1 Aug 2019 17:19:39 +0100 Subject: [PATCH 015/158] Allow setting custom flags when using fwupdate This allows the ODM to specify command line arguments such as: --flags=use-shim-unique,use-legacy-bootmgr-desc,no-ux-capsule ..which is useful for testing broken firmware. --- plugins/uefi/fu-uefi-tool.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/uefi/fu-uefi-tool.c b/plugins/uefi/fu-uefi-tool.c index cdfd869a7..dbc92d13b 100644 --- a/plugins/uefi/fu-uefi-tool.c +++ b/plugins/uefi/fu-uefi-tool.c @@ -62,6 +62,7 @@ main (int argc, char *argv[]) gboolean verbose = FALSE; g_autofree gchar *apply = FALSE; g_autofree gchar *esp_path = NULL; + g_autofree gchar *flags = FALSE; g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1); g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) devices = NULL; @@ -99,6 +100,9 @@ main (int argc, char *argv[]) { "apply", 'a', 0, G_OPTION_ARG_STRING, &apply, /* TRANSLATORS: command line option */ _("Apply firmware updates"), "GUID" }, + { "flags", 'f', 0, G_OPTION_ARG_STRING, &flags, + /* TRANSLATORS: command line option */ + _("Use quirk flags when installing firmware"), NULL }, { NULL} }; @@ -330,6 +334,8 @@ main (int argc, char *argv[]) return EXIT_FAILURE; } fu_device_set_metadata (FU_DEVICE (dev), "EspPath", esp_path); + if (flags != NULL) + fu_device_set_custom_flags (FU_DEVICE (dev), flags); if (!fu_device_write_firmware (FU_DEVICE (dev), fw, FWUPD_INSTALL_FLAG_NONE, &error_local)) { From a7715bc74aa334087e3a65f4f97b1f9d61b24ebb Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Fri, 2 Aug 2019 10:16:41 +0100 Subject: [PATCH 016/158] uefi: Actually write the new device path if different than before If we add or remove a quirk from a machine using the uefi plugin then the new setting is not used until the BootXXXX EFI key is manually removed. Rather than just writing the contents of the found key back to the key (doh) just write the new DP buffer contents if it is different from the previous buffer. --- plugins/uefi/fu-uefi-bootmgr.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/plugins/uefi/fu-uefi-bootmgr.c b/plugins/uefi/fu-uefi-bootmgr.c index dd2389059..5d3ca98b0 100644 --- a/plugins/uefi/fu-uefi-bootmgr.c +++ b/plugins/uefi/fu-uefi-bootmgr.c @@ -163,17 +163,20 @@ fu_uefi_setup_bootnext_with_dp (const guint8 *dp_buf, guint8 *opt, gssize opt_si /* already exists */ if (var_data != NULL) { - efi_loadopt_attr_set (loadopt, LOAD_OPTION_ACTIVE); - rc = efi_set_variable (*guid, name, var_data, - var_data_size, attr, 0644); - if (rc < 0) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "could not set boot variable active"); - return FALSE; - } + /* is different than before */ + if (var_data_size != (gsize) opt_size || + memcmp (var_data, opt, opt_size) != 0) { + efi_loadopt_attr_set (loadopt, LOAD_OPTION_ACTIVE); + rc = efi_set_variable (*guid, name, opt, opt_size, attr, 0644); + if (rc < 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "could not set boot variable active"); + return FALSE; + } + } /* create a new one */ } else { g_autofree gchar *boot_next_name = NULL; From fd734856d29dc2f28b82432921d295095ab3d156 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 1 Aug 2019 16:41:42 -0500 Subject: [PATCH 017/158] fu-tool: Port ability to update a single device from fwupdmgr --- src/fu-tool.c | 142 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 102 insertions(+), 40 deletions(-) diff --git a/src/fu-tool.c b/src/fu-tool.c index eac7a8cc4..60200c168 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -789,17 +789,56 @@ fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error) } static gboolean -fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error) { - g_autoptr(GPtrArray) devices = NULL; + FwupdRemote *remote; + const gchar *remote_id; + const gchar *uri_tmp; + g_auto(GStrv) argv = NULL; - /* load engine */ - if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) + uri_tmp = fwupd_release_get_uri (rel); + if (uri_tmp == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "release missing URI"); + return FALSE; + } + remote_id = fwupd_release_get_remote_id (rel); + if (remote_id == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to find remote for %s", + uri_tmp); + return FALSE; + } + + remote = fu_engine_get_remote_by_id (priv->engine, + remote_id, + error); + if (remote == NULL) return FALSE; - priv->current_operation = FU_UTIL_OPERATION_UPDATE; - g_signal_connect (priv->engine, "device-changed", - G_CALLBACK (fu_util_update_device_changed_cb), priv); + argv = g_new0 (gchar *, 2); + /* local remotes have the firmware already */ + if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL) { + const gchar *fn_cache = fwupd_remote_get_filename_cache (remote); + g_autofree gchar *path = g_path_get_dirname (fn_cache); + argv[0] = g_build_filename (path, uri_tmp, NULL); + } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) { + argv[0] = g_strdup (uri_tmp + 7); + /* web remote, fu_util_install will download file */ + } else { + argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error); + } + return fu_util_install (priv, argv, error); +} + +static gboolean +fu_util_update_all (FuUtilPrivate *priv, GError **error) +{ + g_autoptr(GPtrArray) devices = NULL; devices = fu_engine_get_devices (priv->engine, error); if (devices == NULL) @@ -807,9 +846,7 @@ fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error) for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index (devices, i); FwupdRelease *rel; - const gchar *remote_id; const gchar *device_id; - const gchar *uri_tmp; g_autoptr(GPtrArray) rels = NULL; g_autoptr(GError) error_local = NULL; @@ -827,38 +864,63 @@ fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error) } rel = g_ptr_array_index (rels, 0); - uri_tmp = fwupd_release_get_uri (rel); - remote_id = fwupd_release_get_remote_id (rel); - if (remote_id != NULL) { - FwupdRemote *remote; - g_auto(GStrv) argv = NULL; - - remote = fu_engine_get_remote_by_id (priv->engine, - remote_id, - &error_local); - if (remote == NULL) { - g_printerr ("%s\n", error_local->message); - continue; - } - - argv = g_new0 (gchar *, 2); - /* local remotes have the firmware already */ - if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL) { - const gchar *fn_cache = fwupd_remote_get_filename_cache (remote); - g_autofree gchar *path = g_path_get_dirname (fn_cache); - argv[0] = g_build_filename (path, uri_tmp, NULL); - } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) { - argv[0] = g_strdup (uri_tmp + 7); - /* web remote, fu_util_install will download file */ - } else { - argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error); - } - if (!fu_util_install (priv, argv, &error_local)) { - g_printerr ("%s\n", error_local->message); - continue; - } - fu_util_display_current_message (priv); + if (!fu_util_install_release (priv, rel, &error_local)) { + g_printerr ("%s\n", error_local->message); + continue; } + fu_util_display_current_message (priv); + } + return TRUE; +} + +static gboolean +fu_util_update_by_id (FuUtilPrivate *priv, const gchar *device_id, GError **error) +{ + FwupdRelease *rel; + g_autoptr(FuDevice) dev = NULL; + g_autoptr(GPtrArray) rels = NULL; + + /* do not allow a partial device-id */ + dev = fu_engine_get_device (priv->engine, device_id, error); + if (dev == NULL) + return FALSE; + + /* get the releases for this device and filter for validity */ + rels = fu_engine_get_upgrades (priv->engine, device_id, error); + if (rels == NULL) + return FALSE; + rel = g_ptr_array_index (rels, 0); + if (!fu_util_install_release (priv, rel, error)) + return FALSE; + fu_util_display_current_message (priv); + + return TRUE; +} + +static gboolean +fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error) +{ + if (g_strv_length (values) > 1) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) + return FALSE; + + priv->current_operation = FU_UTIL_OPERATION_UPDATE; + g_signal_connect (priv->engine, "device-changed", + G_CALLBACK (fu_util_update_device_changed_cb), priv); + + if (g_strv_length (values) == 1) { + if (!fu_util_update_by_id (priv, values[0], error)) + return FALSE; + } else { + if (!fu_util_update_all (priv, error)) + return FALSE; } /* we don't want to ask anything */ From c6dba62cf04bc607fba7291ed79a87ba46848b09 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 5 Aug 2019 09:24:25 +0100 Subject: [PATCH 018/158] solokey: Release the interface and rebind the kernel driver on close --- plugins/solokey/fu-solokey-device.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/plugins/solokey/fu-solokey-device.c b/plugins/solokey/fu-solokey-device.c index 599c13758..0fcb7ca5c 100644 --- a/plugins/solokey/fu-solokey-device.c +++ b/plugins/solokey/fu-solokey-device.c @@ -133,6 +133,23 @@ fu_solokey_device_open (FuUsbDevice *device, GError **error) return TRUE; } +static gboolean +fu_solokey_device_close (FuUsbDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + + /* rebind kernel driver so it works as a security key again... */ + if (!g_usb_device_release_interface (usb_device, 0x0000, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error (error, "failed to release interface: "); + return FALSE; + } + + /* success */ + return TRUE; +} + static gboolean fu_solokey_device_packet_tx (FuSolokeyDevice *self, GByteArray *req, GError **error) { @@ -534,6 +551,7 @@ fu_solokey_device_class_init (FuSolokeyDeviceClass *klass) klass_device->write_firmware = fu_solokey_device_write_firmware; klass_device->setup = fu_solokey_device_setup; klass_usb_device->open = fu_solokey_device_open; + klass_usb_device->close = fu_solokey_device_close; klass_usb_device->probe = fu_solokey_device_probe; } From 3eb27564efcb57f965012db9bc39f5d44d369240 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Mon, 5 Aug 2019 16:30:06 +0200 Subject: [PATCH 019/158] modem-manager: add counterpart GUID for the DW5821e The DW5821e in modem mode uses the 0x81D7 PID, but when it boots in fastboot mode it uses the 0x81D6 PID. The upgrade procedure was able to automatically match these two devices because they both shared the very generic per-VID (0x413C) GUID, but this is no longer operational since commit 8ef139faba, as the very generic GUIDs have been removed for matching purposes. So, define a quirk for the DW5821e, in order to add a counterpart GUID to make device matching work properly again. Fixes https://github.com/hughsie/fwupd/issues/1271 --- plugins/modem-manager/meson.build | 4 ++++ plugins/modem-manager/modem-manager.quirk | 11 +++++++++++ 2 files changed, 15 insertions(+) create mode 100644 plugins/modem-manager/modem-manager.quirk diff --git a/plugins/modem-manager/meson.build b/plugins/modem-manager/meson.build index dd686ba39..ce1d3c661 100644 --- a/plugins/modem-manager/meson.build +++ b/plugins/modem-manager/meson.build @@ -1,5 +1,9 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginMm"'] +install_data(['modem-manager.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + shared_module('fu_plugin_modem_manager', fu_hash, sources : [ diff --git a/plugins/modem-manager/modem-manager.quirk b/plugins/modem-manager/modem-manager.quirk new file mode 100644 index 000000000..e73ff4bd7 --- /dev/null +++ b/plugins/modem-manager/modem-manager.quirk @@ -0,0 +1,11 @@ + +# DW5821e +[DeviceInstanceId=USB\VID_413C&PID_81D7] +Summary = Dell DW5821e LTE modem +CounterpartGuid = USB\VID_413C&PID_81D6 + +# DW5821e in fastboot mode +[DeviceInstanceId=USB\VID_413C&PID_81D6] +Summary = Dell DW5821e LTE modem (fastboot) +CounterpartGuid = USB\VID_413C&PID_81D7 + From dc6577c8b8b5ab31025bbbc6d702318f56206694 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 5 Aug 2019 16:05:02 -0500 Subject: [PATCH 020/158] trivial: fixup standalone installer There were some logic failures in the handling of pre-installed version that were reported on the mailing list. --- contrib/standalone-installer/assets/header.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/contrib/standalone-installer/assets/header.py b/contrib/standalone-installer/assets/header.py index 232a82058..4d14d0739 100644 --- a/contrib/standalone-installer/assets/header.py +++ b/contrib/standalone-installer/assets/header.py @@ -221,7 +221,7 @@ def use_included_version(minimum_version): print("fwupd %s is already installed but this package requires %s" % (version.version, minimum_version)) else: - print("New enough fwupd already installed") + print("Using existing fwupd version %s already installed on system." % version.version) return False else: print("fwupd %s is installed and must be removed" % version.version) @@ -229,18 +229,20 @@ def use_included_version(minimum_version): def remove_packaged_version(pkg, cache): res = False - while not res: + while True: res = input("Remove now (Y/N)? ") if res.lower() == 'n': - return False - if res.lower() == 'y': + res = False break - res = False - pkg.mark_delete() - res = cache.commit() + if res.lower() == 'y': + res = True + break + if res: + pkg.mark_delete() + res = cache.commit() if not res: raise Exception("Need to remove packaged version") - return res + return True def install_builtin(directory, verbose, allow_reinstall, allow_older): cabs = [] @@ -271,7 +273,7 @@ def run_installation (directory, verbose, allow_reinstall, allow_older, uninstal with open(minimum_path, "r") as rfd: minimum = rfd.read() - if use_included_version(minimum): + if not use_included_version(minimum): install_builtin(directory, verbose, allow_reinstall, allow_older) return From 011511e42f12833df744fc8355da9ca741944ba4 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 6 Aug 2019 09:49:06 +0100 Subject: [PATCH 021/158] trivial: Add translation for X-ThunderboltController --- src/fu-util-common.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 636f875cc..55b906ba6 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -557,6 +557,12 @@ fu_util_release_get_name (FwupdRelease *release) * the first %s is the device name, e.g. 'Intel ThunderBolt` */ return g_strdup_printf (_("%s Controller Update"), name); } + if (g_strcmp0 (cat, "X-ThunderboltController") == 0) { + /* TRANSLATORS: the Thunderbolt controller is a device that + * has other high speed Thunderbolt devices plugged into it; + * the first %s is the system name, e.g. 'ThinkPad P50` */ + return g_strdup_printf (_("%s Thunderbolt Controller Update"), name); + } } /* TRANSLATORS: this is the fallback where we don't know if the release From d59075bbf84702d144412c222f2c4d413318aada Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 6 Aug 2019 15:45:49 +0100 Subject: [PATCH 022/158] Be more accepting when trying to recover a failed database migration Fixes https://github.com/hughsie/fwupd/issues/1196 --- src/fu-history.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fu-history.c b/src/fu-history.c index bc163978d..aa0097faf 100644 --- a/src/fu-history.c +++ b/src/fu-history.c @@ -157,11 +157,11 @@ fu_history_create_database (FuHistory *self, GError **error) gint rc; rc = sqlite3_exec (self->db, "BEGIN TRANSACTION;" - "CREATE TABLE schema (" + "CREATE TABLE IF NOT EXISTS schema (" "created timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP," "version INTEGER DEFAULT 0);" "INSERT INTO schema (version) VALUES (0);" - "CREATE TABLE history (" + "CREATE TABLE IF NOT EXISTS history (" "device_id TEXT," "update_state INTEGER DEFAULT 0," "update_error TEXT," @@ -178,7 +178,7 @@ fu_history_create_database (FuHistory *self, GError **error) "version_new TEXT," "checksum_device TEXT DEFAULT NULL," "protocol TEXT DEFAULT NULL);" - "CREATE TABLE approved_firmware (" + "CREATE TABLE IF NOT EXISTS approved_firmware (" "checksum TEXT);" "COMMIT;", NULL, NULL, NULL); if (rc != SQLITE_OK) { @@ -257,7 +257,7 @@ fu_history_migrate_database_v4 (FuHistory *self, GError **error) { gint rc; rc = sqlite3_exec (self->db, - "CREATE TABLE approved_firmware (checksum TEXT);", + "CREATE TABLE IF NOT EXISTS approved_firmware (checksum TEXT);", NULL, NULL, NULL); if (rc != SQLITE_OK) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, From 747f570310ff5348d8481cd57fc108d38e9ac487 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 6 Aug 2019 14:27:26 +0100 Subject: [PATCH 023/158] Allow filtering devices when using the command line tools --- .../bash-completion/{fwupdmgr => fwupdmgr.in} | 15 +++ data/bash-completion/fwupdtool.in | 13 ++ data/bash-completion/meson.build | 19 +-- src/fu-tool.c | 118 ++++++++++++++++-- src/fu-util-common.c | 71 +++++++++++ src/fu-util-common.h | 6 + src/fu-util.c | 61 ++++++++- 7 files changed, 283 insertions(+), 20 deletions(-) rename data/bash-completion/{fwupdmgr => fwupdmgr.in} (92%) diff --git a/data/bash-completion/fwupdmgr b/data/bash-completion/fwupdmgr.in similarity index 92% rename from data/bash-completion/fwupdmgr rename to data/bash-completion/fwupdmgr.in index 27d8fd507..d090cb1a5 100644 --- a/data/bash-completion/fwupdmgr +++ b/data/bash-completion/fwupdmgr.in @@ -41,8 +41,16 @@ _fwupdmgr_opts=( '--no-reboot-check' '--show-all-devices' '--sign' + '--filter' ) +_show_filters() +{ + local flags + flags="$(command @libexecdir@/fwupdtool get-device-flags 2>/dev/null)" + COMPREPLY+=( $(compgen -W "${flags}" -- "$cur") ) +} + _show_modifiers() { COMPREPLY+=( $(compgen -W '${_fwupdmgr_opts[@]}' -- "$cur") ) @@ -73,6 +81,13 @@ _fwupdmgr() prev=${COMP_WORDS[COMP_CWORD-1]} command=${COMP_WORDS[1]} + case $prev in + --filter) + _show_filters + return 0 + ;; + esac + case $command in activate|clear-results|downgrade|get-releases|get-results|unlock|verify|verify-update) if [[ "$prev" = "$command" ]]; then diff --git a/data/bash-completion/fwupdtool.in b/data/bash-completion/fwupdtool.in index 9d8a8daf8..b5b2a1f59 100644 --- a/data/bash-completion/fwupdtool.in +++ b/data/bash-completion/fwupdtool.in @@ -3,6 +3,7 @@ _fwupdtool_cmd_list=( 'build-firmware' 'get-updates' 'get-details' + 'get-device-flags' 'get-devices' 'get-history' 'get-plugins' @@ -30,8 +31,16 @@ _fwupdtool_opts=( '--plugin-whitelist' '--prepare' '--cleanup' + '--filter' ) +_show_filters() +{ + local flags + flags="$(command @libexecdir@/fwupdtool get-device-flags 2>/dev/null)" + COMPREPLY+=( $(compgen -W "${flags}" -- "$cur") ) +} + _show_plugins() { local plugins @@ -57,6 +66,10 @@ _fwupdtool() _show_plugins return 0 ;; + --filter) + _show_filters + return 0 + ;; esac case $command in diff --git a/data/bash-completion/meson.build b/data/bash-completion/meson.build index f85ab1c30..37a79d995 100644 --- a/data/bash-completion/meson.build +++ b/data/bash-completion/meson.build @@ -4,17 +4,11 @@ if bashcomp.found() ) -if get_option('daemon') - install_data(['fwupdmgr'], - install_dir : tgt, - ) -endif - if get_option('agent') install_data(['fwupdagent'], install_dir : tgt, ) -endif +endif # get_option('agent') # replace @libexecdir@ fwupdtool_path = join_paths(libexecdir, 'fwupd') @@ -27,4 +21,13 @@ configure_file( install: true, install_dir: tgt) -endif +if get_option('daemon') +configure_file( + input : 'fwupdmgr.in', + output : 'fwupdmgr', + configuration : con2, + install: true, + install_dir: tgt) +endif # get_option('daemon') + +endif # bashcomp.found() diff --git a/src/fu-tool.c b/src/fu-tool.c index 60200c168..a0c532401 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -59,6 +59,8 @@ struct FuUtilPrivate { FwupdDevice *current_device; gchar *current_message; FwupdDeviceFlags completion_flags; + FwupdDeviceFlags filter_include; + FwupdDeviceFlags filter_exclude; }; static gboolean @@ -278,6 +280,20 @@ fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } +static gboolean +fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev) +{ + if (priv->filter_include != FWUPD_DEVICE_FLAG_NONE) { + if (!fwupd_device_has_flag (dev, priv->filter_include)) + return FALSE; + } + if (priv->filter_exclude != FWUPD_DEVICE_FLAG_NONE) { + if (fwupd_device_has_flag (dev, priv->filter_exclude)) + return FALSE; + } + return TRUE; +} + static gboolean fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -296,9 +312,11 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) g_autoptr(GPtrArray) rels = NULL; g_autoptr(GError) error_local = NULL; - /* not going to have results, so save a D-Bus round-trip */ + /* not going to have results, so save a engine round-trip */ if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) continue; + if (!fu_util_filter_device (priv, dev)) + continue; /* get the releases for this device and filter for validity */ rels = fu_engine_get_upgrades (priv->engine, @@ -363,6 +381,8 @@ fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) for (guint i = 0; i < array->len; i++) { FwupdDevice *dev = g_ptr_array_index (array, i); g_autofree gchar *tmp = NULL; + if (!fu_util_filter_device (priv, dev)) + continue; tmp = fwupd_device_to_string (dev); g_print ("%s\n", tmp); } @@ -389,6 +409,8 @@ fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) } for (guint i = 0; i < devs->len; i++) { FwupdDevice *dev = g_ptr_array_index (devs, i); + if (!fu_util_filter_device (priv, dev)) + continue; if (priv->show_all_devices || fu_util_is_interesting_device (dev)) { g_autofree gchar *tmp = fwupd_device_to_string (dev); g_print ("%s\n", tmp); @@ -402,11 +424,33 @@ fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } +static gboolean +fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(GString) str = g_string_new (NULL); + + for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) { + const gchar *tmp = fwupd_device_flag_to_string (i); + if (tmp == NULL) + break; + if (i != FWUPD_DEVICE_FLAG_INTERNAL) + g_string_append (str, " "); + g_string_append (str, tmp); + g_string_append (str, " ~"); + g_string_append (str, tmp); + } + g_print ("%s\n", str->str); + + return TRUE; +} + static void fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev) { for (guint i = 0; i < devs->len; i++) { FuDevice *dev_tmp = g_ptr_array_index (devs, i); + if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp))) + continue; if (!priv->show_all_devices && !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp))) continue; @@ -445,22 +489,42 @@ fu_util_get_topology (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } - static FuDevice * fu_util_prompt_for_device (FuUtilPrivate *priv, GError **error) { FuDevice *dev; guint idx; g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GPtrArray) devices_filtered = NULL; /* get devices from daemon */ devices = fu_engine_get_devices (priv->engine, error); if (devices == NULL) return NULL; + /* filter results */ + devices_filtered = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + for (guint i = 0; i < devices->len; i++) { + dev = g_ptr_array_index (devices, i); + if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev))) + continue; + if (!fwupd_device_has_flag (FWUPD_DEVICE (dev), FWUPD_DEVICE_FLAG_SUPPORTED)) + continue; + g_ptr_array_add (devices_filtered, g_object_ref (dev)); + } + + /* nothing */ + if (devices_filtered->len == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No supported devices"); + return NULL; + } + /* exactly one */ - if (devices->len == 1) { - dev = g_ptr_array_index (devices, 0); + if (devices_filtered->len == 1) { + dev = g_ptr_array_index (devices_filtered, 0); return g_object_ref (dev); } @@ -468,14 +532,14 @@ fu_util_prompt_for_device (FuUtilPrivate *priv, GError **error) g_print ("%s\n", _("Choose a device:")); /* TRANSLATORS: this is to abort the interactive prompt */ g_print ("0.\t%s\n", _("Cancel")); - for (guint i = 0; i < devices->len; i++) { - dev = g_ptr_array_index (devices, i); + for (guint i = 0; i < devices_filtered->len; i++) { + dev = g_ptr_array_index (devices_filtered, i); g_print ("%u.\t%s (%s)\n", i + 1, fu_device_get_id (dev), fu_device_get_name (dev)); } - idx = fu_util_prompt_for_number (devices->len); + idx = fu_util_prompt_for_number (devices_filtered->len); if (idx == 0) { g_set_error_literal (error, FWUPD_ERROR, @@ -483,7 +547,7 @@ fu_util_prompt_for_device (FuUtilPrivate *priv, GError **error) "Request canceled"); return NULL; } - dev = g_ptr_array_index (devices, idx - 1); + dev = g_ptr_array_index (devices_filtered, idx - 1); return g_object_ref (dev); } @@ -811,7 +875,7 @@ fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error) FWUPD_ERROR_INVALID_FILE, "failed to find remote for %s", uri_tmp); - return FALSE; + return FALSE; } remote = fu_engine_get_remote_by_id (priv->engine, @@ -855,6 +919,8 @@ fu_util_update_all (FuUtilPrivate *priv, GError **error) /* only show stuff that has metadata available */ if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) continue; + if (!fu_util_filter_device (priv, dev)) + continue; device_id = fu_device_get_id (dev); rels = fu_engine_get_upgrades (priv->engine, device_id, &error_local); @@ -866,7 +932,7 @@ fu_util_update_all (FuUtilPrivate *priv, GError **error) rel = g_ptr_array_index (rels, 0); if (!fu_util_install_release (priv, rel, &error_local)) { g_printerr ("%s\n", error_local->message); - continue; + continue; } fu_util_display_current_message (priv); } @@ -1062,6 +1128,8 @@ fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error) /* activate anything with _NEEDS_ACTIVATION */ for (guint i = 0; i < devices->len; i++) { FuDevice *device = g_ptr_array_index (devices, i); + if (!fu_util_filter_device (priv, FWUPD_DEVICE (device))) + continue; if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) continue; /* TRANSLATORS: shown when shutting down to switch to the new version */ @@ -1316,7 +1384,10 @@ fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) /* show each device */ for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index (devices, i); - g_autofree gchar *str = fwupd_device_to_string (dev); + g_autofree gchar *str = NULL; + if (!fu_util_filter_device (priv, dev)) + continue; + str = fwupd_device_to_string (dev); g_print ("%s\n", str); } @@ -1337,6 +1408,7 @@ main (int argc, char *argv[]) g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new (); g_autofree gchar *cmd_descriptions = NULL; + g_autofree gchar *filter = NULL; const GOptionEntry options[] = { { "version", '\0', 0, G_OPTION_ARG_NONE, &version, /* TRANSLATORS: command line option */ @@ -1368,6 +1440,10 @@ main (int argc, char *argv[]) { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state, /* TRANSLATORS: command line option */ _("Save device state into a JSON file between executions"), NULL }, + { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter, + /* TRANSLATORS: command line option */ + _("Filter with a set of device flags using a ~ prefix to " + "exclude, e.g. 'internal,~needs-reboot'"), NULL }, { NULL} }; @@ -1429,6 +1505,12 @@ main (int argc, char *argv[]) /* TRANSLATORS: command description */ _("Get all devices that support firmware updates"), fu_util_get_devices); + fu_util_cmd_array_add (cmd_array, + "get-device-flags", + NULL, + /* TRANSLATORS: command description */ + _("Get all device flags supported by fwupd"), + fu_util_get_device_flags); fu_util_cmd_array_add (cmd_array, "get-topology", NULL, @@ -1540,6 +1622,20 @@ main (int argc, char *argv[]) return EXIT_FAILURE; } + /* parse filter flags */ + if (filter != NULL) { + if (!fu_util_parse_filter_flags (filter, + &priv->filter_include, + &priv->filter_exclude, + &error)) { + /* TRANSLATORS: the user didn't read the man page */ + g_print ("%s: %s\n", _("Failed to parse flags for --filter"), + error->message); + return EXIT_FAILURE; + } + } + + /* set flags */ if (allow_reinstall) priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 55b906ba6..2ed21bc88 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -657,3 +657,74 @@ fu_util_warning_box (const gchar *str, guint width) /* footer */ fu_util_warning_box_line ("╚", NULL, "╝", "═", width); } + +gboolean +fu_util_parse_filter_flags (const gchar *filter, FwupdDeviceFlags *include, + FwupdDeviceFlags *exclude, GError **error) +{ + FwupdDeviceFlags tmp; + g_auto(GStrv) strv = g_strsplit (filter, ",", -1); + + g_return_val_if_fail (include != NULL, FALSE); + g_return_val_if_fail (exclude != NULL, FALSE); + + for (guint i = 0; strv[i] != NULL; i++) { + if (g_str_has_prefix (strv[i], "~")) { + tmp = fwupd_device_flag_from_string (strv[i] + 1); + if (tmp == FWUPD_DEVICE_FLAG_UNKNOWN) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Unknown device flag %s", + strv[i] + 1); + return FALSE; + } + if ((tmp & *include) > 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Filter %s already included", + fwupd_device_flag_to_string (tmp)); + return FALSE; + } + if ((tmp & *exclude) > 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Filter %s already excluded", + fwupd_device_flag_to_string (tmp)); + return FALSE; + } + *exclude |= tmp; + } else { + tmp = fwupd_device_flag_from_string (strv[i]); + if (tmp == FWUPD_DEVICE_FLAG_UNKNOWN) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Unknown device flag %s", + strv[i]); + return FALSE; + } + if ((tmp & *exclude) > 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Filter %s already excluded", + fwupd_device_flag_to_string (tmp)); + return FALSE; + } + if ((tmp & *include) > 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Filter %s already included", + fwupd_device_flag_to_string (tmp)); + return FALSE; + } + *include |= tmp; + } + } + + return TRUE; +} diff --git a/src/fu-util-common.h b/src/fu-util-common.h index ef19aeb20..a00ce71bb 100644 --- a/src/fu-util-common.h +++ b/src/fu-util-common.h @@ -63,4 +63,10 @@ gchar *fu_util_release_get_name (FwupdRelease *release); const gchar *fu_util_get_systemd_unit (void); gboolean fu_util_using_correct_daemon (GError **error); +gboolean fu_util_parse_filter_flags (const gchar *filter, + FwupdDeviceFlags *include, + FwupdDeviceFlags *exclude, + GError **error); + + G_END_DECLS diff --git a/src/fu-util.c b/src/fu-util.c index 517e5798b..237767422 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -62,6 +62,8 @@ struct FuUtilPrivate { FwupdDevice *current_device; gchar *current_message; FwupdDeviceFlags completion_flags; + FwupdDeviceFlags filter_include; + FwupdDeviceFlags filter_exclude; }; static gboolean fu_util_report_history (FuUtilPrivate *priv, gchar **values, GError **error); @@ -127,6 +129,20 @@ fu_util_update_device_changed_cb (FwupdClient *client, } } +static gboolean +fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev) +{ + if (priv->filter_include != FWUPD_DEVICE_FLAG_NONE) { + if (!fwupd_device_has_flag (dev, priv->filter_include)) + return FALSE; + } + if (priv->filter_exclude != FWUPD_DEVICE_FLAG_NONE) { + if (fwupd_device_has_flag (dev, priv->filter_exclude)) + return FALSE; + } + return TRUE; +} + static FwupdDevice * fu_util_prompt_for_device (FuUtilPrivate *priv, GError **error) { @@ -144,6 +160,8 @@ fu_util_prompt_for_device (FuUtilPrivate *priv, GError **error) devices_filtered = g_ptr_array_new (); for (guint i = 0; i < devices->len; i++) { dev = g_ptr_array_index (devices, i); + if (!fu_util_filter_device (priv, dev)) + continue; if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) continue; g_ptr_array_add (devices_filtered, dev); @@ -212,6 +230,8 @@ fu_util_perhaps_show_unreported (FuUtilPrivate *priv, GError **error) for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index (devices, i); + if (!fu_util_filter_device (priv, dev)) + continue; if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_REPORTED)) continue; if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) @@ -401,6 +421,8 @@ fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, Fw { for (guint i = 0; i < devs->len; i++) { FwupdDevice *dev_tmp = g_ptr_array_index (devs, i); + if (!fu_util_filter_device (priv, dev_tmp)) + continue; if (!priv->show_all_devices && !fu_util_is_interesting_device (dev_tmp)) continue; @@ -455,6 +477,8 @@ fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) for (guint i = 0; i < devs->len; i++) { g_autofree gchar *tmp = NULL; FwupdDevice *dev = g_ptr_array_index (devs, i); + if (!fu_util_filter_device (priv, dev)) + continue; if (!priv->show_all_devices) { if (!fu_util_is_interesting_device (dev)) continue; @@ -561,6 +585,8 @@ fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) for (guint i = 0; i < array->len; i++) { FwupdDevice *dev = g_ptr_array_index (array, i); g_autofree gchar *tmp = NULL; + if (!fu_util_filter_device (priv, dev)) + continue; tmp = fwupd_device_to_string (dev); g_print ("%s\n", tmp); } @@ -767,6 +793,8 @@ fu_util_report_history (FuUtilPrivate *priv, gchar **values, GError **error) GPtrArray *devices_tmp; /* filter, if not forcing */ + if (!fu_util_filter_device (priv, dev)) + continue; if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_REPORTED)) continue; @@ -854,7 +882,10 @@ fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) /* show each device */ for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index (devices, i); - g_autofree gchar *str = fwupd_device_to_string (dev); + g_autofree gchar *str = NULL; + if (!fu_util_filter_device (priv, dev)) + continue; + str = fwupd_device_to_string (dev); g_print ("%s\n", str); } @@ -915,6 +946,8 @@ fu_util_verify_update_all (FuUtilPrivate *priv, GError **error) for (guint i = 0; i < devs->len; i++) { g_autoptr(GError) error_local = NULL; FwupdDevice *dev = g_ptr_array_index (devs, i); + if (!fu_util_filter_device (priv, dev)) + continue; if (!fwupd_client_verify_update (priv->client, fwupd_device_get_id (dev), NULL, @@ -1387,6 +1420,8 @@ fu_util_verify_all (FuUtilPrivate *priv, GError **error) for (guint i = 0; i < devs->len; i++) { g_autoptr(GError) error_local = NULL; FwupdDevice *dev = g_ptr_array_index (devs, i); + if (!fu_util_filter_device (priv, dev)) + continue; if (!fwupd_client_verify (priv->client, fwupd_device_get_id (dev), NULL, @@ -1553,6 +1588,8 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) /* not going to have results, so save a D-Bus round-trip */ if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) continue; + if (!fu_util_filter_device (priv, dev)) + continue; /* get the releases for this device and filter for validity */ rels = fwupd_client_get_upgrades (priv->client, @@ -1858,6 +1895,8 @@ fu_util_update_all (FuUtilPrivate *priv, GError **error) /* not going to have results, so save a D-Bus round-trip */ if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) continue; + if (!fu_util_filter_device (priv, dev)) + continue; /* get the releases for this device and filter for validity */ rels = fwupd_client_get_upgrades (priv->client, @@ -2056,6 +2095,8 @@ fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error) /* activate anything with _NEEDS_ACTIVATION */ for (guint i = 0; i < devices->len; i++) { FwupdDevice *device = g_ptr_array_index (devices, i); + if (!fu_util_filter_device (priv, device)) + continue; if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) continue; /* TRANSLATORS: shown when shutting down to switch to the new version */ @@ -2249,6 +2290,7 @@ main (int argc, char *argv[]) g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new (); g_autofree gchar *cmd_descriptions = NULL; + g_autofree gchar *filter = NULL; const GOptionEntry options[] = { { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, /* TRANSLATORS: command line option */ @@ -2289,6 +2331,10 @@ main (int argc, char *argv[]) { "show-all-devices", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all_devices, /* TRANSLATORS: command line option */ _("Show devices that are not updatable"), NULL }, + { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter, + /* TRANSLATORS: command line option */ + _("Filter with a set of device flags using a ~ prefix to " + "exclude, e.g. 'internal,~needs-reboot'"), NULL }, { NULL} }; @@ -2500,6 +2546,19 @@ main (int argc, char *argv[]) return EXIT_FAILURE; } + /* parse filter flags */ + if (filter != NULL) { + if (!fu_util_parse_filter_flags (filter, + &priv->filter_include, + &priv->filter_exclude, + &error)) { + /* TRANSLATORS: the user didn't read the man page */ + g_print ("%s: %s\n", _("Failed to parse flags for --filter"), + error->message); + return EXIT_FAILURE; + } + } + /* set verbose? */ if (verbose) { g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); From 7afd7cba0d327b155b2201c9fbb5414ced9cfe06 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 7 Aug 2019 11:42:42 +0100 Subject: [PATCH 024/158] Use FuFirmware as a container for firmware images In many plugins we've wanted to use ->prepare_firmware() to parse the firmware ahead of ->detach() and ->write_firmware() but this has the limitation that it can only return a single blob of data. For many devices, multiple binary blobs are required from one parsed image, for instance providing signatures, config and data blobs that have to be pushed to the device in different way. This also means we parse the firmware *before* we ask the user to detach. Break the internal FuDevice API to support these firmware types as they become more popular. This also allows us to move the Intel HEX and SREC parsing out of the dfu plugin as they are used by a few plugins now, and resolving symbols between plugins isn't exactly awesome. --- {plugins/dfu => data}/tests/firmware.bin | Bin {plugins/dfu => data}/tests/firmware.hex | 0 data/tests/firmware.shex | 11 + {plugins/dfu => data}/tests/firmware.srec | 0 plugins/altos/fu-altos-device.c | 33 +- plugins/altos/fu-altos-firmware.c | 60 +-- plugins/altos/fu-altos-firmware.h | 13 +- plugins/ata/fu-ata-device.c | 8 +- plugins/colorhug/fu-colorhug-device.c | 8 +- plugins/csr/fu-csr-device.c | 53 +-- plugins/dell-dock/fu-dell-dock-hub.c | 13 +- plugins/dell-dock/fu-dell-dock-i2c-ec.c | 13 +- plugins/dell-dock/fu-dell-dock-i2c-mst.c | 19 +- plugins/dell-dock/fu-dell-dock-i2c-tbt.c | 15 +- plugins/dell-dock/fu-dell-dock-status.c | 15 +- plugins/dfu/dfu-common.c | 154 ------- plugins/dfu/dfu-common.h | 11 - plugins/dfu/dfu-device.c | 7 +- plugins/dfu/dfu-element.c | 57 --- plugins/dfu/dfu-element.h | 5 - plugins/dfu/dfu-format-ihex.c | 375 ++-------------- plugins/dfu/dfu-format-srec.c | 305 +------------ plugins/dfu/dfu-format-srec.h | 5 - plugins/dfu/dfu-self-test.c | 207 --------- plugins/dfu/tests/firmware.shex | Bin 735 -> 0 bytes plugins/dfu/tests/firmware.sig | 1 - plugins/ebitdo/fu-ebitdo-device.c | 8 +- plugins/fastboot/fu-fastboot-device.c | 8 +- plugins/modem-manager/fu-mm-device.c | 8 +- plugins/nvme/fu-nvme-device.c | 8 +- plugins/rts54hid/fu-rts54hid-device.c | 8 +- plugins/rts54hid/fu-rts54hid-module.c | 8 +- plugins/rts54hub/fu-rts54hub-device.c | 12 +- plugins/solokey/fu-solokey-device.c | 117 ++--- plugins/solokey/fu-solokey-firmware.c | 112 +++++ plugins/solokey/fu-solokey-firmware.h | 18 + plugins/solokey/meson.build | 3 +- plugins/superio/fu-superio-device.c | 4 +- plugins/superio/fu-superio-it89-device.c | 8 +- plugins/synaptics-prometheus/fu-dump.c | 10 +- plugins/synaptics-prometheus/fu-self-test.c | 25 +- .../synaptics-prometheus/fu-synaprom-config.c | 26 +- .../synaptics-prometheus/fu-synaprom-device.c | 26 +- .../synaptics-prometheus/fu-synaprom-device.h | 2 +- .../fu-synaprom-firmware.c | 101 ++--- .../fu-synaprom-firmware.h | 16 +- plugins/synaptics-prometheus/meson.build | 5 + plugins/uefi/fu-uefi-device.c | 8 +- .../unifying/fu-unifying-bootloader-nordic.c | 8 +- .../unifying/fu-unifying-bootloader-texas.c | 8 +- plugins/unifying/fu-unifying-peripheral.c | 8 +- plugins/wacom-raw/fu-wacom-device.c | 68 ++- plugins/wacom-raw/fu-wacom-device.h | 1 - plugins/wacom-raw/meson.build | 3 +- plugins/wacom-usb/fu-self-test.c | 18 +- plugins/wacom-usb/fu-wac-device.c | 73 ++-- plugins/wacom-usb/fu-wac-firmware.c | 99 +++-- plugins/wacom-usb/fu-wac-firmware.h | 14 +- plugins/wacom-usb/fu-wac-module-bluetooth.c | 10 +- plugins/wacom-usb/fu-wac-module-touch.c | 63 ++- plugins/wacom-usb/fu-wac-module.c | 3 - plugins/wacom-usb/meson.build | 4 - src/fu-common.c | 54 +++ src/fu-common.h | 6 + src/fu-device.c | 66 +-- src/fu-device.h | 7 +- src/fu-firmware-common.c | 113 +++++ src/fu-firmware-common.h | 19 + src/fu-firmware-image.c | 329 ++++++++++++++ src/fu-firmware-image.h | 49 +++ src/fu-firmware.c | 402 ++++++++++++++++++ src/fu-firmware.h | 71 ++++ src/fu-ihex-firmware.c | 377 ++++++++++++++++ src/fu-ihex-firmware.h | 18 + src/fu-self-test.c | 249 +++++++++++ src/fu-srec-firmware.c | 303 +++++++++++++ src/fu-srec-firmware.h | 18 + src/meson.build | 23 + 78 files changed, 2795 insertions(+), 1618 deletions(-) rename {plugins/dfu => data}/tests/firmware.bin (100%) rename {plugins/dfu => data}/tests/firmware.hex (100%) create mode 100644 data/tests/firmware.shex rename {plugins/dfu => data}/tests/firmware.srec (100%) delete mode 100644 plugins/dfu/tests/firmware.shex delete mode 100644 plugins/dfu/tests/firmware.sig create mode 100644 plugins/solokey/fu-solokey-firmware.c create mode 100644 plugins/solokey/fu-solokey-firmware.h create mode 100644 src/fu-firmware-common.c create mode 100644 src/fu-firmware-common.h create mode 100644 src/fu-firmware-image.c create mode 100644 src/fu-firmware-image.h create mode 100644 src/fu-firmware.c create mode 100644 src/fu-firmware.h create mode 100644 src/fu-ihex-firmware.c create mode 100644 src/fu-ihex-firmware.h create mode 100644 src/fu-srec-firmware.c create mode 100644 src/fu-srec-firmware.h diff --git a/plugins/dfu/tests/firmware.bin b/data/tests/firmware.bin similarity index 100% rename from plugins/dfu/tests/firmware.bin rename to data/tests/firmware.bin diff --git a/plugins/dfu/tests/firmware.hex b/data/tests/firmware.hex similarity index 100% rename from plugins/dfu/tests/firmware.hex rename to data/tests/firmware.hex diff --git a/data/tests/firmware.shex b/data/tests/firmware.shex new file mode 100644 index 000000000..041286463 --- /dev/null +++ b/data/tests/firmware.shex @@ -0,0 +1,11 @@ +:100000003DEF20F000000000FACF01F0FBCF02F03E +:10001000E9CF03F0EACF04F0E1CF05F0E2CF06F03C +:10002000D9CF07F0DACF08F0F3CF09F0F4CF0AF018 +:10003000F6CF0BF0F7CF0CF0F8CF0DF0F5CF0EF0B8 +:100040000EC0F5FF0DC0F8FF0CC0F7FF0BC0F6FFA8 +:100050000AC0F4FF09C0F3FF08C0DAFF07C0D9FFE8 +:1000600006C0E2FF05C0E1FF04C0EAFF03C0E9FFEC +:1000700002C0FBFF01C0FAFF11003FEF20F00001BA +:0800800042EF20F03DEF20F0FB +:080000FD6465616462656566DB +:00000001FF diff --git a/plugins/dfu/tests/firmware.srec b/data/tests/firmware.srec similarity index 100% rename from plugins/dfu/tests/firmware.srec rename to data/tests/firmware.srec diff --git a/plugins/altos/fu-altos-device.c b/plugins/altos/fu-altos-device.c index 2dda22e2c..300f3aa2a 100644 --- a/plugins/altos/fu-altos-device.c +++ b/plugins/altos/fu-altos-device.c @@ -268,19 +268,31 @@ fu_altos_device_write_page (FuAltosDevice *self, return TRUE; } +static FuFirmware * +fu_altos_device_prepare_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuFirmware) firmware = fu_altos_firmware_new (); + if (!fu_firmware_parse (firmware, fw, flags, error)) + return FALSE; + return g_steal_pointer (&firmware); +} + static gboolean fu_altos_device_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuAltosDevice *self = FU_ALTOS_DEVICE (device); - GBytes *fw_blob; const gchar *data; const gsize data_len; guint flash_len; - g_autoptr(FuAltosFirmware) altos_firmware = NULL; g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuFirmwareImage) img = NULL; + g_autoptr(GBytes) fw = NULL; g_autoptr(GString) buf = g_string_new (NULL); /* check kind */ @@ -312,25 +324,27 @@ fu_altos_device_write_firmware (FuDevice *device, } /* load ihex blob */ - altos_firmware = fu_altos_firmware_new (); - if (!fu_altos_firmware_parse (altos_firmware, fw, error)) + img = fu_firmware_get_image_default (firmware, error); + if (img == NULL) return FALSE; /* check the start address */ - if (fu_altos_firmware_get_address (altos_firmware) != self->addr_base) { + if (fu_firmware_image_get_addr (img) != self->addr_base) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "start address not correct %" G_GUINT64_FORMAT ":" "%" G_GUINT64_FORMAT, - fu_altos_firmware_get_address (altos_firmware), + fu_firmware_image_get_addr (img), self->addr_base); return FALSE; } /* check firmware will fit */ - fw_blob = fu_altos_firmware_get_data (altos_firmware); - data = g_bytes_get_data (fw_blob, (gsize *) &data_len); + fw = fu_firmware_image_get_bytes (img, error); + if (fw == NULL) + return FALSE; + data = g_bytes_get_data (fw, (gsize *) &data_len); if (data_len > flash_len) { g_set_error (error, FWUPD_ERROR, @@ -628,6 +642,7 @@ fu_altos_device_class_init (FuAltosDeviceClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); klass_device->probe = fu_altos_device_probe; + klass_device->prepare_firmware = fu_altos_device_prepare_firmware; klass_device->write_firmware = fu_altos_device_write_firmware; klass_device->read_firmware = fu_altos_device_read_firmware; object_class->finalize = fu_altos_device_finalize; diff --git a/plugins/altos/fu-altos-firmware.c b/plugins/altos/fu-altos-firmware.c index 79af716c3..5b2d24fab 100644 --- a/plugins/altos/fu-altos-firmware.c +++ b/plugins/altos/fu-altos-firmware.c @@ -1,45 +1,34 @@ /* - * Copyright (C) 2017 Richard Hughes + * Copyright (C) 2017-2019 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ #include "config.h" -#include #include #include #include "fu-altos-firmware.h" -#include "fwupd-error.h" struct _FuAltosFirmware { - GObject parent_instance; - GBytes *data; - guint64 address; + FuFirmware parent_instance; }; -G_DEFINE_TYPE (FuAltosFirmware, fu_altos_firmware, G_TYPE_OBJECT) +G_DEFINE_TYPE (FuAltosFirmware, fu_altos_firmware, FU_TYPE_FIRMWARE) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" G_DEFINE_AUTOPTR_CLEANUP_FUNC(Elf, elf_end); #pragma clang diagnostic pop -GBytes * -fu_altos_firmware_get_data (FuAltosFirmware *self) -{ - return self->data; -} - -guint64 -fu_altos_firmware_get_address (FuAltosFirmware *self) -{ - return self->address; -} - -gboolean -fu_altos_firmware_parse (FuAltosFirmware *self, GBytes *blob, GError **error) +static gboolean +fu_altos_firmware_parse (FuFirmware *firmware, + GBytes *blob, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error) { const gchar *name; Elf_Scn *scn = NULL; @@ -99,8 +88,12 @@ fu_altos_firmware_parse (FuAltosFirmware *self, GBytes *blob, GError **error) if (g_strcmp0 (name, ".text") == 0) { Elf_Data *data = elf_getdata (scn, NULL); if (data != NULL && data->d_buf != NULL) { - self->data = g_bytes_new (data->d_buf, data->d_size); - self->address = shdr.sh_addr; + g_autoptr(FuFirmwareImage) img = NULL; + g_autoptr(GBytes) bytes = NULL; + bytes = g_bytes_new (data->d_buf, data->d_size); + img = fu_firmware_image_new (bytes); + fu_firmware_image_set_addr (img, shdr.sh_addr); + fu_firmware_add_image (firmware, img); } return TRUE; } @@ -112,22 +105,11 @@ fu_altos_firmware_parse (FuAltosFirmware *self, GBytes *blob, GError **error) return FALSE; } -static void -fu_altos_firmware_finalize (GObject *object) -{ - FuAltosFirmware *self = FU_ALTOS_FIRMWARE (object); - - if (self->data != NULL) - g_bytes_unref (self->data); - - G_OBJECT_CLASS (fu_altos_firmware_parent_class)->finalize (object); -} - static void fu_altos_firmware_class_init (FuAltosFirmwareClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->finalize = fu_altos_firmware_finalize; + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass); + klass_firmware->parse = fu_altos_firmware_parse; } static void @@ -135,10 +117,8 @@ fu_altos_firmware_init (FuAltosFirmware *self) { } -FuAltosFirmware * +FuFirmware * fu_altos_firmware_new (void) { - FuAltosFirmware *self; - self = g_object_new (FU_TYPE_ALTOS_FIRMWARE, NULL); - return FU_ALTOS_FIRMWARE (self); + return FU_FIRMWARE (g_object_new (FU_TYPE_ALTOS_FIRMWARE, NULL)); } diff --git a/plugins/altos/fu-altos-firmware.h b/plugins/altos/fu-altos-firmware.h index 9051161ef..939a86336 100644 --- a/plugins/altos/fu-altos-firmware.h +++ b/plugins/altos/fu-altos-firmware.h @@ -1,22 +1,19 @@ /* - * Copyright (C) 2017 Richard Hughes + * Copyright (C) 2017-2019 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include "fu-firmware.h" + G_BEGIN_DECLS #define FU_TYPE_ALTOS_FIRMWARE (fu_altos_firmware_get_type ()) -G_DECLARE_FINAL_TYPE (FuAltosFirmware, fu_altos_firmware, FU, ALTOS_FIRMWARE, GObject) +G_DECLARE_FINAL_TYPE (FuAltosFirmware, fu_altos_firmware, FU, ALTOS_FIRMWARE, FuFirmware) -FuAltosFirmware *fu_altos_firmware_new (void); -GBytes *fu_altos_firmware_get_data (FuAltosFirmware *self); -guint64 fu_altos_firmware_get_address (FuAltosFirmware *self); -gboolean fu_altos_firmware_parse (FuAltosFirmware *self, - GBytes *blob, - GError **error); +FuFirmware *fu_altos_firmware_new (void); G_END_DECLS diff --git a/plugins/ata/fu-ata-device.c b/plugins/ata/fu-ata-device.c index 53db4a4eb..73858d4ab 100644 --- a/plugins/ata/fu-ata-device.c +++ b/plugins/ata/fu-ata-device.c @@ -578,15 +578,21 @@ fu_ata_device_fw_download (FuAtaDevice *self, static gboolean fu_ata_device_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuAtaDevice *self = FU_ATA_DEVICE (device); guint32 chunksz = (guint32) self->transfer_blocks * FU_ATA_BLOCK_SIZE; guint max_size = 0xffff * FU_ATA_BLOCK_SIZE; + g_autoptr(GBytes) fw = NULL; g_autoptr(GPtrArray) chunks = NULL; + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + /* only one block allowed */ if (self->transfer_mode == ATA_SUBCMD_MICROCODE_DOWNLOAD_CHUNK) max_size = 0xffff; diff --git a/plugins/colorhug/fu-colorhug-device.c b/plugins/colorhug/fu-colorhug-device.c index cfc4d95c4..1a393ab91 100644 --- a/plugins/colorhug/fu-colorhug-device.c +++ b/plugins/colorhug/fu-colorhug-device.c @@ -328,13 +328,19 @@ ch_colorhug_device_calculate_checksum (const guint8 *data, guint32 len) static gboolean fu_colorhug_device_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuColorhugDevice *self = FU_COLORHUG_DEVICE (device); + g_autoptr(GBytes) fw = NULL; g_autoptr(GPtrArray) chunks = NULL; + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + /* build packets */ chunks = fu_chunk_array_new_from_bytes (fw, self->start_addr, diff --git a/plugins/csr/fu-csr-device.c b/plugins/csr/fu-csr-device.c index 6a96e3a1f..559a93cd6 100644 --- a/plugins/csr/fu-csr-device.c +++ b/plugins/csr/fu-csr-device.c @@ -10,9 +10,9 @@ #include "fu-chunk.h" #include "fu-csr-device.h" +#include "fu-ihex-firmware.h" #include "dfu-common.h" -#include "dfu-firmware.h" /** * FU_CSR_DEVICE_QUIRK_FLAG_REQUIRE_DELAY: @@ -416,71 +416,44 @@ fu_csr_device_download_chunk (FuCsrDevice *self, guint16 idx, GBytes *chunk, GEr return TRUE; } -static GBytes * -_dfu_firmware_get_default_element_data (DfuFirmware *firmware) -{ - DfuElement *element; - DfuImage *image; - image = dfu_firmware_get_image_default (firmware); - if (image == NULL) - return NULL; - element = dfu_image_get_element_default (image); - if (element == NULL) - return NULL; - return dfu_element_get_contents (element); -} - -static GBytes * +static FuFirmware * fu_csr_device_prepare_firmware (FuDevice *device, GBytes *fw, FwupdInstallFlags flags, GError **error) { - GBytes *blob_noftr; - g_autoptr(DfuFirmware) dfu_firmware = dfu_firmware_new (); + g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new (); /* parse the file */ - if (!dfu_firmware_parse_data (dfu_firmware, fw, - DFU_FIRMWARE_PARSE_FLAG_NONE, error)) + if (!fu_firmware_parse (firmware, fw, flags, error)) return NULL; if (g_getenv ("FWUPD_CSR_VERBOSE") != NULL) { g_autofree gchar *fw_str = NULL; - fw_str = dfu_firmware_to_string (dfu_firmware); + fw_str = fu_firmware_to_string (firmware); g_debug ("%s", fw_str); } - if (dfu_firmware_get_format (dfu_firmware) != DFU_FIRMWARE_FORMAT_DFU) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "expected DFU firmware"); - return NULL; - } - - /* get the blob from the firmware file */ - blob_noftr = _dfu_firmware_get_default_element_data (dfu_firmware); - if (blob_noftr == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "firmware contained no data"); - return NULL; - } /* success */ - return g_bytes_ref (blob_noftr); + return g_steal_pointer (&firmware); } static gboolean fu_csr_device_download (FuDevice *device, - GBytes *blob, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuCsrDevice *self = FU_CSR_DEVICE (device); guint16 idx; g_autoptr(GBytes) blob_empty = NULL; + g_autoptr(GBytes) blob = NULL; g_autoptr(GPtrArray) chunks = NULL; + /* get default image */ + blob = fu_firmware_get_image_default_bytes (firmware, error); + if (blob == NULL) + return FALSE; + /* notify UI */ fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); diff --git a/plugins/dell-dock/fu-dell-dock-hub.c b/plugins/dell-dock/fu-dell-dock-hub.c index 4f4bc4a20..41516976a 100644 --- a/plugins/dell-dock/fu-dell-dock-hub.c +++ b/plugins/dell-dock/fu-dell-dock-hub.c @@ -46,22 +46,29 @@ fu_dell_dock_hub_probe (FuDevice *device, GError **error) static gboolean fu_dell_dock_hub_write_fw (FuDevice *device, - GBytes *blob_fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuDellDockHub *self = FU_DELL_DOCK_HUB (device); gsize fw_size = 0; - const guint8 *data = g_bytes_get_data (blob_fw, &fw_size); + const guint8 *data; gsize write_size = (fw_size / HIDI2C_MAX_WRITE) >= 1 ? HIDI2C_MAX_WRITE : fw_size; gsize nwritten = 0; guint32 address = 0; gboolean result = FALSE; g_autofree gchar *dynamic_version = NULL; + g_autoptr(GBytes) fw = NULL; g_return_val_if_fail (device != NULL, FALSE); - g_return_val_if_fail (blob_fw != NULL, FALSE); + g_return_val_if_fail (FU_IS_FIRMWARE (firmware), FALSE); + + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + data = g_bytes_get_data (fw, &fw_size); dynamic_version = g_strdup_printf ("%02x.%02x", data[self->blob_major_offset], diff --git a/plugins/dell-dock/fu-dell-dock-i2c-ec.c b/plugins/dell-dock/fu-dell-dock-i2c-ec.c index e4c22ba32..6d3c9a5cb 100644 --- a/plugins/dell-dock/fu-dell-dock-i2c-ec.c +++ b/plugins/dell-dock/fu-dell-dock-i2c-ec.c @@ -745,7 +745,7 @@ fu_dell_dock_ec_commit_package (FuDevice *device, GBytes *blob_fw, static gboolean fu_dell_dock_ec_write_fw (FuDevice *device, - GBytes *blob_fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { @@ -753,15 +753,22 @@ fu_dell_dock_ec_write_fw (FuDevice *device, FuDellDockECFWUpdateStatus status = FW_UPDATE_IN_PROGRESS; guint8 progress1 = 0, progress0 = 0; gsize fw_size = 0; - const guint8 *data = g_bytes_get_data (blob_fw, &fw_size); + const guint8 *data; gsize write_size = (fw_size / HIDI2C_MAX_WRITE) >= 1 ? HIDI2C_MAX_WRITE : fw_size; gsize nwritten = 0; guint32 address = 0 | 0xff << 24; g_autofree gchar *dynamic_version = NULL; + g_autoptr(GBytes) fw = NULL; g_return_val_if_fail (device != NULL, FALSE); - g_return_val_if_fail (blob_fw != NULL, FALSE); + g_return_val_if_fail (FU_IS_FIRMWARE (firmware), FALSE); + + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + data = g_bytes_get_data (fw, &fw_size); dynamic_version = g_strndup ((gchar *) data + self->blob_version_offset, 11); g_debug ("writing EC firmware version %s", dynamic_version); diff --git a/plugins/dell-dock/fu-dell-dock-i2c-mst.c b/plugins/dell-dock/fu-dell-dock-i2c-mst.c index c42c2ec50..c8eb4d4f2 100644 --- a/plugins/dell-dock/fu-dell-dock-i2c-mst.c +++ b/plugins/dell-dock/fu-dell-dock-i2c-mst.c @@ -735,7 +735,7 @@ fu_dell_dock_mst_invalidate_bank (FuDevice *symbiote, MSTBank bank_in_use, static gboolean fu_dell_dock_mst_write_fw (FuDevice *device, - GBytes *blob_fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { @@ -745,13 +745,20 @@ fu_dell_dock_mst_write_fw (FuDevice *device, gboolean checksum = FALSE; guint8 order[2] = {ESM, Bank0}; guint16 chip_id; - const guint8* data = g_bytes_get_data (blob_fw, NULL); + const guint8 *data; g_autofree gchar *dynamic_version = NULL; + g_autoptr(GBytes) fw = NULL; g_return_val_if_fail (device != NULL, FALSE); - g_return_val_if_fail (blob_fw != NULL, FALSE); + g_return_val_if_fail (FU_IS_FIRMWARE (firmware), FALSE); g_return_val_if_fail (self->symbiote != NULL, FALSE); + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + data = g_bytes_get_data (fw, NULL); + dynamic_version = g_strdup_printf ("%02x.%02x.%02x", data[self->blob_major_offset], data[self->blob_minor_offset], @@ -787,7 +794,7 @@ fu_dell_dock_mst_write_fw (FuDevice *device, for (guint phase = 0; phase < 2; phase++) { g_debug ("MST: Checking bank %u", order[phase]); if (!fu_dell_dock_mst_checksum_bank (self->symbiote, - blob_fw, + fw, order[phase], &checksum, error)) return FALSE; @@ -804,11 +811,11 @@ fu_dell_dock_mst_write_fw (FuDevice *device, error)) return FALSE; fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); - if (!fu_dell_dock_write_flash_bank (device, blob_fw, + if (!fu_dell_dock_write_flash_bank (device, fw, order[phase], error)) return FALSE; if (!fu_dell_dock_mst_checksum_bank (self->symbiote, - blob_fw, + fw, order[phase], &checksum, error)) diff --git a/plugins/dell-dock/fu-dell-dock-i2c-tbt.c b/plugins/dell-dock/fu-dell-dock-i2c-tbt.c index 8b0db5935..79c00834b 100644 --- a/plugins/dell-dock/fu-dell-dock-i2c-tbt.c +++ b/plugins/dell-dock/fu-dell-dock-i2c-tbt.c @@ -53,20 +53,27 @@ G_DEFINE_TYPE (FuDellDockTbt, fu_dell_dock_tbt, FU_TYPE_DEVICE) static gboolean fu_dell_dock_tbt_write_fw (FuDevice *device, - GBytes *blob_fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuDellDockTbt *self = FU_DELL_DOCK_TBT (device); guint32 start_offset = 0; - gsize image_size; - const guint8 *buffer = g_bytes_get_data (blob_fw, &image_size); + gsize image_size = 0; + const guint8 *buffer; guint16 target_system = 0; g_autoptr(GTimer) timer = g_timer_new (); g_autofree gchar *dynamic_version = NULL; + g_autoptr(GBytes) fw = NULL; g_return_val_if_fail (device != NULL, FALSE); - g_return_val_if_fail (blob_fw != NULL, FALSE); + g_return_val_if_fail (FU_IS_FIRMWARE (firmware), FALSE); + + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + buffer = g_bytes_get_data (fw, &image_size); dynamic_version = g_strdup_printf ("%02x.%02x", buffer[self->blob_major_offset], diff --git a/plugins/dell-dock/fu-dell-dock-status.c b/plugins/dell-dock/fu-dell-dock-status.c index 3565a4c11..9c803bfbd 100644 --- a/plugins/dell-dock/fu-dell-dock-status.c +++ b/plugins/dell-dock/fu-dell-dock-status.c @@ -58,7 +58,7 @@ fu_dell_dock_status_setup (FuDevice *device, GError **error) static gboolean fu_dell_dock_status_write (FuDevice *device, - GBytes *blob_fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { @@ -66,18 +66,25 @@ fu_dell_dock_status_write (FuDevice *device, FuDevice *parent; gsize length = 0; guint32 status_version = 0; - const guint8 *data = g_bytes_get_data (blob_fw, &length); + const guint8 *data; g_autofree gchar *dynamic_version = NULL; + g_autoptr(GBytes) fw = NULL; g_return_val_if_fail (device != NULL, FALSE); - g_return_val_if_fail (blob_fw != NULL, FALSE); + g_return_val_if_fail (FU_IS_FIRMWARE (firmware), FALSE); + + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + data = g_bytes_get_data (fw, &length); memcpy (&status_version, data + self->blob_version_offset, sizeof (guint32)); dynamic_version = fu_dell_dock_status_ver_string (status_version); g_debug ("writing status firmware version %s", dynamic_version); parent = fu_device_get_parent (device); - if (!fu_dell_dock_ec_commit_package (parent, blob_fw, error)) + if (!fu_dell_dock_ec_commit_package (parent, fw, error)) return FALSE; /* dock will reboot to re-read; this is to appease the daemon */ diff --git a/plugins/dfu/dfu-common.c b/plugins/dfu/dfu-common.c index b0acab376..f0d9df69b 100644 --- a/plugins/dfu/dfu-common.c +++ b/plugins/dfu/dfu-common.c @@ -176,157 +176,3 @@ dfu_utils_bytes_join_array (GPtrArray *chunks) } return g_bytes_new_take (buffer, total_size); } - -/** - * dfu_utils_bytes_pad: - * @bytes: a #GBytes - * @sz: the desired size in bytes - * - * Pads a GBytes to a given @sz with `0xff`. - * - * Return value: (transfer full): a #GBytes - **/ -GBytes * -dfu_utils_bytes_pad (GBytes *bytes, gsize sz) -{ - gsize bytes_sz; - - g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL); - - /* pad */ - bytes_sz = g_bytes_get_size (bytes); - if (bytes_sz < sz) { - const guint8 *data = g_bytes_get_data (bytes, NULL); - guint8 *data_new = g_malloc (sz); - memcpy (data_new, data, bytes_sz); - memset (data_new + bytes_sz, 0xff, sz - bytes_sz); - return g_bytes_new_take (data_new, sz); - } - - /* exactly right */ - return g_bytes_ref (bytes); -} - -/** - * dfu_utils_buffer_parse_uint4: - * @data: a string - * - * Parses a base 16 number from a string. - * - * The string MUST be at least 1 byte long as this function cannot check the - * length of @data. Checking the size must be done in the caller. - * - * Return value: A parsed value, or 0 for error - **/ -guint8 -dfu_utils_buffer_parse_uint4 (const gchar *data) -{ - gchar buffer[2]; - memcpy (buffer, data, 1); - buffer[1] = '\0'; - return (guint8) g_ascii_strtoull (buffer, NULL, 16); -} - -/** - * dfu_utils_buffer_parse_uint8: - * @data: a string - * - * Parses a base 16 number from a string. - * - * The string MUST be at least 2 bytes long as this function cannot check the - * length of @data. Checking the size must be done in the caller. - * - * Return value: A parsed value, or 0 for error - **/ -guint8 -dfu_utils_buffer_parse_uint8 (const gchar *data) -{ - gchar buffer[3]; - memcpy (buffer, data, 2); - buffer[2] = '\0'; - return (guint8) g_ascii_strtoull (buffer, NULL, 16); -} - -/** - * dfu_utils_buffer_parse_uint16: - * @data: a string - * - * Parses a base 16 number from a string. - * - * The string MUST be at least 4 bytes long as this function cannot check the - * length of @data. Checking the size must be done in the caller. - * - * Return value: A parsed value, or 0 for error - **/ -guint16 -dfu_utils_buffer_parse_uint16 (const gchar *data) -{ - gchar buffer[5]; - memcpy (buffer, data, 4); - buffer[4] = '\0'; - return (guint16) g_ascii_strtoull (buffer, NULL, 16); -} - -/** - * dfu_utils_buffer_parse_uint24: - * @data: a string - * - * Parses a base 16 number from a string. - * - * The string MUST be at least 6 bytes long as this function cannot check the - * length of @data. Checking the size must be done in the caller. - * - * Return value: A parsed value, or 0 for error - **/ -guint32 -dfu_utils_buffer_parse_uint24 (const gchar *data) -{ - gchar buffer[7]; - memcpy (buffer, data, 6); - buffer[6] = '\0'; - return (guint32) g_ascii_strtoull (buffer, NULL, 16); -} - -/** - * dfu_utils_buffer_parse_uint32: - * @data: a string - * - * Parses a base 16 number from a string. - * - * The string MUST be at least 8 bytes long as this function cannot check the - * length of @data. Checking the size must be done in the caller. - * - * Return value: A parsed value, or 0 for error - **/ -guint32 -dfu_utils_buffer_parse_uint32 (const gchar *data) -{ - gchar buffer[9]; - memcpy (buffer, data, 8); - buffer[8] = '\0'; - return (guint32) g_ascii_strtoull (buffer, NULL, 16); -} - -/** - * dfu_utils_strnsplit: - * @str: a string to split - * @sz: size of @str - * @delimiter: a string which specifies the places at which to split the string - * @max_tokens: the maximum number of pieces to split @str into - * - * Splits a string into a maximum of @max_tokens pieces, using the given - * delimiter. If @max_tokens is reached, the remainder of string is appended - * to the last token. - * - * Return value: a newly-allocated NULL-terminated array of strings - **/ -gchar ** -dfu_utils_strnsplit (const gchar *str, gsize sz, - const gchar *delimiter, gint max_tokens) -{ - if (str[sz - 1] != '\0') { - g_autofree gchar *str2 = g_strndup (str, sz); - return g_strsplit (str2, delimiter, max_tokens); - } - return g_strsplit (str, delimiter, max_tokens); -} diff --git a/plugins/dfu/dfu-common.h b/plugins/dfu/dfu-common.h index 19a782eff..148a30b86 100644 --- a/plugins/dfu/dfu-common.h +++ b/plugins/dfu/dfu-common.h @@ -156,16 +156,5 @@ const gchar *dfu_version_to_string (DfuVersion version); /* helpers */ GBytes *dfu_utils_bytes_join_array (GPtrArray *chunks); -GBytes *dfu_utils_bytes_pad (GBytes *bytes, - gsize sz); -guint8 dfu_utils_buffer_parse_uint4 (const gchar *data); -guint8 dfu_utils_buffer_parse_uint8 (const gchar *data); -guint16 dfu_utils_buffer_parse_uint16 (const gchar *data); -guint32 dfu_utils_buffer_parse_uint24 (const gchar *data); -guint32 dfu_utils_buffer_parse_uint32 (const gchar *data); -gchar **dfu_utils_strnsplit (const gchar *str, - gsize sz, - const gchar *delimiter, - gint max_tokens); G_END_DECLS diff --git a/plugins/dfu/dfu-device.c b/plugins/dfu/dfu-device.c index f55c23f14..4a50d4ad0 100644 --- a/plugins/dfu/dfu-device.c +++ b/plugins/dfu/dfu-device.c @@ -86,6 +86,7 @@ #include "dfu-target-stm.h" #include "fu-device-locker.h" +#include "fu-firmware-common.h" #include "fwupd-error.h" @@ -1120,8 +1121,8 @@ dfu_device_detach (DfuDevice *device, GError **error) g_autoptr(GError) error_jabra = NULL; /* parse string and create magic packet */ - rep = dfu_utils_buffer_parse_uint8 (priv->jabra_detach + 0); - adr = dfu_utils_buffer_parse_uint8 (priv->jabra_detach + 2); + rep = fu_firmware_strparse_uint8 (priv->jabra_detach + 0); + adr = fu_firmware_strparse_uint8 (priv->jabra_detach + 2); buf[0] = rep; buf[1] = adr; buf[2] = 0x00; @@ -2052,7 +2053,7 @@ dfu_device_set_quirk_kv (FuDevice *device, } if (g_strcmp0 (key, FU_QUIRKS_DFU_FORCE_VERSION) == 0) { if (value != NULL && strlen (value) == 4) { - priv->force_version = dfu_utils_buffer_parse_uint16 (value); + priv->force_version = fu_firmware_strparse_uint16 (value); return TRUE; } g_set_error_literal (error, diff --git a/plugins/dfu/dfu-element.c b/plugins/dfu/dfu-element.c index aa146fc52..23f015065 100644 --- a/plugins/dfu/dfu-element.c +++ b/plugins/dfu/dfu-element.c @@ -239,60 +239,3 @@ dfu_element_set_target_size (DfuElement *element, guint32 target_size) g_bytes_unref (priv->contents); priv->contents = g_bytes_new_take (buf, target_size); } - -/** - * dfu_element_get_contents_chunk: - * @element: a #DfuElement - * @address: an address greater than dfu_element_get_address() - * @chunk_sz_max: the size of the new chunk - * @error: a #GError, or %NULL - * - * Gets a block of data from the @element. If the contents of the element is - * smaller than the requested chunk size then the #GBytes will be smaller - * than @chunk_sz_max. Use dfu_utils_bytes_pad() if padding is required. - * - * If the @address is larger than the size of the @element then an error is returned. - * - * Return value: (transfer full): a #GBytes, or %NULL - **/ -GBytes * -dfu_element_get_contents_chunk (DfuElement *element, - guint32 address, - guint32 chunk_sz_max, - GError **error) -{ - GBytes *blob; - gsize chunk_left; - guint32 offset; - - /* check address requested is larger than base address */ - if (address < dfu_element_get_address (element)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "requested address 0x%x less than base address 0x%x", - (guint) address, (guint) dfu_element_get_address (element)); - return NULL; - } - - /* offset into data */ - offset = address - dfu_element_get_address (element); - blob = dfu_element_get_contents (element); - if (offset > g_bytes_get_size (blob)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "offset 0x%x larger than data size 0x%x", - (guint) offset, - (guint) g_bytes_get_size (blob)); - return NULL; - } - - /* if we have less data than requested */ - chunk_left = g_bytes_get_size (blob) - offset; - if (chunk_sz_max > chunk_left) - return g_bytes_new_from_bytes (blob, offset, chunk_left); - - /* check chunk */ - return g_bytes_new_from_bytes (blob, offset, chunk_sz_max); -} diff --git a/plugins/dfu/dfu-element.h b/plugins/dfu/dfu-element.h index b6a60a1d0..ee838c49b 100644 --- a/plugins/dfu/dfu-element.h +++ b/plugins/dfu/dfu-element.h @@ -23,11 +23,6 @@ DfuElement *dfu_element_new (void); GBytes *dfu_element_get_contents (DfuElement *element); guint32 dfu_element_get_address (DfuElement *element); -GBytes *dfu_element_get_contents_chunk (DfuElement *element, - guint32 address, - guint32 chunk_sz_max, - GError **error); - void dfu_element_set_contents (DfuElement *element, GBytes *contents); void dfu_element_set_address (DfuElement *element, diff --git a/plugins/dfu/dfu-format-ihex.c b/plugins/dfu/dfu-format-ihex.c index 22f5dda29..9bea8a763 100644 --- a/plugins/dfu/dfu-format-ihex.c +++ b/plugins/dfu/dfu-format-ihex.c @@ -15,6 +15,9 @@ #include "dfu-format-ihex.h" #include "dfu-image.h" +#include "fu-firmware-common.h" +#include "fu-ihex-firmware.h" + #include "fwupd-error.h" /** @@ -46,34 +49,6 @@ dfu_firmware_detect_ihex (GBytes *bytes) return DFU_FIRMWARE_FORMAT_UNKNOWN; } -#define DFU_INHX32_RECORD_TYPE_DATA 0x00 -#define DFU_INHX32_RECORD_TYPE_EOF 0x01 -#define DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT 0x02 -#define DFU_INHX32_RECORD_TYPE_START_SEGMENT 0x03 -#define DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR 0x04 -#define DFU_INHX32_RECORD_TYPE_START_LINEAR 0x05 -#define DFU_INHX32_RECORD_TYPE_SIGNATURE 0xfd - -static const gchar * -dfu_firmware_ihex_record_type_to_string (guint8 record_type) -{ - if (record_type == DFU_INHX32_RECORD_TYPE_DATA) - return "DATA"; - if (record_type == DFU_INHX32_RECORD_TYPE_EOF) - return "EOF"; - if (record_type == DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT) - return "EXTENDED_SEGMENT"; - if (record_type == DFU_INHX32_RECORD_TYPE_START_SEGMENT) - return "START_SEGMENT"; - if (record_type == DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR) - return "EXTENDED_LINEAR"; - if (record_type == DFU_INHX32_RECORD_TYPE_START_LINEAR) - return "ADDR32"; - if (record_type == DFU_INHX32_RECORD_TYPE_SIGNATURE) - return "SIGNATURE"; - return NULL; -} - /** * dfu_firmware_from_ihex: (skip) * @firmware: a #DfuFirmware @@ -91,315 +66,28 @@ dfu_firmware_from_ihex (DfuFirmware *firmware, DfuFirmwareParseFlags flags, GError **error) { - const gchar *data; - gboolean got_eof = FALSE; - gsize sz = 0; - guint32 abs_addr = 0x0; - guint32 addr_last = 0x0; - guint32 base_addr = G_MAXUINT32; - guint32 seg_addr = 0x0; - g_auto(GStrv) lines = NULL; - g_autoptr(DfuElement) element = NULL; - g_autoptr(DfuImage) image = NULL; - g_autoptr(GBytes) contents = NULL; - g_autoptr(GString) buf = g_string_new (NULL); - g_autoptr(GString) buf_signature = g_string_new (NULL); + g_autoptr(FuFirmware) firmware_new = fu_ihex_firmware_new (); + g_autoptr(GPtrArray) imgs = NULL; + FwupdInstallFlags flags_new = FWUPD_INSTALL_FLAG_NONE; - g_return_val_if_fail (bytes != NULL, FALSE); - - /* create element */ - image = dfu_image_new (); - dfu_image_set_name (image, "ihex"); - element = dfu_element_new (); - - /* parse records */ - data = g_bytes_get_data (bytes, &sz); - lines = dfu_utils_strnsplit (data, sz, "\n", -1); - for (guint ln = 0; lines[ln] != NULL; ln++) { - const gchar *line = lines[ln]; - gsize linesz; - guint32 addr; - guint8 byte_cnt; - guint8 record_type; - guint line_end; - - /* ignore comments */ - if (g_str_has_prefix (line, ";")) - continue; - - /* ignore blank lines */ - g_strdelimit (lines[ln], "\r\x1a", '\0'); - linesz = strlen (line); - if (linesz == 0) - continue; - - /* check starting token */ - if (line[0] != ':') { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "invalid starting token on line %u: %s", - ln + 1, line); - return FALSE; - } - - /* check there's enough data for the smallest possible record */ - if (linesz < 11) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "line %u is incomplete, length %u", - ln + 1, (guint) linesz); - return FALSE; - } - - /* length, 16-bit address, type */ - byte_cnt = dfu_utils_buffer_parse_uint8 (line + 1); - addr = dfu_utils_buffer_parse_uint16 (line + 3); - record_type = dfu_utils_buffer_parse_uint8 (line + 7); - g_debug ("%s:", dfu_firmware_ihex_record_type_to_string (record_type)); - g_debug (" addr_start:\t0x%04x", addr); - g_debug (" length:\t0x%02x", byte_cnt); - addr += seg_addr; - addr += abs_addr; - g_debug (" addr:\t0x%08x", addr); - - /* position of checksum */ - line_end = 9 + byte_cnt * 2; - if (line_end > (guint) linesz) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "line %u malformed, length: %u", - ln + 1, line_end); - return FALSE; - } - - /* verify checksum */ - if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST) == 0) { - guint8 checksum = 0; - for (guint i = 1; i < line_end + 2; i += 2) { - guint8 data_tmp = dfu_utils_buffer_parse_uint8 (line + i); - checksum += data_tmp; - } - if (checksum != 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "line %u has invalid checksum (0x%02x)", - ln + 1, checksum); - return FALSE; - } - } - - /* process different record types */ - switch (record_type) { - case DFU_INHX32_RECORD_TYPE_DATA: - /* base address for element */ - if (base_addr == G_MAXUINT32) - base_addr = addr; - - /* does not make sense */ - if (addr < addr_last) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "invalid address 0x%x, last was 0x%x", - (guint) addr, - (guint) addr_last); - return FALSE; - } - - /* parse bytes from line */ - g_debug ("writing data 0x%08x", (guint32) addr); - for (guint i = 9; i < line_end; i += 2) { - /* any holes in the hex record */ - guint32 len_hole = addr - addr_last; - guint8 data_tmp; - if (addr_last > 0 && len_hole > 0x100000) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "hole of 0x%x bytes too large to fill", - (guint) len_hole); - return FALSE; - } - if (addr_last > 0x0 && len_hole > 1) { - g_debug ("filling address 0x%08x to 0x%08x", - addr_last + 1, addr_last + len_hole - 1); - for (guint j = 1; j < len_hole; j++) { - /* although 0xff might be clearer, - * we can't write 0xffff to pic14 */ - g_string_append_c (buf, 0x00); - } - } - /* write into buf */ - data_tmp = dfu_utils_buffer_parse_uint8 (line + i); - g_string_append_c (buf, (gchar) data_tmp); - addr_last = addr++; - } - break; - case DFU_INHX32_RECORD_TYPE_EOF: - if (got_eof) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "duplicate EOF, perhaps " - "corrupt file"); - return FALSE; - } - got_eof = TRUE; - break; - case DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR: - abs_addr = dfu_utils_buffer_parse_uint16 (line + 9) << 16; - g_debug (" abs_addr:\t0x%02x", abs_addr); - break; - case DFU_INHX32_RECORD_TYPE_START_LINEAR: - abs_addr = dfu_utils_buffer_parse_uint32 (line + 9); - g_debug (" abs_addr:\t0x%08x", abs_addr); - break; - case DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT: - /* segment base address, so ~1Mb addressable */ - seg_addr = dfu_utils_buffer_parse_uint16 (line + 9) * 16; - g_debug (" seg_addr:\t0x%08x", seg_addr); - break; - case DFU_INHX32_RECORD_TYPE_START_SEGMENT: - /* initial content of the CS:IP registers */ - seg_addr = dfu_utils_buffer_parse_uint32 (line + 9); - g_debug (" seg_addr:\t0x%02x", seg_addr); - break; - case DFU_INHX32_RECORD_TYPE_SIGNATURE: - for (guint i = 9; i < line_end; i += 2) { - guint8 tmp_c = dfu_utils_buffer_parse_uint8 (line + i); - g_string_append_c (buf_signature, tmp_c); - } - break; - default: - /* vendors sneak in nonstandard sections past the EOF */ - if (got_eof) - break; - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "invalid ihex record type %i", - record_type); - return FALSE; - } - } - - /* no EOF */ - if (!got_eof) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "no EOF, perhaps truncated file"); + /* make a native objects from the abstract firmware */ + if (flags & DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST) + flags_new |= FWUPD_INSTALL_FLAG_FORCE; + if (!fu_firmware_parse (firmware_new, bytes, flags_new, error)) return FALSE; - } + imgs = fu_firmware_get_images (firmware_new); + for (guint i = 0; i < imgs->len; i++) { + FuFirmwareImage *img = g_ptr_array_index (imgs, i); + g_autoptr(DfuElement) element = dfu_element_new (); + g_autoptr(DfuImage) image = dfu_image_new (); + dfu_element_set_contents (element, fu_firmware_image_get_bytes (img, NULL)); + dfu_element_set_address (element, fu_firmware_image_get_addr (img)); + dfu_image_add_element (image, element); + dfu_image_set_name (image, "ihex"); + dfu_firmware_add_image (firmware, image); - /* add single image */ - contents = g_bytes_new (buf->str, buf->len); - dfu_element_set_contents (element, contents); - if (base_addr != G_MAXUINT32) - dfu_element_set_address (element, base_addr); - dfu_image_add_element (image, element); - dfu_firmware_add_image (firmware, image); + } dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_INTEL_HEX); - - /* add optional signature */ - if (buf_signature->len > 0) { - g_autoptr(DfuElement) element_sig = dfu_element_new (); - g_autoptr(DfuImage) image_sig = dfu_image_new (); - g_autoptr(GBytes) data_sig = g_bytes_new_static (buf_signature->str, buf_signature->len); - dfu_element_set_contents (element_sig, data_sig); - dfu_image_add_element (image_sig, element_sig); - dfu_image_set_name (image_sig, "signature"); - dfu_firmware_add_image (firmware, image_sig); - } - return TRUE; -} - -static void -dfu_firmware_ihex_emit_chunk (GString *str, - guint16 address, - guint8 record_type, - const guint8 *data, - gsize sz) -{ - guint8 checksum = 0x00; - g_string_append_printf (str, ":%02X%04X%02X", - (guint) sz, - (guint) address, - (guint) record_type); - for (gsize j = 0; j < sz; j++) - g_string_append_printf (str, "%02X", data[j]); - checksum = (guint8) sz; - checksum += (guint8) ((address & 0xff00) >> 8); - checksum += (guint8) (address & 0xff); - checksum += record_type; - for (gsize j = 0; j < sz; j++) - checksum += data[j]; - g_string_append_printf (str, "%02X\n", (guint) (((~checksum) + 0x01) & 0xff)); -} - -static void -dfu_firmware_to_ihex_bytes (GString *str, guint8 record_type, - guint32 address, GBytes *contents) -{ - const guint8 *data; - const guint chunk_size = 16; - gsize len; - guint32 address_offset_last = 0x0; - - /* get number of chunks */ - data = g_bytes_get_data (contents, &len); - for (gsize i = 0; i < len; i += chunk_size) { - guint32 address_tmp = address + i; - guint32 address_offset = (address_tmp >> 16) & 0xffff; - gsize chunk_len = MIN (len - i, 16); - - /* need to offset */ - if (address_offset != address_offset_last) { - guint8 buf[2]; - fu_common_write_uint16 (buf, address_offset, G_BIG_ENDIAN); - dfu_firmware_ihex_emit_chunk (str, 0x0, - DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR, - buf, 2); - address_offset_last = address_offset; - } - address_tmp &= 0xffff; - dfu_firmware_ihex_emit_chunk (str, address_tmp, - record_type, data + i, chunk_len); - } -} - -static gboolean -dfu_firmware_to_ihex_element (DfuElement *element, GString *str, - guint8 record_type, GError **error) -{ - GBytes *contents = dfu_element_get_contents (element); - dfu_firmware_to_ihex_bytes (str, record_type, - dfu_element_get_address (element), - contents); - return TRUE; -} - -static gboolean -dfu_firmware_to_ihex_image (DfuImage *image, GString *str, GError **error) -{ - GPtrArray *elements; - guint8 record_type = DFU_INHX32_RECORD_TYPE_DATA; - - if (g_strcmp0 (dfu_image_get_name (image), "signature") == 0) - record_type = DFU_INHX32_RECORD_TYPE_SIGNATURE; - elements = dfu_image_get_elements (image); - for (guint i = 0; i < elements->len; i++) { - DfuElement *element = g_ptr_array_index (elements, i); - if (!dfu_firmware_to_ihex_element (element, - str, - record_type, - error)) - return FALSE; - } return TRUE; } @@ -416,18 +104,21 @@ GBytes * dfu_firmware_to_ihex (DfuFirmware *firmware, GError **error) { GPtrArray *images; - g_autoptr(GString) str = NULL; + g_autoptr(FuFirmware) firmware_new = fu_ihex_firmware_new (); - /* write all the element data */ - str = g_string_new (""); + /* make a new object from the native firmware */ images = dfu_firmware_get_images (firmware); for (guint i = 0; i < images->len; i++) { DfuImage *image = g_ptr_array_index (images, i); - if (!dfu_firmware_to_ihex_image (image, str, error)) - return NULL; + GPtrArray *elements = dfu_image_get_elements (image); + for (guint j = 0; j < elements->len; j++) { + DfuElement *element = g_ptr_array_index (elements, j); + g_autoptr(FuFirmwareImage) img = NULL; + img = fu_firmware_image_new (dfu_element_get_contents (element)); + fu_firmware_image_set_id (img, dfu_image_get_name (image)); + fu_firmware_image_set_addr (img, dfu_element_get_address (element)); + fu_firmware_add_image (firmware_new, img); + } } - - /* add EOF */ - dfu_firmware_ihex_emit_chunk (str, 0x0, DFU_INHX32_RECORD_TYPE_EOF, NULL, 0); - return g_bytes_new (str->str, str->len); + return fu_firmware_write (firmware_new, error); } diff --git a/plugins/dfu/dfu-format-srec.c b/plugins/dfu/dfu-format-srec.c index bfcd49bc6..cfeacdfa8 100644 --- a/plugins/dfu/dfu-format-srec.c +++ b/plugins/dfu/dfu-format-srec.c @@ -10,11 +10,13 @@ #include "fu-common.h" -#include "dfu-element.h" #include "dfu-firmware.h" #include "dfu-format-srec.h" #include "dfu-image.h" +#include "fu-firmware-common.h" +#include "fu-srec-firmware.h" + #include "fwupd-error.h" /** @@ -38,282 +40,6 @@ dfu_firmware_detect_srec (GBytes *bytes) return DFU_FIRMWARE_FORMAT_SREC; } -/** - * dfu_firmware_from_srec: (skip) - * @firmware: a #DfuFirmware - * @bytes: data to parse - * @flags: some #DfuFirmwareParseFlags - * @error: a #GError, or %NULL - * - * Unpacks into a firmware object from raw data. - * - * Returns: %TRUE for success - **/ -gboolean -dfu_image_from_srec (DfuImage *image, - GBytes *bytes, - guint32 start_addr, - DfuFirmwareParseFlags flags, - GError **error) -{ - const gchar *data; - gboolean got_eof = FALSE; - gboolean got_hdr = FALSE; - gsize sz = 0; - guint16 data_cnt = 0; - guint32 addr32_last = 0; - guint32 element_address = 0; - g_auto(GStrv) lines = NULL; - g_autoptr(DfuElement) element = dfu_element_new (); - g_autoptr(GBytes) contents = NULL; - g_autoptr(GString) outbuf = g_string_new (NULL); - - g_return_val_if_fail (bytes != NULL, FALSE); - - /* parse records */ - data = g_bytes_get_data (bytes, &sz); - lines = dfu_utils_strnsplit (data, sz, "\n", -1); - for (guint ln = 0; lines[ln] != NULL; ln++) { - const gchar *line = lines[ln]; - gsize linesz; - guint32 rec_addr32; - guint8 addrsz = 0; /* bytes */ - guint8 rec_count; /* words */ - guint8 rec_kind; - - /* ignore blank lines */ - g_strdelimit (lines[ln], "\r", '\0'); - linesz = strlen (line); - if (linesz == 0) - continue; - - /* check starting token */ - if (line[0] != 'S') { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "invalid starting token, got '%c' at line %u", - line[0], ln); - return FALSE; - } - - /* check there's enough data for the smallest possible record */ - if (linesz < 10) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "record incomplete at line %u, length %u", - ln, (guint) linesz); - return FALSE; - } - - /* kind, count, address, (data), checksum, linefeed */ - rec_kind = line[1] - '0'; - rec_count = dfu_utils_buffer_parse_uint8 (line + 2); - if (rec_count * 2 != linesz - 4) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "count incomplete at line %u, " - "length %u, expected %u", - ln, (guint) linesz - 4, (guint) rec_count * 2); - return FALSE; - } - - /* checksum check */ - if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST) == 0) { - guint8 rec_csum = 0; - guint8 rec_csum_expected; - for (guint8 i = 0; i < rec_count; i++) - rec_csum += dfu_utils_buffer_parse_uint8 (line + (i * 2) + 2); - rec_csum ^= 0xff; - rec_csum_expected = dfu_utils_buffer_parse_uint8 (line + (rec_count * 2) + 2); - if (rec_csum != rec_csum_expected) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "checksum incorrect line %u, " - "expected %02x, got %02x", - ln, rec_csum_expected, rec_csum); - return FALSE; - } - } - - /* set each command settings */ - switch (rec_kind) { - case 0: - addrsz = 2; - if (got_hdr) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "duplicate header record"); - return FALSE; - } - got_hdr = TRUE; - break; - case 1: - addrsz = 2; - break; - case 2: - addrsz = 3; - break; - case 3: - addrsz = 4; - break; - case 5: - addrsz = 2; - got_eof = TRUE; - break; - case 6: - addrsz = 3; - break; - case 7: - addrsz = 4; - got_eof = TRUE; - break; - case 8: - addrsz = 3; - got_eof = TRUE; - break; - case 9: - addrsz = 2; - got_eof = TRUE; - break; - default: - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "invalid srec record type S%c", - line[1]); - return FALSE; - } - - /* parse address */ - switch (addrsz) { - case 2: - rec_addr32 = dfu_utils_buffer_parse_uint16 (line + 4); - break; - case 3: - rec_addr32 = dfu_utils_buffer_parse_uint24 (line + 4); - break; - case 4: - rec_addr32 = dfu_utils_buffer_parse_uint32 (line + 4); - break; - default: - g_assert_not_reached (); - } - - /* header */ - if (rec_kind == 0) { - g_autoptr(GString) modname = g_string_new (NULL); - if (rec_addr32 != 0x0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "invalid header record address, got %04x", - rec_addr32); - return FALSE; - } - - /* could be anything, lets assume text */ - for (guint8 i = 4 + (addrsz * 2); i <= rec_count * 2; i += 2) { - guint8 tmp = dfu_utils_buffer_parse_uint8 (line + i); - if (!g_ascii_isgraph (tmp)) - break; - g_string_append_c (modname, tmp); - } - if (modname->len != 0) - dfu_image_set_name (image, modname->str); - continue; - } - - /* verify we got all records */ - if (rec_kind == 5) { - if (rec_addr32 != data_cnt) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "count record was not valid, got 0x%02x expected 0x%02x", - (guint) rec_addr32, (guint) data_cnt); - return FALSE; - } - } - - /* data */ - if (rec_kind == 1 || rec_kind == 2 || rec_kind == 3) { - /* invalid */ - if (!got_hdr) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "missing header record"); - return FALSE; - } - /* does not make sense */ - if (rec_addr32 < addr32_last) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "invalid address 0x%x, last was 0x%x", - (guint) rec_addr32, - (guint) addr32_last); - return FALSE; - } - if (rec_addr32 < start_addr) { - g_debug ("ignoring data at 0x%x as before start address 0x%x", - (guint) rec_addr32, (guint) start_addr); - } else { - guint bytecnt = 0; - guint32 len_hole = rec_addr32 - addr32_last; - - /* fill any holes, but only up to 1Mb to avoid a DoS */ - if (addr32_last > 0 && len_hole > 0x100000) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "hole of 0x%x bytes too large to fill", - (guint) len_hole); - return FALSE; - } - if (addr32_last > 0x0 && len_hole > 1) { - g_debug ("filling address 0x%08x to 0x%08x", - addr32_last + 1, addr32_last + len_hole - 1); - for (guint j = 0; j < len_hole; j++) - g_string_append_c (outbuf, 0xff); - } - - /* add data */ - for (guint8 i = 4 + (addrsz * 2); i <= rec_count * 2; i += 2) { - guint8 tmp = dfu_utils_buffer_parse_uint8 (line + i); - g_string_append_c (outbuf, tmp); - bytecnt++; - } - if (element_address == 0x0) - element_address = rec_addr32; - addr32_last = rec_addr32 + bytecnt; - } - data_cnt++; - } - } - - /* no EOF */ - if (!got_eof) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "no EOF, perhaps truncated file"); - return FALSE; - } - - /* add single image */ - contents = g_bytes_new (outbuf->str, outbuf->len); - dfu_element_set_contents (element, contents); - dfu_element_set_address (element, element_address); - dfu_image_add_element (image, element); - return TRUE; -} - /** * dfu_firmware_from_srec: (skip) * @firmware: a #DfuFirmware @@ -331,15 +57,26 @@ dfu_firmware_from_srec (DfuFirmware *firmware, DfuFirmwareParseFlags flags, GError **error) { - g_autoptr(DfuImage) image = NULL; + g_autoptr(FuFirmware) firmware_new = fu_srec_firmware_new (); + g_autoptr(GPtrArray) imgs = NULL; + FwupdInstallFlags flags_new = FWUPD_INSTALL_FLAG_NONE; - g_return_val_if_fail (bytes != NULL, FALSE); - - /* add single image */ - image = dfu_image_new (); - if (!dfu_image_from_srec (image, bytes, 0x0, flags, error)) + /* make a native objects from the abstract firmware */ + if (flags & DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST) + flags_new |= FWUPD_INSTALL_FLAG_FORCE; + if (!fu_firmware_parse (firmware_new, bytes, flags_new, error)) return FALSE; - dfu_firmware_add_image (firmware, image); + imgs = fu_firmware_get_images (firmware_new); + for (guint i = 0; i < imgs->len; i++) { + FuFirmwareImage *img = g_ptr_array_index (imgs, i); + g_autoptr(DfuElement) element = dfu_element_new (); + g_autoptr(DfuImage) image = dfu_image_new (); + dfu_element_set_contents (element, fu_firmware_image_get_bytes (img, NULL)); + dfu_element_set_address (element, fu_firmware_image_get_addr (img)); + dfu_image_add_element (image, element); + dfu_firmware_add_image (firmware, image); + + } dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_SREC); return TRUE; } diff --git a/plugins/dfu/dfu-format-srec.h b/plugins/dfu/dfu-format-srec.h index 18be5054f..b5a9a87e3 100644 --- a/plugins/dfu/dfu-format-srec.h +++ b/plugins/dfu/dfu-format-srec.h @@ -20,10 +20,5 @@ gboolean dfu_firmware_from_srec (DfuFirmware *firmware, GBytes *bytes, DfuFirmwareParseFlags flags, GError **error); -gboolean dfu_image_from_srec (DfuImage *image, - GBytes *bytes, - guint32 start_addr, - DfuFirmwareParseFlags flags, - GError **error); G_END_DECLS diff --git a/plugins/dfu/dfu-self-test.c b/plugins/dfu/dfu-self-test.c index 1232b6aab..fb9e1be15 100644 --- a/plugins/dfu/dfu-self-test.c +++ b/plugins/dfu/dfu-self-test.c @@ -320,209 +320,6 @@ dfu_firmware_metadata_func (void) g_assert_true (ret); } -static void -dfu_firmware_intel_hex_offset_func (void) -{ - DfuElement *element_verify; - DfuImage *image_verify; - const guint8 *data; - gboolean ret; - gsize len; - g_autofree gchar *str = NULL; - g_autoptr(DfuElement) element = NULL; - g_autoptr(DfuFirmware) firmware = NULL; - g_autoptr(DfuFirmware) firmware_verify = NULL; - g_autoptr(DfuImage) image = NULL; - g_autoptr(GBytes) data_bin = NULL; - g_autoptr(GBytes) data_dummy = NULL; - g_autoptr(GError) error = NULL; - - /* add a 4 byte image in high memory */ - element = dfu_element_new (); - data_dummy = g_bytes_new_static ("foo", 4); - dfu_element_set_address (element, 0x80000000); - dfu_element_set_contents (element, data_dummy); - image = dfu_image_new (); - dfu_image_add_element (image, element); - firmware = dfu_firmware_new (); - dfu_firmware_add_image (firmware, image); - dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_INTEL_HEX); - data_bin = dfu_firmware_write_data (firmware, &error); - g_assert_no_error (error); - g_assert (data_bin != NULL); - data = g_bytes_get_data (data_bin, &len); - str = g_strndup ((const gchar *) data, len); - g_assert_cmpstr (str, ==, - ":0200000480007A\n" - ":04000000666F6F00B8\n" - ":00000001FF\n"); - - /* check we can load it too */ - firmware_verify = dfu_firmware_new (); - ret = dfu_firmware_parse_data (firmware_verify, data_bin, DFU_FIRMWARE_PARSE_FLAG_NONE, &error); - g_assert_no_error (error); - g_assert (ret); - image_verify = dfu_firmware_get_image_default (firmware_verify); - g_assert (image_verify != NULL); - element_verify = dfu_image_get_element_default (image); - g_assert (element_verify != NULL); - g_assert_cmpint (dfu_element_get_address (element_verify), ==, 0x80000000); - g_assert_cmpint (g_bytes_get_size (dfu_element_get_contents (element_verify)), ==, 0x4); -} - -static void -dfu_firmware_srec_func (void) -{ - gboolean ret; - g_autofree gchar *filename_hex = NULL; - g_autofree gchar *filename_ref = NULL; - g_autoptr(DfuFirmware) firmware = NULL; - g_autoptr(GBytes) data_bin = NULL; - g_autoptr(GBytes) data_ref = NULL; - g_autoptr(GError) error = NULL; - g_autoptr(GFile) file_bin = NULL; - g_autoptr(GFile) file_hex = NULL; - - filename_hex = dfu_test_get_filename ("firmware.srec"); - g_assert (filename_hex != NULL); - file_hex = g_file_new_for_path (filename_hex); - firmware = dfu_firmware_new (); - ret = dfu_firmware_parse_file (firmware, file_hex, - DFU_FIRMWARE_PARSE_FLAG_NONE, - &error); - g_assert_no_error (error); - g_assert (ret); - g_assert_cmpint (dfu_firmware_get_size (firmware), ==, 136); - - dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_RAW); - data_bin = dfu_firmware_write_data (firmware, &error); - g_assert_no_error (error); - g_assert (data_bin != NULL); - - /* did we match the reference file? */ - filename_ref = dfu_test_get_filename ("firmware.bin"); - g_assert (filename_ref != NULL); - file_bin = g_file_new_for_path (filename_ref); - data_ref = dfu_self_test_get_bytes_for_file (file_bin, &error); - g_assert_no_error (error); - g_assert (data_ref != NULL); - ret = fu_common_bytes_compare (data_bin, data_ref, &error); - g_assert_no_error (error); - g_assert_true (ret); -} - -static void -dfu_firmware_intel_hex_func (void) -{ - const guint8 *data; - gboolean ret; - gsize len; - g_autofree gchar *filename_hex = NULL; - g_autofree gchar *filename_ref = NULL; - g_autofree gchar *str = NULL; - g_autoptr(DfuFirmware) firmware = NULL; - g_autoptr(GBytes) data_bin2 = NULL; - g_autoptr(GBytes) data_bin = NULL; - g_autoptr(GBytes) data_hex = NULL; - g_autoptr(GBytes) data_ref = NULL; - g_autoptr(GError) error = NULL; - g_autoptr(GFile) file_bin = NULL; - g_autoptr(GFile) file_hex = NULL; - - /* load a Intel hex32 file */ - filename_hex = dfu_test_get_filename ("firmware.hex"); - g_assert (filename_hex != NULL); - file_hex = g_file_new_for_path (filename_hex); - firmware = dfu_firmware_new (); - ret = dfu_firmware_parse_file (firmware, file_hex, - DFU_FIRMWARE_PARSE_FLAG_NONE, - &error); - g_assert_no_error (error); - g_assert (ret); - g_assert_cmpint (dfu_firmware_get_size (firmware), ==, 136); - dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_RAW); - data_bin = dfu_firmware_write_data (firmware, &error); - g_assert_no_error (error); - g_assert (data_bin != NULL); - - /* did we match the reference file? */ - filename_ref = dfu_test_get_filename ("firmware.bin"); - g_assert (filename_ref != NULL); - file_bin = g_file_new_for_path (filename_ref); - data_ref = dfu_self_test_get_bytes_for_file (file_bin, &error); - g_assert_no_error (error); - g_assert (data_ref != NULL); - ret = fu_common_bytes_compare (data_bin, data_ref, &error); - g_assert_no_error (error); - g_assert_true (ret); - - /* export a ihex file (which will be slightly different due to - * non-continous regions being expanded */ - dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_INTEL_HEX); - data_hex = dfu_firmware_write_data (firmware, &error); - g_assert_no_error (error); - g_assert (data_hex != NULL); - data = g_bytes_get_data (data_hex, &len); - str = g_strndup ((const gchar *) data, len); - g_assert_cmpstr (str, ==, - ":104000003DEF20F000000000FACF01F0FBCF02F0FE\n" - ":10401000E9CF03F0EACF04F0E1CF05F0E2CF06F0FC\n" - ":10402000D9CF07F0DACF08F0F3CF09F0F4CF0AF0D8\n" - ":10403000F6CF0BF0F7CF0CF0F8CF0DF0F5CF0EF078\n" - ":104040000EC0F5FF0DC0F8FF0CC0F7FF0BC0F6FF68\n" - ":104050000AC0F4FF09C0F3FF08C0DAFF07C0D9FFA8\n" - ":1040600006C0E2FF05C0E1FF04C0EAFF03C0E9FFAC\n" - ":1040700002C0FBFF01C0FAFF11003FEF20F000017A\n" - ":0840800042EF20F03DEF20F0BB\n" - ":00000001FF\n"); - - /* do we match the binary file again */ - dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_RAW); - data_bin2 = dfu_firmware_write_data (firmware, &error); - g_assert_no_error (error); - g_assert (data_bin2 != NULL); - ret = fu_common_bytes_compare (data_bin, data_bin2, &error); - g_assert_no_error (error); - g_assert_true (ret); -} - -static void -dfu_firmware_intel_hex_signed_func (void) -{ - DfuElement *element; - DfuImage *image; - GBytes *data_sig; - const guint8 *data; - gboolean ret; - gsize len; - g_autofree gchar *filename_hex = NULL; - g_autoptr(DfuFirmware) firmware = NULL; - g_autoptr(GError) error = NULL; - g_autoptr(GFile) file_hex = NULL; - - /* load a Intel hex32 file */ - filename_hex = dfu_test_get_filename ("firmware.shex"); - g_assert (filename_hex != NULL); - file_hex = g_file_new_for_path (filename_hex); - firmware = dfu_firmware_new (); - ret = dfu_firmware_parse_file (firmware, file_hex, - DFU_FIRMWARE_PARSE_FLAG_NONE, - &error); - g_assert_no_error (error); - g_assert (ret); - g_assert_cmpint (dfu_firmware_get_size (firmware), ==, 144); - - /* get the signed image element */ - image = dfu_firmware_get_image_by_name (firmware, "signature"); - g_assert (image != NULL); - element = dfu_image_get_element_default (image); - data_sig = dfu_element_get_contents (element); - g_assert (data_sig != NULL); - data = g_bytes_get_data (data_sig, &len); - g_assert_cmpint (len, ==, 8); - g_assert (data != NULL); -} - static gchar * dfu_target_sectors_to_string (DfuTarget *target) { @@ -794,7 +591,6 @@ main (int argc, char **argv) g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); /* tests go here */ - g_test_add_func ("/dfu/firmware{srec}", dfu_firmware_srec_func); g_test_add_func ("/dfu/patch", dfu_patch_func); g_test_add_func ("/dfu/patch{merges}", dfu_patch_merges_func); g_test_add_func ("/dfu/patch{apply}", dfu_patch_apply_func); @@ -806,9 +602,6 @@ main (int argc, char **argv) g_test_add_func ("/dfu/firmware{dfuse}", dfu_firmware_dfuse_func); g_test_add_func ("/dfu/firmware{xdfu}", dfu_firmware_xdfu_func); g_test_add_func ("/dfu/firmware{metadata}", dfu_firmware_metadata_func); - g_test_add_func ("/dfu/firmware{intel-hex-offset}", dfu_firmware_intel_hex_offset_func); - g_test_add_func ("/dfu/firmware{intel-hex}", dfu_firmware_intel_hex_func); - g_test_add_func ("/dfu/firmware{intel-hex-signed}", dfu_firmware_intel_hex_signed_func); return g_test_run (); } diff --git a/plugins/dfu/tests/firmware.shex b/plugins/dfu/tests/firmware.shex deleted file mode 100644 index 4de1fa6b2cddf7950aacbc67d9247bf9979d65dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 735 zcmd^6yAAUY|u+CEu-09f~L(*OVf diff --git a/plugins/dfu/tests/firmware.sig b/plugins/dfu/tests/firmware.sig deleted file mode 100644 index e8b5f8233..000000000 --- a/plugins/dfu/tests/firmware.sig +++ /dev/null @@ -1 +0,0 @@ -deadbeef \ No newline at end of file diff --git a/plugins/ebitdo/fu-ebitdo-device.c b/plugins/ebitdo/fu-ebitdo-device.c index 5ebc52a5a..6ee60e2a5 100644 --- a/plugins/ebitdo/fu-ebitdo-device.c +++ b/plugins/ebitdo/fu-ebitdo-device.c @@ -348,7 +348,7 @@ fu_ebitdo_device_get_serial (FuEbitdoDevice *self) static gboolean fu_ebitdo_device_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { @@ -358,6 +358,7 @@ fu_ebitdo_device_write_firmware (FuDevice *device, const guint chunk_sz = 32; guint32 payload_len; guint32 serial_new[3]; + g_autoptr(GBytes) fw = NULL; g_autoptr(GError) error_local = NULL; const guint32 app_key_index[16] = { 0x186976e5, 0xcac67acd, 0x38f27fee, 0x0a4948f1, @@ -408,6 +409,11 @@ fu_ebitdo_device_write_firmware (FuDevice *device, return FALSE; } + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + /* corrupt */ if (g_bytes_get_size (fw) < sizeof (FuEbitdoFirmwareHeader)) { g_set_error_literal (error, diff --git a/plugins/fastboot/fu-fastboot-device.c b/plugins/fastboot/fu-fastboot-device.c index d41b254d7..db9482b19 100644 --- a/plugins/fastboot/fu-fastboot-device.c +++ b/plugins/fastboot/fu-fastboot-device.c @@ -595,11 +595,17 @@ fu_fastboot_device_write_qfil (FuDevice *device, FuArchive* archive, GError **er static gboolean fu_fastboot_device_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { g_autoptr(FuArchive) archive = NULL; + g_autoptr(GBytes) fw = NULL; + + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; /* decompress entire archive ahead of time */ archive = fu_archive_new (fw, FU_ARCHIVE_FLAG_IGNORE_PATH, error); diff --git a/plugins/modem-manager/fu-mm-device.c b/plugins/modem-manager/fu-mm-device.c index a61d638bd..86ccdc0f5 100644 --- a/plugins/modem-manager/fu-mm-device.c +++ b/plugins/modem-manager/fu-mm-device.c @@ -600,15 +600,21 @@ fu_mm_device_write_firmware_qmi_pdc (FuDevice *device, GBytes *fw, GArray **acti static gboolean fu_mm_device_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuMmDevice *self = FU_MM_DEVICE (device); g_autoptr(FuDeviceLocker) locker = NULL; g_autoptr(FuArchive) archive = NULL; + g_autoptr(GBytes) fw = NULL; g_autoptr(GPtrArray) array = NULL; + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + /* lock device */ locker = fu_device_locker_new (device, error); if (locker == NULL) diff --git a/plugins/nvme/fu-nvme-device.c b/plugins/nvme/fu-nvme-device.c index ef3a6b65c..e4b4504be 100644 --- a/plugins/nvme/fu-nvme-device.c +++ b/plugins/nvme/fu-nvme-device.c @@ -395,16 +395,22 @@ fu_nvme_device_close (FuDevice *device, GError **error) static gboolean fu_nvme_device_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuNvmeDevice *self = FU_NVME_DEVICE (device); g_autoptr(GBytes) fw2 = NULL; + g_autoptr(GBytes) fw = NULL; g_autoptr(GPtrArray) chunks = NULL; guint64 block_size = self->write_block_size > 0 ? self->write_block_size : 0x1000; + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + /* some vendors provide firmware files whose sizes are not multiples * of blksz *and* the device won't accept blocks of different sizes */ if (fu_device_has_custom_flag (device, "force-align")) { diff --git a/plugins/rts54hid/fu-rts54hid-device.c b/plugins/rts54hid/fu-rts54hid-device.c index 79b262fa9..66b105e02 100644 --- a/plugins/rts54hid/fu-rts54hid-device.c +++ b/plugins/rts54hid/fu-rts54hid-device.c @@ -315,13 +315,19 @@ fu_rts54hid_device_close (FuUsbDevice *device, GError **error) static gboolean fu_rts54hid_device_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuRts54HidDevice *self = FU_RTS54HID_DEVICE (device); + g_autoptr(GBytes) fw = NULL; g_autoptr(GPtrArray) chunks = NULL; + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + /* set MCU to high clock rate for better ISP performance */ if (!fu_rts54hid_device_set_clock_mode (self, TRUE, error)) return FALSE; diff --git a/plugins/rts54hid/fu-rts54hid-module.c b/plugins/rts54hid/fu-rts54hid-module.c index b8155eaa9..d7e86ee4c 100644 --- a/plugins/rts54hid/fu-rts54hid-module.c +++ b/plugins/rts54hid/fu-rts54hid-module.c @@ -212,13 +212,19 @@ fu_rts54hid_module_close (FuDevice *device, GError **error) static gboolean fu_rts54hid_module_write_firmware (FuDevice *module, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuRts54HidModule *self = FU_RTS54HID_MODULE (module); + g_autoptr(GBytes) fw = NULL; g_autoptr(GPtrArray) chunks = NULL; + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + /* build packets */ chunks = fu_chunk_array_new_from_bytes (fw, 0x00, /* start addr */ diff --git a/plugins/rts54hub/fu-rts54hub-device.c b/plugins/rts54hub/fu-rts54hub-device.c index 75f47b91d..ba0d819ee 100644 --- a/plugins/rts54hub/fu-rts54hub-device.c +++ b/plugins/rts54hub/fu-rts54hub-device.c @@ -309,13 +309,19 @@ fu_rts54hub_device_close (FuUsbDevice *device, GError **error) static gboolean fu_rts54hub_device_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuRts54HubDevice *self = FU_RTS54HUB_DEVICE (device); + g_autoptr(GBytes) fw = NULL; g_autoptr(GPtrArray) chunks = NULL; + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + /* enable vendor commands */ if (!fu_rts54hub_device_vendor_cmd (self, FU_RTS54HUB_VENDOR_CMD_STATUS | @@ -383,7 +389,7 @@ fu_rts54hub_device_write_firmware (FuDevice *device, return TRUE; } -static GBytes * +static FuFirmware * fu_rts54hub_device_prepare_firmware (FuDevice *device, GBytes *fw, FwupdInstallFlags flags, @@ -405,7 +411,7 @@ fu_rts54hub_device_prepare_firmware (FuDevice *device, "firmware needs to be dual bank"); return NULL; } - return g_bytes_ref (fw); + return fu_firmware_new_from_bytes (fw); } static void diff --git a/plugins/solokey/fu-solokey-device.c b/plugins/solokey/fu-solokey-device.c index 0fcb7ca5c..d7759f389 100644 --- a/plugins/solokey/fu-solokey-device.c +++ b/plugins/solokey/fu-solokey-device.c @@ -7,11 +7,10 @@ #include "config.h" #include -#include -#include "dfu-firmware.h" #include "fu-chunk.h" #include "fu-solokey-device.h" +#include "fu-solokey-firmware.h" struct _FuSolokeyDevice { FuUsbDevice parent_instance; @@ -396,30 +395,15 @@ fu_solokey_device_setup (FuDevice *device, GError **error) return TRUE; } -static GByteArray * -_g_base64_decode_to_byte_array (const gchar *text) -{ - gsize out_len = 0; - guchar *out = g_base64_decode (text, &out_len); - return g_byte_array_new_take ((guint8 *) out, out_len); -} - -static GBytes * -_g_base64_decode_to_bytes (const gchar *text) -{ - gsize out_len = 0; - guchar *out = g_base64_decode (text, &out_len); - return g_bytes_new_take ((guint8 *) out, out_len); -} - static gboolean -fu_solokey_device_verify (FuSolokeyDevice *self, const gchar *base64, GError **error) +fu_solokey_device_verify (FuSolokeyDevice *self, GBytes *fw_sig, GError **error) { g_autoptr(GByteArray) req = g_byte_array_new (); g_autoptr(GByteArray) res = NULL; - g_autoptr(GByteArray) sig = _g_base64_decode_to_byte_array (base64); + g_autoptr(GByteArray) sig = g_byte_array_new (); fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_VERIFY); + g_byte_array_append (sig, g_bytes_get_data (fw_sig, NULL), g_bytes_get_size (fw_sig)); fu_solokey_device_exchange (req, SOLO_BOOTLOADER_DONE, 0x00, sig); res = fu_solokey_device_packet (self, SOLO_BOOTLOADER_HID_CMD_BOOT, req, error); if (res == NULL) @@ -427,75 +411,42 @@ fu_solokey_device_verify (FuSolokeyDevice *self, const gchar *base64, GError **e return TRUE; } +static FuFirmware * +fu_solokey_device_prepare_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuFirmware) firmware = fu_solokey_firmware_new (); + fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); + if (!fu_firmware_parse (firmware, fw, flags, error)) + return NULL; + return g_steal_pointer (&firmware); +} + static gboolean fu_solokey_device_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuSolokeyDevice *self = FU_SOLOKEY_DEVICE (device); - DfuElement *element; - DfuImage *image; - JsonNode *json_root; - JsonObject *json_obj; - const gchar *base64; - g_autoptr(DfuFirmware) firmware = dfu_firmware_new (); - g_autoptr(GBytes) fw_ihex = NULL; + g_autoptr(FuFirmwareImage) img = NULL; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GBytes) fw_sig = NULL; g_autoptr(GPtrArray) chunks = NULL; - g_autoptr(GString) base64_websafe = NULL; - g_autoptr(JsonParser) parser = json_parser_new (); - /* parse JSON */ - fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); - if (!json_parser_load_from_data (parser, - (const gchar *) g_bytes_get_data (fw, NULL), - (gssize) g_bytes_get_size (fw), - error)) { - g_prefix_error (error, "firmware not in JSON format: "); + /* get main image */ + img = fu_firmware_get_image_by_id (firmware, NULL, error); + if (img == NULL) return FALSE; - } - json_root = json_parser_get_root (parser); - json_obj = json_node_get_object (json_root); - if (!json_object_has_member (json_obj, "firmware")) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "JSON invalid as has no 'firmware'"); - return FALSE; - } - if (!json_object_has_member (json_obj, "signature")) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "JSON invalid as has no 'signature'"); - return FALSE; - } - - /* decode */ - base64 = json_object_get_string_member (json_obj, "firmware"); - fw_ihex = _g_base64_decode_to_bytes (base64); - if (!dfu_firmware_parse_data (firmware, fw_ihex, DFU_FIRMWARE_PARSE_FLAG_NONE, error)) - return FALSE; - image = dfu_firmware_get_image_default (firmware); - if (image == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "No image data"); - return FALSE; - } - element = dfu_image_get_element_default (image); - if (element == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "No element data"); - return FALSE; - } /* build packets */ - chunks = fu_chunk_array_new_from_bytes (dfu_element_get_contents (element), - dfu_element_get_address (element), + fw = fu_firmware_image_get_bytes (img, error); + if (fw == NULL) + return FALSE; + chunks = fu_chunk_array_new_from_bytes (fw, + fu_firmware_image_get_addr (img), 0x00, /* page_sz */ 2048); @@ -525,11 +476,10 @@ fu_solokey_device_write_firmware (FuDevice *device, } /* verify the signature and reboot back to runtime */ - base64_websafe = g_string_new (json_object_get_string_member (json_obj, "signature")); - fu_common_string_replace (base64_websafe, "-", "+"); - fu_common_string_replace (base64_websafe, "_", "/"); - g_string_append (base64_websafe, "=="); - return fu_solokey_device_verify (self, base64_websafe->str, error); + fw_sig = fu_firmware_get_image_by_id_bytes (firmware, "signature", error); + if (fw_sig == NULL) + return FALSE; + return fu_solokey_device_verify (self, fw_sig, error); } static void @@ -549,6 +499,7 @@ fu_solokey_device_class_init (FuSolokeyDeviceClass *klass) FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); klass_device->write_firmware = fu_solokey_device_write_firmware; + klass_device->prepare_firmware = fu_solokey_device_prepare_firmware; klass_device->setup = fu_solokey_device_setup; klass_usb_device->open = fu_solokey_device_open; klass_usb_device->close = fu_solokey_device_close; diff --git a/plugins/solokey/fu-solokey-firmware.c b/plugins/solokey/fu-solokey-firmware.c new file mode 100644 index 000000000..9fc856699 --- /dev/null +++ b/plugins/solokey/fu-solokey-firmware.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-common.h" +#include "fu-ihex-firmware.h" + +#include "fu-solokey-firmware.h" + +struct _FuSolokeyFirmware { + FuFirmware parent_instance; +}; + +G_DEFINE_TYPE (FuSolokeyFirmware, fu_solokey_firmware, FU_TYPE_FIRMWARE) + +static GBytes * +_g_base64_decode_to_bytes (const gchar *text) +{ + gsize out_len = 0; + guchar *out = g_base64_decode (text, &out_len); + return g_bytes_new_take ((guint8 *) out, out_len); +} + +static gboolean +fu_solokey_firmware_parse (FuFirmware *firmware, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error) +{ + JsonNode *json_root; + JsonObject *json_obj; + const gchar *base64; + g_autoptr(FuFirmware) ihex_firmware = fu_ihex_firmware_new (); + g_autoptr(FuFirmwareImage) img = NULL; + g_autoptr(FuFirmwareImage) img_sig = fu_firmware_image_new (NULL); + g_autoptr(GBytes) fw_ihex = NULL; + g_autoptr(GBytes) fw_sig = NULL; + g_autoptr(GString) base64_websafe = NULL; + g_autoptr(JsonParser) parser = json_parser_new (); + + /* parse JSON */ + if (!json_parser_load_from_data (parser, + (const gchar *) g_bytes_get_data (fw, NULL), + (gssize) g_bytes_get_size (fw), + error)) { + g_prefix_error (error, "firmware not in JSON format: "); + return FALSE; + } + json_root = json_parser_get_root (parser); + json_obj = json_node_get_object (json_root); + if (!json_object_has_member (json_obj, "firmware")) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "JSON invalid as has no 'firmware'"); + return FALSE; + } + if (!json_object_has_member (json_obj, "signature")) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "JSON invalid as has no 'signature'"); + return FALSE; + } + + /* decode */ + base64 = json_object_get_string_member (json_obj, "firmware"); + fw_ihex = _g_base64_decode_to_bytes (base64); + if (!fu_firmware_parse (ihex_firmware, fw_ihex, flags, error)) + return FALSE; + img = fu_firmware_get_image_default (ihex_firmware, error); + if (img == NULL) + return FALSE; + fu_firmware_add_image (firmware, img); + + /* signature */ + base64_websafe = g_string_new (json_object_get_string_member (json_obj, "signature")); + fu_common_string_replace (base64_websafe, "-", "+"); + fu_common_string_replace (base64_websafe, "_", "/"); + g_string_append (base64_websafe, "=="); + fw_sig = _g_base64_decode_to_bytes (base64_websafe->str); + fu_firmware_image_set_bytes (img_sig, fw_sig); + fu_firmware_image_set_id (img_sig, "signature"); + fu_firmware_add_image (firmware, img_sig); + return TRUE; +} + +static void +fu_solokey_firmware_init (FuSolokeyFirmware *self) +{ +} + +static void +fu_solokey_firmware_class_init (FuSolokeyFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass); + klass_firmware->parse = fu_solokey_firmware_parse; +} + +FuFirmware * +fu_solokey_firmware_new (void) +{ + return FU_FIRMWARE (g_object_new (FU_TYPE_SOLOKEY_FIRMWARE, NULL)); +} diff --git a/plugins/solokey/fu-solokey-firmware.h b/plugins/solokey/fu-solokey-firmware.h new file mode 100644 index 000000000..7bf9110a5 --- /dev/null +++ b/plugins/solokey/fu-solokey-firmware.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +G_BEGIN_DECLS + +#define FU_TYPE_SOLOKEY_FIRMWARE (fu_solokey_firmware_get_type ()) +G_DECLARE_FINAL_TYPE (FuSolokeyFirmware, fu_solokey_firmware, FU, SOLOKEY_FIRMWARE, FuFirmware) + +FuFirmware *fu_solokey_firmware_new (void); + +G_END_DECLS diff --git a/plugins/solokey/meson.build b/plugins/solokey/meson.build index 868efa368..b4e3b115a 100644 --- a/plugins/solokey/meson.build +++ b/plugins/solokey/meson.build @@ -10,11 +10,11 @@ shared_module('fu_plugin_solokey', fu_hash, sources : [ 'fu-solokey-device.c', + 'fu-solokey-firmware.c', 'fu-plugin-solokey.c', ], include_directories : [ include_directories('../..'), - include_directories('../dfu'), include_directories('../../src'), include_directories('../../libfwupd'), ], @@ -22,7 +22,6 @@ shared_module('fu_plugin_solokey', install_dir: plugin_dir, link_with : [ libfwupdprivate, - dfu, ], c_args : cargs, dependencies : [ diff --git a/plugins/superio/fu-superio-device.c b/plugins/superio/fu-superio-device.c index 632a19afa..64d4d5292 100644 --- a/plugins/superio/fu-superio-device.c +++ b/plugins/superio/fu-superio-device.c @@ -362,7 +362,7 @@ fu_superio_device_setup (FuDevice *device, GError **error) return TRUE; } -static GBytes * +static FuFirmware * fu_superio_device_prepare_firmware (FuDevice *device, GBytes *fw, FwupdInstallFlags flags, @@ -378,7 +378,7 @@ fu_superio_device_prepare_firmware (FuDevice *device, if (memcmp (&buf[off], sig1, sizeof(sig1)) == 0 && memcmp (&buf[off + 8], sig2, sizeof(sig2)) == 0) { g_debug ("found signature at 0x%04x", (guint) off); - return g_bytes_ref (fw); + return fu_firmware_new_from_bytes (fw); } } g_set_error_literal (error, diff --git a/plugins/superio/fu-superio-it89-device.c b/plugins/superio/fu-superio-it89-device.c index 530df9550..f8d324224 100644 --- a/plugins/superio/fu-superio-it89-device.c +++ b/plugins/superio/fu-superio-it89-device.c @@ -591,13 +591,14 @@ fu_superio_it89_device_get_jedec_id (FuSuperioDevice *self, guint8 *id, GError * static gboolean fu_superio_it89_device_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); guint8 id[4] = { 0x0 }; g_autoptr(GBytes) fw_fixed = NULL; + g_autoptr(GBytes) fw = NULL; g_autoptr(GPtrArray) chunks = NULL; /* check JEDEC ID */ @@ -618,6 +619,11 @@ fu_superio_it89_device_write_firmware (FuDevice *device, if (!fu_superio_it89_device_check_eflash (self, error)) return FALSE; + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + /* disable the mirroring of e-flash */ if (g_getenv ("FWUPD_SUPERIO_DISABLE_MIRROR") != NULL) { fw_fixed = fu_plugin_superio_fix_signature (self, fw, error); diff --git a/plugins/synaptics-prometheus/fu-dump.c b/plugins/synaptics-prometheus/fu-dump.c index 2499c428b..acc2893aa 100644 --- a/plugins/synaptics-prometheus/fu-dump.c +++ b/plugins/synaptics-prometheus/fu-dump.c @@ -14,12 +14,11 @@ fu_dump_parse (const gchar *filename, GError **error) gchar *data = NULL; gsize len = 0; g_autoptr(GBytes) blob = NULL; - g_autoptr(GPtrArray) array = NULL; + g_autoptr(FuFirmware) firmware = fu_synaprom_firmware_new (); if (!g_file_get_contents (filename, &data, &len, error)) return FALSE; blob = g_bytes_new_take (data, len); - array = fu_synaprom_firmware_new (blob, error); - return array != NULL; + return fu_firmware_parse (firmware, blob, 0, error); } static gboolean @@ -28,7 +27,10 @@ fu_dump_generate (const gchar *filename, GError **error) const gchar *data; gsize len = 0; g_autoptr(GBytes) blob = NULL; - blob = fu_synaprom_firmware_generate (); + g_autoptr(FuFirmware) firmware = fu_synaprom_firmware_new (); + blob = fu_firmware_write (firmware, error); + if (blob == NULL) + return FALSE; data = g_bytes_get_data (blob, &len); return g_file_set_contents (filename, data, len, error); } diff --git a/plugins/synaptics-prometheus/fu-self-test.c b/plugins/synaptics-prometheus/fu-self-test.c index 0ab70c925..651a68d00 100644 --- a/plugins/synaptics-prometheus/fu-self-test.c +++ b/plugins/synaptics-prometheus/fu-self-test.c @@ -18,6 +18,7 @@ static void fu_test_synaprom_firmware_func (void) { const guint8 *buf; + gboolean ret; gsize sz = 0; g_autofree gchar *filename = NULL; g_autoptr(FuSynapromDevice) device = fu_synaprom_device_new (NULL); @@ -25,7 +26,8 @@ fu_test_synaprom_firmware_func (void) g_autoptr(GBytes) blob2 = NULL; g_autoptr(GBytes) fw = NULL; g_autoptr(GError) error = NULL; - g_autoptr(GPtrArray) firmware = NULL; + g_autoptr(FuFirmware) firmware2 = NULL; + g_autoptr(FuFirmware) firmware = fu_synaprom_firmware_new (); filename = fu_test_get_filename (TESTDATADIR, "test.pkg"); g_assert_nonnull (filename); @@ -36,22 +38,18 @@ fu_test_synaprom_firmware_func (void) g_assert_cmpint (sz, ==, 294); g_assert_cmpint (buf[0], ==, 0x01); g_assert_cmpint (buf[1], ==, 0x00); - firmware = fu_synaprom_firmware_new (fw, &error); + ret = fu_firmware_parse (firmware, fw, 0, &error); g_assert_no_error (error); - g_assert_nonnull (firmware); + g_assert_true (ret); /* does not exist */ - blob1 = fu_synaprom_firmware_get_bytes_by_tag (firmware, 0, NULL); + blob1 = fu_firmware_get_image_by_id_bytes (firmware, "NotGoingToExist", NULL); g_assert_null (blob1); - blob1 = fu_synaprom_firmware_get_bytes_by_tag (firmware, - FU_SYNAPROM_FIRMWARE_TAG_CFG_HEADER, - NULL); + blob1 = fu_firmware_get_image_by_id_bytes (firmware, "cfg-update-header", NULL); g_assert_null (blob1); /* header needs to exist */ - blob1 = fu_synaprom_firmware_get_bytes_by_tag (firmware, - FU_SYNAPROM_FIRMWARE_TAG_MFW_HEADER, - &error); + blob1 = fu_firmware_get_image_by_id_bytes (firmware, "mfw-update-header", &error); g_assert_no_error (error); g_assert_nonnull (blob1); buf = g_bytes_get_data (blob1, &sz); @@ -64,8 +62,11 @@ fu_test_synaprom_firmware_func (void) /* payload needs to exist */ fu_synaprom_device_set_version (device, 10, 1, 1234); - blob2 = fu_synaprom_device_prepare_fw (FU_DEVICE (device), fw, - FWUPD_INSTALL_FLAG_NONE, &error); + firmware2 = fu_synaprom_device_prepare_fw (FU_DEVICE (device), fw, + FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error (error); + g_assert_nonnull (firmware2); + blob2 = fu_firmware_get_image_by_id_bytes (firmware2, "mfw-update-payload", &error); g_assert_no_error (error); g_assert_nonnull (blob2); buf = g_bytes_get_data (blob2, &sz); diff --git a/plugins/synaptics-prometheus/fu-synaprom-config.c b/plugins/synaptics-prometheus/fu-synaprom-config.c index a9b510047..f0b9c365d 100644 --- a/plugins/synaptics-prometheus/fu-synaprom-config.c +++ b/plugins/synaptics-prometheus/fu-synaprom-config.c @@ -112,7 +112,7 @@ fu_synaprom_config_setup (FuDevice *device, GError **error) return TRUE; } -static GBytes * +static FuFirmware * fu_synaprom_config_prepare_firmware (FuDevice *device, GBytes *fw, FwupdInstallFlags flags, @@ -121,20 +121,17 @@ fu_synaprom_config_prepare_firmware (FuDevice *device, FuSynapromConfig *self = FU_SYNAPROM_CONFIG (device); FuSynapromFirmwareCfgHeader hdr; g_autoptr(GBytes) blob = NULL; - g_autoptr(GPtrArray) firmware = NULL; + g_autoptr(FuFirmware) firmware = fu_synaprom_firmware_new (); guint32 product; guint32 id1; /* parse the firmware */ fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); - firmware = fu_synaprom_firmware_new (fw, error); - if (firmware == NULL) + if (!fu_firmware_parse (firmware, fw, flags, error)) return NULL; /* check the update header product and version */ - blob = fu_synaprom_firmware_get_bytes_by_tag (firmware, - FU_SYNAPROM_FIRMWARE_TAG_CFG_HEADER, - error); + blob = fu_firmware_get_image_by_id_bytes (firmware, "cfg-update-header", error); if (blob == NULL) return NULL; if (g_bytes_get_size (blob) != sizeof(hdr)) { @@ -178,19 +175,24 @@ fu_synaprom_config_prepare_firmware (FuDevice *device, } } - /* get payload */ - return fu_synaprom_firmware_get_bytes_by_tag (firmware, - FU_SYNAPROM_FIRMWARE_TAG_CFG_PAYLOAD, - error); + /* success */ + return g_steal_pointer (&firmware); } static gboolean fu_synaprom_config_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuSynapromConfig *self = FU_SYNAPROM_CONFIG (device); + g_autoptr(GBytes) fw = NULL; + + /* get default image */ + fw = fu_firmware_get_image_by_id_bytes (firmware, "cfg-update-payload", error); + if (fw == NULL) + return FALSE; + /* I assume the CFG/MFW difference is detected in the device...*/ return fu_synaprom_device_write_fw (self->device, fw, error); } diff --git a/plugins/synaptics-prometheus/fu-synaprom-device.c b/plugins/synaptics-prometheus/fu-synaprom-device.c index 298e20a78..74906a874 100644 --- a/plugins/synaptics-prometheus/fu-synaprom-device.c +++ b/plugins/synaptics-prometheus/fu-synaprom-device.c @@ -230,7 +230,7 @@ fu_synaprom_device_cmd_download_chunk (FuSynapromDevice *device, return fu_synaprom_device_cmd_send (device, request, reply, 20000, error); } -GBytes * +FuFirmware * fu_synaprom_device_prepare_fw (FuDevice *device, GBytes *fw, FwupdInstallFlags flags, @@ -240,18 +240,15 @@ fu_synaprom_device_prepare_fw (FuDevice *device, FuSynapromFirmwareMfwHeader hdr; guint32 product; g_autoptr(GBytes) blob = NULL; - g_autoptr(GPtrArray) firmware = NULL; + g_autoptr(FuFirmware) firmware = fu_synaprom_firmware_new (); /* parse the firmware */ fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); - firmware = fu_synaprom_firmware_new (fw, error); - if (firmware == NULL) + if (!fu_firmware_parse (firmware, fw, flags, error)) return NULL; /* check the update header product and version */ - blob = fu_synaprom_firmware_get_bytes_by_tag (firmware, - FU_SYNAPROM_FIRMWARE_TAG_MFW_HEADER, - error); + blob = fu_firmware_get_image_by_id_bytes (firmware, "mfw-update-header", error); if (blob == NULL) return NULL; if (g_bytes_get_size (blob) != sizeof(hdr)) { @@ -296,10 +293,8 @@ fu_synaprom_device_prepare_fw (FuDevice *device, } } - /* get payload */ - return fu_synaprom_firmware_get_bytes_by_tag (firmware, - FU_SYNAPROM_FIRMWARE_TAG_MFW_PAYLOAD, - error); + /* success */ + return g_steal_pointer (&firmware); } gboolean @@ -352,11 +347,18 @@ fu_synaprom_device_write_fw (FuSynapromDevice *self, GBytes *fw, GError **error) static gboolean fu_synaprom_device_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuSynapromDevice *self = FU_SYNAPROM_DEVICE (device); + g_autoptr(GBytes) fw = NULL; + + /* get default image */ + fw = fu_firmware_get_image_by_id_bytes (firmware, "mfw-update-payload", error); + if (fw == NULL) + return FALSE; + return fu_synaprom_device_write_fw (self, fw, error); } diff --git a/plugins/synaptics-prometheus/fu-synaprom-device.h b/plugins/synaptics-prometheus/fu-synaprom-device.h index 2d91b8481..e363715e9 100644 --- a/plugins/synaptics-prometheus/fu-synaprom-device.h +++ b/plugins/synaptics-prometheus/fu-synaprom-device.h @@ -37,7 +37,7 @@ void fu_synaprom_device_set_version (FuSynapromDevice *self, guint8 vmajor, guint8 vminor, guint32 buildnum); -GBytes *fu_synaprom_device_prepare_fw (FuDevice *device, +FuFirmware *fu_synaprom_device_prepare_fw (FuDevice *device, GBytes *fw, FwupdInstallFlags flags, GError **error); diff --git a/plugins/synaptics-prometheus/fu-synaprom-firmware.c b/plugins/synaptics-prometheus/fu-synaprom-firmware.c index 0f219de0b..ac0b0603d 100644 --- a/plugins/synaptics-prometheus/fu-synaprom-firmware.c +++ b/plugins/synaptics-prometheus/fu-synaprom-firmware.c @@ -11,27 +11,21 @@ #include "fu-synaprom-firmware.h" +struct _FuSynapromFirmware { + FuFirmware parent_instance; +}; + +G_DEFINE_TYPE (FuSynapromFirmware, fu_synaprom_firmware, FU_TYPE_FIRMWARE) + typedef struct __attribute__((packed)) { guint16 tag; guint32 bufsz; } FuSynapromFirmwareHdr; -typedef struct { - guint16 tag; - GBytes *bytes; -} FuSynapromFirmwareItem; - /* use only first 12 bit of 16 bits as tag value */ #define FU_SYNAPROM_FIRMWARE_TAG_MAX 0xfff0 #define FU_SYNAPROM_FIRMWARE_SIGSIZE 0x0100 -static void -fu_synaprom_firmware_item_free (FuSynapromFirmwareItem *item) -{ - g_bytes_unref (item->bytes); - g_free (item); -} - static const gchar * fu_synaprom_firmware_tag_to_string (guint16 tag) { @@ -46,18 +40,21 @@ fu_synaprom_firmware_tag_to_string (guint16 tag) return NULL; } -GPtrArray * -fu_synaprom_firmware_new (GBytes *blob, GError **error) +static gboolean +fu_synaprom_firmware_parse (FuFirmware *firmware, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error) { const guint8 *buf; gsize bufsz = 0; gsize offset = 0; - g_autoptr(GPtrArray) firmware = NULL; - g_return_val_if_fail (blob != NULL, NULL); + g_return_val_if_fail (fw != NULL, FALSE); - firmware = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_synaprom_firmware_item_free); - buf = g_bytes_get_data (blob, &bufsz); + buf = g_bytes_get_data (fw, &bufsz); /* 256 byte signature as footer */ if (bufsz < FU_SYNAPROM_FIRMWARE_SIGSIZE + sizeof(FuSynapromFirmwareHdr)) { @@ -65,7 +62,7 @@ fu_synaprom_firmware_new (GBytes *blob, GError **error) G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "blob is too small to be firmware"); - return NULL; + return FALSE; } bufsz -= FU_SYNAPROM_FIRMWARE_SIGSIZE; @@ -73,19 +70,20 @@ fu_synaprom_firmware_new (GBytes *blob, GError **error) while (offset != bufsz) { FuSynapromFirmwareHdr header; guint32 hdrsz; - g_autofree FuSynapromFirmwareItem *item = NULL; + guint32 tag; + g_autoptr(GBytes) bytes = NULL; + g_autoptr(FuFirmwareImage) img = NULL; /* verify item header */ memcpy (&header, buf, sizeof(header)); - item = g_new0 (FuSynapromFirmwareItem, 1); - item->tag = GUINT16_FROM_LE(header.tag); - if (item->tag >= FU_SYNAPROM_FIRMWARE_TAG_MAX) { + tag = GUINT16_FROM_LE(header.tag); + if (tag >= FU_SYNAPROM_FIRMWARE_TAG_MAX) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "tag 0x%04x is too large", - item->tag); - return NULL; + tag); + return FALSE; } hdrsz = GUINT32_FROM_LE(header.bufsz); offset += sizeof(header) + hdrsz; @@ -95,41 +93,29 @@ fu_synaprom_firmware_new (GBytes *blob, GError **error) G_IO_ERROR_INVALID_DATA, "data is corrupted 0x%04x > 0x%04x", (guint) offset, (guint) bufsz); - return NULL; + return FALSE; } /* move pointer to data */ buf += sizeof(header); - item->bytes = g_bytes_new (buf, hdrsz); + bytes = g_bytes_new (buf, hdrsz); g_debug ("adding 0x%04x (%s) with size 0x%04x", - item->tag, - fu_synaprom_firmware_tag_to_string (item->tag), + tag, + fu_synaprom_firmware_tag_to_string (tag), hdrsz); - g_ptr_array_add (firmware, g_steal_pointer (&item)); + img = fu_firmware_image_new (bytes); + fu_firmware_image_set_idx (img, tag); + fu_firmware_image_set_id (img, fu_synaprom_firmware_tag_to_string (tag)); + fu_firmware_add_image (firmware, img); /* next item */ buf += hdrsz; } - return g_steal_pointer (&firmware); + return TRUE; } -GBytes * -fu_synaprom_firmware_get_bytes_by_tag (GPtrArray *firmware, guint16 tag, GError **error) -{ - for (guint i = 0; i < firmware->len; i++) { - FuSynapromFirmwareItem *item = g_ptr_array_index (firmware, i); - if (item->tag == tag) - return g_bytes_ref (item->bytes); - } - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - "no item with tag 0x%04x", tag); - return NULL; -} - -GBytes * -fu_synaprom_firmware_generate (void) +static GBytes * +fu_synaprom_firmware_write (FuFirmware *self, GError **error) { GByteArray *blob = g_byte_array_new (); const guint8 data[] = { 'R', 'H' }; @@ -163,3 +149,22 @@ fu_synaprom_firmware_generate (void) } return g_byte_array_free_to_bytes (blob); } + +static void +fu_synaprom_firmware_init (FuSynapromFirmware *self) +{ +} + +static void +fu_synaprom_firmware_class_init (FuSynapromFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass); + klass_firmware->parse = fu_synaprom_firmware_parse; + klass_firmware->write = fu_synaprom_firmware_write; +} + +FuFirmware * +fu_synaprom_firmware_new (void) +{ + return FU_FIRMWARE (g_object_new (FU_TYPE_SYNAPROM_FIRMWARE, NULL)); +} diff --git a/plugins/synaptics-prometheus/fu-synaprom-firmware.h b/plugins/synaptics-prometheus/fu-synaprom-firmware.h index aa33974f1..96a8f80ec 100644 --- a/plugins/synaptics-prometheus/fu-synaprom-firmware.h +++ b/plugins/synaptics-prometheus/fu-synaprom-firmware.h @@ -7,7 +7,12 @@ #pragma once -#include +#include "fu-firmware.h" + +G_BEGIN_DECLS + +#define FU_TYPE_SYNAPROM_FIRMWARE (fu_synaprom_firmware_get_type ()) +G_DECLARE_FINAL_TYPE (FuSynapromFirmware, fu_synaprom_firmware, FU, SYNAPROM_FIRMWARE, FuFirmware) #define FU_SYNAPROM_FIRMWARE_TAG_MFW_HEADER 0x0001 #define FU_SYNAPROM_FIRMWARE_TAG_MFW_PAYLOAD 0x0002 @@ -34,9 +39,6 @@ typedef struct __attribute__((packed)) { guint8 unused[2]; } FuSynapromFirmwareCfgHeader; -GPtrArray *fu_synaprom_firmware_new (GBytes *blob, - GError **error); -GBytes *fu_synaprom_firmware_get_bytes_by_tag (GPtrArray *firmware, - guint16 tag, - GError **error); -GBytes *fu_synaprom_firmware_generate (void); +FuFirmware *fu_synaprom_firmware_new (void); + +G_END_DECLS diff --git a/plugins/synaptics-prometheus/meson.build b/plugins/synaptics-prometheus/meson.build index e819d266a..8fc887def 100644 --- a/plugins/synaptics-prometheus/meson.build +++ b/plugins/synaptics-prometheus/meson.build @@ -66,10 +66,15 @@ if get_option('tests') ], include_directories : [ include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), ], dependencies : [ gio, ], + link_with : [ + libfwupdprivate, + ], c_args : cargs ) endif diff --git a/plugins/uefi/fu-uefi-device.c b/plugins/uefi/fu-uefi-device.c index 1d50c5123..ab5d96de8 100644 --- a/plugins/uefi/fu-uefi-device.c +++ b/plugins/uefi/fu-uefi-device.c @@ -401,7 +401,7 @@ fu_uefi_device_write_update_info (FuUefiDevice *self, static gboolean fu_uefi_device_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags install_flags, GError **error) { @@ -411,6 +411,7 @@ fu_uefi_device_write_firmware (FuDevice *device, const gchar *esp_path = fu_device_get_metadata (device, "EspPath"); efi_guid_t guid; g_autoptr(GBytes) fixed_fw = NULL; + g_autoptr(GBytes) fw = NULL; g_autofree gchar *basename = NULL; g_autofree gchar *directory = NULL; g_autofree gchar *fn = NULL; @@ -425,6 +426,11 @@ fu_uefi_device_write_firmware (FuDevice *device, return FALSE; } + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + /* save the blob to the ESP */ directory = fu_uefi_get_esp_path_for_os (esp_path); basename = g_strdup_printf ("fwupd-%s.cap", self->fw_class); diff --git a/plugins/unifying/fu-unifying-bootloader-nordic.c b/plugins/unifying/fu-unifying-bootloader-nordic.c index e933708f6..bbf58b889 100644 --- a/plugins/unifying/fu-unifying-bootloader-nordic.c +++ b/plugins/unifying/fu-unifying-bootloader-nordic.c @@ -196,15 +196,21 @@ fu_unifying_bootloader_nordic_erase (FuUnifyingBootloader *self, guint16 addr, G static gboolean fu_unifying_bootloader_nordic_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device); const FuUnifyingBootloaderRequest *payload; guint16 addr; + g_autoptr(GBytes) fw = NULL; g_autoptr(GPtrArray) reqs = NULL; + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + /* erase firmware pages up to the bootloader */ fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); for (addr = fu_unifying_bootloader_get_addr_lo (self); diff --git a/plugins/unifying/fu-unifying-bootloader-texas.c b/plugins/unifying/fu-unifying-bootloader-texas.c index e4ba61c43..a0e5c7d1b 100644 --- a/plugins/unifying/fu-unifying-bootloader-texas.c +++ b/plugins/unifying/fu-unifying-bootloader-texas.c @@ -109,15 +109,21 @@ fu_unifying_bootloader_texas_clear_ram_buffer (FuUnifyingBootloader *self, GErro static gboolean fu_unifying_bootloader_texas_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device); const FuUnifyingBootloaderRequest *payload; + g_autoptr(GBytes) fw = NULL; g_autoptr(GPtrArray) reqs = NULL; g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + /* transfer payload */ reqs = fu_unifying_bootloader_parse_requests (self, fw, error); if (reqs == NULL) diff --git a/plugins/unifying/fu-unifying-peripheral.c b/plugins/unifying/fu-unifying-peripheral.c index 4d98ed7ef..e63eaede0 100644 --- a/plugins/unifying/fu-unifying-peripheral.c +++ b/plugins/unifying/fu-unifying-peripheral.c @@ -882,7 +882,7 @@ fu_unifying_peripheral_write_firmware_pkt (FuUnifyingPeripheral *self, static gboolean fu_unifying_peripheral_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { @@ -891,6 +891,7 @@ fu_unifying_peripheral_write_firmware (FuDevice *device, const guint8 *data; guint8 cmd = 0x04; guint8 idx; + g_autoptr(GBytes) fw = NULL; /* if we're in bootloader mode, we should be able to get this feature */ idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU); @@ -902,6 +903,11 @@ fu_unifying_peripheral_write_firmware (FuDevice *device, return FALSE; } + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; + /* flash hardware */ data = g_bytes_get_data (fw, &sz); fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); diff --git a/plugins/wacom-raw/fu-wacom-device.c b/plugins/wacom-raw/fu-wacom-device.c index 7f66d0c22..aa09103a8 100644 --- a/plugins/wacom-raw/fu-wacom-device.c +++ b/plugins/wacom-raw/fu-wacom-device.c @@ -14,9 +14,9 @@ #include #include "fu-chunk.h" +#include "fu-ihex-firmware.h" #include "fu-wacom-common.h" #include "fu-wacom-device.h" -#include "dfu-firmware.h" typedef struct { @@ -216,69 +216,56 @@ fu_wacom_device_set_version_bootloader (FuWacomDevice *self, GError **error) return TRUE; } +static FuFirmware * +fu_wacom_device_prepare_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new (); + if (!fu_firmware_parse (firmware, fw, flags, error)) + return NULL; + return g_steal_pointer (&firmware); +} + static gboolean fu_wacom_device_write_firmware (FuDevice *device, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { FuWacomDevice *self = FU_WACOM_DEVICE (device); FuWacomDevicePrivate *priv = GET_PRIVATE (self); FuWacomDeviceClass *klass = FU_WACOM_DEVICE_GET_CLASS (device); - DfuElement *element; - DfuImage *image; - GBytes *fw_new; - g_autoptr(DfuFirmware) firmware = dfu_firmware_new (); + g_autoptr(FuFirmwareImage) img = NULL; + g_autoptr(GBytes) fw = NULL; g_autoptr(GPtrArray) chunks = NULL; - /* parse hex file */ - if (!dfu_firmware_parse_data (firmware, fw, DFU_FIRMWARE_PARSE_FLAG_NONE, error)) - return FALSE; - if (dfu_firmware_get_format (firmware) != DFU_FIRMWARE_FORMAT_INTEL_HEX) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "expected firmware format is 'ihex', got '%s'", - dfu_firmware_format_to_string (dfu_firmware_get_format (firmware))); - return FALSE; - } - /* use the correct image from the firmware */ - image = dfu_firmware_get_image_default (firmware); - if (image == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "no firmware image"); + img = fu_firmware_get_image_default (firmware, error); + if (img == NULL) return FALSE; - } - element = dfu_image_get_element_default (image); - if (element == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "no element in image"); - return FALSE; - } g_debug ("using element at addr 0x%0x", - (guint) dfu_element_get_address (element)); + (guint) fu_firmware_image_get_addr (img)); /* check start address and size */ - if (dfu_element_get_address (element) != priv->flash_base_addr) { + if (fu_firmware_image_get_addr (img) != priv->flash_base_addr) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "base addr invalid: 0x%05x", - (guint) dfu_element_get_address (element)); + (guint) fu_firmware_image_get_addr (img)); return FALSE; } - fw_new = dfu_element_get_contents (element); - if (g_bytes_get_size (fw_new) > priv->flash_size) { + fw = fu_firmware_image_get_bytes (img, error); + if (fw == NULL) + return FALSE; + if (g_bytes_get_size (fw) > priv->flash_size) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "size is invalid: 0x%05x", - (guint) g_bytes_get_size (fw_new)); + (guint) g_bytes_get_size (fw)); return FALSE; } @@ -289,7 +276,7 @@ fu_wacom_device_write_firmware (FuDevice *device, return FALSE; /* flash chunks */ - chunks = fu_chunk_array_new_from_bytes (fw_new, priv->flash_base_addr, + chunks = fu_chunk_array_new_from_bytes (fw, priv->flash_base_addr, 0x00, /* page_sz */ priv->flash_block_size); return klass->write_firmware (device, chunks, error); @@ -415,6 +402,7 @@ fu_wacom_device_class_init (FuWacomDeviceClass *klass) klass_device->to_string = fu_wacom_device_to_string; klass_device->open = fu_wacom_device_open; klass_device->close = fu_wacom_device_close; + klass_device->prepare_firmware = fu_wacom_device_prepare_firmware; klass_device->write_firmware = fu_wacom_device_write_firmware; klass_device->attach = fu_wacom_device_attach; klass_device->detach = fu_wacom_device_detach; diff --git a/plugins/wacom-raw/fu-wacom-device.h b/plugins/wacom-raw/fu-wacom-device.h index 0959eede7..ff0948c71 100644 --- a/plugins/wacom-raw/fu-wacom-device.h +++ b/plugins/wacom-raw/fu-wacom-device.h @@ -8,7 +8,6 @@ #include "fu-wacom-common.h" #include "fu-udev-device.h" -#include "dfu-element.h" G_BEGIN_DECLS diff --git a/plugins/wacom-raw/meson.build b/plugins/wacom-raw/meson.build index ec0b63f70..1a27f7c4d 100644 --- a/plugins/wacom-raw/meson.build +++ b/plugins/wacom-raw/meson.build @@ -15,7 +15,6 @@ shared_module('fu_plugin_wacom_raw', ], include_directories : [ include_directories('../..'), - include_directories('../dfu'), include_directories('../../src'), include_directories('../../libfwupd'), ], @@ -26,6 +25,6 @@ shared_module('fu_plugin_wacom_raw', plugin_deps, ], link_with : [ - dfu, + libfwupdprivate, ], ) diff --git a/plugins/wacom-usb/fu-self-test.c b/plugins/wacom-usb/fu-self-test.c index 2e271baa9..25926fb3c 100644 --- a/plugins/wacom-usb/fu-self-test.c +++ b/plugins/wacom-usb/fu-self-test.c @@ -19,11 +19,10 @@ static void fu_wac_firmware_parse_func (void) { - DfuElement *element; - DfuImage *image; gboolean ret; g_autofree gchar *fn = NULL; - g_autoptr(DfuFirmware) firmware = dfu_firmware_new (); + g_autoptr(FuFirmware) firmware = fu_wac_firmware_new (); + g_autoptr(FuFirmwareImage) img = NULL; g_autoptr(GBytes) blob_block = NULL; g_autoptr(GBytes) bytes = NULL; g_autoptr(GError) error = NULL; @@ -37,20 +36,17 @@ fu_wac_firmware_parse_func (void) bytes = fu_common_get_contents_bytes (fn, &error); g_assert_no_error (error); g_assert_nonnull (bytes); - ret = fu_wac_firmware_parse_data (firmware, bytes, - DFU_FIRMWARE_PARSE_FLAG_NONE, &error); + ret = fu_firmware_parse (firmware, bytes, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error (error); g_assert_true (ret); /* get image data */ - image = dfu_firmware_get_image (firmware, 0); - g_assert_nonnull (image); - element = dfu_image_get_element_default (image); - g_assert_nonnull (element); + img = fu_firmware_get_image_default (firmware, &error); + g_assert_no_error (error); + g_assert_nonnull (img); /* get block */ - blob_block = dfu_element_get_contents_chunk (element, 0x8008000, - 1024, &error); + blob_block = fu_firmware_image_get_bytes_chunk (img, 0x8008000, 1024, &error); g_assert_no_error (error); g_assert_nonnull (blob_block); fu_wac_buffer_dump ("IMG", FU_WAC_REPORT_ID_MODULE, diff --git a/plugins/wacom-usb/fu-wac-device.c b/plugins/wacom-usb/fu-wac-device.c index b698c4ce6..8f1556582 100644 --- a/plugins/wacom-usb/fu-wac-device.c +++ b/plugins/wacom-usb/fu-wac-device.c @@ -15,9 +15,6 @@ #include "fu-wac-module-bluetooth.h" #include "fu-wac-module-touch.h" -#include "dfu-common.h" -#include "dfu-firmware.h" - typedef struct __attribute__((packed)) { guint32 start_addr; guint32 block_sz; @@ -481,34 +478,37 @@ fu_wac_device_switch_to_flash_loader (FuWacDevice *self, GError **error) error); } +static FuFirmware * +fu_wac_device_prepare_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuFirmware) firmware = fu_wac_firmware_new (); + fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); + if (!fu_firmware_parse (firmware, fw, flags, error)) + return NULL; + return g_steal_pointer (&firmware); +} + static gboolean fu_wac_device_write_firmware (FuDevice *device, - GBytes *blob, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { - DfuElement *element; - DfuImage *image; FuWacDevice *self = FU_WAC_DEVICE (device); gsize blocks_done = 0; gsize blocks_total = 0; - g_autoptr(DfuFirmware) firmware = dfu_firmware_new (); - g_autoptr(GHashTable) fd_blobs = NULL; g_autofree guint32 *csum_local = NULL; + g_autoptr(FuFirmwareImage) img = NULL; + g_autoptr(GHashTable) fd_blobs = NULL; - /* load .wac file, including metadata */ - if (!fu_wac_firmware_parse_data (firmware, blob, - DFU_FIRMWARE_PARSE_FLAG_NONE, - error)) + /* use the correct image from the firmware */ + img = fu_firmware_get_image_by_idx (firmware, self->firmware_index == 1 ? 1 : 0, error); + if (img == NULL) return FALSE; - if (dfu_firmware_get_format (firmware) != DFU_FIRMWARE_FORMAT_SREC) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "expected firmware format is 'srec', got '%s'", - dfu_firmware_format_to_string (dfu_firmware_get_format (firmware))); - return FALSE; - } + g_debug ("using image at addr 0x%0x", (guint) fu_firmware_image_get_addr (img)); /* enter flash mode */ if (!fu_wac_device_switch_to_flash_loader (self, error)) @@ -518,28 +518,6 @@ fu_wac_device_write_firmware (FuDevice *device, if (!fu_wac_device_ensure_firmware_index (self, error)) return FALSE; - /* use the correct image from the firmware */ - image = dfu_firmware_get_image (firmware, self->firmware_index == 1 ? 1 : 0); - if (image == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "no firmware image for index %" G_GUINT16_FORMAT, - self->firmware_index); - return FALSE; - } - element = dfu_image_get_element_default (image); - if (element == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "no element in image %" G_GUINT16_FORMAT, - self->firmware_index); - return FALSE; - } - g_debug ("using element at addr 0x%0x", - (guint) dfu_element_get_address (element)); - /* get firmware parameters (page sz and transfer sz) */ if (!fu_wac_device_ensure_parameters (self, error)) return FALSE; @@ -572,13 +550,13 @@ fu_wac_device_write_firmware (FuDevice *device, if (fu_wav_device_flash_descriptor_is_wp (fd)) continue; - blob_tmp = dfu_element_get_contents_chunk (element, - fd->start_addr, - fd->block_sz, - NULL); + blob_tmp = fu_firmware_image_get_bytes_chunk (img, + fd->start_addr, + fd->block_sz, + NULL); if (blob_tmp == NULL) break; - blob_block = dfu_utils_bytes_pad (blob_tmp, fd->block_sz); + blob_block = fu_common_bytes_pad (blob_tmp, fd->block_sz); g_hash_table_insert (fd_blobs, fd, blob_block); } @@ -898,6 +876,7 @@ fu_wac_device_class_init (FuWacDeviceClass *klass) FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); object_class->finalize = fu_wac_device_finalize; + klass_device->prepare_firmware = fu_wac_device_prepare_firmware; klass_device->write_firmware = fu_wac_device_write_firmware; klass_device->to_string = fu_wac_device_to_string; klass_device->setup = fu_wac_device_setup; diff --git a/plugins/wacom-usb/fu-wac-firmware.c b/plugins/wacom-usb/fu-wac-firmware.c index e9b35673d..15e94a55f 100644 --- a/plugins/wacom-usb/fu-wac-firmware.c +++ b/plugins/wacom-usb/fu-wac-firmware.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2018-2019 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ @@ -8,47 +8,42 @@ #include -#include "dfu-element.h" -#include "dfu-format-srec.h" -#include "dfu-image.h" - +#include "fu-common.h" +#include "fu-srec-firmware.h" +#include "fu-firmware-common.h" #include "fu-wac-firmware.h" #include "fwupd-error.h" +struct _FuWacFirmware { + FuFirmware parent_instance; +}; + +G_DEFINE_TYPE (FuWacFirmware, fu_wac_firmware, FU_TYPE_FIRMWARE) + typedef struct { guint32 addr; guint32 sz; guint32 prog_start_addr; -} DfuFirmwareWacHeaderRecord; +} FuFirmwareWacHeaderRecord; -/** - * fu_wac_firmware_parse_data: - * @firmware: a #DfuFirmware - * @bytes: data to parse - * @flags: some #DfuFirmwareParseFlags - * @error: a #GError, or %NULL - * - * Unpacks into a firmware object from DfuSe data. - * - * Returns: %TRUE for success - **/ -gboolean -fu_wac_firmware_parse_data (DfuFirmware *firmware, - GBytes *bytes, - DfuFirmwareParseFlags flags, - GError **error) +static gboolean +fu_wac_firmware_parse (FuFirmware *firmware, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error) { gsize len; guint8 *data; - g_auto(GStrv) lines = NULL; - g_autoptr(GString) image_buffer = NULL; - g_autofree gchar *data_str = NULL; guint8 images_cnt = 0; + g_auto(GStrv) lines = NULL; g_autoptr(GPtrArray) header_infos = g_ptr_array_new_with_free_func (g_free); + g_autoptr(GString) image_buffer = NULL; /* check the prefix (BE) */ - data = (guint8 *) g_bytes_get_data (bytes, &len); + data = (guint8 *) g_bytes_get_data (fw, &len); if (memcmp (data, "WACOM", 5) != 0) { g_set_error_literal (error, FWUPD_ERROR, @@ -58,8 +53,7 @@ fu_wac_firmware_parse_data (DfuFirmware *firmware, } /* parse each line */ - data_str = g_strndup ((const gchar *) data, len); - lines = g_strsplit (data_str, "\n", -1); + lines = fu_common_strnsplit ((const gchar *) data, len, "\n", -1); for (guint i = 0; lines[i] != NULL; i++) { g_autofree gchar *cmd = g_strndup (lines[i], 2); @@ -81,11 +75,11 @@ fu_wac_firmware_parse_data (DfuFirmware *firmware, cmdlen); return FALSE; } - header_image_cnt = dfu_utils_buffer_parse_uint4 (lines[i] + 5); + header_image_cnt = fu_firmware_strparse_uint4 (lines[i] + 5); for (guint j = 0; j < header_image_cnt; j++) { - DfuFirmwareWacHeaderRecord *hdr = g_new0 (DfuFirmwareWacHeaderRecord, 1); - hdr->addr = dfu_utils_buffer_parse_uint32 (lines[i] + (j * 16) + 6); - hdr->sz = dfu_utils_buffer_parse_uint32 (lines[i] + (j * 16) + 14); + FuFirmwareWacHeaderRecord *hdr = g_new0 (FuFirmwareWacHeaderRecord, 1); + hdr->addr = fu_firmware_strparse_uint32 (lines[i] + (j * 16) + 6); + hdr->sz = fu_firmware_strparse_uint32 (lines[i] + (j * 16) + 14); g_ptr_array_add (header_infos, hdr); g_debug ("header_fw%u_addr: 0x%x", j, hdr->addr); g_debug ("header_fw%u_sz: 0x%x", j, hdr->sz); @@ -95,8 +89,8 @@ fu_wac_firmware_parse_data (DfuFirmware *firmware, /* firmware headline record */ if (cmdlen == 13) { - DfuFirmwareWacHeaderRecord *hdr; - guint8 idx = dfu_utils_buffer_parse_uint4 (lines[i] + 2); + FuFirmwareWacHeaderRecord *hdr; + guint8 idx = fu_firmware_strparse_uint4 (lines[i] + 2); if (idx == 0) { g_set_error (error, FWUPD_ERROR, @@ -114,7 +108,7 @@ fu_wac_firmware_parse_data (DfuFirmware *firmware, return FALSE; } hdr = g_ptr_array_index (header_infos, idx - 1); - hdr->prog_start_addr = dfu_utils_buffer_parse_uint32 (lines[i] + 3); + hdr->prog_start_addr = fu_firmware_strparse_uint32 (lines[i] + 3); if (hdr->prog_start_addr != hdr->addr) { g_set_error (error, FWUPD_ERROR, @@ -168,8 +162,9 @@ fu_wac_firmware_parse_data (DfuFirmware *firmware, /* end */ if (g_strcmp0 (cmd, "S7") == 0) { g_autoptr(GBytes) blob = NULL; - g_autoptr(DfuImage) image = dfu_image_new (); - DfuFirmwareWacHeaderRecord *hdr; + g_autoptr(FuFirmware) firmware_srec = fu_srec_firmware_new (); + g_autoptr(FuFirmwareImage) img = NULL; + FuFirmwareWacHeaderRecord *hdr; /* get the correct relocated start address */ if (images_cnt >= header_infos->len) { @@ -191,12 +186,13 @@ fu_wac_firmware_parse_data (DfuFirmware *firmware, /* parse SREC file and add as image */ blob = g_bytes_new (image_buffer->str, image_buffer->len); - if (!dfu_image_from_srec (image, blob, hdr->addr, flags, error)) + if (!fu_firmware_parse_full (firmware_srec, blob, hdr->addr, 0x0, flags, error)) return FALSE; - - /* the alt-setting is used for the firmware index */ - dfu_image_set_alt_setting (image, images_cnt); - dfu_firmware_add_image (firmware, image); + img = fu_firmware_get_image_default (firmware_srec, error); + if (img == NULL) + return FALSE; + fu_firmware_image_set_idx (img, images_cnt); + fu_firmware_add_image (firmware, img); images_cnt++; /* clear the image buffer */ @@ -226,6 +222,23 @@ fu_wac_firmware_parse_data (DfuFirmware *firmware, } /* success */ - dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_SREC); return TRUE; } + +static void +fu_wac_firmware_init (FuWacFirmware *self) +{ +} + +static void +fu_wac_firmware_class_init (FuWacFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass); + klass_firmware->parse = fu_wac_firmware_parse; +} + +FuFirmware * +fu_wac_firmware_new (void) +{ + return FU_FIRMWARE (g_object_new (FU_TYPE_WAC_FIRMWARE, NULL)); +} diff --git a/plugins/wacom-usb/fu-wac-firmware.h b/plugins/wacom-usb/fu-wac-firmware.h index 29221a44e..03d8c2b3b 100644 --- a/plugins/wacom-usb/fu-wac-firmware.h +++ b/plugins/wacom-usb/fu-wac-firmware.h @@ -1,20 +1,18 @@ /* - * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2018-2019 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -#include - -#include "dfu-firmware.h" +#include "fu-firmware.h" G_BEGIN_DECLS -gboolean fu_wac_firmware_parse_data (DfuFirmware *firmware, - GBytes *bytes, - DfuFirmwareParseFlags flags, - GError **error); +#define FU_TYPE_WAC_FIRMWARE (fu_wac_firmware_get_type ()) +G_DECLARE_FINAL_TYPE (FuWacFirmware, fu_wac_firmware, FU, WAC_FIRMWARE, FuFirmware) + +FuFirmware *fu_wac_firmware_new (void); G_END_DECLS diff --git a/plugins/wacom-usb/fu-wac-module-bluetooth.c b/plugins/wacom-usb/fu-wac-module-bluetooth.c index f8a2c2de5..d4d07b673 100644 --- a/plugins/wacom-usb/fu-wac-module-bluetooth.c +++ b/plugins/wacom-usb/fu-wac-module-bluetooth.c @@ -106,7 +106,7 @@ fu_wac_module_bluetooth_parse_blocks (const guint8 *data, gsize sz, gboolean ski static gboolean fu_wac_module_bluetooth_write_firmware (FuDevice *device, - GBytes *blob, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { @@ -117,9 +117,15 @@ fu_wac_module_bluetooth_write_firmware (FuDevice *device, const guint8 buf_start[] = { 0x00 }; g_autoptr(GPtrArray) blocks = NULL; g_autoptr(GBytes) blob_start = g_bytes_new_static (buf_start, 1); + g_autoptr(GBytes) fw = NULL; + + /* get default image */ + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; /* build each data packet */ - data = g_bytes_get_data (blob, &len); + data = g_bytes_get_data (fw, &len); blocks = fu_wac_module_bluetooth_parse_blocks (data, len, TRUE); blocks_total = blocks->len + 2; diff --git a/plugins/wacom-usb/fu-wac-module-touch.c b/plugins/wacom-usb/fu-wac-module-touch.c index e39e288dc..f9fd494d7 100644 --- a/plugins/wacom-usb/fu-wac-module-touch.c +++ b/plugins/wacom-usb/fu-wac-module-touch.c @@ -12,7 +12,7 @@ #include "fu-wac-module-touch.h" #include "fu-chunk.h" -#include "dfu-firmware.h" +#include "fu-ihex-firmware.h" struct _FuWacModuleTouch { @@ -21,57 +21,43 @@ struct _FuWacModuleTouch G_DEFINE_TYPE (FuWacModuleTouch, fu_wac_module_touch, FU_TYPE_WAC_MODULE) +static FuFirmware * +fu_wac_module_touch_prepare_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new (); + if (!fu_firmware_parse (firmware, fw, flags, error)) + return NULL; + return g_steal_pointer (&firmware); +} + static gboolean fu_wac_module_touch_write_firmware (FuDevice *device, - GBytes *blob, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { - DfuElement *element; - DfuImage *image; FuWacModule *self = FU_WAC_MODULE (device); gsize blocks_total = 0; + g_autoptr(FuFirmwareImage) img = NULL; + g_autoptr(GBytes) fw = NULL; g_autoptr(GPtrArray) chunks = NULL; - g_autoptr(DfuFirmware) firmware = dfu_firmware_new (); - - /* load .hex file */ - if (!dfu_firmware_parse_data (firmware, blob, DFU_FIRMWARE_PARSE_FLAG_NONE, error)) - return FALSE; - - /* check type */ - if (dfu_firmware_get_format (firmware) != DFU_FIRMWARE_FORMAT_INTEL_HEX) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "expected firmware format is 'ihex', got '%s'", - dfu_firmware_format_to_string (dfu_firmware_get_format (firmware))); - return FALSE; - } /* use the correct image from the firmware */ - image = dfu_firmware_get_image (firmware, 0); - if (image == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "no firmware image"); + img = fu_firmware_get_image_default (firmware, error); + if (img == NULL) return FALSE; - } - element = dfu_image_get_element_default (image); - if (element == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "no firmware element"); - return FALSE; - } g_debug ("using element at addr 0x%0x", - (guint) dfu_element_get_address (element)); - blob = dfu_element_get_contents (element); + (guint) fu_firmware_image_get_addr (img)); + fw = fu_firmware_image_get_bytes (img, error); + if (fw == NULL) + return FALSE; /* build each data packet */ - chunks = fu_chunk_array_new_from_bytes (blob, - dfu_element_get_address (element), + chunks = fu_chunk_array_new_from_bytes (fw, + fu_firmware_image_get_addr (img), 0x0, /* page_sz */ 128); /* packet_sz */ blocks_total = chunks->len + 2; @@ -129,6 +115,7 @@ static void fu_wac_module_touch_class_init (FuWacModuleTouchClass *klass) { FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + klass_device->prepare_firmware = fu_wac_module_touch_prepare_firmware; klass_device->write_firmware = fu_wac_module_touch_write_firmware; } diff --git a/plugins/wacom-usb/fu-wac-module.c b/plugins/wacom-usb/fu-wac-module.c index 11e666a6e..033f56bb4 100644 --- a/plugins/wacom-usb/fu-wac-module.c +++ b/plugins/wacom-usb/fu-wac-module.c @@ -12,9 +12,6 @@ #include "fu-wac-common.h" #include "fu-wac-device.h" -#include "dfu-common.h" -#include "dfu-firmware.h" - #define FU_WAC_MODULE_STATUS_OK 0 #define FU_WAC_MODULE_STATUS_BUSY 1 #define FU_WAC_MODULE_STATUS_ERR_CRC 2 diff --git a/plugins/wacom-usb/meson.build b/plugins/wacom-usb/meson.build index fd92ed4bf..3090aa0ae 100644 --- a/plugins/wacom-usb/meson.build +++ b/plugins/wacom-usb/meson.build @@ -17,7 +17,6 @@ shared_module('fu_plugin_wacom_usb', ], include_directories : [ include_directories('../..'), - include_directories('../dfu'), include_directories('../../src'), include_directories('../../libfwupd'), ], @@ -29,7 +28,6 @@ shared_module('fu_plugin_wacom_usb', ], link_with : [ libfwupdprivate, - dfu, ], ) @@ -46,7 +44,6 @@ if get_option('tests') ], include_directories : [ include_directories('..'), - include_directories('../dfu'), include_directories('../..'), include_directories('../../libfwupd'), include_directories('../../src'), @@ -59,7 +56,6 @@ if get_option('tests') libm, ], link_with : [ - dfu, libfwupdprivate, ], c_args : cargs diff --git a/src/fu-common.c b/src/fu-common.c index 205af83de..e2803c240 100644 --- a/src/fu-common.c +++ b/src/fu-common.c @@ -1333,6 +1333,36 @@ fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error) return TRUE; } +/** + * fu_common_bytes_pad: + * @bytes: a #GBytes + * @sz: the desired size in bytes + * + * Pads a GBytes to a given @sz with `0xff`. + * + * Return value: (transfer full): a #GBytes + **/ +GBytes * +fu_common_bytes_pad (GBytes *bytes, gsize sz) +{ + gsize bytes_sz; + + g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL); + + /* pad */ + bytes_sz = g_bytes_get_size (bytes); + if (bytes_sz < sz) { + const guint8 *data = g_bytes_get_data (bytes, NULL); + guint8 *data_new = g_malloc (sz); + memcpy (data_new, data, bytes_sz); + memset (data_new + bytes_sz, 0xff, sz - bytes_sz); + return g_bytes_new_take (data_new, sz); + } + + /* exactly right */ + return g_bytes_ref (bytes); +} + /** * fu_common_realpath: * @filename: a filename @@ -1359,3 +1389,27 @@ fu_common_realpath (const gchar *filename, GError **error) } return g_strdup (full_tmp); } + +/** + * fu_common_strnsplit: + * @str: a string to split + * @sz: size of @str + * @delimiter: a string which specifies the places at which to split the string + * @max_tokens: the maximum number of pieces to split @str into + * + * Splits a string into a maximum of @max_tokens pieces, using the given + * delimiter. If @max_tokens is reached, the remainder of string is appended + * to the last token. + * + * Return value: a newly-allocated NULL-terminated array of strings + **/ +gchar ** +fu_common_strnsplit (const gchar *str, gsize sz, + const gchar *delimiter, gint max_tokens) +{ + if (str[sz - 1] != '\0') { + g_autofree gchar *str2 = g_strndup (str, sz); + return g_strsplit (str2, delimiter, max_tokens); + } + return g_strsplit (str, delimiter, max_tokens); +} diff --git a/src/fu-common.h b/src/fu-common.h index 231b9d5a5..25672ab27 100644 --- a/src/fu-common.h +++ b/src/fu-common.h @@ -133,6 +133,8 @@ gboolean fu_common_bytes_is_empty (GBytes *bytes); gboolean fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error); +GBytes *fu_common_bytes_pad (GBytes *bytes, + gsize sz); typedef guint FuEndianType; @@ -150,5 +152,9 @@ guint32 fu_common_read_uint32 (const guint8 *buf, guint fu_common_string_replace (GString *string, const gchar *search, const gchar *replace); +gchar **fu_common_strnsplit (const gchar *str, + gsize sz, + const gchar *delimiter, + gint max_tokens); G_END_DECLS diff --git a/src/fu-device.c b/src/fu-device.c index 2248e68ab..d1e18eacb 100644 --- a/src/fu-device.c +++ b/src/fu-device.c @@ -1754,7 +1754,7 @@ fu_device_write_firmware (FuDevice *self, GError **error) { FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); - g_autoptr(GBytes) fw_new = NULL; + g_autoptr(FuFirmware) firmware = NULL; g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); @@ -1769,12 +1769,12 @@ fu_device_write_firmware (FuDevice *self, } /* prepare (e.g. decompress) firmware */ - fw_new = fu_device_prepare_firmware (self, fw, flags, error); - if (fw_new == NULL) + firmware = fu_device_prepare_firmware (self, fw, flags, error); + if (firmware == NULL) return FALSE; /* call vfunc */ - return klass->write_firmware (self, fw_new, flags, error); + return klass->write_firmware (self, firmware, flags, error); } /** @@ -1796,7 +1796,7 @@ fu_device_write_firmware (FuDevice *self, * * Since: 1.1.2 **/ -GBytes * +FuFirmware * fu_device_prepare_firmware (FuDevice *self, GBytes *fw, FwupdInstallFlags flags, @@ -1804,8 +1804,8 @@ fu_device_prepare_firmware (FuDevice *self, { FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); FuDevicePrivate *priv = GET_PRIVATE (self); - guint64 fw_sz; - g_autoptr(GBytes) fw_new = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(GBytes) fw_def = NULL; g_return_val_if_fail (FU_IS_DEVICE (self), NULL); g_return_val_if_fail (fw != NULL, NULL); @@ -1813,37 +1813,41 @@ fu_device_prepare_firmware (FuDevice *self, /* optionally subclassed */ if (klass->prepare_firmware != NULL) { - fw_new = klass->prepare_firmware (self, fw, flags, error); - if (fw_new == NULL) + firmware = klass->prepare_firmware (self, fw, flags, error); + if (firmware == NULL) return NULL; } else { - fw_new = g_bytes_ref (fw); + firmware = fu_firmware_new_from_bytes (fw); } /* check size */ - fw_sz = (guint64) g_bytes_get_size (fw_new); - if (priv->size_max > 0 && fw_sz > priv->size_max) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "firmware is %04x bytes larger than the allowed " - "maximum size of %04x bytes", - (guint) (fw_sz - priv->size_max), - (guint) priv->size_max); - return NULL; - } - if (priv->size_min > 0 && fw_sz < priv->size_min) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "firmware is %04x bytes smaller than the allowed " - "minimum size of %04x bytes", - (guint) (priv->size_min - fw_sz), - (guint) priv->size_max); - return NULL; + fw_def = fu_firmware_get_image_default_bytes (firmware, NULL); + if (fw_def != NULL) { + guint64 fw_sz = (guint64) g_bytes_get_size (fw_def); + if (priv->size_max > 0 && fw_sz > priv->size_max) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware is %04x bytes larger than the allowed " + "maximum size of %04x bytes", + (guint) (fw_sz - priv->size_max), + (guint) priv->size_max); + return NULL; + } + if (priv->size_min > 0 && fw_sz < priv->size_min) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware is %04x bytes smaller than the allowed " + "minimum size of %04x bytes", + (guint) (priv->size_min - fw_sz), + (guint) priv->size_max); + return NULL; + } } - return g_steal_pointer (&fw_new); + /* success */ + return g_steal_pointer (&firmware); } /** diff --git a/src/fu-device.h b/src/fu-device.h index ba9cecc4f..1e482d9d7 100644 --- a/src/fu-device.h +++ b/src/fu-device.h @@ -9,6 +9,7 @@ #include #include +#include "fu-firmware.h" #include "fu-quirks.h" #include "fu-common-version.h" @@ -23,7 +24,7 @@ struct _FuDeviceClass void (*to_string) (FuDevice *self, GString *str); gboolean (*write_firmware) (FuDevice *self, - GBytes *fw, + FuFirmware *firmware, FwupdInstallFlags flags, GError **error); GBytes *(*read_firmware) (FuDevice *self, @@ -38,7 +39,7 @@ struct _FuDeviceClass GError **error); gboolean (*probe) (FuDevice *self, GError **error); - GBytes *(*prepare_firmware) (FuDevice *self, + FuFirmware *(*prepare_firmware) (FuDevice *self, GBytes *fw, FwupdInstallFlags flags, GError **error); @@ -210,7 +211,7 @@ gboolean fu_device_write_firmware (FuDevice *self, GBytes *fw, FwupdInstallFlags flags, GError **error); -GBytes *fu_device_prepare_firmware (FuDevice *self, +FuFirmware *fu_device_prepare_firmware (FuDevice *self, GBytes *fw, FwupdInstallFlags flags, GError **error); diff --git a/src/fu-firmware-common.c b/src/fu-firmware-common.c new file mode 100644 index 000000000..79b7c2638 --- /dev/null +++ b/src/fu-firmware-common.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2015-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include + +#include + +#include "fu-firmware-common.h" + +/** + * fu_firmware_strparse_uint4: + * @data: a string + * + * Parses a base 16 number from a string. + * + * The string MUST be at least 1 byte long as this function cannot check the + * length of @data. Checking the size must be done in the caller. + * + * Return value: A parsed value, or 0 for error + **/ +guint8 +fu_firmware_strparse_uint4 (const gchar *data) +{ + gchar buffer[2]; + memcpy (buffer, data, 1); + buffer[1] = '\0'; + return (guint8) g_ascii_strtoull (buffer, NULL, 16); +} + +/** + * fu_firmware_strparse_uint8: + * @data: a string + * + * Parses a base 16 number from a string. + * + * The string MUST be at least 2 bytes long as this function cannot check the + * length of @data. Checking the size must be done in the caller. + * + * Return value: A parsed value, or 0 for error + **/ +guint8 +fu_firmware_strparse_uint8 (const gchar *data) +{ + gchar buffer[3]; + memcpy (buffer, data, 2); + buffer[2] = '\0'; + return (guint8) g_ascii_strtoull (buffer, NULL, 16); +} + +/** + * fu_firmware_strparse_uint16: + * @data: a string + * + * Parses a base 16 number from a string. + * + * The string MUST be at least 4 bytes long as this function cannot check the + * length of @data. Checking the size must be done in the caller. + * + * Return value: A parsed value, or 0 for error + **/ +guint16 +fu_firmware_strparse_uint16 (const gchar *data) +{ + gchar buffer[5]; + memcpy (buffer, data, 4); + buffer[4] = '\0'; + return (guint16) g_ascii_strtoull (buffer, NULL, 16); +} + +/** + * fu_firmware_strparse_uint24: + * @data: a string + * + * Parses a base 16 number from a string. + * + * The string MUST be at least 6 bytes long as this function cannot check the + * length of @data. Checking the size must be done in the caller. + * + * Return value: A parsed value, or 0 for error + **/ +guint32 +fu_firmware_strparse_uint24 (const gchar *data) +{ + gchar buffer[7]; + memcpy (buffer, data, 6); + buffer[6] = '\0'; + return (guint32) g_ascii_strtoull (buffer, NULL, 16); +} + +/** + * fu_firmware_strparse_uint32: + * @data: a string + * + * Parses a base 16 number from a string. + * + * The string MUST be at least 8 bytes long as this function cannot check the + * length of @data. Checking the size must be done in the caller. + * + * Return value: A parsed value, or 0 for error + **/ +guint32 +fu_firmware_strparse_uint32 (const gchar *data) +{ + gchar buffer[9]; + memcpy (buffer, data, 8); + buffer[8] = '\0'; + return (guint32) g_ascii_strtoull (buffer, NULL, 16); +} diff --git a/src/fu-firmware-common.h b/src/fu-firmware-common.h new file mode 100644 index 000000000..6aa25c82d --- /dev/null +++ b/src/fu-firmware-common.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2015-2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +guint8 fu_firmware_strparse_uint4 (const gchar *data); +guint8 fu_firmware_strparse_uint8 (const gchar *data); +guint16 fu_firmware_strparse_uint16 (const gchar *data); +guint32 fu_firmware_strparse_uint24 (const gchar *data); +guint32 fu_firmware_strparse_uint32 (const gchar *data); + +G_END_DECLS diff --git a/src/fu-firmware-image.c b/src/fu-firmware-image.c new file mode 100644 index 000000000..708e8b351 --- /dev/null +++ b/src/fu-firmware-image.c @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include + +#include "fu-firmware-image.h" + +/** + * SECTION:fu-firmware_image + * @short_description: a firmware_image file + * + * An object that represents a firmware_image file. + */ + +typedef struct { + gchar *id; + GBytes *bytes; + guint64 addr; + guint64 idx; +} FuFirmwareImagePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (FuFirmwareImage, fu_firmware_image, G_TYPE_OBJECT) +#define GET_PRIVATE(o) (fu_firmware_image_get_instance_private (o)) + +/** + * fu_firmware_image_set_id: + * @self: a #FuPlugin + * @id: image ID, e.g. "config" + * + * Since: 1.2.11 + **/ +void +fu_firmware_image_set_id (FuFirmwareImage *self, const gchar *id) +{ + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_FIRMWARE_IMAGE (self)); + g_return_if_fail (id != NULL); + g_free (priv->id); + priv->id = g_strdup (id); +} + +/** + * fu_firmware_image_get_id: + * @self: a #FuPlugin + * + * Gets the image ID, typically set at construction. + * + * Returns: image ID, e.g. "config" + * + * Since: 1.2.11 + **/ +const gchar * +fu_firmware_image_get_id (FuFirmwareImage *self) +{ + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), FALSE); + return priv->id; +} + +/** + * fu_firmware_image_set_addr: + * @self: a #FuPlugin + * @addr: integer + * + * Sets the base address of the image. + * + * Since: 1.2.11 + **/ +void +fu_firmware_image_set_addr (FuFirmwareImage *self, guint64 addr) +{ + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_FIRMWARE_IMAGE (self)); + priv->addr = addr; +} + +/** + * fu_firmware_image_get_addr: + * @self: a #FuPlugin + * + * Gets the base address of the image. + * + * Returns: integer + * + * Since: 1.2.11 + **/ +guint64 +fu_firmware_image_get_addr (FuFirmwareImage *self) +{ + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), G_MAXUINT64); + return priv->addr; +} + +/** + * fu_firmware_image_set_idx: + * @self: a #FuPlugin + * @idx: integer + * + * Sets the index of the image which is used for ordering. + * + * Since: 1.2.11 + **/ +void +fu_firmware_image_set_idx (FuFirmwareImage *self, guint64 idx) +{ + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_FIRMWARE_IMAGE (self)); + priv->idx = idx; +} + +/** + * fu_firmware_image_get_idx: + * @self: a #FuPlugin + * + * Gets the index of the image which is used for ordering. + * + * Returns: integer + * + * Since: 1.2.11 + **/ +guint64 +fu_firmware_image_get_idx (FuFirmwareImage *self) +{ + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), G_MAXUINT64); + return priv->idx; +} + +/** + * fu_firmware_image_set_bytes: + * @self: a #FuPlugin + * @bytes: A #GBytes + * + * Sets the contents of the image if not created with fu_firmware_image_new(). + * + * Since: 1.2.11 + **/ +void +fu_firmware_image_set_bytes (FuFirmwareImage *self, GBytes *bytes) +{ + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_FIRMWARE_IMAGE (self)); + g_return_if_fail (bytes != NULL); + g_return_if_fail (priv->bytes == NULL); + priv->bytes = g_bytes_ref (bytes); +} + +/** + * fu_firmware_image_get_bytes: + * @self: a #FuPlugin + * @error: A #GError, or %NULL + * + * Gets the contents of the bytes. + * + * Returns: (transfer full): a #GBytes of the bytes, or %NULL if the bytes is not set + * + * Since: 1.2.11 + **/ +GBytes * +fu_firmware_image_get_bytes (FuFirmwareImage *self, GError **error) +{ + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + + g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (priv->bytes == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no bytes found in firmware bytes %s", priv->id); + return NULL; + } + return g_bytes_ref (priv->bytes); +} + +/** + * fu_firmware_image_get_bytes_chunk: + * @self: a #FuFirmwareImage + * @address: an address greater than dfu_element_get_address() + * @chunk_sz_max: the size of the new chunk + * @error: a #GError, or %NULL + * + * Gets a block of data from the image. If the contents of the image is + * smaller than the requested chunk size then the #GBytes will be smaller + * than @chunk_sz_max. Use fu_common_bytes_pad() if padding is required. + * + * If the @address is larger than the size of the image then an error is returned. + * + * Return value: (transfer full): a #GBytes, or %NULL + * + * Since: 1.2.11 + **/ +GBytes * +fu_firmware_image_get_bytes_chunk (FuFirmwareImage *self, + guint64 address, + guint64 chunk_sz_max, + GError **error) +{ + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + gsize chunk_left; + guint64 offset; + + /* check address requested is larger than base address */ + if (address < priv->addr) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "requested address 0x%x less than base address 0x%x", + (guint) address, (guint) priv->addr); + return NULL; + } + + /* offset into data */ + offset = address - priv->addr; + if (offset > g_bytes_get_size (priv->bytes)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "offset 0x%x larger than data size 0x%x", + (guint) offset, + (guint) g_bytes_get_size (priv->bytes)); + return NULL; + } + + /* if we have less data than requested */ + chunk_left = g_bytes_get_size (priv->bytes) - offset; + if (chunk_sz_max > chunk_left) + return g_bytes_new_from_bytes (priv->bytes, offset, chunk_left); + + /* check chunk */ + return g_bytes_new_from_bytes (priv->bytes, offset, chunk_sz_max); +} + +static void +fwupd_pad_kv_str (GString *str, const gchar *key, const gchar *value) +{ + /* ignore */ + if (key == NULL || value == NULL) + return; + g_string_append_printf (str, " %s: ", key); + for (gsize i = strlen (key); i < 20; i++) + g_string_append (str, " "); + g_string_append_printf (str, "%s\n", value); +} + +/** + * fu_firmware_image_to_string: + * @self: A #FuFirmwareImage + * + * This allows us to easily print the object. + * + * Returns: a string value, or %NULL for invalid. + * + * Since: 1.2.11 + **/ +gchar * +fu_firmware_image_to_string (FuFirmwareImage *self) +{ + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + GString *str = g_string_new (" FuFirmwareImage:\n"); + if (priv->id != NULL) + fwupd_pad_kv_str (str, "ID", priv->id); + if (priv->idx != 0x0) { + g_autofree gchar *tmp = g_strdup_printf ("0x%04x", (guint) priv->idx); + fwupd_pad_kv_str (str, "Index", tmp); + } + if (priv->addr != 0x0) { + g_autofree gchar *tmp = g_strdup_printf ("0x%04x", (guint) priv->addr); + fwupd_pad_kv_str (str, "Address", tmp); + } + if (priv->bytes != NULL) { + gsize sz = g_bytes_get_size (priv->bytes); + g_autofree gchar *tmp = g_strdup_printf ("%04x", (guint) sz); + fwupd_pad_kv_str (str, "Data", tmp); + } + return g_string_free (str, FALSE); +} + +static void +fu_firmware_image_init (FuFirmwareImage *self) +{ +} + +static void +fu_firmware_image_finalize (GObject *object) +{ + FuFirmwareImage *self = FU_FIRMWARE_IMAGE (object); + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + g_free (priv->id); + if (priv->bytes != NULL) + g_bytes_unref (priv->bytes); + G_OBJECT_CLASS (fu_firmware_image_parent_class)->finalize (object); +} + +static void +fu_firmware_image_class_init (FuFirmwareImageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = fu_firmware_image_finalize; +} + +/** + * fu_firmware_image_new: + * @id: Optional ID + * @bytes: Optional #GBytes + * + * Creates an empty firmware_image object. + * + * Returns: a #FuFirmwareImage + * + * Since: 1.2.11 + **/ +FuFirmwareImage * +fu_firmware_image_new (GBytes *bytes) +{ + FuFirmwareImage *self = g_object_new (FU_TYPE_FIRMWARE_IMAGE, NULL); + if (bytes != NULL) + fu_firmware_image_set_bytes (self, bytes); + return self; +} diff --git a/src/fu-firmware-image.h b/src/fu-firmware-image.h new file mode 100644 index 000000000..5a20b1578 --- /dev/null +++ b/src/fu-firmware-image.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +#define FU_TYPE_FIRMWARE_IMAGE (fu_firmware_image_get_type ()) +G_DECLARE_DERIVABLE_TYPE (FuFirmwareImage, fu_firmware_image, FU, FIRMWARE_IMAGE, GObject) + +struct _FuFirmwareImageClass +{ + GObjectClass parent_class; + gboolean (*parse) (FuFirmwareImage *self, + GBytes *fw, + FwupdInstallFlags flags, + GError **error); + /*< private >*/ + gpointer padding[30]; +}; + +FuFirmwareImage *fu_firmware_image_new (GBytes *bytes); +gchar *fu_firmware_image_to_string (FuFirmwareImage *self); + +const gchar *fu_firmware_image_get_id (FuFirmwareImage *self); +void fu_firmware_image_set_id (FuFirmwareImage *self, + const gchar *id); +guint64 fu_firmware_image_get_addr (FuFirmwareImage *self); +void fu_firmware_image_set_addr (FuFirmwareImage *self, + guint64 addr); +guint64 fu_firmware_image_get_idx (FuFirmwareImage *self); +void fu_firmware_image_set_idx (FuFirmwareImage *self, + guint64 idx); +void fu_firmware_image_set_bytes (FuFirmwareImage *self, + GBytes *bytes); +GBytes *fu_firmware_image_get_bytes (FuFirmwareImage *self, + GError **error); +GBytes *fu_firmware_image_get_bytes_chunk(FuFirmwareImage *self, + guint64 address, + guint64 chunk_sz_max, + GError **error); + +G_END_DECLS diff --git a/src/fu-firmware.c b/src/fu-firmware.c new file mode 100644 index 000000000..1cb945aac --- /dev/null +++ b/src/fu-firmware.c @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include "fu-firmware.h" + +/** + * SECTION:fu-firmware + * @short_description: a firmware file + * + * An object that represents a firmware file. + */ + +typedef struct { + GPtrArray *images; /* FuFirmwareImage */ +} FuFirmwarePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (FuFirmware, fu_firmware, G_TYPE_OBJECT) +#define GET_PRIVATE(o) (fu_firmware_get_instance_private (o)) + +/** + * fu_firmware_parse_full: + * @self: A #FuFirmware + * @image: A #GBytes + * @addr_start: Start address, useful for ignoring a bootloader + * @addr_end: End address, useful for ignoring config bytes + * @flags: some #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @error: A #GError, or %NULL + * + * Parses a firmware, typically breaking the firmware into images. + * + * Returns: %TRUE for success + * + * Since: 1.2.11 + **/ +gboolean +fu_firmware_parse_full (FuFirmware *self, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error) +{ + FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS (self); + g_autoptr(FuFirmwareImage) img = NULL; + + g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE); + g_return_val_if_fail (fw != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* subclassed */ + if (klass->parse != NULL) + return klass->parse (self, fw, addr_start, addr_end, flags, error); + + /* just add entire blob */ + img = fu_firmware_image_new (fw); + fu_firmware_add_image (self, img); + return TRUE; +} + +/** + * fu_firmware_parse: + * @self: A #FuFirmware + * @image: A #GBytes + * @flags: some #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @error: A #GError, or %NULL + * + * Parses a firmware, typically breaking the firmware into images. + * + * Returns: %TRUE for success + * + * Since: 1.2.11 + **/ +gboolean +fu_firmware_parse (FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError **error) +{ + return fu_firmware_parse_full (self, fw, 0x0, 0x0, flags, error); +} + +/** + * fu_firmware_write: + * @self: A #FuFirmware + * @error: A #GError, or %NULL + * + * Writes a firmware, typically packing the images into a binary blob. + * + * Returns: (transfer full): a #GBytes + * + * Since: 1.2.11 + **/ +GBytes * +fu_firmware_write (FuFirmware *self, GError **error) +{ + FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS (self); + + g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* subclassed */ + if (klass->write != NULL) + return klass->write (self, error); + + /* just add default blob */ + return fu_firmware_get_image_default_bytes (self, error); +} + +/** + * fu_firmware_add_image: + * @self: a #FuPlugin + * @img: A #FuFirmwareImage + * + * Adds an image to the firmware. + * + * If an image with the same ID is already present it is replaced. + * + * Since: 1.2.11 + **/ +void +fu_firmware_add_image (FuFirmware *self, FuFirmwareImage *img) +{ + FuFirmwarePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FU_IS_FIRMWARE (self)); + g_return_if_fail (FU_IS_FIRMWARE_IMAGE (img)); + g_ptr_array_add (priv->images, g_object_ref (img)); +} + +/** + * fu_firmware_get_images: + * @self: a #FuPlugin + * + * Returns all the images in the firmware. + * + * Returns: (transfer container) (element-type FuFirmwareImage): images + * + * Since: 1.2.11 + **/ +GPtrArray * +fu_firmware_get_images (FuFirmware *self) +{ + FuFirmwarePrivate *priv = GET_PRIVATE (self); + g_autoptr(GPtrArray) imgs = NULL; + + g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE); + + imgs = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + for (guint i = 0; i < priv->images->len; i++) { + FuFirmwareImage *img = g_ptr_array_index (priv->images, i); + g_ptr_array_add (imgs, g_object_ref (img)); + } + return g_steal_pointer (&imgs); +} + +/** + * fu_firmware_get_image_by_id: + * @self: a #FuPlugin + * @id: (nullable): image ID, e.g. "config" + * @error: A #GError, or %NULL + * + * Gets the firmware image using the image ID. + * + * Returns: (transfer full): a #FuFirmwareImage, or %NULL if the image is not found + * + * Since: 1.2.11 + **/ +FuFirmwareImage * +fu_firmware_get_image_by_id (FuFirmware *self, const gchar *id, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE (self); + + g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + for (guint i = 0; i < priv->images->len; i++) { + FuFirmwareImage *img = g_ptr_array_index (priv->images, i); + if (g_strcmp0 (fu_firmware_image_get_id (img), id) == 0) + return g_object_ref (img); + } + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no image id %s found in firmware", id); + return NULL; +} + +/** + * fu_firmware_get_image_by_id_bytes: + * @self: a #FuPlugin + * @id: (nullable): image ID, e.g. "config" + * @error: A #GError, or %NULL + * + * Gets the firmware image bytes using the image ID. + * + * Returns: (transfer full): a #GBytes of a #FuFirmwareImage, or %NULL if the image is not found + * + * Since: 1.2.11 + **/ +GBytes * +fu_firmware_get_image_by_id_bytes (FuFirmware *self, const gchar *id, GError **error) +{ + g_autoptr(FuFirmwareImage) img = fu_firmware_get_image_by_id (self, id, error); + if (img == NULL) + return NULL; + return fu_firmware_image_get_bytes (img, error); +} + +/** + * fu_firmware_get_image_by_idx: + * @self: a #FuPlugin + * @idx: image index + * @error: A #GError, or %NULL + * + * Gets the firmware image using the image index. + * + * Returns: (transfer full): a #FuFirmwareImage, or %NULL if the image is not found + * + * Since: 1.2.11 + **/ +FuFirmwareImage * +fu_firmware_get_image_by_idx (FuFirmware *self, guint64 idx, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE (self); + + g_return_val_if_fail (FU_IS_FIRMWARE (self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + for (guint i = 0; i < priv->images->len; i++) { + FuFirmwareImage *img = g_ptr_array_index (priv->images, i); + if (fu_firmware_image_get_idx (img) == idx) + return g_object_ref (img); + } + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no image idx %" G_GUINT64_FORMAT " found in firmware", idx); + return NULL; +} + +/** + * fu_firmware_get_image_by_idx_bytes: + * @self: a #FuPlugin + * @idx: image index + * @error: A #GError, or %NULL + * + * Gets the firmware image bytes using the image index. + * + * Returns: (transfer full): a #GBytes of a #FuFirmwareImage, or %NULL if the image is not found + * + * Since: 1.2.11 + **/ +GBytes * +fu_firmware_get_image_by_idx_bytes (FuFirmware *self, guint64 idx, GError **error) +{ + g_autoptr(FuFirmwareImage) img = fu_firmware_get_image_by_idx (self, idx, error); + if (img == NULL) + return NULL; + return fu_firmware_image_get_bytes (img, error); +} + +/** + * fu_firmware_get_image_default: + * @self: a #FuPlugin + * @error: A #GError, or %NULL + * + * Gets the default firmware image. + * + * NOTE: If the firmware has multiple images included then fu_firmware_get_image_by_id() + * or fu_firmware_get_image_by_idx() must be used rather than this function. + * + * Returns: (transfer full): a #FuFirmwareImage, or %NULL if the image is not found + * + * Since: 1.2.11 + **/ +FuFirmwareImage * +fu_firmware_get_image_default (FuFirmware *self, GError **error) +{ + FuFirmwarePrivate *priv = GET_PRIVATE (self); + if (priv->images->len == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no images in firmware"); + return NULL; + } + if (priv->images->len > 1) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "multiple images present in firmware"); + return NULL; + } + return g_object_ref (FU_FIRMWARE_IMAGE (g_ptr_array_index (priv->images, 0))); +} + +/** + * fu_firmware_get_image_default_bytes: + * @self: a #FuPlugin + * @error: A #GError, or %NULL + * + * Gets the default firmware image. + * + * Returns: (transfer full): a #GBytes of the image, or %NULL if the image is not found + * + * Since: 1.2.11 + **/ +GBytes * +fu_firmware_get_image_default_bytes (FuFirmware *self, GError **error) +{ + g_autoptr(FuFirmwareImage) img = fu_firmware_get_image_default (self, error); + if (img == NULL) + return NULL; + return fu_firmware_image_get_bytes (img, error); +} + +/** + * fu_firmware_to_string: + * @self: A #FuFirmware + * + * This allows us to easily print the object. + * + * Returns: a string value, or %NULL for invalid. + * + * Since: 1.2.11 + **/ +gchar * +fu_firmware_to_string (FuFirmware *self) +{ + FuFirmwarePrivate *priv = GET_PRIVATE (self); + GString *str = g_string_new ("FuFirmware:\n"); + + for (guint i = 0; i < priv->images->len; i++) { + FuFirmwareImage *img = g_ptr_array_index (priv->images, i); + g_autofree gchar *tmp = fu_firmware_image_to_string (img); + g_string_append (str, tmp); + } + + return g_string_free (str, FALSE); +} + +static void +fu_firmware_init (FuFirmware *self) +{ + FuFirmwarePrivate *priv = GET_PRIVATE (self); + priv->images = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); +} + +static void +fu_firmware_finalize (GObject *object) +{ + FuFirmware *self = FU_FIRMWARE (object); + FuFirmwarePrivate *priv = GET_PRIVATE (self); + g_ptr_array_unref (priv->images); + G_OBJECT_CLASS (fu_firmware_parent_class)->finalize (object); +} + +static void +fu_firmware_class_init (FuFirmwareClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = fu_firmware_finalize; +} + +/** + * fu_firmware_new: + * + * Creates an empty firmware object. + * + * Returns: a #FuFirmware + * + * Since: 1.2.11 + **/ +FuFirmware * +fu_firmware_new (void) +{ + FuFirmware *self = g_object_new (FU_TYPE_FIRMWARE, NULL); + return FU_FIRMWARE (self); +} + +/** + * fu_firmware_new_from_bytes: + * @self: A #GBytes image + * + * Creates a firmware object with the provided image set as default. + * + * Returns: a #FuFirmware + * + * Since: 1.2.11 + **/ +FuFirmware * +fu_firmware_new_from_bytes (GBytes *fw) +{ + FuFirmware *self = fu_firmware_new (); + g_autoptr(FuFirmwareImage) img = NULL; + img = fu_firmware_image_new (fw); + fu_firmware_add_image (self, img); + return self; +} diff --git a/src/fu-firmware.h b/src/fu-firmware.h new file mode 100644 index 000000000..a3eb18973 --- /dev/null +++ b/src/fu-firmware.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +#include "fu-firmware-image.h" + +G_BEGIN_DECLS + +#define FU_TYPE_FIRMWARE (fu_firmware_get_type ()) +G_DECLARE_DERIVABLE_TYPE (FuFirmware, fu_firmware, FU, FIRMWARE, GObject) + +struct _FuFirmwareClass +{ + GObjectClass parent_class; + gboolean (*parse) (FuFirmware *self, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error); + GBytes *(*write) (FuFirmware *self, + GError **error); + /*< private >*/ + gpointer padding[30]; +}; + +FuFirmware *fu_firmware_new (void); +FuFirmware *fu_firmware_new_from_bytes (GBytes *fw); +gchar *fu_firmware_to_string (FuFirmware *self); + +gboolean fu_firmware_parse (FuFirmware *self, + GBytes *fw, + FwupdInstallFlags flags, + GError **error); +gboolean fu_firmware_parse_full (FuFirmware *self, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error); +GBytes *fu_firmware_write (FuFirmware *self, + GError **error); + +void fu_firmware_add_image (FuFirmware *self, + FuFirmwareImage *image); +GPtrArray *fu_firmware_get_images (FuFirmware *self); +FuFirmwareImage *fu_firmware_get_image_by_id (FuFirmware *self, + const gchar *id, + GError **error); +GBytes *fu_firmware_get_image_by_id_bytes (FuFirmware *self, + const gchar *id, + GError **error); +FuFirmwareImage *fu_firmware_get_image_by_idx (FuFirmware *self, + guint64 idx, + GError **error); +GBytes *fu_firmware_get_image_by_idx_bytes (FuFirmware *self, + guint64 idx, + GError **error); +FuFirmwareImage *fu_firmware_get_image_default (FuFirmware *self, + GError **error); +GBytes *fu_firmware_get_image_default_bytes (FuFirmware *self, + GError **error); + +G_END_DECLS diff --git a/src/fu-ihex-firmware.c b/src/fu-ihex-firmware.c new file mode 100644 index 000000000..e3c600060 --- /dev/null +++ b/src/fu-ihex-firmware.c @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include + +#include "fu-common.h" +#include "fu-firmware-common.h" +#include "fu-ihex-firmware.h" + +struct _FuIhexFirmware { + FuFirmware parent_instance; +}; + +G_DEFINE_TYPE (FuIhexFirmware, fu_ihex_firmware, FU_TYPE_FIRMWARE) + +#define DFU_INHX32_RECORD_TYPE_DATA 0x00 +#define DFU_INHX32_RECORD_TYPE_EOF 0x01 +#define DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT 0x02 +#define DFU_INHX32_RECORD_TYPE_START_SEGMENT 0x03 +#define DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR 0x04 +#define DFU_INHX32_RECORD_TYPE_START_LINEAR 0x05 +#define DFU_INHX32_RECORD_TYPE_SIGNATURE 0xfd + +static const gchar * +fu_ihex_firmware_record_type_to_string (guint8 record_type) +{ + if (record_type == DFU_INHX32_RECORD_TYPE_DATA) + return "DATA"; + if (record_type == DFU_INHX32_RECORD_TYPE_EOF) + return "EOF"; + if (record_type == DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT) + return "EXTENDED_SEGMENT"; + if (record_type == DFU_INHX32_RECORD_TYPE_START_SEGMENT) + return "START_SEGMENT"; + if (record_type == DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR) + return "EXTENDED_LINEAR"; + if (record_type == DFU_INHX32_RECORD_TYPE_START_LINEAR) + return "ADDR32"; + if (record_type == DFU_INHX32_RECORD_TYPE_SIGNATURE) + return "SIGNATURE"; + return NULL; +} + +static gboolean +fu_ihex_firmware_parse (FuFirmware *firmware, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error) +{ + const gchar *data; + gboolean got_eof = FALSE; + gsize sz = 0; + guint32 abs_addr = 0x0; + guint32 addr_last = 0x0; + guint32 img_addr = G_MAXUINT32; + guint32 seg_addr = 0x0; + g_auto(GStrv) lines = NULL; + g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (NULL); + g_autoptr(GBytes) img_bytes = NULL; + g_autoptr(GString) buf = g_string_new (NULL); + g_autoptr(GByteArray) buf_signature = g_byte_array_new (); + + g_return_val_if_fail (fw != NULL, FALSE); + + /* parse records */ + data = g_bytes_get_data (fw, &sz); + lines = fu_common_strnsplit (data, sz, "\n", -1); + for (guint ln = 0; lines[ln] != NULL; ln++) { + const gchar *line = lines[ln]; + gsize linesz; + guint32 addr; + guint8 byte_cnt; + guint8 record_type; + guint line_end; + + /* ignore comments */ + if (g_str_has_prefix (line, ";")) + continue; + + /* ignore blank lines */ + g_strdelimit (lines[ln], "\r\x1a", '\0'); + linesz = strlen (line); + if (linesz == 0) + continue; + + /* check starting token */ + if (line[0] != ':') { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid starting token on line %u: %s", + ln + 1, line); + return FALSE; + } + + /* check there's enough data for the smallest possible record */ + if (linesz < 11) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "line %u is incomplete, length %u", + ln + 1, (guint) linesz); + return FALSE; + } + + /* length, 16-bit address, type */ + byte_cnt = fu_firmware_strparse_uint8 (line + 1); + addr = fu_firmware_strparse_uint16 (line + 3); + record_type = fu_firmware_strparse_uint8 (line + 7); + g_debug ("%s:", fu_ihex_firmware_record_type_to_string (record_type)); + g_debug (" addr_start:\t0x%04x", addr); + g_debug (" length:\t0x%02x", byte_cnt); + addr += seg_addr; + addr += abs_addr; + g_debug (" addr:\t0x%08x", addr); + + /* position of checksum */ + line_end = 9 + byte_cnt * 2; + if (line_end > (guint) linesz) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "line %u malformed, length: %u", + ln + 1, line_end); + return FALSE; + } + + /* verify checksum */ + if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + guint8 checksum = 0; + for (guint i = 1; i < line_end + 2; i += 2) { + guint8 data_tmp = fu_firmware_strparse_uint8 (line + i); + checksum += data_tmp; + } + if (checksum != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "line %u has invalid checksum (0x%02x)", + ln + 1, checksum); + return FALSE; + } + } + + /* process different record types */ + switch (record_type) { + case DFU_INHX32_RECORD_TYPE_DATA: + /* base address for element */ + if (img_addr == G_MAXUINT32) + img_addr = addr; + + /* does not make sense */ + if (addr < addr_last) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid address 0x%x, last was 0x%x", + (guint) addr, + (guint) addr_last); + return FALSE; + } + + /* parse bytes from line */ + g_debug ("writing data 0x%08x", (guint32) addr); + for (guint i = 9; i < line_end; i += 2) { + /* any holes in the hex record */ + guint32 len_hole = addr - addr_last; + guint8 data_tmp; + if (addr_last > 0 && len_hole > 0x100000) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "hole of 0x%x bytes too large to fill", + (guint) len_hole); + return FALSE; + } + if (addr_last > 0x0 && len_hole > 1) { + g_debug ("filling address 0x%08x to 0x%08x", + addr_last + 1, addr_last + len_hole - 1); + for (guint j = 1; j < len_hole; j++) { + /* although 0xff might be clearer, + * we can't write 0xffff to pic14 */ + g_string_append_c (buf, 0x00); + } + } + /* write into buf */ + data_tmp = fu_firmware_strparse_uint8 (line + i); + g_string_append_c (buf, (gchar) data_tmp); + addr_last = addr++; + } + break; + case DFU_INHX32_RECORD_TYPE_EOF: + if (got_eof) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "duplicate EOF, perhaps " + "corrupt file"); + return FALSE; + } + got_eof = TRUE; + break; + case DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR: + abs_addr = fu_firmware_strparse_uint16 (line + 9) << 16; + g_debug (" abs_addr:\t0x%02x", abs_addr); + break; + case DFU_INHX32_RECORD_TYPE_START_LINEAR: + abs_addr = fu_firmware_strparse_uint32 (line + 9); + g_debug (" abs_addr:\t0x%08x", abs_addr); + break; + case DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT: + /* segment base address, so ~1Mb addressable */ + seg_addr = fu_firmware_strparse_uint16 (line + 9) * 16; + g_debug (" seg_addr:\t0x%08x", seg_addr); + break; + case DFU_INHX32_RECORD_TYPE_START_SEGMENT: + /* initial content of the CS:IP registers */ + seg_addr = fu_firmware_strparse_uint32 (line + 9); + g_debug (" seg_addr:\t0x%02x", seg_addr); + break; + case DFU_INHX32_RECORD_TYPE_SIGNATURE: + for (guint i = 9; i < line_end; i += 2) { + guint8 tmp_c = fu_firmware_strparse_uint8 (line + i); + g_byte_array_append (buf_signature, &tmp_c, 1); + } + break; + default: + /* vendors sneak in nonstandard sections past the EOF */ + if (got_eof) + break; + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid ihex record type %i", + record_type); + return FALSE; + } + } + + /* no EOF */ + if (!got_eof) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no EOF, perhaps truncated file"); + return FALSE; + } + + /* add single image */ + img_bytes = g_bytes_new (buf->str, buf->len); + fu_firmware_image_set_bytes (img, img_bytes); + if (img_addr != G_MAXUINT32) + fu_firmware_image_set_addr (img, img_addr); + fu_firmware_add_image (firmware, img); + + /* add optional signature */ + if (buf_signature->len > 0) { + g_autoptr(GBytes) data_sig = g_bytes_new (buf_signature->data, buf_signature->len); + g_autoptr(FuFirmwareImage) img_sig = fu_firmware_image_new (data_sig); + fu_firmware_image_set_id (img_sig, "signature"); + fu_firmware_add_image (firmware, img_sig); + } + return TRUE; +} + +static void +fu_ihex_firmware_emit_chunk (GString *str, + guint16 address, + guint8 record_type, + const guint8 *data, + gsize sz) +{ + guint8 checksum = 0x00; + g_string_append_printf (str, ":%02X%04X%02X", + (guint) sz, + (guint) address, + (guint) record_type); + for (gsize j = 0; j < sz; j++) + g_string_append_printf (str, "%02X", data[j]); + checksum = (guint8) sz; + checksum += (guint8) ((address & 0xff00) >> 8); + checksum += (guint8) (address & 0xff); + checksum += record_type; + for (gsize j = 0; j < sz; j++) + checksum += data[j]; + g_string_append_printf (str, "%02X\n", (guint) (((~checksum) + 0x01) & 0xff)); +} + +static gboolean +dfu_firmware_to_ihex_image (FuFirmwareImage *img, GString *str, GError **error) +{ + const guint8 *data; + const guint chunk_size = 16; + gsize len; + guint32 address_offset_last = 0x0; + guint8 record_type = DFU_INHX32_RECORD_TYPE_DATA; + g_autoptr(GBytes) bytes = NULL; + + /* get data */ + bytes = fu_firmware_image_get_bytes (img, error); + if (bytes == NULL) + return FALSE; + + /* special case */ + if (g_strcmp0 (fu_firmware_image_get_id (img), "signature") == 0) + record_type = DFU_INHX32_RECORD_TYPE_SIGNATURE; + + /* get number of chunks */ + data = g_bytes_get_data (bytes, &len); + for (gsize i = 0; i < len; i += chunk_size) { + guint32 address_tmp = fu_firmware_image_get_addr (img) + i; + guint32 address_offset = (address_tmp >> 16) & 0xffff; + gsize chunk_len = MIN (len - i, 16); + + /* need to offset */ + if (address_offset != address_offset_last) { + guint8 buf[2]; + fu_common_write_uint16 (buf, address_offset, G_BIG_ENDIAN); + fu_ihex_firmware_emit_chunk (str, 0x0, + DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR, + buf, 2); + address_offset_last = address_offset; + } + address_tmp &= 0xffff; + fu_ihex_firmware_emit_chunk (str, address_tmp, + record_type, data + i, chunk_len); + } + return TRUE; +} + +static GBytes * +fu_ihex_firmware_write (FuFirmware *firmware, GError **error) +{ + g_autoptr(GPtrArray) imgs = NULL; + g_autoptr(GString) str = NULL; + + /* write all the element data */ + str = g_string_new (""); + imgs = fu_firmware_get_images (firmware); + for (guint i = 0; i < imgs->len; i++) { + FuFirmwareImage *img = g_ptr_array_index (imgs, i); + if (!dfu_firmware_to_ihex_image (img, str, error)) + return NULL; + } + + /* add EOF */ + fu_ihex_firmware_emit_chunk (str, 0x0, DFU_INHX32_RECORD_TYPE_EOF, NULL, 0); + return g_bytes_new (str->str, str->len); +} + +static void +fu_ihex_firmware_init (FuIhexFirmware *self) +{ +} + +static void +fu_ihex_firmware_class_init (FuIhexFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass); + klass_firmware->parse = fu_ihex_firmware_parse; + klass_firmware->write = fu_ihex_firmware_write; +} + +FuFirmware * +fu_ihex_firmware_new (void) +{ + return FU_FIRMWARE (g_object_new (FU_TYPE_IHEX_FIRMWARE, NULL)); +} diff --git a/src/fu-ihex-firmware.h b/src/fu-ihex-firmware.h new file mode 100644 index 000000000..ad90137ac --- /dev/null +++ b/src/fu-ihex-firmware.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +G_BEGIN_DECLS + +#define FU_TYPE_IHEX_FIRMWARE (fu_ihex_firmware_get_type ()) +G_DECLARE_FINAL_TYPE (FuIhexFirmware, fu_ihex_firmware, FU, IHEX_FIRMWARE, FuFirmware) + +FuFirmware *fu_ihex_firmware_new (void); + +G_END_DECLS diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 24b121107..7ff116025 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -23,6 +23,7 @@ #include "fu-device-list.h" #include "fu-device-private.h" #include "fu-engine.h" +#include "fu-ihex-firmware.h" #include "fu-quirks.h" #include "fu-keyring.h" #include "fu-history.h" @@ -33,6 +34,7 @@ #include "fu-hash.h" #include "fu-hwids.h" #include "fu-smbios.h" +#include "fu-srec-firmware.h" #include "fu-test.h" #ifdef ENABLE_GPG @@ -3665,6 +3667,248 @@ fu_common_vercmp_func (void) g_assert_cmpint (fu_common_vercmp (NULL, NULL), ==, G_MAXINT); } +static void +fu_firmware_ihex_func (void) +{ + const guint8 *data; + gboolean ret; + gsize len; + g_autofree gchar *filename_hex = NULL; + g_autofree gchar *filename_ref = NULL; + g_autofree gchar *str = NULL; + g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new (); + g_autoptr(GBytes) data_file = NULL; + g_autoptr(GBytes) data_fw = NULL; + g_autoptr(GBytes) data_hex = NULL; + g_autoptr(GBytes) data_ref = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GFile) file_ref = NULL; + g_autoptr(GFile) file_hex = NULL; + + /* load a Intel hex32 file */ + filename_hex = fu_test_get_filename (TESTDATADIR, "firmware.hex"); + g_assert (filename_hex != NULL); + file_hex = g_file_new_for_path (filename_hex); + data_file = g_file_load_bytes (file_hex, NULL, NULL, &error); + g_assert_no_error (error); + g_assert (data_file != NULL); + ret = fu_firmware_parse (firmware, data_file, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error (error); + g_assert (ret); + data_fw = fu_firmware_get_image_default_bytes (firmware, &error); + g_assert_no_error (error); + g_assert_nonnull (data_fw); + g_assert_cmpint (g_bytes_get_size (data_fw), ==, 136); + + /* did we match the reference file? */ + filename_ref = fu_test_get_filename (TESTDATADIR, "firmware.bin"); + g_assert (filename_ref != NULL); + file_ref = g_file_new_for_path (filename_ref); + data_ref = g_file_load_bytes (file_ref, NULL, NULL, &error); + g_assert_no_error (error); + g_assert (data_ref != NULL); + ret = fu_common_bytes_compare (data_fw, data_ref, &error); + g_assert_no_error (error); + g_assert_true (ret); + + /* export a ihex file (which will be slightly different due to + * non-continous regions being expanded */ + data_hex = fu_firmware_write (firmware, &error); + g_assert_no_error (error); + g_assert (data_hex != NULL); + data = g_bytes_get_data (data_hex, &len); + str = g_strndup ((const gchar *) data, len); + g_assert_cmpstr (str, ==, + ":104000003DEF20F000000000FACF01F0FBCF02F0FE\n" + ":10401000E9CF03F0EACF04F0E1CF05F0E2CF06F0FC\n" + ":10402000D9CF07F0DACF08F0F3CF09F0F4CF0AF0D8\n" + ":10403000F6CF0BF0F7CF0CF0F8CF0DF0F5CF0EF078\n" + ":104040000EC0F5FF0DC0F8FF0CC0F7FF0BC0F6FF68\n" + ":104050000AC0F4FF09C0F3FF08C0DAFF07C0D9FFA8\n" + ":1040600006C0E2FF05C0E1FF04C0EAFF03C0E9FFAC\n" + ":1040700002C0FBFF01C0FAFF11003FEF20F000017A\n" + ":0840800042EF20F03DEF20F0BB\n" + ":00000001FF\n"); +} + +static void +fu_firmware_ihex_signed_func (void) +{ + const guint8 *data; + gboolean ret; + gsize len; + g_autofree gchar *filename_shex = NULL; + g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new (); + g_autoptr(GBytes) data_file = NULL; + g_autoptr(GBytes) data_fw = NULL; + g_autoptr(GBytes) data_sig = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GFile) file_hex = NULL; + + /* load a signed Intel hex32 file */ + filename_shex = fu_test_get_filename (TESTDATADIR, "firmware.shex"); + g_assert (filename_shex != NULL); + file_hex = g_file_new_for_path (filename_shex); + data_file = g_file_load_bytes (file_hex, NULL, NULL, &error); + g_assert_no_error (error); + g_assert (data_file != NULL); + ret = fu_firmware_parse (firmware, data_file, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error (error); + g_assert (ret); + data_fw = fu_firmware_get_image_by_id_bytes (firmware, NULL, &error); + g_assert_no_error (error); + g_assert_nonnull (data_fw); + g_assert_cmpint (g_bytes_get_size (data_fw), ==, 136); + + /* get the signed image */ + data_sig = fu_firmware_get_image_by_id_bytes (firmware, "signature", &error); + g_assert_no_error (error); + g_assert_nonnull (data_sig); + data = g_bytes_get_data (data_sig, &len); + g_assert_cmpint (len, ==, 8); + g_assert (data != NULL); + g_assert_cmpint (memcmp (data, "deadbeef", 8), ==, 0); +} + +static void +fu_firmware_ihex_offset_func (void) +{ + const guint8 *data; + gboolean ret; + gsize len; + g_autofree gchar *str = NULL; + g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new (); + g_autoptr(FuFirmware) firmware_verify = fu_ihex_firmware_new (); + g_autoptr(FuFirmwareImage) img_verify = NULL; + g_autoptr(FuFirmwareImage) img = NULL; + g_autoptr(GBytes) data_bin = NULL; + g_autoptr(GBytes) data_dummy = NULL; + g_autoptr(GBytes) data_verify = NULL; + g_autoptr(GError) error = NULL; + + /* add a 4 byte image in high memory */ + data_dummy = g_bytes_new_static ("foo", 4); + img = fu_firmware_image_new (data_dummy); + fu_firmware_image_set_addr (img, 0x80000000); + fu_firmware_add_image (firmware, img); + data_bin = fu_firmware_write (firmware, &error); + g_assert_no_error (error); + g_assert (data_bin != NULL); + data = g_bytes_get_data (data_bin, &len); + str = g_strndup ((const gchar *) data, len); + g_assert_cmpstr (str, ==, + ":0200000480007A\n" + ":04000000666F6F00B8\n" + ":00000001FF\n"); + + /* check we can load it too */ + ret = fu_firmware_parse (firmware_verify, data_bin, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error (error); + g_assert (ret); + img_verify = fu_firmware_get_image_default (firmware_verify, &error); + g_assert_no_error (error); + g_assert (img_verify != NULL); + g_assert_cmpint (fu_firmware_image_get_addr (img_verify), ==, 0x80000000); + data_verify = fu_firmware_image_get_bytes (img_verify, &error); + g_assert_no_error (error); + g_assert (data_verify != NULL); + g_assert_cmpint (g_bytes_get_size (data_verify), ==, 0x4); +} + +static void +fu_firmware_srec_func (void) +{ + gboolean ret; + g_autofree gchar *filename_srec = NULL; + g_autofree gchar *filename_ref = NULL; + g_autoptr(FuFirmware) firmware = fu_srec_firmware_new (); + g_autoptr(GBytes) data_ref = NULL; + g_autoptr(GBytes) data_srec = NULL; + g_autoptr(GBytes) data_bin = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GFile) file_bin = NULL; + g_autoptr(GFile) file_srec = NULL; + + filename_srec = fu_test_get_filename (TESTDATADIR, "firmware.srec"); + g_assert (filename_srec != NULL); + file_srec = g_file_new_for_path (filename_srec); + data_srec = g_file_load_bytes (file_srec, NULL, NULL, &error); + g_assert_no_error (error); + g_assert (data_srec != NULL); + ret = fu_firmware_parse (firmware, data_srec, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error (error); + g_assert (ret); + data_bin = fu_firmware_get_image_default_bytes (firmware, &error); + g_assert_no_error (error); + g_assert_nonnull (data_bin); + g_assert_cmpint (g_bytes_get_size (data_bin), ==, 136); + + /* did we match the reference file? */ + filename_ref = fu_test_get_filename (TESTDATADIR, "firmware.bin"); + g_assert (filename_ref != NULL); + file_bin = g_file_new_for_path (filename_ref); + data_ref = g_file_load_bytes (file_bin, NULL, NULL, &error); + g_assert_no_error (error); + g_assert (data_ref != NULL); + ret = fu_common_bytes_compare (data_bin, data_ref, &error); + g_assert_no_error (error); + g_assert_true (ret); +} + +static void +fu_firmware_func (void) +{ + g_autoptr(FuFirmware) firmware = fu_firmware_new (); + g_autoptr(FuFirmwareImage) img1 = fu_firmware_image_new (NULL); + g_autoptr(FuFirmwareImage) img2 = fu_firmware_image_new (NULL); + g_autoptr(FuFirmwareImage) img_id = NULL; + g_autoptr(FuFirmwareImage) img_idx = NULL; + g_autoptr(GError) error = NULL; + g_autofree gchar *str = NULL; + + fu_firmware_image_set_addr (img1, 0x200); + fu_firmware_image_set_idx (img1, 13); + fu_firmware_image_set_id (img1, "primary"); + fu_firmware_add_image (firmware, img1); + fu_firmware_image_set_addr (img2, 0x400); + fu_firmware_image_set_idx (img2, 23); + fu_firmware_image_set_id (img2, "secondary"); + fu_firmware_add_image (firmware, img2); + + img_id = fu_firmware_get_image_by_id (firmware, "NotGoingToExist", &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_null (img_id); + g_clear_error (&error); + img_id = fu_firmware_get_image_by_id (firmware, "primary", &error); + g_assert_no_error (error); + g_assert_nonnull (img_id); + g_assert_cmpint (fu_firmware_image_get_addr (img_id), ==, 0x200); + g_assert_cmpint (fu_firmware_image_get_idx (img_id), ==, 13); + g_assert_cmpstr (fu_firmware_image_get_id (img_id), ==, "primary"); + + img_idx = fu_firmware_get_image_by_idx (firmware, 123456, &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_null (img_idx); + g_clear_error (&error); + img_idx = fu_firmware_get_image_by_idx (firmware, 23, &error); + g_assert_no_error (error); + g_assert_nonnull (img_idx); + g_assert_cmpint (fu_firmware_image_get_addr (img_idx), ==, 0x400); + g_assert_cmpint (fu_firmware_image_get_idx (img_idx), ==, 23); + g_assert_cmpstr (fu_firmware_image_get_id (img_idx), ==, "secondary"); + + str = fu_firmware_to_string (firmware); + g_assert_cmpstr (str, ==, "FuFirmware:\n" + " FuFirmwareImage:\n" + " ID: primary\n" + " Index: 0x000d\n" + " Address: 0x0200\n" + " FuFirmwareImage:\n" + " ID: secondary\n" + " Index: 0x0017\n" + " Address: 0x0400\n"); +} + int main (int argc, char **argv) { @@ -3685,6 +3929,11 @@ main (int argc, char **argv) /* tests go here */ if (g_test_slow ()) g_test_add_func ("/fwupd/progressbar", fu_progressbar_func); + g_test_add_func ("/fwupd/firmware", fu_firmware_func); + g_test_add_func ("/fwupd/firmware{ihex}", fu_firmware_ihex_func); + g_test_add_func ("/fwupd/firmware{ihex-offset}", fu_firmware_ihex_offset_func); + g_test_add_func ("/fwupd/firmware{ihex-signed}", fu_firmware_ihex_signed_func); + g_test_add_func ("/fwupd/firmware{srec}", fu_firmware_srec_func); g_test_add_func ("/fwupd/archive{invalid}", fu_archive_invalid_func); g_test_add_func ("/fwupd/archive{cab}", fu_archive_cab_func); g_test_add_func ("/fwupd/engine{requirements-other-device}", fu_engine_requirements_other_device_func); diff --git a/src/fu-srec-firmware.c b/src/fu-srec-firmware.c new file mode 100644 index 000000000..3d5b4754d --- /dev/null +++ b/src/fu-srec-firmware.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include + +#include "fu-common.h" +#include "fu-firmware-common.h" +#include "fu-srec-firmware.h" + +struct _FuSrecFirmware { + FuFirmware parent_instance; +}; + +G_DEFINE_TYPE (FuSrecFirmware, fu_srec_firmware, FU_TYPE_FIRMWARE) + +static gboolean +fu_srec_firmware_parse (FuFirmware *firmware, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error) +{ + const gchar *data; + gboolean got_eof = FALSE; + gboolean got_hdr = FALSE; + gsize sz = 0; + guint16 data_cnt = 0; + guint32 addr32_last = 0; + guint32 img_address = 0; + g_auto(GStrv) lines = NULL; + g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (NULL); + g_autoptr(GBytes) img_bytes = NULL; + g_autoptr(GString) outbuf = g_string_new (NULL); + + /* parse records */ + data = g_bytes_get_data (fw, &sz); + lines = fu_common_strnsplit (data, sz, "\n", -1); + for (guint ln = 0; lines[ln] != NULL; ln++) { + const gchar *line = lines[ln]; + gsize linesz; + guint32 rec_addr32; + guint8 addrsz = 0; /* bytes */ + guint8 rec_count; /* words */ + guint8 rec_kind; + + /* ignore blank lines */ + g_strdelimit (lines[ln], "\r", '\0'); + linesz = strlen (line); + if (linesz == 0) + continue; + + /* check starting token */ + if (line[0] != 'S') { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid starting token, got '%c' at line %u", + line[0], ln); + return FALSE; + } + + /* check there's enough data for the smallest possible record */ + if (linesz < 10) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "record incomplete at line %u, length %u", + ln, (guint) linesz); + return FALSE; + } + + /* kind, count, address, (data), checksum, linefeed */ + rec_kind = line[1] - '0'; + rec_count = fu_firmware_strparse_uint8 (line + 2); + if (rec_count * 2 != linesz - 4) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "count incomplete at line %u, " + "length %u, expected %u", + ln, (guint) linesz - 4, (guint) rec_count * 2); + return FALSE; + } + + /* checksum check */ + if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + guint8 rec_csum = 0; + guint8 rec_csum_expected; + for (guint8 i = 0; i < rec_count; i++) + rec_csum += fu_firmware_strparse_uint8 (line + (i * 2) + 2); + rec_csum ^= 0xff; + rec_csum_expected = fu_firmware_strparse_uint8 (line + (rec_count * 2) + 2); + if (rec_csum != rec_csum_expected) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "checksum incorrect line %u, " + "expected %02x, got %02x", + ln, rec_csum_expected, rec_csum); + return FALSE; + } + } + + /* set each command settings */ + switch (rec_kind) { + case 0: + addrsz = 2; + if (got_hdr) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "duplicate header record"); + return FALSE; + } + got_hdr = TRUE; + break; + case 1: + addrsz = 2; + break; + case 2: + addrsz = 3; + break; + case 3: + addrsz = 4; + break; + case 5: + addrsz = 2; + got_eof = TRUE; + break; + case 6: + addrsz = 3; + break; + case 7: + addrsz = 4; + got_eof = TRUE; + break; + case 8: + addrsz = 3; + got_eof = TRUE; + break; + case 9: + addrsz = 2; + got_eof = TRUE; + break; + default: + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid srec record type S%c", + line[1]); + return FALSE; + } + + /* parse address */ + switch (addrsz) { + case 2: + rec_addr32 = fu_firmware_strparse_uint16 (line + 4); + break; + case 3: + rec_addr32 = fu_firmware_strparse_uint24 (line + 4); + break; + case 4: + rec_addr32 = fu_firmware_strparse_uint32 (line + 4); + break; + default: + g_assert_not_reached (); + } + + /* header */ + if (rec_kind == 0) { + g_autoptr(GString) modname = g_string_new (NULL); + if (rec_addr32 != 0x0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid header record address, got %04x", + rec_addr32); + return FALSE; + } + + /* could be anything, lets assume text */ + for (guint8 i = 4 + (addrsz * 2); i <= rec_count * 2; i += 2) { + guint8 tmp = fu_firmware_strparse_uint8 (line + i); + if (!g_ascii_isgraph (tmp)) + break; + g_string_append_c (modname, tmp); + } + if (modname->len != 0) + fu_firmware_image_set_id (img, modname->str); + continue; + } + + /* verify we got all records */ + if (rec_kind == 5) { + if (rec_addr32 != data_cnt) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "count record was not valid, got 0x%02x expected 0x%02x", + (guint) rec_addr32, (guint) data_cnt); + return FALSE; + } + } + + /* data */ + if (rec_kind == 1 || rec_kind == 2 || rec_kind == 3) { + /* invalid */ + if (!got_hdr) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "missing header record"); + return FALSE; + } + /* does not make sense */ + if (rec_addr32 < addr32_last) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid address 0x%x, last was 0x%x", + (guint) rec_addr32, + (guint) addr32_last); + return FALSE; + } + if (rec_addr32 < addr_start) { + g_debug ("ignoring data at 0x%x as before start address 0x%x", + (guint) rec_addr32, (guint) addr_start); + } else { + guint bytecnt = 0; + guint32 len_hole = rec_addr32 - addr32_last; + + /* fill any holes, but only up to 1Mb to avoid a DoS */ + if (addr32_last > 0 && len_hole > 0x100000) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "hole of 0x%x bytes too large to fill", + (guint) len_hole); + return FALSE; + } + if (addr32_last > 0x0 && len_hole > 1) { + g_debug ("filling address 0x%08x to 0x%08x", + addr32_last + 1, addr32_last + len_hole - 1); + for (guint j = 0; j < len_hole; j++) + g_string_append_c (outbuf, 0xff); + } + + /* add data */ + for (guint8 i = 4 + (addrsz * 2); i <= rec_count * 2; i += 2) { + guint8 tmp = fu_firmware_strparse_uint8 (line + i); + g_string_append_c (outbuf, tmp); + bytecnt++; + } + if (img_address == 0x0) + img_address = rec_addr32; + addr32_last = rec_addr32 + bytecnt; + } + data_cnt++; + } + } + + /* no EOF */ + if (!got_eof) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no EOF, perhaps truncated file"); + return FALSE; + } + + /* add single image */ + img_bytes = g_bytes_new (outbuf->str, outbuf->len); + fu_firmware_image_set_bytes (img, img_bytes); + fu_firmware_image_set_addr (img, img_address); + fu_firmware_add_image (firmware, img); + return TRUE; +} + +static void +fu_srec_firmware_init (FuSrecFirmware *self) +{ +} + +static void +fu_srec_firmware_class_init (FuSrecFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass); + klass_firmware->parse = fu_srec_firmware_parse; +} + +FuFirmware * +fu_srec_firmware_new (void) +{ + return FU_FIRMWARE (g_object_new (FU_TYPE_SREC_FIRMWARE, NULL)); +} diff --git a/src/fu-srec-firmware.h b/src/fu-srec-firmware.h new file mode 100644 index 000000000..291548593 --- /dev/null +++ b/src/fu-srec-firmware.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +G_BEGIN_DECLS + +#define FU_TYPE_SREC_FIRMWARE (fu_srec_firmware_get_type ()) +G_DECLARE_FINAL_TYPE (FuSrecFirmware, fu_srec_firmware, FU, SREC_FIRMWARE, FuFirmware) + +FuFirmware *fu_srec_firmware_new (void); + +G_END_DECLS diff --git a/src/meson.build b/src/meson.build index 922831794..35381c468 100644 --- a/src/meson.build +++ b/src/meson.build @@ -38,13 +38,18 @@ libfwupdprivate = static_library( 'fu-chunk.c', 'fu-device.c', 'fu-device-locker.c', + 'fu-firmware.c', + 'fu-firmware-common.c', + 'fu-firmware-image.c', 'fu-hwids.c', 'fu-history.c', + 'fu-ihex-firmware.c', 'fu-io-channel.c', 'fu-plugin.c', 'fu-progressbar.c', 'fu-quirks.c', 'fu-smbios.c', + 'fu-srec-firmware.c', 'fu-test.c', 'fu-udev-device.c', 'fu-usb-device.c', @@ -203,6 +208,7 @@ fwupdtool = executable( 'fu-device-locker.c', 'fu-idle.c', 'fu-install-task.c', + 'fu-ihex-firmware.c', 'fu-io-channel.c', 'fu-keyring.c', 'fu-keyring-utils.c', @@ -211,6 +217,7 @@ fwupdtool = executable( 'fu-plugin-list.c', 'fu-quirks.c', 'fu-smbios.c', + 'fu-srec-firmware.c', 'fu-udev-device.c', 'fu-usb-device.c', 'fu-util-common.c', @@ -286,7 +293,11 @@ executable( 'fu-device.c', 'fu-device-list.c', 'fu-device-locker.c', + 'fu-firmware.c', + 'fu-firmware-common.c', + 'fu-firmware-image.c', 'fu-idle.c', + 'fu-ihex-firmware.c', 'fu-io-channel.c', 'fu-install-task.c', 'fu-keyring.c', @@ -296,6 +307,7 @@ executable( 'fu-plugin-list.c', 'fu-quirks.c', 'fu-smbios.c', + 'fu-srec-firmware.c', 'fu-udev-device.c', 'fu-usb-device.c', ], @@ -353,6 +365,9 @@ if get_option('tests') 'fu-common-version.c', 'fu-config.c', 'fu-engine.c', + 'fu-firmware.c', + 'fu-firmware-common.c', + 'fu-firmware-image.c', 'fu-keyring.c', 'fu-keyring-utils.c', 'fu-hwids.c', @@ -361,6 +376,7 @@ if get_option('tests') 'fu-device-locker.c', 'fu-history.c', 'fu-idle.c', + 'fu-ihex-firmware.c', 'fu-install-task.c', 'fu-io-channel.c', 'fu-keyring.c', @@ -370,6 +386,7 @@ if get_option('tests') 'fu-progressbar.c', 'fu-quirks.c', 'fu-smbios.c', + 'fu-srec-firmware.c', 'fu-test.c', 'fu-udev-device.c', 'fu-usb-device.c', @@ -424,6 +441,12 @@ if get_option('introspection') 'fu-device.h', 'fu-device-locker.c', 'fu-device-locker.h', + 'fu-firmware.c', + 'fu-firmware.h', + 'fu-firmware-common.c', + 'fu-firmware-common.h', + 'fu-firmware-image.c', + 'fu-firmware-image.h', 'fu-io-channel.c', 'fu-plugin.c', 'fu-plugin.h', From cea28dea96f7e436e7e862f56a8e140944f2e6b8 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Fri, 9 Aug 2019 11:16:40 +0100 Subject: [PATCH 025/158] Add a ->to_string() vfunc to FuFirmware This allows firmware to add to the existing image output and will be used for future firmware types. --- src/fu-common.c | 18 ++++++++++++++++++ src/fu-common.h | 4 ++++ src/fu-device.c | 22 +++++----------------- src/fu-firmware-image.c | 23 +++++------------------ src/fu-firmware.c | 10 +++++++++- src/fu-firmware.h | 4 +++- src/fu-self-test.c | 12 ++++++------ 7 files changed, 50 insertions(+), 43 deletions(-) diff --git a/src/fu-common.c b/src/fu-common.c index e2803c240..c264696cb 100644 --- a/src/fu-common.c +++ b/src/fu-common.c @@ -1114,6 +1114,24 @@ fu_common_string_replace (GString *string, const gchar *search, const gchar *rep return count; } +void +fu_common_string_append_kv (GString *str, guint key_offset, const gchar *key, const gchar *value) +{ + const guint align = 32; + + g_return_if_fail (key_offset < align); + + /* ignore */ + if (key == NULL || value == NULL) + return; + for (gsize i = 0; i < key_offset; i++) + g_string_append (str, " "); + g_string_append_printf (str, "%s: ", key); + for (gsize i = strlen (key) + key_offset + 2; i < align; i++) + g_string_append (str, " "); + g_string_append_printf (str, "%s\n", value); +} + /** * fu_common_dump_full: * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL diff --git a/src/fu-common.h b/src/fu-common.h index 25672ab27..62d552bcc 100644 --- a/src/fu-common.h +++ b/src/fu-common.h @@ -152,6 +152,10 @@ guint32 fu_common_read_uint32 (const guint8 *buf, guint fu_common_string_replace (GString *string, const gchar *search, const gchar *replace); +void fu_common_string_append_kv (GString *str, + guint key_offset, + const gchar *key, + const gchar *value); gchar **fu_common_strnsplit (const gchar *str, gsize sz, const gchar *delimiter, diff --git a/src/fu-device.c b/src/fu-device.c index d1e18eacb..cd7550f61 100644 --- a/src/fu-device.c +++ b/src/fu-device.c @@ -1472,18 +1472,6 @@ fu_device_has_custom_flag (FuDevice *self, const gchar *hint) return g_strv_contains ((const gchar * const *) hints, hint); } -static void -fwupd_pad_kv_str (GString *str, const gchar *key, const gchar *value) -{ - /* ignore */ - if (key == NULL || value == NULL) - return; - g_string_append_printf (str, " %s: ", key); - for (gsize i = strlen (key); i < 20; i++) - g_string_append (str, " "); - g_string_append_printf (str, "%s\n", value); -} - /** * fu_device_get_remove_delay: * @self: A #FuDevice @@ -1649,22 +1637,22 @@ fu_device_to_string (FuDevice *self) if (tmp != NULL && tmp[0] != '\0') g_string_append (str, tmp); if (priv->alternate_id != NULL) - fwupd_pad_kv_str (str, "AlternateId", priv->alternate_id); + fu_common_string_append_kv (str, 2, "AlternateId", priv->alternate_id); if (priv->equivalent_id != NULL) - fwupd_pad_kv_str (str, "EquivalentId", priv->equivalent_id); + fu_common_string_append_kv (str, 2, "EquivalentId", priv->equivalent_id); if (priv->size_min > 0) { g_autofree gchar *sz = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->size_min); - fwupd_pad_kv_str (str, "FirmwareSizeMin", sz); + fu_common_string_append_kv (str, 2, "FirmwareSizeMin", sz); } if (priv->size_max > 0) { g_autofree gchar *sz = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->size_max); - fwupd_pad_kv_str (str, "FirmwareSizeMax", sz); + fu_common_string_append_kv (str, 2, "FirmwareSizeMax", sz); } keys = g_hash_table_get_keys (priv->metadata); for (GList *l = keys; l != NULL; l = l->next) { const gchar *key = l->data; const gchar *value = g_hash_table_lookup (priv->metadata, key); - fwupd_pad_kv_str (str, key, value); + fu_common_string_append_kv (str, 2, key, value); } /* subclassed */ diff --git a/src/fu-firmware-image.c b/src/fu-firmware-image.c index 708e8b351..d2212c01d 100644 --- a/src/fu-firmware-image.c +++ b/src/fu-firmware-image.c @@ -8,8 +8,7 @@ #include "config.h" -#include - +#include "fu-common.h" #include "fu-firmware-image.h" /** @@ -240,18 +239,6 @@ fu_firmware_image_get_bytes_chunk (FuFirmwareImage *self, return g_bytes_new_from_bytes (priv->bytes, offset, chunk_sz_max); } -static void -fwupd_pad_kv_str (GString *str, const gchar *key, const gchar *value) -{ - /* ignore */ - if (key == NULL || value == NULL) - return; - g_string_append_printf (str, " %s: ", key); - for (gsize i = strlen (key); i < 20; i++) - g_string_append (str, " "); - g_string_append_printf (str, "%s\n", value); -} - /** * fu_firmware_image_to_string: * @self: A #FuFirmwareImage @@ -268,19 +255,19 @@ fu_firmware_image_to_string (FuFirmwareImage *self) FuFirmwareImagePrivate *priv = GET_PRIVATE (self); GString *str = g_string_new (" FuFirmwareImage:\n"); if (priv->id != NULL) - fwupd_pad_kv_str (str, "ID", priv->id); + fu_common_string_append_kv (str, 4, "ID", priv->id); if (priv->idx != 0x0) { g_autofree gchar *tmp = g_strdup_printf ("0x%04x", (guint) priv->idx); - fwupd_pad_kv_str (str, "Index", tmp); + fu_common_string_append_kv (str, 4, "Index", tmp); } if (priv->addr != 0x0) { g_autofree gchar *tmp = g_strdup_printf ("0x%04x", (guint) priv->addr); - fwupd_pad_kv_str (str, "Address", tmp); + fu_common_string_append_kv (str, 4, "Address", tmp); } if (priv->bytes != NULL) { gsize sz = g_bytes_get_size (priv->bytes); g_autofree gchar *tmp = g_strdup_printf ("%04x", (guint) sz); - fwupd_pad_kv_str (str, "Data", tmp); + fu_common_string_append_kv (str, 4, "Data", tmp); } return g_string_free (str, FALSE); } diff --git a/src/fu-firmware.c b/src/fu-firmware.c index 1cb945aac..da5cc7744 100644 --- a/src/fu-firmware.c +++ b/src/fu-firmware.c @@ -330,8 +330,16 @@ fu_firmware_get_image_default_bytes (FuFirmware *self, GError **error) gchar * fu_firmware_to_string (FuFirmware *self) { + FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS (self); FuFirmwarePrivate *priv = GET_PRIVATE (self); - GString *str = g_string_new ("FuFirmware:\n"); + GString *str = g_string_new (NULL); + + /* subclassed type */ + g_string_append_printf (str, "%s:\n", G_OBJECT_TYPE_NAME (self)); + + /* vfunc */ + if (klass->to_string != NULL) + klass->to_string (self, str); for (guint i = 0; i < priv->images->len; i++) { FuFirmwareImage *img = g_ptr_array_index (priv->images, i); diff --git a/src/fu-firmware.h b/src/fu-firmware.h index a3eb18973..f7814bcc4 100644 --- a/src/fu-firmware.h +++ b/src/fu-firmware.h @@ -27,8 +27,10 @@ struct _FuFirmwareClass GError **error); GBytes *(*write) (FuFirmware *self, GError **error); + void (*to_string) (FuFirmware *self, + GString *str); /*< private >*/ - gpointer padding[30]; + gpointer padding[29]; }; FuFirmware *fu_firmware_new (void); diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 7ff116025..3f55f1b32 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -3900,13 +3900,13 @@ fu_firmware_func (void) str = fu_firmware_to_string (firmware); g_assert_cmpstr (str, ==, "FuFirmware:\n" " FuFirmwareImage:\n" - " ID: primary\n" - " Index: 0x000d\n" - " Address: 0x0200\n" + " ID: primary\n" + " Index: 0x000d\n" + " Address: 0x0200\n" " FuFirmwareImage:\n" - " ID: secondary\n" - " Index: 0x0017\n" - " Address: 0x0400\n"); + " ID: secondary\n" + " Index: 0x0017\n" + " Address: 0x0400\n"); } int From 5308ea4fae961ffed3d514e336c5d63d68e06394 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Fri, 9 Aug 2019 12:25:13 +0100 Subject: [PATCH 026/158] Never use memcpy() in a possibly unsafe way There are several subtle bugs in various places in fwupd caused by not treating user-provided offsets into buffers as unsafe. As fwupd runs as root we have to assume that all user firmware is evil, and also that devices cannot be trusted. Make a helper to put all the logic into one place and convert all users. --- plugins/altos/fu-altos-device.c | 5 +- plugins/colorhug/fu-colorhug-device.c | 22 ++++-- plugins/csr/fu-csr-device.c | 5 +- plugins/dell-dock/fu-dell-dock-status.c | 6 +- plugins/dfu/dfu-cipher-xtea.c | 5 +- plugins/dfu/dfu-format-dfuse.c | 15 +++- plugins/dfu/dfu-format-metadata.c | 12 ++- plugins/dfu/dfu-patch.c | 34 +++++++-- plugins/ebitdo/fu-ebitdo-device.c | 24 ++++-- plugins/modem-manager/fu-qmi-pdc-updater.c | 8 +- plugins/rts54hid/fu-rts54hid-device.c | 5 +- plugins/rts54hid/fu-rts54hid-module.c | 11 ++- .../synaptics-prometheus/fu-synaprom-config.c | 5 +- plugins/unifying/fu-unifying-bootloader.c | 5 +- plugins/unifying/fu-unifying-runtime.c | 5 +- plugins/wacom-usb/fu-wac-device.c | 8 +- plugins/wacom-usb/fu-wac-module-bluetooth.c | 11 ++- src/fu-common.c | 70 ++++++++++++++++++ src/fu-common.h | 8 ++ src/fu-self-test.c | 73 +++++++++++++++++++ 20 files changed, 292 insertions(+), 45 deletions(-) diff --git a/plugins/altos/fu-altos-device.c b/plugins/altos/fu-altos-device.c index 300f3aa2a..c924bff1d 100644 --- a/plugins/altos/fu-altos-device.c +++ b/plugins/altos/fu-altos-device.c @@ -371,7 +371,10 @@ fu_altos_device_write_firmware (FuDevice *device, gsize chunk_len = 0x100; if (i + 0x100 > data_len) chunk_len = data_len - i; - memcpy (buf_tmp, data + i, chunk_len); + if (!fu_memcpy_safe (buf_tmp, sizeof(buf_tmp), 0, /* dst */ + (const guint8 *) data, data_len, i, /* src */ + chunk_len, error)) + return FALSE; } /* verify data from device */ diff --git a/plugins/colorhug/fu-colorhug-device.c b/plugins/colorhug/fu-colorhug-device.c index 1a393ab91..769f33150 100644 --- a/plugins/colorhug/fu-colorhug-device.c +++ b/plugins/colorhug/fu-colorhug-device.c @@ -78,8 +78,12 @@ fu_colorhug_device_msg (FuColorhugDevice *self, guint8 cmd, } /* optionally copy in data */ - if (ibuf != NULL) - memcpy (buf + 1, ibuf, ibufsz); + if (ibuf != NULL) { + if (!fu_memcpy_safe (buf, sizeof(buf), 0x1, /* dst */ + ibuf, ibufsz, 0x0, /* src */ + ibufsz, error)) + return FALSE; + } /* request */ if (g_getenv ("FWUPD_COLORHUG_VERBOSE") != NULL) @@ -151,9 +155,12 @@ fu_colorhug_device_msg (FuColorhugDevice *self, guint8 cmd, } /* copy back optional buf */ - if (obuf != NULL) - memcpy (obuf, buf + 2, obufsz); - + if (obuf != NULL) { + if (!fu_memcpy_safe (obuf, obufsz, 0x0, /* dst */ + buf, sizeof(buf), 0x2, /* src */ + obufsz, error)) + return FALSE; + } return TRUE; } @@ -366,7 +373,10 @@ fu_colorhug_device_write_firmware (FuDevice *device, fu_common_write_uint16 (buf + 0, chk->address, G_LITTLE_ENDIAN); buf[2] = chk->data_sz; buf[3] = ch_colorhug_device_calculate_checksum (chk->data, chk->data_sz); - memcpy (buf + 4, chk->data, chk->data_sz); + if (!fu_memcpy_safe (buf, sizeof(buf), 0x4, /* dst */ + chk->data, chk->data_sz, 0x0, /* src */ + chk->data_sz, error)) + return FALSE; if (!fu_colorhug_device_msg (self, CH_CMD_WRITE_FLASH, buf, sizeof(buf), /* in */ NULL, 0, /* out */ diff --git a/plugins/csr/fu-csr-device.c b/plugins/csr/fu-csr-device.c index 559a93cd6..3d479a2f3 100644 --- a/plugins/csr/fu-csr-device.c +++ b/plugins/csr/fu-csr-device.c @@ -352,7 +352,10 @@ fu_csr_device_download_chunk (FuCsrDevice *self, guint16 idx, GBytes *chunk, GEr buf[1] = FU_CSR_COMMAND_UPGRADE; fu_common_write_uint16 (&buf[2], idx, G_LITTLE_ENDIAN); fu_common_write_uint16 (&buf[4], chunk_sz, G_LITTLE_ENDIAN); - memcpy (buf + FU_CSR_COMMAND_HEADER_SIZE, chunk_data, chunk_sz); + if (!fu_memcpy_safe (buf, sizeof(buf), FU_CSR_COMMAND_HEADER_SIZE, /* dst */ + chunk_data, chunk_sz, 0x0, /* src */ + chunk_sz, error)) + return FALSE; /* hit hardware */ if (g_getenv ("FWUPD_CSR_VERBOSE") != NULL) diff --git a/plugins/dell-dock/fu-dell-dock-status.c b/plugins/dell-dock/fu-dell-dock-status.c index 9c803bfbd..d8ac892b7 100644 --- a/plugins/dell-dock/fu-dell-dock-status.c +++ b/plugins/dell-dock/fu-dell-dock-status.c @@ -78,8 +78,10 @@ fu_dell_dock_status_write (FuDevice *device, if (fw == NULL) return FALSE; data = g_bytes_get_data (fw, &length); - - memcpy (&status_version, data + self->blob_version_offset, sizeof (guint32)); + if (!fu_memcpy_safe ((guint8 *) &status_version, sizeof(status_version), 0x0, /* dst */ + data, length, self->blob_version_offset, /* src */ + sizeof(status_version), error)) + return FALSE; dynamic_version = fu_dell_dock_status_ver_string (status_version); g_debug ("writing status firmware version %s", dynamic_version); diff --git a/plugins/dfu/dfu-cipher-xtea.c b/plugins/dfu/dfu-cipher-xtea.c index b686e7237..9a3240d79 100644 --- a/plugins/dfu/dfu-cipher-xtea.c +++ b/plugins/dfu/dfu-cipher-xtea.c @@ -55,7 +55,10 @@ dfu_tool_parse_xtea_key (const gchar *key, guint32 *keys, GError **error) guint64 tmp; /* copy to 4-char buf (with NUL) */ - memcpy (buf, key + i*8, 8); + if (!fu_memcpy_safe ((guint8 *) buf, sizeof(buf), 0x0, /* dst */ + (const guint8 *) key, key_len, i * 8, /* src */ + 8, error)) + return FALSE; tmp = g_ascii_strtoull (buf, &endptr, 16); if (endptr && endptr[0] != '\0') { g_set_error (error, diff --git a/plugins/dfu/dfu-format-dfuse.c b/plugins/dfu/dfu-format-dfuse.c index 1e5308361..0dd3a4d33 100644 --- a/plugins/dfu/dfu-format-dfuse.c +++ b/plugins/dfu/dfu-format-dfuse.c @@ -8,6 +8,8 @@ #include +#include "fu-common.h" + #include "dfu-element.h" #include "dfu-format-dfuse.h" #include "dfu-image.h" @@ -232,7 +234,13 @@ dfu_image_to_dfuse (DfuImage *image) gsize length; GBytes *bytes = g_ptr_array_index (element_array, i); const guint8 *data = g_bytes_get_data (bytes, &length); - memcpy (buf + offset, data, length); + g_autoptr(GError) error = NULL; + if (!fu_memcpy_safe (buf, length_total + sizeof (DfuSeImagePrefix), offset, /* dst */ + data, length, 0x0, /* src */ + length, &error)) { + g_critical ("failed to pack buffer: %s", error->message); + continue; + } offset += (guint32) length; } return g_bytes_new_take (buf, length_total + sizeof (DfuSeImagePrefix)); @@ -300,7 +308,10 @@ dfu_firmware_to_dfuse (DfuFirmware *firmware, GError **error) gsize length; const guint8 *data; data = g_bytes_get_data (contents, &length); - memcpy (buf + offset, data, length); + if (!fu_memcpy_safe (buf, sizeof (DfuSePrefix) + image_size_total, offset, /* dst */ + data, length, 0x0, /* src */ + length, error)) + return NULL; offset += (guint32) length; } diff --git a/plugins/dfu/dfu-format-metadata.c b/plugins/dfu/dfu-format-metadata.c index 519992a2c..b98420e31 100644 --- a/plugins/dfu/dfu-format-metadata.c +++ b/plugins/dfu/dfu-format-metadata.c @@ -8,6 +8,8 @@ #include +#include "fu-common.h" + #include "dfu-element.h" #include "dfu-format-metadata.h" #include "dfu-image.h" @@ -188,12 +190,18 @@ dfu_firmware_to_metadata (DfuFirmware *firmware, GError **error) /* write the key */ mdbuf[idx++] = (guint8) key_len; - memcpy(mdbuf + idx, key, key_len); + if (!fu_memcpy_safe (mdbuf, sizeof(mdbuf), idx, /* dst */ + (const guint8 *) key, key_len, 0x0, /* src */ + key_len, error)) + return NULL; idx += key_len; /* write the value */ mdbuf[idx++] = (guint8) value_len; - memcpy(mdbuf + idx, value, value_len); + if (!fu_memcpy_safe (mdbuf, sizeof(mdbuf), idx, /* dst */ + (const guint8 *) value, value_len, 0x0, /* src */ + value_len, error)) + return NULL; idx += value_len; } g_debug ("metadata table was %u/%" G_GSIZE_FORMAT " bytes", diff --git a/plugins/dfu/dfu-patch.c b/plugins/dfu/dfu-patch.c index 6d42a7916..d1e2b7d63 100644 --- a/plugins/dfu/dfu-patch.c +++ b/plugins/dfu/dfu-patch.c @@ -23,6 +23,8 @@ #include #include +#include "fu-common.h" + #include "dfu-common.h" #include "dfu-patch.h" @@ -137,14 +139,18 @@ dfu_patch_export (DfuPatch *self, GError **error) if (priv->checksum_old != NULL) { gsize csum_sz = 0; const guint8 *csum_data = g_bytes_get_data (priv->checksum_old, &csum_sz); - memcpy (data + G_STRUCT_OFFSET(DfuPatchFileHeader,checksum_old), - csum_data, csum_sz); + if (!fu_memcpy_safe (data, sz, G_STRUCT_OFFSET(DfuPatchFileHeader,checksum_old), /* dst */ + csum_data, csum_sz, 0x0, /* src */ + csum_sz, error)) + return NULL; } if (priv->checksum_new != NULL) { gsize csum_sz = 0; const guint8 *csum_data = g_bytes_get_data (priv->checksum_new, &csum_sz); - memcpy (data + G_STRUCT_OFFSET(DfuPatchFileHeader,checksum_new), - csum_data, csum_sz); + if (!fu_memcpy_safe (data, sz, G_STRUCT_OFFSET(DfuPatchFileHeader,checksum_new), /* dst */ + csum_data, csum_sz, 0x0, /* src */ + csum_sz, error)) + return NULL; } addr = sizeof(DfuPatchFileHeader); @@ -158,9 +164,15 @@ dfu_patch_export (DfuPatch *self, GError **error) chunkhdr.off = GUINT32_TO_LE (chunk->off); chunkhdr.sz = GUINT32_TO_LE (sz_tmp); chunkhdr.flags = 0; - memcpy (data + addr, &chunkhdr, sizeof(DfuPatchChunkHeader)); - memcpy (data + addr + sizeof(DfuPatchChunkHeader), data_new, sz_tmp); + if (!fu_memcpy_safe (data, sz, addr, /* dst */ + (const guint8 *) &chunkhdr, sizeof(DfuPatchChunkHeader), 0x0, /* src */ + sizeof(DfuPatchChunkHeader), error)) + return NULL; + if (!fu_memcpy_safe (data, sz, addr + sizeof(DfuPatchChunkHeader), /* dst */ + data_new, sz_tmp, 0x0, /* src */ + sz_tmp, error)) + return NULL; /* move up after the copied data */ addr += sizeof(DfuPatchChunkHeader) + sz_tmp; } @@ -506,7 +518,10 @@ dfu_patch_apply (DfuPatch *self, GBytes *blob, DfuPatchApplyFlags flags, GError } data_new = g_malloc0 (sz_max); - memcpy (data_new, data_old, MIN (sz, sz_max)); + if (!fu_memcpy_safe (data_new, sz_max, 0x0, /* dst */ + data_old, sz, 0x0, /* src */ + MIN (sz, sz_max), error)) + return FALSE; for (guint i = 0; i < priv->chunks->len; i++) { DfuPatchChunk *chunk = g_ptr_array_index (priv->chunks, i); const guint8 *chunk_data; @@ -525,7 +540,10 @@ dfu_patch_apply (DfuPatch *self, GBytes *blob, DfuPatchApplyFlags flags, GError /* apply one chunk */ g_debug ("applying chunk %u/%u @0x%04x (length %" G_GSIZE_FORMAT ")", i + 1, priv->chunks->len, chunk->off, chunk_sz); - memcpy (data_new + chunk->off, chunk_data, chunk_sz); + if (!fu_memcpy_safe (data_new, sz_max, chunk->off, /* dst */ + chunk_data, chunk_sz, 0x0, /* src */ + chunk_sz, error)) + return NULL; } /* check we got the desired hash */ diff --git a/plugins/ebitdo/fu-ebitdo-device.c b/plugins/ebitdo/fu-ebitdo-device.c index 6ee60e2a5..1ee9d2a68 100644 --- a/plugins/ebitdo/fu-ebitdo-device.c +++ b/plugins/ebitdo/fu-ebitdo-device.c @@ -56,7 +56,10 @@ fu_ebitdo_device_send (FuEbitdoDevice *self, hdr->cmd_len = GUINT16_TO_LE (in_len + 3); hdr->cmd = cmd; hdr->payload_len = GUINT16_TO_LE (in_len); - memcpy (packet + 0x08, in, in_len); + if (!fu_memcpy_safe (packet, sizeof(packet), 0x08, /* dst */ + in, in_len, 0x0, /* src */ + in_len, error)) + return FALSE; hdr->pkt_len = (guint8) (in_len + 7); } else { hdr->cmd_len = GUINT16_TO_LE (in_len + 1); @@ -145,9 +148,10 @@ fu_ebitdo_device_receive (FuEbitdoDevice *self, hdr->payload_len); return FALSE; } - memcpy (out, - packet + sizeof(FuEbitdoPkt), - hdr->payload_len); + if (!fu_memcpy_safe (out, out_len, 0x0, /* dst */ + packet, sizeof(packet), sizeof(FuEbitdoPkt), /* src */ + hdr->payload_len, error)) + return FALSE; } return TRUE; } @@ -163,7 +167,10 @@ fu_ebitdo_device_receive (FuEbitdoDevice *self, out_len); return FALSE; } - memcpy (out, packet + 1, 4); + if (!fu_memcpy_safe (out, out_len, 0x0, /* dst */ + packet, sizeof(packet), 0x1, /* src */ + 4, error)) + return FALSE; } return TRUE; } @@ -181,9 +188,10 @@ fu_ebitdo_device_receive (FuEbitdoDevice *self, hdr->cmd_len); return FALSE; } - memcpy (out, - packet + sizeof(FuEbitdoPkt) - 3, - hdr->cmd_len); + if (!fu_memcpy_safe (out, out_len, 0x0, /* dst */ + packet, sizeof(packet), sizeof(FuEbitdoPkt) - 3, /* src */ + hdr->cmd_len, error)) + return FALSE; } return TRUE; } diff --git a/plugins/modem-manager/fu-qmi-pdc-updater.c b/plugins/modem-manager/fu-qmi-pdc-updater.c index 7e997a9a9..1be45368c 100644 --- a/plugins/modem-manager/fu-qmi-pdc-updater.c +++ b/plugins/modem-manager/fu-qmi-pdc-updater.c @@ -343,6 +343,7 @@ fu_qmi_pdc_updater_load_config (WriteContext *ctx) g_autoptr(GArray) chunk = NULL; gsize full_size; gsize chunk_size; + g_autoptr(GError) error = NULL; input = qmi_message_pdc_load_config_input_new (); qmi_message_pdc_load_config_input_set_token (input, ctx->token++, NULL); @@ -355,7 +356,12 @@ fu_qmi_pdc_updater_load_config (WriteContext *ctx) chunk = g_array_sized_new (FALSE, FALSE, sizeof (guint8), chunk_size); g_array_set_size (chunk, chunk_size); - memcpy (chunk->data, (const guint8 *)g_bytes_get_data (ctx->blob, NULL) + ctx->offset, chunk_size); + if (!fu_memcpy_safe (chunk->data, chunk_size, 0x0, /* dst */ + (const guint8 *)g_bytes_get_data (ctx->blob, NULL), /* src */ + g_bytes_get_size (ctx->blob), ctx->offset, + chunk_size, &error)) { + g_critical ("failed to copy chunk: %s", error->message); + } qmi_message_pdc_load_config_input_set_config_chunk (input, QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, diff --git a/plugins/rts54hid/fu-rts54hid-device.c b/plugins/rts54hid/fu-rts54hid-device.c index 66b105e02..cd7344eee 100644 --- a/plugins/rts54hid/fu-rts54hid-device.c +++ b/plugins/rts54hid/fu-rts54hid-device.c @@ -149,7 +149,10 @@ fu_rts54hid_device_write_flash (FuRts54HidDevice *self, g_return_val_if_fail (data_sz != 0, FALSE); memcpy (buf, &cmd_buffer, sizeof(cmd_buffer)); - memcpy (buf + FU_RTS54HID_CMD_BUFFER_OFFSET_DATA, data, data_sz); + if (!fu_memcpy_safe (buf, sizeof(buf), FU_RTS54HID_CMD_BUFFER_OFFSET_DATA, /* dst */ + data, data_sz, 0x0, /* src */ + data_sz, error)) + return FALSE; if (!fu_rts54hid_device_set_report (self, buf, sizeof(buf), error)) { g_prefix_error (error, "failed to write flash @%08x: ", (guint) addr); return FALSE; diff --git a/plugins/rts54hid/fu-rts54hid-module.c b/plugins/rts54hid/fu-rts54hid-module.c index d7e86ee4c..1402dfb64 100644 --- a/plugins/rts54hid/fu-rts54hid-module.c +++ b/plugins/rts54hid/fu-rts54hid-module.c @@ -74,7 +74,10 @@ fu_rts54hid_module_i2c_write (FuRts54HidModule *self, return FALSE; memcpy (buf, &cmd_buffer, sizeof(cmd_buffer)); - memcpy (buf + FU_RTS54HID_CMD_BUFFER_OFFSET_DATA, data, data_sz); + if (!fu_memcpy_safe (buf, sizeof(buf), FU_RTS54HID_CMD_BUFFER_OFFSET_DATA, /* dst */ + data, data_sz, 0x0, /* src */ + data_sz, error)) + return FALSE; if (!fu_rts54hid_device_set_report (parent, buf, sizeof(buf), error)) { g_prefix_error (error, "failed to write i2c @%04x: ", self->slave_addr); return FALSE; @@ -118,9 +121,9 @@ fu_rts54hid_module_i2c_read (FuRts54HidModule *self, } if (!fu_rts54hid_device_get_report (parent, buf, sizeof(buf), error)) return FALSE; - memcpy (data, buf + FU_RTS54HID_CMD_BUFFER_OFFSET_DATA, data_sz); - - return TRUE; + return fu_memcpy_safe (data, data_sz, 0x0, + buf, sizeof(buf), FU_RTS54HID_CMD_BUFFER_OFFSET_DATA, + data_sz, error); } static gboolean diff --git a/plugins/synaptics-prometheus/fu-synaprom-config.c b/plugins/synaptics-prometheus/fu-synaprom-config.c index f0b9c365d..2005ec0d0 100644 --- a/plugins/synaptics-prometheus/fu-synaprom-config.c +++ b/plugins/synaptics-prometheus/fu-synaprom-config.c @@ -98,7 +98,10 @@ fu_synaprom_config_setup (FuDevice *device, GError **error) GUINT32_FROM_LE(hdr.itype)); return FALSE; } - memcpy (&cfg, reply->data + sizeof(hdr), sizeof(cfg)); + if (!fu_memcpy_safe ((guint8 *) &cfg, sizeof(cfg), 0x0, /* dst */ + reply->data, reply->len, sizeof(hdr), /* src */ + sizeof(cfg), error)) + return FALSE; self->configid1 = GUINT32_FROM_LE(cfg.config_id1); self->configid2 = GUINT32_FROM_LE(cfg.config_id2); g_debug ("id1=%u, id2=%u, ver=%u", diff --git a/plugins/unifying/fu-unifying-bootloader.c b/plugins/unifying/fu-unifying-bootloader.c index 3e148d8c2..8497ef544 100644 --- a/plugins/unifying/fu-unifying-bootloader.c +++ b/plugins/unifying/fu-unifying-bootloader.c @@ -296,7 +296,10 @@ fu_unifying_bootloader_request (FuUnifyingBootloader *self, buf_request[0x01] = req->addr >> 8; buf_request[0x02] = req->addr & 0xff; buf_request[0x03] = req->len; - memcpy (buf_request + 0x04, req->data, 28); + if (!fu_memcpy_safe (buf_request, sizeof(buf_request), 0x04, /* dst */ + req->data, sizeof(req->data), 0x0, /* src */ + sizeof(req->data), error)) + return FALSE; /* send request */ if (g_getenv ("FWUPD_UNIFYING_VERBOSE") != NULL) { diff --git a/plugins/unifying/fu-unifying-runtime.c b/plugins/unifying/fu-unifying-runtime.c index f843ec583..d3730eb62 100644 --- a/plugins/unifying/fu-unifying-runtime.c +++ b/plugins/unifying/fu-unifying-runtime.c @@ -206,7 +206,10 @@ fu_unifying_runtime_setup_internal (FuDevice *device, GError **error) g_prefix_error (error, "failed to read device config: "); return FALSE; } - memcpy (config + (i * 2), msg->data + 1, 2); + if (!fu_memcpy_safe (config, sizeof(config), i * 2, /* dst */ + msg->data, sizeof(msg->data), 0x1, /* src */ + 2, error)) + return FALSE; } /* get firmware version */ diff --git a/plugins/wacom-usb/fu-wac-device.c b/plugins/wacom-usb/fu-wac-device.c index 8f1556582..738697f28 100644 --- a/plugins/wacom-usb/fu-wac-device.c +++ b/plugins/wacom-usb/fu-wac-device.c @@ -381,8 +381,12 @@ fu_wac_device_write_block (FuWacDevice *self, memset (buf, 0xff, bufsz); buf[0] = FU_WAC_REPORT_ID_WRITE_BLOCK; fu_common_write_uint32 (buf + 1, addr, G_LITTLE_ENDIAN); - if (sz > 0) - memcpy (buf + 5, tmp, sz); + if (sz > 0) { + if (!fu_memcpy_safe (buf, bufsz, 0x5, /* dst */ + tmp, sz, 0x0, /* src */ + sz, error)) + return FALSE; + } /* hit hardware */ return fu_wac_device_set_feature_report (self, buf, bufsz, diff --git a/plugins/wacom-usb/fu-wac-module-bluetooth.c b/plugins/wacom-usb/fu-wac-module-bluetooth.c index d4d07b673..5c7ada5a7 100644 --- a/plugins/wacom-usb/fu-wac-module-bluetooth.c +++ b/plugins/wacom-usb/fu-wac-module-bluetooth.c @@ -72,7 +72,7 @@ fu_wac_module_bluetooth_calculate_crc (const guint8 *data, gsize sz) } static GPtrArray * -fu_wac_module_bluetooth_parse_blocks (const guint8 *data, gsize sz, gboolean skip_user_data) +fu_wac_module_bluetooth_parse_blocks (const guint8 *data, gsize sz, gboolean skip_user_data, GError **error) { const guint8 preamble[] = {0x02, 0x00, 0x0f, 0x06, 0x01, 0x08, 0x01}; GPtrArray *blocks = g_ptr_array_new_with_free_func (g_free); @@ -96,7 +96,10 @@ fu_wac_module_bluetooth_parse_blocks (const guint8 *data, gsize sz, gboolean ski /* if file is not in multiples of payload size */ if (addr + FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ >= sz) cdata_sz = sz - addr; - memcpy (bd->cdata, data + addr, cdata_sz); + if (!fu_memcpy_safe (bd->cdata, sizeof(bd->cdata), 0x0, /* dst */ + data, sz, addr, /* src */ + cdata_sz, error)) + return NULL; bd->crc = fu_wac_module_bluetooth_calculate_crc (bd->cdata, FU_WAC_MODULE_BLUETOOTH_PAYLOAD_SZ); g_ptr_array_add (blocks, bd); @@ -126,7 +129,9 @@ fu_wac_module_bluetooth_write_firmware (FuDevice *device, /* build each data packet */ data = g_bytes_get_data (fw, &len); - blocks = fu_wac_module_bluetooth_parse_blocks (data, len, TRUE); + blocks = fu_wac_module_bluetooth_parse_blocks (data, len, TRUE, error); + if (blocks == NULL) + return FALSE; blocks_total = blocks->len + 2; /* start, which will erase the module */ diff --git a/src/fu-common.c b/src/fu-common.c index c264696cb..4055784d1 100644 --- a/src/fu-common.c +++ b/src/fu-common.c @@ -1431,3 +1431,73 @@ fu_common_strnsplit (const gchar *str, gsize sz, } return g_strsplit (str, delimiter, max_tokens); } + +/** + * fu_memcpy_safe: + * @dst: destination buffer + * @dst_sz: maximum size of @dst, typically `sizeof(dst)` + * @dst_offset: offset in bytes into @dst to copy to + * @src: source buffer + * @src_sz: maximum size of @dst, typically `sizeof(src)` + * @src_offset: offset in bytes into @src to copy from + * @n: number of bytes to copy from @src+@offset from + * @error: A #GError or %NULL + * + * Copies some memory using memcpy in a safe way. Providing the buffer sizes + * of both the destination and the source allows us to check for buffer overflow. + * + * Providing the buffer offsets also allows us to check reading past the end of + * the source buffer. For this reason the caller should NEVER add an offset to + * @src or @dst. + * + * You don't need to use this function in "obviously correct" cases, nor should + * you use it when performance is a concern. Only us it when you're not sure if + * malicious data from a device or firmware could cause memory corruption. + * + * Return value: %TRUE if the bytes were copied, %FALSE otherwise + **/ +gboolean +fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset, + const guint8 *src, gsize src_sz, gsize src_offset, + gsize n, GError **error) +{ + if (n == 0) + return TRUE; + + if (n > src_sz) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "attempted to read 0x%02x bytes from buffer of 0x%02x", + (guint) n, (guint) src_sz); + return FALSE; + } + if (n + src_offset > src_sz) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x", + (guint) n, (guint) src_offset, (guint) src_sz); + return FALSE; + } + if (n > dst_sz) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "attempted to write 0x%02x bytes to buffer of 0x%02x", + (guint) n, (guint) dst_sz); + return FALSE; + } + if (n + dst_offset > dst_sz) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x", + (guint) n, (guint) dst_offset, (guint) dst_sz); + return FALSE; + } + + /* phew! */ + memcpy (dst + dst_offset, src + src_offset, n); + return TRUE; +} diff --git a/src/fu-common.h b/src/fu-common.h index 62d552bcc..dccd0df4a 100644 --- a/src/fu-common.h +++ b/src/fu-common.h @@ -135,6 +135,14 @@ gboolean fu_common_bytes_compare (GBytes *bytes1, GError **error); GBytes *fu_common_bytes_pad (GBytes *bytes, gsize sz); +gboolean fu_memcpy_safe (guint8 *dst, + gsize dst_sz, + gsize dst_offset, + const guint8 *src, + gsize src_sz, + gsize src_offset, + gsize n, + GError **error); typedef guint FuEndianType; diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 3f55f1b32..c705a7f22 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -3855,6 +3855,78 @@ fu_firmware_srec_func (void) g_assert_true (ret); } +static void +fu_memcpy_func (void) +{ + const guint8 src[] = {'a', 'b', 'c', 'd', 'e' }; + gboolean ret; + guint8 dst[4]; + g_autoptr(GError) error = NULL; + + /* copy entire buffer */ + ret = fu_memcpy_safe (dst, sizeof(dst), 0x0, + src, sizeof(src), 0x0, + 4, &error); + g_assert_no_error (error); + g_assert_true (ret); + g_assert (memcmp (src, dst, 4) == 0); + + /* copy first char */ + ret = fu_memcpy_safe (dst, sizeof(dst), 0x0, + src, sizeof(src), 0x0, + 1, &error); + g_assert_no_error (error); + g_assert_true (ret); + g_assert_cmpint (dst[0], ==, 'a'); + + /* copy last char */ + ret = fu_memcpy_safe (dst, sizeof(dst), 0x0, + src, sizeof(src), 0x4, + 1, &error); + g_assert_no_error (error); + g_assert_true (ret); + g_assert_cmpint (dst[0], ==, 'e'); + + /* copy nothing */ + ret = fu_memcpy_safe (dst, sizeof(dst), 0x0, + src, sizeof(src), 0x0, + 0, &error); + g_assert_no_error (error); + g_assert_true (ret); + + /* write past the end of dst */ + ret = fu_memcpy_safe (dst, sizeof(dst), 0x0, + src, sizeof(src), 0x0, + 5, &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_WRITE); + g_assert_false (ret); + g_clear_error (&error); + + /* write past the end of dst with offset */ + ret = fu_memcpy_safe (dst, sizeof(dst), 0x1, + src, sizeof(src), 0x0, + 4, &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_WRITE); + g_assert_false (ret); + g_clear_error (&error); + + /* read past past the end of dst */ + ret = fu_memcpy_safe (dst, sizeof(dst), 0x0, + src, sizeof(src), 0x0, + 6, &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_READ); + g_assert_false (ret); + g_clear_error (&error); + + /* read past the end of src with offset */ + ret = fu_memcpy_safe (dst, sizeof(dst), 0x0, + src, sizeof(src), 0x4, + 4, &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_READ); + g_assert_false (ret); + g_clear_error (&error); +} + static void fu_firmware_func (void) { @@ -3929,6 +4001,7 @@ main (int argc, char **argv) /* tests go here */ if (g_test_slow ()) g_test_add_func ("/fwupd/progressbar", fu_progressbar_func); + g_test_add_func ("/fwupd/memcpy", fu_memcpy_func); g_test_add_func ("/fwupd/firmware", fu_firmware_func); g_test_add_func ("/fwupd/firmware{ihex}", fu_firmware_ihex_func); g_test_add_func ("/fwupd/firmware{ihex-offset}", fu_firmware_ihex_offset_func); From d0e952bd93ce06900291f063d81636b86b51eea2 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 13 Aug 2019 17:05:54 +0100 Subject: [PATCH 027/158] trivial: Remove unused variable --- plugins/synapticsmst/synapticsmst-device.c | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/synapticsmst/synapticsmst-device.c b/plugins/synapticsmst/synapticsmst-device.c index b02ad1ad9..52846a72c 100644 --- a/plugins/synapticsmst/synapticsmst-device.c +++ b/plugins/synapticsmst/synapticsmst-device.c @@ -46,7 +46,6 @@ typedef struct guint32 board_id; guint16 chip_id; gchar *chip_id_str; - GPtrArray *guids; gchar *aux_node; guint8 layer; guint16 rad; From ba3d91380478111b7716e8c0774a5881ec7d7bbb Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 13 Aug 2019 15:59:51 +0100 Subject: [PATCH 028/158] synapticsmst: Use a more suitable icon for the device --- plugins/synapticsmst/fu-plugin-synapticsmst.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/synapticsmst/fu-plugin-synapticsmst.c b/plugins/synapticsmst/fu-plugin-synapticsmst.c index 98ccd23a8..7a9b264e0 100644 --- a/plugins/synapticsmst/fu-plugin-synapticsmst.c +++ b/plugins/synapticsmst/fu-plugin-synapticsmst.c @@ -188,7 +188,7 @@ fu_plugin_synaptics_add_device (FuPlugin *plugin, fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_set_vendor (dev, "Synaptics"); fu_device_set_summary (dev, "Multi-Stream Transport Device"); - fu_device_add_icon (dev, "computer"); + fu_device_add_icon (dev, "video-display"); fu_device_set_version (dev, synapticsmst_device_get_version (device), FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_quirks (dev, fu_plugin_get_quirks (plugin)); From c85a89eff278d93d450c95e0ccff4a0d4c429198 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 13 Aug 2019 16:00:58 +0100 Subject: [PATCH 029/158] synapticsmst: Display the board ID in non-hex form in the error message The SynapticsMSTBoardID quirk option is generated with the non-hex version, so output the same format to avoid head-scratching when adding new models. --- plugins/synapticsmst/fu-plugin-synapticsmst.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/synapticsmst/fu-plugin-synapticsmst.c b/plugins/synapticsmst/fu-plugin-synapticsmst.c index 7a9b264e0..fcfaf6788 100644 --- a/plugins/synapticsmst/fu-plugin-synapticsmst.c +++ b/plugins/synapticsmst/fu-plugin-synapticsmst.c @@ -124,7 +124,7 @@ fu_plugin_synapticsmst_lookup_device (FuPlugin *plugin, g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "Unknown board_id %x", + "Unknown board_id %u", board_id); return FALSE; From c6db17fd6a5f2e73dd0e8ac7d5d1fe106e1091f7 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 13 Aug 2019 16:04:53 +0100 Subject: [PATCH 030/158] synapticsmst: Add a SynapticsMSTBoardID for a Lenovo dock --- plugins/synapticsmst/synapticsmst.quirk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/synapticsmst/synapticsmst.quirk b/plugins/synapticsmst/synapticsmst.quirk index 6a5c8ad2e..a5631ae5e 100644 --- a/plugins/synapticsmst/synapticsmst.quirk +++ b/plugins/synapticsmst/synapticsmst.quirk @@ -38,3 +38,7 @@ DeviceKind = system [SynapticsMSTBoardID=259] Name = Dell dock DeviceKind = panamera + +[SynapticsMSTBoardID=595] +Name = ThinkPad Thunderbolt 3 Workstation Dock +DeviceKind = panamera From 63eb12a2415801b6cc30eb3ff652f1331fa650fc Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 14 Aug 2019 10:36:25 -0500 Subject: [PATCH 031/158] trivial: remove unnecessary CUSTOMERID_DELL define This was previously used when the plugin was only run on Dell devices. The plugin was already adjusted to run on other vendor's firmware but this definition was not removed. --- plugins/synapticsmst/synapticsmst-device.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/synapticsmst/synapticsmst-device.h b/plugins/synapticsmst/synapticsmst-device.h index 401a22150..165d13bbf 100644 --- a/plugins/synapticsmst/synapticsmst-device.h +++ b/plugins/synapticsmst/synapticsmst-device.h @@ -46,8 +46,6 @@ typedef enum { SYNAPTICSMST_CHIP_KIND_LAST } SynapticsMSTChipKind; -#define CUSTOMERID_DELL 0x1 - SynapticsMSTDevice *synapticsmst_device_new (SynapticsMSTDeviceKind kind, const gchar *aux_node, guint8 layer, From 21ef40f19b161089a30ed8080e96139d8571b164 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 14 Aug 2019 09:38:32 +0100 Subject: [PATCH 032/158] synapticsmst: Add a SynapticsMSTBoardID for another Lenovo dock --- plugins/synapticsmst/synapticsmst.quirk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/synapticsmst/synapticsmst.quirk b/plugins/synapticsmst/synapticsmst.quirk index a5631ae5e..7d3dc32e3 100644 --- a/plugins/synapticsmst/synapticsmst.quirk +++ b/plugins/synapticsmst/synapticsmst.quirk @@ -39,6 +39,10 @@ DeviceKind = system Name = Dell dock DeviceKind = panamera +[SynapticsMSTBoardID=513] +Name = ThinkPad Workstation Dock +DeviceKind = panamera + [SynapticsMSTBoardID=595] Name = ThinkPad Thunderbolt 3 Workstation Dock DeviceKind = panamera From b962bc206bb27aff572cdc00263990423ec2f8cb Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 14 Aug 2019 09:52:11 +0100 Subject: [PATCH 033/158] synapticsmst: Use G_DECLARE_FINAL_TYPE to simplify the object No logic changes. --- plugins/synapticsmst/synapticsmst-device.c | 283 +++++++++------------ plugins/synapticsmst/synapticsmst-device.h | 31 +-- 2 files changed, 140 insertions(+), 174 deletions(-) diff --git a/plugins/synapticsmst/synapticsmst-device.c b/plugins/synapticsmst/synapticsmst-device.c index 52846a72c..a664aa984 100644 --- a/plugins/synapticsmst/synapticsmst-device.c +++ b/plugins/synapticsmst/synapticsmst-device.c @@ -39,8 +39,8 @@ #define FLASH_SETTLE_TIME 5000000 /* us */ -typedef struct -{ +struct _SynapticsMSTDevice { + GObject parent_instance; SynapticsMSTDeviceKind kind; gchar *version; guint32 board_id; @@ -53,11 +53,9 @@ typedef struct gboolean has_cascade; gchar *fw_dir; gboolean test_mode; -} SynapticsMSTDevicePrivate; +}; -G_DEFINE_TYPE_WITH_PRIVATE (SynapticsMSTDevice, synapticsmst_device, G_TYPE_OBJECT) - -#define GET_PRIVATE(o) (synapticsmst_device_get_instance_private (o)) +G_DEFINE_TYPE (SynapticsMSTDevice, synapticsmst_device, G_TYPE_OBJECT) SynapticsMSTDeviceKind synapticsmst_device_kind_from_string (const gchar *kind) @@ -82,31 +80,29 @@ synapticsmst_device_kind_to_string (SynapticsMSTDeviceKind kind) static void synapticsmst_device_finalize (GObject *object) { - SynapticsMSTDevice *device = SYNAPTICSMST_DEVICE (object); - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); + SynapticsMSTDevice *self = SYNAPTICSMST_DEVICE (object); - if (priv->fd > 0) - close (priv->fd); + if (self->fd > 0) + close (self->fd); - g_free (priv->fw_dir); - g_free (priv->aux_node); - g_free (priv->version); - g_free (priv->chip_id_str); + g_free (self->fw_dir); + g_free (self->aux_node); + g_free (self->version); + g_free (self->chip_id_str); G_OBJECT_CLASS (synapticsmst_device_parent_class)->finalize (object); } static void -synapticsmst_device_init (SynapticsMSTDevice *device) +synapticsmst_device_init (SynapticsMSTDevice *self) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); const gchar *tmp; tmp = g_getenv ("FWUPD_SYNAPTICSMST_FW_DIR"); if (tmp == NULL) { - priv->test_mode = FALSE; - priv->fw_dir = g_strdup ("/dev"); + self->test_mode = FALSE; + self->fw_dir = g_strdup ("/dev"); } else { - priv->test_mode = TRUE; - priv->fw_dir = g_strdup (tmp); + self->test_mode = TRUE; + self->fw_dir = g_strdup (tmp); } } @@ -118,32 +114,29 @@ synapticsmst_device_class_init (SynapticsMSTDeviceClass *klass) } SynapticsMSTDeviceKind -synapticsmst_device_get_kind (SynapticsMSTDevice *device) +synapticsmst_device_get_kind (SynapticsMSTDevice *self) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); - return priv->kind; + return self->kind; } guint16 -synapticsmst_device_get_board_id (SynapticsMSTDevice *device) +synapticsmst_device_get_board_id (SynapticsMSTDevice *self) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); - return priv->board_id; + return self->board_id; } static gboolean -synapticsmst_device_enable_remote_control (SynapticsMSTDevice *device, GError **error) +synapticsmst_device_enable_remote_control (SynapticsMSTDevice *self, GError **error) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); g_autoptr(SynapticsMSTConnection) connection = NULL; /* in test mode we need to open a different file node instead */ - if (priv->test_mode) { + if (self->test_mode) { g_autofree gchar *filename = NULL; - close(priv->fd); + close(self->fd); filename = g_strdup_printf ("%s/remote/%s", - priv->fw_dir, - priv->aux_node); + self->fw_dir, + self->aux_node); if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { g_set_error (error, G_IO_ERROR, @@ -152,8 +145,8 @@ synapticsmst_device_enable_remote_control (SynapticsMSTDevice *device, GError ** filename); return FALSE; } - priv->fd = open (filename, O_RDWR); - if (priv->fd == -1) { + self->fd = open (filename, O_RDWR); + if (self->fd == -1) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), @@ -164,7 +157,7 @@ synapticsmst_device_enable_remote_control (SynapticsMSTDevice *device, GError ** return TRUE; } - connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); + connection = synapticsmst_common_new (self->fd, self->layer, self->rad); if (!synapticsmst_common_enable_remote_control (connection, error)) return FALSE; @@ -172,18 +165,17 @@ synapticsmst_device_enable_remote_control (SynapticsMSTDevice *device, GError ** } static gboolean -synapticsmst_device_disable_remote_control (SynapticsMSTDevice *device, GError **error) +synapticsmst_device_disable_remote_control (SynapticsMSTDevice *self, GError **error) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); g_autoptr(SynapticsMSTConnection) connection = NULL; /* in test mode we need to open a different file node instead */ - if (priv->test_mode) { + if (self->test_mode) { g_autofree gchar *filename = NULL; - close(priv->fd); + close(self->fd); filename = g_strdup_printf ("%s/%s", - priv->fw_dir, - priv->aux_node); + self->fw_dir, + self->aux_node); if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { g_set_error (error, G_IO_ERROR, @@ -192,8 +184,8 @@ synapticsmst_device_disable_remote_control (SynapticsMSTDevice *device, GError * filename); return FALSE; } - priv->fd = open (filename, O_RDWR); - if (priv->fd == -1) { + self->fd = open (filename, O_RDWR); + if (self->fd == -1) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, @@ -204,7 +196,7 @@ synapticsmst_device_disable_remote_control (SynapticsMSTDevice *device, GError * return TRUE; } - connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); + connection = synapticsmst_common_new (self->fd, self->layer, self->rad); if (!synapticsmst_common_disable_remote_control (connection, error)) return FALSE; @@ -212,33 +204,32 @@ synapticsmst_device_disable_remote_control (SynapticsMSTDevice *device, GError * } gboolean -synapticsmst_device_scan_cascade_device (SynapticsMSTDevice *device, +synapticsmst_device_scan_cascade_device (SynapticsMSTDevice *self, GError **error, guint8 tx_port) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); - guint8 layer = priv->layer + 1; - guint16 rad = priv->rad | (tx_port << (2 * (priv->layer))); + guint8 layer = self->layer + 1; + guint16 rad = self->rad | (tx_port << (2 * (self->layer))); guint8 byte[4]; g_autoptr(SynapticsMSTConnection) connection = NULL; g_autoptr(GError) error_local = NULL; g_autoptr(FuDeviceLocker) locker = NULL; - if (priv->test_mode) + if (self->test_mode) return TRUE; /* reset */ - priv->has_cascade = FALSE; + self->has_cascade = FALSE; /* enable remote control and disable on exit */ - locker = fu_device_locker_new_full (device, + locker = fu_device_locker_new_full (self, (FuDeviceLockerFunc) synapticsmst_device_enable_remote_control, (FuDeviceLockerFunc) synapticsmst_device_disable_remote_control, error); if (locker == NULL) return FALSE; - connection = synapticsmst_common_new (priv->fd, layer, rad); + connection = synapticsmst_common_new (self->fd, layer, rad); if (!synapticsmst_common_read (connection, REG_RC_CAP, byte, 1, &error_local)) { g_debug ("No cascade device found: %s", error_local->message); return TRUE; @@ -251,26 +242,25 @@ synapticsmst_device_scan_cascade_device (SynapticsMSTDevice *device, return FALSE; } if (byte[0] == 0x90 && byte[1] == 0xCC && byte[2] == 0x24) - priv->has_cascade = TRUE; + self->has_cascade = TRUE; } return TRUE; } static gboolean -synapticsmst_device_read_board_id (SynapticsMSTDevice *device, +synapticsmst_device_read_board_id (SynapticsMSTDevice *self, SynapticsMSTConnection *connection, guint8 *byte, GError **error) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); - if (priv->test_mode) { + if (self->test_mode) { g_autofree gchar *filename = NULL; gint fd; filename = g_strdup_printf ("%s/remote/%s_eeprom", - priv->fw_dir, - priv->aux_node); + self->fw_dir, + self->aux_node); if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { g_set_error (error, G_IO_ERROR, @@ -314,16 +304,15 @@ synapticsmst_device_read_board_id (SynapticsMSTDevice *device, } static gboolean -synapticsmst_device_get_active_bank_panamera (SynapticsMSTDevice *device, +synapticsmst_device_get_active_bank_panamera (SynapticsMSTDevice *self, guint8 *bank_out, GError **error) { g_autoptr(SynapticsMSTConnection) connection = NULL; - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); guint32 dwData[16]; /* get used bank */ - connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); + connection = synapticsmst_common_new (self->fd, self->layer, self->rad); if (!synapticsmst_common_rc_get_command (connection, UPDC_READ_FROM_MEMORY, ((sizeof(dwData)/sizeof(dwData[0]))*4), @@ -343,23 +332,22 @@ synapticsmst_device_get_active_bank_panamera (SynapticsMSTDevice *device, } gboolean -synapticsmst_device_enumerate_device (SynapticsMSTDevice *device, +synapticsmst_device_enumerate_device (SynapticsMSTDevice *self, GError **error) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); guint8 byte[16]; guint8 bank; g_autoptr(SynapticsMSTConnection) connection = NULL; g_autoptr(FuDeviceLocker) locker = NULL; - if (!synapticsmst_device_open (device, error)) { + if (!synapticsmst_device_open (self, error)) { g_prefix_error (error, "Failed to open device in DP Aux Node %s: ", - synapticsmst_device_get_aux_node (device)); + synapticsmst_device_get_aux_node (self)); return FALSE; } /* enable remote control and disable on exit */ - locker = fu_device_locker_new_full (device, + locker = fu_device_locker_new_full (self, (FuDeviceLockerFunc) synapticsmst_device_enable_remote_control, (FuDeviceLockerFunc) synapticsmst_device_disable_remote_control, error); @@ -367,18 +355,18 @@ synapticsmst_device_enumerate_device (SynapticsMSTDevice *device, return FALSE; /* read firmware version */ - connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); + connection = synapticsmst_common_new (self->fd, self->layer, self->rad); if (!synapticsmst_common_read (connection, REG_FIRMWARE_VERSION, byte, 3, error)) return FALSE; - priv->version = g_strdup_printf ("%1d.%02d.%03d", byte[0], byte[1], byte[2]); + self->version = g_strdup_printf ("%1d.%02d.%03d", byte[0], byte[1], byte[2]); /* read board ID */ - if (!synapticsmst_device_read_board_id (device, connection, byte, error)) + if (!synapticsmst_device_read_board_id (self, connection, byte, error)) return FALSE; - priv->board_id = (byte[0] << 8) | (byte[1]); - g_debug ("BoardID %x", priv->board_id); + self->board_id = (byte[0] << 8) | (byte[1]); + g_debug ("BoardID %x", self->board_id); /* read board chip_id */ if (!synapticsmst_common_read (connection, REG_CHIP_ID, @@ -386,76 +374,68 @@ synapticsmst_device_enumerate_device (SynapticsMSTDevice *device, g_prefix_error (error, "failed to read chip id: "); return FALSE; } - priv->chip_id = (byte[0] << 8) | (byte[1]); - priv->chip_id_str = g_strdup_printf ("VMM%02x%02x", byte[0], byte[1]); + self->chip_id = (byte[0] << 8) | (byte[1]); + self->chip_id_str = g_strdup_printf ("VMM%02x%02x", byte[0], byte[1]); /* if running on panamera, check the active bank (for debugging logs) */ - if (priv->chip_id > 0x5000 && - !synapticsmst_device_get_active_bank_panamera (device, &bank, error)) + if (self->chip_id > 0x5000 && + !synapticsmst_device_get_active_bank_panamera (self, &bank, error)) return FALSE; return TRUE; } const gchar * -synapticsmst_device_get_aux_node (SynapticsMSTDevice *device) +synapticsmst_device_get_aux_node (SynapticsMSTDevice *self) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); - return priv->aux_node; + return self->aux_node; } const gchar * -synapticsmst_device_get_version (SynapticsMSTDevice *device) +synapticsmst_device_get_version (SynapticsMSTDevice *self) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); - return priv->version; + return self->version; } static guint16 -synapticsmst_device_get_chip_id (SynapticsMSTDevice *device) +synapticsmst_device_get_chip_id (SynapticsMSTDevice *self) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); - return priv->chip_id; + return self->chip_id; } const gchar * -synapticsmst_device_get_chip_id_str (SynapticsMSTDevice *device) +synapticsmst_device_get_chip_id_str (SynapticsMSTDevice *self) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); - return priv->chip_id_str; + return self->chip_id_str; } guint16 -synapticsmst_device_get_rad (SynapticsMSTDevice *device) +synapticsmst_device_get_rad (SynapticsMSTDevice *self) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); - return priv->rad; + return self->rad; } guint8 -synapticsmst_device_get_layer (SynapticsMSTDevice *device) +synapticsmst_device_get_layer (SynapticsMSTDevice *self) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); - return priv->layer; + return self->layer; } gboolean -synapticsmst_device_get_cascade (SynapticsMSTDevice *device) +synapticsmst_device_get_cascade (SynapticsMSTDevice *self) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); - return priv->has_cascade; + return self->has_cascade; } static gboolean -synapticsmst_device_get_flash_checksum (SynapticsMSTDevice *device, +synapticsmst_device_get_flash_checksum (SynapticsMSTDevice *self, guint32 length, guint32 offset, guint32 *checksum, GError **error) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); g_autoptr(SynapticsMSTConnection) connection = NULL; - connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); + connection = synapticsmst_common_new (self->fd, self->layer, self->rad); if (!synapticsmst_common_rc_special_get_command (connection, UPDC_CAL_EEPROM_CHECKSUM, length, offset, @@ -528,16 +508,15 @@ synapticsmst_device_get_crc (guint16 crc, guint8 type, guint32 length, const gui } static gboolean -synapticsmst_device_set_flash_sector_erase (SynapticsMSTDevice *device, +synapticsmst_device_set_flash_sector_erase (SynapticsMSTDevice *self, guint16 rc_cmd, guint16 offset, GError **error) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); guint16 us_data; g_autoptr(SynapticsMSTConnection) connection = NULL; - connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); + connection = synapticsmst_common_new (self->fd, self->layer, self->rad); /* Need to add Wp control ? */ us_data = rc_cmd + offset; @@ -554,13 +533,12 @@ synapticsmst_device_set_flash_sector_erase (SynapticsMSTDevice *device, } static gboolean -synapticsmst_device_update_esm (SynapticsMSTDevice *device, +synapticsmst_device_update_esm (SynapticsMSTDevice *self, const guint8 *payload_data, GFileProgressCallback progress_cb, gpointer progress_data, GError **error) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); guint32 checksum = 0; guint32 esm_sz = ESM_CODE_SIZE; guint32 flash_checksum = 0; @@ -568,11 +546,11 @@ synapticsmst_device_update_esm (SynapticsMSTDevice *device, guint32 write_loops = 0; g_autoptr(SynapticsMSTConnection) connection = NULL; - connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); + connection = synapticsmst_common_new (self->fd, self->layer, self->rad); for (guint32 i = 0; i < esm_sz; i++) checksum += *(payload_data + EEPROM_ESM_OFFSET +i); - if (!synapticsmst_device_get_flash_checksum (device, + if (!synapticsmst_device_get_flash_checksum (self, esm_sz, EEPROM_ESM_OFFSET, &flash_checksum, error)) { @@ -595,7 +573,7 @@ synapticsmst_device_update_esm (SynapticsMSTDevice *device, /* erase ESM firmware; erase failure is fatal */ for (guint32 j = 0; j < 4; j++) { - if (!synapticsmst_device_set_flash_sector_erase (device, + if (!synapticsmst_device_set_flash_sector_erase (self, FLASH_SECTOR_ERASE_64K, j + 4, error)) { @@ -634,7 +612,7 @@ synapticsmst_device_update_esm (SynapticsMSTDevice *device, flash_checksum = 0; for (guint32 i = 0; i < esm_sz; i++) checksum += *(payload_data + EEPROM_ESM_OFFSET +i); - if (!synapticsmst_device_get_flash_checksum (device, + if (!synapticsmst_device_get_flash_checksum (self, esm_sz, EEPROM_ESM_OFFSET, &flash_checksum, @@ -661,14 +639,13 @@ synapticsmst_device_update_esm (SynapticsMSTDevice *device, } static gboolean -synapticsmst_device_update_tesla_leaf_firmware (SynapticsMSTDevice *device, +synapticsmst_device_update_tesla_leaf_firmware (SynapticsMSTDevice *self, guint32 payload_len, const guint8 *payload_data, GFileProgressCallback progress_cb, gpointer progress_data, GError **error) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); g_autoptr(SynapticsMSTConnection) connection = NULL; guint32 data_to_write = 0; guint32 offset = 0; @@ -680,12 +657,12 @@ synapticsmst_device_update_tesla_leaf_firmware (SynapticsMSTDevice *device, if (payload_len % BLOCK_UNIT) write_loops++; - connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); + connection = synapticsmst_common_new (self->fd, self->layer, self->rad); for (guint32 retries_cnt = 0; ; retries_cnt++) { guint32 checksum = 0; guint32 flash_checksum = 0; - if (!synapticsmst_device_set_flash_sector_erase (device, 0xffff, 0, error)) + if (!synapticsmst_device_set_flash_sector_erase (self, 0xffff, 0, error)) return FALSE; g_debug ("Waiting for flash clear to settle"); g_usleep (FLASH_SETTLE_TIME); @@ -727,7 +704,7 @@ synapticsmst_device_update_tesla_leaf_firmware (SynapticsMSTDevice *device, for (guint32 i = 0; i < payload_len; i++) checksum += *(payload_data + i); - if (!synapticsmst_device_get_flash_checksum (device, + if (!synapticsmst_device_get_flash_checksum (self, payload_len, 0, &flash_checksum, @@ -752,7 +729,7 @@ synapticsmst_device_update_tesla_leaf_firmware (SynapticsMSTDevice *device, } static gboolean -synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *device, +synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *self, guint32 payload_len, const guint8 *payload_data, GFileProgressCallback progress_cb, @@ -771,10 +748,9 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *device, struct tm *pTM; time_t timeptr; g_autoptr(SynapticsMSTConnection) connection = NULL; - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); /* get used bank */ - if (!synapticsmst_device_get_active_bank_panamera (device, &bank_in_use, error)) + if (!synapticsmst_device_get_active_bank_panamera (self, &bank_in_use, error)) return FALSE; if (bank_in_use == BANKTAG_1) bank_to_update = BANKTAG_0; @@ -805,10 +781,10 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *device, /* erase storage */ erase_offset = bank_to_update * 2; - if (!synapticsmst_device_set_flash_sector_erase (device, + if (!synapticsmst_device_set_flash_sector_erase (self, FLASH_SECTOR_ERASE_64K, erase_offset++, error)) return FALSE; - if (!synapticsmst_device_set_flash_sector_erase (device, + if (!synapticsmst_device_set_flash_sector_erase (self, FLASH_SECTOR_ERASE_64K, erase_offset, error)) return FALSE; g_debug ("Waiting for flash clear to settle"); @@ -817,7 +793,7 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *device, /* write */ write_idx = 0; write_offset = EEPROM_BANK_OFFSET * bank_to_update; - connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); + connection = synapticsmst_common_new (self->fd, self->layer, self->rad); for (guint32 i = 0; i < write_loops ; i++ ) { g_autoptr(GError) error_local = NULL; if (!synapticsmst_common_rc_set_command (connection, @@ -943,7 +919,7 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *device, guint32 erase_offset; /* offset for last 4k of bank# */ erase_offset = (EEPROM_BANK_OFFSET * bank_in_use + EEPROM_BANK_OFFSET - 0x1000) / 0x1000; - if (!synapticsmst_device_set_flash_sector_erase (device, + if (!synapticsmst_device_set_flash_sector_erase (self, FLASH_SECTOR_ERASE_4K, erase_offset, error)) @@ -985,7 +961,7 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *device, } static gboolean -synapticsmst_device_check_firmware_content (SynapticsMSTDevice *device, +synapticsmst_device_check_firmware_content (SynapticsMSTDevice *self, GBytes *fw, SynapticsMSTChipKind chip_type, GError **error) @@ -1026,15 +1002,14 @@ synapticsmst_device_check_firmware_content (SynapticsMSTDevice *device, } static gboolean -synapticsmst_device_panamera_prepare_write (SynapticsMSTDevice *device, GError **error) +synapticsmst_device_panamera_prepare_write (SynapticsMSTDevice *self, GError **error) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); guint32 dwData[4] = {0}; g_autoptr(SynapticsMSTConnection) connection = NULL; /* Need to detect flash mode and ESM first ? */ /* disable flash Quad mode and ESM/HDCP2.2*/ - connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); + connection = synapticsmst_common_new (self->fd, self->layer, self->rad); /* disable ESM first */ dwData[0] = 0x21; @@ -1090,16 +1065,15 @@ synapticsmst_device_panamera_prepare_write (SynapticsMSTDevice *device, GError * } static gboolean -synapticsmst_device_restart (SynapticsMSTDevice *device, +synapticsmst_device_restart (SynapticsMSTDevice *self, GError **error) { g_autoptr(SynapticsMSTConnection) connection = NULL; - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); guint8 dwData[4] = {0xF5, 0, 0 ,0}; g_autoptr(GError) error_local = NULL; /* issue the reboot command, ignore return code (triggers before returning) */ - connection = synapticsmst_common_new (priv->fd, priv->layer, priv->rad); + connection = synapticsmst_common_new (self->fd, self->layer, self->rad); if (!synapticsmst_common_rc_set_command (connection, UPDC_WRITE_TO_MEMORY, 4, (gint) 0x2000FC, (guint8*) &dwData, @@ -1110,7 +1084,7 @@ synapticsmst_device_restart (SynapticsMSTDevice *device, } gboolean -synapticsmst_device_write_firmware (SynapticsMSTDevice *device, +synapticsmst_device_write_firmware (SynapticsMSTDevice *self, GBytes *fw, GFileProgressCallback progress_cb, gpointer progress_data, @@ -1126,19 +1100,19 @@ synapticsmst_device_write_firmware (SynapticsMSTDevice *device, payload_data = g_bytes_get_data (fw, &payload_len); - if (synapticsmst_device_get_chip_id (device) > 0x5000) + if (synapticsmst_device_get_chip_id (self) > 0x5000) chip_type = SYNAPTICSMST_CHIP_KIND_PANAMERA; else chip_type = SYNAPTICSMST_CHIP_KIND_TESLA_LEAF; - if (!synapticsmst_device_check_firmware_content (device, fw, chip_type, error)){ + if (!synapticsmst_device_check_firmware_content (self, fw, chip_type, error)){ g_prefix_error (error, "Invalid file content: "); return FALSE; } /* check firmware and board ID again */ tmp = (*(payload_data + ADDR_CUSTOMER_ID) << 8) + *(payload_data + ADDR_BOARD_ID); - if (tmp != synapticsmst_device_get_board_id (device) && !install_force) { + if (tmp != synapticsmst_device_get_board_id (self) && !install_force) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, @@ -1147,21 +1121,21 @@ synapticsmst_device_write_firmware (SynapticsMSTDevice *device, } /* open device */ - if (!synapticsmst_device_open (device, error)) { + if (!synapticsmst_device_open (self, error)) { g_prefix_error (error, "can't open DP Aux node %s", - synapticsmst_device_get_aux_node (device)); + synapticsmst_device_get_aux_node (self)); return FALSE; } /* enable remote control and disable on exit */ if (reboot) { - locker = fu_device_locker_new_full (device, + locker = fu_device_locker_new_full (self, (FuDeviceLockerFunc) synapticsmst_device_enable_remote_control, (FuDeviceLockerFunc) synapticsmst_device_restart, error); } else { - locker = fu_device_locker_new_full (device, + locker = fu_device_locker_new_full (self, (FuDeviceLockerFunc) synapticsmst_device_enable_remote_control, (FuDeviceLockerFunc) synapticsmst_device_disable_remote_control, error); @@ -1171,11 +1145,11 @@ synapticsmst_device_write_firmware (SynapticsMSTDevice *device, /* update firmware */ if (chip_type == SYNAPTICSMST_CHIP_KIND_PANAMERA) { - if (!synapticsmst_device_panamera_prepare_write (device, error)) { + if (!synapticsmst_device_panamera_prepare_write (self, error)) { g_prefix_error (error, "Failed to prepare for write: "); return FALSE; } - if (!synapticsmst_device_update_esm (device, + if (!synapticsmst_device_update_esm (self, payload_data, progress_cb, progress_data, @@ -1183,7 +1157,7 @@ synapticsmst_device_write_firmware (SynapticsMSTDevice *device, g_prefix_error (error, "ESM update failed: "); return FALSE; } - if (!synapticsmst_device_update_panamera_firmware (device, + if (!synapticsmst_device_update_panamera_firmware (self, payload_len, payload_data, progress_cb, @@ -1193,7 +1167,7 @@ synapticsmst_device_write_firmware (SynapticsMSTDevice *device, return FALSE; } } else { - if (!synapticsmst_device_update_tesla_leaf_firmware (device, + if (!synapticsmst_device_update_tesla_leaf_firmware (self, payload_len, payload_data, progress_cb, @@ -1213,32 +1187,29 @@ synapticsmst_device_new (SynapticsMSTDeviceKind kind, guint8 layer, guint16 rad) { - SynapticsMSTDevice *device; - SynapticsMSTDevicePrivate *priv; + SynapticsMSTDevice *self; - device = g_object_new (SYNAPTICSMST_TYPE_DEVICE, NULL); - priv = GET_PRIVATE (device); + self = g_object_new (SYNAPTICSMST_TYPE_DEVICE, NULL); - priv->aux_node = g_strdup(aux_node); - priv->kind = kind; - priv->version = NULL; - priv->layer = layer; - priv->rad = rad; - priv->has_cascade = FALSE; + self->aux_node = g_strdup(aux_node); + self->kind = kind; + self->version = NULL; + self->layer = layer; + self->rad = rad; + self->has_cascade = FALSE; - return SYNAPTICSMST_DEVICE (device); + return SYNAPTICSMST_DEVICE (self); } gboolean -synapticsmst_device_open (SynapticsMSTDevice *device, GError **error) +synapticsmst_device_open (SynapticsMSTDevice *self, GError **error) { - SynapticsMSTDevicePrivate *priv = GET_PRIVATE (device); g_autofree gchar *filename = NULL; guint8 byte[4]; g_autoptr(SynapticsMSTConnection) connection = NULL; /* file doesn't exist on this system */ - filename = g_strdup_printf ("%s/%s", priv->fw_dir, priv->aux_node); + filename = g_strdup_printf ("%s/%s", self->fw_dir, self->aux_node); if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { g_set_error (error, G_IO_ERROR, @@ -1249,8 +1220,8 @@ synapticsmst_device_open (SynapticsMSTDevice *device, GError **error) } /* can't open aux node, try use sudo to get the permission */ - priv->fd = open (filename, O_RDWR); - if (priv->fd == -1) { + self->fd = open (filename, O_RDWR); + if (self->fd == -1) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), @@ -1259,7 +1230,7 @@ synapticsmst_device_open (SynapticsMSTDevice *device, GError **error) return FALSE; } - connection = synapticsmst_common_new (priv->fd, 0, 0); + connection = synapticsmst_common_new (self->fd, 0, 0); if (!synapticsmst_common_read (connection, REG_RC_CAP, byte, 1, error)) { g_prefix_error (error, "failed to read device: "); return FALSE; diff --git a/plugins/synapticsmst/synapticsmst-device.h b/plugins/synapticsmst/synapticsmst-device.h index 165d13bbf..cf3d1ca0b 100644 --- a/plugins/synapticsmst/synapticsmst-device.h +++ b/plugins/synapticsmst/synapticsmst-device.h @@ -13,15 +13,10 @@ G_BEGIN_DECLS #define SYNAPTICSMST_TYPE_DEVICE (synapticsmst_device_get_type ()) -G_DECLARE_DERIVABLE_TYPE (SynapticsMSTDevice, synapticsmst_device, SYNAPTICSMST, DEVICE, GObject) +G_DECLARE_FINAL_TYPE (SynapticsMSTDevice, synapticsmst_device, SYNAPTICSMST, DEVICE, GObject) #define SYSFS_DRM_DP_AUX "/sys/class/drm_dp_aux_dev" -struct _SynapticsMSTDeviceClass -{ - GObjectClass parent_class; -}; - /** * SynapticsMSTDeviceKind: * @SYNAPTICSMST_DEVICE_KIND_UNKNOWN: Type invalid or not known @@ -54,27 +49,27 @@ SynapticsMSTDevice *synapticsmst_device_new (SynapticsMSTDeviceKind kind, /* helpers */ SynapticsMSTDeviceKind synapticsmst_device_kind_from_string (const gchar *kind); const gchar *synapticsmst_device_kind_to_string (SynapticsMSTDeviceKind kind); -GPtrArray *synapticsmst_device_get_guids (SynapticsMSTDevice *device); -gboolean synapticsmst_device_scan_cascade_device (SynapticsMSTDevice *device, +GPtrArray *synapticsmst_device_get_guids (SynapticsMSTDevice *self); +gboolean synapticsmst_device_scan_cascade_device (SynapticsMSTDevice *self, GError **error, guint8 tx_port); -gboolean synapticsmst_device_open (SynapticsMSTDevice *device, +gboolean synapticsmst_device_open (SynapticsMSTDevice *self, GError **error); /* getters */ -SynapticsMSTDeviceKind synapticsmst_device_get_kind (SynapticsMSTDevice *device); -guint16 synapticsmst_device_get_board_id (SynapticsMSTDevice *device); -const gchar *synapticsmst_device_get_version (SynapticsMSTDevice *device); -const gchar *synapticsmst_device_get_chip_id_str (SynapticsMSTDevice *device); -const gchar *synapticsmst_device_get_aux_node (SynapticsMSTDevice *device); -guint16 synapticsmst_device_get_rad (SynapticsMSTDevice *device); -guint8 synapticsmst_device_get_layer (SynapticsMSTDevice *device); -gboolean synapticsmst_device_get_cascade (SynapticsMSTDevice *device); +SynapticsMSTDeviceKind synapticsmst_device_get_kind (SynapticsMSTDevice *self); +guint16 synapticsmst_device_get_board_id (SynapticsMSTDevice *self); +const gchar *synapticsmst_device_get_version (SynapticsMSTDevice *self); +const gchar *synapticsmst_device_get_chip_id_str (SynapticsMSTDevice *self); +const gchar *synapticsmst_device_get_aux_node (SynapticsMSTDevice *self); +guint16 synapticsmst_device_get_rad (SynapticsMSTDevice *self); +guint8 synapticsmst_device_get_layer (SynapticsMSTDevice *self); +gboolean synapticsmst_device_get_cascade (SynapticsMSTDevice *self); /* object methods */ gboolean synapticsmst_device_enumerate_device (SynapticsMSTDevice *devices, GError **error); -gboolean synapticsmst_device_write_firmware (SynapticsMSTDevice *device, +gboolean synapticsmst_device_write_firmware (SynapticsMSTDevice *self, GBytes *fw, GFileProgressCallback progress_cb, gpointer user_data, From 6e3e62bee8b37f6e18f0134586edee670e55d12a Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 14 Aug 2019 10:43:08 +0100 Subject: [PATCH 034/158] Use fu_common_string_append_kv() in subclassed device Also define the indent level in the the ->to_string vfunc so we can correctly print client devices in all cases. --- plugins/ata/fu-ata-device.c | 13 ++-- plugins/csr/fu-csr-device.c | 7 +- plugins/dell-dock/fu-dell-dock-i2c-ec.c | 42 ++++------- plugins/fastboot/fu-fastboot-device.c | 9 ++- plugins/modem-manager/fu-mm-device.c | 15 ++-- plugins/nvme/fu-nvme-device.c | 7 +- plugins/rts54hid/fu-rts54hid-device.c | 7 +- plugins/rts54hid/fu-rts54hid-module.c | 9 ++- plugins/rts54hub/fu-rts54hub-device.c | 9 ++- plugins/superio/fu-superio-device.c | 15 ++-- plugins/uefi/fu-uefi-device.c | 28 +++----- plugins/unifying/fu-unifying-bootloader.c | 11 ++- plugins/unifying/fu-unifying-peripheral.c | 30 ++++---- plugins/unifying/fu-unifying-runtime.c | 4 +- plugins/wacom-raw/fu-wacom-device.c | 11 ++- plugins/wacom-usb/fu-wac-device.c | 65 ++++++++--------- plugins/wacom-usb/fu-wac-module.c | 15 ++-- src/fu-common.c | 43 ++++++++--- src/fu-common.h | 14 +++- src/fu-device.c | 87 +++++++++++++---------- src/fu-device.h | 1 + src/fu-firmware-image-private.h | 17 +++++ src/fu-firmware-image.c | 38 +++++----- src/fu-firmware-image.h | 2 +- src/fu-firmware.c | 9 +-- src/fu-firmware.h | 1 + src/fu-self-test.c | 14 ++-- 27 files changed, 278 insertions(+), 245 deletions(-) create mode 100644 src/fu-firmware-image-private.h diff --git a/plugins/ata/fu-ata-device.c b/plugins/ata/fu-ata-device.c index 73858d4ab..9b894edd9 100644 --- a/plugins/ata/fu-ata-device.c +++ b/plugins/ata/fu-ata-device.c @@ -119,15 +119,14 @@ fu_ata_device_get_string (const guint16 *buf, guint start, guint end) } static void -fu_ata_device_to_string (FuDevice *device, GString *str) +fu_ata_device_to_string (FuDevice *device, guint idt, GString *str) { FuAtaDevice *self = FU_ATA_DEVICE (device); - g_string_append (str, " FuAtaDevice:\n"); - g_string_append_printf (str, " fd:\t\t\t%i\n", self->fd); - g_string_append_printf (str, " transfer-mode:\t0x%x\n", (guint) self->transfer_mode); - g_string_append_printf (str, " transfer-size:\t0x%x\n", (guint) self->transfer_blocks); - g_string_append_printf (str, " pci-depth:\t\t%u\n", self->pci_depth); - g_string_append_printf (str, " usb-depth:\t\t%u\n", self->usb_depth); + fu_common_string_append_ku (str, idt, "FD", (guint) self->fd); + fu_common_string_append_kx (str, idt, "TransferMode", self->transfer_mode); + fu_common_string_append_kx (str, idt, "TransferBlocks", self->transfer_blocks); + fu_common_string_append_ku (str, idt, "PciDepth", self->pci_depth); + fu_common_string_append_ku (str, idt, "UsbDepth", self->usb_depth); } /* https://docs.microsoft.com/en-us/windows-hardware/drivers/install/identifiers-for-ide-devices */ diff --git a/plugins/csr/fu-csr-device.c b/plugins/csr/fu-csr-device.c index 3d479a2f3..550878a5c 100644 --- a/plugins/csr/fu-csr-device.c +++ b/plugins/csr/fu-csr-device.c @@ -59,12 +59,11 @@ G_DEFINE_TYPE (FuCsrDevice, fu_csr_device, FU_TYPE_USB_DEVICE) #define FU_CSR_DEVICE_TIMEOUT 5000 /* ms */ static void -fu_csr_device_to_string (FuDevice *device, GString *str) +fu_csr_device_to_string (FuDevice *device, guint idt, GString *str) { FuCsrDevice *self = FU_CSR_DEVICE (device); - g_string_append (str, " DfuCsrDevice:\n"); - g_string_append_printf (str, " state:\t\t%s\n", dfu_state_to_string (self->dfu_state)); - g_string_append_printf (str, " timeout:\t\t%" G_GUINT32_FORMAT "\n", self->dnload_timeout); + fu_common_string_append_kv (str, idt, "State", dfu_state_to_string (self->dfu_state)); + fu_common_string_append_ku (str, idt, "DownloadTimeout", self->dnload_timeout); } static gboolean diff --git a/plugins/dell-dock/fu-dell-dock-i2c-ec.c b/plugins/dell-dock/fu-dell-dock-i2c-ec.c index 6d3c9a5cb..f8e6a3aed 100644 --- a/plugins/dell-dock/fu-dell-dock-i2c-ec.c +++ b/plugins/dell-dock/fu-dell-dock-i2c-ec.c @@ -529,39 +529,25 @@ fu_dell_dock_ec_get_dock_data (FuDevice *device, } static void -fu_dell_dock_ec_to_string (FuDevice *device, GString *str) +fu_dell_dock_ec_to_string (FuDevice *device, guint idt, GString *str) { FuDellDockEc *self = FU_DELL_DOCK_EC (device); gchar service_tag[8] = {0x00}; - g_string_append (str, " FuDellDellDockEc:\n"); - g_string_append_printf (str, "\tboard ID: %u\n", - self->data->board_id); - g_string_append_printf (str, "\tpower supply: %uW\n", - self->data->power_supply_wattage); - g_string_append_printf (str, "\tstatus (port0): %x\n", - self->data->port0_dock_status); - g_string_append_printf (str, "\tstatus (port1): %x\n", - self->data->port1_dock_status); + fu_common_string_append_ku (str, idt, "BoardId", self->data->board_id); + fu_common_string_append_ku (str, idt, "PowerSupply", self->data->power_supply_wattage); + fu_common_string_append_kx (str, idt, "StatusPort0", self->data->port0_dock_status); + fu_common_string_append_kx (str, idt, "StatusPort1", self->data->port1_dock_status); memcpy (service_tag, self->data->service_tag, 7); - g_string_append_printf (str, "\tservice tag: %s\n", - service_tag); - g_string_append_printf (str, "\tconfiguration: %u\n", - self->data->dock_configuration); - g_string_append_printf (str, "\tpackage firmware version: %x\n", - self->data->dock_firmware_pkg_ver); - g_string_append_printf (str, "\tmodule serial #: %08" G_GUINT64_FORMAT "\n", - self->data->module_serial); - g_string_append_printf (str, "\toriginal module serial #: %08" G_GUINT64_FORMAT "\n", - self->data->original_module_serial); - g_string_append_printf (str, "\ttype: %u\n", - self->data->dock_type); - g_string_append_printf (str, "\tmodule type: %x\n", - self->data->module_type); - g_string_append_printf (str, "\tminimum ec: %s\n", - self->ec_minimum_version); - g_string_append_printf (str, "\tpassive flow: %d\n", - self->passive_flow); + fu_common_string_append_kv (str, idt, "ServiceTag", service_tag); + fu_common_string_append_ku (str, idt, "Configuration", self->data->dock_configuration); + fu_common_string_append_kx (str, idt, "PackageFirmwareVersion", self->data->dock_firmware_pkg_ver); + fu_common_string_append_ku (str, idt, "ModuleSerial", self->data->module_serial); + fu_common_string_append_ku (str, idt, "OriginalModuleSerial", self->data->original_module_serial); + fu_common_string_append_ku (str, idt, "Type", self->data->dock_type); + fu_common_string_append_kx (str, idt, "ModuleType", self->data->module_type); + fu_common_string_append_kv (str, idt, "MinimumEc", self->ec_minimum_version); + fu_common_string_append_ku (str, idt, "PassiveFlow", self->passive_flow); } gboolean diff --git a/plugins/fastboot/fu-fastboot-device.c b/plugins/fastboot/fu-fastboot-device.c index db9482b19..4bc9af6f8 100644 --- a/plugins/fastboot/fu-fastboot-device.c +++ b/plugins/fastboot/fu-fastboot-device.c @@ -30,13 +30,12 @@ struct _FuFastbootDevice { G_DEFINE_TYPE (FuFastbootDevice, fu_fastboot_device, FU_TYPE_USB_DEVICE) static void -fu_fastboot_device_to_string (FuDevice *device, GString *str) +fu_fastboot_device_to_string (FuDevice *device, guint idt, GString *str) { FuFastbootDevice *self = FU_FASTBOOT_DEVICE (device); - g_string_append (str, " FuFastbootDevice:\n"); - g_string_append_printf (str, " intf:\t0x%02x\n", (guint) self->intf_nr); - g_string_append_printf (str, " secure:\t%i\n", self->secure); - g_string_append_printf (str, " blocksz:\t%u\n", self->blocksz); + fu_common_string_append_kx (str, idt, "InterfaceNumber", self->intf_nr); + fu_common_string_append_kx (str, idt, "BlockSize", self->blocksz); + fu_common_string_append_kb (str, idt, "Secure", self->secure); } static gboolean diff --git a/plugins/modem-manager/fu-mm-device.c b/plugins/modem-manager/fu-mm-device.c index 86ccdc0f5..3704c4db2 100644 --- a/plugins/modem-manager/fu-mm-device.c +++ b/plugins/modem-manager/fu-mm-device.c @@ -62,18 +62,13 @@ static guint signals [SIGNAL_LAST] = { 0 }; G_DEFINE_TYPE (FuMmDevice, fu_mm_device, FU_TYPE_DEVICE) static void -fu_mm_device_to_string (FuDevice *device, GString *str) +fu_mm_device_to_string (FuDevice *device, guint idt, GString *str) { FuMmDevice *self = FU_MM_DEVICE (device); - g_string_append (str, " FuMmDevice:\n"); - if (self->port_at != NULL) { - g_string_append_printf (str, " at-port:\t\t\t%s\n", - self->port_at); - } - if (self->port_qmi != NULL) { - g_string_append_printf (str, " qmi-port:\t\t\t%s\n", - self->port_qmi); - } + if (self->port_at != NULL) + fu_common_string_append_kv (str, idt, "AtPort", self->port_at); + if (self->port_qmi != NULL) + fu_common_string_append_kv (str, idt, "QmiPort", self->port_qmi); } const gchar * diff --git a/plugins/nvme/fu-nvme-device.c b/plugins/nvme/fu-nvme-device.c index e4b4504be..ddede6d59 100644 --- a/plugins/nvme/fu-nvme-device.c +++ b/plugins/nvme/fu-nvme-device.c @@ -40,12 +40,11 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref) #endif static void -fu_nvme_device_to_string (FuDevice *device, GString *str) +fu_nvme_device_to_string (FuDevice *device, guint idt, GString *str) { FuNvmeDevice *self = FU_NVME_DEVICE (device); - g_string_append (str, " FuNvmeDevice:\n"); - g_string_append_printf (str, " fd:\t\t\t%i\n", self->fd); - g_string_append_printf (str, " pci-depth:\t\t%u\n", self->pci_depth); + fu_common_string_append_ku (str, idt, "FD", (guint) self->fd); + fu_common_string_append_ku (str, idt, "PciDepth", self->pci_depth); } /* @addr_start and @addr_end are *inclusive* to match the NMVe specification */ diff --git a/plugins/rts54hid/fu-rts54hid-device.c b/plugins/rts54hid/fu-rts54hid-device.c index cd7344eee..346f14336 100644 --- a/plugins/rts54hid/fu-rts54hid-device.c +++ b/plugins/rts54hid/fu-rts54hid-device.c @@ -23,12 +23,11 @@ G_DEFINE_TYPE (FuRts54HidDevice, fu_rts54hid_device, FU_TYPE_USB_DEVICE) #define FU_RTS54HID_DEVICE_TIMEOUT 1000 /* ms */ static void -fu_rts54hid_device_to_string (FuDevice *device, GString *str) +fu_rts54hid_device_to_string (FuDevice *device, guint idt, GString *str) { FuRts54HidDevice *self = FU_RTS54HID_DEVICE (device); - g_string_append (str, " FuRts54HidDevice:\n"); - g_string_append_printf (str, " fw-auth: %i\n", self->fw_auth); - g_string_append_printf (str, " dual-bank: %i\n", self->dual_bank); + fu_common_string_append_kb (str, idt, "FwAuth", self->fw_auth); + fu_common_string_append_kb (str, idt, "DualBank", self->dual_bank); } gboolean diff --git a/plugins/rts54hid/fu-rts54hid-module.c b/plugins/rts54hid/fu-rts54hid-module.c index 1402dfb64..6d8606ac5 100644 --- a/plugins/rts54hid/fu-rts54hid-module.c +++ b/plugins/rts54hid/fu-rts54hid-module.c @@ -23,13 +23,12 @@ struct _FuRts54HidModule { G_DEFINE_TYPE (FuRts54HidModule, fu_rts54hid_module, FU_TYPE_DEVICE) static void -fu_rts54hid_module_to_string (FuDevice *module, GString *str) +fu_rts54hid_module_to_string (FuDevice *module, guint idt, GString *str) { FuRts54HidModule *self = FU_RTS54HID_MODULE (module); - g_string_append (str, " FuRts54HidModule:\n"); - g_string_append_printf (str, " slave-addr: 0x%02x\n", self->slave_addr); - g_string_append_printf (str, " i2c-speed: 0x%02x\n", self->i2c_speed); - g_string_append_printf (str, " register_addr_len: 0x%02x\n", self->register_addr_len); + fu_common_string_append_kx (str, idt, "SlaveAddr", self->slave_addr); + fu_common_string_append_kx (str, idt, "I2cSpeed", self->i2c_speed); + fu_common_string_append_kx (str, idt, "RegisterAddrLen", self->register_addr_len); } static FuRts54HidDevice * diff --git a/plugins/rts54hub/fu-rts54hub-device.c b/plugins/rts54hub/fu-rts54hub-device.c index ba0d819ee..a12750ef0 100644 --- a/plugins/rts54hub/fu-rts54hub-device.c +++ b/plugins/rts54hub/fu-rts54hub-device.c @@ -35,13 +35,12 @@ typedef enum { } FuRts54HubVendorCmd; static void -fu_rts54hub_device_to_string (FuDevice *device, GString *str) +fu_rts54hub_device_to_string (FuDevice *device, guint idt, GString *str) { FuRts54HubDevice *self = FU_RTS54HUB_DEVICE (device); - g_string_append (str, " FuRts54HubDevice:\n"); - g_string_append_printf (str, " fw-auth: %i\n", self->fw_auth); - g_string_append_printf (str, " dual-bank: %i\n", self->dual_bank); - g_string_append_printf (str, " running-on-flash: %i\n", self->running_on_flash); + fu_common_string_append_kb (str, idt, "FwAuth", self->fw_auth); + fu_common_string_append_kb (str, idt, "DualBank", self->dual_bank); + fu_common_string_append_kb (str, idt, "RunningOnFlash", self->running_on_flash); } static gboolean diff --git a/plugins/superio/fu-superio-device.c b/plugins/superio/fu-superio-device.c index 64d4d5292..9d2d5cfa0 100644 --- a/plugins/superio/fu-superio-device.c +++ b/plugins/superio/fu-superio-device.c @@ -118,17 +118,16 @@ fu_superio_device_regdump (FuSuperioDevice *self, guint8 ldn, GError **error) } static void -fu_superio_device_to_string (FuDevice *device, GString *str) +fu_superio_device_to_string (FuDevice *device, guint idt, GString *str) { FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); FuSuperioDevicePrivate *priv = GET_PRIVATE (self); - g_string_append (str, " FuSuperioDevice:\n"); - g_string_append_printf (str, " fd:\t\t\t%i\n", priv->fd); - g_string_append_printf (str, " chipset:\t\t%s\n", priv->chipset); - g_string_append_printf (str, " id:\t\t\t0x%04x\n", (guint) priv->id); - g_string_append_printf (str, " port:\t\t0x%04x\n", (guint) priv->port); - g_string_append_printf (str, " pm1-iobad0:\t\t0x%04x\n", (guint) priv->pm1_iobad0); - g_string_append_printf (str, " pm1-iobad1:\t\t0x%04x\n", (guint) priv->pm1_iobad1); + fu_common_string_append_ku (str, idt, "FD", (guint) priv->fd); + fu_common_string_append_kv (str, idt, "Chipset", priv->chipset); + fu_common_string_append_kx (str, idt, "Id", priv->id); + fu_common_string_append_kx (str, idt, "Port", priv->port); + fu_common_string_append_kx (str, idt, "PM1_IOBAD0", priv->pm1_iobad0); + fu_common_string_append_kx (str, idt, "PM1_IOBAD1", priv->pm1_iobad1); } static guint16 diff --git a/plugins/uefi/fu-uefi-device.c b/plugins/uefi/fu-uefi-device.c index ab5d96de8..21c6d272c 100644 --- a/plugins/uefi/fu-uefi-device.c +++ b/plugins/uefi/fu-uefi-device.c @@ -92,25 +92,19 @@ fu_uefi_device_status_to_string (FuUefiDeviceStatus status) } static void -fu_uefi_device_to_string (FuDevice *device, GString *str) +fu_uefi_device_to_string (FuDevice *device, guint idt, GString *str) { FuUefiDevice *self = FU_UEFI_DEVICE (device); - g_string_append (str, " FuUefiDevice:\n"); - g_string_append_printf (str, " kind:\t\t\t%s\n", - fu_uefi_device_kind_to_string (self->kind)); - g_string_append_printf (str, " fw_class:\t\t\t%s\n", self->fw_class); - g_string_append_printf (str, " capsule_flags:\t\t%" G_GUINT32_FORMAT "\n", - self->capsule_flags); - g_string_append_printf (str, " fw_version:\t\t\t%" G_GUINT32_FORMAT "\n", - self->fw_version); - g_string_append_printf (str, " fw_version_lowest:\t\t%" G_GUINT32_FORMAT "\n", - self->fw_version_lowest); - g_string_append_printf (str, " last_attempt_status:\t%s\n", - fu_uefi_device_status_to_string (self->last_attempt_status)); - g_string_append_printf (str, " last_attempt_version:\t%" G_GUINT32_FORMAT "\n", - self->last_attempt_version); - g_string_append_printf (str, " esp path:\t%s\n", - fu_device_get_metadata (device, "EspPath")); + fu_common_string_append_kv (str, idt, "Kind", fu_uefi_device_kind_to_string (self->kind)); + fu_common_string_append_kv (str, idt, "FwClass", self->fw_class); + fu_common_string_append_kx (str, idt, "CapsuleFlags", self->capsule_flags); + fu_common_string_append_kx (str, idt, "FwVersion", self->fw_version); + fu_common_string_append_kx (str, idt, "FwVersionLowest", self->fw_version_lowest); + fu_common_string_append_kv (str, idt, "LastAttemptStatus", + fu_uefi_device_status_to_string (self->last_attempt_status)); + fu_common_string_append_kx (str, idt, "LastAttemptVersion", self->last_attempt_version); + fu_common_string_append_kv (str, idt, "EspPath", + fu_device_get_metadata (device, "EspPath")); } FuUefiDeviceKind diff --git a/plugins/unifying/fu-unifying-bootloader.c b/plugins/unifying/fu-unifying-bootloader.c index 8497ef544..b4f471051 100644 --- a/plugins/unifying/fu-unifying-bootloader.c +++ b/plugins/unifying/fu-unifying-bootloader.c @@ -27,16 +27,13 @@ G_DEFINE_TYPE_WITH_PRIVATE (FuUnifyingBootloader, fu_unifying_bootloader, FU_TYP #define GET_PRIVATE(o) (fu_unifying_bootloader_get_instance_private (o)) static void -fu_unifying_bootloader_to_string (FuDevice *device, GString *str) +fu_unifying_bootloader_to_string (FuDevice *device, guint idt, GString *str) { FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device); FuUnifyingBootloaderPrivate *priv = GET_PRIVATE (self); - g_string_append_printf (str, " FlashAddrHigh:\t0x%04x\n", - priv->flash_addr_hi); - g_string_append_printf (str, " FlashAddrLow:\t0x%04x\n", - priv->flash_addr_lo); - g_string_append_printf (str, " FlashBlockSize:\t0x%04x\n", - priv->flash_blocksize); + fu_common_string_append_kx (str, idt, "FlashAddrHigh", priv->flash_addr_hi); + fu_common_string_append_kx (str, idt, "FlashAddrLow", priv->flash_addr_lo); + fu_common_string_append_kx (str, idt, "FlashBlockSize", priv->flash_blocksize); } FuUnifyingBootloaderRequest * diff --git a/plugins/unifying/fu-unifying-peripheral.c b/plugins/unifying/fu-unifying-peripheral.c index e63eaede0..8adffe16d 100644 --- a/plugins/unifying/fu-unifying-peripheral.c +++ b/plugins/unifying/fu-unifying-peripheral.c @@ -247,23 +247,27 @@ fu_unifying_peripheral_open (FuDevice *device, GError **error) } static void -fu_unifying_peripheral_to_string (FuDevice *device, GString *str) +fu_unifying_hidpp_map_to_string (FuUnifyingHidppMap *map, guint idt, GString *str) +{ + g_autofree gchar *title = g_strdup_printf ("Feature%02x", map->idx); + g_autofree gchar *tmp = g_strdup_printf ("%s [0x%04x]", + fu_unifying_hidpp_feature_to_string (map->feature), + map->feature); + fu_common_string_append_kv (str, idt, title, tmp); +} + +static void +fu_unifying_peripheral_to_string (FuDevice *device, guint idt, GString *str) { FuUnifyingPeripheral *self = FU_UNIFYING_PERIPHERAL (device); - - g_string_append_printf (str, " HidppVersion:\t\t%u\n", self->hidpp_version); - if (self->hidpp_id != HIDPP_DEVICE_ID_UNSET) - g_string_append_printf (str, " HidppId:\t\t0x%02x\n", (guint) self->hidpp_id); - if (self->battery_level != 0) - g_string_append_printf (str, " Battery-level:\t\t%u\n", self->battery_level); - g_string_append_printf (str, " IsUpdatable:\t\t%i\n", self->is_updatable); - g_string_append_printf (str, " IsActive:\t\t%i\n", self->is_active); + fu_common_string_append_ku (str, idt, "HidppVersion", self->hidpp_version); + fu_common_string_append_kx (str, idt, "HidppId", self->hidpp_id); + fu_common_string_append_ku (str, idt, "BatteryLevel", self->battery_level); + fu_common_string_append_kb (str, idt, "IsUpdatable", self->is_updatable); + fu_common_string_append_kb (str, idt, "IsActive", self->is_active); for (guint i = 0; i < self->feature_index->len; i++) { FuUnifyingHidppMap *map = g_ptr_array_index (self->feature_index, i); - g_string_append_printf (str, " Feature%02x:\t\t%s [0x%04x]\n", - map->idx, - fu_unifying_hidpp_feature_to_string (map->feature), - map->feature); + fu_unifying_hidpp_map_to_string (map, idt, str); } } diff --git a/plugins/unifying/fu-unifying-runtime.c b/plugins/unifying/fu-unifying-runtime.c index d3730eb62..869b4dc8d 100644 --- a/plugins/unifying/fu-unifying-runtime.c +++ b/plugins/unifying/fu-unifying-runtime.c @@ -27,10 +27,10 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref) #endif static void -fu_unifying_runtime_to_string (FuDevice *device, GString *str) +fu_unifying_runtime_to_string (FuDevice *device, guint idt, GString *str) { FuUnifyingRuntime *self = FU_UNIFYING_RUNTIME (device); - g_string_append_printf (str, " SignedFirmware:\t%i\n", self->signed_firmware); + fu_common_string_append_kb (str, idt, "SignedFirmware", self->signed_firmware); } static gboolean diff --git a/plugins/wacom-raw/fu-wacom-device.c b/plugins/wacom-raw/fu-wacom-device.c index aa09103a8..4eb8cad3b 100644 --- a/plugins/wacom-raw/fu-wacom-device.c +++ b/plugins/wacom-raw/fu-wacom-device.c @@ -31,15 +31,14 @@ G_DEFINE_TYPE_WITH_PRIVATE (FuWacomDevice, fu_wacom_device, FU_TYPE_UDEV_DEVICE) #define GET_PRIVATE(o) (fu_wacom_device_get_instance_private (o)) static void -fu_wacom_device_to_string (FuDevice *device, GString *str) +fu_wacom_device_to_string (FuDevice *device, guint idt, GString *str) { FuWacomDevice *self = FU_WACOM_DEVICE (device); FuWacomDevicePrivate *priv = GET_PRIVATE (self); - g_string_append (str, " FuWacomDevice:\n"); - g_string_append_printf (str, " fd:\t\t\t%i\n", priv->fd); - g_string_append_printf (str, " flash-block-size:\t0x%04x\n", priv->flash_block_size); - g_string_append_printf (str, " flash-base-addr:\t0x%04x\n", priv->flash_base_addr); - g_string_append_printf (str, " flash-size:\t\t0x%04x\n", priv->flash_size); + fu_common_string_append_ku (str, idt, "FD", (guint) priv->fd); + fu_common_string_append_kx (str, idt, "FlashBlockSize", priv->flash_block_size); + fu_common_string_append_kx (str, idt, "FlashBaseAddr", priv->flash_base_addr); + fu_common_string_append_kx (str, idt, "FlashSize", priv->flash_size); } guint diff --git a/plugins/wacom-usb/fu-wac-device.c b/plugins/wacom-usb/fu-wac-device.c index 738697f28..e6c1775cb 100644 --- a/plugins/wacom-usb/fu-wac-device.c +++ b/plugins/wacom-usb/fu-wac-device.c @@ -79,64 +79,57 @@ fu_wav_device_flash_descriptor_is_wp (const FuWacFlashDescriptor *fd) } static void -fu_wac_device_to_string (FuDevice *device, GString *str) +fu_wac_device_flash_descriptor_to_string (FuWacFlashDescriptor *fd, guint idt, GString *str) +{ + fu_common_string_append_kx (str, idt, "StartAddr", fd->start_addr); + fu_common_string_append_kx (str, idt, "BlockSize", fd->block_sz); + fu_common_string_append_kx (str, idt, "WriteSize", fd->write_sz & ~0x8000); + fu_common_string_append_kb (str, idt, "Protected", + fu_wav_device_flash_descriptor_is_wp (fd)); +} + +static void +fu_wac_device_to_string (FuDevice *device, guint idt, GString *str) { - GPtrArray *children; FuWacDevice *self = FU_WAC_DEVICE (device); g_autoptr(GString) status_str = NULL; - g_string_append (str, " FuWacDevice:\n"); if (self->firmware_index != 0xffff) { - g_string_append_printf (str, " fw-index: 0x%04x\n", - self->firmware_index); + g_autofree gchar *tmp = g_strdup_printf ("0x%04x", self->firmware_index); + fu_common_string_append_kv (str, idt, "FwIndex", tmp); } if (self->loader_ver > 0) { - g_string_append_printf (str, " loader-ver: 0x%04x\n", - (guint) self->loader_ver); + g_autofree gchar *tmp = g_strdup_printf ("0x%04x", (guint) self->loader_ver); + fu_common_string_append_kv (str, idt, "LoaderVer", tmp); } if (self->read_data_sz > 0) { - g_string_append_printf (str, " read-data-sz: 0x%04x\n", - (guint) self->read_data_sz); + g_autofree gchar *tmp = g_strdup_printf ("0x%04x", (guint) self->read_data_sz); + fu_common_string_append_kv (str, idt, "ReadDataSize", tmp); } if (self->write_word_sz > 0) { - g_string_append_printf (str, " write-word-sz: 0x%04x\n", - (guint) self->write_word_sz); + g_autofree gchar *tmp = g_strdup_printf ("0x%04x", (guint) self->write_word_sz); + fu_common_string_append_kv (str, idt, "WriteWordSize", tmp); } if (self->write_block_sz > 0) { - g_string_append_printf (str, " write-block-sz: 0x%04x\n", - (guint) self->write_block_sz); + g_autofree gchar *tmp = g_strdup_printf ("0x%04x", (guint) self->write_block_sz); + fu_common_string_append_kv (str, idt, "WriteBlockSize", tmp); } if (self->nr_flash_blocks > 0) { - g_string_append_printf (str, " nr-flash-blocks: 0x%04x\n", - (guint) self->nr_flash_blocks); + g_autofree gchar *tmp = g_strdup_printf ("0x%04x", (guint) self->nr_flash_blocks); + fu_common_string_append_kv (str, idt, "NrFlashBlocks", tmp); } if (self->configuration != 0xffff) { - g_string_append_printf (str, " configuration: 0x%04x\n", - (guint) self->configuration); + g_autofree gchar *tmp = g_strdup_printf ("0x%04x", (guint) self->configuration); + fu_common_string_append_kv (str, idt, "Configuration", tmp); } for (guint i = 0; i < self->flash_descriptors->len; i++) { FuWacFlashDescriptor *fd = g_ptr_array_index (self->flash_descriptors, i); - g_string_append_printf (str, " flash-descriptor-%02u:\n", i); - g_string_append_printf (str, " start-addr:\t0x%08x\n", - (guint) fd->start_addr); - g_string_append_printf (str, " block-sz:\t0x%08x\n", - (guint) fd->block_sz); - g_string_append_printf (str, " write-sz:\t0x%04x\n", - (guint) fd->write_sz & ~0x8000); - g_string_append_printf (str, " protected:\t%s\n", - fu_wav_device_flash_descriptor_is_wp (fd) ? "yes" : "no"); + g_autofree gchar *title = g_strdup_printf ("FlashDescriptor%02u", i); + fu_common_string_append_kv (str, idt, title, NULL); + fu_wac_device_flash_descriptor_to_string (fd, idt + 1, str); } status_str = fu_wac_device_status_to_string (self->status_word); - g_string_append_printf (str, " status:\t\t%s\n", status_str->str); - - /* print children also */ - children = fu_device_get_children (device); - for (guint i = 0; i < children->len; i++) { - FuDevice *child = g_ptr_array_index (children, i); - g_autofree gchar *tmp = fu_device_to_string (FU_DEVICE (child)); - g_string_append (str, " FuWacDeviceChild:\n"); - g_string_append (str, tmp); - } + fu_common_string_append_kv (str, idt, "Status", status_str->str); } gboolean diff --git a/plugins/wacom-usb/fu-wac-module.c b/plugins/wacom-usb/fu-wac-module.c index 033f56bb4..d68a44bb6 100644 --- a/plugins/wacom-usb/fu-wac-module.c +++ b/plugins/wacom-usb/fu-wac-module.c @@ -108,17 +108,16 @@ fu_wac_module_status_to_string (guint8 status) } static void -fu_wac_module_to_string (FuDevice *device, GString *str) +fu_wac_module_to_string (FuDevice *device, guint idt, GString *str) { FuWacModule *self = FU_WAC_MODULE (device); FuWacModulePrivate *priv = GET_PRIVATE (self); - g_string_append (str, " FuWacSubModule:\n"); - g_string_append_printf (str, " fw-type:\t\t%s\n", - fu_wac_module_fw_type_to_string (priv->fw_type)); - g_string_append_printf (str, " status:\t\t%s\n", - fu_wac_module_status_to_string (priv->status)); - g_string_append_printf (str, " command:\t\t%s\n", - fu_wac_module_command_to_string (priv->command)); + fu_common_string_append_kv (str, idt, "FwType", + fu_wac_module_fw_type_to_string (priv->fw_type)); + fu_common_string_append_kv (str, idt, "Status", + fu_wac_module_status_to_string (priv->status)); + fu_common_string_append_kv (str, idt, "Command", + fu_wac_module_command_to_string (priv->command)); } static gboolean diff --git a/src/fu-common.c b/src/fu-common.c index 4055784d1..716c3fde8 100644 --- a/src/fu-common.c +++ b/src/fu-common.c @@ -1115,21 +1115,44 @@ fu_common_string_replace (GString *string, const gchar *search, const gchar *rep } void -fu_common_string_append_kv (GString *str, guint key_offset, const gchar *key, const gchar *value) +fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value) { - const guint align = 32; + const guint align = 23; - g_return_if_fail (key_offset < align); + g_return_if_fail (idt * 2 < align); /* ignore */ - if (key == NULL || value == NULL) + if (key == NULL) return; - for (gsize i = 0; i < key_offset; i++) - g_string_append (str, " "); - g_string_append_printf (str, "%s: ", key); - for (gsize i = strlen (key) + key_offset + 2; i < align; i++) - g_string_append (str, " "); - g_string_append_printf (str, "%s\n", value); + for (gsize i = 0; i < idt; i++) + g_string_append (str, " "); + g_string_append_printf (str, "%s:", key); + if (value != NULL) { + for (gsize i = strlen (key) + idt + 1; i < align; i++) + g_string_append (str, " "); + g_string_append (str, value); + } + g_string_append (str, "\n"); +} + +void +fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value) +{ + g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value); + fu_common_string_append_kv (str, idt, key, tmp); +} + +void +fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value) +{ + g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value); + fu_common_string_append_kv (str, idt, key, tmp); +} + +void +fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value) +{ + fu_common_string_append_kv (str, idt, key, value ? "true" : "false"); } /** diff --git a/src/fu-common.h b/src/fu-common.h index dccd0df4a..040650e34 100644 --- a/src/fu-common.h +++ b/src/fu-common.h @@ -161,9 +161,21 @@ guint fu_common_string_replace (GString *string, const gchar *search, const gchar *replace); void fu_common_string_append_kv (GString *str, - guint key_offset, + guint idt, const gchar *key, const gchar *value); +void fu_common_string_append_ku (GString *str, + guint idt, + const gchar *key, + guint64 value); +void fu_common_string_append_kx (GString *str, + guint idt, + const gchar *key, + guint64 value); +void fu_common_string_append_kb (GString *str, + guint idt, + const gchar *key, + gboolean value); gchar **fu_common_strnsplit (const gchar *str, gsize sz, const gchar *delimiter, diff --git a/src/fu-device.c b/src/fu-device.c index cd7550f61..746c29701 100644 --- a/src/fu-device.c +++ b/src/fu-device.c @@ -1609,6 +1609,55 @@ fu_device_set_progress_full (FuDevice *self, gsize progress_done, gsize progress fu_device_set_progress (self, (guint) percentage); } +static void +fu_device_add_string (FuDevice *self, guint idt, GString *str) +{ + GPtrArray *children; + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + FuDevicePrivate *priv = GET_PRIVATE (self); + g_autofree gchar *tmp = NULL; + g_autoptr(GList) keys = NULL; + g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&priv->metadata_mutex); + + g_return_if_fail (locker != NULL); + + /* subclassed type */ + fu_common_string_append_kv (str, idt, G_OBJECT_TYPE_NAME (self), NULL); + + tmp = fwupd_device_to_string (FWUPD_DEVICE (self)); + if (tmp != NULL && tmp[0] != '\0') + g_string_append (str, tmp); + if (priv->alternate_id != NULL) + fu_common_string_append_kv (str, idt + 1, "AlternateId", priv->alternate_id); + if (priv->equivalent_id != NULL) + fu_common_string_append_kv (str, idt + 1, "EquivalentId", priv->equivalent_id); + if (priv->size_min > 0) { + g_autofree gchar *sz = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->size_min); + fu_common_string_append_kv (str, idt + 1, "FirmwareSizeMin", sz); + } + if (priv->size_max > 0) { + g_autofree gchar *sz = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->size_max); + fu_common_string_append_kv (str, idt + 1, "FirmwareSizeMax", sz); + } + keys = g_hash_table_get_keys (priv->metadata); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *key = l->data; + const gchar *value = g_hash_table_lookup (priv->metadata, key); + fu_common_string_append_kv (str, idt + 1, key, value); + } + + /* subclassed */ + if (klass->to_string != NULL) + klass->to_string (self, idt + 1, str); + + /* print children also */ + children = fu_device_get_children (self); + for (guint i = 0; i < children->len; i++) { + FuDevice *child = g_ptr_array_index (children, i); + fu_device_add_string (child, idt + 1, str); + } +} + /** * fu_device_to_string: * @self: A #FuDevice @@ -1623,42 +1672,8 @@ fu_device_set_progress_full (FuDevice *self, gsize progress_done, gsize progress gchar * fu_device_to_string (FuDevice *self) { - FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); - FuDevicePrivate *priv = GET_PRIVATE (self); - GString *str = g_string_new (""); - g_autofree gchar *tmp = NULL; - g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&priv->metadata_mutex); - g_autoptr(GList) keys = NULL; - - g_return_val_if_fail (FU_IS_DEVICE (self), NULL); - g_return_val_if_fail (locker != NULL, NULL); - - tmp = fwupd_device_to_string (FWUPD_DEVICE (self)); - if (tmp != NULL && tmp[0] != '\0') - g_string_append (str, tmp); - if (priv->alternate_id != NULL) - fu_common_string_append_kv (str, 2, "AlternateId", priv->alternate_id); - if (priv->equivalent_id != NULL) - fu_common_string_append_kv (str, 2, "EquivalentId", priv->equivalent_id); - if (priv->size_min > 0) { - g_autofree gchar *sz = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->size_min); - fu_common_string_append_kv (str, 2, "FirmwareSizeMin", sz); - } - if (priv->size_max > 0) { - g_autofree gchar *sz = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->size_max); - fu_common_string_append_kv (str, 2, "FirmwareSizeMax", sz); - } - keys = g_hash_table_get_keys (priv->metadata); - for (GList *l = keys; l != NULL; l = l->next) { - const gchar *key = l->data; - const gchar *value = g_hash_table_lookup (priv->metadata, key); - fu_common_string_append_kv (str, 2, key, value); - } - - /* subclassed */ - if (klass->to_string != NULL) - klass->to_string (self, str); - + GString *str = g_string_new (NULL); + fu_device_add_string (self, 0, str); return g_string_free (str, FALSE); } diff --git a/src/fu-device.h b/src/fu-device.h index 1e482d9d7..a9d5838d7 100644 --- a/src/fu-device.h +++ b/src/fu-device.h @@ -22,6 +22,7 @@ struct _FuDeviceClass { FwupdDeviceClass parent_class; void (*to_string) (FuDevice *self, + guint indent, GString *str); gboolean (*write_firmware) (FuDevice *self, FuFirmware *firmware, diff --git a/src/fu-firmware-image-private.h b/src/fu-firmware-image-private.h new file mode 100644 index 000000000..abd84003f --- /dev/null +++ b/src/fu-firmware-image-private.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware-image.h" + +G_BEGIN_DECLS + +void fu_firmware_image_add_string (FuFirmwareImage *self, + guint idt, + GString *str); + +G_END_DECLS diff --git a/src/fu-firmware-image.c b/src/fu-firmware-image.c index d2212c01d..5c0be1018 100644 --- a/src/fu-firmware-image.c +++ b/src/fu-firmware-image.c @@ -9,7 +9,7 @@ #include "config.h" #include "fu-common.h" -#include "fu-firmware-image.h" +#include "fu-firmware-image-private.h" /** * SECTION:fu-firmware_image @@ -239,6 +239,23 @@ fu_firmware_image_get_bytes_chunk (FuFirmwareImage *self, return g_bytes_new_from_bytes (priv->bytes, offset, chunk_sz_max); } +void +fu_firmware_image_add_string (FuFirmwareImage *self, guint idt, GString *str) +{ + FuFirmwareImagePrivate *priv = GET_PRIVATE (self); + fu_common_string_append_kv (str, idt, G_OBJECT_TYPE_NAME (self), NULL); + if (priv->id != NULL) + fu_common_string_append_kv (str, idt + 1, "ID", priv->id); + if (priv->idx != 0x0) + fu_common_string_append_kx (str, idt + 1, "Index", priv->idx); + if (priv->addr != 0x0) + fu_common_string_append_kx (str, idt + 1, "Address", priv->addr); + if (priv->bytes != NULL) { + fu_common_string_append_kx (str, idt + 1, "Data", + g_bytes_get_size (priv->bytes)); + } +} + /** * fu_firmware_image_to_string: * @self: A #FuFirmwareImage @@ -252,23 +269,8 @@ fu_firmware_image_get_bytes_chunk (FuFirmwareImage *self, gchar * fu_firmware_image_to_string (FuFirmwareImage *self) { - FuFirmwareImagePrivate *priv = GET_PRIVATE (self); - GString *str = g_string_new (" FuFirmwareImage:\n"); - if (priv->id != NULL) - fu_common_string_append_kv (str, 4, "ID", priv->id); - if (priv->idx != 0x0) { - g_autofree gchar *tmp = g_strdup_printf ("0x%04x", (guint) priv->idx); - fu_common_string_append_kv (str, 4, "Index", tmp); - } - if (priv->addr != 0x0) { - g_autofree gchar *tmp = g_strdup_printf ("0x%04x", (guint) priv->addr); - fu_common_string_append_kv (str, 4, "Address", tmp); - } - if (priv->bytes != NULL) { - gsize sz = g_bytes_get_size (priv->bytes); - g_autofree gchar *tmp = g_strdup_printf ("%04x", (guint) sz); - fu_common_string_append_kv (str, 4, "Data", tmp); - } + GString *str = g_string_new (NULL); + fu_firmware_image_add_string (self, 0, str); return g_string_free (str, FALSE); } diff --git a/src/fu-firmware-image.h b/src/fu-firmware-image.h index 5a20b1578..826311676 100644 --- a/src/fu-firmware-image.h +++ b/src/fu-firmware-image.h @@ -26,7 +26,7 @@ struct _FuFirmwareImageClass }; FuFirmwareImage *fu_firmware_image_new (GBytes *bytes); -gchar *fu_firmware_image_to_string (FuFirmwareImage *self); +gchar *fu_firmware_image_to_string (FuFirmwareImage *selfz); const gchar *fu_firmware_image_get_id (FuFirmwareImage *self); void fu_firmware_image_set_id (FuFirmwareImage *self, diff --git a/src/fu-firmware.c b/src/fu-firmware.c index da5cc7744..c930a5cdb 100644 --- a/src/fu-firmware.c +++ b/src/fu-firmware.c @@ -8,7 +8,9 @@ #include "config.h" +#include "fu-common.h" #include "fu-firmware.h" +#include "fu-firmware-image-private.h" /** * SECTION:fu-firmware @@ -335,16 +337,15 @@ fu_firmware_to_string (FuFirmware *self) GString *str = g_string_new (NULL); /* subclassed type */ - g_string_append_printf (str, "%s:\n", G_OBJECT_TYPE_NAME (self)); + fu_common_string_append_kv (str, 0, G_OBJECT_TYPE_NAME (self), NULL); /* vfunc */ if (klass->to_string != NULL) - klass->to_string (self, str); + klass->to_string (self, 0, str); for (guint i = 0; i < priv->images->len; i++) { FuFirmwareImage *img = g_ptr_array_index (priv->images, i); - g_autofree gchar *tmp = fu_firmware_image_to_string (img); - g_string_append (str, tmp); + fu_firmware_image_add_string (img, 1, str); } return g_string_free (str, FALSE); diff --git a/src/fu-firmware.h b/src/fu-firmware.h index f7814bcc4..0e2821d34 100644 --- a/src/fu-firmware.h +++ b/src/fu-firmware.h @@ -28,6 +28,7 @@ struct _FuFirmwareClass GBytes *(*write) (FuFirmware *self, GError **error); void (*to_string) (FuFirmware *self, + guint indent, GString *str); /*< private >*/ gpointer padding[29]; diff --git a/src/fu-self-test.c b/src/fu-self-test.c index c705a7f22..6cb7258c2 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -1143,6 +1143,7 @@ fu_engine_history_func (void) device_str = fu_device_to_string (device2); checksum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob_cab); device_str_expected = g_strdup_printf ( + "FuDevice:\n" "Test Device\n" " DeviceId: 894e8c17a29428b09d10cd90d1db74ea76fbcfe8\n" " Guid: 12345678-1234-1234-1234-123456789012\n" @@ -1375,6 +1376,7 @@ fu_engine_history_error_func (void) device_str = fu_device_to_string (device2); checksum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob_cab); device_str_expected = g_strdup_printf ( + "FuDevice:\n" "Test Device\n" " DeviceId: 894e8c17a29428b09d10cd90d1db74ea76fbcfe8\n" " Guid: 12345678-1234-1234-1234-123456789012\n" @@ -3972,13 +3974,13 @@ fu_firmware_func (void) str = fu_firmware_to_string (firmware); g_assert_cmpstr (str, ==, "FuFirmware:\n" " FuFirmwareImage:\n" - " ID: primary\n" - " Index: 0x000d\n" - " Address: 0x0200\n" + " ID: primary\n" + " Index: 0xd\n" + " Address: 0x200\n" " FuFirmwareImage:\n" - " ID: secondary\n" - " Index: 0x0017\n" - " Address: 0x0400\n"); + " ID: secondary\n" + " Index: 0x17\n" + " Address: 0x400\n"); } int From c521ab94be07b53160b62235bdc19b4992431014 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 14 Aug 2019 14:57:28 +0100 Subject: [PATCH 035/158] Do not use FuDevice metadata for the physical and logical IDs This allows us to print it nicely in the ->to_string() vfunc. --- src/fu-device.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/fu-device.c b/src/fu-device.c index 746c29701..870335814 100644 --- a/src/fu-device.c +++ b/src/fu-device.c @@ -34,6 +34,8 @@ static void fu_device_finalize (GObject *object); typedef struct { gchar *alternate_id; gchar *equivalent_id; + gchar *physical_id; + gchar *logical_id; FuDevice *alternate; FuDevice *parent; /* noref */ FuQuirks *quirks; @@ -82,10 +84,10 @@ fu_device_get_property (GObject *object, guint prop_id, g_value_set_uint (value, priv->progress); break; case PROP_PHYSICAL_ID: - g_value_set_string (value, fu_device_get_physical_id (self)); + g_value_set_string (value, priv->physical_id); break; case PROP_LOGICAL_ID: - g_value_set_string (value, fu_device_get_logical_id (self)); + g_value_set_string (value, priv->logical_id); break; case PROP_QUIRKS: g_value_set_object (value, priv->quirks); @@ -1307,8 +1309,9 @@ fu_device_ensure_id (FuDevice *self, GError **error) const gchar * fu_device_get_logical_id (FuDevice *self) { + FuDevicePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (FU_IS_DEVICE (self), NULL); - return fu_device_get_metadata (self, "logical-id"); + return priv->logical_id; } /** @@ -1324,8 +1327,10 @@ fu_device_get_logical_id (FuDevice *self) void fu_device_set_logical_id (FuDevice *self, const gchar *logical_id) { + FuDevicePrivate *priv = GET_PRIVATE (self); g_return_if_fail (FU_IS_DEVICE (self)); - fu_device_set_metadata (self, "logical-id", logical_id); + g_free (priv->logical_id); + priv->logical_id = g_strdup (logical_id); } /** @@ -1347,9 +1352,11 @@ fu_device_set_logical_id (FuDevice *self, const gchar *logical_id) void fu_device_set_physical_id (FuDevice *self, const gchar *physical_id) { + FuDevicePrivate *priv = GET_PRIVATE (self); g_return_if_fail (FU_IS_DEVICE (self)); g_return_if_fail (physical_id != NULL); - fu_device_set_metadata (self, "physical-id", physical_id); + g_free (priv->physical_id); + priv->physical_id = g_strdup (physical_id); } /** @@ -1368,8 +1375,9 @@ fu_device_set_physical_id (FuDevice *self, const gchar *physical_id) const gchar * fu_device_get_physical_id (FuDevice *self) { + FuDevicePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (FU_IS_DEVICE (self), NULL); - return fu_device_get_metadata (self, "physical-id"); + return priv->physical_id; } static void @@ -1631,6 +1639,10 @@ fu_device_add_string (FuDevice *self, guint idt, GString *str) fu_common_string_append_kv (str, idt + 1, "AlternateId", priv->alternate_id); if (priv->equivalent_id != NULL) fu_common_string_append_kv (str, idt + 1, "EquivalentId", priv->equivalent_id); + if (priv->physical_id != NULL) + fu_common_string_append_kv (str, idt + 1, "PhysicalId", priv->physical_id); + if (priv->logical_id != NULL) + fu_common_string_append_kv (str, idt + 1, "LogicalId", priv->logical_id); if (priv->size_min > 0) { g_autofree gchar *sz = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->size_min); fu_common_string_append_kv (str, idt + 1, "FirmwareSizeMin", sz); @@ -2360,6 +2372,8 @@ fu_device_finalize (GObject *object) g_ptr_array_unref (priv->parent_guids); g_free (priv->alternate_id); g_free (priv->equivalent_id); + g_free (priv->physical_id); + g_free (priv->logical_id); G_OBJECT_CLASS (fu_device_parent_class)->finalize (object); } From 37c6a7ba467b359b5f3fa6722119040c1896fc9c Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 14 Aug 2019 21:57:43 +0100 Subject: [PATCH 036/158] trivial: Add some helper functions for GByteArray --- plugins/solokey/fu-solokey-device.c | 20 ++++++----------- .../synaptics-prometheus/fu-synaprom-common.c | 3 ++- .../fu-synaprom-firmware.c | 19 ++++++---------- src/fu-common.c | 22 +++++++++++++++++++ src/fu-common.h | 11 +++++++++- src/fu-ihex-firmware.c | 2 +- 6 files changed, 49 insertions(+), 28 deletions(-) diff --git a/plugins/solokey/fu-solokey-device.c b/plugins/solokey/fu-solokey-device.c index d7759f389..c98d2054e 100644 --- a/plugins/solokey/fu-solokey-device.c +++ b/plugins/solokey/fu-solokey-device.c @@ -38,7 +38,7 @@ fu_solokey_device_exchange (GByteArray *req, guint8 cmd, guint32 addr, GByteArra guint8 buf_len[2] = { 0x00 }; /* command */ - g_byte_array_append (req, &cmd, sizeof(cmd)); + fu_byte_array_append_uint8 (req, cmd); /* first *3* bytes of the LE address */ fu_common_write_uint32 (buf_addr, addr, G_LITTLE_ENDIAN); @@ -58,10 +58,8 @@ fu_solokey_device_exchange (GByteArray *req, guint8 cmd, guint32 addr, GByteArra /* dummy data */ fu_common_write_uint16 (buf_len, 16, G_BIG_ENDIAN); g_byte_array_append (req, buf_len, sizeof(buf_len)); - for (guint i = 0; i < 16; i++) { - guint8 tmp = 'A'; - g_byte_array_append (req, &tmp, sizeof(tmp)); - } + for (guint i = 0; i < 16; i++) + fu_byte_array_append_uint8 (req, 'A'); } static gboolean @@ -156,10 +154,8 @@ fu_solokey_device_packet_tx (FuSolokeyDevice *self, GByteArray *req, GError **er gsize actual_length = 0; /* round up to the endpoint size */ - for (guint i = req->len; i < SOLO_USB_HID_EP_SIZE; i++) { - const guint8 tmp = 0x0; - g_byte_array_append (req, &tmp, 1); - } + for (guint i = req->len; i < SOLO_USB_HID_EP_SIZE; i++) + fu_byte_array_append_uint8 (req, 0x0); /* request */ if (g_getenv ("FWUPD_SOLOKEY_VERBOSE") != NULL) { @@ -333,10 +329,8 @@ fu_solokey_device_setup_cid (FuSolokeyDevice *self, GError **error) return TRUE; /* get a channel ID */ - for (guint i = 0; i < 8; i++) { - guint8 tmp = g_random_int_range (0x00, 0xff); - g_byte_array_append (nonce, &tmp, 1); - } + for (guint i = 0; i < 8; i++) + fu_byte_array_append_uint8 (nonce, g_random_int_range (0x00, 0xff)); res = fu_solokey_device_packet (self, 0x06, nonce, error); if (res == NULL) return FALSE; diff --git a/plugins/synaptics-prometheus/fu-synaprom-common.c b/plugins/synaptics-prometheus/fu-synaprom-common.c index 7089aaa9f..80ac679a3 100644 --- a/plugins/synaptics-prometheus/fu-synaprom-common.c +++ b/plugins/synaptics-prometheus/fu-synaprom-common.c @@ -10,6 +10,7 @@ #include #include +#include "fu-common.h" #include "fu-synaprom-common.h" enum { @@ -30,7 +31,7 @@ GByteArray * fu_synaprom_request_new (guint8 cmd, const gpointer data, gsize len) { GByteArray *blob = g_byte_array_new (); - g_byte_array_append (blob, &cmd, 1); + fu_byte_array_append_uint8 (blob, cmd); if (data != NULL) g_byte_array_append (blob, data, len); return blob; diff --git a/plugins/synaptics-prometheus/fu-synaprom-firmware.c b/plugins/synaptics-prometheus/fu-synaprom-firmware.c index ac0b0603d..efebc584d 100644 --- a/plugins/synaptics-prometheus/fu-synaprom-firmware.c +++ b/plugins/synaptics-prometheus/fu-synaprom-firmware.c @@ -9,6 +9,7 @@ #include +#include "fu-common.h" #include "fu-synaprom-firmware.h" struct _FuSynapromFirmware { @@ -127,26 +128,20 @@ fu_synaprom_firmware_write (FuFirmware *self, GError **error) .vmajor = 10, .vminor = 1, }; - guint16 tag1 = GUINT16_TO_LE(FU_SYNAPROM_FIRMWARE_TAG_MFW_HEADER); - guint16 tag2 = GUINT16_TO_LE(FU_SYNAPROM_FIRMWARE_TAG_MFW_PAYLOAD); - guint32 hdrsz = GUINT32_TO_LE(sizeof(hdr)); - guint32 datasz = GUINT32_TO_LE(sizeof(data)); /* add header */ - g_byte_array_append (blob, (const guint8 *) &tag1, sizeof(tag1)); - g_byte_array_append (blob, (const guint8 *) &hdrsz, sizeof(hdrsz)); + fu_byte_array_append_uint16 (blob, FU_SYNAPROM_FIRMWARE_TAG_MFW_HEADER, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32 (blob, sizeof(hdr), G_LITTLE_ENDIAN); g_byte_array_append (blob, (const guint8 *) &hdr, sizeof(hdr)); /* add payload */ - g_byte_array_append (blob, (const guint8 *) &tag2, sizeof(tag2)); - g_byte_array_append (blob, (const guint8 *) &datasz, sizeof(datasz)); + fu_byte_array_append_uint16 (blob, FU_SYNAPROM_FIRMWARE_TAG_MFW_PAYLOAD, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32 (blob, sizeof(data), G_LITTLE_ENDIAN); g_byte_array_append (blob, data, sizeof(data)); /* add signature */ - for (guint i = 0; i < FU_SYNAPROM_FIRMWARE_SIGSIZE; i++) { - guint8 sig = 0xff; - g_byte_array_append (blob, &sig, 1); - } + for (guint i = 0; i < FU_SYNAPROM_FIRMWARE_SIGSIZE; i++) + fu_byte_array_append_uint8 (blob, 0xff); return g_byte_array_free_to_bytes (blob); } diff --git a/src/fu-common.c b/src/fu-common.c index 716c3fde8..be686663e 100644 --- a/src/fu-common.c +++ b/src/fu-common.c @@ -1524,3 +1524,25 @@ fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset, memcpy (dst + dst_offset, src + src_offset, n); return TRUE; } + +void +fu_byte_array_append_uint8 (GByteArray *array, guint8 data) +{ + g_byte_array_append (array, &data, sizeof(data)); +} + +void +fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian) +{ + guint8 buf[2]; + fu_common_write_uint16 (buf, data, endian); + g_byte_array_append (array, buf, sizeof(buf)); +} + +void +fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian) +{ + guint8 buf[4]; + fu_common_write_uint32 (buf, data, endian); + g_byte_array_append (array, buf, sizeof(buf)); +} diff --git a/src/fu-common.h b/src/fu-common.h index 040650e34..e980df194 100644 --- a/src/fu-common.h +++ b/src/fu-common.h @@ -40,6 +40,8 @@ typedef enum { FU_DUMP_FLAGS_LAST } FuDumpFlags; +typedef guint FuEndianType; + /** * FuPathKind: * @FU_PATH_KIND_CACHEDIR_PKG: The cache directory (IE /var/cache/fwupd) @@ -144,7 +146,14 @@ gboolean fu_memcpy_safe (guint8 *dst, gsize n, GError **error); -typedef guint FuEndianType; +void fu_byte_array_append_uint8 (GByteArray *array, + guint8 data); +void fu_byte_array_append_uint16 (GByteArray *array, + guint16 data, + FuEndianType endian); +void fu_byte_array_append_uint32 (GByteArray *array, + guint32 data, + FuEndianType endian); void fu_common_write_uint16 (guint8 *buf, guint16 val_native, diff --git a/src/fu-ihex-firmware.c b/src/fu-ihex-firmware.c index e3c600060..17df546f6 100644 --- a/src/fu-ihex-firmware.c +++ b/src/fu-ihex-firmware.c @@ -230,7 +230,7 @@ fu_ihex_firmware_parse (FuFirmware *firmware, case DFU_INHX32_RECORD_TYPE_SIGNATURE: for (guint i = 9; i < line_end; i += 2) { guint8 tmp_c = fu_firmware_strparse_uint8 (line + i); - g_byte_array_append (buf_signature, &tmp_c, 1); + fu_byte_array_append_uint8 (buf_signature, tmp_c); } break; default: From 9cfe6b6a218be27dd594b912216499c7c9e55bb1 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 15 Aug 2019 10:03:22 +0100 Subject: [PATCH 037/158] synapticsmst: Use the same style as other plugins Also split up the Tesla and Leaf hardware families. No logic changes. --- plugins/synapticsmst/fu-plugin-synapticsmst.c | 84 ++--- plugins/synapticsmst/fu-synapticsmst-common.c | 54 +++ plugins/synapticsmst/fu-synapticsmst-common.h | 41 +++ ...-common.c => fu-synapticsmst-connection.c} | 281 ++++++++-------- ...-common.h => fu-synapticsmst-connection.h} | 23 +- ...smst-device.c => fu-synapticsmst-device.c} | 316 ++++++++---------- plugins/synapticsmst/fu-synapticsmst-device.h | 56 ++++ plugins/synapticsmst/meson.build | 10 +- plugins/synapticsmst/synapticsmst-device.h | 80 ----- plugins/synapticsmst/synapticsmst.quirk | 2 +- 10 files changed, 501 insertions(+), 446 deletions(-) create mode 100644 plugins/synapticsmst/fu-synapticsmst-common.c create mode 100644 plugins/synapticsmst/fu-synapticsmst-common.h rename plugins/synapticsmst/{synapticsmst-common.c => fu-synapticsmst-connection.c} (56%) rename plugins/synapticsmst/{synapticsmst-common.h => fu-synapticsmst-connection.h} (71%) rename plugins/synapticsmst/{synapticsmst-device.c => fu-synapticsmst-device.c} (75%) create mode 100644 plugins/synapticsmst/fu-synapticsmst-device.h delete mode 100644 plugins/synapticsmst/synapticsmst-device.h diff --git a/plugins/synapticsmst/fu-plugin-synapticsmst.c b/plugins/synapticsmst/fu-plugin-synapticsmst.c index fcfaf6788..1afadab61 100644 --- a/plugins/synapticsmst/fu-plugin-synapticsmst.c +++ b/plugins/synapticsmst/fu-plugin-synapticsmst.c @@ -1,14 +1,14 @@ /* * Copyright (C) 2017 Mario Limonciello * Copyright (C) 2017 Peichen Huang - * Copyright (C) 2017 Richard Hughes + * Copyright (C) 2017-2019 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ #include "config.h" -#include "synapticsmst-device.h" -#include "synapticsmst-common.h" +#include "fu-synapticsmst-device.h" +#include "fu-synapticsmst-common.h" #include "fu-plugin-vfuncs.h" #include "fu-device-metadata.h" @@ -16,7 +16,7 @@ #define SYNAPTICS_UPDATE_ENUMERATE_TRIES 3 static gboolean -syanpticsmst_check_amdgpu_safe (GError **error) +fu_synapticsmst_check_amdgpu_safe (GError **error) { gsize bufsz = 0; g_autofree gchar *buf = NULL; @@ -40,7 +40,7 @@ syanpticsmst_check_amdgpu_safe (GError **error) } static gboolean -synapticsmst_common_check_supported_system (FuPlugin *plugin, GError **error) +fu_synapticsmst_check_supported_system (FuPlugin *plugin, GError **error) { if (g_getenv ("FWUPD_SYNAPTICSMST_FW_DIR") != NULL) { @@ -49,7 +49,7 @@ synapticsmst_common_check_supported_system (FuPlugin *plugin, GError **error) } /* See https://github.com/hughsie/fwupd/issues/1121 for more details */ - if (!syanpticsmst_check_amdgpu_safe (error)) + if (!fu_synapticsmst_check_amdgpu_safe (error)) return FALSE; if (!g_file_test (SYSFS_DRM_DP_AUX, G_FILE_TEST_IS_DIR)) { @@ -65,10 +65,10 @@ synapticsmst_common_check_supported_system (FuPlugin *plugin, GError **error) /* creates MST-$str-$BOARDID */ static void fu_plugin_synapticsmst_create_simple_guid (FuDevice *fu_device, - SynapticsMSTDevice *device, + FuSynapticsmstDevice *device, const gchar *str) { - guint16 board_id = synapticsmst_device_get_board_id (device); + guint16 board_id = fu_synapticsmst_device_get_board_id (device); g_autofree gchar *devid = g_strdup_printf ("MST-%s-%u", str, board_id); fu_device_add_instance_id (fu_device, devid); } @@ -76,10 +76,10 @@ fu_plugin_synapticsmst_create_simple_guid (FuDevice *fu_device, /* creates MST-$str-$chipid-$BOARDID */ static void fu_plugin_synapticsmst_create_complex_guid (FuDevice *fu_device, - SynapticsMSTDevice *device, + FuSynapticsmstDevice *device, const gchar *device_kind) { - const gchar *chip_id_str = synapticsmst_device_get_chip_id_str (device); + const gchar *chip_id_str = fu_synapticsmst_device_get_chip_id_str (device); g_autofree gchar *chip_id_down = g_ascii_strdown (chip_id_str, -1); g_autofree gchar *tmp = g_strdup_printf ("%s-%s", device_kind, chip_id_down); @@ -89,13 +89,13 @@ fu_plugin_synapticsmst_create_complex_guid (FuDevice *fu_device, static gboolean fu_plugin_synapticsmst_lookup_device (FuPlugin *plugin, FuDevice *fu_device, - SynapticsMSTDevice *device, + FuSynapticsmstDevice *device, GError **error) { const gchar *board_str; const gchar *guid_template; - guint16 board_id = synapticsmst_device_get_board_id (device); - const gchar *chip_id_str = synapticsmst_device_get_chip_id_str (device); + guint16 board_id = fu_synapticsmst_device_get_board_id (device); + const gchar *chip_id_str = fu_synapticsmst_device_get_chip_id_str (device); g_autofree gchar *group = NULL; g_autofree gchar *name = NULL; @@ -113,7 +113,7 @@ fu_plugin_synapticsmst_lookup_device (FuPlugin *plugin, FU_QUIRKS_NAME); if (board_str == NULL) board_str = "Unknown Platform"; - name = g_strdup_printf ("Synaptics %s inside %s", synapticsmst_device_get_chip_id_str (device), + name = g_strdup_printf ("Synaptics %s inside %s", fu_synapticsmst_device_get_chip_id_str (device), board_str); fu_device_set_name (fu_device, name); @@ -150,7 +150,7 @@ fu_plugin_synapticsmst_lookup_device (FuPlugin *plugin, static gboolean fu_plugin_synaptics_add_device (FuPlugin *plugin, - SynapticsMSTDevice *device, + FuSynapticsmstDevice *device, GError **error) { g_autoptr(FuDevice) dev = NULL; @@ -162,8 +162,8 @@ fu_plugin_synaptics_add_device (FuPlugin *plugin, guint8 layer; guint16 rad; - aux_node = synapticsmst_device_get_aux_node (device); - if (!synapticsmst_device_enumerate_device (device, + aux_node = fu_synapticsmst_device_get_aux_node (device); + if (!fu_synapticsmst_device_enumerate_device (device, error)) { g_prefix_error (error, "Error enumerating device at %s: ", aux_node); return FALSE; @@ -172,9 +172,9 @@ fu_plugin_synaptics_add_device (FuPlugin *plugin, /* create the device */ dev = fu_device_new (); /* Store $KIND-$AUXNODE-$LAYER-$RAD as device ID */ - layer = synapticsmst_device_get_layer (device); - rad = synapticsmst_device_get_rad (device); - kind_str = synapticsmst_device_kind_to_string (synapticsmst_device_get_kind (device)); + layer = fu_synapticsmst_device_get_layer (device); + rad = fu_synapticsmst_device_get_rad (device); + kind_str = fu_synapticsmst_mode_to_string (fu_synapticsmst_device_get_kind (device)); dev_id_str = g_strdup_printf ("MST-%s-%s-%u-%u", kind_str, aux_node, layer, rad); fu_device_set_id (dev, dev_id_str); @@ -189,7 +189,7 @@ fu_plugin_synaptics_add_device (FuPlugin *plugin, fu_device_set_vendor (dev, "Synaptics"); fu_device_set_summary (dev, "Multi-Stream Transport Device"); fu_device_add_icon (dev, "video-display"); - fu_device_set_version (dev, synapticsmst_device_get_version (device), + fu_device_set_version (dev, fu_synapticsmst_device_get_version (device), FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_quirks (dev, fu_plugin_get_quirks (plugin)); @@ -209,15 +209,15 @@ fu_plugin_synaptics_add_device (FuPlugin *plugin, static gboolean fu_plugin_synaptics_scan_cascade (FuPlugin *plugin, - SynapticsMSTDevice *device, + FuSynapticsmstDevice *device, GError **error) { - g_autoptr(SynapticsMSTDevice) cascade_device = NULL; + g_autoptr(FuSynapticsmstDevice) cascade_device = NULL; FuDevice *fu_dev = NULL; const gchar *aux_node; - aux_node = synapticsmst_device_get_aux_node (device); - if (!synapticsmst_device_open (device, error)) { + aux_node = fu_synapticsmst_device_get_aux_node (device); + if (!fu_synapticsmst_device_open (device, error)) { g_prefix_error (error, "failed to open aux node %s again", aux_node); @@ -225,19 +225,19 @@ fu_plugin_synaptics_scan_cascade (FuPlugin *plugin, } for (guint8 j = 0; j < 2; j++) { - guint8 layer = synapticsmst_device_get_layer (device) + 1; - guint16 rad = synapticsmst_device_get_rad (device) | (j << (2 * (layer - 1))); + guint8 layer = fu_synapticsmst_device_get_layer (device) + 1; + guint16 rad = fu_synapticsmst_device_get_rad (device) | (j << (2 * (layer - 1))); g_autofree gchar *dev_id_str = NULL; dev_id_str = g_strdup_printf ("MST-REMOTE-%s-%u-%u", aux_node, layer, rad); fu_dev = fu_plugin_cache_lookup (plugin, dev_id_str); /* run the scan */ - if (!synapticsmst_device_scan_cascade_device (device, error, j)) + if (!fu_synapticsmst_device_scan_cascade_device (device, error, j)) return FALSE; /* check if cascaded device was found */ - if (!synapticsmst_device_get_cascade (device)) { + if (!fu_synapticsmst_device_get_cascade (device)) { /* not found, nothing new to see here, move along */ if (fu_dev == NULL) continue; @@ -250,7 +250,7 @@ fu_plugin_synaptics_scan_cascade (FuPlugin *plugin, } /* Found a device, add it */ } else { - cascade_device = synapticsmst_device_new (SYNAPTICSMST_DEVICE_KIND_REMOTE, + cascade_device = fu_synapticsmst_device_new (FU_SYNAPTICSMST_MODE_REMOTE, aux_node, layer, rad); /* new device */ if (fu_dev == NULL) { @@ -309,7 +309,7 @@ fu_plugin_synapticsmst_enumerate (FuPlugin *plugin, do { g_autofree gchar *dev_id_str = NULL; g_autoptr(GError) error_local = NULL; - g_autoptr(SynapticsMSTDevice) device = NULL; + g_autoptr(FuSynapticsmstDevice) device = NULL; FuDevice *fu_dev = NULL; aux_node = g_dir_read_name (dir); @@ -320,8 +320,8 @@ fu_plugin_synapticsmst_enumerate (FuPlugin *plugin, fu_dev = fu_plugin_cache_lookup (plugin, dev_id_str); /* If we open successfully a device exists here */ - device = synapticsmst_device_new (SYNAPTICSMST_DEVICE_KIND_DIRECT, aux_node, 0, 0); - if (!synapticsmst_device_open (device, &error_local)) { + device = fu_synapticsmst_device_new (FU_SYNAPTICSMST_MODE_DIRECT, aux_node, 0, 0); + if (!fu_synapticsmst_device_open (device, &error_local)) { /* No device exists here, but was there - remove from DB */ if (fu_dev != NULL) { g_debug ("Removing devices on %s", aux_node); @@ -367,8 +367,8 @@ fu_plugin_update (FuPlugin *plugin, FwupdInstallFlags flags, GError **error) { - g_autoptr(SynapticsMSTDevice) device = NULL; - SynapticsMSTDeviceKind kind; + g_autoptr(FuSynapticsmstDevice) device = NULL; + FuSynapticsmstMode kind; const gchar *aux_node; guint8 layer; guint8 rad; @@ -376,7 +376,7 @@ fu_plugin_update (FuPlugin *plugin, gboolean install_force; /* extract details to build a new device */ - kind = synapticsmst_device_kind_from_string (fu_device_get_metadata (dev, "SynapticsMSTKind")); + kind = fu_synapticsmst_mode_from_string (fu_device_get_metadata (dev, "SynapticsMSTKind")); aux_node = fu_device_get_metadata (dev, "SynapticsMSTAuxNode"); layer = g_ascii_strtoull (fu_device_get_metadata (dev, "SynapticsMSTLayer"), NULL, 0); rad = g_ascii_strtoull (fu_device_get_metadata (dev, "SynapticsMSTRad"), NULL, 0); @@ -388,15 +388,15 @@ fu_plugin_update (FuPlugin *plugin, fu_device_set_status (dev, FWUPD_STATUS_DEVICE_BUSY); g_usleep (SYNAPTICS_FLASH_MODE_DELAY * 1000000); - device = synapticsmst_device_new (kind, aux_node, layer, rad); + device = fu_synapticsmst_device_new (kind, aux_node, layer, rad); - if (!synapticsmst_device_enumerate_device (device, error)) + if (!fu_synapticsmst_device_enumerate_device (device, error)) return FALSE; reboot = !fu_device_has_custom_flag (dev, "skip-restart"); install_force = (flags & FWUPD_INSTALL_FLAG_FORCE) != 0 || fu_device_has_custom_flag (dev, "ignore-board-id"); fu_device_set_status (dev, FWUPD_STATUS_DEVICE_WRITE); - if (!synapticsmst_device_write_firmware (device, blob_fw, + if (!fu_synapticsmst_device_write_firmware (device, blob_fw, fu_synapticsmst_write_progress_cb, dev, reboot, @@ -416,7 +416,7 @@ fu_plugin_update (FuPlugin *plugin, for (guint i = 1; i <= SYNAPTICS_UPDATE_ENUMERATE_TRIES; i++) { g_autoptr(GError) error_local = NULL; g_usleep (SYNAPTICS_FLASH_MODE_DELAY * 1000000); - if (!synapticsmst_device_enumerate_device (device, + if (!fu_synapticsmst_device_enumerate_device (device, &error_local)) { g_warning ("Unable to find device after %u seconds: %s", SYNAPTICS_FLASH_MODE_DELAY * i, @@ -431,7 +431,7 @@ fu_plugin_update (FuPlugin *plugin, } } } - fu_device_set_version (dev, synapticsmst_device_get_version (device), + fu_device_set_version (dev, fu_synapticsmst_device_get_version (device), FWUPD_VERSION_FORMAT_TRIPLET); return TRUE; @@ -474,7 +474,7 @@ fu_plugin_synapticsmst_coldplug (FuPlugin *plugin, GError **error) { g_autoptr(GError) error_local = NULL; /* verify that this is a supported system */ - if (!synapticsmst_common_check_supported_system (plugin, error)) + if (!fu_synapticsmst_check_supported_system (plugin, error)) return FALSE; /* look for host devices or already plugged in dock devices */ diff --git a/plugins/synapticsmst/fu-synapticsmst-common.c b/plugins/synapticsmst/fu-synapticsmst-common.c new file mode 100644 index 000000000..4d5672900 --- /dev/null +++ b/plugins/synapticsmst/fu-synapticsmst-common.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 Mario Limonciello + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-synapticsmst-common.h" + +FuSynapticsmstMode +fu_synapticsmst_mode_from_string (const gchar *mode) +{ + if (g_strcmp0 (mode, "DIRECT") == 0) + return FU_SYNAPTICSMST_MODE_DIRECT; + if (g_strcmp0 (mode, "REMOTE") == 0) + return FU_SYNAPTICSMST_MODE_REMOTE; + return FU_SYNAPTICSMST_MODE_UNKNOWN; +} + +const gchar * +fu_synapticsmst_mode_to_string (FuSynapticsmstMode mode) +{ + if (mode == FU_SYNAPTICSMST_MODE_DIRECT) + return "DIRECT"; + if (mode == FU_SYNAPTICSMST_MODE_REMOTE) + return "REMOTE"; + return NULL; +} + +const gchar * +fu_synapticsmst_family_to_string (FuSynapticsmstFamily family) +{ + if (family == FU_SYNAPTICSMST_FAMILY_TESLA) + return "tesla"; + if (family == FU_SYNAPTICSMST_FAMILY_LEAF) + return "leaf"; + if (family == FU_SYNAPTICSMST_FAMILY_PANAMERA) + return "panamera"; + return NULL; +} + +FuSynapticsmstFamily +fu_synapticsmst_family_from_chip_id (guint16 chip_id) +{ + if (chip_id >= 0x5000 && chip_id < 0x6000) + return FU_SYNAPTICSMST_FAMILY_PANAMERA; + if (chip_id >= 0x3000 && chip_id < 0x4000) + return FU_SYNAPTICSMST_FAMILY_LEAF; + if (chip_id >= 0x2000 && chip_id < 0x3000) + return FU_SYNAPTICSMST_FAMILY_TESLA; + return FU_SYNAPTICSMST_FAMILY_UNKNOWN; +} diff --git a/plugins/synapticsmst/fu-synapticsmst-common.h b/plugins/synapticsmst/fu-synapticsmst-common.h new file mode 100644 index 000000000..30cc2b767 --- /dev/null +++ b/plugins/synapticsmst/fu-synapticsmst-common.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016 Mario Limonciello + * Copyright (C) 2017 Peichen Huang + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/** + * FuSynapticsmstMode: + * @FU_SYNAPTICSMST_MODE_UNKNOWN: Type invalid or not known + * @FU_SYNAPTICSMST_MODE_DIRECT: Directly addressable + * @FU_SYNAPTICSMST_MODE_REMOTE: Requires remote register work + * + * The device type. + **/ +typedef enum { + FU_SYNAPTICSMST_MODE_UNKNOWN, + FU_SYNAPTICSMST_MODE_DIRECT, + FU_SYNAPTICSMST_MODE_REMOTE, + /*< private >*/ + FU_SYNAPTICSMST_MODE_LAST +} FuSynapticsmstMode; + +typedef enum { + FU_SYNAPTICSMST_FAMILY_UNKNOWN, + FU_SYNAPTICSMST_FAMILY_TESLA, + FU_SYNAPTICSMST_FAMILY_LEAF, + FU_SYNAPTICSMST_FAMILY_PANAMERA, + /**/ + FU_SYNAPTICSMST_FAMILY_LAST +} FuSynapticsmstFamily; + +FuSynapticsmstMode fu_synapticsmst_mode_from_string (const gchar *mode); +const gchar *fu_synapticsmst_mode_to_string (FuSynapticsmstMode mode); +const gchar *fu_synapticsmst_family_to_string (FuSynapticsmstFamily family); +FuSynapticsmstFamily fu_synapticsmst_family_from_chip_id (guint16 chip_id); diff --git a/plugins/synapticsmst/synapticsmst-common.c b/plugins/synapticsmst/fu-synapticsmst-connection.c similarity index 56% rename from plugins/synapticsmst/synapticsmst-common.c rename to plugins/synapticsmst/fu-synapticsmst-connection.c index 22a97c326..5691bc2cf 100644 --- a/plugins/synapticsmst/synapticsmst-common.c +++ b/plugins/synapticsmst/fu-synapticsmst-connection.c @@ -8,12 +8,13 @@ #include "config.h" -#include "synapticsmst-common.h" +#include "fu-synapticsmst-connection.h" +#include "fu-synapticsmst-common.h" #define UNIT_SIZE 32 #define MAX_WAIT_TIME 3 /* unit : second */ -struct _SynapticsMSTConnection { +struct _FuSynapticsmstConnection { gint fd; /* not owned by the connection */ guint8 layer; guint8 remain_layer; @@ -21,9 +22,9 @@ struct _SynapticsMSTConnection { }; static gboolean -synapticsmst_common_aux_node_read (SynapticsMSTConnection *connection, - guint32 offset, guint8 *buf, - gint length, GError **error) +fu_synapticsmst_connection_aux_node_read (FuSynapticsmstConnection *connection, + guint32 offset, guint8 *buf, + gint length, GError **error) { if (lseek (connection->fd, offset, SEEK_SET) != offset) { g_set_error_literal (error, @@ -45,9 +46,9 @@ synapticsmst_common_aux_node_read (SynapticsMSTConnection *connection, } static gboolean -synapticsmst_common_aux_node_write (SynapticsMSTConnection *connection, - guint32 offset, const guint8 *buf, - gint length, GError **error) +fu_synapticsmst_connection_aux_node_write (FuSynapticsmstConnection *connection, + guint32 offset, const guint8 *buf, + gint length, GError **error) { if (lseek (connection->fd, offset, SEEK_SET) != offset) { g_set_error_literal (error, @@ -69,35 +70,35 @@ synapticsmst_common_aux_node_write (SynapticsMSTConnection *connection, } static gboolean -synapticsmst_common_bus_read (SynapticsMSTConnection *connection, - guint32 offset, - guint8 *buf, - guint32 length, GError **error) +fu_synapticsmst_connection_bus_read (FuSynapticsmstConnection *connection, + guint32 offset, + guint8 *buf, + guint32 length, GError **error) { - return synapticsmst_common_aux_node_read (connection, offset, buf, - length, error); + return fu_synapticsmst_connection_aux_node_read (connection, offset, buf, + length, error); } static gboolean -synapticsmst_common_bus_write (SynapticsMSTConnection *connection, - guint32 offset, - const guint8 *buf, - guint32 length, GError **error) +fu_synapticsmst_connection_bus_write (FuSynapticsmstConnection *connection, + guint32 offset, + const guint8 *buf, + guint32 length, GError **error) { - return synapticsmst_common_aux_node_write (connection, offset, buf, - length, error); + return fu_synapticsmst_connection_aux_node_write (connection, offset, buf, + length, error); } void -synapticsmst_common_free (SynapticsMSTConnection *connection) +fu_synapticsmst_connection_free (FuSynapticsmstConnection *connection) { g_free (connection); } -SynapticsMSTConnection * -synapticsmst_common_new (gint fd, guint8 layer, guint rad) +FuSynapticsmstConnection * +fu_synapticsmst_connection_new (gint fd, guint8 layer, guint rad) { - SynapticsMSTConnection *connection = g_new0 (SynapticsMSTConnection, 1); + FuSynapticsmstConnection *connection = g_new0 (FuSynapticsmstConnection, 1); connection->fd = fd; connection->layer = layer; connection->remain_layer = layer; @@ -106,9 +107,9 @@ synapticsmst_common_new (gint fd, guint8 layer, guint rad) } gboolean -synapticsmst_common_read (SynapticsMSTConnection *connection, - guint32 offset, guint8 *buf, - guint32 length, GError **error) +fu_synapticsmst_connection_read (FuSynapticsmstConnection *connection, + guint32 offset, guint8 *buf, + guint32 length, GError **error) { if (connection->layer && connection->remain_layer) { guint8 node; @@ -116,22 +117,22 @@ synapticsmst_common_read (SynapticsMSTConnection *connection, connection->remain_layer--; node = (connection->rad >> connection->remain_layer * 2) & 0x03; - result = synapticsmst_common_rc_get_command (connection, - UPDC_READ_FROM_TX_DPCD + node, - length, offset, (guint8 *)buf, - error); + result = fu_synapticsmst_connection_rc_get_command (connection, + UPDC_READ_FROM_TX_DPCD + node, + length, offset, (guint8 *)buf, + error); connection->remain_layer++; return result; } - return synapticsmst_common_bus_read (connection, offset, buf, length, error); + return fu_synapticsmst_connection_bus_read (connection, offset, buf, length, error); } gboolean -synapticsmst_common_write (SynapticsMSTConnection *connection, - guint32 offset, - const guint8 *buf, - guint32 length, GError **error) +fu_synapticsmst_connection_write (FuSynapticsmstConnection *connection, + guint32 offset, + const guint8 *buf, + guint32 length, GError **error) { if (connection->layer && connection->remain_layer) { guint8 node; @@ -139,24 +140,24 @@ synapticsmst_common_write (SynapticsMSTConnection *connection, connection->remain_layer--; node = (connection->rad >> connection->remain_layer * 2) & 0x03; - result = synapticsmst_common_rc_set_command (connection, - UPDC_WRITE_TO_TX_DPCD + node, - length, offset, (guint8 *)buf, - error); + result = fu_synapticsmst_connection_rc_set_command (connection, + UPDC_WRITE_TO_TX_DPCD + node, + length, offset, (guint8 *)buf, + error); connection->remain_layer++; return result; } - return synapticsmst_common_bus_write (connection, offset, buf, length, error); + return fu_synapticsmst_connection_bus_write (connection, offset, buf, length, error); } gboolean -synapticsmst_common_rc_set_command (SynapticsMSTConnection *connection, - guint32 rc_cmd, - guint32 length, - guint32 offset, - const guint8 *buf, - GError **error) +fu_synapticsmst_connection_rc_set_command (FuSynapticsmstConnection *connection, + guint32 rc_cmd, + guint32 length, + guint32 offset, + const guint8 *buf, + GError **error) { guint32 cur_offset = offset; guint32 cur_length; @@ -175,28 +176,28 @@ synapticsmst_common_rc_set_command (SynapticsMSTConnection *connection, if (cur_length) { /* write data */ - if (!synapticsmst_common_write (connection, - REG_RC_DATA, - buf, cur_length, - error)) { + if (!fu_synapticsmst_connection_write (connection, + REG_RC_DATA, + buf, cur_length, + error)) { g_prefix_error (error, "failure writing data register: "); return FALSE; } /* write offset */ - if (!synapticsmst_common_write (connection, - REG_RC_OFFSET, - (guint8 *)&cur_offset, 4, - error)) { + if (!fu_synapticsmst_connection_write (connection, + REG_RC_OFFSET, + (guint8 *)&cur_offset, 4, + error)) { g_prefix_error (error, "failure writing offset register: "); return FALSE; } /* write length */ - if (!synapticsmst_common_write (connection, - REG_RC_LEN, - (guint8 *)&cur_length, 4, - error)) { + if (!fu_synapticsmst_connection_write (connection, + REG_RC_LEN, + (guint8 *)&cur_length, 4, + error)) { g_prefix_error (error, "failure writing length register: "); return FALSE; } @@ -204,10 +205,10 @@ synapticsmst_common_rc_set_command (SynapticsMSTConnection *connection, /* send command */ cmd = 0x80 | rc_cmd; - if (!synapticsmst_common_write (connection, - REG_RC_CMD, - (guint8 *)&cmd, 1, - error)) { + if (!fu_synapticsmst_connection_write (connection, + REG_RC_CMD, + (guint8 *)&cmd, 1, + error)) { g_prefix_error (error, "failed to write command: "); return FALSE; } @@ -217,10 +218,10 @@ synapticsmst_common_rc_set_command (SynapticsMSTConnection *connection, deadline = t_spec.tv_sec + MAX_WAIT_TIME; do { - if (!synapticsmst_common_read (connection, - REG_RC_CMD, - (guint8 *)&readData, 2, - error)) { + if (!fu_synapticsmst_connection_read (connection, + REG_RC_CMD, + (guint8 *)&readData, 2, + error)) { g_prefix_error (error, "failed to read command: "); return FALSE; } @@ -253,12 +254,12 @@ synapticsmst_common_rc_set_command (SynapticsMSTConnection *connection, } gboolean -synapticsmst_common_rc_get_command (SynapticsMSTConnection *connection, - guint32 rc_cmd, - guint32 length, - guint32 offset, - guint8 *buf, - GError **error) +fu_synapticsmst_connection_rc_get_command (FuSynapticsmstConnection *connection, + guint32 rc_cmd, + guint32 length, + guint32 offset, + guint8 *buf, + GError **error) { guint32 cur_offset = offset; guint32 cur_length; @@ -277,19 +278,19 @@ synapticsmst_common_rc_get_command (SynapticsMSTConnection *connection, if (cur_length) { /* write offset */ - if (!synapticsmst_common_write (connection, - REG_RC_OFFSET, - (guint8 *)&cur_offset, 4, - error)) { + if (!fu_synapticsmst_connection_write (connection, + REG_RC_OFFSET, + (guint8 *)&cur_offset, 4, + error)) { g_prefix_error (error, "failed to write offset: "); return FALSE; } /* write length */ - if (!synapticsmst_common_write (connection, - REG_RC_LEN, - (guint8 *)&cur_length, 4, - error)) { + if (!fu_synapticsmst_connection_write (connection, + REG_RC_LEN, + (guint8 *)&cur_length, 4, + error)) { g_prefix_error (error, "failed to write length: "); return FALSE; } @@ -297,10 +298,10 @@ synapticsmst_common_rc_get_command (SynapticsMSTConnection *connection, /* send command */ cmd = 0x80 | rc_cmd; - if (!synapticsmst_common_write (connection, - REG_RC_CMD, - (guint8 *)&cmd, 1, - error)) { + if (!fu_synapticsmst_connection_write (connection, + REG_RC_CMD, + (guint8 *)&cmd, 1, + error)) { g_prefix_error (error, "failed to write command: "); return FALSE; } @@ -310,10 +311,10 @@ synapticsmst_common_rc_get_command (SynapticsMSTConnection *connection, deadline = t_spec.tv_sec + MAX_WAIT_TIME; do { - if (!synapticsmst_common_read (connection, - REG_RC_CMD, - (guint8 *)&readData, 2, - error)) { + if (!fu_synapticsmst_connection_read (connection, + REG_RC_CMD, + (guint8 *)&readData, 2, + error)) { g_prefix_error (error, "failed to read command: "); return FALSE; } @@ -338,11 +339,11 @@ synapticsmst_common_rc_get_command (SynapticsMSTConnection *connection, } if (cur_length) { - if (!synapticsmst_common_read (connection, - REG_RC_DATA, - buf, - cur_length, - error)) { + if (!fu_synapticsmst_connection_read (connection, + REG_RC_DATA, + buf, + cur_length, + error)) { g_prefix_error (error, "failed to read data: "); return FALSE; } @@ -357,14 +358,14 @@ synapticsmst_common_rc_get_command (SynapticsMSTConnection *connection, } gboolean -synapticsmst_common_rc_special_get_command (SynapticsMSTConnection *connection, - guint32 rc_cmd, - guint32 cmd_length, - guint32 cmd_offset, - guint8 *cmd_data, - guint32 length, - guint8 *buf, - GError **error) +fu_synapticsmst_connection_rc_special_get_command (FuSynapticsmstConnection *connection, + guint32 rc_cmd, + guint32 cmd_length, + guint32 cmd_offset, + guint8 *cmd_data, + guint32 length, + guint8 *buf, + GError **error) { guint32 readData = 0; guint32 cmd; @@ -374,30 +375,30 @@ synapticsmst_common_rc_special_get_command (SynapticsMSTConnection *connection, if (cmd_length) { /* write cmd data */ if (cmd_data != NULL) { - if (!synapticsmst_common_write (connection, - REG_RC_DATA, - cmd_data, - cmd_length, - error)) { + if (!fu_synapticsmst_connection_write (connection, + REG_RC_DATA, + cmd_data, + cmd_length, + error)) { g_prefix_error (error, "Failed to write command data: "); return FALSE; } } /* write offset */ - if (!synapticsmst_common_write (connection, - REG_RC_OFFSET, - (guint8 *)&cmd_offset, 4, - error)) { + if (!fu_synapticsmst_connection_write (connection, + REG_RC_OFFSET, + (guint8 *)&cmd_offset, 4, + error)) { g_prefix_error (error, "failed to write offset: "); return FALSE; } /* write length */ - if (!synapticsmst_common_write (connection, - REG_RC_LEN, - (guint8 *)&cmd_length, 4, - error)) { + if (!fu_synapticsmst_connection_write (connection, + REG_RC_LEN, + (guint8 *)&cmd_length, 4, + error)) { g_prefix_error (error, "failed to write length: "); return FALSE; } @@ -405,7 +406,7 @@ synapticsmst_common_rc_special_get_command (SynapticsMSTConnection *connection, /* send command */ cmd = 0x80 | rc_cmd; - if (!synapticsmst_common_write (connection, + if (!fu_synapticsmst_connection_write (connection, REG_RC_CMD, (guint8 *)&cmd, 1, error)) { @@ -417,7 +418,7 @@ synapticsmst_common_rc_special_get_command (SynapticsMSTConnection *connection, clock_gettime (CLOCK_REALTIME, &t_spec); deadline = t_spec.tv_sec + MAX_WAIT_TIME; do { - if (!synapticsmst_common_read (connection, + if (!fu_synapticsmst_connection_read (connection, REG_RC_CMD, (guint8 *)&readData, 2, error)) { @@ -446,10 +447,10 @@ synapticsmst_common_rc_special_get_command (SynapticsMSTConnection *connection, } if (length) { - if (!synapticsmst_common_read (connection, - REG_RC_DATA, - buf, length, - error)) { + if (!fu_synapticsmst_connection_read (connection, + REG_RC_DATA, + buf, length, + error)) { g_prefix_error (error, "failed to read length: "); } } @@ -458,27 +459,27 @@ synapticsmst_common_rc_special_get_command (SynapticsMSTConnection *connection, } gboolean -synapticsmst_common_enable_remote_control (SynapticsMSTConnection *connection, - GError **error) +fu_synapticsmst_connection_enable_rc (FuSynapticsmstConnection *connection, GError **error) { const gchar *sc = "PRIUS"; for (gint i = 0; i <= connection->layer; i++) { - g_autoptr(SynapticsMSTConnection) connection_tmp = synapticsmst_common_new (connection->fd, i, connection->rad); + g_autoptr(FuSynapticsmstConnection) connection_tmp = NULL; g_autoptr(GError) error_local = NULL; - if (!synapticsmst_common_rc_set_command (connection_tmp, - UPDC_ENABLE_RC, - 5, 0, (guint8*)sc, - &error_local)) { + connection_tmp = fu_synapticsmst_connection_new (connection->fd, i, connection->rad); + if (!fu_synapticsmst_connection_rc_set_command (connection_tmp, + UPDC_ENABLE_RC, + 5, 0, (guint8*)sc, + &error_local)) { g_debug ("Failed to enable remote control in layer %d: %s, retrying", i, error_local->message); - if (!synapticsmst_common_disable_remote_control (connection_tmp, error)) + if (!fu_synapticsmst_connection_disable_rc (connection_tmp, error)) return FALSE; - if (!synapticsmst_common_rc_set_command (connection_tmp, - UPDC_ENABLE_RC, - 5, 0, (guint8*)sc, - error)) { + if (!fu_synapticsmst_connection_rc_set_command (connection_tmp, + UPDC_ENABLE_RC, + 5, 0, (guint8*)sc, + error)) { g_prefix_error (error, "failed to enable remote control in layer %d: ", i); @@ -491,15 +492,15 @@ synapticsmst_common_enable_remote_control (SynapticsMSTConnection *connection, } gboolean -synapticsmst_common_disable_remote_control (SynapticsMSTConnection *connection, - GError **error) +fu_synapticsmst_connection_disable_rc (FuSynapticsmstConnection *connection, GError **error) { for (gint i = connection->layer; i >= 0; i--) { - g_autoptr(SynapticsMSTConnection) connection_tmp = synapticsmst_common_new (connection->fd, i, connection->rad); - if (!synapticsmst_common_rc_set_command (connection_tmp, - UPDC_DISABLE_RC, - 0, 0, NULL, - error)) { + g_autoptr(FuSynapticsmstConnection) connection_tmp = NULL; + connection_tmp = fu_synapticsmst_connection_new (connection->fd, i, connection->rad); + if (!fu_synapticsmst_connection_rc_set_command (connection_tmp, + UPDC_DISABLE_RC, + 0, 0, NULL, + error)) { g_prefix_error (error, "failed to disable remote control in layer %d: ", i); diff --git a/plugins/synapticsmst/synapticsmst-common.h b/plugins/synapticsmst/fu-synapticsmst-connection.h similarity index 71% rename from plugins/synapticsmst/synapticsmst-common.h rename to plugins/synapticsmst/fu-synapticsmst-connection.h index cc01b0338..186a55edb 100644 --- a/plugins/synapticsmst/synapticsmst-common.h +++ b/plugins/synapticsmst/fu-synapticsmst-connection.h @@ -54,27 +54,27 @@ typedef enum { UPDC_READ_FROM_TX_DPCD = 0x32, } SynapticsMstUpdcCmd; -typedef struct _SynapticsMSTConnection SynapticsMSTConnection; +typedef struct _FuSynapticsmstConnection FuSynapticsmstConnection; -void synapticsmst_common_free (SynapticsMSTConnection *connection); +void fu_synapticsmst_connection_free (FuSynapticsmstConnection *connection); -SynapticsMSTConnection *synapticsmst_common_new (gint fd, +FuSynapticsmstConnection *fu_synapticsmst_connection_new (gint fd, guint8 layer, guint rad); -gboolean synapticsmst_common_read (SynapticsMSTConnection *connection, +gboolean fu_synapticsmst_connection_read (FuSynapticsmstConnection *connection, guint32 offset, guint8 *buf, guint32 length, GError **error); -gboolean synapticsmst_common_write (SynapticsMSTConnection *connection, +gboolean fu_synapticsmst_connection_write (FuSynapticsmstConnection *connection, guint32 offset, const guint8 *buf, guint32 length, GError **error); -gboolean synapticsmst_common_rc_set_command (SynapticsMSTConnection *connection, +gboolean fu_synapticsmst_connection_rc_set_command (FuSynapticsmstConnection *connection, guint32 rc_cmd, guint32 length, guint32 offset, @@ -82,14 +82,14 @@ gboolean synapticsmst_common_rc_set_command (SynapticsMSTConnection *connecti GError **error); -gboolean synapticsmst_common_rc_get_command (SynapticsMSTConnection *connection, +gboolean fu_synapticsmst_connection_rc_get_command (FuSynapticsmstConnection *connection, guint32 rc_cmd, guint32 length, guint32 offset, guint8 *buf, GError **error); -gboolean synapticsmst_common_rc_special_get_command (SynapticsMSTConnection *connection, +gboolean fu_synapticsmst_connection_rc_special_get_command(FuSynapticsmstConnection *connection, guint32 rc_cmd, guint32 cmd_length, guint32 cmd_offset, @@ -98,12 +98,13 @@ gboolean synapticsmst_common_rc_special_get_command (SynapticsMSTConnection *co guint8 *buf, GError **error); -gboolean synapticsmst_common_enable_remote_control (SynapticsMSTConnection *connection, +gboolean fu_synapticsmst_connection_enable_rc (FuSynapticsmstConnection *connection, GError **error); -gboolean synapticsmst_common_disable_remote_control (SynapticsMSTConnection *connection, +gboolean fu_synapticsmst_connection_disable_rc (FuSynapticsmstConnection *connection, GError **error); + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" -G_DEFINE_AUTOPTR_CLEANUP_FUNC(SynapticsMSTConnection, synapticsmst_common_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuSynapticsmstConnection, fu_synapticsmst_connection_free) #pragma clang diagnostic pop diff --git a/plugins/synapticsmst/synapticsmst-device.c b/plugins/synapticsmst/fu-synapticsmst-device.c similarity index 75% rename from plugins/synapticsmst/synapticsmst-device.c rename to plugins/synapticsmst/fu-synapticsmst-device.c index a664aa984..89feddbcb 100644 --- a/plugins/synapticsmst/synapticsmst-device.c +++ b/plugins/synapticsmst/fu-synapticsmst-device.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2017 Richard Hughes + * Copyright (C) 2015-2019 Richard Hughes * Copyright (C) 2016 Mario Limonciello * Copyright (C) 2017 Peichen Huang * Copyright (C) 2018 Ryan Chang @@ -10,14 +10,15 @@ #include "config.h" #include "fu-device-locker.h" -#include "synapticsmst-device.h" -#include "synapticsmst-common.h" +#include "fu-synapticsmst-device.h" +#include "fu-synapticsmst-connection.h" +#include "fu-synapticsmst-common.h" #include #include #include -#define BIT( n ) ( 1 << (n) ) +#define BIT(n) (1 << (n)) #define FLASH_SECTOR_ERASE_4K 0x1000 #define FLASH_SECTOR_ERASE_32K 0x2000 #define FLASH_SECTOR_ERASE_64K 0x3000 @@ -39,9 +40,9 @@ #define FLASH_SETTLE_TIME 5000000 /* us */ -struct _SynapticsMSTDevice { +struct _FuSynapticsmstDevice { GObject parent_instance; - SynapticsMSTDeviceKind kind; + FuSynapticsmstMode kind; gchar *version; guint32 board_id; guint16 chip_id; @@ -55,32 +56,12 @@ struct _SynapticsMSTDevice { gboolean test_mode; }; -G_DEFINE_TYPE (SynapticsMSTDevice, synapticsmst_device, G_TYPE_OBJECT) - -SynapticsMSTDeviceKind -synapticsmst_device_kind_from_string (const gchar *kind) -{ - if (g_strcmp0 (kind, "DIRECT") == 0) - return SYNAPTICSMST_DEVICE_KIND_DIRECT; - if (g_strcmp0 (kind, "REMOTE") == 0) - return SYNAPTICSMST_DEVICE_KIND_REMOTE; - return SYNAPTICSMST_DEVICE_KIND_UNKNOWN; -} - -const gchar * -synapticsmst_device_kind_to_string (SynapticsMSTDeviceKind kind) -{ - if (kind == SYNAPTICSMST_DEVICE_KIND_DIRECT) - return "DIRECT"; - if (kind == SYNAPTICSMST_DEVICE_KIND_REMOTE) - return "REMOTE"; - return NULL; -} +G_DEFINE_TYPE (FuSynapticsmstDevice, fu_synapticsmst_device, G_TYPE_OBJECT) static void -synapticsmst_device_finalize (GObject *object) +fu_synapticsmst_device_finalize (GObject *object) { - SynapticsMSTDevice *self = SYNAPTICSMST_DEVICE (object); + FuSynapticsmstDevice *self = FU_SYNAPTICSMST_DEVICE (object); if (self->fd > 0) close (self->fd); @@ -89,11 +70,11 @@ synapticsmst_device_finalize (GObject *object) g_free (self->aux_node); g_free (self->version); g_free (self->chip_id_str); - G_OBJECT_CLASS (synapticsmst_device_parent_class)->finalize (object); + G_OBJECT_CLASS (fu_synapticsmst_device_parent_class)->finalize (object); } static void -synapticsmst_device_init (SynapticsMSTDevice *self) +fu_synapticsmst_device_init (FuSynapticsmstDevice *self) { const gchar *tmp; tmp = g_getenv ("FWUPD_SYNAPTICSMST_FW_DIR"); @@ -107,28 +88,28 @@ synapticsmst_device_init (SynapticsMSTDevice *self) } static void -synapticsmst_device_class_init (SynapticsMSTDeviceClass *klass) +fu_synapticsmst_device_class_init (FuSynapticsmstDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->finalize = synapticsmst_device_finalize; + object_class->finalize = fu_synapticsmst_device_finalize; } -SynapticsMSTDeviceKind -synapticsmst_device_get_kind (SynapticsMSTDevice *self) +FuSynapticsmstMode +fu_synapticsmst_device_get_kind (FuSynapticsmstDevice *self) { return self->kind; } guint16 -synapticsmst_device_get_board_id (SynapticsMSTDevice *self) +fu_synapticsmst_device_get_board_id (FuSynapticsmstDevice *self) { return self->board_id; } static gboolean -synapticsmst_device_enable_remote_control (SynapticsMSTDevice *self, GError **error) +fu_synapticsmst_device_enable_rc (FuSynapticsmstDevice *self, GError **error) { - g_autoptr(SynapticsMSTConnection) connection = NULL; + g_autoptr(FuSynapticsmstConnection) connection = NULL; /* in test mode we need to open a different file node instead */ if (self->test_mode) { @@ -157,17 +138,17 @@ synapticsmst_device_enable_remote_control (SynapticsMSTDevice *self, GError **er return TRUE; } - connection = synapticsmst_common_new (self->fd, self->layer, self->rad); - if (!synapticsmst_common_enable_remote_control (connection, error)) + connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); + if (!fu_synapticsmst_connection_enable_rc (connection, error)) return FALSE; return TRUE; } static gboolean -synapticsmst_device_disable_remote_control (SynapticsMSTDevice *self, GError **error) +fu_synapticsmst_device_disable_rc (FuSynapticsmstDevice *self, GError **error) { - g_autoptr(SynapticsMSTConnection) connection = NULL; + g_autoptr(FuSynapticsmstConnection) connection = NULL; /* in test mode we need to open a different file node instead */ if (self->test_mode) { @@ -196,22 +177,22 @@ synapticsmst_device_disable_remote_control (SynapticsMSTDevice *self, GError **e return TRUE; } - connection = synapticsmst_common_new (self->fd, self->layer, self->rad); - if (!synapticsmst_common_disable_remote_control (connection, error)) + connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); + if (!fu_synapticsmst_connection_disable_rc (connection, error)) return FALSE; return TRUE; } gboolean -synapticsmst_device_scan_cascade_device (SynapticsMSTDevice *self, +fu_synapticsmst_device_scan_cascade_device (FuSynapticsmstDevice *self, GError **error, guint8 tx_port) { guint8 layer = self->layer + 1; guint16 rad = self->rad | (tx_port << (2 * (self->layer))); guint8 byte[4]; - g_autoptr(SynapticsMSTConnection) connection = NULL; + g_autoptr(FuSynapticsmstConnection) connection = NULL; g_autoptr(GError) error_local = NULL; g_autoptr(FuDeviceLocker) locker = NULL; @@ -223,19 +204,19 @@ synapticsmst_device_scan_cascade_device (SynapticsMSTDevice *self, /* enable remote control and disable on exit */ locker = fu_device_locker_new_full (self, - (FuDeviceLockerFunc) synapticsmst_device_enable_remote_control, - (FuDeviceLockerFunc) synapticsmst_device_disable_remote_control, + (FuDeviceLockerFunc) fu_synapticsmst_device_enable_rc, + (FuDeviceLockerFunc) fu_synapticsmst_device_disable_rc, error); if (locker == NULL) return FALSE; - connection = synapticsmst_common_new (self->fd, layer, rad); - if (!synapticsmst_common_read (connection, REG_RC_CAP, byte, 1, &error_local)) { + connection = fu_synapticsmst_connection_new (self->fd, layer, rad); + if (!fu_synapticsmst_connection_read (connection, REG_RC_CAP, byte, 1, &error_local)) { g_debug ("No cascade device found: %s", error_local->message); return TRUE; } if (byte[0] & 0x04) { - if (!synapticsmst_common_read (connection, REG_VENDOR_ID, byte, 3, error)) { + if (!fu_synapticsmst_connection_read (connection, REG_VENDOR_ID, byte, 3, error)) { g_prefix_error (error, "failed to read cascade device on tx_port %d: ", tx_port); @@ -249,8 +230,8 @@ synapticsmst_device_scan_cascade_device (SynapticsMSTDevice *self, } static gboolean -synapticsmst_device_read_board_id (SynapticsMSTDevice *self, - SynapticsMSTConnection *connection, +fu_synapticsmst_device_read_board_id (FuSynapticsmstDevice *self, + FuSynapticsmstConnection *connection, guint8 *byte, GError **error) { @@ -290,7 +271,7 @@ synapticsmst_device_read_board_id (SynapticsMSTDevice *self, close (fd); } else { /* get board ID via MCU address 0x170E instead of flash access due to HDCP2.2 running */ - if (!synapticsmst_common_rc_get_command (connection, + if (!fu_synapticsmst_connection_rc_get_command (connection, UPDC_READ_FROM_MEMORY, 2, (gint)ADDR_MEMORY_CUSTOMER_ID, byte, @@ -304,16 +285,16 @@ synapticsmst_device_read_board_id (SynapticsMSTDevice *self, } static gboolean -synapticsmst_device_get_active_bank_panamera (SynapticsMSTDevice *self, +fu_synapticsmst_device_get_active_bank_panamera (FuSynapticsmstDevice *self, guint8 *bank_out, GError **error) { - g_autoptr(SynapticsMSTConnection) connection = NULL; + g_autoptr(FuSynapticsmstConnection) connection = NULL; guint32 dwData[16]; /* get used bank */ - connection = synapticsmst_common_new (self->fd, self->layer, self->rad); - if (!synapticsmst_common_rc_get_command (connection, + connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); + if (!fu_synapticsmst_connection_rc_get_command (connection, UPDC_READ_FROM_MEMORY, ((sizeof(dwData)/sizeof(dwData[0]))*4), (gint) 0x20010c, (guint8*) dwData, @@ -332,44 +313,44 @@ synapticsmst_device_get_active_bank_panamera (SynapticsMSTDevice *self, } gboolean -synapticsmst_device_enumerate_device (SynapticsMSTDevice *self, +fu_synapticsmst_device_enumerate_device (FuSynapticsmstDevice *self, GError **error) { guint8 byte[16]; guint8 bank; - g_autoptr(SynapticsMSTConnection) connection = NULL; + g_autoptr(FuSynapticsmstConnection) connection = NULL; g_autoptr(FuDeviceLocker) locker = NULL; - if (!synapticsmst_device_open (self, error)) { + if (!fu_synapticsmst_device_open (self, error)) { g_prefix_error (error, "Failed to open device in DP Aux Node %s: ", - synapticsmst_device_get_aux_node (self)); + fu_synapticsmst_device_get_aux_node (self)); return FALSE; } /* enable remote control and disable on exit */ locker = fu_device_locker_new_full (self, - (FuDeviceLockerFunc) synapticsmst_device_enable_remote_control, - (FuDeviceLockerFunc) synapticsmst_device_disable_remote_control, + (FuDeviceLockerFunc) fu_synapticsmst_device_enable_rc, + (FuDeviceLockerFunc) fu_synapticsmst_device_disable_rc, error); if (locker == NULL) return FALSE; /* read firmware version */ - connection = synapticsmst_common_new (self->fd, self->layer, self->rad); - if (!synapticsmst_common_read (connection, REG_FIRMWARE_VERSION, + connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); + if (!fu_synapticsmst_connection_read (connection, REG_FIRMWARE_VERSION, byte, 3, error)) return FALSE; self->version = g_strdup_printf ("%1d.%02d.%03d", byte[0], byte[1], byte[2]); /* read board ID */ - if (!synapticsmst_device_read_board_id (self, connection, byte, error)) + if (!fu_synapticsmst_device_read_board_id (self, connection, byte, error)) return FALSE; self->board_id = (byte[0] << 8) | (byte[1]); g_debug ("BoardID %x", self->board_id); /* read board chip_id */ - if (!synapticsmst_common_read (connection, REG_CHIP_ID, + if (!fu_synapticsmst_connection_read (connection, REG_CHIP_ID, byte, 2, error)) { g_prefix_error (error, "failed to read chip id: "); return FALSE; @@ -378,65 +359,59 @@ synapticsmst_device_enumerate_device (SynapticsMSTDevice *self, self->chip_id_str = g_strdup_printf ("VMM%02x%02x", byte[0], byte[1]); /* if running on panamera, check the active bank (for debugging logs) */ - if (self->chip_id > 0x5000 && - !synapticsmst_device_get_active_bank_panamera (self, &bank, error)) + if (fu_synapticsmst_family_from_chip_id (self->chip_id) == FU_SYNAPTICSMST_FAMILY_PANAMERA && + !fu_synapticsmst_device_get_active_bank_panamera (self, &bank, error)) return FALSE; return TRUE; } const gchar * -synapticsmst_device_get_aux_node (SynapticsMSTDevice *self) +fu_synapticsmst_device_get_aux_node (FuSynapticsmstDevice *self) { return self->aux_node; } const gchar * -synapticsmst_device_get_version (SynapticsMSTDevice *self) +fu_synapticsmst_device_get_version (FuSynapticsmstDevice *self) { return self->version; } -static guint16 -synapticsmst_device_get_chip_id (SynapticsMSTDevice *self) -{ - return self->chip_id; -} - const gchar * -synapticsmst_device_get_chip_id_str (SynapticsMSTDevice *self) +fu_synapticsmst_device_get_chip_id_str (FuSynapticsmstDevice *self) { return self->chip_id_str; } guint16 -synapticsmst_device_get_rad (SynapticsMSTDevice *self) +fu_synapticsmst_device_get_rad (FuSynapticsmstDevice *self) { return self->rad; } guint8 -synapticsmst_device_get_layer (SynapticsMSTDevice *self) +fu_synapticsmst_device_get_layer (FuSynapticsmstDevice *self) { return self->layer; } gboolean -synapticsmst_device_get_cascade (SynapticsMSTDevice *self) +fu_synapticsmst_device_get_cascade (FuSynapticsmstDevice *self) { return self->has_cascade; } static gboolean -synapticsmst_device_get_flash_checksum (SynapticsMSTDevice *self, +fu_synapticsmst_device_get_flash_checksum (FuSynapticsmstDevice *self, guint32 length, guint32 offset, guint32 *checksum, GError **error) { - g_autoptr(SynapticsMSTConnection) connection = NULL; + g_autoptr(FuSynapticsmstConnection) connection = NULL; - connection = synapticsmst_common_new (self->fd, self->layer, self->rad); - if (!synapticsmst_common_rc_special_get_command (connection, + connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); + if (!fu_synapticsmst_connection_rc_special_get_command (connection, UPDC_CAL_EEPROM_CHECKSUM, length, offset, NULL, 4, @@ -450,7 +425,7 @@ synapticsmst_device_get_flash_checksum (SynapticsMSTDevice *self, } static guint16 -synapticsmst_device_get_crc (guint16 crc, guint8 type, guint32 length, const guint8 *payload_data) +fu_synapticsmst_device_get_crc (guint16 crc, guint8 type, guint32 length, const guint8 *payload_data) { static const guint16 CRC16_table[] = { 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022, @@ -508,19 +483,19 @@ synapticsmst_device_get_crc (guint16 crc, guint8 type, guint32 length, const gui } static gboolean -synapticsmst_device_set_flash_sector_erase (SynapticsMSTDevice *self, +fu_synapticsmst_device_set_flash_sector_erase (FuSynapticsmstDevice *self, guint16 rc_cmd, guint16 offset, GError **error) { guint16 us_data; - g_autoptr(SynapticsMSTConnection) connection = NULL; + g_autoptr(FuSynapticsmstConnection) connection = NULL; - connection = synapticsmst_common_new (self->fd, self->layer, self->rad); + connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); /* Need to add Wp control ? */ us_data = rc_cmd + offset; - if (!synapticsmst_common_rc_set_command (connection, + if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_FLASH_ERASE, 2, 0, (guint8 *)&us_data, error)) { @@ -533,7 +508,7 @@ synapticsmst_device_set_flash_sector_erase (SynapticsMSTDevice *self, } static gboolean -synapticsmst_device_update_esm (SynapticsMSTDevice *self, +fu_synapticsmst_device_update_esm (FuSynapticsmstDevice *self, const guint8 *payload_data, GFileProgressCallback progress_cb, gpointer progress_data, @@ -544,13 +519,13 @@ synapticsmst_device_update_esm (SynapticsMSTDevice *self, guint32 flash_checksum = 0; guint32 unit_sz = BLOCK_UNIT; guint32 write_loops = 0; - g_autoptr(SynapticsMSTConnection) connection = NULL; + g_autoptr(FuSynapticsmstConnection) connection = NULL; - connection = synapticsmst_common_new (self->fd, self->layer, self->rad); + connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); for (guint32 i = 0; i < esm_sz; i++) checksum += *(payload_data + EEPROM_ESM_OFFSET +i); - if (!synapticsmst_device_get_flash_checksum (self, + if (!fu_synapticsmst_device_get_flash_checksum (self, esm_sz, EEPROM_ESM_OFFSET, &flash_checksum, error)) { @@ -573,7 +548,7 @@ synapticsmst_device_update_esm (SynapticsMSTDevice *self, /* erase ESM firmware; erase failure is fatal */ for (guint32 j = 0; j < 4; j++) { - if (!synapticsmst_device_set_flash_sector_erase (self, + if (!fu_synapticsmst_device_set_flash_sector_erase (self, FLASH_SECTOR_ERASE_64K, j + 4, error)) { @@ -588,7 +563,7 @@ synapticsmst_device_update_esm (SynapticsMSTDevice *self, /* write firmware */ for (guint32 i = 0; i < write_loops; i++) { g_autoptr(GError) error_local = NULL; - if (!synapticsmst_common_rc_set_command (connection, + if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_WRITE_TO_EEPROM, unit_sz, write_offset, @@ -612,7 +587,7 @@ synapticsmst_device_update_esm (SynapticsMSTDevice *self, flash_checksum = 0; for (guint32 i = 0; i < esm_sz; i++) checksum += *(payload_data + EEPROM_ESM_OFFSET +i); - if (!synapticsmst_device_get_flash_checksum (self, + if (!fu_synapticsmst_device_get_flash_checksum (self, esm_sz, EEPROM_ESM_OFFSET, &flash_checksum, @@ -639,14 +614,14 @@ synapticsmst_device_update_esm (SynapticsMSTDevice *self, } static gboolean -synapticsmst_device_update_tesla_leaf_firmware (SynapticsMSTDevice *self, +fu_synapticsmst_device_update_tesla_leaf_firmware (FuSynapticsmstDevice *self, guint32 payload_len, const guint8 *payload_data, GFileProgressCallback progress_cb, gpointer progress_data, GError **error) { - g_autoptr(SynapticsMSTConnection) connection = NULL; + g_autoptr(FuSynapticsmstConnection) connection = NULL; guint32 data_to_write = 0; guint32 offset = 0; guint32 write_loops = 0; @@ -657,12 +632,12 @@ synapticsmst_device_update_tesla_leaf_firmware (SynapticsMSTDevice *self, if (payload_len % BLOCK_UNIT) write_loops++; - connection = synapticsmst_common_new (self->fd, self->layer, self->rad); + connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); for (guint32 retries_cnt = 0; ; retries_cnt++) { guint32 checksum = 0; guint32 flash_checksum = 0; - if (!synapticsmst_device_set_flash_sector_erase (self, 0xffff, 0, error)) + if (!fu_synapticsmst_device_set_flash_sector_erase (self, 0xffff, 0, error)) return FALSE; g_debug ("Waiting for flash clear to settle"); g_usleep (FLASH_SETTLE_TIME); @@ -673,7 +648,7 @@ synapticsmst_device_update_tesla_leaf_firmware (SynapticsMSTDevice *self, if (data_to_write < BLOCK_UNIT) length = data_to_write; - if (!synapticsmst_common_rc_set_command (connection, + if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_WRITE_TO_EEPROM, length, offset, payload_data + offset, @@ -681,7 +656,7 @@ synapticsmst_device_update_tesla_leaf_firmware (SynapticsMSTDevice *self, g_warning ("Failed to write flash offset 0x%04x: %s, retrying", offset, error_local->message); /* repeat once */ - if (!synapticsmst_common_rc_set_command (connection, + if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_WRITE_TO_EEPROM, length, offset, payload_data + offset, @@ -704,7 +679,7 @@ synapticsmst_device_update_tesla_leaf_firmware (SynapticsMSTDevice *self, for (guint32 i = 0; i < payload_len; i++) checksum += *(payload_data + i); - if (!synapticsmst_device_get_flash_checksum (self, + if (!fu_synapticsmst_device_get_flash_checksum (self, payload_len, 0, &flash_checksum, @@ -729,7 +704,7 @@ synapticsmst_device_update_tesla_leaf_firmware (SynapticsMSTDevice *self, } static gboolean -synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *self, +fu_synapticsmst_device_update_panamera_firmware (FuSynapticsmstDevice *self, guint32 payload_len, const guint8 *payload_data, GFileProgressCallback progress_cb, @@ -747,10 +722,10 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *self, guint8 tagData[16]; struct tm *pTM; time_t timeptr; - g_autoptr(SynapticsMSTConnection) connection = NULL; + g_autoptr(FuSynapticsmstConnection) connection = NULL; /* get used bank */ - if (!synapticsmst_device_get_active_bank_panamera (self, &bank_in_use, error)) + if (!fu_synapticsmst_device_get_active_bank_panamera (self, &bank_in_use, error)) return FALSE; if (bank_in_use == BANKTAG_1) bank_to_update = BANKTAG_0; @@ -781,10 +756,10 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *self, /* erase storage */ erase_offset = bank_to_update * 2; - if (!synapticsmst_device_set_flash_sector_erase (self, + if (!fu_synapticsmst_device_set_flash_sector_erase (self, FLASH_SECTOR_ERASE_64K, erase_offset++, error)) return FALSE; - if (!synapticsmst_device_set_flash_sector_erase (self, + if (!fu_synapticsmst_device_set_flash_sector_erase (self, FLASH_SECTOR_ERASE_64K, erase_offset, error)) return FALSE; g_debug ("Waiting for flash clear to settle"); @@ -793,10 +768,10 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *self, /* write */ write_idx = 0; write_offset = EEPROM_BANK_OFFSET * bank_to_update; - connection = synapticsmst_common_new (self->fd, self->layer, self->rad); + connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); for (guint32 i = 0; i < write_loops ; i++ ) { g_autoptr(GError) error_local = NULL; - if (!synapticsmst_common_rc_set_command (connection, + if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_WRITE_TO_EEPROM, unit_sz, write_offset, @@ -804,7 +779,7 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *self, &error_local)) { g_warning ("Write failed: %s, retrying", error_local->message); /* repeat once */ - if (!synapticsmst_common_rc_set_command (connection, + if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_WRITE_TO_EEPROM, unit_sz, write_offset, @@ -825,10 +800,10 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *self, } /* verify CRC */ - checksum = synapticsmst_device_get_crc ( 0, 16, fw_size, payload_data ); + checksum = fu_synapticsmst_device_get_crc ( 0, 16, fw_size, payload_data ); for (guint32 i = 0; i < 4; i++) { g_usleep (1000); /* wait crc calculation */ - if (!synapticsmst_common_rc_special_get_command (connection, + if (!fu_synapticsmst_connection_rc_special_get_command (connection, UPDC_CAL_EEPROM_CHECK_CRC16, fw_size, (EEPROM_BANK_OFFSET * bank_to_update), NULL, 4, (guint8 *)(&flash_checksum), @@ -858,16 +833,16 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *self, tagData[1] = pTM->tm_mon + 1; tagData[2] = pTM->tm_mday; tagData[3] = pTM->tm_year + 1900 - 2000; - crc_tmp = synapticsmst_device_get_crc (0, 16, fw_size, payload_data); + crc_tmp = fu_synapticsmst_device_get_crc (0, 16, fw_size, payload_data); tagData[0] = bank_to_update; tagData[4] = (crc_tmp >> 8) & 0xff; tagData[5] = crc_tmp & 0xff; - tagData[15] = (guint8) synapticsmst_device_get_crc (0, 8, 15, tagData); + tagData[15] = (guint8) fu_synapticsmst_device_get_crc (0, 8, 15, tagData); g_debug ("tag date %x %x %x crc %x %x %x %x", tagData[1], tagData[2], tagData[3], tagData[0], tagData[4], tagData[5], tagData[15]); for (guint32 retries_cnt = 0; ; retries_cnt++) { gboolean match = TRUE; - if (!synapticsmst_common_rc_set_command (connection, + if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_WRITE_TO_EEPROM, 16, (EEPROM_BANK_OFFSET * bank_to_update + EEPROM_TAG_OFFSET), @@ -877,7 +852,7 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *self, return FALSE; } g_usleep (200); - if (!synapticsmst_common_rc_get_command (connection, + if (!fu_synapticsmst_connection_rc_get_command (connection, UPDC_READ_FROM_EEPROM, 16, (EEPROM_BANK_OFFSET * bank_to_update + EEPROM_TAG_OFFSET), @@ -904,7 +879,7 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *self, } /* set tag invalid*/ - if (!synapticsmst_common_rc_get_command (connection, + if (!fu_synapticsmst_connection_rc_get_command (connection, UPDC_READ_FROM_EEPROM, 1, (EEPROM_BANK_OFFSET * bank_in_use + EEPROM_TAG_OFFSET + 15), tagData, @@ -919,7 +894,7 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *self, guint32 erase_offset; /* offset for last 4k of bank# */ erase_offset = (EEPROM_BANK_OFFSET * bank_in_use + EEPROM_BANK_OFFSET - 0x1000) / 0x1000; - if (!synapticsmst_device_set_flash_sector_erase (self, + if (!fu_synapticsmst_device_set_flash_sector_erase (self, FLASH_SECTOR_ERASE_4K, erase_offset, error)) @@ -927,7 +902,7 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *self, /* CRC8 is 0xff, set it to 0x00 */ } else { tagData[1] = 0x00; - if (!synapticsmst_common_rc_set_command (connection, + if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_WRITE_TO_EEPROM, 1, (EEPROM_BANK_OFFSET * bank_in_use + EEPROM_TAG_OFFSET + 15), &tagData[1], @@ -936,7 +911,7 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *self, return FALSE; } } - if (!synapticsmst_common_rc_get_command (connection, + if (!fu_synapticsmst_connection_rc_get_command (connection, UPDC_READ_FROM_EEPROM, 1, (EEPROM_BANK_OFFSET * bank_in_use + EEPROM_TAG_OFFSET + 15), readBuf, @@ -961,18 +936,19 @@ synapticsmst_device_update_panamera_firmware (SynapticsMSTDevice *self, } static gboolean -synapticsmst_device_check_firmware_content (SynapticsMSTDevice *self, +fu_synapticsmst_device_check_firmware_content (FuSynapticsmstDevice *self, GBytes *fw, - SynapticsMSTChipKind chip_type, + FuSynapticsmstFamily chip_type, GError **error) { gsize payload_len, payload_len_max; switch (chip_type) { - case SYNAPTICSMST_CHIP_KIND_PANAMERA: + case FU_SYNAPTICSMST_FAMILY_PANAMERA: payload_len_max = PAYLOAD_SIZE_512K; break; - case SYNAPTICSMST_CHIP_KIND_TESLA_LEAF: + case FU_SYNAPTICSMST_FAMILY_TESLA: + case FU_SYNAPTICSMST_FAMILY_LEAF: payload_len_max = PAYLOAD_SIZE_64K; break; default: @@ -1002,18 +978,18 @@ synapticsmst_device_check_firmware_content (SynapticsMSTDevice *self, } static gboolean -synapticsmst_device_panamera_prepare_write (SynapticsMSTDevice *self, GError **error) +fu_synapticsmst_device_panamera_prepare_write (FuSynapticsmstDevice *self, GError **error) { guint32 dwData[4] = {0}; - g_autoptr(SynapticsMSTConnection) connection = NULL; + g_autoptr(FuSynapticsmstConnection) connection = NULL; /* Need to detect flash mode and ESM first ? */ /* disable flash Quad mode and ESM/HDCP2.2*/ - connection = synapticsmst_common_new (self->fd, self->layer, self->rad); + connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); /* disable ESM first */ dwData[0] = 0x21; - if (!synapticsmst_common_rc_set_command (connection, + if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_WRITE_TO_MEMORY, 4, (gint)REG_ESM_DISABLE, (guint8*)dwData, error)) { @@ -1025,7 +1001,7 @@ synapticsmst_device_panamera_prepare_write (SynapticsMSTDevice *self, GError **e g_usleep (200); /* disable QUAD mode */ - if (!synapticsmst_common_rc_get_command (connection, + if (!fu_synapticsmst_connection_rc_get_command (connection, UPDC_READ_FROM_MEMORY, ((sizeof(dwData)/sizeof(dwData[0]))*4), (gint)REG_QUAD_DISABLE, (guint8*)dwData, @@ -1035,7 +1011,7 @@ synapticsmst_device_panamera_prepare_write (SynapticsMSTDevice *self, GError **e } dwData[0] = 0x00; - if (!synapticsmst_common_rc_set_command (connection, + if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_WRITE_TO_MEMORY, 4, (gint)REG_QUAD_DISABLE, (guint8*)dwData, error)) { @@ -1044,7 +1020,7 @@ synapticsmst_device_panamera_prepare_write (SynapticsMSTDevice *self, GError **e } /* disable HDCP2.2 */ - if (!synapticsmst_common_rc_get_command (connection, + if (!fu_synapticsmst_connection_rc_get_command (connection, UPDC_READ_FROM_MEMORY, 4, (gint)REG_HDCP22_DISABLE, (guint8*)dwData, error)) { @@ -1053,7 +1029,7 @@ synapticsmst_device_panamera_prepare_write (SynapticsMSTDevice *self, GError **e } dwData[0] = dwData[0] & (~BIT(2)); - if (!synapticsmst_common_rc_set_command (connection, + if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_WRITE_TO_MEMORY, 4, (gint)REG_HDCP22_DISABLE, (guint8*)dwData, error)) { @@ -1065,16 +1041,16 @@ synapticsmst_device_panamera_prepare_write (SynapticsMSTDevice *self, GError **e } static gboolean -synapticsmst_device_restart (SynapticsMSTDevice *self, +fu_synapticsmst_device_restart (FuSynapticsmstDevice *self, GError **error) { - g_autoptr(SynapticsMSTConnection) connection = NULL; + g_autoptr(FuSynapticsmstConnection) connection = NULL; guint8 dwData[4] = {0xF5, 0, 0 ,0}; g_autoptr(GError) error_local = NULL; /* issue the reboot command, ignore return code (triggers before returning) */ - connection = synapticsmst_common_new (self->fd, self->layer, self->rad); - if (!synapticsmst_common_rc_set_command (connection, + connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); + if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_WRITE_TO_MEMORY, 4, (gint) 0x2000FC, (guint8*) &dwData, &error_local)) @@ -1084,7 +1060,7 @@ synapticsmst_device_restart (SynapticsMSTDevice *self, } gboolean -synapticsmst_device_write_firmware (SynapticsMSTDevice *self, +fu_synapticsmst_device_write_firmware (FuSynapticsmstDevice *self, GBytes *fw, GFileProgressCallback progress_cb, gpointer progress_data, @@ -1095,24 +1071,28 @@ synapticsmst_device_write_firmware (SynapticsMSTDevice *self, const guint8 *payload_data; gsize payload_len; guint16 tmp; - SynapticsMSTChipKind chip_type = SYNAPTICSMST_CHIP_KIND_UNKNOWN; + FuSynapticsmstFamily family = FU_SYNAPTICSMST_FAMILY_UNKNOWN; g_autoptr(FuDeviceLocker) locker = NULL; payload_data = g_bytes_get_data (fw, &payload_len); - if (synapticsmst_device_get_chip_id (self) > 0x5000) - chip_type = SYNAPTICSMST_CHIP_KIND_PANAMERA; - else - chip_type = SYNAPTICSMST_CHIP_KIND_TESLA_LEAF; + family = fu_synapticsmst_family_from_chip_id (self->chip_id); + if (family == FU_SYNAPTICSMST_FAMILY_UNKNOWN) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "chip family unsupported"); + return FALSE; + } - if (!synapticsmst_device_check_firmware_content (self, fw, chip_type, error)){ + if (!fu_synapticsmst_device_check_firmware_content (self, fw, family, error)){ g_prefix_error (error, "Invalid file content: "); return FALSE; } /* check firmware and board ID again */ tmp = (*(payload_data + ADDR_CUSTOMER_ID) << 8) + *(payload_data + ADDR_BOARD_ID); - if (tmp != synapticsmst_device_get_board_id (self) && !install_force) { + if (tmp != fu_synapticsmst_device_get_board_id (self) && !install_force) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, @@ -1121,35 +1101,35 @@ synapticsmst_device_write_firmware (SynapticsMSTDevice *self, } /* open device */ - if (!synapticsmst_device_open (self, error)) { + if (!fu_synapticsmst_device_open (self, error)) { g_prefix_error (error, "can't open DP Aux node %s", - synapticsmst_device_get_aux_node (self)); + fu_synapticsmst_device_get_aux_node (self)); return FALSE; } /* enable remote control and disable on exit */ if (reboot) { locker = fu_device_locker_new_full (self, - (FuDeviceLockerFunc) synapticsmst_device_enable_remote_control, - (FuDeviceLockerFunc) synapticsmst_device_restart, + (FuDeviceLockerFunc) fu_synapticsmst_device_enable_rc, + (FuDeviceLockerFunc) fu_synapticsmst_device_restart, error); } else { locker = fu_device_locker_new_full (self, - (FuDeviceLockerFunc) synapticsmst_device_enable_remote_control, - (FuDeviceLockerFunc) synapticsmst_device_disable_remote_control, + (FuDeviceLockerFunc) fu_synapticsmst_device_enable_rc, + (FuDeviceLockerFunc) fu_synapticsmst_device_disable_rc, error); } if (locker == NULL) return FALSE; /* update firmware */ - if (chip_type == SYNAPTICSMST_CHIP_KIND_PANAMERA) { - if (!synapticsmst_device_panamera_prepare_write (self, error)) { + if (family == FU_SYNAPTICSMST_FAMILY_PANAMERA) { + if (!fu_synapticsmst_device_panamera_prepare_write (self, error)) { g_prefix_error (error, "Failed to prepare for write: "); return FALSE; } - if (!synapticsmst_device_update_esm (self, + if (!fu_synapticsmst_device_update_esm (self, payload_data, progress_cb, progress_data, @@ -1157,7 +1137,7 @@ synapticsmst_device_write_firmware (SynapticsMSTDevice *self, g_prefix_error (error, "ESM update failed: "); return FALSE; } - if (!synapticsmst_device_update_panamera_firmware (self, + if (!fu_synapticsmst_device_update_panamera_firmware (self, payload_len, payload_data, progress_cb, @@ -1167,7 +1147,7 @@ synapticsmst_device_write_firmware (SynapticsMSTDevice *self, return FALSE; } } else { - if (!synapticsmst_device_update_tesla_leaf_firmware (self, + if (!fu_synapticsmst_device_update_tesla_leaf_firmware (self, payload_len, payload_data, progress_cb, @@ -1181,15 +1161,15 @@ synapticsmst_device_write_firmware (SynapticsMSTDevice *self, return TRUE; } -SynapticsMSTDevice * -synapticsmst_device_new (SynapticsMSTDeviceKind kind, +FuSynapticsmstDevice * +fu_synapticsmst_device_new (FuSynapticsmstMode kind, const gchar *aux_node, guint8 layer, guint16 rad) { - SynapticsMSTDevice *self; + FuSynapticsmstDevice *self; - self = g_object_new (SYNAPTICSMST_TYPE_DEVICE, NULL); + self = g_object_new (FU_SYNAPTICSMST_TYPE_DEVICE, NULL); self->aux_node = g_strdup(aux_node); self->kind = kind; @@ -1198,15 +1178,15 @@ synapticsmst_device_new (SynapticsMSTDeviceKind kind, self->rad = rad; self->has_cascade = FALSE; - return SYNAPTICSMST_DEVICE (self); + return FU_SYNAPTICSMST_DEVICE (self); } gboolean -synapticsmst_device_open (SynapticsMSTDevice *self, GError **error) +fu_synapticsmst_device_open (FuSynapticsmstDevice *self, GError **error) { g_autofree gchar *filename = NULL; guint8 byte[4]; - g_autoptr(SynapticsMSTConnection) connection = NULL; + g_autoptr(FuSynapticsmstConnection) connection = NULL; /* file doesn't exist on this system */ filename = g_strdup_printf ("%s/%s", self->fw_dir, self->aux_node); @@ -1230,13 +1210,13 @@ synapticsmst_device_open (SynapticsMSTDevice *self, GError **error) return FALSE; } - connection = synapticsmst_common_new (self->fd, 0, 0); - if (!synapticsmst_common_read (connection, REG_RC_CAP, byte, 1, error)) { + connection = fu_synapticsmst_connection_new (self->fd, 0, 0); + if (!fu_synapticsmst_connection_read (connection, REG_RC_CAP, byte, 1, error)) { g_prefix_error (error, "failed to read device: "); return FALSE; } if (byte[0] & 0x04) { - if (!synapticsmst_common_read (connection, REG_VENDOR_ID, + if (!fu_synapticsmst_connection_read (connection, REG_VENDOR_ID, byte, 3, error)) { g_prefix_error (error, "failed to read vendor ID: "); return FALSE; diff --git a/plugins/synapticsmst/fu-synapticsmst-device.h b/plugins/synapticsmst/fu-synapticsmst-device.h new file mode 100644 index 000000000..87d3d5bbe --- /dev/null +++ b/plugins/synapticsmst/fu-synapticsmst-device.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 Richard Hughes + * Copyright (C) 2016 Mario Limonciello + * Copyright (C) 2017 Peichen Huang + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-synapticsmst-common.h" + +G_BEGIN_DECLS + +#define FU_SYNAPTICSMST_TYPE_DEVICE (fu_synapticsmst_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuSynapticsmstDevice, fu_synapticsmst_device, FU, SYNAPTICSMST_DEVICE, GObject) + +#define SYSFS_DRM_DP_AUX "/sys/class/drm_dp_aux_dev" + +FuSynapticsmstDevice *fu_synapticsmst_device_new (FuSynapticsmstMode kind, + const gchar *aux_node, + guint8 layer, + guint16 rad); + +/* helpers */ +GPtrArray *fu_synapticsmst_device_get_guids (FuSynapticsmstDevice *self); +gboolean fu_synapticsmst_device_scan_cascade_device (FuSynapticsmstDevice *self, + GError **error, + guint8 tx_port); +gboolean fu_synapticsmst_device_open (FuSynapticsmstDevice *self, + GError **error); + +/* getters */ +FuSynapticsmstMode fu_synapticsmst_device_get_kind (FuSynapticsmstDevice *self); +guint16 fu_synapticsmst_device_get_board_id (FuSynapticsmstDevice *self); +const gchar *fu_synapticsmst_device_get_version (FuSynapticsmstDevice *self); +const gchar *fu_synapticsmst_device_get_chip_id_str (FuSynapticsmstDevice *self); +const gchar *fu_synapticsmst_device_get_aux_node (FuSynapticsmstDevice *self); +guint16 fu_synapticsmst_device_get_rad (FuSynapticsmstDevice *self); +guint8 fu_synapticsmst_device_get_layer (FuSynapticsmstDevice *self); +gboolean fu_synapticsmst_device_get_cascade (FuSynapticsmstDevice *self); + +/* object methods */ +gboolean fu_synapticsmst_device_enumerate_device (FuSynapticsmstDevice *devices, + GError **error); +gboolean fu_synapticsmst_device_write_firmware (FuSynapticsmstDevice *self, + GBytes *fw, + GFileProgressCallback progress_cb, + gpointer user_data, + gboolean reboot, + gboolean install_force, + GError **error); + +G_END_DECLS diff --git a/plugins/synapticsmst/meson.build b/plugins/synapticsmst/meson.build index 046ac6e4d..eb2723737 100644 --- a/plugins/synapticsmst/meson.build +++ b/plugins/synapticsmst/meson.build @@ -8,8 +8,9 @@ shared_module('fu_plugin_synapticsmst', fu_hash, sources : [ 'fu-plugin-synapticsmst.c', - 'synapticsmst-common.c', - 'synapticsmst-device.c', + 'fu-synapticsmst-common.c', + 'fu-synapticsmst-connection.c', + 'fu-synapticsmst-device.c', ], include_directories : [ include_directories('../..'), @@ -38,8 +39,9 @@ if get_option('tests') fu_hash, sources : [ 'fu-self-test.c', - 'synapticsmst-common.c', - 'synapticsmst-device.c', + 'fu-synapticsmst-common.c', + 'fu-synapticsmst-connection.c', + 'fu-synapticsmst-device.c', ], include_directories : [ include_directories('../..'), diff --git a/plugins/synapticsmst/synapticsmst-device.h b/plugins/synapticsmst/synapticsmst-device.h deleted file mode 100644 index cf3d1ca0b..000000000 --- a/plugins/synapticsmst/synapticsmst-device.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2015 Richard Hughes - * Copyright (C) 2016 Mario Limonciello - * Copyright (C) 2017 Peichen Huang - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#pragma once - -#include - -G_BEGIN_DECLS - -#define SYNAPTICSMST_TYPE_DEVICE (synapticsmst_device_get_type ()) -G_DECLARE_FINAL_TYPE (SynapticsMSTDevice, synapticsmst_device, SYNAPTICSMST, DEVICE, GObject) - -#define SYSFS_DRM_DP_AUX "/sys/class/drm_dp_aux_dev" - -/** - * SynapticsMSTDeviceKind: - * @SYNAPTICSMST_DEVICE_KIND_UNKNOWN: Type invalid or not known - * @SYNAPTICSMST_DEVICE_KIND_DIRECT: Directly addressable - * @SYNAPTICSMST_DEVICE_KIND_REMOTE: Requires remote register work - * - * The device type. - **/ -typedef enum { - SYNAPTICSMST_DEVICE_KIND_UNKNOWN, - SYNAPTICSMST_DEVICE_KIND_DIRECT, - SYNAPTICSMST_DEVICE_KIND_REMOTE, - /*< private >*/ - SYNAPTICSMST_DEVICE_KIND_LAST -} SynapticsMSTDeviceKind; - -typedef enum { - SYNAPTICSMST_CHIP_KIND_UNKNOWN, - SYNAPTICSMST_CHIP_KIND_TESLA_LEAF, - SYNAPTICSMST_CHIP_KIND_PANAMERA, - /**/ - SYNAPTICSMST_CHIP_KIND_LAST -} SynapticsMSTChipKind; - -SynapticsMSTDevice *synapticsmst_device_new (SynapticsMSTDeviceKind kind, - const gchar *aux_node, - guint8 layer, - guint16 rad); - -/* helpers */ -SynapticsMSTDeviceKind synapticsmst_device_kind_from_string (const gchar *kind); -const gchar *synapticsmst_device_kind_to_string (SynapticsMSTDeviceKind kind); -GPtrArray *synapticsmst_device_get_guids (SynapticsMSTDevice *self); -gboolean synapticsmst_device_scan_cascade_device (SynapticsMSTDevice *self, - GError **error, - guint8 tx_port); -gboolean synapticsmst_device_open (SynapticsMSTDevice *self, - GError **error); - -/* getters */ -SynapticsMSTDeviceKind synapticsmst_device_get_kind (SynapticsMSTDevice *self); -guint16 synapticsmst_device_get_board_id (SynapticsMSTDevice *self); -const gchar *synapticsmst_device_get_version (SynapticsMSTDevice *self); -const gchar *synapticsmst_device_get_chip_id_str (SynapticsMSTDevice *self); -const gchar *synapticsmst_device_get_aux_node (SynapticsMSTDevice *self); -guint16 synapticsmst_device_get_rad (SynapticsMSTDevice *self); -guint8 synapticsmst_device_get_layer (SynapticsMSTDevice *self); -gboolean synapticsmst_device_get_cascade (SynapticsMSTDevice *self); - -/* object methods */ -gboolean synapticsmst_device_enumerate_device (SynapticsMSTDevice *devices, - GError **error); -gboolean synapticsmst_device_write_firmware (SynapticsMSTDevice *self, - GBytes *fw, - GFileProgressCallback progress_cb, - gpointer user_data, - gboolean reboot, - gboolean install_force, - GError **error); - -G_END_DECLS diff --git a/plugins/synapticsmst/synapticsmst.quirk b/plugins/synapticsmst/synapticsmst.quirk index 7d3dc32e3..226f7966d 100644 --- a/plugins/synapticsmst/synapticsmst.quirk +++ b/plugins/synapticsmst/synapticsmst.quirk @@ -41,7 +41,7 @@ DeviceKind = panamera [SynapticsMSTBoardID=513] Name = ThinkPad Workstation Dock -DeviceKind = panamera +DeviceKind = tesla [SynapticsMSTBoardID=595] Name = ThinkPad Thunderbolt 3 Workstation Dock From 5074b97d78c404cfac278fc26a3c140f73019a74 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 15 Aug 2019 14:44:40 +0100 Subject: [PATCH 038/158] synapticsmst: Make FuSynapticsmstConnection an actual GObject This allows us to use FuDeviceLocker in the future without a wrapper device. --- .../synapticsmst/fu-synapticsmst-connection.c | 129 +++++++++--------- .../synapticsmst/fu-synapticsmst-connection.h | 27 ++-- 2 files changed, 79 insertions(+), 77 deletions(-) diff --git a/plugins/synapticsmst/fu-synapticsmst-connection.c b/plugins/synapticsmst/fu-synapticsmst-connection.c index 5691bc2cf..77a17039f 100644 --- a/plugins/synapticsmst/fu-synapticsmst-connection.c +++ b/plugins/synapticsmst/fu-synapticsmst-connection.c @@ -15,18 +15,31 @@ #define MAX_WAIT_TIME 3 /* unit : second */ struct _FuSynapticsmstConnection { + GObject parent_instance; gint fd; /* not owned by the connection */ guint8 layer; guint8 remain_layer; guint8 rad; }; +G_DEFINE_TYPE (FuSynapticsmstConnection, fu_synapticsmst_connection, G_TYPE_OBJECT) + +static void +fu_synapticsmst_connection_init (FuSynapticsmstConnection *self) +{ +} + +static void +fu_synapticsmst_connection_class_init (FuSynapticsmstConnectionClass *klass) +{ +} + static gboolean -fu_synapticsmst_connection_aux_node_read (FuSynapticsmstConnection *connection, +fu_synapticsmst_connection_aux_node_read (FuSynapticsmstConnection *self, guint32 offset, guint8 *buf, gint length, GError **error) { - if (lseek (connection->fd, offset, SEEK_SET) != offset) { + if (lseek (self->fd, offset, SEEK_SET) != offset) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, @@ -34,7 +47,7 @@ fu_synapticsmst_connection_aux_node_read (FuSynapticsmstConnection *connection, return FALSE; } - if (read (connection->fd, buf, length) != length) { + if (read (self->fd, buf, length) != length) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, @@ -46,11 +59,11 @@ fu_synapticsmst_connection_aux_node_read (FuSynapticsmstConnection *connection, } static gboolean -fu_synapticsmst_connection_aux_node_write (FuSynapticsmstConnection *connection, +fu_synapticsmst_connection_aux_node_write (FuSynapticsmstConnection *self, guint32 offset, const guint8 *buf, gint length, GError **error) { - if (lseek (connection->fd, offset, SEEK_SET) != offset) { + if (lseek (self->fd, offset, SEEK_SET) != offset) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, @@ -58,7 +71,7 @@ fu_synapticsmst_connection_aux_node_write (FuSynapticsmstConnection *connection, return FALSE; } - if (write (connection->fd, buf, length) != length) { + if (write (self->fd, buf, length) != length) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, @@ -70,89 +83,83 @@ fu_synapticsmst_connection_aux_node_write (FuSynapticsmstConnection *connection, } static gboolean -fu_synapticsmst_connection_bus_read (FuSynapticsmstConnection *connection, +fu_synapticsmst_connection_bus_read (FuSynapticsmstConnection *self, guint32 offset, guint8 *buf, guint32 length, GError **error) { - return fu_synapticsmst_connection_aux_node_read (connection, offset, buf, + return fu_synapticsmst_connection_aux_node_read (self, offset, buf, length, error); } static gboolean -fu_synapticsmst_connection_bus_write (FuSynapticsmstConnection *connection, +fu_synapticsmst_connection_bus_write (FuSynapticsmstConnection *self, guint32 offset, const guint8 *buf, guint32 length, GError **error) { - return fu_synapticsmst_connection_aux_node_write (connection, offset, buf, + return fu_synapticsmst_connection_aux_node_write (self, offset, buf, length, error); } -void -fu_synapticsmst_connection_free (FuSynapticsmstConnection *connection) -{ - g_free (connection); -} - FuSynapticsmstConnection * fu_synapticsmst_connection_new (gint fd, guint8 layer, guint rad) { - FuSynapticsmstConnection *connection = g_new0 (FuSynapticsmstConnection, 1); - connection->fd = fd; - connection->layer = layer; - connection->remain_layer = layer; - connection->rad = rad; - return connection; + FuSynapticsmstConnection *self = g_object_new (FU_TYPE_SYNAPTICSMST_CONNECTION, NULL); + self->fd = fd; + self->layer = layer; + self->remain_layer = layer; + self->rad = rad; + return self; } gboolean -fu_synapticsmst_connection_read (FuSynapticsmstConnection *connection, +fu_synapticsmst_connection_read (FuSynapticsmstConnection *self, guint32 offset, guint8 *buf, guint32 length, GError **error) { - if (connection->layer && connection->remain_layer) { + if (self->layer && self->remain_layer) { guint8 node; gboolean result; - connection->remain_layer--; - node = (connection->rad >> connection->remain_layer * 2) & 0x03; - result = fu_synapticsmst_connection_rc_get_command (connection, + self->remain_layer--; + node = (self->rad >> self->remain_layer * 2) & 0x03; + result = fu_synapticsmst_connection_rc_get_command (self, UPDC_READ_FROM_TX_DPCD + node, length, offset, (guint8 *)buf, error); - connection->remain_layer++; + self->remain_layer++; return result; } - return fu_synapticsmst_connection_bus_read (connection, offset, buf, length, error); + return fu_synapticsmst_connection_bus_read (self, offset, buf, length, error); } gboolean -fu_synapticsmst_connection_write (FuSynapticsmstConnection *connection, +fu_synapticsmst_connection_write (FuSynapticsmstConnection *self, guint32 offset, const guint8 *buf, guint32 length, GError **error) { - if (connection->layer && connection->remain_layer) { + if (self->layer && self->remain_layer) { guint8 node; gboolean result; - connection->remain_layer--; - node = (connection->rad >> connection->remain_layer * 2) & 0x03; - result = fu_synapticsmst_connection_rc_set_command (connection, + self->remain_layer--; + node = (self->rad >> self->remain_layer * 2) & 0x03; + result = fu_synapticsmst_connection_rc_set_command (self, UPDC_WRITE_TO_TX_DPCD + node, length, offset, (guint8 *)buf, error); - connection->remain_layer++; + self->remain_layer++; return result; } - return fu_synapticsmst_connection_bus_write (connection, offset, buf, length, error); + return fu_synapticsmst_connection_bus_write (self, offset, buf, length, error); } gboolean -fu_synapticsmst_connection_rc_set_command (FuSynapticsmstConnection *connection, +fu_synapticsmst_connection_rc_set_command (FuSynapticsmstConnection *self, guint32 rc_cmd, guint32 length, guint32 offset, @@ -176,7 +183,7 @@ fu_synapticsmst_connection_rc_set_command (FuSynapticsmstConnection *connection, if (cur_length) { /* write data */ - if (!fu_synapticsmst_connection_write (connection, + if (!fu_synapticsmst_connection_write (self, REG_RC_DATA, buf, cur_length, error)) { @@ -185,7 +192,7 @@ fu_synapticsmst_connection_rc_set_command (FuSynapticsmstConnection *connection, } /* write offset */ - if (!fu_synapticsmst_connection_write (connection, + if (!fu_synapticsmst_connection_write (self, REG_RC_OFFSET, (guint8 *)&cur_offset, 4, error)) { @@ -194,7 +201,7 @@ fu_synapticsmst_connection_rc_set_command (FuSynapticsmstConnection *connection, } /* write length */ - if (!fu_synapticsmst_connection_write (connection, + if (!fu_synapticsmst_connection_write (self, REG_RC_LEN, (guint8 *)&cur_length, 4, error)) { @@ -205,7 +212,7 @@ fu_synapticsmst_connection_rc_set_command (FuSynapticsmstConnection *connection, /* send command */ cmd = 0x80 | rc_cmd; - if (!fu_synapticsmst_connection_write (connection, + if (!fu_synapticsmst_connection_write (self, REG_RC_CMD, (guint8 *)&cmd, 1, error)) { @@ -218,7 +225,7 @@ fu_synapticsmst_connection_rc_set_command (FuSynapticsmstConnection *connection, deadline = t_spec.tv_sec + MAX_WAIT_TIME; do { - if (!fu_synapticsmst_connection_read (connection, + if (!fu_synapticsmst_connection_read (self, REG_RC_CMD, (guint8 *)&readData, 2, error)) { @@ -254,7 +261,7 @@ fu_synapticsmst_connection_rc_set_command (FuSynapticsmstConnection *connection, } gboolean -fu_synapticsmst_connection_rc_get_command (FuSynapticsmstConnection *connection, +fu_synapticsmst_connection_rc_get_command (FuSynapticsmstConnection *self, guint32 rc_cmd, guint32 length, guint32 offset, @@ -278,7 +285,7 @@ fu_synapticsmst_connection_rc_get_command (FuSynapticsmstConnection *connection, if (cur_length) { /* write offset */ - if (!fu_synapticsmst_connection_write (connection, + if (!fu_synapticsmst_connection_write (self, REG_RC_OFFSET, (guint8 *)&cur_offset, 4, error)) { @@ -287,7 +294,7 @@ fu_synapticsmst_connection_rc_get_command (FuSynapticsmstConnection *connection, } /* write length */ - if (!fu_synapticsmst_connection_write (connection, + if (!fu_synapticsmst_connection_write (self, REG_RC_LEN, (guint8 *)&cur_length, 4, error)) { @@ -298,7 +305,7 @@ fu_synapticsmst_connection_rc_get_command (FuSynapticsmstConnection *connection, /* send command */ cmd = 0x80 | rc_cmd; - if (!fu_synapticsmst_connection_write (connection, + if (!fu_synapticsmst_connection_write (self, REG_RC_CMD, (guint8 *)&cmd, 1, error)) { @@ -311,7 +318,7 @@ fu_synapticsmst_connection_rc_get_command (FuSynapticsmstConnection *connection, deadline = t_spec.tv_sec + MAX_WAIT_TIME; do { - if (!fu_synapticsmst_connection_read (connection, + if (!fu_synapticsmst_connection_read (self, REG_RC_CMD, (guint8 *)&readData, 2, error)) { @@ -339,7 +346,7 @@ fu_synapticsmst_connection_rc_get_command (FuSynapticsmstConnection *connection, } if (cur_length) { - if (!fu_synapticsmst_connection_read (connection, + if (!fu_synapticsmst_connection_read (self, REG_RC_DATA, buf, cur_length, @@ -358,7 +365,7 @@ fu_synapticsmst_connection_rc_get_command (FuSynapticsmstConnection *connection, } gboolean -fu_synapticsmst_connection_rc_special_get_command (FuSynapticsmstConnection *connection, +fu_synapticsmst_connection_rc_special_get_command (FuSynapticsmstConnection *self, guint32 rc_cmd, guint32 cmd_length, guint32 cmd_offset, @@ -375,7 +382,7 @@ fu_synapticsmst_connection_rc_special_get_command (FuSynapticsmstConnection *con if (cmd_length) { /* write cmd data */ if (cmd_data != NULL) { - if (!fu_synapticsmst_connection_write (connection, + if (!fu_synapticsmst_connection_write (self, REG_RC_DATA, cmd_data, cmd_length, @@ -386,7 +393,7 @@ fu_synapticsmst_connection_rc_special_get_command (FuSynapticsmstConnection *con } /* write offset */ - if (!fu_synapticsmst_connection_write (connection, + if (!fu_synapticsmst_connection_write (self, REG_RC_OFFSET, (guint8 *)&cmd_offset, 4, error)) { @@ -395,7 +402,7 @@ fu_synapticsmst_connection_rc_special_get_command (FuSynapticsmstConnection *con } /* write length */ - if (!fu_synapticsmst_connection_write (connection, + if (!fu_synapticsmst_connection_write (self, REG_RC_LEN, (guint8 *)&cmd_length, 4, error)) { @@ -406,7 +413,7 @@ fu_synapticsmst_connection_rc_special_get_command (FuSynapticsmstConnection *con /* send command */ cmd = 0x80 | rc_cmd; - if (!fu_synapticsmst_connection_write (connection, + if (!fu_synapticsmst_connection_write (self, REG_RC_CMD, (guint8 *)&cmd, 1, error)) { @@ -418,7 +425,7 @@ fu_synapticsmst_connection_rc_special_get_command (FuSynapticsmstConnection *con clock_gettime (CLOCK_REALTIME, &t_spec); deadline = t_spec.tv_sec + MAX_WAIT_TIME; do { - if (!fu_synapticsmst_connection_read (connection, + if (!fu_synapticsmst_connection_read (self, REG_RC_CMD, (guint8 *)&readData, 2, error)) { @@ -447,7 +454,7 @@ fu_synapticsmst_connection_rc_special_get_command (FuSynapticsmstConnection *con } if (length) { - if (!fu_synapticsmst_connection_read (connection, + if (!fu_synapticsmst_connection_read (self, REG_RC_DATA, buf, length, error)) { @@ -459,14 +466,14 @@ fu_synapticsmst_connection_rc_special_get_command (FuSynapticsmstConnection *con } gboolean -fu_synapticsmst_connection_enable_rc (FuSynapticsmstConnection *connection, GError **error) +fu_synapticsmst_connection_enable_rc (FuSynapticsmstConnection *self, GError **error) { const gchar *sc = "PRIUS"; - for (gint i = 0; i <= connection->layer; i++) { + for (gint i = 0; i <= self->layer; i++) { g_autoptr(FuSynapticsmstConnection) connection_tmp = NULL; g_autoptr(GError) error_local = NULL; - connection_tmp = fu_synapticsmst_connection_new (connection->fd, i, connection->rad); + connection_tmp = fu_synapticsmst_connection_new (self->fd, i, self->rad); if (!fu_synapticsmst_connection_rc_set_command (connection_tmp, UPDC_ENABLE_RC, 5, 0, (guint8*)sc, @@ -492,11 +499,11 @@ fu_synapticsmst_connection_enable_rc (FuSynapticsmstConnection *connection, GErr } gboolean -fu_synapticsmst_connection_disable_rc (FuSynapticsmstConnection *connection, GError **error) +fu_synapticsmst_connection_disable_rc (FuSynapticsmstConnection *self, GError **error) { - for (gint i = connection->layer; i >= 0; i--) { + for (gint i = self->layer; i >= 0; i--) { g_autoptr(FuSynapticsmstConnection) connection_tmp = NULL; - connection_tmp = fu_synapticsmst_connection_new (connection->fd, i, connection->rad); + connection_tmp = fu_synapticsmst_connection_new (self->fd, i, self->rad); if (!fu_synapticsmst_connection_rc_set_command (connection_tmp, UPDC_DISABLE_RC, 0, 0, NULL, diff --git a/plugins/synapticsmst/fu-synapticsmst-connection.h b/plugins/synapticsmst/fu-synapticsmst-connection.h index 186a55edb..340cac870 100644 --- a/plugins/synapticsmst/fu-synapticsmst-connection.h +++ b/plugins/synapticsmst/fu-synapticsmst-connection.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2016 Mario Limonciello * Copyright (C) 2017 Peichen Huang + * Copyright (C) 2019 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ @@ -10,6 +11,9 @@ #include #include +#define FU_TYPE_SYNAPTICSMST_CONNECTION (fu_synapticsmst_connection_get_type ()) +G_DECLARE_FINAL_TYPE (FuSynapticsmstConnection, fu_synapticsmst_connection, FU, SYNAPTICSMST_CONNECTION, GObject) + #define ADDR_CUSTOMER_ID 0X10E #define ADDR_BOARD_ID 0x10F @@ -54,27 +58,23 @@ typedef enum { UPDC_READ_FROM_TX_DPCD = 0x32, } SynapticsMstUpdcCmd; -typedef struct _FuSynapticsmstConnection FuSynapticsmstConnection; - -void fu_synapticsmst_connection_free (FuSynapticsmstConnection *connection); - FuSynapticsmstConnection *fu_synapticsmst_connection_new (gint fd, guint8 layer, guint rad); -gboolean fu_synapticsmst_connection_read (FuSynapticsmstConnection *connection, +gboolean fu_synapticsmst_connection_read (FuSynapticsmstConnection *self, guint32 offset, guint8 *buf, guint32 length, GError **error); -gboolean fu_synapticsmst_connection_write (FuSynapticsmstConnection *connection, +gboolean fu_synapticsmst_connection_write (FuSynapticsmstConnection *self, guint32 offset, const guint8 *buf, guint32 length, GError **error); -gboolean fu_synapticsmst_connection_rc_set_command (FuSynapticsmstConnection *connection, +gboolean fu_synapticsmst_connection_rc_set_command (FuSynapticsmstConnection *self, guint32 rc_cmd, guint32 length, guint32 offset, @@ -82,14 +82,14 @@ gboolean fu_synapticsmst_connection_rc_set_command (FuSynapticsmstConnection * GError **error); -gboolean fu_synapticsmst_connection_rc_get_command (FuSynapticsmstConnection *connection, +gboolean fu_synapticsmst_connection_rc_get_command (FuSynapticsmstConnection *self, guint32 rc_cmd, guint32 length, guint32 offset, guint8 *buf, GError **error); -gboolean fu_synapticsmst_connection_rc_special_get_command(FuSynapticsmstConnection *connection, +gboolean fu_synapticsmst_connection_rc_special_get_command(FuSynapticsmstConnection *self, guint32 rc_cmd, guint32 cmd_length, guint32 cmd_offset, @@ -98,13 +98,8 @@ gboolean fu_synapticsmst_connection_rc_special_get_command(FuSynapticsmstConnec guint8 *buf, GError **error); -gboolean fu_synapticsmst_connection_enable_rc (FuSynapticsmstConnection *connection, +gboolean fu_synapticsmst_connection_enable_rc (FuSynapticsmstConnection *self, GError **error); -gboolean fu_synapticsmst_connection_disable_rc (FuSynapticsmstConnection *connection, +gboolean fu_synapticsmst_connection_disable_rc (FuSynapticsmstConnection *self, GError **error); - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuSynapticsmstConnection, fu_synapticsmst_connection_free) -#pragma clang diagnostic pop From b106542aefca170a91ff1cb87f7863a9ce280b26 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 15 Aug 2019 16:44:34 +0100 Subject: [PATCH 039/158] trivial: Fix a potential crash when using FuPlugin in the self tests --- src/fu-plugin.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fu-plugin.c b/src/fu-plugin.c index 501db42f7..ffcb52e23 100644 --- a/src/fu-plugin.c +++ b/src/fu-plugin.c @@ -1977,6 +1977,7 @@ fu_plugin_init (FuPlugin *self) { FuPluginPrivate *priv = GET_PRIVATE (self); priv->enabled = TRUE; + priv->udev_subsystems = g_ptr_array_new_with_free_func (g_free); priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref); g_rw_lock_init (&priv->devices_mutex); From 2a8a5e0609f8889c159c5f1e98196f9e6eb78a5b Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 15 Aug 2019 16:43:52 +0100 Subject: [PATCH 040/158] trivial: Fix regression when using fu_device_incorporate() This broke in c521ab94be07b53160b62235bdc19b4992431014 --- src/fu-device.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/fu-device.c b/src/fu-device.c index 870335814..c355fc396 100644 --- a/src/fu-device.c +++ b/src/fu-device.c @@ -1267,6 +1267,7 @@ fu_device_set_version (FuDevice *self, const gchar *version, FwupdVersionFormat gboolean fu_device_ensure_id (FuDevice *self, GError **error) { + FuDevicePrivate *priv = GET_PRIVATE (self); g_autofree gchar *device_id = NULL; g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); @@ -1277,7 +1278,7 @@ fu_device_ensure_id (FuDevice *self, GError **error) return TRUE; /* nothing we can do! */ - if (fu_device_get_physical_id (self) == NULL) { + if (priv->physical_id == NULL) { g_autofree gchar *tmp = fu_device_to_string (self); g_set_error (error, G_IO_ERROR, @@ -2253,6 +2254,10 @@ fu_device_incorporate (FuDevice *self, FuDevice *donor) fu_device_set_alternate_id (self, fu_device_get_alternate_id (donor)); if (priv->equivalent_id == NULL) fu_device_set_equivalent_id (self, fu_device_get_equivalent_id (donor)); + if (priv->physical_id == NULL && priv_donor->physical_id != NULL) + fu_device_set_physical_id (self, priv_donor->physical_id); + if (priv->logical_id == NULL && priv_donor->logical_id != NULL) + fu_device_set_logical_id (self, priv_donor->logical_id); if (priv->quirks == NULL) fu_device_set_quirks (self, fu_device_get_quirks (donor)); g_rw_lock_reader_lock (&priv_donor->parent_guids_mutex); From 6e73f50edc48b68cc36205f1e7f6360ab07cdcda Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 15 Aug 2019 16:46:11 +0100 Subject: [PATCH 041/158] Add fu_udev_device_get_device_file() helper This allows us to set the subsystem and device file during construction, which means we can create FuUdevDevice objects not backed by physical devices. --- src/fu-udev-device.c | 83 ++++++++++++++++++++++++++++++++++++++++---- src/fu-udev-device.h | 1 + 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/src/fu-udev-device.c b/src/fu-udev-device.c index bf6920dd3..d205970da 100644 --- a/src/fu-udev-device.c +++ b/src/fu-udev-device.c @@ -28,6 +28,8 @@ typedef struct guint16 vendor; guint16 model; guint8 revision; + gchar *subsystem; + gchar *device_file; } FuUdevDevicePrivate; G_DEFINE_TYPE_WITH_PRIVATE (FuUdevDevice, fu_udev_device, FU_TYPE_DEVICE) @@ -42,6 +44,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref) enum { PROP_0, PROP_UDEV_DEVICE, + PROP_SUBSYSTEM, + PROP_DEVICE_FILE, PROP_LAST }; @@ -107,6 +111,8 @@ void fu_udev_device_dump (FuUdevDevice *self) { FuUdevDevicePrivate *priv = GET_PRIVATE (self); + if (priv->udev_device == NULL) + return; fu_udev_device_dump_internal (priv->udev_device); } @@ -120,6 +126,10 @@ fu_udev_device_probe (FuDevice *device, GError **error) g_autofree gchar *subsystem = NULL; g_autoptr(GUdevDevice) udev_parent = NULL; + /* nothing to do */ + if (priv->udev_device == NULL) + return TRUE; + /* set ven:dev:rev */ priv->vendor = fu_udev_device_get_sysfs_attr_as_uint64 (priv->udev_device, "vendor"); priv->model = fu_udev_device_get_sysfs_attr_as_uint64 (priv->udev_device, "device"); @@ -137,7 +147,7 @@ fu_udev_device_probe (FuDevice *device, GError **error) /* hidraw helpfully encodes the information in a different place */ if (udev_parent != NULL && priv->vendor == 0x0 && priv->model == 0x0 && priv->revision == 0x0 && - g_strcmp0 (g_udev_device_get_subsystem (priv->udev_device), "hidraw") == 0) { + g_strcmp0 (priv->subsystem, "hidraw") == 0) { tmp = g_udev_device_get_property (udev_parent, "HID_ID"); if (tmp != NULL && strlen (tmp) == 22) { priv->vendor = fu_udev_device_read_uint16 (tmp + 10); @@ -202,7 +212,7 @@ fu_udev_device_probe (FuDevice *device, GError **error) } /* set vendor ID */ - subsystem = g_ascii_strup (fu_udev_device_get_subsystem (self), -1); + subsystem = g_ascii_strup (g_udev_device_get_subsystem (priv->udev_device), -1); if (subsystem != NULL && priv->vendor != 0x0000) { g_autofree gchar *vendor_id = NULL; vendor_id = g_strdup_printf ("%s:0x%04X", subsystem, (guint) priv->vendor); @@ -251,6 +261,8 @@ fu_udev_device_set_dev (FuUdevDevice *self, GUdevDevice *udev_device) g_set_object (&priv->udev_device, udev_device); if (priv->udev_device == NULL) return; + priv->subsystem = g_strdup (g_udev_device_get_subsystem (priv->udev_device)); + priv->device_file = g_strdup (g_udev_device_get_device_file (priv->udev_device)); } guint @@ -274,10 +286,18 @@ fu_udev_device_get_slot_depth (FuUdevDevice *self, const gchar *subsystem) static void fu_udev_device_incorporate (FuDevice *self, FuDevice *donor) { + FuUdevDevice *uself = FU_UDEV_DEVICE (self); + FuUdevDevice *udonor = FU_UDEV_DEVICE (donor); + FuUdevDevicePrivate *priv = GET_PRIVATE (uself); + g_return_if_fail (FU_IS_UDEV_DEVICE (self)); g_return_if_fail (FU_IS_UDEV_DEVICE (donor)); - fu_udev_device_set_dev (FU_UDEV_DEVICE (self), - fu_udev_device_get_dev (FU_UDEV_DEVICE (donor))); + + fu_udev_device_set_dev (uself, fu_udev_device_get_dev (udonor)); + if (priv->device_file == NULL) { + priv->subsystem = g_strdup (fu_udev_device_get_subsystem (udonor)); + priv->device_file = g_strdup (fu_udev_device_get_device_file (udonor)); + } } /** @@ -313,7 +333,25 @@ fu_udev_device_get_subsystem (FuUdevDevice *self) { FuUdevDevicePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), NULL); - return g_udev_device_get_subsystem (priv->udev_device); + return priv->subsystem; +} + +/** + * fu_udev_device_get_device_file: + * @self: A #GUdevDevice + * + * Gets the device node. + * + * Returns: a device file, or NULL if unset + * + * Since: 1.2.11 + **/ +const gchar * +fu_udev_device_get_device_file (FuUdevDevice *self) +{ + FuUdevDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), NULL); + return priv->device_file; } /** @@ -331,6 +369,8 @@ fu_udev_device_get_sysfs_path (FuUdevDevice *self) { FuUdevDevicePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), NULL); + if (priv->udev_device == NULL) + return NULL; return g_udev_device_get_sysfs_path (priv->udev_device); } @@ -436,8 +476,12 @@ fu_udev_device_set_physical_id (FuUdevDevice *self, const gchar *subsystem, GErr g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), FALSE); g_return_val_if_fail (subsystem != NULL, FALSE); + /* nothing to do */ + if (priv->udev_device == NULL) + return TRUE; + /* get the correct device */ - if (g_strcmp0 (g_udev_device_get_subsystem (priv->udev_device), subsystem) == 0) { + if (g_strcmp0 (priv->subsystem, subsystem) == 0) { udev_device = g_object_ref (priv->udev_device); } else { udev_device = g_udev_device_get_parent_with_subsystem (priv->udev_device, @@ -508,6 +552,12 @@ fu_udev_device_get_property (GObject *object, guint prop_id, case PROP_UDEV_DEVICE: g_value_set_object (value, priv->udev_device); break; + case PROP_SUBSYSTEM: + g_value_set_string (value, priv->subsystem); + break; + case PROP_DEVICE_FILE: + g_value_set_string (value, priv->device_file); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -519,10 +569,17 @@ fu_udev_device_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { FuUdevDevice *self = FU_UDEV_DEVICE (object); + FuUdevDevicePrivate *priv = GET_PRIVATE (self); switch (prop_id) { case PROP_UDEV_DEVICE: fu_udev_device_set_dev (self, g_value_get_object (value)); break; + case PROP_SUBSYSTEM: + priv->subsystem = g_value_dup_string (value); + break; + case PROP_DEVICE_FILE: + priv->device_file = g_value_dup_string (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -535,6 +592,8 @@ fu_udev_device_finalize (GObject *object) FuUdevDevice *self = FU_UDEV_DEVICE (object); FuUdevDevicePrivate *priv = GET_PRIVATE (self); + g_free (priv->subsystem); + g_free (priv->device_file); if (priv->udev_device != NULL) g_object_unref (priv->udev_device); @@ -571,6 +630,18 @@ fu_udev_device_class_init (FuUdevDeviceClass *klass) G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_UDEV_DEVICE, pspec); + + pspec = g_param_spec_string ("subsystem", NULL, NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_SUBSYSTEM, pspec); + + pspec = g_param_spec_string ("device-file", NULL, NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_DEVICE_FILE, pspec); } /** diff --git a/src/fu-udev-device.h b/src/fu-udev-device.h index d8813dfae..621443af9 100644 --- a/src/fu-udev-device.h +++ b/src/fu-udev-device.h @@ -26,6 +26,7 @@ struct _FuUdevDeviceClass FuUdevDevice *fu_udev_device_new (GUdevDevice *udev_device); GUdevDevice *fu_udev_device_get_dev (FuUdevDevice *self); +const gchar *fu_udev_device_get_device_file (FuUdevDevice *self); const gchar *fu_udev_device_get_sysfs_path (FuUdevDevice *self); const gchar *fu_udev_device_get_subsystem (FuUdevDevice *self); guint16 fu_udev_device_get_vendor (FuUdevDevice *self); From 4fa965a9f76c37d95a983c296bec42fc689df4a1 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 19 Aug 2019 11:20:09 -0500 Subject: [PATCH 042/158] thunderbolt: Fix logic to work properly with ICL thunderbolt controller This controller isn't flashable in fwupd, but fwupd can display information about it. * Use a generic device ID (similar to safemode) * Build device name attribute from DMI data --- plugins/thunderbolt/fu-plugin-thunderbolt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/thunderbolt/fu-plugin-thunderbolt.c b/plugins/thunderbolt/fu-plugin-thunderbolt.c index dc5dbbecc..d72643a7e 100644 --- a/plugins/thunderbolt/fu-plugin-thunderbolt.c +++ b/plugins/thunderbolt/fu-plugin-thunderbolt.c @@ -337,6 +337,7 @@ fu_plugin_thunderbolt_add (FuPlugin *plugin, GUdevDevice *device) is_native ? "-native" : ""); fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); } else { + device_id = g_strdup ("TBT-fixed"); fu_device_set_update_error (dev, "Missing non-active nvmem"); } } else { @@ -347,6 +348,8 @@ fu_plugin_thunderbolt_add (FuPlugin *plugin, GUdevDevice *device) fu_device_set_metadata (dev, "sysfs-path", devpath); name = g_udev_device_get_sysfs_attr (device, "device_name"); + if (name == NULL && is_host) + name = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_PRODUCT_NAME); if (name != NULL) { if (is_host) { g_autofree gchar *pretty_name = NULL; From 246ab42951c0ad2d5f530b5af41a6d02ec14e5b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Wed, 21 Aug 2019 08:39:05 +0100 Subject: [PATCH 043/158] unifying: update Solaar url MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- plugins/unifying/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/unifying/README.md b/plugins/unifying/README.md index 19e05539d..8125a6adb 100644 --- a/plugins/unifying/README.md +++ b/plugins/unifying/README.md @@ -51,4 +51,4 @@ means the hardware keeps working while probing, and also allows us to detect paired devices. [1] https://www.mousejack.com/ -[2] https://pwr.github.io/Solaar/ +[2] https://pwr-Solaar.github.io/Solaar/ From 8de8e3523a1a17affdaaf5a755eaa947ae4aa8ec Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 21 Aug 2019 10:02:39 -0500 Subject: [PATCH 044/158] trivial: uefi: set vendor name for system firwmare from DMI data --- plugins/uefi/fu-plugin-uefi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/uefi/fu-plugin-uefi.c b/plugins/uefi/fu-plugin-uefi.c index 1d37d1c60..ab5a5c797 100644 --- a/plugins/uefi/fu-plugin-uefi.c +++ b/plugins/uefi/fu-plugin-uefi.c @@ -549,6 +549,13 @@ fu_plugin_uefi_coldplug_device (FuPlugin *plugin, FuUefiDevice *dev, GError **er if (name != NULL) fu_device_set_name (FU_DEVICE (dev), name); } + /* set fallback vendor if nothing else is set */ + if (fu_device_get_vendor (FU_DEVICE (dev)) == NULL && + fu_uefi_device_get_kind (dev) == FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE) { + const gchar *vendor = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_MANUFACTURER); + if (vendor != NULL) + fu_device_set_vendor (FU_DEVICE (dev), vendor); + } /* success */ return TRUE; From d2afb59b4259379b0c6c0172db8d4ced2fc2391d Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 20 Aug 2019 11:24:18 -0500 Subject: [PATCH 045/158] trivial: If no devices support updates, show messaging (Closes: #1295) Normally the flag 'supported' is used to indicate that devices can update from a remote such as LVFS. Running `#fwupdmgr get-updates` in this situation will show a message for each device that is already up to date as well as messages for devices that have updates available. If no devices contain the supported flag, `# fwupdmgr get-updates` will show no output, which is confusing to some people. Show "No updatable devices" instead for those situations. --- src/fu-util.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/fu-util.c b/src/fu-util.c index 237767422..b9ff53ff7 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -1569,6 +1569,7 @@ static gboolean fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GPtrArray) devices = NULL; + gboolean supported = FALSE; /* are the remotes very old */ if (!fu_util_perhaps_refresh_remotes (priv, error)) @@ -1590,6 +1591,7 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) continue; if (!fu_util_filter_device (priv, dev)) continue; + supported = TRUE; /* get the releases for this device and filter for validity */ rels = fwupd_client_get_upgrades (priv->client, @@ -1675,6 +1677,15 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) if (!fu_util_perhaps_show_unreported (priv, error)) return FALSE; + /* no devices supported by LVFS or all are filtered */ + if (!supported) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No updatable devices"); + return FALSE; + } + /* success */ return TRUE; } From 263cab99e90de6302ebad39f23bddb13b5c46644 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 20 Aug 2019 17:16:00 -0500 Subject: [PATCH 046/158] fu-engine/fu-config: Reload metadata store when configuration changes Emit a signal from FuConfig object that is caught by FuEngine to reload the store. This should fix problems with Fedora CI failing. --- src/fu-config.c | 22 ++++++++++++++++++++++ src/fu-engine.c | 14 ++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/fu-config.c b/src/fu-config.c index a2bfaef3a..4e4bb6d4b 100644 --- a/src/fu-config.c +++ b/src/fu-config.c @@ -20,6 +20,14 @@ #include "fwupd-error.h" #include "fwupd-remote-private.h" + +enum { + SIGNAL_CHANGED, + SIGNAL_LAST +}; + +static guint signals[SIGNAL_LAST] = { 0 }; + static void fu_config_finalize (GObject *obj); struct _FuConfig @@ -40,6 +48,13 @@ struct _FuConfig G_DEFINE_TYPE (FuConfig, fu_config, G_TYPE_OBJECT) +static void +fu_config_emit_changed (FuConfig *self) +{ + g_debug ("::configuration changed"); + g_signal_emit (self, signals[SIGNAL_CHANGED], 0); +} + static GPtrArray * fu_config_get_config_paths (void) { @@ -80,6 +95,7 @@ fu_config_monitor_changed_cb (GFileMonitor *monitor, g_debug ("%s changed, reloading all configs", filename); if (!fu_config_load (self, FU_CONFIG_LOAD_FLAG_NONE, &error)) g_warning ("failed to rescan config: %s", error->message); + fu_config_emit_changed (self); } static guint64 @@ -603,6 +619,12 @@ fu_config_class_init (FuConfigClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = fu_config_finalize; + + signals[SIGNAL_CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); } static void diff --git a/src/fu-engine.c b/src/fu-engine.c index 88b6d96ef..d07843c3a 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -2362,6 +2362,16 @@ fu_engine_load_metadata_store (FuEngine *self, FuEngineLoadFlags flags, GError * return TRUE; } +static void +fu_engine_config_changed_cb (FuConfig *config, FuEngine *self) +{ + g_autoptr(GError) error_local = NULL; + if (!fu_engine_load_metadata_store (self, FU_ENGINE_LOAD_FLAG_NONE, + &error_local)) + g_warning ("Failed to reload metadata store: %s", + error_local->message); +} + static FuKeyringResult * fu_engine_get_existing_keyring_result (FuEngine *self, FuKeyring *kr, @@ -4670,6 +4680,10 @@ fu_engine_init (FuEngine *self) self->compile_versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); self->approved_firmware = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + g_signal_connect (self->config, "changed", + G_CALLBACK (fu_engine_config_changed_cb), + self); + g_signal_connect (self->idle, "notify::status", G_CALLBACK (fu_engine_idle_status_notify_cb), self); From fee8f499fc3bd2de62a0b637bf10d20c35f3aed0 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Sun, 18 Aug 2019 12:16:07 -0500 Subject: [PATCH 047/158] Simplify get-devices output to not show un-useful fields This prevents showing things not generally useful to most people: Created Modified Icon Plugin FlashesLeft --- src/fu-common.c | 7 +- src/fu-tool.c | 14 +- src/fu-util-common.c | 313 +++++++++++++++++++++++++++++++++++++++++++ src/fu-util-common.h | 7 +- src/fu-util.c | 231 +++---------------------------- src/meson.build | 1 + 6 files changed, 347 insertions(+), 226 deletions(-) diff --git a/src/fu-common.c b/src/fu-common.c index be686663e..2b5abc9d1 100644 --- a/src/fu-common.c +++ b/src/fu-common.c @@ -1126,9 +1126,12 @@ fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gch return; for (gsize i = 0; i < idt; i++) g_string_append (str, " "); - g_string_append_printf (str, "%s:", key); + if (key[0] != '\0') { + g_string_append_printf (str, "%s:", key); + idt++; + } if (value != NULL) { - for (gsize i = strlen (key) + idt + 1; i < align; i++) + for (gsize i = strlen (key) + idt; i < align; i++) g_string_append (str, " "); g_string_append (str, value); } diff --git a/src/fu-tool.c b/src/fu-tool.c index a0c532401..9ff07b240 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -326,7 +326,7 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) g_printerr ("%s\n", error_local->message); continue; } - g_print ("%s", fwupd_device_to_string (dev)); + g_print ("%s", fu_util_device_to_string (dev, 0)); g_print (" Release information:\n"); /* print all releases */ for (guint j = 0; j < rels->len; j++) { @@ -383,7 +383,7 @@ fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) g_autofree gchar *tmp = NULL; if (!fu_util_filter_device (priv, dev)) continue; - tmp = fwupd_device_to_string (dev); + tmp = fu_util_device_to_string (dev, 0); g_print ("%s\n", tmp); } return TRUE; @@ -412,7 +412,7 @@ fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) if (!fu_util_filter_device (priv, dev)) continue; if (priv->show_all_devices || fu_util_is_interesting_device (dev)) { - g_autofree gchar *tmp = fwupd_device_to_string (dev); + g_autofree gchar *tmp = fu_util_device_to_string (dev, 0); g_print ("%s\n", tmp); } } @@ -1280,7 +1280,7 @@ fu_util_device_added_cb (FwupdClient *client, FwupdDevice *device, gpointer user_data) { - g_autofree gchar *tmp = fwupd_device_to_string (device); + g_autofree gchar *tmp = fu_util_device_to_string (device, 0); /* TRANSLATORS: this is when a device is hotplugged */ g_print ("%s\n%s", _("Device added:"), tmp); } @@ -1290,7 +1290,7 @@ fu_util_device_removed_cb (FwupdClient *client, FwupdDevice *device, gpointer user_data) { - g_autofree gchar *tmp = fwupd_device_to_string (device); + g_autofree gchar *tmp = fu_util_device_to_string (device, 0); /* TRANSLATORS: this is when a device is hotplugged */ g_print ("%s\n%s", _("Device removed:"), tmp); } @@ -1300,7 +1300,7 @@ fu_util_device_changed_cb (FwupdClient *client, FwupdDevice *device, gpointer user_data) { - g_autofree gchar *tmp = fwupd_device_to_string (device); + g_autofree gchar *tmp = fu_util_device_to_string (device, 0); /* TRANSLATORS: this is when a device has been updated */ g_print ("%s\n%s", _("Device changed:"), tmp); } @@ -1387,7 +1387,7 @@ fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) g_autofree gchar *str = NULL; if (!fu_util_filter_device (priv, dev)) continue; - str = fwupd_device_to_string (dev); + str = fu_util_device_to_string (dev, 0); g_print ("%s\n", str); } diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 2ed21bc88..01e1d6c72 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -9,7 +9,9 @@ #include #include #include +#include +#include "fu-common.h" #include "fu-util-common.h" #include "fu-device.h" @@ -728,3 +730,314 @@ fu_util_parse_filter_flags (const gchar *filter, FwupdDeviceFlags *include, return TRUE; } + +gchar * +fu_util_convert_description (const gchar *xml, GError **error) +{ + g_autoptr(GString) str = g_string_new (NULL); + g_autoptr(XbNode) n = NULL; + g_autoptr(XbSilo) silo = NULL; + + /* parse XML */ + silo = xb_silo_new_from_xml (xml, error); + if (silo == NULL) + return NULL; + + n = xb_silo_get_root (silo); + while (n != NULL) { + g_autoptr(XbNode) n2 = NULL; + + /* support

,

    ,
      and
    1. , ignore all else */ + if (g_strcmp0 (xb_node_get_element (n), "p") == 0) { + g_string_append_printf (str, "%s\n\n", xb_node_get_text (n)); + } else if (g_strcmp0 (xb_node_get_element (n), "ul") == 0) { + g_autoptr(GPtrArray) children = xb_node_get_children (n); + for (guint i = 0; i < children->len; i++) { + XbNode *nc = g_ptr_array_index (children, i); + if (g_strcmp0 (xb_node_get_element (nc), "li") == 0) { + g_string_append_printf (str, " • %s\n", + xb_node_get_text (nc)); + } + } + g_string_append (str, "\n"); + } else if (g_strcmp0 (xb_node_get_element (n), "ol") == 0) { + g_autoptr(GPtrArray) children = xb_node_get_children (n); + for (guint i = 0; i < children->len; i++) { + XbNode *nc = g_ptr_array_index (children, i); + if (g_strcmp0 (xb_node_get_element (nc), "li") == 0) { + g_string_append_printf (str, " %u. %s\n", + i + 1, + xb_node_get_text (nc)); + } + } + g_string_append (str, "\n"); + } + + n2 = xb_node_get_next (n); + g_set_object (&n, n2); + } + + /* remove extra newline */ + if (str->len > 0) + g_string_truncate (str, str->len - 1); + + /* success */ + return g_string_free (g_steal_pointer (&str), FALSE); +} + +static gchar * +fu_util_time_to_str (guint64 tmp) +{ + g_return_val_if_fail (tmp != 0, NULL); + + /* seconds */ + if (tmp < 60) { + /* TRANSLATORS: duration in seconds */ + return g_strdup_printf (ngettext ("%u second", "%u seconds", + (gint) tmp), + (guint) tmp); + } + + /* minutes */ + tmp /= 60; + if (tmp < 60) { + /* TRANSLATORS: duration in minutes */ + return g_strdup_printf (ngettext ("%u minute", "%u minutes", + (gint) tmp), + (guint) tmp); + } + + /* hours */ + tmp /= 60; + if (tmp < 60) { + /* TRANSLATORS: duration in minutes */ + return g_strdup_printf (ngettext ("%u hour", "%u hours", + (gint) tmp), + (guint) tmp); + } + + /* days */ + tmp /= 24; + /* TRANSLATORS: duration in days! */ + return g_strdup_printf (ngettext ("%u day", "%u days", + (gint) tmp), + (guint) tmp); +} + +gchar * +fu_util_device_to_string (FwupdDevice *dev, guint idt) +{ + FwupdUpdateState state; + GPtrArray *guids = fwupd_device_get_guids (dev); + GString *str = g_string_new (NULL); + const gchar *tmp; + const gchar *tmp2; + guint64 flags = fwupd_device_get_flags (dev); + g_autoptr(GString) flags_str = g_string_new (NULL); + + /* some fields are intentionally not included and are only shown in --verbose */ + if (g_getenv ("FWUPD_VERBOSE") != NULL) { + g_autofree gchar *debug_str = NULL; + debug_str = fwupd_device_to_string (dev); + g_string_append (str, debug_str); + return g_string_free (str, FALSE); + } + + /* all devices have a name */ + fu_common_string_append_kv (str, idt, fwupd_device_get_name (dev), NULL); + + /* TRANSLATORS: ID for hardware, typically a SHA1 sum */ + fu_common_string_append_kv (str, idt + 1, _("Device ID"), + fwupd_device_get_id (dev)); + + /* summary */ + tmp = fwupd_device_get_summary (dev); + if (tmp != NULL) { + /* TRANSLATORS: one line summary of device */ + fu_common_string_append_kv (str, idt + 1, _("Summary"), tmp); + } + + /* description */ + tmp = fwupd_device_get_description (dev); + if (tmp != NULL) { + g_autofree gchar *desc = NULL; + desc = fu_util_convert_description (tmp, NULL); + /* TRANSLATORS: multiline description of device */ + fu_common_string_append_kv (str, idt + 1, _("Description"), desc); + } + + /* versions */ + tmp = fwupd_device_get_version (dev); + if (tmp != NULL) { + /* TRANSLATORS: version number of current firmware */ + fu_common_string_append_kv (str, idt + 1, _("Current version"), tmp); + } + tmp = fwupd_device_get_version_lowest (dev); + if (tmp != NULL) { + /* TRANSLATORS: smallest version number installable on device */ + fu_common_string_append_kv (str, idt + 1, _("Minimum Version"), tmp); + } + tmp = fwupd_device_get_version_bootloader (dev); + if (tmp != NULL) { + /* TRANSLATORS: firmware version of bootloader */ + fu_common_string_append_kv (str, idt + 1, _("Bootloader Version"), tmp); + } + + /* vendor */ + tmp = fwupd_device_get_vendor (dev); + tmp2 = fwupd_device_get_vendor_id (dev); + if (tmp != NULL && tmp2 != NULL) { + g_autofree gchar *both = g_strdup_printf ("%s (%s)", tmp, tmp2); + /* TRANSLATORS: manufacturer of hardware */ + fu_common_string_append_kv (str, idt + 1, _("Vendor"), both); + } else if (tmp != NULL) { + /* TRANSLATORS: manufacturer of hardware */ + fu_common_string_append_kv (str, idt + 1, _("Vendor"), tmp); + } else if (tmp2 != NULL) { + /* TRANSLATORS: manufacturer of hardware */ + fu_common_string_append_kv (str, idt + 1, _("Vendor"), tmp2); + } + + /* install duration */ + if (fwupd_device_get_install_duration (dev) > 0) { + g_autofree gchar *time = fu_util_time_to_str (fwupd_device_get_install_duration (dev)); + /* TRANSLATORS: length of time the update takes to apply */ + fu_common_string_append_kv (str, idt + 1, _("Install Duration"), time); + } + + /* serial # */ + tmp = fwupd_device_get_serial (dev); + if (tmp != NULL) { + /* TRANSLATORS: serial number of hardware */ + fu_common_string_append_kv (str, idt + 1, _("Serial Number"), tmp); + } + + /* update state */ + state = fwupd_device_get_update_state (dev); + if (state != FWUPD_UPDATE_STATE_UNKNOWN) { + /* TRANSLATORS: hardware state, e.g. "pending" */ + fu_common_string_append_kv (str, idt + 1, _("Update State"), + fwupd_update_state_to_string (state)); + } + tmp = fwupd_device_get_update_error (dev); + if (tmp != NULL) { + /* TRANSLATORS: error message from last update attempt */ + fu_common_string_append_kv (str, idt + 1, _("Update Error"), tmp); + } + tmp = fwupd_device_get_update_message (dev); + if (tmp != NULL) { + /* TRANSLATORS: helpful messages from last update */ + fu_common_string_append_kv (str, idt + 1, _("Update Message"), tmp); + } + + for (guint i = 0; i < 64; i++) { + if ((flags & ((guint64) 1 << i)) == 0) + continue; + g_string_append_printf (flags_str, "%s|", + fwupd_device_flag_to_string ((guint64) 1 << i)); + } + if (str->len == 0) { + /* TRANSLATORS: device properties */ + fu_common_string_append_kv (flags_str, idt + 1, _("Flags"), + fwupd_device_flag_to_string (0)); + } else { + g_string_truncate (flags_str, flags_str->len - 1); + /* TRANSLATORS: device properties */ + fu_common_string_append_kv (str, idt + 1, _("Flags"), flags_str->str); + } + + /* all GUIDs for this hardware */ + for (guint i = 0; i < guids->len; i++) { + tmp = g_ptr_array_index (guids, i); + if (i == 0) { + /* TRANSLATORS: global ID common to all similar hardware */ + fu_common_string_append_kv (str, idt + 1, ngettext ("GUID", "GUIDs", guids->len), tmp); + } else { + fu_common_string_append_kv (str, idt + 1, "", tmp); + } + } + return g_string_free (str, FALSE); +} + +gchar * +fu_util_release_to_string (FwupdRelease *rel, guint idt) +{ + GString *str = g_string_new (NULL); + guint64 flags = fwupd_release_get_flags (rel); + g_autoptr(GString) flags_str = g_string_new (NULL); + + g_return_val_if_fail (FWUPD_IS_RELEASE (rel), NULL); + + /* TRANSLATORS: version number of new firmware */ + fu_common_string_append_kv (str, idt, _("Version"), + fwupd_release_get_version (rel)); + + if (fwupd_release_get_remote_id (rel) != NULL) { + /* TRANSLATORS: the server the file is coming from */ + fu_common_string_append_kv (str, idt + 1, _("Remote ID"), + fwupd_release_get_remote_id (rel)); + } + if (fwupd_release_get_summary (rel) != NULL) { + /* TRANSLATORS: one line summary of device */ + fu_common_string_append_kv (str, idt + 1, _("Summary"), + fwupd_release_get_summary (rel)); + } + if (fwupd_release_get_description (rel) != NULL) { + g_autofree gchar *desc = NULL; + desc = fu_util_convert_description (fwupd_release_get_description (rel), NULL); + /* TRANSLATORS: multiline description of device */ + fu_common_string_append_kv (str, idt + 1, _("Description"), desc); + } + if (fwupd_release_get_license (rel) != NULL) { + /* TRANSLATORS: e.g. GPLv2+, Non free etc */ + fu_common_string_append_kv (str, idt + 1, _("License"), + fwupd_release_get_license (rel)); + } + if (fwupd_release_get_size (rel) != 0) { + /* TRANSLATORS: file size of the download */ + fu_common_string_append_kx (str, idt + 1, _("Size"), + fwupd_release_get_size (rel)); + } + if (fwupd_release_get_details_url (rel) != NULL) { + /* TRANSLATORS: more details about the update link */ + fu_common_string_append_kv (str, idt + 1, _("Details"), + fwupd_release_get_details_url (rel)); + } + if (fwupd_release_get_source_url (rel) != NULL) { + /* TRANSLATORS: source (as in code) link */ + fu_common_string_append_kv (str, idt + 1, _("Source"), + fwupd_release_get_source_url (rel)); + } + if (fwupd_release_get_vendor (rel) != NULL) { + /* TRANSLATORS: manufacturer of hardware */ + fu_common_string_append_kv (str, idt + 1, _("Vendor"), + fwupd_release_get_vendor (rel)); + } + if (fwupd_release_get_install_duration (rel) != 0) { + g_autofree gchar *tmp = fu_util_time_to_str (fwupd_release_get_install_duration (rel)); + /* TRANSLATORS: length of time the update takes to apply */ + fu_common_string_append_kv (str, idt + 1, _("Duration"), tmp); + } + if (fwupd_release_get_update_message (rel) != NULL) { + /* TRANSLATORS: helpful messages for the update */ + fu_common_string_append_kv (str, idt + 1, _("Update Message"), + fwupd_release_get_update_message (rel)); + } + + for (guint i = 0; i < 64; i++) { + if ((flags & ((guint64) 1 << i)) == 0) + continue; + g_string_append_printf (flags_str, "%s|", + fwupd_release_flag_to_string ((guint64) 1 << i)); + } + if (flags_str->len == 0) { + /* TRANSLATORS: release properties */ + fu_common_string_append_kv (str, idt + 1, _("Flags"), fwupd_release_flag_to_string (0)); + } else { + g_string_truncate (flags_str, flags_str->len - 1); + /* TRANSLATORS: release properties */ + fu_common_string_append_kv (str, idt + 1, _("Flags"), flags_str->str); + } + + return g_string_free (str, FALSE); +} diff --git a/src/fu-util-common.h b/src/fu-util-common.h index a00ce71bb..ba4c6031d 100644 --- a/src/fu-util-common.h +++ b/src/fu-util-common.h @@ -67,6 +67,11 @@ gboolean fu_util_parse_filter_flags (const gchar *filter, FwupdDeviceFlags *include, FwupdDeviceFlags *exclude, GError **error); - +gchar *fu_util_convert_description (const gchar *xml, + GError **error); +gchar *fu_util_device_to_string (FwupdDevice *dev, + guint idt); +gchar *fu_util_release_to_string (FwupdRelease *rel, + guint idt); G_END_DECLS diff --git a/src/fu-util.c b/src/fu-util.c index b9ff53ff7..e91f61508 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -306,60 +306,6 @@ fu_util_perhaps_show_unreported (FuUtilPrivate *priv, GError **error) return fu_util_report_history (priv, NULL, error); } -static gchar * -fu_util_convert_appstream_description (const gchar *xml, GError **error) -{ - g_autoptr(GString) str = g_string_new (NULL); - g_autoptr(XbNode) n = NULL; - g_autoptr(XbSilo) silo = NULL; - - /* parse XML */ - silo = xb_silo_new_from_xml (xml, error); - if (silo == NULL) - return NULL; - - n = xb_silo_get_root (silo); - while (n != NULL) { - g_autoptr(XbNode) n2 = NULL; - - /* support

      ,

        ,
          and
        1. , ignore all else */ - if (g_strcmp0 (xb_node_get_element (n), "p") == 0) { - g_string_append_printf (str, "%s\n\n", xb_node_get_text (n)); - } else if (g_strcmp0 (xb_node_get_element (n), "ul") == 0) { - g_autoptr(GPtrArray) children = xb_node_get_children (n); - for (guint i = 0; i < children->len; i++) { - XbNode *nc = g_ptr_array_index (children, i); - if (g_strcmp0 (xb_node_get_element (nc), "li") == 0) { - g_string_append_printf (str, " • %s\n", - xb_node_get_text (nc)); - } - } - g_string_append (str, "\n"); - } else if (g_strcmp0 (xb_node_get_element (n), "ol") == 0) { - g_autoptr(GPtrArray) children = xb_node_get_children (n); - for (guint i = 0; i < children->len; i++) { - XbNode *nc = g_ptr_array_index (children, i); - if (g_strcmp0 (xb_node_get_element (nc), "li") == 0) { - g_string_append_printf (str, " %u. %s\n", - i + 1, - xb_node_get_text (nc)); - } - } - g_string_append (str, "\n"); - } - - n2 = xb_node_get_next (n); - g_set_object (&n, n2); - } - - /* remove extra newline */ - if (str->len > 0) - g_string_truncate (str, str->len - 1); - - /* success */ - return g_string_free (g_steal_pointer (&str), FALSE); -} - static gboolean fu_util_modify_remote_warning (FuUtilPrivate *priv, FwupdRemote *remote, GError **error) { @@ -370,7 +316,7 @@ fu_util_modify_remote_warning (FuUtilPrivate *priv, FwupdRemote *remote, GError warning_markup = fwupd_remote_get_agreement (remote); if (warning_markup == NULL) return TRUE; - warning_plain = fu_util_convert_appstream_description (warning_markup, error); + warning_plain = fu_util_convert_description (warning_markup, error); if (warning_plain == NULL) return FALSE; @@ -483,7 +429,7 @@ fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) if (!fu_util_is_interesting_device (dev)) continue; } - tmp = fwupd_device_to_string (dev); + tmp = fu_util_device_to_string (dev, 0); g_print ("%s\n", tmp); } @@ -587,7 +533,7 @@ fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) g_autofree gchar *tmp = NULL; if (!fu_util_filter_device (priv, dev)) continue; - tmp = fwupd_device_to_string (dev); + tmp = fu_util_device_to_string (dev, 0); g_print ("%s\n", tmp); } return TRUE; @@ -885,7 +831,7 @@ fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) g_autofree gchar *str = NULL; if (!fu_util_filter_device (priv, dev)) continue; - str = fwupd_device_to_string (dev); + str = fu_util_device_to_string (dev, 0); g_print ("%s\n", str); } @@ -1269,7 +1215,7 @@ fu_util_get_results (FuUtilPrivate *priv, gchar **values, GError **error) rel = fwupd_client_get_results (priv->client, fwupd_device_get_id (dev), NULL, error); if (rel == NULL) return FALSE; - tmp = fwupd_device_to_string (rel); + tmp = fu_util_device_to_string (rel, 0); g_print ("%s", tmp); return TRUE; } @@ -1291,57 +1237,8 @@ fu_util_get_releases (FuUtilPrivate *priv, gchar **values, GError **error) g_print ("%s:\n", fwupd_device_get_name (dev)); for (guint i = 0; i < rels->len; i++) { FwupdRelease *rel = g_ptr_array_index (rels, i); - FwupdReleaseFlags flags = fwupd_release_get_flags (rel); - GPtrArray *checksums; - const gchar *tmp; - - /* TRANSLATORS: section header for release version number */ - fu_util_print_data (_("Version"), fwupd_release_get_version (rel)); - - /* TRANSLATORS: section header for the release name */ - fu_util_print_data (_("Name"), fu_util_release_get_name (rel)); - - /* TRANSLATORS: section header for the release one line summary */ - fu_util_print_data (_("Summary"), fwupd_release_get_summary (rel)); - - /* TRANSLATORS: section header for the remote the file is coming from */ - fu_util_print_data (_("Remote"), fwupd_release_get_remote_id (rel)); - - /* TRANSLATORS: section header for firmware URI */ - fu_util_print_data (_("URI"), fwupd_release_get_uri (rel)); - tmp = fwupd_release_get_description (rel); - if (tmp != NULL) { - g_autofree gchar *desc = NULL; - desc = fu_util_convert_appstream_description (tmp, NULL); - /* TRANSLATORS: section header for firmware description */ - fu_util_print_data (_("Description"), desc); - } - checksums = fwupd_release_get_checksums (rel); - for (guint j = 0; j < checksums->len; j++) { - const gchar *checksum = g_ptr_array_index (checksums, j); - g_autofree gchar *checksum_display = NULL; - checksum_display = fwupd_checksum_format_for_display (checksum); - /* TRANSLATORS: section header for firmware checksum */ - fu_util_print_data (_("Checksum"), checksum_display); - } - - /* show flags if set */ - if (flags != FWUPD_RELEASE_FLAG_NONE) { - g_autoptr(GString) str = g_string_new (""); - for (guint j = 0; j < 64; j++) { - if ((flags & ((guint64) 1 << j)) == 0) - continue; - g_string_append_printf (str, "%s,", - fwupd_release_flag_to_string ((guint64) 1 << j)); - } - if (str->len == 0) { - g_string_append (str, fwupd_release_flag_to_string (0)); - } else { - g_string_truncate (str, str->len - 1); - } - /* TRANSLATORS: section header for firmware flags */ - fu_util_print_data (_("Flags"), str->str); - } + g_autofree gchar *tmp = fu_util_release_to_string (rel, 0); + g_print ("%s", tmp); /* new line between all but last entries */ if (i != rels->len - 1) @@ -1389,7 +1286,7 @@ fu_util_prompt_for_release (FuUtilPrivate *priv, GPtrArray *rels, GError **error } /* remove markup, and fall back if we fail */ - desc = fu_util_convert_appstream_description (desc_tmp, NULL); + desc = fu_util_convert_description (desc_tmp, NULL); if (desc == NULL) desc = g_strdup (desc_tmp); g_print ("%u.\t%s (%s)\n", i + 1, fwupd_release_get_version (rel), desc); @@ -1526,45 +1423,6 @@ fu_util_perhaps_refresh_remotes (FuUtilPrivate *priv, GError **error) return fu_util_download_metadata (priv, error); } -static gchar * -fu_util_time_to_str (guint64 tmp) -{ - g_return_val_if_fail (tmp != 0, NULL); - - /* seconds */ - if (tmp < 60) { - /* TRANSLATORS: duration in seconds */ - return g_strdup_printf (ngettext ("%u second", "%u seconds", - (gint) tmp), - (guint) tmp); - } - - /* minutes */ - tmp /= 60; - if (tmp < 60) { - /* TRANSLATORS: duration in minutes */ - return g_strdup_printf (ngettext ("%u minute", "%u minutes", - (gint) tmp), - (guint) tmp); - } - - /* hours */ - tmp /= 60; - if (tmp < 60) { - /* TRANSLATORS: duration in minutes */ - return g_strdup_printf (ngettext ("%u hour", "%u hours", - (gint) tmp), - (guint) tmp); - } - - /* days */ - tmp /= 24; - /* TRANSLATORS: duration in days! */ - return g_strdup_printf (ngettext ("%u day", "%u days", - (gint) tmp), - (guint) tmp); -} - static gboolean fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -1581,8 +1439,7 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) return FALSE; for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index (devices, i); - GPtrArray *guids; - const gchar *tmp; + g_autofree gchar *tmp = NULL; g_autoptr(GPtrArray) rels = NULL; g_autoptr(GError) error_local = NULL; @@ -1602,74 +1459,16 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) continue; } - /* TRANSLATORS: first replacement is device name */ - g_print (_("%s has firmware updates:"), fwupd_device_get_name (dev)); - g_print ("\n"); - - /* TRANSLATORS: ID for hardware, typically a SHA1 sum */ - fu_util_print_data (_("Device ID"), fwupd_device_get_id (dev)); - - /* TRANSLATORS: a GUID for the hardware */ - guids = fwupd_device_get_guids (dev); - for (guint j = 0; j < guids->len; j++) { - tmp = g_ptr_array_index (guids, j); - fu_util_print_data (_("GUID"), tmp); - } + /* TRANSLATORS: list of devices */ + g_print ("%s\n", _("Firmware updates:")); + tmp = fu_util_device_to_string (dev, 0); + g_print ("%s", tmp); /* print all releases */ for (guint j = 0; j < rels->len; j++) { FwupdRelease *rel = g_ptr_array_index (rels, j); - guint64 duration; - GPtrArray *checksums; - - /* TRANSLATORS: Appstream ID for the hardware type */ - fu_util_print_data (_("ID"), fwupd_release_get_appstream_id (rel)); - - /* TRANSLATORS: section header for firmware version */ - fu_util_print_data (_("Update Version"), - fwupd_release_get_version (rel)); - - /* TRANSLATORS: section header for the release name */ - fu_util_print_data (_("Update Name"), fu_util_release_get_name (rel)); - - /* TRANSLATORS: section header for the release one line summary */ - fu_util_print_data (_("Update Summary"), fwupd_release_get_summary (rel)); - - /* TRANSLATORS: section header for remote ID, e.g. lvfs-testing */ - fu_util_print_data (_("Update Remote ID"), - fwupd_release_get_remote_id (rel)); - - /* optional approximate duration */ - duration = fwupd_release_get_install_duration (rel); - if (duration > 0) { - g_autofree gchar *str = fu_util_time_to_str (duration); - /* TRANSLATORS: section header for the amount - * of time it takes to install the update */ - fu_util_print_data (_("Update Duration"), str); - } - - checksums = fwupd_release_get_checksums (rel); - for (guint k = 0; k < checksums->len; k++) { - const gchar *checksum = g_ptr_array_index (checksums, k); - g_autofree gchar *checksum_display = NULL; - checksum_display = fwupd_checksum_format_for_display (checksum); - /* TRANSLATORS: section header for firmware checksum */ - fu_util_print_data (_("Update Checksum"), checksum_display); - } - - /* TRANSLATORS: section header for firmware remote http:// */ - fu_util_print_data (_("Update Location"), fwupd_release_get_uri (rel)); - - /* convert XML -> text */ - tmp = fwupd_release_get_description (rel); - if (tmp != NULL) { - g_autofree gchar *md = NULL; - md = fu_util_convert_appstream_description (tmp, NULL); - if (md != NULL) { - /* TRANSLATORS: section header for long firmware desc */ - fu_util_print_data (_("Update Description"), md); - } - } + g_autofree gchar *tmp2 = fu_util_release_to_string (rel, 1); + g_print ("%s\n", tmp2); } } diff --git a/src/meson.build b/src/meson.build index 35381c468..595fcbdde 100644 --- a/src/meson.build +++ b/src/meson.build @@ -153,6 +153,7 @@ fwupdagent = executable( giounix, gudev, gusb, + libxmlb, soup, ], link_with : [ From b390b14f8f9b796dcda62a6fc6edf1e033817c1d Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 15 Jul 2019 21:05:44 -0500 Subject: [PATCH 048/158] Add support to integrate into the motd (Fixes: #1270) Fixes Debian bug https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=921820 Introduce a new --log option to fwupdmgr that will log stdout to an argument. If run under systemd, prefix that argument with $RUNTIME_DIRECTORY. Add a new systemd unit and associated timer to regularly refresh metadata. After the metadata refresh is complete, save the output to the motd location. The timer and service are disabled by default and can be enabled by an admin. --- contrib/debian/fwupd.install | 1 + contrib/fwupd.spec.in | 2 + data/bash-completion/fwupdmgr.in | 1 + data/meson.build | 1 + data/motd/85-fwupd.motd.in | 5 ++ data/motd/README.md | 17 +++++ data/motd/fwupd-refresh.service.in | 19 ++++++ data/motd/fwupd-refresh.timer | 10 +++ data/motd/meson.build | 30 +++++++++ src/fu-util-common.c | 9 ++- src/fu-util.c | 103 +++++++++++++++++++++++------ 11 files changed, 174 insertions(+), 24 deletions(-) create mode 100755 data/motd/85-fwupd.motd.in create mode 100644 data/motd/README.md create mode 100644 data/motd/fwupd-refresh.service.in create mode 100644 data/motd/fwupd-refresh.timer create mode 100644 data/motd/meson.build diff --git a/contrib/debian/fwupd.install b/contrib/debian/fwupd.install index f9dfd4f69..ba9c1eca6 100644 --- a/contrib/debian/fwupd.install +++ b/contrib/debian/fwupd.install @@ -21,3 +21,4 @@ data/daemon.conf etc/fwupd debian/fwupd.pkla /var/lib/polkit-1/localauthority/10-vendor.d usr/lib/*/fwupd-plugins-*/*.so debian/lintian/fwupd usr/share/lintian/overrides +obj*/data/motd/85-fwupd /etc/update-motd.d diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index edf90f99f..2a115ca3b 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -275,6 +275,8 @@ rm ${RPM_BUILD_ROOT}%{_sbindir}/flashrom %{_datadir}/fwupd/firmware-packager %{_unitdir}/fwupd-offline-update.service %{_unitdir}/fwupd.service +%{_unitdir}/fwupd-refresh.service +%{_unitdir}/fwupd-refresh.timer %{_unitdir}/system-update.target.wants/ %dir %{_localstatedir}/lib/fwupd %dir %{_datadir}/fwupd/quirks.d diff --git a/data/bash-completion/fwupdmgr.in b/data/bash-completion/fwupdmgr.in index d090cb1a5..259c179a0 100644 --- a/data/bash-completion/fwupdmgr.in +++ b/data/bash-completion/fwupdmgr.in @@ -42,6 +42,7 @@ _fwupdmgr_opts=( '--show-all-devices' '--sign' '--filter' + '--log' ) _show_filters() diff --git a/data/meson.build b/data/meson.build index c40d56836..20f87dd96 100644 --- a/data/meson.build +++ b/data/meson.build @@ -2,6 +2,7 @@ subdir('builder') subdir('pki') subdir('remotes.d') subdir('bash-completion') +subdir('motd') if get_option('tests') subdir('tests') diff --git a/data/motd/85-fwupd.motd.in b/data/motd/85-fwupd.motd.in new file mode 100755 index 000000000..450d5c717 --- /dev/null +++ b/data/motd/85-fwupd.motd.in @@ -0,0 +1,5 @@ +#!/bin/sh + +if [ -f @motd_fullpath@ ]; then + cat @motd_fullpath@ +fi diff --git a/data/motd/README.md b/data/motd/README.md new file mode 100644 index 000000000..b735d9dfa --- /dev/null +++ b/data/motd/README.md @@ -0,0 +1,17 @@ +# Message of the day integration + +Message on the day integration is used to display the availability of updates when connecting to a remote console. + +It has two elements: +* Automatic firmware metadata refresh +* Message of the day display + +## Automatic firmware metadata refresh +This uses a systemd timer to run on a regular cadence. +To enable this, run +``` +# systemctl enable fwupd-refresh.timer +``` + +## Motd display +Motd display is dependent upon the availability of the update-motd snippet consumption service such as pam_motd. diff --git a/data/motd/fwupd-refresh.service.in b/data/motd/fwupd-refresh.service.in new file mode 100644 index 000000000..c6a66326c --- /dev/null +++ b/data/motd/fwupd-refresh.service.in @@ -0,0 +1,19 @@ +[Unit] +Description=Refresh fwupd metadata and update motd +Documentation=man:fwupdmgr(1) +After=network.target network-online.target systemd-networkd.service NetworkManager.service connman.service + +[Service] +Type=oneshot +RuntimeDirectory=@motd_dir@ +CacheDirectory=fwupd +RuntimeDirectoryPreserve=yes +StandardError=null +ExecStart=@bindir@/fwupdmgr refresh +ExecStart=@bindir@/fwupdmgr get-updates --log @motd_file@ +DynamicUser=yes +RestrictAddressFamilies=AF_NETLINK AF_UNIX AF_INET AF_INET6 +SystemCallFilter=~@mount +ProtectKernelModules=yes +ProtectControlGroups=yes +RestrictRealtime=yes diff --git a/data/motd/fwupd-refresh.timer b/data/motd/fwupd-refresh.timer new file mode 100644 index 000000000..5cd67ce61 --- /dev/null +++ b/data/motd/fwupd-refresh.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Refresh fwupd metadata regularly + +[Timer] +OnCalendar=*-*-* 6,18:00 +RandomizedDelaySec=12h +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/data/motd/meson.build b/data/motd/meson.build new file mode 100644 index 000000000..956773583 --- /dev/null +++ b/data/motd/meson.build @@ -0,0 +1,30 @@ +motd_file = '85-fwupd' +motd_dir = 'motd.d' +motd_fullpath = join_paths ('/run', motd_dir, motd_file) +con2 = configuration_data() +con2.set('bindir', bindir) +con2.set('motd_file', motd_file) +con2.set('motd_dir', motd_dir) +con2.set('motd_fullpath', motd_fullpath) + +# This file is only used in Ubuntu, which chooses to use update-motd instead +# of sourcing /run/motd.d/* +# See https://bugs.launchpad.net/ubuntu/+source/pam/+bug/399071 +configure_file( + input : '85-fwupd.motd.in', + output : motd_file, + configuration : con2, + install: false, +) + +if get_option('systemd') + install_data(['fwupd-refresh.timer'], + install_dir: systemdunitdir) + + configure_file( + input : 'fwupd-refresh.service.in', + output : 'fwupd-refresh.service', + configuration : con2, + install: true, + install_dir: systemdunitdir) +endif diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 01e1d6c72..c728b01a0 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -187,16 +187,21 @@ fu_util_is_interesting_device (FwupdDevice *dev) gchar * fu_util_get_user_cache_path (const gchar *fn) { + const gchar *root = g_get_user_cache_dir (); g_autofree gchar *basename = g_path_get_basename (fn); g_autofree gchar *cachedir_legacy = NULL; + /* if run from a systemd unit, use the cache directory set there */ + if (g_getenv ("CACHE_DIRECTORY") != NULL) + root = g_getenv ("CACHE_DIRECTORY"); + /* return the legacy path if it exists rather than renaming it to * prevent problems when using old and new versions of fwupd */ - cachedir_legacy = g_build_filename (g_get_user_cache_dir (), "fwupdmgr", NULL); + cachedir_legacy = g_build_filename (root, "fwupdmgr", NULL); if (g_file_test (cachedir_legacy, G_FILE_TEST_IS_DIR)) return g_build_filename (cachedir_legacy, basename, NULL); - return g_build_filename (g_get_user_cache_dir (), "fwupd", basename, NULL); + return g_build_filename (root, "fwupd", basename, NULL); } gchar * diff --git a/src/fu-util.c b/src/fu-util.c index e91f61508..09b2e3bad 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -66,6 +66,8 @@ struct FuUtilPrivate { FwupdDeviceFlags filter_exclude; }; +static GFileOutputStream *log_output = NULL; + static gboolean fu_util_report_history (FuUtilPrivate *priv, gchar **values, GError **error); static gboolean fu_util_download_file (FuUtilPrivate *priv, SoupURI *uri, @@ -2080,6 +2082,41 @@ fu_util_check_polkit_actions (GError **error) return TRUE; } +static void +fu_util_log_output (const gchar *str) +{ + if (log_output != NULL) { + g_autoptr(GError) error_local = NULL; + if (g_output_stream_write (G_OUTPUT_STREAM (log_output), + str, strlen(str), + NULL, &error_local) <0) + g_printerr ("%s\n", error_local->message); + } +} + +static void +fu_util_close_log (void) +{ + if (log_output != NULL) { + g_autoptr(GError) error_local = NULL; + if (!g_output_stream_close (G_OUTPUT_STREAM (log_output), + NULL, + &error_local)) { + g_printerr ("%s\n", error_local->message); + return; + } + g_object_unref (log_output); + } +} + +static void +fu_util_display_help (FuUtilPrivate *priv) +{ + g_autofree gchar *tmp = NULL; + tmp = g_option_context_get_help (priv->context, TRUE, NULL); + g_printerr ("%s\n", tmp); +} + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free) @@ -2101,6 +2138,7 @@ main (int argc, char *argv[]) g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new (); g_autofree gchar *cmd_descriptions = NULL; g_autofree gchar *filter = NULL; + g_autofree gchar *log = NULL; const GOptionEntry options[] = { { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, /* TRANSLATORS: command line option */ @@ -2123,6 +2161,9 @@ main (int argc, char *argv[]) { "assume-yes", 'y', 0, G_OPTION_ARG_NONE, &priv->assume_yes, /* TRANSLATORS: command line option */ _("Answer yes to all questions"), NULL }, + { "log", '\0', 0, G_OPTION_ARG_STRING, &log, + /* TRANSLATORS: command line option */ + _("Log output to FILE (typically for scripting use)"), NULL }, { "sign", '\0', 0, G_OPTION_ARG_NONE, &priv->sign, /* TRANSLATORS: command line option */ _("Sign the uploaded data with the client certificate"), NULL }, @@ -2328,14 +2369,6 @@ main (int argc, char *argv[]) /* sort by command name */ fu_util_cmd_array_sort (cmd_array); - /* non-TTY consoles cannot answer questions */ - if (isatty (fileno (stdout)) == 0) { - priv->no_unreported_check = TRUE; - priv->no_metadata_check = TRUE; - priv->no_reboot_check = TRUE; - fu_progressbar_set_interactive (priv->progressbar, FALSE); - } - /* get a list of the commands */ priv->context = g_option_context_new (NULL); cmd_descriptions = fu_util_cmd_array_to_string (cmd_array); @@ -2356,6 +2389,15 @@ main (int argc, char *argv[]) return EXIT_FAILURE; } + /* non-TTY consoles cannot answer questions */ + if (log != NULL || + isatty (fileno (stdout)) == 0) { + priv->no_unreported_check = TRUE; + priv->no_metadata_check = TRUE; + priv->no_reboot_check = TRUE; + fu_progressbar_set_interactive (priv->progressbar, FALSE); + } + /* parse filter flags */ if (filter != NULL) { if (!fu_util_parse_filter_flags (filter, @@ -2442,23 +2484,40 @@ main (int argc, char *argv[]) return EXIT_FAILURE; } + /* configure stdout redirection */ + if (log != NULL) { + g_autoptr(GFile) file = NULL; + g_autofree gchar *target_fname = NULL; + /* If running under systemd unit, use the directory as a base */ + if (g_getenv ("RUNTIME_DIRECTORY") != NULL) { + target_fname = g_build_filename (g_getenv ("RUNTIME_DIRECTORY"), + log, + NULL); + } else { + target_fname = g_steal_pointer (&log); + } + file = g_file_new_for_commandline_arg (target_fname); + log_output = g_file_append_to (file, G_FILE_CREATE_NONE, NULL, &error); + if (log_output == NULL) { + g_printerr ("%s\n", error->message); + return EXIT_FAILURE; + } + g_set_print_handler (fu_util_log_output); + } + /* run the specified command */ ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error); if (!ret) { - if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) { - g_autofree gchar *tmp = NULL; - tmp = g_option_context_get_help (priv->context, TRUE, NULL); - g_print ("%s\n\n%s", error->message, tmp); - return EXIT_FAILURE; - } - if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { - g_print ("%s\n", error->message); - return EXIT_NOTHING_TO_DO; - } - g_print ("%s\n", error->message); - return EXIT_FAILURE; + ret = EXIT_FAILURE; + g_printerr ("%s\n", error->message); + if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) + fu_util_display_help (priv); + else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) + ret = EXIT_NOTHING_TO_DO; + } else { + ret = EXIT_SUCCESS; } + fu_util_close_log (); - /* success */ - return EXIT_SUCCESS; + return ret; } From d3122a53ff76b40ad97fc785a06b99854bc66353 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 21 Aug 2019 16:34:41 -0500 Subject: [PATCH 049/158] Publish docs to fwupd.github.io using CircelCI For now this is happening on every master build, but in the future after it's working reliably it should be restricted to only tagged builds. To accomplish this, swap a build from circlei and travisci that will save docs to publish. --- .circleci/config.yml | 51 ++++++++++++++++++++++++++++++++++++++++---- .travis.yml | 2 +- contrib/ci/ubuntu.sh | 8 ++++++- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 32805d1c2..6734b3689 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,17 +12,21 @@ jobs: name: "Run build script" command: docker run -t -v `pwd`:/build fwupd-debian-x86_64 ./contrib/ci/check-abi $(git describe --abbrev=0 --tags) $(git rev-parse HEAD) - build-s390x: + build-ubuntu-x86_64: machine: image: circleci/classic:latest steps: - checkout - run: name: "Build container" - command: OS=debian-s390x ./contrib/ci/generate_docker.py + command: OS=ubuntu-x86_64 ./contrib/ci/generate_docker.py - run: name: "Run build script" - command: docker run --privileged -e CI=true -t -v `pwd`/dist:/build/dist fwupd-debian-s390x + command: docker run --privileged -e CI=true -t -v `pwd`/dist:/build/dist fwupd-ubuntu-x86_64 + - persist_to_workspace: + root: . + paths: + - "dist/docs" build-snap: docker: @@ -40,6 +44,31 @@ jobs: paths: - "*.snap" + publish-docs: + machine: true + steps: + - attach_workspace: + at: . + - add_ssh_keys: + fingerprints: + - "d8:73:05:1b:7c:93:8c:12:41:78:15:3d:5d:af:b4:c2" + - run: + name: Clone docs + working_directory: dist/docs + command: | + git clone --depth 1 git@github.com:fwupd/fwupd.github.io.git + - deploy: + name: Trigger docs deployment + working_directory: dist/docs/fwupd.github.io + command: | + git config credential.helper 'cache --timeout=120' + git config user.email "info@fwupd.org" + git config user.name "Documentation deployment Bot" + rm -rf * + cp ../html/* . -R + git commit -a --allow-empty -m "Trigger deployment" + git push git@github.com:fwupd/fwupd.github.io.git + publish-edge: docker: - image: cibuilds/snapcraft:stable @@ -70,7 +99,7 @@ workflows: version: 2 main: jobs: - - build-s390x + - build-ubuntu-x86_64 - build-snap - check-abi - publish-edge: @@ -81,6 +110,20 @@ workflows: only: master deploy: jobs: + - build-ubuntu-x86_64: + filters: + branches: + ignore: /.*/ + tags: + only: /^\d+\.\d+\.\d+$/ + - publish-docs: + requires: + - build-ubuntu-x86_64 + filters: + branches: + ignore: /.*/ + tags: + only: /^\d+\.\d+\.\d+$/ - build-snap: filters: branches: diff --git a/.travis.yml b/.travis.yml index a1ea9b83e..7029901e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ env: - OS=debian-x86_64 - OS=arch - OS=debian-i386 - - OS=ubuntu-x86_64 + - OS=debian-s390x install: - ./contrib/ci/generate_docker.py diff --git a/contrib/ci/ubuntu.sh b/contrib/ci/ubuntu.sh index 359d71b75..0b0c4b7f8 100755 --- a/contrib/ci/ubuntu.sh +++ b/contrib/ci/ubuntu.sh @@ -8,8 +8,14 @@ eval "$(dpkg-buildflags --export=sh)" export LDFLAGS=$(dpkg-buildflags --get LDFLAGS | sed "s/-Wl,-Bsymbolic-functions\s//") rm -rf build -meson build --werror +meson build --werror -Dman=false #build with clang and -Werror ninja -C build test -v + +#make docs available outside of docker +ninja -C build install -v +mkdir -p dist/docs +cp build/docs/libfwupd/* dist/docs -R + #run static analysis (these mostly won't be critical) ninja -C build scan-build -v From 71d2f62dc2c9308374e2b020fb83994c4bfd6ddc Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 22 Aug 2019 09:31:04 -0500 Subject: [PATCH 050/158] trivial: update references of hughsie/fwupd to fwupd/fwupd --- README.md | 4 ++-- contrib/PKGBUILD | 2 +- contrib/ci/README.md | 2 +- contrib/debian/control.in | 12 ++++++------ contrib/debian/copyright.in | 2 +- contrib/debian/signing-template/control | 2 +- contrib/debian/signing-template/copyright | 2 +- contrib/debian/watch | 2 +- contrib/fwupd.spec.in | 2 +- data/org.freedesktop.fwupd.metainfo.xml | 2 +- plugins/README.md | 2 +- plugins/synapticsmst/fu-plugin-synapticsmst.c | 2 +- plugins/thunderbolt/fu-plugin-thunderbolt.c | 2 +- plugins/uefi/fu-plugin-uefi.c | 4 ++-- policy/org.freedesktop.fwupd.policy.in | 2 +- src/fu-config.c | 2 +- src/fu-self-test.c | 2 +- src/fu-util.c | 2 +- subprojects/flashrom.wrap | 2 +- 19 files changed, 26 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 611064bcc..3cbcc5255 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ fwupd ===== -[![Build Status](https://travis-ci.org/hughsie/fwupd.png?branch=master)](https://travis-ci.org/hughsie/fwupd) +[![Build Status](https://travis-ci.org/fwupd/fwupd.png?branch=master)](https://travis-ci.org/fwupd/fwupd) [![Coverity Scan Build Status](https://scan.coverity.com/projects/10744/badge.svg)](https://scan.coverity.com/projects/10744) [![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-white.svg)](https://snapcraft.io/fwupd) @@ -11,7 +11,7 @@ Additional information is available at the website: https://fwupd.org ## Compiling -The most up to date compilation instructions are available in the [Wiki](https://github.com/hughsie/fwupd/wiki/Compilation) +The most up to date compilation instructions are available in the [Wiki](https://github.com/fwupd/fwupd/wiki/Compilation) LVFS ---- diff --git a/contrib/PKGBUILD b/contrib/PKGBUILD index 359ea8dd1..b0c26c0bb 100644 --- a/contrib/PKGBUILD +++ b/contrib/PKGBUILD @@ -6,7 +6,7 @@ pkgver=#VERSION# pkgrel=1 pkgdesc='A simple daemon to allow session software to update firmware' arch=('i686' 'x86_64') -url='https://github.com/hughsie/fwupd' +url='https://github.com/fwupd/fwupd' license=('GPL2') depends=('libgusb' 'modemmanager') optdepends=('tpm2-abrmd' 'tpm2-tools') diff --git a/contrib/ci/README.md b/contrib/ci/README.md index 227580163..16d60c138 100644 --- a/contrib/ci/README.md +++ b/contrib/ci/README.md @@ -1,6 +1,6 @@ Continuous Integration ====================== -Continuous integration for fwupd is provided by [Travis CI](https://travis-ci.org/hughsie/fwupd). +Continuous integration for fwupd is provided by [Travis CI](https://travis-ci.org/fwupd/fwupd). By using Travis CI, builds are exercised across a variety of environments attempting to maximize code coverage. For every commit or pull request 6 builds are performed: diff --git a/contrib/debian/control.in b/contrib/debian/control.in index c92dad269..46f9d4eac 100644 --- a/contrib/debian/control.in +++ b/contrib/debian/control.in @@ -8,7 +8,7 @@ Uploaders: Steve McIntyre <93sam@debian.org>, Build-Depends: %%%DYNAMIC%%% Standards-Version: 4.3.0 Section: admin -Homepage: https://github.com/hughsie/fwupd +Homepage: https://github.com/fwupd/fwupd Vcs-Git: https://salsa.debian.org/efi-team/fwupd.git Vcs-Browser: https://salsa.debian.org/efi-team/fwupd @@ -23,7 +23,7 @@ Description: Firmware update daemon library You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the system D-Bus interface directly. Firmware updates are supported for a variety of technologies. - See for details + See for details . This package provides the library used by the daemon. @@ -49,7 +49,7 @@ Description: Firmware update daemon You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the system D-Bus interface directly. Firmware updates are supported for a variety of technologies. - See for details + See for details Package: fwupd-tests Architecture: linux-any @@ -71,7 +71,7 @@ Description: Test suite for firmware update daemon You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the system D-Bus interface directly. Firmware updates are supported for a variety of technologies. - See for details + See for details . This package provides a set of installed tests that can be run to validate the daemon in a continuous integration system. @@ -86,7 +86,7 @@ Description: Firmware update daemon documentation (HTML format) You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the system D-Bus interface directly. Firmware updates are supported for a variety of technologies. - See for details + See for details . This package provides development documentation for creating a package that uses fwupd. @@ -105,7 +105,7 @@ Description: development files for libfwupd You can either use a GUI software manager like GNOME Software to view and apply updates, the command-line tool or the system D-Bus interface directly. Firmware updates are supported for a variety of technologies. - See for details + See for details . This package provides the development files for libfwupd diff --git a/contrib/debian/copyright.in b/contrib/debian/copyright.in index c05ee61a3..55343e77e 100644 --- a/contrib/debian/copyright.in +++ b/contrib/debian/copyright.in @@ -1,6 +1,6 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: fwupd -Source: https://github.com/hughsie/fwupd +Source: https://github.com/fwupd/fwupd %%%DYNAMIC%%% Files: *.metainfo.xml diff --git a/contrib/debian/signing-template/control b/contrib/debian/signing-template/control index 3006d94d1..4280703af 100644 --- a/contrib/debian/signing-template/control +++ b/contrib/debian/signing-template/control @@ -5,7 +5,7 @@ Uploaders: Daniel Jared Dominguez , Steve McIntyre <93 Build-Depends: debhelper (>= 9.0.0), sbsigntool [amd64 arm64 armhf i386], fwupd (= SIGNVERSION) [SIGNARCH] Standards-Version: 4.1.3 Section: libs -Homepage: https://github.com/hughsie/fwupd +Homepage: https://github.com/fwupd/fwupd Vcs-Git: https://salsa.debian.org/efi-team/fwupd.git Vcs-Browser: https://salsa.debian.org/efi-team/fwupd diff --git a/contrib/debian/signing-template/copyright b/contrib/debian/signing-template/copyright index d3d339573..b684e6ac0 100644 --- a/contrib/debian/signing-template/copyright +++ b/contrib/debian/signing-template/copyright @@ -1,6 +1,6 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: fwupd -Source: https://github.com/hughsie/fwupd +Source: https://github.com/fwupd/fwupd Files: * Copyright: 2015 Richard Hughes diff --git a/contrib/debian/watch b/contrib/debian/watch index 4d77e5279..4350c7f63 100644 --- a/contrib/debian/watch +++ b/contrib/debian/watch @@ -3,4 +3,4 @@ version=3 opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/fwupd-$1\.tar\.gz/ \ -https://github.com/hughsie/fwupd/tags .*/v?(\d\S*)\.tar\.gz +https://github.com/fwupd/fwupd/tags .*/v?(\d\S*)\.tar\.gz diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index 2a115ca3b..f45b117e5 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -37,7 +37,7 @@ Name: fwupd Version: #VERSION# Release: 0.#BUILD#%{?alphatag}%{?dist} License: LGPLv2+ -URL: https://github.com/hughsie/fwupd +URL: https://github.com/fwupd/fwupd Source0: http://people.freedesktop.org/~hughsient/releases/%{name}-%{version}.tar.xz BuildRequires: gettext diff --git a/data/org.freedesktop.fwupd.metainfo.xml b/data/org.freedesktop.fwupd.metainfo.xml index 3275d3566..221814125 100644 --- a/data/org.freedesktop.fwupd.metainfo.xml +++ b/data/org.freedesktop.fwupd.metainfo.xml @@ -20,7 +20,7 @@ tablets and on headless servers.

          - https://github.com/hughsie/fwupd/issues + https://github.com/fwupd/fwupd/issues https://fwupd.org/ https://www.transifex.com/freedesktop/fwupd/ richard_at_hughsie.com diff --git a/plugins/README.md b/plugins/README.md index 42d83921f..339252a10 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -21,6 +21,6 @@ This includes things like one plugin turning on a device, or providing missing metadata to another plugin. The ABI for these interactions is defined in: -https://github.com/hughsie/fwupd/blob/master/src/fu-device-metadata.h +https://github.com/fwupd/fwupd/blob/master/src/fu-device-metadata.h All interactions between plugins should have the interface defined in that file. diff --git a/plugins/synapticsmst/fu-plugin-synapticsmst.c b/plugins/synapticsmst/fu-plugin-synapticsmst.c index 1afadab61..be6b4cf01 100644 --- a/plugins/synapticsmst/fu-plugin-synapticsmst.c +++ b/plugins/synapticsmst/fu-plugin-synapticsmst.c @@ -48,7 +48,7 @@ fu_synapticsmst_check_supported_system (FuPlugin *plugin, GError **error) return TRUE; } - /* See https://github.com/hughsie/fwupd/issues/1121 for more details */ + /* See https://github.com/fwupd/fwupd/issues/1121 for more details */ if (!fu_synapticsmst_check_amdgpu_safe (error)) return FALSE; diff --git a/plugins/thunderbolt/fu-plugin-thunderbolt.c b/plugins/thunderbolt/fu-plugin-thunderbolt.c index d72643a7e..9279bbed9 100644 --- a/plugins/thunderbolt/fu-plugin-thunderbolt.c +++ b/plugins/thunderbolt/fu-plugin-thunderbolt.c @@ -688,7 +688,7 @@ fu_plugin_update (FuPlugin *plugin, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "%s. " - "See https://github.com/hughsie/fwupd/wiki/Thunderbolt:-Validation-failed-or-unknown-device for more information.", + "See https://github.com/fwupd/fwupd/wiki/Thunderbolt:-Validation-failed-or-unknown-device for more information.", msg); return FALSE; } diff --git a/plugins/uefi/fu-plugin-uefi.c b/plugins/uefi/fu-plugin-uefi.c index ab5a5c797..e7024ceee 100644 --- a/plugins/uefi/fu-plugin-uefi.c +++ b/plugins/uefi/fu-plugin-uefi.c @@ -392,7 +392,7 @@ fu_plugin_update (FuPlugin *plugin, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "%s only has %" G_GUINT32_FORMAT " flashes left -- " - "see https://github.com/hughsie/fwupd/wiki/Dell-TPM:-flashes-left for more information.", + "see https://github.com/fwupd/fwupd/wiki/Dell-TPM:-flashes-left for more information.", fu_device_get_name (device), flashes_left); return FALSE; } @@ -677,7 +677,7 @@ fu_plugin_uefi_ensure_esp_path (FuPlugin *plugin, GError **error) G_IO_ERROR, G_IO_ERROR_INVALID_FILENAME, "Unable to determine EFI system partition location, " - "See https://github.com/hughsie/fwupd/wiki/Determining-EFI-system-partition-location"); + "See https://github.com/fwupd/fwupd/wiki/Determining-EFI-system-partition-location"); return FALSE; } diff --git a/policy/org.freedesktop.fwupd.policy.in b/policy/org.freedesktop.fwupd.policy.in index 9fab34b5c..5e45825df 100644 --- a/policy/org.freedesktop.fwupd.policy.in +++ b/policy/org.freedesktop.fwupd.policy.in @@ -10,7 +10,7 @@ --> System firmware update - https://github.com/hughsie/fwupd + https://github.com/fwupd/fwupd application-x-firmware diff --git a/src/fu-config.c b/src/fu-config.c index 4e4bb6d4b..0e908397f 100644 --- a/src/fu-config.c +++ b/src/fu-config.c @@ -245,7 +245,7 @@ fu_config_add_remotes_for_path (FuConfig *self, const gchar *path, GError **erro fu_common_string_replace (agreement_markup, "$OS_RELEASE:NAME$", tmp); tmp = g_hash_table_lookup (self->os_release, "BUG_REPORT_URL"); if (tmp == NULL) - tmp = "https://github.com/hughsie/fwupd/issues"; + tmp = "https://github.com/fwupd/fwupd/issues"; fu_common_string_replace (agreement_markup, "$OS_RELEASE:BUG_REPORT_URL$", tmp); fwupd_remote_set_agreement (remote, agreement_markup->str); } diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 6cb7258c2..56bae55db 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -767,7 +767,7 @@ fu_engine_require_hwid_func (void) g_autoptr(XbSilo) silo = NULL; #if !defined(HAVE_GCAB_0_8) && defined(__s390x__) - /* See https://github.com/hughsie/fwupd/issues/318 for more information */ + /* See https://github.com/fwupd/fwupd/issues/318 for more information */ g_test_skip ("Skipping HWID test on s390x due to known problem with gcab"); return; #endif diff --git a/src/fu-util.c b/src/fu-util.c index 09b2e3bad..e79f52f03 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -2075,7 +2075,7 @@ fu_util_check_polkit_actions (GError **error) g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_AUTH_FAILED, - "PolicyKit files are missing, see https://github.com/hughsie/fwupd/wiki/PolicyKit-files-are-missing"); + "PolicyKit files are missing, see https://github.com/fwupd/fwupd/wiki/PolicyKit-files-are-missing"); return FALSE; } diff --git a/subprojects/flashrom.wrap b/subprojects/flashrom.wrap index 1039a40ff..fb93608da 100644 --- a/subprojects/flashrom.wrap +++ b/subprojects/flashrom.wrap @@ -1,4 +1,4 @@ [wrap-git] directory = flashrom -url = https://github.com/hughsie/flashrom.git +url = https://github.com/fwupd/flashrom.git revision = wip/hughsie/fwupd From 805f7990f3bc14b2af1a249176aa006b15aed4e2 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 23 Aug 2019 10:08:16 -0500 Subject: [PATCH 051/158] trivial: disable gtkdoc by default Since https://fwupd.github.io is now a thing, people can be directed there rather than relying upon locally built documentation by default. Also this will mean one less dependency to install for people who build from source. Lastly this finally means that I can do this set of actions without failure: ``` meson build ninja -C build ninja -C build install (PK prompts for password) rm -rf build ``` Previously gtkdoc stuff was built as root due to the PK prompt and removing it would lead to stuff like this: ``` rm: cannot remove 'build/docs/libfwupd/html/libfwupd-FwupdClient.html': Permission denied ``` --- contrib/ci/ubuntu.sh | 2 +- contrib/debian/rules | 2 +- meson_options.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/ci/ubuntu.sh b/contrib/ci/ubuntu.sh index 0b0c4b7f8..8be1673ff 100755 --- a/contrib/ci/ubuntu.sh +++ b/contrib/ci/ubuntu.sh @@ -8,7 +8,7 @@ eval "$(dpkg-buildflags --export=sh)" export LDFLAGS=$(dpkg-buildflags --get LDFLAGS | sed "s/-Wl,-Bsymbolic-functions\s//") rm -rf build -meson build --werror -Dman=false +meson build --werror -Dman=false -Dgtkdoc=true #build with clang and -Werror ninja -C build test -v diff --git a/contrib/debian/rules b/contrib/debian/rules index 56091c24a..25428e401 100755 --- a/contrib/debian/rules +++ b/contrib/debian/rules @@ -48,7 +48,7 @@ override_dh_auto_configure: else \ export FLASHROM="-Dplugin_flashrom=false"; \ fi; \ - dh_auto_configure -- $$UEFI $$DELL $$FLASHROM $$CI -Dplugin_dummy=true --libexecdir=/usr/lib + dh_auto_configure -- $$UEFI $$DELL $$FLASHROM $$CI -Dplugin_dummy=true -Dgtkdoc=true --libexecdir=/usr/lib override_dh_install: find debian/tmp/usr -type f -name "*a" -print | xargs rm -f diff --git a/meson_options.txt b/meson_options.txt index b72799fbd..e94e16c1e 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -3,7 +3,7 @@ option('agent', type : 'boolean', value : true, description : 'enable the fwupd option('consolekit', type : 'boolean', value : true, description : 'enable ConsoleKit support') option('firmware-packager', type : 'boolean', value : true, description : 'enable firmware-packager installation') option('gpg', type : 'boolean', value : true, description : 'enable the GPG verification support') -option('gtkdoc', type : 'boolean', value : true, description : 'enable developer documentation') +option('gtkdoc', type : 'boolean', value : false, description : 'enable developer documentation') option('introspection', type : 'boolean', value : true, description : 'generate GObject Introspection data') option('lvfs', type : 'boolean', value : true, description : 'enable LVFS remotes') option('man', type : 'boolean', value : true, description : 'enable man pages') From 36d2fea85834fd0e2efa9d5388e97824e406c583 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 23 Aug 2019 10:15:07 -0500 Subject: [PATCH 052/158] trivial: plugins/: Update README Reference fwupd.github.io --- plugins/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/README.md b/plugins/README.md index 339252a10..0cc17c93c 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -7,6 +7,9 @@ and writing different firmware) as well as ways quirk their behavior. You can find more information about the architecture in the developers section of the [fwupd website](https://fwupd.org). +You can use the [fwupd developer documentation](https://fwupd.github.io) to assist +with APIs available to write the plugin. + If you have a firmware specification and would like to see support in this project, please file an issue and share the spec. Patches are also welcome. From b56571eceb810448a7058cb5fe23ea85425e5088 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 23 Aug 2019 10:16:14 -0500 Subject: [PATCH 053/158] trivial: plugins: clarify stance on afuefi.efi --- plugins/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/README.md b/plugins/README.md index 0cc17c93c..f55ad46ed 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -15,7 +15,7 @@ in this project, please file an issue and share the spec. Patches are also welcome. We will not accept plugins that upgrade hardware using a proprietary Linux -executable, library, or DBus interface. +executable, proprietary UEFI executable, proprietary library, or DBus interface. Plugin interaction ------------------ From db8262041366a9ee8f3f953af3d9fc4221182546 Mon Sep 17 00:00:00 2001 From: Jonas Witschel Date: Sat, 24 Aug 2019 22:14:53 +0200 Subject: [PATCH 054/158] uefi: add support for tpm2-tools 4.X tpm2_pcrlist has been renamed to tpm2_pcrread in https://github.com/tpm2-software/tpm2-tools/commit/aedb0291d2dde7f2422333a5a7806d60943d5451 and the output format has changed slightly to include a leading "0x" in https://github.com/tpm2-software/tpm2-tools/commit/9374bd70f444b5bf905337a06537cfd2c5150854 Also add an example of the new output format to the self test. --- plugins/uefi/fu-self-test.c | 9 ++++++++- plugins/uefi/fu-uefi-pcrs.c | 7 ++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/plugins/uefi/fu-self-test.c b/plugins/uefi/fu-self-test.c index e3a561f1f..f8d150919 100644 --- a/plugins/uefi/fu-self-test.c +++ b/plugins/uefi/fu-self-test.c @@ -43,13 +43,17 @@ fu_uefi_pcrs_2_0_func (void) g_autoptr(FuUefiPcrs) pcrs = fu_uefi_pcrs_new (); g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) pcr0s = NULL; + g_autoptr(GPtrArray) pcr1s = NULL; g_autoptr(GPtrArray) pcrXs = NULL; g_setenv ("FWUPD_UEFI_TPM2_YAML_DATA", "sha1 :\n" " 0 : cbd9e4112727bc75761001abcb2dddd87a66caf5\n" "sha256 :\n" - " 0 : 122de8b579cce17b0703ca9f9716d6f99125af9569e7303f51ea7f85d317f01e\n", TRUE); + /* old output format of tpm2_listpcrs and tpm2_pcrlist */ + " 0 : 122de8b579cce17b0703ca9f9716d6f99125af9569e7303f51ea7f85d317f01e\n" + /* new output format of tpm2_pcrread */ + " 1 : 0x0D89E7CA2C487EED36DB5684826B4ABF0904D3BDCD74E61999573570CF3F9C75\n", TRUE); ret = fu_uefi_pcrs_setup (pcrs, &error); g_assert_no_error (error); @@ -57,6 +61,9 @@ fu_uefi_pcrs_2_0_func (void) pcr0s = fu_uefi_pcrs_get_checksums (pcrs, 0); g_assert_nonnull (pcr0s); g_assert_cmpint (pcr0s->len, ==, 2); + pcr1s = fu_uefi_pcrs_get_checksums (pcrs, 1); + g_assert_nonnull (pcr1s); + g_assert_cmpint (pcr1s->len, ==, 1); pcrXs = fu_uefi_pcrs_get_checksums (pcrs, 999); g_assert_nonnull (pcrXs); g_assert_cmpint (pcrXs->len, ==, 0); diff --git a/plugins/uefi/fu-uefi-pcrs.c b/plugins/uefi/fu-uefi-pcrs.c index 5c7e5239a..4c0813d3a 100644 --- a/plugins/uefi/fu-uefi-pcrs.c +++ b/plugins/uefi/fu-uefi-pcrs.c @@ -61,6 +61,7 @@ fu_uefi_pcrs_parse_line (const gchar *line, gpointer user_data) /* parse hash */ str = g_string_new (split[1]); fu_common_string_replace (str, " ", ""); + fu_common_string_replace (str, "0x", ""); if ((str->len != 40 && str->len != 64) || !_g_string_isxdigit (str)) { g_debug ("not SHA-1 or SHA-256, skipping: %s", split[1]); return; @@ -144,10 +145,14 @@ fu_uefi_pcrs_setup (FuUefiPcrs *self, GError **error) } else { g_autofree gchar *argv0 = NULL; - /* old name, then new name */ + /* tpm2-tools 2 tool name */ argv0 = fu_common_find_program_in_path ("tpm2_listpcrs", NULL); if (argv0 == NULL) + /* tpm2-tools 3 tool name */ argv0 = fu_common_find_program_in_path ("tpm2_pcrlist", error); + if (argv0 == NULL) + /* tpm2-tools 4 tool name */ + argv0 = fu_common_find_program_in_path ("tpm2_pcrread", error); if (argv0 == NULL) return FALSE; if (!fu_uefi_pcrs_setup_tpm20 (self, argv0, error)) From 22d482c244ea8ab6b0e2a417ecc08f2d1949b65f Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Sun, 25 Aug 2019 08:31:07 +0100 Subject: [PATCH 055/158] Split up source files in the build script --- src/meson.build | 146 +++++++++++++++--------------------------------- 1 file changed, 45 insertions(+), 101 deletions(-) diff --git a/src/meson.build b/src/meson.build index 595fcbdde..3def87721 100644 --- a/src/meson.build +++ b/src/meson.build @@ -7,7 +7,31 @@ endif keyring_deps = [] keyring_src = [] test_deps = [] -init_src = [] +libfwupdprivate_src = [ + 'fu-archive.c', + 'fu-chunk.c', + 'fu-common.c', + 'fu-common-cab.c', + 'fu-common-guid.c', + 'fu-common-version.c', + 'fu-device.c', + 'fu-device-locker.c', + 'fu-firmware.c', + 'fu-firmware-common.c', + 'fu-firmware-image.c', + 'fu-hwids.c', + 'fu-history.c', + 'fu-ihex-firmware.c', + 'fu-io-channel.c', + 'fu-plugin.c', + 'fu-progressbar.c', + 'fu-quirks.c', + 'fu-smbios.c', + 'fu-srec-firmware.c', + 'fu-test.c', + 'fu-udev-device.c', + 'fu-usb-device.c', +] if get_option('gpg') keyring_src += 'fu-keyring-gpg.c' @@ -24,35 +48,13 @@ if get_option('pkcs7') endif if get_option('systemd') - init_src += 'fu-systemd.c' + libfwupdprivate_src += 'fu-systemd.c' endif libfwupdprivate = static_library( 'fwupdprivate', sources : [ - init_src, - 'fu-archive.c', - 'fu-common.c', - 'fu-common-guid.c', - 'fu-common-version.c', - 'fu-chunk.c', - 'fu-device.c', - 'fu-device-locker.c', - 'fu-firmware.c', - 'fu-firmware-common.c', - 'fu-firmware-image.c', - 'fu-hwids.c', - 'fu-history.c', - 'fu-ihex-firmware.c', - 'fu-io-channel.c', - 'fu-plugin.c', - 'fu-progressbar.c', - 'fu-quirks.c', - 'fu-smbios.c', - 'fu-srec-firmware.c', - 'fu-test.c', - 'fu-udev-device.c', - 'fu-usb-device.c', + libfwupdprivate_src, ], include_directories : [ include_directories('..'), @@ -62,11 +64,12 @@ libfwupdprivate = static_library( giounix, gudev, gusb, - soup, - sqlite, libarchive, + libgcab, libjsonglib, libxmlb, + soup, + sqlite, valgrind, ], link_with : [ @@ -191,36 +194,18 @@ fwupdtool = executable( sources : [ 'fu-tool.c', keyring_src, - init_src, - 'fu-archive.c', - 'fu-chunk.c', - 'fu-common.c', - 'fu-common-cab.c', - 'fu-common-guid.c', - 'fu-common-version.c', + libfwupdprivate_src, 'fu-config.c', - 'fu-keyring.c', - 'fu-keyring-result.c', - 'fu-engine.c', - 'fu-hwids.c', 'fu-debug.c', - 'fu-device.c', 'fu-device-list.c', - 'fu-device-locker.c', + 'fu-engine.c', 'fu-idle.c', 'fu-install-task.c', - 'fu-ihex-firmware.c', - 'fu-io-channel.c', 'fu-keyring.c', + 'fu-keyring.c', + 'fu-keyring-result.c', 'fu-keyring-utils.c', - 'fu-history.c', - 'fu-plugin.c', 'fu-plugin-list.c', - 'fu-quirks.c', - 'fu-smbios.c', - 'fu-srec-firmware.c', - 'fu-udev-device.c', - 'fu-usb-device.c', 'fu-util-common.c', ], include_directories : [ @@ -277,40 +262,20 @@ executable( fu_hash, sources : [ keyring_src, - init_src, - 'fu-archive.c', - 'fu-chunk.c', - 'fu-common.c', - 'fu-common-cab.c', - 'fu-common-guid.c', - 'fu-common-version.c', + libfwupdprivate_src, 'fu-config.c', - 'fu-keyring.c', - 'fu-keyring-result.c', - 'fu-engine.c', - 'fu-main.c', - 'fu-hwids.c', 'fu-debug.c', - 'fu-device.c', 'fu-device-list.c', - 'fu-device-locker.c', - 'fu-firmware.c', - 'fu-firmware-common.c', - 'fu-firmware-image.c', + 'fu-engine.c', + 'fu-history.c', 'fu-idle.c', - 'fu-ihex-firmware.c', - 'fu-io-channel.c', 'fu-install-task.c', 'fu-keyring.c', + 'fu-keyring.c', + 'fu-keyring-result.c', 'fu-keyring-utils.c', - 'fu-history.c', - 'fu-plugin.c', + 'fu-main.c', 'fu-plugin-list.c', - 'fu-quirks.c', - 'fu-smbios.c', - 'fu-srec-firmware.c', - 'fu-udev-device.c', - 'fu-usb-device.c', ], include_directories : [ include_directories('..'), @@ -356,41 +321,20 @@ if get_option('tests') fu_hash, sources : [ keyring_src, - init_src, - 'fu-self-test.c', - 'fu-archive.c', - 'fu-chunk.c', - 'fu-common.c', - 'fu-common-cab.c', - 'fu-common-guid.c', - 'fu-common-version.c', + libfwupdprivate_src, 'fu-config.c', - 'fu-engine.c', - 'fu-firmware.c', - 'fu-firmware-common.c', - 'fu-firmware-image.c', - 'fu-keyring.c', - 'fu-keyring-utils.c', - 'fu-hwids.c', - 'fu-device.c', 'fu-device-list.c', - 'fu-device-locker.c', - 'fu-history.c', + 'fu-engine.c', 'fu-idle.c', - 'fu-ihex-firmware.c', 'fu-install-task.c', - 'fu-io-channel.c', + 'fu-keyring.c', 'fu-keyring.c', 'fu-keyring-result.c', - 'fu-plugin.c', + 'fu-keyring-utils.c', 'fu-plugin-list.c', - 'fu-progressbar.c', - 'fu-quirks.c', + 'fu-self-test.c', 'fu-smbios.c', - 'fu-srec-firmware.c', 'fu-test.c', - 'fu-udev-device.c', - 'fu-usb-device.c', ], include_directories : [ include_directories('..'), From 5611fd0581b1c28de97321daefae798a78f475b8 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Sun, 25 Aug 2019 09:50:28 +0100 Subject: [PATCH 056/158] Hash the source files when generating the daemon hash The unstripped static library changes between builds with profiling metadata when compiling with LTO. Fixes https://github.com/fwupd/fwupd/issues/1298 --- src/fu-hash.py | 17 +++++++++-------- src/meson.build | 4 ++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/fu-hash.py b/src/fu-hash.py index 13b20fc6f..639ca37af 100644 --- a/src/fu-hash.py +++ b/src/fu-hash.py @@ -16,17 +16,18 @@ def usage(return_code): out = sys.stdout else: out = sys.stderr - out.write("usage: fu-hash.py
          ") + out.write("usage: fu-hash.py
          ...") sys.exit(return_code) if __name__ == '__main__': if {'-?', '--help', '--usage'}.intersection(set(sys.argv)): usage(0) - if len(sys.argv) != 3: + if len(sys.argv) < 3: usage(1) - with open(sys.argv[1], 'rb') as f: - buf = f.read() - csum = hashlib.sha256(buf).hexdigest() - with open(sys.argv[2], 'w') as f2: - f2.write('#pragma once\n') - f2.write('#define FU_BUILD_HASH "%s"\n' % csum) + m = hashlib.sha256() + for argv in sys.argv[2:]: + with open(argv, 'rb') as f: + m.update(f.read()) + with open(sys.argv[1], 'w') as f2: + f2.write('#pragma once\n') + f2.write('#define FU_BUILD_HASH "%s"\n' % m.hexdigest()) diff --git a/src/meson.build b/src/meson.build index 3def87721..4ea09ba77 100644 --- a/src/meson.build +++ b/src/meson.build @@ -180,11 +180,11 @@ resources_src = gnome.compile_resources( fu_hash = custom_target( 'fu-hash.h', - input : libfwupdprivate, + input : libfwupdprivate_src, output : 'fu-hash.h', command : [python3.path(), join_paths(meson.current_source_dir(), 'fu-hash.py'), - '@INPUT@', '@OUTPUT@'] + '@OUTPUT@', '@INPUT@'] ) fwupdtool = executable( From 68f47471824df400cafd4e0fd78ecc9333ebfac9 Mon Sep 17 00:00:00 2001 From: Gary Lin Date: Mon, 26 Aug 2019 15:24:57 +0800 Subject: [PATCH 057/158] Remove the duplicate files in meson.build Some source files were duplicated when splitting up the source files. Signed-off-by: Gary Lin --- src/meson.build | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/meson.build b/src/meson.build index 4ea09ba77..ff0e4a4cc 100644 --- a/src/meson.build +++ b/src/meson.build @@ -202,7 +202,6 @@ fwupdtool = executable( 'fu-idle.c', 'fu-install-task.c', 'fu-keyring.c', - 'fu-keyring.c', 'fu-keyring-result.c', 'fu-keyring-utils.c', 'fu-plugin-list.c', @@ -267,11 +266,9 @@ executable( 'fu-debug.c', 'fu-device-list.c', 'fu-engine.c', - 'fu-history.c', 'fu-idle.c', 'fu-install-task.c', 'fu-keyring.c', - 'fu-keyring.c', 'fu-keyring-result.c', 'fu-keyring-utils.c', 'fu-main.c', @@ -328,7 +325,6 @@ if get_option('tests') 'fu-idle.c', 'fu-install-task.c', 'fu-keyring.c', - 'fu-keyring.c', 'fu-keyring-result.c', 'fu-keyring-utils.c', 'fu-plugin-list.c', From 751e125634c1929d6938c843128c1b003a15e4eb Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 15 Aug 2019 17:02:00 +0100 Subject: [PATCH 058/158] Add fu_device_rescan() and a FuDevice->rescan() vfunc This allows the plugin to cause a superclassed ->rescan() to be called in response to some vague hardware change. --- src/fu-device.c | 35 +++++++++++++++++++++++++++++++++++ src/fu-device.h | 5 +++++ 2 files changed, 40 insertions(+) diff --git a/src/fu-device.c b/src/fu-device.c index c355fc396..4e3058337 100644 --- a/src/fu-device.c +++ b/src/fu-device.c @@ -2100,6 +2100,41 @@ fu_device_probe (FuDevice *self, GError **error) return TRUE; } +/** + * fu_device_rescan: + * @self: A #FuDevice + * @error: A #GError, or %NULL + * + * Rescans a device, re-adding GUIDs or flags based on some hardware change. + * + * Returns: %TRUE for success + * + * Since: 1.2.11 + **/ +gboolean +fu_device_rescan (FuDevice *self, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); + + g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* remove all GUIDs */ + g_ptr_array_set_size (fu_device_get_instance_ids (self), 0); + g_ptr_array_set_size (fu_device_get_guids (self), 0); + + /* subclassed */ + if (klass->rescan != NULL) { + if (!klass->rescan (self, error)) { + fu_device_convert_instance_ids (self); + return FALSE; + } + } + + fu_device_convert_instance_ids (self); + return TRUE; +} + /** * fu_device_convert_instance_ids: * @self: A #FuDevice diff --git a/src/fu-device.h b/src/fu-device.h index a9d5838d7..05e36f4b2 100644 --- a/src/fu-device.h +++ b/src/fu-device.h @@ -40,6 +40,8 @@ struct _FuDeviceClass GError **error); gboolean (*probe) (FuDevice *self, GError **error); + gboolean (*rescan) (FuDevice *self, + GError **error); FuFirmware *(*prepare_firmware) (FuDevice *self, GBytes *fw, FwupdInstallFlags flags, @@ -110,6 +112,7 @@ FuDevice *fu_device_new (void); #define fu_device_get_modified(d) fwupd_device_get_modified(FWUPD_DEVICE(d)) #define fu_device_get_guids(d) fwupd_device_get_guids(FWUPD_DEVICE(d)) #define fu_device_get_guid_default(d) fwupd_device_get_guid_default(FWUPD_DEVICE(d)) +#define fu_device_get_instance_ids(d) fwupd_device_get_instance_ids(FWUPD_DEVICE(d)) #define fu_device_get_icons(d) fwupd_device_get_icons(FWUPD_DEVICE(d)) #define fu_device_get_name(d) fwupd_device_get_name(FWUPD_DEVICE(d)) #define fu_device_get_serial(d) fwupd_device_get_serial(FWUPD_DEVICE(d)) @@ -232,6 +235,8 @@ gboolean fu_device_probe (FuDevice *self, GError **error); gboolean fu_device_setup (FuDevice *self, GError **error); +gboolean fu_device_rescan (FuDevice *self, + GError **error); gboolean fu_device_activate (FuDevice *self, GError **error); void fu_device_probe_invalidate (FuDevice *self); From 5e952ce35f106f971e77d6721a3326ba918f60ad Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 26 Aug 2019 11:09:46 +0100 Subject: [PATCH 059/158] Add a ->udev_device_changed plugin vfunc This allows plugins to rescan hardware based on uevents of any device class registered with fu_plugin_add_udev_subsystem(). Additionally, the events are rate limited to avoid causing lots of extra plugin processing when replugging hardware. --- src/fu-engine.c | 75 ++++++++++++++++++++++++++++++++++++++++- src/fu-plugin-private.h | 3 ++ src/fu-plugin-vfuncs.h | 3 ++ src/fu-plugin.c | 37 ++++++++++++++++++++ 4 files changed, 117 insertions(+), 1 deletion(-) diff --git a/src/fu-engine.c b/src/fu-engine.c index d07843c3a..d8dc4b15a 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -70,6 +70,7 @@ struct _FuEngine FuPluginList *plugin_list; GPtrArray *plugin_filter; GPtrArray *udev_subsystems; + GHashTable *udev_changed_ids; /* sysfs:FuEngineUdevChangedHelper */ FuSmbios *smbios; FuHwids *hwids; FuQuirks *quirks; @@ -3938,10 +3939,69 @@ fu_engine_udev_device_remove (FuEngine *self, GUdevDevice *udev_device) } } +typedef struct { + FuEngine *self; + GUdevDevice *udev_device; + guint idle_id; +} FuEngineUdevChangedHelper; + +static void +fu_engine_udev_changed_helper_free (FuEngineUdevChangedHelper *helper) +{ + if (helper->idle_id != 0) + g_source_remove (helper->idle_id); + g_object_unref (helper->self); + g_object_unref (helper->udev_device); + g_free (helper); +} + +static FuEngineUdevChangedHelper * +fu_engine_udev_changed_helper_new (FuEngine *self, GUdevDevice *udev_device) +{ + FuEngineUdevChangedHelper *helper = g_new0 (FuEngineUdevChangedHelper, 1); + helper->self = g_object_ref (self); + helper->udev_device = g_object_ref (udev_device); + return helper; +} + +static gboolean +fu_engine_udev_changed_cb (gpointer user_data) +{ + FuEngineUdevChangedHelper *helper = (FuEngineUdevChangedHelper *) user_data; + GPtrArray *plugins = fu_plugin_list_get_all (helper->self->plugin_list); + g_autoptr(FuUdevDevice) device = fu_udev_device_new (helper->udev_device); + + /* run all plugins */ + for (guint j = 0; j < plugins->len; j++) { + FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j); + g_autoptr(GError) error = NULL; + if (!fu_plugin_runner_udev_device_changed (plugin_tmp, device, &error)) { + if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_debug ("%s ignoring: %s", + fu_plugin_get_name (plugin_tmp), + error->message); + continue; + } + g_warning ("%s failed to change udev device %s: %s", + fu_plugin_get_name (plugin_tmp), + g_udev_device_get_sysfs_path (helper->udev_device), + error->message); + } + } + + /* device done, so remove ref */ + helper->idle_id = 0; + g_hash_table_remove (helper->self->udev_changed_ids, + g_udev_device_get_sysfs_path (helper->udev_device)); + return FALSE; +} + static void fu_engine_udev_device_changed (FuEngine *self, GUdevDevice *udev_device) { + const gchar *sysfs_path = g_udev_device_get_sysfs_path (udev_device); g_autoptr(GPtrArray) devices = NULL; + FuEngineUdevChangedHelper *helper; /* emit changed on any that match */ devices = fu_device_list_get_all (self->device_list); @@ -3950,10 +4010,20 @@ fu_engine_udev_device_changed (FuEngine *self, GUdevDevice *udev_device) if (!FU_IS_UDEV_DEVICE (device)) continue; if (g_strcmp0 (fu_udev_device_get_sysfs_path (FU_UDEV_DEVICE (device)), - g_udev_device_get_sysfs_path (udev_device)) == 0) { + sysfs_path) == 0) { fu_udev_device_emit_changed (FU_UDEV_DEVICE (device)); } } + + /* run all plugins, with per-device rate limiting */ + if (g_hash_table_remove (self->udev_changed_ids, sysfs_path)) { + g_debug ("re-adding rate-limited timeout for %s", sysfs_path); + } else { + g_debug ("adding rate-limited timeout for %s", sysfs_path); + } + helper = fu_engine_udev_changed_helper_new (self, udev_device); + helper->idle_id = g_timeout_add (500, fu_engine_udev_changed_cb, helper); + g_hash_table_insert (self->udev_changed_ids, g_strdup (sysfs_path), helper); } static void @@ -4676,6 +4746,8 @@ fu_engine_init (FuEngine *self) self->plugin_list = fu_plugin_list_new (); self->plugin_filter = g_ptr_array_new_with_free_func (g_free); self->udev_subsystems = g_ptr_array_new_with_free_func (g_free); + self->udev_changed_ids = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) fu_engine_udev_changed_helper_free); self->runtime_versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); self->compile_versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); self->approved_firmware = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); @@ -4733,6 +4805,7 @@ fu_engine_finalize (GObject *obj) g_object_unref (self->device_list); g_ptr_array_unref (self->plugin_filter); g_ptr_array_unref (self->udev_subsystems); + g_hash_table_unref (self->udev_changed_ids); g_hash_table_unref (self->runtime_versions); g_hash_table_unref (self->compile_versions); g_hash_table_unref (self->approved_firmware); diff --git a/src/fu-plugin-private.h b/src/fu-plugin-private.h index f8d868590..11f6c4c8d 100644 --- a/src/fu-plugin-private.h +++ b/src/fu-plugin-private.h @@ -86,6 +86,9 @@ gboolean fu_plugin_runner_usb_device_added (FuPlugin *self, gboolean fu_plugin_runner_udev_device_added (FuPlugin *self, FuUdevDevice *device, GError **error); +gboolean fu_plugin_runner_udev_device_changed (FuPlugin *self, + FuUdevDevice *device, + GError **error); void fu_plugin_runner_device_removed (FuPlugin *self, FuDevice *device); void fu_plugin_runner_device_register (FuPlugin *self, diff --git a/src/fu-plugin-vfuncs.h b/src/fu-plugin-vfuncs.h index 16c157b26..a2c22a4a1 100644 --- a/src/fu-plugin-vfuncs.h +++ b/src/fu-plugin-vfuncs.h @@ -80,6 +80,9 @@ gboolean fu_plugin_usb_device_added (FuPlugin *plugin, gboolean fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **error); +gboolean fu_plugin_udev_device_changed (FuPlugin *plugin, + FuUdevDevice *device, + GError **error); gboolean fu_plugin_device_removed (FuPlugin *plugin, FuDevice *device, GError **error); diff --git a/src/fu-plugin.c b/src/fu-plugin.c index ffcb52e23..5d3a1450b 100644 --- a/src/fu-plugin.c +++ b/src/fu-plugin.c @@ -1305,6 +1305,43 @@ fu_plugin_runner_udev_device_added (FuPlugin *self, FuUdevDevice *device, GError return TRUE; } +gboolean +fu_plugin_runner_udev_device_changed (FuPlugin *self, FuUdevDevice *device, GError **error) +{ + FuPluginPrivate *priv = GET_PRIVATE (self); + FuPluginUdevDeviceAddedFunc func = NULL; + g_autoptr(GError) error_local = NULL; + + /* not enabled */ + if (!priv->enabled) + return TRUE; + + /* no object loaded */ + if (priv->module == NULL) + return TRUE; + + /* optional */ + g_module_symbol (priv->module, "fu_plugin_udev_device_changed", (gpointer *) &func); + if (func == NULL) + return TRUE; + g_debug ("performing udev_device_changed() on %s", priv->name); + if (!func (self, device, &error_local)) { + if (error_local == NULL) { + g_critical ("unset error in plugin %s for udev_device_changed()", + priv->name); + g_set_error_literal (&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + } + g_propagate_prefixed_error (error, g_steal_pointer (&error_local), + "failed to change device on %s: ", + priv->name); + return FALSE; + } + return TRUE; +} + void fu_plugin_runner_device_removed (FuPlugin *self, FuDevice *device) { From d8aaa21cde3dbd4dbc6343cff7463e5e7732d485 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 26 Aug 2019 11:59:20 +0100 Subject: [PATCH 060/158] Allow turning off the timestamp prefix for debugging This makes it easier to 'diff' the fwupd and fwupdtool output between runs. --- src/fu-debug.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/fu-debug.c b/src/fu-debug.c index 1f288a4bb..c9c073306 100644 --- a/src/fu-debug.c +++ b/src/fu-debug.c @@ -17,6 +17,7 @@ typedef struct { GOptionGroup *group; gboolean verbose; gboolean console; + gboolean no_timestamp; gchar **plugin_verbose; gchar **daemon_verbose; } FuDebug; @@ -73,11 +74,13 @@ fu_debug_handler_cb (const gchar *log_domain, return; /* time header */ - tmp = g_strdup_printf ("%02i:%02i:%02i:%04i", - g_date_time_get_hour (dt), - g_date_time_get_minute (dt), - g_date_time_get_second (dt), - g_date_time_get_microsecond (dt) / 1000); + if (!self->no_timestamp) { + tmp = g_strdup_printf ("%02i:%02i:%02i:%04i", + g_date_time_get_hour (dt), + g_date_time_get_minute (dt), + g_date_time_get_second (dt), + g_date_time_get_microsecond (dt) / 1000); + } /* each file should have set this */ if (log_domain == NULL) @@ -129,6 +132,9 @@ fu_debug_pre_parse_hook (GOptionContext *context, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &self->verbose, /* TRANSLATORS: turn on all debugging */ N_("Show debugging information for all domains"), NULL }, + { "no-timestamp", '\0', 0, G_OPTION_ARG_NONE, &self->no_timestamp, + /* TRANSLATORS: turn on all debugging */ + N_("Do not include timestamp prefix"), NULL }, { "plugin-verbose", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &self->plugin_verbose, /* TRANSLATORS: this is for plugin development */ N_("Show plugin verbose information"), "PLUGIN-NAME" }, From b76960efe0ddf53ae9ab15eb568d4060a13f9939 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 26 Aug 2019 23:43:40 -0500 Subject: [PATCH 061/158] trivial: fu-util: fix debug output for devices I noticed this was only actually working for fwupdtool, and that's because fwupdmgr doesn't set FWUPD_VERBOSE currently in the environment. --- src/fu-util.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fu-util.c b/src/fu-util.c index e79f52f03..8a1aebcc7 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -2414,6 +2414,7 @@ main (int argc, char *argv[]) /* set verbose? */ if (verbose) { g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); + g_setenv ("FWUPD_VERBOSE", "1", FALSE); } else { g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, fu_util_ignore_cb, NULL); From 921e426f5ab31755670de550cc0903af1c211838 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 26 Aug 2019 23:37:32 -0500 Subject: [PATCH 062/158] Use more systemd directives for directories systemd will automatically create these directories if missing and also will set permissions accordingly on them. --- data/fwupd.service.in | 3 +++ data/meson.build | 6 +----- meson_post_install.sh | 2 -- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/data/fwupd.service.in b/data/fwupd.service.in index 36b994b92..4085632e5 100644 --- a/data/fwupd.service.in +++ b/data/fwupd.service.in @@ -8,6 +8,9 @@ Before=display-manager.service Type=dbus BusName=org.freedesktop.fwupd ExecStart=@libexecdir@/fwupd/fwupd +StateDirectory=@package_name@ +CacheDirectory=@package_name@ +ConfigurationDirectory=@package_name@ PrivateTmp=yes ProtectHome=yes ProtectSystem=full diff --git a/data/meson.build b/data/meson.build index 20f87dd96..829c01f89 100644 --- a/data/meson.build +++ b/data/meson.build @@ -39,12 +39,8 @@ if get_option('systemd') con2.set('bindir', bindir) con2.set('datadir', datadir) con2.set('localstatedir', localstatedir) - + con2.set('package_name', meson.project_name()) rw_directories = [] - rw_directories += join_paths (localstatedir, 'lib', 'fwupd') - rw_directories += join_paths (localstatedir, 'cache', 'fwupd') - rw_directories += join_paths (sysconfdir, 'fwupd') - rw_directories += join_paths (sysconfdir, 'fwupd', 'remotes.d') if get_option('plugin_uefi') rw_directories += ['-/boot/efi', '-/efi/EFI', '-/boot/EFI'] endif diff --git a/meson_post_install.sh b/meson_post_install.sh index 0cbb6f419..d757a81ae 100755 --- a/meson_post_install.sh +++ b/meson_post_install.sh @@ -11,6 +11,4 @@ LOCALSTATEDIR=$2 echo 'Updating systemd deps' mkdir -p ${DESTDIR}${SYSTEMDUNITDIR}/system-update.target.wants ln -sf ../fwupd-offline-update.service ${DESTDIR}${SYSTEMDUNITDIR}/system-update.target.wants/fwupd-offline-update.service - echo 'Creating stateful directory' - mkdir -p ${DESTDIR}${LOCALSTATEDIR}/lib/fwupd #fi From 277c196369f86f65f8831a049d01fab84cee4b1f Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 26 Aug 2019 23:42:23 -0500 Subject: [PATCH 063/158] fu-common: Use environment variables for systemd managed directories The systemd unit will set these, create these directories and set all permissions. If that has happened, use them. --- src/fu-common.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/fu-common.c b/src/fu-common.c index 2b5abc9d1..684a21185 100644 --- a/src/fu-common.c +++ b/src/fu-common.c @@ -1025,14 +1025,23 @@ fu_common_get_path (FuPathKind path_kind) #endif /* /etc/fwupd */ case FU_PATH_KIND_SYSCONFDIR_PKG: + tmp = g_getenv ("CONFIGURATION_DIRECTORY"); + if (tmp != NULL) + return g_build_filename (tmp, NULL); basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR); return g_build_filename (basedir, PACKAGE_NAME, NULL); /* /var/lib/fwupd */ case FU_PATH_KIND_LOCALSTATEDIR_PKG: + tmp = g_getenv ("STATE_DIRECTORY"); + if (tmp != NULL) + return g_build_filename (tmp, NULL); basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR); return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL); /* /var/cache/fwupd */ case FU_PATH_KIND_CACHEDIR_PKG: + tmp = g_getenv ("CACHE_DIRECTORY"); + if (tmp != NULL) + return g_build_filename (tmp, NULL); basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR); return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL); case FU_PATH_KIND_POLKIT_ACTIONS: From 1b5f1da2028189d5f743ea7e6ea5c45ebc09e4b8 Mon Sep 17 00:00:00 2001 From: Jonas Witschel Date: Mon, 26 Aug 2019 13:40:09 +0200 Subject: [PATCH 064/158] uefi: use tpm2-tss library to read PCR values Using the library instead of the command line tools provides a more stable interface. This implementation only fetches PCR 0 for all available hash algorithms since this is the only PCR that is actually used in fwupd. --- .lgtm.yml | 10 ++++ contrib/PKGBUILD | 3 +- contrib/ci/dependencies.xml | 23 ++++++++ contrib/debian/control.in | 2 - contrib/fwupd.spec.in | 1 - meson.build | 1 + plugins/uefi/fu-self-test.c | 53 ----------------- plugins/uefi/fu-uefi-pcrs.c | 113 +++++++++++++++++++++++++----------- plugins/uefi/meson.build | 3 + snap/snapcraft.yaml | 14 ++++- 10 files changed, 131 insertions(+), 92 deletions(-) diff --git a/.lgtm.yml b/.lgtm.yml index a817b260a..1463254d6 100644 --- a/.lgtm.yml +++ b/.lgtm.yml @@ -10,6 +10,7 @@ extraction: - libcogl-pango-dev - python3-pil - python3-cairo + - libssl-dev after_prepare: - "wget -O libxmlb.zip https://github.com/hughsie/libxmlb/archive/0.1.7.zip" - "mkdir -p subprojects/libxmlb" @@ -17,6 +18,15 @@ extraction: - "wget -O flashrom.zip https://github.com/hughsie/flashrom/archive/wip/hughsie/fwupd.zip" - "mkdir -p subprojects/flashrom" - "bsdtar --strip-components=1 -xvf flashrom.zip -C subprojects/flashrom" + - "cd $LGTM_WORKSPACE" + - "mkdir installdir" + - "wget https://github.com/tpm2-software/tpm2-tss/releases/download/2.3.0/tpm2-tss-2.3.0.tar.gz" + - "tar xf tpm2-tss-2.3.0.tar.gz" + - "cd tpm2-tss-2.3.0" + - "./configure --prefix=$LGTM_WORKSPACE/installdir/usr --disable-doxygen-doc" + - "make install" + - "export PKG_CONFIG_PATH=$LGTM_WORKSPACE/installdir/usr/lib/pkgconfig:$PKG_CONFIG_PATH" + - "export LD_LIBRARY_PATH=$LGTM_WORKSPACE/installdir/usr/lib:$LD_LIBRARY_PATH" index: build_command: - "meson setup build" diff --git a/contrib/PKGBUILD b/contrib/PKGBUILD index b0c26c0bb..38b4f912f 100644 --- a/contrib/PKGBUILD +++ b/contrib/PKGBUILD @@ -8,8 +8,7 @@ pkgdesc='A simple daemon to allow session software to update firmware' arch=('i686' 'x86_64') url='https://github.com/fwupd/fwupd' license=('GPL2') -depends=('libgusb' 'modemmanager') -optdepends=('tpm2-abrmd' 'tpm2-tools') +depends=('libgusb' 'modemmanager' 'tpm2-tss') makedepends=('meson' 'valgrind' 'gobject-introspection' 'gtk-doc' 'python-pillow' 'git' 'python-cairo' 'noto-fonts' 'noto-fonts-cjk' 'python-gobject' 'vala' 'libsoup' 'polkit' 'gcab') diff --git a/contrib/ci/dependencies.xml b/contrib/ci/dependencies.xml index 0edd6679f..316220366 100644 --- a/contrib/ci/dependencies.xml +++ b/contrib/ci/dependencies.xml @@ -1291,4 +1291,27 @@ + + + tpm2-tss + + + tpm2-tss-devel + + + tpm2-tss-devel + + + + + + + + + + + + + + diff --git a/contrib/debian/control.in b/contrib/debian/control.in index 46f9d4eac..622a56504 100644 --- a/contrib/debian/control.in +++ b/contrib/debian/control.in @@ -34,8 +34,6 @@ Depends: ${misc:Depends}, shared-mime-info Recommends: python3, bolt, - tpm2-tools, - tpm2-abrmd, fwupd-signed Breaks: gir1.2-dfu-1.0 (<< 0.9.7-1), libdfu1 (<< 0.9.7-1), diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index f45b117e5..91c84d59b 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -106,7 +106,6 @@ Requires: bubblewrap Requires: shared-mime-info Recommends: python3 -Recommends: tpm2-tools tpm2-abrmd Obsoletes: fwupd-sign < 0.1.6 Obsoletes: libebitdo < 0.7.5-3 diff --git a/meson.build b/meson.build index 1c177e0b3..6f242b5e9 100644 --- a/meson.build +++ b/meson.build @@ -219,6 +219,7 @@ if get_option('plugin_uefi') efiboot = dependency('efiboot') objcopy = find_program ('objcopy') readelf = find_program ('readelf') + tpm2tss = dependency('tss2-esys', version : '>= 2.0') efi_app_location = join_paths(libexecdir, 'fwupd', 'efi') conf.set_quoted ('EFI_APP_LOCATION', efi_app_location) diff --git a/plugins/uefi/fu-self-test.c b/plugins/uefi/fu-self-test.c index f8d150919..32a6cbe20 100644 --- a/plugins/uefi/fu-self-test.c +++ b/plugins/uefi/fu-self-test.c @@ -36,57 +36,6 @@ fu_uefi_pcrs_1_2_func (void) g_assert_cmpint (pcrXs->len, ==, 0); } -static void -fu_uefi_pcrs_2_0_func (void) -{ - gboolean ret; - g_autoptr(FuUefiPcrs) pcrs = fu_uefi_pcrs_new (); - g_autoptr(GError) error = NULL; - g_autoptr(GPtrArray) pcr0s = NULL; - g_autoptr(GPtrArray) pcr1s = NULL; - g_autoptr(GPtrArray) pcrXs = NULL; - - g_setenv ("FWUPD_UEFI_TPM2_YAML_DATA", - "sha1 :\n" - " 0 : cbd9e4112727bc75761001abcb2dddd87a66caf5\n" - "sha256 :\n" - /* old output format of tpm2_listpcrs and tpm2_pcrlist */ - " 0 : 122de8b579cce17b0703ca9f9716d6f99125af9569e7303f51ea7f85d317f01e\n" - /* new output format of tpm2_pcrread */ - " 1 : 0x0D89E7CA2C487EED36DB5684826B4ABF0904D3BDCD74E61999573570CF3F9C75\n", TRUE); - - ret = fu_uefi_pcrs_setup (pcrs, &error); - g_assert_no_error (error); - g_assert_true (ret); - pcr0s = fu_uefi_pcrs_get_checksums (pcrs, 0); - g_assert_nonnull (pcr0s); - g_assert_cmpint (pcr0s->len, ==, 2); - pcr1s = fu_uefi_pcrs_get_checksums (pcrs, 1); - g_assert_nonnull (pcr1s); - g_assert_cmpint (pcr1s->len, ==, 1); - pcrXs = fu_uefi_pcrs_get_checksums (pcrs, 999); - g_assert_nonnull (pcrXs); - g_assert_cmpint (pcrXs->len, ==, 0); -} - -static void -fu_uefi_pcrs_2_0_failure_func (void) -{ - gboolean ret; - g_autoptr(FuUefiPcrs) pcrs = fu_uefi_pcrs_new (); - g_autoptr(GError) error = NULL; - - g_setenv ("FWUPD_UEFI_TPM2_YAML_DATA", - "Something is not working properly!\n" - "999:hello\n" - "0:dave\n" - "\n", TRUE); - - ret = fu_uefi_pcrs_setup (pcrs, &error); - g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); - g_assert_false (ret); -} - static void fu_uefi_ucs2_func (void) { @@ -330,8 +279,6 @@ main (int argc, char **argv) /* tests go here */ g_test_add_func ("/uefi/pcrs1.2", fu_uefi_pcrs_1_2_func); - g_test_add_func ("/uefi/pcrs2.0", fu_uefi_pcrs_2_0_func); - g_test_add_func ("/uefi/pcrs2.0{failure}", fu_uefi_pcrs_2_0_failure_func); g_test_add_func ("/uefi/ucs2", fu_uefi_ucs2_func); g_test_add_func ("/uefi/variable", fu_uefi_vars_func); g_test_add_func ("/uefi/bgrt", fu_uefi_bgrt_func); diff --git a/plugins/uefi/fu-uefi-pcrs.c b/plugins/uefi/fu-uefi-pcrs.c index 4c0813d3a..b1dc28d6b 100644 --- a/plugins/uefi/fu-uefi-pcrs.c +++ b/plugins/uefi/fu-uefi-pcrs.c @@ -6,6 +6,8 @@ #include "config.h" +#include + #include "fu-common.h" #include "fu-uefi-pcrs.h" @@ -21,6 +23,12 @@ struct _FuUefiPcrs { G_DEFINE_TYPE (FuUefiPcrs, fu_uefi_pcrs, G_TYPE_OBJECT) +static void Esys_Finalize_autoptr_cleanup (ESYS_CONTEXT *esys_context) +{ + Esys_Finalize (&esys_context); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC (ESYS_CONTEXT, Esys_Finalize_autoptr_cleanup) + static gboolean _g_string_isxdigit (GString *str) { @@ -61,7 +69,6 @@ fu_uefi_pcrs_parse_line (const gchar *line, gpointer user_data) /* parse hash */ str = g_string_new (split[1]); fu_common_string_replace (str, " ", ""); - fu_common_string_replace (str, "0x", ""); if ((str->len != 40 && str->len != 64) || !_g_string_isxdigit (str)) { g_debug ("not SHA-1 or SHA-256, skipping: %s", split[1]); return; @@ -74,15 +81,6 @@ fu_uefi_pcrs_parse_line (const gchar *line, gpointer user_data) g_debug ("added PCR-%02u=%s", item->idx, item->checksum); } -static gboolean -fu_uefi_pcrs_setup_dummy (FuUefiPcrs *self, const gchar *test_yaml, GError **error) -{ - g_auto(GStrv) lines = g_strsplit (test_yaml, "\n", -1); - for (guint i = 0; lines[i] != NULL; i++) - fu_uefi_pcrs_parse_line (lines[i], self); - return TRUE; -} - static gboolean fu_uefi_pcrs_setup_tpm12 (FuUefiPcrs *self, const gchar *fn_pcrs, GError **error) { @@ -103,10 +101,77 @@ fu_uefi_pcrs_setup_tpm12 (FuUefiPcrs *self, const gchar *fn_pcrs, GError **error } static gboolean -fu_uefi_pcrs_setup_tpm20 (FuUefiPcrs *self, const gchar *argv0, GError **error) +fu_uefi_pcrs_setup_tpm20 (FuUefiPcrs *self, GError **error) { - const gchar *argv[] = { argv0, NULL }; - return fu_common_spawn_sync (argv, fu_uefi_pcrs_parse_line, self, 1500, NULL, error); + TSS2_RC rc; + g_autoptr(ESYS_CONTEXT) ctx = NULL; + g_autofree TPMS_CAPABILITY_DATA *capability_data = NULL; + TPML_PCR_SELECTION pcr_selection_in = { 0, }; + g_autofree TPML_DIGEST *pcr_values = NULL; + + /* suppress warning messages about missing TCTI libraries for tpm2-tss <2.3 */ + if (g_getenv ("FWUPD_UEFI_VERBOSE") == NULL) { + g_setenv ("TSS2_LOG", "esys+error", FALSE); + } + + rc = Esys_Initialize (&ctx, NULL, NULL); + if (rc != TSS2_RC_SUCCESS) { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "failed to initialize TPM library"); + return FALSE; + } + rc = Esys_Startup (ctx, TPM2_SU_CLEAR); + if (rc != TSS2_RC_SUCCESS) { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "failed to initialize TPM"); + return FALSE; + } + + /* get hash algorithms supported by the TPM */ + rc = Esys_GetCapability (ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + TPM2_CAP_PCRS, 0, 1, NULL, &capability_data); + if (rc != TSS2_RC_SUCCESS) { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "failed to get hash algorithms supported by TPM"); + return FALSE; + } + + /* fetch PCR 0 for every supported hash algorithm */ + pcr_selection_in.count = capability_data->data.assignedPCR.count; + for (guint i = 0; i < pcr_selection_in.count; i++) { + pcr_selection_in.pcrSelections[i].hash = + capability_data->data.assignedPCR.pcrSelections[i].hash; + pcr_selection_in.pcrSelections[i].sizeofSelect = + capability_data->data.assignedPCR.pcrSelections[i].sizeofSelect; + pcr_selection_in.pcrSelections[i].pcrSelect[0] = 0b00000001; + } + + rc = Esys_PCR_Read (ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + &pcr_selection_in, NULL, NULL, &pcr_values); + if (rc != TSS2_RC_SUCCESS) { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "failed to read PCR values from TPM"); + return FALSE; + } + + for (guint i = 0; i < pcr_values->count; i++) { + FuUefiPcrItem *item; + g_autoptr(GString) str = NULL; + + str = g_string_new (NULL); + for (guint j = 0; j < pcr_values->digests[i].size; j++) { + g_string_append_printf (str, "%02x", pcr_values->digests[i].buffer[j]); + } + + item = g_new0 (FuUefiPcrItem, 1); + item->idx = 0; /* constant PCR index 0, since we only read this single PCR */ + item->checksum = g_string_free (g_steal_pointer (&str), FALSE); + g_ptr_array_add (self->items, item); + g_debug ("added PCR-%02u=%s", item->idx, item->checksum); + } + + /* success */ + return TRUE; } gboolean @@ -115,7 +180,6 @@ fu_uefi_pcrs_setup (FuUefiPcrs *self, GError **error) g_autofree gchar *devpath = NULL; g_autofree gchar *sysfstpmdir = NULL; g_autofree gchar *fn_pcrs = NULL; - const gchar *test_yaml = g_getenv ("FWUPD_UEFI_TPM2_YAML_DATA"); g_return_val_if_fail (FU_IS_UEFI_PCRS (self), FALSE); @@ -131,31 +195,14 @@ fu_uefi_pcrs_setup (FuUefiPcrs *self, GError **error) } fn_pcrs = g_build_filename (devpath, "pcrs", NULL); - /* fake device */ - if (test_yaml != NULL) { - if (!fu_uefi_pcrs_setup_dummy (self, test_yaml, error)) - return FALSE; - /* look for TPM 1.2 */ - } else if (g_file_test (fn_pcrs, G_FILE_TEST_EXISTS)) { + if (g_file_test (fn_pcrs, G_FILE_TEST_EXISTS)) { if (!fu_uefi_pcrs_setup_tpm12 (self, fn_pcrs, error)) return FALSE; /* assume TPM 2.0 */ } else { - g_autofree gchar *argv0 = NULL; - - /* tpm2-tools 2 tool name */ - argv0 = fu_common_find_program_in_path ("tpm2_listpcrs", NULL); - if (argv0 == NULL) - /* tpm2-tools 3 tool name */ - argv0 = fu_common_find_program_in_path ("tpm2_pcrlist", error); - if (argv0 == NULL) - /* tpm2-tools 4 tool name */ - argv0 = fu_common_find_program_in_path ("tpm2_pcrread", error); - if (argv0 == NULL) - return FALSE; - if (!fu_uefi_pcrs_setup_tpm20 (self, argv0, error)) + if (!fu_uefi_pcrs_setup_tpm20 (self, error)) return FALSE; } diff --git a/plugins/uefi/meson.build b/plugins/uefi/meson.build index b78e3cfc1..e3d394318 100644 --- a/plugins/uefi/meson.build +++ b/plugins/uefi/meson.build @@ -40,6 +40,7 @@ shared_module('fu_plugin_uefi', plugin_deps, efivar, efiboot, + tpm2tss, ], ) @@ -71,6 +72,7 @@ executable( gudev, efivar, efiboot, + tpm2tss, ], link_with : [ libfwupdprivate, @@ -111,6 +113,7 @@ if get_option('tests') plugin_deps, efivar, efiboot, + tpm2tss, ], link_with : [ libfwupdprivate, diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 047383101..56dfa9d14 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -66,6 +66,18 @@ parts: - -share/ - -etc/ - -lib/*.a + tpm2-tss: + plugin: autotools + source: https://github.com/tpm2-software/tpm2-tss/releases/download/2.3.0/tpm2-tss-2.3.0.tar.gz + configflags: + - --disable-doxygen-doc + prime: + - -include + - -bin + - -share/man + - -lib/pkgconfig + build-packages: + - libssl-dev meson: plugin: python source: https://github.com/mesonbuild/meson/releases/download/0.47.2/meson-0.47.2.tar.gz @@ -324,7 +336,7 @@ parts: - -usr/share/gir-1.0 - -usr/share/upstart - -usr/lib/*/pkgconfig - after: [gudev, gusb, gnu-efi, libefivar-fixpkgconfig, libsmbios, build-introspection, gettext, modemmanager, libmbim, libqmi] + after: [gudev, gusb, gnu-efi, libefivar-fixpkgconfig, libsmbios, build-introspection, gettext, modemmanager, libmbim, libqmi, tpm2-tss] fix-bash-completion: plugin: make source: contrib/snap/fix-bash-completion From 5a7575d9478165a615194a0cbb1f8eedc5d9d84a Mon Sep 17 00:00:00 2001 From: Jonas Witschel Date: Mon, 26 Aug 2019 19:42:07 +0200 Subject: [PATCH 065/158] uefi: add test for TPM 2.0 PCR read function The test is run if a physical TPM is available or if the environment variable "TPM_SERVER_RUNNING" is set. In the latter case, the user is expected to start a TPM simulator on their own, like we do in the Arch Linux CI script here. --- contrib/ci/arch.sh | 6 ++++++ plugins/uefi/fu-self-test.c | 37 ++++++++++++++++++++++++++++++++++++- plugins/uefi/fu-uefi-pcrs.c | 3 ++- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/contrib/ci/arch.sh b/contrib/ci/arch.sh index 964e5226f..49573f91a 100755 --- a/contrib/ci/arch.sh +++ b/contrib/ci/arch.sh @@ -16,6 +16,12 @@ cp -R ../../../!(build|dist) . popd chown nobody . -R +# install and run TPM simulator necessary for plugins/uefi/uefi-self-test +pacman -S --noconfirm ibm-sw-tpm2 +tpm_server & +trap "kill $!" EXIT +export TPM_SERVER_RUNNING=1 + # build the package and install it sudo -E -u nobody makepkg -e --noconfirm pacman -U --noconfirm *.pkg.tar.xz diff --git a/plugins/uefi/fu-self-test.c b/plugins/uefi/fu-self-test.c index 32a6cbe20..f13f12592 100644 --- a/plugins/uefi/fu-self-test.c +++ b/plugins/uefi/fu-self-test.c @@ -25,6 +25,8 @@ fu_uefi_pcrs_1_2_func (void) g_autoptr(GPtrArray) pcr0s = NULL; g_autoptr(GPtrArray) pcrXs = NULL; + g_setenv ("FWUPD_SYSFSTPMDIR", TESTDATADIR, TRUE); + ret = fu_uefi_pcrs_setup (pcrs, &error); g_assert_no_error (error); g_assert_true (ret); @@ -34,6 +36,39 @@ fu_uefi_pcrs_1_2_func (void) pcrXs = fu_uefi_pcrs_get_checksums (pcrs, 999); g_assert_nonnull (pcrXs); g_assert_cmpint (pcrXs->len, ==, 0); + + g_unsetenv ("FWUPD_SYSFSTPMDIR"); +} + +static void +fu_uefi_pcrs_2_0_func (void) +{ + g_autofree gchar *devpath = NULL; + g_autofree gchar *sysfstpmdir = NULL; + const gchar *tpm_server_running = g_getenv ("TPM_SERVER_RUNNING"); + gboolean ret; + g_autoptr(FuUefiPcrs) pcrs = fu_uefi_pcrs_new (); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) pcr0s = NULL; + g_autoptr(GPtrArray) pcrXs = NULL; + + sysfstpmdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_TPM); + devpath = g_build_filename (sysfstpmdir, "tpm0", NULL); + + if (!g_file_test (devpath, G_FILE_TEST_EXISTS) && (tpm_server_running == NULL)) { + g_test_skip ("no physical or simulated TPM 2.0 device available"); + return; + } + + ret = fu_uefi_pcrs_setup (pcrs, &error); + g_assert_no_error (error); + g_assert_true (ret); + pcr0s = fu_uefi_pcrs_get_checksums (pcrs, 0); + g_assert_nonnull (pcr0s); + g_assert_cmpint (pcr0s->len, >=, 2); + pcrXs = fu_uefi_pcrs_get_checksums (pcrs, 999); + g_assert_nonnull (pcrXs); + g_assert_cmpint (pcrXs->len, ==, 0); } static void @@ -271,7 +306,6 @@ main (int argc, char **argv) g_test_init (&argc, &argv, NULL); g_setenv ("FWUPD_SYSFSFWDIR", TESTDATADIR, TRUE); g_setenv ("FWUPD_SYSFSDRIVERDIR", TESTDATADIR, TRUE); - g_setenv ("FWUPD_SYSFSTPMDIR", TESTDATADIR, TRUE); /* only critical and error are fatal */ g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); @@ -279,6 +313,7 @@ main (int argc, char **argv) /* tests go here */ g_test_add_func ("/uefi/pcrs1.2", fu_uefi_pcrs_1_2_func); + g_test_add_func ("/uefi/pcrs2.0", fu_uefi_pcrs_2_0_func); g_test_add_func ("/uefi/ucs2", fu_uefi_ucs2_func); g_test_add_func ("/uefi/variable", fu_uefi_vars_func); g_test_add_func ("/uefi/bgrt", fu_uefi_bgrt_func); diff --git a/plugins/uefi/fu-uefi-pcrs.c b/plugins/uefi/fu-uefi-pcrs.c index b1dc28d6b..bb99d3554 100644 --- a/plugins/uefi/fu-uefi-pcrs.c +++ b/plugins/uefi/fu-uefi-pcrs.c @@ -180,13 +180,14 @@ fu_uefi_pcrs_setup (FuUefiPcrs *self, GError **error) g_autofree gchar *devpath = NULL; g_autofree gchar *sysfstpmdir = NULL; g_autofree gchar *fn_pcrs = NULL; + const gchar *tpm_server_running = g_getenv ("TPM_SERVER_RUNNING"); g_return_val_if_fail (FU_IS_UEFI_PCRS (self), FALSE); /* check the TPM device exists at all */ sysfstpmdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_TPM); devpath = g_build_filename (sysfstpmdir, "tpm0", NULL); - if (!g_file_test (devpath, G_FILE_TEST_EXISTS)) { + if (!g_file_test (devpath, G_FILE_TEST_EXISTS) && (tpm_server_running == NULL)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, From 7fac3788bfe95d5e4070ed3d33fc520b2056227b Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 27 Aug 2019 12:52:46 +0100 Subject: [PATCH 066/158] trivial: Update BRs for the example Fedora package --- contrib/fwupd.spec.in | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index 91c84d59b..a1aa09c4f 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -86,6 +86,7 @@ BuildRequires: freetype BuildRequires: fontconfig BuildRequires: google-noto-sans-cjk-ttc-fonts BuildRequires: gnu-efi-devel +BuildRequires: tpm2-tss-devel BuildRequires: pesign %endif From 2b2605fb584d80136e11cf37c06e1dd01791848b Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 27 Aug 2019 12:05:58 +0100 Subject: [PATCH 067/158] trivial: Do not warn about deprecated declarations We need to silence the warning about GTimeVal, as the type is part of the ABI of libgcab for better or worse. --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 6f242b5e9..14c901146 100644 --- a/meson.build +++ b/meson.build @@ -79,6 +79,7 @@ warning_flags = [ '-Wno-cast-function-type', '-Wno-address-of-packed-member', # incompatible with g_autoptr() '-Wno-unknown-pragmas', + '-Wno-deprecated-declarations', '-Wno-discarded-qualifiers', '-Wno-missing-field-initializers', '-Wno-strict-aliasing', From e13f7a142ee06c24647e842e7a6bc5167a3b01bd Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 27 Aug 2019 09:28:30 -0500 Subject: [PATCH 068/158] trivial: dell-dock: correct an error preventing write_size from being set This fixes 7afd7cba0d327b155b2201c9fbb5414ced9cfe06 which led to write_size being set to 0 and an endless loop. --- plugins/dell-dock/fu-dell-dock-i2c-ec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/dell-dock/fu-dell-dock-i2c-ec.c b/plugins/dell-dock/fu-dell-dock-i2c-ec.c index f8e6a3aed..27f18f7ea 100644 --- a/plugins/dell-dock/fu-dell-dock-i2c-ec.c +++ b/plugins/dell-dock/fu-dell-dock-i2c-ec.c @@ -740,8 +740,7 @@ fu_dell_dock_ec_write_fw (FuDevice *device, guint8 progress1 = 0, progress0 = 0; gsize fw_size = 0; const guint8 *data; - gsize write_size = - (fw_size / HIDI2C_MAX_WRITE) >= 1 ? HIDI2C_MAX_WRITE : fw_size; + gsize write_size = 0; gsize nwritten = 0; guint32 address = 0 | 0xff << 24; g_autofree gchar *dynamic_version = NULL; @@ -755,6 +754,7 @@ fu_dell_dock_ec_write_fw (FuDevice *device, if (fw == NULL) return FALSE; data = g_bytes_get_data (fw, &fw_size); + write_size = (fw_size / HIDI2C_MAX_WRITE) >= 1 ? HIDI2C_MAX_WRITE : fw_size; dynamic_version = g_strndup ((gchar *) data + self->blob_version_offset, 11); g_debug ("writing EC firmware version %s", dynamic_version); From 847cae824ae9e27c74f640f17c502b8dac1c385e Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 27 Aug 2019 11:22:23 +0100 Subject: [PATCH 069/158] trivial: Fix fu_common_string_append_kv() to align properly with idx > 0 --- src/fu-common.c | 9 ++++++--- src/fu-self-test.c | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/fu-common.c b/src/fu-common.c index 684a21185..66eb10b36 100644 --- a/src/fu-common.c +++ b/src/fu-common.c @@ -1126,7 +1126,8 @@ fu_common_string_replace (GString *string, const gchar *search, const gchar *rep void fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value) { - const guint align = 23; + const guint align = 25; + gsize keysz; g_return_if_fail (idt * 2 < align); @@ -1137,10 +1138,12 @@ fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gch g_string_append (str, " "); if (key[0] != '\0') { g_string_append_printf (str, "%s:", key); - idt++; + keysz = (idt * 2) + strlen (key) + 1; + } else { + keysz = idt * 2; } if (value != NULL) { - for (gsize i = strlen (key) + idt; i < align; i++) + for (gsize i = keysz; i < align; i++) g_string_append (str, " "); g_string_append (str, value); } diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 56bae55db..b194849df 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -155,6 +155,25 @@ fu_archive_cab_func (void) g_assert_null (data_tmp); } +static void +fu_common_string_append_kv_func (void) +{ + g_autoptr(GString) str = g_string_new (NULL); + fu_common_string_append_kv (str, 0, "hdr", NULL); + fu_common_string_append_kv (str, 0, "key", "value"); + fu_common_string_append_kv (str, 0, "key1", "value1"); + fu_common_string_append_kv (str, 1, "key2", "value2"); + fu_common_string_append_kv (str, 1, "", "value2"); + fu_common_string_append_kv (str, 2, "key3", "value3"); + g_assert_cmpstr (str->str, ==, + "hdr:\n" + "key: value\n" + "key1: value1\n" + " key2: value2\n" + " value2\n" + " key3: value3\n"); +} + static void fu_common_version_guess_format_func (void) { @@ -4062,6 +4081,7 @@ main (int argc, char **argv) g_test_add_func ("/fwupd/keyring{pkcs7-self-signed}", fu_keyring_pkcs7_self_signed_func); g_test_add_func ("/fwupd/plugin{build-hash}", fu_plugin_hash_func); g_test_add_func ("/fwupd/chunk", fu_chunk_func); + g_test_add_func ("/fwupd/common{string-append-kv}", fu_common_string_append_kv_func); g_test_add_func ("/fwupd/common{version-guess-format}", fu_common_version_guess_format_func); g_test_add_func ("/fwupd/common{version}", fu_common_version_func); g_test_add_func ("/fwupd/common{vercmp}", fu_common_vercmp_func); From c67b4e726a3efbd1434f735bb93107628468ef2b Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 27 Aug 2019 09:48:42 +0100 Subject: [PATCH 070/158] Make get-topology more useful by showing all information --- src/fu-util-common.c | 63 +++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/src/fu-util-common.c b/src/fu-util-common.c index c728b01a0..b08a92021 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -140,37 +140,58 @@ gboolean fu_util_print_device_tree (GNode *n, gpointer data) { FwupdDevice *dev = FWUPD_DEVICE (n->data); - const gchar *name; - g_autoptr(GString) str = g_string_new (NULL); + guint idx = g_node_depth (n) - 1; + g_autofree gchar *tmp = NULL; + g_auto(GStrv) split = NULL; /* root node */ if (dev == NULL) { g_print ("○\n"); return FALSE; } + if (n->parent == NULL) + return FALSE; - /* add previous branches */ - for (GNode *c = n->parent; c->parent != NULL; c = c->parent) { - if (g_node_next_sibling (c) == NULL) + /* get split lines */ + tmp = fu_util_device_to_string (dev, idx); + split = g_strsplit (tmp, "\n", -1); + for (guint i = 0; split[i] != NULL; i++) { + g_autoptr(GString) str = g_string_new (NULL); + + /* device header */ + if (i == 0) { + if (g_node_next_sibling (n) == NULL) + g_string_prepend (str, "└─"); + else + g_string_prepend (str, "├─"); + + /* device properties */ + } else { + g_string_prepend (str, n->children == NULL ? " " : " │"); + g_string_prepend (str, g_node_next_sibling (n) == NULL ? " " : "│"); + g_string_append (str, " "); + } + + /* ancestors */ + for (GNode *c = n->parent; c->parent != NULL; c = c->parent) { + if (g_node_next_sibling (c) != NULL || idx == 0) { + g_string_prepend (str, "│ "); + continue; + } g_string_prepend (str, " "); - else - g_string_prepend (str, "│ "); + } + + /* empty line */ + if (split[i][0] == '\0') { + g_print ("%s\n", str->str); + continue; + } + + /* dump to the console */ + g_string_append (str, split[i] + (idx * 2)); + g_print ("%s\n", str->str); } - /* add this branch */ - if (g_node_last_sibling (n) == n) - g_string_append (str, "└─ "); - else - g_string_append (str, "├─ "); - - /* dump to the console */ - name = fwupd_device_get_name (dev); - if (name == NULL) - name = "Unknown device"; - g_string_append (str, name); - for (guint i = strlen (name) + 2 * g_node_depth (n); i < 45; i++) - g_string_append_c (str, ' '); - g_print ("%s %s\n", str->str, fu_device_get_id (dev)); return FALSE; } From 6ff8a163909890dbb037adb4132d02f6d22cf23b Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 23 Aug 2019 12:55:10 -0500 Subject: [PATCH 071/158] trivial: Adjust daemon device sorting This should output devices by priority followed by name instead now. --- src/fu-engine.c | 10 ++++++++-- src/fu-self-test.c | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/fu-engine.c b/src/fu-engine.c index d8dc4b15a..1ab2816a0 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -2739,17 +2739,23 @@ fu_engine_get_details (FuEngine *self, gint fd, GError **error) } static gint -fu_engine_sort_devices_by_priority (gconstpointer a, gconstpointer b) +fu_engine_sort_devices_by_priority_name (gconstpointer a, gconstpointer b) { FuDevice *dev_a = *((FuDevice **) a); FuDevice *dev_b = *((FuDevice **) b); gint prio_a = fu_device_get_priority (dev_a); gint prio_b = fu_device_get_priority (dev_b); + const gchar *name_a = fu_device_get_name (dev_a); + const gchar *name_b = fu_device_get_name (dev_b); if (prio_a > prio_b) return -1; if (prio_a < prio_b) return 1; + if (g_strcmp0 (name_a, name_b) > 0) + return 1; + if (g_strcmp0 (name_a, name_b) < 0) + return -1; return 0; } @@ -2778,7 +2784,7 @@ fu_engine_get_devices (FuEngine *self, GError **error) "No detected devices"); return NULL; } - g_ptr_array_sort (devices, fu_engine_sort_devices_by_priority); + g_ptr_array_sort (devices, fu_engine_sort_devices_by_priority_name); return g_steal_pointer (&devices); } diff --git a/src/fu-self-test.c b/src/fu-self-test.c index b194849df..b749271fc 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -567,10 +567,13 @@ fu_engine_requirements_other_device_func (void) static void fu_engine_device_priority_func (void) { + FuDevice *device; g_autoptr(FuDevice) device1 = fu_device_new (); g_autoptr(FuDevice) device2 = fu_device_new (); g_autoptr(FuDevice) device3 = fu_device_new (); - g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDevice) device4 = fu_device_new (); + g_autoptr(FuDevice) device5 = fu_device_new (); + g_autoptr(GPtrArray) devices = NULL; g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); g_autoptr(GError) error = NULL; g_autoptr(XbSilo) silo_empty = xb_silo_new (); @@ -589,6 +592,7 @@ fu_engine_device_priority_func (void) fu_device_set_priority (device2, 1); fu_device_set_plugin (device2, "redfish"); fu_device_add_instance_id (device2, "GUID1"); + fu_device_set_name (device2, "123"); fu_device_convert_instance_ids (device2); fu_engine_add_device (engine, device2); fu_device_set_id (device3, "id3"); @@ -614,6 +618,37 @@ fu_engine_device_priority_func (void) device = fu_engine_get_device (engine, "c48feddbbcfee514f530ce8f7f2dccd98b6cc150", &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); g_assert_null (device); + g_clear_error (&error); + + /* add extra devices that should sort */ + fu_device_set_id (device4, "id4"); + fu_device_set_priority (device4, 0); + fu_device_set_plugin (device4, "redfish"); + fu_device_add_instance_id (device4, "GUID4"); + fu_device_set_name (device4, "BCD"); + fu_device_convert_instance_ids (device4); + fu_engine_add_device (engine, device4); + fu_device_set_id (device5, "id5"); + fu_device_set_priority (device5, 0); + fu_device_set_plugin (device5, "uefi"); + fu_device_add_instance_id (device5, "GUID5"); + fu_device_set_name (device5, "ABC"); + fu_device_convert_instance_ids (device5); + fu_engine_add_device (engine, device5); + + /* make sure the devices are all sorted properly */ + devices = fu_engine_get_devices (engine, &error); + g_assert_no_error (error); + g_assert_nonnull (devices); + g_assert_cmpint (devices->len, ==, 3); + + /* first should be top priority device */ + device = g_ptr_array_index (devices, 0); + g_assert_cmpstr (fu_device_get_name (device), ==, "123"); + device = g_ptr_array_index (devices, 1); + g_assert_cmpstr (fu_device_get_name (device), ==, "ABC"); + device = g_ptr_array_index (devices, 2); + g_assert_cmpstr (fu_device_get_name (device), ==, "BCD"); } static void From 1a9127d676ba41ff123473527f73360751cd7649 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 27 Aug 2019 11:32:51 +0100 Subject: [PATCH 072/158] Make get-devices and get-topology aliases for one another Since get-topology can now show all the details while in a topology, just make both commands point to the same code. --- src/fu-tool.c | 48 ++++-------------------------------------------- src/fu-util.c | 43 ++----------------------------------------- 2 files changed, 6 insertions(+), 85 deletions(-) diff --git a/src/fu-tool.c b/src/fu-tool.c index 9ff07b240..8ea0bada8 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -389,41 +389,6 @@ fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } -static gboolean -fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) -{ - g_autoptr(GPtrArray) devs = NULL; - - /* load engine */ - if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) - return FALSE; - - /* print */ - devs = fu_engine_get_devices (priv->engine, error); - if (devs == NULL) - return FALSE; - if (devs->len == 0) { - /* TRANSLATORS: nothing attached */ - g_print ("%s\n", _("No hardware detected with firmware update capability")); - return TRUE; - } - for (guint i = 0; i < devs->len; i++) { - FwupdDevice *dev = g_ptr_array_index (devs, i); - if (!fu_util_filter_device (priv, dev)) - continue; - if (priv->show_all_devices || fu_util_is_interesting_device (dev)) { - g_autofree gchar *tmp = fu_util_device_to_string (dev, 0); - g_print ("%s\n", tmp); - } - } - - /* save the device state for other applications to see */ - if (!fu_util_save_current_state (priv, error)) - return FALSE; - - return TRUE; -} - static gboolean fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -462,7 +427,7 @@ fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, Fu } static gboolean -fu_util_get_topology (FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GNode) root = g_node_new (NULL); g_autoptr(GPtrArray) devs = NULL; @@ -486,7 +451,8 @@ fu_util_get_topology (FuUtilPrivate *priv, gchar **values, GError **error) g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, fu_util_print_device_tree, priv); - return TRUE; + /* save the device state for other applications to see */ + return fu_util_save_current_state (priv, error); } static FuDevice * @@ -1500,7 +1466,7 @@ main (int argc, char *argv[]) _("Gets the list of updates for connected hardware"), fu_util_get_updates); fu_util_cmd_array_add (cmd_array, - "get-devices", + "get-devices,get-topology", NULL, /* TRANSLATORS: command description */ _("Get all devices that support firmware updates"), @@ -1511,12 +1477,6 @@ main (int argc, char *argv[]) /* TRANSLATORS: command description */ _("Get all device flags supported by fwupd"), fu_util_get_device_flags); - fu_util_cmd_array_add (cmd_array, - "get-topology", - NULL, - /* TRANSLATORS: command description */ - _("Get all devices according to the system topology"), - fu_util_get_topology); fu_util_cmd_array_add (cmd_array, "watch", NULL, diff --git a/src/fu-util.c b/src/fu-util.c index 8a1aebcc7..1670144a2 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -382,7 +382,7 @@ fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, Fw } static gboolean -fu_util_get_topology (FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GNode) root = g_node_new (NULL); g_autoptr(GPtrArray) devs = NULL; @@ -402,39 +402,6 @@ fu_util_get_topology (FuUtilPrivate *priv, gchar **values, GError **error) g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, fu_util_print_device_tree, priv); - return TRUE; -} - -static gboolean -fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) -{ - g_autoptr(GPtrArray) devs = NULL; - - /* get results from daemon */ - devs = fwupd_client_get_devices (priv->client, NULL, error); - if (devs == NULL) - return FALSE; - - /* print */ - if (devs->len == 0) { - /* TRANSLATORS: nothing attached that can be upgraded */ - g_print ("%s\n", _("No hardware detected with firmware update capability")); - return TRUE; - } - - for (guint i = 0; i < devs->len; i++) { - g_autofree gchar *tmp = NULL; - FwupdDevice *dev = g_ptr_array_index (devs, i); - if (!fu_util_filter_device (priv, dev)) - continue; - if (!priv->show_all_devices) { - if (!fu_util_is_interesting_device (dev)) - continue; - } - tmp = fu_util_device_to_string (dev, 0); - g_print ("%s\n", tmp); - } - /* nag? */ if (!fu_util_perhaps_show_unreported (priv, error)) return FALSE; @@ -2204,17 +2171,11 @@ main (int argc, char *argv[]) /* add commands */ fu_util_cmd_array_add (cmd_array, - "get-devices", + "get-devices,get-topology", NULL, /* TRANSLATORS: command description */ _("Get all devices that support firmware updates"), fu_util_get_devices); - fu_util_cmd_array_add (cmd_array, - "get-topology", - NULL, - /* TRANSLATORS: command description */ - _("Get all devices according to the system topology"), - fu_util_get_topology); fu_util_cmd_array_add (cmd_array, "get-history", NULL, From 17e9bf50563ac7e3f64838fd85b1f9865d223fac Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 27 Aug 2019 09:03:19 -0500 Subject: [PATCH 073/158] trivial: Don't use topology printing in --verbose mode The strings won't be properly indented and characters will be lobbed off. --- src/fu-util-common.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/fu-util-common.c b/src/fu-util-common.c index b08a92021..1a346e012 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -154,6 +154,8 @@ fu_util_print_device_tree (GNode *n, gpointer data) /* get split lines */ tmp = fu_util_device_to_string (dev, idx); + if (tmp == NULL) + return FALSE; split = g_strsplit (tmp, "\n", -1); for (guint i = 0; split[i] != NULL; i++) { g_autoptr(GString) str = g_string_new (NULL); @@ -865,8 +867,8 @@ fu_util_device_to_string (FwupdDevice *dev, guint idt) if (g_getenv ("FWUPD_VERBOSE") != NULL) { g_autofree gchar *debug_str = NULL; debug_str = fwupd_device_to_string (dev); - g_string_append (str, debug_str); - return g_string_free (str, FALSE); + g_debug ("%s", debug_str); + return NULL; } /* all devices have a name */ From 764fc2a6769e1211029fcd221d05a7d756e4e121 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 27 Aug 2019 10:02:59 -0500 Subject: [PATCH 074/158] trivial: fu-util: cleanup fwupdmgr get-details output * Show "No Device ID" if no matching devices * Don't show flags if no flags available Before: ``` Device ID: Description: Updating the MST FW improves display performance. Flags: GUID: 0a52c8c7-26d5-59a0-ae44-6b00e276d775 ``` After: ``` Device ID: No Device ID Description: Updating the MST FW improves display performance. GUID: 0a52c8c7-26d5-59a0-ae44-6b00e276d775 ``` --- src/fu-util-common.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 1a346e012..192ff64d3 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -871,12 +871,18 @@ fu_util_device_to_string (FwupdDevice *dev, guint idt) return NULL; } - /* all devices have a name */ - fu_common_string_append_kv (str, idt, fwupd_device_get_name (dev), NULL); + tmp = fwupd_device_get_name (dev); + if (tmp == NULL) { + /* TRANSLATORS: Name of hardware */ + tmp = _("Unknown Device"); + } + fu_common_string_append_kv (str, idt, tmp, NULL); - /* TRANSLATORS: ID for hardware, typically a SHA1 sum */ - fu_common_string_append_kv (str, idt + 1, _("Device ID"), - fwupd_device_get_id (dev)); + tmp = fwupd_device_get_id (dev); + if (tmp != NULL) { + /* TRANSLATORS: ID for hardware, typically a SHA1 sum */ + fu_common_string_append_kv (str, idt + 1, _("Device ID"), tmp); + } /* summary */ tmp = fwupd_device_get_summary (dev); @@ -964,11 +970,7 @@ fu_util_device_to_string (FwupdDevice *dev, guint idt) g_string_append_printf (flags_str, "%s|", fwupd_device_flag_to_string ((guint64) 1 << i)); } - if (str->len == 0) { - /* TRANSLATORS: device properties */ - fu_common_string_append_kv (flags_str, idt + 1, _("Flags"), - fwupd_device_flag_to_string (0)); - } else { + if (flags_str->len > 0) { g_string_truncate (flags_str, flags_str->len - 1); /* TRANSLATORS: device properties */ fu_common_string_append_kv (str, idt + 1, _("Flags"), flags_str->str); From 0887b675c3d4044d3f8141926c87da34932d1a17 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 27 Aug 2019 10:16:18 -0500 Subject: [PATCH 075/158] trivial: fu-util-common: more cleanup related to topology * Set the log domain for verbose messages * Don't show root node in verbose messages --- src/fu-util-common.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 192ff64d3..5a94a715f 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -4,6 +4,8 @@ * SPDX-License-Identifier: LGPL-2.1+ */ +#define G_LOG_DOMAIN "FuMain" + #include #include @@ -145,7 +147,7 @@ fu_util_print_device_tree (GNode *n, gpointer data) g_auto(GStrv) split = NULL; /* root node */ - if (dev == NULL) { + if (dev == NULL && g_getenv ("FWUPD_VERBOSE") == NULL) { g_print ("○\n"); return FALSE; } From 758f3b5abd162e5cff3d1797cb0cf8b099be10a5 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 27 Aug 2019 20:03:53 +0100 Subject: [PATCH 076/158] trivial: Fix running the tests on devices with the TPM in v1.2 mode --- plugins/uefi/fu-self-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/uefi/fu-self-test.c b/plugins/uefi/fu-self-test.c index f13f12592..4ebd50c7f 100644 --- a/plugins/uefi/fu-self-test.c +++ b/plugins/uefi/fu-self-test.c @@ -65,7 +65,7 @@ fu_uefi_pcrs_2_0_func (void) g_assert_true (ret); pcr0s = fu_uefi_pcrs_get_checksums (pcrs, 0); g_assert_nonnull (pcr0s); - g_assert_cmpint (pcr0s->len, >=, 2); + g_assert_cmpint (pcr0s->len, >=, 1); pcrXs = fu_uefi_pcrs_get_checksums (pcrs, 999); g_assert_nonnull (pcrXs); g_assert_cmpint (pcrXs->len, ==, 0); From da55c48b7d9e45cbb6c8e33bcef91ac4b1f4df00 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 27 Aug 2019 15:38:07 -0500 Subject: [PATCH 077/158] Remove replug flag after the device comes back from reboot This prevents errors like this from happening: ``` failed to get device after update: failed to wait for detach replug: device did not come back ``` --- src/fu-device-list.c | 1 + src/fu-self-test.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/fu-device-list.c b/src/fu-device-list.c index 6a0583c06..c37c7457a 100644 --- a/src/fu-device-list.c +++ b/src/fu-device-list.c @@ -702,6 +702,7 @@ fu_device_list_wait_for_replug (FuDeviceList *self, FuDevice *device, GError **e if (item->replug_id != 0) { g_debug ("waited for replug"); g_source_remove (item->replug_id); + fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); item->replug_id = 0; return TRUE; } diff --git a/src/fu-self-test.c b/src/fu-self-test.c index b749271fc..618844c31 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -1585,6 +1585,7 @@ fu_device_list_replug_auto_func (void) ret = fu_device_list_wait_for_replug (device_list, device1, &error); g_assert_no_error (error); g_assert (ret); + g_assert_false (fu_device_has_flag (device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)); /* check device2 now has parent too */ g_assert (fu_device_get_parent (device2) == parent); @@ -1594,6 +1595,7 @@ fu_device_list_replug_auto_func (void) ret = fu_device_list_wait_for_replug (device_list, device2, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); g_assert (!ret); + g_assert_true (fu_device_has_flag (device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)); } static void @@ -1643,6 +1645,7 @@ fu_device_list_replug_user_func (void) ret = fu_device_list_wait_for_replug (device_list, device1, &error); g_assert_no_error (error); g_assert (ret); + g_assert_false (fu_device_has_flag (device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)); } static void From d350b2ecd7e329e29863d66853141cdf09503e6d Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 26 Aug 2019 09:51:07 +0100 Subject: [PATCH 078/158] synapticsmst: Partially rewrite the plugin (Fixes #1105) This removes the 'two-layer' FuDevice and FuSyanpticsmstDevice model, where a complicated plugin cache was used to add and remove devices to the daemon. By making FuSynapticsmstDevice derive from FuDevice rather than GObject we can also use a lot of the helper functionality like the other plugins, for instance ->prepare_firmware(). The `drm_dp_aux_dev` devices do not emit uevents on unplug/re-plug and so all devices of `drm` class are watched and the actual DP AUX devices rescanned after a small delay. When the AUX devices emit changes from the kernel this workaround can be removed. Also drop force power support for MST controllers in the Dell plugin. Overall this has just led to more problems than it's helped. * Monitor flickers when turned on * Crashing graphics drivers from time to time * Fragile logic that doesn't always represent the device state --- plugins/dell/fu-plugin-dell.c | 120 --- plugins/synapticsmst/README.md | 2 + plugins/synapticsmst/fu-plugin-synapticsmst.c | 537 ++-------- plugins/synapticsmst/fu-self-test.c | 96 +- plugins/synapticsmst/fu-synapticsmst-common.c | 10 - plugins/synapticsmst/fu-synapticsmst-common.h | 5 +- .../synapticsmst/fu-synapticsmst-connection.c | 59 +- plugins/synapticsmst/fu-synapticsmst-device.c | 954 +++++++++--------- plugins/synapticsmst/fu-synapticsmst-device.h | 50 +- plugins/synapticsmst/synapticsmst.quirk | 3 - 10 files changed, 679 insertions(+), 1157 deletions(-) diff --git a/plugins/dell/fu-plugin-dell.c b/plugins/dell/fu-plugin-dell.c index 27c8a2363..0603576c2 100644 --- a/plugins/dell/fu-plugin-dell.c +++ b/plugins/dell/fu-plugin-dell.c @@ -85,9 +85,6 @@ struct da_structure { #define UNIV_CBL_DESC "Universal Cable" #define TBT_CBL_DESC "Thunderbolt Cable" -/* supported host related GUIDs */ -#define MST_GPIO_GUID EFI_GUID (0xF24F9bE4, 0x2a13, 0x4344, 0xBC05, 0x01, 0xCE, 0xF7, 0xDA, 0xEF, 0x92) - /** * Devices that should allow modeswitching */ @@ -117,24 +114,6 @@ static guint8 enclosure_whitelist [] = { 0x03, /* desktop */ 0x21, /* IoT gateway */ 0x22, /* embedded PC */}; -/** - * Systems containing host MST device - */ -static guint16 systems_host_mst [] = { 0x062d, /* Latitude E7250 */ - 0x062e, /* Latitude E7450 */ - 0x062a, /* Latitude E5250 */ - 0x062b, /* Latitude E5450 */ - 0x062c, /* Latitude E5550 */ - 0x06db, /* Latitude E7270 */ - 0x06dc, /* Latitude E7470 */ - 0x06dd, /* Latitude E5270 */ - 0x06de, /* Latitude E5470 */ - 0x06df, /* Latitude E5570 */ - 0x06e0, /* Precision 3510 */ - 0x071d, /* Latitude Rugged 7214 */ - 0x071e, /* Latitude Rugged 5414 */ - 0x071c, /* Latitude Rugged 7414 */}; - static guint16 fu_dell_get_system_id (FuPlugin *plugin) { @@ -152,20 +131,6 @@ fu_dell_get_system_id (FuPlugin *plugin) return system_id; } -static gboolean -fu_dell_host_mst_supported (FuPlugin *plugin) -{ - guint16 system_id; - - system_id = fu_dell_get_system_id (plugin); - if (system_id == 0) - return FALSE; - for (guint i = 0; i < G_N_ELEMENTS (systems_host_mst); i++) - if (systems_host_mst[i] == system_id) - return TRUE; - return FALSE; -} - static gboolean fu_dell_supported (FuPlugin *plugin) { @@ -473,9 +438,6 @@ fu_plugin_usb_device_added (FuPlugin *plugin, return FALSE; } -#if defined (HAVE_SYNAPTICS) - fu_plugin_request_recoldplug (plugin); -#endif return TRUE; } @@ -742,82 +704,6 @@ fu_plugin_device_registered (FuPlugin *plugin, FuDevice *device) } } -static gboolean -fu_dell_toggle_flash (FuPlugin *plugin, FuDevice *device, - gboolean enable, GError **error) -{ - FuPluginData *data = fu_plugin_get_data (plugin); - gboolean has_host = fu_dell_host_mst_supported (plugin); - gboolean has_dock; - guint32 dock_location; - const gchar *tmp; - - if (device) { - if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE)) - return TRUE; - tmp = fu_device_get_plugin (device); - if (g_strcmp0 (tmp, "synapticsmst") != 0) - return TRUE; - g_debug ("preparing/cleaning update for %s", tmp); - } - - /* Dock MST Hub */ - has_dock = fu_dell_detect_dock (data->smi_obj, &dock_location); - if (has_dock) { - if (!fu_dell_toggle_dock_mode (data->smi_obj, enable, - dock_location, error)) - g_debug ("unable to change dock to %d", enable); - else - g_debug ("Toggled dock mode to %d", enable); - } - - /* System MST hub */ - if (has_host) { - if (!fu_dell_toggle_host_mode (data->smi_obj, MST_GPIO_GUID, enable)) - g_debug ("Unable to toggle MST hub GPIO to %d", enable); - else - g_debug ("Toggled MST hub GPIO to %d", enable); - } - -#if defined (HAVE_SYNAPTICS) - /* set a delay to allow OS response to settling the GPIO change */ - if (enable && device == NULL && (has_dock || has_host)) - fu_plugin_set_coldplug_delay (plugin, DELL_FLASH_MODE_DELAY * 1000); -#endif - return TRUE; -} - -gboolean -fu_plugin_update_prepare (FuPlugin *plugin, - FwupdInstallFlags flags, - FuDevice *device, - GError **error) -{ - - return fu_dell_toggle_flash (plugin, device, TRUE, error); -} - -gboolean -fu_plugin_update_cleanup (FuPlugin *plugin, - FwupdInstallFlags flags, - FuDevice *device, - GError **error) -{ - return fu_dell_toggle_flash (plugin, device , FALSE, error); -} - -gboolean -fu_plugin_coldplug_prepare (FuPlugin *plugin, GError **error) -{ - return fu_dell_toggle_flash (plugin, NULL, TRUE, error); -} - -gboolean -fu_plugin_coldplug_cleanup (FuPlugin *plugin, GError **error) -{ - return fu_dell_toggle_flash (plugin, NULL, FALSE, error); -} - void fu_plugin_init (FuPlugin *plugin) { @@ -914,9 +800,3 @@ fu_plugin_coldplug (FuPlugin *plugin, GError **error) { return fu_plugin_dell_coldplug (plugin, error); } - -gboolean -fu_plugin_recoldplug (FuPlugin *plugin, GError **error) -{ - return fu_plugin_dell_coldplug (plugin, error); -} diff --git a/plugins/synapticsmst/README.md b/plugins/synapticsmst/README.md index 1428db78f..bd19fde10 100644 --- a/plugins/synapticsmst/README.md +++ b/plugins/synapticsmst/README.md @@ -19,6 +19,8 @@ GUID Generation These devices use custom GUID values, e.g. * `MST-$(device_kind)-$(chip-ID)-$(board-ID)` + * `MST-$(device_kind)-$(board-ID)` + * `MST-$(device_kind)` Please refer to the plugin source for more details about how the GUID is constructed for specific hardware. diff --git a/plugins/synapticsmst/fu-plugin-synapticsmst.c b/plugins/synapticsmst/fu-plugin-synapticsmst.c index be6b4cf01..7278705d6 100644 --- a/plugins/synapticsmst/fu-plugin-synapticsmst.c +++ b/plugins/synapticsmst/fu-plugin-synapticsmst.c @@ -7,14 +7,20 @@ */ #include "config.h" -#include "fu-synapticsmst-device.h" -#include "fu-synapticsmst-common.h" + #include "fu-plugin-vfuncs.h" -#include "fu-device-metadata.h" -#define SYNAPTICS_FLASH_MODE_DELAY 3 -#define SYNAPTICS_UPDATE_ENUMERATE_TRIES 3 +#include "fu-synapticsmst-common.h" +#include "fu-synapticsmst-device.h" +#define FU_SYNAPTICSMST_DRM_REPLUG_DELAY 5 /* s */ + +struct FuPluginData { + GPtrArray *devices; + guint drm_changed_id; +}; + +/* see https://github.com/hughsie/fwupd/issues/1121 for more details */ static gboolean fu_synapticsmst_check_amdgpu_safe (GError **error) { @@ -39,468 +45,139 @@ fu_synapticsmst_check_amdgpu_safe (GError **error) return TRUE; } -static gboolean -fu_synapticsmst_check_supported_system (FuPlugin *plugin, GError **error) -{ - - if (g_getenv ("FWUPD_SYNAPTICSMST_FW_DIR") != NULL) { - g_debug ("Running Synaptics plugin in test mode"); - return TRUE; - } - - /* See https://github.com/fwupd/fwupd/issues/1121 for more details */ - if (!fu_synapticsmst_check_amdgpu_safe (error)) - return FALSE; - - if (!g_file_test (SYSFS_DRM_DP_AUX, G_FILE_TEST_IS_DIR)) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "MST firmware updating not supported, missing kernel support."); - return FALSE; - } - return TRUE; -} - -/* creates MST-$str-$BOARDID */ static void -fu_plugin_synapticsmst_create_simple_guid (FuDevice *fu_device, - FuSynapticsmstDevice *device, - const gchar *str) +fu_plugin_synapticsmst_device_rescan (FuPlugin *plugin, FuDevice *device) { - guint16 board_id = fu_synapticsmst_device_get_board_id (device); - g_autofree gchar *devid = g_strdup_printf ("MST-%s-%u", str, board_id); - fu_device_add_instance_id (fu_device, devid); -} + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GError) error_local = NULL; -/* creates MST-$str-$chipid-$BOARDID */ -static void -fu_plugin_synapticsmst_create_complex_guid (FuDevice *fu_device, - FuSynapticsmstDevice *device, - const gchar *device_kind) -{ - const gchar *chip_id_str = fu_synapticsmst_device_get_chip_id_str (device); - g_autofree gchar *chip_id_down = g_ascii_strdown (chip_id_str, -1); - g_autofree gchar *tmp = g_strdup_printf ("%s-%s", device_kind, chip_id_down); - - fu_plugin_synapticsmst_create_simple_guid (fu_device, device, tmp); -} - -static gboolean -fu_plugin_synapticsmst_lookup_device (FuPlugin *plugin, - FuDevice *fu_device, - FuSynapticsmstDevice *device, - GError **error) -{ - const gchar *board_str; - const gchar *guid_template; - guint16 board_id = fu_synapticsmst_device_get_board_id (device); - const gchar *chip_id_str = fu_synapticsmst_device_get_chip_id_str (device); - g_autofree gchar *group = NULL; - g_autofree gchar *name = NULL; - - /* GUIDs used only for test mode */ - if (g_getenv ("FWUPD_SYNAPTICSMST_FW_DIR") != NULL) { - g_autofree gchar *tmp = NULL; - tmp = g_strdup_printf ("test-%s", chip_id_str); - fu_plugin_synapticsmst_create_simple_guid (fu_device, device, tmp); - return TRUE; + /* open fd */ + locker = fu_device_locker_new (device, &error_local); + if (locker == NULL) { + g_debug ("failed to open device %s: %s", + fu_device_get_logical_id (device), + error_local->message); + return; } - - /* set up the device name via quirks */ - group = g_strdup_printf ("SynapticsMSTBoardID=%u", board_id); - board_str = fu_plugin_lookup_quirk_by_id (plugin, group, - FU_QUIRKS_NAME); - if (board_str == NULL) - board_str = "Unknown Platform"; - name = g_strdup_printf ("Synaptics %s inside %s", fu_synapticsmst_device_get_chip_id_str (device), - board_str); - fu_device_set_name (fu_device, name); - - /* build the GUIDs for the device */ - guid_template = fu_plugin_lookup_quirk_by_id (plugin, group, "DeviceKind"); - /* no quirks defined for this board */ - if (guid_template == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Unknown board_id %u", - board_id); - return FALSE; - - /* this is a host system, use system ID */ - } else if (g_strcmp0 (guid_template, "system") == 0) { - const gchar *system_type = fu_plugin_get_dmi_value (plugin, - FU_HWIDS_KEY_PRODUCT_SKU); - fu_plugin_synapticsmst_create_simple_guid (fu_device, device, - system_type); - /* docks or something else */ + if (!fu_device_rescan (device, &error_local)) { + g_debug ("no device found on %s: %s", + fu_device_get_logical_id (device), + error_local->message); + if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REGISTERED)) + fu_plugin_device_remove (plugin, device); } else { - g_auto(GStrv) templates = NULL; - templates = g_strsplit (guid_template, ",", -1); - for (guint i = 0; templates[i] != NULL; i++) { - fu_plugin_synapticsmst_create_complex_guid (fu_device, - device, - templates[i]); - } + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_plugin_device_add (plugin, device); } - - return TRUE; -} - -static gboolean -fu_plugin_synaptics_add_device (FuPlugin *plugin, - FuSynapticsmstDevice *device, - GError **error) -{ - g_autoptr(FuDevice) dev = NULL; - const gchar *kind_str = NULL; - g_autofree gchar *dev_id_str = NULL; - g_autofree gchar *layer_str = NULL; - g_autofree gchar *rad_str = NULL; - const gchar *aux_node; - guint8 layer; - guint16 rad; - - aux_node = fu_synapticsmst_device_get_aux_node (device); - if (!fu_synapticsmst_device_enumerate_device (device, - error)) { - g_prefix_error (error, "Error enumerating device at %s: ", aux_node); - return FALSE; - } - - /* create the device */ - dev = fu_device_new (); - /* Store $KIND-$AUXNODE-$LAYER-$RAD as device ID */ - layer = fu_synapticsmst_device_get_layer (device); - rad = fu_synapticsmst_device_get_rad (device); - kind_str = fu_synapticsmst_mode_to_string (fu_synapticsmst_device_get_kind (device)); - dev_id_str = g_strdup_printf ("MST-%s-%s-%u-%u", - kind_str, aux_node, layer, rad); - fu_device_set_id (dev, dev_id_str); - fu_device_set_physical_id (dev, aux_node); - fu_device_set_metadata (dev, "SynapticsMSTKind", kind_str); - fu_device_set_metadata (dev, "SynapticsMSTAuxNode", aux_node); - layer_str = g_strdup_printf ("%u", layer); - fu_device_set_metadata (dev, "SynapticsMSTLayer", layer_str); - rad_str = g_strdup_printf ("%u", rad); - fu_device_set_metadata (dev, "SynapticsMSTRad", rad_str); - fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_set_vendor (dev, "Synaptics"); - fu_device_set_summary (dev, "Multi-Stream Transport Device"); - fu_device_add_icon (dev, "video-display"); - fu_device_set_version (dev, fu_synapticsmst_device_get_version (device), - FWUPD_VERSION_FORMAT_TRIPLET); - fu_device_set_quirks (dev, fu_plugin_get_quirks (plugin)); - - /* create GUIDs and name */ - if (!fu_plugin_synapticsmst_lookup_device (plugin, dev, device, error)) - return FALSE; - if (!fu_device_setup (dev, error)) - return FALSE; - fu_plugin_device_add (plugin, dev); - fu_plugin_cache_add (plugin, dev_id_str, dev); - - /* inhibit the idle sleep of the daemon */ - fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_INHIBITS_IDLE, - "SynapticsMST can cause the screen to flash when probing"); - return TRUE; -} - -static gboolean -fu_plugin_synaptics_scan_cascade (FuPlugin *plugin, - FuSynapticsmstDevice *device, - GError **error) -{ - g_autoptr(FuSynapticsmstDevice) cascade_device = NULL; - FuDevice *fu_dev = NULL; - const gchar *aux_node; - - aux_node = fu_synapticsmst_device_get_aux_node (device); - if (!fu_synapticsmst_device_open (device, error)) { - g_prefix_error (error, - "failed to open aux node %s again", - aux_node); - return FALSE; - } - - for (guint8 j = 0; j < 2; j++) { - guint8 layer = fu_synapticsmst_device_get_layer (device) + 1; - guint16 rad = fu_synapticsmst_device_get_rad (device) | (j << (2 * (layer - 1))); - g_autofree gchar *dev_id_str = NULL; - dev_id_str = g_strdup_printf ("MST-REMOTE-%s-%u-%u", - aux_node, layer, rad); - fu_dev = fu_plugin_cache_lookup (plugin, dev_id_str); - - /* run the scan */ - if (!fu_synapticsmst_device_scan_cascade_device (device, error, j)) - return FALSE; - - /* check if cascaded device was found */ - if (!fu_synapticsmst_device_get_cascade (device)) { - /* not found, nothing new to see here, move along */ - if (fu_dev == NULL) - continue; - /* not found, but should have existed - remove it */ - else { - fu_plugin_device_remove (plugin, fu_dev); - fu_plugin_cache_remove (plugin, dev_id_str); - /* don't scan any deeper on this node */ - continue; - } - /* Found a device, add it */ - } else { - cascade_device = fu_synapticsmst_device_new (FU_SYNAPTICSMST_MODE_REMOTE, - aux_node, layer, rad); - /* new device */ - if (fu_dev == NULL) { - g_debug ("Adding remote device %s", dev_id_str); - if (!fu_plugin_synaptics_add_device (plugin, cascade_device, error)) - return FALSE; - } - else - g_debug ("Skipping previously added device %s", - dev_id_str); - - /* check recursively for more devices */ - if (!fu_plugin_synaptics_scan_cascade (plugin, cascade_device, error)) - return FALSE; - } - } - - return TRUE; } +/* reprobe all existing devices added by this plugin */ static void -fu_plugin_synapticsmst_remove_cascaded (FuPlugin *plugin, const gchar *aux_node) +fu_plugin_synapticsmst_rescan (FuPlugin *plugin) { - FuDevice *fu_dev = NULL; - - for (guint8 i = 0; i < 8; i++) { - for (guint16 j = 0; j < 256; j++) { - g_autofree gchar *dev_id_str = NULL; - dev_id_str = g_strdup_printf ("MST-REMOTE-%s-%u-%u", - aux_node, i, j); - fu_dev = fu_plugin_cache_lookup (plugin, dev_id_str); - if (fu_dev != NULL) { - fu_plugin_device_remove (plugin, fu_dev); - fu_plugin_cache_remove (plugin, dev_id_str); - continue; - } - break; - } + FuPluginData *priv = fu_plugin_get_data (plugin); + for (guint i = 0; i < priv->devices->len; i++) { + FuDevice *device = FU_DEVICE (g_ptr_array_index (priv->devices, i)); + fu_plugin_synapticsmst_device_rescan (plugin, device); } } static gboolean -fu_plugin_synapticsmst_enumerate (FuPlugin *plugin, - GError **error) +fu_plugin_synapticsmst_rescan_cb (gpointer user_data) { - g_autoptr(GDir) dir = NULL; - const gchar *dp_aux_dir; - const gchar *aux_node = NULL; + FuPlugin *plugin = FU_PLUGIN (user_data); + FuPluginData *priv = fu_plugin_get_data (plugin); + fu_plugin_synapticsmst_rescan (plugin); + priv->drm_changed_id = 0; + return FALSE; +} - dp_aux_dir = g_getenv ("FWUPD_SYNAPTICSMST_FW_DIR"); - if (dp_aux_dir == NULL) - dp_aux_dir = SYSFS_DRM_DP_AUX; - else - g_debug ("Using %s to look for MST devices", dp_aux_dir); - dir = g_dir_open (dp_aux_dir, 0, NULL); - do { - g_autofree gchar *dev_id_str = NULL; - g_autoptr(GError) error_local = NULL; - g_autoptr(FuSynapticsmstDevice) device = NULL; - FuDevice *fu_dev = NULL; +gboolean +fu_plugin_udev_device_changed (FuPlugin *plugin, FuUdevDevice *device, GError **error) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); - aux_node = g_dir_read_name (dir); - if (aux_node == NULL) - break; + /* interesting device? */ + if (g_strcmp0 (fu_udev_device_get_subsystem (device), "drm") != 0) + return TRUE; - dev_id_str = g_strdup_printf ("MST-DIRECT-%s-0-0", aux_node); - fu_dev = fu_plugin_cache_lookup (plugin, dev_id_str); - - /* If we open successfully a device exists here */ - device = fu_synapticsmst_device_new (FU_SYNAPTICSMST_MODE_DIRECT, aux_node, 0, 0); - if (!fu_synapticsmst_device_open (device, &error_local)) { - /* No device exists here, but was there - remove from DB */ - if (fu_dev != NULL) { - g_debug ("Removing devices on %s", aux_node); - fu_plugin_device_remove (plugin, fu_dev); - fu_plugin_cache_remove (plugin, dev_id_str); - fu_plugin_synapticsmst_remove_cascaded (plugin, - aux_node); - } else { - /* Nothing to see here - move on*/ - g_debug ("No device found on %s: %s", aux_node, error_local->message); - g_clear_error (&error_local); - } - continue; - } - - /* Add direct devices */ - if (fu_dev == NULL) { - g_debug ("Adding direct device %s", dev_id_str); - if (!fu_plugin_synaptics_add_device (plugin, device, &error_local)) - g_debug ("failed to add device: %s", error_local->message); - } else { - g_debug ("Skipping previously added device %s", dev_id_str); - } - - /* recursively look for cascade devices */ - if (!fu_plugin_synaptics_scan_cascade (plugin, device, error)) - return FALSE; - } while(TRUE); + /* recoldplug all drm_dp_aux_dev devices after a *long* delay */ + if (priv->drm_changed_id != 0) + g_source_remove (priv->drm_changed_id); + priv->drm_changed_id = g_timeout_add_seconds (FU_SYNAPTICSMST_DRM_REPLUG_DELAY, + fu_plugin_synapticsmst_rescan_cb, + plugin); return TRUE; } -static void -fu_synapticsmst_write_progress_cb (goffset current, goffset total, gpointer user_data) +gboolean +fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **error) { - FuDevice *device = FU_DEVICE (user_data); - fu_device_set_progress_full (device, current, total); + FuPluginData *priv = fu_plugin_get_data (plugin); + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuSynapticsmstDevice) dev = NULL; + + /* interesting device? */ + if (g_strcmp0 (fu_udev_device_get_subsystem (device), "drm_dp_aux_dev") != 0) + return TRUE; + + dev = fu_synapticsmst_device_new (device); + locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + + /* for DeviceKind=system devices */ + fu_synapticsmst_device_set_system_type (FU_SYNAPTICSMST_DEVICE (dev), + fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_PRODUCT_SKU)); + + /* this might fail if there is nothing connected */ + fu_plugin_synapticsmst_device_rescan (plugin, FU_DEVICE (dev)); + g_ptr_array_add (priv->devices, g_steal_pointer (&dev)); + return TRUE; +} + +gboolean +fu_plugin_startup (FuPlugin *plugin, GError **error) +{ + return fu_synapticsmst_check_amdgpu_safe (error); } gboolean fu_plugin_update (FuPlugin *plugin, - FuDevice *dev, + FuDevice *device, GBytes *blob_fw, FwupdInstallFlags flags, GError **error) { - g_autoptr(FuSynapticsmstDevice) device = NULL; - FuSynapticsmstMode kind; - const gchar *aux_node; - guint8 layer; - guint8 rad; - gboolean reboot; - gboolean install_force; - - /* extract details to build a new device */ - kind = fu_synapticsmst_mode_from_string (fu_device_get_metadata (dev, "SynapticsMSTKind")); - aux_node = fu_device_get_metadata (dev, "SynapticsMSTAuxNode"); - layer = g_ascii_strtoull (fu_device_get_metadata (dev, "SynapticsMSTLayer"), NULL, 0); - rad = g_ascii_strtoull (fu_device_get_metadata (dev, "SynapticsMSTRad"), NULL, 0); - - - /* sleep to allow device wakeup to complete */ - g_debug ("waiting %d seconds for MST hub wakeup", - SYNAPTICS_FLASH_MODE_DELAY); - fu_device_set_status (dev, FWUPD_STATUS_DEVICE_BUSY); - g_usleep (SYNAPTICS_FLASH_MODE_DELAY * 1000000); - - device = fu_synapticsmst_device_new (kind, aux_node, layer, rad); - - if (!fu_synapticsmst_device_enumerate_device (device, error)) + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (device, error); + if (locker == NULL) return FALSE; - reboot = !fu_device_has_custom_flag (dev, "skip-restart"); - install_force = (flags & FWUPD_INSTALL_FLAG_FORCE) != 0 || - fu_device_has_custom_flag (dev, "ignore-board-id"); - fu_device_set_status (dev, FWUPD_STATUS_DEVICE_WRITE); - if (!fu_synapticsmst_device_write_firmware (device, blob_fw, - fu_synapticsmst_write_progress_cb, - dev, - reboot, - install_force, - error)) { - g_prefix_error (error, "failed to flash firmware: "); + if (!fu_device_write_firmware (device, blob_fw, flags, error)) return FALSE; - } - - if (!reboot) { - g_debug ("Skipping device restart per quirk request"); - return TRUE; - } - - /* Re-run device enumeration to find the new device version */ - fu_device_set_status (dev, FWUPD_STATUS_DEVICE_RESTART); - for (guint i = 1; i <= SYNAPTICS_UPDATE_ENUMERATE_TRIES; i++) { - g_autoptr(GError) error_local = NULL; - g_usleep (SYNAPTICS_FLASH_MODE_DELAY * 1000000); - if (!fu_synapticsmst_device_enumerate_device (device, - &error_local)) { - g_warning ("Unable to find device after %u seconds: %s", - SYNAPTICS_FLASH_MODE_DELAY * i, - error_local->message); - if (i == SYNAPTICS_UPDATE_ENUMERATE_TRIES) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "%s", - error_local->message); - return FALSE; - } - } - } - fu_device_set_version (dev, fu_synapticsmst_device_get_version (device), - FWUPD_VERSION_FORMAT_TRIPLET); - + if (!fu_device_has_custom_flag (device, "skip-restart")) + fu_plugin_device_remove (plugin, device); return TRUE; } -gboolean -fu_plugin_device_removed (FuPlugin *plugin, FuDevice *device, GError **error) -{ - const gchar *aux_node; - const gchar *kind_str; - const gchar *layer_str; - const gchar *rad_str; - g_autofree gchar *dev_id_str = NULL; - - aux_node = fu_device_get_metadata (device, "SynapticsMSTAuxNode"); - if (aux_node == NULL) - return TRUE; - kind_str = fu_device_get_metadata (device, "SynapticsMSTKind"); - if (kind_str == NULL) - return TRUE; - layer_str = fu_device_get_metadata (device, "SynapticsMSTLayer"); - if (layer_str == NULL) - return TRUE; - rad_str = fu_device_get_metadata (device, "SynapticsMSTRad"); - if (rad_str == NULL) - return TRUE; - dev_id_str = g_strdup_printf ("MST-%s-%s-%s-%s", - kind_str, aux_node, layer_str, rad_str); - if (fu_plugin_cache_lookup (plugin, dev_id_str) != NULL) { - g_debug ("Removing %s from cache", dev_id_str); - fu_plugin_cache_remove (plugin, dev_id_str); - } else { - g_debug ("%s constructed but not found in cache", dev_id_str); - } - return TRUE; -} - -static gboolean -fu_plugin_synapticsmst_coldplug (FuPlugin *plugin, GError **error) -{ - g_autoptr(GError) error_local = NULL; - /* verify that this is a supported system */ - if (!fu_synapticsmst_check_supported_system (plugin, error)) - return FALSE; - - /* look for host devices or already plugged in dock devices */ - if (!fu_plugin_synapticsmst_enumerate (plugin, &error_local)) - g_debug ("error enumerating: %s", error_local->message); - return TRUE; -} - -gboolean -fu_plugin_coldplug (FuPlugin *plugin, GError **error) -{ - return fu_plugin_synapticsmst_coldplug (plugin, error); -} - -gboolean -fu_plugin_recoldplug (FuPlugin *plugin, GError **error) -{ - return fu_plugin_synapticsmst_coldplug (plugin, error); -} - - void fu_plugin_init (FuPlugin *plugin) { - /* make sure dell is already coldplugged */ - fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_AFTER, "dell"); - fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.synaptics.mst"); + FuPluginData *priv = fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); + + /* devices added by this plugin */ + priv->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_udev_subsystem (plugin, "drm"); /* used for uevent only */ + fu_plugin_add_udev_subsystem (plugin, "drm_dp_aux_dev"); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.synaptics.mst"); +} + +void +fu_plugin_destroy (FuPlugin *plugin) +{ + FuPluginData *priv = fu_plugin_get_data (plugin); + if (priv->drm_changed_id != 0) + g_source_remove (priv->drm_changed_id); + g_ptr_array_unref (priv->devices); } diff --git a/plugins/synapticsmst/fu-self-test.c b/plugins/synapticsmst/fu-self-test.c index 4892df5fa..ffa4312a0 100644 --- a/plugins/synapticsmst/fu-self-test.c +++ b/plugins/synapticsmst/fu-self-test.c @@ -16,23 +16,47 @@ static void _plugin_device_added_cb (FuPlugin *plugin, FuDevice *device, gpointer user_data) { GPtrArray **devices = (GPtrArray **) user_data; - g_ptr_array_add (*devices, device); + g_ptr_array_add (*devices, g_object_ref (device)); } static void -fu_plugin_synapticsmst_func (void) +_test_add_fake_devices_from_dir (FuPlugin *plugin, const gchar *path) +{ + const gchar *basename; + gboolean ret; + g_autoptr(FuQuirks) quirks = fu_quirks_new (); + g_autoptr(GError) error = NULL; + g_autoptr(GDir) dir = g_dir_open (path, 0, &error); + g_assert_no_error (error); + g_assert_nonnull (dir); + while ((basename = g_dir_read_name (dir)) != NULL) { + g_autofree gchar *fn = g_build_filename (path, basename, NULL); + g_autoptr(FuUdevDevice) dev = NULL; + if (!g_str_has_prefix (basename, "drm_dp_aux")) + continue; + dev = g_object_new (FU_TYPE_UDEV_DEVICE, + "quirks", quirks, + "physical-id", "PCI_SLOT_NAME=0000:3e:00.0", + "logical-id", basename, + "subsystem", "drm_dp_aux_dev", + "device-file", fn, + NULL); + g_debug ("creating drm_dp_aux_dev object backed by %s", fn); + ret = fu_plugin_runner_udev_device_added (plugin, dev, &error); + g_assert_no_error (error); + g_assert (ret); + } +} + +/* test with no Synaptics MST devices */ +static void +fu_plugin_synapticsmst_none_func (void) { gboolean ret; - guint device_count; - GPtrArray *devices = NULL; + g_autoptr(FuPlugin) plugin = fu_plugin_new (); g_autoptr(GError) error = NULL; - FuDevice *device = NULL; - g_autoptr(FuPlugin) plugin = NULL; - const gchar *test_directory; + g_autoptr(GPtrArray) devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - devices = g_ptr_array_new (); - - plugin = fu_plugin_new (); g_signal_connect (plugin, "device-added", G_CALLBACK (_plugin_device_added_cb), &devices); @@ -43,35 +67,36 @@ fu_plugin_synapticsmst_func (void) g_assert_no_error (error); g_assert (ret); - /* Test with no Synaptics MST devices */ - test_directory = SOURCEDIR "/tests/no_devices"; - g_assert(g_file_test (test_directory, G_FILE_TEST_IS_DIR)); + _test_add_fake_devices_from_dir (plugin, SOURCEDIR "/tests/no_devices"); + g_assert_cmpint (devices->len, ==, 0); +} - g_setenv ("FWUPD_SYNAPTICSMST_FW_DIR", test_directory, TRUE); - ret = fu_plugin_runner_coldplug (plugin, &error); +/* emulate adding/removing a Dell TB16 dock */ +static void +fu_plugin_synapticsmst_tb16_func (void) +{ + gboolean ret; + g_autoptr(FuPlugin) plugin = fu_plugin_new (); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + + g_signal_connect (plugin, "device-added", + G_CALLBACK (_plugin_device_added_cb), + &devices); + ret = fu_plugin_open (plugin, PLUGINBUILDDIR "/libfu_plugin_synapticsmst.so", &error); + g_assert_no_error (error); + g_assert (ret); + ret = fu_plugin_runner_startup (plugin, &error); g_assert_no_error (error); g_assert (ret); - /* Emulate adding/removing a Dell TB16 dock */ - test_directory = SOURCEDIR "/tests/tb16_dock"; - - g_assert (g_file_test (test_directory, G_FILE_TEST_IS_DIR)); - - g_setenv ("FWUPD_SYNAPTICSMST_FW_DIR", test_directory, TRUE); - ret = fu_plugin_runner_coldplug (plugin, &error); - g_assert_no_error (error); - g_assert (ret); - - device_count = devices->len; - g_assert_cmpuint (device_count, ==, 2); - - for (guint i = 0; i < device_count; i++) { - device = g_ptr_array_index (devices, i); - g_assert_cmpstr (fu_device_get_version (device), ==, "3.10.002"); - g_ptr_array_remove (devices, device); - fu_plugin_device_remove (plugin, device); + _test_add_fake_devices_from_dir (plugin, SOURCEDIR "/tests/tb16_dock"); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index (devices, i); + g_autofree gchar *tmp = fu_device_to_string (device); + g_debug ("%s", tmp); } - g_ptr_array_unref (devices); + g_assert_cmpint (devices->len, ==, 2); } int @@ -85,6 +110,7 @@ main (int argc, char **argv) g_assert_cmpint (g_mkdir_with_parents ("/tmp/fwupd-self-test/var/lib/fwupd", 0755), ==, 0); /* tests go here */ - g_test_add_func ("/fwupd/plugin{synapticsmst}", fu_plugin_synapticsmst_func); + g_test_add_func ("/fwupd/plugin/synapticsmst{none}", fu_plugin_synapticsmst_none_func); + g_test_add_func ("/fwupd/plugin/synapticsmst{tb16}", fu_plugin_synapticsmst_tb16_func); return g_test_run (); } diff --git a/plugins/synapticsmst/fu-synapticsmst-common.c b/plugins/synapticsmst/fu-synapticsmst-common.c index 4d5672900..a7f4d2863 100644 --- a/plugins/synapticsmst/fu-synapticsmst-common.c +++ b/plugins/synapticsmst/fu-synapticsmst-common.c @@ -9,16 +9,6 @@ #include "fu-synapticsmst-common.h" -FuSynapticsmstMode -fu_synapticsmst_mode_from_string (const gchar *mode) -{ - if (g_strcmp0 (mode, "DIRECT") == 0) - return FU_SYNAPTICSMST_MODE_DIRECT; - if (g_strcmp0 (mode, "REMOTE") == 0) - return FU_SYNAPTICSMST_MODE_REMOTE; - return FU_SYNAPTICSMST_MODE_UNKNOWN; -} - const gchar * fu_synapticsmst_mode_to_string (FuSynapticsmstMode mode) { diff --git a/plugins/synapticsmst/fu-synapticsmst-common.h b/plugins/synapticsmst/fu-synapticsmst-common.h index 30cc2b767..233906911 100644 --- a/plugins/synapticsmst/fu-synapticsmst-common.h +++ b/plugins/synapticsmst/fu-synapticsmst-common.h @@ -10,6 +10,8 @@ #include +#define SYNAPTICS_FLASH_MODE_DELAY 3 /* seconds */ + /** * FuSynapticsmstMode: * @FU_SYNAPTICSMST_MODE_UNKNOWN: Type invalid or not known @@ -35,7 +37,6 @@ typedef enum { FU_SYNAPTICSMST_FAMILY_LAST } FuSynapticsmstFamily; -FuSynapticsmstMode fu_synapticsmst_mode_from_string (const gchar *mode); -const gchar *fu_synapticsmst_mode_to_string (FuSynapticsmstMode mode); +const gchar *fu_synapticsmst_mode_to_string (FuSynapticsmstMode mode); const gchar *fu_synapticsmst_family_to_string (FuSynapticsmstFamily family); FuSynapticsmstFamily fu_synapticsmst_family_from_chip_id (guint16 chip_id); diff --git a/plugins/synapticsmst/fu-synapticsmst-connection.c b/plugins/synapticsmst/fu-synapticsmst-connection.c index 77a17039f..713c01fbe 100644 --- a/plugins/synapticsmst/fu-synapticsmst-connection.c +++ b/plugins/synapticsmst/fu-synapticsmst-connection.c @@ -40,18 +40,20 @@ fu_synapticsmst_connection_aux_node_read (FuSynapticsmstConnection *self, gint length, GError **error) { if (lseek (self->fd, offset, SEEK_SET) != offset) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "failed to lseek"); + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to lseek to 0x%x on layer:%u, rad:0x%x", + offset, self->layer, self->rad); return FALSE; } if (read (self->fd, buf, length) != length) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "failed to read"); + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to read 0x%x bytes on layer:%u, rad:0x%x", + (guint) length, self->layer, self->rad); return FALSE; } @@ -64,18 +66,20 @@ fu_synapticsmst_connection_aux_node_write (FuSynapticsmstConnection *self, gint length, GError **error) { if (lseek (self->fd, offset, SEEK_SET) != offset) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "failed to lseek"); + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to lseek to 0x%x on layer:%u, rad:0x%x", + offset, self->layer, self->rad); return FALSE; } if (write (self->fd, buf, length) != length) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "failed to write"); + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "failed to write 0x%x bytes on layer:%u, rad:0x%x", + (guint) length, self->layer, self->rad); return FALSE; } @@ -472,26 +476,13 @@ fu_synapticsmst_connection_enable_rc (FuSynapticsmstConnection *self, GError **e for (gint i = 0; i <= self->layer; i++) { g_autoptr(FuSynapticsmstConnection) connection_tmp = NULL; - g_autoptr(GError) error_local = NULL; connection_tmp = fu_synapticsmst_connection_new (self->fd, i, self->rad); if (!fu_synapticsmst_connection_rc_set_command (connection_tmp, UPDC_ENABLE_RC, 5, 0, (guint8*)sc, - &error_local)) { - g_debug ("Failed to enable remote control in layer %d: %s, retrying", - i, error_local->message); - - if (!fu_synapticsmst_connection_disable_rc (connection_tmp, error)) - return FALSE; - if (!fu_synapticsmst_connection_rc_set_command (connection_tmp, - UPDC_ENABLE_RC, - 5, 0, (guint8*)sc, - error)) { - g_prefix_error (error, - "failed to enable remote control in layer %d: ", - i); - return FALSE; - } + error)) { + g_prefix_error (error, "failed to enable remote control: "); + return FALSE; } } @@ -508,9 +499,7 @@ fu_synapticsmst_connection_disable_rc (FuSynapticsmstConnection *self, GError ** UPDC_DISABLE_RC, 0, 0, NULL, error)) { - g_prefix_error (error, - "failed to disable remote control in layer %d: ", - i); + g_prefix_error (error, "failed to disable remote control: "); return FALSE; } } diff --git a/plugins/synapticsmst/fu-synapticsmst-device.c b/plugins/synapticsmst/fu-synapticsmst-device.c index 89feddbcb..b5a48bb51 100644 --- a/plugins/synapticsmst/fu-synapticsmst-device.c +++ b/plugins/synapticsmst/fu-synapticsmst-device.c @@ -9,14 +9,17 @@ #include "config.h" -#include "fu-device-locker.h" -#include "fu-synapticsmst-device.h" -#include "fu-synapticsmst-connection.h" -#include "fu-synapticsmst-common.h" - -#include #include -#include +#include + +#include + +#include "fu-synapticsmst-common.h" +#include "fu-synapticsmst-connection.h" +#include "fu-synapticsmst-device.h" + +#define FU_SYNAPTICSMST_ID_CTRL_SIZE 0x1000 +#define SYNAPTICS_UPDATE_ENUMERATE_TRIES 3 #define BIT(n) (1 << (n)) #define FLASH_SECTOR_ERASE_4K 0x1000 @@ -41,22 +44,20 @@ #define FLASH_SETTLE_TIME 5000000 /* us */ struct _FuSynapticsmstDevice { - GObject parent_instance; - FuSynapticsmstMode kind; - gchar *version; + FuUdevDevice parent_instance; + gint fd; + gchar *system_type; + guint64 write_block_size; + FuSynapticsmstFamily family; + FuSynapticsmstMode mode; + guint8 active_bank; + guint8 layer; + guint16 rad; /* relative address */ guint32 board_id; guint16 chip_id; - gchar *chip_id_str; - gchar *aux_node; - guint8 layer; - guint16 rad; - gint fd; - gboolean has_cascade; - gchar *fw_dir; - gboolean test_mode; }; -G_DEFINE_TYPE (FuSynapticsmstDevice, fu_synapticsmst_device, G_TYPE_OBJECT) +G_DEFINE_TYPE (FuSynapticsmstDevice, fu_synapticsmst_device, FU_TYPE_UDEV_DEVICE) static void fu_synapticsmst_device_finalize (GObject *object) @@ -65,45 +66,36 @@ fu_synapticsmst_device_finalize (GObject *object) if (self->fd > 0) close (self->fd); + g_free (self->system_type); - g_free (self->fw_dir); - g_free (self->aux_node); - g_free (self->version); - g_free (self->chip_id_str); G_OBJECT_CLASS (fu_synapticsmst_device_parent_class)->finalize (object); } static void fu_synapticsmst_device_init (FuSynapticsmstDevice *self) { - const gchar *tmp; - tmp = g_getenv ("FWUPD_SYNAPTICSMST_FW_DIR"); - if (tmp == NULL) { - self->test_mode = FALSE; - self->fw_dir = g_strdup ("/dev"); - } else { - self->test_mode = TRUE; - self->fw_dir = g_strdup (tmp); - } + fu_device_set_vendor (FU_DEVICE (self), "Synaptics"); + fu_device_set_summary (FU_DEVICE (self), "Multi-Stream Transport Device"); + fu_device_add_icon (FU_DEVICE (self), "video-display"); } static void -fu_synapticsmst_device_class_init (FuSynapticsmstDeviceClass *klass) +fu_synapticsmst_device_to_string (FuDevice *device, guint idt, GString *str) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->finalize = fu_synapticsmst_device_finalize; -} - -FuSynapticsmstMode -fu_synapticsmst_device_get_kind (FuSynapticsmstDevice *self) -{ - return self->kind; -} - -guint16 -fu_synapticsmst_device_get_board_id (FuSynapticsmstDevice *self) -{ - return self->board_id; + FuSynapticsmstDevice *self = FU_SYNAPTICSMST_DEVICE (device); + fu_common_string_append_ku (str, idt, "FD", (guint) self->fd); + if (self->mode != FU_SYNAPTICSMST_MODE_UNKNOWN) { + fu_common_string_append_kv (str, idt, "Mode", + fu_synapticsmst_mode_to_string (self->mode)); + } + if (self->family == FU_SYNAPTICSMST_FAMILY_PANAMERA) + fu_common_string_append_kx (str, idt, "ActiveBank", self->active_bank); + fu_common_string_append_kx (str, idt, "Layer", self->layer); + fu_common_string_append_kx (str, idt, "Rad", self->rad); + if (self->board_id != 0x0) + fu_common_string_append_ku (str, idt, "BoardId", self->board_id); + if (self->chip_id != 0x0) + fu_common_string_append_kx (str, idt, "ChipId", self->chip_id); } static gboolean @@ -112,18 +104,21 @@ fu_synapticsmst_device_enable_rc (FuSynapticsmstDevice *self, GError **error) g_autoptr(FuSynapticsmstConnection) connection = NULL; /* in test mode we need to open a different file node instead */ - if (self->test_mode) { + if (fu_udev_device_get_dev (FU_UDEV_DEVICE (self)) == NULL) { g_autofree gchar *filename = NULL; - close(self->fd); + g_autofree gchar *dirname = NULL; + close (self->fd); + self->fd = 0; + dirname = g_path_get_dirname (fu_udev_device_get_device_file (FU_UDEV_DEVICE (self))); filename = g_strdup_printf ("%s/remote/%s", - self->fw_dir, - self->aux_node); + dirname, + fu_device_get_logical_id (FU_DEVICE (self))); if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_NOT_FOUND, - "no device exists %s", - filename); + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "no device exists %s", + filename); return FALSE; } self->fd = open (filename, O_RDWR); @@ -139,10 +134,7 @@ fu_synapticsmst_device_enable_rc (FuSynapticsmstDevice *self, GError **error) } connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); - if (!fu_synapticsmst_connection_enable_rc (connection, error)) - return FALSE; - - return TRUE; + return fu_synapticsmst_connection_enable_rc (connection, error); } static gboolean @@ -151,18 +143,21 @@ fu_synapticsmst_device_disable_rc (FuSynapticsmstDevice *self, GError **error) g_autoptr(FuSynapticsmstConnection) connection = NULL; /* in test mode we need to open a different file node instead */ - if (self->test_mode) { + if (fu_udev_device_get_dev (FU_UDEV_DEVICE (self)) == NULL) { g_autofree gchar *filename = NULL; - close(self->fd); + g_autofree gchar *dirname = NULL; + close (self->fd); + self->fd = 0; + dirname = g_path_get_dirname (fu_udev_device_get_device_file (FU_UDEV_DEVICE (self))); filename = g_strdup_printf ("%s/%s", - self->fw_dir, - self->aux_node); + dirname, + fu_device_get_logical_id (FU_DEVICE (self))); if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_NOT_FOUND, - "no device exists %s", - filename); + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "no device exists %s", + filename); return FALSE; } self->fd = open (filename, O_RDWR); @@ -178,235 +173,57 @@ fu_synapticsmst_device_disable_rc (FuSynapticsmstDevice *self, GError **error) } connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); - if (!fu_synapticsmst_connection_disable_rc (connection, error)) - return FALSE; - - return TRUE; + return fu_synapticsmst_connection_disable_rc (connection, error); } -gboolean -fu_synapticsmst_device_scan_cascade_device (FuSynapticsmstDevice *self, - GError **error, - guint8 tx_port) +static gboolean +fu_synapticsmst_device_open (FuDevice *device, GError **error) { - guint8 layer = self->layer + 1; - guint16 rad = self->rad | (tx_port << (2 * (self->layer))); - guint8 byte[4]; - g_autoptr(FuSynapticsmstConnection) connection = NULL; - g_autoptr(GError) error_local = NULL; - g_autoptr(FuDeviceLocker) locker = NULL; + FuSynapticsmstDevice *self = FU_SYNAPTICSMST_DEVICE (device); - if (self->test_mode) - return TRUE; - - /* reset */ - self->has_cascade = FALSE; - - /* enable remote control and disable on exit */ - locker = fu_device_locker_new_full (self, - (FuDeviceLockerFunc) fu_synapticsmst_device_enable_rc, - (FuDeviceLockerFunc) fu_synapticsmst_device_disable_rc, - error); - if (locker == NULL) + /* open device */ + self->fd = g_open (fu_udev_device_get_device_file (FU_UDEV_DEVICE (device)), O_RDWR); + if (self->fd < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to open %s: %s", + fu_udev_device_get_device_file (FU_UDEV_DEVICE (device)), + strerror (errno)); return FALSE; - - connection = fu_synapticsmst_connection_new (self->fd, layer, rad); - if (!fu_synapticsmst_connection_read (connection, REG_RC_CAP, byte, 1, &error_local)) { - g_debug ("No cascade device found: %s", error_local->message); - return TRUE; - } - if (byte[0] & 0x04) { - if (!fu_synapticsmst_connection_read (connection, REG_VENDOR_ID, byte, 3, error)) { - g_prefix_error (error, - "failed to read cascade device on tx_port %d: ", - tx_port); - return FALSE; - } - if (byte[0] == 0x90 && byte[1] == 0xCC && byte[2] == 0x24) - self->has_cascade = TRUE; } + /* success */ return TRUE; } static gboolean -fu_synapticsmst_device_read_board_id (FuSynapticsmstDevice *self, - FuSynapticsmstConnection *connection, - guint8 *byte, - GError **error) +fu_synapticsmst_device_probe (FuUdevDevice *device, GError **error) { - - if (self->test_mode) { - g_autofree gchar *filename = NULL; - gint fd; - filename = g_strdup_printf ("%s/remote/%s_eeprom", - self->fw_dir, - self->aux_node); - if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_NOT_FOUND, - "no device exists %s", - filename); - return FALSE; - } - fd = open (filename, O_RDONLY); - if (fd == -1) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_PERMISSION_DENIED, - "cannot open device %s", - filename); - return FALSE; - } - if (read (fd, byte, 2) != 2) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "error reading EEPROM file %s", - filename); - close (fd); - return FALSE; - } - close (fd); - } else { - /* get board ID via MCU address 0x170E instead of flash access due to HDCP2.2 running */ - if (!fu_synapticsmst_connection_rc_get_command (connection, - UPDC_READ_FROM_MEMORY, - 2, - (gint)ADDR_MEMORY_CUSTOMER_ID, byte, - error)) { - g_prefix_error (error, "Memory query failed: "); - return FALSE; - } - } - + g_autofree gchar *logical_id = NULL; + logical_id = g_path_get_basename (fu_udev_device_get_sysfs_path(device)); + fu_device_set_logical_id (FU_DEVICE (device), logical_id); + if (!fu_udev_device_set_physical_id (device, "pci", error)) + return FALSE; return TRUE; } static gboolean -fu_synapticsmst_device_get_active_bank_panamera (FuSynapticsmstDevice *self, - guint8 *bank_out, - GError **error) +fu_synapticsmst_device_close (FuDevice *device, GError **error) { - g_autoptr(FuSynapticsmstConnection) connection = NULL; - guint32 dwData[16]; - - /* get used bank */ - connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); - if (!fu_synapticsmst_connection_rc_get_command (connection, - UPDC_READ_FROM_MEMORY, - ((sizeof(dwData)/sizeof(dwData[0]))*4), - (gint) 0x20010c, (guint8*) dwData, - error)) { - g_prefix_error (error, "get active bank failed: "); + FuSynapticsmstDevice *self = FU_SYNAPTICSMST_DEVICE (device); + if (self->fd == 0) + return TRUE; + if (!g_close (self->fd, error)) return FALSE; - } - if ((dwData[0] & BIT(7)) || (dwData[0] & BIT(30))) - *bank_out = BANKTAG_1; - else - *bank_out = BANKTAG_0; - - g_debug ("bank in use:%x", *bank_out); - + self->fd = 0; return TRUE; } -gboolean -fu_synapticsmst_device_enumerate_device (FuSynapticsmstDevice *self, - GError **error) -{ - guint8 byte[16]; - guint8 bank; - g_autoptr(FuSynapticsmstConnection) connection = NULL; - g_autoptr(FuDeviceLocker) locker = NULL; - - if (!fu_synapticsmst_device_open (self, error)) { - g_prefix_error (error, "Failed to open device in DP Aux Node %s: ", - fu_synapticsmst_device_get_aux_node (self)); - return FALSE; - } - - /* enable remote control and disable on exit */ - locker = fu_device_locker_new_full (self, - (FuDeviceLockerFunc) fu_synapticsmst_device_enable_rc, - (FuDeviceLockerFunc) fu_synapticsmst_device_disable_rc, - error); - if (locker == NULL) - return FALSE; - - /* read firmware version */ - connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); - if (!fu_synapticsmst_connection_read (connection, REG_FIRMWARE_VERSION, - byte, 3, error)) - return FALSE; - - self->version = g_strdup_printf ("%1d.%02d.%03d", byte[0], byte[1], byte[2]); - - /* read board ID */ - if (!fu_synapticsmst_device_read_board_id (self, connection, byte, error)) - return FALSE; - self->board_id = (byte[0] << 8) | (byte[1]); - g_debug ("BoardID %x", self->board_id); - - /* read board chip_id */ - if (!fu_synapticsmst_connection_read (connection, REG_CHIP_ID, - byte, 2, error)) { - g_prefix_error (error, "failed to read chip id: "); - return FALSE; - } - self->chip_id = (byte[0] << 8) | (byte[1]); - self->chip_id_str = g_strdup_printf ("VMM%02x%02x", byte[0], byte[1]); - - /* if running on panamera, check the active bank (for debugging logs) */ - if (fu_synapticsmst_family_from_chip_id (self->chip_id) == FU_SYNAPTICSMST_FAMILY_PANAMERA && - !fu_synapticsmst_device_get_active_bank_panamera (self, &bank, error)) - return FALSE; - - return TRUE; -} - -const gchar * -fu_synapticsmst_device_get_aux_node (FuSynapticsmstDevice *self) -{ - return self->aux_node; -} - -const gchar * -fu_synapticsmst_device_get_version (FuSynapticsmstDevice *self) -{ - return self->version; -} - -const gchar * -fu_synapticsmst_device_get_chip_id_str (FuSynapticsmstDevice *self) -{ - return self->chip_id_str; -} - - -guint16 -fu_synapticsmst_device_get_rad (FuSynapticsmstDevice *self) -{ - return self->rad; -} - -guint8 -fu_synapticsmst_device_get_layer (FuSynapticsmstDevice *self) -{ - return self->layer; -} - -gboolean -fu_synapticsmst_device_get_cascade (FuSynapticsmstDevice *self) -{ - return self->has_cascade; -} - static gboolean fu_synapticsmst_device_get_flash_checksum (FuSynapticsmstDevice *self, - guint32 length, guint32 offset, - guint32 *checksum, GError **error) + guint32 length, guint32 offset, + guint32 *checksum, GError **error) { g_autoptr(FuSynapticsmstConnection) connection = NULL; @@ -509,10 +326,8 @@ fu_synapticsmst_device_set_flash_sector_erase (FuSynapticsmstDevice *self, static gboolean fu_synapticsmst_device_update_esm (FuSynapticsmstDevice *self, - const guint8 *payload_data, - GFileProgressCallback progress_cb, - gpointer progress_data, - GError **error) + const guint8 *payload_data, + GError **error) { guint32 checksum = 0; guint32 esm_sz = ESM_CODE_SIZE; @@ -574,12 +389,9 @@ fu_synapticsmst_device_update_esm (FuSynapticsmstDevice *self, } write_offset += unit_sz; write_idx += unit_sz; - if (progress_cb != NULL) { - progress_cb ((goffset) i * 100, - (goffset) (write_loops -1) * 100, - progress_data); - } - + fu_device_set_progress_full (FU_DEVICE (self), + (goffset) i * 100, + (goffset) (write_loops -1) * 100); } /* check ESM checksum */ @@ -615,11 +427,9 @@ fu_synapticsmst_device_update_esm (FuSynapticsmstDevice *self, static gboolean fu_synapticsmst_device_update_tesla_leaf_firmware (FuSynapticsmstDevice *self, - guint32 payload_len, - const guint8 *payload_data, - GFileProgressCallback progress_cb, - gpointer progress_data, - GError **error) + guint32 payload_len, + const guint8 *payload_data, + GError **error) { g_autoptr(FuSynapticsmstConnection) connection = NULL; guint32 data_to_write = 0; @@ -668,11 +478,9 @@ fu_synapticsmst_device_update_tesla_leaf_firmware (FuSynapticsmstDevice *self, } offset += length; data_to_write -= length; - if (progress_cb != NULL) { - progress_cb ((goffset) i * 100, - (goffset) (write_loops -1) * 100, - progress_data); - } + fu_device_set_progress_full (FU_DEVICE (self), + (goffset) i * 100, + (goffset) (write_loops -1) * 100); } /* check data just written */ @@ -703,20 +511,40 @@ fu_synapticsmst_device_update_tesla_leaf_firmware (FuSynapticsmstDevice *self, return TRUE; } +static gboolean +fu_synapticsmst_device_get_active_bank_panamera (FuSynapticsmstDevice *self, GError **error) +{ + g_autoptr(FuSynapticsmstConnection) connection = NULL; + guint32 buf[16]; + + /* get used bank */ + connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); + if (!fu_synapticsmst_connection_rc_get_command (connection, + UPDC_READ_FROM_MEMORY, + ((sizeof(buf)/sizeof(buf[0]))*4), + (gint) 0x20010c, (guint8*) buf, + error)) { + g_prefix_error (error, "get active bank failed: "); + return FALSE; + } + if ((buf[0] & BIT(7)) || (buf[0] & BIT(30))) + self->active_bank = BANKTAG_1; + else + self->active_bank = BANKTAG_0; + return TRUE; +} + static gboolean fu_synapticsmst_device_update_panamera_firmware (FuSynapticsmstDevice *self, - guint32 payload_len, - const guint8 *payload_data, - GFileProgressCallback progress_cb, - gpointer progress_data, - GError **error) + guint32 payload_len, + const guint8 *payload_data, + GError **error) { guint16 crc_tmp = 0; guint32 fw_size; guint32 unit_sz = BLOCK_UNIT; guint32 write_loops = 0; - guint8 bank_in_use; guint8 bank_to_update = BANKTAG_1; guint8 readBuf[256]; guint8 tagData[16]; @@ -725,9 +553,9 @@ fu_synapticsmst_device_update_panamera_firmware (FuSynapticsmstDevice *self, g_autoptr(FuSynapticsmstConnection) connection = NULL; /* get used bank */ - if (!fu_synapticsmst_device_get_active_bank_panamera (self, &bank_in_use, error)) + if (!fu_synapticsmst_device_get_active_bank_panamera (self, error)) return FALSE; - if (bank_in_use == BANKTAG_1) + if (self->active_bank == BANKTAG_1) bank_to_update = BANKTAG_0; g_debug ("bank to update:%x", bank_to_update); @@ -792,15 +620,13 @@ fu_synapticsmst_device_update_panamera_firmware (FuSynapticsmstDevice *self, write_offset += unit_sz; write_idx += unit_sz; - if (progress_cb != NULL) { - progress_cb ((goffset) i * 100, - (goffset) (write_loops -1) * 100, - progress_data); - } + fu_device_set_progress_full (FU_DEVICE (self), + (goffset) i * 100, + (goffset) (write_loops -1) * 100); } /* verify CRC */ - checksum = fu_synapticsmst_device_get_crc ( 0, 16, fw_size, payload_data ); + checksum = fu_synapticsmst_device_get_crc (0, 16, fw_size, payload_data ); for (guint32 i = 0; i < 4; i++) { g_usleep (1000); /* wait crc calculation */ if (!fu_synapticsmst_connection_rc_special_get_command (connection, @@ -881,7 +707,7 @@ fu_synapticsmst_device_update_panamera_firmware (FuSynapticsmstDevice *self, /* set tag invalid*/ if (!fu_synapticsmst_connection_rc_get_command (connection, UPDC_READ_FROM_EEPROM, 1, - (EEPROM_BANK_OFFSET * bank_in_use + EEPROM_TAG_OFFSET + 15), + (EEPROM_BANK_OFFSET * self->active_bank + EEPROM_TAG_OFFSET + 15), tagData, error)) { g_prefix_error (error, "failed to read tag from flash: "); @@ -893,7 +719,7 @@ fu_synapticsmst_device_update_panamera_firmware (FuSynapticsmstDevice *self, if (tagData[0] != 0xff) { guint32 erase_offset; /* offset for last 4k of bank# */ - erase_offset = (EEPROM_BANK_OFFSET * bank_in_use + EEPROM_BANK_OFFSET - 0x1000) / 0x1000; + erase_offset = (EEPROM_BANK_OFFSET * self->active_bank + EEPROM_BANK_OFFSET - 0x1000) / 0x1000; if (!fu_synapticsmst_device_set_flash_sector_erase (self, FLASH_SECTOR_ERASE_4K, erase_offset, @@ -904,7 +730,7 @@ fu_synapticsmst_device_update_panamera_firmware (FuSynapticsmstDevice *self, tagData[1] = 0x00; if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_WRITE_TO_EEPROM, 1, - (EEPROM_BANK_OFFSET * bank_in_use + EEPROM_TAG_OFFSET + 15), + (EEPROM_BANK_OFFSET * self->active_bank + EEPROM_TAG_OFFSET + 15), &tagData[1], error)) { g_prefix_error (error, "failed to clear CRC: "); @@ -913,7 +739,7 @@ fu_synapticsmst_device_update_panamera_firmware (FuSynapticsmstDevice *self, } if (!fu_synapticsmst_connection_rc_get_command (connection, UPDC_READ_FROM_EEPROM, 1, - (EEPROM_BANK_OFFSET * bank_in_use + EEPROM_TAG_OFFSET + 15), + (EEPROM_BANK_OFFSET * self->active_bank + EEPROM_TAG_OFFSET + 15), readBuf, error)) { g_prefix_error (error, "failed to read CRC from flash: "); @@ -935,52 +761,10 @@ fu_synapticsmst_device_update_panamera_firmware (FuSynapticsmstDevice *self, return TRUE; } -static gboolean -fu_synapticsmst_device_check_firmware_content (FuSynapticsmstDevice *self, - GBytes *fw, - FuSynapticsmstFamily chip_type, - GError **error) -{ - gsize payload_len, payload_len_max; - - switch (chip_type) { - case FU_SYNAPTICSMST_FAMILY_PANAMERA: - payload_len_max = PAYLOAD_SIZE_512K; - break; - case FU_SYNAPTICSMST_FAMILY_TESLA: - case FU_SYNAPTICSMST_FAMILY_LEAF: - payload_len_max = PAYLOAD_SIZE_64K; - break; - default: - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "unknown chip type %u", - chip_type); - return FALSE; - - } - - /* check size */ - payload_len = g_bytes_get_size (fw); - if (payload_len > payload_len_max || payload_len == 0) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "invalid payload size %" G_GSIZE_FORMAT "(max %" G_GSIZE_FORMAT")", - payload_len, - payload_len_max); - return FALSE; - } - - - return TRUE; -} - static gboolean fu_synapticsmst_device_panamera_prepare_write (FuSynapticsmstDevice *self, GError **error) { - guint32 dwData[4] = {0}; + guint32 buf[4] = {0}; g_autoptr(FuSynapticsmstConnection) connection = NULL; /* Need to detect flash mode and ESM first ? */ @@ -988,10 +772,10 @@ fu_synapticsmst_device_panamera_prepare_write (FuSynapticsmstDevice *self, GErro connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); /* disable ESM first */ - dwData[0] = 0x21; + buf[0] = 0x21; if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_WRITE_TO_MEMORY, - 4, (gint)REG_ESM_DISABLE, (guint8*)dwData, + 4, (gint)REG_ESM_DISABLE, (guint8*)buf, error)) { g_prefix_error (error, "ESM disable failed: "); return FALSE; @@ -1003,17 +787,17 @@ fu_synapticsmst_device_panamera_prepare_write (FuSynapticsmstDevice *self, GErro /* disable QUAD mode */ if (!fu_synapticsmst_connection_rc_get_command (connection, UPDC_READ_FROM_MEMORY, - ((sizeof(dwData)/sizeof(dwData[0]))*4), - (gint)REG_QUAD_DISABLE, (guint8*)dwData, + ((sizeof(buf)/sizeof(buf[0]))*4), + (gint)REG_QUAD_DISABLE, (guint8*)buf, error)) { g_prefix_error (error, "quad query failed: "); return FALSE; } - dwData[0] = 0x00; + buf[0] = 0x00; if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_WRITE_TO_MEMORY, - 4, (gint)REG_QUAD_DISABLE, (guint8*)dwData, + 4, (gint)REG_QUAD_DISABLE, (guint8*)buf, error)) { g_prefix_error (error, "quad disable failed: "); return FALSE; @@ -1022,16 +806,16 @@ fu_synapticsmst_device_panamera_prepare_write (FuSynapticsmstDevice *self, GErro /* disable HDCP2.2 */ if (!fu_synapticsmst_connection_rc_get_command (connection, UPDC_READ_FROM_MEMORY, - 4, (gint)REG_HDCP22_DISABLE, (guint8*)dwData, + 4, (gint)REG_HDCP22_DISABLE, (guint8*)buf, error)) { g_prefix_error (error, "HDCP query failed: "); return FALSE; } - dwData[0] = dwData[0] & (~BIT(2)); + buf[0] = buf[0] & (~BIT(2)); if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_WRITE_TO_MEMORY, - 4, (gint)REG_HDCP22_DISABLE, (guint8*)dwData, + 4, (gint)REG_HDCP22_DISABLE, (guint8*)buf, error)) { g_prefix_error (error, "HDCP disable failed: "); return FALSE; @@ -1041,79 +825,78 @@ fu_synapticsmst_device_panamera_prepare_write (FuSynapticsmstDevice *self, GErro } static gboolean -fu_synapticsmst_device_restart (FuSynapticsmstDevice *self, - GError **error) +fu_synapticsmst_device_restart (FuSynapticsmstDevice *self, GError **error) { g_autoptr(FuSynapticsmstConnection) connection = NULL; - guint8 dwData[4] = {0xF5, 0, 0 ,0}; + guint8 buf[4] = {0xF5, 0, 0 ,0}; g_autoptr(GError) error_local = NULL; /* issue the reboot command, ignore return code (triggers before returning) */ connection = fu_synapticsmst_connection_new (self->fd, self->layer, self->rad); if (!fu_synapticsmst_connection_rc_set_command (connection, UPDC_WRITE_TO_MEMORY, - 4, (gint) 0x2000FC, (guint8*) &dwData, + 4, (gint) 0x2000FC, (guint8*) &buf, &error_local)) g_debug ("failed to restart: %s", error_local->message); return TRUE; } -gboolean -fu_synapticsmst_device_write_firmware (FuSynapticsmstDevice *self, - GBytes *fw, - GFileProgressCallback progress_cb, - gpointer progress_data, - gboolean reboot, - gboolean install_force, - GError **error) +static FuFirmware * +fu_synapticsmst_device_prepare_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) { + FuSynapticsmstDevice *self = FU_SYNAPTICSMST_DEVICE (device); + + /* check firmware and board ID match */ + if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + !fu_device_has_custom_flag (device, "ignore-board-id")) { + const guint8 *buf; + gsize len; + guint16 board_id; + + buf = g_bytes_get_data (fw, &len); + board_id = fu_common_read_uint16 (buf + ADDR_CUSTOMER_ID, G_BIG_ENDIAN); + if (board_id != self->board_id) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "board ID mismatch, got 0x%04x, expected 0x%04x", + board_id, self->board_id); + return NULL; + } + } + return fu_firmware_new_from_bytes (fw); +} + +static gboolean +fu_synapticsmst_device_write_firmware (FuDevice *device, + FuFirmware *firmware, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsmstDevice *self = FU_SYNAPTICSMST_DEVICE (device); + g_autoptr(GBytes) fw = NULL; const guint8 *payload_data; gsize payload_len; - guint16 tmp; - FuSynapticsmstFamily family = FU_SYNAPTICSMST_FAMILY_UNKNOWN; g_autoptr(FuDeviceLocker) locker = NULL; + fw = fu_firmware_get_image_default_bytes (firmware, error); + if (fw == NULL) + return FALSE; payload_data = g_bytes_get_data (fw, &payload_len); - family = fu_synapticsmst_family_from_chip_id (self->chip_id); - if (family == FU_SYNAPTICSMST_FAMILY_UNKNOWN) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "chip family unsupported"); - return FALSE; - } - - if (!fu_synapticsmst_device_check_firmware_content (self, fw, family, error)){ - g_prefix_error (error, "Invalid file content: "); - return FALSE; - } - - /* check firmware and board ID again */ - tmp = (*(payload_data + ADDR_CUSTOMER_ID) << 8) + *(payload_data + ADDR_BOARD_ID); - if (tmp != fu_synapticsmst_device_get_board_id (self) && !install_force) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "board ID mismatch"); - return FALSE; - } - - /* open device */ - if (!fu_synapticsmst_device_open (self, error)) { - g_prefix_error (error, - "can't open DP Aux node %s", - fu_synapticsmst_device_get_aux_node (self)); - return FALSE; - } - /* enable remote control and disable on exit */ - if (reboot) { + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + if (!fu_device_has_custom_flag (device, "skip-restart")) { locker = fu_device_locker_new_full (self, (FuDeviceLockerFunc) fu_synapticsmst_device_enable_rc, (FuDeviceLockerFunc) fu_synapticsmst_device_restart, error); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + fu_device_set_remove_delay (FU_DEVICE (self), 10000); /* a long time */ } else { locker = fu_device_locker_new_full (self, (FuDeviceLockerFunc) fu_synapticsmst_device_enable_rc, @@ -1124,111 +907,324 @@ fu_synapticsmst_device_write_firmware (FuSynapticsmstDevice *self, return FALSE; /* update firmware */ - if (family == FU_SYNAPTICSMST_FAMILY_PANAMERA) { + if (self->family == FU_SYNAPTICSMST_FAMILY_PANAMERA) { if (!fu_synapticsmst_device_panamera_prepare_write (self, error)) { g_prefix_error (error, "Failed to prepare for write: "); return FALSE; } if (!fu_synapticsmst_device_update_esm (self, - payload_data, - progress_cb, - progress_data, - error)) { + payload_data, + error)) { g_prefix_error (error, "ESM update failed: "); return FALSE; } if (!fu_synapticsmst_device_update_panamera_firmware (self, - payload_len, - payload_data, - progress_cb, - progress_data, - error)) { + payload_len, + payload_data, + error)) { g_prefix_error (error, "Firmware update failed: "); return FALSE; } } else { if (!fu_synapticsmst_device_update_tesla_leaf_firmware (self, - payload_len, - payload_data, - progress_cb, - progress_data, - error)) { + payload_len, + payload_data, + error)) { g_prefix_error (error, "Firmware update failed: "); return FALSE; } } - + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); return TRUE; } FuSynapticsmstDevice * -fu_synapticsmst_device_new (FuSynapticsmstMode kind, - const gchar *aux_node, - guint8 layer, - guint16 rad) +fu_synapticsmst_device_new (FuUdevDevice *device) { - FuSynapticsmstDevice *self; - - self = g_object_new (FU_SYNAPTICSMST_TYPE_DEVICE, NULL); - - self->aux_node = g_strdup(aux_node); - self->kind = kind; - self->version = NULL; - self->layer = layer; - self->rad = rad; - self->has_cascade = FALSE; - - return FU_SYNAPTICSMST_DEVICE (self); + FuSynapticsmstDevice *self = g_object_new (FU_TYPE_SYNAPTICSMST_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; } -gboolean -fu_synapticsmst_device_open (FuSynapticsmstDevice *self, GError **error) +static gboolean +fu_synapticsmst_device_read_board_id (FuSynapticsmstDevice *self, + FuSynapticsmstConnection *connection, + guint8 *byte, + GError **error) { - g_autofree gchar *filename = NULL; - guint8 byte[4]; + /* in test mode we need to open a different file node instead */ + if (fu_udev_device_get_dev (FU_UDEV_DEVICE (self)) == NULL) { + g_autofree gchar *filename = NULL; + g_autofree gchar *dirname = NULL; + gint fd; + dirname = g_path_get_dirname (fu_udev_device_get_device_file (FU_UDEV_DEVICE (self))); + filename = g_strdup_printf ("%s/remote/%s_eeprom", + dirname, + fu_device_get_logical_id (FU_DEVICE (self))); + if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "no device exists %s", + filename); + return FALSE; + } + fd = open (filename, O_RDONLY); + if (fd == -1) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_PERMISSION_DENIED, + "cannot open device %s", + filename); + return FALSE; + } + if (read (fd, byte, 2) != 2) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "error reading EEPROM file %s", + filename); + close (fd); + return FALSE; + } + close (fd); + return TRUE; + } + + /* get board ID via MCU address 0x170E instead of flash access due to HDCP2.2 running */ + if (!fu_synapticsmst_connection_rc_get_command (connection, + UPDC_READ_FROM_MEMORY, + 2, + (gint)ADDR_MEMORY_CUSTOMER_ID, byte, + error)) { + g_prefix_error (error, "Memory query failed: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_synapticsmst_device_scan_cascade (FuSynapticsmstDevice *self, guint8 layer, GError **error) +{ + /* in test mode we skip this */ + if (fu_udev_device_get_dev (FU_UDEV_DEVICE (self)) == NULL) + return TRUE; + + /* test each relative address in this layer */ + for (guint16 rad = 0; rad <= 2; rad++) { + guint8 byte[4]; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuSynapticsmstConnection) connection = NULL; + g_autoptr(FuSynapticsmstDevice) device_tmp = NULL; + g_autoptr(GError) error_local = NULL; + + /* enable remote control and disable on exit */ + device_tmp = fu_synapticsmst_device_new (FU_UDEV_DEVICE (self)); + device_tmp->layer = layer; + device_tmp->rad = rad; + locker = fu_device_locker_new_full (device_tmp, + (FuDeviceLockerFunc) fu_synapticsmst_device_enable_rc, + (FuDeviceLockerFunc) fu_synapticsmst_device_disable_rc, + &error_local); + if (locker == NULL) { + g_debug ("no cascade device found: %s", error_local->message); + continue; + } + connection = fu_synapticsmst_connection_new (device_tmp->fd, layer + 1, rad); + if (!fu_synapticsmst_connection_read (connection, REG_RC_CAP, byte, 1, &error_local)) { + g_debug ("no valid cascade device: %s", error_local->message); + continue; + } + + /* check recursively for more devices */ + g_clear_object (&locker); + self->mode = FU_SYNAPTICSMST_MODE_REMOTE; + self->layer = layer + 1; + self->rad = rad; + if (!fu_synapticsmst_device_scan_cascade (self, layer + 1, error)) + return FALSE; + } + return TRUE; +} + +void +fu_synapticsmst_device_set_system_type (FuSynapticsmstDevice *self, const gchar *system_type) +{ + g_return_if_fail (FU_IS_SYNAPTICSMST_DEVICE (self)); + self->system_type = g_strdup (system_type); +} + +static gboolean +fu_synapticsmst_device_rescan (FuDevice *device, GError **error) +{ + FuSynapticsmstDevice *self = FU_SYNAPTICSMST_DEVICE (device); + FuQuirks *quirks; + guint8 buf_vid[4]; g_autoptr(FuSynapticsmstConnection) connection = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autofree gchar *version = NULL; + g_autofree gchar *guid1 = NULL; + g_autofree gchar *guid2 = NULL; + g_autofree gchar *guid3 = NULL; + g_autofree gchar *group = NULL; + g_autofree gchar *name = NULL; + const gchar *guid_template; + const gchar *name_parent; + const gchar *name_family; + guint8 buf_ver[16]; - /* file doesn't exist on this system */ - filename = g_strdup_printf ("%s/%s", self->fw_dir, self->aux_node); - if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_NOT_FOUND, - "no device exists %s", - filename); - return FALSE; - } - - /* can't open aux node, try use sudo to get the permission */ - self->fd = open (filename, O_RDWR); - if (self->fd == -1) { - g_set_error (error, - G_IO_ERROR, - g_io_error_from_errno (errno), - "cannot open device %s: %s", - filename, g_strerror (errno)); - return FALSE; - } - + /* read vendor ID */ connection = fu_synapticsmst_connection_new (self->fd, 0, 0); - if (!fu_synapticsmst_connection_read (connection, REG_RC_CAP, byte, 1, error)) { + if (!fu_synapticsmst_connection_read (connection, REG_RC_CAP, buf_vid, 1, error)) { g_prefix_error (error, "failed to read device: "); return FALSE; } - if (byte[0] & 0x04) { + if (buf_vid[0] & 0x04) { if (!fu_synapticsmst_connection_read (connection, REG_VENDOR_ID, - byte, 3, error)) { + buf_vid, 3, error)) { g_prefix_error (error, "failed to read vendor ID: "); return FALSE; } - if (byte[0] == 0x90 && byte[1] == 0xCC && byte[2] == 0x24) - return TRUE; + /* not a correct device */ + if (buf_vid[0] != 0x90 || buf_vid[1] != 0xCC || buf_vid[2] != 0x24) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "no device"); + return FALSE; + } } - /* not a correct device */ - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "no device"); - return FALSE; + /* direct */ + self->mode = FU_SYNAPTICSMST_MODE_DIRECT; + self->layer = 0; + self->rad = 0; + + /* enable remote control and disable on exit */ + locker = fu_device_locker_new_full (self, + (FuDeviceLockerFunc) fu_synapticsmst_device_enable_rc, + (FuDeviceLockerFunc) fu_synapticsmst_device_disable_rc, + error); + if (locker == NULL) + return FALSE; + + /* read firmware version */ + if (!fu_synapticsmst_connection_read (connection, REG_FIRMWARE_VERSION, + buf_ver, 3, error)) + return FALSE; + + version = g_strdup_printf ("%1d.%02d.%03d", buf_ver[0], buf_ver[1], buf_ver[2]); + fu_device_set_version (FU_DEVICE (self), version, FWUPD_VERSION_FORMAT_TRIPLET); + + /* read board ID */ + if (!fu_synapticsmst_device_read_board_id (self, connection, buf_ver, error)) + return FALSE; + self->board_id = fu_common_read_uint16 (buf_ver, G_BIG_ENDIAN); + + /* read board chip_id */ + if (!fu_synapticsmst_connection_read (connection, REG_CHIP_ID, + buf_ver, 2, error)) { + g_prefix_error (error, "failed to read chip id: "); + return FALSE; + } + self->chip_id = (buf_ver[0] << 8) | (buf_ver[1]); + self->family = fu_synapticsmst_family_from_chip_id (self->chip_id); + + /* check the active bank for debugging */ + if (self->family == FU_SYNAPTICSMST_FAMILY_PANAMERA) { + if (!fu_synapticsmst_device_get_active_bank_panamera (self, error)) + return FALSE; + } + + /* recursively look for cascade devices */ + g_clear_object (&locker); + if (!fu_synapticsmst_device_scan_cascade (self, 0, error)) + return FALSE; + + /* set up the device name via quirks */ + group = g_strdup_printf ("SynapticsMSTBoardID=%u", self->board_id); + quirks = fu_device_get_quirks (FU_DEVICE (self)); + name_parent = fu_quirks_lookup_by_id (quirks, group, FU_QUIRKS_NAME); + if (name_parent != NULL) { + name = g_strdup_printf ("VMM%04x inside %s", + self->chip_id, name_parent); + } else { + name = g_strdup_printf ("VMM%04x", self->chip_id); + } + fu_device_set_name (FU_DEVICE (self), name); + + /* this is a host system, use system ID */ + guid_template = fu_quirks_lookup_by_id (quirks, group, "DeviceKind"); + name_family = fu_synapticsmst_family_to_string (self->family); + if (g_strcmp0 (guid_template, "system") == 0) { + g_autofree gchar *guid = NULL; + guid = g_strdup_printf ("MST-%s-%s-%u", + name_family, + self->system_type, + self->board_id); + fu_device_add_instance_id (FU_DEVICE (self), guid); + + /* docks or something else */ + } else if (guid_template != NULL) { + g_auto(GStrv) templates = NULL; + templates = g_strsplit (guid_template, ",", -1); + for (guint i = 0; templates[i] != NULL; i++) { + g_autofree gchar *dock_id1 = NULL; + g_autofree gchar *dock_id2 = NULL; + dock_id1 = g_strdup_printf ("MST-%s-%u", + templates[i], + self->board_id); + fu_device_add_instance_id (FU_DEVICE (self), dock_id1); + dock_id2 = g_strdup_printf ("MST-%s-vmm%04x-%u", + templates[i], + self->chip_id, + self->board_id); + fu_device_add_instance_id (FU_DEVICE (self), dock_id2); + } + } + + /* detect chip family */ + switch (self->family) { + case FU_SYNAPTICSMST_FAMILY_TESLA: + fu_device_set_firmware_size_max (device, 0x10000); + fu_device_add_instance_id (device, "MST-tesla"); + break; + case FU_SYNAPTICSMST_FAMILY_LEAF: + fu_device_set_firmware_size_max (device, 0x10000); + fu_device_add_instance_id (device, "MST-leaf"); + break; + case FU_SYNAPTICSMST_FAMILY_PANAMERA: + fu_device_set_firmware_size_max (device, 0x80000); + fu_device_add_instance_id (device, "MST-panamera"); + break; + default: + break; + } + + /* add non-standard GUIDs */ + guid1 = g_strdup_printf ("MST-%s-vmm%04x-%u", name_family, self->chip_id, self->board_id); + fu_device_add_instance_id (FU_DEVICE (self), guid1); + guid2 = g_strdup_printf ("MST-%s-%u", name_family, self->board_id); + fu_device_add_instance_id (FU_DEVICE (self), guid2); + guid3 = g_strdup_printf ("MST-%s", name_family); + fu_device_add_instance_id (FU_DEVICE (self), guid3); + + /* success */ + return TRUE; +} + +static void +fu_synapticsmst_device_class_init (FuSynapticsmstDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUdevDeviceClass *klass_udev_device = FU_UDEV_DEVICE_CLASS (klass); + object_class->finalize = fu_synapticsmst_device_finalize; + klass_device->to_string = fu_synapticsmst_device_to_string; + klass_device->open = fu_synapticsmst_device_open; + klass_device->close = fu_synapticsmst_device_close; + klass_device->rescan = fu_synapticsmst_device_rescan; + klass_device->write_firmware = fu_synapticsmst_device_write_firmware; + klass_device->prepare_firmware = fu_synapticsmst_device_prepare_firmware; + klass_udev_device->probe = fu_synapticsmst_device_probe; } diff --git a/plugins/synapticsmst/fu-synapticsmst-device.h b/plugins/synapticsmst/fu-synapticsmst-device.h index 87d3d5bbe..cdc0f7761 100644 --- a/plugins/synapticsmst/fu-synapticsmst-device.h +++ b/plugins/synapticsmst/fu-synapticsmst-device.h @@ -1,56 +1,20 @@ /* - * Copyright (C) 2015 Richard Hughes - * Copyright (C) 2016 Mario Limonciello - * Copyright (C) 2017 Peichen Huang + * Copyright (C) 2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -#include - -#include "fu-synapticsmst-common.h" +#include "fu-plugin.h" G_BEGIN_DECLS -#define FU_SYNAPTICSMST_TYPE_DEVICE (fu_synapticsmst_device_get_type ()) -G_DECLARE_FINAL_TYPE (FuSynapticsmstDevice, fu_synapticsmst_device, FU, SYNAPTICSMST_DEVICE, GObject) +#define FU_TYPE_SYNAPTICSMST_DEVICE (fu_synapticsmst_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuSynapticsmstDevice, fu_synapticsmst_device, FU, SYNAPTICSMST_DEVICE, FuUdevDevice) -#define SYSFS_DRM_DP_AUX "/sys/class/drm_dp_aux_dev" - -FuSynapticsmstDevice *fu_synapticsmst_device_new (FuSynapticsmstMode kind, - const gchar *aux_node, - guint8 layer, - guint16 rad); - -/* helpers */ -GPtrArray *fu_synapticsmst_device_get_guids (FuSynapticsmstDevice *self); -gboolean fu_synapticsmst_device_scan_cascade_device (FuSynapticsmstDevice *self, - GError **error, - guint8 tx_port); -gboolean fu_synapticsmst_device_open (FuSynapticsmstDevice *self, - GError **error); - -/* getters */ -FuSynapticsmstMode fu_synapticsmst_device_get_kind (FuSynapticsmstDevice *self); -guint16 fu_synapticsmst_device_get_board_id (FuSynapticsmstDevice *self); -const gchar *fu_synapticsmst_device_get_version (FuSynapticsmstDevice *self); -const gchar *fu_synapticsmst_device_get_chip_id_str (FuSynapticsmstDevice *self); -const gchar *fu_synapticsmst_device_get_aux_node (FuSynapticsmstDevice *self); -guint16 fu_synapticsmst_device_get_rad (FuSynapticsmstDevice *self); -guint8 fu_synapticsmst_device_get_layer (FuSynapticsmstDevice *self); -gboolean fu_synapticsmst_device_get_cascade (FuSynapticsmstDevice *self); - -/* object methods */ -gboolean fu_synapticsmst_device_enumerate_device (FuSynapticsmstDevice *devices, - GError **error); -gboolean fu_synapticsmst_device_write_firmware (FuSynapticsmstDevice *self, - GBytes *fw, - GFileProgressCallback progress_cb, - gpointer user_data, - gboolean reboot, - gboolean install_force, - GError **error); +FuSynapticsmstDevice *fu_synapticsmst_device_new (FuUdevDevice *device); +void fu_synapticsmst_device_set_system_type (FuSynapticsmstDevice *self, + const gchar *system_type); G_END_DECLS diff --git a/plugins/synapticsmst/synapticsmst.quirk b/plugins/synapticsmst/synapticsmst.quirk index 226f7966d..3461d31c3 100644 --- a/plugins/synapticsmst/synapticsmst.quirk +++ b/plugins/synapticsmst/synapticsmst.quirk @@ -37,12 +37,9 @@ DeviceKind = system [SynapticsMSTBoardID=259] Name = Dell dock -DeviceKind = panamera [SynapticsMSTBoardID=513] Name = ThinkPad Workstation Dock -DeviceKind = tesla [SynapticsMSTBoardID=595] Name = ThinkPad Thunderbolt 3 Workstation Dock -DeviceKind = panamera From dca4d6a5d69c46f7368b2f65b49b98d58f9217b8 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 28 Aug 2019 08:55:24 -0500 Subject: [PATCH 079/158] trivial: fu-util: Drop GUID support from get-devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To the average user this is not a useful feature. None of the command line options will take a GUID as an argument. Really it's an internal representation of device to firwmare upgrade CAB file. This simplifies this output: ``` ├─VMM3320 inside Dell WD15/TB16/TB18 wired Dock: │ Device ID: 585c346c009da3cc19a400fb59a4ed8d0cbd0151 │ Summary: Multi-Stream Transport Device │ Current version: 3.12.002 │ Vendor: Synaptics │ Update State: success │ Flags: updatable|registered │ GUIDs: c1e81f96-234e-597d-b9cb-6477d0652868 │ 152a9833-41e6-5c21-9dde-0ff8984f3e20 │ c1e2bea5-4cf2-5f84-b965-533e8e5fea45 │ 0a52c8c7-26d5-59a0-ae44-6b00e276d775 │ 82460515-30d3-5cdf-be4c-571340842a10 │ 51000744-2de6-5a52-8a86-d5f69fd73d34 │ d6072785-6fc0-5f83-9d49-11376e7f48b1 │ 6bc9fc61-618a-535f-a629-98345e11336e │ 4e4e06b6-8217-5cf0-8eff-ce5e987d531a ``` To this: ``` ├─VMM3320 inside Dell WD15/TB16/TB18 wired Dock: │ Device ID: 585c346c009da3cc19a400fb59a4ed8d0cbd0151 │ Summary: Multi-Stream Transport Device │ Current version: 3.12.002 │ Vendor: Synaptics │ Update State: success │ Flags: updatable|registered ``` Those who are developing upgrades to devices can run fwupdmgr --verbose and will get the verbose output that includes GUIDs: ``` VMM3320 inside Dell WD15/TB16/TB18 wired Dock DeviceId: 585c346c009da3cc19a400fb59a4ed8d0cbd0151 Guid: c1e81f96-234e-597d-b9cb-6477d0652868 Guid: 152a9833-41e6-5c21-9dde-0ff8984f3e20 Guid: c1e2bea5-4cf2-5f84-b965-533e8e5fea45 Guid: 0a52c8c7-26d5-59a0-ae44-6b00e276d775 Guid: 82460515-30d3-5cdf-be4c-571340842a10 Guid: 51000744-2de6-5a52-8a86-d5f69fd73d34 Guid: d6072785-6fc0-5f83-9d49-11376e7f48b1 Guid: 6bc9fc61-618a-535f-a629-98345e11336e Guid: 4e4e06b6-8217-5cf0-8eff-ce5e987d531a Summary: Multi-Stream Transport Device Plugin: synapticsmst Flags: updatable|registered Vendor: Synaptics Version: 3.12.002 VersionFormat: triplet Icon: video-display Created: 2019-08-28 Modified: 2019-08-28 UpdateState: success ``` If they run this as an administrator they'll also get the strings used to build the GUID: ``` VMM3320 inside Dell WD15/TB16/TB18 wired Dock DeviceId: 585c346c009da3cc19a400fb59a4ed8d0cbd0151 Guid: c1e81f96-234e-597d-b9cb-6477d0652868 <- MST-wd15-274 Guid: 152a9833-41e6-5c21-9dde-0ff8984f3e20 <- MST-wd15-vmm3320-274 Guid: c1e2bea5-4cf2-5f84-b965-533e8e5fea45 <- MST-tb16-274 Guid: 0a52c8c7-26d5-59a0-ae44-6b00e276d775 <- MST-tb16-vmm3320-274 Guid: 82460515-30d3-5cdf-be4c-571340842a10 <- MST-tb18-274 Guid: 51000744-2de6-5a52-8a86-d5f69fd73d34 <- MST-tb18-vmm3320-274 Guid: d6072785-6fc0-5f83-9d49-11376e7f48b1 <- MST-leaf Guid: 6bc9fc61-618a-535f-a629-98345e11336e <- MST-leaf-vmm3320-274 Guid: 4e4e06b6-8217-5cf0-8eff-ce5e987d531a <- MST-leaf-274 Summary: Multi-Stream Transport Device Plugin: synapticsmst Flags: updatable|registered Vendor: Synaptics Version: 3.12.002 VersionFormat: triplet Icon: video-display Created: 2019-08-28 Modified: 2019-08-28 UpdateState: success ``` --- src/fu-util-common.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 5a94a715f..12d31297a 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -858,7 +858,6 @@ gchar * fu_util_device_to_string (FwupdDevice *dev, guint idt) { FwupdUpdateState state; - GPtrArray *guids = fwupd_device_get_guids (dev); GString *str = g_string_new (NULL); const gchar *tmp; const gchar *tmp2; @@ -978,16 +977,6 @@ fu_util_device_to_string (FwupdDevice *dev, guint idt) fu_common_string_append_kv (str, idt + 1, _("Flags"), flags_str->str); } - /* all GUIDs for this hardware */ - for (guint i = 0; i < guids->len; i++) { - tmp = g_ptr_array_index (guids, i); - if (i == 0) { - /* TRANSLATORS: global ID common to all similar hardware */ - fu_common_string_append_kv (str, idt + 1, ngettext ("GUID", "GUIDs", guids->len), tmp); - } else { - fu_common_string_append_kv (str, idt + 1, "", tmp); - } - } return g_string_free (str, FALSE); } From f679e5efa88b10daa45dbb90f5490806d08acca5 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 28 Aug 2019 15:31:34 +0100 Subject: [PATCH 080/158] Include the kernel release as a runtime version Although not always useful (due to patches being backported) being able to depend on a specific kernel version might save our bacon in the future. --- src/fu-engine.c | 9 ++++++--- src/fu-self-test.c | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/fu-engine.c b/src/fu-engine.c index 1ab2816a0..9191e8e1e 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -1305,9 +1305,6 @@ fu_engine_get_report_metadata (FuEngine *self) g_hash_table_insert (hash, g_strdup ("CpuArchitecture"), g_strdup (name_tmp.machine)); - g_hash_table_insert (hash, - g_strdup ("KernelVersion"), - g_strdup (name_tmp.release)); } /* add the kernel boot time so we can detect a reboot */ @@ -4740,6 +4737,7 @@ fu_engine_idle_status_notify_cb (FuIdle *idle, GParamSpec *pspec, FuEngine *self static void fu_engine_init (FuEngine *self) { + struct utsname uname_tmp; self->percentage = 0; self->status = FWUPD_STATUS_IDLE; self->config = fu_config_new (); @@ -4773,6 +4771,11 @@ fu_engine_init (FuEngine *self) fu_engine_add_runtime_version (self, "org.freedesktop.gusb", g_usb_version_string ()); #endif + /* optional kernel version */ + memset (&uname_tmp, 0, sizeof(uname_tmp)); + if (uname (&uname_tmp) >= 0) + fu_engine_add_runtime_version (self, "org.kernel", uname_tmp.release); + g_hash_table_insert (self->compile_versions, g_strdup ("com.redhat.fwupdate"), g_strdup ("12")); diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 618844c31..8d5fdb63d 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -420,6 +420,7 @@ fu_engine_requirements_device_func (void) " " " bootloader" " vendor-id" + " org.kernel" " " " " " 12345678-1234-1234-1234-123456789012" From 6865dd3bacab9a796e967e1fe5549c4902759210 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 28 Aug 2019 09:48:07 -0500 Subject: [PATCH 081/158] thunderbolt: Add support for kernel safety checks (Fixes: #1312) There are commits to the Thunderbolt kernel driver that make sure that the upgrade process goes smoothly. If these commits aren't present then it will look like a fwupd problem, when it's actually a kernel problem. When this issue was reported it appeared that commit https://github.com/torvalds/linux/commit/e4be8c9b6a512e274cb6bbac4ac869d73880a8b3 was missing from the locally tested kernel, but it's impossible to determine that from userspace. Prevent running the thunderbolt plugin on older kernels than that set in `$sysconfdir/fwupd/thunderbolt.conf`. By default that is set to 4.13.0, but if a distribution vendor has backported all the necessary support it can be decreased to a lower version for distro packages. --- contrib/fwupd.spec.in | 1 + plugins/thunderbolt/fu-plugin-thunderbolt.c | 38 +++++++++++++++++++++ plugins/thunderbolt/meson.build | 3 ++ plugins/thunderbolt/thunderbolt.conf | 6 ++++ 4 files changed, 48 insertions(+) create mode 100644 plugins/thunderbolt/thunderbolt.conf diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index a1aa09c4f..87888f10f 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -232,6 +232,7 @@ rm ${RPM_BUILD_ROOT}%{_sbindir}/flashrom %if 0%{?have_redfish} %config(noreplace)%{_sysconfdir}/fwupd/redfish.conf %endif +%config(noreplace)%{_sysconfdir}/fwupd/thunderbolt.conf %dir %{_libexecdir}/fwupd %{_libexecdir}/fwupd/fwupd %{_libexecdir}/fwupd/fwupdtool diff --git a/plugins/thunderbolt/fu-plugin-thunderbolt.c b/plugins/thunderbolt/fu-plugin-thunderbolt.c index 9279bbed9..1ce54ae6b 100644 --- a/plugins/thunderbolt/fu-plugin-thunderbolt.c +++ b/plugins/thunderbolt/fu-plugin-thunderbolt.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -37,6 +38,37 @@ struct FuPluginData { GUdevClient *udev; }; +static gboolean +fu_plugin_thunderbolt_safe_kernel (FuPlugin *plugin, GError **error) +{ + g_autofree gchar *minimum_kernel = NULL; + struct utsname name_tmp; + + memset (&name_tmp, 0, sizeof(struct utsname)); + if (uname (&name_tmp) < 0) { + g_debug ("Failed to read current kernel version"); + return TRUE; + } + + minimum_kernel = fu_plugin_get_config_value (plugin, "MinimumKernelVersion"); + if (minimum_kernel == NULL) { + g_debug ("Ignoring kernel safety checks"); + return TRUE; + } + + if (fu_common_vercmp (name_tmp.release, minimum_kernel) < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "kernel %s may not have full Thunderbolt support", + name_tmp.release); + return FALSE; + } + g_debug ("Using kernel %s (minimum %s)", name_tmp.release, minimum_kernel); + + return TRUE; +} + static gchar * fu_plugin_thunderbolt_gen_id_from_syspath (const gchar *syspath) { @@ -627,6 +659,12 @@ fu_plugin_thunderbolt_coldplug (FuPlugin *plugin, GError **error) return TRUE; } +gboolean +fu_plugin_startup (FuPlugin *plugin, GError **error) +{ + return fu_plugin_thunderbolt_safe_kernel (plugin, error); +} + gboolean fu_plugin_coldplug (FuPlugin *plugin, GError **error) { diff --git a/plugins/thunderbolt/meson.build b/plugins/thunderbolt/meson.build index c15e4d87d..42718abf6 100644 --- a/plugins/thunderbolt/meson.build +++ b/plugins/thunderbolt/meson.build @@ -45,6 +45,9 @@ executable('tbtfwucli', ], ) +install_data(['thunderbolt.conf'], + install_dir: join_paths(sysconfdir, 'fwupd') +) # we use functions from 2.52 in the tests if get_option('tests') and umockdev.found() and gio.version().version_compare('>= 2.52') cargs += '-DFU_OFFLINE_DESTDIR="/tmp/fwupd-self-test"' diff --git a/plugins/thunderbolt/thunderbolt.conf b/plugins/thunderbolt/thunderbolt.conf new file mode 100644 index 000000000..72dc0e4ee --- /dev/null +++ b/plugins/thunderbolt/thunderbolt.conf @@ -0,0 +1,6 @@ +[thunderbolt] + +# Minimum kernel version to allow use of this plugin +# It's important that all backports from this kernel have been +# made if using an older kernel +MinimumKernelVersion=4.13.0 From 0f431da26fe1e297e5824639d3358a980af9d6df Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 28 Aug 2019 17:03:08 +0100 Subject: [PATCH 082/158] trivial: Post branch version bump We've added enough new code to justify a minor bump. --- RELEASE | 4 ++-- meson.build | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/RELEASE b/RELEASE index b425e191b..795b30e86 100644 --- a/RELEASE +++ b/RELEASE @@ -2,7 +2,7 @@ fwupd Release Notes Write release entries: -git log --format="%s" --cherry-pick --right-only 1.2.10... | grep -i -v trivial | grep -v Merge | sort | uniq +git log --format="%s" --cherry-pick --right-only 1.2.11... | grep -i -v trivial | grep -v Merge | sort | uniq Add any user visible changes into ../data/org.freedesktop.fwupd.metainfo.xml appstream-util appdata-to-news ../data/org.freedesktop.fwupd.metainfo.xml > NEWS @@ -17,7 +17,7 @@ git add ../po/*.po 2. Commit changes to git: # MAKE SURE THIS IS CORRECT -export release_ver="1.2.11" +export release_ver="1.3.1" git commit -a -m "Release fwupd ${release_ver}" git tag -s -f -m "Release fwupd ${release_ver}" "${release_ver}" diff --git a/meson.build b/meson.build index 14c901146..d30a64327 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('fwupd', 'c', - version : '1.2.11', + version : '1.3.1', license : 'LGPL-2.1+', meson_version : '>=0.47.0', default_options : ['warning_level=2', 'c_std=c99'], From 7831c76368bb0eacd08c9d30d4ae3e4b99e2176a Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 28 Aug 2019 17:03:32 +0100 Subject: [PATCH 083/158] trivial: Fix up the GtkDoc Since lines for API only available in 1.3.x --- src/fu-device.c | 2 +- src/fu-firmware-image.c | 22 +++++++++++----------- src/fu-firmware.c | 28 ++++++++++++++-------------- src/fu-udev-device.c | 2 +- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/fu-device.c b/src/fu-device.c index 4e3058337..9cf4ac830 100644 --- a/src/fu-device.c +++ b/src/fu-device.c @@ -2109,7 +2109,7 @@ fu_device_probe (FuDevice *self, GError **error) * * Returns: %TRUE for success * - * Since: 1.2.11 + * Since: 1.3.1 **/ gboolean fu_device_rescan (FuDevice *self, GError **error) diff --git a/src/fu-firmware-image.c b/src/fu-firmware-image.c index 5c0be1018..7012ff627 100644 --- a/src/fu-firmware-image.c +++ b/src/fu-firmware-image.c @@ -33,7 +33,7 @@ G_DEFINE_TYPE_WITH_PRIVATE (FuFirmwareImage, fu_firmware_image, G_TYPE_OBJECT) * @self: a #FuPlugin * @id: image ID, e.g. "config" * - * Since: 1.2.11 + * Since: 1.3.1 **/ void fu_firmware_image_set_id (FuFirmwareImage *self, const gchar *id) @@ -53,7 +53,7 @@ fu_firmware_image_set_id (FuFirmwareImage *self, const gchar *id) * * Returns: image ID, e.g. "config" * - * Since: 1.2.11 + * Since: 1.3.1 **/ const gchar * fu_firmware_image_get_id (FuFirmwareImage *self) @@ -70,7 +70,7 @@ fu_firmware_image_get_id (FuFirmwareImage *self) * * Sets the base address of the image. * - * Since: 1.2.11 + * Since: 1.3.1 **/ void fu_firmware_image_set_addr (FuFirmwareImage *self, guint64 addr) @@ -88,7 +88,7 @@ fu_firmware_image_set_addr (FuFirmwareImage *self, guint64 addr) * * Returns: integer * - * Since: 1.2.11 + * Since: 1.3.1 **/ guint64 fu_firmware_image_get_addr (FuFirmwareImage *self) @@ -105,7 +105,7 @@ fu_firmware_image_get_addr (FuFirmwareImage *self) * * Sets the index of the image which is used for ordering. * - * Since: 1.2.11 + * Since: 1.3.1 **/ void fu_firmware_image_set_idx (FuFirmwareImage *self, guint64 idx) @@ -123,7 +123,7 @@ fu_firmware_image_set_idx (FuFirmwareImage *self, guint64 idx) * * Returns: integer * - * Since: 1.2.11 + * Since: 1.3.1 **/ guint64 fu_firmware_image_get_idx (FuFirmwareImage *self) @@ -140,7 +140,7 @@ fu_firmware_image_get_idx (FuFirmwareImage *self) * * Sets the contents of the image if not created with fu_firmware_image_new(). * - * Since: 1.2.11 + * Since: 1.3.1 **/ void fu_firmware_image_set_bytes (FuFirmwareImage *self, GBytes *bytes) @@ -161,7 +161,7 @@ fu_firmware_image_set_bytes (FuFirmwareImage *self, GBytes *bytes) * * Returns: (transfer full): a #GBytes of the bytes, or %NULL if the bytes is not set * - * Since: 1.2.11 + * Since: 1.3.1 **/ GBytes * fu_firmware_image_get_bytes (FuFirmwareImage *self, GError **error) @@ -196,7 +196,7 @@ fu_firmware_image_get_bytes (FuFirmwareImage *self, GError **error) * * Return value: (transfer full): a #GBytes, or %NULL * - * Since: 1.2.11 + * Since: 1.3.1 **/ GBytes * fu_firmware_image_get_bytes_chunk (FuFirmwareImage *self, @@ -264,7 +264,7 @@ fu_firmware_image_add_string (FuFirmwareImage *self, guint idt, GString *str) * * Returns: a string value, or %NULL for invalid. * - * Since: 1.2.11 + * Since: 1.3.1 **/ gchar * fu_firmware_image_to_string (FuFirmwareImage *self) @@ -306,7 +306,7 @@ fu_firmware_image_class_init (FuFirmwareImageClass *klass) * * Returns: a #FuFirmwareImage * - * Since: 1.2.11 + * Since: 1.3.1 **/ FuFirmwareImage * fu_firmware_image_new (GBytes *bytes) diff --git a/src/fu-firmware.c b/src/fu-firmware.c index c930a5cdb..6179ef55c 100644 --- a/src/fu-firmware.c +++ b/src/fu-firmware.c @@ -39,7 +39,7 @@ G_DEFINE_TYPE_WITH_PRIVATE (FuFirmware, fu_firmware, G_TYPE_OBJECT) * * Returns: %TRUE for success * - * Since: 1.2.11 + * Since: 1.3.1 **/ gboolean fu_firmware_parse_full (FuFirmware *self, @@ -77,7 +77,7 @@ fu_firmware_parse_full (FuFirmware *self, * * Returns: %TRUE for success * - * Since: 1.2.11 + * Since: 1.3.1 **/ gboolean fu_firmware_parse (FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError **error) @@ -94,7 +94,7 @@ fu_firmware_parse (FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError * * Returns: (transfer full): a #GBytes * - * Since: 1.2.11 + * Since: 1.3.1 **/ GBytes * fu_firmware_write (FuFirmware *self, GError **error) @@ -121,7 +121,7 @@ fu_firmware_write (FuFirmware *self, GError **error) * * If an image with the same ID is already present it is replaced. * - * Since: 1.2.11 + * Since: 1.3.1 **/ void fu_firmware_add_image (FuFirmware *self, FuFirmwareImage *img) @@ -140,7 +140,7 @@ fu_firmware_add_image (FuFirmware *self, FuFirmwareImage *img) * * Returns: (transfer container) (element-type FuFirmwareImage): images * - * Since: 1.2.11 + * Since: 1.3.1 **/ GPtrArray * fu_firmware_get_images (FuFirmware *self) @@ -168,7 +168,7 @@ fu_firmware_get_images (FuFirmware *self) * * Returns: (transfer full): a #FuFirmwareImage, or %NULL if the image is not found * - * Since: 1.2.11 + * Since: 1.3.1 **/ FuFirmwareImage * fu_firmware_get_image_by_id (FuFirmware *self, const gchar *id, GError **error) @@ -200,7 +200,7 @@ fu_firmware_get_image_by_id (FuFirmware *self, const gchar *id, GError **error) * * Returns: (transfer full): a #GBytes of a #FuFirmwareImage, or %NULL if the image is not found * - * Since: 1.2.11 + * Since: 1.3.1 **/ GBytes * fu_firmware_get_image_by_id_bytes (FuFirmware *self, const gchar *id, GError **error) @@ -221,7 +221,7 @@ fu_firmware_get_image_by_id_bytes (FuFirmware *self, const gchar *id, GError **e * * Returns: (transfer full): a #FuFirmwareImage, or %NULL if the image is not found * - * Since: 1.2.11 + * Since: 1.3.1 **/ FuFirmwareImage * fu_firmware_get_image_by_idx (FuFirmware *self, guint64 idx, GError **error) @@ -253,7 +253,7 @@ fu_firmware_get_image_by_idx (FuFirmware *self, guint64 idx, GError **error) * * Returns: (transfer full): a #GBytes of a #FuFirmwareImage, or %NULL if the image is not found * - * Since: 1.2.11 + * Since: 1.3.1 **/ GBytes * fu_firmware_get_image_by_idx_bytes (FuFirmware *self, guint64 idx, GError **error) @@ -276,7 +276,7 @@ fu_firmware_get_image_by_idx_bytes (FuFirmware *self, guint64 idx, GError **erro * * Returns: (transfer full): a #FuFirmwareImage, or %NULL if the image is not found * - * Since: 1.2.11 + * Since: 1.3.1 **/ FuFirmwareImage * fu_firmware_get_image_default (FuFirmware *self, GError **error) @@ -308,7 +308,7 @@ fu_firmware_get_image_default (FuFirmware *self, GError **error) * * Returns: (transfer full): a #GBytes of the image, or %NULL if the image is not found * - * Since: 1.2.11 + * Since: 1.3.1 **/ GBytes * fu_firmware_get_image_default_bytes (FuFirmware *self, GError **error) @@ -327,7 +327,7 @@ fu_firmware_get_image_default_bytes (FuFirmware *self, GError **error) * * Returns: a string value, or %NULL for invalid. * - * Since: 1.2.11 + * Since: 1.3.1 **/ gchar * fu_firmware_to_string (FuFirmware *self) @@ -381,7 +381,7 @@ fu_firmware_class_init (FuFirmwareClass *klass) * * Returns: a #FuFirmware * - * Since: 1.2.11 + * Since: 1.3.1 **/ FuFirmware * fu_firmware_new (void) @@ -398,7 +398,7 @@ fu_firmware_new (void) * * Returns: a #FuFirmware * - * Since: 1.2.11 + * Since: 1.3.1 **/ FuFirmware * fu_firmware_new_from_bytes (GBytes *fw) diff --git a/src/fu-udev-device.c b/src/fu-udev-device.c index d205970da..470ce4d1f 100644 --- a/src/fu-udev-device.c +++ b/src/fu-udev-device.c @@ -344,7 +344,7 @@ fu_udev_device_get_subsystem (FuUdevDevice *self) * * Returns: a device file, or NULL if unset * - * Since: 1.2.11 + * Since: 1.3.1 **/ const gchar * fu_udev_device_get_device_file (FuUdevDevice *self) From 37a3ecf0ef11885c34d49141f5d3ee49a3a7aa96 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 29 Aug 2019 10:27:41 -0500 Subject: [PATCH 084/158] trivial: add libtss2-dev into debian/control (Closes: #1324) --- contrib/ci/dependencies.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/ci/dependencies.xml b/contrib/ci/dependencies.xml index 316220366..ecb5e4285 100644 --- a/contrib/ci/dependencies.xml +++ b/contrib/ci/dependencies.xml @@ -1302,10 +1302,12 @@ tpm2-tss-devel + + From d1681e6de558eca6aad42adcc2c1c47534972d4a Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 29 Aug 2019 08:51:48 -0500 Subject: [PATCH 085/158] libfwupd: Add a new private function `fwupd_remote_set_remotes_dir` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows the daemon to set the base directory to store remotes. This fixes issues with systemd where the installation prefix was set to soemthing not writable such as `/usr/local` but systemd `STATE_DIRECTORY` doesn't match up. ``` $ fwupdmgr refresh Fetching metadata https://cdn.fwupd.org/downloads/firmware.xml.gz Downloading… [***************************************] Less than one minute remaining… Fetching signature https://cdn.fwupd.org/downloads/firmware.xml.gz.asc Failed to update metadata for lvfs: Error creating directory /usr/local/var/lib/fwupd/remotes.d: Read-only file system ``` It should also hopefully help with immutable systems. --- libfwupd/fwupd-remote-private.h | 2 ++ libfwupd/fwupd-remote.c | 54 ++++++++++++++++++++++++++++++--- libfwupd/fwupd-remote.h | 1 + libfwupd/fwupd-self-test.c | 21 +++++++++++++ libfwupd/fwupd.map | 7 +++++ src/fu-config.c | 7 +++++ 6 files changed, 88 insertions(+), 4 deletions(-) diff --git a/libfwupd/fwupd-remote-private.h b/libfwupd/fwupd-remote-private.h index 843405f7b..9a34355fb 100644 --- a/libfwupd/fwupd-remote-private.h +++ b/libfwupd/fwupd-remote-private.h @@ -24,4 +24,6 @@ void fwupd_remote_set_mtime (FwupdRemote *self, gchar **fwupd_remote_get_order_after (FwupdRemote *self); gchar **fwupd_remote_get_order_before (FwupdRemote *self); +void fwupd_remote_set_remotes_dir (FwupdRemote *self, + const gchar *directory); G_END_DECLS diff --git a/libfwupd/fwupd-remote.c b/libfwupd/fwupd-remote.c index ff4283bb1..df5dd132f 100644 --- a/libfwupd/fwupd-remote.c +++ b/libfwupd/fwupd-remote.c @@ -47,6 +47,7 @@ typedef struct { guint64 mtime; gchar **order_after; gchar **order_before; + gchar *remotes_dir; } FwupdRemotePrivate; enum { @@ -447,11 +448,15 @@ fwupd_remote_load_from_filename (FwupdRemote *self, if (password != NULL) fwupd_remote_set_password (self, password); + if (priv->remotes_dir == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Remotes directory not set"); + return FALSE; + } /* set cache to /var/lib... */ - filename_cache = g_build_filename (LOCALSTATEDIR, - "lib", - "fwupd", - "remotes.d", + filename_cache = g_build_filename (priv->remotes_dir, priv->id, "metadata.xml.gz", NULL); @@ -677,6 +682,24 @@ fwupd_remote_get_age (FwupdRemote *self) return now - priv->mtime; } +/** + * fwupd_remote_set_remotes_dir: + * @self: A #FwupdRemote + * @directory: Remotes directory + * + * Sets the directory to store remote data + * + * Since: 1.3.1 + **/ +void +fwupd_remote_set_remotes_dir (FwupdRemote *self, const gchar *directory) +{ + FwupdRemotePrivate *priv = GET_PRIVATE (self); + g_return_if_fail (FWUPD_IS_REMOTE (self)); + g_free (priv->remotes_dir); + priv->remotes_dir = g_strdup (directory); +} + /** * fwupd_remote_set_priority: * @self: A #FwupdRemote @@ -783,6 +806,24 @@ fwupd_remote_get_agreement (FwupdRemote *self) return priv->agreement; } +/** + * fwupd_remote_get_remotes_dir: + * @self: A #FwupdRemote + * + * Gets the base directory for storing remote metadata + * + * Returns: a string, or %NULL if unset + * + * Since: 1.3.1 + **/ +const gchar * +fwupd_remote_get_remotes_dir (FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FWUPD_IS_REMOTE (self), NULL); + return priv->remotes_dir; +} + /** * fwupd_remote_get_checksum: * @self: A #FwupdRemote @@ -1083,6 +1124,10 @@ fwupd_remote_to_variant (FwupdRemote *self) g_variant_builder_add (&builder, "{sv}", "FilenameSource", g_variant_new_string (priv->filename_source)); } + if (priv->remotes_dir != NULL) { + g_variant_builder_add (&builder, "{sv}", "RemotesDir", + g_variant_new_string (priv->remotes_dir)); + } g_variant_builder_add (&builder, "{sv}", "Enabled", g_variant_new_boolean (priv->enabled)); g_variant_builder_add (&builder, "{sv}", "ApprovalRequired", @@ -1200,6 +1245,7 @@ fwupd_remote_finalize (GObject *obj) g_free (priv->password); g_free (priv->title); g_free (priv->agreement); + g_free (priv->remotes_dir); g_free (priv->checksum); g_free (priv->filename_cache); g_free (priv->filename_cache_sig); diff --git a/libfwupd/fwupd-remote.h b/libfwupd/fwupd-remote.h index 10e221b3b..c78d8219d 100644 --- a/libfwupd/fwupd-remote.h +++ b/libfwupd/fwupd-remote.h @@ -51,6 +51,7 @@ FwupdRemote *fwupd_remote_new (void); const gchar *fwupd_remote_get_id (FwupdRemote *self); const gchar *fwupd_remote_get_title (FwupdRemote *self); const gchar *fwupd_remote_get_agreement (FwupdRemote *self); +const gchar *fwupd_remote_get_remotes_dir (FwupdRemote *self); const gchar *fwupd_remote_get_checksum (FwupdRemote *self); const gchar *fwupd_remote_get_username (FwupdRemote *self); const gchar *fwupd_remote_get_password (FwupdRemote *self); diff --git a/libfwupd/fwupd-self-test.c b/libfwupd/fwupd-self-test.c index c68f4fc0f..5ca972b18 100644 --- a/libfwupd/fwupd-self-test.c +++ b/libfwupd/fwupd-self-test.c @@ -142,10 +142,17 @@ fwupd_remote_download_func (void) { gboolean ret; g_autofree gchar *fn = NULL; + g_autofree gchar *directory = NULL; g_autoptr(FwupdRemote) remote = NULL; g_autoptr(GError) error = NULL; remote = fwupd_remote_new (); + directory = g_build_filename (LOCALSTATEDIR, + "lib", + "fwupd", + "remotes.d", + NULL); + fwupd_remote_set_remotes_dir (remote, directory); fn = g_build_filename (FU_SELF_TEST_REMOTES_DIR, "remotes.d", "lvfs.conf", NULL); ret = fwupd_remote_load_from_filename (remote, fn, NULL, &error); g_assert_no_error (error); @@ -172,9 +179,16 @@ fwupd_remote_baseuri_func (void) g_autofree gchar *firmware_uri = NULL; g_autofree gchar *fn = NULL; g_autoptr(FwupdRemote) remote = NULL; + g_autofree gchar *directory = NULL; g_autoptr(GError) error = NULL; remote = fwupd_remote_new (); + directory = g_build_filename (LOCALSTATEDIR, + "lib", + "fwupd", + "remotes.d", + NULL); + fwupd_remote_set_remotes_dir (remote, directory); fn = g_build_filename (TESTDATADIR, "tests", "firmware-base-uri.conf", NULL); ret = fwupd_remote_load_from_filename (remote, fn, NULL, &error); g_assert_no_error (error); @@ -202,8 +216,15 @@ fwupd_remote_nopath_func (void) g_autofree gchar *fn = NULL; g_autoptr(FwupdRemote) remote = NULL; g_autoptr(GError) error = NULL; + g_autofree gchar *directory = NULL; remote = fwupd_remote_new (); + directory = g_build_filename (LOCALSTATEDIR, + "lib", + "fwupd", + "remotes.d", + NULL); + fwupd_remote_set_remotes_dir (remote, directory); fn = g_build_filename (TESTDATADIR, "tests", "firmware-nopath.conf", NULL); ret = fwupd_remote_load_from_filename (remote, fn, NULL, &error); g_assert_no_error (error); diff --git a/libfwupd/fwupd.map b/libfwupd/fwupd.map index f7456b53d..d83739604 100644 --- a/libfwupd/fwupd.map +++ b/libfwupd/fwupd.map @@ -371,3 +371,10 @@ LIBFWUPD_1.2.10 { fwupd_remote_array_from_variant; local: *; } LIBFWUPD_1.2.9; + +LIBFWUPD_1.3.1 { + global: + fwupd_remote_get_remotes_dir; + fwupd_remote_set_remotes_dir; + local: *; +} LIBFWUPD_1.2.10; diff --git a/src/fu-config.c b/src/fu-config.c index 0e908397f..01c250d80 100644 --- a/src/fu-config.c +++ b/src/fu-config.c @@ -200,6 +200,8 @@ fu_config_add_remotes_for_path (FuConfig *self, const gchar *path, GError **erro while ((tmp = g_dir_read_name (dir)) != NULL) { g_autofree gchar *filename = g_build_filename (path_remotes, tmp, NULL); g_autoptr(FwupdRemote) remote = fwupd_remote_new (); + g_autofree gchar *localstatedir = NULL; + g_autofree gchar *remotesdir = NULL; /* skip invalid files */ if (!g_str_has_suffix (tmp, ".conf")) { @@ -207,6 +209,11 @@ fu_config_add_remotes_for_path (FuConfig *self, const gchar *path, GError **erro continue; } + /* set directory to store data */ + localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); + remotesdir = g_build_filename (localstatedir, "remotes.d", NULL); + fwupd_remote_set_remotes_dir (remote, remotesdir); + /* load from keyfile */ g_debug ("loading config from %s", filename); if (!fwupd_remote_load_from_filename (remote, filename, From 0f490525f0f235da3539daf768bdab595e8f2671 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 29 Aug 2019 10:54:15 -0500 Subject: [PATCH 086/158] trivial: UEFI skip self tests if library fails to initialize This means no simulator or TPM actually available --- plugins/uefi/fu-self-test.c | 22 +++++++++------------- plugins/uefi/fu-uefi-pcrs.c | 15 +++------------ 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/plugins/uefi/fu-self-test.c b/plugins/uefi/fu-self-test.c index 4ebd50c7f..75eff39bd 100644 --- a/plugins/uefi/fu-self-test.c +++ b/plugins/uefi/fu-self-test.c @@ -16,6 +16,8 @@ #include "fu-uefi-pcrs.h" #include "fu-uefi-vars.h" +#include "fwupd-error.h" + static void fu_uefi_pcrs_1_2_func (void) { @@ -43,26 +45,20 @@ fu_uefi_pcrs_1_2_func (void) static void fu_uefi_pcrs_2_0_func (void) { - g_autofree gchar *devpath = NULL; - g_autofree gchar *sysfstpmdir = NULL; - const gchar *tpm_server_running = g_getenv ("TPM_SERVER_RUNNING"); - gboolean ret; g_autoptr(FuUefiPcrs) pcrs = fu_uefi_pcrs_new (); g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) pcr0s = NULL; g_autoptr(GPtrArray) pcrXs = NULL; + const gchar *tpm_server_running = g_getenv ("TPM_SERVER_RUNNING"); - sysfstpmdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_TPM); - devpath = g_build_filename (sysfstpmdir, "tpm0", NULL); - - if (!g_file_test (devpath, G_FILE_TEST_EXISTS) && (tpm_server_running == NULL)) { - g_test_skip ("no physical or simulated TPM 2.0 device available"); - return; + if (!fu_uefi_pcrs_setup (pcrs, &error)) { + if (tpm_server_running == NULL && + g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_test_skip ("no physical or simulated TPM 2.0 device available"); + return; + } } - - ret = fu_uefi_pcrs_setup (pcrs, &error); g_assert_no_error (error); - g_assert_true (ret); pcr0s = fu_uefi_pcrs_get_checksums (pcrs, 0); g_assert_nonnull (pcr0s); g_assert_cmpint (pcr0s->len, >=, 1); diff --git a/plugins/uefi/fu-uefi-pcrs.c b/plugins/uefi/fu-uefi-pcrs.c index bb99d3554..2b030e22c 100644 --- a/plugins/uefi/fu-uefi-pcrs.c +++ b/plugins/uefi/fu-uefi-pcrs.c @@ -10,6 +10,7 @@ #include "fu-common.h" #include "fu-uefi-pcrs.h" +#include "fwupd-error.h" typedef struct { guint idx; @@ -116,7 +117,7 @@ fu_uefi_pcrs_setup_tpm20 (FuUefiPcrs *self, GError **error) rc = Esys_Initialize (&ctx, NULL, NULL); if (rc != TSS2_RC_SUCCESS) { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "failed to initialize TPM library"); return FALSE; } @@ -180,23 +181,13 @@ fu_uefi_pcrs_setup (FuUefiPcrs *self, GError **error) g_autofree gchar *devpath = NULL; g_autofree gchar *sysfstpmdir = NULL; g_autofree gchar *fn_pcrs = NULL; - const gchar *tpm_server_running = g_getenv ("TPM_SERVER_RUNNING"); g_return_val_if_fail (FU_IS_UEFI_PCRS (self), FALSE); - /* check the TPM device exists at all */ + /* look for TPM 1.2 */ sysfstpmdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_TPM); devpath = g_build_filename (sysfstpmdir, "tpm0", NULL); - if (!g_file_test (devpath, G_FILE_TEST_EXISTS) && (tpm_server_running == NULL)) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "no TPM device found"); - return FALSE; - } fn_pcrs = g_build_filename (devpath, "pcrs", NULL); - - /* look for TPM 1.2 */ if (g_file_test (fn_pcrs, G_FILE_TEST_EXISTS)) { if (!fu_uefi_pcrs_setup_tpm12 (self, fn_pcrs, error)) return FALSE; From dfff18e3e8e9076cf5c262c0960ee4052173a066 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 29 Aug 2019 11:51:41 -0500 Subject: [PATCH 087/158] Add aliases for `get-upgrades` and `upgrade` Mostly for consistency purpose. Details: * It's confusing that internally the functions for `FwupdClient` use `upgrade` in the name. * The logical antonym of `downgrade` is `upgrade` not `update` * People who don't use the tool frequently may try `get-upgrades` --- data/bash-completion/fwupdmgr.in | 2 ++ data/bash-completion/fwupdtool.in | 2 ++ src/fu-tool.c | 4 ++-- src/fu-util.c | 4 ++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/data/bash-completion/fwupdmgr.in b/data/bash-completion/fwupdmgr.in index 259c179a0..45590caab 100644 --- a/data/bash-completion/fwupdmgr.in +++ b/data/bash-completion/fwupdmgr.in @@ -15,6 +15,7 @@ _fwupdmgr_cmd_list=( 'get-results' 'get-topology' 'get-updates' + 'get-upgrades' 'install' 'modify-config' 'modify-remote' @@ -23,6 +24,7 @@ _fwupdmgr_cmd_list=( 'set-approved-firmware' 'unlock' 'update' + 'upgrade' 'verify' 'verify-update' '--version' diff --git a/data/bash-completion/fwupdtool.in b/data/bash-completion/fwupdtool.in index b5b2a1f59..93deffa0c 100644 --- a/data/bash-completion/fwupdtool.in +++ b/data/bash-completion/fwupdtool.in @@ -2,6 +2,7 @@ _fwupdtool_cmd_list=( 'activate' 'build-firmware' 'get-updates' + 'get-updgrades' 'get-details' 'get-device-flags' 'get-devices' @@ -10,6 +11,7 @@ _fwupdtool_cmd_list=( 'get-topology' 'hwids' 'update' + 'upgrade' 'install' 'install-blob' 'monitor' diff --git a/src/fu-tool.c b/src/fu-tool.c index 8ea0bada8..c21f110ac 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -1460,7 +1460,7 @@ main (int argc, char *argv[]) _("Show history of firmware updates"), fu_util_get_history); fu_util_cmd_array_add (cmd_array, - "get-updates", + "get-updates,get-upgrades", NULL, /* TRANSLATORS: command description */ _("Gets the list of updates for connected hardware"), @@ -1526,7 +1526,7 @@ main (int argc, char *argv[]) _("Monitor the daemon for events"), fu_util_monitor); fu_util_cmd_array_add (cmd_array, - "update", + "update,upgrade", NULL, /* TRANSLATORS: command description */ _("Update all devices that match local metadata"), diff --git a/src/fu-util.c b/src/fu-util.c index 1670144a2..54ab65826 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -2207,13 +2207,13 @@ main (int argc, char *argv[]) _("Gets details about a firmware file"), fu_util_get_details); fu_util_cmd_array_add (cmd_array, - "get-updates", + "get-updates,get-upgrades", NULL, /* TRANSLATORS: command description */ _("Gets the list of updates for connected hardware"), fu_util_get_updates); fu_util_cmd_array_add (cmd_array, - "update", + "update,upgrade", NULL, /* TRANSLATORS: command description */ _("Updates all firmware to latest versions available"), From 4250d9da62e95737c6420a2d1e6fdb508005fc6c Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 29 Aug 2019 09:53:44 -0500 Subject: [PATCH 088/158] fu-util/fu-tool: Print devices, remotes, releases using a tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This brings consistency to all fwupd output and allows stuff like this: ``` No upgrades for Thunderbolt controller in Dell dock, current is 43.00: 40.00=older No upgrades for Package level of Dell dock, current is 01.00.08.01: 01.00.04.01=older No upgrades for RTS5413 in Dell dock, current is 01.21: 01.21=same No upgrades for RTS5487 in Dell dock, current is 01.47: 01.47=same No upgrades for VMM5331 in Dell dock, current is 05.04.00: 05.03.10=older No upgrades for WD19TB, current is 01.00.00.02: 01.00.00.00=older ○ └─XPS 13 9380 System Firmware: │ Device ID: 6c24a747f97668873b761558e322398a91dbf394 │ Current version: 0.1.6.0 │ Minimum Version: 0.1.6.0 │ Vendor: Dell Inc. │ Flags: internal|updatable|require-ac|supported|registered|needs-reboot │ └─XPS 13 9380 System Update: Version: 0.1.7.0 Remote ID: lvfs Summary: Firmware for the Dell XPS 13 9380 License: proprietary Size: 0x1563d67 Vendor: Dell Inc. Flags: is-upgrade Description: This stable release fixes the following issues: Fixed the issue where the Dell Power Manager displays an error when a 130W Type-C adapter is connected to the system. new functionality has also been added: Added a new feature to automatically suspend BitLocker before upgrading the firmware. After the firmware upgrade is complete, BitLocker is automatically enabled. ``` --- src/fu-tool.c | 28 +++---- src/fu-util-common.c | 171 +++++++++++++++++++++++++++++++++++++----- src/fu-util-common.h | 4 +- src/fu-util.c | 174 ++++++++++--------------------------------- 4 files changed, 210 insertions(+), 167 deletions(-) diff --git a/src/fu-tool.c b/src/fu-tool.c index c21f110ac..b5f21c795 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -298,6 +298,7 @@ static gboolean fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GNode) root = g_node_new (NULL); /* load engine */ if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) @@ -311,6 +312,7 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) FwupdDevice *dev = g_ptr_array_index (devices, i); g_autoptr(GPtrArray) rels = NULL; g_autoptr(GError) error_local = NULL; + GNode *child; /* not going to have results, so save a engine round-trip */ if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) @@ -326,15 +328,15 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) g_printerr ("%s\n", error_local->message); continue; } - g_print ("%s", fu_util_device_to_string (dev, 0)); - g_print (" Release information:\n"); - /* print all releases */ + child = g_node_append_data (root, dev); + for (guint j = 0; j < rels->len; j++) { FwupdRelease *rel = g_ptr_array_index (rels, j); - g_print ("%s\n", fwupd_release_to_string (rel)); + g_node_append_data (child, g_object_ref (rel)); } } - + if (g_node_n_nodes (root, G_TRAVERSE_ALL) > 1) + fu_util_print_tree (root, priv); /* save the device state for other applications to see */ if (!fu_util_save_current_state (priv, error)) return FALSE; @@ -347,6 +349,7 @@ static gboolean fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GPtrArray) array = NULL; + g_autoptr(GNode) root = g_node_new (NULL); gint fd; /* load engine */ @@ -380,12 +383,12 @@ fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) return FALSE; for (guint i = 0; i < array->len; i++) { FwupdDevice *dev = g_ptr_array_index (array, i); - g_autofree gchar *tmp = NULL; if (!fu_util_filter_device (priv, dev)) continue; - tmp = fu_util_device_to_string (dev, 0); - g_print ("%s\n", tmp); + g_node_append_data (root, dev); } + fu_util_print_tree (root, priv); + return TRUE; } @@ -448,8 +451,7 @@ fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } fu_util_build_device_tree (priv, root, devs, NULL); - g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, - fu_util_print_device_tree, priv); + fu_util_print_tree (root, priv); /* save the device state for other applications to see */ return fu_util_save_current_state (priv, error); @@ -1337,6 +1339,7 @@ static gboolean fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GNode) root = g_node_new (NULL); /* load engine */ if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) @@ -1350,12 +1353,11 @@ fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) /* show each device */ for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index (devices, i); - g_autofree gchar *str = NULL; if (!fu_util_filter_device (priv, dev)) continue; - str = fu_util_device_to_string (dev, 0); - g_print ("%s\n", str); + g_node_append_data (root, dev); } + fu_util_print_tree (root, priv); return TRUE; } diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 12d31297a..5766e71bc 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -138,38 +138,48 @@ fu_util_prompt_for_boolean (gboolean def) return FALSE; } -gboolean -fu_util_print_device_tree (GNode *n, gpointer data) +static gboolean +fu_util_traverse_tree (GNode *n, gpointer data) { - FwupdDevice *dev = FWUPD_DEVICE (n->data); guint idx = g_node_depth (n) - 1; g_autofree gchar *tmp = NULL; g_auto(GStrv) split = NULL; + /* get split lines */ + if (FWUPD_IS_DEVICE (n->data)) { + FwupdDevice *dev = FWUPD_DEVICE (n->data); + tmp = fu_util_device_to_string (dev, idx); + } else if (FWUPD_IS_REMOTE (n->data)) { + FwupdRemote *remote = FWUPD_REMOTE (n->data); + tmp = fu_util_remote_to_string (remote, idx); + } else if (FWUPD_IS_RELEASE (n->data)) { + FwupdRelease *release = FWUPD_RELEASE (n->data); + tmp = fu_util_release_to_string (release, idx); + g_debug ("%s", tmp); + } + /* root node */ - if (dev == NULL && g_getenv ("FWUPD_VERBOSE") == NULL) { + if (n->data == NULL && g_getenv ("FWUPD_VERBOSE") == NULL) { g_print ("○\n"); return FALSE; } if (n->parent == NULL) return FALSE; - /* get split lines */ - tmp = fu_util_device_to_string (dev, idx); if (tmp == NULL) return FALSE; split = g_strsplit (tmp, "\n", -1); for (guint i = 0; split[i] != NULL; i++) { g_autoptr(GString) str = g_string_new (NULL); - /* device header */ + /* header */ if (i == 0) { if (g_node_next_sibling (n) == NULL) g_string_prepend (str, "└─"); else g_string_prepend (str, "├─"); - /* device properties */ + /* properties */ } else { g_string_prepend (str, n->children == NULL ? " " : " │"); g_string_prepend (str, g_node_next_sibling (n) == NULL ? " " : "│"); @@ -199,6 +209,13 @@ fu_util_print_device_tree (GNode *n, gpointer data) return FALSE; } +void +fu_util_print_tree (GNode *n, gpointer data) +{ + g_node_traverse (n, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + fu_util_traverse_tree, data); +} + gboolean fu_util_is_interesting_device (FwupdDevice *dev) { @@ -989,8 +1006,10 @@ fu_util_release_to_string (FwupdRelease *rel, guint idt) g_return_val_if_fail (FWUPD_IS_RELEASE (rel), NULL); + fu_common_string_append_kv (str, idt, fwupd_release_get_name (rel), NULL); + /* TRANSLATORS: version number of new firmware */ - fu_common_string_append_kv (str, idt, _("Version"), + fu_common_string_append_kv (str, idt + 1 , _("Version"), fwupd_release_get_version (rel)); if (fwupd_release_get_remote_id (rel) != NULL) { @@ -1003,21 +1022,16 @@ fu_util_release_to_string (FwupdRelease *rel, guint idt) fu_common_string_append_kv (str, idt + 1, _("Summary"), fwupd_release_get_summary (rel)); } - if (fwupd_release_get_description (rel) != NULL) { - g_autofree gchar *desc = NULL; - desc = fu_util_convert_description (fwupd_release_get_description (rel), NULL); - /* TRANSLATORS: multiline description of device */ - fu_common_string_append_kv (str, idt + 1, _("Description"), desc); - } if (fwupd_release_get_license (rel) != NULL) { /* TRANSLATORS: e.g. GPLv2+, Non free etc */ fu_common_string_append_kv (str, idt + 1, _("License"), fwupd_release_get_license (rel)); } if (fwupd_release_get_size (rel) != 0) { + g_autofree gchar *tmp = NULL; + tmp = g_format_size (fwupd_release_get_size (rel)); /* TRANSLATORS: file size of the download */ - fu_common_string_append_kx (str, idt + 1, _("Size"), - fwupd_release_get_size (rel)); + fu_common_string_append_kv (str, idt + 1, _("Size"), tmp); } if (fwupd_release_get_details_url (rel) != NULL) { /* TRANSLATORS: more details about the update link */ @@ -1059,6 +1073,129 @@ fu_util_release_to_string (FwupdRelease *rel, guint idt) /* TRANSLATORS: release properties */ fu_common_string_append_kv (str, idt + 1, _("Flags"), flags_str->str); } + if (fwupd_release_get_description (rel) != NULL) { + g_autofree gchar *desc = NULL; + desc = fu_util_convert_description (fwupd_release_get_description (rel), NULL); + /* TRANSLATORS: multiline description of device */ + fu_common_string_append_kv (str, idt + 1, _("Description"), desc); + } + + return g_string_free (str, FALSE); +} + +gchar * +fu_util_remote_to_string (FwupdRemote *remote, guint idt) +{ + GString *str = g_string_new (NULL); + FwupdRemoteKind kind = fwupd_remote_get_kind (remote); + FwupdKeyringKind keyring_kind = fwupd_remote_get_keyring_kind (remote); + const gchar *tmp; + gint priority; + gdouble age; + + g_return_val_if_fail (FWUPD_IS_REMOTE (remote), NULL); + + fu_common_string_append_kv (str, idt, + fwupd_remote_get_title (remote), NULL); + + /* TRANSLATORS: remote identifier, e.g. lvfs-testing */ + fu_common_string_append_kv (str, idt + 1, _("Remote ID"), + fwupd_remote_get_id (remote)); + + /* TRANSLATORS: remote type, e.g. remote or local */ + fu_common_string_append_kv (str, idt + 1, _("Type"), + fwupd_remote_kind_to_string (kind)); + + /* TRANSLATORS: keyring type, e.g. GPG or PKCS7 */ + if (keyring_kind != FWUPD_KEYRING_KIND_UNKNOWN) { + fu_common_string_append_kv (str, idt + 1, _("Keyring"), + fwupd_keyring_kind_to_string (keyring_kind)); + } + + /* TRANSLATORS: if the remote is enabled */ + fu_common_string_append_kv (str, idt + 1, _("Enabled"), + fwupd_remote_get_enabled (remote) ? "true" : "false"); + + tmp = fwupd_remote_get_checksum (remote); + if (tmp != NULL) { + /* TRANSLATORS: remote checksum */ + fu_common_string_append_kv (str, idt + 1, _("Checksum"), tmp); + } + + /* optional parameters */ + age = fwupd_remote_get_age (remote); + if (kind == FWUPD_REMOTE_KIND_DOWNLOAD && + age > 0 && age != G_MAXUINT64) { + const gchar *unit = "s"; + g_autofree gchar *age_str = NULL; + if (age > 60) { + age /= 60.f; + unit = "m"; + } + if (age > 60) { + age /= 60.f; + unit = "h"; + } + if (age > 24) { + age /= 24.f; + unit = "d"; + } + if (age > 7) { + age /= 7.f; + unit = "w"; + } + age_str = g_strdup_printf ("%.2f%s", age, unit); + /* TRANSLATORS: the age of the metadata */ + fu_common_string_append_kv (str, idt + 1, _("Age"), age_str); + } + priority = fwupd_remote_get_priority (remote); + if (priority != 0) { + g_autofree gchar *priority_str = NULL; + priority_str = g_strdup_printf ("%i", priority); + /* TRANSLATORS: the numeric priority */ + fu_common_string_append_kv (str, idt + 1, _("Priority"), priority_str); + } + tmp = fwupd_remote_get_username (remote); + if (tmp != NULL) { + /* TRANSLATORS: remote filename base */ + fu_common_string_append_kv (str, idt + 1, _("Username"), tmp); + } + tmp = fwupd_remote_get_password (remote); + if (tmp != NULL) { + g_autofree gchar *hidden = g_strnfill (strlen (tmp), '*'); + /* TRANSLATORS: remote filename base */ + fu_common_string_append_kv (str, idt + 1, _("Password"), hidden); + } + tmp = fwupd_remote_get_filename_cache (remote); + if (tmp != NULL) { + /* TRANSLATORS: filename of the local file */ + fu_common_string_append_kv (str, idt + 1, _("Filename"), tmp); + } + tmp = fwupd_remote_get_filename_cache_sig (remote); + if (tmp != NULL) { + /* TRANSLATORS: filename of the local file */ + fu_common_string_append_kv (str, idt + 1, _("Filename Signature"), tmp); + } + tmp = fwupd_remote_get_metadata_uri (remote); + if (tmp != NULL) { + /* TRANSLATORS: remote URI */ + fu_common_string_append_kv (str, idt + 1, _("Metadata URI"), tmp); + } + tmp = fwupd_remote_get_metadata_uri_sig (remote); + if (tmp != NULL) { + /* TRANSLATORS: remote URI */ + fu_common_string_append_kv (str, idt + 1, _("Metadata Signature"), tmp); + } + tmp = fwupd_remote_get_firmware_base_uri (remote); + if (tmp != NULL) { + /* TRANSLATORS: remote URI */ + fu_common_string_append_kv (str, idt + 1, _("Firmware Base URI"), tmp); + } + tmp = fwupd_remote_get_report_uri (remote); + if (tmp != NULL) { + /* TRANSLATORS: URI to send success/failure reports */ + fu_common_string_append_kv (str, idt + 1, _("Report URI"), tmp); + } return g_string_free (str, FALSE); } diff --git a/src/fu-util-common.h b/src/fu-util-common.h index ba4c6031d..6b3beaeea 100644 --- a/src/fu-util-common.h +++ b/src/fu-util-common.h @@ -31,7 +31,7 @@ void fu_util_print_data (const gchar *title, guint fu_util_prompt_for_number (guint maxnum); gboolean fu_util_prompt_for_boolean (gboolean def); -gboolean fu_util_print_device_tree (GNode *n, gpointer data); +void fu_util_print_tree (GNode *n, gpointer data); gboolean fu_util_is_interesting_device (FwupdDevice *dev); gchar *fu_util_get_user_cache_path (const gchar *fn); SoupSession *fu_util_setup_networking (GError **error); @@ -73,5 +73,7 @@ gchar *fu_util_device_to_string (FwupdDevice *dev, guint idt); gchar *fu_util_release_to_string (FwupdRelease *rel, guint idt); +gchar *fu_util_remote_to_string (FwupdRemote *remote, + guint idt); G_END_DECLS diff --git a/src/fu-util.c b/src/fu-util.c index 54ab65826..80e4b3223 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -399,8 +399,7 @@ fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } fu_util_build_device_tree (priv, root, devs, NULL); - g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, - fu_util_print_device_tree, priv); + fu_util_print_tree (root, priv); /* nag? */ if (!fu_util_perhaps_show_unreported (priv, error)) @@ -485,6 +484,7 @@ static gboolean fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GPtrArray) array = NULL; + g_autoptr(GNode) root = g_node_new (NULL); /* check args */ if (g_strv_length (values) != 1) { @@ -499,12 +499,12 @@ fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) return FALSE; for (guint i = 0; i < array->len; i++) { FwupdDevice *dev = g_ptr_array_index (array, i); - g_autofree gchar *tmp = NULL; if (!fu_util_filter_device (priv, dev)) continue; - tmp = fu_util_device_to_string (dev, 0); - g_print ("%s\n", tmp); + g_node_append_data (root, dev); } + fu_util_print_tree (root, priv); + return TRUE; } @@ -788,6 +788,7 @@ static gboolean fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GPtrArray) devices = NULL; + g_autoptr(GNode) root = g_node_new (NULL); /* get all devices from the history database */ devices = fwupd_client_get_history (priv->client, NULL, error); @@ -797,12 +798,11 @@ fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) /* show each device */ for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index (devices, i); - g_autofree gchar *str = NULL; if (!fu_util_filter_device (priv, dev)) continue; - str = fu_util_device_to_string (dev, 0); - g_print ("%s\n", str); + g_node_append_data (root, dev); } + fu_util_print_tree (root, priv); return TRUE; } @@ -1194,6 +1194,7 @@ fu_util_get_releases (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(FwupdDevice) dev = NULL; g_autoptr(GPtrArray) rels = NULL; + g_autoptr(GNode) root = g_node_new (NULL); dev = fu_util_get_device_or_prompt (priv, values, error); if (dev == NULL) @@ -1203,16 +1204,19 @@ fu_util_get_releases (FuUtilPrivate *priv, gchar **values, GError **error) rels = fwupd_client_get_releases (priv->client, fwupd_device_get_id (dev), NULL, error); if (rels == NULL) return FALSE; - g_print ("%s:\n", fwupd_device_get_name (dev)); + + if (rels->len == 0) { + /* TRANSLATORS: no repositories to download from */ + g_print ("%s\n", _("No releases available")); + return TRUE; + } + for (guint i = 0; i < rels->len; i++) { FwupdRelease *rel = g_ptr_array_index (rels, i); - g_autofree gchar *tmp = fu_util_release_to_string (rel, 0); - g_print ("%s", tmp); - - /* new line between all but last entries */ - if (i != rels->len - 1) - g_print ("\n"); + g_node_append_data (root, rel); } + fu_util_print_tree (root, priv); + return TRUE; } @@ -1397,6 +1401,7 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GPtrArray) devices = NULL; gboolean supported = FALSE; + g_autoptr(GNode) root = g_node_new (NULL); /* are the remotes very old */ if (!fu_util_perhaps_refresh_remotes (priv, error)) @@ -1408,9 +1413,9 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) return FALSE; for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index (devices, i); - g_autofree gchar *tmp = NULL; g_autoptr(GPtrArray) rels = NULL; g_autoptr(GError) error_local = NULL; + GNode *child; /* not going to have results, so save a D-Bus round-trip */ if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) @@ -1427,20 +1432,18 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) g_printerr ("%s\n", error_local->message); continue; } + child = g_node_append_data (root, dev); - /* TRANSLATORS: list of devices */ - g_print ("%s\n", _("Firmware updates:")); - tmp = fu_util_device_to_string (dev, 0); - g_print ("%s", tmp); - - /* print all releases */ + /* add all releases */ for (guint j = 0; j < rels->len; j++) { FwupdRelease *rel = g_ptr_array_index (rels, j); - g_autofree gchar *tmp2 = fu_util_release_to_string (rel, 1); - g_print ("%s\n", tmp2); + g_node_append_data (child, g_object_ref (rel)); } } + if (g_node_n_nodes (root, G_TRAVERSE_ALL) > 1) + fu_util_print_tree (root, priv); + /* nag? */ if (!fu_util_perhaps_show_unreported (priv, error)) return FALSE; @@ -1461,126 +1464,25 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) static gboolean fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error) { + g_autoptr(GNode) root = g_node_new (NULL); g_autoptr(GPtrArray) remotes = NULL; - /* print any updates */ remotes = fwupd_client_get_remotes (priv->client, NULL, error); if (remotes == NULL) return FALSE; - for (guint i = 0; i < remotes->len; i++) { - FwupdRemote *remote = g_ptr_array_index (remotes, i); - FwupdRemoteKind kind = fwupd_remote_get_kind (remote); - FwupdKeyringKind keyring_kind = fwupd_remote_get_keyring_kind (remote); - const gchar *tmp; - gint priority; - gdouble age; - /* TRANSLATORS: remote identifier, e.g. lvfs-testing */ - fu_util_print_data (_("Remote ID"), - fwupd_remote_get_id (remote)); - - /* TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" */ - fu_util_print_data (_("Title"), - fwupd_remote_get_title (remote)); - - /* TRANSLATORS: remote type, e.g. remote or local */ - fu_util_print_data (_("Type"), - fwupd_remote_kind_to_string (kind)); - - /* TRANSLATORS: keyring type, e.g. GPG or PKCS7 */ - if (keyring_kind != FWUPD_KEYRING_KIND_UNKNOWN) { - fu_util_print_data (_("Keyring"), - fwupd_keyring_kind_to_string (keyring_kind)); - } - - /* TRANSLATORS: if the remote is enabled */ - fu_util_print_data (_("Enabled"), - fwupd_remote_get_enabled (remote) ? "true" : "false"); - - /* TRANSLATORS: remote checksum */ - fu_util_print_data (_("Checksum"), - fwupd_remote_get_checksum (remote)); - - /* optional parameters */ - age = fwupd_remote_get_age (remote); - if (kind == FWUPD_REMOTE_KIND_DOWNLOAD && - age > 0 && age != G_MAXUINT64) { - const gchar *unit = "s"; - g_autofree gchar *age_str = NULL; - if (age > 60) { - age /= 60.f; - unit = "m"; - } - if (age > 60) { - age /= 60.f; - unit = "h"; - } - if (age > 24) { - age /= 24.f; - unit = "d"; - } - if (age > 7) { - age /= 7.f; - unit = "w"; - } - age_str = g_strdup_printf ("%.2f%s", age, unit); - /* TRANSLATORS: the age of the metadata */ - fu_util_print_data (_("Age"), age_str); - } - priority = fwupd_remote_get_priority (remote); - if (priority != 0) { - g_autofree gchar *priority_str = NULL; - priority_str = g_strdup_printf ("%i", priority); - /* TRANSLATORS: the numeric priority */ - fu_util_print_data (_("Priority"), priority_str); - } - tmp = fwupd_remote_get_username (remote); - if (tmp != NULL) { - /* TRANSLATORS: remote filename base */ - fu_util_print_data (_("Username"), tmp); - } - tmp = fwupd_remote_get_password (remote); - if (tmp != NULL) { - g_autofree gchar *hidden = g_strnfill (strlen (tmp), '*'); - /* TRANSLATORS: remote filename base */ - fu_util_print_data (_("Password"), hidden); - } - tmp = fwupd_remote_get_filename_cache (remote); - if (tmp != NULL) { - /* TRANSLATORS: filename of the local file */ - fu_util_print_data (_("Filename"), tmp); - } - tmp = fwupd_remote_get_filename_cache_sig (remote); - if (tmp != NULL) { - /* TRANSLATORS: filename of the local file */ - fu_util_print_data (_("Filename Signature"), tmp); - } - tmp = fwupd_remote_get_metadata_uri (remote); - if (tmp != NULL) { - /* TRANSLATORS: remote URI */ - fu_util_print_data (_("Metadata URI"), tmp); - } - tmp = fwupd_remote_get_metadata_uri_sig (remote); - if (tmp != NULL) { - /* TRANSLATORS: remote URI */ - fu_util_print_data (_("Metadata URI Signature"), tmp); - } - tmp = fwupd_remote_get_firmware_base_uri (remote); - if (tmp != NULL) { - /* TRANSLATORS: remote URI */ - fu_util_print_data (_("Firmware Base URI"), tmp); - } - tmp = fwupd_remote_get_report_uri (remote); - if (tmp != NULL) { - /* TRANSLATORS: URI to send success/failure reports */ - fu_util_print_data (_("Report URI"), tmp); - } - - /* newline */ - if (i != remotes->len - 1) - g_print ("\n"); + if (remotes->len == 0) { + /* TRANSLATORS: no repositories to download from */ + g_print ("%s\n", _("No remotes available")); + return TRUE; } + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i); + g_node_append_data (root, remote_tmp); + } + fu_util_print_tree (root, priv); + return TRUE; } From e3f380afda978c0573ac816e4b13c1f950e5aea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Thu, 29 Aug 2019 09:35:08 +0100 Subject: [PATCH 089/158] unifying: fix incomplete hex file parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new firmware for receivers with the nordic chips is using the 0x04 record type. This was previously not handled when parsing the file, causing wrong behavior when trying to update the device. This patch tries to properly deal with the record types, being them 0x04 or other. Signed-off-by: Filipe Laíns --- plugins/unifying/fu-unifying-bootloader.c | 49 ++++++++++++++++++++--- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/plugins/unifying/fu-unifying-bootloader.c b/plugins/unifying/fu-unifying-bootloader.c index b4f471051..5a276dc5d 100644 --- a/plugins/unifying/fu-unifying-bootloader.c +++ b/plugins/unifying/fu-unifying-bootloader.c @@ -8,6 +8,7 @@ #include +#include "fu-firmware-common.h" #include "fu-unifying-common.h" #include "fu-unifying-bootloader.h" #include "fu-unifying-hidpp.h" @@ -57,6 +58,8 @@ fu_unifying_bootloader_parse_requests (FuUnifyingBootloader *self, GBytes *fw, G for (guint i = 0; lines[i] != NULL; i++) { g_autoptr(FuUnifyingBootloaderRequest) payload = NULL; guint8 rec_type = 0x00; + guint16 offset = 0x0000; + gboolean exit = FALSE; /* skip empty lines */ tmp = lines[i]; @@ -75,16 +78,52 @@ fu_unifying_bootloader_parse_requests (FuUnifyingBootloader *self, GBytes *fw, G } payload->addr = ((guint16) fu_unifying_buffer_read_uint8 (tmp + 0x03)) << 8; payload->addr |= fu_unifying_buffer_read_uint8 (tmp + 0x05); + payload->cmd = FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER; rec_type = fu_unifying_buffer_read_uint8 (tmp + 0x07); - /* record type of 0xFD indicates signature data */ - if (rec_type == 0xFD) { - payload->cmd = FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE; - } else { - payload->cmd = FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER; + switch (rec_type) { + case 0x00: /* data */ + break; + case 0x01: /* EOF */ + exit = TRUE; + break; + case 0x03: /* start segment address */ + /* this is used to specify the start address, + it is doesn't mater in this context so we can + safely ignore it */ + continue; + case 0x04: /* extended linear address */ + offset = fu_firmware_strparse_uint16 (tmp + 0x09); + if (offset != 0x0000) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "extended linear addresses with offset different from 0 are not supported"); + return NULL; + } + continue; + case 0x05: /* start linear address */ + /* this is used to specify the start address, + it is doesn't mater in this context so we can + safely ignore it */ + continue; + case 0xFD: /* custom - vendor */ + /* record type of 0xFD indicates signature data */ + payload->cmd = FU_UNIFYING_BOOTLOADER_CMD_WRITE_SIGNATURE; + break; + default: + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "intel hex file record type %02x not supported", + rec_type); + return NULL; } + if (exit) + break; + /* read the data, but skip the checksum byte */ for (guint j = 0; j < payload->len; j++) { const gchar *ptr = tmp + 0x09 + (j * 2); From 21b591f9967e5a935f3736875f8be286e4434295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Thu, 29 Aug 2019 16:38:56 +0100 Subject: [PATCH 090/158] unifying: use fu_firmware_strparse_uint16 to read be 16bit values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- plugins/unifying/fu-unifying-bootloader.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/unifying/fu-unifying-bootloader.c b/plugins/unifying/fu-unifying-bootloader.c index 5a276dc5d..09ac3e361 100644 --- a/plugins/unifying/fu-unifying-bootloader.c +++ b/plugins/unifying/fu-unifying-bootloader.c @@ -76,8 +76,7 @@ fu_unifying_bootloader_parse_requests (FuUnifyingBootloader *self, GBytes *fw, G payload->len); return NULL; } - payload->addr = ((guint16) fu_unifying_buffer_read_uint8 (tmp + 0x03)) << 8; - payload->addr |= fu_unifying_buffer_read_uint8 (tmp + 0x05); + payload->addr = fu_firmware_strparse_uint16 (tmp + 0x03); payload->cmd = FU_UNIFYING_BOOTLOADER_CMD_WRITE_RAM_BUFFER; rec_type = fu_unifying_buffer_read_uint8 (tmp + 0x07); From 8e14544d37d9572da055159486ff4af592f53807 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 4 Sep 2019 15:19:21 -0500 Subject: [PATCH 091/158] fu-util: show release output in get-details again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was lost in the move to tree output ``` $ fwupdmgr get-details tpm.cab Decompressing… [***************************************] ○ └─XPS 13 9380 TPM 2.0: │ Device ID: 1a433daa3acb71e6befadcf2b1a783c1549663b1 │ Description: Updating the system firmware improves performance. │ │ Flags: internal|updatable|require-ac|registered|needs-reboot │ └─TPM 2.0 Update: Version: 7.2.0.2 Summary: Firmware for the Dell TPM 2.0 License: proprietary Size: 1.1 MB Vendor: Dell Inc. Flags: none ``` --- src/fu-util.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/fu-util.c b/src/fu-util.c index 80e4b3223..a4019b8ef 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -375,7 +375,10 @@ fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, Fw !fu_util_is_interesting_device (dev_tmp)) continue; if (fwupd_device_get_parent (dev_tmp) == dev) { + FwupdRelease *rel = fwupd_device_get_release_default (dev_tmp); GNode *child = g_node_append_data (root, dev_tmp); + if (rel != NULL) + g_node_append_data (child, rel); fu_util_build_device_tree (priv, child, devs, dev_tmp); } } @@ -497,12 +500,7 @@ fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) array = fwupd_client_get_details (priv->client, values[0], NULL, error); if (array == NULL) return FALSE; - for (guint i = 0; i < array->len; i++) { - FwupdDevice *dev = g_ptr_array_index (array, i); - if (!fu_util_filter_device (priv, dev)) - continue; - g_node_append_data (root, dev); - } + fu_util_build_device_tree (priv, root, array, NULL); fu_util_print_tree (root, priv); return TRUE; From ef3c766e1f388f55bb998ad07742204318bf8dca Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 4 Sep 2019 23:37:59 -0500 Subject: [PATCH 092/158] trivial: fu-common: match all whitespace not just ' ' in `fu_common_strstrip` --- src/fu-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fu-common.c b/src/fu-common.c index 66eb10b36..e1e16e787 100644 --- a/src/fu-common.c +++ b/src/fu-common.c @@ -835,7 +835,7 @@ fu_common_strstrip (const gchar *str) /* find last non-space char */ for (guint i = head; str[i] != '\0'; i++) { - if (str[i] != ' ') + if (!g_ascii_isspace (str[i])) tail = i; } return g_strndup (str + head, tail - head + 1); From 26563d750fd49d13bdd0290bdd6cc83c5ffc19ce Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 4 Sep 2019 15:33:46 -0500 Subject: [PATCH 093/158] fu-util-common: strip all trailing whitespace in XML description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before: ``` └─Thunderbolt controller in Dell dock: │ Device ID: 42b9e069d0b5515065cf4a32cec39552861439c9 │ Description: Updating the dock improves performance. │ │ Flags: updatable|require-ac|registered │ └─Thunderbolt controller Update: Version: 43.00 Summary: Update for the Thunderbolt controller within a Dell dock License: proprietary Size: 409.6 kB Vendor: Dell Inc. Flags: trusted-payload ``` After: ``` └─Thunderbolt controller in Dell dock: │ Device ID: 42b9e069d0b5515065cf4a32cec39552861439c9 │ Description: Updating the dock improves performance. │ Flags: updatable|require-ac|registered │ └─Thunderbolt controller Update: Version: 43.00 Summary: Update for the Thunderbolt controller within a Dell dock License: proprietary Size: 409.6 kB Vendor: Dell Inc. Flags: trusted-payload ``` --- src/fu-util-common.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 5766e71bc..37ea4b386 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -824,12 +824,8 @@ fu_util_convert_description (const gchar *xml, GError **error) g_set_object (&n, n2); } - /* remove extra newline */ - if (str->len > 0) - g_string_truncate (str, str->len - 1); - /* success */ - return g_string_free (g_steal_pointer (&str), FALSE); + return fu_common_strstrip (str->str); } static gchar * From 43742bcd3bd014adeec103a7645dcdb2a2adeba6 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 3 Sep 2019 23:36:01 -0500 Subject: [PATCH 094/158] trivial: dell: show reasons for lack of updates in `UpdateError` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` └─XPS 13 7390 TPM 2.0: Device ID: c56e9f77cfee65151bdef90310776f9d62827f5a Summary: Platform TPM device Current version: 7.2.1.0 Vendor: Dell Inc. Update Error: Updating disabled due to TPM owernship Flags: internal|require-ac|registered ``` --- plugins/dell/fu-plugin-dell.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/dell/fu-plugin-dell.c b/plugins/dell/fu-plugin-dell.c index 0603576c2..0c5c314ab 100644 --- a/plugins/dell/fu-plugin-dell.c +++ b/plugins/dell/fu-plugin-dell.c @@ -637,8 +637,8 @@ fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error) } fu_device_set_flashes_left (dev, out->flashes_left); } else { - g_debug ("%s updating disabled due to TPM ownership", - pretty_tpm_name); + fu_device_set_update_error (dev, + "Updating disabled due to TPM ownership"); } fu_plugin_device_register (plugin, dev); @@ -666,8 +666,7 @@ fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error) if ((out->status & TPM_OWN_MASK) == 0 && out->flashes_left > 0) { fu_device_set_flashes_left (dev_alt, out->flashes_left); } else { - g_debug ("%s mode switch disabled due to TPM ownership", - pretty_tpm_name); + fu_device_set_update_error (dev_alt, "mode switch disabled due to TPM ownership"); } fu_plugin_device_register (plugin, dev_alt); } From 227537919659eff455d3556d239360371a2dd8fe Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 3 Sep 2019 15:59:53 -0500 Subject: [PATCH 095/158] dell: Use TSS to query and build TPM vendor strings for GUIDs These are a more scalable way to apply firmware across a variety of platforms. An example: ``` XPS 13 7390 TPM 2.0 DeviceId: c56e9f77cfee65151bdef90310776f9d62827f5a Guid: a4352c96-f8d7-526c-8485-7f84085f348e <- 0962-2.0 Guid: 7d65b10b-bb24-552d-ade5-590b3b278188 <- DELL-TPM-2.0-NTC-NPCT Guid: 6f5ddd3a-8339-5b2a-b9a6-cf3b92f6c86d <- DELL-TPM-2.0-NTC-NPCT75x Guid: fe462d4a-e48f-5069-9172-47330fc5e838 <- DELL-TPM-2.0-NTC-NPCT75xrls Summary: Platform TPM device Plugin: uefi Flags: internal|require-ac|registered Vendor: Dell Inc. Version: 7.2.1.0 VersionFormat: quad Icon: computer Created: 2019-09-04 ``` When this system is queried using tpm2-tools: ``` $ sudo tpm2_getcap -c properties-fixed TPM_PT_FAMILY_INDICATOR: as UINT32: 0x08322e3000 as string: "2.0" TPM_PT_LEVEL: 0 TPM_PT_REVISION: 1.38 TPM_PT_DAY_OF_YEAR: 0x00000008 TPM_PT_YEAR: 0x000007e2 TPM_PT_MANUFACTURER: 0x4e544300 TPM_PT_VENDOR_STRING_1: as UINT32: 0x4e504354 as string: "NPCT" TPM_PT_VENDOR_STRING_2: as UINT32: 0x37357800 as string: "75x" TPM_PT_VENDOR_STRING_3: as UINT32: 0x02010024 as string: "" TPM_PT_VENDOR_STRING_4: as UINT32: 0x726c7300 as string: "rls" ``` --- plugins/dell/README.md | 17 +++++ plugins/dell/fu-plugin-dell.c | 124 +++++++++++++++++++++++++++++++++- plugins/dell/fu-self-test.c | 8 +++ plugins/dell/meson.build | 2 + 4 files changed, 149 insertions(+), 2 deletions(-) diff --git a/plugins/dell/README.md b/plugins/dell/README.md index eb54bcfa0..4a4aeb46c 100644 --- a/plugins/dell/README.md +++ b/plugins/dell/README.md @@ -16,6 +16,23 @@ These devices uses custom GUIDs for Dell-specific hardware. In both cases the `system-id` is derived from the SMBIOS Product SKU property. +TPM GUIDs are also built using the TSS properties +`TPM2_PT_FAMILY_INDICATOR`, `TPM2_PT_MANUFACTURER`, and `TPM2_PT_VENDOR_STRING_*` +These are built hierarchically with more parts for each GUID: + * `DELL-TPM-$FAMILY-$MANUFACTURER-$VENDOR_STRING_1` + * `DELL-TPM-$FAMILY-$MANUFACTURER-$VENDOR_STRING_1$VENDOR_STRING_2` + * `DELL-TPM-$FAMILY-$MANUFACTURER-$VENDOR_STRING_1$VENDOR_STRING_2$VENDOR_STRING_3` + * `DELL-TPM-$FAMILY-$MANUFACTURER-$VENDOR_STRING_1$VENDOR_STRING_2$VENDOR_STRING_3$VENDOR_STRING_4` + +If there are non-ASCII values in any vendor string or any vendor is missing that octet will be skipped. + +Example resultant GUIDs from a real system containing a TPM from Nuvoton: +``` + Guid: 7d65b10b-bb24-552d-ade5-590b3b278188 <- DELL-TPM-2.0-NTC-NPCT + Guid: 6f5ddd3a-8339-5b2a-b9a6-cf3b92f6c86d <- DELL-TPM-2.0-NTC-NPCT75x + Guid: fe462d4a-e48f-5069-9172-47330fc5e838 <- DELL-TPM-2.0-NTC-NPCT75xrls +``` + Build Requirements ------------------ diff --git a/plugins/dell/fu-plugin-dell.c b/plugins/dell/fu-plugin-dell.c index 0c5c314ab..28ce9de18 100644 --- a/plugins/dell/fu-plugin-dell.c +++ b/plugins/dell/fu-plugin-dell.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "fwupd-common.h" #include "fu-plugin-dell.h" @@ -522,6 +523,117 @@ fu_plugin_get_results (FuPlugin *plugin, FuDevice *device, GError **error) return TRUE; } +static void Esys_Finalize_autoptr_cleanup (ESYS_CONTEXT *esys_context) +{ + Esys_Finalize (&esys_context); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC (ESYS_CONTEXT, Esys_Finalize_autoptr_cleanup) + +static gchar * +fu_plugin_dell_get_tpm_capability (ESYS_CONTEXT *ctx, guint32 query) +{ + TSS2_RC rc; + guint32 val; + gchar result[5] = {'\0'}; + g_autofree TPMS_CAPABILITY_DATA *capability = NULL; + rc = Esys_GetCapability (ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + TPM2_CAP_TPM_PROPERTIES, query, 1, NULL, &capability); + if (rc != TSS2_RC_SUCCESS) { + g_debug ("capability request failed for query %x", query); + return NULL; + } + if (capability->data.tpmProperties.count == 0) { + g_debug ("no properties returned for query %x", query); + return NULL; + } + if (capability->data.tpmProperties.tpmProperty[0].property != query) { + g_debug ("wrong query returned (got %x expected %x)", + capability->data.tpmProperties.tpmProperty[0].property, + query); + return NULL; + } + + val = GUINT32_FROM_BE (capability->data.tpmProperties.tpmProperty[0].value); + memcpy (result, (gchar *) &val, 4); + + /* convert non-ASCII into spaces */ + for (guint i = 0; i < 4; i++) { + if (!g_ascii_isgraph (result[i]) && result[i] != '\0') + result[i] = 0x20; + } + + return fu_common_strstrip (result); +} + +static gboolean +fu_plugin_dell_add_tpm_model (FuDevice *dev, GError **error) +{ + TSS2_RC rc; + const gchar *base = "DELL-TPM"; + g_autoptr(ESYS_CONTEXT) ctx = NULL; + g_autofree gchar *family = NULL; + g_autofree gchar *manufacturer = NULL; + g_autofree gchar *vendor1 = NULL; + g_autofree gchar *vendor2 = NULL; + g_autofree gchar *vendor3 = NULL; + g_autofree gchar *vendor4 = NULL; + g_autofree gchar *v1 = NULL; + g_autofree gchar *v1_v2 = NULL; + g_autofree gchar *v1_v2_v3 = NULL; + g_autofree gchar *v1_v2_v3_v4 = NULL; + + rc = Esys_Initialize (&ctx, NULL, NULL); + if (rc != TSS2_RC_SUCCESS) { + g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, + "failed to initialize TPM library"); + return FALSE; + } + rc = Esys_Startup (ctx, TPM2_SU_CLEAR); + if (rc != TSS2_RC_SUCCESS) { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "failed to initialize TPM"); + return FALSE; + } + + /* lookup guaranteed details from TPM */ + family = fu_plugin_dell_get_tpm_capability (ctx, + TPM2_PT_FAMILY_INDICATOR); + if (family == NULL) { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "failed to read TPM family"); + return FALSE; + } + manufacturer = fu_plugin_dell_get_tpm_capability (ctx, TPM2_PT_MANUFACTURER); + if (manufacturer == NULL) { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "failed to read TPM manufacturer"); + return FALSE; + } + vendor1 = fu_plugin_dell_get_tpm_capability (ctx, TPM2_PT_VENDOR_STRING_1); + if (vendor1 == NULL) { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "failed to read TPM vendor string"); + return FALSE; + } + + /* these are not guaranteed by spec and may be NULL */ + vendor2 = fu_plugin_dell_get_tpm_capability (ctx, TPM2_PT_VENDOR_STRING_2); + vendor3 = fu_plugin_dell_get_tpm_capability (ctx, TPM2_PT_VENDOR_STRING_3); + vendor4 = fu_plugin_dell_get_tpm_capability (ctx, TPM2_PT_VENDOR_STRING_4); + + /* add GUIDs to daemon */ + v1 = g_strjoin ("-", base, family, manufacturer, vendor1, NULL); + v1_v2 = g_strconcat (v1, vendor2, NULL); + v1_v2_v3 = g_strconcat (v1_v2, vendor3, NULL); + v1_v2_v3_v4 = g_strconcat (v1_v2_v3, vendor4, NULL); + fu_device_add_instance_id (dev, v1); + fu_device_add_instance_id (dev, v1_v2); + fu_device_add_instance_id (dev, v1_v2_v3); + fu_device_add_instance_id (dev, v1_v2_v3_v4); + + return TRUE; +} + gboolean fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error) { @@ -618,7 +730,7 @@ fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error) /* build Standard device nodes */ dev = fu_device_new (); fu_device_set_id (dev, tpm_id); - fu_device_add_guid (dev, tpm_guid); + fu_device_add_instance_id (dev, tpm_guid_raw); fu_device_set_vendor (dev, "Dell Inc."); fu_device_set_name (dev, pretty_tpm_name); fu_device_set_summary (dev, "Platform TPM device"); @@ -640,13 +752,19 @@ fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error) fu_device_set_update_error (dev, "Updating disabled due to TPM ownership"); } + /* build GUIDs from TSS strings */ + if (!fu_plugin_dell_add_tpm_model (dev, error)) + return FALSE; + + if (!fu_device_setup (dev, error)) + return FALSE; fu_plugin_device_register (plugin, dev); /* build alternate device node */ if (can_switch_modes) { dev_alt = fu_device_new (); fu_device_set_id (dev_alt, tpm_id_alt); - fu_device_add_guid (dev_alt, tpm_guid_alt); + fu_device_add_instance_id (dev_alt, tpm_guid_raw_alt); fu_device_set_vendor (dev, "Dell Inc."); fu_device_set_name (dev_alt, pretty_tpm_name_alt); fu_device_set_summary (dev_alt, "Alternate mode for platform TPM device"); @@ -668,6 +786,8 @@ fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error) } else { fu_device_set_update_error (dev_alt, "mode switch disabled due to TPM ownership"); } + if (!fu_device_setup (dev_alt, error)) + return FALSE; fu_plugin_device_register (plugin, dev_alt); } else diff --git a/plugins/dell/fu-self-test.c b/plugins/dell/fu-self-test.c index cbd5719a4..cd64a2e88 100644 --- a/plugins/dell/fu-self-test.c +++ b/plugins/dell/fu-self-test.c @@ -68,6 +68,7 @@ fu_plugin_dell_tpm_func (void) const guint8 fw[30] = { 'F', 'W', 0x00 }; gboolean ret; struct tpm_status tpm_out; + const gchar *tpm_server_running = g_getenv ("TPM_SERVER_RUNNING"); g_autoptr(FuPlugin) plugin_dell = NULL; g_autoptr(FuPlugin) plugin_uefi = NULL; g_autoptr(GBytes) blob_fw = g_bytes_new_static (fw, sizeof(fw)); @@ -126,6 +127,13 @@ fu_plugin_dell_tpm_func (void) (guint32 *) &tpm_out, 0, 0, NULL, TRUE); ret = fu_plugin_dell_detect_tpm (plugin_dell, &error); + if (!ret) { + if (tpm_server_running == NULL && + g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_test_skip ("no physical or simulated TPM 2.0 device available"); + return; + } + } g_assert_true (ret); g_assert_cmpint (devices->len, ==, 2); diff --git a/plugins/dell/meson.build b/plugins/dell/meson.build index 2c0532f14..171154f81 100644 --- a/plugins/dell/meson.build +++ b/plugins/dell/meson.build @@ -27,6 +27,7 @@ shared_module('fu_plugin_dell', plugin_deps, efivar, libsmbios_c, + tpm2tss, ], ) @@ -54,6 +55,7 @@ if get_option('tests') sqlite, libsmbios_c, valgrind, + tpm2tss, ], link_with : [ libfwupdprivate, From 3996af38c82a0331732500767be5951eb4c483c8 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 5 Sep 2019 10:28:52 -0500 Subject: [PATCH 096/158] trivial: fu-util-common: Show unknown devices as interesting devices This restore `fwupdmgr get-details` working with nothing plugged in. --- src/fu-util-common.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 37ea4b386..f7a7be3dc 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -223,6 +223,9 @@ fu_util_is_interesting_device (FwupdDevice *dev) return TRUE; if (fwupd_device_get_update_error (dev) != NULL) return TRUE; + /* device not plugged in, get-details */ + if (fwupd_device_get_flags (dev) == 0) + return TRUE; return FALSE; } From 20cc9eebc57c6c070d8bc13e6aad4d175fdf37df Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 5 Sep 2019 07:27:26 -0500 Subject: [PATCH 097/158] Try to only show DMI product name once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * libfwupd: export new property HostProduct * clients: Use this property for setting the title of trees Before: ``` ○ ├─XPS 13 7390 TPM 2.0: │ Device ID: c56e9f77cfee65151bdef90310776f9d62827f5a │ Summary: Platform TPM device │ Current version: 7.2.1.0 │ Vendor: Dell Inc. │ Update Error: Updating disabled due to TPM ownership │ Flags: internal|require-ac|registered └─XPS 13 7390 System Firmware: Device ID: b6c08fb9e5384d9d101853cc1ca20cf0ce2df2e2 Current version: 0.1.1.1 Minimum Version: 0.1.1.1 Vendor: Dell Inc. Flags: internal|updatable|require-ac|registered|needs-reboot ``` After: ``` XPS 13 7390 │ ├─TPM 2.0: │ Device ID: c56e9f77cfee65151bdef90310776f9d62827f5a │ Summary: Platform TPM device │ Current version: 7.2.1.0 │ Vendor: Dell Inc. │ Update Error: Updating disabled due to TPM ownership │ Flags: internal|require-ac|registered └─System Firmware: Device ID: b6c08fb9e5384d9d101853cc1ca20cf0ce2df2e2 Current version: 0.1.1.1 Minimum Version: 0.1.1.1 Vendor: Dell Inc. Flags: internal|updatable|require-ac|registered|needs-reboot ``` --- libfwupd/fwupd-client.c | 55 +++++++++++++++++++++ libfwupd/fwupd-client.h | 1 + libfwupd/fwupd.map | 1 + plugins/dell/fu-plugin-dell.c | 8 +-- plugins/dell/fu-self-test.c | 16 +++--- plugins/thunderbolt/fu-plugin-thunderbolt.c | 2 - plugins/uefi/fu-plugin-uefi.c | 10 +--- src/fu-engine.c | 7 +++ src/fu-engine.h | 1 + src/fu-main.c | 3 ++ src/fu-tool.c | 18 +++++-- src/fu-util-common.c | 4 +- src/fu-util.c | 24 ++++++--- src/org.freedesktop.fwupd.xml | 11 +++++ 14 files changed, 125 insertions(+), 36 deletions(-) diff --git a/libfwupd/fwupd-client.c b/libfwupd/fwupd-client.c index 1e444b88c..0caad38a5 100644 --- a/libfwupd/fwupd-client.c +++ b/libfwupd/fwupd-client.c @@ -40,6 +40,7 @@ typedef struct { gboolean tainted; guint percentage; gchar *daemon_version; + gchar *host_product; GDBusConnection *conn; GDBusProxy *proxy; } FwupdClientPrivate; @@ -59,6 +60,7 @@ enum { PROP_PERCENTAGE, PROP_DAEMON_VERSION, PROP_TAINTED, + PROP_HOST_PRODUCT, PROP_LAST }; @@ -102,6 +104,15 @@ fwupd_client_helper_new (void) G_DEFINE_AUTOPTR_CLEANUP_FUNC(FwupdClientHelper, fwupd_client_helper_free) #pragma clang diagnostic pop +static void +fwupd_client_set_host_product (FwupdClient *client, const gchar *host_product) +{ + FwupdClientPrivate *priv = GET_PRIVATE (client); + g_free (priv->host_product); + priv->host_product = g_strdup (host_product); + g_object_notify (G_OBJECT (client), "host-product"); +} + static void fwupd_client_set_daemon_version (FwupdClient *client, const gchar *daemon_version) { @@ -155,6 +166,12 @@ fwupd_client_properties_changed_cb (GDBusProxy *proxy, if (val != NULL) fwupd_client_set_daemon_version (client, g_variant_get_string (val, NULL)); } + if (g_variant_dict_contains (dict, "HostProduct")) { + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property (proxy, "HostProduct"); + if (val != NULL) + fwupd_client_set_host_product (client, g_variant_get_string (val, NULL)); + } } static void @@ -249,6 +266,10 @@ fwupd_client_connect (FwupdClient *client, GCancellable *cancellable, GError **e val2 = g_dbus_proxy_get_cached_property (priv->proxy, "Tainted"); if (val2 != NULL) priv->tainted = g_variant_get_boolean (val2); + val = g_dbus_proxy_get_cached_property (priv->proxy, "HostProduct"); + if (val != NULL) + fwupd_client_set_host_product (client, g_variant_get_string (val, NULL)); + return TRUE; } @@ -1150,6 +1171,24 @@ fwupd_client_get_daemon_version (FwupdClient *client) return priv->daemon_version; } +/** + * fwupd_client_get_host_product: + * @client: A #FwupdClient + * + * Gets the string that represents the host running fwupd + * + * Returns: a string, or %NULL for unknown. + * + * Since: 1.3.1 + **/ +const gchar * +fwupd_client_get_host_product (FwupdClient *client) +{ + FwupdClientPrivate *priv = GET_PRIVATE (client); + g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); + return priv->host_product; +} + /** * fwupd_client_get_status: * @client: A #FwupdClient @@ -1672,6 +1711,9 @@ fwupd_client_get_property (GObject *object, guint prop_id, case PROP_DAEMON_VERSION: g_value_set_string (value, priv->daemon_version); break; + case PROP_HOST_PRODUCT: + g_value_set_string (value, priv->host_product); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1836,6 +1878,18 @@ fwupd_client_class_init (FwupdClientClass *klass) pspec = g_param_spec_string ("daemon-version", NULL, NULL, NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_DAEMON_VERSION, pspec); + + /** + * FwupdClient:host-product: + * + * The host product string + * + * Since: 1.3.1 + */ + pspec = g_param_spec_string ("host-product", NULL, NULL, + NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_DAEMON_VERSION, pspec); + } static void @@ -1850,6 +1904,7 @@ fwupd_client_finalize (GObject *object) FwupdClientPrivate *priv = GET_PRIVATE (client); g_free (priv->daemon_version); + g_free (priv->host_product); if (priv->conn != NULL) g_object_unref (priv->conn); if (priv->proxy != NULL) diff --git a/libfwupd/fwupd-client.h b/libfwupd/fwupd-client.h index 2a16cecac..b6fa21a6d 100644 --- a/libfwupd/fwupd-client.h +++ b/libfwupd/fwupd-client.h @@ -127,6 +127,7 @@ FwupdStatus fwupd_client_get_status (FwupdClient *client); gboolean fwupd_client_get_tainted (FwupdClient *client); guint fwupd_client_get_percentage (FwupdClient *client); const gchar *fwupd_client_get_daemon_version (FwupdClient *client); +const gchar *fwupd_client_get_host_product (FwupdClient *client); GPtrArray *fwupd_client_get_remotes (FwupdClient *client, GCancellable *cancellable, diff --git a/libfwupd/fwupd.map b/libfwupd/fwupd.map index d83739604..2a497d3c1 100644 --- a/libfwupd/fwupd.map +++ b/libfwupd/fwupd.map @@ -374,6 +374,7 @@ LIBFWUPD_1.2.10 { LIBFWUPD_1.3.1 { global: + fwupd_client_get_host_product; fwupd_remote_get_remotes_dir; fwupd_remote_set_remotes_dir; local: *; diff --git a/plugins/dell/fu-plugin-dell.c b/plugins/dell/fu-plugin-dell.c index 28ce9de18..812bf8631 100644 --- a/plugins/dell/fu-plugin-dell.c +++ b/plugins/dell/fu-plugin-dell.c @@ -654,7 +654,6 @@ fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error) struct tpm_status *out = NULL; g_autoptr (FuDevice) dev_alt = NULL; g_autoptr (FuDevice) dev = NULL; - const gchar *product_name = "Unknown"; fu_dell_clear_smi (data->smi_obj); out = (struct tpm_status *) data->smi_obj->output; @@ -721,11 +720,8 @@ fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error) FWUPD_VERSION_FORMAT_QUAD); /* make it clear that the TPM is a discrete device of the product */ - if (!data->smi_obj->fake_smbios) { - product_name = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_PRODUCT_NAME); - } - pretty_tpm_name = g_strdup_printf ("%s TPM %s", product_name, tpm_mode); - pretty_tpm_name_alt = g_strdup_printf ("%s TPM %s", product_name, tpm_mode_alt); + pretty_tpm_name = g_strdup_printf ("TPM %s", tpm_mode); + pretty_tpm_name_alt = g_strdup_printf ("TPM %s", tpm_mode_alt); /* build Standard device nodes */ dev = fu_device_new (); diff --git a/plugins/dell/fu-self-test.c b/plugins/dell/fu-self-test.c index cd64a2e88..079ce499e 100644 --- a/plugins/dell/fu-self-test.c +++ b/plugins/dell/fu-self-test.c @@ -138,12 +138,12 @@ fu_plugin_dell_tpm_func (void) g_assert_cmpint (devices->len, ==, 2); /* make sure 2.0 is locked */ - device_v20 = _find_device_by_name (devices, "Unknown TPM 2.0"); + device_v20 = _find_device_by_name (devices, "TPM 2.0"); g_assert_nonnull (device_v20); g_assert_true (fu_device_has_flag (device_v20, FWUPD_DEVICE_FLAG_LOCKED)); /* make sure not allowed to flash 1.2 */ - device_v12 = _find_device_by_name (devices, "Unknown TPM 1.2"); + device_v12 = _find_device_by_name (devices, "TPM 1.2"); g_assert_nonnull (device_v12); g_assert_false (fu_device_has_flag (device_v12, FWUPD_DEVICE_FLAG_UPDATABLE)); @@ -172,12 +172,12 @@ fu_plugin_dell_tpm_func (void) g_assert (ret); /* make sure not allowed to flash 1.2 */ - device_v12 = _find_device_by_name (devices, "Unknown TPM 1.2"); + device_v12 = _find_device_by_name (devices, "TPM 1.2"); g_assert_nonnull (device_v12); g_assert_false (fu_device_has_flag (device_v12, FWUPD_DEVICE_FLAG_UPDATABLE)); /* try to unlock 2.0 */ - device_v20 = _find_device_by_name (devices, "Unknown TPM 2.0"); + device_v20 = _find_device_by_name (devices, "TPM 2.0"); g_assert_nonnull (device_v20); ret = fu_plugin_runner_unlock (plugin_uefi, device_v20, &error); g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); @@ -203,10 +203,10 @@ fu_plugin_dell_tpm_func (void) g_assert (ret); /* make sure allowed to flash 1.2 but not 2.0 */ - device_v12 = _find_device_by_name (devices, "Unknown TPM 1.2"); + device_v12 = _find_device_by_name (devices, "TPM 1.2"); g_assert_nonnull (device_v12); g_assert_true (fu_device_has_flag (device_v12, FWUPD_DEVICE_FLAG_UPDATABLE)); - device_v20 = _find_device_by_name (devices, "Unknown TPM 2.0"); + device_v20 = _find_device_by_name (devices, "TPM 2.0"); g_assert_nonnull (device_v20); g_assert_false (fu_device_has_flag (device_v20, FWUPD_DEVICE_FLAG_UPDATABLE)); @@ -238,10 +238,10 @@ fu_plugin_dell_tpm_func (void) g_assert (ret); /* make sure allowed to flash 2.0 but not 1.2 */ - device_v20 = _find_device_by_name (devices, "Unknown TPM 2.0"); + device_v20 = _find_device_by_name (devices, "TPM 2.0"); g_assert_nonnull (device_v20); g_assert_true (fu_device_has_flag (device_v20, FWUPD_DEVICE_FLAG_UPDATABLE)); - device_v12 = _find_device_by_name (devices, "Unknown TPM 1.2"); + device_v12 = _find_device_by_name (devices, "TPM 1.2"); g_assert_nonnull (device_v12); g_assert_false (fu_device_has_flag (device_v12, FWUPD_DEVICE_FLAG_UPDATABLE)); diff --git a/plugins/thunderbolt/fu-plugin-thunderbolt.c b/plugins/thunderbolt/fu-plugin-thunderbolt.c index 1ce54ae6b..e422e8ba8 100644 --- a/plugins/thunderbolt/fu-plugin-thunderbolt.c +++ b/plugins/thunderbolt/fu-plugin-thunderbolt.c @@ -380,8 +380,6 @@ fu_plugin_thunderbolt_add (FuPlugin *plugin, GUdevDevice *device) fu_device_set_metadata (dev, "sysfs-path", devpath); name = g_udev_device_get_sysfs_attr (device, "device_name"); - if (name == NULL && is_host) - name = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_PRODUCT_NAME); if (name != NULL) { if (is_host) { g_autofree gchar *pretty_name = NULL; diff --git a/plugins/uefi/fu-plugin-uefi.c b/plugins/uefi/fu-plugin-uefi.c index e7024ceee..83235ffd0 100644 --- a/plugins/uefi/fu-plugin-uefi.c +++ b/plugins/uefi/fu-plugin-uefi.c @@ -505,16 +505,8 @@ fu_plugin_uefi_get_name_for_type (FuPlugin *plugin, FuUefiDeviceKind device_kind /* set Display Name prefix for capsules that are not PCI cards */ display_name = g_string_new (fu_plugin_uefi_uefi_type_to_string (device_kind)); - if (device_kind == FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE) { + if (device_kind == FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE) g_string_prepend (display_name, "UEFI "); - } else { - const gchar *tmp; - tmp = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_PRODUCT_NAME); - if (tmp != NULL && tmp[0] != '\0') { - g_string_prepend (display_name, " "); - g_string_prepend (display_name, tmp); - } - } return g_string_free (display_name, FALSE); } diff --git a/src/fu-engine.c b/src/fu-engine.c index 9191e8e1e..49c75ce5b 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -4145,6 +4145,13 @@ fu_engine_get_tainted (FuEngine *self) return self->tainted; } +const gchar * +fu_engine_get_host_product (FuEngine *self) +{ + g_return_val_if_fail (FU_IS_ENGINE (self), NULL); + return fu_hwids_get_value (self->hwids, FU_HWIDS_KEY_PRODUCT_NAME); +} + gboolean fu_engine_load_plugins (FuEngine *self, GError **error) { diff --git a/src/fu-engine.h b/src/fu-engine.h index 710f29bc4..3fd535f48 100644 --- a/src/fu-engine.h +++ b/src/fu-engine.h @@ -48,6 +48,7 @@ gboolean fu_engine_load (FuEngine *self, gboolean fu_engine_load_plugins (FuEngine *self, GError **error); gboolean fu_engine_get_tainted (FuEngine *self); +const gchar *fu_engine_get_host_product (FuEngine *self); FwupdStatus fu_engine_get_status (FuEngine *self); XbSilo *fu_engine_get_silo_from_blob (FuEngine *self, GBytes *blob_cab, diff --git a/src/fu-main.c b/src/fu-main.c index b89b9b68e..fa1e02b3b 100644 --- a/src/fu-main.c +++ b/src/fu-main.c @@ -1363,6 +1363,9 @@ fu_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender, if (g_strcmp0 (property_name, "Status") == 0) return g_variant_new_uint32 (fu_engine_get_status (priv->engine)); + if (g_strcmp0 (property_name, "HostProduct") == 0) + return g_variant_new_string (fu_engine_get_host_product (priv->engine)); + /* return an error */ g_set_error (error, G_DBUS_ERROR, diff --git a/src/fu-tool.c b/src/fu-tool.c index b5f21c795..f27808759 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -294,11 +294,18 @@ fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev) return TRUE; } +static gchar * +fu_util_get_tree_title (FuUtilPrivate *priv) +{ + return g_strdup (fu_engine_get_host_product (priv->engine)); +} + static gboolean fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GPtrArray) devices = NULL; g_autoptr(GNode) root = g_node_new (NULL); + g_autofree gchar *title = fu_util_get_tree_title (priv); /* load engine */ if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) @@ -336,7 +343,7 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) } } if (g_node_n_nodes (root, G_TRAVERSE_ALL) > 1) - fu_util_print_tree (root, priv); + fu_util_print_tree (root, title); /* save the device state for other applications to see */ if (!fu_util_save_current_state (priv, error)) return FALSE; @@ -350,6 +357,7 @@ fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GPtrArray) array = NULL; g_autoptr(GNode) root = g_node_new (NULL); + g_autofree gchar *title = fu_util_get_tree_title (priv); gint fd; /* load engine */ @@ -387,7 +395,7 @@ fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) continue; g_node_append_data (root, dev); } - fu_util_print_tree (root, priv); + fu_util_print_tree (root, title); return TRUE; } @@ -433,6 +441,7 @@ static gboolean fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GNode) root = g_node_new (NULL); + g_autofree gchar *title = fu_util_get_tree_title (priv); g_autoptr(GPtrArray) devs = NULL; /* load engine */ @@ -451,7 +460,7 @@ fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } fu_util_build_device_tree (priv, root, devs, NULL); - fu_util_print_tree (root, priv); + fu_util_print_tree (root, title); /* save the device state for other applications to see */ return fu_util_save_current_state (priv, error); @@ -1340,6 +1349,7 @@ fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GPtrArray) devices = NULL; g_autoptr(GNode) root = g_node_new (NULL); + g_autofree gchar *title = fu_util_get_tree_title (priv); /* load engine */ if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) @@ -1357,7 +1367,7 @@ fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) continue; g_node_append_data (root, dev); } - fu_util_print_tree (root, priv); + fu_util_print_tree (root, title); return TRUE; } diff --git a/src/fu-util-common.c b/src/fu-util-common.c index f7a7be3dc..7349b5ef5 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -160,9 +160,11 @@ fu_util_traverse_tree (GNode *n, gpointer data) /* root node */ if (n->data == NULL && g_getenv ("FWUPD_VERBOSE") == NULL) { - g_print ("○\n"); + const gchar *str = data; + g_print ("%s\n│\n", str != NULL ? str : "○"); return FALSE; } + if (n->parent == NULL) return FALSE; diff --git a/src/fu-util.c b/src/fu-util.c index a4019b8ef..b66b8ae63 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -384,11 +384,18 @@ fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, Fw } } +static gchar * +fu_util_get_tree_title (FuUtilPrivate *priv) +{ + return g_strdup (fwupd_client_get_host_product (priv->client)); +} + static gboolean fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GNode) root = g_node_new (NULL); g_autoptr(GPtrArray) devs = NULL; + g_autofree gchar *title = fu_util_get_tree_title (priv); /* get results from daemon */ devs = fwupd_client_get_devices (priv->client, NULL, error); @@ -402,7 +409,7 @@ fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } fu_util_build_device_tree (priv, root, devs, NULL); - fu_util_print_tree (root, priv); + fu_util_print_tree (root, title); /* nag? */ if (!fu_util_perhaps_show_unreported (priv, error)) @@ -488,6 +495,7 @@ fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GPtrArray) array = NULL; g_autoptr(GNode) root = g_node_new (NULL); + g_autofree gchar *title = fu_util_get_tree_title (priv); /* check args */ if (g_strv_length (values) != 1) { @@ -501,7 +509,7 @@ fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error) if (array == NULL) return FALSE; fu_util_build_device_tree (priv, root, array, NULL); - fu_util_print_tree (root, priv); + fu_util_print_tree (root, title); return TRUE; } @@ -787,6 +795,7 @@ fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GPtrArray) devices = NULL; g_autoptr(GNode) root = g_node_new (NULL); + g_autofree gchar *title = fu_util_get_tree_title (priv); /* get all devices from the history database */ devices = fwupd_client_get_history (priv->client, NULL, error); @@ -800,7 +809,7 @@ fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) continue; g_node_append_data (root, dev); } - fu_util_print_tree (root, priv); + fu_util_print_tree (root, title); return TRUE; } @@ -1193,6 +1202,7 @@ fu_util_get_releases (FuUtilPrivate *priv, gchar **values, GError **error) g_autoptr(FwupdDevice) dev = NULL; g_autoptr(GPtrArray) rels = NULL; g_autoptr(GNode) root = g_node_new (NULL); + g_autofree gchar *title = fu_util_get_tree_title (priv); dev = fu_util_get_device_or_prompt (priv, values, error); if (dev == NULL) @@ -1213,7 +1223,7 @@ fu_util_get_releases (FuUtilPrivate *priv, gchar **values, GError **error) FwupdRelease *rel = g_ptr_array_index (rels, i); g_node_append_data (root, rel); } - fu_util_print_tree (root, priv); + fu_util_print_tree (root, title); return TRUE; } @@ -1400,6 +1410,7 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) g_autoptr(GPtrArray) devices = NULL; gboolean supported = FALSE; g_autoptr(GNode) root = g_node_new (NULL); + g_autofree gchar *title = fu_util_get_tree_title (priv); /* are the remotes very old */ if (!fu_util_perhaps_refresh_remotes (priv, error)) @@ -1440,7 +1451,7 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error) } if (g_node_n_nodes (root, G_TRAVERSE_ALL) > 1) - fu_util_print_tree (root, priv); + fu_util_print_tree (root, title); /* nag? */ if (!fu_util_perhaps_show_unreported (priv, error)) @@ -1464,6 +1475,7 @@ fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error) { g_autoptr(GNode) root = g_node_new (NULL); g_autoptr(GPtrArray) remotes = NULL; + g_autofree gchar *title = fu_util_get_tree_title (priv); remotes = fwupd_client_get_remotes (priv->client, NULL, error); if (remotes == NULL) @@ -1479,7 +1491,7 @@ fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error) FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i); g_node_append_data (root, remote_tmp); } - fu_util_print_tree (root, priv); + fu_util_print_tree (root, title); return TRUE; } diff --git a/src/org.freedesktop.fwupd.xml b/src/org.freedesktop.fwupd.xml index 1d0491613..a61025153 100644 --- a/src/org.freedesktop.fwupd.xml +++ b/src/org.freedesktop.fwupd.xml @@ -22,6 +22,17 @@ + + + + + + The product name string for the host. + + + + + From c4ee883ea4c47feef2591c666ce00b5a1ad85281 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 5 Sep 2019 17:42:48 +0100 Subject: [PATCH 098/158] trivial: Allow ignoring the daemon version check by using --force --- src/fu-util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fu-util.c b/src/fu-util.c index b66b8ae63..7abef11ce 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -2338,7 +2338,8 @@ main (int argc, char *argv[]) } /* check that we have at least this version daemon running */ - if (!fu_util_check_daemon_version (priv, &error)) { + if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + !fu_util_check_daemon_version (priv, &error)) { g_printerr ("%s\n", error->message); return EXIT_FAILURE; } From 0e46b2272842ed056a8f5b75d1f88ca405b951f2 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 5 Sep 2019 12:13:35 +0100 Subject: [PATCH 099/158] Allow disabling SSL strict mode for broken corporate proxies --- data/bash-completion/fwupdmgr.in | 1 + data/bash-completion/fwupdtool.in | 1 + src/fu-tool.c | 13 +++++++++++++ src/fu-util-common.c | 4 ++++ src/fu-util.c | 13 +++++++++++++ 5 files changed, 32 insertions(+) diff --git a/data/bash-completion/fwupdmgr.in b/data/bash-completion/fwupdmgr.in index 45590caab..b0b03de95 100644 --- a/data/bash-completion/fwupdmgr.in +++ b/data/bash-completion/fwupdmgr.in @@ -45,6 +45,7 @@ _fwupdmgr_opts=( '--sign' '--filter' '--log' + '--disable-ssl-strict' ) _show_filters() diff --git a/data/bash-completion/fwupdtool.in b/data/bash-completion/fwupdtool.in index 93deffa0c..c7c6d3c7f 100644 --- a/data/bash-completion/fwupdtool.in +++ b/data/bash-completion/fwupdtool.in @@ -34,6 +34,7 @@ _fwupdtool_opts=( '--prepare' '--cleanup' '--filter' + '--disable-ssl-strict' ) _show_filters() diff --git a/src/fu-tool.c b/src/fu-tool.c index f27808759..e4a670670 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -54,6 +54,7 @@ struct FuUtilPrivate { gboolean enable_json_state; FwupdInstallFlags flags; gboolean show_all_devices; + gboolean disable_ssl_strict; /* only valid in update and downgrade */ FuUtilOperation current_operation; FwupdDevice *current_device; @@ -1418,6 +1419,9 @@ main (int argc, char *argv[]) { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state, /* TRANSLATORS: command line option */ _("Save device state into a JSON file between executions"), NULL }, + { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict, + /* TRANSLATORS: command line option */ + _("Ignore SSL strict checks when downloading files"), NULL }, { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter, /* TRANSLATORS: command line option */ _("Filter with a set of device flags using a ~ prefix to " @@ -1594,6 +1598,15 @@ main (int argc, char *argv[]) return EXIT_FAILURE; } + /* allow disabling SSL strict mode for broken corporate proxies */ + if (priv->disable_ssl_strict) { + /* TRANSLATORS: try to help */ + g_printerr ("%s\n", _("WARNING: Ignoring SSL strict checks, " + "to do this automatically in the future " + "export DISABLE_SSL_STRICT in your environment")); + g_setenv ("DISABLE_SSL_STRICT", "1", TRUE); + } + /* parse filter flags */ if (filter != NULL) { if (!fu_util_parse_filter_flags (filter, diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 7349b5ef5..1daa18865 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -542,6 +542,10 @@ fu_util_setup_networking (GError **error) return NULL; } + /* relax the SSL checks for broken corporate proxies */ + if (g_getenv ("DISABLE_SSL_STRICT") != NULL) + g_object_set (session, SOUP_SESSION_SSL_STRICT, FALSE, NULL); + /* set the proxy */ http_proxy = g_getenv ("https_proxy"); if (http_proxy == NULL) diff --git a/src/fu-util.c b/src/fu-util.c index 7abef11ce..fd4bc052a 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -57,6 +57,7 @@ struct FuUtilPrivate { gboolean assume_yes; gboolean sign; gboolean show_all_devices; + gboolean disable_ssl_strict; /* only valid in update and downgrade */ FuUtilOperation current_operation; FwupdDevice *current_device; @@ -2061,6 +2062,9 @@ main (int argc, char *argv[]) { "show-all-devices", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all_devices, /* TRANSLATORS: command line option */ _("Show devices that are not updatable"), NULL }, + { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict, + /* TRANSLATORS: command line option */ + _("Ignore SSL strict checks when downloading files"), NULL }, { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter, /* TRANSLATORS: command line option */ _("Filter with a set of device flags using a ~ prefix to " @@ -2262,6 +2266,15 @@ main (int argc, char *argv[]) return EXIT_FAILURE; } + /* allow disabling SSL strict mode for broken corporate proxies */ + if (priv->disable_ssl_strict) { + /* TRANSLATORS: try to help */ + g_printerr ("%s\n", _("WARNING: Ignoring SSL strict checks, " + "to do this automatically in the future " + "export DISABLE_SSL_STRICT in your environment")); + g_setenv ("DISABLE_SSL_STRICT", "1", TRUE); + } + /* non-TTY consoles cannot answer questions */ if (log != NULL || isatty (fileno (stdout)) == 0) { From a39951b289d6811813c18c807df0252b10603734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Fri, 6 Sep 2019 12:39:31 +0100 Subject: [PATCH 100/158] unifying: fix timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- plugins/unifying/fu-unifying-bootloader.c | 2 +- plugins/unifying/fu-unifying-common.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/unifying/fu-unifying-bootloader.c b/plugins/unifying/fu-unifying-bootloader.c index 09ac3e361..813db96ab 100644 --- a/plugins/unifying/fu-unifying-bootloader.c +++ b/plugins/unifying/fu-unifying-bootloader.c @@ -446,7 +446,7 @@ fu_unifying_bootloader_init (FuUnifyingBootloader *self) fu_device_add_icon (FU_DEVICE (self), "preferences-desktop-keyboard"); fu_device_set_name (FU_DEVICE (self), "Unifying Receiver"); fu_device_set_summary (FU_DEVICE (self), "A miniaturised USB wireless receiver (bootloader)"); - fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_set_remove_delay (FU_DEVICE (self), FU_UNIFYING_DEVICE_TIMEOUT_MS); } static void diff --git a/plugins/unifying/fu-unifying-common.h b/plugins/unifying/fu-unifying-common.h index 15db475cb..413d553e5 100644 --- a/plugins/unifying/fu-unifying-common.h +++ b/plugins/unifying/fu-unifying-common.h @@ -19,7 +19,7 @@ G_BEGIN_DECLS #define FU_UNIFYING_DEVICE_PID_BOOTLOADER_TEXAS_PICO 0xaaad /* Signed firmware are very long to verify on the device */ -#define FU_UNIFYING_DEVICE_TIMEOUT_MS 20000 +#define FU_UNIFYING_DEVICE_TIMEOUT_MS 30000 guint8 fu_unifying_buffer_read_uint8 (const gchar *str); guint16 fu_unifying_buffer_read_uint16 (const gchar *str); From ccda6790f5447da007cd2fe56dcdcece5f3b6690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Fri, 6 Sep 2019 14:00:50 +0100 Subject: [PATCH 101/158] ci: arch: minor improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Moved version discovery routine to PKGBUILD - Set PKGEXT to .pkg.tar to avoid the package being compressed - Added --needed to pacman arguments when installing the dependencies to avoid reinstalling packages Signed-off-by: Filipe Laíns --- contrib/PKGBUILD | 11 ++++++++++- contrib/ci/arch.sh | 10 +++------- contrib/ci/generate_docker.py | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/contrib/PKGBUILD b/contrib/PKGBUILD index 38b4f912f..55be00aec 100644 --- a/contrib/PKGBUILD +++ b/contrib/PKGBUILD @@ -2,7 +2,7 @@ # Contributor: Mirco Tischler pkgname=fwupd -pkgver=#VERSION# +pkgver=dummy pkgrel=1 pkgdesc='A simple daemon to allow session software to update firmware' arch=('i686' 'x86_64') @@ -13,6 +13,15 @@ makedepends=('meson' 'valgrind' 'gobject-introspection' 'gtk-doc' 'python-pillow 'python-cairo' 'noto-fonts' 'noto-fonts-cjk' 'python-gobject' 'vala' 'libsoup' 'polkit' 'gcab') +pkgver() { + cd ${pkgname} + + VERSION=$(git describe | sed 's/-/.r/;s/-/./') + [ -z $VERSION ] && VERSION=$(head meson.build | grep ' version :' | cut -d \' -f2) + + echo $VERSION +} + build() { cd ${pkgname} if [ -n "$CI" ]; then diff --git a/contrib/ci/arch.sh b/contrib/ci/arch.sh index 49573f91a..b5bdf6c06 100755 --- a/contrib/ci/arch.sh +++ b/contrib/ci/arch.sh @@ -3,14 +3,10 @@ set -e set -x shopt -s extglob -VERSION=`git describe | sed 's/-/.r/;s/-/./'` -[ -z $VERSION ] && VERSION=`head meson.build | grep ' version :' | cut -d \' -f2` - # prepare the build tree rm -rf build mkdir build && pushd build cp ../contrib/PKGBUILD . -sed -i "s,#VERSION#,$VERSION," PKGBUILD mkdir -p src/fwupd && pushd src/fwupd cp -R ../../../!(build|dist) . popd @@ -23,10 +19,10 @@ trap "kill $!" EXIT export TPM_SERVER_RUNNING=1 # build the package and install it -sudo -E -u nobody makepkg -e --noconfirm -pacman -U --noconfirm *.pkg.tar.xz +sudo -E -u nobody PKGEXT='.pkg.tar' makepkg -e --noconfirm +pacman -U --noconfirm *.pkg.* # move the package to working dir -mv *.pkg.tar.xz ../dist +mv *.pkg.* ../dist # no testing here because gnome-desktop-testing isn’t available in Arch diff --git a/contrib/ci/generate_docker.py b/contrib/ci/generate_docker.py index 29d56b698..f8c6343c2 100755 --- a/contrib/ci/generate_docker.py +++ b/contrib/ci/generate_docker.py @@ -79,7 +79,7 @@ with open(out.name, 'w') as wfd: wfd.write("RUN apt update -qq && \\\n") wfd.write("\tapt install -yq --no-install-recommends\\\n") elif OS == "arch": - wfd.write("RUN pacman -Syu --noconfirm \\\n") + wfd.write("RUN pacman -Syu --noconfirm --needed\\\n") for i in range(0, len(deps)): if i < len(deps)-1: wfd.write("\t%s \\\n" % deps[i]) From 718905f7f0f4c3e2d62c0996ae5c52a67ca6c4fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Sat, 7 Sep 2019 20:53:21 +0100 Subject: [PATCH 102/158] unifying: fix timeout metadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- plugins/unifying/unifying.quirk | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/unifying/unifying.quirk b/plugins/unifying/unifying.quirk index 2191bb080..2f6f94e96 100644 --- a/plugins/unifying/unifying.quirk +++ b/plugins/unifying/unifying.quirk @@ -3,7 +3,7 @@ Plugin = unifying Flags = is-receiver VendorId=USB:0x046D -InstallDuration = 7 +InstallDuration = 30 # Nordic [DeviceInstanceId=USB\VID_046D&PID_AAAA] @@ -11,7 +11,7 @@ Plugin = unifying Flags = is-bootloader,is-nordic FirmwareSizeMin = 0x4000 CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B -InstallDuration = 7 +InstallDuration = 30 # Nordic Pico [DeviceInstanceId=USB\VID_046D&PID_AAAE] @@ -19,7 +19,7 @@ Plugin = unifying Flags = is-bootloader,is-nordic FirmwareSizeMin = 0x4000 CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B -InstallDuration = 7 +InstallDuration = 30 # Texas [DeviceInstanceId=USB\VID_046D&PID_AAAC] @@ -27,7 +27,7 @@ Plugin = unifying Flags = is-bootloader,is-texas FirmwareSizeMin = 0x4000 CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B -InstallDuration = 7 +InstallDuration = 18 # Texas Pico [DeviceInstanceId=USB\VID_046D&PID_AAAD] @@ -35,4 +35,4 @@ Plugin = unifying Flags = is-bootloader,is-texas FirmwareSizeMin = 0x4000 CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B -InstallDuration = 7 +InstallDuration = 18 From f5e68d4e8987428610cdca000222a84071a81434 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 9 Sep 2019 13:39:38 +0100 Subject: [PATCH 103/158] trivial: Don't use Recommends for RHEL 7 --- contrib/fwupd.spec.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index 87888f10f..a9fd8b1d9 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -106,7 +106,9 @@ Requires: libsoup%{?_isa} >= %{libsoup_version} Requires: bubblewrap Requires: shared-mime-info +%if 0%{?rhel} > 7 || 0%{?fedora} > 28 Recommends: python3 +%endif Obsoletes: fwupd-sign < 0.1.6 Obsoletes: libebitdo < 0.7.5-3 From 83e2ca2129da671ef8c41ed787a6b9b04ffb19ff Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 9 Sep 2019 13:40:35 +0100 Subject: [PATCH 104/158] trivial: Make it easy to disable or enable libflashrom for distros --- contrib/fwupd.spec.in | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index a9fd8b1d9..bc2603292 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -10,6 +10,7 @@ %global enable_ci 0 %global enable_tests 1 %global enable_dummy 1 +%global enable_flashrom 1 %global __meson_wrap_mode default # fwupd.efi is only available on these arches @@ -154,7 +155,11 @@ Data files for installed tests. %else -Dplugin_dummy=false \ %endif +%if 0%{?enable_flashrom} -Dplugin_flashrom=true \ +%else + -Dplugin_flashrom=false \ +%endif -Dplugin_thunderbolt=true \ %if 0%{?have_redfish} -Dplugin_redfish=true \ @@ -206,11 +211,13 @@ Data files for installed tests. mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg +%if 0%{?enable_flashrom} # delete most files from the subproject rm ${RPM_BUILD_ROOT}%{_includedir}/libflashrom.h rm ${RPM_BUILD_ROOT}%{_libdir}/libflashrom.so rm ${RPM_BUILD_ROOT}%{_libdir}/pkgconfig/libflashrom.pc rm ${RPM_BUILD_ROOT}%{_sbindir}/flashrom +%endif %find_lang %{name} @@ -303,7 +310,9 @@ rm ${RPM_BUILD_ROOT}%{_sbindir}/flashrom %{_libdir}/fwupd-plugins-3/libfu_plugin_dfu.so %{_libdir}/fwupd-plugins-3/libfu_plugin_ebitdo.so %{_libdir}/fwupd-plugins-3/libfu_plugin_fastboot.so +%if 0%{?enable_flashrom} %{_libdir}/fwupd-plugins-3/libfu_plugin_flashrom.so +%endif %if 0%{?have_modem_manager} %{_libdir}/fwupd-plugins-3/libfu_plugin_modem_manager.so %endif @@ -341,8 +350,10 @@ rm ${RPM_BUILD_ROOT}%{_sbindir}/flashrom %{_datadir}/locale/*/LC_IMAGES/fwupd* %endif +%if 0%{?enable_flashrom} # eww, but just until the Fedora package ships these... %{_libdir}/libflashrom.so.1* +%endif %files devel %{_datadir}/gir-1.0/Fwupd-2.0.gir From 1984180592cfaa35d187ba428e379f651c2cfa56 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 10 Sep 2019 16:48:00 +0100 Subject: [PATCH 105/158] Add a DMI quirk for the Minnowboard Turbot The fwupd UEFI plugin refuses to start if bit 3 of the "BIOS Characteristics Extension Byte 2" (13h) is cleared. Bit 3 is UEFI Specification is supported. Also, double-check that /sys/firmware/efi does not exist before disabling the plugin as other machines may be affected too. The quirk then acts to supress the console warning about the broken SMBIOS value. Fixes https://github.com/fwupd/fwupd/issues/1342 --- plugins/uefi/fu-plugin-uefi.c | 30 +++++++++++++++++++++++-- plugins/uefi/uefi.quirk | 4 ++++ src/fu-plugin.c | 41 +++++++++++++++++++++++++++++++++++ src/fu-plugin.h | 2 ++ 4 files changed, 75 insertions(+), 2 deletions(-) diff --git a/plugins/uefi/fu-plugin-uefi.c b/plugins/uefi/fu-plugin-uefi.c index 83235ffd0..d4d4be5a4 100644 --- a/plugins/uefi/fu-plugin-uefi.c +++ b/plugins/uefi/fu-plugin-uefi.c @@ -587,8 +587,8 @@ fu_plugin_uefi_delete_old_capsules (FuPlugin *plugin, GError **error) return TRUE; } -gboolean -fu_plugin_startup (FuPlugin *plugin, GError **error) +static gboolean +fu_plugin_uefi_smbios_enabled (FuPlugin *plugin, GError **error) { const guint8 *data; gsize sz; @@ -625,6 +625,32 @@ fu_plugin_startup (FuPlugin *plugin, GError **error) "System does not support UEFI mode"); return FALSE; } + return TRUE; +} + +gboolean +fu_plugin_startup (FuPlugin *plugin, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* some platforms have broken SMBIOS data */ + if (fu_plugin_has_custom_flag (plugin, "uefi-force-enable")) + return TRUE; + + /* check SMBIOS for 'UEFI Specification is supported' */ + if (!fu_plugin_uefi_smbios_enabled (plugin, &error_local)) { + g_autofree gchar *fw = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); + g_autofree gchar *fn = g_build_filename (fw, "efi", NULL); + if (g_file_test (fn, G_FILE_TEST_EXISTS)) { + g_warning ("SMBIOS BIOS Characteristics Extension Byte 2 is invalid -- " + "UEFI Specification is unsupported, but %s exists: %s", + fn, error_local->message); + return TRUE; + } + g_propagate_error (error, g_steal_pointer (&error_local)); + return FALSE; + } + /* test for invalid ESP in coldplug, and set the update-error rather * than showing no output if the plugin had self-disabled here */ return TRUE; diff --git a/plugins/uefi/uefi.quirk b/plugins/uefi/uefi.quirk index ab2f02993..46b2934ee 100644 --- a/plugins/uefi/uefi.quirk +++ b/plugins/uefi/uefi.quirk @@ -20,3 +20,7 @@ Flags = use-shim-unique # Star Labs Lite (Mk II) [Guid=797f8bae-0ea2-4c0f-8a30-7d10ccfacbc0] Flags = use-shim-unique,no-ux-capsule + +# Silicom Minnowboard Turbot D0/D1 PLATFORM +[HwId=386c2f52-44ec-55d3-b802-47631bfb8451] +Flags = uefi-force-enable diff --git a/src/fu-plugin.c b/src/fu-plugin.c index 5d3a1450b..4efa58dc2 100644 --- a/src/fu-plugin.c +++ b/src/fu-plugin.c @@ -529,6 +529,47 @@ fu_plugin_get_hwids (FuPlugin *self) return fu_hwids_get_guids (priv->hwids); } +/** + * fu_plugin_has_custom_flag: + * @self: A #FuPlugin + * @flag: A custom text flag, specific to the plugin, e.g. `uefi-force-enable` + * + * Returns if a per-plugin HwId custom flag exists, typically added from a DMI quirk. + * + * Returns: %TRUE if the quirk entry exists + * + * Since: 1.3.1 + **/ +gboolean +fu_plugin_has_custom_flag (FuPlugin *self, const gchar *flag) +{ + FuPluginPrivate *priv = GET_PRIVATE (self); + GPtrArray *hwids = fu_plugin_get_hwids (self); + + g_return_val_if_fail (FU_IS_PLUGIN (self), FALSE); + g_return_val_if_fail (flag != NULL, FALSE); + + /* never set up, e.g. in tests */ + if (hwids == NULL) + return FALSE; + + /* search each hwid */ + for (guint i = 0; i < hwids->len; i++) { + const gchar *hwid = g_ptr_array_index (hwids, i); + const gchar *value; + g_autofree gchar *key = g_strdup_printf ("HwId=%s", hwid); + + /* does prefixed quirk exist */ + value = fu_quirks_lookup_by_id (priv->quirks, key, FU_QUIRKS_FLAGS); + if (value != NULL) { + g_auto(GStrv) quirks = g_strsplit (value, ",", -1); + if (g_strv_contains ((const gchar * const *) quirks, flag)) + return TRUE; + } + } + return FALSE; +} + /** * fu_plugin_check_supported: * @self: A #FuPlugin diff --git a/src/fu-plugin.h b/src/fu-plugin.h index edf6633f5..fbbfe54cf 100644 --- a/src/fu-plugin.h +++ b/src/fu-plugin.h @@ -151,5 +151,7 @@ void fu_plugin_add_runtime_version (FuPlugin *self, void fu_plugin_add_compile_version (FuPlugin *self, const gchar *component_id, const gchar *version); +gboolean fu_plugin_has_custom_flag (FuPlugin *self, + const gchar *flag); G_END_DECLS From 04cb360847b4f11560eba16ea06cc899f5784293 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 10 Sep 2019 21:11:32 +0100 Subject: [PATCH 106/158] uefi-recovery: Add a plugin to add a fake ESRT entry for device recovery The factory-shipped MinnowBoardMAX board has firmware that does not include the ESRT table. Create a 'fake' UEFI device with the lowest possible version so that it can be updated to any version firmware. All the HwId GUIDs are used for the fake UEFI device, and so should be used in the firmware metadata for releases that should recover the system. --- contrib/fwupd.spec.in | 1 + plugins/meson.build | 1 + plugins/uefi-recovery/README.md | 17 ++++++ .../uefi-recovery/fu-plugin-uefi-recovery.c | 53 +++++++++++++++++++ plugins/uefi-recovery/meson.build | 28 ++++++++++ plugins/uefi-recovery/uefi-recovery.quirk | 3 ++ plugins/uefi/fu-plugin-uefi.c | 3 +- 7 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 plugins/uefi-recovery/README.md create mode 100644 plugins/uefi-recovery/fu-plugin-uefi-recovery.c create mode 100644 plugins/uefi-recovery/meson.build create mode 100644 plugins/uefi-recovery/uefi-recovery.quirk diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index bc2603292..5c183936d 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -340,6 +340,7 @@ rm ${RPM_BUILD_ROOT}%{_sbindir}/flashrom %{_libdir}/fwupd-plugins-3/libfu_plugin_udev.so %if 0%{?have_uefi} %{_libdir}/fwupd-plugins-3/libfu_plugin_uefi.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_uefi_recovery.so %endif %{_libdir}/fwupd-plugins-3/libfu_plugin_unifying.so %{_libdir}/fwupd-plugins-3/libfu_plugin_upower.so diff --git a/plugins/meson.build b/plugins/meson.build index 68daba8c6..edc3519ae 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -57,6 +57,7 @@ endif if get_option('plugin_uefi') subdir('uefi') +subdir('uefi-recovery') endif if get_option('plugin_flashrom') diff --git a/plugins/uefi-recovery/README.md b/plugins/uefi-recovery/README.md new file mode 100644 index 000000000..311a2974f --- /dev/null +++ b/plugins/uefi-recovery/README.md @@ -0,0 +1,17 @@ +UEFI Support +============ + +Introduction +------------ + +Some devices have firmware bugs which mean they do not include a valid ESRT +table in old firmware versions. + +Create a 'fake' UEFI device with the lowest possible version so that it can be +updated to a version of firmware which does have an ESRT table. + +GUID Generation +--------------- + +All the HwId GUIDs are used for the fake UEFI device, and so should be used in +the firmware metadata for releases that should recover the system. diff --git a/plugins/uefi-recovery/fu-plugin-uefi-recovery.c b/plugins/uefi-recovery/fu-plugin-uefi-recovery.c new file mode 100644 index 000000000..cd2103b2a --- /dev/null +++ b/plugins/uefi-recovery/fu-plugin-uefi-recovery.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-device-metadata.h" +#include "fu-plugin-vfuncs.h" + +void +fu_plugin_init (FuPlugin *plugin) +{ + /* make sure that UEFI plugin is ready to receive devices */ + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_AFTER, "uefi"); + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); +} + +gboolean +fu_plugin_startup (FuPlugin *plugin, GError **error) +{ + if (!fu_plugin_has_custom_flag (plugin, "requires-uefi-recovery")) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not required"); + return FALSE; + } + return TRUE; +} + +gboolean +fu_plugin_coldplug (FuPlugin *plugin, GError **error) +{ + GPtrArray *hwids = fu_plugin_get_hwids (plugin); + g_autoptr(FuDevice) device = fu_device_new (); + fu_device_set_id (device, "uefi-recovery"); + fu_device_set_name (device, "System Firmware ESRT Recovery"); + fu_device_set_version (device, "0.0.0", FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_set_metadata (device, FU_DEVICE_METADATA_UEFI_DEVICE_KIND, "system-firmware"); + fu_device_add_icon (device, "computer"); + for (guint i = 0; i < hwids->len; i++) { + const gchar *hwid = g_ptr_array_index (hwids, i); + fu_device_add_guid (device, hwid); + } + fu_plugin_device_register (plugin, device); + return TRUE; +} diff --git a/plugins/uefi-recovery/meson.build b/plugins/uefi-recovery/meson.build new file mode 100644 index 000000000..a674b2a57 --- /dev/null +++ b/plugins/uefi-recovery/meson.build @@ -0,0 +1,28 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginUefiRecovery"'] + +install_data(['uefi-recovery.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_uefi_recovery', + fu_hash, + sources : [ + 'fu-plugin-uefi-recovery.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + install : true, + install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], + c_args : [ + cargs, + ], + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/uefi-recovery/uefi-recovery.quirk b/plugins/uefi-recovery/uefi-recovery.quirk new file mode 100644 index 000000000..8370ecf40 --- /dev/null +++ b/plugins/uefi-recovery/uefi-recovery.quirk @@ -0,0 +1,3 @@ +# Silicom Minnowboard Turbot MNW2MAX1.X64.0100.R01.1811141729 +[HwId=ea358e00-39f1-55b6-97be-a39225a585e1] +Flags = requires-uefi-recovery diff --git a/plugins/uefi/fu-plugin-uefi.c b/plugins/uefi/fu-plugin-uefi.c index d4d4be5a4..96b476755 100644 --- a/plugins/uefi/fu-plugin-uefi.c +++ b/plugins/uefi/fu-plugin-uefi.c @@ -12,6 +12,7 @@ #include #include +#include "fu-device-metadata.h" #include "fu-plugin-vfuncs.h" #include "fu-uefi-bgrt.h" @@ -448,7 +449,7 @@ fu_plugin_uefi_register_proxy_device (FuPlugin *plugin, FuDevice *device) void fu_plugin_device_registered (FuPlugin *plugin, FuDevice *device) { - if (fu_device_get_metadata (device, "UefiDeviceKind") != NULL) { + if (fu_device_get_metadata (device, FU_DEVICE_METADATA_UEFI_DEVICE_KIND) != NULL) { if (fu_device_get_guid_default (device) == NULL) { g_autofree gchar *dbg = fu_device_to_string (device); g_warning ("cannot create proxy device as no GUID: %s", dbg); From 71db86f466b27ffe28dcfa5bfdeda6c336448c8c Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 11 Sep 2019 10:25:47 +0100 Subject: [PATCH 107/158] Re-introduce GUID support to fwupdmgr and fwupdtool This reverts commit dca4d6a5d69c46f7368b2f65b49b98d58f9217b8. I've had two vendors ask me why the GUIDs have disappeared from the device. --- src/fu-util-common.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 1daa18865..addf30b1b 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -880,10 +880,13 @@ gchar * fu_util_device_to_string (FwupdDevice *dev, guint idt) { FwupdUpdateState state; + GPtrArray *guids = fwupd_device_get_guids (dev); + GPtrArray *instance_ids = fwupd_device_get_instance_ids (dev); GString *str = g_string_new (NULL); const gchar *tmp; const gchar *tmp2; guint64 flags = fwupd_device_get_flags (dev); + g_autoptr(GHashTable) ids = NULL; g_autoptr(GString) flags_str = g_string_new (NULL); /* some fields are intentionally not included and are only shown in --verbose */ @@ -999,6 +1002,32 @@ fu_util_device_to_string (FwupdDevice *dev, guint idt) fu_common_string_append_kv (str, idt + 1, _("Flags"), flags_str->str); } + /* all GUIDs for this hardware, with IDs if available */ + ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + for (guint i = 0; i < instance_ids->len; i++) { + const gchar *instance_id = g_ptr_array_index (instance_ids, i); + g_hash_table_insert (ids, + fwupd_guid_hash_string (instance_id), + g_strdup (instance_id)); + } + for (guint i = 0; i < guids->len; i++) { + const gchar *guid = g_ptr_array_index (guids, i); + const gchar *instance_id = g_hash_table_lookup (ids, guid); + g_autofree gchar *guid_src = NULL; + + /* instance IDs are only available as root */ + if (instance_id == NULL) { + guid_src = g_strdup (guid); + } else { + guid_src = g_strdup_printf ("%s ← %s", guid, instance_id); + } + if (i == 0) { + /* TRANSLATORS: global ID common to all similar hardware */ + fu_common_string_append_kv (str, idt + 1, ngettext ("GUID", "GUIDs", guids->len), guid_src); + } else { + fu_common_string_append_kv (str, idt + 1, "", guid_src); + } + } return g_string_free (str, FALSE); } From 1c257b7779a168946b898dbe93c908b8d9d3aa6d Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 12 Sep 2019 09:44:38 +0100 Subject: [PATCH 108/158] synapticsmst: Add another name quirk for the ThinkPad USB-C Dock --- plugins/synapticsmst/synapticsmst.quirk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/synapticsmst/synapticsmst.quirk b/plugins/synapticsmst/synapticsmst.quirk index 3461d31c3..3aa6ce791 100644 --- a/plugins/synapticsmst/synapticsmst.quirk +++ b/plugins/synapticsmst/synapticsmst.quirk @@ -43,3 +43,6 @@ Name = ThinkPad Workstation Dock [SynapticsMSTBoardID=595] Name = ThinkPad Thunderbolt 3 Workstation Dock + +[SynapticsMSTBoardID=596] +Name = ThinkPad USB-C Dock Gen2 From fa0c41fb367a4f46a0ccf3fefb2701d3e4b5fec8 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 12 Sep 2019 15:58:06 +0100 Subject: [PATCH 109/158] modem-manager: Fix two build warnings on Fedora 30 --- plugins/modem-manager/fu-qmi-pdc-updater.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/modem-manager/fu-qmi-pdc-updater.c b/plugins/modem-manager/fu-qmi-pdc-updater.c index 1be45368c..b50183a77 100644 --- a/plugins/modem-manager/fu-qmi-pdc-updater.c +++ b/plugins/modem-manager/fu-qmi-pdc-updater.c @@ -8,6 +8,7 @@ #include +#include "fu-common.h" #include "fu-qmi-pdc-updater.h" #define FU_QMI_PDC_MAX_OPEN_ATTEMPTS 8 @@ -356,7 +357,7 @@ fu_qmi_pdc_updater_load_config (WriteContext *ctx) chunk = g_array_sized_new (FALSE, FALSE, sizeof (guint8), chunk_size); g_array_set_size (chunk, chunk_size); - if (!fu_memcpy_safe (chunk->data, chunk_size, 0x0, /* dst */ + if (!fu_memcpy_safe ((guint8 *)chunk->data, chunk_size, 0x0, /* dst */ (const guint8 *)g_bytes_get_data (ctx->blob, NULL), /* src */ g_bytes_get_size (ctx->blob), ctx->offset, chunk_size, &error)) { From 03ce21c61fb6fb442a969c6640a5b6eec1715a98 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 12 Sep 2019 15:23:00 +0100 Subject: [PATCH 110/158] Move to CI Fedora 30 Fedora 29 is EOL in just a few weeks. --- contrib/ci/Dockerfile-fedora.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/ci/Dockerfile-fedora.in b/contrib/ci/Dockerfile-fedora.in index 6f6994316..4a65f3696 100644 --- a/contrib/ci/Dockerfile-fedora.in +++ b/contrib/ci/Dockerfile-fedora.in @@ -1,4 +1,4 @@ -FROM fedora:29 +FROM fedora:30 %%%OS%%% ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en From 6f82b989e870c5b47cad8d460e6994c77be3a9bb Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 12 Sep 2019 15:21:57 +0100 Subject: [PATCH 111/158] trivial: Specify the tpm2-tss-devel version in the example spec file Lower versions than this fail to link the uefi self test binary. --- contrib/fwupd.spec.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index 5c183936d..dbd7affb7 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -87,7 +87,7 @@ BuildRequires: freetype BuildRequires: fontconfig BuildRequires: google-noto-sans-cjk-ttc-fonts BuildRequires: gnu-efi-devel -BuildRequires: tpm2-tss-devel +BuildRequires: tpm2-tss-devel >= 2.2.3 BuildRequires: pesign %endif From fbff921b358c4969d60d9d15170b5f5bd5f5f863 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Fri, 13 Sep 2019 08:32:05 +0100 Subject: [PATCH 112/158] Allow disabling all plugins This allows us to easily build just libfwupd in a flatpak manifest without installing dozens of deps to build things we're just going to delete anyway. --- data/bash-completion/meson.build | 4 +- data/meson.build | 25 ++++----- data/remotes.d/meson.build | 2 +- meson.build | 93 +++++++++++++++++++------------- meson_options.txt | 2 +- src/meson.build | 8 +-- 6 files changed, 77 insertions(+), 57 deletions(-) diff --git a/data/bash-completion/meson.build b/data/bash-completion/meson.build index 37a79d995..707461292 100644 --- a/data/bash-completion/meson.build +++ b/data/bash-completion/meson.build @@ -21,13 +21,13 @@ configure_file( install: true, install_dir: tgt) -if get_option('daemon') +if build_daemon configure_file( input : 'fwupdmgr.in', output : 'fwupdmgr', configuration : con2, install: true, install_dir: tgt) -endif # get_option('daemon') +endif # build_daemon endif # bashcomp.found() diff --git a/data/meson.build b/data/meson.build index 829c01f89..814c77cd3 100644 --- a/data/meson.build +++ b/data/meson.build @@ -2,19 +2,21 @@ subdir('builder') subdir('pki') subdir('remotes.d') subdir('bash-completion') + +if build_daemon subdir('motd') +endif if get_option('tests') subdir('tests') endif -if get_option('daemon') +if build_daemon subdir('installed-tests') + install_data(['daemon.conf'], + install_dir : join_paths(sysconfdir, 'fwupd') + ) endif -install_data(['daemon.conf'], - install_dir : join_paths(sysconfdir, 'fwupd') -) - install_data(['org.freedesktop.fwupd.metainfo.xml'], install_dir: join_paths(datadir, 'metainfo') ) @@ -23,17 +25,16 @@ install_data(['org.freedesktop.fwupd.svg'], install_dir : join_paths(datadir, 'icons', 'hicolor', 'scalable', 'apps') ) -install_data(['org.freedesktop.fwupd.conf'], - install_dir : join_paths(sysconfdir, 'dbus-1', 'system.d') -) - -if get_option('daemon') +if build_daemon + install_data(['org.freedesktop.fwupd.conf'], + install_dir : join_paths(sysconfdir, 'dbus-1', 'system.d') + ) install_data(['90-fwupd-devices.rules'], install_dir : join_paths(udevdir, 'rules.d') ) endif -if get_option('systemd') +if get_option('systemd') and build_daemon con2 = configuration_data() con2.set('libexecdir', libexecdir) con2.set('bindir', bindir) @@ -87,7 +88,7 @@ if get_option('systemd') ) endif -if get_option('systemd') or get_option('elogind') +if (get_option('systemd') or get_option('elogind')) and build_daemon con2 = configuration_data() con2.set('libexecdir', libexecdir) diff --git a/data/remotes.d/meson.build b/data/remotes.d/meson.build index a27c31efd..826a3c1d4 100644 --- a/data/remotes.d/meson.build +++ b/data/remotes.d/meson.build @@ -1,4 +1,4 @@ -if get_option('daemon') and get_option('lvfs') +if build_daemon and get_option('lvfs') install_data([ 'lvfs.conf', 'lvfs-testing.conf', diff --git a/meson.build b/meson.build index d30a64327..82dc9a0e6 100644 --- a/meson.build +++ b/meson.build @@ -135,6 +135,18 @@ add_project_arguments('-DFWUPD_DISABLE_DEPRECATED', language : 'c') add_project_arguments('-D_BSD_SOURCE', language : 'c') add_project_arguments('-D_XOPEN_SOURCE=700', language : 'c') +# sanity check +if get_option('build') == 'all' + build_standalone = true + build_daemon = true +elif get_option('build') == 'standalone' + build_standalone = true + build_daemon = false +elif get_option('build') == 'library' + build_standalone = false + build_daemon = false +endif + prefix = get_option('prefix') bindir = join_paths(prefix, get_option('bindir')) @@ -147,11 +159,12 @@ mandir = join_paths(prefix, get_option('mandir')) localedir = join_paths(prefix, get_option('localedir')) gio = dependency('gio-2.0', version : '>= 2.45.8') +giounix = dependency('gio-unix-2.0', version : '>= 2.45.8') if gio.version().version_compare ('>= 2.55.0') conf.set('HAVE_GIO_2_55_0', '1') endif +if build_standalone gmodule = dependency('gmodule-2.0') -giounix = dependency('gio-unix-2.0', version : '>= 2.45.8') gudev = dependency('gudev-1.0') if gudev.version().version_compare('>= 232') conf.set('HAVE_GUDEV_232', '1') @@ -160,10 +173,11 @@ libxmlb = dependency('xmlb', version : '>= 0.1.7', fallback : ['libxmlb', 'libxm gusb = dependency('gusb', version : '>= 0.2.9') sqlite = dependency('sqlite3') libarchive = dependency('libarchive') +endif libjsonglib = dependency('json-glib-1.0', version : '>= 1.1.1') valgrind = dependency('valgrind', required: false) soup = dependency('libsoup-2.4', version : '>= 2.51.92') -if get_option('daemon') +if build_daemon polkit = dependency('polkit-gobject-1', version : '>= 0.103') if polkit.version().version_compare('>= 0.114') conf.set('HAVE_POLKIT_0_114', '1') @@ -203,15 +217,15 @@ if valgrind.found() conf.set('HAVE_VALGRIND', '1') endif -if get_option('plugin_redfish') +if build_standalone and get_option('plugin_redfish') efivar = dependency('efivar') endif -if get_option('plugin_altos') +if build_standalone and get_option('plugin_altos') libelf = dependency('libelf') endif -if get_option('plugin_uefi') +if build_standalone and get_option('plugin_uefi') cairo = dependency('cairo') fontconfig = cc.find_library('fontconfig') freetype = cc.find_library('freetype') @@ -249,7 +263,7 @@ if get_option('plugin_uefi') endif endif -if get_option('plugin_dell') +if build_standalone and get_option('plugin_dell') libsmbios_c = dependency('libsmbios_c', version : '>= 2.4.0') efivar = dependency('efivar') conf.set('HAVE_DELL', '1') @@ -258,63 +272,64 @@ if get_option('plugin_dell') endif endif -if get_option('plugin_modem_manager') +if build_standalone and get_option('plugin_modem_manager') libmm_glib = dependency('mm-glib', version : '>= 1.10.0') add_project_arguments('-DMM_REQUIRED_VERSION="1.10.0"', language : 'c') libqmi_glib = dependency('qmi-glib', version : '>= 1.22.0') add_project_arguments('-DQMI_REQUIRED_VERSION="1.23.1"', language : 'c') endif -if get_option('plugin_nvme') +if build_standalone and get_option('plugin_nvme') if not cc.has_header('linux/nvme_ioctl.h') error('NVMe support requires kernel >= 4.4') endif endif -if get_option('plugin_synaptics') +if build_standalone and get_option('plugin_synaptics') conf.set('HAVE_SYNAPTICS', '1') endif -if get_option('plugin_thunderbolt') +if build_standalone and get_option('plugin_thunderbolt') umockdev = dependency('umockdev-1.0', required: false) conf.set('HAVE_THUNDERBOLT', '1') endif -if get_option('plugin_flashrom') +if build_standalone and get_option('plugin_flashrom') libflashrom = dependency('flashrom', fallback : ['flashrom', 'flashrom_dep']) endif -if get_option('systemd') +if build_standalone and get_option('systemd') systemd = dependency('systemd', version : '>= 211') conf.set('HAVE_SYSTEMD' , '1') conf.set('HAVE_LOGIND' , '1') + systemdunitdir = get_option('systemdunitdir') + if systemdunitdir == '' and get_option('systemd') + systemdunitdir = systemd.get_pkgconfig_variable('systemdsystemunitdir') + endif endif -if get_option('elogind') +if build_standalone and get_option('elogind') elogind = dependency('libelogind', version : '>= 211') conf.set('HAVE_LOGIND' , '1') endif -if get_option('consolekit') +if build_standalone and get_option('consolekit') conf.set('HAVE_CONSOLEKIT' , '1') endif -systemdunitdir = get_option('systemdunitdir') -if systemdunitdir == '' and get_option('systemd') - systemdunitdir = systemd.get_pkgconfig_variable('systemdsystemunitdir') -endif - gnome = import('gnome') i18n = import('i18n') -plugin_dir = join_paths(libdir, 'fwupd-plugins-3') - conf.set_quoted('BINDIR', bindir) conf.set_quoted('LIBEXECDIR', libexecdir) conf.set_quoted('DATADIR', datadir) conf.set_quoted('LOCALSTATEDIR', localstatedir) conf.set_quoted('SYSCONFDIR', sysconfdir) + +if build_standalone +plugin_dir = join_paths(libdir, 'fwupd-plugins-3') conf.set_quoted('PLUGINDIR', plugin_dir) +endif conf.set_quoted('GETTEXT_PACKAGE', meson.project_name()) conf.set_quoted('PACKAGE_NAME', meson.project_name()) @@ -325,30 +340,34 @@ configure_file( configuration : conf ) -plugin_deps = [] -plugin_deps += libxmlb -plugin_deps += gio -plugin_deps += giounix -plugin_deps += gmodule -plugin_deps += gusb -plugin_deps += soup -plugin_deps += libarchive -plugin_deps += gudev +if build_standalone + plugin_deps = [] + plugin_deps += libxmlb + plugin_deps += gio + plugin_deps += giounix + plugin_deps += gmodule + plugin_deps += gusb + plugin_deps += soup + plugin_deps += libarchive + plugin_deps += gudev +endif -subdir('data') if get_option('gtkdoc') gtkdocscan = find_program('gtkdoc-scan', required : true) subdir('docs') endif subdir('libfwupd') -subdir('po') -if get_option('daemon') +if build_daemon subdir('policy') endif -subdir('src') -subdir('plugins') -subdir('contrib') +if build_standalone + subdir('data') + subdir('po') + subdir('src') + subdir('plugins') + subdir('contrib') +endif -if get_option('systemd') and get_option('daemon') +if get_option('systemd') and build_daemon meson.add_install_script('meson_post_install.sh', systemdunitdir, localstatedir) endif diff --git a/meson_options.txt b/meson_options.txt index e94e16c1e..0acb7f136 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,4 +1,4 @@ -option('daemon', type : 'boolean', value : true, description : 'enable the fwupd daemon') +option('build', type : 'combo', choices : ['all', 'standalone', 'library'], value : 'all', description : 'build type') option('agent', type : 'boolean', value : true, description : 'enable the fwupd agent') option('consolekit', type : 'boolean', value : true, description : 'enable ConsoleKit support') option('firmware-packager', type : 'boolean', value : true, description : 'enable firmware-packager installation') diff --git a/src/meson.build b/src/meson.build index ff0e4a4cc..a97db534f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,4 +1,4 @@ -if get_option('daemon') +if build_daemon install_data(['org.freedesktop.fwupd.xml'], install_dir : join_paths(datadir, 'dbus-1', 'interfaces') ) @@ -80,7 +80,7 @@ libfwupdprivate = static_library( ], ) -if get_option('daemon') +if build_daemon fwupdmgr = executable( 'fwupdmgr', sources : [ @@ -236,7 +236,7 @@ fwupdtool = executable( install_dir : join_paths(libexecdir, 'fwupd') ) -if get_option('daemon') and get_option('man') +if build_daemon and get_option('man') help2man = find_program('help2man') custom_target('fwupdmgr-man', input : fwupdmgr, @@ -254,7 +254,7 @@ if get_option('daemon') and get_option('man') ) endif -if get_option('daemon') +if build_daemon executable( 'fwupd', resources_src, From 67ad8d0d223978c334cd192a3b4a0a282d22273b Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Fri, 13 Sep 2019 10:58:38 +0100 Subject: [PATCH 113/158] Release fwupd 1.3.1 --- data/org.freedesktop.fwupd.metainfo.xml | 34 +++ po/af.po | 53 +---- po/ca.po | 177 +++++++++----- po/cs.po | 69 +----- po/da.po | 181 ++++++++------ po/de.po | 58 +---- po/en_GB.po | 72 +----- po/eo.po | 6 +- po/fi.po | 83 ++----- po/fr.po | 1 - po/fur.po | 63 +---- po/he.po | 11 +- po/hi.po | 1 - po/hr.po | 177 +++++++++----- po/hu.po | 75 +----- po/id.po | 63 +---- po/it.po | 177 +++++++++----- po/ko.po | 75 +----- po/lt.po | 75 +----- po/nl.po | 27 --- po/oc.po | 11 +- po/pl.po | 177 +++++++++----- po/pt_BR.po | 179 +++++++++----- po/ru.po | 75 +----- po/sk.po | 32 +-- po/sr.po | 63 +---- po/sv.po | 300 +++++++++++++++++++----- po/tr.po | 22 -- po/uk.po | 177 +++++++++----- po/zh_CN.po | 75 +----- po/zh_TW.po | 67 +----- 31 files changed, 1150 insertions(+), 1506 deletions(-) diff --git a/data/org.freedesktop.fwupd.metainfo.xml b/data/org.freedesktop.fwupd.metainfo.xml index 221814125..ecf1a8b92 100644 --- a/data/org.freedesktop.fwupd.metainfo.xml +++ b/data/org.freedesktop.fwupd.metainfo.xml @@ -32,6 +32,40 @@ fwupdmgr + + +

          This release adds the following features:

          +
            +
          • Add support for the Minnowboard Turbot
          • +
          • Add support for the SoloKey Secure
          • +
          • Add support for thunderbolt kernel safety checks
          • +
          • Add support to integrate into the motd
          • +
          • Allow filtering devices when using the command line tools
          • +
          • Allow setting custom flags when using fwupdate
          • +
          • Allow specifying a firmware GUID to check any version exists
          • +
          • Include the kernel release as a runtime version
          • +
          • Print devices, remotes, releases using a tree
          • +
          • Publish docs to fwupd.github.io using CircleCI
          • +
          +

          This release fixes the following bugs:

          +
            +
          • Add aliases for get-upgrades and upgrade
          • +
          • Allow disabling SSL strict mode for broken corporate proxies
          • +
          • Be more accepting when trying to recover a failed database migration
          • +
          • Do not segfault when trying to quit the downgrade selection
          • +
          • Fix a possible crash when stopping the fwupd service
          • +
          • Fix incomplete hex file parsing in unifying plugin
          • +
          • Fix thunderbolt logic to work properly with ICL thunderbolt controller
          • +
          • Never show AppStream markup on the console
          • +
          • Never use memcpy() in a possibly unsafe way
          • +
          • Only write the new UEFI device path if different than before
          • +
          • Partially rewrite the Synapticsmst plugin to support more hardware
          • +
          • Reload metadata store when configuration changes
          • +
          • Use environment variables for systemd managed directories
          • +
          • Use tpm2-tss library to read PCR values
          • +
          +
          +

          This release adds the following features:

          diff --git a/po/af.po b/po/af.po index 7ef5174a1..573f294a9 100644 --- a/po/af.po +++ b/po/af.po @@ -84,7 +84,6 @@ msgstr "Gekanselleer" msgid "Changed" msgstr "Verander" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Kontrolesom" @@ -112,7 +111,7 @@ msgstr "DFU" msgid "Decompressing…" msgstr "Pak tans uit…" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Beskrywing" @@ -192,10 +191,6 @@ msgstr "Lêernaamhandtekening" msgid "Found" msgstr "Gevind" -msgid "GUID" -msgstr "GUID" - -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -223,16 +218,11 @@ msgstr "Laai tans…" msgid "Metadata URI" msgstr "Metadata-URI" -#. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Metadata-URI-handtekening" - msgid "Mode" msgstr "Modus" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Naam" @@ -317,17 +307,13 @@ msgstr "Toestand" msgid "Status" msgstr "Status" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Opsomming" msgid "Target" msgstr "Teiken" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Titel" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Oordraggrootte" @@ -336,43 +322,10 @@ msgstr "Oordraggrootte" msgid "Type" msgstr "Tipe" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Onbekend" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Bywerking se kontrolesom" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Bywerking se beskrywing" - -#. TRANSLATORS: section header for the amount -#. * of time it takes to install the update -msgid "Update Duration" -msgstr "Bywerking se duur" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Bywerking se ligging" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Bywerking se naam" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Bywerking se opsomming" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Bywerking se weergawe" - #. TRANSLATORS: ask the user if we can update the metadata msgid "Update now?" msgstr "Werk nou by?" @@ -405,7 +358,7 @@ msgstr "Gebruikernaam" msgid "Verifying…" msgstr "Verifieer tans…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Weergawe" diff --git a/po/ca.po b/po/ca.po index 78569d211..db9f8e59d 100644 --- a/po/ca.po +++ b/po/ca.po @@ -66,6 +66,13 @@ msgstr "Actualització ME de %s" msgid "%s System Update" msgstr "Actualizació del sistema %s" +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Actualització del controlador Thunderbolt %s" + #. TRANSLATORS: this is the fallback where we don't know if the release #. * is updating the system, the device, or a device class, or something else #. -- @@ -74,11 +81,6 @@ msgstr "Actualizació del sistema %s" msgid "%s Update" msgstr "Actualització de %s" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s té actualitzacions de microprogramari:" - #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -239,6 +241,10 @@ msgstr "Es requereix autenticació per actualitzar el microprogramari en aquesta msgid "Authentication is required to update the stored checksums for the device" msgstr "Es requereix autenticació per actualitzar les sumes de verificació emmagatzemades pels dispositius" +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Versió del carregador d'arrencada" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Construeix el microprogramari usant un entorn de proves" @@ -256,7 +262,6 @@ msgstr "S'ha cancel·lat" msgid "Changed" msgstr "S'ha canviat" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Suma de comprovació" @@ -297,6 +302,10 @@ msgstr "Converteix el microprogramari al format DFU" msgid "Create a binary patch using two files" msgstr "Crea un pedaç binari emprant dos fitxers" +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Versió actual" + msgid "DFU" msgstr "DFU" @@ -316,7 +325,7 @@ msgstr "S'està descomprimint..." msgid "Decrypt firmware data" msgstr "Desencripta les dades del microprogramari" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Descripció" @@ -328,6 +337,10 @@ msgstr "Desconecta el dispositiu amb capacitat DFU actualment connectat" msgid "Detach to bootloader mode" msgstr "Separa del mode carregador d'arrencada" +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Detalls" + #. TRANSLATORS: ID for hardware, typically a SHA1 sum msgid "Device ID" msgstr "ID del dispositiu" @@ -375,6 +388,10 @@ msgstr "No realitzis el reinici després de l'actualització" msgid "Do not check for unreported history" msgstr "No comprovar si hi ha un historial sense informar" +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "No incloure el prefix de la marca de temps" + #. TRANSLATORS: command line option msgid "Do not write to the history database" msgstr "No escriure a la base de dades de l'historial" @@ -415,6 +432,10 @@ msgstr "Bolca els detalls sobre un fitxer de microprogramari" msgid "Dump information about a binary patch to the screen" msgstr "Bolca la informació sobre un pedaç binari a la pantalla" +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Durada" + #. TRANSLATORS: ESP is EFI System Partition msgid "ESP specified was not valid" msgstr "L'ESP especificat no era vàlid" @@ -489,6 +510,10 @@ msgstr "No s'han pogut carregar les peculiaritats" msgid "Failed to parse arguments" msgstr "Ha fallat en analitzar els arguments" +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Ha fallat en analitzar les etiquetes per a --filter" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" msgstr "Ha fallat en tornar a arrencar" @@ -521,6 +546,10 @@ msgstr "Nom del fitxer" msgid "Filename Signature" msgstr "Signatura del nom del fitxer" +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filtra amb un conjunt d'etiquetes del dispositiu amb un prefix «~» per a excloure, p. ex., «internal,~needs-reboot»" + #. TRANSLATORS: program name msgid "Firmware Agent" msgstr "Agent de microprogramari" @@ -554,7 +583,8 @@ msgstr "Les actualitzacions de microprogramari no estan admeses en aquesta màqu msgid "Firmware updates are supported on this machine." msgstr "Les actualitzacions de microprogramari estan admeses en aquesta màquina." -#. TRANSLATORS: section header for firmware flags +#. TRANSLATORS: device properties +#. TRANSLATORS: release properties msgid "Flags" msgstr "Etiquetes" @@ -565,12 +595,9 @@ msgstr "Força l'acció ignorant tots els avisos" msgid "Found" msgstr "S'ha trobat" -msgid "GUID" -msgstr "GUID" - #. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "Obtén tots els dispositius segons la topologia del sistema" +msgid "Get all device flags supported by fwupd" +msgstr "Obté totes les etiquetes admeses per «fwupd»" #. TRANSLATORS: command description msgid "Get all devices and possible releases" @@ -612,7 +639,6 @@ msgstr "Obté els alliberaments per a un dispositiu" msgid "Gets the results from the last update" msgstr "Obté els resultats de l'última actualització" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -620,6 +646,14 @@ msgstr "ID" msgid "Idle…" msgstr "Està ociós..." +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ignora les comprovacions estrictes de SSL quan es baixin els fitxers" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Durada de la instal·lació" + #. TRANSLATORS: command description msgid "Install a firmware blob on a device" msgstr "Instal·la un blob de microprogramari en un dispositiu" @@ -663,6 +697,10 @@ msgstr " Anell de claus" msgid "Less than one minute remaining" msgstr "Manca menys d'un minut" +#. TRANSLATORS: e.g. GPLv2+, Non free etc +msgid "License" +msgstr "Llicència" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Servei de microprogramari del proveïdor Linux (microprogramari estable)" @@ -681,6 +719,10 @@ msgstr "Llista les actualitzacions de microprogramari compatibles" msgid "Loading…" msgstr "S'està carregant..." +#. TRANSLATORS: command line option +msgid "Log output to FILE (typically for scripting use)" +msgstr "Sortida del registre a FITXER (normalment per a ús en crear scripts)" + #. TRANSLATORS: command line option msgid "Manually whitelist specific plugins" msgstr "Connectors especíificats manualment a la llista blanca" @@ -690,17 +732,21 @@ msgid "Merge multiple firmware files into one" msgstr "Fusiona múltiples fitxers de microprogramari en un de sol" #. TRANSLATORS: remote URI -msgid "Metadata URI" -msgstr "URI de les metadades" +msgid "Metadata Signature" +msgstr "Signatura de les metadades" #. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Signatura de l'URI de les metadades" +msgid "Metadata URI" +msgstr "URI de les metadades" #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Les metadades es poden obtenir des del servei de microprogramari del proveïdor Linux." +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Versió mínima" + #. TRANSLATORS: error message #, c-format msgid "Mismatched daemon and client, use %s instead" @@ -729,14 +775,12 @@ msgstr "Monitora el dimoni pels esdeveniments" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Nom" msgid "No action specified!" msgstr "No s'ha especificat cap acció!" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "No s'ha detectat cap maquinari amb capacitat per a l'actualització del microprogramari" @@ -745,10 +789,18 @@ msgstr "No s'ha detectat cap maquinari amb capacitat per a l'actualització del msgid "No plugins found" msgstr "No s'ha trobat cap connector" +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "No hi ha cap llançament disponible" + #. TRANSLATORS: explain why no metadata available msgid "No remotes are currently enabled so no metadata is available." msgstr "Actualment, no hi ha cap remot habilitat, de manera que no hi ha metadades disponibles." +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "No hi ha cap remot disponible" + #. TRANSLATORS: nothing was updated offline msgid "No updates were applied" msgstr "No s’han aplicat les actualitzacions" @@ -844,10 +896,7 @@ msgstr "Regió" msgid "Reinstalling %s with %s... " msgstr "S'està reinstal·lant %s amb %s... " -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Remot" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "ID remot" @@ -915,6 +964,10 @@ msgstr "Planificació..." msgid "Serial" msgstr "Sèrie" +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Número de sèrie" + #. TRANSLATORS: command description msgid "Set alternative name on firmware file" msgstr "Estableix el nom alternatiu al fitxer del microprogramari" @@ -1020,6 +1073,14 @@ msgstr "Signa les dades pujades amb el certificat del client" msgid "Signature" msgstr "Signatura" +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Mida" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Origen" + msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Especifiqueu el proveïdor/ID del producte del dispositiu DFU" @@ -1036,7 +1097,7 @@ msgstr "Estat" msgid "Status" msgstr "Estat" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Resum" @@ -1063,10 +1124,6 @@ msgstr "Aquest remot conté el microprogramari que no està embargat, però enca msgid "This tool can only be used by the root user" msgstr "Aquesta eina només pot ser usada per l'usuari «root»." -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Títol" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Mida a transferir" @@ -1079,14 +1136,14 @@ msgstr "Tipus" msgid "UEFI Firmware Utility" msgstr "Utilitat per al microprogramari UEFI" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Desconegut" +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Dispositiu desconegut" + msgid "Unlock the device to allow access" msgstr "Desbloqueja el dispositiu per a permetre l'accés" @@ -1103,38 +1160,18 @@ msgstr "No estableixis l'indicador de depuració durant l'actualització" msgid "Unsupported daemon version %s, client version is %s" msgstr "Versió %s no admesa del dimoni, la versió del client és %s" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Suma de verificació de l'actualització" +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Error en actualitzar" -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Descripció de l'actualització" +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Missatge de l'actualització" -#. TRANSLATORS: section header for the amount -#. * of time it takes to install the update -msgid "Update Duration" -msgstr "Durada de l'actualització" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Ubicació de l'actualització" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Actualitza el nom" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "Actualitza l'ID remot:" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Actualitza el resum" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Versió de l'actualització" +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Estat en actualitzar" #. TRANSLATORS: command description msgid "Update all devices that match local metadata" @@ -1187,18 +1224,30 @@ msgstr "Pujo l'informe ara?" msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." msgstr "L'enviament dels informes de microprogramari ajudarà als proveïdors de maquinari a identificar amb rapidesa les actualitzacions fallides i satisfactòries sobre els dispositius reals." +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Usa les etiquetes peculiars en instal·lar el microprogramari" + #. TRANSLATORS: remote filename base msgid "Username" msgstr "Nom d'usuari" +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Venedor" + #. TRANSLATORS: verifying we wrote the firmware correctly msgid "Verifying…" msgstr "S'està verificant..." -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Versió:" +#. TRANSLATORS: try to help +msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "AVÍS: S'ignoren les comprovacions estrictes de SSL. Per a fer-ho automàticament en el futur, exporteu DISABLE_SSL_STRICT en el vostre entorn" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "S'està esperant…" diff --git a/po/cs.po b/po/cs.po index f49089f55..8395453c2 100644 --- a/po/cs.po +++ b/po/cs.po @@ -3,7 +3,7 @@ # This file is distributed under the same license as the fwupd package. # # Translators: -# Ascii Wolf , 2017 +# Ascii Wolf , 2017,2019 # Ascii Wolf , 2017 # Marek Černocký , 2016,2018 msgid "" @@ -26,11 +26,6 @@ msgstr[1] "Zbývají %.0f minuty" msgstr[2] "Zbývá %.0f minut" msgstr[3] "Zbývá %.0f minuty" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s má aktualizace firmwaru:" - #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Přidáno" @@ -134,7 +129,6 @@ msgstr "Zrušeno" msgid "Changed" msgstr "Změněno" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Kontrolní součet" @@ -194,7 +188,7 @@ msgstr "Rozbaluje se…" msgid "Decrypt firmware data" msgstr "Dešifrovat data firmwaru" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Popis" @@ -405,13 +399,6 @@ msgstr "Aktualizace firmwaru jsou na tomto počítači podporované." msgid "Found" msgstr "Nalezeno" -msgid "GUID" -msgstr "GUID" - -#. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "Zjistit všechna zařízení podle topologie systému" - #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Zjistit všechna zařízení podporující aktualizaci firmwaru" @@ -444,7 +431,6 @@ msgstr "Vypsat vydání pro zařízení" msgid "Gets the results from the last update" msgstr "Vypsat výsledky z poslední aktualizace" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -521,10 +507,6 @@ msgstr "Sloučit více firmwarů do jednoho" msgid "Metadata URI" msgstr "URI metadat" -#. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Podpis URI metadat" - #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Metadata lze získat ze služby Linux Vendor Firmware." @@ -545,14 +527,12 @@ msgstr "Sledovat události démona" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Název" msgid "No action specified!" msgstr "Není určena žádné akce!" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Nebylo nalezeno žádné zařízení schopné aktualizace firmwaru" @@ -638,10 +618,7 @@ msgstr "Oblast" msgid "Reinstalling %s with %s... " msgstr "Přeinstalovává se %s na %s…" -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Vzdálený zdroj" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "ID vzdáleného zdroje" @@ -775,7 +752,7 @@ msgstr "Stav" msgid "Status" msgstr "Stav" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Souhrn" @@ -793,10 +770,6 @@ msgstr "Tento program může správně fungovat jen pod uživatelem root" msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." msgstr "Tento zdroj obsahuje firmware, který není zakázaný, ale zatím je u výrobce stále ve stádiu testování. Měli byste se ujistit, že znáte způsob, jak se vrátit k předchozí verzi firmwaru, kdyby došlo k selhání." -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Název" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Přenášená velikost" @@ -809,10 +782,6 @@ msgstr "Typ" msgid "UEFI Firmware Utility" msgstr "Nástroj pro práci s firmwarem UEFI" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Neznámý" @@ -828,34 +797,6 @@ msgstr "Odemknout zařízení pro přístup k firmwaru" msgid "Unset the debugging flag during update" msgstr "Během aktualizace zrušit příznak ladění" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Kontrolní součet aktualizace" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Popis aktualizace" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Umístění aktualizace" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Název aktualizace" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "ID vzdáleného zdroje aktualizace" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Souhrn aktualizace" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Verze aktualizace" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "O selhání aktualizace se ví, více informací najdete na této adrese:" @@ -902,7 +843,7 @@ msgstr "Uživatelské jméno" msgid "Verifying…" msgstr "Ověřuje se…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Verze" diff --git a/po/da.po b/po/da.po index 068c611a5..5a32ce1b6 100644 --- a/po/da.po +++ b/po/da.po @@ -34,7 +34,7 @@ msgstr "%s ME-opdatering for forbruger" #. * the first %s is the device name, e.g. 'Intel ThunderBolt` #, c-format msgid "%s Controller Update" -msgstr "Kontrolleropdatering for %s" +msgstr "Opdatering for controller %s" #. TRANSLATORS: ME stands for Management Engine (with Intel AMT), #. * where the first %s is the device name, e.g. 'ThinkPad P50` @@ -52,7 +52,7 @@ msgstr "Enhedsopdatering for %s" #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format msgid "%s Embedded Controller Update" -msgstr "Controlleropdatering for indlejret %s" +msgstr "Opdatering af indlejret controller %s" #. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, #. * the first %s is the device name, e.g. 'ThinkPad P50` @@ -66,6 +66,13 @@ msgstr "ME-opdatering for %s" msgid "%s System Update" msgstr "Systemopdatering for %s" +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "%s opdatering af Thunderbolt-controller" + #. TRANSLATORS: this is the fallback where we don't know if the release #. * is updating the system, the device, or a device class, or something else #. -- @@ -74,11 +81,6 @@ msgstr "Systemopdatering for %s" msgid "%s Update" msgstr "Opdatering for %s" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s har firmwareopdateringer:" - #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -239,6 +241,10 @@ msgstr "Der kræves autentifikation for at opdatere firmwaren på maskinen" msgid "Authentication is required to update the stored checksums for the device" msgstr "Der kræves autentifikation for at opdatere de gemte checksumme for enheden" +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Opstartsindlæser version" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Byg firmware med en sandkasse" @@ -256,7 +262,6 @@ msgstr "Annulleret" msgid "Changed" msgstr "Ændret" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Checksum" @@ -297,6 +302,10 @@ msgstr "Konverter firmware til DFU-format" msgid "Create a binary patch using two files" msgstr "Opret en binær patch ved brug af to filer" +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Nuværende version" + msgid "DFU" msgstr "DFU" @@ -316,7 +325,7 @@ msgstr "Udpakker …" msgid "Decrypt firmware data" msgstr "Afkryptér firmwaredata" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Beskrivelse" @@ -328,6 +337,10 @@ msgstr "Frakobl tilkoblede enheder som formår DFU" msgid "Detach to bootloader mode" msgstr "Frakobl til opstartsindlæsertilstand" +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Detaljer" + #. TRANSLATORS: ID for hardware, typically a SHA1 sum msgid "Device ID" msgstr "Enheds-id" @@ -375,6 +388,10 @@ msgstr "Tjek ikke om der er genstartet, efter opdatering" msgid "Do not check for unreported history" msgstr "Tjek ikke efter historik som ikke er blevet rapporteret" +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Medtag ikke tidsstempelpræfiks" + #. TRANSLATORS: command line option msgid "Do not write to the history database" msgstr "Skriv ikke til historikdatabasen" @@ -415,6 +432,10 @@ msgstr "Dump detaljer om en firmwarefil" msgid "Dump information about a binary patch to the screen" msgstr "Dump information om en binær patch på skærmen" +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Varighed" + #. TRANSLATORS: ESP is EFI System Partition msgid "ESP specified was not valid" msgstr "Angivet ESP var ikke gyldig" @@ -489,6 +510,10 @@ msgstr "Kunne ikke indlæse quirks" msgid "Failed to parse arguments" msgstr "Kunne ikke fortolke argumenter" +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Kunne ikke fortolke flag for --filter" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" msgstr "Kunne ikke genstarte" @@ -521,6 +546,10 @@ msgstr "Filnavn" msgid "Filename Signature" msgstr "Filnavnets underskrift" +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filtrer med et sæt enhedsflag med et ~-præfiks for at udelukke, f.eks. 'intern,~behøver-genstart'" + #. TRANSLATORS: program name msgid "Firmware Agent" msgstr "Firmwareagent" @@ -554,7 +583,8 @@ msgstr "Maskinen understøtter ikke firmwareopdateringer." msgid "Firmware updates are supported on this machine." msgstr "Maskinen understøtter firmwareopdateringer." -#. TRANSLATORS: section header for firmware flags +#. TRANSLATORS: device properties +#. TRANSLATORS: release properties msgid "Flags" msgstr "Flag" @@ -565,12 +595,9 @@ msgstr "Tving handlingen og ignorer alle advarsler" msgid "Found" msgstr "Fundet" -msgid "GUID" -msgstr "GUID" - #. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "Hent alle enheder i henhold til systemets topologi" +msgid "Get all device flags supported by fwupd" +msgstr "Hent alle enhedsflag som understøttes af fwupd" #. TRANSLATORS: command description msgid "Get all devices and possible releases" @@ -612,7 +639,6 @@ msgstr "Henter resultaterne fra en enhed" msgid "Gets the results from the last update" msgstr "Henter resultaterne fra den sidste opdatering" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "Id" @@ -620,6 +646,14 @@ msgstr "Id" msgid "Idle…" msgstr "Inaktiv …" +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ignorer strikse SSL-tjek ved download af filer" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Varighed for installation" + #. TRANSLATORS: command description msgid "Install a firmware blob on a device" msgstr "Installer en firmwareblob på en enhed" @@ -663,6 +697,10 @@ msgstr "Nøglering" msgid "Less than one minute remaining" msgstr "Mindre end et minut tilbage" +#. TRANSLATORS: e.g. GPLv2+, Non free etc +msgid "License" +msgstr "Licens" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Linux Vendor Firmware Service (stabilt firmware)" @@ -681,6 +719,10 @@ msgstr "Vis understøttede firmwareopdateringer" msgid "Loading…" msgstr "Indlæser …" +#. TRANSLATORS: command line option +msgid "Log output to FILE (typically for scripting use)" +msgstr "Log output til FIL (typisk til brug ved scripting)" + #. TRANSLATORS: command line option msgid "Manually whitelist specific plugins" msgstr "Manuel hvidlistning af bestemte plugins" @@ -690,17 +732,21 @@ msgid "Merge multiple firmware files into one" msgstr "Sammenlæg flere firmwarefiler i én" #. TRANSLATORS: remote URI -msgid "Metadata URI" -msgstr "Metadata-URI" +msgid "Metadata Signature" +msgstr "Underskrift for metadata" #. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Underskrift for metadata-URI" +msgid "Metadata URI" +msgstr "Metadata-URI" #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Metadata kan hentes fra Linux Vendor Firmware Service." +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Minimum version" + #. TRANSLATORS: error message #, c-format msgid "Mismatched daemon and client, use %s instead" @@ -729,14 +775,12 @@ msgstr "Overvåg dæmonen for hændelser" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Navn" msgid "No action specified!" msgstr "Der er ikke angivet nogen handling!" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Der blev ikke fundet nogen hardware med firmware som kan opdateres" @@ -745,10 +789,18 @@ msgstr "Der blev ikke fundet nogen hardware med firmware som kan opdateres" msgid "No plugins found" msgstr "Der blev ikke fundet nogen plugins" +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Ingen tilgængelige udgivelser" + #. TRANSLATORS: explain why no metadata available msgid "No remotes are currently enabled so no metadata is available." msgstr "Der er ikke nogen metadata da der ikke er aktiveret nogen fjerne." +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Ingen tilgængelige fjerne" + #. TRANSLATORS: nothing was updated offline msgid "No updates were applied" msgstr "Der blev ikke anvendt nogen opdateringer" @@ -844,10 +896,7 @@ msgstr "Område" msgid "Reinstalling %s with %s... " msgstr "Geninstallerer %s med %s... " -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Fjern" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "Fjern-id" @@ -915,6 +964,10 @@ msgstr "Planlægger …" msgid "Serial" msgstr "Serienummer" +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Serienummer" + #. TRANSLATORS: command description msgid "Set alternative name on firmware file" msgstr "Indstil alternativt navn på firmwarefil" @@ -1020,6 +1073,14 @@ msgstr "Underskriv den uploadede data med klientcertifikatet" msgid "Signature" msgstr "Underskrift" +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Størrelse" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Kilde" + msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Angiv producent-/produkt-id('er) for DFU-enhed" @@ -1036,7 +1097,7 @@ msgstr "Tilstand" msgid "Status" msgstr "Status" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Opsummering" @@ -1063,10 +1124,6 @@ msgstr "Fjernen indeholder firmware som der ikke er embargo på, men som stadigv msgid "This tool can only be used by the root user" msgstr "Værktøjet kan kun bruges af root-brugeren" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Titel" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Overførselsstørrelse" @@ -1079,14 +1136,14 @@ msgstr "Type" msgid "UEFI Firmware Utility" msgstr "UEFI-firmwareredskab" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Ukendt" +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Ukendt enhed" + msgid "Unlock the device to allow access" msgstr "Lås enheden op for at tillade adgang" @@ -1103,38 +1160,18 @@ msgstr "Fjern fejlsøgningsflaget under opdatering" msgid "Unsupported daemon version %s, client version is %s" msgstr "Uunderstøttet dæmonversion %s, klientversionen er %s" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Opdateringens checksum" +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Fejl ved opdatering" -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Opdateringens beskrivelse" +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Opdateringsmeddelelese" -#. TRANSLATORS: section header for the amount -#. * of time it takes to install the update -msgid "Update Duration" -msgstr "Opdateringens varighed" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Opdateringens placering" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Opdateringens navn" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "Opdateringens fjern-id" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Opdateringens opsummering" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Opdateringens version" +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Opdateringstilstand" #. TRANSLATORS: command description msgid "Update all devices that match local metadata" @@ -1187,18 +1224,30 @@ msgstr "Upload rapport nu?" msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." msgstr "Upload af firmwarerapporter hjælper hardwareproducenter til hurtigt at identificere opdateringer som fejlede og lykkedes på rigtigt hardware." +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Brug quirk-flag ved installation af firmware" + #. TRANSLATORS: remote filename base msgid "Username" msgstr "Brugernavn" +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Producent" + #. TRANSLATORS: verifying we wrote the firmware correctly msgid "Verifying…" msgstr "Verificerer …" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Version" +#. TRANSLATORS: try to help +msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "ADVARSEL: Ignorerer strikse SSL-tjeks. Eksportér DISABLE_SSL_STRICT i dit miljø for at gøre det automatisk i fremtiden" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Venter …" diff --git a/po/de.po b/po/de.po index 87485db12..0cba4fc49 100644 --- a/po/de.po +++ b/po/de.po @@ -24,11 +24,6 @@ msgid_plural "%.0f minutes remaining" msgstr[0] "%.0f Minute verbleibt" msgstr[1] "%.0f Minuten verbleiben" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "Firmware-Aktualisierungen für %s verfügbar:" - #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Hinzugefügt" @@ -120,7 +115,6 @@ msgstr "Abgebrochen" msgid "Changed" msgstr "Geändert" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Prüfsumme" @@ -176,7 +170,7 @@ msgstr "Entpacken …" msgid "Decrypt firmware data" msgstr "Firmware-Daten entschlüsseln" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Beschreibung" @@ -365,13 +359,6 @@ msgstr "Aktion erzwingen, bei der alle Warnungen ignoriert werden" msgid "Found" msgstr "Gefunden" -msgid "GUID" -msgstr "GUID" - -#. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "Ermittelt alle Geräte gemäß der Systemtopologie" - #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Alle Geräte ermitteln, die Firmware-Aktualisierungen unterstützen" @@ -400,7 +387,6 @@ msgstr "Ermittelt die Versionen für ein Gerät" msgid "Gets the results from the last update" msgstr "Ermittelt die Ergebnisse der letzten Aktualisierung" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "Kennung" @@ -473,10 +459,6 @@ msgstr "Mehrere Firmware-Dateien in eine zusammenführen" msgid "Metadata URI" msgstr "Metadaten-URI" -#. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Metadaten URI-Signatur" - #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Metadaten können über den Linux Vendor Firmware Service bezogen werden." @@ -490,14 +472,12 @@ msgstr "Hintergrundprogramm auf Ereignisse überwachen" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Name" msgid "No action specified!" msgstr "Keine Aktion angegeben!" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Es wurde keine Hardware erkannt, deren Firmware aktualisiert werden kann" @@ -713,7 +693,7 @@ msgstr "Zustand" msgid "Status" msgstr "Status" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Zusammenfassung" @@ -728,10 +708,6 @@ msgstr "Der LVFS ist ein kostenloser Dienst, der als unabhängige juristische Pe msgid "This program may only work correctly as root" msgstr "Dieses Programm funktioniert möglicherweise nur als root korrekt" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Titel" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Übertragungsgröße" @@ -744,10 +720,6 @@ msgstr "Typ" msgid "UEFI Firmware Utility" msgstr "UEFI-Firmware-Dienstprogramm" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Unbekannt" @@ -759,30 +731,6 @@ msgstr "Das Gerät entsperren, um Zugriff zu ermöglichen" msgid "Unlocks the device for firmware access" msgstr "Entsperrt das Gerät für Zugriff auf die Firmware" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Prüfsumme aktualisieren" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Beschreibung aktualisieren" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Ort aktualisieren" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Aktualisierungsname" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Aktualisierungszusammenfassung" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Version aktualisieren" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "Der Aktualisierungsfehler ist ein bekanntes Problem, besuchen Sie diese URL für weitere Informationen:" @@ -834,7 +782,7 @@ msgstr "Benutzername" msgid "Verifying…" msgstr "Überprüfung …" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Version" diff --git a/po/en_GB.po b/po/en_GB.po index 1a764e42a..6348ad9d7 100644 --- a/po/en_GB.po +++ b/po/en_GB.po @@ -22,11 +22,6 @@ msgid_plural "%.0f minutes remaining" msgstr[0] "%.0f minute remaining" msgstr[1] "%.0f minutes remaining" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s has firmware updates:" - #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -162,7 +157,6 @@ msgstr "Cancelled" msgid "Changed" msgstr "Changed" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Checksum" @@ -222,7 +216,7 @@ msgstr "Decompressing…" msgid "Decrypt firmware data" msgstr "Decrypt firmware data" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Description" @@ -439,13 +433,6 @@ msgstr "Force the action ignoring all warnings" msgid "Found" msgstr "Found" -msgid "GUID" -msgstr "GUID" - -#. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "Get all devices according to the system topology" - #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Get all devices that support firmware updates" @@ -478,7 +465,6 @@ msgstr "Gets the releases for a device" msgid "Gets the results from the last update" msgstr "Gets the results from the last update" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -555,10 +541,6 @@ msgstr "Merge multiple firmware files into one" msgid "Metadata URI" msgstr "Metadata URI" -#. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Metadata URI Signature" - #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Metadata can be obtained from the Linux Vendor Firmware Service." @@ -579,14 +561,12 @@ msgstr "Monitor the daemon for events" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Name" msgid "No action specified!" msgstr "No action specified!" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "No hardware detected with firmware update capability" @@ -678,10 +658,7 @@ msgstr "Region" msgid "Reinstalling %s with %s... " msgstr "Reinstalling %s with %s... " -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Remote" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "Remote ID" @@ -833,7 +810,7 @@ msgstr "State" msgid "Status" msgstr "Status" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Summary" @@ -851,10 +828,6 @@ msgstr "This program may only work correctly as root" msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." msgstr "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Title" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Transfer Size" @@ -867,10 +840,6 @@ msgstr "Type" msgid "UEFI Firmware Utility" msgstr "UEFI Firmware Utility" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Unknown" @@ -886,39 +855,6 @@ msgstr "Unlocks the device for firmware access" msgid "Unset the debugging flag during update" msgstr "Unset the debugging flag during update" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Update Checksum" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Update Description" - -#. TRANSLATORS: section header for the amount -#. * of time it takes to install the update -msgid "Update Duration" -msgstr "Update Duration" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Update Location" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Update Name" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "Update Remote ID" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Update Summary" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Update Version" - #. TRANSLATORS: command description msgid "Update all devices that match local metadata" msgstr "Update all devices that match local metadata" @@ -974,7 +910,7 @@ msgstr "Username" msgid "Verifying…" msgstr "Verifying…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Version" diff --git a/po/eo.po b/po/eo.po index 5d790d6be..85e692836 100644 --- a/po/eo.po +++ b/po/eo.po @@ -43,7 +43,7 @@ msgstr "Ŝanĝita" msgid "Choose a device:" msgstr "Elektu aparaton:" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Priskribo" @@ -55,7 +55,6 @@ msgstr "Farita!" msgid "Found" msgstr "Trovita" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -64,7 +63,6 @@ msgstr "Reĝimo" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Nomo" @@ -90,6 +88,6 @@ msgstr "Celo" msgid "Unknown" msgstr "Nekonata" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Versio" diff --git a/po/fi.po b/po/fi.po index 1d812e92c..5cc75f530 100644 --- a/po/fi.po +++ b/po/fi.po @@ -74,11 +74,6 @@ msgstr "%sJärjestelmän päivitys" msgid "%s Update" msgstr "%sPäivitys" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s on laiteohjelmistopäivityksiä:" - #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -256,7 +251,6 @@ msgstr "Peruttu" msgid "Changed" msgstr "Muutettu" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Tarkistussumma" @@ -316,7 +310,7 @@ msgstr "Puretaan…" msgid "Decrypt firmware data" msgstr "Pura firmwaren data" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Kuvaus" @@ -375,6 +369,10 @@ msgstr "Älä tarkista käynnistystä uudelleen päivityksen jälkeen" msgid "Do not check for unreported history" msgstr "Älä tarkista ilmoittamattomasta historiasta" +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Älä sisällytä aikaleiman etuliitettä" + #. TRANSLATORS: command line option msgid "Do not write to the history database" msgstr "Älä kirjoita historiatietokantaan" @@ -554,7 +552,8 @@ msgstr "Ohjelmistopäivitys 'firmware' ei tue tätä laitetta" msgid "Firmware updates are supported on this machine." msgstr "Ohjelmistopäivitys 'firmware' tukee tätä laitetta" -#. TRANSLATORS: section header for firmware flags +#. TRANSLATORS: device properties +#. TRANSLATORS: release properties msgid "Flags" msgstr "Liput" @@ -565,13 +564,6 @@ msgstr "Pakota toimenpide huomioimatta kaikki varoitukset" msgid "Found" msgstr "Löydetty" -msgid "GUID" -msgstr "GUID" - -#. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "Hae kaikki laitteet järjestelmän topologian mukaisesti" - #. TRANSLATORS: command description msgid "Get all devices and possible releases" msgstr "Hanki kaikki laitteet ja mahdolliset julkaisut" @@ -612,7 +604,6 @@ msgstr "Antaa laitteen julkaisut" msgid "Gets the results from the last update" msgstr "Saat viimeisimmän päivityksen tulokset" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "Henkilöllisyys" @@ -693,10 +684,6 @@ msgstr "Yhdistä useita firmware-tiedostoja yhteen" msgid "Metadata URI" msgstr "Metatietojen URI" -#. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Metatietojen URI-allekirjoitus" - #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Metatiedot voidaan hankkia Linux Vendor Firmware -palvelusta." @@ -729,14 +716,12 @@ msgstr "Seuraa tapahtumien taustaprosessia" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Nimi" msgid "No action specified!" msgstr "Toimintoa ei ole määritetty!" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Ei havaittu sopivaa laitteistoa firmware päivitykselle" @@ -844,10 +829,7 @@ msgstr "Alue" msgid "Reinstalling %s with %s... " msgstr "Uudelleenasennus %s kera %s... " -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Etä" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "Etätunnus" @@ -1036,7 +1018,7 @@ msgstr "Osavaltio" msgid "Status" msgstr "Tila" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Yhteenveto" @@ -1063,10 +1045,6 @@ msgstr "Tämä sisältää firmwaren, jota ei ole vientikiellossa, mutta jota la msgid "This tool can only be used by the root user" msgstr "Tätä työkalua voi käyttää vain root-käyttäjä" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Otsikko" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Siirron koko" @@ -1079,10 +1057,6 @@ msgstr "Tyyppi" msgid "UEFI Firmware Utility" msgstr "UEFI firmware -apuohjelma" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Tuntematon" @@ -1103,39 +1077,6 @@ msgstr "Poista virheenkorjauksen lippu päivityksen aikana" msgid "Unsupported daemon version %s, client version is %s" msgstr "Ei tuettu taustaprosessin versio %s, asiakasversio on %s" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Päivitä tarkistussumma" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Päivitä kuvaus" - -#. TRANSLATORS: section header for the amount -#. * of time it takes to install the update -msgid "Update Duration" -msgstr "Päivitä kesto" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Päivitä sijainti" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Päivitä nimi" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "Päivitä etätunnus" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Päivitä yhteenveto" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Päivitä versio" - #. TRANSLATORS: command description msgid "Update all devices that match local metadata" msgstr "Päivitä kaikki paikallisia metatietoja vastaavat laitteet" @@ -1187,6 +1128,10 @@ msgstr "Lataa raportti nyt?" msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." msgstr "Firmware-raporttien lataaminen auttaa laitteistotoimittajia tunnistamaan nopeasti virheelliset ja onnistuneet päivitykset todellisissa laitteissa." +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Käytä Quirk-lippuja asennettaessa laiteohjelmistoa" + #. TRANSLATORS: remote filename base msgid "Username" msgstr "Käyttäjätunnus" @@ -1195,7 +1140,7 @@ msgstr "Käyttäjätunnus" msgid "Verifying…" msgstr "Vahvistetaan…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Versio" diff --git a/po/fr.po b/po/fr.po index d3d31b03c..d6eae3871 100644 --- a/po/fr.po +++ b/po/fr.po @@ -67,7 +67,6 @@ msgstr "Obtenir les détails d'un fichier de micrologiciel" msgid "Install a firmware file on this hardware" msgstr "Installer un fichier de micrologiciel sur ce matériel" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Aucun matériel ayant des capacités de mise à jour du micrologiciel n'a été détecté" diff --git a/po/fur.po b/po/fur.po index 8c269890d..82a318e22 100644 --- a/po/fur.po +++ b/po/fur.po @@ -15,11 +15,6 @@ msgstr "" "Language: fur\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s al à inzornaments dal firmware:" - #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Zontât" @@ -107,7 +102,6 @@ msgstr "Anulât" msgid "Changed" msgstr "Modificât" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Checksum" @@ -156,7 +150,7 @@ msgstr "Daûr a decomprimi…" msgid "Decrypt firmware data" msgstr "Decifre i dâts dal firmware" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Descrizion" @@ -274,9 +268,6 @@ msgstr[1] "I metadâts dal firmware no son stâts inzornâts par %u dîs e a pod msgid "Found" msgstr "Cjatât" -msgid "GUID" -msgstr "GUID" - #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Oten ducj i dispositîfs che a supuartin i inzornaments dal firmware" @@ -305,7 +296,6 @@ msgstr "Al oten lis publicazions par un dispositîf" msgid "Gets the results from the last update" msgstr "Al oten i risultâts dal ultin inzornament" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -351,10 +341,6 @@ msgstr "Unìs plui file firmware intun" msgid "Metadata URI" msgstr "URI metadata" -#. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Firme dal URI dal metadata" - msgid "Mode" msgstr "Modalitât" @@ -371,11 +357,9 @@ msgstr "Monitore il demoni pai events" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Non" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Nissun hardware rilevât un funzionalitâts di inzornament dal firmware" @@ -434,10 +418,7 @@ msgstr "Regjon" msgid "Reinstalling %s with %s... " msgstr "Daûr a tornâ a instalâ %s cun %s... " -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Rimot" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "ID rimot" @@ -548,22 +529,14 @@ msgstr "Stât" msgid "Status" msgstr "Stât" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Sintesi" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Titul" - #. TRANSLATORS: remote type, e.g. remote or local msgid "Type" msgstr "Gjenar" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "No cognossût" @@ -575,34 +548,6 @@ msgstr "Sbloche il dispositîf par permeti l'acès" msgid "Unlocks the device for firmware access" msgstr "Al sbloche il dispositîf pal acès al firmware" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Inzorne some di control - checksum" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Inzorne descrizion" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Inzorne posizion" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Inzorne non" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "Inzorne ID rimot" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Inzorne sintesi" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Inzorne version" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "Il faliment dal inzornament al è un probleme cognossût, visite chest URL par vê plui informazions:" @@ -637,7 +582,7 @@ msgstr "Non utent" msgid "Verifying…" msgstr "Daûr a verificâ…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Version" diff --git a/po/he.po b/po/he.po index bbc68338b..3f6ca6717 100644 --- a/po/he.po +++ b/po/he.po @@ -16,11 +16,6 @@ msgstr "" "Language: he\n" "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;\n" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "ישנם עדכוני קושחה עבור %s:" - #. TRANSLATORS: this is a command alias, e.g. 'get-devices' #, c-format msgid "Alias to %s" @@ -30,7 +25,6 @@ msgstr "כינוי עבור %s" msgid "Authentication is required to update the firmware on this machine" msgstr "אימות משתמש נדרש לעדכון קושחה מערכת זו" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "סכום ביקורת" @@ -43,7 +37,7 @@ msgstr "פקודה לא נמצאה" msgid "Debugging Options" msgstr "אפשרויות ניפוי שגיאות" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "תיאור" @@ -90,7 +84,6 @@ msgstr "מציג פרטים אודות קובץ קושחה" msgid "Install a firmware file on this hardware" msgstr "מתקין קובץ קושחה בחומרה זו" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "לא אותרה חומרה בעלת יכולת עדכון קושחה" @@ -120,6 +113,6 @@ msgstr "הצג מידע ניפוי שגיאות מורחב" msgid "Updating %s from %s to %s... " msgstr "מעדכן %s מ־%s ל־%s..." -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "גרסא" diff --git a/po/hi.po b/po/hi.po index 519fd45e6..4417c958e 100644 --- a/po/hi.po +++ b/po/hi.po @@ -67,7 +67,6 @@ msgstr "फर्मवेयर फाइल की अधिक जानक msgid "Install a firmware file on this hardware" msgstr "फर्मवेयर फाइल को इस हार्डवेयर पर स्थापित करें " -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "अपडेट की क्षमता वाला हार्डवेयर उपलब्ध नहीं " diff --git a/po/hr.po b/po/hr.po index f397424e3..66c56d3dc 100644 --- a/po/hr.po +++ b/po/hr.po @@ -68,6 +68,13 @@ msgstr "%s Pogon upravljanja (ME) nadopunjen" msgid "%s System Update" msgstr "%s sustav nadopunjen" +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "%s nadopuna Thunderbolt upravljača" + #. TRANSLATORS: this is the fallback where we don't know if the release #. * is updating the system, the device, or a device class, or something else #. -- @@ -76,11 +83,6 @@ msgstr "%s sustav nadopunjen" msgid "%s Update" msgstr "%s nadopuna" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s ima nadopune frimvera:" - #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -246,6 +248,10 @@ msgstr "Potrebna je ovjera za nadopunu frimvera na ovom računalu" msgid "Authentication is required to update the stored checksums for the device" msgstr "Potrebna je ovjera za nadopunu spremljenog kontrolnog zbroja uređaja" +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Inačica učitača pokretanja" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Izgradi frimver u osiguranom okruženju" @@ -263,7 +269,6 @@ msgstr "Prekinuto" msgid "Changed" msgstr "Promijenjeno" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Kontrolni zbroj" @@ -304,6 +309,10 @@ msgstr "Pretvori firmver u DFU format" msgid "Create a binary patch using two files" msgstr "Stvori binarnu zakrpu koristeći dvije datoteke" +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Trenutna inačica" + msgid "DFU" msgstr "DFU" @@ -323,7 +332,7 @@ msgstr "Raspakiravanje..." msgid "Decrypt firmware data" msgstr "Dešifriraj podatke frimvera" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Opis" @@ -335,6 +344,10 @@ msgstr "Odspoji trenutno povezane DFU sposobne uređaje" msgid "Detach to bootloader mode" msgstr "Prebaci u način učitača pokretanja" +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Pojedinosti" + #. TRANSLATORS: ID for hardware, typically a SHA1 sum msgid "Device ID" msgstr "ID uređaja" @@ -382,6 +395,10 @@ msgstr "Ne provjeravaj za ponovno pokretanje nakon nadopune" msgid "Do not check for unreported history" msgstr "Ne provjeravaj neprijavljenu povijest" +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Ne uključuj prefiks vremena" + #. TRANSLATORS: command line option msgid "Do not write to the history database" msgstr "Ne zapisuj bazu podataka povijesti" @@ -422,6 +439,10 @@ msgstr "Ispiši opširnije pojedinosti o frimveru u datoteku" msgid "Dump information about a binary patch to the screen" msgstr "Ispiši informacije o binarnoj zakrpi na zaslonu" +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Trajanje" + #. TRANSLATORS: ESP is EFI System Partition msgid "ESP specified was not valid" msgstr "Određeni ESP nije ispravan" @@ -496,10 +517,18 @@ msgstr "Neuspjelo učitavanje okolnosti uređaja" msgid "Failed to parse arguments" msgstr "Neuspjela obrada argumenata" +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Neuspjela obrada oznake za --filter" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" msgstr "Neuspjelo ponovno pokretanje" +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Neuspjelo postavljanje splash načina" + #. TRANSLATORS: downloading unknown file msgid "Fetching file" msgstr "Dohvaćanje datoteke" @@ -524,6 +553,10 @@ msgstr "Naziv datoteke" msgid "Filename Signature" msgstr "Potpis naziva datoteke" +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filter sa skupom oznaka uređaja koji koristi ~ prefiks za izuzimanje, npr. 'internal,~needs-reboot'" + #. TRANSLATORS: program name msgid "Firmware Agent" msgstr "Firmver agent" @@ -558,7 +591,8 @@ msgstr "Nadopuna frimvera nije podržana na ovom računalu." msgid "Firmware updates are supported on this machine." msgstr "Nadopuna frimvera je podržana na ovom računalu." -#. TRANSLATORS: section header for firmware flags +#. TRANSLATORS: device properties +#. TRANSLATORS: release properties msgid "Flags" msgstr "Oznake" @@ -569,12 +603,9 @@ msgstr "Prisili radnju zanemarivanja svih upozorenja" msgid "Found" msgstr "Pronađen" -msgid "GUID" -msgstr "GUID" - #. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "Prikaži sve uređaje prema topologiji sustava" +msgid "Get all device flags supported by fwupd" +msgstr "Prikaži sve oznake uređaja koje podržava fwupd" #. TRANSLATORS: command description msgid "Get all devices and possible releases" @@ -616,7 +647,6 @@ msgstr "Prikazuje izdanja za uređaj" msgid "Gets the results from the last update" msgstr "Prikaži rezultate posljednje nadopune" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -624,6 +654,14 @@ msgstr "ID" msgid "Idle…" msgstr "Mirovanje..." +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Zanemari SSL ograničene provjere pri preuzimanju datoteka" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Trajanje instalacije" + #. TRANSLATORS: command description msgid "Install a firmware blob on a device" msgstr "Instaliraj blob frimvera na uređaj" @@ -667,6 +705,10 @@ msgstr "Skup ključeva" msgid "Less than one minute remaining" msgstr "Preostalo je manje od minute" +#. TRANSLATORS: e.g. GPLv2+, Non free etc +msgid "License" +msgstr "Licenca" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Frimver Usluga Linux Proizvođača (LVFS) (stabilni frimver)." @@ -685,6 +727,10 @@ msgstr "Prikaži nadopune podržanih frimvera" msgid "Loading…" msgstr "Učitavanje..." +#. TRANSLATORS: command line option +msgid "Log output to FILE (typically for scripting use)" +msgstr "Zapisuj izlaz u DATOTEKU (uobičajeno za upotrebu sa skriptama)" + #. TRANSLATORS: command line option msgid "Manually whitelist specific plugins" msgstr "Ručno dopusti određene priključke" @@ -694,17 +740,21 @@ msgid "Merge multiple firmware files into one" msgstr "Spoji više frimver datoteka u jednu" #. TRANSLATORS: remote URI -msgid "Metadata URI" -msgstr "URI metapodataka" +msgid "Metadata Signature" +msgstr "Metapodaci potpisa" #. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Potpis URI metapodataka" +msgid "Metadata URI" +msgstr "URI metapodataka" #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Metapodaci se mogu dobiti od Frimver Usluge Linux Proizvođača (LVFS)." +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Najmanja inačica" + #. TRANSLATORS: error message #, c-format msgid "Mismatched daemon and client, use %s instead" @@ -733,14 +783,12 @@ msgstr "Nadgledaj događaje pozadinskim programom" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Naziv" msgid "No action specified!" msgstr "Nema zadane radnje!" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Nema otkrivenog hardvera s mogućnosti nadopune frimvera" @@ -749,10 +797,18 @@ msgstr "Nema otkrivenog hardvera s mogućnosti nadopune frimvera" msgid "No plugins found" msgstr "Nema pronađenih priključaka" +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Nema dostupnih izdanja" + #. TRANSLATORS: explain why no metadata available msgid "No remotes are currently enabled so no metadata is available." msgstr "Trenutno nema omogućenih udaljenih lokacija stoga nema dostupnih metapodataka." +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Nema dostupnih udaljenih lokacija" + #. TRANSLATORS: nothing was updated offline msgid "No updates were applied" msgstr "Nema primijenjenih nadopuna" @@ -845,10 +901,7 @@ msgstr "Regija" msgid "Reinstalling %s with %s... " msgstr "Ponovna instalacija %s inačice %s... " -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Udaljena lokacija" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "Udaljeni ID" @@ -916,6 +969,10 @@ msgstr "Zakazivanje..." msgid "Serial" msgstr "Serijski broj" +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Serijski broj" + #. TRANSLATORS: command description msgid "Set alternative name on firmware file" msgstr "Postavi zamjenski naziv u datoteku firmvera" @@ -1021,6 +1078,14 @@ msgstr "Potpiši poslane podatke s vjerodajnicom klijenta" msgid "Signature" msgstr "Potpis" +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Veličina" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Izvor" + msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Odredi ID-ove Proizvođača/Proizvoda DFU uređaja" @@ -1037,7 +1102,7 @@ msgstr "Stanje" msgid "Status" msgstr "Stanje" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Sažetak" @@ -1064,10 +1129,6 @@ msgstr "Ova udaljena lokacija sadrži frimver koji nije zabranjen, ali se još u msgid "This tool can only be used by the root user" msgstr "Ovaj alat može koristiti samo korijenski korisnik" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Naziv" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Veličina prijenosa" @@ -1080,14 +1141,14 @@ msgstr "Vrsta" msgid "UEFI Firmware Utility" msgstr "UEFI firmver pomagalo" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Nepoznat" +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Nepoznat uređaj" + msgid "Unlock the device to allow access" msgstr "Otključaj uređaj za dopuštenje pristupa" @@ -1104,38 +1165,18 @@ msgstr "Ukloni oznaku otklanjanja grešaka tijekom nadopune" msgid "Unsupported daemon version %s, client version is %s" msgstr "Nepodržana inačica pozadinskog programa %s, inačica klijenta je %s" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Kontrolni zbroj nadopune" +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Greška nadopune" -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Opis nadopune" +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Poruka nadopune" -#. TRANSLATORS: section header for the amount -#. * of time it takes to install the update -msgid "Update Duration" -msgstr "Trajanje nadopune" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Lokacija nadopune" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Naziv nadopune" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "Udaljeni ID nadopune" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Sažetak nadopune" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Inačica nadopune" +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Stanje nadopune" #. TRANSLATORS: command description msgid "Update all devices that match local metadata" @@ -1192,14 +1233,22 @@ msgstr "Slanje izvještaja frimvera pomaže proizvođačima hardvera brzo otkriv msgid "Username" msgstr "Korisničko ime" +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Proizvođač" + #. TRANSLATORS: verifying we wrote the firmware correctly msgid "Verifying…" msgstr "Provjeravanje..." -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Inačica" +#. TRANSLATORS: try to help +msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "UPOZORENJE: Zanemarivanje SSL ograničenja provjera, kako bi se ovo obavljalo ubuduće automatski postavite DISABLE_SSL_STRICT u vašem radnom okruženju" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Čekanje…" diff --git a/po/hu.po b/po/hu.po index 973729077..efecdf312 100644 --- a/po/hu.po +++ b/po/hu.po @@ -64,11 +64,6 @@ msgstr "%s rendszerfrissítés" msgid "%s Update" msgstr "%s frissítés" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s firmware frissítésekkel rendelkezik:" - #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -242,7 +237,6 @@ msgstr "Megszakítva" msgid "Changed" msgstr "Módosítva" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Ellenőrzőösszeg" @@ -302,7 +296,7 @@ msgstr "Kibontás…" msgid "Decrypt firmware data" msgstr "Firmware adatok visszafejtése" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Leírás" @@ -540,7 +534,8 @@ msgstr "A firmware frissítések nem támogatottak ezen a gépen." msgid "Firmware updates are supported on this machine." msgstr "A firmware frissítések támogatottak ezen a gépen." -#. TRANSLATORS: section header for firmware flags +#. TRANSLATORS: device properties +#. TRANSLATORS: release properties msgid "Flags" msgstr "Jelzők" @@ -551,13 +546,6 @@ msgstr "A művelet erőltetése, az összes figyelmeztetés mellőzése" msgid "Found" msgstr "Megtalálva" -msgid "GUID" -msgstr "GUID" - -#. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "Az összes eszköz lekérdezése a rendszer topológiájának megfelelően" - #. TRANSLATORS: command description msgid "Get all devices and possible releases" msgstr "Összes eszköz és a lehetséges kiadások lekérése" @@ -598,7 +586,6 @@ msgstr "Lekéri az eszközhöz tartozó kiadásokat" msgid "Gets the results from the last update" msgstr "A legutóbbi frissítésből származó eredményeket kéri le" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "Azonosító" @@ -679,10 +666,6 @@ msgstr "Több firmware fájl egyesítése" msgid "Metadata URI" msgstr "Metaadat URI" -#. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Metaadat URI aláírása" - #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "A metaadatok nem szerezhetőek be a Linux gyártói firmware szolgáltatásból." @@ -703,14 +686,12 @@ msgstr "A démon eseményeinek figyelése" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Név" msgid "No action specified!" msgstr "Nincs művelet megadva!" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Nem észlelhető firmware frissítési képességgel rendelkező hardver" @@ -814,10 +795,7 @@ msgstr "Régió" msgid "Reinstalling %s with %s... " msgstr "%s újratelepítése ezzel: %s…" -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Távoli tároló" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "Távoli azonosító" @@ -986,7 +964,7 @@ msgstr "Állapot" msgid "Status" msgstr "Állapot" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Összegzés" @@ -1013,10 +991,6 @@ msgstr "Ez a távoli tároló olyan firmware-t tartalmaz, amelyre nem vonatkozik msgid "This tool can only be used by the root user" msgstr "Ezt az eszközt csak a root felhasználó használhatja" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Cím" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Átviteli méret" @@ -1029,10 +1003,6 @@ msgstr "Típus" msgid "UEFI Firmware Utility" msgstr "UEFI firmware segédprogram" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Ismeretlen" @@ -1048,39 +1018,6 @@ msgstr "Eszköz feloldása a firmware eléréséhez" msgid "Unset the debugging flag during update" msgstr "A hibakeresési jelző kikapcsolása frissítéskor" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Frissítés ellenőrzőösszege" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Frissítés leírása" - -#. TRANSLATORS: section header for the amount -#. * of time it takes to install the update -msgid "Update Duration" -msgstr "Frissítés hossza" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Frissítés helye" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Frissítés neve" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "Távoli azonosító frissítése" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Frissítés összegzése" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Frissítés verziója" - #. TRANSLATORS: command description msgid "Update all devices that match local metadata" msgstr "Az összes olyan eszköz frissítése, amely illeszkedik a helyi metaadatokra" @@ -1140,7 +1077,7 @@ msgstr "Felhasználónév" msgid "Verifying…" msgstr "Ellenőrzés…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Verzió" diff --git a/po/id.po b/po/id.po index 4ab054781..99b2d7926 100644 --- a/po/id.po +++ b/po/id.po @@ -15,11 +15,6 @@ msgstr "" "Language: id\n" "Plural-Forms: nplurals=1; plural=0;\n" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s memiliki pemutakhiran firmware:" - #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Ditambahkan" @@ -107,7 +102,6 @@ msgstr "Dibatalkan" msgid "Changed" msgstr "Diubah" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Checksum" @@ -167,7 +161,7 @@ msgstr "Mendekompresi..." msgid "Decrypt firmware data" msgstr "Dekripsikan data firmware" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Deskripsi" @@ -320,9 +314,6 @@ msgstr[0] "Metadata firmware belum diperbarui selama %uhari dan mungkin tidak mu msgid "Found" msgstr "Ditemukan" -msgid "GUID" -msgstr "GUID" - #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Dapatkan semua perangkat yang mendukung pemutakhiran firmware" @@ -351,7 +342,6 @@ msgstr "Dapatkan rilis-rilis bagi sebuah peranti" msgid "Gets the results from the last update" msgstr "Dapatkan hasil dari pemutakhiran terakhir" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -401,10 +391,6 @@ msgstr "Gabungkan beberapa berkas firmware menjadi satu" msgid "Metadata URI" msgstr "URI Metadata" -#. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Tanda Tangan URI Metadata" - msgid "Mode" msgstr "Mode" @@ -421,11 +407,9 @@ msgstr "Pantau daemon untuk kejadian" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Nama" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Tidak terdeteksi perangkat keras dengan kapabilitas pemutakhiran firmware" @@ -490,10 +474,7 @@ msgstr "Wilayah" msgid "Reinstalling %s with %s... " msgstr "Memasang ulang %s dengan %s..." -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Remote" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "ID Remote" @@ -611,17 +592,13 @@ msgstr "Keadaan" msgid "Status" msgstr "Status" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Ringkasan" msgid "Target" msgstr "Target" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Judul" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Ukuran Transfer" @@ -630,10 +607,6 @@ msgstr "Ukuran Transfer" msgid "Type" msgstr "Tipe" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Tidak diketahui" @@ -645,34 +618,6 @@ msgstr "Buka kunci perangkat untuk mengizinkan akses" msgid "Unlocks the device for firmware access" msgstr "Buka kunci perangkat bagi akses firmware" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Mutakhirkan Checksum" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Mutakhirkan Keterangan" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Mutakhirkan Lokasi" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Nama Pembaruan" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "Mutakhirkan ID Remote" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Ringkasan Pembaruan" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Mutakhirkan Versi" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "Kegagalan pembaruan adalah masalah yang telah diketahui, kunjungi URL ini untuk informasi lebih lanjut:" @@ -715,7 +660,7 @@ msgstr "Nama Pengguna" msgid "Verifying…" msgstr "Verifikasi..." -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Versi" diff --git a/po/it.po b/po/it.po index 372b4f546..2237cec49 100644 --- a/po/it.po +++ b/po/it.po @@ -66,6 +66,13 @@ msgstr "Aggiornamento ME di %s" msgid "%s System Update" msgstr "Aggiornamento sistema %s" +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Aggiornamento unità di controllo Thunderbolt %s" + #. TRANSLATORS: this is the fallback where we don't know if the release #. * is updating the system, the device, or a device class, or something else #. -- @@ -74,11 +81,6 @@ msgstr "Aggiornamento sistema %s" msgid "%s Update" msgstr "Aggiornamento di %s" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s ha degli aggiornamenti del firmware:" - #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -239,6 +241,10 @@ msgstr "È richiesto autenticarsi per aggiornare il firmware su questa macchina" msgid "Authentication is required to update the stored checksums for the device" msgstr "È richiesto autenticarsi per aggiornare il codice di controllo del dispositivo salvato" +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Versione bootloader" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Compila il firmware utilizzando una sandbox" @@ -256,7 +262,6 @@ msgstr "Annullato" msgid "Changed" msgstr "Modificato" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Codice di controllo" @@ -297,6 +302,10 @@ msgstr "Converte il firmware nel formato DFU" msgid "Create a binary patch using two files" msgstr "Crea una patch binaria utilizzando due file" +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Versione attuale" + msgid "DFU" msgstr "DFU" @@ -316,7 +325,7 @@ msgstr "Estrazione…" msgid "Decrypt firmware data" msgstr "Decifra i dati del firmware" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Descrizione" @@ -328,6 +337,10 @@ msgstr "Rimuove l'attuale dispositivo collegato con supporto DFU" msgid "Detach to bootloader mode" msgstr "Scollega in modalità bootloader" +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Dettagli" + #. TRANSLATORS: ID for hardware, typically a SHA1 sum msgid "Device ID" msgstr "ID dispositivo" @@ -375,6 +388,10 @@ msgstr "Non controlla se è necessario riavviare dopo un aggiornamento" msgid "Do not check for unreported history" msgstr "Non controlla la cronologia non segnalata " +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Non include il prefisso della marcatura temporale" + #. TRANSLATORS: command line option msgid "Do not write to the history database" msgstr "Non scrive la cronologia" @@ -415,6 +432,10 @@ msgstr "Scarta informazioni su un file di firmware" msgid "Dump information about a binary patch to the screen" msgstr "Stampa le informazioni riguardo a una patch binaria su schermo" +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Durata" + #. TRANSLATORS: ESP is EFI System Partition msgid "ESP specified was not valid" msgstr "ESP specificata non era valida" @@ -489,6 +510,10 @@ msgstr "Caricamento stranezze non riuscito" msgid "Failed to parse arguments" msgstr "Analisi degli argomenti non riuscita" +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Analisi del flag --filter non riuscita" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" msgstr "Riavvio non riuscito" @@ -521,6 +546,10 @@ msgstr "Nome file" msgid "Filename Signature" msgstr "Firma nome file" +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filtra tramite un insieme di flag utilizzando ~ per escludere, per esempio «internal, ~needs-reboot»" + #. TRANSLATORS: program name msgid "Firmware Agent" msgstr "Strumento firmware" @@ -554,7 +583,8 @@ msgstr "Gli aggiornamenti firmware non sono supportati su questo dispositivo." msgid "Firmware updates are supported on this machine." msgstr "Gli aggiornamenti firmware sono supportati su questo dispositivo." -#. TRANSLATORS: section header for firmware flags +#. TRANSLATORS: device properties +#. TRANSLATORS: release properties msgid "Flags" msgstr "Flag" @@ -565,12 +595,9 @@ msgstr "Forza l'azione ignorando gli avvisi" msgid "Found" msgstr "Trovato" -msgid "GUID" -msgstr "GUID" - #. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "Recupera tutti i dispositivi in base alla topologia di sistema" +msgid "Get all device flags supported by fwupd" +msgstr "Ottiene tutti i flag dispositivo supportati da fwupd" #. TRANSLATORS: command description msgid "Get all devices and possible releases" @@ -612,7 +639,6 @@ msgstr "Ottiene i rilasci di un dispositivo" msgid "Gets the results from the last update" msgstr "Ottiene i risultati dell'ultimo aggiornamento" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "Identificativo" @@ -620,6 +646,14 @@ msgstr "Identificativo" msgid "Idle…" msgstr "Inattivo…" +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ignora controlli SSL nello scaricare i file" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Tempo di installazione" + #. TRANSLATORS: command description msgid "Install a firmware blob on a device" msgstr "Installa blob firmware su un dispositivo" @@ -663,6 +697,10 @@ msgstr "Portachiavi" msgid "Less than one minute remaining" msgstr "Manca meno di un minuto" +#. TRANSLATORS: e.g. GPLv2+, Non free etc +msgid "License" +msgstr "Licenza" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Linux Vendor Firmware Service (firmware stabile)" @@ -681,6 +719,10 @@ msgstr "Elenca gli aggiornamenti firmware supportati" msgid "Loading…" msgstr "Caricamento…" +#. TRANSLATORS: command line option +msgid "Log output to FILE (typically for scripting use)" +msgstr "Registra su FILE (usato solitamente per script)" + #. TRANSLATORS: command line option msgid "Manually whitelist specific plugins" msgstr "Abilita manualmente plugin specifici" @@ -690,17 +732,21 @@ msgid "Merge multiple firmware files into one" msgstr "Unisce più file di firmware in uno" #. TRANSLATORS: remote URI -msgid "Metadata URI" -msgstr "URI metadati" +msgid "Metadata Signature" +msgstr "Firma metadati" #. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Firma URI dei metadati" +msgid "Metadata URI" +msgstr "URI metadati" #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Metadati possono essere scaricati da Linux Vendor Firmware Service." +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Versione minima" + #. TRANSLATORS: error message #, c-format msgid "Mismatched daemon and client, use %s instead" @@ -729,14 +775,12 @@ msgstr "Controlla il demone per gli eventi" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Nome" msgid "No action specified!" msgstr "Nessuna azione specificata." -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Non è stato rilevato nessun hardware con capacità di aggiornamento del firmware" @@ -745,10 +789,18 @@ msgstr "Non è stato rilevato nessun hardware con capacità di aggiornamento del msgid "No plugins found" msgstr "Non è stato trovato alcun plugin" +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Nessuna versione disponibile" + #. TRANSLATORS: explain why no metadata available msgid "No remotes are currently enabled so no metadata is available." msgstr "Non è abilitato alcun remoto e non sono disponibili metadati." +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Nessun server disponibile" + #. TRANSLATORS: nothing was updated offline msgid "No updates were applied" msgstr "Non è stato applicato alcun aggiornamento" @@ -844,10 +896,7 @@ msgstr "Regione" msgid "Reinstalling %s with %s... " msgstr "Reinstallazione di %s con %s..." -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Remoto" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "ID remoto" @@ -915,6 +964,10 @@ msgstr "Pianificazione…" msgid "Serial" msgstr "Seriale" +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Numero di serie" + #. TRANSLATORS: command description msgid "Set alternative name on firmware file" msgstr "Imposta il nome alternativo sul file del firmware" @@ -1020,6 +1073,14 @@ msgstr "Firma i dati caricati col certificato del client" msgid "Signature" msgstr "Firma" +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Dimensione" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Sorgente" + msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Specifica vendor/ID prodotto di un dispositivo DFU" @@ -1036,7 +1097,7 @@ msgstr "Stato" msgid "Status" msgstr "Stato" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Riepilogo" @@ -1063,10 +1124,6 @@ msgstr "Questo remoto contiene firmware che non è bloccato, ma è ancora in fas msgid "This tool can only be used by the root user" msgstr "Questo strumento può essere usato solamente dall'utente root" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Titolo" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Dimensione trasferimento" @@ -1079,14 +1136,14 @@ msgstr "Tipo" msgid "UEFI Firmware Utility" msgstr "Strumento firmware UEFI" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Sconosciuto" +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Dispositivo sconosciuto" + msgid "Unlock the device to allow access" msgstr "Sblocco del dispositivo per consentire l'accesso" @@ -1103,38 +1160,18 @@ msgstr "Ripristina il flag di debug durante l'aggiornamento" msgid "Unsupported daemon version %s, client version is %s" msgstr "Demone versione %s non supportato, la versione del client è %s" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Codice di controllo aggiornamento" +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Errore aggiornamento" -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Descrizione aggiornamento" +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Messaggio aggiornamento" -#. TRANSLATORS: section header for the amount -#. * of time it takes to install the update -msgid "Update Duration" -msgstr "Durata aggiornamento" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Posizione aggiornamento" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Nome aggiornamento" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "ID remoto aggiornamento" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Riepilogo aggiornameto" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Versione aggiornamento" +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Stato aggiornamento" #. TRANSLATORS: command description msgid "Update all devices that match local metadata" @@ -1187,18 +1224,30 @@ msgstr "Caricare il rapporto ora?" msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." msgstr "Inviare resoconti sul firmware aiuta gli sviluppatori a identificare velocemente aggiornamenti eseguiti con successo o non riusciti su dispositivi reali." +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Usa flag quirk nell'installare il firmware" + #. TRANSLATORS: remote filename base msgid "Username" msgstr "Nome utente" +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Fornitore" + #. TRANSLATORS: verifying we wrote the firmware correctly msgid "Verifying…" msgstr "Verifica…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Versione" +#. TRANSLATORS: try to help +msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Attenzione: controlli SSL ignorati, per fare ciò automaticamente in futuro, esportare DISABLE_SSL_STRICT nel proprio ambiente" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Attesa…" diff --git a/po/ko.po b/po/ko.po index cc59b03e9..8b31a8326 100644 --- a/po/ko.po +++ b/po/ko.po @@ -73,11 +73,6 @@ msgstr "%s 시스템 업데이트" msgid "%s Update" msgstr "%s 업데이트" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s의 최신 펌웨어가 있습니다:" - #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -250,7 +245,6 @@ msgstr "취소함" msgid "Changed" msgstr "바뀜" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "체크섬" @@ -310,7 +304,7 @@ msgstr "압축 해제 중…" msgid "Decrypt firmware data" msgstr "펌웨어 데이터 복호화" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "설명" @@ -547,7 +541,8 @@ msgstr "이 머신에서 펌웨어 업데이트를 지원하지 않습니다." msgid "Firmware updates are supported on this machine." msgstr "이 머신에서 펌웨어 업데이트를 지원합니다." -#. TRANSLATORS: section header for firmware flags +#. TRANSLATORS: device properties +#. TRANSLATORS: release properties msgid "Flags" msgstr "플래그" @@ -558,13 +553,6 @@ msgstr "모든 경고를 무시하고 작업 강제 진행" msgid "Found" msgstr "감지 장치" -msgid "GUID" -msgstr "GUID" - -#. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "시스템에 연결된 순서대로 모든 장치 목록 가져오기" - #. TRANSLATORS: command description msgid "Get all devices and possible releases" msgstr "모든 장치와 릴리스 가져오기" @@ -605,7 +593,6 @@ msgstr "장치 펌웨어 릴리스를 가져옵니다" msgid "Gets the results from the last update" msgstr "최근 업데이트 결과를 가져옵니다" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -686,10 +673,6 @@ msgstr "다중 펌웨어 파일을 하나로 병합" msgid "Metadata URI" msgstr "메타데이터 URI" -#. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "메타데이터 URI 서명" - #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "리눅스 제조사 펌웨어 서비스에서 메타데이터를 가져올 수 있습니다." @@ -722,14 +705,12 @@ msgstr "데몬 이벤트를 감시합니다" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "이름" msgid "No action specified!" msgstr "동작을 지정하지 않았습니다!" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "펌웨어를 업데이트할 수 있는 하드웨어가 없음" @@ -837,10 +818,7 @@ msgstr "메모리 영역" msgid "Reinstalling %s with %s... " msgstr "%2$s(으)로 %1$s 다시 설치하는 중... " -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "원격" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "원격 ID" @@ -1029,7 +1007,7 @@ msgstr "상태" msgid "Status" msgstr "장치 상태" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "요약" @@ -1056,10 +1034,6 @@ msgstr "이 원격 저장소에서는 하드웨어 제조사에서 검증 단계 msgid "This tool can only be used by the root user" msgstr "이 도구는 루트로만 사용할 수 있습니다" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "제목" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "전송 용량" @@ -1072,10 +1046,6 @@ msgstr "형식" msgid "UEFI Firmware Utility" msgstr "UEFI 펌웨어 유틸리티" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "알 수 없음" @@ -1096,39 +1066,6 @@ msgstr "업데이트 중 디버깅 플래그 설정 해제" msgid "Unsupported daemon version %s, client version is %s" msgstr "지원하지 않는 데몬 버전 %s, 클라이언트 버전 %s" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "업데이트 체크섬" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "업데이트 설명" - -#. TRANSLATORS: section header for the amount -#. * of time it takes to install the update -msgid "Update Duration" -msgstr "업데이트 예상 시간" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "업데이트 위치" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "업데이트 이름" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "업데이트 원격 ID" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "업데이트 요약" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "업데이트 버전" - #. TRANSLATORS: command description msgid "Update all devices that match local metadata" msgstr "로컬 메타데이터와 일치하는 모든 장치 업데이트" @@ -1188,7 +1125,7 @@ msgstr "사용자 이름" msgid "Verifying…" msgstr "검증 중…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "버전" diff --git a/po/lt.po b/po/lt.po index 33d4312cb..3a9d27053 100644 --- a/po/lt.po +++ b/po/lt.po @@ -54,11 +54,6 @@ msgstr "%s sisteminis atnaujinimas" msgid "%s Update" msgstr "%s atnaujinimas" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s turi programinės aparatinės įrangos atnaujinimų:" - #. TRANSLATORS: command description msgid "Activate devices" msgstr "Aktyvuoti įrenginius" @@ -197,7 +192,6 @@ msgstr "Atsisakyta" msgid "Changed" msgstr "Pakeistas" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Kontrolinė suma" @@ -257,7 +251,7 @@ msgstr "Išskleidžiama…" msgid "Decrypt firmware data" msgstr "Iššifruoti programinės aparatinės įrangos duomenis" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Aprašas" @@ -485,7 +479,8 @@ msgstr "Šiame kompiuteryje programinės aparatinės įrangos atnaujinimai yra n msgid "Firmware updates are supported on this machine." msgstr "Šiame kompiuteryje yra prieinami programinės aparatinės įrangos atnaujinimai." -#. TRANSLATORS: section header for firmware flags +#. TRANSLATORS: device properties +#. TRANSLATORS: release properties msgid "Flags" msgstr "Vėliavėlės" @@ -496,13 +491,6 @@ msgstr "Priverstinai atlikti veiksmą nepaisant visų įspėjimų" msgid "Found" msgstr "Rastas" -msgid "GUID" -msgstr "GUID" - -#. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "Gauti visus įrenginius pagal sistemos topologiją" - #. TRANSLATORS: command description msgid "Get all devices and possible releases" msgstr "Gauti visus įrenginius ir galimas laidas" @@ -543,7 +531,6 @@ msgstr "Gauna laidas įrenginiui" msgid "Gets the results from the last update" msgstr "Gauna rezultatus iš paskutinio atnaujinimo" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -624,10 +611,6 @@ msgstr "Sujungti kelis programinės aparatinės įrangos failus į vieną" msgid "Metadata URI" msgstr "Metaduomenų URI" -#. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Metaduomenų URI parašas" - #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Metaduomenys gali būti gauti iš Linux tiekėjų programinės aparatinės įrangos paslaugos." @@ -648,14 +631,12 @@ msgstr "Stebėti tarnybą, ar yra įvykių" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Pavadinimas" msgid "No action specified!" msgstr "Nenurodytas joks veiksmas!" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Neaptikta jokios aparatinės įrangos su programinės aparatinės įrangos atnaujinimo galimybėmis" @@ -759,10 +740,7 @@ msgstr "Sritis" msgid "Reinstalling %s with %s... " msgstr "Iš naujo įdiegiama %s su %s... " -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Nuotolinė saugykla" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "Nuotolinės saugyklos ID" @@ -939,7 +917,7 @@ msgstr "Būsena" msgid "Status" msgstr "Būsena" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Santrauka" @@ -959,10 +937,6 @@ msgstr "Ši programa gali tinkamai veikti tik pagrindinio naudotojo teisėmis" msgid "This tool can only be used by the root user" msgstr "Šį įrankį gali naudoti tik pagrindinis naudotojas" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Pavadinimas" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Persiuntimo dydis" @@ -975,10 +949,6 @@ msgstr "Tipas" msgid "UEFI Firmware Utility" msgstr "UEFI programinės aparatinės įrangos paslaugų programa" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Nežinoma" @@ -994,39 +964,6 @@ msgstr "Atrakina įrenginį programinės aparatinės įrangos prieigai" msgid "Unset the debugging flag during update" msgstr "Atnaujinimo metu panaikinti derinimo vėliavėlės nustatymą" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Atnaujinimo kontrolinė suma" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Atnaujinimo aprašas" - -#. TRANSLATORS: section header for the amount -#. * of time it takes to install the update -msgid "Update Duration" -msgstr "Atnaujinimo trukmė" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Atnaujinimo vieta" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Atnaujinimo pavadinimas" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "Atnaujinimų nuotolinės saugyklos ID" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Atnaujinimo santrauka" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Atnaujinimo versija" - #. TRANSLATORS: command description msgid "Update all devices that match local metadata" msgstr "Atnaujinti visus įrenginius, kurie atitinka vietinius metaduomenis" @@ -1086,7 +1023,7 @@ msgstr "Naudotojo vardas" msgid "Verifying…" msgstr "Patikrinama…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Versija" diff --git a/po/nl.po b/po/nl.po index a987bc409..6c1aeb1e8 100644 --- a/po/nl.po +++ b/po/nl.po @@ -15,11 +15,6 @@ msgstr "" "Language: nl\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s heeft firmware-updates:" - #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Toegevoegd" @@ -169,9 +164,6 @@ msgstr "Firmware-hulpmiddel" msgid "Found" msgstr "Gevonden" -msgid "GUID" -msgstr "GUID" - #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Alle apparaten verkrijgen die firmware-updates ondersteunen" @@ -192,7 +184,6 @@ msgstr "Verkrijgt een lijst van updates voor verbonden hardware" msgid "Gets the results from the last update" msgstr "Verkrijgt de resultaten van de laatste update" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -240,11 +231,9 @@ msgstr "De achtergrondservice controleren op gebeurtenissen" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Naam" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Geen hardware aangetroffen die in staat is firmware bij te werken" @@ -377,22 +366,6 @@ msgstr "Ontgrendel het apparaat om toegang te verlenen" msgid "Unlocks the device for firmware access" msgstr "Ontgrendelt het apparaat voor firmware-toegang" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Controlesom bijwerken" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Omschrijving bijwerken" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Locatie bijwerken" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Versie bijwerken" - msgid "Update the stored device verification information" msgstr "Opgeslagen apparaatverificatie-informatie bijwerken" diff --git a/po/oc.po b/po/oc.po index f323c18a7..1b87ecd1a 100644 --- a/po/oc.po +++ b/po/oc.po @@ -34,7 +34,6 @@ msgstr "Anullat" msgid "Changed" msgstr "Cambiat" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Soma de contraròtle" @@ -51,7 +50,7 @@ msgstr "Comanda pas trobada" msgid "Debugging Options" msgstr "Opcions de desbugatge" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Descripcion" @@ -86,9 +85,6 @@ msgstr "Servici D-Bus de mesa a jorn dels micrologicials" msgid "Found" msgstr "Trobat" -msgid "GUID" -msgstr "GUID" - #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Obténer la lista dels periferics que supòrtan las mesas a jorn de micrologicial" @@ -97,7 +93,6 @@ msgstr "Obténer la lista dels periferics que supòrtan las mesas a jorn de micr msgid "Gets details about a firmware file" msgstr "Obténer los detalhs d'un fichièr de micrologicial" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -110,11 +105,9 @@ msgstr "Mòde" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Nom" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Cap de material amb de capacitats de mesa a jorn del micrologicial es pas estat detectat" @@ -167,6 +160,6 @@ msgstr "Estatut" msgid "Updating %s from %s to %s... " msgstr "Mesa a jorn de %s de %s en %s " -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Version" diff --git a/po/pl.po b/po/pl.po index a0e006314..94e7ce222 100644 --- a/po/pl.po +++ b/po/pl.po @@ -67,6 +67,13 @@ msgstr "Aktualizacja podsystemu ME urządzenia %s" msgid "%s System Update" msgstr "Aktualizacja komputera %s" +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Aktualizacja kontrolera Thunderbolt %s" + #. TRANSLATORS: this is the fallback where we don't know if the release #. * is updating the system, the device, or a device class, or something else #. -- @@ -75,11 +82,6 @@ msgstr "Aktualizacja komputera %s" msgid "%s Update" msgstr "Aktualizacja urządzenia %s" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "Dostępne są aktualizacje oprogramowania sprzętowego dla urządzenia %s:" - #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -250,6 +252,10 @@ msgstr "Wymagane jest uwierzytelnienie, aby zaktualizować oprogramowanie sprzę msgid "Authentication is required to update the stored checksums for the device" msgstr "Wymagane jest uwierzytelnienie, aby zaktualizować przechowywane sumy kontrolne dla urządzenia" +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Wersja programu startowego" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Buduje oprogramowanie sprzętowe za pomocą piaskownicy" @@ -267,7 +273,6 @@ msgstr "Anulowano" msgid "Changed" msgstr "Zmieniono" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Suma kontrolna" @@ -308,6 +313,10 @@ msgstr "Konwertuje oprogramowanie sprzętowe do formatu DFU" msgid "Create a binary patch using two files" msgstr "Tworzy binarną łatę z dwóch plików" +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Obecna wersja" + msgid "DFU" msgstr "DFU" @@ -327,7 +336,7 @@ msgstr "Dekompresowanie…" msgid "Decrypt firmware data" msgstr "Odszyfrowuje dane oprogramowania sprzętowego" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Opis" @@ -339,6 +348,10 @@ msgstr "Odłącza obecnie podłączone urządzenie DFU" msgid "Detach to bootloader mode" msgstr "Odłącza do trybu programu startowego" +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Informacje" + #. TRANSLATORS: ID for hardware, typically a SHA1 sum msgid "Device ID" msgstr "Identyfikator urządzenia" @@ -386,6 +399,10 @@ msgstr "Bez sprawdzania potrzeby ponownego uruchomienia po aktualizacji" msgid "Do not check for unreported history" msgstr "Bez sprawdzania niezgłoszonej historii" +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Bez dołączania przedrostka czasu" + #. TRANSLATORS: command line option msgid "Do not write to the history database" msgstr "Bez zapisywania do bazy danych historii" @@ -426,6 +443,10 @@ msgstr "Zrzuca informacje o pliku oprogramowania sprzętowego" msgid "Dump information about a binary patch to the screen" msgstr "Zrzuca informacje o binarnej łacie na ekran" +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Czas trwania" + #. TRANSLATORS: ESP is EFI System Partition msgid "ESP specified was not valid" msgstr "Podana partycja ESP nie jest prawidłowa" @@ -500,6 +521,10 @@ msgstr "Wczytanie poprawek się nie powiodło" msgid "Failed to parse arguments" msgstr "Przetworzenie parametrów się nie powiodło" +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Przetworzenie flag dla opcji --filter się nie powiodło" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" msgstr "Ponowne uruchomienie się nie powiodło" @@ -532,6 +557,10 @@ msgstr "Nazwa pliku" msgid "Filename Signature" msgstr "Podpis nazwy pliku" +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filtruje za pomocą zestawu flag urządzeń, przedrostek ~ wyklucza, np. „internal,~needs-reboot”" + #. TRANSLATORS: program name msgid "Firmware Agent" msgstr "Agent oprogramowania sprzętowego" @@ -567,7 +596,8 @@ msgstr "Aktualizacje oprogramowania sprzętowego nie są obsługiwane na tym kom msgid "Firmware updates are supported on this machine." msgstr "Aktualizacje oprogramowania sprzętowego są obsługiwane na tym komputerze." -#. TRANSLATORS: section header for firmware flags +#. TRANSLATORS: device properties +#. TRANSLATORS: release properties msgid "Flags" msgstr "Flagi" @@ -578,12 +608,9 @@ msgstr "Wymusza działanie ignorując wszystkie ostrzeżenia" msgid "Found" msgstr "Odnaleziono" -msgid "GUID" -msgstr "GUID" - #. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "Uzyskuje wszystkie urządzenia zgodnie z topologią komputera" +msgid "Get all device flags supported by fwupd" +msgstr "Uzyskuje wszystkie flagi urządzeń obsługiwane przez usługę fwupd" #. TRANSLATORS: command description msgid "Get all devices and possible releases" @@ -625,7 +652,6 @@ msgstr "Uzyskuje wydania dla urządzenia" msgid "Gets the results from the last update" msgstr "Uzyskuje wyniki z ostatniej aktualizacji" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "Identyfikator" @@ -633,6 +659,14 @@ msgstr "Identyfikator" msgid "Idle…" msgstr "Bezczynne…" +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ignoruje ścisłe testy SSL podczas pobierania plików" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Czas trwania instalacji" + #. TRANSLATORS: command description msgid "Install a firmware blob on a device" msgstr "Instaluje zamknięte oprogramowanie sprzętowe na urządzeniu" @@ -676,6 +710,10 @@ msgstr "Baza kluczy" msgid "Less than one minute remaining" msgstr "Pozostała mniej niż jedna minuta" +#. TRANSLATORS: e.g. GPLv2+, Non free etc +msgid "License" +msgstr "Licencja" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Linux Vendor Firmware Service (stabilne oprogramowanie sprzętowe)" @@ -694,6 +732,10 @@ msgstr "Wyświetla listę obsługiwanych aktualizacji oprogramowania sprzętoweg msgid "Loading…" msgstr "Wczytywanie…" +#. TRANSLATORS: command line option +msgid "Log output to FILE (typically for scripting use)" +msgstr "Zapisuje wyjście do PLIKU (zazwyczaj do używania w skryptach)" + #. TRANSLATORS: command line option msgid "Manually whitelist specific plugins" msgstr "Ręcznie wstawia podane wtyczki na białą listę" @@ -703,17 +745,21 @@ msgid "Merge multiple firmware files into one" msgstr "Łączy wiele plików oprogramowania sprzętowego w jeden plik" #. TRANSLATORS: remote URI -msgid "Metadata URI" -msgstr "Adres URI metadanych" +msgid "Metadata Signature" +msgstr "Podpis metadanych" #. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Podpis adresu URI metadanych" +msgid "Metadata URI" +msgstr "Adres URI metadanych" #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Metadane można uzyskać z serwisu Linux Vendor Firmware Service." +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Minimalna wersja" + #. TRANSLATORS: error message #, c-format msgid "Mismatched daemon and client, use %s instead" @@ -742,14 +788,12 @@ msgstr "Monitoruje zdarzenia usługi" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Nazwa" msgid "No action specified!" msgstr "Nie podano działania." -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Nie wykryto sprzętu z możliwością aktualizacji jego oprogramowania" @@ -758,10 +802,18 @@ msgstr "Nie wykryto sprzętu z możliwością aktualizacji jego oprogramowania" msgid "No plugins found" msgstr "Nie odnaleziono wtyczek" +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Brak dostępnych wydań" + #. TRANSLATORS: explain why no metadata available msgid "No remotes are currently enabled so no metadata is available." msgstr "Żadne repozytoria nie są obecnie włączone, więc żadne metadane nie są dostępne." +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Brak dostępnych repozytoriów" + #. TRANSLATORS: nothing was updated offline msgid "No updates were applied" msgstr "Nie zastosowano żadnych aktualizacji" @@ -857,10 +909,7 @@ msgstr "Region" msgid "Reinstalling %s with %s... " msgstr "Ponowne instalowanie %s za pomocą %s… " -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Repozytorium" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "Identyfikator repozytorium" @@ -928,6 +977,10 @@ msgstr "Planowanie…" msgid "Serial" msgstr "Numer seryjny" +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Numer seryjny" + #. TRANSLATORS: command description msgid "Set alternative name on firmware file" msgstr "Ustawia alternatywną nazwę pliku oprogramowania sprzętowego" @@ -1033,6 +1086,14 @@ msgstr "Podpisuje wysłane dane za pomocą certyfikatu klienta" msgid "Signature" msgstr "Podpis" +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Rozmiar" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Źródło" + msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Podaje identyfikatory dostawcy/produktu urządzenia DFU" @@ -1049,7 +1110,7 @@ msgstr "Stan" msgid "Status" msgstr "Stan" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Podsumowanie" @@ -1076,10 +1137,6 @@ msgstr "To repozytorium zawiera oprogramowanie sprzętowe nieobjęte embargo, al msgid "This tool can only be used by the root user" msgstr "To narzędzie może być używane tylko przez użytkownika root" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Tytuł" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Rozmiar przesyłania" @@ -1092,14 +1149,14 @@ msgstr "Typ" msgid "UEFI Firmware Utility" msgstr "Narzędzie oprogramowania sprzętowego UEFI" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "Adres URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Nieznane" +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Nieznane urządzenie" + msgid "Unlock the device to allow access" msgstr "Odblokowanie urządzenia" @@ -1116,38 +1173,18 @@ msgstr "Usuwa flagę debugowania podczas aktualizacji" msgid "Unsupported daemon version %s, client version is %s" msgstr "Nieobsługiwana wersja usługi %s, wersja klienta to %s" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Suma kontrolna aktualizacji" +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Błąd aktualizacji" -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Opis aktualizacji" +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Komunikat aktualizacji" -#. TRANSLATORS: section header for the amount -#. * of time it takes to install the update -msgid "Update Duration" -msgstr "Czas trwania aktualizacji" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Położenie aktualizacji" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Nazwa aktualizacji" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "Identyfikator repozytorium aktualizacji" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Podsumowanie aktualizacji" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Wersja aktualizacji" +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Stan aktualizacji" #. TRANSLATORS: command description msgid "Update all devices that match local metadata" @@ -1200,18 +1237,30 @@ msgstr "Wysłać zgłoszenie?" msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." msgstr "Wysyłanie zgłoszeń o oprogramowaniu sprzętowym pomaga dostawcom sprzętu szybko identyfikować nieudane i udane aktualizacje na prawdziwych urządzeniach." +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Używa flag poprawek podczas instalowania oprogramowania sprzętowego" + #. TRANSLATORS: remote filename base msgid "Username" msgstr "Nazwa użytkownika" +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Producent" + #. TRANSLATORS: verifying we wrote the firmware correctly msgid "Verifying…" msgstr "Sprawdzanie poprawności…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Wersja" +#. TRANSLATORS: try to help +msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "OSTRZEŻENIE: ignorowanie ścisłych testów SSL, aby robić to automatycznie w przyszłości, należy wyeksportować zmienną DISABLE_SSL_STRICT w środowisku" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Oczekiwanie…" diff --git a/po/pt_BR.po b/po/pt_BR.po index e8daf0e2f..1b7f578c1 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -69,6 +69,13 @@ msgstr "Atualização do ME %s" msgid "%s System Update" msgstr "Atualização do sistema %s" +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Atualização de Controlador Thunderbolt %s" + #. TRANSLATORS: this is the fallback where we don't know if the release #. * is updating the system, the device, or a device class, or something else #. -- @@ -77,11 +84,6 @@ msgstr "Atualização do sistema %s" msgid "%s Update" msgstr "Atualização de %s" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s tem atualizações de firmware:" - #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -242,6 +244,10 @@ msgstr "É requerida autenticação para atualizar o firmware nesta máquina" msgid "Authentication is required to update the stored checksums for the device" msgstr "É requerida autenticação para atualizar as somas de verificação armazenadas para o dispositivo" +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Versão do gerenciador de inicialização" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Compila o firmware usando um sandbox" @@ -259,7 +265,6 @@ msgstr "Cancelado" msgid "Changed" msgstr "Alterado" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Soma de verificação" @@ -300,6 +305,10 @@ msgstr "Converte um firmware para formato DFU" msgid "Create a binary patch using two files" msgstr "Cria um patch binário usando dois arquivos" +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Versão atual" + msgid "DFU" msgstr "DFU" @@ -319,7 +328,7 @@ msgstr "Descomprimindo…" msgid "Decrypt firmware data" msgstr "Descriptografa os dados do firmware" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Descrição" @@ -331,6 +340,10 @@ msgstr "Desanexa dispositivos atualmente anexados com capacidade de DFU" msgid "Detach to bootloader mode" msgstr "Modo desanexar ao gerenciador de boot" +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Detalhes" + #. TRANSLATORS: ID for hardware, typically a SHA1 sum msgid "Device ID" msgstr "ID do dispositivo" @@ -378,6 +391,10 @@ msgstr "Não verifica por reinicialização após atualizar" msgid "Do not check for unreported history" msgstr "Não verifica por histórico não relatado" +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Não inclui prefixo com marca de tempo" + #. TRANSLATORS: command line option msgid "Do not write to the history database" msgstr "Não escreve no banco de dados de histórico" @@ -418,6 +435,10 @@ msgstr "Despeja os detalhes sobre um arquivo firmware" msgid "Dump information about a binary patch to the screen" msgstr "Despeja informações sobre um patch binário na tela" +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Duração" + #. TRANSLATORS: ESP is EFI System Partition msgid "ESP specified was not valid" msgstr "O ESP especificado não era válido" @@ -492,6 +513,10 @@ msgstr "Falha ao carregar peculiaridades" msgid "Failed to parse arguments" msgstr "Falha ao interpretar argumentos" +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Falha ao analisar opções para --filter" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" msgstr "Falha ao reinicializar" @@ -524,6 +549,10 @@ msgstr "Nome de arquivo" msgid "Filename Signature" msgstr "Assinatura de nome de arquivo" +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Filtra com um conjunto de opções de dispositivo usando um prefixo ~ para excluir, p.ex. \"internal,~needs-reboot\"" + #. TRANSLATORS: program name msgid "Firmware Agent" msgstr "Agente de firmware" @@ -557,9 +586,10 @@ msgstr "Não há suporte a atualizações de firmware nessa máquina." msgid "Firmware updates are supported on this machine." msgstr "Há suporte a atualizações de firmware nesta máquina." -#. TRANSLATORS: section header for firmware flags +#. TRANSLATORS: device properties +#. TRANSLATORS: release properties msgid "Flags" -msgstr "Sinalizadores" +msgstr "Opções" msgid "Force the action ignoring all warnings" msgstr "Força a ação ignorando todos avisos" @@ -568,12 +598,9 @@ msgstr "Força a ação ignorando todos avisos" msgid "Found" msgstr "Encontrado" -msgid "GUID" -msgstr "GUID" - #. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "Obtém todos os dispositivos de acordo com a topologia do sistema" +msgid "Get all device flags supported by fwupd" +msgstr "Obtém todas as opções de dispositivo suportadas pelo fwupd" #. TRANSLATORS: command description msgid "Get all devices and possible releases" @@ -615,7 +642,6 @@ msgstr "Obtém os lançamentos para um dispositivo" msgid "Gets the results from the last update" msgstr "Obtém os resultados da última atualização" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -623,6 +649,14 @@ msgstr "ID" msgid "Idle…" msgstr "Ocioso…" +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ignora verificações estritas de SSL ao baixar arquivos" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Duração de instalação" + #. TRANSLATORS: command description msgid "Install a firmware blob on a device" msgstr "Instala um blob de firmware em um dispositivos" @@ -666,6 +700,10 @@ msgstr "Chaveiro" msgid "Less than one minute remaining" msgstr "Menos que um minuto restante" +#. TRANSLATORS: e.g. GPLv2+, Non free etc +msgid "License" +msgstr "Licença" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Linux Vendor Firmware Service (firmware estável)" @@ -684,6 +722,10 @@ msgstr "Lista atualizações de firmware suportadas" msgid "Loading…" msgstr "Carregando…" +#. TRANSLATORS: command line option +msgid "Log output to FILE (typically for scripting use)" +msgstr "Registra a saída para ARQUIVO (normalmente para usar com script)" + #. TRANSLATORS: command line option msgid "Manually whitelist specific plugins" msgstr "Insere manualmente na lista branca plugins específicos" @@ -693,17 +735,21 @@ msgid "Merge multiple firmware files into one" msgstr "Mescla múltiplos arquivos de firmware em um" #. TRANSLATORS: remote URI -msgid "Metadata URI" -msgstr "URI de metadados" +msgid "Metadata Signature" +msgstr "Assinatura de metadados" #. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Assinatura de URI de metadados" +msgid "Metadata URI" +msgstr "URI de metadados" #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Metadados podem ser obtidos do Linux Vendor Firmware Service." +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Versão mínima" + #. TRANSLATORS: error message #, c-format msgid "Mismatched daemon and client, use %s instead" @@ -732,14 +778,12 @@ msgstr "Monitora o daemon por eventos" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Nome" msgid "No action specified!" msgstr "Nenhuma ação especificada!" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Nenhum periférico com capacidade de atualização de firmware foi detectado" @@ -748,10 +792,18 @@ msgstr "Nenhum periférico com capacidade de atualização de firmware foi detec msgid "No plugins found" msgstr "Nenhum plugin localizado" +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Nenhum lançamento disponível" + #. TRANSLATORS: explain why no metadata available msgid "No remotes are currently enabled so no metadata is available." msgstr "Nenhum remoto está atualmente habilitado, então nenhum metadado está disponível." +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Nenhum remoto disponível" + #. TRANSLATORS: nothing was updated offline msgid "No updates were applied" msgstr "Nenhuma atualização foi aplicada" @@ -847,10 +899,7 @@ msgstr "Região" msgid "Reinstalling %s with %s... " msgstr "Reinstalando %s com %s… " -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Remoto" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "ID remoto" @@ -918,6 +967,10 @@ msgstr "Agendando…" msgid "Serial" msgstr "Serial" +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Número de série" + #. TRANSLATORS: command description msgid "Set alternative name on firmware file" msgstr "Define o nome alternativo no arquivo de firmware" @@ -1023,6 +1076,14 @@ msgstr "Assina os dados enviados com o certificado cliente" msgid "Signature" msgstr "Assinatura" +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Tamanho" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Fonte" + msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Especifica ID(s) de Fornecedor/Produto de dispositivo DFU" @@ -1039,7 +1100,7 @@ msgstr "Estado" msgid "Status" msgstr "Status" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Resumo" @@ -1066,10 +1127,6 @@ msgstr "Este remoto contém firmware que não está embargado, mas ainda está s msgid "This tool can only be used by the root user" msgstr "Essa ferramenta só pode ser usada pelo usuário root" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Título" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Tamanho da transferência" @@ -1082,14 +1139,14 @@ msgstr "Tipo" msgid "UEFI Firmware Utility" msgstr "Utilitário de Firmware UEFI" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Desconhecido" +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Dispositivo desconhecido" + msgid "Unlock the device to allow access" msgstr "Desbloquear o dispositivo para permitir acesso" @@ -1106,38 +1163,18 @@ msgstr "Desativa a opção de depuração durante atualização" msgid "Unsupported daemon version %s, client version is %s" msgstr "Sem suporte ao daemon na versão %s, a versão do cliente é %s" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Soma de verificação da atualização" +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Erro na atualização" -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Descrição da atualização" +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Mensagem de atualização" -#. TRANSLATORS: section header for the amount -#. * of time it takes to install the update -msgid "Update Duration" -msgstr "Duração da atualização" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Local da atualização" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Nome da atualização" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "ID remoto da atualização" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Resumo da atualização" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Versão da atualização" +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Estado da atualização" #. TRANSLATORS: command description msgid "Update all devices that match local metadata" @@ -1190,18 +1227,30 @@ msgstr "Enviar relatório agora?" msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." msgstr "O envio de relatórios de firmware ajuda os fornecedores de hardware a identificar rapidamente atualizações com falha e êxito em dispositivos reais." +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Usa opções de peculiaridades ao instalar firmware" + #. TRANSLATORS: remote filename base msgid "Username" msgstr "Nome de usuário" +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Fornecedor" + #. TRANSLATORS: verifying we wrote the firmware correctly msgid "Verifying…" msgstr "Verificando…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Versão" +#. TRANSLATORS: try to help +msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "AVISO: Ignorando verificações estritas de SSL. Para fazer isso automaticamente no futuro, exporte DISABLE_SSL_STRICT em seu ambiente" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Aguardando…" diff --git a/po/ru.po b/po/ru.po index bfda847fb..faac99873 100644 --- a/po/ru.po +++ b/po/ru.po @@ -76,11 +76,6 @@ msgstr "Обновление системы %s" msgid "%s Update" msgstr "Обновление устройства %s" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "У устройства %s есть обновления прошивки:" - #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -268,7 +263,6 @@ msgstr "Отменено" msgid "Changed" msgstr "Изменено" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Контрольная сумма" @@ -328,7 +322,7 @@ msgstr "Распаковка…" msgid "Decrypt firmware data" msgstr "Расшифровать данные прошивки" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Описание" @@ -568,7 +562,8 @@ msgstr "Обновления прошивки не поддерживаются msgid "Firmware updates are supported on this machine." msgstr "Обновления прошивки поддерживаются на этой машине." -#. TRANSLATORS: section header for firmware flags +#. TRANSLATORS: device properties +#. TRANSLATORS: release properties msgid "Flags" msgstr "Флаги" @@ -579,13 +574,6 @@ msgstr "Выполнить действие, игнорируя все пред msgid "Found" msgstr "Найдено" -msgid "GUID" -msgstr "GUID" - -#. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "Получить все устройства в соответствии с топологией системы" - #. TRANSLATORS: command description msgid "Get all devices and possible releases" msgstr "Получить все устройства и возможные выпуски" @@ -626,7 +614,6 @@ msgstr "Получить релизы для устройства" msgid "Gets the results from the last update" msgstr "Получить результаты с последнего обновления" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -707,10 +694,6 @@ msgstr "Объединить несколько файлов прошивок в msgid "Metadata URI" msgstr "URI метаданных" -#. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Подпись URI метаданных" - #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Метаданные можно получить в Linux Vendor Firmware Service." @@ -743,14 +726,12 @@ msgstr "Следить за событиями в фоновой службе" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Наименование" msgid "No action specified!" msgstr "Не определено никаких действий." -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Не обнаружено оборудования с возможностью обновления прошивки" @@ -858,10 +839,7 @@ msgstr "Регион" msgid "Reinstalling %s with %s... " msgstr "Переустановка %s с %s…" -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Репозиторий" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "Идентификатор репозитория" @@ -1050,7 +1028,7 @@ msgstr "Состояние" msgid "Status" msgstr "Статус" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Сводка" @@ -1077,10 +1055,6 @@ msgstr "Этот репозиторий содержит прошивки, ко msgid "This tool can only be used by the root user" msgstr "Этот инструмент может использовать только пользователь root" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Заголовок" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Размер передачи" @@ -1093,10 +1067,6 @@ msgstr "Тип" msgid "UEFI Firmware Utility" msgstr "Средство для прошивки UEFI" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Неизвестно" @@ -1117,39 +1087,6 @@ msgstr "Снять флаг отладки во время обновления" msgid "Unsupported daemon version %s, client version is %s" msgstr "Неподдерживаемая фоновая служба версии %s, клиент версии %s" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Контрольная сумма обновления" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Описание обновления" - -#. TRANSLATORS: section header for the amount -#. * of time it takes to install the update -msgid "Update Duration" -msgstr "Продолжительность обновления" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Расположение обновления" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Наименование обновления" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "Обновить идентификатор репозитория" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Сводка обновления" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Версия обновления" - #. TRANSLATORS: command description msgid "Update all devices that match local metadata" msgstr "Обновить все устройства, которые соответствуют локальным метаданным" @@ -1209,7 +1146,7 @@ msgstr "Имя пользователя" msgid "Verifying…" msgstr "Проверка…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Версия" diff --git a/po/sk.po b/po/sk.po index 3ccd79fe0..0a04dd94a 100644 --- a/po/sk.po +++ b/po/sk.po @@ -15,11 +15,6 @@ msgstr "" "Language: sk\n" "Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "Pre zariadenie %s sú dostupné aktualizácie firmvéru:" - #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Pridané" @@ -70,7 +65,6 @@ msgstr "Zrušené" msgid "Changed" msgstr "Zmenené" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Kontrolný medzisúčet" @@ -103,7 +97,7 @@ msgstr "Voľby ladenia" msgid "Decrypt firmware data" msgstr "Dešifruje údaje firmvéru" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Popis" @@ -170,9 +164,6 @@ msgstr "Nástroj pre firmvéry" msgid "Found" msgstr "Nájdené" -msgid "GUID" -msgstr "GUID" - #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Získa všetky zariadenia, ktoré podporujú aktualizovanie firmvéru" @@ -193,7 +184,6 @@ msgstr "Získa zoznam aktualizácií pre pripojený hardvér" msgid "Gets the results from the last update" msgstr "Získa výsledky z poslednej aktualizácie" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -233,11 +223,9 @@ msgstr "Sleduje démona kvôli udalostiam" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Názov" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Nezistil sa žiadny hardvér s možnosťou aktualizácie firmvéru" @@ -338,22 +326,6 @@ msgstr "Odomknúť zariadenie na umožnenie prístupu" msgid "Unlocks the device for firmware access" msgstr "Odomkne zariadenie pre prístup k firmvéru" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Kontrolný medzisúčet aktualizácie" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Popis aktualizácie" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Umiestnenie aktualizácie" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Verzia aktualizácie" - #. TRANSLATORS: command description msgid "Update the stored metadata with current ROM contents" msgstr "Aktualizuje uložené metaúdaje s aktuálnym obsahom pamäte ROM" @@ -369,7 +341,7 @@ msgstr "Aktualizuje všetok firmvér na najnovšiu dostupnú verziu" msgid "Updating %s from %s to %s... " msgstr "Aktualizuje sa %s z verzie %s na verziu %s... " -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Verzia" diff --git a/po/sr.po b/po/sr.po index cceb15957..80d112487 100644 --- a/po/sr.po +++ b/po/sr.po @@ -17,11 +17,6 @@ msgstr "" "Language: sr\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s има ажурирања за фирмвер:" - #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Додао" @@ -109,7 +104,6 @@ msgstr "Отказао" msgid "Changed" msgstr "Променио" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Чек-сума" @@ -169,7 +163,7 @@ msgstr "Распакујем…" msgid "Decrypt firmware data" msgstr "Дешифруј податке у фирмверу" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Опис" @@ -324,9 +318,6 @@ msgstr[2] "Метаподаци фирмвера нису ажурирани %u msgid "Found" msgstr "Нашао" -msgid "GUID" -msgstr "ГУИД" - #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "Добави све уређаје који подржавају ажурирање фирмвера" @@ -355,7 +346,6 @@ msgstr "Добавља издања за уређај" msgid "Gets the results from the last update" msgstr "Добавља резултате последњег ажурирања" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ИБ" @@ -405,10 +395,6 @@ msgstr "Споји више датотека са фирмвером у једн msgid "Metadata URI" msgstr "URI метаподатака" -#. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Потпис URI-ја метаподатака" - msgid "Mode" msgstr "Режим" @@ -425,11 +411,9 @@ msgstr "Прати демона за догађајима" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Назив" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Нема хардвера којем се може ажурирати фирмвер" @@ -494,10 +478,7 @@ msgstr "Област" msgid "Reinstalling %s with %s... " msgstr "Поново инсталирам %s са %s..." -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Удаљени" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "Удаљени ИБ" @@ -615,17 +596,13 @@ msgstr "Стање" msgid "Status" msgstr "Статус" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Сажетак" msgid "Target" msgstr "Мета" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Наслов" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Величина преноса" @@ -634,10 +611,6 @@ msgstr "Величина преноса" msgid "Type" msgstr "Врста" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "УРИ" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Непознато" @@ -649,34 +622,6 @@ msgstr "Откључајте уређај да бисте дозволили п msgid "Unlocks the device for firmware access" msgstr "Откључава уређај за приступ фирмверу" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Сума провере ажурирања" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Опис ажурирања" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Место ажурирања" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Назив ажурирања" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "ИБ удаљене исправке" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Сажетак ажурирања" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Верзија ажурирања" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "Узрок неуспеха ажурирања је познат, погледајте ову адресу за више података:" @@ -719,7 +664,7 @@ msgstr "Корисничко име" msgid "Verifying…" msgstr "Проверавам…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Издање" diff --git a/po/sv.po b/po/sv.po index a9cdcd16e..387f1f644 100644 --- a/po/sv.po +++ b/po/sv.po @@ -7,6 +7,7 @@ # Andreas Henriksson , 2017 # Josef Andersson , 2015,2017-2018 # Josef Andersson , 2015,2017 +# Sebastian Rasmussen , 2019 # Sebastian Rasmussen , 2018 msgid "" msgstr "" @@ -26,10 +27,56 @@ msgid_plural "%.0f minutes remaining" msgstr[0] "%.0fminut kvarstår" msgstr[1] "%.0f minuter kvarstår" -#. TRANSLATORS: first replacement is device name +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format -msgid "%s has firmware updates:" -msgstr "%s har uppdateringar för fast programvara:" +msgid "%s Consumer ME Update" +msgstr "%s ME-uppdatering för konsument" + +#. TRANSLATORS: the controller is a device that has other devices +#. * plugged into it, for example ThunderBolt, FireWire or USB, +#. * the first %s is the device name, e.g. 'Intel ThunderBolt` +#, c-format +msgid "%s Controller Update" +msgstr "%s Kontrolleruppdatering" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Corporate ME Update" +msgstr "%s ME-uppdatering för företag" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#, c-format +msgid "%s Device Update" +msgstr "%s Enhetsuppdatering" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Embedded Controller Update" +msgstr "%s Uppdatering av inbäddadkontroller" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s ME Update" +msgstr "%s ME-uppdatering" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s System Update" +msgstr "%s Systemuppdatering" + +#. TRANSLATORS: this is the fallback where we don't know if the release +#. * is updating the system, the device, or a device class, or something else +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "%s Uppdatering" #. TRANSLATORS: duration in days! #, c-format @@ -59,6 +106,25 @@ msgid_plural "%u seconds" msgstr[0] "%u sekund" msgstr[1] "%u sekunder" +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Aktivera enheter" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Aktivera väntaden enhter" + +msgid "Activate the new firmware on the device" +msgstr "Aktiverar den nya fasta programvaran på enheten" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Aktiverar uppdatering av fast programvara" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Aktiverar uppdatering av fast programvara för" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Tillagd" @@ -88,6 +154,10 @@ msgstr "Tillåt att installera om befintliga versioner av fast programvara" msgid "An update requires a reboot to complete." msgstr "En uppdatering kräver en omstart för att färdigställas." +#. TRANSLATORS: explain why we want to shutdown +msgid "An update requires the system to shutdown to complete." +msgstr "En uppdatering kräver att systemet stängs ned för att färdigställas." + #. TRANSLATORS: command line option msgid "Answer yes to all questions" msgstr "Svara ja på alla frågor" @@ -100,6 +170,13 @@ msgstr "Applicera en binär lagning" msgid "Apply firmware updates" msgstr "Tillämpa uppdateringar för fast programvara" +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Godkänd fast programvara:" +msgstr[1] "Godkänd fast programvara:" + #. TRANSLATORS: command description msgid "Attach DFU capable device back to runtime" msgstr "Anslut DFU-kapabel enhet till körtid" @@ -129,6 +206,22 @@ msgstr "Autentisering krävs för att nedgradera den fasta programvaran på denn msgid "Authentication is required to modify a configured remote used for firmware updates" msgstr "Autenticering krävs för att få ändra en konfigurerad fjärrkälla som används för fast programvaru-uppdateringar." +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "Autentisering krävs för att modifiera demonkonfiguration" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "Autentisering krävs för att sätta listan över godkända fasta progrmvaror" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "Autentisering krävs för att signera data med klientcertifikatet" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "Authentisering krävs för att växla till den nya fasta programvaruversionen" + #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" msgstr "Autentisering krävs för att låsa upp en enhet" @@ -162,7 +255,6 @@ msgstr "Avbruten" msgid "Changed" msgstr "Ändrad" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Kontrollsumma" @@ -222,7 +314,7 @@ msgstr "Dekomprimerar…" msgid "Decrypt firmware data" msgstr "Dekryptera fast programvarudata" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Beskrivning" @@ -234,6 +326,10 @@ msgstr "Koppla från anslutna DFU-kapabla enheter" msgid "Detach to bootloader mode" msgstr "Koppla från till starthanterarläge" +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Enhets-ID" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "Enhet tillagd:" @@ -367,10 +463,22 @@ msgstr "Avsluta efter en kort fördröjning" msgid "Exit after the engine has loaded" msgstr "Avsluta efter att motorn har lästs in" +#. TRANSLATORS: we could not talk to the fwupd daemon +msgid "Failed to connect to daemon" +msgstr "Misslyckades med att ansluta till demon" + #. TRANSLATORS: the server is rate-limiting downloads msgid "Failed to download due to server limit" msgstr "Misslyckades med hämtning på grund av servergräns" +#. TRANSLATORS: we could not get the devices to update offline +msgid "Failed to get pending devices" +msgstr "Misslyckades med att hämta väntande enheter" + +#. TRANSLATORS: we could not install for some reason +msgid "Failed to install firmware update" +msgstr "Misslyckades med att installera uppdatering av fast programvara" + #. TRANSLATORS: quirks are device-specific workarounds msgid "Failed to load quirks" msgstr "Misslyckades med att läsa in speciallösning" @@ -379,6 +487,14 @@ msgstr "Misslyckades med att läsa in speciallösning" msgid "Failed to parse arguments" msgstr "Misslyckades med att tolka argument" +#. TRANSLATORS: we could not reboot for some reason +msgid "Failed to reboot" +msgstr "Misslyckades med att starta om" + +#. TRANSLATORS: we could not talk to plymouth +msgid "Failed to set splash mode" +msgstr "Misslyckades med att sätta uppstartsläge" + #. TRANSLATORS: downloading unknown file msgid "Fetching file" msgstr "Hämtar fil" @@ -403,6 +519,10 @@ msgstr "Filnamn" msgid "Filename Signature" msgstr "Filnamn Signatur" +#. TRANSLATORS: program name +msgid "Firmware Agent" +msgstr "Fast programvaru-agent" + #. TRANSLATORS: remote URI msgid "Firmware Base URI" msgstr "Fast programvara bas-URI" @@ -432,6 +552,11 @@ msgstr "Uppdateringar av fast programvara stöds inte på denna maskin." msgid "Firmware updates are supported on this machine." msgstr "Uppdateringar av fast programvara stöds på denna maskin." +#. TRANSLATORS: device properties +#. TRANSLATORS: release properties +msgid "Flags" +msgstr "Faggor" + msgid "Force the action ignoring all warnings" msgstr "Tvinga åtgärden, ignorera alla varningar" @@ -439,12 +564,9 @@ msgstr "Tvinga åtgärden, ignorera alla varningar" msgid "Found" msgstr "Hittad" -msgid "GUID" -msgstr "GUID" - #. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "Hämta alla enheter enligt systemets topologi" +msgid "Get all devices and possible releases" +msgstr "Hämta alla enheter och möjliga utgåvor" #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" @@ -466,6 +588,10 @@ msgstr "Ger de konfigurerade fjärrkällorna" msgid "Gets the cryptographic hash of the dumped firmware" msgstr "Hämtar den kryptografiska hashen för den utskrivna versionen av den fasta programvaran" +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware." +msgstr "Hämtar listan över godkända fast programvara." + #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "Hämtar listan över uppdateringar för ansluten hårdvara" @@ -478,7 +604,6 @@ msgstr "Erhåll utgåvan för en enhet" msgid "Gets the results from the last update" msgstr "Hämtar resultaten från senaste uppdateringen" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -509,6 +634,10 @@ msgstr "Installera osignerad fast programvara för enhet" msgid "Install unsigned system firmware" msgstr "Installera osignerad fast programvara för systemet" +#. TRANSLATORS: console message when no Plymouth is installed +msgid "Installing Firmware…" +msgstr "Installerar fast programvara…" + #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "Installerar uppdatering för fast programvara…" @@ -555,17 +684,22 @@ msgstr "Sammanfoga flera fasta programvaror till en" msgid "Metadata URI" msgstr "Metadata URI" -#. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Metadata URI Signatur" - #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Metadata kan hämtas från Linux Vendor Firmware Service." +#. TRANSLATORS: error message +#, c-format +msgid "Mismatched daemon and client, use %s instead" +msgstr "Demon och klient stämmer inte använd %s istället." + msgid "Mode" msgstr "Läge" +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value." +msgstr "Modifierar ett konfigurationsvärde för en demon." + #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "Modifierar en given fjärrkälla" @@ -573,20 +707,21 @@ msgstr "Modifierar en given fjärrkälla" msgid "Modify a configured remote" msgstr "Modifiera en konfigurerad fjärrkälla" +msgid "Modify daemon configuration" +msgstr "Modifiera demonkonfiguration" + #. TRANSLATORS: command description msgid "Monitor the daemon for events" msgstr "Övervaka demonen för händelser" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Namn" msgid "No action specified!" msgstr "Ingen åtgärd angiven!" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Ingen uppdateringsbar hårdvara upptäcktes" @@ -599,6 +734,10 @@ msgstr "Inga insticksmoduler hittades" msgid "No remotes are currently enabled so no metadata is available." msgstr "Inga fjärrar är aktiverade för närvarande, så ingen metadata är tillgänglig." +#. TRANSLATORS: nothing was updated offline +msgid "No updates were applied" +msgstr "Inga uppdateringar applicerades." + msgid "OK" msgstr "OK" @@ -610,6 +749,10 @@ msgstr "Åsidosätt insticksmodulvarning" msgid "Override the default ESP path" msgstr "Åsidosätt standardsökväg för ESP" +#. TRANSLATORS: command line option +msgid "Override warnings and force the action" +msgstr "Åsidosätt varningar och tvinga åtgärden" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "Lösenord" @@ -617,6 +760,10 @@ msgstr "Lösenord" msgid "Payload" msgstr "Nyttolast" +#. TRANSLATORS: console message when not using plymouth +msgid "Percentage complete" +msgstr "Andel färdigställt" + msgid "Permission denied" msgstr "Åtkomst nekad" @@ -663,6 +810,10 @@ msgstr "Läs fast programvara från en partition till fil" msgid "Reading…" msgstr "Läser..." +#. TRANSLATORS: console message when not using plymouth +msgid "Rebooting…" +msgstr "Startar om…" + #. TRANSLATORS: command description msgid "Refresh metadata from remote server" msgstr "Uppdatera metadata från fjärrserver" @@ -678,10 +829,7 @@ msgstr "Område" msgid "Reinstalling %s with %s... " msgstr "Återinstallerar %s med %s… " -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Fjärrkälla" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "Fjärrkälla-id" @@ -710,6 +858,10 @@ msgstr "Omstart av en DFU-enhet" msgid "Restart now?" msgstr "Starta om nu?" +#. TRANSLATORS: configuration changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Starta om demonen för att göra så att ändringarna få effekt?" + #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" msgstr "Startar om enhet…" @@ -718,9 +870,21 @@ msgstr "Startar om enhet…" msgid "Return all the hardware IDs for the machine" msgstr "Returnera alla hårdvaru-id för maskinen" +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "Kör uppstädningssammansättningsrutinen för insticksmodule när install-blob används" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "Kör förberedelsesammansättningsrutinen för insticksmodule när install-blob används" + msgid "Runtime" msgstr "Exekvering" +#. TRANSLATORS: command line option +msgid "Save device state into a JSON file between executions" +msgstr "Spara enhetstillstånd i en JSON-fil mellan körningar" + #. TRANSLATORS: command line option msgid "Schedule installation for next reboot when possible" msgstr "Schemalägg om möjligt installationen till nästa uppstart" @@ -769,6 +933,13 @@ msgstr "Ange tillverkar-ID för den fasta programvarufilen" msgid "Sets metadata on a firmware file" msgstr "Ange metadata på fast programvarufil" +msgid "Sets the list of approved firmware" +msgstr "Sätter listan av godkända fasta programvaror" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware." +msgstr "Sätter listan över godkänd fast programvara." + #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Dela fast programvaruhistorik med utvecklarna" @@ -777,6 +948,14 @@ msgstr "Dela fast programvaruhistorik med utvecklarna" msgid "Show client and daemon versions" msgstr "Visa klient- och demon-version" +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Visa utförlig information från demonen för en specifikt domän" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "Visa felsökningsinformation för alla domäner" + #. TRANSLATORS: for the --verbose arg msgid "Show debugging options" msgstr "Visa felsökningsalternativ" @@ -805,6 +984,24 @@ msgstr "Visa felsökningsloggen från det senaste uppdateringsförsöket" msgid "Show the information of firmware update status" msgstr "Visa information om uppdateringsstatus för fast programvara" +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Stäng ner nu?" + +msgid "Sign data using the client certificate" +msgstr "Signera data med klientcertifikatet" + +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Signera data med klientcertifikatet" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Signera den uppladdade datan med klientcertifikatet" + +msgid "Signature" +msgstr "Signatur" + msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Ange Tillverkar-/Produkt-ID för DFU-enhet" @@ -821,7 +1018,7 @@ msgstr "Tillstånd" msgid "Status" msgstr "Status" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Sammanfattning" @@ -832,6 +1029,11 @@ msgstr "Mål" msgid "The LVFS is a free service that operates as an independent legal entity and has no connection with $OS_RELEASE:NAME$. Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices. All firmware is provided only by the original equipment manufacturer." msgstr "LVFS är en fri tjänst som fungerar som en oberoende juridisk person och har ingen koppling till $OS_RELEASE:NAME$. Din distributör kanske inte har bekräftat att någon av de fasta programvaruuppdateringarna är kompatibla med ditt system eller anslutna enheter. All fast programvara tillhandahålls endast av den ursprungliga tillverkaren av utrustning." +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Det finns ingen godkänd fast programvara." + #. TRANSLATORS: we're poking around as a power user msgid "This program may only work correctly as root" msgstr "Detta program kommer endast fungera korrekt som root" @@ -839,9 +1041,9 @@ msgstr "Detta program kommer endast fungera korrekt som root" msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." msgstr "Denna fjärrkälla innehåller fast programvara som inte är under embargo, men fortfarande testas av hårdvarutillverkare. Du bör säkerställa att du har ett sätt att manuellt nedgradera den fasta programvaran om uppdateringen misslyckas." -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Titel" +#. TRANSLATORS: the user needs to stop playing with stuff +msgid "This tool can only be used by the root user" +msgstr "Detta verktyg kan endast användas av administratöranvändaren" #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" @@ -855,10 +1057,6 @@ msgstr "Typ" msgid "UEFI Firmware Utility" msgstr "Verktyg för fast UEFI-programvara" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Okänd" @@ -874,38 +1072,14 @@ msgstr "Låser upp enheten för fast programvaruåtkomst" msgid "Unset the debugging flag during update" msgstr "Ta bort felsökningsflaggan under uppdatering" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Uppdateringskontrollsumma" +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Demonversion %s stöds inte, klientversionen är %s" -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Uppdateringsbeskrivning" - -#. TRANSLATORS: section header for the amount -#. * of time it takes to install the update -msgid "Update Duration" -msgstr "Uppdateringslängd" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Uppdateringsplats" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Uppdatera namn" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "Uppdatera fjärrkällans id" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Uppdateringssammanfattning" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Uppdateringsversion" +#. TRANSLATORS: command description +msgid "Update all devices that match local metadata" +msgstr "Uppdatera alla enheter som matchar lokal metadata" #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" @@ -922,6 +1096,10 @@ msgstr "Uppdatera den lagrade enhetens verifikationsinformation" msgid "Update the stored metadata with current ROM contents" msgstr "Uppdatera den lagrade metadatan med aktuellt ROM-innehåll" +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Uppdatera lagrad metadata med aktuellt innehåll" + #. TRANSLATORS: command description msgid "Updates all firmware to latest versions available" msgstr "Uppdaterar all fast programvara till de senast tillgängliga versionerna" @@ -958,7 +1136,7 @@ msgstr "Användarnamn" msgid "Verifying…" msgstr "Verifierar…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Version" diff --git a/po/tr.po b/po/tr.po index 9a3cd5703..e052bbfaf 100644 --- a/po/tr.po +++ b/po/tr.po @@ -96,10 +96,6 @@ msgstr "Ürün Yazılımı Yardımcı Programı" msgid "Found" msgstr "Bulundu" -msgid "GUID" -msgstr "GUID" - -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "Kimlik (ID)" @@ -135,11 +131,9 @@ msgstr "Kip" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "İsim" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Ürün yazılımı güncelleme yeteneğine sahip donanım saptanamadı" @@ -200,22 +194,6 @@ msgstr "Bilinmiyor" msgid "Unlock the device to allow access" msgstr "Erişime izin vermek için cihazın kilidini açın" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Güncelleme Sağlaması" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Güncelleme Açıklaması" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Güncelleme Konumu" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Güncelleme Sürümü" - #. TRANSLATORS: verifying we wrote the firmware correctly msgid "Verifying…" msgstr "Doğrulanıyor…" diff --git a/po/uk.po b/po/uk.po index eb6b18c01..073fc1638 100644 --- a/po/uk.po +++ b/po/uk.po @@ -67,6 +67,13 @@ msgstr "Оновлення ME %s" msgid "%s System Update" msgstr "Оновлення системи %s" +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "Оновлення контролера Thunderbolt %s" + #. TRANSLATORS: this is the fallback where we don't know if the release #. * is updating the system, the device, or a device class, or something else #. -- @@ -75,11 +82,6 @@ msgstr "Оновлення системи %s" msgid "%s Update" msgstr "Оновлення для %s" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s має такі оновлення мікропрограми:" - #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -250,6 +252,10 @@ msgstr "Щоб отримати доступ до оновлення мікро msgid "Authentication is required to update the stored checksums for the device" msgstr "Щоб отримати доступ до оновлення збережених контрольних сум для пристрою, вам слід пройти розпізнавання" +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Версія завантажувача" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Зібрати мікропрограму за допомогою пісочниці" @@ -267,7 +273,6 @@ msgstr "Скасовано" msgid "Changed" msgstr "Змінено" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "Контрольна сума" @@ -308,6 +313,10 @@ msgstr "Перетворити мікропрограму у формат DFU" msgid "Create a binary patch using two files" msgstr "Створити бінарну латку на основі двох файлів" +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Поточна версія" + msgid "DFU" msgstr "DFU" @@ -327,7 +336,7 @@ msgstr "Розпаковування…" msgid "Decrypt firmware data" msgstr "Розшифрувати дані мікропрограми" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "Опис" @@ -339,6 +348,10 @@ msgstr "Від’єднати поточний з’єднаний пристр msgid "Detach to bootloader mode" msgstr "Від'єднатися до режиму завантажувача" +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Подробиці" + #. TRANSLATORS: ID for hardware, typically a SHA1 sum msgid "Device ID" msgstr "Ід. пристрою" @@ -386,6 +399,10 @@ msgstr "Не перевіряти, чи слід перезавантажува msgid "Do not check for unreported history" msgstr "Не перевіряти, чи є ненадіслані звіти у журналі" +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Не включати префікс часової позначки" + #. TRANSLATORS: command line option msgid "Do not write to the history database" msgstr "Не записувати дані до бази даних журналу" @@ -426,6 +443,10 @@ msgstr "Створити дамп даних щодо файла мікропр msgid "Dump information about a binary patch to the screen" msgstr "Вивести дамп інформації щодо бінарної латки на екран" +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Тривалість" + #. TRANSLATORS: ESP is EFI System Partition msgid "ESP specified was not valid" msgstr "Вказаний ESP є некоректним" @@ -500,6 +521,10 @@ msgstr "Не вдалося завантажити коригування" msgid "Failed to parse arguments" msgstr "Не вдалося обробити аргументи" +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Не вдалося обробити прапорці до --filter" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" msgstr "Не вдалося перезавантажити" @@ -532,6 +557,10 @@ msgstr "Назва файла" msgid "Filename Signature" msgstr "Підпис назви файла" +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Фільтрувати за набором прапорців пристроїв за допомогою префікса виключення ~, наприклад «internal,~needs-reboot»" + #. TRANSLATORS: program name msgid "Firmware Agent" msgstr "Агент мікропрограм" @@ -567,7 +596,8 @@ msgstr "На цьому комп'ютері не передбачено підт msgid "Firmware updates are supported on this machine." msgstr "На цьому комп'ютері передбачено підтримку оновлень мікропрограми." -#. TRANSLATORS: section header for firmware flags +#. TRANSLATORS: device properties +#. TRANSLATORS: release properties msgid "Flags" msgstr "Прапорці" @@ -578,12 +608,9 @@ msgstr "Виконати дію примусово, ігноруючи усі п msgid "Found" msgstr "Знайдено" -msgid "GUID" -msgstr "GUID" - #. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "Отримати список усіх пристроїв за топологією системи" +msgid "Get all device flags supported by fwupd" +msgstr "Отримати усі прапорці пристроїв, підтримку яких передбачено у fwupd" #. TRANSLATORS: command description msgid "Get all devices and possible releases" @@ -625,7 +652,6 @@ msgstr "Отримує випуски для пристрою" msgid "Gets the results from the last update" msgstr "Отримує результати з останнього оновлення" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "Ід." @@ -633,6 +659,14 @@ msgstr "Ід." msgid "Idle…" msgstr "Бездіяльність…" +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ігнорувати результати строгої перевірки SSL під час отримання файлів" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Тривалість встановлення" + #. TRANSLATORS: command description msgid "Install a firmware blob on a device" msgstr "Встановити бінарну мікропрограму на пристрій" @@ -676,6 +710,10 @@ msgstr "Сховище ключів" msgid "Less than one minute remaining" msgstr "Лишилося менше за хвилину" +#. TRANSLATORS: e.g. GPLv2+, Non free etc +msgid "License" +msgstr "Ліцензування" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Служба надання мікропрограм для Linux від виробника (стабільна мікропрограма)" @@ -694,6 +732,10 @@ msgstr "Показати список підтримуваних оновлен msgid "Loading…" msgstr "Завантаження…" +#. TRANSLATORS: command line option +msgid "Log output to FILE (typically for scripting use)" +msgstr "Записати виведені дані до файла ФАЙЛ (типово, для використання у скриптах)" + #. TRANSLATORS: command line option msgid "Manually whitelist specific plugins" msgstr "Вручну додати певні додатки до «білого» списку" @@ -703,17 +745,21 @@ msgid "Merge multiple firmware files into one" msgstr "Об’єднати декілька файлів мікропрограм у один" #. TRANSLATORS: remote URI -msgid "Metadata URI" -msgstr "Адреса метаданих" +msgid "Metadata Signature" +msgstr "Підпис метаданих" #. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "Підпис адреси метаданих" +msgid "Metadata URI" +msgstr "Адреса метаданих" #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Метадані можна отримати від служби надання мікропрограм для Linux від виробника." +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Мінімальна версія" + #. TRANSLATORS: error message #, c-format msgid "Mismatched daemon and client, use %s instead" @@ -742,14 +788,12 @@ msgstr "Стежити за подіями у фоновій службі" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "Назва" msgid "No action specified!" msgstr "Не вказано дії!" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "Не виявлено обладнання із передбаченою можливістю оновлення мікропрограми" @@ -758,10 +802,18 @@ msgstr "Не виявлено обладнання із передбаченою msgid "No plugins found" msgstr "Додатків не знайдено" +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Немає доступних випусків" + #. TRANSLATORS: explain why no metadata available msgid "No remotes are currently enabled so no metadata is available." msgstr "Зараз не увімкнено жодного віддаленого сховища, отже, метадані недоступні." +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Немає доступних віддалених сховищ" + #. TRANSLATORS: nothing was updated offline msgid "No updates were applied" msgstr "Не застосовано жодного оновлення" @@ -857,10 +909,7 @@ msgstr "Регіон" msgid "Reinstalling %s with %s... " msgstr "Повторно встановлюємо %s з номером версії %s... " -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "Віддалений пристрій" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "Віддалений ідентифікатор" @@ -928,6 +977,10 @@ msgstr "Плануємо…" msgid "Serial" msgstr "Порядкове" +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Серійний номер" + #. TRANSLATORS: command description msgid "Set alternative name on firmware file" msgstr "Встановити альтернативну назву для файла мікропрограми" @@ -1033,6 +1086,14 @@ msgstr "Підписати вивантажені дані клієнтськи msgid "Signature" msgstr "Підпис" +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Розмір" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Джерело" + msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Вказати ідентифікатори виробника/продукту пристрою DFU" @@ -1049,7 +1110,7 @@ msgstr "Стан" msgid "Status" msgstr "Стан" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Резюме" @@ -1076,10 +1137,6 @@ msgstr "У цьому сховищі міститься мікропрограм msgid "This tool can only be used by the root user" msgstr "Цим інструментом може користуватися лише root" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "Заголовок" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "Об’єм перенесеного" @@ -1092,14 +1149,14 @@ msgstr "Тип" msgid "UEFI Firmware Utility" msgstr "Засіб роботи із мікропрограмами UEFI" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "Адреса" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Невідомий" +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Невідомий пристрій" + msgid "Unlock the device to allow access" msgstr "Розблокування пристрою для отримання доступу" @@ -1116,38 +1173,18 @@ msgstr "Зняти під час оновлення прапорець діаг msgid "Unsupported daemon version %s, client version is %s" msgstr "Непідтримувана версія фонової служби %s, версія клієнта — %s" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "Контрольна сума оновлення" +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Помилка оновлення" -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "Опис оновлення" +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Повідомлення щодо оновлення" -#. TRANSLATORS: section header for the amount -#. * of time it takes to install the update -msgid "Update Duration" -msgstr "Тривалість оновлення" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "Місце оновлення" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "Назва оновлення" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "Оновити віддалений ідентифікатор" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "Резюме оновлення" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "Версія оновлення" +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Стан оновлення" #. TRANSLATORS: command description msgid "Update all devices that match local metadata" @@ -1200,18 +1237,30 @@ msgstr "Вивантажити звіт зараз?" msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." msgstr "Вивантаження звітів щодо мікропрограм допомагає виробникам обладнання швидко виявляти проблеми та дізнаватися про успішне оновлення на реальних пристроях." +#. TRANSLATORS: command line option +msgid "Use quirk flags when installing firmware" +msgstr "Використовувати варіативні прапорці при встановленні мікропрограми" + #. TRANSLATORS: remote filename base msgid "Username" msgstr "Користувач" +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Виробник" + #. TRANSLATORS: verifying we wrote the firmware correctly msgid "Verifying…" msgstr "Перевіряємо…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "Версія" +#. TRANSLATORS: try to help +msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "ПОПЕРЕДЖЕННЯ: ігноруємо строгі перевірки SSL. Щоб зробити ігнорування у майбутньому автоматичним, експортуйте до вашого середовища змінну DISABLE_SSL_STRICT" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Очікуємо…" diff --git a/po/zh_CN.po b/po/zh_CN.po index b28cec81f..56427c75d 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -76,11 +76,6 @@ msgstr "%s 系统更新" msgid "%s Update" msgstr "%s 更新" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s 有固件更新:" - #. TRANSLATORS: duration in days! #, c-format msgid "%u day" @@ -253,7 +248,6 @@ msgstr "已取消" msgid "Changed" msgstr "已变更" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "校验和" @@ -313,7 +307,7 @@ msgstr "正在解压缩…" msgid "Decrypt firmware data" msgstr "解密固件数据" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "描述" @@ -550,7 +544,8 @@ msgstr "此机器不支持固件更新。" msgid "Firmware updates are supported on this machine." msgstr "此机器支持固件更新。" -#. TRANSLATORS: section header for firmware flags +#. TRANSLATORS: device properties +#. TRANSLATORS: release properties msgid "Flags" msgstr "标志" @@ -561,13 +556,6 @@ msgstr "强制操作忽略所有警告" msgid "Found" msgstr "找到" -msgid "GUID" -msgstr "GUID" - -#. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "根据系统拓扑获取所有设备" - #. TRANSLATORS: command description msgid "Get all devices and possible releases" msgstr "获取所有设备和可用版本" @@ -608,7 +596,6 @@ msgstr "获取用于设备的发行版本" msgid "Gets the results from the last update" msgstr "从最后一次更新中获取结果" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -689,10 +676,6 @@ msgstr "将多个固件文件合并为一个" msgid "Metadata URI" msgstr "元数据 URI" -#. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "元数据 URI 签名" - #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "可从 Linux 供应商固件服务获取元数据。" @@ -725,14 +708,12 @@ msgstr "监视守护程序里的事件" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "名称" msgid "No action specified!" msgstr "未指定操作!" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "没有检测到支持更新固件的硬件" @@ -840,10 +821,7 @@ msgstr "区域" msgid "Reinstalling %s with %s... " msgstr "正在重新安装 %s,使用 %s…" -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "远程" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "远程 ID" @@ -1032,7 +1010,7 @@ msgstr "状态" msgid "Status" msgstr "状况" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "概览" @@ -1059,10 +1037,6 @@ msgstr "此远程源包含未禁止的固件,但其仍经过硬件供应商测 msgid "This tool can only be used by the root user" msgstr "此工具只能由 root 用户使用" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "标题" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "传输大小" @@ -1075,10 +1049,6 @@ msgstr "类型" msgid "UEFI Firmware Utility" msgstr "固件 UEFI 实用工具" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "未知" @@ -1099,39 +1069,6 @@ msgstr "更新期间取消设定调试标志" msgid "Unsupported daemon version %s, client version is %s" msgstr "未受支持的守护进程版本 %s,客户端版本为 %s" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "更新校验和" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "更新说明" - -#. TRANSLATORS: section header for the amount -#. * of time it takes to install the update -msgid "Update Duration" -msgstr "更新时长" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "更新位置" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "更新名称" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "更新远程 ID" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "更新概览" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "更新版本" - #. TRANSLATORS: command description msgid "Update all devices that match local metadata" msgstr "更新所有符合本地元数据的设备" @@ -1191,7 +1128,7 @@ msgstr "用户名" msgid "Verifying…" msgstr "正在验证…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "版本" diff --git a/po/zh_TW.po b/po/zh_TW.po index ea0bad6d4..aae422c28 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -21,11 +21,6 @@ msgid "%.0f minute remaining" msgid_plural "%.0f minutes remaining" msgstr[0] "剩下 %.0f 分鐘" -#. TRANSLATORS: first replacement is device name -#, c-format -msgid "%s has firmware updates:" -msgstr "%s 有韌體更新:" - #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "已加入" @@ -129,7 +124,6 @@ msgstr "已取消" msgid "Changed" msgstr "已變更" -#. TRANSLATORS: section header for firmware checksum #. TRANSLATORS: remote checksum msgid "Checksum" msgstr "校驗計算碼" @@ -189,7 +183,7 @@ msgstr "解壓縮中…" msgid "Decrypt firmware data" msgstr "解密韌體資料" -#. TRANSLATORS: section header for firmware description +#. TRANSLATORS: multiline description of device msgid "Description" msgstr "描述說明" @@ -397,13 +391,6 @@ msgstr "此機器有韌體更新支援。" msgid "Found" msgstr "找到" -msgid "GUID" -msgstr "GUID" - -#. TRANSLATORS: command description -msgid "Get all devices according to the system topology" -msgstr "取得根據系統拓樸而得的所有裝置" - #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" msgstr "取得所有支援韌體更新的裝置" @@ -436,7 +423,6 @@ msgstr "取得裝置的發行版本" msgid "Gets the results from the last update" msgstr "取得上次更新的結果" -#. TRANSLATORS: Appstream ID for the hardware type msgid "ID" msgstr "ID" @@ -513,10 +499,6 @@ msgstr "合併多份韌體檔案為一份" msgid "Metadata URI" msgstr "中介資料 URI" -#. TRANSLATORS: remote URI -msgid "Metadata URI Signature" -msgstr "中介資料 URI 簽章" - #. TRANSLATORS: explain why no metadata available msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "中介資料可從 Linux 廠商韌體服務取得。" @@ -537,14 +519,12 @@ msgstr "監控幕後程式是否有活動" #. TRANSLATORS: interface name, e.g. "Flash" #. TRANSLATORS: device name, e.g. 'ColorHug2' -#. TRANSLATORS: section header for the release name msgid "Name" msgstr "名稱" msgid "No action specified!" msgstr "未指定動作!" -#. TRANSLATORS: nothing attached #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" msgstr "未偵測到具備韌體更新能力的硬體" @@ -630,10 +610,7 @@ msgstr "地區" msgid "Reinstalling %s with %s... " msgstr "正將 %s 重新安裝為 %s 版... " -#. TRANSLATORS: section header for the remote the file is coming from -msgid "Remote" -msgstr "遠端" - +#. TRANSLATORS: the server the file is coming from #. TRANSLATORS: remote identifier, e.g. lvfs-testing msgid "Remote ID" msgstr "遠端 ID" @@ -767,7 +744,7 @@ msgstr "狀態" msgid "Status" msgstr "狀態" -#. TRANSLATORS: section header for the release one line summary +#. TRANSLATORS: one line summary of device msgid "Summary" msgstr "摘要" @@ -785,10 +762,6 @@ msgstr "此程式僅有 root 身份才能正常運作" msgid "This remote contains firmware which is not embargoed, but is still being tested by the hardware vendor. You should ensure you have a way to manually downgrade the firmware if the firmware update fails." msgstr "這個遠端站點包含未列入禁運,但仍處於硬體廠商測試階段的韌體。您應該確保自己在韌體更新失敗時有方法能夠手動降級韌體。" -#. TRANSLATORS: remote title, e.g. "Linux Vendor Firmware Service" -msgid "Title" -msgstr "標題" - #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" msgstr "傳輸大小" @@ -801,10 +774,6 @@ msgstr "類型" msgid "UEFI Firmware Utility" msgstr "UEFI 韌體公用程式" -#. TRANSLATORS: section header for firmware URI -msgid "URI" -msgstr "URI" - #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "未知" @@ -820,34 +789,6 @@ msgstr "解鎖裝置以供韌體存取" msgid "Unset the debugging flag during update" msgstr "取消更新期間的除錯旗標設定" -#. TRANSLATORS: section header for firmware checksum -msgid "Update Checksum" -msgstr "更新校驗計算碼" - -#. TRANSLATORS: section header for long firmware desc -msgid "Update Description" -msgstr "更新描述說明" - -#. TRANSLATORS: section header for firmware remote http:// -msgid "Update Location" -msgstr "更新位置" - -#. TRANSLATORS: section header for the release name -msgid "Update Name" -msgstr "更新名稱" - -#. TRANSLATORS: section header for remote ID, e.g. lvfs-testing -msgid "Update Remote ID" -msgstr "更新遠端 ID" - -#. TRANSLATORS: section header for the release one line summary -msgid "Update Summary" -msgstr "更新摘要" - -#. TRANSLATORS: section header for firmware version -msgid "Update Version" -msgstr "更新版本" - #. TRANSLATORS: the server sent the user a small message msgid "Update failure is a known issue, visit this URL for more information:" msgstr "更新失敗是已知議題,請造訪此 URL 瞭解更多資訊:" @@ -894,7 +835,7 @@ msgstr "使用者名稱" msgid "Verifying…" msgstr "核驗中…" -#. TRANSLATORS: section header for release version number +#. TRANSLATORS: version number of new firmware msgid "Version" msgstr "版本" From ff1d7efb9ed0382f4a3f263d45adbae1a5f5d630 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Fri, 13 Sep 2019 11:00:01 +0100 Subject: [PATCH 114/158] trivial: post release version bump --- RELEASE | 4 ++-- meson.build | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/RELEASE b/RELEASE index 795b30e86..a4831ddd7 100644 --- a/RELEASE +++ b/RELEASE @@ -2,7 +2,7 @@ fwupd Release Notes Write release entries: -git log --format="%s" --cherry-pick --right-only 1.2.11... | grep -i -v trivial | grep -v Merge | sort | uniq +git log --format="%s" --cherry-pick --right-only 1.3.1... | grep -i -v trivial | grep -v Merge | sort | uniq Add any user visible changes into ../data/org.freedesktop.fwupd.metainfo.xml appstream-util appdata-to-news ../data/org.freedesktop.fwupd.metainfo.xml > NEWS @@ -17,7 +17,7 @@ git add ../po/*.po 2. Commit changes to git: # MAKE SURE THIS IS CORRECT -export release_ver="1.3.1" +export release_ver="1.3.2" git commit -a -m "Release fwupd ${release_ver}" git tag -s -f -m "Release fwupd ${release_ver}" "${release_ver}" diff --git a/meson.build b/meson.build index 82dc9a0e6..b4ea56e2d 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('fwupd', 'c', - version : '1.3.1', + version : '1.3.2', license : 'LGPL-2.1+', meson_version : '>=0.47.0', default_options : ['warning_level=2', 'c_std=c99'], From ca8868e9fe2b2ed43db624ef6ead5462987d1908 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 16 Sep 2019 11:19:36 +0100 Subject: [PATCH 115/158] trivial: Add fu_io_channel_write_byte_array() for future use --- src/fu-io-channel.c | 70 ++++++++++++++++++++++++++++++++++++++++----- src/fu-io-channel.h | 10 +++++++ 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/src/fu-io-channel.c b/src/fu-io-channel.c index 0860d6d80..2fa83c92e 100644 --- a/src/fu-io-channel.c +++ b/src/fu-io-channel.c @@ -106,6 +106,30 @@ fu_io_channel_write_bytes (FuIOChannel *self, return fu_io_channel_write_raw (self, buf, bufsz, timeout_ms, flags, error); } +/** + * fu_io_channel_write_byte_array: + * @self: a #FuIOChannel + * @buf: buffer to write + * @timeout_ms: timeout in ms + * @flags: some #FuIOChannelFlags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT + * @error: a #GError, or %NULL + * + * Writes bytes to the TTY, that will fail if exceeding @timeout_ms. + * + * Returns: %TRUE if all the bytes was written + * + * Since: 1.3.2 + **/ +gboolean +fu_io_channel_write_byte_array (FuIOChannel *self, + GByteArray *buf, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) +{ + return fu_io_channel_write_raw (self, buf->data, buf->len, timeout_ms, flags, error); +} + /** * fu_io_channel_write_raw: * @self: a #FuIOChannel @@ -202,6 +226,7 @@ fu_io_channel_write_raw (FuIOChannel *self, return TRUE; } + /** * fu_io_channel_read_bytes: * @self: a #FuIOChannel @@ -222,12 +247,43 @@ fu_io_channel_read_bytes (FuIOChannel *self, guint timeout_ms, FuIOChannelFlags flags, GError **error) +{ + GByteArray *buf = fu_io_channel_read_byte_array (self, + max_size, + timeout_ms, + flags, + error); + if (buf == NULL) + return NULL; + return g_byte_array_free_to_bytes (buf); +} + +/** + * fu_io_channel_read_byte_array: + * @self: a #FuIOChannel + * @max_size: maximum size of the returned blob, or -1 for no limit + * @timeout_ms: timeout in ms + * @flags: some #FuIOChannelFlags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT + * @error: a #GError, or %NULL + * + * Reads bytes from the TTY, that will fail if exceeding @timeout_ms. + * + * Returns: a #GByteArray, or %NULL for error + * + * Since: 1.3.2 + **/ +GByteArray * +fu_io_channel_read_byte_array (FuIOChannel *self, + gssize max_size, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) { GPollFD fds = { .fd = self->fd, .events = G_IO_IN | G_IO_PRI | G_IO_ERR, }; - g_autoptr(GString) str = g_string_new (NULL); + g_autoptr(GByteArray) buf2 = g_byte_array_new (); g_return_val_if_fail (FU_IS_IO_CHANNEL (self), NULL); @@ -244,8 +300,8 @@ fu_io_channel_read_bytes (FuIOChannel *self, return NULL; } if (len > 0) - g_string_append_len (str, (gchar *) buf, len); - return g_bytes_new (str->str, str->len); + g_byte_array_append (buf2, buf, len); + return g_steal_pointer (&buf2); } /* nonblocking IO */ @@ -286,10 +342,10 @@ fu_io_channel_read_bytes (FuIOChannel *self, return NULL; } if (len > 0) - g_string_append_len (str, (gchar *) buf, len); + g_byte_array_append (buf2, buf, len); /* check maximum size */ - if (max_size > 0 && str->len >= (guint) max_size) + if (max_size > 0 && buf2->len >= (guint) max_size) break; if (flags & FU_IO_CHANNEL_FLAG_SINGLE_SHOT) break; @@ -319,7 +375,7 @@ fu_io_channel_read_bytes (FuIOChannel *self, } /* no data */ - if (str->len == 0) { + if (buf2->len == 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_READ, @@ -329,7 +385,7 @@ fu_io_channel_read_bytes (FuIOChannel *self, } /* return blob */ - return g_bytes_new (str->str, str->len); + return g_steal_pointer (&buf2); } /** diff --git a/src/fu-io-channel.h b/src/fu-io-channel.h index 7c5122b47..bd5d91ee7 100644 --- a/src/fu-io-channel.h +++ b/src/fu-io-channel.h @@ -57,10 +57,20 @@ gboolean fu_io_channel_write_bytes (FuIOChannel *self, guint timeout_ms, FuIOChannelFlags flags, GError **error); +gboolean fu_io_channel_write_byte_array (FuIOChannel *self, + GByteArray *buf, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error); GBytes *fu_io_channel_read_bytes (FuIOChannel *self, gssize max_size, guint timeout_ms, FuIOChannelFlags flags, GError **error); +GByteArray *fu_io_channel_read_byte_array (FuIOChannel *self, + gssize max_size, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error); G_END_DECLS From 41a25be6f4b371c367904284e9251cd461ad5cfe Mon Sep 17 00:00:00 2001 From: worldofpeace Date: Sun, 15 Sep 2019 23:07:02 -0400 Subject: [PATCH 116/158] Move D-Bus conf file to datadir/dbus-1/system.d Since D-Bus 1.9.18 configuration files installed by third-party should go in share/dbus-1/system.d. The old location is for sysadmin overrides. --- data/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/meson.build b/data/meson.build index 814c77cd3..25db95097 100644 --- a/data/meson.build +++ b/data/meson.build @@ -27,7 +27,7 @@ install_data(['org.freedesktop.fwupd.svg'], if build_daemon install_data(['org.freedesktop.fwupd.conf'], - install_dir : join_paths(sysconfdir, 'dbus-1', 'system.d') + install_dir : join_paths(datadir, 'dbus-1', 'system.d') ) install_data(['90-fwupd-devices.rules'], install_dir : join_paths(udevdir, 'rules.d') From 180111c495ad531583390d251cb4b6253e5fc29a Mon Sep 17 00:00:00 2001 From: worldofpeace Date: Mon, 16 Sep 2019 09:50:37 -0400 Subject: [PATCH 117/158] trivial: Update DBus conf path for example Fedora package --- contrib/fwupd.spec.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index dbd7affb7..dccadf9bd 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -265,7 +265,7 @@ rm ${RPM_BUILD_ROOT}%{_sbindir}/flashrom %config(noreplace)%{_sysconfdir}/fwupd/remotes.d/vendor-directory.conf %config(noreplace)%{_sysconfdir}/pki/fwupd %{_sysconfdir}/pki/fwupd-metadata -%{_sysconfdir}/dbus-1/system.d/org.freedesktop.fwupd.conf +%{_datadir}/dbus-1/system.d/org.freedesktop.fwupd.conf %{_datadir}/bash-completion/completions/fwupdmgr %{_datadir}/bash-completion/completions/fwupdtool %{_datadir}/bash-completion/completions/fwupdagent From 8b98b1cbd9d96479510faca3aed5bfdeff16066b Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 16 Sep 2019 16:34:14 +0100 Subject: [PATCH 118/158] Do not crash when starting tpm2-abrmd The value G_LOG_LEVEL_MASK includes debugging, which tpm2-abrmd is certainly allowed to use. Fixes https://github.com/fwupd/fwupd/issues/1353 --- src/fu-debug.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fu-debug.c b/src/fu-debug.c index c9c073306..fb4df3e38 100644 --- a/src/fu-debug.c +++ b/src/fu-debug.c @@ -166,7 +166,6 @@ fu_debug_post_parse_hook (GOptionContext *context, } /* redirect all domains to be able to change FWUPD_VERBOSE at runtime */ - g_log_set_fatal_mask (NULL, G_LOG_LEVEL_MASK); g_log_set_default_handler (fu_debug_handler_cb, self); /* are we on an actual TTY? */ From 89def5ea51c176de2f1535fd63d5833a2516fd74 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 17 Sep 2019 13:02:10 +0100 Subject: [PATCH 119/158] Include all GUIDs when uploading a report The LVFS already knows how to deal with arrays of strings, so this is a forwards and backwards compatible change. Fixes https://github.com/fwupd/fwupd/issues/1363 --- libfwupd/fwupd-common.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libfwupd/fwupd-common.c b/libfwupd/fwupd-common.c index c6c3d4758..8bd5fff38 100644 --- a/libfwupd/fwupd-common.c +++ b/libfwupd/fwupd-common.c @@ -356,6 +356,7 @@ fwupd_build_history_report_json_device (JsonBuilder *builder, FwupdDevice *dev) { FwupdRelease *rel = fwupd_device_get_release_default (dev); GPtrArray *checksums; + GPtrArray *guids; /* identify the firmware used */ json_builder_set_member_name (builder, "Checksum"); @@ -393,8 +394,16 @@ fwupd_build_history_report_json_device (JsonBuilder *builder, FwupdDevice *dev) } /* map back to the dev type on the LVFS */ - json_builder_set_member_name (builder, "Guid"); - json_builder_add_string_value (builder, fwupd_device_get_guid_default (dev)); + guids = fwupd_device_get_guids (dev); + if (guids->len > 0) { + json_builder_set_member_name (builder, "Guid"); + json_builder_begin_array (builder); + for (guint i = 0; i < guids->len; i++) { + const gchar *guid = g_ptr_array_index (guids, i); + json_builder_add_string_value (builder, guid); + } + json_builder_end_array (builder); + } json_builder_set_member_name (builder, "Plugin"); json_builder_add_string_value (builder, fwupd_device_get_plugin (dev)); From 0ad59cbc03522d068e4c6888a8d4e83283930938 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 16 Sep 2019 10:33:05 +0100 Subject: [PATCH 120/158] Support in AppStream metadata When the LVFS switches over to outputting rather than appending to the update description we need to be in a position to display the new data. --- libfwupd/fwupd-enums-private.h | 1 + libfwupd/fwupd-release.c | 71 ++++++++++++++++++++++++++++++++++ libfwupd/fwupd-release.h | 3 ++ libfwupd/fwupd.map | 7 ++++ src/fu-engine.c | 8 ++++ src/fu-util-common.c | 10 +++++ 6 files changed, 100 insertions(+) diff --git a/libfwupd/fwupd-enums-private.h b/libfwupd/fwupd-enums-private.h index cf7580276..16257aa5f 100644 --- a/libfwupd/fwupd-enums-private.h +++ b/libfwupd/fwupd-enums-private.h @@ -17,6 +17,7 @@ G_BEGIN_DECLS #define FWUPD_RESULT_KEY_FILENAME "Filename" /* s */ #define FWUPD_RESULT_KEY_PROTOCOL "Protocol" /* s */ #define FWUPD_RESULT_KEY_CATEGORIES "Categories" /* as */ +#define FWUPD_RESULT_KEY_ISSUES "Issues" /* as */ #define FWUPD_RESULT_KEY_FLAGS "Flags" /* t */ #define FWUPD_RESULT_KEY_FLASHES_LEFT "FlashesLeft" /* u */ #define FWUPD_RESULT_KEY_INSTALL_DURATION "InstallDuration" /* u */ diff --git a/libfwupd/fwupd-release.c b/libfwupd/fwupd-release.c index 92e7ab4ec..ef7408872 100644 --- a/libfwupd/fwupd-release.c +++ b/libfwupd/fwupd-release.c @@ -31,6 +31,7 @@ static void fwupd_release_finalize (GObject *object); typedef struct { GPtrArray *checksums; GPtrArray *categories; + GPtrArray *issues; GHashTable *metadata; gchar *description; gchar *filename; @@ -239,6 +240,47 @@ fwupd_release_set_protocol (FwupdRelease *release, const gchar *protocol) priv->protocol = g_strdup (protocol); } +/** + * fwupd_release_get_issues: + * @release: A #FwupdRelease + * + * Gets the list of issues fixed in this release. + * + * Returns: (element-type utf8) (transfer none): the issues, which may be empty + * + * Since: 1.3.2 + **/ +GPtrArray * +fwupd_release_get_issues (FwupdRelease *release) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_val_if_fail (FWUPD_IS_RELEASE (release), NULL); + return priv->issues; +} + +/** + * fwupd_release_add_issue: + * @release: A #FwupdRelease + * @issue: the update issue, e.g. `CVE-2019-12345` + * + * Adds an resolved issue to this release. + * + * Since: 1.3.2 + **/ +void +fwupd_release_add_issue (FwupdRelease *release, const gchar *issue) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_if_fail (FWUPD_IS_RELEASE (release)); + g_return_if_fail (issue != NULL); + for (guint i = 0; i < priv->issues->len; i++) { + const gchar *issue_tmp = g_ptr_array_index (priv->issues, i); + if (g_strcmp0 (issue_tmp, issue) == 0) + return; + } + g_ptr_array_add (priv->issues, g_strdup (issue)); +} + /** * fwupd_release_get_categories: * @release: A #FwupdRelease @@ -1112,6 +1154,14 @@ fwupd_release_to_variant (FwupdRelease *release) FWUPD_RESULT_KEY_CATEGORIES, g_variant_new_strv (strv, -1)); } + if (priv->issues->len > 0) { + g_autofree const gchar **strv = g_new0 (const gchar *, priv->issues->len + 1); + for (guint i = 0; i < priv->issues->len; i++) + strv[i] = (const gchar *) g_ptr_array_index (priv->issues, i); + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_ISSUES, + g_variant_new_strv (strv, -1)); + } if (priv->checksums->len > 0) { g_autoptr(GString) str = g_string_new (""); for (guint i = 0; i < priv->checksums->len; i++) { @@ -1218,6 +1268,12 @@ fwupd_release_from_key_value (FwupdRelease *release, const gchar *key, GVariant fwupd_release_add_category (release, strv[i]); return; } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_ISSUES) == 0) { + g_autofree const gchar **strv = g_variant_get_strv (value, NULL); + for (guint i = 0; strv[i] != NULL; i++) + fwupd_release_add_issue (release, strv[i]); + return; + } if (g_strcmp0 (key, FWUPD_RESULT_KEY_CHECKSUM) == 0) { const gchar *checksums = g_variant_get_string (value, NULL); g_auto(GStrv) split = g_strsplit (checksums, ",", -1); @@ -1374,6 +1430,15 @@ fwupd_release_to_json (FwupdRelease *release, JsonBuilder *builder) } json_builder_end_array (builder); } + if (priv->issues->len > 0) { + json_builder_set_member_name (builder, FWUPD_RESULT_KEY_ISSUES); + json_builder_begin_array (builder); + for (guint i = 0; i < priv->issues->len; i++) { + const gchar *tmp = g_ptr_array_index (priv->issues, i); + json_builder_add_string_value (builder, tmp); + } + json_builder_end_array (builder); + } if (priv->checksums->len > 0) { json_builder_set_member_name (builder, FWUPD_RESULT_KEY_CHECKSUM); json_builder_begin_array (builder); @@ -1445,6 +1510,10 @@ fwupd_release_to_string (FwupdRelease *release) const gchar *tmp = g_ptr_array_index (priv->categories, i); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_CATEGORIES, tmp); } + for (guint i = 0; i < priv->issues->len; i++) { + const gchar *tmp = g_ptr_array_index (priv->issues, i); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_ISSUES, tmp); + } for (guint i = 0; i < priv->checksums->len; i++) { const gchar *checksum = g_ptr_array_index (priv->checksums, i); g_autofree gchar *checksum_display = fwupd_checksum_format_for_display (checksum); @@ -1484,6 +1553,7 @@ fwupd_release_init (FwupdRelease *release) { FwupdReleasePrivate *priv = GET_PRIVATE (release); priv->categories = g_ptr_array_new_with_free_func (g_free); + priv->issues = g_ptr_array_new_with_free_func (g_free); priv->checksums = g_ptr_array_new_with_free_func (g_free); priv->metadata = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); } @@ -1510,6 +1580,7 @@ fwupd_release_finalize (GObject *object) g_free (priv->remote_id); g_free (priv->update_message); g_ptr_array_unref (priv->categories); + g_ptr_array_unref (priv->issues); g_ptr_array_unref (priv->checksums); g_hash_table_unref (priv->metadata); diff --git a/libfwupd/fwupd-release.h b/libfwupd/fwupd-release.h index 09d071921..afddc8f05 100644 --- a/libfwupd/fwupd-release.h +++ b/libfwupd/fwupd-release.h @@ -37,6 +37,9 @@ void fwupd_release_set_version (FwupdRelease *release, const gchar *fwupd_release_get_uri (FwupdRelease *release); void fwupd_release_set_uri (FwupdRelease *release, const gchar *uri); +GPtrArray *fwupd_release_get_issues (FwupdRelease *release); +void fwupd_release_add_issue (FwupdRelease *release, + const gchar *issue); GPtrArray *fwupd_release_get_categories (FwupdRelease *release); void fwupd_release_add_category (FwupdRelease *release, const gchar *category); diff --git a/libfwupd/fwupd.map b/libfwupd/fwupd.map index 2a497d3c1..2d7a402f9 100644 --- a/libfwupd/fwupd.map +++ b/libfwupd/fwupd.map @@ -379,3 +379,10 @@ LIBFWUPD_1.3.1 { fwupd_remote_set_remotes_dir; local: *; } LIBFWUPD_1.2.10; + +LIBFWUPD_1.3.2 { + global: + fwupd_release_add_issue; + fwupd_release_get_issues; + local: *; +} LIBFWUPD_1.3.1; diff --git a/src/fu-engine.c b/src/fu-engine.c index 49c75ce5b..a1b94dcfa 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -327,6 +327,7 @@ fu_engine_set_release_from_appstream (FuEngine *self, guint64 tmp64; g_autofree gchar *version_rel = NULL; g_autoptr(GPtrArray) cats = NULL; + g_autoptr(GPtrArray) issues = NULL; g_autoptr(XbNode) description = NULL; /* set from the component */ @@ -419,6 +420,13 @@ fu_engine_set_release_from_appstream (FuEngine *self, fwupd_release_add_category (rel, xb_node_get_text (n)); } } + issues = xb_node_query (component, "issues/issue", 0, NULL); + if (issues != NULL) { + for (guint i = 0; i < issues->len; i++) { + XbNode *n = g_ptr_array_index (issues, i); + fwupd_release_add_issue (rel, xb_node_get_text (n)); + } + } tmp = xb_node_query_text (component, "custom/value[@key='LVFS::UpdateProtocol']", NULL); if (tmp != NULL) fwupd_release_set_protocol (rel, tmp); diff --git a/src/fu-util-common.c b/src/fu-util-common.c index addf30b1b..dafe204d2 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -1034,6 +1034,7 @@ fu_util_device_to_string (FwupdDevice *dev, guint idt) gchar * fu_util_release_to_string (FwupdRelease *rel, guint idt) { + GPtrArray *issues = fwupd_release_get_issues (rel); GString *str = g_string_new (NULL); guint64 flags = fwupd_release_get_flags (rel); g_autoptr(GString) flags_str = g_string_new (NULL); @@ -1113,6 +1114,15 @@ fu_util_release_to_string (FwupdRelease *rel, guint idt) /* TRANSLATORS: multiline description of device */ fu_common_string_append_kv (str, idt + 1, _("Description"), desc); } + for (guint i = 0; i < issues->len; i++) { + const gchar *issue = g_ptr_array_index (issues, i); + if (i == 0) { + /* TRANSLATORS: issue fixed with the release, e.g. CVE */ + fu_common_string_append_kv (str, idt + 1, ngettext ("Issue", "Issues", issues->len), issue); + } else { + fu_common_string_append_kv (str, idt + 1, "", issue); + } + } return g_string_free (str, FALSE); } From 5206592347b52a2cc6b5f0357416e26c9898e178 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 17 Sep 2019 15:18:19 -0500 Subject: [PATCH 121/158] trivial: uefi: Make the TCTI output less verbose --- plugins/uefi/fu-uefi-pcrs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/uefi/fu-uefi-pcrs.c b/plugins/uefi/fu-uefi-pcrs.c index 2b030e22c..3a465da23 100644 --- a/plugins/uefi/fu-uefi-pcrs.c +++ b/plugins/uefi/fu-uefi-pcrs.c @@ -112,7 +112,7 @@ fu_uefi_pcrs_setup_tpm20 (FuUefiPcrs *self, GError **error) /* suppress warning messages about missing TCTI libraries for tpm2-tss <2.3 */ if (g_getenv ("FWUPD_UEFI_VERBOSE") == NULL) { - g_setenv ("TSS2_LOG", "esys+error", FALSE); + g_setenv ("TSS2_LOG", "esys+error,tcti+none", FALSE); } rc = Esys_Initialize (&ctx, NULL, NULL); From 78987fb3945c306e16f73b79bb7f2264ebfc08c6 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 17 Sep 2019 15:22:32 -0500 Subject: [PATCH 122/158] uefi: make the TPM optional for runtime again (Fixes: #1362) It's intentional to use a different error message for library failure so that we can tell if there is a regression in CI related to TPM usage. --- plugins/uefi/fu-uefi-device.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/uefi/fu-uefi-device.c b/plugins/uefi/fu-uefi-device.c index 21c6d272c..98b188ad4 100644 --- a/plugins/uefi/fu-uefi-device.c +++ b/plugins/uefi/fu-uefi-device.c @@ -475,7 +475,10 @@ fu_uefi_device_add_system_checksum (FuDevice *device, GError **error) if (!fu_uefi_pcrs_setup (pcrs, &error_local)) { if (g_error_matches (error_local, G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED)) { + G_IO_ERROR_NOT_SUPPORTED) || + g_error_matches (error_local, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND)) { g_debug ("%s", error_local->message); return TRUE; } From aeb466de42a7794d84bd675e2dcc47d830d90a65 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 18 Sep 2019 11:32:16 +0100 Subject: [PATCH 123/158] Call the set_quirk_kv() vfunc for the superclassed object Although, we have to use the converted instance IDs rather than the GUIDs as fu_device_setup() has not typically been called at this point. --- src/fu-device.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/fu-device.c b/src/fu-device.c index 9cf4ac830..5f0e205a2 100644 --- a/src/fu-device.c +++ b/src/fu-device.c @@ -2278,6 +2278,7 @@ fu_device_incorporate (FuDevice *self, FuDevice *donor) FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); FuDevicePrivate *priv = GET_PRIVATE (self); FuDevicePrivate *priv_donor = GET_PRIVATE (donor); + GPtrArray *instance_ids = fu_device_get_instance_ids (donor); GPtrArray *parent_guids = fu_device_get_parent_guids (donor); g_autoptr(GList) metadata_keys = NULL; @@ -2316,6 +2317,13 @@ fu_device_incorporate (FuDevice *self, FuDevice *donor) /* optional subclass */ if (klass->incorporate != NULL) klass->incorporate (self, donor); + + /* call the set_quirk_kv() vfunc for the superclassed object */ + for (guint i = 0; i < instance_ids->len; i++) { + const gchar *instance_id = g_ptr_array_index (instance_ids, i); + g_autofree gchar *guid = fwupd_guid_hash_string (instance_id); + fu_device_add_guid_quirks (self, guid); + } } /** From 38245ff194bc801787a87ecc3290dadf2c709fc8 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 18 Sep 2019 14:46:09 +0100 Subject: [PATCH 124/158] Add fu_common_bytes_compare_raw() for future usage --- src/fu-common.c | 54 +++++++++++++++++++++++++++++++++++-------------- src/fu-common.h | 5 +++++ 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/fu-common.c b/src/fu-common.c index e1e16e787..bcf106598 100644 --- a/src/fu-common.c +++ b/src/fu-common.c @@ -1340,30 +1340,27 @@ fu_common_bytes_is_empty (GBytes *bytes) } /** - * fu_common_bytes_compare: - * @bytes1: a #GBytes - * @bytes2: another #GBytes + * fu_common_bytes_compare_raw: + * @buf1: a buffer + * @bufsz1: sizeof @buf1 + * @buf2: another buffer + * @bufsz2: sizeof @buf2 * @error: A #GError or %NULL * - * Checks if a byte array are just empty (0xff) bytes. + * Compares the buffers for equality. * - * Return value: %TRUE if @bytes1 and @bytes2 are identical + * Return value: %TRUE if @buf1 and @buf2 are identical **/ gboolean -fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error) +fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1, + const guint8 *buf2, gsize bufsz2, + GError **error) { - const guint8 *buf1; - const guint8 *buf2; - gsize bufsz1; - gsize bufsz2; - - g_return_val_if_fail (bytes1 != NULL, FALSE); - g_return_val_if_fail (bytes2 != NULL, FALSE); + g_return_val_if_fail (buf1 != NULL, FALSE); + g_return_val_if_fail (buf2 != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); /* not the same length */ - buf1 = g_bytes_get_data (bytes1, &bufsz1); - buf2 = g_bytes_get_data (bytes2, &bufsz2); if (bufsz1 != bufsz2) { g_set_error (error, G_IO_ERROR, @@ -1389,6 +1386,33 @@ fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error) return TRUE; } +/** + * fu_common_bytes_compare: + * @bytes1: a #GBytes + * @bytes2: another #GBytes + * @error: A #GError or %NULL + * + * Compares the buffers for equality. + * + * Return value: %TRUE if @bytes1 and @bytes2 are identical + **/ +gboolean +fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error) +{ + const guint8 *buf1; + const guint8 *buf2; + gsize bufsz1; + gsize bufsz2; + + g_return_val_if_fail (bytes1 != NULL, FALSE); + g_return_val_if_fail (bytes2 != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + buf1 = g_bytes_get_data (bytes1, &bufsz1); + buf2 = g_bytes_get_data (bytes2, &bufsz2); + return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error); +} + /** * fu_common_bytes_pad: * @bytes: a #GBytes diff --git a/src/fu-common.h b/src/fu-common.h index e980df194..9cf13dfc9 100644 --- a/src/fu-common.h +++ b/src/fu-common.h @@ -135,6 +135,11 @@ gboolean fu_common_bytes_is_empty (GBytes *bytes); gboolean fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error); +gboolean fu_common_bytes_compare_raw (const guint8 *buf1, + gsize bufsz1, + const guint8 *buf2, + gsize bufsz2, + GError **error); GBytes *fu_common_bytes_pad (GBytes *bytes, gsize sz); gboolean fu_memcpy_safe (guint8 *dst, From 33cb526248c8410c79fedc03cb01c75c85e927c2 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 18 Sep 2019 11:30:54 +0100 Subject: [PATCH 125/158] Print the instance IDs if the GUIDs are unconverted This caused some head scratching about the 'missing' values when doing fu_device_to_string() before fu_device_convert_instance_ids() had been called. --- libfwupd/fwupd-device.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/libfwupd/fwupd-device.c b/libfwupd/fwupd-device.c index 98c1a8913..33c4e9e0e 100644 --- a/libfwupd/fwupd-device.c +++ b/libfwupd/fwupd-device.c @@ -1789,7 +1789,6 @@ fwupd_device_to_string (FwupdDevice *device) { FwupdDevicePrivate *priv = GET_PRIVATE (device); GString *str; - g_autoptr(GHashTable) ids = NULL; g_return_val_if_fail (FWUPD_IS_DEVICE (device), NULL); @@ -1800,22 +1799,30 @@ fwupd_device_to_string (FwupdDevice *device) str = g_string_append (str, "Unknown Device\n"); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_DEVICE_ID, priv->id); fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_PARENT_DEVICE_ID, priv->parent_id); - ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - for (guint i = 0; i < priv->instance_ids->len; i++) { - const gchar *instance_id = g_ptr_array_index (priv->instance_ids, i); - g_hash_table_insert (ids, - fwupd_guid_hash_string (instance_id), - g_strdup (instance_id)); - } - for (guint i = 0; i < priv->guids->len; i++) { - const gchar *guid = g_ptr_array_index (priv->guids, i); - const gchar *instance_id = g_hash_table_lookup (ids, guid); - if (instance_id == NULL) { - fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_GUID, guid); - } else { - g_autofree gchar *tmp = NULL; - tmp = g_strdup_printf ("%s <- %s", guid, instance_id); - fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_GUID, tmp); + if (priv->guids->len > 0) { + g_autoptr(GHashTable) ids = NULL; + ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + for (guint i = 0; i < priv->instance_ids->len; i++) { + const gchar *instance_id = g_ptr_array_index (priv->instance_ids, i); + g_hash_table_insert (ids, + fwupd_guid_hash_string (instance_id), + g_strdup (instance_id)); + } + for (guint i = 0; i < priv->guids->len; i++) { + const gchar *guid = g_ptr_array_index (priv->guids, i); + const gchar *instance_id = g_hash_table_lookup (ids, guid); + if (instance_id == NULL) { + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_GUID, guid); + } else { + g_autofree gchar *tmp = NULL; + tmp = g_strdup_printf ("%s <- %s", guid, instance_id); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_GUID, tmp); + } + } + } else { + for (guint i = 0; i < priv->instance_ids->len; i++) { + const gchar *instance_id = g_ptr_array_index (priv->instance_ids, i); + fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_INSTANCE_IDS, instance_id); } } fwupd_pad_kv_str (str, FWUPD_RESULT_KEY_SERIAL, priv->serial); From 3901fa6bae22d31d253f1261a2fa37cbc44bd966 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 17 Sep 2019 13:41:19 +0100 Subject: [PATCH 126/158] Add some more debugging to the SREC and IHEX parsers Additionally, change the reported line to be indexed from 1 (rather than 0) as most text editors show line 1 as the first line. --- src/fu-ihex-firmware.c | 34 +++++++++++---------- src/fu-srec-firmware.c | 67 +++++++++++++++++++++++------------------- 2 files changed, 55 insertions(+), 46 deletions(-) diff --git a/src/fu-ihex-firmware.c b/src/fu-ihex-firmware.c index 17df546f6..fdae350da 100644 --- a/src/fu-ihex-firmware.c +++ b/src/fu-ihex-firmware.c @@ -66,7 +66,7 @@ fu_ihex_firmware_parse (FuFirmware *firmware, g_auto(GStrv) lines = NULL; g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (NULL); g_autoptr(GBytes) img_bytes = NULL; - g_autoptr(GString) buf = g_string_new (NULL); + g_autoptr(GByteArray) buf = g_byte_array_new (); g_autoptr(GByteArray) buf_signature = g_byte_array_new (); g_return_val_if_fail (fw != NULL, FALSE); @@ -163,9 +163,10 @@ fu_ihex_firmware_parse (FuFirmware *firmware, g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "invalid address 0x%x, last was 0x%x", + "invalid address 0x%x, last was 0x%x on line %u", (guint) addr, - (guint) addr_last); + (guint) addr_last, + ln + 1); return FALSE; } @@ -179,22 +180,23 @@ fu_ihex_firmware_parse (FuFirmware *firmware, g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "hole of 0x%x bytes too large to fill", - (guint) len_hole); + "hole of 0x%x bytes too large to fill on line %u", + (guint) len_hole, + ln + 1); return FALSE; } if (addr_last > 0x0 && len_hole > 1) { - g_debug ("filling address 0x%08x to 0x%08x", - addr_last + 1, addr_last + len_hole - 1); + g_debug ("filling address 0x%08x to 0x%08x on line %u", + addr_last + 1, addr_last + len_hole - 1, ln + 1); for (guint j = 1; j < len_hole; j++) { /* although 0xff might be clearer, * we can't write 0xffff to pic14 */ - g_string_append_c (buf, 0x00); + fu_byte_array_append_uint8 (buf, 0x00); } } /* write into buf */ data_tmp = fu_firmware_strparse_uint8 (line + i); - g_string_append_c (buf, (gchar) data_tmp); + fu_byte_array_append_uint8 (buf, (gchar) data_tmp); addr_last = addr++; } break; @@ -211,21 +213,21 @@ fu_ihex_firmware_parse (FuFirmware *firmware, break; case DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR: abs_addr = fu_firmware_strparse_uint16 (line + 9) << 16; - g_debug (" abs_addr:\t0x%02x", abs_addr); + g_debug (" abs_addr:\t0x%02x on line %u", abs_addr, ln + 1); break; case DFU_INHX32_RECORD_TYPE_START_LINEAR: abs_addr = fu_firmware_strparse_uint32 (line + 9); - g_debug (" abs_addr:\t0x%08x", abs_addr); + g_debug (" abs_addr:\t0x%08x on line %u", abs_addr, ln + 1); break; case DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT: /* segment base address, so ~1Mb addressable */ seg_addr = fu_firmware_strparse_uint16 (line + 9) * 16; - g_debug (" seg_addr:\t0x%08x", seg_addr); + g_debug (" seg_addr:\t0x%08x on line %u", seg_addr, ln + 1); break; case DFU_INHX32_RECORD_TYPE_START_SEGMENT: /* initial content of the CS:IP registers */ seg_addr = fu_firmware_strparse_uint32 (line + 9); - g_debug (" seg_addr:\t0x%02x", seg_addr); + g_debug (" seg_addr:\t0x%02x on line %u", seg_addr, ln + 1); break; case DFU_INHX32_RECORD_TYPE_SIGNATURE: for (guint i = 9; i < line_end; i += 2) { @@ -240,8 +242,8 @@ fu_ihex_firmware_parse (FuFirmware *firmware, g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "invalid ihex record type %i", - record_type); + "invalid ihex record type %i on line %u", + record_type, ln + 1); return FALSE; } } @@ -256,7 +258,7 @@ fu_ihex_firmware_parse (FuFirmware *firmware, } /* add single image */ - img_bytes = g_bytes_new (buf->str, buf->len); + img_bytes = g_bytes_new (buf->data, buf->len); fu_firmware_image_set_bytes (img, img_bytes); if (img_addr != G_MAXUINT32) fu_firmware_image_set_addr (img, img_addr); diff --git a/src/fu-srec-firmware.c b/src/fu-srec-firmware.c index 3d5b4754d..cd1c09ac2 100644 --- a/src/fu-srec-firmware.c +++ b/src/fu-srec-firmware.c @@ -38,7 +38,7 @@ fu_srec_firmware_parse (FuFirmware *firmware, g_auto(GStrv) lines = NULL; g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (NULL); g_autoptr(GBytes) img_bytes = NULL; - g_autoptr(GString) outbuf = g_string_new (NULL); + g_autoptr(GByteArray) outbuf = g_byte_array_new (); /* parse records */ data = g_bytes_get_data (fw, &sz); @@ -63,7 +63,7 @@ fu_srec_firmware_parse (FuFirmware *firmware, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "invalid starting token, got '%c' at line %u", - line[0], ln); + line[0], ln + 1); return FALSE; } @@ -73,7 +73,7 @@ fu_srec_firmware_parse (FuFirmware *firmware, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "record incomplete at line %u, length %u", - ln, (guint) linesz); + ln + 1, (guint) linesz); return FALSE; } @@ -86,7 +86,7 @@ fu_srec_firmware_parse (FuFirmware *firmware, FWUPD_ERROR_INVALID_FILE, "count incomplete at line %u, " "length %u, expected %u", - ln, (guint) linesz - 4, (guint) rec_count * 2); + ln + 1, (guint) linesz - 4, (guint) rec_count * 2); return FALSE; } @@ -104,7 +104,7 @@ fu_srec_firmware_parse (FuFirmware *firmware, FWUPD_ERROR_INVALID_FILE, "checksum incorrect line %u, " "expected %02x, got %02x", - ln, rec_csum_expected, rec_csum); + ln + 1, rec_csum_expected, rec_csum); return FALSE; } } @@ -114,10 +114,11 @@ fu_srec_firmware_parse (FuFirmware *firmware, case 0: addrsz = 2; if (got_hdr) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "duplicate header record"); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "duplicate header record at line %u", + ln + 1); return FALSE; } got_hdr = TRUE; @@ -154,8 +155,8 @@ fu_srec_firmware_parse (FuFirmware *firmware, g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "invalid srec record type S%c", - line[1]); + "invalid srec record type S%c at line %u", + line[1], ln + 1); return FALSE; } @@ -174,6 +175,10 @@ fu_srec_firmware_parse (FuFirmware *firmware, g_assert_not_reached (); } + g_debug ("line %03u S%u addr:0x%04x datalen:0x%02x", + ln + 1, rec_kind, rec_addr32, + (guint) rec_count - addrsz - 1); + /* header */ if (rec_kind == 0) { g_autoptr(GString) modname = g_string_new (NULL); @@ -181,8 +186,8 @@ fu_srec_firmware_parse (FuFirmware *firmware, g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "invalid header record address, got %04x", - rec_addr32); + "invalid header record address, got %04x at line %u", + rec_addr32, ln + 1); return FALSE; } @@ -204,8 +209,8 @@ fu_srec_firmware_parse (FuFirmware *firmware, g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "count record was not valid, got 0x%02x expected 0x%02x", - (guint) rec_addr32, (guint) data_cnt); + "count record was not valid, got 0x%02x expected 0x%02x at line %u", + (guint) rec_addr32, (guint) data_cnt, ln + 1); return FALSE; } } @@ -214,10 +219,11 @@ fu_srec_firmware_parse (FuFirmware *firmware, if (rec_kind == 1 || rec_kind == 2 || rec_kind == 3) { /* invalid */ if (!got_hdr) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "missing header record"); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "missing header record at line %u", + ln + 1); return FALSE; } /* does not make sense */ @@ -225,14 +231,15 @@ fu_srec_firmware_parse (FuFirmware *firmware, g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "invalid address 0x%x, last was 0x%x", + "invalid address 0x%x, last was 0x%x at line %u", (guint) rec_addr32, - (guint) addr32_last); + (guint) addr32_last, + ln + 1); return FALSE; } if (rec_addr32 < addr_start) { - g_debug ("ignoring data at 0x%x as before start address 0x%x", - (guint) rec_addr32, (guint) addr_start); + g_debug ("ignoring data at 0x%x as before start address 0x%x at line %u", + (guint) rec_addr32, (guint) addr_start, ln + 1); } else { guint bytecnt = 0; guint32 len_hole = rec_addr32 - addr32_last; @@ -242,21 +249,21 @@ fu_srec_firmware_parse (FuFirmware *firmware, g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "hole of 0x%x bytes too large to fill", - (guint) len_hole); + "hole of 0x%x bytes too large to fill at line %u", + (guint) len_hole, ln + 1); return FALSE; } if (addr32_last > 0x0 && len_hole > 1) { - g_debug ("filling address 0x%08x to 0x%08x", - addr32_last + 1, addr32_last + len_hole - 1); + g_debug ("filling address 0x%08x to 0x%08x at line %u", + addr32_last + 1, addr32_last + len_hole - 1, ln + 1); for (guint j = 0; j < len_hole; j++) - g_string_append_c (outbuf, 0xff); + fu_byte_array_append_uint8 (outbuf, 0xff); } /* add data */ for (guint8 i = 4 + (addrsz * 2); i <= rec_count * 2; i += 2) { guint8 tmp = fu_firmware_strparse_uint8 (line + i); - g_string_append_c (outbuf, tmp); + fu_byte_array_append_uint8 (outbuf, tmp); bytecnt++; } if (img_address == 0x0) @@ -277,7 +284,7 @@ fu_srec_firmware_parse (FuFirmware *firmware, } /* add single image */ - img_bytes = g_bytes_new (outbuf->str, outbuf->len); + img_bytes = g_bytes_new (outbuf->data, outbuf->len); fu_firmware_image_set_bytes (img, img_bytes); fu_firmware_image_set_addr (img, img_address); fu_firmware_add_image (firmware, img); From 01a7958cd1e5546ec87af4c4c449b994eadf7320 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 19 Sep 2019 08:30:09 -0500 Subject: [PATCH 127/158] uefi, dell-esrt: Show firmware version when capsule updates unavailable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the system doesn't support UEFI capsule updates no firmware version is displayed for the BIOS. Fix this by creating a dummy device: ``` ├─System Firmware: │ Device ID: 123fd4143619569d8ddb6ea47d1d3911eb5ef07a │ Current version: 1.7.0 │ Vendor: Dell Inc. │ Update Error: UEFI Capsule updates not available or enabled │ Flags: internal|require-ac|registered|needs-reboot ``` If the dell-esrt plugin determines that capsule updates can be enabled however, make the device it creates replace the dummy device: ``` ├─Dell UEFI updates: │ Device ID: 123fd4143619569d8ddb6ea47d1d3911eb5ef07a │ Summary: Enable UEFI Update Functionality │ Current version: 0 │ Update Error: Firmware updates disabled; run 'fwupdmgr unlock' to enable │ Flags: locked|supported|registered|needs-reboot ``` Fixes: #1366 --- plugins/dell-esrt/fu-plugin-dell-esrt.c | 4 +++- plugins/uefi/fu-plugin-uefi.c | 17 ++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/plugins/dell-esrt/fu-plugin-dell-esrt.c b/plugins/dell-esrt/fu-plugin-dell-esrt.c index ce694a7a8..bd8042024 100644 --- a/plugins/dell-esrt/fu-plugin-dell-esrt.c +++ b/plugins/dell-esrt/fu-plugin-dell-esrt.c @@ -87,6 +87,7 @@ void fu_plugin_init (FuPlugin *plugin) { fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_BETTER_THAN, "uefi"); } gboolean @@ -159,9 +160,10 @@ fu_plugin_coldplug (FuPlugin *plugin, GError **error) g_autoptr(FuDevice) dev = fu_device_new (); /* create a dummy device so we can unlock the feature */ - fu_device_set_id (dev, "UEFI-dummy-dev0"); + fu_device_set_id (dev, "UEFI-dummy"); fu_device_set_name (dev, "Dell UEFI updates"); fu_device_set_summary (dev, "Enable UEFI Update Functionality"); + fu_device_add_instance_id (dev, "main-system-firmware"); fu_device_add_guid (dev, "2d47f29b-83a2-4f31-a2e8-63474f4d4c2e"); fu_device_set_version (dev, "0", FWUPD_VERSION_FORMAT_NUMBER); fu_device_add_icon (dev, "computer"); diff --git a/plugins/uefi/fu-plugin-uefi.c b/plugins/uefi/fu-plugin-uefi.c index 96b476755..726fea806 100644 --- a/plugins/uefi/fu-plugin-uefi.c +++ b/plugins/uefi/fu-plugin-uefi.c @@ -800,7 +800,7 @@ fu_plugin_unlock (FuPlugin *plugin, FuDevice *device, GError **error) } static gboolean -fu_plugin_uefi_create_dummy (FuPlugin *plugin, GError **error) +fu_plugin_uefi_create_dummy (FuPlugin *plugin, const gchar *reason, GError **error) { const gchar *key; g_autoptr(FuDevice) dev = fu_device_new (); @@ -813,8 +813,7 @@ fu_plugin_uefi_create_dummy (FuPlugin *plugin, GError **error) key = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_BIOS_VERSION); if (key != NULL) fu_device_set_version (dev, key, FWUPD_VERSION_FORMAT_PLAIN); - key = "Firmware can not be updated in legacy mode, switch to UEFI mode."; - fu_device_set_update_error (dev, key); + fu_device_set_update_error (dev, reason); fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); @@ -847,16 +846,20 @@ fu_plugin_coldplug (FuPlugin *plugin, GError **error) /* are the EFI dirs set up so we can update each device */ if (!fu_uefi_vars_supported (&error_local)) { + const gchar *reason = "Firmware can not be updated in legacy mode, switch to UEFI mode"; g_warning ("%s", error_local->message); - return fu_plugin_uefi_create_dummy (plugin, error); + return fu_plugin_uefi_create_dummy (plugin, reason, error); } /* get the directory of ESRT entries */ sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); esrt_path = g_build_filename (sysfsfwdir, "efi", "esrt", NULL); - entries = fu_uefi_get_esrt_entry_paths (esrt_path, error); - if (entries == NULL) - return FALSE; + entries = fu_uefi_get_esrt_entry_paths (esrt_path, &error_local); + if (entries == NULL) { + const gchar *reason = "UEFI Capsule updates not available or enabled"; + g_warning ("%s", error_local->message); + return fu_plugin_uefi_create_dummy (plugin, reason, error); + } /* make sure that efivarfs is rw */ if (!fu_plugin_uefi_ensure_efivarfs_rw (&error_efivarfs)) From f32c3fdbc071103db0864aec9407a05e377d30ba Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 19 Sep 2019 10:47:22 -0500 Subject: [PATCH 128/158] trivial: wacom: Add support for second vendor ID Wacom indicates that some systems will use this alternative VID. --- plugins/wacom-raw/fu-plugin-wacom-raw.c | 11 +---------- plugins/wacom-raw/fu-wacom-common.h | 1 - plugins/wacom-raw/wacom-raw.quirk | 6 ++++++ 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/plugins/wacom-raw/fu-plugin-wacom-raw.c b/plugins/wacom-raw/fu-plugin-wacom-raw.c index db4de4c12..a07d60b60 100644 --- a/plugins/wacom-raw/fu-plugin-wacom-raw.c +++ b/plugins/wacom-raw/fu-plugin-wacom-raw.c @@ -47,10 +47,6 @@ fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **er if (g_strcmp0 (fu_udev_device_get_subsystem (device), "hidraw") != 0) return TRUE; - /* wacom */ - if (fu_udev_device_get_vendor (device) != FU_WACOM_DEVICE_VID) - return TRUE; - /* no actual device to open */ if (g_udev_device_get_device_file (fu_udev_device_get_dev (device)) == NULL) return TRUE; @@ -73,12 +69,7 @@ fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **er fu_plugin_device_add (plugin, FU_DEVICE (dev)); } - /* not supported */ - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Only EMR or AES devices are supported"); - return FALSE; + return TRUE; } gboolean diff --git a/plugins/wacom-raw/fu-wacom-common.h b/plugins/wacom-raw/fu-wacom-common.h index 62ac98a65..5e7150ad3 100644 --- a/plugins/wacom-raw/fu-wacom-common.h +++ b/plugins/wacom-raw/fu-wacom-common.h @@ -10,7 +10,6 @@ G_BEGIN_DECLS -#define FU_WACOM_DEVICE_VID 0x056A #define FU_WACOM_RAW_CMD_RETRIES 1000 #define FU_WACOM_RAW_STATUS_REPORT_ID 0x04 diff --git a/plugins/wacom-raw/wacom-raw.quirk b/plugins/wacom-raw/wacom-raw.quirk index 51ceee1a8..49c3677c6 100644 --- a/plugins/wacom-raw/wacom-raw.quirk +++ b/plugins/wacom-raw/wacom-raw.quirk @@ -13,6 +13,12 @@ Plugin = wacom_raw Guid = WacomAES Flags = is-bootloader +# AES bootloader mode +[DeviceInstanceId=HIDRAW\VEN_2D1F&DEV_0094] +Plugin = wacom_raw +Guid = WacomAES +Flags = is-bootloader + # EMR bootloader mode [DeviceInstanceId=HIDRAW\VEN_056A&DEV_012B] Plugin = wacom_raw From 6819f9e4612635312fe0f733b525bc1cc78415d5 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 19 Sep 2019 17:37:30 +0100 Subject: [PATCH 129/158] Allow parsing a raw SREC file At least two vendors are not using SREC according to the spec. They are using it like a recipe to write specific values at specific addresses. All the logic like line lengths and checksumming (and therefore all the various checks for malicious firmware) are still valid, so provide a new function to just tokenize the file and to not assign a padded image. --- src/fu-firmware.c | 36 +++++ src/fu-firmware.h | 10 +- src/fu-self-test.c | 33 +++++ src/fu-srec-firmware.c | 290 +++++++++++++++++++++++++---------------- src/fu-srec-firmware.h | 8 ++ 5 files changed, 264 insertions(+), 113 deletions(-) diff --git a/src/fu-firmware.c b/src/fu-firmware.c index 6179ef55c..2326d44fa 100644 --- a/src/fu-firmware.c +++ b/src/fu-firmware.c @@ -26,6 +26,38 @@ typedef struct { G_DEFINE_TYPE_WITH_PRIVATE (FuFirmware, fu_firmware, G_TYPE_OBJECT) #define GET_PRIVATE(o) (fu_firmware_get_instance_private (o)) +/** + * fu_firmware_tokenize: + * @self: A #FuFirmware + * @image: A #GBytes + * @flags: some #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @error: A #GError, or %NULL + * + * Tokenizes a firmware, typically breaking the firmware into records. + * + * Records can be enumerated using subclass-specific functionality, for example + * using fu_srec_firmware_get_records(). + * + * Returns: %TRUE for success + * + * Since: 1.3.2 + **/ +gboolean +fu_firmware_tokenize (FuFirmware *self, GBytes *fw, + FwupdInstallFlags flags, GError **error) +{ + FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS (self); + + g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE); + g_return_val_if_fail (fw != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* optionally subclassed */ + if (klass->tokenize != NULL) + return klass->tokenize (self, fw, flags, error); + return TRUE; +} + /** * fu_firmware_parse_full: * @self: A #FuFirmware @@ -57,6 +89,10 @@ fu_firmware_parse_full (FuFirmware *self, g_return_val_if_fail (error == NULL || *error == NULL, FALSE); /* subclassed */ + if (klass->tokenize != NULL) { + if (!klass->tokenize (self, fw, flags, error)) + return FALSE; + } if (klass->parse != NULL) return klass->parse (self, fw, addr_start, addr_end, flags, error); diff --git a/src/fu-firmware.h b/src/fu-firmware.h index 0e2821d34..df137d648 100644 --- a/src/fu-firmware.h +++ b/src/fu-firmware.h @@ -30,14 +30,22 @@ struct _FuFirmwareClass void (*to_string) (FuFirmware *self, guint indent, GString *str); + gboolean (*tokenize) (FuFirmware *self, + GBytes *fw, + FwupdInstallFlags flags, + GError **error); /*< private >*/ - gpointer padding[29]; + gpointer padding[28]; }; FuFirmware *fu_firmware_new (void); FuFirmware *fu_firmware_new_from_bytes (GBytes *fw); gchar *fu_firmware_to_string (FuFirmware *self); +gboolean fu_firmware_tokenize (FuFirmware *self, + GBytes *fw, + FwupdInstallFlags flags, + GError **error); gboolean fu_firmware_parse (FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 8d5fdb63d..475261d4e 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -3915,6 +3915,38 @@ fu_firmware_srec_func (void) g_assert_true (ret); } +static void +fu_firmware_srec_tokenization_func (void) +{ + FuSrecFirmwareRecord *rcd; + GPtrArray *records; + gboolean ret; + g_autoptr(FuFirmware) firmware = fu_srec_firmware_new (); + g_autoptr(GBytes) data_srec = NULL; + g_autoptr(GError) error = NULL; + const gchar *buf = "S3060000001400E5\r\n" + "S31000000002281102000000007F0304002C\r\n" + "S306000000145095\r\n" + "S70500000000FA\r\n"; + data_srec = g_bytes_new_static (buf, strlen (buf)); + g_assert_no_error (error); + g_assert (data_srec != NULL); + ret = fu_firmware_tokenize (firmware, data_srec, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error (error); + g_assert (ret); + + records = fu_srec_firmware_get_records (FU_SREC_FIRMWARE (firmware)); + g_assert_nonnull (records); + g_assert_cmpint (records->len, ==, 4); + rcd = g_ptr_array_index (records, 2); + g_assert_nonnull (rcd); + g_assert_cmpint (rcd->ln, ==, 0x3); + g_assert_cmpint (rcd->kind, ==, 3); + g_assert_cmpint (rcd->addr, ==, 0x14); + g_assert_cmpint (rcd->buf->len, ==, 0x1); + g_assert_cmpint (rcd->buf->data[0], ==, 0x50); +} + static void fu_memcpy_func (void) { @@ -4066,6 +4098,7 @@ main (int argc, char **argv) g_test_add_func ("/fwupd/firmware{ihex}", fu_firmware_ihex_func); g_test_add_func ("/fwupd/firmware{ihex-offset}", fu_firmware_ihex_offset_func); g_test_add_func ("/fwupd/firmware{ihex-signed}", fu_firmware_ihex_signed_func); + g_test_add_func ("/fwupd/firmware{srec-tokenization}", fu_firmware_srec_tokenization_func); g_test_add_func ("/fwupd/firmware{srec}", fu_firmware_srec_func); g_test_add_func ("/fwupd/archive{invalid}", fu_archive_invalid_func); g_test_add_func ("/fwupd/archive{cab}", fu_archive_cab_func); diff --git a/src/fu-srec-firmware.c b/src/fu-srec-firmware.c index cd1c09ac2..dce35e789 100644 --- a/src/fu-srec-firmware.c +++ b/src/fu-srec-firmware.c @@ -16,34 +16,64 @@ struct _FuSrecFirmware { FuFirmware parent_instance; + GPtrArray *records; }; G_DEFINE_TYPE (FuSrecFirmware, fu_srec_firmware, FU_TYPE_FIRMWARE) -static gboolean -fu_srec_firmware_parse (FuFirmware *firmware, - GBytes *fw, - guint64 addr_start, - guint64 addr_end, - FwupdInstallFlags flags, - GError **error) +/** + * fu_srec_firmware_get_records: + * @self: A #FuSrecFirmware + * + * Returns the raw records from SREC tokenization. + * + * This might be useful if the plugin is expecting the SREC file to be a list + * of operations, rather than a simple linear image with filled holes. + * + * Returns: (transfer none) (element-type FuSrecFirmwareRecord): records + * + * Since: 1.3.2 + **/ +GPtrArray * +fu_srec_firmware_get_records (FuSrecFirmware *self) { + g_return_val_if_fail (FU_IS_SREC_FIRMWARE (self), NULL); + return self->records; +} + +static void +fu_srec_firmware_record_free (FuSrecFirmwareRecord *rcd) +{ + g_byte_array_unref (rcd->buf); + g_free (rcd); +} + +static FuSrecFirmwareRecord * +fu_srec_firmware_record_new (guint ln, guint8 kind, guint32 addr) +{ + FuSrecFirmwareRecord *rcd = g_new0 (FuSrecFirmwareRecord, 1); + rcd->ln = ln; + rcd->kind = kind; + rcd->addr = addr; + rcd->buf = g_byte_array_new (); + return rcd; +} + +static gboolean +fu_srec_firmware_tokenize (FuFirmware *firmware, GBytes *fw, + FwupdInstallFlags flags, GError **error) +{ + FuSrecFirmware *self = FU_SREC_FIRMWARE (firmware); const gchar *data; gboolean got_eof = FALSE; - gboolean got_hdr = FALSE; gsize sz = 0; - guint16 data_cnt = 0; - guint32 addr32_last = 0; - guint32 img_address = 0; g_auto(GStrv) lines = NULL; - g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (NULL); - g_autoptr(GBytes) img_bytes = NULL; - g_autoptr(GByteArray) outbuf = g_byte_array_new (); /* parse records */ data = g_bytes_get_data (fw, &sz); lines = fu_common_strnsplit (data, sz, "\n", -1); for (guint ln = 0; lines[ln] != NULL; ln++) { + FuSrecFirmwareRecord *rcd; const gchar *line = lines[ln]; gsize linesz; guint32 rec_addr32; @@ -113,15 +143,6 @@ fu_srec_firmware_parse (FuFirmware *firmware, switch (rec_kind) { case 0: addrsz = 2; - if (got_hdr) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "duplicate header record at line %u", - ln + 1); - return FALSE; - } - got_hdr = TRUE; break; case 1: addrsz = 2; @@ -179,99 +200,15 @@ fu_srec_firmware_parse (FuFirmware *firmware, ln + 1, rec_kind, rec_addr32, (guint) rec_count - addrsz - 1); - /* header */ - if (rec_kind == 0) { - g_autoptr(GString) modname = g_string_new (NULL); - if (rec_addr32 != 0x0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "invalid header record address, got %04x at line %u", - rec_addr32, ln + 1); - return FALSE; - } - - /* could be anything, lets assume text */ + /* data */ + rcd = fu_srec_firmware_record_new (ln + 1, rec_kind, rec_addr32); + if (rec_kind == 1 || rec_kind == 2 || rec_kind == 3) { for (guint8 i = 4 + (addrsz * 2); i <= rec_count * 2; i += 2) { guint8 tmp = fu_firmware_strparse_uint8 (line + i); - if (!g_ascii_isgraph (tmp)) - break; - g_string_append_c (modname, tmp); - } - if (modname->len != 0) - fu_firmware_image_set_id (img, modname->str); - continue; - } - - /* verify we got all records */ - if (rec_kind == 5) { - if (rec_addr32 != data_cnt) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "count record was not valid, got 0x%02x expected 0x%02x at line %u", - (guint) rec_addr32, (guint) data_cnt, ln + 1); - return FALSE; + fu_byte_array_append_uint8 (rcd->buf, tmp); } } - - /* data */ - if (rec_kind == 1 || rec_kind == 2 || rec_kind == 3) { - /* invalid */ - if (!got_hdr) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "missing header record at line %u", - ln + 1); - return FALSE; - } - /* does not make sense */ - if (rec_addr32 < addr32_last) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "invalid address 0x%x, last was 0x%x at line %u", - (guint) rec_addr32, - (guint) addr32_last, - ln + 1); - return FALSE; - } - if (rec_addr32 < addr_start) { - g_debug ("ignoring data at 0x%x as before start address 0x%x at line %u", - (guint) rec_addr32, (guint) addr_start, ln + 1); - } else { - guint bytecnt = 0; - guint32 len_hole = rec_addr32 - addr32_last; - - /* fill any holes, but only up to 1Mb to avoid a DoS */ - if (addr32_last > 0 && len_hole > 0x100000) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "hole of 0x%x bytes too large to fill at line %u", - (guint) len_hole, ln + 1); - return FALSE; - } - if (addr32_last > 0x0 && len_hole > 1) { - g_debug ("filling address 0x%08x to 0x%08x at line %u", - addr32_last + 1, addr32_last + len_hole - 1, ln + 1); - for (guint j = 0; j < len_hole; j++) - fu_byte_array_append_uint8 (outbuf, 0xff); - } - - /* add data */ - for (guint8 i = 4 + (addrsz * 2); i <= rec_count * 2; i += 2) { - guint8 tmp = fu_firmware_strparse_uint8 (line + i); - fu_byte_array_append_uint8 (outbuf, tmp); - bytecnt++; - } - if (img_address == 0x0) - img_address = rec_addr32; - addr32_last = rec_addr32 + bytecnt; - } - data_cnt++; - } + g_ptr_array_add (self->records, rcd); } /* no EOF */ @@ -282,6 +219,124 @@ fu_srec_firmware_parse (FuFirmware *firmware, "no EOF, perhaps truncated file"); return FALSE; } + return TRUE; +} + +static gboolean +fu_srec_firmware_parse (FuFirmware *firmware, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error) +{ + FuSrecFirmware *self = FU_SREC_FIRMWARE (firmware); + gboolean got_hdr = FALSE; + guint16 data_cnt = 0; + guint32 addr32_last = 0; + guint32 img_address = 0; + g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (NULL); + g_autoptr(GBytes) img_bytes = NULL; + g_autoptr(GByteArray) outbuf = g_byte_array_new (); + + /* parse records */ + for (guint j = 0; j < self->records->len; j++) { + FuSrecFirmwareRecord *rcd = g_ptr_array_index (self->records, j); + + /* header */ + if (rcd->kind == 0) { + g_autoptr(GString) modname = g_string_new (NULL); + + /* check for duplicate */ + if (got_hdr) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "duplicate header record at line %u", + rcd->ln); + return FALSE; + } + + /* could be anything, lets assume text */ + for (guint8 i = 0; i < rcd->buf->len; i++) { + gchar tmp = rcd->buf->data[i]; + if (!g_ascii_isgraph (tmp)) + break; + g_string_append_c (modname, tmp); + } + if (modname->len != 0) + fu_firmware_image_set_id (img, modname->str); + got_hdr = TRUE; + continue; + } + + /* verify we got all records */ + if (rcd->kind == 5) { + if (rcd->addr != data_cnt) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "count record was not valid, got 0x%02x expected 0x%02x at line %u", + (guint) rcd->addr, (guint) data_cnt, rcd->ln); + return FALSE; + } + continue; + } + + /* data */ + if (rcd->kind == 1 || rcd->kind == 2 || rcd->kind == 3) { + /* invalid */ + if (!got_hdr) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "missing header record at line %u", + rcd->ln); + return FALSE; + } + + /* does not make sense */ + if (rcd->addr < addr32_last) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid address 0x%x, last was 0x%x at line %u", + (guint) rcd->addr, + (guint) addr32_last, + rcd->ln); + return FALSE; + } + if (rcd->addr < addr_start) { + g_debug ("ignoring data at 0x%x as before start address 0x%x at line %u", + (guint) rcd->addr, (guint) addr_start, rcd->ln); + } else { + guint32 len_hole = rcd->addr - addr32_last; + + /* fill any holes, but only up to 1Mb to avoid a DoS */ + if (addr32_last > 0 && len_hole > 0x100000) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "hole of 0x%x bytes too large to fill at line %u", + (guint) len_hole, rcd->ln); + return FALSE; + } + if (addr32_last > 0x0 && len_hole > 1) { + g_debug ("filling address 0x%08x to 0x%08x at line %u", + addr32_last + 1, addr32_last + len_hole - 1, rcd->ln); + for (guint i = 0; i < len_hole; i++) + fu_byte_array_append_uint8 (outbuf, 0xff); + } + + /* add data */ + g_byte_array_append (outbuf, rcd->buf->data, rcd->buf->len); + if (img_address == 0x0) + img_address = rcd->addr; + addr32_last = rcd->addr + rcd->buf->len; + } + data_cnt++; + } + } /* add single image */ img_bytes = g_bytes_new (outbuf->data, outbuf->len); @@ -291,16 +346,27 @@ fu_srec_firmware_parse (FuFirmware *firmware, return TRUE; } +static void +fu_srec_firmware_finalize (GObject *object) +{ + FuSrecFirmware *self = FU_SREC_FIRMWARE (object); + g_ptr_array_unref (self->records); +} + static void fu_srec_firmware_init (FuSrecFirmware *self) { + self->records = g_ptr_array_new_with_free_func ((GFreeFunc) fu_srec_firmware_record_free); } static void fu_srec_firmware_class_init (FuSrecFirmwareClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass); + object_class->finalize = fu_srec_firmware_finalize; klass_firmware->parse = fu_srec_firmware_parse; + klass_firmware->tokenize = fu_srec_firmware_tokenize; } FuFirmware * diff --git a/src/fu-srec-firmware.h b/src/fu-srec-firmware.h index 291548593..58127755c 100644 --- a/src/fu-srec-firmware.h +++ b/src/fu-srec-firmware.h @@ -13,6 +13,14 @@ G_BEGIN_DECLS #define FU_TYPE_SREC_FIRMWARE (fu_srec_firmware_get_type ()) G_DECLARE_FINAL_TYPE (FuSrecFirmware, fu_srec_firmware, FU, SREC_FIRMWARE, FuFirmware) +typedef struct { + guint ln; + guint8 kind; + guint32 addr; + GByteArray *buf; +} FuSrecFirmwareRecord; + FuFirmware *fu_srec_firmware_new (void); +GPtrArray *fu_srec_firmware_get_records (FuSrecFirmware *self); G_END_DECLS From e091427993122add0bd45349b957f2df0946b7c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Fri, 20 Sep 2019 10:04:43 +0100 Subject: [PATCH 130/158] trivial: fix wget command in fu-tool.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -o will redirect stdout to , -O what we want to be using, it saves the file to . Signed-off-by: Filipe Laíns --- src/fu-tool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fu-tool.c b/src/fu-tool.c index e4a670670..5ad19c93a 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -671,7 +671,7 @@ fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b) static gboolean fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error) { - const gchar *argv[][5] = { { "wget", uri, "-o", fn, NULL }, + const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL }, { "curl", uri, "--output", fn, NULL }, { NULL } }; for (guint i = 0; argv[i][0] != NULL; i++) { From 80eb7bef8bad16f2e8882c0e800b504399b34dfe Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Fri, 20 Sep 2019 18:23:49 +0100 Subject: [PATCH 131/158] trivial: Add some more defines to the srec parser This makes it more helpful when subclassing it. --- src/fu-srec-firmware.c | 30 ++++++++++++++++-------------- src/fu-srec-firmware.h | 29 +++++++++++++++++++++++------ 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/fu-srec-firmware.c b/src/fu-srec-firmware.c index dce35e789..901329892 100644 --- a/src/fu-srec-firmware.c +++ b/src/fu-srec-firmware.c @@ -48,8 +48,8 @@ fu_srec_firmware_record_free (FuSrecFirmwareRecord *rcd) g_free (rcd); } -static FuSrecFirmwareRecord * -fu_srec_firmware_record_new (guint ln, guint8 kind, guint32 addr) +FuSrecFirmwareRecord * +fu_srec_firmware_record_new (guint ln, FuFirmareSrecRecordKind kind, guint32 addr) { FuSrecFirmwareRecord *rcd = g_new0 (FuSrecFirmwareRecord, 1); rcd->ln = ln; @@ -141,34 +141,34 @@ fu_srec_firmware_tokenize (FuFirmware *firmware, GBytes *fw, /* set each command settings */ switch (rec_kind) { - case 0: + case FU_FIRMWARE_SREC_RECORD_KIND_S0_HEADER: addrsz = 2; break; - case 1: + case FU_FIRMWARE_SREC_RECORD_KIND_S1_DATA_16: addrsz = 2; break; - case 2: + case FU_FIRMWARE_SREC_RECORD_KIND_S2_DATA_24: addrsz = 3; break; - case 3: + case FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32: addrsz = 4; break; - case 5: + case FU_FIRMWARE_SREC_RECORD_KIND_S5_COUNT_16: addrsz = 2; got_eof = TRUE; break; - case 6: + case FU_FIRMWARE_SREC_RECORD_KIND_S6_COUNT_24: addrsz = 3; break; - case 7: + case FU_FIRMWARE_SREC_RECORD_KIND_S7_COUNT_32: addrsz = 4; got_eof = TRUE; break; - case 8: + case FU_FIRMWARE_SREC_RECORD_KIND_S8_TERMINATION_24: addrsz = 3; got_eof = TRUE; break; - case 9: + case FU_FIRMWARE_SREC_RECORD_KIND_S9_TERMINATION_16: addrsz = 2; got_eof = TRUE; break; @@ -244,7 +244,7 @@ fu_srec_firmware_parse (FuFirmware *firmware, FuSrecFirmwareRecord *rcd = g_ptr_array_index (self->records, j); /* header */ - if (rcd->kind == 0) { + if (rcd->kind == FU_FIRMWARE_SREC_RECORD_KIND_S0_HEADER) { g_autoptr(GString) modname = g_string_new (NULL); /* check for duplicate */ @@ -271,7 +271,7 @@ fu_srec_firmware_parse (FuFirmware *firmware, } /* verify we got all records */ - if (rcd->kind == 5) { + if (rcd->kind == FU_FIRMWARE_SREC_RECORD_KIND_S5_COUNT_16) { if (rcd->addr != data_cnt) { g_set_error (error, FWUPD_ERROR, @@ -284,7 +284,9 @@ fu_srec_firmware_parse (FuFirmware *firmware, } /* data */ - if (rcd->kind == 1 || rcd->kind == 2 || rcd->kind == 3) { + if (rcd->kind == FU_FIRMWARE_SREC_RECORD_KIND_S1_DATA_16 || + rcd->kind == FU_FIRMWARE_SREC_RECORD_KIND_S2_DATA_24 || + rcd->kind == FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32) { /* invalid */ if (!got_hdr) { g_set_error (error, diff --git a/src/fu-srec-firmware.h b/src/fu-srec-firmware.h index 58127755c..d4263769c 100644 --- a/src/fu-srec-firmware.h +++ b/src/fu-srec-firmware.h @@ -13,14 +13,31 @@ G_BEGIN_DECLS #define FU_TYPE_SREC_FIRMWARE (fu_srec_firmware_get_type ()) G_DECLARE_FINAL_TYPE (FuSrecFirmware, fu_srec_firmware, FU, SREC_FIRMWARE, FuFirmware) +typedef enum { + FU_FIRMWARE_SREC_RECORD_KIND_S0_HEADER, + FU_FIRMWARE_SREC_RECORD_KIND_S1_DATA_16, + FU_FIRMWARE_SREC_RECORD_KIND_S2_DATA_24, + FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32, + FU_FIRMWARE_SREC_RECORD_KIND_S4_RESERVED, + FU_FIRMWARE_SREC_RECORD_KIND_S5_COUNT_16, + FU_FIRMWARE_SREC_RECORD_KIND_S6_COUNT_24, + FU_FIRMWARE_SREC_RECORD_KIND_S7_COUNT_32, + FU_FIRMWARE_SREC_RECORD_KIND_S8_TERMINATION_24, + FU_FIRMWARE_SREC_RECORD_KIND_S9_TERMINATION_16, + FU_FIRMWARE_SREC_RECORD_KIND_LAST +} FuFirmareSrecRecordKind; + typedef struct { - guint ln; - guint8 kind; - guint32 addr; - GByteArray *buf; + guint ln; + FuFirmareSrecRecordKind kind; + guint32 addr; + GByteArray *buf; } FuSrecFirmwareRecord; -FuFirmware *fu_srec_firmware_new (void); -GPtrArray *fu_srec_firmware_get_records (FuSrecFirmware *self); +FuFirmware *fu_srec_firmware_new (void); +GPtrArray *fu_srec_firmware_get_records (FuSrecFirmware *self); +FuSrecFirmwareRecord *fu_srec_firmware_record_new (guint ln, + FuFirmareSrecRecordKind kind, + guint32 addr); G_END_DECLS From d9f52c75738939cc9a6edcf268cc139904523c98 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 19 Sep 2019 15:37:34 +0100 Subject: [PATCH 132/158] unifying: Ensure HID++ v2.0 peripheral devices get scanned by the plugin Since adding fu_plugin_add_rule(FU_PLUGIN_RULE_REQUIRES_QUIRK) all peripheral devices were being ignored. --- plugins/unifying/unifying.quirk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/unifying/unifying.quirk b/plugins/unifying/unifying.quirk index 2f6f94e96..fcffcfcb2 100644 --- a/plugins/unifying/unifying.quirk +++ b/plugins/unifying/unifying.quirk @@ -36,3 +36,7 @@ Flags = is-bootloader,is-texas FirmwareSizeMin = 0x4000 CounterpartGuid = HIDRAW\VEN_046D&DEV_C52B InstallDuration = 18 + +# Possible HID++ v2.0 peripheral device +[DeviceInstanceId=HIDRAW\VEN_046D] +Plugin = unifying From 63c17121944fe92e48c7bef00f7b3e27a886f833 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 19 Sep 2019 15:39:10 +0100 Subject: [PATCH 133/158] unifying: Use the instance ID to check for supported devices If we're using an instance ID with FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS (e.g. for the vendor match in the previous commit) then the GUID is not available. --- plugins/unifying/fu-plugin-unifying.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/unifying/fu-plugin-unifying.c b/plugins/unifying/fu-plugin-unifying.c index 14c20fb8d..9e7be1c8f 100644 --- a/plugins/unifying/fu-plugin-unifying.c +++ b/plugins/unifying/fu-plugin-unifying.c @@ -63,9 +63,10 @@ fu_plugin_update (FuPlugin *plugin, static gboolean fu_plugin_unifying_check_supported_device (FuPlugin *plugin, FuDevice *device) { - GPtrArray *guids = fu_device_get_guids (device); - for (guint i = 0; i < guids->len; i++) { - const gchar *guid = g_ptr_array_index (guids, i); + GPtrArray *instance_ids = fu_device_get_instance_ids (device); + for (guint i = 0; i < instance_ids->len; i++) { + const gchar *instance_id = g_ptr_array_index (instance_ids, i); + g_autofree gchar *guid = fwupd_guid_hash_string (instance_id); if (fu_plugin_check_supported (plugin, guid)) return TRUE; } From 9ec074332f553878337d94f41fdf0e45cc046e1f Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 20 Sep 2019 09:52:37 -0500 Subject: [PATCH 134/158] fu-history: Update device_modified in sql database during updates --- src/fu-history.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/fu-history.c b/src/fu-history.c index aa0097faf..b9aa6a03c 100644 --- a/src/fu-history.c +++ b/src/fu-history.c @@ -485,6 +485,7 @@ fu_history_modify_device (FuHistory *self, FuDevice *device, "update_state = ?1, " "update_error = ?2, " "checksum_device = ?6, " + "device_modified = ?7, " "flags = ?3 " "WHERE device_id = ?4;", -1, &stmt, NULL); @@ -498,6 +499,7 @@ fu_history_modify_device (FuHistory *self, FuDevice *device, "update_state = ?1, " "update_error = ?2, " "checksum_device = ?6, " + "device_modified = ?7, " "flags = ?3 " "WHERE device_id = ?4 AND version_old = ?5;", -1, &stmt, NULL); @@ -511,6 +513,7 @@ fu_history_modify_device (FuHistory *self, FuDevice *device, "update_state = ?1, " "update_error = ?2, " "checksum_device = ?6, " + "device_modified = ?7, " "flags = ?3 " "WHERE device_id = ?4 AND version_new = ?5;", -1, &stmt, NULL); @@ -531,6 +534,8 @@ fu_history_modify_device (FuHistory *self, FuDevice *device, sqlite3_bind_text (stmt, 5, fu_device_get_version (device), -1, SQLITE_STATIC); sqlite3_bind_text (stmt, 6, fwupd_checksum_get_by_kind (fu_device_get_checksums (device), G_CHECKSUM_SHA1), -1, SQLITE_STATIC); + sqlite3_bind_int64 (stmt, 7, fu_device_get_modified (device)); + return fu_history_stmt_exec (self, stmt, NULL, error); } From 3be596b907d5d34d63e9b5e8bf933b22ebb31902 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 20 Sep 2019 10:10:08 -0500 Subject: [PATCH 135/158] fu-util/fu-tool: Better display historical information MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * When firmware has been modified use the device key "Previous version" instead of "Current version". * Show release as a child node when looking at history * Don't output empty flags for release * Use the key "New version" for releases Sample output from `get-history` ``` ├Integrated Webcam™: │ │ Device ID: 08d460be0f1f9f128413f816022a6439e0078018 │ │ Previous version: 1.2.2 │ │ Update State: success │ │ Flags: updatable │ │ Last modified: 2019-09-20 14:53 │ │ GUID: b585990a-003e-5270-89d5-3705a17f9a43 │ │ │ └ New version: 1.2.3 │ └Integrated Webcam™: │ Device ID: 08d460be0f1f9f128413f816022a6439e0078018 │ Modified version: 1.2.2 │ Update State: success │ Flags: updatable │ Last modified: 2019-09-20 14:53 │ GUID: b585990a-003e-5270-89d5-3705a17f9a43 │ └ New version: 1.2.4 ``` Sample output from `get-updates` ``` Integrated Webcam™: │ Device ID: 08d460be0f1f9f128413f816022a6439e0078018 │ Summary: A fake webcam │ Current version: 1.2.2 │ Previous Version: 1.2.0 │ Bootloader Version: 0.1.2 │ Vendor: ACME Corp. (USB:0x046D) │ Flags: updatable|supported|registered │ GUID: b585990a-003e-5270-89d5-3705a17f9a43 │ ├FakeDevice Firmware: │ New version: 1.2.4 │ Remote ID: fwupd-tests │ Summary: Firmware for the ACME Corp Integrated Webcam │ License: GPL-2.0+ │ Size: 17 bytes │ Vendor: ACME Corp │ Flags: is-upgrade │ Description: Fixes another bug with the flux capacitor to prevent time going backwards. │ └FakeDevice Firmware: New version: 1.2.3 Remote ID: fwupd-tests Summary: Firmware for the ACME Corp Integrated Webcam License: GPL-2.0+ Size: 17 bytes Vendor: ACME Corp Flags: is-upgrade Description: Fixes a bug with the flux capacitor to avoid year 2038 overflow. ``` --- libfwupd/fwupd-enums.c | 4 ++++ libfwupd/fwupd-enums.h | 2 ++ src/fu-history.c | 3 ++- src/fu-self-test.c | 6 +++--- src/fu-tool.c | 9 ++++++++- src/fu-util-common.c | 27 ++++++++++++++++++++------- src/fu-util.c | 8 +++++++- 7 files changed, 46 insertions(+), 13 deletions(-) diff --git a/libfwupd/fwupd-enums.c b/libfwupd/fwupd-enums.c index 3073790c9..976d5c6b9 100644 --- a/libfwupd/fwupd-enums.c +++ b/libfwupd/fwupd-enums.c @@ -163,6 +163,8 @@ fwupd_device_flag_to_string (FwupdDeviceFlags device_flag) return "needs-activation"; if (device_flag == FWUPD_DEVICE_FLAG_ENSURE_SEMVER) return "ensure-semver"; + if (device_flag == FWUPD_DEVICE_FLAG_HISTORICAL) + return "historical"; if (device_flag == FWUPD_DEVICE_FLAG_UNKNOWN) return "unknown"; return NULL; @@ -227,6 +229,8 @@ fwupd_device_flag_from_string (const gchar *device_flag) return FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION; if (g_strcmp0 (device_flag, "ensure-semver") == 0) return FWUPD_DEVICE_FLAG_ENSURE_SEMVER; + if (g_strcmp0 (device_flag, "historical") == 0) + return FWUPD_DEVICE_FLAG_HISTORICAL; return FWUPD_DEVICE_FLAG_UNKNOWN; } diff --git a/libfwupd/fwupd-enums.h b/libfwupd/fwupd-enums.h index cec2c27e7..efafad85c 100644 --- a/libfwupd/fwupd-enums.h +++ b/libfwupd/fwupd-enums.h @@ -88,6 +88,7 @@ typedef enum { * @FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED: Requires the update to be retried with a new plugin * @FWUPD_DEVICE_FLAG_NO_AUTO_INSTANCE_IDS: Do not add instance IDs from the device baseclass * @FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION: Device update needs to be separately activated + * @FWUPD_DEVICE_FLAG_HISTORICAL Device is for historical data only * @FWUPD_DEVICE_FLAG_ENSURE_SEMVER: Ensure the version is a valid semantic version, e.g. numbers separated with dots * * The device flags. @@ -115,6 +116,7 @@ typedef enum { #define FWUPD_DEVICE_FLAG_NO_AUTO_INSTANCE_IDS (1u << 19) /* Since: 1.2.5 */ #define FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION (1u << 20) /* Since: 1.2.6 */ #define FWUPD_DEVICE_FLAG_ENSURE_SEMVER (1u << 21) /* Since: 1.2.9 */ +#define FWUPD_DEVICE_FLAG_HISTORICAL (1u << 22) /* Since: 1.3.2 */ #define FWUPD_DEVICE_FLAG_UNKNOWN G_MAXUINT64 /* Since: 0.7.3 */ typedef guint64 FwupdDeviceFlags; diff --git a/src/fu-history.c b/src/fu-history.c index b9aa6a03c..414770eb0 100644 --- a/src/fu-history.c +++ b/src/fu-history.c @@ -81,7 +81,8 @@ fu_history_device_from_stmt (sqlite3_stmt *stmt) fwupd_release_set_filename (release, tmp); /* flags */ - fu_device_set_flags (device, sqlite3_column_int64 (stmt, 7)); + fu_device_set_flags (device, sqlite3_column_int64 (stmt, 7) | + FWUPD_DEVICE_FLAG_HISTORICAL); /* metadata */ tmp = (const gchar *) sqlite3_column_text (stmt, 8); diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 475261d4e..ec03344bf 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -1203,7 +1203,7 @@ fu_engine_history_func (void) " DeviceId: 894e8c17a29428b09d10cd90d1db74ea76fbcfe8\n" " Guid: 12345678-1234-1234-1234-123456789012\n" " Plugin: test\n" - " Flags: updatable\n" + " Flags: updatable|historical\n" " Version: 1.2.2\n" " Created: 2018-01-07\n" " Modified: 2017-12-27\n" @@ -1436,7 +1436,7 @@ fu_engine_history_error_func (void) " DeviceId: 894e8c17a29428b09d10cd90d1db74ea76fbcfe8\n" " Guid: 12345678-1234-1234-1234-123456789012\n" " Plugin: test\n" - " Flags: updatable\n" + " Flags: updatable|historical\n" " Version: 1.2.2\n" " Created: 2018-01-07\n" " Modified: 2017-12-27\n" @@ -2520,7 +2520,7 @@ fu_history_func (void) g_assert_cmpint (fu_device_get_update_state (device), ==, FWUPD_UPDATE_STATE_FAILED); g_assert_cmpstr (fu_device_get_update_error (device), ==, "word"); g_assert_cmpstr (fu_device_get_guid_default (device), ==, "827edddd-9bb6-5632-889f-2c01255503da"); - g_assert_cmpint (fu_device_get_flags (device), ==, FWUPD_DEVICE_FLAG_INTERNAL); + g_assert_cmpint (fu_device_get_flags (device), ==, FWUPD_DEVICE_FLAG_INTERNAL | FWUPD_DEVICE_FLAG_HISTORICAL); g_assert_cmpint (fu_device_get_created (device), ==, 123); g_assert_cmpint (fu_device_get_modified (device), ==, 456); release = fu_device_get_release_default (device); diff --git a/src/fu-tool.c b/src/fu-tool.c index 5ad19c93a..72157c86e 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -1364,9 +1364,16 @@ fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) /* show each device */ for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index (devices, i); + FwupdRelease *rel; + GNode *child; if (!fu_util_filter_device (priv, dev)) continue; - g_node_append_data (root, dev); + child = g_node_append_data (root, dev); + + rel = fwupd_device_get_release_default (dev); + if (rel != NULL) + g_node_append_data (child, rel); + } fu_util_print_tree (root, title); diff --git a/src/fu-util-common.c b/src/fu-util-common.c index dafe204d2..4b81fef96 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -886,6 +886,7 @@ fu_util_device_to_string (FwupdDevice *dev, guint idt) const gchar *tmp; const gchar *tmp2; guint64 flags = fwupd_device_get_flags (dev); + guint64 modified = fwupd_device_get_modified (dev); g_autoptr(GHashTable) ids = NULL; g_autoptr(GString) flags_str = g_string_new (NULL); @@ -929,8 +930,13 @@ fu_util_device_to_string (FwupdDevice *dev, guint idt) /* versions */ tmp = fwupd_device_get_version (dev); if (tmp != NULL) { - /* TRANSLATORS: version number of current firmware */ - fu_common_string_append_kv (str, idt + 1, _("Current version"), tmp); + if (flags & FWUPD_DEVICE_FLAG_HISTORICAL) { + /* TRANSLATORS: version number of previous firmware */ + fu_common_string_append_kv (str, idt + 1, _("Previous version"), tmp); + } else { + /* TRANSLATORS: version number of current firmware */ + fu_common_string_append_kv (str, idt + 1, _("Current version"), tmp); + } } tmp = fwupd_device_get_version_lowest (dev); if (tmp != NULL) { @@ -1002,6 +1008,16 @@ fu_util_device_to_string (FwupdDevice *dev, guint idt) fu_common_string_append_kv (str, idt + 1, _("Flags"), flags_str->str); } + /* modified date: for history devices */ + if (modified > 0) { + g_autoptr(GDateTime) date = NULL; + g_autofree gchar *time_str = NULL; + date = g_date_time_new_from_unix_utc (modified); + time_str = g_date_time_format (date, "%F %R"); + /* TRANSLATORS: the original time/date the device was modified */ + fu_common_string_append_kv (str, idt +1, _("Last modified"), time_str); + } + /* all GUIDs for this hardware, with IDs if available */ ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); for (guint i = 0; i < instance_ids->len; i++) { @@ -1044,7 +1060,7 @@ fu_util_release_to_string (FwupdRelease *rel, guint idt) fu_common_string_append_kv (str, idt, fwupd_release_get_name (rel), NULL); /* TRANSLATORS: version number of new firmware */ - fu_common_string_append_kv (str, idt + 1 , _("Version"), + fu_common_string_append_kv (str, idt + 1 , _("New version"), fwupd_release_get_version (rel)); if (fwupd_release_get_remote_id (rel) != NULL) { @@ -1100,10 +1116,7 @@ fu_util_release_to_string (FwupdRelease *rel, guint idt) g_string_append_printf (flags_str, "%s|", fwupd_release_flag_to_string ((guint64) 1 << i)); } - if (flags_str->len == 0) { - /* TRANSLATORS: release properties */ - fu_common_string_append_kv (str, idt + 1, _("Flags"), fwupd_release_flag_to_string (0)); - } else { + if (flags_str->len > 0) { g_string_truncate (flags_str, flags_str->len - 1); /* TRANSLATORS: release properties */ fu_common_string_append_kv (str, idt + 1, _("Flags"), flags_str->str); diff --git a/src/fu-util.c b/src/fu-util.c index fd4bc052a..a7db8e5df 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -806,9 +806,15 @@ fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) /* show each device */ for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index (devices, i); + FwupdRelease *rel; + GNode *child; if (!fu_util_filter_device (priv, dev)) continue; - g_node_append_data (root, dev); + child = g_node_append_data (root, dev); + + rel = fwupd_device_get_release_default (dev); + if (rel != NULL) + g_node_append_data (child, rel); } fu_util_print_tree (root, title); From ce94d163f8d7bef4748a75def1ace0326083eea5 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 20 Sep 2019 13:37:36 -0500 Subject: [PATCH 136/158] fu-util/fu-tool: Map out changelogs from remotes If the data is there, try to show the changelog that matches the release. --- src/fu-tool.c | 37 ++++++++++++++++++++++++++++++++++++- src/fu-util.c | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/fu-tool.c b/src/fu-tool.c index 72157c86e..7447d4cd4 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -1363,16 +1363,51 @@ fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) /* show each device */ for (guint i = 0; i < devices->len; i++) { + g_autoptr(GPtrArray) rels = NULL; FwupdDevice *dev = g_ptr_array_index (devices, i); FwupdRelease *rel; + const gchar *remote; GNode *child; + if (!fu_util_filter_device (priv, dev)) continue; child = g_node_append_data (root, dev); rel = fwupd_device_get_release_default (dev); - if (rel != NULL) + if (rel == NULL) + continue; + remote = fwupd_release_get_remote_id (rel); + + /* doesn't actually map to remote */ + if (remote == NULL) { g_node_append_data (child, rel); + continue; + } + + /* try to lookup releases from client */ + rels = fu_engine_get_releases (priv->engine, fwupd_device_get_id (dev), error); + if (rels == NULL) + return FALSE; + + /* map to a release in client */ + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel2 = g_ptr_array_index (rels, j); + if (g_strcmp0 (remote, + fwupd_release_get_remote_id (rel2)) != 0) + continue; + if (g_strcmp0 (fwupd_release_get_version (rel), + fwupd_release_get_version (rel2)) != 0) + continue; + g_node_append_data (child, g_object_ref (rel2)); + rel = NULL; + break; + } + + /* didn't match anything */ + if (rels->len == 0 || rel != NULL) { + g_node_append_data (child, rel); + continue; + } } fu_util_print_tree (root, title); diff --git a/src/fu-util.c b/src/fu-util.c index a7db8e5df..3a80b8363 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -805,17 +805,53 @@ fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error) /* show each device */ for (guint i = 0; i < devices->len; i++) { + g_autoptr(GPtrArray) rels = NULL; FwupdDevice *dev = g_ptr_array_index (devices, i); FwupdRelease *rel; + const gchar *remote; GNode *child; + if (!fu_util_filter_device (priv, dev)) continue; child = g_node_append_data (root, dev); rel = fwupd_device_get_release_default (dev); - if (rel != NULL) + if (rel == NULL) + continue; + remote = fwupd_release_get_remote_id (rel); + + /* doesn't actually map to remote */ + if (remote == NULL) { g_node_append_data (child, rel); + continue; + } + + /* try to lookup releases from client */ + rels = fwupd_client_get_releases (priv->client, fwupd_device_get_id (dev), NULL, error); + if (rels == NULL) + return FALSE; + + /* map to a release in client */ + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel2 = g_ptr_array_index (rels, j); + if (g_strcmp0 (remote, + fwupd_release_get_remote_id (rel2)) != 0) + continue; + if (g_strcmp0 (fwupd_release_get_version (rel), + fwupd_release_get_version (rel2)) != 0) + continue; + g_node_append_data (child, g_object_ref (rel2)); + rel = NULL; + break; + } + + /* didn't match anything */ + if (rels->len == 0 || rel != NULL) { + g_node_append_data (child, rel); + continue; + } } + fu_util_print_tree (root, title); return TRUE; From 1dbb82d25efb3cdecca5364f7715861fbca7f823 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 20 Sep 2019 14:22:14 -0500 Subject: [PATCH 137/158] trivial: fu-util-common: fix description wrapping (Fixes: #1378) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Embedded newline characters don't do so well. Before: ``` └─XPS 13 9380 System Update: New version: 0.1.3.2 Remote ID: lvfs Summary: Firmware for the Dell XPS 13 9380 License: proprietary Size: 19.9 MB Vendor: Dell Inc. Flags: is-downgrade|blocked-version Description: This stable release fixes the following issues: �� Fixed an issue with Secure Boot Option ROM Signature Verification. �� Firmware updates to address security advisory INTEL-SA-00185 (CVE-2018-12188 CVE-2018-12190 CVE-2018-12191 CVE-2018-12192 CVE-2018-12199 CVE-2018-12198 CVE-2018-12200 CVE-2018-12187 CVE-2018-12196 CVE-2018-12185). �� Firmware updates to address security advisories INTEL-SA-00191(CVE-2018-12201, CVE-2018- 12202, CVE-2018-12203, CVE-2018-12205). me new functionality has also been added: �� Removed the option Always Allow Dell Docks from BIOS settings. Dell dock connection and the port behavior will be controlled via the USB and Thunderbolt Adapter configuration settings under operating system environment. �� Replaces Absolute Software service from Computrace to Absolute Persistence Module service. �� Supports Dell Dock Station WD19. ``` After: ``` └─XPS 13 9380 System Update: New version: 0.1.3.2 Remote ID: lvfs Summary: Firmware for the Dell XPS 13 9380 License: proprietary Size: 19.9 MB Vendor: Dell Inc. Flags: is-downgrade|blocked-version Description: This stable release fixes the following issues: • Fixed an issue with Secure Boot Option ROM Signature Verification. • Firmware updates to address security advisory INTEL-SA-00185 (CVE-2018-12188 CVE-2018-12190 CVE-2018-12191 CVE-2018-12192 CVE-2018-12199 CVE-2018-12198 CVE-2018-12200 CVE-2018-12187 CVE-2018-12196 CVE-2018-12185). • Firmware updates to address security advisories INTEL-SA-00191(CVE-2018-12201, CVE-2018- 12202, CVE-2018-12203, CVE-2018-12205). Some new functionality has also been added: • Removed the option Always Allow Dell Docks from BIOS settings. Dell dock connection and the port behavior will be controlled via the USB and Thunderbolt Adapter configuration settings under operating system environment. • Replaces Absolute Software service from Computrace to Absolute Persistence Module service. • Supports Dell Dock Station WD19. ``` --- src/fu-common.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/fu-common.c b/src/fu-common.c index bcf106598..dee459d0d 100644 --- a/src/fu-common.c +++ b/src/fu-common.c @@ -1143,11 +1143,22 @@ fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gch keysz = idt * 2; } if (value != NULL) { - for (gsize i = keysz; i < align; i++) - g_string_append (str, " "); - g_string_append (str, value); + g_auto(GStrv) split = NULL; + split = g_strsplit (value, "\n", -1); + for (guint i = 0; split[i] != NULL; i++) { + if (i == 0) { + for (gsize j = keysz; j < align; j++) + g_string_append (str, " "); + } else { + for (gsize j = 0; j < idt; j++) + g_string_append (str, " "); + } + g_string_append (str, split[i]); + g_string_append (str, "\n"); + } + } else { + g_string_append (str, "\n"); } - g_string_append (str, "\n"); } void From 6d0e7fddd5d4b2957392989f57127bfdd6aa514e Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Sat, 21 Sep 2019 12:42:52 +0100 Subject: [PATCH 138/158] trivial: Return the correct property when getting the host product --- libfwupd/fwupd-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfwupd/fwupd-client.c b/libfwupd/fwupd-client.c index 0caad38a5..64865281a 100644 --- a/libfwupd/fwupd-client.c +++ b/libfwupd/fwupd-client.c @@ -1888,7 +1888,7 @@ fwupd_client_class_init (FwupdClientClass *klass) */ pspec = g_param_spec_string ("host-product", NULL, NULL, NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME); - g_object_class_install_property (object_class, PROP_DAEMON_VERSION, pspec); + g_object_class_install_property (object_class, PROP_HOST_PRODUCT, pspec); } From ae71a62620f88b7285c4339eed036643da0966d7 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Sat, 21 Sep 2019 13:27:44 -0500 Subject: [PATCH 139/158] snap: switch to core18 This makes the snap build quicker by using more distro dependencies and also fixes some unicode output errors. --- .circleci/config.yml | 5 +- snap/snapcraft.yaml | 150 +++++++------------------------------------ 2 files changed, 27 insertions(+), 128 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6734b3689..9363fde9f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,12 +30,15 @@ jobs: build-snap: docker: - - image: superm1/snapcraft-edge:latest + - image: ubuntu:18.04 steps: - checkout - run: name: "Update apt" command: apt update + - run: + name: "install snapcraft" + command: apt install snapcraft -y - run: name: "Build Snap" command: snapcraft diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 56dfa9d14..6544627eb 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -8,6 +8,7 @@ description: | grade: stable confinement: classic +base: core18 architectures: - amd64 @@ -28,44 +29,6 @@ apps: share/bash-completion/completions/fwupdmgr parts: - libefivar-dev: - plugin: make - make-parameters: - - prefix=/ - - libdir=/lib - source: https://github.com/rhboot/efivar/releases/download/37/efivar-37.tar.bz2 - build-packages: - - libpopt-dev - prime: - - -include - - -bin - - -share/man - - -lib/pkgconfig - #adjust the paths from libefivar - libefivar-fixpkgconfig: - plugin: make - source: contrib/snap/libefivar-fixpkgconfig - make-parameters: - - SNAPCRAFT_STAGE=$SNAPCRAFT_STAGE - after: [libefivar-dev] - libsmbios: - plugin: autotools - source: https://github.com/dell/libsmbios/archive/v2.4.2.tar.gz - build-packages: - - libxml2-dev - - pkg-config - - autoconf - - automake - - libtool - - autopoint - prime: - - -include/ - - -lib/pkgconfig - - -lib/python3.5 - - -sbin/ - - -share/ - - -etc/ - - -lib/*.a tpm2-tss: plugin: autotools source: https://github.com/tpm2-software/tpm2-tss/releases/download/2.3.0/tpm2-tss-2.3.0.tar.gz @@ -80,38 +43,24 @@ parts: - libssl-dev meson: plugin: python - source: https://github.com/mesonbuild/meson/releases/download/0.47.2/meson-0.47.2.tar.gz + source: https://github.com/mesonbuild/meson/releases/download/0.51.2/meson-0.51.2.tar.gz build-packages: - ninja-build + - python3-distutils-extra prime: - -bin - -etc - -lib - -share - -usr - gudev: - plugin: autotools - source: https://github.com/GNOME/libgudev/archive/232.tar.gz - configflags: - - --disable-umockdev - build-packages: - - libglib2.0-dev - - pkg-config - - libudev-dev - - gtk-doc-tools - - gnome-common - prime: - - -include - - -lib/girepository-1.0 - - -lib/pkgconfig - - -share/ # this is for the library only, we don't care about the daemon "in-snap" modemmanager: plugin: autotools source: https://gitlab.freedesktop.org/mobile-broadband/ModemManager.git source-tag: 1.10.0 - after: [gudev, gettext] # build without these; system daemon needs them + build-packages: + - xsltproc configflags: - --without-mbim - --without-qmi @@ -131,7 +80,7 @@ parts: plugin: autotools source: https://gitlab.freedesktop.org/mobile-broadband/libmbim.git source-tag: 1.18.0 - after: [gudev, gettext, modemmanager] + after: [modemmanager] # build without these; system daemon needs them configflags: - --without-udev @@ -151,7 +100,7 @@ parts: plugin: autotools source: https://gitlab.freedesktop.org/mobile-broadband/libqmi.git source-tag: 1.23.1 - after: [gudev, gettext, modemmanager, libmbim] + after: [modemmanager, libmbim] # build without these; system daemon needs them configflags: - --without-udev @@ -167,39 +116,6 @@ parts: - -lib/systemd - -lib/udev - -lib/girepository-1.0 - libusb: - plugin: autotools - source: https://github.com/libusb/libusb/releases/download/v1.0.22/libusb-1.0.22.tar.bz2 - configflags: - - --disable-static - prime: - - -include/ - - -lib/pkgconfig - gusb: - plugin: meson - source: https://github.com/hughsie/libgusb/archive/0.3.0.tar.gz - meson-parameters: [--prefix=/, - -Dtests=false, - -Dvapi=false, - -Ddocs=false] - build-packages: - - libgirepository1.0-dev - prime: - - -bin/ - - -include - - -share - - -lib/*/pkgconfig - - -lib/*/girepository-1.0 - after: [meson, libusb] - gnu-efi: - plugin: make - source: http://superb-dca2.dl.sourceforge.net/project/gnu-efi/gnu-efi-3.0.5.tar.bz2 - make-parameters: - - PREFIX=/usr - make-install-var: INSTALLROOT - prime: - - -usr/include/ - - -usr/lib #fetch the latest version of the signed bootloader #this might not match our fwupdx64.efi, but it's better than nothing fwup-efi-signed: @@ -219,51 +135,18 @@ parts: - -usr - -lib - -var - #0.19.8.1 adds support for GETTEXTDATADIRS which is needed by meson's msgfmthelper - gettext: - source: https://ftp.gnu.org/pub/gnu/gettext/gettext-0.19.8.1.tar.xz - plugin: autotools - build-packages: - - bison - - libunistring-dev - - libxml2-dev - configflags: - - --prefix=/usr - - --disable-static - - --disable-curses - - --disable-java - - --enable-relocatable - - --without-emacs - - --without-included-glib - - --without-included-libunistring - - --without-included-libxml - stage-packages: - - libunistring0 - - libxml2 - - libgomp1 - prime: - - -**/*.a - - -**/*.la - - -usr/bin - - -usr/include - - -usr/lib/gettext - - -usr/share fwupd: plugin: meson meson-parameters: [--prefix=/, - -Defi-includedir=$SNAPCRAFT_STAGE/usr/include/efi, - -Defi-ldsdir=$SNAPCRAFT_STAGE/usr/lib, - -Defi-libdir=$SNAPCRAFT_STAGE/usr/lib, -Dtests=false, - -Ddaemon=true, + -Dbuild=all, -Dgtkdoc=false, -Dintrospection=false, -Dman=false, -Dplugin_modem_manager=true, -Dudevdir=$SNAPCRAFT_STAGE/lib/udev, "-Dlibxmlb:gtkdoc=false", - "-Dlibxmlb:introspection=false", - -Dpkcs7=false] + "-Dlibxmlb:introspection=false"] source: . source-type: git override-build: | @@ -273,10 +156,15 @@ parts: - bash-completion - gcab - gnutls-dev + - gnu-efi - libarchive-dev - libcairo-dev - libelf-dev + - libefiboot-dev + - libefivar-dev - libftdi1-dev + - libgudev-1.0-dev + - libgusb-dev - libgcab-dev - libglib2.0-dev - libgpgme11-dev @@ -284,20 +172,28 @@ parts: - libpango1.0-dev - libpci-dev - libpolkit-gobject-1-dev + - libsmbios-dev - libsoup2.4-dev - libsqlite3-dev - locales - pkg-config + - systemd - uuid-dev stage-packages: - libgcab-1.0-0 - libarchive13 - libassuan0 - liblcms2-2 + - libefivar1 + - libefiboot1 - libelf1 + - libgusb2 + - libusb-1.0-0 + - libgudev-1.0-0 - libgpgme11 - libjson-glib-1.0-0 - libpolkit-gobject-1-0 + - libsmbios-c2 - libsoup2.4-1 - glib-networking - libglib2.0-bin @@ -336,7 +232,7 @@ parts: - -usr/share/gir-1.0 - -usr/share/upstart - -usr/lib/*/pkgconfig - after: [gudev, gusb, gnu-efi, libefivar-fixpkgconfig, libsmbios, build-introspection, gettext, modemmanager, libmbim, libqmi, tpm2-tss] + after: [meson, build-introspection, modemmanager, libmbim, libqmi, tpm2-tss] fix-bash-completion: plugin: make source: contrib/snap/fix-bash-completion From 6f627aba840a0b3ef3afde82a8e4573d8596c121 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 23 Sep 2019 09:33:46 +0100 Subject: [PATCH 140/158] trivial: Fix tiny memory leak when using FuSrecFirmware --- src/fu-srec-firmware.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fu-srec-firmware.c b/src/fu-srec-firmware.c index 901329892..d8e29b945 100644 --- a/src/fu-srec-firmware.c +++ b/src/fu-srec-firmware.c @@ -353,6 +353,7 @@ fu_srec_firmware_finalize (GObject *object) { FuSrecFirmware *self = FU_SREC_FIRMWARE (object); g_ptr_array_unref (self->records); + G_OBJECT_CLASS (fu_srec_firmware_parent_class)->finalize (object); } static void From d1afec8e273daac1cd7fd246ebaf7dffccf7d0fc Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 23 Sep 2019 09:35:06 +0100 Subject: [PATCH 141/158] synaptics-prometheus: Fix tiny memory leak when using FuSynapromConfig --- plugins/synaptics-prometheus/fu-synaprom-config.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/synaptics-prometheus/fu-synaprom-config.c b/plugins/synaptics-prometheus/fu-synaprom-config.c index 2005ec0d0..b874f4b8c 100644 --- a/plugins/synaptics-prometheus/fu-synaprom-config.c +++ b/plugins/synaptics-prometheus/fu-synaprom-config.c @@ -213,6 +213,7 @@ fu_synaprom_config_finalize (GObject *obj) { FuSynapromConfig *self = FU_SYNAPROM_CONFIG (obj); g_object_unref (self->device); + G_OBJECT_CLASS (fu_synaprom_config_parent_class)->finalize (obj); } static void From 17c02b8b1fc2fd89ac0f98c7891e7c39459eefc4 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 23 Sep 2019 09:38:44 +0100 Subject: [PATCH 142/158] trivial: Fix some NULL/FALSE confusion --- libfwupd/fwupd-client.c | 2 +- plugins/altos/fu-altos-device.c | 2 +- plugins/dfu/dfu-patch.c | 2 +- plugins/solokey/fu-solokey-device.c | 2 +- src/fu-firmware-image.c | 6 +++--- src/fu-firmware.c | 10 +++++----- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libfwupd/fwupd-client.c b/libfwupd/fwupd-client.c index 64865281a..5b6455481 100644 --- a/libfwupd/fwupd-client.c +++ b/libfwupd/fwupd-client.c @@ -1185,7 +1185,7 @@ const gchar * fwupd_client_get_host_product (FwupdClient *client) { FwupdClientPrivate *priv = GET_PRIVATE (client); - g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); + g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL); return priv->host_product; } diff --git a/plugins/altos/fu-altos-device.c b/plugins/altos/fu-altos-device.c index c924bff1d..1ac1f66b0 100644 --- a/plugins/altos/fu-altos-device.c +++ b/plugins/altos/fu-altos-device.c @@ -276,7 +276,7 @@ fu_altos_device_prepare_firmware (FuDevice *device, { g_autoptr(FuFirmware) firmware = fu_altos_firmware_new (); if (!fu_firmware_parse (firmware, fw, flags, error)) - return FALSE; + return NULL; return g_steal_pointer (&firmware); } diff --git a/plugins/dfu/dfu-patch.c b/plugins/dfu/dfu-patch.c index d1e2b7d63..d6d172fce 100644 --- a/plugins/dfu/dfu-patch.c +++ b/plugins/dfu/dfu-patch.c @@ -521,7 +521,7 @@ dfu_patch_apply (DfuPatch *self, GBytes *blob, DfuPatchApplyFlags flags, GError if (!fu_memcpy_safe (data_new, sz_max, 0x0, /* dst */ data_old, sz, 0x0, /* src */ MIN (sz, sz_max), error)) - return FALSE; + return NULL; for (guint i = 0; i < priv->chunks->len; i++) { DfuPatchChunk *chunk = g_ptr_array_index (priv->chunks, i); const guint8 *chunk_data; diff --git a/plugins/solokey/fu-solokey-device.c b/plugins/solokey/fu-solokey-device.c index c98d2054e..1aa38b7ae 100644 --- a/plugins/solokey/fu-solokey-device.c +++ b/plugins/solokey/fu-solokey-device.c @@ -212,7 +212,7 @@ fu_solokey_device_packet_rx (FuSolokeyDevice *self, GError **error) NULL, /* cancellable */ error)) { g_prefix_error (error, "failed to get reply: "); - return FALSE; + return NULL; } if (g_getenv ("FWUPD_SOLOKEY_VERBOSE") != NULL) fu_common_dump_raw (G_LOG_DOMAIN, "RES", buf, actual_length); diff --git a/src/fu-firmware-image.c b/src/fu-firmware-image.c index 7012ff627..fb9c8d2cd 100644 --- a/src/fu-firmware-image.c +++ b/src/fu-firmware-image.c @@ -59,7 +59,7 @@ const gchar * fu_firmware_image_get_id (FuFirmwareImage *self) { FuFirmwareImagePrivate *priv = GET_PRIVATE (self); - g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), FALSE); + g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), NULL); return priv->id; } @@ -168,8 +168,8 @@ fu_firmware_image_get_bytes (FuFirmwareImage *self, GError **error) { FuFirmwareImagePrivate *priv = GET_PRIVATE (self); - g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); if (priv->bytes == NULL) { g_set_error (error, diff --git a/src/fu-firmware.c b/src/fu-firmware.c index 2326d44fa..ddd7afbb3 100644 --- a/src/fu-firmware.c +++ b/src/fu-firmware.c @@ -137,8 +137,8 @@ fu_firmware_write (FuFirmware *self, GError **error) { FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS (self); - g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (FU_IS_FIRMWARE (self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); /* subclassed */ if (klass->write != NULL) @@ -184,7 +184,7 @@ fu_firmware_get_images (FuFirmware *self) FuFirmwarePrivate *priv = GET_PRIVATE (self); g_autoptr(GPtrArray) imgs = NULL; - g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE); + g_return_val_if_fail (FU_IS_FIRMWARE (self), NULL); imgs = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); for (guint i = 0; i < priv->images->len; i++) { @@ -211,8 +211,8 @@ fu_firmware_get_image_by_id (FuFirmware *self, const gchar *id, GError **error) { FuFirmwarePrivate *priv = GET_PRIVATE (self); - g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (FU_IS_FIRMWARE (self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); for (guint i = 0; i < priv->images->len; i++) { FuFirmwareImage *img = g_ptr_array_index (priv->images, i); From 0a9665c708acaed15f349dfda5ef31600ce7a19b Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 23 Sep 2019 07:47:37 -0500 Subject: [PATCH 143/158] trivial: Move dbus directory location for snap too This error was happening on fresh snap install (not upgrade): ``` install: cannot stat '/snap/fwupd/1065/etc/dbus-1/system.d/org.freedesktop.fwupd.conf': No such file or directory ``` Fixes 41a25be6 ("Move D-Bus conf file to datadir/dbus-1/system.d") --- snap/hooks/install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snap/hooks/install b/snap/hooks/install index 8d625aebc..a277b9a62 100755 --- a/snap/hooks/install +++ b/snap/hooks/install @@ -13,7 +13,7 @@ install_if_missing share/polkit-1/rules.d/org.freedesktop.fwupd.rules /usr #install dbus related items install_if_missing share/dbus-1/system-services/org.freedesktop.fwupd.service /usr install_if_missing share/dbus-1/interfaces/org.freedesktop.fwupd.xml /usr -install_if_missing etc/dbus-1/system.d/org.freedesktop.fwupd.conf / +install_if_missing share/dbus-1/system.d/org.freedesktop.fwupd.conf / #activation via systemd install_if_missing etc/systemd/system/fwupd-activate.service / systemctl daemon-reload From ae96a1f27f7e7e660a6e8cae26f9e771cf0e8030 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 23 Sep 2019 11:16:36 +0100 Subject: [PATCH 144/158] Align the key values to the text *width* not the number of bytes This fixes the output when using locales like zh_CN.utf-8 --- src/fu-common.c | 28 +++++++++++++++++++++++++++- src/fu-common.h | 1 + src/fu-progressbar.c | 3 ++- src/fu-util-common.c | 16 ++++++++-------- 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/fu-common.c b/src/fu-common.c index dee459d0d..07ed20db2 100644 --- a/src/fu-common.c +++ b/src/fu-common.c @@ -1123,6 +1123,32 @@ fu_common_string_replace (GString *string, const gchar *search, const gchar *rep return count; } +/** + * fu_common_strwidth: + * @text: The string to operate on + * + * Returns the width of the string in displayed characters on the console. + * + * Returns: width of text + * + * Since: 1.3.2 + **/ +gsize +fu_common_strwidth (const gchar *text) +{ + const gchar *p = text; + gsize width = 0; + while (*p) { + gunichar c = g_utf8_get_char (p); + if (g_unichar_iswide (c)) + width += 2; + else if (!g_unichar_iszerowidth (c)) + width += 1; + p = g_utf8_next_char (p); + } + return width; +} + void fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value) { @@ -1138,7 +1164,7 @@ fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gch g_string_append (str, " "); if (key[0] != '\0') { g_string_append_printf (str, "%s:", key); - keysz = (idt * 2) + strlen (key) + 1; + keysz = (idt * 2) + fu_common_strwidth (key) + 1; } else { keysz = idt * 2; } diff --git a/src/fu-common.h b/src/fu-common.h index 9cf13dfc9..9eadf71d1 100644 --- a/src/fu-common.h +++ b/src/fu-common.h @@ -142,6 +142,7 @@ gboolean fu_common_bytes_compare_raw (const guint8 *buf1, GError **error); GBytes *fu_common_bytes_pad (GBytes *bytes, gsize sz); +gsize fu_common_strwidth (const gchar *text); gboolean fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset, diff --git a/src/fu-progressbar.c b/src/fu-progressbar.c index da3826b85..8eb9dbd5e 100644 --- a/src/fu-progressbar.c +++ b/src/fu-progressbar.c @@ -11,6 +11,7 @@ #include #include +#include "fu-common.h" #include "fu-progressbar.h" static void fu_progressbar_finalize (GObject *obj); @@ -160,7 +161,7 @@ fu_progressbar_refresh (FuProgressbar *self, FwupdStatus status, guint percentag } title = fu_progressbar_status_to_string (status); g_string_append (str, title); - for (i = g_utf8_strlen (str->str, -1); i < self->length_status; i++) + for (i = fu_common_strwidth (str->str); i < self->length_status; i++) g_string_append_c (str, ' '); /* add progressbar */ diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 4b81fef96..08cff5a79 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -80,7 +80,7 @@ fu_util_print_data (const gchar *title, const gchar *msg) g_print ("%s:", title); /* pad */ - title_len = strlen (title) + 1; + title_len = fu_common_strwidth (title) + 1; lines = g_strsplit (msg, "\n", -1); for (guint j = 0; lines[j] != NULL; j++) { for (gsize i = title_len; i < 25; i++) @@ -495,11 +495,11 @@ fu_util_cmd_array_to_string (GPtrArray *array) FuUtilCmd *item = g_ptr_array_index (array, i); g_string_append (string, " "); g_string_append (string, item->name); - len = strlen (item->name) + 2; + len = fu_common_strwidth (item->name) + 2; if (item->arguments != NULL) { g_string_append (string, " "); g_string_append (string, item->arguments); - len += strlen (item->arguments) + 1; + len += fu_common_strwidth (item->arguments) + 1; } if (len < max_len) { for (gsize j = len; j < max_len + 1; j++) @@ -647,7 +647,7 @@ fu_util_strsplit_words (const gchar *text, guint line_len) for (guint i = 0; tokens[i] != NULL; i++) { /* current line plus new token is okay */ - if (curline->len + strlen (tokens[i]) < line_len) { + if (curline->len + fu_common_strwidth (tokens[i]) < line_len) { g_string_append_printf (curline, "%s ", tokens[i]); continue; } @@ -677,15 +677,15 @@ fu_util_warning_box_line (const gchar *start, { guint offset = 0; if (start != NULL) { - offset += g_utf8_strlen (start, -1); + offset += fu_common_strwidth (start); g_print ("%s", start); } if (text != NULL) { - offset += g_utf8_strlen (text, -1); + offset += fu_common_strwidth (text); g_print ("%s", text); } if (end != NULL) - offset += g_utf8_strlen (end, -1); + offset += fu_common_strwidth (end); for (guint i = offset; i < width; i++) g_print ("%s", padding); if (end != NULL) @@ -1219,7 +1219,7 @@ fu_util_remote_to_string (FwupdRemote *remote, guint idt) } tmp = fwupd_remote_get_password (remote); if (tmp != NULL) { - g_autofree gchar *hidden = g_strnfill (strlen (tmp), '*'); + g_autofree gchar *hidden = g_strnfill (fu_common_strwidth (tmp), '*'); /* TRANSLATORS: remote filename base */ fu_common_string_append_kv (str, idt + 1, _("Password"), hidden); } From 15cd821094df21391d1842ac24d7ed8989b3b804 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 23 Sep 2019 09:44:00 -0500 Subject: [PATCH 145/158] trivial: dell-dock: correct another write_size error This was more fallout from 7afd7cba0d327b155b2201c9fbb5414ced9cfe06 which led to write_size being set to 0 and an endless loop. --- plugins/dell-dock/fu-dell-dock-hub.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/dell-dock/fu-dell-dock-hub.c b/plugins/dell-dock/fu-dell-dock-hub.c index 41516976a..66a04cea9 100644 --- a/plugins/dell-dock/fu-dell-dock-hub.c +++ b/plugins/dell-dock/fu-dell-dock-hub.c @@ -53,8 +53,7 @@ fu_dell_dock_hub_write_fw (FuDevice *device, FuDellDockHub *self = FU_DELL_DOCK_HUB (device); gsize fw_size = 0; const guint8 *data; - gsize write_size = - (fw_size / HIDI2C_MAX_WRITE) >= 1 ? HIDI2C_MAX_WRITE : fw_size; + gsize write_size; gsize nwritten = 0; guint32 address = 0; gboolean result = FALSE; @@ -69,6 +68,8 @@ fu_dell_dock_hub_write_fw (FuDevice *device, if (fw == NULL) return FALSE; data = g_bytes_get_data (fw, &fw_size); + write_size = (fw_size / HIDI2C_MAX_WRITE) >= 1 ? + HIDI2C_MAX_WRITE : fw_size; dynamic_version = g_strdup_printf ("%02x.%02x", data[self->blob_major_offset], From cee877dfc4c4ecad00f92a976ce4f38a09f85e5b Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 23 Sep 2019 11:23:15 -0500 Subject: [PATCH 146/158] trivial: add stalebot (Fixes: #1393) --- .github/stale.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..8519bf6ef --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,17 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 30 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - enhancement + - regression +# Label to use when marking an issue as stale +staleLabel: wontfix +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false From a45994cb4a56ba0044a40ec99b8da5559b0a01fe Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 18 Sep 2019 08:26:14 +0100 Subject: [PATCH 147/158] Fall back to /var/lib/dbus/machine-id when required Fixes https://github.com/fwupd/fwupd/issues/1365 --- libfwupd/fwupd-common.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/libfwupd/fwupd-common.c b/libfwupd/fwupd-common.c index 8bd5fff38..078b42b78 100644 --- a/libfwupd/fwupd-common.c +++ b/libfwupd/fwupd-common.c @@ -313,12 +313,31 @@ fwupd_build_user_agent (const gchar *package_name, const gchar *package_version) gchar * fwupd_build_machine_id (const gchar *salt, GError **error) { + const gchar *fn = NULL; g_autofree gchar *buf = NULL; + g_auto(GStrv) fns = g_new0 (gchar *, 5); g_autoptr(GChecksum) csum = NULL; gsize sz = 0; - /* this has to exist */ - if (!g_file_get_contents ("/etc/machine-id", &buf, &sz, error)) + /* one of these has to exist */ + fns[0] = g_build_filename (SYSCONFDIR, "machine-id", NULL); + fns[1] = g_build_filename (LOCALSTATEDIR, "lib", "dbus", "machine-id", NULL); + fns[2] = g_strdup ("/etc/machine-id"); + fns[3] = g_strdup ("/var/lib/dbus/machine-id"); + for (guint i = 0; fns[i] != NULL; i++) { + if (g_file_test (fns[i], G_FILE_TEST_EXISTS)) { + fn = fns[i]; + break; + } + } + if (fn == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "The machine-id is not present"); + return NULL; + } + if (!g_file_get_contents (fn, &buf, &sz, error)) return NULL; if (sz == 0) { g_set_error_literal (error, From 0917fb6aec177375a2241f57d63e21a71fe19cd6 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Sat, 21 Sep 2019 12:55:37 +0100 Subject: [PATCH 148/158] Export the salted machine ID as a daemon property --- libfwupd/fwupd-client.c | 52 +++++++++++++++++++++++++++++++++++ libfwupd/fwupd-client.h | 1 + libfwupd/fwupd.map | 1 + src/fu-engine.c | 14 ++++++++++ src/fu-engine.h | 1 + src/fu-main.c | 3 ++ src/org.freedesktop.fwupd.xml | 11 ++++++++ 7 files changed, 83 insertions(+) diff --git a/libfwupd/fwupd-client.c b/libfwupd/fwupd-client.c index 5b6455481..2ef0a041e 100644 --- a/libfwupd/fwupd-client.c +++ b/libfwupd/fwupd-client.c @@ -41,6 +41,7 @@ typedef struct { guint percentage; gchar *daemon_version; gchar *host_product; + gchar *host_machine_id; GDBusConnection *conn; GDBusProxy *proxy; } FwupdClientPrivate; @@ -61,6 +62,7 @@ enum { PROP_DAEMON_VERSION, PROP_TAINTED, PROP_HOST_PRODUCT, + PROP_HOST_MACHINE_ID, PROP_LAST }; @@ -113,6 +115,15 @@ fwupd_client_set_host_product (FwupdClient *client, const gchar *host_product) g_object_notify (G_OBJECT (client), "host-product"); } +static void +fwupd_client_set_host_machine_id (FwupdClient *client, const gchar *host_machine_id) +{ + FwupdClientPrivate *priv = GET_PRIVATE (client); + g_free (priv->host_machine_id); + priv->host_machine_id = g_strdup (host_machine_id); + g_object_notify (G_OBJECT (client), "host-machine-id"); +} + static void fwupd_client_set_daemon_version (FwupdClient *client, const gchar *daemon_version) { @@ -172,6 +183,12 @@ fwupd_client_properties_changed_cb (GDBusProxy *proxy, if (val != NULL) fwupd_client_set_host_product (client, g_variant_get_string (val, NULL)); } + if (g_variant_dict_contains (dict, "HostMachineId")) { + g_autoptr(GVariant) val = NULL; + val = g_dbus_proxy_get_cached_property (proxy, "HostMachineId"); + if (val != NULL) + fwupd_client_set_host_machine_id (client, g_variant_get_string (val, NULL)); + } } static void @@ -269,6 +286,9 @@ fwupd_client_connect (FwupdClient *client, GCancellable *cancellable, GError **e val = g_dbus_proxy_get_cached_property (priv->proxy, "HostProduct"); if (val != NULL) fwupd_client_set_host_product (client, g_variant_get_string (val, NULL)); + val = g_dbus_proxy_get_cached_property (priv->proxy, "HostMachineId"); + if (val != NULL) + fwupd_client_set_host_machine_id (client, g_variant_get_string (val, NULL)); return TRUE; } @@ -1189,6 +1209,24 @@ fwupd_client_get_host_product (FwupdClient *client) return priv->host_product; } +/** + * fwupd_client_get_host_machine_id: + * @client: A #FwupdClient + * + * Gets the string that represents the host machine ID + * + * Returns: a string, or %NULL for unknown. + * + * Since: 1.3.2 + **/ +const gchar * +fwupd_client_get_host_machine_id (FwupdClient *client) +{ + FwupdClientPrivate *priv = GET_PRIVATE (client); + g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE); + return priv->host_machine_id; +} + /** * fwupd_client_get_status: * @client: A #FwupdClient @@ -1714,6 +1752,9 @@ fwupd_client_get_property (GObject *object, guint prop_id, case PROP_HOST_PRODUCT: g_value_set_string (value, priv->host_product); break; + case PROP_HOST_MACHINE_ID: + g_value_set_string (value, priv->host_machine_id); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1890,6 +1931,16 @@ fwupd_client_class_init (FwupdClientClass *klass) NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_HOST_PRODUCT, pspec); + /** + * FwupdClient:host-machine-id: + * + * The host machine-id string + * + * Since: 1.3.2 + */ + pspec = g_param_spec_string ("host-machine-id", NULL, NULL, + NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_HOST_MACHINE_ID, pspec); } static void @@ -1905,6 +1956,7 @@ fwupd_client_finalize (GObject *object) g_free (priv->daemon_version); g_free (priv->host_product); + g_free (priv->host_machine_id); if (priv->conn != NULL) g_object_unref (priv->conn); if (priv->proxy != NULL) diff --git a/libfwupd/fwupd-client.h b/libfwupd/fwupd-client.h index b6fa21a6d..129488d69 100644 --- a/libfwupd/fwupd-client.h +++ b/libfwupd/fwupd-client.h @@ -128,6 +128,7 @@ gboolean fwupd_client_get_tainted (FwupdClient *client); guint fwupd_client_get_percentage (FwupdClient *client); const gchar *fwupd_client_get_daemon_version (FwupdClient *client); const gchar *fwupd_client_get_host_product (FwupdClient *client); +const gchar *fwupd_client_get_host_machine_id (FwupdClient *client); GPtrArray *fwupd_client_get_remotes (FwupdClient *client, GCancellable *cancellable, diff --git a/libfwupd/fwupd.map b/libfwupd/fwupd.map index 2d7a402f9..ef7e954a8 100644 --- a/libfwupd/fwupd.map +++ b/libfwupd/fwupd.map @@ -382,6 +382,7 @@ LIBFWUPD_1.3.1 { LIBFWUPD_1.3.2 { global: + fwupd_client_get_host_machine_id; fwupd_release_add_issue; fwupd_release_get_issues; local: *; diff --git a/src/fu-engine.c b/src/fu-engine.c index a1b94dcfa..191acc855 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -77,6 +77,7 @@ struct _FuEngine GHashTable *runtime_versions; GHashTable *compile_versions; GHashTable *approved_firmware; + gchar *host_machine_id; gboolean loaded; }; @@ -4160,6 +4161,13 @@ fu_engine_get_host_product (FuEngine *self) return fu_hwids_get_value (self->hwids, FU_HWIDS_KEY_PRODUCT_NAME); } +const gchar * +fu_engine_get_host_machine_id (FuEngine *self) +{ + g_return_val_if_fail (FU_IS_ENGINE (self), NULL); + return self->host_machine_id; +} + gboolean fu_engine_load_plugins (FuEngine *self, GError **error) { @@ -4573,6 +4581,11 @@ fu_engine_load (FuEngine *self, FuEngineLoadFlags flags, GError **error) if (self->loaded) return TRUE; + /* cache machine ID so we can use it from a sandboxed app */ + self->host_machine_id = fwupd_build_machine_id ("fwupd", error); + if (self->host_machine_id == NULL) + return FALSE; + /* read config file */ if (flags & FU_ENGINE_LOAD_FLAG_READONLY_FS) config_flags |= FU_CONFIG_LOAD_FLAG_READONLY_FS; @@ -4820,6 +4833,7 @@ fu_engine_finalize (GObject *obj) if (self->coldplug_id != 0) g_source_remove (self->coldplug_id); + g_free (self->host_machine_id); g_object_unref (self->idle); g_object_unref (self->config); g_object_unref (self->smbios); diff --git a/src/fu-engine.h b/src/fu-engine.h index 3fd535f48..2dfa23d57 100644 --- a/src/fu-engine.h +++ b/src/fu-engine.h @@ -49,6 +49,7 @@ gboolean fu_engine_load_plugins (FuEngine *self, GError **error); gboolean fu_engine_get_tainted (FuEngine *self); const gchar *fu_engine_get_host_product (FuEngine *self); +const gchar *fu_engine_get_host_machine_id (FuEngine *self); FwupdStatus fu_engine_get_status (FuEngine *self); XbSilo *fu_engine_get_silo_from_blob (FuEngine *self, GBytes *blob_cab, diff --git a/src/fu-main.c b/src/fu-main.c index fa1e02b3b..8feedc3bd 100644 --- a/src/fu-main.c +++ b/src/fu-main.c @@ -1366,6 +1366,9 @@ fu_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender, if (g_strcmp0 (property_name, "HostProduct") == 0) return g_variant_new_string (fu_engine_get_host_product (priv->engine)); + if (g_strcmp0 (property_name, "HostMachineId") == 0) + return g_variant_new_string (fu_engine_get_host_machine_id (priv->engine)); + /* return an error */ g_set_error (error, G_DBUS_ERROR, diff --git a/src/org.freedesktop.fwupd.xml b/src/org.freedesktop.fwupd.xml index a61025153..4db832aab 100644 --- a/src/org.freedesktop.fwupd.xml +++ b/src/org.freedesktop.fwupd.xml @@ -33,6 +33,17 @@ + + + + + + The machine ID for the host. + + + + + From bb59837ae0cd4ecfb7ab22e71cef172efdca4805 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 18 Sep 2019 10:15:34 +0100 Subject: [PATCH 149/158] Add a new plugin to update Conexant audio devices --- contrib/fwupd.spec.in | 1 + plugins/meson.build | 1 + plugins/synaptics-cxaudio/README.md | 44 + .../fu-plugin-synaptics-cxaudio.c | 45 + .../fu-synaptics-cxaudio-common.h | 100 ++ .../fu-synaptics-cxaudio-device.c | 877 ++++++++++++++++++ .../fu-synaptics-cxaudio-device.h | 23 + .../fu-synaptics-cxaudio-firmware.c | 296 ++++++ .../fu-synaptics-cxaudio-firmware.h | 36 + plugins/synaptics-cxaudio/meson.build | 28 + .../synaptics-cxaudio/synaptics-cxaudio.quirk | 17 + 11 files changed, 1468 insertions(+) create mode 100644 plugins/synaptics-cxaudio/README.md create mode 100644 plugins/synaptics-cxaudio/fu-plugin-synaptics-cxaudio.c create mode 100644 plugins/synaptics-cxaudio/fu-synaptics-cxaudio-common.h create mode 100644 plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.c create mode 100644 plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.h create mode 100644 plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.c create mode 100644 plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.h create mode 100644 plugins/synaptics-cxaudio/meson.build create mode 100644 plugins/synaptics-cxaudio/synaptics-cxaudio.quirk diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index dccadf9bd..ea18b800b 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -331,6 +331,7 @@ rm ${RPM_BUILD_ROOT}%{_sbindir}/flashrom %if 0%{?have_dell} %{_libdir}/fwupd-plugins-3/libfu_plugin_synapticsmst.so %endif +%{_libdir}/fwupd-plugins-3/libfu_plugin_synaptics_cxaudio.so %{_libdir}/fwupd-plugins-3/libfu_plugin_synaptics_prometheus.so %if 0%{?enable_dummy} %{_libdir}/fwupd-plugins-3/libfu_plugin_test.so diff --git a/plugins/meson.build b/plugins/meson.build index edc3519ae..a0bec45a3 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -9,6 +9,7 @@ subdir('nitrokey') subdir('rts54hid') subdir('rts54hub') subdir('solokey') +subdir('synaptics-cxaudio') subdir('synaptics-prometheus') subdir('test') subdir('udev') diff --git a/plugins/synaptics-cxaudio/README.md b/plugins/synaptics-cxaudio/README.md new file mode 100644 index 000000000..4a9779fd1 --- /dev/null +++ b/plugins/synaptics-cxaudio/README.md @@ -0,0 +1,44 @@ +Conexant Audio Support +====================== + +Introduction +------------ + +This plugin is used to update a small subset of Conexant (now owned by Synaptics) +audio devices. + +Firmware Format +--------------- + +The daemon will decompress the cabinet archive and extract a firmware blob in +a modified SREC file format. + +This plugin supports the following protocol ID: + + * com.synaptics.synaptics-cxaudio + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_17EF&PID_3083&REV_0001` + * `USB\VID_17EF&PID_3083` + * `USB\VID_17EF` + +These devices also use custom GUID values, e.g. + + * `SYNAPTICS_CXAUDIO\CX2198X` + * `SYNAPTICS_CXAUDIO\CX21985` + +Quirk Use +--------- + +This plugin uses the following plugin-specific quirks: + +| Quirk | Description | Minimum fwupd version | +|----------------------------|----------------------------------|-----------------------| +| `ChipIdBase` | Base integer for ChipID | 1.3.2 | +| `IsSoftwareResetSupported` | If the chip supports self-reset | 1.3.2 | +| `EepromPatchValidAddr` | Address of patch location #1 | 1.3.2 | +| `EepromPatch2ValidAddr` | Address of patch location #2 | 1.3.2 | diff --git a/plugins/synaptics-cxaudio/fu-plugin-synaptics-cxaudio.c b/plugins/synaptics-cxaudio/fu-plugin-synaptics-cxaudio.c new file mode 100644 index 000000000..53a0e78ca --- /dev/null +++ b/plugins/synaptics-cxaudio/fu-plugin-synaptics-cxaudio.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" + +#include "fu-synaptics-cxaudio-device.h" + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); +} + +gboolean +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *device, GError **error) +{ + g_autoptr(FuSynapticsCxaudioDevice) dev = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + dev = fu_synaptics_cxaudio_device_new (device); + locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + return TRUE; +} + +gboolean +fu_plugin_update (FuPlugin *plugin, + FuDevice *device, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + return fu_device_write_firmware (device, blob_fw, flags, error); +} diff --git a/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-common.h b/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-common.h new file mode 100644 index 000000000..ca2b13592 --- /dev/null +++ b/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-common.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2005-2019 Synaptics Incorporated + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +/* usb */ +#define FU_SYNAPTICS_CXAUDIO_INPUT_REPORT_SIZE 35 +#define FU_SYNAPTICS_CXAUDIO_OUTPUT_REPORT_SIZE 39 +#define FU_SYNAPTICS_CXAUDIO_HID_INTERFACE 0x03 +#define FU_SYNAPTICS_CXAUDIO_USB_TIMEOUT 2000 /* ms */ + +/* commands */ +#define FU_SYNAPTICS_CXAUDIO_MEM_WRITEID 0x4 +#define FU_SYNAPTICS_CXAUDIO_MEM_READID 0x5 + +typedef enum { + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_UNKNOWN, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX20562 = 20562, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2070x = 20700, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2077x = 20770, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2076x = 20760, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2085x = 20850, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2089x = 20890, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2098x = 20980, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2198x = 21980, + FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_LAST +} FuSynapticsCxaudioDeviceKind; + +typedef enum { + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_RAM, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_ROM, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_LAST +} FuSynapticsCxaudioMemKind; + +/* EEPROM */ +#define FU_SYNAPTICS_CXAUDIO_EEPROM_VALIDITY_SIGNATURE_OFFSET 0x0000 +#define FU_SYNAPTICS_CXAUDIO_EEPROM_CUSTOM_INFO_OFFSET 0x0020 +#define FU_SYNAPTICS_CXAUDIO_EEPROM_CPX_PATCH_VERSION_ADDRESS 0x0022 +#define FU_SYNAPTICS_CXAUDIO_EEPROM_CPX_PATCH2_VERSION_ADDRESS 0x0176 +#define FU_SYNAPTICS_CXAUDIO_EEPROM_STORAGE_SIZE_ADDRESS 0x0005 +#define FU_SYNAPTICS_CXAUDIO_EEPROM_STORAGE_PADDING_SIZE 0x4 /* bytes */ + +#define FU_SYNAPTICS_CXAUDIO_DEVICE_CAPABILITIES_STRIDX 50 +#define FU_SYNAPTICS_CXAUDIO_DEVICE_CAPABILITIES_BYTE 0x03 +#define FU_SYNAPTICS_CXAUDIO_MAGIC_BYTE 'L' +#define FU_SYNAPTICS_CXAUDIO_SIGNATURE_BYTE 'S' +#define FU_SYNAPTICS_CXAUDIO_SIGNATURE_PATCH_BYTE 'P' +#define FU_SYNAPTICS_CXAUDIO_REG_FIRMWARE_PARK_ADDR 0x1000 +#define FU_SYNAPTICS_CXAUDIO_REG_FIRMWARE_VERSION_ADDR 0x1001 +#define FU_SYNAPTICS_CXAUDIO_REG_RESET_ADDR 0x0400 +#define FU_SYNAPTICS_CXAUDIO_EEPROM_SHADOW_SIZE (8 * 1024) + +typedef guint16 FuSynapticsCxaudioEepromPtr; +typedef struct __attribute__ ((packed)) { + FuSynapticsCxaudioEepromPtr PatchVersionStringAddress; + guint8 CpxPatchVersion[3]; + guint8 SpxPatchVersion[4]; + guint8 LayoutSignature; + guint8 LayoutVersion; + guint8 ApplicationStatus; + guint16 VendorID; + guint16 ProductID; + guint16 RevisionID; + FuSynapticsCxaudioEepromPtr LanguageStringAddress; + FuSynapticsCxaudioEepromPtr ManufacturerStringAddress; + FuSynapticsCxaudioEepromPtr ProductStringAddress; + FuSynapticsCxaudioEepromPtr SerialNumberStringAddress; +} FuSynapticsCxaudioEepromCustomInfo; + +#define FU_SYNAPTICS_CXAUDIO_EEPROM_APP_STATUS_ADDRESS (FU_SYNAPTICS_CXAUDIO_EEPROM_CUSTOM_INFO_OFFSET + G_STRUCT_OFFSET(FuSynapticsCxaudioEepromCustomInfo, ApplicationStatus)) +#define FU_SYNAPTICS_CXAUDIO_EEPROM_LAYOUT_SIGNATURE_ADDRESS (FU_SYNAPTICS_CXAUDIO_EEPROM_CUSTOM_INFO_OFFSET + G_STRUCT_OFFSET(FuSynapticsCxaudioEepromCustomInfo, LayoutSignature)) +#define FU_SYNAPTICS_CXAUDIO_EEPROM_LAYOUT_VERSION_ADDRESS (FU_SYNAPTICS_CXAUDIO_EEPROM_CUSTOM_INFO_OFFSET + G_STRUCT_OFFSET(FuSynapticsCxaudioEepromCustomInfo, LayoutVersion)) + +typedef struct __attribute__ ((packed)) { + guint8 Length; + guint8 Type; +} FuSynapticsCxaudioEepromStringHeader; + +typedef struct __attribute__ ((packed)) { + guint8 PatchSignature; + FuSynapticsCxaudioEepromPtr PatchAddress; +} FuSynapticsCxaudioEepromPatchInfo; + +typedef struct __attribute__ ((packed)) { + guint8 MagicByte; + guint8 EeepromSizeCode; +} FuSynapticsCxaudioEepromValiditySignature; + +#define FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_INFO_OFFSET 0x0014 +#define FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_INFO_SIZE (sizeof(FuSynapticsCxaudioEepromPatchInfo)) +#define FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_SIGNATURE_ADDRESS (FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_INFO_OFFSET + G_STRUCT_OFFSET(FuSynapticsCxaudioEepromPatchInfo, PatchSignature)) +#define FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_PTR_ADDRESS (FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_INFO_OFFSET + G_STRUCT_OFFSET(FuSynapticsCxaudioEepromPatchInfo, PatchAddress)) +#define FU_SYNAPTICS_CXAUDIO_FIRMWARE_SIGNATURE_OFFSET (FU_SYNAPTICS_CXAUDIO_EEPROM_VALIDITY_SIGNATURE_OFFSET + sizeof(FuSynapticsCxaudioEepromValiditySignature)) diff --git a/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.c b/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.c new file mode 100644 index 000000000..71ac0d541 --- /dev/null +++ b/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.c @@ -0,0 +1,877 @@ +/* + * Copyright (C) 2005-2019 Synaptics Incorporated + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-chunk.h" +#include "fu-srec-firmware.h" + +#include "fu-synaptics-cxaudio-common.h" +#include "fu-synaptics-cxaudio-device.h" +#include "fu-synaptics-cxaudio-firmware.h" + +struct _FuSynapticsCxaudioDevice +{ + FuUsbDevice parent_instance; + guint32 chip_id_base; + guint32 chip_id; + gboolean serial_number_set; + gboolean sw_reset_supported; + guint32 eeprom_layout_version; + guint32 eeprom_patch2_valid_addr; + guint32 eeprom_patch_valid_addr; + guint32 eeprom_storage_address; + guint32 eeprom_storage_sz; + guint32 eeprom_sz; + guint8 patch_level; +}; + +G_DEFINE_TYPE (FuSynapticsCxaudioDevice, fu_synaptics_cxaudio_device, FU_TYPE_USB_DEVICE) + +static void +fu_synaptics_cxaudio_device_to_string (FuDevice *device, guint idt, GString *str) +{ + FuSynapticsCxaudioDevice *self = FU_SYNAPTICS_CXAUDIO_DEVICE (device); + fu_common_string_append_ku (str, idt, "ChipIdBase", self->chip_id_base); + fu_common_string_append_ku (str, idt, "ChipId", self->chip_id); + fu_common_string_append_kx (str, idt, "EepromLayoutVersion", self->eeprom_layout_version); + fu_common_string_append_kx (str, idt, "EepromStorageAddress", self->eeprom_storage_address); + fu_common_string_append_kx (str, idt, "EepromStorageSz", self->eeprom_storage_sz); + fu_common_string_append_kx (str, idt, "EepromSz", self->eeprom_sz); + fu_common_string_append_kb (str, idt, "SwResetSupported", self->sw_reset_supported); + fu_common_string_append_kb (str, idt, "SerialNumberSet", self->serial_number_set); +} + +static gboolean +fu_synaptics_cxaudio_device_open (FuUsbDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + + /* get firmware version */ + if (!g_usb_device_claim_interface (usb_device, FU_SYNAPTICS_CXAUDIO_HID_INTERFACE, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error (error, "failed to claim interface: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_cxaudio_device_output_report (FuSynapticsCxaudioDevice *self, + guint8 *buf, + guint16 bufsz, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + guint16 report_number = buf[0]; + gsize actual_length = 0; + + g_return_val_if_fail (buf != NULL, FALSE); + g_return_val_if_fail (bufsz != 0, FALSE); + + /* weird */ + if (report_number == 0x0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "report 0 not supported"); + return FALSE; + } + + /* to device */ + if (g_getenv ("FWUPD_SYNAPTICS_CXAUDIO_VERBOSE") != NULL) + fu_common_dump_raw (G_LOG_DOMAIN, "HID::WRITE", buf, bufsz); + if (!g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, + HID_REPORT_SET, + (HID_REPORT_TYPE_OUTPUT << 8) | report_number, + FU_SYNAPTICS_CXAUDIO_HID_INTERFACE, + buf, bufsz, &actual_length, + FU_SYNAPTICS_CXAUDIO_USB_TIMEOUT, NULL, error)) { + return FALSE; + } + if (bufsz != actual_length) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "wrote 0x%x bytes of 0x%x", + (guint) actual_length, bufsz); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_cxaudio_device_input_report (FuSynapticsCxaudioDevice *self, + guint8 ReportID, + guint8 *buf, + guint16 bufsz, + GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (self)); + gsize actual_length = 0; + + g_return_val_if_fail (buf != NULL, FALSE); + g_return_val_if_fail (bufsz != 0, FALSE); + + /* from device */ + if (!g_usb_device_control_transfer (usb_device, + G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, + G_USB_DEVICE_REQUEST_TYPE_CLASS, + G_USB_DEVICE_RECIPIENT_INTERFACE, + HID_REPORT_GET, + (HID_REPORT_TYPE_INPUT << 8) | ReportID, + FU_SYNAPTICS_CXAUDIO_HID_INTERFACE, + buf, bufsz, &actual_length, + FU_SYNAPTICS_CXAUDIO_USB_TIMEOUT, NULL, error)) { + return FALSE; + } + if (g_getenv ("FWUPD_SYNAPTICS_CXAUDIO_VERBOSE") != NULL) + fu_common_dump_raw (G_LOG_DOMAIN, "HID::READ", buf, bufsz); + if (bufsz != actual_length) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "read 0x%x bytes of expected 0x%x", + (guint) actual_length, bufsz); + return FALSE; + } + + /* success */ + return TRUE; +} + +typedef enum { + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, + FU_SYNAPTICS_CXAUDIO_OPERATION_LAST +} FuSynapticsCxaudioOperation; + +typedef enum { + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE = 0, + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_VERIFY = (1 << 4), +} FuSynapticsCxaudioOperationFlags; + +static gboolean +fu_synaptics_cxaudio_device_operation (FuSynapticsCxaudioDevice *self, + FuSynapticsCxaudioOperation operation, + FuSynapticsCxaudioMemKind mem_kind, + guint32 addr, + guint8 *buf, + guint32 bufsz, + FuSynapticsCxaudioOperationFlags flags, + GError **error) +{ + const guint32 idx_read = 0x1; + const guint32 idx_write = 0x5; + const guint32 payload_max = 0x20; + guint32 size = 0x02800; + g_autoptr(GPtrArray) chunks = NULL; + + g_return_val_if_fail (bufsz > 0, FALSE); + g_return_val_if_fail (buf != NULL, FALSE); + + /* check if memory operation is supported by device */ + if (operation == FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE && + mem_kind == FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_ROM) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "trying to write unwritable section %u", + mem_kind); + return FALSE; + } + + /* check memory address - should be within valid range */ + if (mem_kind == FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM) + size = 0x20000; + if (addr > size) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "address out of range 0x%x < 0x%x", + addr, size); + return FALSE; + } + + /* send to hardware */ + chunks = fu_chunk_array_new (buf, bufsz, addr, 0x0, payload_max); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chunk = g_ptr_array_index (chunks, i); + guint8 inbuf[FU_SYNAPTICS_CXAUDIO_INPUT_REPORT_SIZE] = { 0 }; + guint8 outbuf[FU_SYNAPTICS_CXAUDIO_OUTPUT_REPORT_SIZE] = { 0 }; + + /* first byte is always report ID */ + outbuf[0] = FU_SYNAPTICS_CXAUDIO_MEM_WRITEID; + + /* set memory address and payload length (if relevant) */ + if (chunk->address >= 64 * 1024) + outbuf[1] |= 1 << 4; + outbuf[2] = chunk->data_sz; + fu_common_write_uint16 (outbuf + 3, chunk->address, G_BIG_ENDIAN); + + /* set memtype */ + if (mem_kind == FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM) + outbuf[1] |= 1 << 5; + + /* fill the report payload part */ + if (operation == FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE) { + outbuf[1] |= 1 << 6; + if (!fu_memcpy_safe (outbuf, sizeof(outbuf), idx_write, /* dst */ + chunk->data, chunk->data_sz, 0x0, /* src */ + chunk->data_sz, error)) + return FALSE; + } + if (!fu_synaptics_cxaudio_device_output_report (self, outbuf, sizeof(outbuf), error)) + return FALSE; + + /* issue additional write directive to read */ + if (operation == FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE && + flags & FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_VERIFY) { + outbuf[1] &= ~(1 << 6); + if (!fu_synaptics_cxaudio_device_output_report (self, outbuf, sizeof(outbuf), error)) + return FALSE; + } + if (operation == FU_SYNAPTICS_CXAUDIO_OPERATION_READ || + flags & FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_VERIFY) { + if (!fu_synaptics_cxaudio_device_input_report (self, + FU_SYNAPTICS_CXAUDIO_MEM_READID, + inbuf, sizeof(inbuf), + error)) + return FALSE; + } + if (operation == FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE && + flags & FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_VERIFY) { + if (!fu_common_bytes_compare_raw (outbuf + idx_write, payload_max, + inbuf + idx_read, payload_max, + error)) { + g_prefix_error (error, + "failed to verify on packet %u @0x%x: ", + chunk->idx, chunk->address); + return FALSE; + } + } + if (operation == FU_SYNAPTICS_CXAUDIO_OPERATION_READ) { + if (!fu_memcpy_safe ((guint8 *) chunk->data, chunk->data_sz, 0x0, /* dst */ + inbuf, sizeof(inbuf), idx_read, /* src */ + chunk->data_sz, error)) + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_cxaudio_device_register_clear_bit (FuSynapticsCxaudioDevice *self, + guint32 address, + guint8 bit_position, + GError **error) +{ + guint8 tmp = 0x0; + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_RAM, + address, &tmp, sizeof(tmp), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) + return FALSE; + tmp &= ~(1 << bit_position); + return fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_RAM, + address, + &tmp, sizeof(guint8), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error); +} + +static gboolean +fu_synaptics_cxaudio_device_register_set_bit (FuSynapticsCxaudioDevice *self, + guint32 address, + guint8 bit_position, + GError **error) +{ + guint8 tmp = 0x0; + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_RAM, + address, &tmp, sizeof(tmp), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) + return FALSE; + tmp |= 1 << bit_position; + return fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_RAM, + address, &tmp, sizeof(tmp), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error); +} + +static gchar * +fu_synaptics_cxaudio_device_eeprom_read_string (FuSynapticsCxaudioDevice *self, + guint32 address, + GError **error) +{ + FuSynapticsCxaudioEepromStringHeader header = { 0 }; + g_autofree gchar *str = NULL; + + /* read header */ + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + address, + (guint8 *) &header, sizeof(header), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error (error, + "failed to read EEPROM string header @0x%x: ", + address); + return NULL; + } + + /* sanity check */ + if (header.Type != FU_SYNAPTICS_CXAUDIO_DEVICE_CAPABILITIES_BYTE) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "EEPROM string header type invalid"); + return NULL; + } + if (header.Length < sizeof(header)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "EEPROM string header length invalid"); + return NULL; + } + + /* allocate buffer + NUL terminator */ + str = g_malloc0 (header.Length - sizeof(header) + 1); + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + address + sizeof(header), + (guint8 *) str, + header.Length - sizeof(header), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error (error, + "failed to read EEPROM string @0x%x: ", + address); + return NULL; + } + return g_steal_pointer (&str); +} + +static gboolean +fu_synaptics_cxaudio_device_reset (FuSynapticsCxaudioDevice *self, GError **error) +{ + guint8 tmp = 1 << 6; + g_autoptr(GError) error_local = NULL; + + /* is disabled on EVK board using jumper */ + if (!self->sw_reset_supported) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "software reset is not supported"); + return FALSE; + } + + /* this fails on success */ + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_RAM, + FU_SYNAPTICS_CXAUDIO_REG_RESET_ADDR, &tmp, sizeof(tmp), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + if (g_error_matches (error_local, + G_USB_DEVICE_ERROR, + G_USB_DEVICE_ERROR_FAILED)) { + return TRUE; + } + g_propagate_error (error, g_steal_pointer (&error_local)); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_synaptics_cxaudio_device_ensure_patch_level (FuSynapticsCxaudioDevice *self, GError **error) +{ + guint8 tmp = 0x0; + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + self->eeprom_patch_valid_addr, + &tmp, sizeof(tmp), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error (error, "failed to read EEPROM patch validation byte: "); + return FALSE; + } + if (tmp == FU_SYNAPTICS_CXAUDIO_SIGNATURE_PATCH_BYTE) { + self->patch_level = 1; + return TRUE; + } + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + self->eeprom_patch2_valid_addr, + &tmp, sizeof(tmp), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error (error, "failed to read EEPROM patch validation byte: "); + return FALSE; + } + if (tmp == FU_SYNAPTICS_CXAUDIO_SIGNATURE_PATCH_BYTE) { + self->patch_level = 2; + return TRUE; + } + + /* not sure what to do here */ + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "EEPROM patch version undiscoverable"); + return FALSE; +} + +static gboolean +fu_synaptics_cxaudio_device_setup (FuDevice *device, GError **error) +{ + FuSynapticsCxaudioDevice *self = FU_SYNAPTICS_CXAUDIO_DEVICE (device); + GUsbDevice *usb_device = fu_usb_device_get_dev (FU_USB_DEVICE (device)); + FuSynapticsCxaudioEepromCustomInfo cinfo = { 0x0 }; + guint32 addr; + guint8 chip_id_offset = 0x0; + guint8 sigbuf[2] = { 0x0 }; + guint8 verbuf_fw[4] = { 0x0 }; + guint8 verbuf_patch[3] = { 0x0 }; + g_autofree gchar *cap_str = NULL; + g_autofree gchar *chip_id = NULL; + g_autofree gchar *summary = NULL; + g_autofree gchar *version_fw = NULL; + g_autofree gchar *version_patch = NULL; + + /* get the ChipID */ + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_RAM, + 0x1005, + &chip_id_offset, sizeof(chip_id_offset), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error (error, "failed to read ChipID: "); + return FALSE; + } + self->chip_id = self->chip_id_base + chip_id_offset; + chip_id = g_strdup_printf ("SYNAPTICS_CXAUDIO\\CX%u", self->chip_id); + fu_device_add_instance_id (device, chip_id); + + /* set summary */ + summary = g_strdup_printf ("CX%u USB audio device", self->chip_id); + fu_device_set_summary (device, summary); + + /* read the EEPROM validity signature */ + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + FU_SYNAPTICS_CXAUDIO_EEPROM_VALIDITY_SIGNATURE_OFFSET, + sigbuf, sizeof(sigbuf), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error (error, "failed to read EEPROM signature bytes: "); + return FALSE; + } + + /* blank EEPROM */ + if (sigbuf[0] == 0xff && sigbuf[1] == 0xff) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "EEPROM is missing or blank"); + return FALSE; + } + + /* is disabled on EVK board using jumper */ + if ((sigbuf[0] == 0x00 && sigbuf[1] == 0x00) || + (sigbuf[0] == 0xff && sigbuf[1] == 0x00)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "EEPROM has been disabled using a jumper"); + return FALSE; + } + + /* check magic byte */ + if (sigbuf[0] != FU_SYNAPTICS_CXAUDIO_MAGIC_BYTE) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "EEPROM magic byte invalid, got 0x%02x expected 0x%02x", + sigbuf[0], (guint) FU_SYNAPTICS_CXAUDIO_MAGIC_BYTE); + return FALSE; + } + + /* calculate EEPROM size */ + self->eeprom_sz = (guint32) 1 << (sigbuf[1] + 8); + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + FU_SYNAPTICS_CXAUDIO_EEPROM_STORAGE_SIZE_ADDRESS, + sigbuf, sizeof(sigbuf), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error (error, "failed to read EEPROM signature bytes: "); + return FALSE; + } + self->eeprom_storage_sz = fu_common_read_uint16 (sigbuf, G_LITTLE_ENDIAN); + if (self->eeprom_storage_sz < self->eeprom_sz - FU_SYNAPTICS_CXAUDIO_EEPROM_STORAGE_PADDING_SIZE) { + self->eeprom_storage_address = self->eeprom_sz - \ + self->eeprom_storage_sz - \ + FU_SYNAPTICS_CXAUDIO_EEPROM_STORAGE_PADDING_SIZE; + } + + /* get EEPROM custom info */ + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + FU_SYNAPTICS_CXAUDIO_EEPROM_CUSTOM_INFO_OFFSET, + (guint8 *) &cinfo, sizeof(cinfo), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error (error, "failed to read EEPROM custom info: "); + return FALSE; + } + if (cinfo.LayoutSignature == FU_SYNAPTICS_CXAUDIO_SIGNATURE_BYTE) + self->eeprom_layout_version = cinfo.LayoutVersion; + g_debug ("CpxPatchVersion: %u.%u.%u", + cinfo.CpxPatchVersion[0], + cinfo.CpxPatchVersion[1], + cinfo.CpxPatchVersion[2]); + g_debug ("SpxPatchVersion: %u.%u.%u.%u", + cinfo.SpxPatchVersion[0], + cinfo.SpxPatchVersion[1], + cinfo.SpxPatchVersion[2], + cinfo.SpxPatchVersion[3]); + g_debug ("VendorID: 0x%04x", cinfo.VendorID); + g_debug ("ProductID: 0x%04x", cinfo.ProductID); + g_debug ("RevisionID: 0x%04x", cinfo.RevisionID); + g_debug ("ApplicationStatus: 0x%02x", cinfo.ApplicationStatus); + + /* serial number, which also allows us to recover it after write */ + if (self->eeprom_layout_version >= 0x01) { + self->serial_number_set = cinfo.SerialNumberStringAddress != 0x0; + if (self->serial_number_set) { + g_autofree gchar *tmp = NULL; + tmp = fu_synaptics_cxaudio_device_eeprom_read_string (self, + cinfo.SerialNumberStringAddress, + error); + if (tmp == NULL) + return FALSE; + fu_device_set_serial (device, tmp); + } + } + + /* read fw version */ + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_CPX_RAM, + FU_SYNAPTICS_CXAUDIO_REG_FIRMWARE_VERSION_ADDR, + verbuf_fw, sizeof(verbuf_fw), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error (error, "failed to read EEPROM firmware version: "); + return FALSE; + } + version_fw = g_strdup_printf ("%02X.%02X.%02X.%02X", + verbuf_fw[1], verbuf_fw[0], + verbuf_fw[3], verbuf_fw[2]); + fu_device_set_version_bootloader (device, version_fw); + + /* use a different address if a patch is in use */ + if (self->eeprom_patch_valid_addr != 0x0) { + if (!fu_synaptics_cxaudio_device_ensure_patch_level (self, error)) + return FALSE; + } + addr = self->patch_level == 0 ? FU_SYNAPTICS_CXAUDIO_EEPROM_CPX_PATCH_VERSION_ADDRESS : + FU_SYNAPTICS_CXAUDIO_EEPROM_CPX_PATCH2_VERSION_ADDRESS; + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + addr, verbuf_patch, sizeof(verbuf_patch), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error (error, "failed to read EEPROM patch version: "); + return FALSE; + } + version_patch = g_strdup_printf ("%u.%u.%u", + verbuf_patch[0], verbuf_patch[1], verbuf_patch[2]); + fu_device_set_version (device, version_patch, FWUPD_VERSION_FORMAT_TRIPLET); + + /* find out if patch supports additional capabilities (optional) */ + cap_str = g_usb_device_get_string_descriptor (usb_device, + FU_SYNAPTICS_CXAUDIO_DEVICE_CAPABILITIES_STRIDX, + NULL); + if (cap_str != NULL) { + g_auto(GStrv) split = g_strsplit (cap_str, ";", -1); + for (guint i = 0; split[i] != NULL; i++) { + g_debug ("capability: %s", split[i]); + if (g_strcmp0 (split[i], "RESET") == 0) + self->sw_reset_supported = TRUE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_cxaudio_device_close (FuUsbDevice *device, GError **error) +{ + GUsbDevice *usb_device = fu_usb_device_get_dev (device); + + /* we're done here */ + if (!g_usb_device_release_interface (usb_device, FU_SYNAPTICS_CXAUDIO_HID_INTERFACE, + G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, + error)) { + g_prefix_error (error, "failed to release interface: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_synaptics_cxaudio_device_prepare_firmware (FuDevice *device, + GBytes *fw, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsCxaudioDevice *self = FU_SYNAPTICS_CXAUDIO_DEVICE (device); + guint32 chip_id_base; + g_autoptr(FuFirmware) firmware = fu_synaptics_cxaudio_firmware_new (); + + fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); + if (!fu_firmware_parse (firmware, fw, flags, error)) + return NULL; + chip_id_base = fu_synaptics_cxaudio_firmware_get_devtype (FU_SYNAPTICS_CXAUDIO_FIRMWARE (firmware)); + if (chip_id_base != self->chip_id_base) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "device 0x%04u is incompatible with firmware 0x%04u", + self->chip_id_base, chip_id_base); + return NULL; + } + return g_steal_pointer (&firmware); +} + +static gboolean +fu_synaptics_cxaudio_device_write_firmware (FuDevice *device, + FuFirmware *firmware, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsCxaudioDevice *self = FU_SYNAPTICS_CXAUDIO_DEVICE (device); + GPtrArray *records = fu_srec_firmware_get_records (FU_SREC_FIRMWARE (firmware)); + FuSynapticsCxaudioFileKind file_kind; + + /* check if a patch file fits completely into the EEPROM */ + for (guint i = 0; i < records->len; i++) { + FuSrecFirmwareRecord *rcd = g_ptr_array_index (records, i); + if (rcd->kind == FU_FIRMWARE_SREC_RECORD_KIND_S9_TERMINATION_16) + continue; + if (rcd->kind == FU_FIRMWARE_SREC_RECORD_KIND_LAST) + continue; + if (rcd->addr > self->eeprom_sz) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "EEPROM address 0x%02x is bigger than size 0x%02x", + rcd->addr, self->eeprom_sz); + return FALSE; + } + } + + /* park the FW: run only the basic functionality until the upgrade is over */ + if (!fu_synaptics_cxaudio_device_register_set_bit (self, FU_SYNAPTICS_CXAUDIO_REG_FIRMWARE_PARK_ADDR, 7, error)) + return FALSE; + g_usleep (10 * 1000); + + /* initialize layout signature and version to 0 if transitioning from + * EEPROM layout version 1 => 0 */ + file_kind = fu_synaptics_cxaudio_firmware_get_file_type (FU_SYNAPTICS_CXAUDIO_FIRMWARE (firmware)); + if (file_kind == FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2070X_FW && + self->eeprom_layout_version >= 1 && + fu_synaptics_cxaudio_firmware_get_layout_version (FU_SYNAPTICS_CXAUDIO_FIRMWARE (firmware)) == 0) { + guint8 value = 0; + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + FU_SYNAPTICS_CXAUDIO_EEPROM_LAYOUT_SIGNATURE_ADDRESS, + &value, sizeof(value), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error (error, "failed to initialize layout signature "); + return FALSE; + } + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + FU_SYNAPTICS_CXAUDIO_EEPROM_LAYOUT_VERSION_ADDRESS, + &value, sizeof(value), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error (error, "failed to initialize layout signature "); + return FALSE; + } + g_debug ("initialized layout signature"); + } + + /* perform the actual write */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < records->len; i++) { + FuSrecFirmwareRecord *rcd = g_ptr_array_index (records, i); + if (rcd->kind != FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32) + continue; + g_debug ("writing @0x%04x len:0x%02x", rcd->addr, rcd->buf->len); + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + rcd->addr, + rcd->buf->data, rcd->buf->len, + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_VERIFY, + error)) { + g_prefix_error (error, "failed to write @0x%04x len:0x%02x: ", + rcd->addr, rcd->buf->len); + return FALSE; + } + fu_device_set_progress_full (device, (gsize) i, (gsize) records->len); + } + + /* in case of a full FW upgrade invalidate the old FW patch (if any) + * as it may have not been done by the S37 file */ + if (file_kind == FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2070X_FW) { + FuSynapticsCxaudioEepromPatchInfo pinfo = { 0 }; + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_READ, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_INFO_OFFSET, + (guint8 *) &pinfo, sizeof(pinfo), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error (error, "failed to read EEPROM patch info: "); + return FALSE; + } + if (pinfo.PatchSignature == FU_SYNAPTICS_CXAUDIO_SIGNATURE_PATCH_BYTE) { + memset (&pinfo, 0x0, sizeof(pinfo)); + if (!fu_synaptics_cxaudio_device_operation (self, + FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, + FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, + FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_INFO_OFFSET, + (guint8 *) &pinfo, sizeof(pinfo), + FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, + error)) { + g_prefix_error (error, "failed to write empty EEPROM patch info"); + return FALSE; + } + g_debug ("invalidated old FW patch for CX2070x (RAM) device"); + } + } + + /* unpark the FW */ + if (!fu_synaptics_cxaudio_device_register_clear_bit (self, + FU_SYNAPTICS_CXAUDIO_REG_FIRMWARE_PARK_ADDR, + 7, error)) + return FALSE; + + /* if supported, self reset */ + if (self->sw_reset_supported) { + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + return fu_synaptics_cxaudio_device_reset (self, error); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_synaptics_cxaudio_device_set_quirk_kv (FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuSynapticsCxaudioDevice *self = FU_SYNAPTICS_CXAUDIO_DEVICE (device); + if (g_strcmp0 (key, "ChipIdBase") == 0) { + self->chip_id_base = fu_common_strtoull (value); + return TRUE; + } + if (g_strcmp0 (key, "IsSoftwareResetSupported") == 0) { + self->sw_reset_supported = fu_common_strtoull (value); + return TRUE; + } + if (g_strcmp0 (key, "EepromPatchValidAddr") == 0) { + self->eeprom_patch_valid_addr = fu_common_strtoull (value); + return TRUE; + } + if (g_strcmp0 (key, "EepromPatch2ValidAddr") == 0) { + self->eeprom_patch2_valid_addr = fu_common_strtoull (value); + return TRUE; + } + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_synaptics_cxaudio_device_init (FuSynapticsCxaudioDevice *self) +{ + self->sw_reset_supported = TRUE; + fu_device_add_icon (FU_DEVICE (self), "audio-card"); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_install_duration (FU_DEVICE (self), 3); /* seconds */ + fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); +} + +static void +fu_synaptics_cxaudio_device_class_init (FuSynapticsCxaudioDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); + klass_device->to_string = fu_synaptics_cxaudio_device_to_string; + klass_device->set_quirk_kv = fu_synaptics_cxaudio_device_set_quirk_kv; + klass_device->setup = fu_synaptics_cxaudio_device_setup; + klass_device->write_firmware = fu_synaptics_cxaudio_device_write_firmware; + klass_device->prepare_firmware = fu_synaptics_cxaudio_device_prepare_firmware; + klass_usb_device->open = fu_synaptics_cxaudio_device_open; + klass_usb_device->close = fu_synaptics_cxaudio_device_close; +} + +FuSynapticsCxaudioDevice * +fu_synaptics_cxaudio_device_new (FuUsbDevice *usb_device) +{ + FuSynapticsCxaudioDevice *self = g_object_new (FU_TYPE_SYNAPTICS_CXAUDIO_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (usb_device)); + return self; +} diff --git a/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.h b/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.h new file mode 100644 index 000000000..2e06f540a --- /dev/null +++ b/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define FU_TYPE_SYNAPTICS_CXAUDIO_DEVICE (fu_synaptics_cxaudio_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuSynapticsCxaudioDevice, fu_synaptics_cxaudio_device, FU, SYNAPTICS_CXAUDIO_DEVICE, FuUsbDevice) + +struct _FuSynapticsCxaudioDeviceClass +{ + FuUsbDeviceClass parent_class; +}; + +FuSynapticsCxaudioDevice *fu_synaptics_cxaudio_device_new (FuUsbDevice *usb_device); + +G_END_DECLS diff --git a/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.c b/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.c new file mode 100644 index 000000000..16b81031c --- /dev/null +++ b/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.c @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2005-2019 Synaptics Incorporated + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-common.h" + +#include "fu-synaptics-cxaudio-firmware.h" + +struct _FuSynapticsCxaudioFirmware { + FuSrecFirmwareClass parent_instance; + FuSynapticsCxaudioFileKind file_kind; + FuSynapticsCxaudioDeviceKind device_kind; + FuSynapticsCxaudioEepromCustomInfo cinfo; +}; + +G_DEFINE_TYPE (FuSynapticsCxaudioFirmware, fu_synaptics_cxaudio_firmware, FU_TYPE_SREC_FIRMWARE) + +FuSynapticsCxaudioFileKind +fu_synaptics_cxaudio_firmware_get_file_type (FuSynapticsCxaudioFirmware *self) +{ + g_return_val_if_fail (FU_IS_SYNAPTICS_CXAUDIO_FIRMWARE (self), 0); + return self->file_kind; +} + +FuSynapticsCxaudioDeviceKind +fu_synaptics_cxaudio_firmware_get_devtype (FuSynapticsCxaudioFirmware *self) +{ + g_return_val_if_fail (FU_IS_SYNAPTICS_CXAUDIO_FIRMWARE (self), 0); + return self->device_kind; +} + +guint8 +fu_synaptics_cxaudio_firmware_get_layout_version (FuSynapticsCxaudioFirmware *self) +{ + g_return_val_if_fail (FU_IS_SYNAPTICS_CXAUDIO_FIRMWARE (self), 0); + return self->cinfo.LayoutVersion; +} + +static void +fu_synaptics_cxaudio_firmware_to_string (FuFirmware *firmware, guint idt, GString *str) +{ + FuSynapticsCxaudioFirmware *self = FU_SYNAPTICS_CXAUDIO_FIRMWARE (firmware); + fu_common_string_append_kx (str, idt, "FileKind", self->file_kind); + fu_common_string_append_kx (str, idt, "DeviceKind", self->device_kind); + fu_common_string_append_kx (str, idt, "LayoutSignature", self->cinfo.LayoutSignature); + fu_common_string_append_kx (str, idt, "LayoutVersion", self->cinfo.LayoutVersion); + if (self->cinfo.LayoutVersion >= 1) { + fu_common_string_append_kx (str, idt, "VendorID", self->cinfo.VendorID); + fu_common_string_append_kx (str, idt, "ProductID", self->cinfo.ProductID); + fu_common_string_append_kx (str, idt, "RevisionID", self->cinfo.RevisionID); + } +} + +typedef struct { + const gchar *str; + guint32 addr; + guint32 len; +} FuSynapticsCxaudioFirmwareBadblock; + +static void +fu_synaptics_cxaudio_firmware_badblock_add (GPtrArray *badblocks, const gchar *str, guint32 addr, guint32 len) +{ + FuSynapticsCxaudioFirmwareBadblock *bb = g_new0 (FuSynapticsCxaudioFirmwareBadblock, 1); + g_debug ("created reserved range @0x%04x len:0x%x: %s", addr, len, str); + bb->str = str; + bb->addr = addr; + bb->len = len; + g_ptr_array_add (badblocks, bb); +} + +static gboolean +fu_synaptics_cxaudio_firmware_is_addr_valid (GPtrArray *badblocks, guint32 addr, guint32 len) +{ + for (guint j = 0; j < badblocks->len; j++) { + FuSynapticsCxaudioFirmwareBadblock *bb = g_ptr_array_index (badblocks, j); + if (addr <= bb->addr + bb->len - 1 && + addr + len - 1 >= bb->addr) { + g_debug ("addr @0x%04x len:0x%x invalid " + "as 0x%02x->0x%02x protected: %s", + addr, len, bb->addr, bb->addr + bb->len - 1, bb->str); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_synaptics_cxaudio_firmware_is_record_valid (GPtrArray *badblocks, FuSrecFirmwareRecord *rcd) +{ + /* the entire record is not within an ignored range */ + return fu_synaptics_cxaudio_firmware_is_addr_valid (badblocks, rcd->addr, rcd->buf->len); +} + +static void +fu_synaptics_cxaudio_firmware_avoid_badblocks (GPtrArray *badblocks, GPtrArray *records) +{ + g_autoptr(GPtrArray) records_new = g_ptr_array_new (); + + /* find records that include addresses with blocks we want to avoid */ + for (guint i = 0; i < records->len; i++) { + FuSrecFirmwareRecord *rcd = g_ptr_array_index (records, i); + FuSrecFirmwareRecord *rcd1; + if (rcd->kind != FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32) + continue; + if (fu_synaptics_cxaudio_firmware_is_record_valid (badblocks, rcd)) { + rcd1 = fu_srec_firmware_record_new (rcd->ln, rcd->kind, rcd->addr); + g_byte_array_append (rcd1->buf, rcd->buf->data, rcd->buf->len); + g_ptr_array_add (records_new, rcd1); + continue; + } + g_debug ("splitting record @0x%04x len:0x%x as protected", + rcd->addr, rcd->buf->len); + for (guint j = 0; j < rcd->buf->len; j++) { + if (!fu_synaptics_cxaudio_firmware_is_addr_valid (badblocks, rcd->addr + j, 0x1)) + continue; + rcd1 = fu_srec_firmware_record_new (rcd->ln, rcd->kind, rcd->addr + j); + g_byte_array_append (rcd1->buf, rcd->buf->data + j, 0x1); + g_ptr_array_add (records_new, rcd1); + } + } + + /* swap the old set of records with the new records */ + g_ptr_array_set_size (records, 0); + for (guint i = 0; i < records_new->len; i++) { + FuSrecFirmwareRecord *rcd1 = g_ptr_array_index (records_new, i); + g_ptr_array_add (records, rcd1); + } +} + +static gboolean +fu_synaptics_cxaudio_firmware_parse (FuFirmware *firmware, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error) +{ + FuSynapticsCxaudioFirmware *self = FU_SYNAPTICS_CXAUDIO_FIRMWARE (firmware); + GPtrArray *records = fu_srec_firmware_get_records (FU_SREC_FIRMWARE (firmware)); + guint8 dev_kind_candidate = G_MAXUINT8; + g_autofree guint8 *shadow = g_malloc0 (FU_SYNAPTICS_CXAUDIO_EEPROM_SHADOW_SIZE); + g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (fw); + + /* copy shadow EEPROM */ + for (guint i = 0; i < records->len; i++) { + FuSrecFirmwareRecord *rcd = g_ptr_array_index (records, i); + if (rcd->kind != FU_FIRMWARE_SREC_RECORD_KIND_S3_DATA_32) + continue; + if (rcd->addr > FU_SYNAPTICS_CXAUDIO_EEPROM_SHADOW_SIZE) + continue; + if (!fu_memcpy_safe (shadow, FU_SYNAPTICS_CXAUDIO_EEPROM_SHADOW_SIZE, rcd->addr, /* dst */ + rcd->buf->data, rcd->buf->len, 0x0, /* src */ + rcd->buf->len, error)) + return FALSE; + } + + /* parse EEPROM map */ + if (!fu_memcpy_safe ((guint8 *) &self->cinfo, sizeof(FuSynapticsCxaudioEepromCustomInfo), 0x0, /* dst */ + shadow, FU_SYNAPTICS_CXAUDIO_EEPROM_SHADOW_SIZE, FU_SYNAPTICS_CXAUDIO_EEPROM_CUSTOM_INFO_OFFSET, /* src */ + sizeof(FuSynapticsCxaudioEepromCustomInfo), error)) + return FALSE; + + /* just layout version byte is not enough in case of old CX20562 patch + * files that could have non-zero value of the Layout version */ + if (shadow[FU_SYNAPTICS_CXAUDIO_FIRMWARE_SIGNATURE_OFFSET] == FU_SYNAPTICS_CXAUDIO_SIGNATURE_BYTE) { + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2070x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2070X_FW; + g_debug ("FileKind: CX2070x (FW)"); + } else if (shadow[FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_SIGNATURE_ADDRESS] == FU_SYNAPTICS_CXAUDIO_SIGNATURE_PATCH_BYTE) { + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2070x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2070X_PATCH; + g_debug ("FileKind: CX2070x (Patch)"); + } else { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "CX20562 is not supported"); + return FALSE; + } + for (guint i = records->len - 3; i < records->len; i++) { + FuSrecFirmwareRecord *rcd = g_ptr_array_index (records, i); + if (rcd->kind == FU_FIRMWARE_SREC_RECORD_KIND_S9_TERMINATION_16) + continue; + if (rcd->buf->len < 2) + continue; + if (memcmp (rcd->buf->data, "CX", 2) == 0) { + dev_kind_candidate = rcd->buf->data[2]; + g_debug ("DeviceKind signature suspected 0x%0x", dev_kind_candidate); + break; + } + } + + /* check the signature character to see if it defines the device */ + switch (dev_kind_candidate) { + case '2': /* fallthrough */ /* CX2070x */ + case '4': /* CX2070x-21Z */ + case '6': /* CX2070x-21Z */ + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2070x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2070X_PATCH; + g_debug ("FileKind: CX2070x overwritten from signature"); + break; + case '3': + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2077x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2077X_PATCH; + g_debug ("FileKind: CX2077x overwritten from signature"); + break; + case '5': + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2076x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2076X_PATCH; + g_debug ("FileKind: CX2076x overwritten from signature"); + break; + case '7': + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2085x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2085X_PATCH; + g_debug ("FileKind: CX2085x overwritten from signature"); + break; + case '8': + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2089x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2089X_PATCH; + g_debug ("FileKind: CX2089x overwritten from signature"); + break; + case '9': + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2098x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2098X_PATCH; + g_debug ("FileKind: CX2098x overwritten from signature"); + break; + case 'A': + self->device_kind = FU_SYNAPTICS_CXAUDIO_DEVICE_KIND_CX2198x; + self->file_kind = FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2198X_PATCH; + g_debug ("FileKind: CX2198x overwritten from signature"); + break; + default: + /* probably future devices */ + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "DeviceKind signature invalid 0x%x", dev_kind_candidate); + return FALSE; + } + + /* ignore records with protected content */ + if (self->cinfo.LayoutVersion >= 1) { + g_autoptr(GPtrArray) badblocks = g_ptr_array_new_with_free_func (g_free); + + /* add standard ranges to ignore */ + fu_synaptics_cxaudio_firmware_badblock_add (badblocks, "test mark", 0x00BC, 0x02); + fu_synaptics_cxaudio_firmware_badblock_add (badblocks, "application status", + FU_SYNAPTICS_CXAUDIO_EEPROM_APP_STATUS_ADDRESS, 1); + fu_synaptics_cxaudio_firmware_badblock_add (badblocks, "boot bytes", + FU_SYNAPTICS_CXAUDIO_EEPROM_VALIDITY_SIGNATURE_OFFSET, + sizeof(FuSynapticsCxaudioEepromValiditySignature) + 1); + + /* serial number address and also string pointer itself if set */ + if (self->cinfo.SerialNumberStringAddress != 0x0) { + FuSynapticsCxaudioEepromPtr addr_tmp; + FuSynapticsCxaudioEepromPtr addr_str; + addr_tmp = FU_SYNAPTICS_CXAUDIO_EEPROM_CUSTOM_INFO_OFFSET + + G_STRUCT_OFFSET(FuSynapticsCxaudioEepromCustomInfo, + SerialNumberStringAddress); + fu_synaptics_cxaudio_firmware_badblock_add (badblocks, "serial number", + addr_tmp, sizeof(FuSynapticsCxaudioEepromPtr)); + memcpy (&addr_str, shadow + addr_tmp, sizeof(addr_str)); + fu_synaptics_cxaudio_firmware_badblock_add (badblocks, "serial number data", + addr_str, shadow[addr_str]); + } + fu_synaptics_cxaudio_firmware_avoid_badblocks (badblocks, records); + } + + /* this isn't used, but it seems a good thing to add */ + fu_firmware_add_image (firmware, img); + return TRUE; +} + +static void +fu_synaptics_cxaudio_firmware_init (FuSynapticsCxaudioFirmware *self) +{ +} + +static void +fu_synaptics_cxaudio_firmware_class_init (FuSynapticsCxaudioFirmwareClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass); + klass_firmware->parse = fu_synaptics_cxaudio_firmware_parse; + klass_firmware->to_string = fu_synaptics_cxaudio_firmware_to_string; +} + +FuFirmware * +fu_synaptics_cxaudio_firmware_new (void) +{ + return FU_FIRMWARE (g_object_new (FU_TYPE_SYNAPTICS_CXAUDIO_FIRMWARE, NULL)); +} diff --git a/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.h b/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.h new file mode 100644 index 000000000..4f9e70bf2 --- /dev/null +++ b/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005-2019 Synaptics Incorporated + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-srec-firmware.h" +#include "fu-synaptics-cxaudio-common.h" + +G_BEGIN_DECLS + +#define FU_TYPE_SYNAPTICS_CXAUDIO_FIRMWARE (fu_synaptics_cxaudio_firmware_get_type ()) +G_DECLARE_FINAL_TYPE (FuSynapticsCxaudioFirmware, fu_synaptics_cxaudio_firmware, FU, SYNAPTICS_CXAUDIO_FIRMWARE, FuSrecFirmware) + +typedef enum { + FU_SYNAPTICS_CXAUDIO_FILE_KIND_UNKNOWN, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2070X_FW, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2070X_PATCH, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2077X_PATCH, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2076X_PATCH, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2085X_PATCH, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2089X_PATCH, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2098X_PATCH, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2198X_PATCH, + FU_SYNAPTICS_CXAUDIO_FILE_KIND_LAST +} FuSynapticsCxaudioFileKind; + +FuFirmware *fu_synaptics_cxaudio_firmware_new (void); +FuSynapticsCxaudioFileKind fu_synaptics_cxaudio_firmware_get_file_type (FuSynapticsCxaudioFirmware *self); +FuSynapticsCxaudioDeviceKind fu_synaptics_cxaudio_firmware_get_devtype (FuSynapticsCxaudioFirmware *self); +guint8 fu_synaptics_cxaudio_firmware_get_layout_version (FuSynapticsCxaudioFirmware *self); + +G_END_DECLS diff --git a/plugins/synaptics-cxaudio/meson.build b/plugins/synaptics-cxaudio/meson.build new file mode 100644 index 000000000..850caad94 --- /dev/null +++ b/plugins/synaptics-cxaudio/meson.build @@ -0,0 +1,28 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginSynapticsCxaudio"'] + +install_data(['synaptics-cxaudio.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_synaptics_cxaudio', + fu_hash, + sources : [ + 'fu-plugin-synaptics-cxaudio.c', + 'fu-synaptics-cxaudio-device.c', + 'fu-synaptics-cxaudio-firmware.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + install : true, + install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/synaptics-cxaudio/synaptics-cxaudio.quirk b/plugins/synaptics-cxaudio/synaptics-cxaudio.quirk new file mode 100644 index 000000000..880581f6b --- /dev/null +++ b/plugins/synaptics-cxaudio/synaptics-cxaudio.quirk @@ -0,0 +1,17 @@ +# ThinkPad TBT3-TR Gen 2 dock +[DeviceInstanceId=USB\VID_17EF&PID_3083] +Guid = SYNAPTICS_CXAUDIO\CX2098X + +# ThinkPad TBT3-MS Gen 2 dock +[DeviceInstanceId=USB\VID_17EF&PID_3092] +Guid = SYNAPTICS_CXAUDIO\CX2198X + +[Guid=SYNAPTICS_CXAUDIO\CX2098X] +Plugin = synaptics_cxaudio +ChipIdBase = 20980 + +[Guid=SYNAPTICS_CXAUDIO\CX2198X] +Plugin = synaptics_cxaudio +ChipIdBase = 21980 +EepromPatchValidAddr = 0x0014 +EepromPatch2ValidAddr = 0x0171 From 481929459f3ab0caf39f514d259d833451b239a0 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 24 Sep 2019 10:29:22 -0500 Subject: [PATCH 150/158] trivial: libfwupd: skip tests if machine-id is empty too Ubuntu's buildds seem to have changed and this is causing test suite failures. --- libfwupd/fwupd-self-test.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/libfwupd/fwupd-self-test.c b/libfwupd/fwupd-self-test.c index 5ca972b18..aa31fe425 100644 --- a/libfwupd/fwupd-self-test.c +++ b/libfwupd/fwupd-self-test.c @@ -525,6 +525,8 @@ fwupd_has_system_bus (void) static void fwupd_common_machine_hash_func (void) { + gsize sz = 0; + g_autofree gchar *buf = NULL; g_autofree gchar *mhash1 = NULL; g_autofree gchar *mhash2 = NULL; g_autoptr(GError) error = NULL; @@ -533,6 +535,15 @@ fwupd_common_machine_hash_func (void) g_test_skip ("Missing /etc/machine-id"); return; } + if (!g_file_get_contents ("/etc/machine-id", &buf, &sz, &error)) { + g_test_skip ("/etc/machine-id is unreadable"); + return; + } + + if (sz == 0) { + g_test_skip ("Empty /etc/machine-id"); + return; + } mhash1 = fwupd_build_machine_id ("salt1", &error); g_assert_no_error (error); From e37575192d370518d28830ba8a0422d9c5b91747 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 25 Sep 2019 10:12:28 +0100 Subject: [PATCH 151/158] Do not enumerate physical devices when running the internal self tests We don't need to enumerate USB and UDev devices in the self tests. In the case where the probe fails (due to a permissions error, as not running as root) we don't want the tests to fail. --- src/fu-engine.c | 6 ++++-- src/fu-engine.h | 1 + src/fu-self-test.c | 18 +++++++++--------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/fu-engine.c b/src/fu-engine.c index 191acc855..56c5e4b53 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -4683,10 +4683,12 @@ fu_engine_load (FuEngine *self, FuEngineLoadFlags flags, GError **error) g_signal_connect (self->usb_ctx, "device-removed", G_CALLBACK (fu_engine_usb_device_removed_cb), self); - g_usb_context_enumerate (self->usb_ctx); + if ((flags & FU_ENGINE_LOAD_FLAG_NO_ENUMERATE) == 0) + g_usb_context_enumerate (self->usb_ctx); /* coldplug udev devices */ - fu_engine_enumerate_udev (self); + if ((flags & FU_ENGINE_LOAD_FLAG_NO_ENUMERATE) == 0) + fu_engine_enumerate_udev (self); /* update the db for devices that were updated during the reboot */ if (!fu_engine_update_history_database (self, error)) diff --git a/src/fu-engine.h b/src/fu-engine.h index 2dfa23d57..f02dca219 100644 --- a/src/fu-engine.h +++ b/src/fu-engine.h @@ -32,6 +32,7 @@ G_DECLARE_FINAL_TYPE (FuEngine, fu_engine, FU, ENGINE, GObject) typedef enum { FU_ENGINE_LOAD_FLAG_NONE = 0, FU_ENGINE_LOAD_FLAG_READONLY_FS = 1 << 0, + FU_ENGINE_LOAD_FLAG_NO_ENUMERATE = 1 << 1, /*< private >*/ FU_ENGINE_LOAD_FLAG_LAST } FuEngineLoadFlags; diff --git a/src/fu-self-test.c b/src/fu-self-test.c index ec03344bf..de05772b0 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -98,7 +98,7 @@ fu_engine_generate_md_func (void) g_assert (ret); /* load engine and check the device was found */ - ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NONE, &error); + ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, &error); g_assert_no_error (error); g_assert (ret); fu_device_add_guid (device, "12345678-1234-1234-1234-123456789012"); @@ -777,7 +777,7 @@ fu_engine_device_unlock_func (void) g_autoptr(XbSilo) silo = NULL; /* load engine to get FuConfig set up */ - ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NONE, &error); + ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, &error); g_assert_no_error (error); g_assert (ret); @@ -831,7 +831,7 @@ fu_engine_require_hwid_func (void) fu_engine_set_silo (engine, silo_empty); /* load engine to get FuConfig set up */ - ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NONE, &error); + ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, &error); g_assert_no_error (error); g_assert (ret); @@ -961,7 +961,7 @@ fu_engine_downgrade_func (void) testdatadir = fu_test_get_filename (TESTDATADIR, "."); g_assert (testdatadir != NULL); g_setenv ("FU_SELF_TEST_REMOTES_DIR", testdatadir, TRUE); - ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NONE, &error); + ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpint (fu_engine_get_status (engine), ==, FWUPD_STATUS_IDLE); @@ -1072,7 +1072,7 @@ fu_engine_install_duration_func (void) testdatadir = fu_test_get_filename (TESTDATADIR, "."); g_assert (testdatadir != NULL); g_setenv ("FU_SELF_TEST_REMOTES_DIR", testdatadir, TRUE); - ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NONE, &error); + ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, &error); g_assert_no_error (error); g_assert (ret); @@ -1137,7 +1137,7 @@ fu_engine_history_func (void) testdatadir = fu_test_get_filename (TESTDATADIR, "."); g_assert (testdatadir != NULL); g_setenv ("FU_SELF_TEST_REMOTES_DIR", testdatadir, TRUE); - ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NONE, &error); + ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpint (fu_engine_get_status (engine), ==, FWUPD_STATUS_IDLE); @@ -1266,7 +1266,7 @@ fu_engine_history_inherit (void) testdatadir = fu_test_get_filename (TESTDATADIR, "."); g_assert (testdatadir != NULL); g_setenv ("FU_SELF_TEST_REMOTES_DIR", testdatadir, TRUE); - ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NONE, &error); + ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpint (fu_engine_get_status (engine), ==, FWUPD_STATUS_IDLE); @@ -1377,7 +1377,7 @@ fu_engine_history_error_func (void) testdatadir = fu_test_get_filename (TESTDATADIR, "."); g_assert (testdatadir != NULL); g_setenv ("FU_SELF_TEST_REMOTES_DIR", testdatadir, TRUE); - ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NONE, &error); + ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpint (fu_engine_get_status (engine), ==, FWUPD_STATUS_IDLE); @@ -2206,7 +2206,7 @@ fu_plugin_hash_func (void) g_autoptr(FuPlugin) plugin = fu_plugin_new (); gboolean ret = FALSE; - ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NONE, &error); + ret = fu_engine_load (engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, &error); g_assert_no_error (error); g_assert (ret); From f1accad201b24565f3e19daa09d26b477c3f4b9c Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 25 Sep 2019 09:43:33 +0100 Subject: [PATCH 152/158] Do not ask the user to upload a report if ReportURI is not set Fixes https://github.com/fwupd/fwupd/issues/1400 --- src/fu-util.c | 53 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/src/fu-util.c b/src/fu-util.c index 3a80b8363..e8654c6e1 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -215,6 +215,8 @@ fu_util_perhaps_show_unreported (FuUtilPrivate *priv, GError **error) g_autoptr(GPtrArray) devices = NULL; g_autoptr(GPtrArray) devices_failed = g_ptr_array_new (); g_autoptr(GPtrArray) devices_success = g_ptr_array_new (); + g_autoptr(GPtrArray) remotes = NULL; + g_autoptr(GHashTable) remote_id_uri_map = NULL; /* we don't want to ask anything */ if (priv->no_unreported_check) { @@ -231,23 +233,60 @@ fu_util_perhaps_show_unreported (FuUtilPrivate *priv, GError **error) return FALSE; } + /* create a map of RemoteID to RemoteURI */ + remotes = fwupd_client_get_remotes (priv->client, NULL, error); + if (remotes == NULL) + return FALSE; + remote_id_uri_map = g_hash_table_new (g_str_hash, g_str_equal); + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote = g_ptr_array_index (remotes, i); + if (fwupd_remote_get_id (remote) == NULL) + continue; + if (fwupd_remote_get_report_uri (remote) == NULL) + continue; + g_debug ("adding %s for %s", + fwupd_remote_get_report_uri (remote), + fwupd_remote_get_id (remote)); + g_hash_table_insert (remote_id_uri_map, + (gpointer) fwupd_remote_get_id (remote), + (gpointer) fwupd_remote_get_report_uri (remote)); + } + + /* check that they can be reported */ for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index (devices, i); + FwupdRelease *rel = fwupd_device_get_release_default (dev); + const gchar *remote_id; + const gchar *remote_uri; + if (!fu_util_filter_device (priv, dev)) continue; if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_REPORTED)) continue; if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) continue; - switch (fwupd_device_get_update_state (dev)) { - case FWUPD_UPDATE_STATE_FAILED: + + /* find the RemoteURI to use for the device */ + remote_id = fwupd_release_get_remote_id (rel); + if (remote_id == NULL) { + g_debug ("%s has no RemoteID", fwupd_device_get_id (dev)); + continue; + } + remote_uri = g_hash_table_lookup (remote_id_uri_map, remote_id); + if (remote_uri == NULL) { + g_debug ("%s has no RemoteURI", remote_id); + continue; + } + + /* only send success and failure */ + if (fwupd_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED) { g_ptr_array_add (devices_failed, dev); - break; - case FWUPD_UPDATE_STATE_SUCCESS: + } else if (fwupd_device_get_update_state (dev) == FWUPD_UPDATE_STATE_SUCCESS) { g_ptr_array_add (devices_success, dev); - break; - default: - break; + } else { + g_debug ("ignoring %s with UpdateState %s", + fwupd_device_get_id (dev), + fwupd_update_state_to_string (fwupd_device_get_update_state (dev))); } } From 42ee62ef34b3ea0f79652ae62392637a4c517983 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 24 Sep 2019 14:57:02 -0500 Subject: [PATCH 153/158] trivial: uefi: allow forcing to TPM2 via environment variable Use this environment variable to ensure that TPM2 is used in self tests even if the system has TPM1.2. --- plugins/uefi/fu-self-test.c | 2 ++ plugins/uefi/fu-uefi-pcrs.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/uefi/fu-self-test.c b/plugins/uefi/fu-self-test.c index 75eff39bd..942a8477a 100644 --- a/plugins/uefi/fu-self-test.c +++ b/plugins/uefi/fu-self-test.c @@ -50,6 +50,7 @@ fu_uefi_pcrs_2_0_func (void) g_autoptr(GPtrArray) pcr0s = NULL; g_autoptr(GPtrArray) pcrXs = NULL; const gchar *tpm_server_running = g_getenv ("TPM_SERVER_RUNNING"); + g_setenv ("FWUPD_FORCE_TPM2", "1", TRUE); if (!fu_uefi_pcrs_setup (pcrs, &error)) { if (tpm_server_running == NULL && @@ -65,6 +66,7 @@ fu_uefi_pcrs_2_0_func (void) pcrXs = fu_uefi_pcrs_get_checksums (pcrs, 999); g_assert_nonnull (pcrXs); g_assert_cmpint (pcrXs->len, ==, 0); + g_unsetenv ("FWUPD_FORCE_TPM2"); } static void diff --git a/plugins/uefi/fu-uefi-pcrs.c b/plugins/uefi/fu-uefi-pcrs.c index 3a465da23..818c81459 100644 --- a/plugins/uefi/fu-uefi-pcrs.c +++ b/plugins/uefi/fu-uefi-pcrs.c @@ -188,7 +188,8 @@ fu_uefi_pcrs_setup (FuUefiPcrs *self, GError **error) sysfstpmdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_TPM); devpath = g_build_filename (sysfstpmdir, "tpm0", NULL); fn_pcrs = g_build_filename (devpath, "pcrs", NULL); - if (g_file_test (fn_pcrs, G_FILE_TEST_EXISTS)) { + if (g_file_test (fn_pcrs, G_FILE_TEST_EXISTS) && + g_getenv ("FWUPD_FORCE_TPM2") == NULL) { if (!fu_uefi_pcrs_setup_tpm12 (self, fn_pcrs, error)) return FALSE; From 4e343bc1b9ab7939abc648be073401dd0b553c94 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 24 Sep 2019 14:14:19 -0500 Subject: [PATCH 154/158] trivial: dell: quiet the TSS output This is the same as commit 5206592347b52a2cc6b5f0357416e26c9898e178 --- plugins/dell/fu-plugin-dell.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/dell/fu-plugin-dell.c b/plugins/dell/fu-plugin-dell.c index 812bf8631..0c3e8c7b6 100644 --- a/plugins/dell/fu-plugin-dell.c +++ b/plugins/dell/fu-plugin-dell.c @@ -835,6 +835,8 @@ fu_plugin_init (FuPlugin *plugin) data->smi_obj = g_malloc0 (sizeof (FuDellSmiObj)); if (g_getenv ("FWUPD_DELL_VERBOSE") != NULL) g_setenv ("LIBSMBIOS_C_DEBUG_OUTPUT_ALL", "1", TRUE); + else + g_setenv ("TSS2_LOG", "esys+error,tcti+none", FALSE); if (fu_dell_supported (plugin)) data->smi_obj->smi = dell_smi_factory (DELL_SMI_DEFAULTS); data->smi_obj->fake_smbios = FALSE; From a1f9d5850b0064441d2297f2febed2961eeaef96 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 24 Sep 2019 08:15:47 -0500 Subject: [PATCH 155/158] trivial: uefi, dell: skip self tests with TPM if non-root (Fixes: #1396) There are some packaging problems in some distributions that lead to TSS stack emitting warnings that will fail self tests. These don't occur as root, and furthermore those distributions run CI as root already. And in the dell plugin make it non-fatal to have TPM register read failures in case the system has TPM1.2 not TPM2.0 --- plugins/dell/fu-plugin-dell.c | 5 +++-- plugins/dell/fu-self-test.c | 13 ++++++------- plugins/uefi/fu-self-test.c | 6 ++++++ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/plugins/dell/fu-plugin-dell.c b/plugins/dell/fu-plugin-dell.c index 0c3e8c7b6..b00586f6d 100644 --- a/plugins/dell/fu-plugin-dell.c +++ b/plugins/dell/fu-plugin-dell.c @@ -654,6 +654,7 @@ fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error) struct tpm_status *out = NULL; g_autoptr (FuDevice) dev_alt = NULL; g_autoptr (FuDevice) dev = NULL; + g_autoptr(GError) error_tss = NULL; fu_dell_clear_smi (data->smi_obj); out = (struct tpm_status *) data->smi_obj->output; @@ -749,8 +750,8 @@ fu_plugin_dell_detect_tpm (FuPlugin *plugin, GError **error) "Updating disabled due to TPM ownership"); } /* build GUIDs from TSS strings */ - if (!fu_plugin_dell_add_tpm_model (dev, error)) - return FALSE; + if (!fu_plugin_dell_add_tpm_model (dev, &error_tss)) + g_debug ("could not build instances: %s", error_tss->message); if (!fu_device_setup (dev, error)) return FALSE; diff --git a/plugins/dell/fu-self-test.c b/plugins/dell/fu-self-test.c index 079ce499e..5a1a40915 100644 --- a/plugins/dell/fu-self-test.c +++ b/plugins/dell/fu-self-test.c @@ -103,6 +103,12 @@ fu_plugin_dell_tpm_func (void) g_assert_no_error (error); g_assert (ret); + if (tpm_server_running == NULL && + (getuid () != 0 || geteuid () != 0)) { + g_test_skip ("TPM tests require simulated TPM2.0 running or need root access with physical TPM"); + return; + } + /* inject fake data (no TPM) */ tpm_out.ret = -2; fu_plugin_dell_inject_fake_data (plugin_dell, @@ -127,13 +133,6 @@ fu_plugin_dell_tpm_func (void) (guint32 *) &tpm_out, 0, 0, NULL, TRUE); ret = fu_plugin_dell_detect_tpm (plugin_dell, &error); - if (!ret) { - if (tpm_server_running == NULL && - g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { - g_test_skip ("no physical or simulated TPM 2.0 device available"); - return; - } - } g_assert_true (ret); g_assert_cmpint (devices->len, ==, 2); diff --git a/plugins/uefi/fu-self-test.c b/plugins/uefi/fu-self-test.c index 942a8477a..5142f6d42 100644 --- a/plugins/uefi/fu-self-test.c +++ b/plugins/uefi/fu-self-test.c @@ -52,6 +52,12 @@ fu_uefi_pcrs_2_0_func (void) const gchar *tpm_server_running = g_getenv ("TPM_SERVER_RUNNING"); g_setenv ("FWUPD_FORCE_TPM2", "1", TRUE); + if (tpm_server_running == NULL && + (getuid () != 0 || geteuid () != 0)) { + g_test_skip ("TPM2.0 tests require simulated TPM2.0 running or need root access with physical TPM"); + return; + } + if (!fu_uefi_pcrs_setup (pcrs, &error)) { if (tpm_server_running == NULL && g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { From 59593b0c502f2ef92e2ee64713f95e8c5cbc5f7d Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Fri, 6 Sep 2019 00:25:06 +0100 Subject: [PATCH 156/158] thelio-io: Add a plugin to detach the Thelio IO board This does not use the DFU runtime to detach. --- contrib/fwupd.spec.in | 1 + plugins/meson.build | 1 + plugins/thelio-io/README.md | 17 ++++ plugins/thelio-io/fu-plugin-thelio-io.c | 44 ++++++++++ plugins/thelio-io/fu-thelio-io-device.c | 103 ++++++++++++++++++++++++ plugins/thelio-io/fu-thelio-io-device.h | 19 +++++ plugins/thelio-io/meson.build | 27 +++++++ plugins/thelio-io/thelio-io.quirk | 3 + src/fu-usb-device.c | 49 +++++++++++ src/fu-usb-device.h | 3 + 10 files changed, 267 insertions(+) create mode 100644 plugins/thelio-io/README.md create mode 100644 plugins/thelio-io/fu-plugin-thelio-io.c create mode 100644 plugins/thelio-io/fu-thelio-io-device.c create mode 100644 plugins/thelio-io/fu-thelio-io-device.h create mode 100644 plugins/thelio-io/meson.build create mode 100644 plugins/thelio-io/thelio-io.quirk diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index ea18b800b..7c0341bc6 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -336,6 +336,7 @@ rm ${RPM_BUILD_ROOT}%{_sbindir}/flashrom %if 0%{?enable_dummy} %{_libdir}/fwupd-plugins-3/libfu_plugin_test.so %endif +%{_libdir}/fwupd-plugins-3/libfu_plugin_thelio_io.so %{_libdir}/fwupd-plugins-3/libfu_plugin_thunderbolt.so %{_libdir}/fwupd-plugins-3/libfu_plugin_thunderbolt_power.so %{_libdir}/fwupd-plugins-3/libfu_plugin_udev.so diff --git a/plugins/meson.build b/plugins/meson.build index a0bec45a3..151e106bb 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -12,6 +12,7 @@ subdir('solokey') subdir('synaptics-cxaudio') subdir('synaptics-prometheus') subdir('test') +subdir('thelio-io') subdir('udev') subdir('unifying') subdir('upower') diff --git a/plugins/thelio-io/README.md b/plugins/thelio-io/README.md new file mode 100644 index 000000000..9dbf202a1 --- /dev/null +++ b/plugins/thelio-io/README.md @@ -0,0 +1,17 @@ +Thelio IO Support +================= + +Introduction +------------ + +This plugin is used to detach the Thelio IO device to DFU mode. + +To switch to this mode `1` has to be written to the `bootloader` file +in sysfs. + +GUID Generation +--------------- + +These devices use the standard USB DeviceInstanceId values, e.g. + + * `USB\VID_1209&PID_1776&REV_0001` diff --git a/plugins/thelio-io/fu-plugin-thelio-io.c b/plugins/thelio-io/fu-plugin-thelio-io.c new file mode 100644 index 000000000..1aa7c190e --- /dev/null +++ b/plugins/thelio-io/fu-plugin-thelio-io.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Jeremy Soller + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" + +#include "fu-thelio-io-device.h" + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_REQUIRES_QUIRK, FU_QUIRKS_PLUGIN); +} + +gboolean +fu_plugin_update_detach (FuPlugin *plugin, FuDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + if (!fu_device_detach (device, error)) + return FALSE; + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +gboolean +fu_plugin_usb_device_added (FuPlugin *plugin, FuUsbDevice *device, GError **error) +{ + g_autoptr(FuThelioIoDevice) dev = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + dev = fu_thelio_io_device_new (device); + locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + return TRUE; +} diff --git a/plugins/thelio-io/fu-thelio-io-device.c b/plugins/thelio-io/fu-thelio-io-device.c new file mode 100644 index 000000000..61ac9a12b --- /dev/null +++ b/plugins/thelio-io/fu-thelio-io-device.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Jeremy Soller + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-io-channel.h" +#include "fu-thelio-io-device.h" + +struct _FuThelioIoDevice { + FuUsbDevice parent_instance; +}; + +G_DEFINE_TYPE (FuThelioIoDevice, fu_thelio_io_device, FU_TYPE_USB_DEVICE) + +static gboolean +fu_thelio_io_device_probe (FuDevice *device, GError **error) +{ + const gchar *devpath; + g_autofree gchar *fn = NULL; + g_autofree gchar *buf = NULL; + g_autoptr(GUdevDevice) udev_device = NULL; + + fu_device_add_instance_id (device, "USB\\VID_03EB&PID_2FF4"); + + /* convert GUsbDevice to GUdevDevice */ + udev_device = fu_usb_device_find_udev_device (FU_USB_DEVICE (device), error); + if (udev_device == NULL) + return FALSE; + + devpath = g_udev_device_get_sysfs_path (udev_device); + if (G_UNLIKELY (devpath == NULL)) { + g_set_error_literal (error, + FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Could not determine sysfs path for device"); + return FALSE; + } + + fn = g_build_filename (devpath, "revision", NULL); + if (!g_file_get_contents(fn, &buf, NULL, error)) + return FALSE; + + fu_device_set_version (device, (const gchar *) buf, FWUPD_VERSION_FORMAT_TRIPLET); + + return TRUE; +} + +static gboolean +fu_thelio_io_device_detach (FuDevice *device, GError **error) +{ + const gchar *devpath; + g_autofree gchar *fn = NULL; + g_autoptr(FuIOChannel) io_channel = NULL; + g_autoptr(GUdevDevice) udev_device = NULL; + const guint8 buf[] = { '1', '\n' }; + + /* convert GUsbDevice to GUdevDevice */ + udev_device = fu_usb_device_find_udev_device (FU_USB_DEVICE (device), error); + if (udev_device == NULL) + return FALSE; + fu_device_set_status (device, FWUPD_STATUS_DEVICE_RESTART); + + devpath = g_udev_device_get_sysfs_path (udev_device); + if (G_UNLIKELY (devpath == NULL)) { + g_set_error_literal (error, + FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Could not determine sysfs path for device"); + return FALSE; + } + + fn = g_build_filename (devpath, "bootloader", NULL); + io_channel = fu_io_channel_new_file (fn, error); + if (io_channel == NULL) + return FALSE; + return fu_io_channel_write_raw (io_channel, buf, sizeof(buf), + 500, FU_IO_CHANNEL_FLAG_SINGLE_SHOT, error); +} + +static void +fu_thelio_io_device_init (FuThelioIoDevice *self) +{ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_remove_delay (FU_DEVICE (self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); +} + +static void +fu_thelio_io_device_class_init (FuThelioIoDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + klass_device->probe = fu_thelio_io_device_probe; + klass_device->detach = fu_thelio_io_device_detach; +} + +FuThelioIoDevice * +fu_thelio_io_device_new (FuUsbDevice *device) +{ + FuThelioIoDevice *self = g_object_new (FU_TYPE_THELIO_IO_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; +} diff --git a/plugins/thelio-io/fu-thelio-io-device.h b/plugins/thelio-io/fu-thelio-io-device.h new file mode 100644 index 000000000..2e8bd58e8 --- /dev/null +++ b/plugins/thelio-io/fu-thelio-io-device.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 Richard Hughes + * Copyright (C) 2019 Jeremy Soller + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-plugin.h" + +G_BEGIN_DECLS + +#define FU_TYPE_THELIO_IO_DEVICE (fu_thelio_io_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuThelioIoDevice, fu_thelio_io_device, FU, THELIO_IO_DEVICE, FuUsbDevice) + +FuThelioIoDevice *fu_thelio_io_device_new (FuUsbDevice *device); + +G_END_DECLS diff --git a/plugins/thelio-io/meson.build b/plugins/thelio-io/meson.build new file mode 100644 index 000000000..9f4f85cd0 --- /dev/null +++ b/plugins/thelio-io/meson.build @@ -0,0 +1,27 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginThelioIo"'] + +install_data(['thelio-io.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_thelio_io', + fu_hash, + sources : [ + 'fu-plugin-thelio-io.c', + 'fu-thelio-io-device.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + install : true, + install_dir: plugin_dir, + link_with : [ + libfwupdprivate, + ], + c_args : cargs, + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/thelio-io/thelio-io.quirk b/plugins/thelio-io/thelio-io.quirk new file mode 100644 index 000000000..4a0c5ae06 --- /dev/null +++ b/plugins/thelio-io/thelio-io.quirk @@ -0,0 +1,3 @@ +# System76 Thelio IO +[DeviceInstanceId=USB\VID_1209&PID_1776&REV_0001] +Plugin = thelio_io diff --git a/src/fu-usb-device.c b/src/fu-usb-device.c index fb2daabfc..dfd4bbda6 100644 --- a/src/fu-usb-device.c +++ b/src/fu-usb-device.c @@ -388,6 +388,55 @@ fu_usb_device_set_dev (FuUsbDevice *device, GUsbDevice *usb_device) g_usb_device_get_platform_id (usb_device)); } +/** + * fu_usb_device_find_udev_device: + * @device: A #FuUsbDevice + * @usb_device: A #GUsbDevice, or %NULL + * + * Gets the matching #GUdevDevice for the #GUsbDevice. + * + * Returns: a #GUdevDevice, or NULL if unset or invalid + * + * Since: 1.3.2 + **/ +GUdevDevice * +fu_usb_device_find_udev_device (FuUsbDevice *device, GError **error) +{ + FuUsbDevicePrivate *priv = GET_PRIVATE (device); + g_autoptr(GList) devices = NULL; + g_autoptr(GUdevClient) gudev_client = g_udev_client_new (NULL); + + /* find all tty devices */ + devices = g_udev_client_query_by_subsystem (gudev_client, "usb"); + for (GList *l = devices; l != NULL; l = l->next) { + GUdevDevice *dev = G_UDEV_DEVICE (l->data); + + /* check correct device */ + if (g_udev_device_get_sysfs_attr_as_int (dev, "busnum") != + g_usb_device_get_bus (priv->usb_device)) + continue; + if (g_udev_device_get_sysfs_attr_as_int (dev, "devnum") != + g_usb_device_get_address (priv->usb_device)) + continue; + + /* success */ + g_debug ("USB device %u:%u is %s", + g_usb_device_get_bus (priv->usb_device), + g_usb_device_get_address (priv->usb_device), + g_udev_device_get_sysfs_path (dev)); + return g_object_ref (dev); + } + + /* failure */ + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "could not find sysfs device for %u:%u", + g_usb_device_get_bus (priv->usb_device), + g_usb_device_get_address (priv->usb_device)); + return NULL; +} + /** * fu_usb_device_get_dev: * @device: A #FuUsbDevice diff --git a/src/fu-usb-device.h b/src/fu-usb-device.h index e92f55118..d4a9d9715 100644 --- a/src/fu-usb-device.h +++ b/src/fu-usb-device.h @@ -8,6 +8,7 @@ #include #include +#include #include "fu-plugin.h" @@ -45,5 +46,7 @@ GUsbDevice *fu_usb_device_get_dev (FuUsbDevice *device); void fu_usb_device_set_dev (FuUsbDevice *device, GUsbDevice *usb_device); gboolean fu_usb_device_is_open (FuUsbDevice *device); +GUdevDevice *fu_usb_device_find_udev_device (FuUsbDevice *device, + GError **error); G_END_DECLS From f54ddf42d428e73a23cfe5dae4f9d33f5f1db889 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Sat, 21 Sep 2019 12:36:14 +0100 Subject: [PATCH 157/158] Add support for '' to disambiguate some firmware --- libfwupd/fwupd-enums-private.h | 1 + libfwupd/fwupd-release.c | 47 ++++++++++++++++++++++++++++++++++ libfwupd/fwupd-release.h | 3 +++ libfwupd/fwupd.map | 2 ++ src/fu-util-common.c | 5 ++++ 5 files changed, 58 insertions(+) diff --git a/libfwupd/fwupd-enums-private.h b/libfwupd/fwupd-enums-private.h index 16257aa5f..0216c8ff3 100644 --- a/libfwupd/fwupd-enums-private.h +++ b/libfwupd/fwupd-enums-private.h @@ -31,6 +31,7 @@ G_BEGIN_DECLS #define FWUPD_RESULT_KEY_MODIFIED "Modified" /* t */ #define FWUPD_RESULT_KEY_METADATA "Metadata" /* a{ss} */ #define FWUPD_RESULT_KEY_NAME "Name" /* s */ +#define FWUPD_RESULT_KEY_NAME_VARIANT_SUFFIX "NameVariantSuffix" /* s */ #define FWUPD_RESULT_KEY_PLUGIN "Plugin" /* s */ #define FWUPD_RESULT_KEY_RELEASE "Release" /* a{sv} */ #define FWUPD_RESULT_KEY_REMOTE_ID "RemoteId" /* s */ diff --git a/libfwupd/fwupd-release.c b/libfwupd/fwupd-release.c index ef7408872..d3a8e5c31 100644 --- a/libfwupd/fwupd-release.c +++ b/libfwupd/fwupd-release.c @@ -42,6 +42,7 @@ typedef struct { gchar *appstream_id; gchar *license; gchar *name; + gchar *name_variant_suffix; gchar *summary; gchar *uri; gchar *vendor; @@ -893,6 +894,42 @@ fwupd_release_set_name (FwupdRelease *release, const gchar *name) priv->name = g_strdup (name); } +/** + * fwupd_release_get_name_variant_suffix: + * @release: A #FwupdRelease + * + * Gets the update variant suffix. + * + * Returns: the update variant, or %NULL if unset + * + * Since: 1.3.2 + **/ +const gchar * +fwupd_release_get_name_variant_suffix (FwupdRelease *release) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_val_if_fail (FWUPD_IS_RELEASE (release), NULL); + return priv->name_variant_suffix; +} + +/** + * fwupd_release_set_name_variant_suffix: + * @release: A #FwupdRelease + * @name_variant_suffix: the description + * + * Sets the update variant suffix. + * + * Since: 1.3.2 + **/ +void +fwupd_release_set_name_variant_suffix (FwupdRelease *release, const gchar *name_variant_suffix) +{ + FwupdReleasePrivate *priv = GET_PRIVATE (release); + g_return_if_fail (FWUPD_IS_RELEASE (release)); + g_free (priv->name_variant_suffix); + priv->name_variant_suffix = g_strdup (name_variant_suffix); +} + /** * fwupd_release_get_trust_flags: * @release: A #FwupdRelease @@ -1131,6 +1168,11 @@ fwupd_release_to_variant (FwupdRelease *release) FWUPD_RESULT_KEY_NAME, g_variant_new_string (priv->name)); } + if (priv->name_variant_suffix != NULL) { + g_variant_builder_add (&builder, "{sv}", + FWUPD_RESULT_KEY_NAME_VARIANT_SUFFIX, + g_variant_new_string (priv->name_variant_suffix)); + } if (priv->size != 0) { g_variant_builder_add (&builder, "{sv}", FWUPD_RESULT_KEY_SIZE, @@ -1250,6 +1292,10 @@ fwupd_release_from_key_value (FwupdRelease *release, const gchar *key, GVariant fwupd_release_set_name (release, g_variant_get_string (value, NULL)); return; } + if (g_strcmp0 (key, FWUPD_RESULT_KEY_NAME_VARIANT_SUFFIX) == 0) { + fwupd_release_set_name_variant_suffix (release, g_variant_get_string (value, NULL)); + return; + } if (g_strcmp0 (key, FWUPD_RESULT_KEY_SIZE) == 0) { fwupd_release_set_size (release, g_variant_get_uint64 (value)); return; @@ -1570,6 +1616,7 @@ fwupd_release_finalize (GObject *object) g_free (priv->appstream_id); g_free (priv->license); g_free (priv->name); + g_free (priv->name_variant_suffix); g_free (priv->summary); g_free (priv->uri); g_free (priv->homepage); diff --git a/libfwupd/fwupd-release.h b/libfwupd/fwupd-release.h index afddc8f05..02ea1e898 100644 --- a/libfwupd/fwupd-release.h +++ b/libfwupd/fwupd-release.h @@ -78,6 +78,9 @@ void fwupd_release_set_vendor (FwupdRelease *release, const gchar *fwupd_release_get_name (FwupdRelease *release); void fwupd_release_set_name (FwupdRelease *release, const gchar *name); +const gchar *fwupd_release_get_name_variant_suffix (FwupdRelease *release); +void fwupd_release_set_name_variant_suffix (FwupdRelease *release, + const gchar *name_variant_suffix); const gchar *fwupd_release_get_summary (FwupdRelease *release); void fwupd_release_set_summary (FwupdRelease *release, const gchar *summary); diff --git a/libfwupd/fwupd.map b/libfwupd/fwupd.map index ef7e954a8..99b2446bd 100644 --- a/libfwupd/fwupd.map +++ b/libfwupd/fwupd.map @@ -385,5 +385,7 @@ LIBFWUPD_1.3.2 { fwupd_client_get_host_machine_id; fwupd_release_add_issue; fwupd_release_get_issues; + fwupd_release_get_name_variant_suffix; + fwupd_release_set_name_variant_suffix; local: *; } LIBFWUPD_1.3.1; diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 08cff5a79..16f6524da 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -1073,6 +1073,11 @@ fu_util_release_to_string (FwupdRelease *rel, guint idt) fu_common_string_append_kv (str, idt + 1, _("Summary"), fwupd_release_get_summary (rel)); } + if (fwupd_release_get_name_variant_suffix (rel) != NULL) { + /* TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') */ + fu_common_string_append_kv (str, idt + 1, _("Variant"), + fwupd_release_get_name_variant_suffix (rel)); + } if (fwupd_release_get_license (rel) != NULL) { /* TRANSLATORS: e.g. GPLv2+, Non free etc */ fu_common_string_append_kv (str, idt + 1, _("License"), From 0d8efd237318ef31713a6ee848edb3d1e4757bd1 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 26 Sep 2019 11:05:06 +0100 Subject: [PATCH 158/158] Release fwupd 1.3.2 --- data/org.freedesktop.fwupd.metainfo.xml | 22 +++++ po/af.po | 4 - po/ca.po | 4 - po/cs.po | 4 - po/da.po | 4 - po/de.po | 4 - po/en_GB.po | 4 - po/eo.po | 4 - po/fi.po | 106 +++++++++++++++++++++++- po/fur.po | 4 - po/he.po | 4 - po/hr.po | 4 - po/hu.po | 4 - po/id.po | 70 ++++++++++++++-- po/it.po | 4 - po/ko.po | 4 - po/lt.po | 4 - po/oc.po | 4 - po/pl.po | 4 - po/pt_BR.po | 4 - po/ru.po | 4 - po/sk.po | 4 - po/sr.po | 4 - po/sv.po | 18 ++-- po/uk.po | 4 - po/zh_CN.po | 4 - po/zh_TW.po | 4 - 27 files changed, 197 insertions(+), 111 deletions(-) diff --git a/data/org.freedesktop.fwupd.metainfo.xml b/data/org.freedesktop.fwupd.metainfo.xml index ecf1a8b92..7da863046 100644 --- a/data/org.freedesktop.fwupd.metainfo.xml +++ b/data/org.freedesktop.fwupd.metainfo.xml @@ -32,6 +32,28 @@ fwupdmgr + + +

          This release adds the following features:

          +
            +
          • Add a plugin to detach the Thelio IO board
          • +
          • Add a plugin to update Conexant audio devices
          • +
          • Support issues in AppStream metadata
          • +
          +

          This release fixes the following bugs:

          +
            +
          • Align the key values to the text width not the number of bytes
          • +
          • Display more helpful historical device information
          • +
          • Do not ask the user to upload a report if ReportURI is not set
          • +
          • Do not crash when starting tpm2-abrmd
          • +
          • Ensure HID++ v2.0 peripheral devices get added
          • +
          • Fall back to /var/lib/dbus/machine-id when required
          • +
          • Include all GUIDs when uploading a report
          • +
          • Move D-Bus conf file to datadir/dbus-1/system.d
          • +
          • Update device_modified in sql database during updates
          • +
          +
          +

          This release adds the following features:

          diff --git a/po/af.po b/po/af.po index 573f294a9..b44bd7414 100644 --- a/po/af.po +++ b/po/af.po @@ -358,10 +358,6 @@ msgstr "Gebruikernaam" msgid "Verifying…" msgstr "Verifieer tans…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Weergawe" - #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Wag tans…" diff --git a/po/ca.po b/po/ca.po index db9f8e59d..fce999f6a 100644 --- a/po/ca.po +++ b/po/ca.po @@ -1240,10 +1240,6 @@ msgstr "Venedor" msgid "Verifying…" msgstr "S'està verificant..." -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Versió:" - #. TRANSLATORS: try to help msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" msgstr "AVÍS: S'ignoren les comprovacions estrictes de SSL. Per a fer-ho automàticament en el futur, exporteu DISABLE_SSL_STRICT en el vostre entorn" diff --git a/po/cs.po b/po/cs.po index 8395453c2..a8055e1fb 100644 --- a/po/cs.po +++ b/po/cs.po @@ -843,10 +843,6 @@ msgstr "Uživatelské jméno" msgid "Verifying…" msgstr "Ověřuje se…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Verze" - #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Čeká se…" diff --git a/po/da.po b/po/da.po index 5a32ce1b6..8859a7e0c 100644 --- a/po/da.po +++ b/po/da.po @@ -1240,10 +1240,6 @@ msgstr "Producent" msgid "Verifying…" msgstr "Verificerer …" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Version" - #. TRANSLATORS: try to help msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" msgstr "ADVARSEL: Ignorerer strikse SSL-tjeks. Eksportér DISABLE_SSL_STRICT i dit miljø for at gøre det automatisk i fremtiden" diff --git a/po/de.po b/po/de.po index 0cba4fc49..d22d4ba78 100644 --- a/po/de.po +++ b/po/de.po @@ -782,10 +782,6 @@ msgstr "Benutzername" msgid "Verifying…" msgstr "Überprüfung …" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Version" - #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Warten …" diff --git a/po/en_GB.po b/po/en_GB.po index 6348ad9d7..674207907 100644 --- a/po/en_GB.po +++ b/po/en_GB.po @@ -910,10 +910,6 @@ msgstr "Username" msgid "Verifying…" msgstr "Verifying…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Version" - #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Waiting…" diff --git a/po/eo.po b/po/eo.po index 85e692836..4ae925c6a 100644 --- a/po/eo.po +++ b/po/eo.po @@ -87,7 +87,3 @@ msgstr "Celo" #. TRANSLATORS: currect daemon status is unknown msgid "Unknown" msgstr "Nekonata" - -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Versio" diff --git a/po/fi.po b/po/fi.po index 5cc75f530..28cb31326 100644 --- a/po/fi.po +++ b/po/fi.po @@ -66,6 +66,13 @@ msgstr "%sME päivitys" msgid "%s System Update" msgstr "%sJärjestelmän päivitys" +#. TRANSLATORS: the Thunderbolt controller is a device that +#. * has other high speed Thunderbolt devices plugged into it; +#. * the first %s is the system name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "%s Thunderbolt-ohjaimen päivitys" + #. TRANSLATORS: this is the fallback where we don't know if the release #. * is updating the system, the device, or a device class, or something else #. -- @@ -234,6 +241,10 @@ msgstr "Tämän laitteen firmwaren päivittäminen vaatii tunnistautumisen" msgid "Authentication is required to update the stored checksums for the device" msgstr "Todennus edellyttää laitteen tallennettujen tarkistussummien päivittämistä" +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Käynnistyslataimen versio" + #. TRANSLATORS: command description msgid "Build firmware using a sandbox" msgstr "Rakenna laiteohjelmisto hiekkalaatikon avulla" @@ -291,6 +302,10 @@ msgstr "Muunna firmware DFU-muotoon" msgid "Create a binary patch using two files" msgstr "Luo binäärinen korjaustiedosto käyttäen kahta tiedostoa" +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Nykyinen versio" + msgid "DFU" msgstr "DFU" @@ -322,6 +337,10 @@ msgstr "Irrota tällä hetkellä liitetty DFU-laite" msgid "Detach to bootloader mode" msgstr "Irrota käynnistyslataimen tilaan" +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Tiedot" + #. TRANSLATORS: ID for hardware, typically a SHA1 sum msgid "Device ID" msgstr "Laitteen tunnus" @@ -413,6 +432,10 @@ msgstr "Tyhjennä laiteohjelmiston tiedot" msgid "Dump information about a binary patch to the screen" msgstr "Tulosta tiedot binäärisestä korjaustiedostosta näytölle" +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Kesto" + #. TRANSLATORS: ESP is EFI System Partition msgid "ESP specified was not valid" msgstr "Määritetty ESP ei ollut voimassa" @@ -487,6 +510,10 @@ msgstr "Quirksin lataaminen epäonnistui" msgid "Failed to parse arguments" msgstr "Argumenttien jäsentäminen epäonnistui" +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse flags for --filter" +msgstr "Lippujen erittely ei onnistunut --suodatin" + #. TRANSLATORS: we could not reboot for some reason msgid "Failed to reboot" msgstr "Käynnistys epäonnistui" @@ -519,6 +546,10 @@ msgstr "Tiedostonimi" msgid "Filename Signature" msgstr "Tiedoston allekirjoitus" +#. TRANSLATORS: command line option +msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" +msgstr "Suodata laite lippuja käyttäen ~ etuliiteellä sulkea pois, esim. 'sisäiset, ~ vaatii käynnistyksen'" + #. TRANSLATORS: program name msgid "Firmware Agent" msgstr "Firmware-agentti" @@ -564,6 +595,10 @@ msgstr "Pakota toimenpide huomioimatta kaikki varoitukset" msgid "Found" msgstr "Löydetty" +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Hanki kaikki fwupd -tukemat laiteliput" + #. TRANSLATORS: command description msgid "Get all devices and possible releases" msgstr "Hanki kaikki laitteet ja mahdolliset julkaisut" @@ -611,6 +646,14 @@ msgstr "Henkilöllisyys" msgid "Idle…" msgstr "Jouten…" +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Ohita tiukat SSL tarkistukset tiedostoja ladattaessa" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Asennuksen kesto" + #. TRANSLATORS: command description msgid "Install a firmware blob on a device" msgstr "Asenna laiteohjelmiston merkintä laitteeseen" @@ -654,6 +697,10 @@ msgstr "Avaimet" msgid "Less than one minute remaining" msgstr "Jäljellä on alle minuutti" +#. TRANSLATORS: e.g. GPLv2+, Non free etc +msgid "License" +msgstr "Lisenssi" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Linux-toimittajan laiteohjelmisto (vakaa firmware)" @@ -672,6 +719,10 @@ msgstr "Lista tuetuista firmware-päivityksistä" msgid "Loading…" msgstr "Ladataan…" +#. TRANSLATORS: command line option +msgid "Log output to FILE (typically for scripting use)" +msgstr "Kirjaa tuloste tiedostoon (yleensä komentosarjojen käyttöä varten)" + #. TRANSLATORS: command line option msgid "Manually whitelist specific plugins" msgstr "Luetteloi tiettyjä lisäosia manuaalisesti" @@ -680,6 +731,10 @@ msgstr "Luetteloi tiettyjä lisäosia manuaalisesti" msgid "Merge multiple firmware files into one" msgstr "Yhdistä useita firmware-tiedostoja yhteen" +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Metatietojen allekirjoitus" + #. TRANSLATORS: remote URI msgid "Metadata URI" msgstr "Metatietojen URI" @@ -688,6 +743,10 @@ msgstr "Metatietojen URI" msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Metatiedot voidaan hankkia Linux Vendor Firmware -palvelusta." +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Vähimmäisversio" + #. TRANSLATORS: error message #, c-format msgid "Mismatched daemon and client, use %s instead" @@ -730,10 +789,18 @@ msgstr "Ei havaittu sopivaa laitteistoa firmware päivitykselle" msgid "No plugins found" msgstr "Ei laajennuksia" +#. TRANSLATORS: no repositories to download from +msgid "No releases available" +msgstr "Ei julkaisuja saatavilla" + #. TRANSLATORS: explain why no metadata available msgid "No remotes are currently enabled so no metadata is available." msgstr "Mitään etäyhteyksiä ei ole tällä hetkellä käytössä, joten metatietoja ei ole saatavilla." +#. TRANSLATORS: no repositories to download from +msgid "No remotes available" +msgstr "Kaukosäätimet ei saatavilla" + #. TRANSLATORS: nothing was updated offline msgid "No updates were applied" msgstr "Ei soveltuvia päivityksiä" @@ -897,6 +964,10 @@ msgstr "Ajoitetaan…" msgid "Serial" msgstr "Sarjanumero" +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Sarjanumero" + #. TRANSLATORS: command description msgid "Set alternative name on firmware file" msgstr "Aseta vaihtoehtoinen nimi firmware-tiedostolle" @@ -1002,6 +1073,14 @@ msgstr "Kirjoita ladatut tiedot asiakaan sertifikaatilla" msgid "Signature" msgstr "Allekirjoitus" +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Koko" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Lähde" + msgid "Specify Vendor/Product ID(s) of DFU device" msgstr "Määritä DFU laitteen toimittaja/tuotetunnus(s)" @@ -1061,6 +1140,10 @@ msgstr "UEFI firmware -apuohjelma" msgid "Unknown" msgstr "Tuntematon" +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Tuntematon laite" + msgid "Unlock the device to allow access" msgstr "Sallia pääsy laitteeseen" @@ -1077,6 +1160,19 @@ msgstr "Poista virheenkorjauksen lippu päivityksen aikana" msgid "Unsupported daemon version %s, client version is %s" msgstr "Ei tuettu taustaprosessin versio %s, asiakasversio on %s" +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Päivitys virhe" + +#. TRANSLATORS: helpful messages from last update +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Päivitä viesti" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Päivitä tila" + #. TRANSLATORS: command description msgid "Update all devices that match local metadata" msgstr "Päivitä kaikki paikallisia metatietoja vastaavat laitteet" @@ -1136,13 +1232,17 @@ msgstr "Käytä Quirk-lippuja asennettaessa laiteohjelmistoa" msgid "Username" msgstr "Käyttäjätunnus" +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Toimittaja" + #. TRANSLATORS: verifying we wrote the firmware correctly msgid "Verifying…" msgstr "Vahvistetaan…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Versio" +#. TRANSLATORS: try to help +msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "VAROITUS: SSL tarkistusten sivuuttaminen, jotta se tapahtuu automaattisesti tulevassa DISABLE_SSL_STRICT ympäristössä" #. TRANSLATORS: waiting for device to do something msgid "Waiting…" diff --git a/po/fur.po b/po/fur.po index 82a318e22..a1d34664e 100644 --- a/po/fur.po +++ b/po/fur.po @@ -582,10 +582,6 @@ msgstr "Non utent" msgid "Verifying…" msgstr "Daûr a verificâ…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Version" - #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "In spiete…" diff --git a/po/he.po b/po/he.po index 3f6ca6717..cfff829f4 100644 --- a/po/he.po +++ b/po/he.po @@ -112,7 +112,3 @@ msgstr "הצג מידע ניפוי שגיאות מורחב" #, c-format msgid "Updating %s from %s to %s... " msgstr "מעדכן %s מ־%s ל־%s..." - -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "גרסא" diff --git a/po/hr.po b/po/hr.po index 66c56d3dc..4e2a99bb5 100644 --- a/po/hr.po +++ b/po/hr.po @@ -1241,10 +1241,6 @@ msgstr "Proizvođač" msgid "Verifying…" msgstr "Provjeravanje..." -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Inačica" - #. TRANSLATORS: try to help msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" msgstr "UPOZORENJE: Zanemarivanje SSL ograničenja provjera, kako bi se ovo obavljalo ubuduće automatski postavite DISABLE_SSL_STRICT u vašem radnom okruženju" diff --git a/po/hu.po b/po/hu.po index efecdf312..008af4acd 100644 --- a/po/hu.po +++ b/po/hu.po @@ -1077,10 +1077,6 @@ msgstr "Felhasználónév" msgid "Verifying…" msgstr "Ellenőrzés…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Verzió" - #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Várakozás…" diff --git a/po/id.po b/po/id.po index 99b2d7926..1e6da4e7e 100644 --- a/po/id.po +++ b/po/id.po @@ -3,7 +3,7 @@ # This file is distributed under the same license as the fwupd package. # # Translators: -# Andika Triwidada , 2017-2018 +# Andika Triwidada , 2017-2019 msgid "" msgstr "" "Project-Id-Version: fwupd\n" @@ -15,6 +15,10 @@ msgstr "" "Language: id\n" "Plural-Forms: nplurals=1; plural=0;\n" +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Mengaktifkan pemutakhiran firmware" + #. TRANSLATORS: this is when a device is hotplugged msgid "Added" msgstr "Ditambahkan" @@ -169,6 +173,10 @@ msgstr "Deskripsi" msgid "Detach currently attached DFU capable device" msgstr "Lepas perangkat mampu-DFU yang kini tercantol" +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Rincian" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "Perangkat ditambahkan:" @@ -189,6 +197,10 @@ msgstr "Peranti yang sukses diperbarui:" msgid "Devices that were not updated correctly:" msgstr "Peranti yang tidak diperbarui dengan benar:" +#. TRANSLATORS: command line option +msgid "Display version" +msgstr "Tampilkan versi" + #. TRANSLATORS: command line option msgid "Do not check for old metadata" msgstr "Jangan periksa untuk metadata lama" @@ -232,6 +244,10 @@ msgstr "Curahkan rincian tentang suatu berkas firmware" msgid "Dump information about a binary patch to the screen" msgstr "Curahkan informasi tentang suatu patch biner ke layar" +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Durasi" + #. TRANSLATORS: if the remote is enabled msgid "Enabled" msgstr "Difungsikan" @@ -330,6 +346,10 @@ msgstr "Dapatkan remote-remote yang terkonfigurasi" msgid "Gets the cryptographic hash of the dumped firmware" msgstr "Dapatkan hash kriptografis dari firmware yang dicurahkan" +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware." +msgstr "Dapatkan daftar firmware yang disetujui." + #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" msgstr "Dapatkan daftar pemutakhiran bagi perangkat keras yang tersambung" @@ -372,9 +392,18 @@ msgstr "Pasang firmware sistem yang tak ditandatangani" msgid "Installing firmware update…" msgstr "Sedang memasang pembaruan firmware..." +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Memasang pada %s…" + msgid "Keyring" msgstr "Keyring" +#. TRANSLATORS: e.g. GPLv2+, Non free etc +msgid "License" +msgstr "Lisensi" + #. TRANSLATORS: command description msgid "List currently attached DFU capable devices" msgstr "Tampilkan daftar perangkat mampu-DFU yang kini tercantol" @@ -387,6 +416,10 @@ msgstr "Memuat..." msgid "Merge multiple firmware files into one" msgstr "Gabungkan beberapa berkas firmware menjadi satu" +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Tanda Tangan Metadata" + #. TRANSLATORS: remote URI msgid "Metadata URI" msgstr "URI Metadata" @@ -394,6 +427,10 @@ msgstr "URI Metadata" msgid "Mode" msgstr "Mode" +#. TRANSLATORS: sets something in daemon.conf +msgid "Modifies a daemon configuration value." +msgstr "Mengubah suatu nilai konfigurasi daemon." + #. TRANSLATORS: command description msgid "Modifies a given remote" msgstr "Mengubah suatu remote yang diberikan" @@ -431,6 +468,12 @@ msgstr "Payload" msgid "Permission denied" msgstr "Izin ditolak" +msgid "Print the version number" +msgstr "Cetak nomor versi" + +msgid "Print verbose debug statements" +msgstr "Cetak pernyataan awakutu rinci" + #. TRANSLATORS: the numeric priority msgid "Priority" msgstr "Prioritas" @@ -558,6 +601,10 @@ msgstr "Atur ID vendor pada berkas firmware" msgid "Sets metadata on a firmware file" msgstr "Atur metadata pada suatu berkas firmware" +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware." +msgstr "Tata daftar firmware yang disetujui." + #. TRANSLATORS: command description msgid "Share firmware history with the developers" msgstr "Bagikan riwayat firmware dengan para pengembang" @@ -570,6 +617,10 @@ msgstr "Tampilkan versi daemon dan klien" msgid "Show debugging options" msgstr "Tampilkan opsi pengawakutuan" +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Tampilkan peranti yang tidak dapat dimutakhirkan" + #. TRANSLATORS: command line option msgid "Show extra debugging information" msgstr "Tampilkan informasi pengawakutuan ekstra" @@ -582,6 +633,14 @@ msgstr "Tampilkan riwayat pembaruan firmware" msgid "Show plugin verbose information" msgstr "Tampilkan informasi rinci pengaya" +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Ukuran" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Sumber" + #. TRANSLATORS: device state, i.e. appIDLE msgid "State" msgstr "Keadaan" @@ -644,6 +703,11 @@ msgstr "Mutakhirkan semua firmware ke versi terbaru yang tersedia" msgid "Updating %s from %s to %s... " msgstr "Memutakhirkan %s dari %s ke %s..." +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Memutakhirkan %s…" + #. TRANSLATORS: the server sent the user a small message msgid "Upload message:" msgstr "Pesan unggah:" @@ -660,10 +724,6 @@ msgstr "Nama Pengguna" msgid "Verifying…" msgstr "Verifikasi..." -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Versi" - #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Menunggu..." diff --git a/po/it.po b/po/it.po index 2237cec49..480dd0159 100644 --- a/po/it.po +++ b/po/it.po @@ -1240,10 +1240,6 @@ msgstr "Fornitore" msgid "Verifying…" msgstr "Verifica…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Versione" - #. TRANSLATORS: try to help msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" msgstr "Attenzione: controlli SSL ignorati, per fare ciò automaticamente in futuro, esportare DISABLE_SSL_STRICT nel proprio ambiente" diff --git a/po/ko.po b/po/ko.po index 8b31a8326..e60145a25 100644 --- a/po/ko.po +++ b/po/ko.po @@ -1125,10 +1125,6 @@ msgstr "사용자 이름" msgid "Verifying…" msgstr "검증 중…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "버전" - #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "기다리는 중…" diff --git a/po/lt.po b/po/lt.po index 3a9d27053..c4c2864c8 100644 --- a/po/lt.po +++ b/po/lt.po @@ -1023,10 +1023,6 @@ msgstr "Naudotojo vardas" msgid "Verifying…" msgstr "Patikrinama…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Versija" - #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Laukiama…" diff --git a/po/oc.po b/po/oc.po index 1b87ecd1a..61e1c6e10 100644 --- a/po/oc.po +++ b/po/oc.po @@ -159,7 +159,3 @@ msgstr "Estatut" #, c-format msgid "Updating %s from %s to %s... " msgstr "Mesa a jorn de %s de %s en %s " - -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Version" diff --git a/po/pl.po b/po/pl.po index 94e7ce222..74f10f55b 100644 --- a/po/pl.po +++ b/po/pl.po @@ -1253,10 +1253,6 @@ msgstr "Producent" msgid "Verifying…" msgstr "Sprawdzanie poprawności…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Wersja" - #. TRANSLATORS: try to help msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" msgstr "OSTRZEŻENIE: ignorowanie ścisłych testów SSL, aby robić to automatycznie w przyszłości, należy wyeksportować zmienną DISABLE_SSL_STRICT w środowisku" diff --git a/po/pt_BR.po b/po/pt_BR.po index 1b7f578c1..b21396b13 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -1243,10 +1243,6 @@ msgstr "Fornecedor" msgid "Verifying…" msgstr "Verificando…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Versão" - #. TRANSLATORS: try to help msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" msgstr "AVISO: Ignorando verificações estritas de SSL. Para fazer isso automaticamente no futuro, exporte DISABLE_SSL_STRICT em seu ambiente" diff --git a/po/ru.po b/po/ru.po index faac99873..e8385378c 100644 --- a/po/ru.po +++ b/po/ru.po @@ -1146,10 +1146,6 @@ msgstr "Имя пользователя" msgid "Verifying…" msgstr "Проверка…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Версия" - #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Ожидание…" diff --git a/po/sk.po b/po/sk.po index 0a04dd94a..63e0f349d 100644 --- a/po/sk.po +++ b/po/sk.po @@ -341,10 +341,6 @@ msgstr "Aktualizuje všetok firmvér na najnovšiu dostupnú verziu" msgid "Updating %s from %s to %s... " msgstr "Aktualizuje sa %s z verzie %s na verziu %s... " -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Verzia" - #. TRANSLATORS: command description msgid "Watch DFU devices being hotplugged" msgstr "Sleduje pripojenie zariadení DFU" diff --git a/po/sr.po b/po/sr.po index 80d112487..a3870bda4 100644 --- a/po/sr.po +++ b/po/sr.po @@ -664,10 +664,6 @@ msgstr "Корисничко име" msgid "Verifying…" msgstr "Проверавам…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Издање" - #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Чекам…" diff --git a/po/sv.po b/po/sv.po index 387f1f644..9c55215fd 100644 --- a/po/sv.po +++ b/po/sv.po @@ -112,7 +112,7 @@ msgstr "Aktivera enheter" #. TRANSLATORS: command description msgid "Activate pending devices" -msgstr "Aktivera väntaden enhter" +msgstr "Aktivera väntande enheter" msgid "Activate the new firmware on the device" msgstr "Aktiverar den nya fasta programvaran på enheten" @@ -555,7 +555,7 @@ msgstr "Uppdateringar av fast programvara stöds på denna maskin." #. TRANSLATORS: device properties #. TRANSLATORS: release properties msgid "Flags" -msgstr "Faggor" +msgstr "Flaggor" msgid "Force the action ignoring all warnings" msgstr "Tvinga åtgärden, ignorera alla varningar" @@ -590,7 +590,7 @@ msgstr "Hämtar den kryptografiska hashen för den utskrivna versionen av den fa #. TRANSLATORS: firmware approved by the admin msgid "Gets the list of approved firmware." -msgstr "Hämtar listan över godkända fast programvara." +msgstr "Hämtar listan över godkänd fast programvara." #. TRANSLATORS: command description msgid "Gets the list of updates for connected hardware" @@ -691,7 +691,7 @@ msgstr "Metadata kan hämtas från Linux Vendor Firmware Service." #. TRANSLATORS: error message #, c-format msgid "Mismatched daemon and client, use %s instead" -msgstr "Demon och klient stämmer inte använd %s istället." +msgstr "Demon och klient stämmer inte, använd %s istället" msgid "Mode" msgstr "Läge" @@ -736,7 +736,7 @@ msgstr "Inga fjärrar är aktiverade för närvarande, så ingen metadata är ti #. TRANSLATORS: nothing was updated offline msgid "No updates were applied" -msgstr "Inga uppdateringar applicerades." +msgstr "Inga uppdateringar applicerades" msgid "OK" msgstr "OK" @@ -860,7 +860,7 @@ msgstr "Starta om nu?" #. TRANSLATORS: configuration changes only take effect on restart msgid "Restart the daemon to make the change effective?" -msgstr "Starta om demonen för att göra så att ändringarna få effekt?" +msgstr "Starta om demonen för att göra så att ändringarna får effekt?" #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" @@ -1043,7 +1043,7 @@ msgstr "Denna fjärrkälla innehåller fast programvara som inte är under embar #. TRANSLATORS: the user needs to stop playing with stuff msgid "This tool can only be used by the root user" -msgstr "Detta verktyg kan endast användas av administratöranvändaren" +msgstr "Detta verktyg kan endast användas av administratörsanvändaren" #. TRANSLATORS: transfer size in bytes msgid "Transfer Size" @@ -1136,10 +1136,6 @@ msgstr "Användarnamn" msgid "Verifying…" msgstr "Verifierar…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Version" - #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Väntar..." diff --git a/po/uk.po b/po/uk.po index 073fc1638..c259852bf 100644 --- a/po/uk.po +++ b/po/uk.po @@ -1253,10 +1253,6 @@ msgstr "Виробник" msgid "Verifying…" msgstr "Перевіряємо…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "Версія" - #. TRANSLATORS: try to help msgid "WARNING: Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" msgstr "ПОПЕРЕДЖЕННЯ: ігноруємо строгі перевірки SSL. Щоб зробити ігнорування у майбутньому автоматичним, експортуйте до вашого середовища змінну DISABLE_SSL_STRICT" diff --git a/po/zh_CN.po b/po/zh_CN.po index 56427c75d..65818c37f 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -1128,10 +1128,6 @@ msgstr "用户名" msgid "Verifying…" msgstr "正在验证…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "版本" - #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "等待中..." diff --git a/po/zh_TW.po b/po/zh_TW.po index aae422c28..e4a570619 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -835,10 +835,6 @@ msgstr "使用者名稱" msgid "Verifying…" msgstr "核驗中…" -#. TRANSLATORS: version number of new firmware -msgid "Version" -msgstr "版本" - #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "等候中…"